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 }