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 }