1 /**
2   * Printer for AST
3   *
4   * Copyright:
5   *     Copyright (c) 2018, Maxim Tyapkin.
6   * Authors:
7   *     Maxim Tyapkin
8   * License:
9   *     This software is licensed under the terms of the BSD 3-clause license.
10   *     The full terms of the license can be found in the LICENSE.md file.
11   */
12 
13 module djinja.ast.printer;
14 
15 private
16 {
17     import djinja.ast.node;
18     import djinja.ast.visitor;
19 }
20 
21 
22 class NullVisitor : IVisitor
23 {
24     static foreach(NT; NodeTypes)
25     {
26         void visit(NT node)
27         {
28             import std.stdio: wl = writeln;
29             wl("# ", NT.stringof, " #");
30         }
31     }
32 }
33 
34 
35 class Printer : NullVisitor
36 {
37     import std.stdio: wl = writeln, w = write;
38     import std.format: fmt = format;
39 
40     uint _tab = 0;
41 
42     override void visit(StmtBlockNode node)
43     {
44         print("Statement Block:"); 
45         _tab++;
46         foreach(ch; node.children)
47         {
48             ch.accept(this);
49         }
50         _tab--;
51     }
52 
53 
54     override void visit(TemplateNode node)
55     {
56         print("Template:");
57 
58         _tab++;
59 
60         if (!node.blocks.length)
61             print("Blocks: NONE");
62         else
63         {
64             print("Blocks:");
65             _tab++;
66             foreach (key, block; node.blocks)
67                 print(key);
68             _tab--;
69         }
70 
71         print("Statements:");
72         _tab++;
73         if (!node.stmt.isNull)
74             node.stmt.accept(this);
75         _tab--;
76 
77         _tab--;
78     }
79 
80 
81     override void visit(BlockNode node)
82     {
83         print("Block: %s".fmt(node.name));
84         _tab++;
85         if (!node.stmt.isNull)
86             node.stmt.accept(this);
87         _tab--;
88     }
89 
90 
91     override void visit(RawNode node)
92     {
93         import std.array : replace;
94         print("Raw block: \"%s\"".fmt(node.raw.replace("\n", "\\n").replace(" ", ".")));
95     }
96 
97 
98     override void visit(ExprNode node)
99     {
100         print("Expression block:");
101         _tab++;
102         node.expr.accept(this);
103         _tab--;
104     }
105 
106 
107     override void visit(InlineIfNode node)
108     {
109         print("Inline If:");
110 
111         _tab++;
112 
113         if (node.cond.isNull)
114             print("If: NONE");
115         else
116         {
117             print("If:");
118             _tab++;
119             node.cond.accept(this);
120             _tab--;
121         }
122         
123         print("Expression:");
124         _tab++;
125         node.expr.accept(this);
126         _tab--;
127 
128         if (node.other.isNull)
129             print("Else: nil");
130         else
131         {
132             print("Else:");
133             _tab++;
134             node.other.accept(this);
135             _tab--;
136         }
137 
138         _tab--;
139     }
140 
141 
142     override void visit(BinOpNode node)
143     {
144         print("BinaryOp: %s".fmt(node.op));
145         _tab++;
146         node.lhs.accept(this);
147         node.rhs.accept(this);
148         _tab--;
149     }
150 
151 
152     override void visit(UnaryOpNode node)
153     {
154         print("UnaryOp: %s".fmt(node.op));
155         _tab++;
156         node.expr.accept(this);
157         _tab--;
158     }
159 
160 
161     override void visit(NumNode node)
162     {
163         if (node.type == NumNode.Type.Integer)
164             print("Integer: %d".fmt(node.data._integer));
165         else
166             print("Float: %f".fmt(node.data._float));
167     }
168 
169 
170     override void visit(BooleanNode node)
171     {
172         print("Bool: %s".fmt(node.boolean));
173     }
174 
175 
176     override void visit(NilNode node)
177     {
178         print("Nil");
179     }
180 
181 
182     override void visit(IdentNode node)
183     {
184         print("Ident: '%s'".fmt(node.name));
185         if (node.subIdents.length)
186         {
187             print("Sub idents:");
188             _tab++;
189             foreach (id; node.subIdents)
190                 id.accept(this);
191             _tab--;
192         }
193     }
194 
195 
196     override void visit(AssignableNode node)
197     {
198         print("Assignable: %s".fmt(node.name));
199         if (node.subIdents.length)
200         {
201             _tab++;
202             print("Sub idents:");
203             _tab++;
204             foreach (id; node.subIdents)
205                 id.accept(this);
206             _tab--;
207             _tab--;
208         }
209     }
210 
211 
212     override void visit(StringNode node)
213     {
214         print("String: %s".fmt(node.str));
215     }
216 
217 
218     override void visit(ListNode node)
219     {
220         print("List:");
221         _tab++;
222         foreach (l; node.list)
223             l.accept(this);
224         _tab--;
225     }
226 
227 
228     override void visit(DictNode node)
229     {
230         print("Dict:");
231         _tab++;
232         foreach (key, value; node.dict)
233         {
234             print("Key: %s".fmt(key));
235             print("Value:");
236             _tab++;
237             value.accept(this);
238             _tab--;
239         }
240         _tab--;
241     }
242 
243 
244     override void visit(IfNode node)
245     {
246         print("If:");
247         _tab++;
248 
249         print("Condition:");
250         _tab++;
251         node.cond.accept(this);
252         _tab--;
253 
254         print("Then:");
255         _tab++;
256         node.then.accept(this);
257         _tab--;
258 
259         if (node.other)
260         {
261             print("Else:");
262             _tab++;
263             node.other.accept(this);
264             _tab--;
265         }
266         else
267             print("Else: NONE");
268         _tab--;
269     }
270 
271 
272     override void visit(ForNode node)
273     {
274         print("For:");
275         _tab++;
276 
277         print("Keys:");
278         _tab++;
279         foreach (key; node.keys)
280             print(key);
281         _tab--;
282 
283         print("Iterable:");
284         _tab++;
285         node.iterable.accept(this);
286         _tab--;
287 
288         print("Block:");
289         _tab++;
290         node.block.accept(this);
291         _tab--;
292 
293         if (!node.cond.isNull)
294         {
295             print("Condition:");
296             _tab++;
297             node.cond.accept(this);
298             _tab--;
299         }
300         else
301             print("Condition: NONE");
302 
303 
304         if (!node.other.isNull)
305         {
306             print("Else:");
307             _tab++;
308             node.other.accept(this);
309             _tab--;
310         }
311         else
312             print("Else: NONE");
313 
314         print("Recursive: %s".fmt(node.isRecursive));
315 
316         _tab--;
317     }
318 
319 
320     override void visit(SetNode node)
321     {
322         print("Set:");
323         _tab++;
324 
325         print("Assigns:");
326         _tab++;
327         foreach (assign; node.assigns)
328             assign.accept(this);
329         _tab--;
330 
331         print("Expression:");
332         _tab++;
333         node.expr.accept(this);
334         _tab--;
335 
336         _tab--;
337     }
338 
339 
340     override void visit(MacroNode node)
341     {
342         print("Macro: '%s'".fmt(node.name));
343 
344         _tab++;
345         if (!node.args.length)
346             print("Args: NONE");
347         else
348         {
349             print("Args:");
350             _tab++;
351             foreach(arg; node.args)
352             {
353                 print("Name: %s".fmt(arg.name));
354                 if (!arg.defaultExpr.isNull)
355                 {
356                     _tab++;
357                     print("Default:");
358                     _tab++;
359                     arg.defaultExpr.accept(this);
360                     _tab--;
361                     _tab--;
362                 }
363             }
364             _tab--;
365         }
366 
367         print("Body:");
368         _tab++;
369         node.block.accept(this);
370         _tab--;
371 
372         _tab--;
373 
374         print("Return: %s".fmt(node.isReturn));
375     }
376 
377 
378     override void visit(CallNode node)
379     {
380         print("Call: '%s'".fmt(node.macroName));
381 
382         _tab++;
383 
384         if (!node.formArgs.length)
385             print("Formal args: NONE");
386         else
387         {
388             print("Formal args:");
389             _tab++;
390             foreach(arg; node.formArgs)
391             {
392                 print("Name: %s".fmt(arg.name));
393                 if (!arg.defaultExpr.isNull)
394                 {
395                     _tab++;
396                     print("Default:");
397                     _tab++;
398                     arg.defaultExpr.accept(this);
399                     _tab--;
400                     _tab--;
401                 }
402             }
403             _tab--;
404         }
405 
406         if (node.factArgs.isNull)
407             print("Fact args: NONE");
408         else
409         {
410             print("Fact args:");
411             _tab++;
412             node.factArgs.accept(this);
413             _tab--;
414         }
415 
416         print("Body:");
417         _tab++;
418         node.block.accept(this);
419         _tab--;
420 
421         _tab--;
422     }
423 
424 
425     override void visit(FilterBlockNode node)
426     {
427         print("Filter: '%s'".fmt(node.filterName));
428 
429         _tab++;
430 
431         if (node.args.isNull)
432             print("Args: NONE");
433         else
434         {
435             print("Args:");
436             _tab++;
437             node.args.accept(this);
438             _tab--;
439         }
440 
441         print("Body:");
442         _tab++;
443         node.block.accept(this);
444         _tab--;
445 
446         _tab--;
447     }
448 
449 
450     override void visit(ImportNode node)
451     {
452         print("Import: '%s'".fmt(node.fileName));
453 
454         _tab++;
455 
456         if (!node.macrosNames.length)
457             print("Macros: all");
458         else
459         {
460             print("Macros:");
461             _tab++;
462             foreach(name; node.macrosNames)
463                 print("%s -> %s".fmt(name.was, name.become));
464             _tab--;
465         }
466 
467         if (node.tmplBlock.isNull)
468             print("Block: Missing");
469         else
470             print("Block: %s children".fmt(node.tmplBlock.stmt.children.length));
471 
472         if (node.withContext)
473             print("Context: with");
474         else
475             print("Context: without");
476 
477         _tab--;
478     }
479 
480 
481     override void visit(IncludeNode node)
482     {
483         print("Include: '%s'".fmt(node.fileName));
484 
485         _tab++;
486 
487         if (node.tmplBlock.isNull)
488             print("Block: Missing");
489         else
490             print("Block: %s children".fmt(node.tmplBlock.stmt.children.length));
491 
492         if (node.withContext)
493             print("Context: with");
494         else
495             print("Context: without");
496 
497         _tab--;
498     }
499 
500 
501     override void visit(ExtendsNode node)
502     {
503         print("Extends: '%s'".fmt(node.fileName));
504 
505         _tab++;
506 
507         if (node.tmplBlock.isNull)
508             print("Block: Missing");
509         else
510             print("Block: %s children".fmt(node.tmplBlock.stmt.children.length));
511 
512         _tab--;
513     }
514 
515 
516 protected:
517 
518 
519     void print(string str)
520     {
521         foreach(i; 0 .. _tab)
522             w("-   ");
523         wl(str);
524     }
525 }