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 }