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 }