1 /** 2 Copyright: Copyright (c) 2016-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.ast.tree; 11 12 import logger = std.experimental.logger; 13 14 import clang.Cursor : Cursor; 15 16 import clang.c.Index : CXCursorKind; 17 18 import libclang_ast.ast.attribute; 19 import libclang_ast.ast.declaration; 20 import libclang_ast.ast.directive; 21 import libclang_ast.ast.expression; 22 import libclang_ast.ast.extra; 23 import libclang_ast.ast.preprocessor; 24 import libclang_ast.ast.reference; 25 import libclang_ast.ast.statement; 26 import libclang_ast.ast.translationunit; 27 28 import libclang_ast.ast.nodes : makeNodeClassName; 29 30 version (unittest) { 31 import unit_threaded : shouldEqual, shouldBeTrue; 32 } 33 34 /** 35 * Wrap a clang cursor. No restrictions on what type of cursor it is. 36 * Accept a Visitor. 37 * During accept traverse the AST to: 38 * - wrap Cursors's in specific classes corresponding to the kind of cursor. 39 * - call Visitor's visit(...) with wrapped cursor. 40 * 41 * Starts traversing the AST from the root. 42 * 43 * Optional functions: 44 * void incr(). Called before descending a node. 45 * void decr(). Called after ascending a node. 46 */ 47 struct ClangAST(VisitorT) { 48 import std.traits : isArray; 49 import clang.Cursor : Cursor; 50 51 Cursor root; 52 53 void accept(ref VisitorT visitor) @safe { 54 dispatch(root, visitor); 55 } 56 } 57 58 /** Apply the visitor to the children of the cursor. 59 * 60 * Optional functions: 61 * void incr(). Called before descending a node. 62 * void decr(). Called after ascending a node. 63 */ 64 void accept(VisitorT)(ref const(Cursor) cursor, ref VisitorT visitor) @safe { 65 import clang.Visitor : Visitor; 66 67 visitor.incr(); 68 scope (exit) 69 visitor.decr(); 70 71 () @trusted { 72 foreach (child, _; Visitor(cursor)) { 73 dispatch(child, visitor); 74 } 75 }(); 76 } 77 78 /** Static wrapping of the cursor followed by a passing it to the visitor. 79 * 80 * The cursor is wrapped in the class that corresponds to the kind of the 81 * cursor. 82 * 83 * Note that the mixins shall be ordered alphabetically. 84 */ 85 void dispatch(VisitorT)(ref const(Cursor) cursor, VisitorT visitor) @safe { 86 import clang.Visitor : Visitor; 87 import libclang_ast.ast.nodes; 88 import std.conv : to; 89 90 // expecting ignoreCursors to be dextool.set.Set. 91 static if (__traits(hasMember, VisitorT, "ignoreCursors")) { 92 const h = cursor.toHash; 93 if (h in visitor.ignoreCursors) { 94 visitor.ignoreCursors.remove(h); 95 () @trusted { 96 foreach (child, _; Visitor(cursor)) { 97 dispatch(child, visitor); 98 } 99 }(); 100 return; 101 } 102 } 103 104 switch (cursor.kind) { 105 mixin(wrapCursor!(visitor, cursor)(AttributeSeq)); 106 mixin(wrapCursor!(visitor, cursor)(DeclarationSeq)); 107 mixin(wrapCursor!(visitor, cursor)(DirectiveSeq)); 108 mixin(wrapCursor!(visitor, cursor)(ExpressionSeq)); 109 mixin(wrapCursor!(visitor, cursor)(ExtraSeq)); 110 mixin(wrapCursor!(visitor, cursor)(PreprocessorSeq)); 111 mixin(wrapCursor!(visitor, cursor)(ReferenceSeq)); 112 mixin(wrapCursor!(visitor, cursor)(StatementSeq)); 113 mixin(wrapCursor!(visitor, cursor)(TranslationUnitSeq)); 114 115 default: 116 debug logger.trace("Node not handled:", to!string(cursor.kind)); 117 } 118 } 119 120 private: 121 122 string wrapCursor(alias visitor, alias cursor)(immutable(string)[] cases) { 123 import std.format : format; 124 125 //TODO allocate in an allocator, not GC with "new" 126 string result; 127 128 foreach (case_; cases) { 129 result ~= format("case CXCursorKind.%s: auto wrapped = new %s(%s); %s.visit(wrapped); break;\n", 130 case_, makeNodeClassName(case_), cursor.stringof, visitor.stringof); 131 } 132 return result; 133 } 134 135 @("shall generate code for a case block that wraps a libclang Cursor in the correct class") 136 unittest { 137 import std.conv : to; 138 139 enum Dummy { 140 xCase1, 141 xCase2 142 } 143 144 int visitor; 145 int cursor; 146 147 wrapCursor!(visitor, cursor)(["Dummy.xCase1", "Dummy.xCase2"]).shouldEqual( 148 "case Dummy.xCase1: auto wrapped = new Case1(cursor); visitor.visit(wrapped); break; 149 case Dummy.xCase2: auto wrapped = new Case2(cursor); visitor.visit(wrapped); break; 150 "); 151 } 152 153 @("shall route nodes visits to the base node of each group") 154 @safe unittest { 155 import libclang_ast.ast : Visitor; 156 import libclang_ast.ast.nodes; 157 158 final class TestVisitor : Visitor { 159 alias visit = Visitor.visit; 160 161 bool attribute; 162 override void visit(const(Attribute)) { 163 attribute = true; 164 } 165 166 bool declaration; 167 override void visit(const(Declaration)) { 168 declaration = true; 169 } 170 171 bool directive; 172 override void visit(const(Directive)) { 173 directive = true; 174 } 175 176 bool expression; 177 override void visit(const(Expression)) { 178 expression = true; 179 } 180 181 bool extra; 182 override void visit(const(Extra)) { 183 extra = true; 184 } 185 186 bool preprocessor; 187 override void visit(const(Preprocessor)) { 188 preprocessor = true; 189 } 190 191 bool reference; 192 override void visit(const(Reference)) { 193 reference = true; 194 } 195 196 bool statement; 197 override void visit(const(Statement)) { 198 statement = true; 199 } 200 201 bool translationunit; 202 override void visit(const(TranslationUnit)) { 203 translationunit = true; 204 } 205 } 206 207 auto test = new TestVisitor; 208 209 Cursor cursor; 210 211 foreach (kind; [ 212 __traits(getMember, CXCursorKind, AttributeSeq[0]), 213 __traits(getMember, CXCursorKind, DeclarationSeq[0]), 214 __traits(getMember, CXCursorKind, DirectiveSeq[0]), 215 __traits(getMember, CXCursorKind, ExpressionSeq[0]), 216 __traits(getMember, CXCursorKind, ExtraSeq[0]), 217 __traits(getMember, CXCursorKind, PreprocessorSeq[0]), 218 __traits(getMember, CXCursorKind, ReferenceSeq[0]), 219 __traits(getMember, CXCursorKind, StatementSeq[0]), 220 __traits(getMember, CXCursorKind, TranslationUnitSeq[0]) 221 ]) { 222 switch (kind) { 223 mixin(wrapCursor!(test, cursor)(AttributeSeq)); 224 mixin(wrapCursor!(test, cursor)(DeclarationSeq)); 225 mixin(wrapCursor!(test, cursor)(DirectiveSeq)); 226 mixin(wrapCursor!(test, cursor)(ExpressionSeq)); 227 mixin(wrapCursor!(test, cursor)(ExtraSeq)); 228 mixin(wrapCursor!(test, cursor)(PreprocessorSeq)); 229 mixin(wrapCursor!(test, cursor)(ReferenceSeq)); 230 mixin(wrapCursor!(test, cursor)(StatementSeq)); 231 mixin(wrapCursor!(test, cursor)(TranslationUnitSeq)); 232 default: 233 assert(0); 234 } 235 } 236 237 test.attribute.shouldBeTrue; 238 test.declaration.shouldBeTrue; 239 test.directive.shouldBeTrue; 240 test.expression.shouldBeTrue; 241 test.extra.shouldBeTrue; 242 test.preprocessor.shouldBeTrue; 243 test.reference.shouldBeTrue; 244 test.statement.shouldBeTrue; 245 test.translationunit.shouldBeTrue; 246 }