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].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 						break;
315 					}
316 				}
317 			}
318 			return lastNode;
319 		}
320 		
321 		/// generates AST for operators like +, -...
322 		/// 
323 		/// `firstOperand` is the first operand for the operator.
324 		/// `index` is the index of the token which is the operator
325 		/// 
326 		/// changes `index` to the index of the token after the last token related to the operator
327 		OperatorNode generateOperatorAST(CodeNode firstOperand){
328 			OperatorNode operator;
329 			operator.lineno = tokens.getTokenLine(index);
330 			// make sure it's an operator, and there is a second operand
331 			if (tokens.tokens[index].type == Token.Type.Operator && OPERATORS.hasElement(tokens.tokens[index].token) &&
332 				index+1 < tokens.tokens.length){
333 				// read the next operand
334 				CodeNode secondOperand;
335 				string operatorString = tokens.tokens[index].token;
336 				index ++;
337 				secondOperand = generateNodeAST();
338 				// put both operands under one node
339 				operator = OperatorNode(operatorString, firstOperand, secondOperand);
340 			}else{
341 				compileErrors.append(CompileError(tokens.getTokenLine(index), "no second operand found"));
342 				index ++;
343 			}
344 			return operator;
345 		}
346 
347 		/// generates AST for single operand operators
348 		SOperatorNode generateSOperatorAST(){
349 			SOperatorNode operator;
350 			operator.lineno = tokens.getTokenLine(index);
351 			// make sure its a single operand operator
352 			if (tokens.tokens[index].type == Token.Type.Operator && SOPERATORS.hasElement(tokens.tokens[index].token)){
353 				// read the operator
354 				operator.operator = tokens.tokens[index].token;
355 				index++;
356 				// and the operand
357 				operator.operand = generateNodeAST();
358 			}else{
359 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not an operator"));
360 			}
361 			return operator;
362 		}
363 		/// generates AST for a variable (or array) and changes value of index to the token after variable ends
364 		VariableNode generateVariableAST(){
365 			VariableNode var;
366 			var.lineno = tokens.getTokenLine(index);
367 			// make sure first token is identifier
368 			if (tokens.tokens[index].type == Token.Type.Identifier){
369 				// set var name
370 				var.varName = tokens.tokens[index].token;
371 				index ++;
372 			}else{
373 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not a variable"));
374 				index ++;
375 			}
376 			return var;
377 		}
378 		
379 		/// generates AST for assignment operator
380 		AssignmentNode generateAssignmentAST(){
381 			AssignmentNode assignment;
382 			assignment.lineno = tokens.getTokenLine(index);
383 			// get the variable to assign to
384 			// check if the var is being deref-ed first
385 			if (tokens.tokens[index] == Token(Token.Type.Operator, "@")){
386 				assignment.deref = true;
387 				index++;
388 			}
389 			CodeNode varCodeNode = generateCodeAST();
390 			VariableNode var;
391 			CodeNode[] indexes;
392 			if (varCodeNode.type == CodeNode.Type.ReadElement){
393 				LinkedList!CodeNode indexesList = new LinkedList!CodeNode;
394 				ReadElement indexRead;
395 				while (varCodeNode.type == CodeNode.Type.ReadElement){
396 					indexRead = varCodeNode.node!(CodeNode.Type.ReadElement);
397 					indexesList.append(indexRead.index);
398 					varCodeNode = indexRead.readFromNode;
399 				}
400 				indexes = indexesList.toArray.reverseArray;
401 			}
402 			// make sure it's a var
403 			if (varCodeNode.type != CodeNode.Type.Variable){
404 				compileErrors.append (CompileError(tokens.getTokenLine(index),
405 						"can only assign to variables or deref-ed (@) references"));
406 			}else{
407 				var = varCodeNode.node!(CodeNode.Type.Variable);
408 				// now at index, the token should be a `=` operator
409 				if (tokens.tokens[index].type == Token.Type.AssignmentOperator){
410 					// everything's ok till the `=` operator
411 					index++;
412 					CodeNode val = generateCodeAST();
413 					assignment.var = var;
414 					assignment.indexes = indexes;
415 					assignment.val = val;
416 					// make sure it's followed by a semicolon
417 					if (tokens.tokens[index].type != Token.Type.StatementEnd){
418 						compileErrors.append(CompileError(tokens.getTokenLine(index),
419 								"assingment statement not followed by semicolon"));
420 					}else{
421 						// skip the semicolon too
422 						index++;
423 					}
424 				}else{
425 					compileErrors.append(CompileError(tokens.getTokenLine(index), "not an assignment statement"));
426 					index ++;
427 				}
428 			}
429 			return assignment;
430 		}
431 		
432 		/// generates AST for variable declarations
433 		VarDeclareNode generateVarDeclareAST(){
434 			VarDeclareNode varDeclare;
435 			varDeclare.lineno = tokens.getTokenLine(index);
436 			// make sure it's a var declaration, and the vars are enclosed in parantheses
437 			if ((tokens.tokens[index].token == "@" && DATA_TYPES.hasElement(tokens.tokens[index+1].token)) ||
438 				DATA_TYPES.hasElement(tokens.tokens[index].token)){
439 				// read the type
440 				try{
441 					varDeclare.type = readType();
442 				}catch(Exception e){
443 					compileErrors.append(CompileError(tokens.getTokenLine(index), e.msg));
444 					.destroy (e);
445 				}
446 				while (tokens.tokens[index].type != Token.Type.StatementEnd && index < tokens.tokens.length){
447 					if (tokens.tokens[index].type == Token.Type.Identifier){
448 						string varName = tokens.tokens[index].token;
449 						// see if it has a assigned value, case not, skip to next
450 						if (tokens.tokens[index+1].type == Token.Type.AssignmentOperator){
451 							index += 2;
452 							CodeNode value = generateCodeAST();
453 							varDeclare.addVar(varName, value);
454 						}else{
455 							varDeclare.addVar(varName);
456 							index ++;
457 						}
458 						// now there must be a comma
459 						if (tokens.tokens[index].type == Token.Type.Comma){
460 							index ++;
461 						}else if (tokens.tokens[index].type != Token.Type.StatementEnd){
462 							compileErrors.append (CompileError(tokens.getTokenLine(index), 
463 							"variable names must be separated by a comma"));
464 						}
465 						continue;
466 					}else{
467 						compileErrors.append (CompileError(tokens.getTokenLine(index), "variable name expected"));
468 						// jump to end of statement
469 						while (tokens.tokens[index].type != Token.Type.StatementEnd && index < tokens.tokens.length){
470 							index ++;
471 						}
472 						break;
473 					}
474 				}
475 				// skip the semicolon
476 				if (tokens.tokens[index].type == Token.Type.StatementEnd){
477 					index ++;
478 				}
479 			}else{
480 				compileErrors.append(CompileError(tokens.getTokenLine(index), "invalid variable declaration"));
481 				index ++;
482 			}
483 			return varDeclare;
484 		}
485 		
486 		/// generates AST for if statements
487 		IfNode generateIfAST(){
488 			IfNode ifNode;
489 			ifNode.lineno = tokens.getTokenLine(index);
490 			// check if is an if
491 			if (tokens.tokens[index].type == Token.Type.Keyword && tokens.tokens[index].token == "if" &&
492 				tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
493 				// now do the real work
494 				uinteger brackEnd = tokens.tokens.tokenBracketPos(index+1);
495 				index += 2;
496 				ifNode.condition = generateCodeAST();
497 				// make sure index & brackEnd are now same
498 				if (index == brackEnd){
499 					index = brackEnd+1;
500 					ifNode.statement = generateStatementAST();
501 					ifNode.hasElse = false;
502 					// check if there's any else statement
503 					if (tokens.tokens[index].type == Token.Type.Keyword && tokens.tokens[index].token == "else"){
504 						// add that as well
505 						index ++;
506 						ifNode.elseStatement = generateStatementAST();
507 						ifNode.hasElse = true;
508 					}
509 				}else{
510 					compileErrors.append(CompileError(tokens.getTokenLine(index), "syntax error in condition"));
511 				}
512 			}else{
513 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not a valid if/while statement"));
514 				index ++;
515 			}
516 			return ifNode;
517 		}
518 
519 		/// generates AST for while statements
520 		WhileNode generateWhileAST(){
521 			WhileNode whileNode;
522 			whileNode.lineno = tokens.getTokenLine(index);
523 			// check if is an if
524 			if (tokens.tokens[index].type == Token.Type.Keyword && tokens.tokens[index].token == "while" &&
525 				tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
526 				// now do the real work
527 				uinteger brackEnd = tokens.tokens.tokenBracketPos!true(index+1);
528 				index += 2;
529 				whileNode.condition = generateCodeAST();
530 				// skip the brackEnd, if index matches it
531 				if (index == brackEnd){
532 					index++;
533 					whileNode.statement = generateStatementAST();
534 				}else{
535 					compileErrors.append(CompileError(tokens.getTokenLine(index), "syntax error in condition"));
536 				}
537 			}else{
538 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not a valid if/while statement"));
539 				index ++;
540 			}
541 			return whileNode;
542 		}
543 
544 		/// generates AST for for loop statements
545 		ForNode generateForAST(){
546 			ForNode forNode;
547 			forNode.lineno = tokens.getTokenLine(index);
548 			// check if is a for statement
549 			if (tokens.tokens[index] == Token(Token.Type.Keyword, "for") && tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
550 				/// where the parantheses ends
551 				uinteger bracketEnd = tokens.tokens.tokenBracketPos(index+1);
552 				/// get the init statement
553 				index = index + 2;
554 				forNode.initStatement = generateStatementAST();
555 				/// get the condition
556 				forNode.condition = generateCodeAST();
557 				/// make sure there's a semicolon
558 				if (tokens.tokens[index].type != Token.Type.StatementEnd){
559 					compileErrors.append(CompileError(tokens.getTokenLine(index), "semicolon expected after for loop condition"));
560 				}
561 				index ++;
562 				/// get the increment statement
563 				forNode.incStatement = generateStatementAST();
564 				if (index == bracketEnd){
565 					/// now for the for loop body
566 					index ++;
567 					forNode.statement = generateStatementAST();
568 				}else{
569 					compileErrors.append(CompileError(tokens.getTokenLine(index), "closing parantheses expected after statement"));
570 				}
571 			}
572 			return forNode;
573 		}
574 
575 		/// generates AST for do-while loops statements
576 		DoWhileNode generateDoWhileAST(){
577 			DoWhileNode doWhile;
578 			doWhile.lineno = tokens.getTokenLine(index);
579 			if (tokens.tokens[index] == Token(Token.Type.Keyword, "do")){
580 				// get the statement
581 				index ++;
582 				doWhile.statement = generateStatementAST();
583 				// now make sure there's a while there
584 				if (tokens.tokens[index] == Token(Token.Type.Keyword, "while") &&
585 					tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
586 					// read the condition
587 					uinteger brackEnd = tokens.tokens.tokenBracketPos(index+1);
588 					index += 2;
589 					doWhile.condition = generateCodeAST();
590 					if (index != brackEnd){
591 						compileErrors.append (CompileError(tokens.getTokenLine(index), "syntax error"));
592 					}else{
593 						// skip the bracket end
594 						index ++;
595 						// the semicolon after `do ... while(...)` is optional, so if it's there, skip it
596 						if (tokens.tokens[index].type == Token.Type.StatementEnd){
597 							index ++;
598 						}
599 					}
600 				}else{
601 					compileErrors.append (CompileError(tokens.getTokenLine(index),
602 							"while followed by condition expected after end of loop statement"));
603 				}
604 			}
605 			return doWhile;
606 		}
607 
608 		/// returns a node representing a return statement
609 		ReturnNode generateReturnAST(){
610 			if (tokens.tokens[index].token != "return"){
611 				compileErrors.append(CompileError(tokens.getTokenLine(index), "not a return statement"));
612 			}else{
613 				ReturnNode r;
614 				r.lineno = tokens.getTokenLine(index);
615 				index ++;
616 				r.value = generateCodeAST();
617 				if (tokens.tokens[index].type == Token.Type.StatementEnd){
618 					index ++; //skip semicolon
619 				}else{
620 					compileErrors.append(CompileError(tokens.getTokenLine(index),"semicolon expected"));
621 				}
622 				return r;
623 			}
624 			return ReturnNode();
625 		}
626 
627 		/// returns a node representing either of the following:
628 		/// 1. String literal
629 		/// 2. Number literal
630 		/// 3. Function Call (uses `generateFunctionCallAST`)
631 		/// 4. Variable (uses `generateVariableAST`)
632 		/// 5. Some code inside parantheses (uses `generateCodeAST`)
633 		/// 6. A literal array (`[x, y, z]`)
634 		/// 
635 		/// This function is used by `generateCodeAST` to separate nodes, and by `generateOperatorAST` to read operands
636 		CodeNode generateNodeAST(){
637 			Token token = tokens.tokens[index];
638 			// an identifier, or literal (i.e some data) was expected
639 			if (token.type == Token.Type.Identifier){
640 				if (index+1 < tokens.tokens.length && tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
641 					// is a function call
642 					CodeNode r = CodeNode(generateFunctionCallAST());
643 					// check if it skipped the semicolon, because if it did, it shouldnt have, so undo it
644 					if (tokens.tokens[index-1].type == Token.Type.StatementEnd){
645 						index --;
646 					}
647 					return r;
648 				}else{
649 					// just a var
650 					return CodeNode(generateVariableAST());
651 				}
652 			}else if (token.type == Token.Type.Operator && SOPERATORS.hasElement(token.token)){
653 				return CodeNode(generateSOperatorAST());
654 			}else if (token.type == Token.Type.ParanthesesOpen){
655 				// some code
656 				index ++;
657 				CodeNode r = generateCodeAST();
658 				index ++;
659 				return r;
660 			}else if (token.type == Token.Type.IndexBracketOpen){
661 				// literal array
662 				uinteger brackEnd = tokens.tokens.tokenBracketPos(index);
663 				// read into ArrayNode
664 				CodeNode[] elements = [];
665 				index ++;
666 				for (; index < brackEnd; index ++){
667 					elements = elements ~ generateCodeAST();
668 					if (tokens.tokens[index].type != Token.Type.Comma && index != brackEnd){
669 						compileErrors.append (CompileError (tokens.getTokenLine(index),
670 								"Unexpected token, comma must be used to separate array elements"));
671 						index = brackEnd;
672 					}
673 				}
674 				index = brackEnd+1;
675 				return CodeNode(ArrayNode(elements));
676 			}else if (token.type == Token.Type.Double || token.type == Token.Type.Integer || token.type == Token.Type.String){
677 				// literal
678 				index ++;
679 				try{
680 					LiteralNode r = LiteralNode([token]);
681 					r.lineno = tokens.getTokenLine(index);
682 					return CodeNode(r);
683 				}catch (Exception e){
684 					compileErrors.append(CompileError(tokens.getTokenLine(index), e.msg));
685 					.destroy (e);
686 				}
687 			}else{
688 				index ++;
689 				compileErrors.append(CompileError(tokens.getTokenLine(index), "unexpected token"));
690 			}
691 			return CodeNode();
692 		}
693 
694 	}
695 }