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 }