1 #!/usr/bin/env dub 2 /+ dub.sdl: 3 name "generate_clang_ast_nodes" 4 lflags "-L/usr/lib/llvm-10/lib" 5 libs ":libclang.so.1" 6 sourcePaths "source" "../libs/libclang/source" 7 +/ 8 /** 9 Copyright: Copyright (c) 2016-2021, Joakim Brännström. All rights reserved. 10 License: MPL-2 11 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 12 13 This Source Code Form is subject to the terms of the Mozilla Public License, 14 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 15 one at http://mozilla.org/MPL/2.0/. 16 */ 17 module app; 18 19 import std.format : format; 20 import std.stdio : File; 21 import std.string : toLower, toUpper; 22 23 import libclang_ast.ast.nodes; 24 25 void main(string[] args) { 26 generateNodeCode(AttributeSeq, "Attribute"); 27 generateNodeCode(DeclarationSeq, "Declaration"); 28 generateNodeCode(DirectiveSeq, "Directive"); 29 generateNodeCode(ExpressionSeq, "Expression"); 30 generateNodeCode(ExtraSeq, "Extra"); 31 generateNodeCode(PreprocessorSeq, "Preprocessor"); 32 generateNodeCode(ReferenceSeq, "Reference"); 33 generateNodeCode(StatementSeq, "Statement"); 34 35 struct SeqBase { 36 immutable(string)[] seq; 37 string base; 38 } 39 40 // dfmt off 41 generateVisitorCode( 42 SeqBase(AttributeSeq, "Attribute"), 43 SeqBase(DeclarationSeq, "Declaration"), 44 SeqBase(DirectiveSeq, "Directive"), 45 SeqBase(ExpressionSeq, "Expression"), 46 SeqBase(ExtraSeq, "Extra"), 47 SeqBase(PreprocessorSeq, "Preprocessor"), 48 SeqBase(ReferenceSeq, "Reference"), 49 SeqBase(StatementSeq, "Statement"), 50 ); 51 // dfmt on 52 } 53 54 void generateNodeCode(Char)(Char[] seq, string name) { 55 // template arguments 56 // 0: the module name of the base node group. 57 // 1: the name of the base module. 58 // 2: data containing the specialized nodes. 59 immutable template_ = `/** 60 Copyright: Copyright (c) 2016, Joakim Brännström. All rights reserved. 61 License: MPL-2 62 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 63 64 This Source Code Form is subject to the terms of the Mozilla Public License, 65 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 66 one at http://mozilla.org/MPL/2.0/. 67 68 DO NOT EDIT. THIS FILE IS GENERATED. 69 See the generator script source/devtool/generator_clang_ast_nodes.d 70 */ 71 module libclang_ast.ast.%s; 72 import libclang_ast.ast.node : Node; 73 74 abstract class %s : Node { 75 import clang.Cursor : Cursor; 76 import libclang_ast.ast : Visitor; 77 78 Cursor cursor; 79 alias cursor this; 80 81 this(Cursor cursor) @safe { 82 this.cursor = cursor; 83 } 84 85 override void accept(Visitor v) @safe const { 86 static import libclang_ast.ast; 87 libclang_ast.ast.accept(cursor, v); 88 } 89 } 90 91 %s 92 `; 93 94 string name_lower = name.toLower; 95 96 auto file = File("../source/libclang_ast/ast/" ~ name_lower ~ ".d", "w"); 97 file.write(format(template_, name_lower, name, generateNodes(seq.dup, name))); 98 } 99 100 template generateNodeAccept() { 101 enum generateNodeAccept = q{ 102 override void accept(Visitor v) @safe const { 103 static import libclang_ast.ast; 104 libclang_ast.ast.accept(cursor, v); 105 } 106 }; 107 } 108 109 template generateNodeCtor() { 110 enum generateNodeCtor = q{ 111 import clang.Cursor : Cursor; 112 this(Cursor cursor) @safe { 113 super(cursor); 114 } 115 }; 116 } 117 118 string generateNodeClass(string kind, string base) { 119 import std.format : format; 120 121 return format(q{ 122 final class %s : %s {%s%s}}, makeNodeClassName(kind), base, 123 generateNodeCtor!(), generateNodeAccept!()); 124 } 125 126 unittest { 127 // @Name("Should be the mixin string of an AST node") 128 129 // dfmt off 130 generateNodeClass("CXCursorKind.unexposedDecl", "UtNode") 131 .splitter('\n') 132 .map!(a => a.strip) 133 .shouldEqual( 134 q{ 135 final class UnexposedDecl : UtNode { 136 import clang.Cursor : Cursor; 137 this(Cursor cursor) @safe { 138 super(cursor); 139 } 140 141 override void accept(Visitor v) @safe const { 142 static import libclang_ast.ast; 143 libclang_ast.ast.accept(cursor, v); 144 } 145 }}.splitter('\n') 146 .map!(a => a.strip)); 147 // dfmt on 148 } 149 150 string generateNodes(string[] seq, string base) { 151 import std.meta : staticMap; 152 import std.conv : to; 153 154 string mixins; 155 foreach (node; seq) { 156 mixins ~= generateNodeClass(node, base); 157 mixins ~= "\n"; 158 } 159 160 return mixins; 161 } 162 163 unittest { 164 // @Name("Should be the mixin string for many AST nodes") 165 166 // dfmt off 167 generateNodes(["unexposedDecl", 168 "structDecl"], "UtNode") 169 .splitter('\n') 170 .map!(a => a.strip) 171 .shouldEqualPretty( 172 q{ 173 final class UnexposedDecl : UtNode { 174 import clang.Cursor : Cursor; 175 this(Cursor cursor) @safe { 176 super(cursor); 177 } 178 179 override void accept(Visitor v) @safe const { 180 static import libclang_ast.ast; 181 libclang_ast.ast.accept(cursor, v); 182 } 183 } 184 185 final class StructDecl : UtNode { 186 import clang.Cursor : Cursor; 187 this(Cursor cursor) @safe { 188 super(cursor); 189 } 190 191 override void accept(Visitor v) @safe const { 192 static import libclang_ast.ast; 193 libclang_ast.ast.accept(cursor, v); 194 } 195 } 196 }.splitter('\n') 197 .map!(a => a.strip)); 198 // dfmt on 199 } 200 201 void generateVisitorCode(ARGS...)(ARGS args) { 202 immutable template_ = `/** 203 Copyright: Copyright (c) 2016-2021, Joakim Brännström. All rights reserved. 204 License: MPL-2 205 Author: Joakim Brännström (joakim.brannstrom@gmx.com) 206 207 This Source Code Form is subject to the terms of the Mozilla Public License, 208 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain 209 one at http://mozilla.org/MPL/2.0/. 210 211 DO NOT EDIT. THIS FILE IS GENERATED. 212 See the generator script source/devtool/generator_clang_ast_nodes.d 213 */ 214 module libclang_ast.ast.base_visitor; 215 abstract class Visitor { 216 import libclang_ast.ast; 217 218 @safe: 219 220 /// Called when entering a node 221 void incr() { 222 } 223 224 /// Called when leaving a node 225 void decr() { 226 } 227 228 void visit(const TranslationUnit) { 229 } 230 %s 231 } 232 `; 233 234 auto file = File("../source/libclang_ast/ast/base_visitor.d", "w"); 235 236 string visits; 237 foreach (arg; args) { 238 visits ~= generateVisit(arg.base, arg.seq); 239 } 240 241 file.write(format(template_, visits)); 242 } 243 244 string generateVisit(string Base, immutable(string)[] E) { 245 import std.format : format; 246 247 string result = format(q{ 248 void visit(const(%s)) {} 249 }, Base); 250 251 foreach (e; E) { 252 253 result ~= format(q{ 254 void visit(const(%s) value) { 255 visit(cast(const(%s)) value); 256 } 257 }, makeNodeClassName(e), Base); 258 } 259 260 return result; 261 } 262 263 unittest { 264 class Declaration { 265 } 266 267 // dfmt off 268 generateVisit("Declaration", ["unexposedDecl", 269 "unionDecl"]) 270 .splitter('\n') 271 .map!(a => a.strip) 272 .array() 273 .shouldEqualPretty([ 274 "", 275 "void visit(const(Declaration)) {}", 276 "", 277 "void visit(const(UnexposedDecl) value) {", 278 "visit(cast(const(Declaration)) value);", 279 "}", 280 "", 281 "void visit(const(UnionDecl) value) {", 282 "visit(cast(const(Declaration)) value);", 283 "}", 284 "" 285 ]); 286 // dfmt on 287 }