1 /*
2  * Hunt - A high-level D Programming Language Web framework that encourages rapid development and clean, pragmatic design.
3  *
4  * Copyright (C) 2015-2019, HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.framework.view.Render;
13 
14 private
15 {
16     import std.range;
17     import std.format: fmt = format;
18 
19     import hunt.framework.view.ast.Node;
20     import hunt.framework.view.ast.Visitor;
21     import hunt.framework.view.algo;
22     import hunt.framework.view.algo.Wrapper;
23     import hunt.framework.view.Lexer;
24     import hunt.framework.view.Parser;
25     import hunt.framework.view.Exception : TemplateRenderException,
26                               assertTemplate = assertTemplateRender;
27 
28     import hunt.framework.view.Uninode;
29     import hunt.framework.http.Request;
30 
31     import hunt.framework.Simplify;
32     import hunt.framework.view.Util;
33     import hunt.logging;
34 }
35 
36 
37 
38 
39 struct FormArg
40 {
41     string name;
42     Nullable!UniNode def;
43 
44     this (string name)
45     {
46         this.name = name;
47         this.def = Nullable!UniNode.init;
48     }
49 
50     this (string name, UniNode def)
51     {
52         this.name = name;
53         this.def = Nullable!UniNode(def);
54     }
55 }
56 
57 
58 struct Macro
59 {
60     FormArg[] args;
61     Nullable!Context context;
62     Nullable!Node block;
63 
64     this(FormArg[] args, Context context, Node block)
65     {
66         this.args = args;
67         this.context = context.toNullable;
68         this.block = block.toNullable;
69     }
70 }
71 
72 
73 class Context
74 {
75     private Context prev;
76 
77     UniNode data;
78     Function[string] functions;
79     Macro[string] macros;
80 
81     this ()
82     {
83         prev = null;
84         data = UniNode.emptyObject();
85     }
86 
87     this (Context ctx, UniNode data)
88     {
89         prev = ctx;
90         this.data = data;
91     }
92 
93     Context previos() @property
94     {
95         if (prev !is null)
96             return prev;
97         return this;
98     }
99 
100     bool has(string name)
101     {
102         if (name in data)
103             return true;
104         if (prev is null)
105             return false;
106         return prev.has(name);
107     }
108 
109     UniNode get(string name)
110     {
111         if (name in data)
112             return data[name];
113         if (prev is null)
114             return UniNode(null);
115         return prev.get(name);
116     }
117 
118     UniNode* getPtr(string name)
119     {
120         if (name in data)
121             return &(data[name]);
122         if (prev is null)
123             assertTemplate(0, "Non declared var `%s`".fmt(name));
124         return prev.getPtr(name);
125     }
126 
127     T get(T)(string name)
128     {
129         return this.get(name).get!T;
130     }
131 
132     bool hasFunc(string name)
133     {
134         if (name in functions)
135             return true;
136         if (prev is null)
137             return false;
138         return prev.hasFunc(name);
139     }
140 
141 
142     Function getFunc(string name)
143     {
144         if (name in functions)
145             return functions[name];
146         if (prev is null)
147             assertTemplate(0, "Non declared function `%s`".fmt(name));
148         return prev.getFunc(name);
149     }
150 
151 
152     bool hasMacro(string name)
153     {
154         if (name in macros)
155             return true;
156         if (prev is null)
157             return false;
158         return prev.hasMacro(name);
159     }
160 
161 
162     Macro getMacro(string name)
163     {
164         if (name in macros)
165             return macros[name];
166         if (prev is null)
167             assertTemplate(0, "Non declared macro `%s`".fmt(name));
168         return prev.getMacro(name);
169     }
170 }
171 
172 
173 struct AppliedFilter
174 {
175     string name;
176     UniNode args;
177 }
178 
179 
180 class Render : VisitorInterface
181 {
182     private
183     {
184         TemplateNode    _root;
185         Context         _globalContext;
186         Context         _rootContext;
187         UniNode[]       _dataStack;
188         AppliedFilter[] _appliedFilters;
189         TemplateNode[]  _extends;
190 
191         Context         _context;
192         Request _request;
193 
194         string          _renderedResult;
195         bool            _isExtended;
196 
197         string _routeGroup = DEFAULT_ROUTE_GROUP;
198         string _locale = "en-us";
199     }
200 
201     this(TemplateNode root)
202     {
203         _root = root;
204         _rootContext = new Context();
205 
206         foreach(key, value; globalFunctions) {
207             _rootContext.functions[key] = cast(Function)value;
208         }
209 
210         foreach(key, value; globalFilters) {
211             _rootContext.functions[key] = cast(Function)value;
212         }
213 
214         foreach(key, value; globalTests) {
215             _rootContext.functions[key] = cast(Function)value;
216         }
217 
218         _rootContext.functions["input"] = &input;
219     }
220 
221     Request request() {
222         return _request;
223     }
224 
225     void request(Request value) {
226         _request = value;
227     }
228 
229     UniNode input(UniNode node) {
230         version(HUNT_VIEW_DEBUG) {
231             tracef("node: %s,  kind: %s", node.toString(), node.kind());
232         }
233 
234         if(_request is null) {
235             warningf("The reques does NOT set for node: %s", node.toString());
236             return UniNode("");
237         }
238 
239         UniNode varargs = node["varargs"];
240         if(varargs.length == 0) {
241             UniNode[string] data;
242 
243             foreach(string key, string value; _request.input()) {
244                 data[key] = UniNode(value);
245             }
246             return UniNode(data);
247         } else  {
248             UniNode[] argNodes = varargs.get!(UniNode[])();
249             UniNode keyNode = argNodes[0];
250             UniNode defaultValueNode = UniNode("");
251 
252             if(varargs.length >= 2) {
253                 defaultValueNode = argNodes[1];
254             }
255             
256             if(keyNode.kind == UniNode.Kind.text) {
257                 string key = keyNode.get!string();
258                 string[string] allInputs = _request.input();
259                 auto itemPtr = key in allInputs;
260                 if(itemPtr is null) {
261                     warning(fmt("No value found for %s", key));
262                     return defaultValueNode;
263                 } else {
264                     return UniNode(*itemPtr);
265                 }
266             } else {
267                 // return UniNode("Only string can be accepted.");
268                 warning("Only string can be accepted.");
269                 return defaultValueNode;
270             }
271         }
272     }
273 
274     void setRouteGroup(string rg)
275     {
276         _routeGroup = rg;
277     }
278 
279     void setLocale(string locale)
280     {
281         _locale = locale;
282     }
283 
284     string render(UniNode data)
285     {
286         import hunt.logging;
287         version(HUNT_VIEW_DEBUG) logDebug("----render data : ", data);
288         _context = new Context(_rootContext, data);
289         _globalContext = _context;
290 
291         _extends = [_root];
292         _isExtended = false;
293 
294         _renderedResult = "";
295         if (_root !is null)
296             tryAccept(_root);
297         return _renderedResult;
298     }
299 
300 
301     override void visit(TemplateNode node)
302     {
303         tryAccept(node.stmt.get);
304     }
305 
306     override void visit(BlockNode node)
307     {
308         void super_()
309         {
310             tryAccept(node.stmt.get);
311         }
312 
313         foreach (tmpl; _extends[0 .. $-1])
314             if (node.name in tmpl.blocks)
315             {
316                 pushNewContext();
317                 _context.functions["super"] = wrapper!super_;
318                 tryAccept(tmpl.blocks[node.name].stmt.get);
319                 popContext();
320                 return;
321             }
322 
323         super_();
324     }
325 
326 
327     override void visit(StmtBlockNode node)
328     {
329         pushNewContext();
330         foreach(ch; node.children)
331             tryAccept(ch);
332         popContext();
333     }
334 
335     override void visit(RawNode node)
336     {
337         writeToResult(node.raw);
338     }
339 
340     override void visit(ExprNode node)
341     {
342         tryAccept(node.expr.get);
343         auto n = pop();
344         n.toStringType;
345         writeToResult(n.get!string);
346     }
347 
348     override void visit(InlineIfNode node)
349     {
350         bool condition = true;
351 
352         if (!node.cond.isNull)
353         {
354             tryAccept(node.cond.get);
355             auto res = pop();
356             res.toBoolType;
357             condition = res.get!bool;
358         }
359 
360         if (condition)
361         {
362             tryAccept(node.expr.get);
363         }
364         else if (!node.other.isNull)
365         {
366             tryAccept(node.other.get);
367         }
368         else
369         {
370             push(UniNode(null));
371         }
372     }
373 
374     override void visit(BinOpNode node)
375     {
376         UniNode calc(Operator op)()
377         {
378             tryAccept(node.lhs);
379             UniNode lhs = pop();
380 
381             tryAccept(node.rhs);
382             auto rhs = pop();
383 
384             return binary!op(lhs, rhs);
385         }
386 
387         UniNode calcLogic(bool stopCondition)()
388         {
389             tryAccept(node.lhs);
390             auto lhs = pop();
391             lhs.toBoolType;
392             if (lhs.get!bool == stopCondition)
393                 return UniNode(stopCondition);
394 
395             tryAccept(node.rhs);
396             auto rhs = pop();
397             rhs.toBoolType;
398             return UniNode(rhs.get!bool);
399         }
400 
401         UniNode calcCall(string type)()
402         {
403             tryAccept(node.lhs);
404             auto lhs = pop();
405 
406             tryAccept(node.rhs);
407             auto args = pop();
408             auto name = args["name"].get!string;
409             args["varargs"] = UniNode([lhs] ~ args["varargs"].get!(UniNode[]));
410 
411             if (_context.hasFunc(name))
412                 return visitFunc(name, args);
413             else if (_context.hasMacro(name))
414                 return visitMacro(name, args);
415             else
416                 assertTemplate(0, "Undefined " ~ type ~ " %s".fmt(name), node.pos);
417             assert(0);
418         }
419 
420         UniNode calcFilter()
421         {
422             return calcCall!"filter";
423         }
424 
425         UniNode calcIs()
426         {
427             auto res = calcCall!"test";
428             res.toBoolType;
429             return res;
430         }
431 
432         UniNode doSwitch()
433         {
434             switch (node.op) with (Operator)
435             {
436                 case Concat:    return calc!Concat;
437                 case Plus:      return calc!Plus;
438                 case Minus:     return calc!Minus;
439                 case DivInt:    return calc!DivInt;
440                 case DivFloat:  return calc!DivFloat;
441                 case Rem:       return calc!Rem;
442                 case Mul:       return calc!Mul;
443                 case Greater:   return calc!Greater;
444                 case Less:      return calc!Less;
445                 case GreaterEq: return calc!GreaterEq;
446                 case LessEq:    return calc!LessEq;
447                 case Eq:        return calc!Eq;
448                 case NotEq:     return calc!NotEq;
449                 case Pow:       return calc!Pow;
450                 case In:        return calc!In;
451 
452                 case Or:        return calcLogic!true;
453                 case And:       return calcLogic!false;
454 
455                 case Filter:    return calcFilter;
456                 case Is:        return calcIs;
457 
458                 default:
459                     assert(0, "Not implemented binary operator");
460             }
461         }
462 
463         push(doSwitch());
464     }
465 
466     override void visit(UnaryOpNode node)
467     {
468         tryAccept(node.expr);
469         auto res = pop();
470         UniNode doSwitch()
471         {
472             switch (node.op) with (Operator)
473             {
474                 case Plus:      return unary!Plus(res);
475                 case Minus:     return unary!Minus(res);
476                 case Not:       return unary!Not(res);
477                 default:
478                     assert(0, "Not implemented unary operator");
479             }
480         }
481 
482         push(doSwitch());
483     }
484 
485     override void visit(NumNode node)
486     {
487         if (node.type == NumNode.Type.Integer)
488             push(UniNode(node.data._integer));
489         else
490             push(UniNode(node.data._float));
491     }
492 
493     override void visit(BooleanNode node)
494     {
495         push(UniNode(node.boolean));
496     }
497 
498     override void visit(NilNode node)
499     {
500         push(UniNode(null));
501     }
502 
503     override void visit(IdentNode node)
504     {
505         UniNode curr;
506         if (node.name.length)
507             curr = _context.get(node.name);
508         else
509             curr = UniNode(null);
510 
511         auto lastPos = node.pos;
512         foreach (sub; node.subIdents)
513         {
514             tryAccept(sub);
515             auto key = pop();
516 
517             switch (key.kind) with (UniNode.Kind)
518             {
519                 // Index of list/tuple
520                 case integer:
521                 case uinteger:
522                     curr.checkNodeType(array, lastPos);
523                     if (key.get!size_t < curr.length)
524                         curr = curr[key.get!size_t];
525                     else
526                         assertTemplate(0, "Range violation  on %s...[%d]".fmt(node.name, key.get!size_t), sub.pos);
527                     break;
528 
529                 // Key of dict
530                 case text:
531                     auto keyStr = key.get!string;
532                     if (curr.kind == UniNode.Kind.object && keyStr in curr)
533                         curr = curr[keyStr];
534                     else if (_context.hasFunc(keyStr))
535                     {
536                         auto args = [
537                             "name": UniNode(keyStr),
538                             "varargs": UniNode([curr]),
539                             "kwargs": UniNode.emptyObject
540                         ];
541                         curr = visitFunc(keyStr, UniNode(args));
542                     }
543                     else if (_context.hasMacro(keyStr))
544                     {
545                         auto args = [
546                             "name": UniNode(keyStr),
547                             "varargs": UniNode([curr]),
548                             "kwargs": UniNode.emptyObject
549                         ];
550                         curr = visitMacro(keyStr, UniNode(args));
551                     }
552                     else
553                     {
554                         curr.checkNodeType(object, lastPos);
555                         assertTemplate(0, "Unknown attribute %s".fmt(key.get!string), sub.pos);
556                     }
557                     break;
558 
559                 // Call of function
560                 case object:
561                     auto name = key["name"].get!string;
562 
563                     if (!curr.isNull)
564                         key["varargs"] = UniNode([curr] ~ key["varargs"].get!(UniNode[]));
565 
566                     if (_context.hasFunc(name))
567                     {
568                         curr = visitFunc(name, key);
569                     }
570                     else if (_context.hasMacro(name))
571                     {
572                         curr = visitMacro(name, key);
573                     }
574                     else
575                         assertTemplate(0, "Not found any macro, function or filter `%s`".fmt(name), sub.pos);
576                     break;
577 
578                 default:
579                     assertTemplate(0, "Unknown attribute %s for %s".fmt(key.toString, node.name), sub.pos);
580             }
581 
582             lastPos = sub.pos;
583         }
584 
585         push(curr);
586     }
587 
588     override void visit(AssignableNode node)
589     {
590         auto expr = pop();
591 
592         // TODO: check flag of set scope
593         if (!_context.has(node.name))
594         {
595             if (node.subIdents.length)
596                 assertTemplate(0, "Unknow variable %s".fmt(node.name), node.pos);
597             _context.data[node.name] = expr;
598             return;
599         }
600 
601         UniNode* curr = _context.getPtr(node.name);
602 
603         if (!node.subIdents.length)
604         {
605             (*curr) = expr;
606             return;
607         }
608 
609         auto lastPos = node.pos;
610         for(int i = 0; i < cast(int)(node.subIdents.length) - 1; i++)
611         {
612             tryAccept(node.subIdents[i]);
613             auto key = pop();
614 
615             switch (key.kind) with (UniNode.Kind)
616             {
617                 // Index of list/tuple
618                 case integer:
619                 case uinteger:
620                     checkNodeType(*curr, array, lastPos);
621                     if (key.get!size_t < curr.length)
622                         curr = &((*curr)[key.get!size_t]);
623                     else
624                         assertTemplate(0, "Range violation  on %s...[%d]".fmt(node.name, key.get!size_t), node.subIdents[i].pos);
625                     break;
626 
627                 // Key of dict
628                 case text:
629                     checkNodeType(*curr, object, lastPos);
630                     if (key.get!string in *curr)
631                         curr = &((*curr)[key.get!string]);
632                     else
633                         assertTemplate(0, "Unknown attribute %s".fmt(key.get!string), node.subIdents[i].pos);
634                     break;
635 
636                 default:
637                     assertTemplate(0, "Unknown attribute %s for %s".fmt(key.toString, node.name), node.subIdents[i].pos);
638             }
639             lastPos = node.subIdents[i].pos;
640         }
641 
642         if (node.subIdents.length)
643         {
644             tryAccept(node.subIdents[$-1]);
645             auto key = pop();
646 
647             switch (key.kind) with (UniNode.Kind)
648             {
649                 // Index of list/tuple
650                 case integer:
651                 case uinteger:
652                     checkNodeType(*curr, array, lastPos);
653                     if (key.get!size_t < curr.length)
654                         (*curr).opIndex(key.get!size_t) = expr; // ¯\_(ツ)_/¯
655                     else
656                         assertTemplate(0, "Range violation  on %s...[%d]".fmt(node.name, key.get!size_t), node.subIdents[$-1].pos);
657                     break;
658 
659                 // Key of dict
660                 case text:
661                     checkNodeType(*curr, object, lastPos);
662                     (*curr)[key.get!string] = expr;
663                     break;
664 
665                 default:
666                     assertTemplate(0, "Unknown attribute %s for %s".fmt(key.toString, node.name, node.subIdents[$-1].pos));
667             }
668         }
669     }
670 
671     override void visit(StringNode node)
672     {
673         push(UniNode(node.str));
674     }
675 
676     override void visit(ListNode node)
677     {
678         UniNode[] list = [];
679         foreach (l; node.list)
680         {
681             tryAccept(l);
682             list ~= pop();
683         }
684         push(UniNode(list));
685     }
686 
687     override void visit(DictNode node)
688     {
689         UniNode[string] dict;
690         foreach (key, value; node.dict)
691         {
692             tryAccept(value);
693             dict[key] = pop();
694         }
695         push(UniNode(dict));
696     }
697 
698     override void visit(IfNode node)
699     {
700         tryAccept(node.cond);
701 
702         auto cond = pop();
703         cond.toBoolType;
704 
705         if (cond.get!bool)
706         {
707             tryAccept(node.then);
708         }
709         else if (node.other)
710         {
711             tryAccept(node.other);
712         }
713     }
714 
715     override void visit(ForNode node)
716     {
717         bool iterated = false;
718         int depth = 0;
719         bool calcCondition()
720         {
721             bool condition = true;
722             if (!node.cond.isNull)
723             {
724                 tryAccept(node.cond.get);
725                 auto cond = pop();
726                 cond.toBoolType;
727                 condition = cond.get!bool;
728             }
729             return condition;
730         }
731 
732         UniNode cycle(UniNode loop, UniNode varargs)
733         {
734             if (!varargs.length)
735                 return UniNode(null);
736             return varargs[loop["index0"].get!size_t % varargs.length];
737         }
738 
739 
740         void loop(UniNode iterable)
741         {
742             Nullable!UniNode lastVal;
743             bool changed(UniNode loop, UniNode val)
744             {
745                 if (!lastVal.isNull && val == lastVal.get)
746                     return false;
747                 lastVal = val;
748                 return true;
749             }
750 
751             depth++;
752             pushNewContext();
753 
754             iterable.toIterableNode;
755 
756             if (!node.cond.isNull)
757             {
758                 auto newIterable = UniNode.emptyArray;
759                 for (int i = 0; i < iterable.length; i++)
760                 {
761                     if (node.keys.length == 1)
762                         _context.data[node.keys[0]] = iterable[i];
763                     else
764                     {
765                         iterable[i].checkNodeType(UniNode.Kind.array, node.iterable.get.pos);
766                         assertTemplate(iterable[i].length >= node.keys.length, "Num of keys less then values", node.iterable.get.pos);
767                         foreach(j, key; node.keys)
768                             _context.data[key] = iterable[i][j];
769                     }
770 
771                     if (calcCondition())
772                         newIterable ~= iterable[i];
773                 }
774                 iterable = newIterable;
775             }
776 
777             _context.data["loop"] = UniNode.emptyObject;
778             _context.data["loop"]["length"] = UniNode(iterable.length);
779             _context.data["loop"]["depth"] = UniNode(depth);
780             _context.data["loop"]["depth0"] = UniNode(depth - 1);
781             _context.functions["cycle"] = wrapper!cycle;
782             _context.functions["changed"] = wrapper!changed;
783 
784             for (int i = 0; i < iterable.length; i++)
785             {
786                 _context.data["loop"]["index"] = UniNode(i + 1);
787                 _context.data["loop"]["index0"] = UniNode(i);
788                 _context.data["loop"]["revindex"] = UniNode(iterable.length - i);
789                 _context.data["loop"]["revindex0"] = UniNode(iterable.length - i - 1);
790                 _context.data["loop"]["first"] = UniNode(i == 0);
791                 _context.data["loop"]["last"] = UniNode(i == iterable.length - 1);
792                 _context.data["loop"]["previtem"] = i > 0 ? iterable[i - 1] : UniNode(null);
793                 _context.data["loop"]["nextitem"] = i < iterable.length - 1 ? iterable[i + 1] : UniNode(null);
794 
795                 if (node.isRecursive)
796                     _context.functions["loop"] = wrapper!loop;
797 
798                 if (node.keys.length == 1)
799                     _context.data[node.keys[0]] = iterable[i];
800                 else
801                 {
802                     iterable[i].checkNodeType(UniNode.Kind.array, node.iterable.get.pos);
803                     assertTemplate(iterable[i].length >= node.keys.length, "Num of keys less then values", node.iterable.get.pos);
804                     foreach(j, key; node.keys)
805                         _context.data[key] = iterable[i][j];
806                 }
807 
808                 tryAccept(node.block.get);
809                 iterated = true;
810             }
811             popContext();
812             depth--;
813         }
814 
815 
816 
817         tryAccept(node.iterable.get);
818         UniNode iterable = pop();
819         loop(iterable);
820 
821         if (!iterated && !node.other.isNull)
822             tryAccept(node.other.get);
823     }
824 
825 
826     override void visit(SetNode node)
827     {
828         tryAccept(node.expr);
829 
830         if (node.assigns.length == 1)
831             tryAccept(node.assigns[0]);
832         else
833         {
834             auto expr = pop();
835             expr.checkNodeType(UniNode.Kind.array, node.expr.pos);
836 
837             if (expr.length < node.assigns.length)
838                 assertTemplate(0, "Iterable length less then number of assigns", node.expr.pos);
839 
840             foreach(idx, assign; node.assigns)
841             {
842                 push(expr[idx]);
843                 tryAccept(assign);
844             }
845         }
846     }
847 
848 
849     override void visit(MacroNode node)
850     {
851         FormArg[] args;
852 
853         foreach(arg; node.args)
854         {
855             if (arg.defaultExpr.isNull)
856                 args ~= FormArg(arg.name);
857             else
858             {
859                 tryAccept(arg.defaultExpr.get);
860                 args ~= FormArg(arg.name, pop());
861             }
862         }
863 
864         _context.macros[node.name] = Macro(args, _context, node.block.get);
865     }
866 
867 
868     override void visit(CallNode node)
869     {
870         FormArg[] args;
871 
872         foreach(arg; node.formArgs)
873         {
874             if (arg.defaultExpr.isNull)
875                 args ~= FormArg(arg.name);
876             else
877             {
878                 tryAccept(arg.defaultExpr.get);
879                 args ~= FormArg(arg.name, pop());
880             }
881         }
882 
883         auto caller = Macro(args, _context, node.block.get);
884 
885         tryAccept(node.factArgs.get);
886         auto factArgs = pop();
887 
888         visitMacro(node.macroName, factArgs, caller.nullable);
889     }
890 
891 
892     override void visit(FilterBlockNode node)
893     {
894         tryAccept(node.args.get);
895         auto args = pop();
896 
897         pushFilter(node.filterName, args);
898         tryAccept(node.block.get);
899         popFilter();
900     }
901 
902 
903     override void visit(ImportNode node)
904     {
905         if (node.tmplBlock.isNull)
906             return;
907 
908         auto stashedContext = _context;
909         auto stashedResult = _renderedResult;
910 
911         if (!node.withContext)
912             _context = _globalContext;
913 
914         _renderedResult = "";
915 
916         pushNewContext();
917 
918         foreach (child; node.tmplBlock.get.stmt.get.children)
919             tryAccept(child);
920 
921         auto macros = _context.macros;
922 
923         popContext();
924 
925         _renderedResult = stashedResult;
926 
927         if (!node.withContext)
928             _context = stashedContext;
929 
930         if (node.macrosNames.length)
931             foreach (name; node.macrosNames)
932             {
933                 assertTemplate(cast(bool)(name.was in macros), "Undefined macro `%s` in `%s`".fmt(name.was, node.fileName), node.pos);
934                 _context.macros[name.become] = macros[name.was];
935             }
936         else
937             foreach (key, val; macros)
938                 _context.macros[key] = val;
939     }
940 
941 
942     override void visit(IncludeNode node)
943     {
944         if (node.tmplBlock.isNull)
945             return;
946 
947         auto stashedContext = _context;
948 
949         if (!node.withContext)
950             _context = _globalContext;
951 
952         tryAccept(node.tmplBlock.get);
953 
954         if (!node.withContext)
955             _context = stashedContext;
956     }
957 
958 
959     override void visit(ExtendsNode node)
960     {
961         Nullable!TemplateNode templateNode = node.tmplBlock;
962         if(!templateNode.isNull()) {
963             _extends ~= templateNode.get();
964             tryAccept(templateNode.get());
965         }
966         _extends.popBack;
967         _isExtended = true;
968     }
969 
970     private void tryAccept(Node node)
971     {
972         if (!_isExtended)
973             node.accept(this);
974     }
975 
976 
977     private UniNode visitFunc(string name, UniNode args)
978     {
979         version(HUNT_VIEW_DEBUG) info("---Func: ", name,", args: ",args);
980 
981         if(name == "trans")
982         {
983             if("varargs" in args)
984             {
985                 return doTrans(args["varargs"]);
986             }
987         }
988         else if(name == "date")
989         {
990             import hunt.util.DateTime;
991             auto format = args["varargs"][0].get!string;
992             auto timestamp = args["varargs"][1].get!int;
993             return UniNode(date(format, timestamp));
994         }
995         else if(name == "url")
996         {
997             import hunt.framework.Simplify : url;
998 
999             auto mca = args["varargs"][0].get!string;
1000             auto params = args["varargs"][1].get!string;
1001             return UniNode(url(mca, Util.parseFormData(params), _routeGroup));
1002         }
1003         return _context.getFunc(name)(args);
1004     }
1005 
1006     private UniNode doTrans(UniNode arg)
1007     {
1008         import hunt.framework.i18n;
1009         import hunt.framework.util.uninode.Serialization;
1010 
1011         if(arg.kind == UniNode.Kind.array)
1012         {
1013             if(arg.length == 1)
1014             {
1015                 return UniNode(transWithLocale(_locale,arg[0].get!string));
1016             }
1017             else if(arg.length > 1)
1018             {
1019                 string msg = arg[0].get!string;
1020                 UniNode[] args;
1021                 for(int i=1; i < arg.length ; i++)
1022                 {
1023                      args ~= arg[i];
1024                 }
1025                 
1026                 return UniNode(transWithLocale(_locale,msg,uniNodeToJSON(UniNode(args))));
1027             }
1028         }
1029         throw new TemplateRenderException("unsupport param : " ~ arg.toString);
1030     }
1031 
1032 
1033     private UniNode visitMacro(string name, UniNode args, Nullable!Macro caller = Nullable!Macro.init)
1034     {
1035         UniNode result;
1036 
1037         auto macro_ = _context.getMacro(name);
1038         auto stashedContext = _context;
1039         _context = macro_.context.get;
1040         pushNewContext();
1041 
1042         UniNode[] varargs;
1043         UniNode[string] kwargs;
1044 
1045         foreach(arg; macro_.args)
1046             if (!arg.def.isNull)
1047                 _context.data[arg.name] = arg.def.get;
1048 
1049         for(int i = 0; i < args["varargs"].length; i++)
1050         {
1051             if (i < macro_.args.length)
1052                 _context.data[macro_.args[i].name] = args["varargs"][i];
1053             else
1054                 varargs ~= args["varargs"][i];
1055         }
1056 
1057         foreach (string key, value; args["kwargs"])
1058         {
1059             if (macro_.args.has(key))
1060                 _context.data[key] = value;
1061             else
1062                 kwargs[key] = value;
1063         }
1064 
1065         _context.data["varargs"] = UniNode(varargs);
1066         _context.data["kwargs"] = UniNode(kwargs);
1067 
1068         foreach(arg; macro_.args)
1069             if (arg.name !in _context.data)
1070                 assertTemplate(0, "Missing value for argument `%s` in macro `%s`".fmt(arg.name, name));
1071 
1072         if (!caller.isNull)
1073             _context.macros["caller"] = caller.get();
1074 
1075         tryAccept(macro_.block.get);
1076         result = pop();
1077 
1078         popContext();
1079         _context = stashedContext;
1080 
1081         return result;
1082     }
1083 
1084     private void writeToResult(string str)
1085     {
1086         if (!_appliedFilters.length)
1087         {
1088             _renderedResult ~= str;
1089         }
1090         else
1091         {
1092             UniNode curr = UniNode(str);
1093             foreach_reverse (filter; _appliedFilters)
1094             {
1095                 auto args = filter.args;
1096                 args["varargs"] = UniNode([curr] ~ args["varargs"].get!(UniNode[]));
1097 
1098                 if (_context.hasFunc(filter.name))
1099                     curr = visitFunc(filter.name, args);
1100                 else if (_context.hasMacro(filter.name))
1101                     curr = visitMacro(filter.name, args);
1102                 else
1103                     assert(0);
1104 
1105                 curr.toStringType;
1106             }
1107 
1108             _renderedResult ~= curr.get!string;
1109         }
1110     }
1111 
1112     private void pushNewContext()
1113     {
1114         _context = new Context(_context, UniNode.emptyObject);
1115     }
1116 
1117 
1118     private void popContext()
1119     {
1120         _context = _context.previos;
1121     }
1122 
1123 
1124     private void push(UniNode un)
1125     {
1126         _dataStack ~= un;
1127     }
1128 
1129 
1130     private UniNode pop()
1131     {
1132         if (!_dataStack.length)
1133             assertTemplate(0, "Unexpected empty stack");
1134 
1135         auto un = _dataStack.back;
1136         _dataStack.popBack;
1137         return un;
1138     }
1139 
1140 
1141     private void pushFilter(string name, UniNode args)
1142     {
1143         _appliedFilters ~= AppliedFilter(name, args);
1144     }
1145 
1146 
1147     private void popFilter()
1148     {
1149         if (!_appliedFilters.length)
1150             assertTemplate(0, "Unexpected empty filter stack");
1151 
1152         _appliedFilters.popBack;
1153     }
1154 }
1155 
1156 
1157 void registerFunction(alias func)(Render render, string name)
1158 {
1159     render._rootContext.functions[name] = wrapper!func;
1160 }
1161 
1162 
1163 void registerFunction(alias func)(Render render)
1164 {
1165     enum name = __traits(identifier, func);
1166     render._rootContext.functions[name] = wrapper!func;
1167 }
1168 
1169 
1170 private bool has(FormArg[] arr, string name)
1171 {
1172     foreach(a; arr) {
1173         if (a.name == name)
1174             return true;
1175     }
1176     return false;
1177 }