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