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 }