1 /++
2 Contains functions to generate AST from tokens
3 +/
4 module qscript.compiler.astgen;
5 
6 import qscript.compiler.misc;
7 import qscript.compiler.tokengen;
8 import qscript.compiler.ast;
9 
10 import utils.misc;
11 import utils.lists;
12 
13 import std.conv : to;
14 
15 /// contains functions and stuff to convert a QScript from tokens to Syntax Trees
16 struct ASTGen{
17 	/// generates an AST representing a script.
18 	/// 
19 	/// `tokens` is the TokenList to generate AST from
20 	/// `errors` is the array in which errors will be put
21 	/// 
22 	/// The script must be converted to tokens using `qscript.compiler.tokengen.toTokens`
23 	/// If any errors occur, they will be contanied in `qscript.compiler.misc.`
24 	public ScriptNode generateScriptAST(TokenList scriptTokens, ref CompileError[] errors){
25 		tokens = scriptTokens;
26 		tokens.tokens = tokens.tokens.dup;
27 		tokens.tokenPerLine = tokens.tokenPerLine.dup;
28 		ScriptNode scriptNode;
29 		LinkedList!FunctionNode functions = new LinkedList!FunctionNode;
30 		compileErrors = new LinkedList!CompileError;
31 		// go through the script, compile function nodes, link them to this node
32 		index = 0;
33 		for (uinteger lastIndex = tokens.tokens.length - 1; index < tokens.tokens.length; index ++){
34 			// look for TokenType.Keyword where token.token == "function"
35 			if (tokens.tokens[index].type == Token.Type.Keyword && tokens.tokens[index].token == "function"){
36 				uinteger errorCount = compileErrors.count;
37 				FunctionNode functionNode = generateFunctionAST();
38 				index --;
39 				// check if error free
40 				if (compileErrors.count == errorCount){
41 					functions.append(functionNode);
42 				}
43 			}
44 		}
45 		scriptNode = ScriptNode(functions.toArray);
46 		.destroy(functions);
47 		errors = compileErrors.toArray;
48 		.destroy(compileErrors);
49 		return scriptNode;
50 	}
51 	private{
52 		/// stores the list of tokens
53 		TokenList tokens;
54 		/// stores the current index (of tokens)
55 		uinteger index;
56 		/// stores all compilation errors
57 		LinkedList!CompileError compileErrors;
58 		/// reads a type from TokensList
59 		/// 
60 		/// returns type in DataType struct, changes `index` to token after last token of type
61 		DataType readType(){
62 			uinteger startIndex = index;
63 			// check if it's a ref
64 			if (tokens.tokens[index].token == "@")
65 				index ++;
66 			// the first token has to be the type (int, string, double)
67 			index ++;
68 			// then comes the brackets making it an array
69 			if (tokens.tokens[index].type == Token.Type.IndexBracketOpen){
70 				for (; index < tokens.tokens.length; index ++){
71 					if (tokens.tokens[index].type != Token.Type.IndexBracketOpen &&
72 						tokens.tokens[index].type != Token.Type.IndexBracketClose){
73 						break;
74 					}
75 				}
76 			}
77 			string sType = tokens.toString(tokens.tokens[startIndex .. index]);
78 			return DataType(sType);
79 		}
80 		/// generates AST for a function definition 
81 		/// 
82 		/// changes `index` to the token after function definition
83 		FunctionNode generateFunctionAST(){
84 			FunctionNode functionNode;
85 			functionNode.lineno = tokens.getTokenLine(index);
86 			// make sure it's a function
87 			if (tokens.tokens[index].type == Token.Type.Keyword && tokens.tokens[index].token == "function"){
88 				// read the type
89 				index++;
90 				try{
91 					functionNode.returnType = readType();
92 				}catch(Exception e){
93 					compileErrors.append(CompileError(tokens.getTokenLine(index), e.msg));
94 					.destroy (e);
95 				}
96 				// make sure return type is not ref, thats bad, causes segfault
97 				if (functionNode.returnType.isRef){
98 					compileErrors.append(CompileError(tokens.getTokenLine(index), "functions cannot return references"));
99 				}
100 				// above functionCall moves index to fName
101 				// now index is at function name
102 				functionNode.name = tokens.tokens[index].token;
103 				index ++;
104 				// now for the argument types
105 				if (tokens.tokens[index].type == Token.Type.ParanthesesOpen){
106 					LinkedList!(FunctionNode.Argument) argList = new LinkedList!(FunctionNode.Argument);
107 					uinteger brackEnd = tokens.tokens.tokenBracketPos(index);
108 					bool commaExpected = false;
109 					for (index ++; index < brackEnd; index ++){
110 						if (commaExpected){
111 							if (tokens.tokens[index].type == Token.Type.Comma){
112 								commaExpected = false;
113 								continue;
114 							}else{
115 								compileErrors.append(CompileError(tokens.getTokenLine(index),
116 										"arguments must be separated using a comma"));
117 							}
118 						}else{
119 							// data type + arg_name expected
120 							// read the type
121 							FunctionNode.Argument arg;
122 							try{
123 								arg.argType = readType();
124 							}catch(Exception e){
125 								compileErrors.append(CompileError(tokens.getTokenLine(index), e.msg));
126 								.destroy (e);
127 							}
128 							// the type must not be void, script-defined functions must all be static typed
129 							if (arg.argType.type == DataType.Type.Void){
130 								compileErrors.append(CompileError(tokens.getTokenLine(index),
131 										"script-defined functions can not receive arguments of type void"));
132 							}
133 							// now the arg_name
134 							arg.argName = tokens.tokens[index].token;
135 							commaExpected = true;
136 							// add it to list
137 							argList.append (arg);
138 						}
139 					}
140 					// put args in list
141 					functionNode.arguments = argList.toArray;
142 					.destroy(argList);
143 					index = brackEnd+1;
144 				}
145 				if (tokens.tokens[index].type == Token.Type.BlockStart){
146 					// no args, just body
147 					functionNode.bodyBlock = generateBlockAST();
148 				}else{
149 					compileErrors.append(CompileError(tokens.getTokenLine(index), "function has no body"));
150 				}
151 			}else{
152 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not a function definition"));
153 				index ++;
154 			}
155 			return functionNode;
156 		}
157 		
158 		/// generates AST for a {block-of-code}
159 		/// 
160 		/// changes `index` to token after block end
161 		BlockNode generateBlockAST(){
162 			BlockNode blockNode;
163 			blockNode.lineno = tokens.getTokenLine(index);
164 			// make sure it's a block
165 			if (tokens.tokens[index].type == Token.Type.BlockStart){
166 				uinteger brackEnd = tokens.tokens.tokenBracketPos!(true)(index);
167 				LinkedList!StatementNode statements = new LinkedList!StatementNode;
168 				// read statements
169 				index++;
170 				uinteger errorCount = compileErrors.count;
171 				while (index < brackEnd){
172 					statements.append(generateStatementAST());
173 					// check if error added
174 					if (errorCount != compileErrors.count){
175 						break;
176 					}
177 				}
178 				// put them all in block
179 				blockNode.statements = statements.toArray;
180 				.destroy(statements);
181 				index = brackEnd+1;
182 			}else{
183 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not a block"));
184 				index ++;
185 			}
186 			return blockNode;
187 		}
188 
189 		/// generates AST for one statement
190 		StatementNode generateStatementAST(){
191 			// identify the type, call the appropriate function
192 			if (tokens.tokens[index].type == Token.Type.BlockStart){
193 				// block
194 				return StatementNode(generateBlockAST());
195 			}else if (tokens.tokens[index].type == Token.Type.Keyword){
196 				if (tokens.tokens[index].token == "if"){
197 					// if statement
198 					return StatementNode(generateIfAST());
199 				}else if (tokens.tokens[index].token == "while"){
200 					// while statement
201 					return StatementNode(generateWhileAST());
202 				}else if (tokens.tokens[index].token == "for"){
203 					// for statement
204 					return StatementNode(generateForAST());
205 				}else if (tokens.tokens[index].token == "do"){
206 					// do while
207 					return StatementNode(generateDoWhileAST());
208 				}else if (tokens.tokens[index].token == "return"){
209 					// return statement
210 					return StatementNode(generateReturnAST());
211 				}
212 			}else if (tokens.tokens[index].type == Token.Type.DataType || 
213 				(tokens.tokens[index].token == "@" && tokens.tokens[index+1].type == Token.Type.DataType)){
214 				// var declare
215 				return StatementNode(generateVarDeclareAST());
216 			}else if (tokens.tokens[index].type == Token.Type.Identifier &&
217 				tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
218 				// is a function call
219 				return StatementNode(generateFunctionCallAST());
220 			}else if (tokens.tokens[index].type == Token.Type.Identifier || 
221 				(tokens.tokens[index].token == "@" && tokens.tokens[index+1].type == Token.Type.Identifier)){
222 				// assignment
223 				return StatementNode(generateAssignmentAST());
224 			}
225 			compileErrors.append (CompileError(tokens.getTokenLine(index), "invalid statement"));
226 			index ++;
227 			return StatementNode();
228 		}
229 
230 		/// generates AST for function call, changes `index` to token after statementEnd
231 		FunctionCallNode generateFunctionCallAST(){
232 			FunctionCallNode functionCallNode;
233 			functionCallNode.lineno = tokens.getTokenLine(index);
234 			// check if is function call
235 			if (tokens.tokens[index].type == Token.Type.Identifier &&
236 				tokens.tokens[index + 1].type == Token.Type.ParanthesesOpen){
237 				uinteger brackEnd = tokens.tokens.tokenBracketPos(index + 1);
238 				functionCallNode.fName = tokens.tokens[index].token;
239 				// now for the arguments
240 				index+=2;
241 				if (tokens.tokens[index].type == Token.Type.ParanthesesClose){
242 					// has no args
243 					functionCallNode.arguments = [];
244 				}else{
245 					LinkedList!CodeNode args = new LinkedList!CodeNode;
246 					for (; ; index ++){
247 						args.append(generateCodeAST());
248 						if (tokens.tokens[index].type == Token.Type.ParanthesesClose){
249 							break;
250 						}
251 					}
252 					functionCallNode.arguments = args.toArray;
253 					.destroy(args);
254 				}
255 				// move index to bracketend or semicolon
256 				index = brackEnd+1;
257 				if (tokens.tokens[index].type == Token.Type.StatementEnd){
258 					index ++;
259 				}
260 				return functionCallNode;
261 			}else{
262 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not a valid function call"));
263 				index ++;
264 			}
265 			return functionCallNode;
266 		}
267 		
268 		/// generates AST for "actual code" like `2 + 2 - 6`.
269 		/// 
270 		/// terminates reading when on a Token that is comma, paranthesesEnd, semicolon, index/block bracket close
271 		CodeNode generateCodeAST(){
272 			CodeNode lastNode = null;
273 			bool separatorExpected = false;
274 			for ( ; ; index ++){
275 				Token token = tokens.tokens[index];
276 				// check if has to terminate
277 				if ([Token.Type.Comma, Token.Type.IndexBracketClose, Token.Type.BlockEnd, Token.Type.ParanthesesClose,
278 					Token.Type.StatementEnd, Token.Type.AssignmentOperator].hasElement(token.type)){
279 					break;
280 				}
281 				if (!separatorExpected){
282 					// an identifier, or literal (i.e some data) was expected
283 					lastNode = generateNodeAST();
284 					index --;
285 					separatorExpected = true;
286 				}else{
287 					// an operator or something that deals with data was expected.
288 					// check if there's an [...] to read the array
289 					if (token.type == Token.Type.IndexBracketOpen){
290 						// read the index
291 						uinteger brackStartIndex = index;
292 						uinteger brackEnd = tokens.tokens.tokenBracketPos(index);
293 						index ++;
294 						CodeNode indexNode = generateCodeAST();
295 						// make sure it's a read-element, not a make-array
296 						if (index != brackEnd){
297 							compileErrors.append (CompileError(tokens.getTokenLine(index), "unexpected tokens"));
298 							index++;
299 							break;
300 						}
301 						lastNode = CodeNode(ReadElement(lastNode, indexNode));
302 						lastNode.lineno = tokens.getTokenLine(brackStartIndex);
303 						continue;
304 					}
305 					separatorExpected = false;
306 					if (token.type == Token.Type.Operator){
307 						lastNode = CodeNode(generateOperatorAST(lastNode));
308 						index --;
309 						// in case of operators, generateOperatorAST takes the 2nd operand (!separator), itself, so the next
310 						// token, if any, is to be a separator
311 						separatorExpected = true;
312 						continue;
313 					}else{
314 						compileErrors.append(CompileError(tokens.getTokenLine(index), "Unexpected token '"~token.token~'\''));
315 						break;
316 					}
317 				}
318 			}
319 			return lastNode;
320 		}
321 
322 		/// generates AST for `-x` where x is a codenode
323 		NegativeValueNode generateNegativeNode(){
324 			if (tokens.tokens[index].type != Token.Type.Operator || tokens.tokens[index].token != "-"){
325 				compileErrors.append(CompileError(tokens.getTokenLine(index), "Not a negative node"));
326 				index ++;
327 				return NegativeValueNode(CodeNode());
328 			}
329 			NegativeValueNode r;
330 			index ++;
331 			CodeNode val = generateCodeAST();
332 			r = NegativeValueNode(val);
333 			return r;
334 		}
335 		
336 		/// generates AST for operators like +, -...
337 		/// 
338 		/// `firstOperand` is the first operand for the operator.
339 		/// `index` is the index of the token which is the operator
340 		/// 
341 		/// changes `index` to the index of the token after the last token related to the operator
342 		OperatorNode generateOperatorAST(CodeNode firstOperand){
343 			OperatorNode operator;
344 			operator.lineno = tokens.getTokenLine(index);
345 			// make sure it's an operator, and there is a second operand
346 			if (tokens.tokens[index].type == Token.Type.Operator && OPERATORS.hasElement(tokens.tokens[index].token) &&
347 				index+1 < tokens.tokens.length){
348 				// read the next operand
349 				CodeNode secondOperand;
350 				string operatorString = tokens.tokens[index].token;
351 				index ++;
352 				secondOperand = generateNodeAST();
353 				// put both operands under one node
354 				operator = OperatorNode(operatorString, firstOperand, secondOperand);
355 			}else{
356 				compileErrors.append(CompileError(tokens.getTokenLine(index), "no second operand found"));
357 				index ++;
358 			}
359 			return operator;
360 		}
361 
362 		/// generates AST for single operand operators
363 		SOperatorNode generateSOperatorAST(){
364 			SOperatorNode operator;
365 			operator.lineno = tokens.getTokenLine(index);
366 			// make sure its a single operand operator
367 			if (tokens.tokens[index].type == Token.Type.Operator && SOPERATORS.hasElement(tokens.tokens[index].token)){
368 				// read the operator
369 				operator.operator = tokens.tokens[index].token;
370 				index++;
371 				// and the operand
372 				operator.operand = generateNodeAST();
373 			}else{
374 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not an operator"));
375 			}
376 			return operator;
377 		}
378 		/// generates AST for a variable (or array) and changes value of index to the token after variable ends
379 		VariableNode generateVariableAST(){
380 			VariableNode var;
381 			var.lineno = tokens.getTokenLine(index);
382 			// make sure first token is identifier
383 			if (tokens.tokens[index].type == Token.Type.Identifier){
384 				// set var name
385 				var.varName = tokens.tokens[index].token;
386 				index ++;
387 			}else{
388 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not a variable"));
389 				index ++;
390 			}
391 			return var;
392 		}
393 		
394 		/// generates AST for assignment operator
395 		AssignmentNode generateAssignmentAST(){
396 			AssignmentNode assignment;
397 			assignment.lineno = tokens.getTokenLine(index);
398 			// get the variable to assign to
399 			// check if the var is being deref-ed first
400 			if (tokens.tokens[index] == Token(Token.Type.Operator, "@")){
401 				assignment.deref = true;
402 				index++;
403 			}
404 			CodeNode varCodeNode = generateCodeAST();
405 			VariableNode var;
406 			CodeNode[] indexes;
407 			if (varCodeNode.type == CodeNode.Type.ReadElement){
408 				LinkedList!CodeNode indexesList = new LinkedList!CodeNode;
409 				ReadElement indexRead;
410 				while (varCodeNode.type == CodeNode.Type.ReadElement){
411 					indexRead = varCodeNode.node!(CodeNode.Type.ReadElement);
412 					indexesList.append(indexRead.index);
413 					varCodeNode = indexRead.readFromNode;
414 				}
415 				indexes = indexesList.toArray.reverseArray;
416 			}
417 			// make sure it's a var
418 			if (varCodeNode.type != CodeNode.Type.Variable){
419 				compileErrors.append (CompileError(tokens.getTokenLine(index),
420 						"can only assign to variables or deref-ed (@) references"));
421 			}else{
422 				var = varCodeNode.node!(CodeNode.Type.Variable);
423 				// now at index, the token should be a `=` operator
424 				if (tokens.tokens[index].type == Token.Type.AssignmentOperator){
425 					// everything's ok till the `=` operator
426 					index++;
427 					CodeNode val = generateCodeAST();
428 					assignment.var = var;
429 					assignment.indexes = indexes;
430 					assignment.val = val;
431 					// make sure it's followed by a semicolon
432 					if (tokens.tokens[index].type != Token.Type.StatementEnd){
433 						compileErrors.append(CompileError(tokens.getTokenLine(index),
434 								"assingment statement not followed by semicolon"));
435 					}else{
436 						// skip the semicolon too
437 						index++;
438 					}
439 				}else{
440 					compileErrors.append(CompileError(tokens.getTokenLine(index), "not an assignment statement"));
441 					index ++;
442 				}
443 			}
444 			return assignment;
445 		}
446 		
447 		/// generates AST for variable declarations
448 		VarDeclareNode generateVarDeclareAST(){
449 			VarDeclareNode varDeclare;
450 			varDeclare.lineno = tokens.getTokenLine(index);
451 			// make sure it's a var declaration, and the vars are enclosed in parantheses
452 			if ((tokens.tokens[index].token == "@" && DATA_TYPES.hasElement(tokens.tokens[index+1].token)) ||
453 				DATA_TYPES.hasElement(tokens.tokens[index].token)){
454 				// read the type
455 				try{
456 					varDeclare.type = readType();
457 				}catch(Exception e){
458 					compileErrors.append(CompileError(tokens.getTokenLine(index), e.msg));
459 					.destroy (e);
460 				}
461 				while (tokens.tokens[index].type != Token.Type.StatementEnd && index < tokens.tokens.length){
462 					if (tokens.tokens[index].type == Token.Type.Identifier){
463 						string varName = tokens.tokens[index].token;
464 						// see if it has a assigned value, case not, skip to next
465 						if (tokens.tokens[index+1].type == Token.Type.AssignmentOperator){
466 							index += 2;
467 							CodeNode value = generateCodeAST();
468 							varDeclare.addVar(varName, value);
469 						}else{
470 							varDeclare.addVar(varName);
471 							index ++;
472 						}
473 						// now there must be a comma
474 						if (tokens.tokens[index].type == Token.Type.Comma){
475 							index ++;
476 						}else if (tokens.tokens[index].type != Token.Type.StatementEnd){
477 							compileErrors.append (CompileError(tokens.getTokenLine(index), 
478 							"variable names must be separated by a comma"));
479 						}
480 						continue;
481 					}else{
482 						compileErrors.append (CompileError(tokens.getTokenLine(index), "variable name expected"));
483 						// jump to end of statement
484 						while (tokens.tokens[index].type != Token.Type.StatementEnd && index < tokens.tokens.length){
485 							index ++;
486 						}
487 						break;
488 					}
489 				}
490 				// skip the semicolon
491 				if (tokens.tokens[index].type == Token.Type.StatementEnd){
492 					index ++;
493 				}
494 			}else{
495 				compileErrors.append(CompileError(tokens.getTokenLine(index), "invalid variable declaration"));
496 				index ++;
497 			}
498 			return varDeclare;
499 		}
500 		
501 		/// generates AST for if statements
502 		IfNode generateIfAST(){
503 			IfNode ifNode;
504 			ifNode.lineno = tokens.getTokenLine(index);
505 			// check if is an if
506 			if (tokens.tokens[index].type == Token.Type.Keyword && tokens.tokens[index].token == "if" &&
507 				tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
508 				// now do the real work
509 				uinteger brackEnd = tokens.tokens.tokenBracketPos(index+1);
510 				index += 2;
511 				ifNode.condition = generateCodeAST();
512 				// make sure index & brackEnd are now same
513 				if (index == brackEnd){
514 					index = brackEnd+1;
515 					ifNode.statement = generateStatementAST();
516 					ifNode.hasElse = false;
517 					// check if there's any else statement
518 					if (tokens.tokens[index].type == Token.Type.Keyword && tokens.tokens[index].token == "else"){
519 						// add that as well
520 						index ++;
521 						ifNode.elseStatement = generateStatementAST();
522 						ifNode.hasElse = true;
523 					}
524 				}else{
525 					compileErrors.append(CompileError(tokens.getTokenLine(index), "syntax error in condition"));
526 				}
527 			}else{
528 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not a valid if/while statement"));
529 				index ++;
530 			}
531 			return ifNode;
532 		}
533 
534 		/// generates AST for while statements
535 		WhileNode generateWhileAST(){
536 			WhileNode whileNode;
537 			whileNode.lineno = tokens.getTokenLine(index);
538 			// check if is an if
539 			if (tokens.tokens[index].type == Token.Type.Keyword && tokens.tokens[index].token == "while" &&
540 				tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
541 				// now do the real work
542 				uinteger brackEnd = tokens.tokens.tokenBracketPos!true(index+1);
543 				index += 2;
544 				whileNode.condition = generateCodeAST();
545 				// skip the brackEnd, if index matches it
546 				if (index == brackEnd){
547 					index++;
548 					whileNode.statement = generateStatementAST();
549 				}else{
550 					compileErrors.append(CompileError(tokens.getTokenLine(index), "syntax error in condition"));
551 				}
552 			}else{
553 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not a valid if/while statement"));
554 				index ++;
555 			}
556 			return whileNode;
557 		}
558 
559 		/// generates AST for for loop statements
560 		ForNode generateForAST(){
561 			ForNode forNode;
562 			forNode.lineno = tokens.getTokenLine(index);
563 			// check if is a for statement
564 			if (tokens.tokens[index] == Token(Token.Type.Keyword, "for") && tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
565 				/// where the parantheses ends
566 				uinteger bracketEnd = tokens.tokens.tokenBracketPos(index+1);
567 				/// get the init statement
568 				index = index + 2;
569 				forNode.initStatement = generateStatementAST();
570 				/// get the condition
571 				forNode.condition = generateCodeAST();
572 				/// make sure there's a semicolon
573 				if (tokens.tokens[index].type != Token.Type.StatementEnd){
574 					compileErrors.append(CompileError(tokens.getTokenLine(index), "semicolon expected after for loop condition"));
575 				}
576 				index ++;
577 				/// get the increment statement
578 				forNode.incStatement = generateStatementAST();
579 				if (index == bracketEnd){
580 					/// now for the for loop body
581 					index ++;
582 					forNode.statement = generateStatementAST();
583 				}else{
584 					compileErrors.append(CompileError(tokens.getTokenLine(index), "closing parantheses expected after statement"));
585 				}
586 			}
587 			return forNode;
588 		}
589 
590 		/// generates AST for do-while loops statements
591 		DoWhileNode generateDoWhileAST(){
592 			DoWhileNode doWhile;
593 			doWhile.lineno = tokens.getTokenLine(index);
594 			if (tokens.tokens[index] == Token(Token.Type.Keyword, "do")){
595 				// get the statement
596 				index ++;
597 				doWhile.statement = generateStatementAST();
598 				// now make sure there's a while there
599 				if (tokens.tokens[index] == Token(Token.Type.Keyword, "while") &&
600 					tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
601 					// read the condition
602 					uinteger brackEnd = tokens.tokens.tokenBracketPos(index+1);
603 					index += 2;
604 					doWhile.condition = generateCodeAST();
605 					if (index != brackEnd){
606 						compileErrors.append (CompileError(tokens.getTokenLine(index), "syntax error"));
607 					}else{
608 						// skip the bracket end
609 						index ++;
610 						// the semicolon after `do ... while(...)` is optional, so if it's there, skip it
611 						if (tokens.tokens[index].type == Token.Type.StatementEnd){
612 							index ++;
613 						}
614 					}
615 				}else{
616 					compileErrors.append (CompileError(tokens.getTokenLine(index),
617 							"while followed by condition expected after end of loop statement"));
618 				}
619 			}
620 			return doWhile;
621 		}
622 
623 		/// returns a node representing a return statement
624 		ReturnNode generateReturnAST(){
625 			if (tokens.tokens[index].token != "return"){
626 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not a return statement"));
627 			}else{
628 				ReturnNode r;
629 				r.lineno = tokens.getTokenLine(index);
630 				index ++;
631 				r.value = generateCodeAST();
632 				if (tokens.tokens[index].type == Token.Type.StatementEnd){
633 					index ++; //skip semicolon
634 				}else{
635 					compileErrors.append(CompileError(tokens.getTokenLine(index),"semicolon expected"));
636 				}
637 				return r;
638 			}
639 			return ReturnNode();
640 		}
641 
642 		/// returns a node representing either of the following:
643 		/// 1. String literal
644 		/// 2. Number literal
645 		/// 3. Function Call (uses `generateFunctionCallAST`)
646 		/// 4. Variable (uses `generateVariableAST`)
647 		/// 5. Some code inside parantheses (uses `generateCodeAST`)
648 		/// 6. A literal array (`[x, y, z]`)
649 		/// 
650 		/// This function is used by `generateCodeAST` to separate nodes, and by `generateOperatorAST` to read operands
651 		CodeNode generateNodeAST(){
652 			Token token = tokens.tokens[index];
653 			CodeNode r = CodeNode();
654 			// an identifier, or literal (i.e some data) was expected
655 			if (token.type == Token.Type.Identifier){
656 				if (index+1 < tokens.tokens.length && tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
657 					// is a function call
658 					r = CodeNode(generateFunctionCallAST());
659 					// check if it skipped the semicolon, because if it did, it shouldnt have, so undo it
660 					if (tokens.tokens[index-1].type == Token.Type.StatementEnd){
661 						index --;
662 					}
663 				}else{
664 					// just a var
665 					r = CodeNode(generateVariableAST());
666 				}
667 			}else if (token.type == Token.Type.Operator && SOPERATORS.hasElement(token.token)){
668 				r = CodeNode(generateSOperatorAST());
669 			}else if (token.type == Token.Type.Operator && token.token == "-"){
670 				r = CodeNode(generateNegativeNode());
671 			}else if (token.type == Token.Type.ParanthesesOpen){
672 				// some code
673 				index ++;
674 				r = generateCodeAST();
675 				index ++;
676 			}else if (token.type == Token.Type.IndexBracketOpen){
677 				// literal array
678 				uinteger brackEnd = tokens.tokens.tokenBracketPos(index);
679 				// read into ArrayNode
680 				CodeNode[] elements = [];
681 				index ++;
682 				for (; index < brackEnd; index ++){
683 					elements = elements ~ generateCodeAST();
684 					if (tokens.tokens[index].type != Token.Type.Comma && index != brackEnd){
685 						compileErrors.append (CompileError (tokens.getTokenLine(index),
686 								"Unexpected token, comma must be used to separate array elements"));
687 						index = brackEnd;
688 					}
689 				}
690 				index = brackEnd+1;
691 				r = CodeNode(ArrayNode(elements));
692 			}else if (token.type == Token.Type.Double || token.type == Token.Type.Integer || token.type == Token.Type.String ||
693 			token.type == Token.Type.Char){
694 				// literal
695 				index ++;
696 				try{
697 					LiteralNode ltNode = LiteralNode([token]);
698 					ltNode.lineno = tokens.getTokenLine(index);
699 					r = CodeNode(ltNode);
700 				}catch (Exception e){
701 					compileErrors.append(CompileError(tokens.getTokenLine(index), e.msg));
702 					.destroy (e);
703 				}
704 			}else{
705 				index ++;
706 				compileErrors.append(CompileError(tokens.getTokenLine(index), "unexpected token"));
707 			}
708 			// check if theres a [] ahead of it to read it an element
709 			while (tokens.tokens[index].type == Token.Type.IndexBracketOpen){
710 				// skip the opening bracket `[`
711 				index ++;
712 				r = CodeNode(ReadElement(r, generateCodeAST()));
713 				if (tokens.tokens[index].type == Token.Type.IndexBracketClose)
714 					index ++; // skip the ] bracket
715 			}
716 			return r;
717 		}
718 
719 	}
720 }