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 auto visitBreathFirst(Cursor c) @trusted {
84     return AST_BreathFirstResult(c);
85 }