1 /**
2 Copyright: Copyright (c) 2017-2021, 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 libclang_ast.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 private @safe nothrow struct AST_BreathFirstResult {
22     import std.container : Array;
23 
24     private int depth_;
25     private typeof(Array!(Cursor).opSlice()) r;
26     // index 0: the current range that is operated on.
27     // index 1: the next one that is being filled with data.
28     private Array!(Cursor)[] data;
29 
30     this(Cursor c) @trusted {
31         data ~= Array!Cursor();
32         data ~= Array!Cursor();
33         data[0].insertBack(c);
34         r = data[0][];
35     }
36 
37     ASTCursor front() @safe nothrow const {
38         assert(!empty, "Can't get front of an empty range");
39 
40         return ASTCursor(r.front, depth_);
41     }
42 
43     void popFront() @trusted {
44         assert(!empty, "Can't pop front of an empty range");
45 
46         import clang.Visitor;
47 
48         foreach (cursor, _; Visitor(r.front)) {
49             data[1].insertBack(cursor);
50         }
51 
52         r.popFront;
53 
54         if (r.length == 0) {
55             data = data[1 .. $];
56             r = data[0][];
57             data ~= Array!Cursor();
58             ++depth_;
59         }
60     }
61 
62     bool empty() @safe nothrow const {
63         return r.empty && data[1].empty;
64     }
65 
66     int depth() {
67         return depth_;
68     }
69 }
70 
71 /** opApply compatible visitor of the clang AST, breath first.
72  *
73  * Example:
74  * ---
75  * foreach (child; c.visitBreathFirst.until!(a => a.depth == 3)) {
76  *      if (child.kind == CXCursorKind.CXCursor_StructDecl) {
77  *      ...
78  *      }
79  * }
80  * ---
81  */
82 auto visitBreathFirst(Cursor c) @trusted {
83     return AST_BreathFirstResult(c);
84 }
85 
86 private @safe nothrow struct AST_DepthFirstResult {
87     static import std.array;
88     import std.array : appender;
89     import std.container : Array;
90     import my.set : Set;
91 
92     //private Array!(Cursor) stack;
93     // keeps the first node for each level of nodes that is traversed.
94     //private Set!Cursor depth_;
95     private Cursor[][] stack;
96 
97     this(Cursor c) @trusted {
98         stack ~= [c];
99     }
100 
101     ASTCursor front() @safe nothrow const {
102         assert(!empty, "Can't get front of an empty range");
103         return ASTCursor(stack[$ - 1][0], depth);
104     }
105 
106     void popFront() @trusted {
107         assert(!empty, "Can't pop front of an empty range");
108         import clang.Visitor;
109 
110         void popEmpty() {
111             while (!std.array.empty(stack) && std.array.empty(stack[$ - 1])) {
112                 stack = stack[0 .. $ - 1];
113             }
114         }
115 
116         popEmpty;
117 
118         auto f = stack[$ - 1][0];
119         stack[$ - 1] = stack[$ - 1][1 .. $];
120 
121         auto app = appender!(Cursor[])();
122         foreach (cursor, _; Visitor(f)) {
123             app.put(cursor);
124         }
125         if (!std.array.empty(app.data)) {
126             stack ~= app.data;
127         }
128 
129         popEmpty;
130     }
131 
132     bool empty() @safe nothrow const {
133         return std.array.empty(stack);
134     }
135 
136     size_t depth() @safe pure nothrow const @nogc {
137         return stack.length;
138     }
139 }
140 
141 /** opApply compatible visitor of the clang AST, breath first.
142  *
143  * Example:
144  * ---
145  * foreach (child; c.visitBreathFirst.until!(a => a.depth == 3)) {
146  *      if (child.kind == CXCursorKind.CXCursor_StructDecl) {
147  *      ...
148  *      }
149  * }
150  * ---
151  */
152 auto visitDepthFirst(Cursor c) @trusted {
153     return AST_DepthFirstResult(c);
154 }