1 /**
2 This module implements the expression and statement node classes, which are used internally as a syntax tree.
3 The Parser generates the tree and the Compiler traverses it to emit bytecode into Chunks.
4 
5 ────────────────────────────────────────────────────────────────────────────────
6 
7 Copyright (C) 2021 pillager86.rf.gd
8 
9 This program is free software: you can redistribute it and/or modify it under 
10 the terms of the GNU General Public License as published by the Free Software 
11 Foundation, either version 3 of the License, or (at your option) any later 
12 version.
13 
14 This program is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
16 PARTICULAR PURPOSE.  See the GNU General Public License for more details.
17 
18 You should have received a copy of the GNU General Public License along with 
19 this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 module mildew.nodes;
22 
23 import std.format: format;
24 import std.variant;
25 
26 import mildew.environment: Environment;
27 import mildew.exceptions: ScriptRuntimeException;
28 import mildew.lexer: Token;
29 import mildew.types;
30 import mildew.visitors;
31 
32 package:
33 
34 /// handles class expression and declaration data
35 class ClassDefinition
36 {
37     this(string clsname, FunctionLiteralNode ctor,
38             string[] mnames, FunctionLiteralNode[] ms,
39             string[] gmnames, FunctionLiteralNode[] gms,
40             string[] smnames, FunctionLiteralNode[] sms,
41             string[] statNames, FunctionLiteralNode[] statms,
42             ExpressionNode base = null)
43     {
44         className = clsname;
45         constructor = ctor;
46         methodNames = mnames;
47         methods = ms;
48         assert(methodNames.length == methods.length);
49         getMethodNames = gmnames;
50         getMethods = gms;
51         assert(getMethodNames.length == getMethods.length);
52         setMethodNames = smnames;
53         setMethods = sms;
54         assert(setMethodNames.length == setMethods.length);
55         staticMethodNames = statNames;
56         staticMethods = statms;
57         assert(staticMethodNames.length == staticMethods.length);
58         baseClass = base;
59     }
60 
61     override string toString() const 
62     {
63         string output = "class " ~ className;
64         if(baseClass) output ~= " extends " ~ baseClass.toString();
65         output ~= " {...}";
66         return output;
67     }
68 
69     string className;
70     FunctionLiteralNode constructor;
71     string[] methodNames;
72     FunctionLiteralNode[] methods;
73     string[] getMethodNames;
74     FunctionLiteralNode[] getMethods;
75     string[] setMethodNames;
76     FunctionLiteralNode[] setMethods;
77 	string[] staticMethodNames;
78 	FunctionLiteralNode[] staticMethods;
79     ExpressionNode baseClass; // should be an expression that returns a constructor function
80 }
81 
82 /// root class of expression nodes
83 abstract class ExpressionNode
84 {
85 	abstract Variant accept(IExpressionVisitor visitor);
86 
87     // have to override here for subclasses' override to work
88     override string toString() const
89     {
90         assert(false, "This should never be called as it is virtual");
91     }
92 }
93 
94 class LiteralNode : ExpressionNode 
95 {
96     this(Token token, ScriptAny val)
97     {
98         literalToken = token;
99         value = val;
100     }
101 
102 	override Variant accept(IExpressionVisitor visitor)
103 	{
104 		return visitor.visitLiteralNode(this);
105 	}
106 
107     override string toString() const
108     {
109         if(value.type == ScriptAny.Type.STRING)
110             return "\"" ~ literalToken.text ~ "\"";
111         else
112             return literalToken.text;
113     }
114 
115     Token literalToken;
116     ScriptAny value;
117 }
118 
119 class FunctionLiteralNode : ExpressionNode
120 {
121     this(string[] args, StatementNode[] stmts, string optional = "", bool isC = false, bool isG=false)
122     {
123         argList = args;
124         statements = stmts;
125         optionalName = optional;
126         isClass = isC;
127         isGenerator = isG;
128     }
129 
130     override Variant accept(IExpressionVisitor visitor)
131     {
132         return visitor.visitFunctionLiteralNode(this);
133     }
134 
135     override string toString() const
136     {
137         string output = "function(";
138         for(size_t i = 0; i < argList.length; ++i)
139         {
140             output ~= argList[i];
141             if(i < argList.length - 1)
142                 output ~= ", ";
143         }
144         output ~= "){\n";
145         foreach(stmt ; statements)
146         {
147             output ~= "\t" ~ stmt.toString();
148         }
149         output ~= "\n}";
150         return output;
151     }
152 
153     string[] argList;
154     StatementNode[] statements;
155     string optionalName;
156     bool isClass;
157     bool isGenerator;
158 }
159 
160 class LambdaNode : ExpressionNode
161 {
162     this(Token arrow, string[] args, StatementNode[] stmts)
163     {
164         arrowToken = arrow;
165         argList = args;
166         statements = stmts;
167     }
168 
169     this(Token arrow, string[] args, ExpressionNode ret)
170     {
171         arrowToken = arrow;
172         argList = args;
173         returnExpression = ret;
174     }
175 
176     override string toString() const
177     {
178         auto result = "(";
179         for(size_t i = 0; i < argList.length; ++i)
180         {
181             result ~= argList[i];
182             if(i < argList.length - 1)
183                 result ~= ", ";
184         }
185         result ~= ") => ";
186         if(returnExpression)
187         {
188             result ~= returnExpression.toString();
189         }
190         else
191         {
192             result ~= "{";
193             foreach(stmt ; statements)
194                 result ~= stmt.toString() ~ " ";
195             result ~= "}";
196         }
197         return result;
198     }
199 
200     override Variant accept(IExpressionVisitor visitor)
201     {
202         return visitor.visitLambdaNode(this);
203     }
204 
205     Token arrowToken;
206     string[] argList;
207     StatementNode[] statements;
208     ExpressionNode returnExpression;
209 }
210 
211 class TemplateStringNode : ExpressionNode
212 {
213     this(ExpressionNode[] ns)
214     {
215         nodes = ns;
216     }
217 
218     override Variant accept(IExpressionVisitor visitor)
219     {
220         return visitor.visitTemplateStringNode(this);
221     }
222 
223     override string toString() const
224     {
225         import std.format: format;
226         string output = "`";
227         foreach(node ; nodes)
228         {
229             if(auto lit = cast(LiteralNode)node)
230                 output ~= lit.literalToken.text;
231             else // any other expression
232                 output ~= format("${%s}", node.toString());
233         }
234         output ~= "`";
235         return output;
236     }
237 
238     ExpressionNode[] nodes;
239 }
240 
241 class ArrayLiteralNode : ExpressionNode 
242 {
243     this(ExpressionNode[] values)
244     {
245         valueNodes = values;
246     }
247 
248 	override Variant accept(IExpressionVisitor visitor)
249 	{
250 		return visitor.visitArrayLiteralNode(this);
251 	}
252 
253     override string toString() const
254     {
255         return format("%s", valueNodes);
256     }
257 
258     ExpressionNode[] valueNodes;
259 }
260 
261 class ObjectLiteralNode : ExpressionNode 
262 {
263     this(string[] ks, ExpressionNode[] vs)
264     {
265         keys = ks;
266         valueNodes = vs;
267     }
268 
269 	override Variant accept(IExpressionVisitor visitor)
270 	{
271 		return visitor.visitObjectLiteralNode(this);
272 	}
273 
274     override string toString() const
275     {
276         // return "(object literal node)";
277         if(keys.length != valueNodes.length)
278             return "{invalid_object}";
279         auto result = "{";
280         for(size_t i = 0; i < keys.length; ++i)
281             result ~= keys[i] ~ ":" ~ valueNodes[i].toString;
282         result ~= "}";
283         return result;
284     }
285 
286     string[] keys;
287     ExpressionNode[] valueNodes;
288 }
289 
290 class DestructureTargetNode : ExpressionNode
291 {
292     this(string[] names, string rname, bool isObj)
293     {
294         varNames = names;
295         remainderName = rname;
296         isObject = isObj;
297     }
298 
299     // this can't be directly visited?
300     override Variant accept(IExpressionVisitor visitor)
301     {
302         return Variant(null);
303     }
304 
305     override string toString() const
306     {
307         auto result = isObject ? "{" : "[";
308         for(size_t i = 0; i < varNames.length; ++i)
309         {
310             result ~= varNames[i];
311             if(i < varNames.length - 1)
312                 result ~= ", ";
313         }
314         if(remainderName)
315             result ~= remainderName ~ "...";
316         result ~= isObject ? "}" : "]";
317         return result;
318     }
319 
320     string[] varNames;
321     string remainderName;
322     bool isObject; // false for array
323 }
324 
325 class ClassLiteralNode : ExpressionNode 
326 {
327     this(Token ctoken, ClassDefinition cdef)
328     {
329         classToken = ctoken;
330         classDefinition = cdef;
331     }
332 
333 	override Variant accept(IExpressionVisitor visitor)
334 	{
335 		return visitor.visitClassLiteralNode(this);
336 	}
337 
338     override string toString() const 
339     {
340         return classDefinition.toString();
341     }
342 
343     Token classToken;
344     ClassDefinition classDefinition;
345 }
346 
347 class BinaryOpNode : ExpressionNode
348 {
349     this(Token op, ExpressionNode left, ExpressionNode right)
350     {
351         opToken = op;
352         leftNode = left;
353         rightNode = right;
354     }
355 
356 	override Variant accept(IExpressionVisitor visitor)
357 	{
358 		return visitor.visitBinaryOpNode(this);
359 	}
360 
361     override string toString() const
362     {
363         return format("(%s %s %s)", leftNode, opToken.symbol, rightNode);
364     }
365 
366     Token opToken;
367     ExpressionNode leftNode;
368     ExpressionNode rightNode;
369 }
370 
371 class UnaryOpNode : ExpressionNode
372 {
373     this(Token op, ExpressionNode operand)
374     {
375         opToken = op;
376         operandNode = operand;
377     }
378 
379 	override Variant accept(IExpressionVisitor visitor)
380 	{
381 		return visitor.visitUnaryOpNode(this);
382 	}
383 
384     override string toString() const
385     {
386         return format("(%s %s)", opToken.symbol, operandNode);
387     }
388 
389     Token opToken;
390     ExpressionNode operandNode;
391 }
392 
393 class PostfixOpNode : ExpressionNode 
394 {
395     this(Token op, ExpressionNode node)
396     {
397         opToken = op;
398         operandNode = node;
399     }
400 
401 	override Variant accept(IExpressionVisitor visitor)
402 	{
403 		return visitor.visitPostfixOpNode(this);
404 	}
405 
406     override string toString() const 
407     {
408         return operandNode.toString() ~ opToken.symbol;
409     }
410 
411     Token opToken;
412     ExpressionNode operandNode;
413 }
414 
415 class TerniaryOpNode : ExpressionNode 
416 {
417     this(ExpressionNode cond, ExpressionNode onTrue, ExpressionNode onFalse)
418     {
419         conditionNode = cond;
420         onTrueNode = onTrue;
421         onFalseNode = onFalse;
422     }
423 
424 	override Variant accept(IExpressionVisitor visitor)
425 	{
426 		return visitor.visitTerniaryOpNode(this);
427 	}
428 
429     override string toString() const 
430     {
431         return conditionNode.toString() ~ "? " ~ onTrueNode.toString() ~ " : " ~ onFalseNode.toString();
432     }
433 
434     ExpressionNode conditionNode;
435     ExpressionNode onTrueNode;
436     ExpressionNode onFalseNode;
437 }
438 
439 class VarAccessNode : ExpressionNode
440 {
441     this(Token token)
442     {
443         varToken = token;
444     }
445 
446 	override Variant accept(IExpressionVisitor visitor)
447 	{
448 		return visitor.visitVarAccessNode(this);
449 	}
450 
451     override string toString() const
452     {
453         return varToken.text;
454     }
455 
456     Token varToken;
457 }
458 
459 class FunctionCallNode : ExpressionNode
460 {
461     this(ExpressionNode fn, ExpressionNode[] args, bool retThis=false)
462     {
463         functionToCall = fn;
464         expressionArgs = args;
465         returnThis = retThis;
466     }
467 
468 	override Variant accept(IExpressionVisitor visitor)
469 	{
470 		return visitor.visitFunctionCallNode(this);
471 	}
472 
473     override string toString() const
474     {
475         auto str = functionToCall.toString ~ "(";
476         for(size_t i = 0; i < expressionArgs.length; ++i)
477         {
478             str ~= expressionArgs[i].toString;
479             if(i < expressionArgs.length - 1) // @suppress(dscanner.suspicious.length_subtraction)
480                 str ~= ", ";
481         }
482         str ~= ")";
483         return str;
484     }
485 
486     ExpressionNode functionToCall;
487     ExpressionNode[] expressionArgs;
488     bool returnThis;
489 }
490 
491 // when [] operator is used
492 class ArrayIndexNode : ExpressionNode 
493 {
494     this(ExpressionNode obj, ExpressionNode index)
495     {
496         objectNode = obj;
497         indexValueNode = index;
498     }    
499 
500 	override Variant accept(IExpressionVisitor visitor)
501 	{
502 		return visitor.visitArrayIndexNode(this);
503 	}
504 
505     override string toString() const
506     {
507         return objectNode.toString() ~ "[" ~ indexValueNode.toString() ~ "]";
508     }
509 
510     ExpressionNode objectNode;
511     ExpressionNode indexValueNode;
512 }
513 
514 class MemberAccessNode : ExpressionNode 
515 {
516     this(ExpressionNode obj, Token dt, ExpressionNode member)
517     {
518         objectNode = obj;
519         dotToken = dt;
520         memberNode = member;
521     }
522 
523 	override Variant accept(IExpressionVisitor visitor)
524 	{
525 		return visitor.visitMemberAccessNode(this);
526 	}
527 
528     override string toString() const
529     {
530         return objectNode.toString() ~ "." ~ memberNode.toString();
531     }
532 
533     ExpressionNode objectNode;
534     Token dotToken;
535     ExpressionNode memberNode;
536 }
537 
538 class NewExpressionNode : ExpressionNode 
539 {
540     this(ExpressionNode fn)
541     {
542         functionCallExpression = fn;
543     }
544 
545 	override Variant accept(IExpressionVisitor visitor)
546 	{
547 		return visitor.visitNewExpressionNode(this);
548 	}
549 
550     override string toString() const
551     {
552         return "new " ~ functionCallExpression.toString();
553     }
554 
555     ExpressionNode functionCallExpression;
556 }
557 
558 /// for when the super keyword is not used as a constructor
559 class SuperNode : ExpressionNode
560 {
561     this(Token stoken, ExpressionNode bc)
562     {
563         superToken = stoken;
564         baseClass = bc;
565     }
566 
567     override Variant accept(IExpressionVisitor visitor)
568     {
569         return visitor.visitSuperNode(this);
570     }
571 
572     override string toString() const 
573     {
574         return "super";
575     }
576 
577     Token superToken;
578     ExpressionNode baseClass;
579 }
580 
581 class YieldNode : ExpressionNode
582 {
583     this(Token yToken, ExpressionNode expr)
584     {
585         yieldToken = yToken;
586         yieldExpression = expr;
587     }
588 
589     override Variant accept(IExpressionVisitor visitor)
590     {
591         return visitor.visitYieldNode(this);
592     }
593 
594     override string toString() const
595     {
596         return "yield " ~ (yieldExpression? yieldExpression.toString: "");
597     }
598 
599     Token yieldToken;
600     ExpressionNode yieldExpression;
601 }
602 
603 /// root class of all statement nodes
604 abstract class StatementNode
605 {
606     this(size_t lineNo)
607     {
608         line = lineNo;
609     }
610 
611 	abstract Variant accept(IStatementVisitor visitor);
612 
613     override string toString() const
614     {
615         assert(false, "This method is virtual and should never be called directly");
616     }
617 
618     immutable size_t line;
619 }
620 
621 class VarDeclarationStatementNode : StatementNode
622 {
623     this(Token qual, ExpressionNode[] nodes)
624     {
625         super(qual.position.line);
626         qualifier = qual;
627         varAccessOrAssignmentNodes = nodes;
628     }
629 
630     this(size_t lineNo, Token qual, ExpressionNode[] nodes)
631     {
632         super(lineNo);
633         qualifier = qual;
634         varAccessOrAssignmentNodes = nodes;
635     }
636 
637 	override Variant accept(IStatementVisitor visitor)
638 	{
639 		return visitor.visitVarDeclarationStatementNode(this);
640 	}
641 
642     override string toString() const
643     {
644         string str = qualifier.text ~ " ";
645         for(size_t i = 0; i < varAccessOrAssignmentNodes.length; ++i)
646         {
647             str ~= varAccessOrAssignmentNodes[i].toString();
648             if(i < varAccessOrAssignmentNodes.length - 1) // @suppress(dscanner.suspicious.length_subtraction)
649                 str ~= ", ";
650         }
651         return str;
652     }
653 
654     Token qualifier; // must be var, let, or const
655     ExpressionNode[] varAccessOrAssignmentNodes; // must be VarAccessNode or BinaryOpNode. should be validated by parser
656 }
657 
658 class BlockStatementNode: StatementNode
659 {
660     this(size_t lineNo, StatementNode[] statements)
661     {
662         super(lineNo);
663         statementNodes = statements;
664     }
665 
666 	override Variant accept(IStatementVisitor visitor)
667 	{
668 		return visitor.visitBlockStatementNode(this);
669 	}
670 
671     override string toString() const
672     {
673         string str = "{\n";
674         foreach(st ; statementNodes)
675         {
676             str ~= "  " ~ st.toString ~ "\n";
677         }
678         str ~= "}";
679         return str;
680     }
681 
682     StatementNode[] statementNodes;
683 }
684 
685 class IfStatementNode : StatementNode
686 {
687     this(size_t lineNo, ExpressionNode condition, StatementNode onTrue, StatementNode onFalse=null)
688     {
689         super(lineNo);
690         conditionNode = condition;
691         onTrueStatement = onTrue;
692         onFalseStatement = onFalse;
693     }
694 
695 	override Variant accept(IStatementVisitor visitor)
696 	{
697 		return visitor.visitIfStatementNode(this);
698 	}
699 
700     override string toString() const
701     {
702         auto str = "if(" ~ conditionNode.toString() ~ ") ";
703         str ~= onTrueStatement.toString();
704         if(onFalseStatement !is null)
705             str ~= " else " ~ onFalseStatement.toString();
706         return str;
707     }
708 
709     ExpressionNode conditionNode;
710     StatementNode onTrueStatement, onFalseStatement;
711 }
712 
713 class SwitchStatementNode : StatementNode
714 {
715     this(size_t lineNo, ExpressionNode expr, SwitchBody sbody)
716     {
717         super(lineNo);
718         expressionNode = expr;
719         switchBody = sbody;
720     }
721 
722 	override Variant accept(IStatementVisitor visitor)
723 	{
724 		return visitor.visitSwitchStatementNode(this);
725 	}
726 
727     ExpressionNode expressionNode; // expression to test
728     SwitchBody switchBody;
729 }
730 
731 class SwitchBody
732 {
733     this(StatementNode[] statements, size_t defaultID, size_t[ScriptAny] jumpTableID)
734     {
735         statementNodes = statements;
736         defaultStatementID = defaultID;
737         jumpTable = jumpTableID;
738     }
739 
740     StatementNode[] statementNodes;
741     size_t defaultStatementID; // index into statementNodes
742     size_t[ScriptAny] jumpTable; // indexes into statementNodes
743 }
744 
745 class WhileStatementNode : StatementNode
746 {
747     this(size_t lineNo, ExpressionNode condition, StatementNode bnode, string lbl = "")
748     {
749         super(lineNo);
750         conditionNode = condition;
751         bodyNode = bnode;
752         label = lbl;
753     }
754 
755 	override Variant accept(IStatementVisitor visitor)
756 	{
757 		return visitor.visitWhileStatementNode(this);
758 	}
759 
760     override string toString() const
761     {
762         auto str = "while(" ~ conditionNode.toString() ~ ") ";
763         str ~= bodyNode.toString();
764         return str;
765     }
766 
767     ExpressionNode conditionNode;
768     StatementNode bodyNode;
769     string label;
770 }
771 
772 class DoWhileStatementNode : StatementNode
773 {
774     this(size_t lineNo, StatementNode bnode, ExpressionNode condition, string lbl="")
775     {
776         super(lineNo);
777         bodyNode = bnode;
778         conditionNode = condition;
779         label = lbl;
780     }
781 
782 	override Variant accept(IStatementVisitor visitor)
783 	{
784 		return visitor.visitDoWhileStatementNode(this);
785 	}
786 
787     override string toString() const
788     {
789         auto str = "do " ~ bodyNode.toString() ~ " while("
790             ~ conditionNode.toString() ~ ")";
791         return str;
792     }
793 
794     StatementNode bodyNode;
795     ExpressionNode conditionNode;
796     string label;
797 }
798 
799 class ForStatementNode : StatementNode
800 {
801     this(size_t lineNo, VarDeclarationStatementNode decl, ExpressionNode condition, ExpressionNode increment, 
802          StatementNode bnode, string lbl="")
803     {
804         super(lineNo);
805         varDeclarationStatement = decl;
806         conditionNode = condition;
807         incrementNode = increment;
808         bodyNode = bnode;
809         label = lbl;
810     }
811 
812 	override Variant accept(IStatementVisitor visitor)
813 	{
814 		return visitor.visitForStatementNode(this);
815 	}
816 
817     override string toString() const
818     {
819         auto decl = "";
820         if(varDeclarationStatement !is null)
821             decl = varDeclarationStatement.toString();
822         auto str = "for(" ~ decl ~ ";" ~ conditionNode.toString() 
823             ~ ";" ~ incrementNode.toString() ~ ") " ~ bodyNode.toString();
824         return str;
825     }
826 
827     VarDeclarationStatementNode varDeclarationStatement;
828     ExpressionNode conditionNode;
829     ExpressionNode incrementNode;
830     StatementNode bodyNode;
831     string label;
832 }
833 
834 // for of can't do let {a,b} but it can do let a,b and be used the same as for in in JS
835 class ForOfStatementNode : StatementNode
836 {
837     this(size_t lineNo, Token qual, Token ofIn, VarAccessNode[] vans, ExpressionNode obj, 
838          StatementNode bnode, string lbl="")
839     {
840         super(lineNo);
841         qualifierToken = qual;
842         ofInToken = ofIn;
843         varAccessNodes = vans;
844         objectToIterateNode = obj;
845         bodyNode = bnode;
846         label = lbl;
847     }
848 
849 	override Variant accept(IStatementVisitor visitor)
850 	{
851 		return visitor.visitForOfStatementNode(this);
852 	}
853 
854     override string toString() const
855     {
856         auto str = "for(" ~ qualifierToken.text;
857         for(size_t i = 0; i < varAccessNodes.length; ++i)
858         {
859             str ~= varAccessNodes[i].varToken.text;
860             if(i < varAccessNodes.length - 1) // @suppress(dscanner.suspicious.length_subtraction)
861                 str ~= ", ";
862         }
863         str ~= " of " 
864             ~ objectToIterateNode.toString() ~ ")" 
865             ~ bodyNode.toString();
866         return str;
867     }
868 
869     Token qualifierToken;
870     Token ofInToken;
871     VarAccessNode[] varAccessNodes;
872     ExpressionNode objectToIterateNode;
873     StatementNode bodyNode;
874     string label;
875 }
876 
877 class BreakStatementNode : StatementNode
878 {
879     this(size_t lineNo, string lbl="")
880     {
881         super(lineNo);
882         label = lbl;
883     }
884 
885 	override Variant accept(IStatementVisitor visitor)
886 	{
887 		return visitor.visitBreakStatementNode(this);
888 	}
889 
890     override string toString() const
891     {
892         return "break " ~ label ~ ";";
893     }
894 
895     string label;
896 }
897 
898 class ContinueStatementNode : StatementNode
899 {
900     this(size_t lineNo, string lbl = "")
901     {
902         super(lineNo);
903         label = lbl;
904     }
905 
906 	override Variant accept(IStatementVisitor visitor)
907 	{
908 		return visitor.visitContinueStatementNode(this);
909 	}
910 
911     override string toString() const
912     {
913         return "continue " ~ label ~ ";";
914     }
915 
916     string label;
917 }
918 
919 class ReturnStatementNode : StatementNode
920 {
921     this(size_t lineNo, ExpressionNode expr = null)
922     {
923         super(lineNo);
924         expressionNode = expr;
925     }
926 
927 	override Variant accept(IStatementVisitor visitor)
928 	{
929 		return visitor.visitReturnStatementNode(this);
930 	}
931 
932     override string toString() const
933     {
934         auto str = "return";
935         if(expressionNode !is null)
936             str ~= " " ~ expressionNode.toString;
937         return str ~ ";";
938     }
939 
940     ExpressionNode expressionNode;
941 }
942 
943 class FunctionDeclarationStatementNode : StatementNode
944 {
945     this(size_t lineNo, string n, string[] args, StatementNode[] statements, bool isG = false)
946     {
947         super(lineNo);
948         name = n;
949         argNames = args;
950         statementNodes = statements;
951         isGenerator = isG;
952     }
953 
954 	override Variant accept(IStatementVisitor visitor)
955 	{
956 		return visitor.visitFunctionDeclarationStatementNode(this);
957 	}
958 
959     override string toString() const
960     {
961         auto str = "function " ~ name ~ "(";
962         for(int i = 0; i < argNames.length; ++i)
963         {
964             str ~= argNames[i];
965             if(i < argNames.length - 1) // @suppress(dscanner.suspicious.length_subtraction)
966                 str ~= ", ";
967         }
968         str ~= ") {";
969         foreach(st ; statementNodes)
970             str ~= "\t" ~ st.toString;
971         str ~= "}";
972         return str;
973     }
974 
975     string name;
976     string[] argNames;
977     StatementNode[] statementNodes;
978     bool isGenerator;
979 }
980 
981 class ThrowStatementNode : StatementNode
982 {
983     this(size_t lineNo, ExpressionNode expr)
984     {
985         super(lineNo);
986         expressionNode = expr;
987     }
988 
989 	override Variant accept(IStatementVisitor visitor)
990 	{
991 		return visitor.visitThrowStatementNode(this);
992 	}
993 
994     override string toString() const
995     {
996         return "throw " ~ expressionNode.toString() ~ ";";
997     }
998 
999     ExpressionNode expressionNode;
1000 }
1001 
1002 class TryCatchBlockStatementNode : StatementNode
1003 {
1004     this(size_t lineNo, StatementNode tryBlock, string name, StatementNode catchBlock=null, StatementNode fin=null)
1005     {
1006         super(lineNo);
1007         tryBlockNode = tryBlock;
1008         exceptionName = name;
1009         catchBlockNode = catchBlock;
1010         finallyBlockNode = fin;
1011     }
1012 
1013 	override Variant accept(IStatementVisitor visitor)
1014 	{
1015 		return visitor.visitTryCatchBlockStatementNode(this);
1016 	}
1017 
1018     override string toString() const
1019     {
1020         string output = "try " ~ tryBlockNode.toString();
1021         if(catchBlockNode)
1022             output ~= " catch(" ~ exceptionName ~ ")" ~ catchBlockNode.toString();
1023         if(finallyBlockNode)
1024             output ~= " finally " ~ finallyBlockNode.toString();
1025         return output;
1026     }
1027 
1028     StatementNode tryBlockNode;
1029     string exceptionName;
1030     StatementNode catchBlockNode;
1031     StatementNode finallyBlockNode;
1032 }
1033 
1034 class DeleteStatementNode : StatementNode
1035 {
1036     this(size_t lineNo, Token deleteTok, ExpressionNode accessNode)
1037     {
1038         super(lineNo);
1039         deleteToken = deleteTok;
1040         memberAccessOrArrayIndexNode = accessNode;
1041     }
1042 
1043 	override Variant accept(IStatementVisitor visitor)
1044 	{
1045 		return visitor.visitDeleteStatementNode(this);
1046 	}
1047 
1048     override string toString() const
1049     {
1050         return "delete " ~ memberAccessOrArrayIndexNode.toString ~ ";";
1051     }
1052 
1053     Token deleteToken;
1054     ExpressionNode memberAccessOrArrayIndexNode;
1055 }
1056 
1057 class ClassDeclarationStatementNode : StatementNode
1058 {
1059     this(size_t lineNo, Token ctoken, ClassDefinition cdef)
1060     {
1061         super(lineNo);
1062         classToken = ctoken;
1063         classDefinition = cdef;
1064     }
1065 
1066 	override Variant accept(IStatementVisitor visitor)
1067 	{
1068 		return visitor.visitClassDeclarationStatementNode(this);
1069 	}
1070 
1071     override string toString() const
1072     {
1073         return classDefinition.toString();
1074     }
1075 
1076     Token classToken;
1077     ClassDefinition classDefinition;
1078 }
1079 
1080 class ExpressionStatementNode : StatementNode
1081 {
1082     this(size_t lineNo, ExpressionNode expression)
1083     {
1084         super(lineNo);
1085         expressionNode = expression;
1086     }
1087 
1088 	override Variant accept(IStatementVisitor visitor)
1089 	{
1090 		return visitor.visitExpressionStatementNode(this);
1091 	}
1092 
1093     override string toString() const
1094     {
1095         if(expressionNode is null)
1096             return ";";
1097         return expressionNode.toString() ~ ";";
1098     }
1099 
1100     ExpressionNode expressionNode;
1101 }