1 /**
2   * AST nodes
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.node;
14 
15 public
16 {
17     import std.typecons : Nullable, nullable;
18     import djinja.lexer : Position;
19 }
20 
21 private
22 {
23     import std.meta : AliasSeq;
24 
25     import djinja.ast.visitor;
26 }
27 
28 
29 alias NodeTypes = AliasSeq!(
30         TemplateNode,
31         BlockNode,
32         StmtBlockNode,
33         RawNode,
34         ExprNode,
35         UnaryOpNode,
36         BinOpNode,
37         StringNode,
38         BooleanNode,
39         NilNode,
40         ListNode,
41         DictNode,
42         NumNode,
43         IdentNode,
44         IfNode,
45         ForNode,
46         SetNode,
47         AssignableNode,
48         MacroNode,
49         CallNode,
50         InlineIfNode,
51         FilterBlockNode,
52         ImportNode,
53         IncludeNode,
54         ExtendsNode,
55     );
56 
57 
58 
59 interface INode
60 {
61     void accept(IVisitor);
62 }
63 
64 
65 mixin template AcceptVisitor()
66 {
67     override void accept(IVisitor visitor)
68     {
69         visitor.visit(this);
70     }
71 }
72 
73 
74 
75 abstract class Node : INode
76 {
77     public Position pos;
78 
79     void accept(IVisitor visitor) {}
80 }
81 
82 
83 class TemplateNode : Node
84 {
85     Nullable!StmtBlockNode stmt;
86     BlockNode[string] blocks;
87 
88     this(Position pos, StmtBlockNode stmt, BlockNode[string] blocks)
89     {
90         this.pos = pos;
91         this.stmt = stmt.toNullable;
92         this.blocks = blocks;
93     }
94 
95     mixin AcceptVisitor;
96 }
97 
98 
99 
100 class BlockNode : Node
101 {
102     string name;
103     Nullable!Node stmt;
104 
105     this(Position pos, string name, Node stmt)
106     {
107         this.pos = pos;
108         this.name = name;
109         this.stmt = stmt.toNullable;
110     }
111 
112     mixin AcceptVisitor;
113 }
114 
115 
116 
117 class StmtBlockNode : Node
118 {
119     Node[] children;
120 
121     this(Position pos)
122     {
123         this.pos = pos;
124     }
125 
126     mixin AcceptVisitor;
127 }
128 
129 
130 
131 class RawNode : Node
132 {
133     string raw;
134 
135     this(Position pos, string raw)
136     {
137         this.pos = pos;
138         this.raw = raw;
139     }
140 
141     mixin AcceptVisitor;
142 }
143 
144 
145 
146 class ExprNode : Node
147 {
148     Nullable!Node expr;
149 
150     this(Position pos, Node expr)
151     {
152         this.pos = pos;
153         this.expr = expr.toNullable;
154     }
155 
156     mixin AcceptVisitor;
157 }
158 
159 
160 class InlineIfNode : Node
161 {
162     Nullable!Node expr, cond, other;
163 
164     this(Position pos, Node expr, Node cond, Node other)
165     {
166         this.pos = pos;
167         this.expr = expr.toNullable;
168         this.cond = cond.toNullable;
169         this.other = other.toNullable;
170     }
171 
172     mixin AcceptVisitor;
173 }
174 
175 
176 class BinOpNode : Node
177 {
178     string op;
179     Node lhs, rhs;
180 
181     this(Position pos, string op, Node lhs, Node rhs)
182     {
183         this.pos = pos;
184         this.op = op;
185         this.lhs = lhs;
186         this.rhs = rhs;
187     }
188 
189     mixin AcceptVisitor;
190 }
191 
192 
193 class UnaryOpNode : Node
194 {
195     string op;
196     Node expr;
197 
198     this(Position pos, string op, Node expr)
199     {
200         this.pos = pos;
201         this.op = op;
202         this.expr = expr;
203     }
204 
205     mixin AcceptVisitor;
206 }
207 
208 
209 class NumNode : Node
210 {
211     enum Type
212     {
213         Integer,
214         Float,
215     }
216 
217     union Data
218     {
219         long _integer;
220         double _float;
221     }
222 
223     Data data;
224     Type type;
225 
226     this(Position pos, long num)
227     {
228         this.pos = pos;
229         data._integer = num;
230         type = Type.Integer;
231     }
232 
233     this(Position pos, double num)
234     {
235         this.pos = pos;
236         data._float = num;
237         type = Type.Float;
238     }
239 
240     mixin AcceptVisitor;
241 }
242 
243 
244 class BooleanNode : Node
245 {
246     bool boolean;
247 
248     this(Position pos, bool boolean)
249     {
250         this.pos = pos;
251         this.boolean = boolean;
252     }
253 
254     mixin AcceptVisitor;
255 }
256 
257 
258 class NilNode : Node
259 {
260     mixin AcceptVisitor;
261 }
262 
263 
264 class IdentNode : Node
265 {
266     string name;
267     Node[] subIdents;
268 
269 
270     this(Position pos, string name, Node[] subIdents)
271     {
272         this.pos = pos;
273         this.name = name;
274         this.subIdents = subIdents;
275     }
276 
277     mixin AcceptVisitor;
278 }
279 
280 
281 class AssignableNode : Node
282 {
283     string name;
284     Node[] subIdents;
285 
286 
287     this(Position pos, string name, Node[] subIdents)
288     {
289         this.pos = pos;
290         this.name = name;
291         this.subIdents = subIdents;
292     }
293 
294     mixin AcceptVisitor;
295 }
296 
297 
298 class IfNode : Node
299 {
300     Node cond, then, other;
301 
302     this(Position pos, Node cond, Node then, Node other)
303     {
304         this.pos = pos;
305         this.cond = cond;
306         this.then = then;
307         this.other = other;
308     }
309 
310     mixin AcceptVisitor;
311 }
312 
313 
314 class ForNode : Node
315 {
316     string[] keys;
317     Nullable!Node iterable;
318     Nullable!Node block;
319     Nullable!Node other;
320     Nullable!Node cond;
321     bool isRecursive;
322 
323     this(Position pos, string[] keys, Node iterable, Node block, Node other, Node cond, bool isRecursive)
324     {
325         this.pos = pos;
326         this.keys = keys;
327         this.iterable = iterable.toNullable;
328         this.block = block.toNullable;
329         this.other = other.toNullable;
330         this.cond = cond.toNullable;
331         this.isRecursive = isRecursive;
332     }
333 
334     mixin AcceptVisitor;
335 }
336 
337 
338 class StringNode : Node
339 {
340     string str;
341 
342     this(Position pos, string str)
343     {
344         this.pos = pos;
345         this.str = str;
346     }
347 
348     mixin AcceptVisitor;
349 }
350 
351 
352 class ListNode : Node
353 {
354     Node[] list;
355 
356     this(Position pos, Node[] list)
357     {
358         this.pos = pos;
359         this.list = list;
360     }
361 
362     mixin AcceptVisitor;
363 }
364 
365 
366 class DictNode : Node
367 {
368     Node[string] dict;
369 
370     this(Position pos, Node[string] dict)
371     {
372         this.pos = pos;
373         this.dict = dict;
374     }
375 
376     mixin AcceptVisitor;
377 }
378 
379 
380 class SetNode : Node
381 {
382     Node[] assigns;
383     Node expr;
384 
385     this(Position pos, Node[] assigns, Node expr)
386     {
387         this.pos = pos;
388         this.assigns = assigns;
389         this.expr = expr;
390     }
391 
392     mixin AcceptVisitor;
393 }
394 
395 
396 class MacroNode : Node
397 {
398     string name;
399     Arg[] args;
400     Nullable!Node block;
401     bool isReturn;
402 
403     this(Position pos, string name, Arg[] args, Node block, bool isReturn)
404     {
405         this.pos = pos;
406         this.name = name;
407         this.args = args;
408         this.block = block.toNullable;
409         this.isReturn = isReturn;
410     }
411 
412     mixin AcceptVisitor;
413 }
414 
415 
416 class CallNode : Node
417 {
418     string macroName;
419     Arg[] formArgs;
420     Nullable!Node factArgs;
421     Nullable!Node block;
422 
423     this(Position pos, string macroName, Arg[] formArgs, Node factArgs, Node block)
424     {
425         this.pos = pos;
426         this.macroName = macroName;
427         this.formArgs = formArgs;
428         this.factArgs = factArgs.toNullable;
429         this.block = block.toNullable;
430     }
431 
432     mixin AcceptVisitor;
433 }
434 
435 
436 class FilterBlockNode : Node
437 {
438     string filterName;
439     Nullable!Node args;
440     Nullable!Node block;
441 
442     this(Position pos, string filterName, Node args, Node block)
443     {
444         this.pos = pos;
445         this.filterName = filterName;
446         this.args = args.toNullable;
447         this.block = block.toNullable;
448     }
449 
450     mixin AcceptVisitor;
451 }
452 
453 
454 
455 class ImportNode : Node
456 {
457     struct Rename
458     {
459         string was, become;
460     }
461 
462     string fileName;
463     Rename[] macrosNames;
464     Nullable!TemplateNode tmplBlock;
465     bool withContext;
466 
467     this(Position pos, string fileName, Rename[] macrosNames, TemplateNode tmplBlock, bool withContext)
468     {
469         this.pos = pos;
470         this.fileName = fileName;
471         this.macrosNames = macrosNames;
472         this.tmplBlock = tmplBlock.toNullable;
473         this.withContext = withContext;
474     }
475 
476     mixin AcceptVisitor;
477 }
478 
479 
480 
481 class IncludeNode : Node
482 {
483     string fileName;
484     Nullable!TemplateNode tmplBlock;
485     bool withContext;
486 
487     this(Position pos, string fileName, TemplateNode tmplBlock, bool withContext)
488     {
489         this.pos = pos;
490         this.fileName = fileName;
491         this.tmplBlock = tmplBlock.toNullable;
492         this.withContext = withContext;
493     }
494 
495     mixin AcceptVisitor;
496 }
497 
498 
499 
500 class ExtendsNode : Node
501 {
502     string fileName;
503     Nullable!TemplateNode tmplBlock;
504 
505     this(Position pos, string fileName, TemplateNode tmplBlock)
506     {
507         this.pos = pos;
508         this.fileName = fileName;
509         this.tmplBlock = tmplBlock.toNullable;
510     }
511 
512     mixin AcceptVisitor;
513 }
514 
515 
516 
517 struct Arg
518 {
519     string name;
520     Nullable!Node defaultExpr;
521 
522     this(string name, Node def)
523     {
524         this.name = name;
525         this.defaultExpr = def.toNullable;
526     }
527 }
528 
529 
530 
531 auto toNullable(T)(T val)
532     if (is(T == class))
533 {
534     if (val is null)
535         return Nullable!T.init;
536     else
537         return Nullable!T(val);
538 }