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(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)(const(Cursor) cursor, 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)(const(Cursor) cursor, VisitorT visitor) @safe { 86 import clang.Visitor : Visitor; 87 import libclang_ast.ast.nodes; 88 import std.conv : to; 89 90 static if (__traits(hasMember, VisitorT, "ignoreCursors")) { 91 const h = cursor.toHash; 92 if (h in visitor.ignoreCursors) { 93 visitor.ignoreCursors.remove(h); 94 () @trusted { 95 foreach (child, _; Visitor(cursor)) { 96 dispatch(child, visitor); 97 } 98 }(); 99 return; 100 } 101 } 102 103 switch (cursor.kind) { 104 mixin(wrapCursor!(visitor, cursor)(AttributeSeq)); 105 mixin(wrapCursor!(visitor, cursor)(DeclarationSeq)); 106 mixin(wrapCursor!(visitor, cursor)(DirectiveSeq)); 107 mixin(wrapCursor!(visitor, cursor)(ExpressionSeq)); 108 mixin(wrapCursor!(visitor, cursor)(ExtraSeq)); 109 mixin(wrapCursor!(visitor, cursor)(PreprocessorSeq)); 110 mixin(wrapCursor!(visitor, cursor)(ReferenceSeq)); 111 mixin(wrapCursor!(visitor, cursor)(StatementSeq)); 112 mixin(wrapCursor!(visitor, cursor)(TranslationUnitSeq)); 113 114 default: 115 debug logger.trace("Node not handled:", to!string(cursor.kind)); 116 } 117 } 118 119 private: 120 121 string wrapCursor(alias visitor, alias cursor)(immutable(string)[] cases) { 122 import std.format : format; 123 124 string result; 125 126 foreach (case_; cases) { 127 result ~= format("case CXCursorKind.%s: scope wrapped = new %s(%s); %s.visit(wrapped); break;\n", 128 case_, makeNodeClassName(case_), cursor.stringof, visitor.stringof); 129 } 130 return result; 131 } 132 133 @("shall generate code for a case block that wraps a libclang Cursor in the correct class") 134 unittest { 135 import std.conv : to; 136 137 enum Dummy { 138 xCase1, 139 xCase2 140 } 141 142 int visitor; 143 int cursor; 144 145 wrapCursor!(visitor, cursor)(["Dummy.xCase1", "Dummy.xCase2"]).shouldEqual( 146 "case Dummy.xCase1: scope wrapped = new Case1(cursor); visitor.visit(wrapped); break; 147 case Dummy.xCase2: scope wrapped = new Case2(cursor); visitor.visit(wrapped); break; 148 "); 149 } 150 151 @("shall route nodes visits to the base node of each group") 152 @safe unittest { 153 import libclang_ast.ast : Visitor; 154 import libclang_ast.ast.nodes; 155 156 final class TestVisitor : Visitor { 157 alias visit = Visitor.visit; 158 159 bool attribute; 160 override void visit(const(Attribute)) { 161 attribute = true; 162 } 163 164 bool declaration; 165 override void visit(const(Declaration)) { 166 declaration = true; 167 } 168 169 bool directive; 170 override void visit(const(Directive)) { 171 directive = true; 172 } 173 174 bool expression; 175 override void visit(const(Expression)) { 176 expression = true; 177 } 178 179 bool extra; 180 override void visit(const(Extra)) { 181 extra = true; 182 } 183 184 bool preprocessor; 185 override void visit(const(Preprocessor)) { 186 preprocessor = true; 187 } 188 189 bool reference; 190 override void visit(const(Reference)) { 191 reference = true; 192 } 193 194 bool statement; 195 override void visit(const(Statement)) { 196 statement = true; 197 } 198 199 bool translationunit; 200 override void visit(const(TranslationUnit)) { 201 translationunit = true; 202 } 203 } 204 205 auto test = new TestVisitor; 206 207 Cursor cursor; 208 209 foreach (kind; [ 210 __traits(getMember, CXCursorKind, AttributeSeq[0]), 211 __traits(getMember, CXCursorKind, DeclarationSeq[0]), 212 __traits(getMember, CXCursorKind, DirectiveSeq[0]), 213 __traits(getMember, CXCursorKind, ExpressionSeq[0]), 214 __traits(getMember, CXCursorKind, ExtraSeq[0]), 215 __traits(getMember, CXCursorKind, PreprocessorSeq[0]), 216 __traits(getMember, CXCursorKind, ReferenceSeq[0]), 217 __traits(getMember, CXCursorKind, StatementSeq[0]), 218 __traits(getMember, CXCursorKind, TranslationUnitSeq[0]) 219 ]) { 220 switch (kind) { 221 mixin(wrapCursor!(test, cursor)(AttributeSeq)); 222 mixin(wrapCursor!(test, cursor)(DeclarationSeq)); 223 mixin(wrapCursor!(test, cursor)(DirectiveSeq)); 224 mixin(wrapCursor!(test, cursor)(ExpressionSeq)); 225 mixin(wrapCursor!(test, cursor)(ExtraSeq)); 226 mixin(wrapCursor!(test, cursor)(PreprocessorSeq)); 227 mixin(wrapCursor!(test, cursor)(ReferenceSeq)); 228 mixin(wrapCursor!(test, cursor)(StatementSeq)); 229 mixin(wrapCursor!(test, cursor)(TranslationUnitSeq)); 230 default: 231 assert(0); 232 } 233 } 234 235 test.attribute.shouldBeTrue; 236 test.declaration.shouldBeTrue; 237 test.directive.shouldBeTrue; 238 test.expression.shouldBeTrue; 239 test.extra.shouldBeTrue; 240 test.preprocessor.shouldBeTrue; 241 test.reference.shouldBeTrue; 242 test.statement.shouldBeTrue; 243 test.translationunit.shouldBeTrue; 244 }