1 /**
2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 */
10 module cpptooling.analyzer.clang.cursor_visitor;
11 
12 import clang.Cursor : Cursor;
13 
14 private @safe nothrow struct ASTCursor {
15     Cursor cursor;
16     size_t depth;
17 
18     alias cursor this;
19 }
20 
21 /**
22  */
23 private @safe nothrow struct AST_BreathFirstResult {
24     import std.container : Array;
25 
26     private int depth_;
27     private typeof(Array!(Cursor).opSlice()) r;
28     // index 0: the current range that is operated on.
29     // index 1: the next one that is being filled with data.
30     private Array!(Cursor)[] data;
31 
32     this(Cursor c) @trusted {
33         data ~= Array!Cursor();
34         data ~= Array!Cursor();
35         data[0].insertBack(c);
36         r = data[0][];
37     }
38 
39     ASTCursor front() @safe nothrow const {
40         assert(!empty, "Can't get front of an empty range");
41 
42         return ASTCursor(r.front, depth_);
43     }
44 
45     void popFront() @trusted {
46         assert(!empty, "Can't pop front of an empty range");
47 
48         import clang.Visitor;
49 
50         foreach (cursor, _; Visitor(r.front)) {
51             data[1].insertBack(cursor);
52         }
53 
54         r.popFront;
55 
56         if (r.length == 0) {
57             data = data[1 .. $];
58             r = data[0][];
59             data ~= Array!Cursor();
60             ++depth_;
61         }
62     }
63 
64     bool empty() @safe nothrow const {
65         return r.empty && data[1].empty;
66     }
67 
68     int depth() {
69         return depth_;
70     }
71 }
72 
73 /** opApply compatible visitor of the clang AST, breath first.
74  *
75  * Example:
76  * ---
77  * foreach (child; c.visitBreathFirst.until!(a => a.depth == 3)) {
78  *      if (child.kind == CXCursorKind.CXCursor_StructDecl) {
79  *      ...
80  *      }
81  * }
82  * ---
83  */
84 auto visitBreathFirst(Cursor c) @trusted {
85     return AST_BreathFirstResult(c);
86 }
87 
88 private @safe nothrow struct AST_DepthFirstResult {
89     static import std.array;
90     import std.array : appender;
91     import std.container : Array;
92     import my.set : Set;
93 
94     //private Array!(Cursor) stack;
95     // keeps the first node for each level of nodes that is traversed.
96     //private Set!Cursor depth_;
97     private Cursor[][] stack;
98 
99     this(Cursor c) @trusted {
100         stack ~= [c];
101     }
102 
103     ASTCursor front() @safe nothrow const {
104         assert(!empty, "Can't get front of an empty range");
105         return ASTCursor(stack[$ - 1][0], depth);
106     }
107 
108     void popFront() @trusted {
109         assert(!empty, "Can't pop front of an empty range");
110         import clang.Visitor;
111 
112         void popEmpty() {
113             while (!std.array.empty(stack) && std.array.empty(stack[$ - 1])) {
114                 stack = stack[0 .. $ - 1];
115             }
116         }
117 
118         popEmpty;
119 
120         auto f = stack[$ - 1][0];
121         stack[$ - 1] = stack[$ - 1][1 .. $];
122 
123         auto app = appender!(Cursor[])();
124         foreach (cursor, _; Visitor(f)) {
125             app.put(cursor);
126         }
127         if (!std.array.empty(app.data)) {
128             stack ~= app.data;
129         }
130 
131         popEmpty;
132     }
133 
134     bool empty() @safe nothrow const {
135         return std.array.empty(stack);
136     }
137 
138     size_t depth() @safe pure nothrow const @nogc {
139         return stack.length;
140     }
141 }
142 
143 /** opApply compatible visitor of the clang AST, breath first.
144  *
145  * Example:
146  * ---
147  * foreach (child; c.visitBreathFirst.until!(a => a.depth == 3)) {
148  *      if (child.kind == CXCursorKind.CXCursor_StructDecl) {
149  *      ...
150  *      }
151  * }
152  * ---
153  */
154 auto visitDepthFirst(Cursor c) @trusted {
155     return AST_DepthFirstResult(c);
156 }