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