1 /++
2 Contains functions to generate AST from tokens
3 +/
4 module qscript.compiler.astgen;
5 
6 import qscript.compiler.compiler;
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 public class ASTGen{
17 private:
18 	/// stores the list of tokens
19 	TokenList tokens;
20 	/// stores the current index (of tokens)
21 	uinteger index;
22 	/// stores all compilation errors
23 	LinkedList!CompileError compileErrors;
24 	/// Reads a single import statement.
25 	/// 
26 	/// Returns: the imported libraries' names
27 	string[] readImports(){
28 		string[] r;
29 		if (tokens.tokens[index] != Token(Token.Type.Keyword, "import")){
30 			compileErrors.append(CompileError(tokens.getTokenLine(index), "not an import statement"));
31 			return [];
32 		}
33 		index ++; // skip the import
34 		while (tokens.tokens[index].type != Token.Type.StatementEnd){
35 			if (tokens.tokens[index].type == Token.Type.Identifier){
36 				r ~= tokens.tokens[index].token;
37 			}else if (tokens.tokens[index].type != Token.Type.Comma){
38 				compileErrors.append(CompileError(tokens.getTokenLine(index), "invalid import statement"));
39 			}
40 			index ++;
41 		}
42 		index ++; // skip the semicolon
43 
44 		return r;
45 	}
46 	/// reads a type from TokensList
47 	/// 
48 	/// returns type in DataType struct, changes `index` to token after last token of type
49 	DataType readType(){
50 		uinteger startIndex = index;
51 		// check if it's a ref
52 		if (tokens.tokens[index].token == "@")
53 			index ++;
54 		// the first token has to be the type (int, string, double)
55 		index ++;
56 		// then comes the brackets making it an array
57 		if (tokens.tokens[index].type == Token.Type.IndexBracketOpen){
58 			for (; index < tokens.tokens.length; index ++){
59 				if (tokens.tokens[index].type != Token.Type.IndexBracketOpen &&
60 					tokens.tokens[index].type != Token.Type.IndexBracketClose){
61 					break;
62 				}
63 			}
64 		}
65 		string sType = tokens.toString(tokens.tokens[startIndex .. index]);
66 		return DataType(sType);
67 	}
68 protected:
69 	/// generates AST for a struct definition
70 	/// 
71 	/// changes `index` to next token after struct definition
72 	StructNode generateStructAST(){
73 		StructNode structNode;
74 		if (tokens.tokens[index] == Token(Token.Type.Keyword, "struct")){
75 			structNode.lineno = tokens.getTokenLine(index);
76 			// read the name
77 			index ++;
78 			if (tokens.tokens[index].type == Token.Type.Identifier){
79 				structNode.name = tokens.tokens[index].token;
80 				// now check the { and read members
81 				index ++;
82 				if (tokens.tokens[index].type == Token.Type.BlockStart){
83 					// now start reading it all as VarDeclareNodes
84 					index ++;
85 					while (index + 1 < tokens.tokens.length && tokens.tokens[index].type != Token.Type.BlockEnd){
86 						VarDeclareNode vars = generateVarDeclareAST();
87 						// make sure no members were assignned values, thats not allowed
88 						foreach (memberName; vars.vars){
89 							if (vars.hasValue(memberName)){
90 								compileErrors.append(CompileError(tokens.getTokenLine(index),
91 									"cannot assign value to members in struct definition"));
92 							}
93 							// make sure same member name isn't used >1 times
94 							if (structNode.membersName.hasElement(memberName)){
95 								compileErrors.append(CompileError(tokens.getTokenLine(index),
96 									"member '"~memberName~"' is declared multiple times in struct"));
97 							}else{
98 								structNode.membersName ~= memberName;
99 								structNode.membersDataType ~= vars.type;
100 							}
101 						}
102 					}
103 					index ++; // skip the }
104 					// now check if struct has any members
105 					if (structNode.membersName.length == 0){
106 						compileErrors.append(CompileError(tokens.getTokenLine(index), "struct has no members"));
107 					}
108 					// if there is a semicolon, skip it
109 					if (index + 1 < tokens.tokens.length && tokens.tokens[index].type == Token.Type.StatementEnd)
110 						index ++;
111 				}else{
112 					compileErrors.append(CompileError(tokens.getTokenLine(index), "invalid struct definition, '{' expected"));
113 				}
114 			}else{
115 				compileErrors.append(CompileError(tokens.getTokenLine(index), "struct name expected"));
116 			}
117 		}else{
118 			compileErrors.append(CompileError(tokens.getTokenLine(index), "not a struct definition"));
119 			index ++;
120 		}
121 		return structNode;
122 	}
123 	/// generates AST for a enum definition
124 	/// 
125 	/// changes `index` to next token after enum definition
126 	EnumNode generateEnumAST(){
127 		EnumNode enumNode;
128 		if (tokens.tokens[index] == Token(Token.Type.Keyword, "enum")){
129 			enumNode.lineno = tokens.getTokenLine(index);
130 			index ++;
131 			if (tokens.tokens[index].type == Token.Type.Identifier){
132 				enumNode.name = tokens.tokens[index].token;
133 				index ++;
134 				if (index + 1 < tokens.tokens.length && tokens.tokens[index].type == Token.Type.BlockStart){
135 					index ++;
136 					// now start reading members
137 					while (tokens.tokens[index].type != Token.Type.BlockEnd){
138 						if (tokens.tokens[index].type == Token.Type.Identifier){
139 							enumNode.members ~= tokens.tokens[index].token;
140 							index ++;
141 							// expect a } or a comma
142 							if (tokens.tokens[index].type == Token.Type.Comma){
143 								index++;
144 							}else if (tokens.tokens[index].type != Token.Type.BlockEnd){
145 								compileErrors.append(CompileError(tokens.getTokenLine(index),"enum members must be separated using comma"));
146 							}
147 						}else{
148 							compileErrors.append(CompileError(tokens.getTokenLine(index), "enum member name expected"));
149 						}
150 					}
151 					index ++; // skip the }
152 					// skip the optional semicolon
153 					if (index + 1 < tokens.tokens.length && tokens.tokens[index].type == Token.Type.StatementEnd)
154 						index ++;
155 				}else{
156 					compileErrors.append(CompileError(tokens.getTokenLine(index), "invalid enum definition, '{' expected"));
157 				}
158 			}else{
159 				compileErrors.append(CompileError(tokens.getTokenLine(index), "enum name expected"));
160 			}
161 		}
162 		return enumNode;
163 	}
164 	/// generates AST for a function definition 
165 	/// 
166 	/// changes `index` to the token after function definition
167 	FunctionNode generateFunctionAST(){
168 		FunctionNode functionNode;
169 		functionNode.lineno = tokens.getTokenLine(index);
170 		// make sure it's a function
171 		if (tokens.tokens[index].type == Token.Type.Keyword && tokens.tokens[index].token == "function"){
172 			// read the type
173 			index++;
174 			try{
175 				functionNode.returnType = readType();
176 			}catch(Exception e){
177 				compileErrors.append(CompileError(tokens.getTokenLine(index), e.msg));
178 				.destroy (e);
179 			}
180 			// make sure return type is not ref, thats bad, causes segfault
181 			if (functionNode.returnType.isRef){
182 				compileErrors.append(CompileError(tokens.getTokenLine(index), "functions cannot return references"));
183 			}
184 			// above functionCall moves index to fName
185 			// now index is at function name
186 			functionNode.name = tokens.tokens[index].token;
187 			index ++;
188 			// now for the argument types
189 			if (tokens.tokens[index].type == Token.Type.ParanthesesOpen){
190 				LinkedList!(FunctionNode.Argument) argList = new LinkedList!(FunctionNode.Argument);
191 				uinteger brackEnd = tokens.tokens.tokenBracketPos(index);
192 				bool commaExpected = false;
193 				for (index ++; index < brackEnd; index ++){
194 					if (commaExpected){
195 						if (tokens.tokens[index].type == Token.Type.Comma){
196 							commaExpected = false;
197 							continue;
198 						}else{
199 							compileErrors.append(CompileError(tokens.getTokenLine(index),
200 									"arguments must be separated using a comma"));
201 						}
202 					}else{
203 						// data type + arg_name expected
204 						// read the type
205 						FunctionNode.Argument arg;
206 						try{
207 							arg.argType = readType();
208 						}catch(Exception e){
209 							compileErrors.append(CompileError(tokens.getTokenLine(index), e.msg));
210 							.destroy (e);
211 						}
212 						// the type must not be void, script-defined functions must all be static typed
213 						if (arg.argType.type == DataType.Type.Void){
214 							compileErrors.append(CompileError(tokens.getTokenLine(index),
215 									"script-defined functions can not receive arguments of type void"));
216 						}
217 						// now the arg_name
218 						arg.argName = tokens.tokens[index].token;
219 						commaExpected = true;
220 						// add it to list
221 						argList.append (arg);
222 					}
223 				}
224 				// put args in list
225 				functionNode.arguments = argList.toArray;
226 				.destroy(argList);
227 				index = brackEnd+1;
228 			}
229 			if (tokens.tokens[index].type == Token.Type.BlockStart){
230 				// no args, just body
231 				functionNode.bodyBlock = generateBlockAST();
232 				// if there was a semicolon after function definition, skip that too
233 				if (index +1 < tokens.tokens.length && tokens.tokens[index].type == Token.Type.StatementEnd)
234 					index ++;
235 			}else{
236 				compileErrors.append(CompileError(tokens.getTokenLine(index), "function has no body"));
237 			}
238 		}else{
239 			compileErrors.append(CompileError(tokens.getTokenLine(index), "not a function definition"));
240 			index ++;
241 		}
242 		return functionNode;
243 	}
244 	
245 	/// generates AST for a {block-of-code}
246 	/// 
247 	/// changes `index` to token after block end
248 	BlockNode generateBlockAST(){
249 		BlockNode blockNode;
250 		blockNode.lineno = tokens.getTokenLine(index);
251 		// make sure it's a block
252 		if (tokens.tokens[index].type == Token.Type.BlockStart){
253 			uinteger brackEnd = tokens.tokens.tokenBracketPos!(true)(index);
254 			LinkedList!StatementNode statements = new LinkedList!StatementNode;
255 			// read statements
256 			index++;
257 			uinteger errorCount = compileErrors.count;
258 			while (index < brackEnd){
259 				statements.append(generateStatementAST());
260 				// check if error added
261 				if (errorCount != compileErrors.count){
262 					break;
263 				}
264 			}
265 			// put them all in block
266 			blockNode.statements = statements.toArray;
267 			.destroy(statements);
268 			index = brackEnd+1;
269 		}else{
270 			compileErrors.append(CompileError(tokens.getTokenLine(index), "not a block"));
271 			index ++;
272 		}
273 		return blockNode;
274 	}
275 
276 	/// generates AST for one statement
277 	StatementNode generateStatementAST(){
278 		// identify the type, call the appropriate function
279 		if (tokens.tokens[index].type == Token.Type.BlockStart){
280 			// block
281 			return StatementNode(generateBlockAST());
282 		}else if (tokens.tokens[index].type == Token.Type.Keyword){
283 			if (tokens.tokens[index].token == "if"){
284 				// if statement
285 				return StatementNode(generateIfAST());
286 			}else if (tokens.tokens[index].token == "while"){
287 				// while statement
288 				return StatementNode(generateWhileAST());
289 			}else if (tokens.tokens[index].token == "for"){
290 				// for statement
291 				return StatementNode(generateForAST());
292 			}else if (tokens.tokens[index].token == "do"){
293 				// do while
294 				return StatementNode(generateDoWhileAST());
295 			}else if (tokens.tokens[index].token == "return"){
296 				// return statement
297 				return StatementNode(generateReturnAST());
298 			}else if (tokens.tokens[index].token == "var"){
299 				// var declare
300 				return StatementNode(generateVarDeclareAST());
301 			}
302 		}else if (tokens.tokens[index].type == Token.Type.Identifier &&
303 			tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
304 			// is a function call
305 			return StatementNode(generateFunctionCallAST());
306 		}else{
307 			// could be an assignment statement, could be something.something.something(); function call
308 			// use generateCodeAST to read lvalue, or in case of later, will read whole thing
309 			CodeNode lvalue = generateCodeAST();
310 			if (tokens.tokens[index].type == Token.Type.StatementEnd){
311 				// just a something.something() function call, but still, make sure
312 				if (lvalue.type == CodeNode.Type.FunctionCall){
313 					index ++;
314 					return StatementNode(lvalue.node!(CodeNode.Type.FunctionCall));
315 				}
316 			}else if (tokens.tokens[index].type == Token.Type.AssignmentOperator){
317 				return StatementNode(generateAssignmentAST(lvalue));
318 			}
319 		}
320 		compileErrors.append (CompileError(tokens.getTokenLine(index), "invalid statement"));
321 		index ++;
322 		return StatementNode();
323 	}
324 
325 	/// generates AST for function call, changes `index` to token after statementEnd
326 	FunctionCallNode generateFunctionCallAST(){
327 		FunctionCallNode functionCallNode;
328 		functionCallNode.lineno = tokens.getTokenLine(index);
329 		// check if is function call
330 		if (tokens.tokens[index].type == Token.Type.Identifier &&
331 			tokens.tokens[index + 1].type == Token.Type.ParanthesesOpen){
332 			uinteger brackEnd = tokens.tokens.tokenBracketPos(index + 1);
333 			functionCallNode.fName = tokens.tokens[index].token;
334 			// now for the arguments
335 			index+=2;
336 			if (tokens.tokens[index].type == Token.Type.ParanthesesClose){
337 				// has no args
338 				functionCallNode.arguments = [];
339 			}else{
340 				LinkedList!CodeNode args = new LinkedList!CodeNode;
341 				for (; ; index ++){
342 					args.append(generateCodeAST());
343 					if (tokens.tokens[index].type == Token.Type.ParanthesesClose){
344 						break;
345 					}
346 				}
347 				functionCallNode.arguments = args.toArray;
348 				.destroy(args);
349 			}
350 			// move index to bracketend or semicolon
351 			index = brackEnd+1;
352 			if (tokens.tokens[index].type == Token.Type.StatementEnd){
353 				index ++;
354 			}
355 			return functionCallNode;
356 		}else{
357 			compileErrors.append(CompileError(tokens.getTokenLine(index), "not a valid function call"));
358 			index ++;
359 		}
360 		return functionCallNode;
361 	}
362 	
363 	/// generates AST for "actual code" like `2 + 2 - 6`.
364 	/// 
365 	/// terminates reading when on a Token that is comma, paranthesesEnd, semicolon, index/block bracket close
366 	CodeNode generateCodeAST(){
367 		CodeNode lastNode = null;
368 		bool separatorExpected = false;
369 		for ( ; ; index ++){
370 			Token token = tokens.tokens[index];
371 			// check if has to terminate
372 			if ([Token.Type.Comma, Token.Type.IndexBracketClose, Token.Type.BlockEnd, Token.Type.ParanthesesClose,
373 				Token.Type.StatementEnd, Token.Type.AssignmentOperator].hasElement(token.type)){
374 				break;
375 			}
376 			if (!separatorExpected){
377 				// an identifier, or literal (i.e some data) was expected
378 				lastNode = generateNodeAST();
379 				index --;
380 				separatorExpected = true;
381 			}else{
382 				// an operator or something that deals with data was expected.
383 				// check if there's an [...] to read the array
384 				if (token.type == Token.Type.IndexBracketOpen){
385 					// read the index
386 					uinteger brackStartIndex = index;
387 					uinteger brackEnd = tokens.tokens.tokenBracketPos(index);
388 					index ++;
389 					CodeNode indexNode = generateCodeAST();
390 					// make sure it's a read-element, not a make-array
391 					if (index != brackEnd){
392 						compileErrors.append (CompileError(tokens.getTokenLine(index), "unexpected tokens"));
393 						index++;
394 						break;
395 					}
396 					lastNode = CodeNode(ReadElement(lastNode, indexNode));
397 					lastNode.lineno = tokens.getTokenLine(brackStartIndex);
398 					continue;
399 				}
400 				separatorExpected = false;
401 				if (token.type == Token.Type.Operator){
402 					lastNode = CodeNode(generateOperatorAST(lastNode));
403 					index --;
404 					// in case of operators, generateOperatorAST takes the 2nd operand (!separator), itself, so the next
405 					// token, if any, is to be a separator
406 					separatorExpected = true;
407 					continue;
408 				}else{
409 					compileErrors.append(CompileError(tokens.getTokenLine(index), "Unexpected token '"~token.token~'\''));
410 					break;
411 				}
412 			}
413 		}
414 		return lastNode;
415 	}
416 
417 	/// generates AST for `-x` where x is a codenode
418 	NegativeValueNode generateNegativeNode(){
419 		if (tokens.tokens[index].type != Token.Type.Operator || tokens.tokens[index].token != "-"){
420 			compileErrors.append(CompileError(tokens.getTokenLine(index), "Not a negative node"));
421 			index ++;
422 			return NegativeValueNode(CodeNode());
423 		}
424 		NegativeValueNode r;
425 		index ++;
426 		CodeNode val = generateCodeAST();
427 		r = NegativeValueNode(val);
428 		return r;
429 	}
430 	
431 	/// generates AST for operators like +, -...
432 	/// 
433 	/// `firstOperand` is the first operand for the operator.
434 	/// `index` is the index of the token which is the operator
435 	/// 
436 	/// changes `index` to the index of the token after the last token related to the operator
437 	OperatorNode generateOperatorAST(CodeNode firstOperand){
438 		OperatorNode operator;
439 		operator.lineno = tokens.getTokenLine(index);
440 		// make sure it's an operator, and there is a second operand
441 		if (tokens.tokens[index].type == Token.Type.Operator && OPERATORS.hasElement(tokens.tokens[index].token) &&
442 			index+1 < tokens.tokens.length){
443 			// read the next operand
444 			CodeNode secondOperand;
445 			string operatorString = tokens.tokens[index].token;
446 			index ++;
447 			secondOperand = generateNodeAST();
448 			// put both operands under one node
449 			operator = OperatorNode(operatorString, firstOperand, secondOperand);
450 		}else{
451 			compileErrors.append(CompileError(tokens.getTokenLine(index), "no second operand found"));
452 			index ++;
453 		}
454 		return operator;
455 	}
456 
457 	/// generates AST for single operand operators
458 	SOperatorNode generateSOperatorAST(){
459 		SOperatorNode operator;
460 		operator.lineno = tokens.getTokenLine(index);
461 		// make sure its a single operand operator
462 		if (tokens.tokens[index].type == Token.Type.Operator && SOPERATORS.hasElement(tokens.tokens[index].token)){
463 			// read the operator
464 			operator.operator = tokens.tokens[index].token;
465 			index++;
466 			// and the operand
467 			operator.operand = generateNodeAST();
468 		}else{
469 			compileErrors.append(CompileError(tokens.getTokenLine(index), "not an operator"));
470 		}
471 		return operator;
472 	}
473 	/// generates AST for a variable (or array) and changes value of index to the token after variable ends
474 	VariableNode generateVariableAST(){
475 		VariableNode var;
476 		var.lineno = tokens.getTokenLine(index);
477 		// make sure first token is identifier
478 		if (tokens.tokens[index].type == Token.Type.Identifier){
479 			// set var name
480 			var.varName = tokens.tokens[index].token;
481 			index ++;
482 		}else{
483 			compileErrors.append(CompileError(tokens.getTokenLine(index), "not a variable"));
484 			index ++;
485 		}
486 		return var;
487 	}
488 	
489 	/// generates AST for assignment operator
490 	AssignmentNode generateAssignmentAST(CodeNode lvalue){
491 		AssignmentNode assignment;
492 		assignment.lineno = tokens.getTokenLine(index);
493 		// if lvalue is being read through a @ operator, mark assignment.deref as true
494 		if (lvalue.type == CodeNode.Type.SOperator){
495 			SOperatorNode opNode = lvalue.node!(CodeNode.Type.SOperator);
496 			if (opNode.operator == "@"){
497 				assignment.deref = true;
498 				lvalue = opNode.operand;
499 			}
500 		}
501 		// now at index, the token should be a `=` operator
502 		if (tokens.tokens[index].type == Token.Type.AssignmentOperator){
503 			// everything's ok till the `=` operator
504 			index++;
505 			CodeNode rvalue = generateCodeAST();
506 			assignment.lvalue = lvalue;
507 			assignment.rvalue = rvalue;
508 			// make sure it's followed by a semicolon
509 			if (tokens.tokens[index].type != Token.Type.StatementEnd){
510 				compileErrors.append(CompileError(tokens.getTokenLine(index),
511 						"assingment statement not followed by semicolon"));
512 			}else{
513 				// skip the semicolon too
514 				index++;
515 			}
516 		}else{
517 			compileErrors.append(CompileError(tokens.getTokenLine(index), "not an assignment statement"));
518 			index ++;
519 		}
520 		return assignment;
521 	}
522 	
523 	/// generates AST for variable declarations
524 	VarDeclareNode generateVarDeclareAST(){
525 		VarDeclareNode varDeclare;
526 		varDeclare.lineno = tokens.getTokenLine(index);
527 		// make sure it's a var declaration, and the vars are enclosed in parantheses
528 		if (tokens.tokens[index] == Token(Token.Type.Keyword, "var")){
529 			index ++;
530 			// read the type
531 			try{
532 				varDeclare.type = readType();
533 				if (!AVAILABLE_DATA_TYPES.hasElement(varDeclare.type.type) && DATA_TYPES.hasElement(varDeclare.type.typeName))
534 					throw new Exception("Data type "~varDeclare.type.typeName~" is currently not supported");
535 			}catch(Exception e){
536 				compileErrors.append(CompileError(tokens.getTokenLine(index), e.msg));
537 				.destroy (e);
538 			}
539 			while (tokens.tokens[index].type != Token.Type.StatementEnd && index < tokens.tokens.length){
540 				if (tokens.tokens[index].type == Token.Type.Identifier){
541 					string varName = tokens.tokens[index].token;
542 					// see if it has a assigned value, case not, skip to next
543 					if (tokens.tokens[index+1].type == Token.Type.AssignmentOperator){
544 						index += 2;
545 						CodeNode value = generateCodeAST();
546 						varDeclare.addVar(varName, value);
547 					}else{
548 						varDeclare.addVar(varName);
549 						index ++;
550 					}
551 					// now there must be a comma
552 					if (tokens.tokens[index].type == Token.Type.Comma){
553 						index ++;
554 					}else if (tokens.tokens[index].type != Token.Type.StatementEnd){
555 						compileErrors.append (CompileError(tokens.getTokenLine(index), 
556 						"variable names must be separated by a comma"));
557 					}
558 					continue;
559 				}else{
560 					compileErrors.append (CompileError(tokens.getTokenLine(index), "variable name expected"));
561 					// jump to end of statement
562 					while (tokens.tokens[index].type != Token.Type.StatementEnd && index < tokens.tokens.length){
563 						index ++;
564 					}
565 					break;
566 				}
567 			}
568 			// skip the semicolon
569 			if (tokens.tokens[index].type == Token.Type.StatementEnd){
570 				index ++;
571 			}
572 		}else{
573 			compileErrors.append(CompileError(tokens.getTokenLine(index), "invalid variable declaration"));
574 			index ++;
575 		}
576 		return varDeclare;
577 	}
578 	
579 	/// generates AST for if statements
580 	IfNode generateIfAST(){
581 		IfNode ifNode;
582 		ifNode.lineno = tokens.getTokenLine(index);
583 		// check if is an if
584 		if (tokens.tokens[index].type == Token.Type.Keyword && tokens.tokens[index].token == "if" &&
585 			tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
586 			// now do the real work
587 			uinteger brackEnd = tokens.tokens.tokenBracketPos(index+1);
588 			index += 2;
589 			ifNode.condition = generateCodeAST();
590 			// make sure index & brackEnd are now same
591 			if (index == brackEnd){
592 				index = brackEnd+1;
593 				ifNode.statement = generateStatementAST();
594 				ifNode.hasElse = false;
595 				// check if there's any else statement
596 				if (tokens.tokens[index].type == Token.Type.Keyword && tokens.tokens[index].token == "else"){
597 					// add that as well
598 					index ++;
599 					ifNode.elseStatement = generateStatementAST();
600 					ifNode.hasElse = true;
601 				}
602 			}else{
603 				compileErrors.append(CompileError(tokens.getTokenLine(index), "syntax error in condition"));
604 			}
605 		}else{
606 			compileErrors.append(CompileError(tokens.getTokenLine(index), "not a valid if/while statement"));
607 			index ++;
608 		}
609 		return ifNode;
610 	}
611 
612 	/// generates AST for while statements
613 	WhileNode generateWhileAST(){
614 		WhileNode whileNode;
615 		whileNode.lineno = tokens.getTokenLine(index);
616 		// check if is an if
617 		if (tokens.tokens[index].type == Token.Type.Keyword && tokens.tokens[index].token == "while" &&
618 			tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
619 			// now do the real work
620 			uinteger brackEnd = tokens.tokens.tokenBracketPos!true(index+1);
621 			index += 2;
622 			whileNode.condition = generateCodeAST();
623 			// skip the brackEnd, if index matches it
624 			if (index == brackEnd){
625 				index++;
626 				whileNode.statement = generateStatementAST();
627 			}else{
628 				compileErrors.append(CompileError(tokens.getTokenLine(index), "syntax error in condition"));
629 			}
630 		}else{
631 			compileErrors.append(CompileError(tokens.getTokenLine(index), "not a valid if/while statement"));
632 			index ++;
633 		}
634 		return whileNode;
635 	}
636 
637 	/// generates AST for for loop statements
638 	ForNode generateForAST(){
639 		ForNode forNode;
640 		forNode.lineno = tokens.getTokenLine(index);
641 		// check if is a for statement
642 		if (tokens.tokens[index] == Token(Token.Type.Keyword, "for") && tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
643 			/// where the parantheses ends
644 			uinteger bracketEnd = tokens.tokens.tokenBracketPos(index+1);
645 			/// get the init statement
646 			index = index + 2;
647 			forNode.initStatement = generateStatementAST();
648 			/// get the condition
649 			forNode.condition = generateCodeAST();
650 			/// make sure there's a semicolon
651 			if (tokens.tokens[index].type != Token.Type.StatementEnd){
652 				compileErrors.append(CompileError(tokens.getTokenLine(index), "semicolon expected after for loop condition"));
653 			}
654 			index ++;
655 			/// get the increment statement
656 			forNode.incStatement = generateStatementAST();
657 			if (index == bracketEnd){
658 				/// now for the for loop body
659 				index ++;
660 				forNode.statement = generateStatementAST();
661 			}else{
662 				compileErrors.append(CompileError(tokens.getTokenLine(index), "closing parantheses expected after statement"));
663 			}
664 		}
665 		return forNode;
666 	}
667 
668 	/// generates AST for do-while loops statements
669 	DoWhileNode generateDoWhileAST(){
670 		DoWhileNode doWhile;
671 		doWhile.lineno = tokens.getTokenLine(index);
672 		if (tokens.tokens[index] == Token(Token.Type.Keyword, "do")){
673 			// get the statement
674 			index ++;
675 			doWhile.statement = generateStatementAST();
676 			// now make sure there's a while there
677 			if (tokens.tokens[index] == Token(Token.Type.Keyword, "while") &&
678 				tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
679 				// read the condition
680 				uinteger brackEnd = tokens.tokens.tokenBracketPos(index+1);
681 				index += 2;
682 				doWhile.condition = generateCodeAST();
683 				if (index != brackEnd){
684 					compileErrors.append (CompileError(tokens.getTokenLine(index), "syntax error"));
685 				}else{
686 					// skip the bracket end
687 					index ++;
688 					// the semicolon after `do ... while(...)` is optional, so if it's there, skip it
689 					if (tokens.tokens[index].type == Token.Type.StatementEnd){
690 						index ++;
691 					}
692 				}
693 			}else{
694 				compileErrors.append (CompileError(tokens.getTokenLine(index),
695 						"while followed by condition expected after end of loop statement"));
696 			}
697 		}
698 		return doWhile;
699 	}
700 
701 	/// returns a node representing a return statement
702 	ReturnNode generateReturnAST(){
703 		if (tokens.tokens[index].token != "return"){
704 			compileErrors.append(CompileError(tokens.getTokenLine(index), "not a return statement"));
705 		}else{
706 			ReturnNode r;
707 			r.lineno = tokens.getTokenLine(index);
708 			index ++;
709 			r.value = generateCodeAST();
710 			if (tokens.tokens[index].type == Token.Type.StatementEnd){
711 				index ++; //skip semicolon
712 			}else{
713 				compileErrors.append(CompileError(tokens.getTokenLine(index),"semicolon expected"));
714 			}
715 			return r;
716 		}
717 		return ReturnNode();
718 	}
719 
720 	/// returns a node representing either of the following:
721 	/// 1. String literal
722 	/// 2. Number literal
723 	/// 3. Function Call (uses `generateFunctionCallAST`)
724 	/// 4. Variable (uses `generateVariableAST`)
725 	/// 5. Some code inside parantheses (uses `generateCodeAST`)
726 	/// 6. A literal array (`[x, y, z]`)
727 	/// 
728 	/// This function is used by `generateCodeAST` to separate nodes, and by `generateOperatorAST` to read operands
729 	/// 
730 	/// set `skipPost` to true in case you do not want it to read into `[...]` or `.` after the Node
731 	CodeNode generateNodeAST(CodeNode r = CodeNode()){
732 		Token token = tokens.tokens[index];
733 		// an identifier, or literal (i.e some data) was expected
734 		if (token.type == Token.Type.Identifier){
735 			if (index+1 < tokens.tokens.length && tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
736 				// is a function call
737 				r = CodeNode(generateFunctionCallAST());
738 				// check if it skipped the semicolon, because if it did, it shouldnt have, so undo it
739 				if (tokens.tokens[index-1].type == Token.Type.StatementEnd){
740 					index --;
741 				}
742 			}else{
743 				// just a var
744 				r = CodeNode(generateVariableAST());
745 			}
746 		}else if (token.type == Token.Type.Operator && SOPERATORS.hasElement(token.token)){
747 			r = CodeNode(generateSOperatorAST());
748 		}else if (token.type == Token.Type.Operator && token.token == "-"){
749 			r = CodeNode(generateNegativeNode());
750 		}else if (token.type == Token.Type.ParanthesesOpen){
751 			// some code
752 			index ++;
753 			r = generateCodeAST();
754 			index ++;
755 		}else if (token.type == Token.Type.IndexBracketOpen){
756 			// literal array
757 			uinteger brackEnd = tokens.tokens.tokenBracketPos(index);
758 			// read into ArrayNode
759 			CodeNode[] elements = [];
760 			index ++;
761 			for (; index < brackEnd; index ++){
762 				elements = elements ~ generateCodeAST();
763 				if (tokens.tokens[index].type != Token.Type.Comma && index != brackEnd){
764 					compileErrors.append (CompileError (tokens.getTokenLine(index),
765 							"Unexpected token, comma must be used to separate array elements"));
766 					index = brackEnd;
767 				}
768 			}
769 			index = brackEnd+1;
770 			r = CodeNode(ArrayNode(elements));
771 		}else if (token.type == Token.Type.Double || token.type == Token.Type.Integer || token.type == Token.Type.String ||
772 		token.type == Token.Type.Char || token.type == Token.Type.Bool){
773 			// literal
774 			index ++;
775 			try{
776 				LiteralNode ltNode = LiteralNode(token);
777 				ltNode.lineno = tokens.getTokenLine(index);
778 				r = CodeNode(ltNode);
779 			}catch (Exception e){
780 				compileErrors.append(CompileError(tokens.getTokenLine(index), e.msg));
781 				.destroy (e);
782 			}
783 		}else if (token.type == Token.Type.Keyword && token.token == "null"){
784 			r = CodeNode(LiteralNode(token.token,DataType(DataType.Type.Void, 0, true)));
785 			index ++;
786 		}else{
787 			index ++;
788 			compileErrors.append(CompileError(tokens.getTokenLine(index), "unexpected token"));
789 		}
790 		// check if theres a [] or MemberSelector ahead of it to read it an element
791 		while (tokens.tokens[index].type == Token.Type.IndexBracketOpen ||
792 		tokens.tokens[index].type == Token.Type.MemberSelector){
793 			if (tokens.tokens[index].type == Token.Type.IndexBracketOpen){
794 				// skip the opening bracket `[`
795 				index ++;
796 				r = CodeNode(ReadElement(r, generateCodeAST()));
797 				if (tokens.tokens[index].type == Token.Type.IndexBracketClose)
798 					index ++; // skip the ] bracket
799 			}else if (tokens.tokens[index].type == Token.Type.MemberSelector){
800 				// read just the member name
801 				immutable uinteger lineno = tokens.getTokenLine(index);
802 				index ++;
803 				if (tokens.tokens[index].type != Token.Type.Identifier){
804 					compileErrors.append(CompileError(tokens.getTokenLine(index),
805 							"Expected identifier after member selector operator"));
806 				}else{
807 					// check if is a function call
808 					if (index + 1 < tokens.tokens.length && tokens.tokens[index+1].type == Token.Type.ParanthesesOpen){
809 						//index ++;
810 						FunctionCallNode fCall = generateFunctionCallAST();
811 						fCall.arguments = r ~ fCall.arguments;
812 						r = CodeNode(fCall);
813 						if (tokens.tokens[index-1].type == Token.Type.StatementEnd){
814 							index --;
815 							break;
816 						}
817 					}
818 					r = CodeNode(MemberSelectorNode(r, tokens.tokens[index].token, lineno));
819 					index ++;
820 				}
821 			}
822 		}
823 		
824 		return r;
825 	}
826 	/// generates LiteralNode
827 	LiteralNode generateLiteralAST(){
828 		LiteralNode r;
829 		if ([Token.Type.Double,Token.Type.Integer,Token.Type.String,Token.Type.Char].hasElement(tokens.tokens[index].type)){
830 			try{
831 				r = LiteralNode(tokens.tokens[index]);
832 				r.lineno = tokens.getTokenLine(index);
833 			}catch (Exception e){
834 				compileErrors.append(CompileError(tokens.getTokenLine(index), e.msg));
835 				.destroy(e);
836 			}
837 		}else{
838 			compileErrors.append(CompileError(tokens.getTokenLine(index), "literal expected"));
839 		}
840 		index ++;
841 		return r;
842 	}
843 public:
844 	/// Constructor
845 	this(){
846 		compileErrors = new LinkedList!CompileError;
847 	}
848 	~this(){
849 		.destroy(compileErrors);
850 	}
851 	/// Returns: errors occured during AST generation
852 	@property CompileError[] errors(){
853 		return compileErrors.toArray();
854 	}
855 	/// generates an AST representing a script.
856 	ScriptNode generateScriptAST(TokenList scriptTokens){
857 		tokens = scriptTokens;
858 		tokens.tokens = tokens.tokens.dup;
859 		tokens.tokenPerLine = tokens.tokenPerLine.dup;
860 		ScriptNode scriptNode;
861 		compileErrors.clear();
862 		// go through the script, compile function nodes, link them to this node
863 		index = 0;
864 		for (immutable uinteger lastIndex = tokens.tokens.length - 1; index < tokens.tokens.length; index ++){
865 			// look for visibility specifier, or default to private
866 			Visibility vis = Visibility.Private;
867 			if (tokens.tokens[index].type == Token.Type.Keyword && VISIBILITY_SPECIFIERS.hasElement(tokens.tokens[index].token)){
868 				vis = visibility(tokens.tokens[index].token);
869 				index ++;
870 			}
871 			// look for TokenType.Keyword where token.token == "function"
872 			if (tokens.tokens[index].type == Token.Type.Keyword){
873 				immutable uinteger errorCount = compileErrors.count;
874 				if (tokens.tokens[index].token == "import"){
875 					string[] imports = readImports();
876 					scriptNode.imports ~= imports;
877 					index --; // don't wanna skip the semicolon, for (..; index ++) does that
878 				}else if (tokens.tokens[index].token == "function"){
879 					FunctionNode functionNode = generateFunctionAST();
880 					functionNode.visibility = vis;
881 					index --;
882 					// check if error free
883 					if (compileErrors.count == errorCount){
884 						scriptNode.functions ~= functionNode;
885 					}
886 				}else if (tokens.tokens[index].token == "struct"){
887 					StructNode structNode = generateStructAST();
888 					structNode.visibility = vis;
889 					index --;
890 					if (compileErrors.count == errorCount){
891 						scriptNode.structs ~= structNode;
892 					}
893 				}else if (tokens.tokens[index].token == "enum"){
894 					EnumNode enumNode = generateEnumAST();
895 					enumNode.visibility = vis;
896 					index --;
897 					if (compileErrors.count == errorCount){
898 						scriptNode.enums ~= enumNode;
899 					}
900 				}else if (tokens.tokens[index].token == "var"){
901 					VarDeclareNode varDecNode = generateVarDeclareAST();
902 					varDecNode.visibility = vis;
903 					index --;
904 					if (compileErrors.count == errorCount){
905 						scriptNode.variables ~= varDecNode;
906 					}
907 				}
908 			}
909 		}
910 		return scriptNode;
911 	}
912 }