1 module wren.compiler; 2 import wren.common; 3 import wren.dbg; 4 import wren.opcodes; 5 import wren.utils; 6 import wren.value; 7 import wren.vm; 8 9 // So we can throw errors without statically allocating them 10 import dplug.core : mallocNew; 11 12 @nogc: 13 // This is written in bottom-up order, so the tokenization comes first, then 14 // parsing/code generation. This minimizes the number of explicit forward 15 // declarations needed. 16 17 // The maximum number of local (i.e. not module level) variables that can be 18 // declared in a single function, method, or chunk of top level code. This is 19 // the maximum number of variables in scope at one time, and spans block scopes. 20 // 21 // Note that this limitation is also explicit in the bytecode. Since 22 // `CODE_LOAD_LOCAL` and `CODE_STORE_LOCAL` use a single argument byte to 23 // identify the local, only 256 can be in scope at one time. 24 enum MAX_LOCALS = 256; 25 26 // The maximum number of upvalues (i.e. variables from enclosing functions) 27 // that a function can close over. 28 enum MAX_UPVALUES = 256; 29 30 // The maximum number of distinct constants that a function can contain. This 31 // value is explicit in the bytecode since `CODE_CONSTANT` only takes a single 32 // two-byte argument. 33 enum MAX_CONSTANTS = (1 << 16); 34 35 // The maximum distance a CODE_JUMP or CODE_JUMP_IF instruction can move the 36 // instruction pointer. 37 enum MAX_JUMP = (1 << 16); 38 39 // The maximum depth that interpolation can nest. For example, this string has 40 // three levels: 41 // 42 // "outside %(one + "%(two + "%(three)")")" 43 enum MAX_INTERPOLATION_NESTING = 8; 44 45 // The buffer size used to format a compile error message, excluding the header 46 // with the module name and error location. Using a hardcoded buffer for this 47 // is kind of hairy, but fortunately we can control what the longest possible 48 // message is and handle that. Ideally, we'd use `snprintf()`, but that's not 49 // available in standard C++98. 50 enum ERROR_MESSAGE_SIZE = 80 + MAX_VARIABLE_NAME + 15; 51 52 enum TokenType 53 { 54 TOKEN_LEFT_PAREN, 55 TOKEN_RIGHT_PAREN, 56 TOKEN_LEFT_BRACKET, 57 TOKEN_RIGHT_BRACKET, 58 TOKEN_LEFT_BRACE, 59 TOKEN_RIGHT_BRACE, 60 TOKEN_COLON, 61 TOKEN_DOT, 62 TOKEN_DOTDOT, 63 TOKEN_DOTDOTDOT, 64 TOKEN_COMMA, 65 TOKEN_STAR, 66 TOKEN_SLASH, 67 TOKEN_PERCENT, 68 TOKEN_HASH, 69 TOKEN_PLUS, 70 TOKEN_MINUS, 71 TOKEN_LTLT, 72 TOKEN_GTGT, 73 TOKEN_PIPE, 74 TOKEN_PIPEPIPE, 75 TOKEN_CARET, 76 TOKEN_AMP, 77 TOKEN_AMPAMP, 78 TOKEN_BANG, 79 TOKEN_TILDE, 80 TOKEN_QUESTION, 81 TOKEN_EQ, 82 TOKEN_LT, 83 TOKEN_GT, 84 TOKEN_LTEQ, 85 TOKEN_GTEQ, 86 TOKEN_EQEQ, 87 TOKEN_BANGEQ, 88 89 TOKEN_BREAK, 90 TOKEN_CONTINUE, 91 TOKEN_CLASS, 92 TOKEN_CONSTRUCT, 93 TOKEN_ELSE, 94 TOKEN_FALSE, 95 TOKEN_FOR, 96 TOKEN_FOREIGN, 97 TOKEN_IF, 98 TOKEN_IMPORT, 99 TOKEN_AS, 100 TOKEN_IN, 101 TOKEN_IS, 102 TOKEN_NULL, 103 TOKEN_RETURN, 104 TOKEN_STATIC, 105 TOKEN_SUPER, 106 TOKEN_THIS, 107 TOKEN_TRUE, 108 TOKEN_VAR, 109 TOKEN_WHILE, 110 111 TOKEN_FIELD, 112 TOKEN_STATIC_FIELD, 113 TOKEN_NAME, 114 TOKEN_NUMBER, 115 116 // A string literal without any interpolation, or the last section of a 117 // string following the last interpolated expression. 118 TOKEN_STRING, 119 120 // A portion of a string literal preceding an interpolated expression. This 121 // string: 122 // 123 // "a %(b) c %(d) e" 124 // 125 // is tokenized to: 126 // 127 // TOKEN_INTERPOLATION "a " 128 // TOKEN_NAME b 129 // TOKEN_INTERPOLATION " c " 130 // TOKEN_NAME d 131 // TOKEN_STRING " e" 132 TOKEN_INTERPOLATION, 133 134 TOKEN_LINE, 135 136 TOKEN_ERROR, 137 TOKEN_EOF 138 } 139 140 struct Token 141 { 142 TokenType type; 143 144 // The beginning of the token, pointing directly into the source. 145 const(char)* start; 146 147 // The length of the token in characters. 148 int length; 149 150 // The 1-based line where the token appears. 151 int line; 152 153 // The parsed value if the token is a literal. 154 Value value; 155 } 156 157 struct Parser 158 { 159 WrenVM* vm; 160 161 // The module being parsed. 162 ObjModule* module_; 163 164 // The source code being parsed. 165 const(char)* source; 166 167 // The beginning of the currently-being-lexed token in [source]. 168 const(char)* tokenStart; 169 170 // The current character being lexed in [source]. 171 const(char)* currentChar; 172 173 // The 1-based line number of [currentChar]. 174 int currentLine; 175 176 // The upcoming token. 177 Token next; 178 179 // The most recently lexed token. 180 Token current; 181 182 // The most recently consumed/advanced token. 183 Token previous; 184 185 // Tracks the lexing state when tokenizing interpolated strings. 186 // 187 // Interpolated strings make the lexer not strictly regular: we don't know 188 // whether a ")" should be treated as a RIGHT_PAREN token or as ending an 189 // interpolated expression unless we know whether we are inside a string 190 // interpolation and how many unmatched "(" there are. This is particularly 191 // complex because interpolation can nest: 192 // 193 // " %( " %( inner ) " ) " 194 // 195 // This tracks that state. The parser maintains a stack of ints, one for each 196 // level of current interpolation nesting. Each value is the number of 197 // unmatched "(" that are waiting to be closed. 198 int[MAX_INTERPOLATION_NESTING] parens; 199 int numParens; 200 201 // Whether compile errors should be printed to stderr or discarded. 202 bool printErrors; 203 204 // If a syntax or compile error has occurred. 205 bool hasError; 206 } 207 208 struct Local 209 { 210 // The name of the local variable. This points directly into the original 211 // source code string. 212 const(char)* name; 213 214 // The length of the local variable's name. 215 int length; 216 217 // The depth in the scope chain that this variable was declared at. Zero is 218 // the outermost scope--parameters for a method, or the first local block in 219 // top level code. One is the scope within that, etc. 220 int depth; 221 222 // If this local variable is being used as an upvalue. 223 bool isUpvalue; 224 } 225 226 struct CompilerUpvalue 227 { 228 // True if this upvalue is capturing a local variable from the enclosing 229 // function. False if it's capturing an upvalue. 230 bool isLocal; 231 232 // The index of the local or upvalue being captured in the enclosing function. 233 int index; 234 } 235 236 struct Loop 237 { 238 // Index of the instruction that the loop should jump back to. 239 int start; 240 241 // Index of the argument for the CODE_JUMP_IF instruction used to exit the 242 // loop. Stored so we can patch it once we know where the loop ends. 243 int exitJump; 244 245 // Index of the first instruction of the body of the loop. 246 int body_; 247 248 // Depth of the scope(s) that need to be exited if a break is hit inside the 249 // loop. 250 int scopeDepth; 251 252 // The loop enclosing this one, or null if this is the outermost loop. 253 Loop* enclosing; 254 } 255 256 // The different signature syntaxes for different kinds of methods. 257 enum SignatureType 258 { 259 // A name followed by a (possibly empty) parenthesized parameter list. Also 260 // used for binary operators. 261 SIG_METHOD, 262 263 // Just a name. Also used for unary operators. 264 SIG_GETTER, 265 266 // A name followed by "=". 267 SIG_SETTER, 268 269 // A square bracketed parameter list. 270 SIG_SUBSCRIPT, 271 272 // A square bracketed parameter list followed by "=". 273 SIG_SUBSCRIPT_SETTER, 274 275 // A constructor initializer function. This has a distinct signature to 276 // prevent it from being invoked directly outside of the constructor on the 277 // metaclass. 278 SIG_INITIALIZER 279 } 280 281 struct Signature 282 { 283 const(char)* name; 284 int length; 285 SignatureType type; 286 int arity; 287 } 288 289 struct ClassInfo 290 { 291 // The name of the class. 292 ObjString* name; 293 294 // Attributes for the class itself 295 ObjMap* classAttributes; 296 // Attributes for methods in this class 297 ObjMap* methodAttributes; 298 299 // Symbol table for the fields of the class. 300 SymbolTable fields; 301 302 // Symbols for the methods defined by the class. Used to detect duplicate 303 // method definitions. 304 IntBuffer methods; 305 IntBuffer staticMethods; 306 307 // True if the class being compiled is a foreign class. 308 bool isForeign; 309 310 // True if the current method being compiled is static. 311 bool inStatic; 312 313 // The signature of the method being compiled. 314 Signature* signature; 315 } 316 317 struct Compiler 318 { 319 Parser* parser; 320 321 // The compiler for the function enclosing this one, or null if it's the 322 // top level. 323 Compiler* parent; 324 325 // The currently in scope local variables. 326 Local[MAX_LOCALS] locals; 327 328 // The number of local variables currently in scope. 329 int numLocals; 330 331 // The upvalues that this function has captured from outer scopes. The count 332 // of them is stored in [numUpvalues]. 333 CompilerUpvalue[MAX_UPVALUES] upvalues; 334 335 // The current level of block scope nesting, where zero is no nesting. A -1 336 // here means top-level code is being compiled and there is no block scope 337 // in effect at all. Any variables declared will be module-level. 338 int scopeDepth; 339 340 // The current number of slots (locals and temporaries) in use. 341 // 342 // We use this and maxSlots to track the maximum number of additional slots 343 // a function may need while executing. When the function is called, the 344 // fiber will check to ensure its stack has enough room to cover that worst 345 // case and grow the stack if needed. 346 // 347 // This value here doesn't include parameters to the function. Since those 348 // are already pushed onto the stack by the caller and tracked there, we 349 // don't need to double count them here. 350 int numSlots; 351 352 // The current innermost loop being compiled, or null if not in a loop. 353 Loop* loop; 354 355 // If this is a compiler for a method, keeps track of the class enclosing it. 356 ClassInfo* enclosingClass; 357 358 // The function being compiled. 359 ObjFn* fn; 360 361 // The constants for the function being compiled. 362 ObjMap* constants; 363 364 // Whether or not the compiler is for a constructor initializer 365 bool isInitializer; 366 367 // The number of attributes seen while parsing. 368 // We track this separately as compile time attributes 369 // are not stored, so we can't rely on attributes.count 370 // to enforce an error message when attributes are used 371 // anywhere other than methods or classes. 372 int numAttributes; 373 // Attributes for the next class or method. 374 ObjMap* attributes; 375 } 376 377 // Describes where a variable is declared. 378 enum Scope 379 { 380 // A local variable in the current function. 381 SCOPE_LOCAL, 382 383 // A local variable declared in an enclosing function. 384 SCOPE_UPVALUE, 385 386 // A top-level module variable. 387 SCOPE_MODULE 388 } 389 390 // A reference to a variable and the scope where it is defined. This contains 391 // enough information to emit correct code to load or store the variable. 392 struct Variable 393 { 394 // The stack slot, upvalue slot, or module symbol defining the variable. 395 int index; 396 397 // Where the variable is declared. 398 Scope scope_; 399 } 400 401 import core.stdc.stdarg; 402 static void printError(Parser* parser, int line, const(char)* label, 403 const(char)* format, va_list args) 404 { 405 import core.stdc.stdio; 406 parser.hasError = true; 407 if (!parser.printErrors) return; 408 409 // Only report errors if there is a WrenErrorFn to handle them. 410 if (parser.vm.config.errorFn == null) return; 411 412 // Format the label and message. 413 char[ERROR_MESSAGE_SIZE] message; 414 int length = sprintf(message.ptr, "%s: ", label); 415 length += vsprintf(message.ptr + length, format, args); 416 417 if (length > ERROR_MESSAGE_SIZE) 418 throw mallocNew!Error("Error should not exceed buffer."); 419 // assert(length < ERROR_MESSAGE_SIZE, "Error should not exceed buffer."); 420 421 ObjString* module_ = parser.module_.name; 422 const(char)* module_name = module_ ? module_.value.ptr : "<unknown>"; 423 424 parser.vm.config.errorFn(parser.vm, WrenErrorType.WREN_ERROR_COMPILE, 425 module_name, line, message.ptr); 426 } 427 428 // Outputs a lexical error. 429 static void lexError(Parser* parser, const char* format, ...) 430 { 431 va_list args; 432 va_start(args, format); 433 printError(parser, parser.currentLine, "Error", format, args); 434 va_end(args); 435 } 436 437 // Outputs a compile or syntax error. This also marks the compilation as having 438 // an error, which ensures that the resulting code will be discarded and never 439 // run. This means that after calling error(), it's fine to generate whatever 440 // invalid bytecode you want since it won't be used. 441 // 442 // You'll note that most places that call error() continue to parse and compile 443 // after that. That's so that we can try to find as many compilation errors in 444 // one pass as possible instead of just bailing at the first one. 445 static void error(Compiler* compiler, const char* format, ...) 446 { 447 import core.stdc.stdio; 448 Token* token = &compiler.parser.previous; 449 450 // If the parse error was caused by an error token, the lexer has already 451 // reported it. 452 if (token.type == TokenType.TOKEN_ERROR) return; 453 454 va_list args; 455 va_start(args, format); 456 if (token.type == TokenType.TOKEN_LINE) 457 { 458 printError(compiler.parser, token.line, "Error at newline", format, args); 459 } 460 else if (token.type == TokenType.TOKEN_EOF) 461 { 462 printError(compiler.parser, token.line, 463 "Error at end of file", format, args); 464 } 465 else 466 { 467 // Make sure we don't exceed the buffer with a very long token. 468 char[10 + MAX_VARIABLE_NAME + 4 + 1] label; 469 if (token.length <= MAX_VARIABLE_NAME) 470 { 471 sprintf(label.ptr, "Error at '%.*s'", token.length, token.start); 472 } 473 else 474 { 475 sprintf(label.ptr, "Error at '%.*s...'", MAX_VARIABLE_NAME, token.start); 476 } 477 printError(compiler.parser, token.line, label.ptr, format, args); 478 } 479 va_end(args); 480 } 481 482 // Adds [constant] to the constant pool and returns its index. 483 static int addConstant(Compiler* compiler, Value constant) 484 { 485 if (compiler.parser.hasError) return -1; 486 487 // See if we already have a constant for the value. If so, reuse it. 488 if (compiler.constants != null) 489 { 490 Value existing = wrenMapGet(compiler.constants, constant); 491 if (IS_NUM(existing)) return cast(int)AS_NUM(existing); 492 } 493 494 // It's a new constant. 495 if (compiler.fn.constants.count < MAX_CONSTANTS) 496 { 497 if (IS_OBJ(constant)) wrenPushRoot(compiler.parser.vm, AS_OBJ(constant)); 498 wrenValueBufferWrite(compiler.parser.vm, &compiler.fn.constants, 499 constant); 500 if (IS_OBJ(constant)) wrenPopRoot(compiler.parser.vm); 501 502 if (compiler.constants == null) 503 { 504 compiler.constants = wrenNewMap(compiler.parser.vm); 505 } 506 wrenMapSet(compiler.parser.vm, compiler.constants, constant, 507 NUM_VAL(compiler.fn.constants.count - 1)); 508 } 509 else 510 { 511 error(compiler, "A function may only contain %d unique constants.", 512 MAX_CONSTANTS); 513 } 514 515 return compiler.fn.constants.count - 1; 516 } 517 518 // Initializes [compiler]. 519 static void initCompiler(Compiler* compiler, Parser* parser, Compiler* parent, 520 bool isMethod) 521 { 522 compiler.parser = parser; 523 compiler.parent = parent; 524 compiler.loop = null; 525 compiler.enclosingClass = null; 526 compiler.isInitializer = false; 527 528 // Initialize these to null before allocating in case a GC gets triggered in 529 // the middle of initializing the compiler. 530 compiler.fn = null; 531 compiler.constants = null; 532 compiler.attributes = null; 533 534 parser.vm.compiler = compiler; 535 536 // Declare a local slot for either the closure or method receiver so that we 537 // don't try to reuse that slot for a user-defined local variable. For 538 // methods, we name it "this", so that we can resolve references to that like 539 // a normal variable. For functions, they have no explicit "this", so we use 540 // an empty name. That way references to "this" inside a function walks up 541 // the parent chain to find a method enclosing the function whose "this" we 542 // can close over. 543 compiler.numLocals = 1; 544 compiler.numSlots = compiler.numLocals; 545 546 if (isMethod) 547 { 548 compiler.locals[0].name = "this"; 549 compiler.locals[0].length = 4; 550 } 551 else 552 { 553 compiler.locals[0].name = null; 554 compiler.locals[0].length = 0; 555 } 556 557 compiler.locals[0].depth = -1; 558 compiler.locals[0].isUpvalue = false; 559 560 if (parent == null) 561 { 562 // Compiling top-level code, so the initial scope is module-level. 563 compiler.scopeDepth = -1; 564 } 565 else 566 { 567 // The initial scope for functions and methods is local scope. 568 compiler.scopeDepth = 0; 569 } 570 571 compiler.numAttributes = 0; 572 compiler.attributes = wrenNewMap(parser.vm); 573 compiler.fn = wrenNewFunction(parser.vm, parser.module_, 574 compiler.numLocals); 575 } 576 577 // Lexing ---------------------------------------------------------------------- 578 579 struct Keyword 580 { 581 const(char)* identifier; 582 size_t length; 583 TokenType tokenType; 584 } 585 586 static immutable Keyword[] keywords = 587 [ 588 {"break", 5, TokenType.TOKEN_BREAK}, 589 {"continue", 8, TokenType.TOKEN_CONTINUE}, 590 {"class", 5, TokenType.TOKEN_CLASS}, 591 {"construct", 9, TokenType.TOKEN_CONSTRUCT}, 592 {"else", 4, TokenType.TOKEN_ELSE}, 593 {"false", 5, TokenType.TOKEN_FALSE}, 594 {"for", 3, TokenType.TOKEN_FOR}, 595 {"foreign", 7, TokenType.TOKEN_FOREIGN}, 596 {"if", 2, TokenType.TOKEN_IF}, 597 {"import", 6, TokenType.TOKEN_IMPORT}, 598 {"as", 2, TokenType.TOKEN_AS}, 599 {"in", 2, TokenType.TOKEN_IN}, 600 {"is", 2, TokenType.TOKEN_IS}, 601 {"null", 4, TokenType.TOKEN_NULL}, 602 {"return", 6, TokenType.TOKEN_RETURN}, 603 {"static", 6, TokenType.TOKEN_STATIC}, 604 {"super", 5, TokenType.TOKEN_SUPER}, 605 {"this", 4, TokenType.TOKEN_THIS}, 606 {"true", 4, TokenType.TOKEN_TRUE}, 607 {"var", 3, TokenType.TOKEN_VAR}, 608 {"while", 5, TokenType.TOKEN_WHILE}, 609 {null, 0, TokenType.TOKEN_EOF} // Sentinel to mark the end of the array. 610 ]; 611 612 // Returns true if [c] is a valid (non-initial) identifier character. 613 static bool isName(char c) 614 { 615 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'; 616 } 617 618 // Returns true if [c] is a digit. 619 static bool isDigit(char c) 620 { 621 return c >= '0' && c <= '9'; 622 } 623 624 // Returns the current character the parser is sitting on. 625 static char peekChar(Parser* parser) 626 { 627 return *parser.currentChar; 628 } 629 630 // Returns the character after the current character. 631 static char peekNextChar(Parser* parser) 632 { 633 // If we're at the end of the source, don't read past it. 634 if (peekChar(parser) == '\0') return '\0'; 635 return *(parser.currentChar + 1); 636 } 637 638 // Advances the parser forward one character. 639 static char nextChar(Parser* parser) 640 { 641 char c = peekChar(parser); 642 parser.currentChar++; 643 if (c == '\n') parser.currentLine++; 644 return c; 645 } 646 647 // If the current character is [c], consumes it and returns `true`. 648 static bool matchChar(Parser* parser, char c) 649 { 650 if (peekChar(parser) != c) return false; 651 nextChar(parser); 652 return true; 653 } 654 655 // Sets the parser's current token to the given [type] and current character 656 // range. 657 static void makeToken(Parser* parser, TokenType type) 658 { 659 parser.next.type = type; 660 parser.next.start = parser.tokenStart; 661 parser.next.length = cast(int)(parser.currentChar - parser.tokenStart); 662 parser.next.line = parser.currentLine; 663 664 // Make line tokens appear on the line containing the "\n". 665 if (type == TokenType.TOKEN_LINE) parser.next.line--; 666 } 667 668 // If the current character is [c], then consumes it and makes a token of type 669 // [two]. Otherwise makes a token of type [one]. 670 static void twoCharToken(Parser* parser, char c, TokenType two, TokenType one) 671 { 672 makeToken(parser, matchChar(parser, c) ? two : one); 673 } 674 675 // Skips the rest of the current line. 676 static void skipLineComment(Parser* parser) 677 { 678 while (peekChar(parser) != '\n' && peekChar(parser) != '\0') 679 { 680 nextChar(parser); 681 } 682 } 683 684 // Skips the rest of a block comment. 685 static void skipBlockComment(Parser* parser) 686 { 687 int nesting = 1; 688 while (nesting > 0) 689 { 690 if (peekChar(parser) == '\0') 691 { 692 lexError(parser, "Unterminated block comment."); 693 return; 694 } 695 696 if (peekChar(parser) == '/' && peekNextChar(parser) == '*') 697 { 698 nextChar(parser); 699 nextChar(parser); 700 nesting++; 701 continue; 702 } 703 704 if (peekChar(parser) == '*' && peekNextChar(parser) == '/') 705 { 706 nextChar(parser); 707 nextChar(parser); 708 nesting--; 709 continue; 710 } 711 712 // Regular comment character. 713 nextChar(parser); 714 } 715 } 716 717 // Reads the next character, which should be a hex digit (0-9, a-f, or A-F) and 718 // returns its numeric value. If the character isn't a hex digit, returns -1. 719 static int readHexDigit(Parser* parser) 720 { 721 char c = nextChar(parser); 722 if (c >= '0' && c <= '9') return c - '0'; 723 if (c >= 'a' && c <= 'f') return c - 'a' + 10; 724 if (c >= 'A' && c <= 'F') return c - 'A' + 10; 725 726 // Don't consume it if it isn't expected. Keeps us from reading past the end 727 // of an unterminated string. 728 parser.currentChar--; 729 return -1; 730 } 731 732 // Parses the numeric value of the current token. 733 static void makeNumber(Parser* parser, bool isHex) 734 { 735 import core.stdc.errno; 736 import core.stdc.stdlib; 737 738 errno = 0; 739 740 if (isHex) 741 { 742 parser.next.value = NUM_VAL(cast(double)strtoll(parser.tokenStart, null, 16)); 743 } 744 else 745 { 746 parser.next.value = NUM_VAL(strtod(parser.tokenStart, null)); 747 } 748 749 if (errno == ERANGE) 750 { 751 lexError(parser, "Number literal was too large (%d).", ulong.sizeof); 752 parser.next.value = NUM_VAL(0); 753 } 754 755 // We don't check that the entire token is consumed after calling strtoll() 756 // or strtod() because we've already scanned it ourselves and know it's valid. 757 758 makeToken(parser, TokenType.TOKEN_NUMBER); 759 } 760 761 // Finishes lexing a hexadecimal number literal. 762 static void readHexNumber(Parser* parser) 763 { 764 // Skip past the `x` used to denote a hexadecimal literal. 765 nextChar(parser); 766 767 // Iterate over all the valid hexadecimal digits found. 768 while (readHexDigit(parser) != -1) continue; 769 770 makeNumber(parser, true); 771 } 772 773 // Finishes lexing a number literal. 774 static void readNumber(Parser* parser) 775 { 776 while (isDigit(peekChar(parser))) nextChar(parser); 777 778 // See if it has a floating point. Make sure there is a digit after the "." 779 // so we don't get confused by method calls on number literals. 780 if (peekChar(parser) == '.' && isDigit(peekNextChar(parser))) 781 { 782 nextChar(parser); 783 while (isDigit(peekChar(parser))) nextChar(parser); 784 } 785 786 // See if the number is in scientific notation. 787 if (matchChar(parser, 'e') || matchChar(parser, 'E')) 788 { 789 // Allow a single positive/negative exponent symbol. 790 if(!matchChar(parser, '+')) 791 { 792 matchChar(parser, '-'); 793 } 794 795 if (!isDigit(peekChar(parser))) 796 { 797 lexError(parser, "Unterminated scientific notation."); 798 } 799 800 while (isDigit(peekChar(parser))) nextChar(parser); 801 } 802 803 makeNumber(parser, false); 804 } 805 806 // Finishes lexing an identifier. Handles reserved words. 807 static void readName(Parser* parser, TokenType type, char firstChar) 808 { 809 ByteBuffer string_; 810 wrenByteBufferInit(&string_); 811 wrenByteBufferWrite(parser.vm, &string_, firstChar); 812 813 while (isName(peekChar(parser)) || isDigit(peekChar(parser))) 814 { 815 char c = nextChar(parser); 816 wrenByteBufferWrite(parser.vm, &string_, c); 817 } 818 819 // Update the type if it's a keyword. 820 size_t length = parser.currentChar - parser.tokenStart; 821 for (int i = 0; keywords[i].identifier != null; i++) 822 { 823 import core.stdc.string : memcmp; 824 if (length == keywords[i].length && 825 memcmp(parser.tokenStart, keywords[i].identifier, length) == 0) 826 { 827 type = keywords[i].tokenType; 828 break; 829 } 830 } 831 832 parser.next.value = wrenNewStringLength(parser.vm, 833 cast(char*)string_.data, string_.count); 834 835 wrenByteBufferClear(parser.vm, &string_); 836 makeToken(parser, type); 837 } 838 839 // Reads [digits] hex digits in a string literal and returns their number value. 840 static int readHexEscape(Parser* parser, int digits, const char* description) 841 { 842 int value = 0; 843 for (int i = 0; i < digits; i++) 844 { 845 if (peekChar(parser) == '"' || peekChar(parser) == '\0') 846 { 847 lexError(parser, "Incomplete %s escape sequence.", description); 848 849 // Don't consume it if it isn't expected. Keeps us from reading past the 850 // end of an unterminated string. 851 parser.currentChar--; 852 break; 853 } 854 855 int digit = readHexDigit(parser); 856 if (digit == -1) 857 { 858 lexError(parser, "Invalid %s escape sequence.", description); 859 break; 860 } 861 862 value = (value * 16) | digit; 863 } 864 865 return value; 866 } 867 868 // Reads a hex digit Unicode escape sequence in a string literal. 869 static void readUnicodeEscape(Parser* parser, ByteBuffer* string_, int length) 870 { 871 int value = readHexEscape(parser, length, "Unicode"); 872 873 // Grow the buffer enough for the encoded result. 874 int numBytes = wrenUtf8EncodeNumBytes(value); 875 if (numBytes != 0) 876 { 877 wrenByteBufferFill(parser.vm, string_, 0, numBytes); 878 wrenUtf8Encode(value, string_.data + string_.count - numBytes); 879 } 880 } 881 882 static void readRawString(Parser* parser) 883 { 884 ByteBuffer string_; 885 wrenByteBufferInit(&string_); 886 TokenType type = TokenType.TOKEN_STRING; 887 888 //consume the second and third " 889 nextChar(parser); 890 nextChar(parser); 891 892 int skipStart = 0; 893 int firstNewline = -1; 894 895 int skipEnd = -1; 896 int lastNewline = -1; 897 898 for (;;) 899 { 900 char c = nextChar(parser); 901 char c1 = peekChar(parser); 902 char c2 = peekNextChar(parser); 903 904 if (c == '\r') continue; 905 906 if (c == '\n') { 907 lastNewline = string_.count; 908 skipEnd = lastNewline; 909 firstNewline = firstNewline == -1 ? string_.count : firstNewline; 910 } 911 912 if (c == '"' && c1 == '"' && c2 == '"') break; 913 914 bool isWhitespace = c == ' ' || c == '\t'; 915 skipEnd = c == '\n' || isWhitespace ? skipEnd : -1; 916 917 // If we haven't seen a newline or other character yet, 918 // and still seeing whitespace, count the characters 919 // as skippable till we know otherwise 920 bool skippable = skipStart != -1 && isWhitespace && firstNewline == -1; 921 skipStart = skippable ? string_.count + 1 : skipStart; 922 923 // We've counted leading whitespace till we hit something else, 924 // but it's not a newline, so we reset skipStart since we need these characters 925 if (firstNewline == -1 && !isWhitespace && c != '\n') skipStart = -1; 926 927 if (c == '\0' || c1 == '\0' || c2 == '\0') 928 { 929 lexError(parser, "Unterminated raw string."); 930 931 // Don't consume it if it isn't expected. Keeps us from reading past the 932 // end of an unterminated string. 933 parser.currentChar--; 934 break; 935 } 936 937 wrenByteBufferWrite(parser.vm, &string_, c); 938 } 939 940 //consume the second and third " 941 nextChar(parser); 942 nextChar(parser); 943 944 int offset = 0; 945 int count = string_.count; 946 947 if(firstNewline != -1 && skipStart == firstNewline) offset = firstNewline + 1; 948 if(lastNewline != -1 && skipEnd == lastNewline) count = lastNewline; 949 950 count -= (offset > count) ? count : offset; 951 952 parser.next.value = wrenNewStringLength(parser.vm, 953 (cast(char*)string_.data) + offset, count); 954 955 wrenByteBufferClear(parser.vm, &string_); 956 makeToken(parser, type); 957 } 958 959 // Finishes lexing a string literal. 960 static void readString(Parser* parser) 961 { 962 ByteBuffer string_; 963 TokenType type = TokenType.TOKEN_STRING; 964 wrenByteBufferInit(&string_); 965 966 for (;;) 967 { 968 char c = nextChar(parser); 969 if (c == '"') break; 970 if (c == '\r') continue; 971 972 if (c == '\0') 973 { 974 lexError(parser, "Unterminated string."); 975 976 // Don't consume it if it isn't expected. Keeps us from reading past the 977 // end of an unterminated string. 978 parser.currentChar--; 979 break; 980 } 981 982 if (c == '%') 983 { 984 if (parser.numParens < MAX_INTERPOLATION_NESTING) 985 { 986 // TODO: Allow format string. 987 if (nextChar(parser) != '(') lexError(parser, "Expect '(' after '%%'."); 988 989 parser.parens[parser.numParens++] = 1; 990 type = TokenType.TOKEN_INTERPOLATION; 991 break; 992 } 993 994 lexError(parser, "Interpolation may only nest %d levels deep.", 995 MAX_INTERPOLATION_NESTING); 996 } 997 998 if (c == '\\') 999 { 1000 switch (nextChar(parser)) 1001 { 1002 case '"': wrenByteBufferWrite(parser.vm, &string_, '"'); break; 1003 case '\\': wrenByteBufferWrite(parser.vm, &string_, '\\'); break; 1004 case '%': wrenByteBufferWrite(parser.vm, &string_, '%'); break; 1005 case '0': wrenByteBufferWrite(parser.vm, &string_, '\0'); break; 1006 case 'a': wrenByteBufferWrite(parser.vm, &string_, '\a'); break; 1007 case 'b': wrenByteBufferWrite(parser.vm, &string_, '\b'); break; 1008 case 'e': wrenByteBufferWrite(parser.vm, &string_, '\33'); break; 1009 case 'f': wrenByteBufferWrite(parser.vm, &string_, '\f'); break; 1010 case 'n': wrenByteBufferWrite(parser.vm, &string_, '\n'); break; 1011 case 'r': wrenByteBufferWrite(parser.vm, &string_, '\r'); break; 1012 case 't': wrenByteBufferWrite(parser.vm, &string_, '\t'); break; 1013 case 'u': readUnicodeEscape(parser, &string_, 4); break; 1014 case 'U': readUnicodeEscape(parser, &string_, 8); break; 1015 case 'v': wrenByteBufferWrite(parser.vm, &string_, '\v'); break; 1016 case 'x': 1017 wrenByteBufferWrite(parser.vm, &string_, 1018 cast(ubyte)readHexEscape(parser, 2, "byte")); 1019 break; 1020 1021 default: 1022 lexError(parser, "Invalid escape character '%c'.", 1023 *(parser.currentChar - 1)); 1024 break; 1025 } 1026 } 1027 else 1028 { 1029 wrenByteBufferWrite(parser.vm, &string_, c); 1030 } 1031 } 1032 1033 parser.next.value = wrenNewStringLength(parser.vm, 1034 cast(char*)string_.data, string_.count); 1035 1036 wrenByteBufferClear(parser.vm, &string_); 1037 makeToken(parser, type); 1038 } 1039 1040 // Lex the next token and store it in [parser.next]. 1041 static void nextToken(Parser* parser) 1042 { 1043 parser.previous = parser.current; 1044 parser.current = parser.next; 1045 1046 // If we are out of tokens, don't try to tokenize any more. We *do* still 1047 // copy the TOKEN_EOF to previous so that code that expects it to be consumed 1048 // will still work. 1049 if (parser.next.type == TokenType.TOKEN_EOF) return; 1050 if (parser.current.type == TokenType.TOKEN_EOF) return; 1051 1052 while (peekChar(parser) != '\0') 1053 { 1054 parser.tokenStart = parser.currentChar; 1055 1056 char c = nextChar(parser); 1057 switch (c) with(TokenType) 1058 { 1059 case '(': 1060 // If we are inside an interpolated expression, count the unmatched "(". 1061 if (parser.numParens > 0) parser.parens[parser.numParens - 1]++; 1062 makeToken(parser, TokenType.TOKEN_LEFT_PAREN); 1063 return; 1064 1065 case ')': 1066 // If we are inside an interpolated expression, count the ")". 1067 if (parser.numParens > 0 && 1068 --parser.parens[parser.numParens - 1] == 0) 1069 { 1070 // This is the final ")", so the interpolation expression has ended. 1071 // This ")" now begins the next section of the template string. 1072 parser.numParens--; 1073 readString(parser); 1074 return; 1075 } 1076 1077 makeToken(parser, TokenType.TOKEN_RIGHT_PAREN); 1078 return; 1079 1080 case '[': makeToken(parser, TokenType.TOKEN_LEFT_BRACKET); return; 1081 case ']': makeToken(parser, TokenType.TOKEN_RIGHT_BRACKET); return; 1082 case '{': makeToken(parser, TokenType.TOKEN_LEFT_BRACE); return; 1083 case '}': makeToken(parser, TokenType.TOKEN_RIGHT_BRACE); return; 1084 case ':': makeToken(parser, TokenType.TOKEN_COLON); return; 1085 case ',': makeToken(parser, TokenType.TOKEN_COMMA); return; 1086 case '*': makeToken(parser, TokenType.TOKEN_STAR); return; 1087 case '%': makeToken(parser, TokenType.TOKEN_PERCENT); return; 1088 case '#': { 1089 // Ignore shebang on the first line. 1090 if (parser.currentLine == 1 && peekChar(parser) == '!' && peekNextChar(parser) == '/') 1091 { 1092 skipLineComment(parser); 1093 break; 1094 } 1095 // Otherwise we treat it as a token 1096 makeToken(parser, TokenType.TOKEN_HASH); 1097 return; 1098 } 1099 case '^': makeToken(parser, TokenType.TOKEN_CARET); return; 1100 case '+': makeToken(parser, TokenType.TOKEN_PLUS); return; 1101 case '-': makeToken(parser, TokenType.TOKEN_MINUS); return; 1102 case '~': makeToken(parser, TokenType.TOKEN_TILDE); return; 1103 case '?': makeToken(parser, TokenType.TOKEN_QUESTION); return; 1104 1105 case '|': twoCharToken(parser, '|', TokenType.TOKEN_PIPEPIPE, TokenType.TOKEN_PIPE); return; 1106 case '&': twoCharToken(parser, '&', TokenType.TOKEN_AMPAMP, TokenType.TOKEN_AMP); return; 1107 case '=': twoCharToken(parser, '=', TokenType.TOKEN_EQEQ, TokenType.TOKEN_EQ); return; 1108 case '!': twoCharToken(parser, '=', TokenType.TOKEN_BANGEQ, TokenType.TOKEN_BANG); return; 1109 1110 case '.': 1111 if (matchChar(parser, '.')) 1112 { 1113 twoCharToken(parser, '.', TokenType.TOKEN_DOTDOTDOT, TokenType.TOKEN_DOTDOT); 1114 return; 1115 } 1116 1117 makeToken(parser, TokenType.TOKEN_DOT); 1118 return; 1119 1120 case '/': 1121 if (matchChar(parser, '/')) 1122 { 1123 skipLineComment(parser); 1124 break; 1125 } 1126 1127 if (matchChar(parser, '*')) 1128 { 1129 skipBlockComment(parser); 1130 break; 1131 } 1132 1133 makeToken(parser, TokenType.TOKEN_SLASH); 1134 return; 1135 1136 case '<': 1137 if (matchChar(parser, '<')) 1138 { 1139 makeToken(parser, TokenType.TOKEN_LTLT); 1140 } 1141 else 1142 { 1143 twoCharToken(parser, '=', TokenType.TOKEN_LTEQ, TokenType.TOKEN_LT); 1144 } 1145 return; 1146 1147 case '>': 1148 if (matchChar(parser, '>')) 1149 { 1150 makeToken(parser, TokenType.TOKEN_GTGT); 1151 } 1152 else 1153 { 1154 twoCharToken(parser, '=', TokenType.TOKEN_GTEQ, TokenType.TOKEN_GT); 1155 } 1156 return; 1157 1158 case '\n': 1159 makeToken(parser, TokenType.TOKEN_LINE); 1160 return; 1161 1162 case ' ': 1163 case '\r': 1164 case '\t': 1165 // Skip forward until we run out of whitespace. 1166 while (peekChar(parser) == ' ' || 1167 peekChar(parser) == '\r' || 1168 peekChar(parser) == '\t') 1169 { 1170 nextChar(parser); 1171 } 1172 break; 1173 1174 case '"': { 1175 if(peekChar(parser) == '"' && peekNextChar(parser) == '"') { 1176 readRawString(parser); 1177 return; 1178 } 1179 readString(parser); return; 1180 } 1181 case '_': 1182 readName(parser, 1183 peekChar(parser) == '_' ? TokenType.TOKEN_STATIC_FIELD : TokenType.TOKEN_FIELD, c); 1184 return; 1185 1186 case '0': 1187 if (peekChar(parser) == 'x') 1188 { 1189 readHexNumber(parser); 1190 return; 1191 } 1192 1193 readNumber(parser); 1194 return; 1195 1196 default: 1197 if (isName(c)) 1198 { 1199 readName(parser, TokenType.TOKEN_NAME, c); 1200 } 1201 else if (isDigit(c)) 1202 { 1203 readNumber(parser); 1204 } 1205 else 1206 { 1207 if (c >= 32 && c <= 126) 1208 { 1209 lexError(parser, "Invalid character '%c'.", c); 1210 } 1211 else 1212 { 1213 // Don't show non-ASCII values since we didn't UTF-8 decode the 1214 // bytes. Since there are no non-ASCII byte values that are 1215 // meaningful code units in Wren, the lexer works on raw bytes, 1216 // even though the source code and console output are UTF-8. 1217 lexError(parser, "Invalid byte 0x%x.", cast(ubyte)c); 1218 } 1219 parser.next.type = TokenType.TOKEN_ERROR; 1220 parser.next.length = 0; 1221 } 1222 return; 1223 } 1224 } 1225 1226 // If we get here, we're out of source, so just make EOF tokens. 1227 parser.tokenStart = parser.currentChar; 1228 makeToken(parser, TokenType.TOKEN_EOF); 1229 } 1230 1231 // Parsing --------------------------------------------------------------------- 1232 1233 // Returns the type of the current token. 1234 static TokenType peek(Compiler* compiler) 1235 { 1236 return compiler.parser.current.type; 1237 } 1238 1239 // Returns the type of the current token. 1240 static TokenType peekNext(Compiler* compiler) 1241 { 1242 return compiler.parser.next.type; 1243 } 1244 1245 // Consumes the current token if its type is [expected]. Returns true if a 1246 // token was consumed. 1247 static bool match(Compiler* compiler, TokenType expected) 1248 { 1249 if (peek(compiler) != expected) return false; 1250 1251 nextToken(compiler.parser); 1252 return true; 1253 } 1254 1255 // Consumes the current token. Emits an error if its type is not [expected]. 1256 static void consume(Compiler* compiler, TokenType expected, 1257 const char* errorMessage) 1258 { 1259 nextToken(compiler.parser); 1260 if (compiler.parser.previous.type != expected) 1261 { 1262 error(compiler, errorMessage); 1263 1264 // If the next token is the one we want, assume the current one is just a 1265 // spurious error and discard it to minimize the number of cascaded errors. 1266 if (compiler.parser.current.type == expected) nextToken(compiler.parser); 1267 } 1268 } 1269 1270 // Matches one or more newlines. Returns true if at least one was found. 1271 static bool matchLine(Compiler* compiler) 1272 { 1273 if (!match(compiler, TokenType.TOKEN_LINE)) return false; 1274 1275 while (match(compiler, TokenType.TOKEN_LINE)) {} 1276 return true; 1277 } 1278 1279 // Discards any newlines starting at the current token. 1280 static void ignoreNewlines(Compiler* compiler) 1281 { 1282 matchLine(compiler); 1283 } 1284 1285 // Consumes the current token. Emits an error if it is not a newline. Then 1286 // discards any duplicate newlines following it. 1287 static void consumeLine(Compiler* compiler, const char* errorMessage) 1288 { 1289 consume(compiler, TokenType.TOKEN_LINE, errorMessage); 1290 ignoreNewlines(compiler); 1291 } 1292 1293 static void allowLineBeforeDot(Compiler* compiler) { 1294 if (peek(compiler) == TokenType.TOKEN_LINE && peekNext(compiler) == TokenType.TOKEN_DOT) { 1295 nextToken(compiler.parser); 1296 } 1297 } 1298 1299 // Variables and scopes -------------------------------------------------------- 1300 1301 // Emits one single-byte argument. Returns its index. 1302 static int emitByte(Compiler* compiler, int byte_) 1303 { 1304 wrenByteBufferWrite(compiler.parser.vm, &compiler.fn.code, cast(ubyte)byte_); 1305 1306 // Assume the instruction is associated with the most recently consumed token. 1307 wrenIntBufferWrite(compiler.parser.vm, &compiler.fn.debug_.sourceLines, 1308 compiler.parser.previous.line); 1309 1310 return compiler.fn.code.count - 1; 1311 } 1312 1313 // Emits one bytecode instruction. 1314 static void emitOp(Compiler* compiler, Code instruction) 1315 { 1316 emitByte(compiler, instruction); 1317 1318 // Keep track of the stack's high water mark. 1319 compiler.numSlots += stackEffects[instruction]; 1320 if (compiler.numSlots > compiler.fn.maxSlots) 1321 { 1322 compiler.fn.maxSlots = compiler.numSlots; 1323 } 1324 } 1325 1326 // Emits one 16-bit argument, which will be written big endian. 1327 static void emitShort(Compiler* compiler, int arg) 1328 { 1329 emitByte(compiler, (arg >> 8) & 0xff); 1330 emitByte(compiler, arg & 0xff); 1331 } 1332 1333 // Emits one bytecode instruction followed by a 8-bit argument. Returns the 1334 // index of the argument in the bytecode. 1335 static int emitByteArg(Compiler* compiler, Code instruction, int arg) 1336 { 1337 emitOp(compiler, instruction); 1338 return emitByte(compiler, arg); 1339 } 1340 1341 // Emits one bytecode instruction followed by a 16-bit argument, which will be 1342 // written big endian. 1343 static void emitShortArg(Compiler* compiler, Code instruction, int arg) 1344 { 1345 emitOp(compiler, instruction); 1346 emitShort(compiler, arg); 1347 } 1348 1349 // Emits [instruction] followed by a placeholder for a jump offset. The 1350 // placeholder can be patched by calling [jumpPatch]. Returns the index of the 1351 // placeholder. 1352 static int emitJump(Compiler* compiler, Code instruction) 1353 { 1354 emitOp(compiler, instruction); 1355 emitByte(compiler, 0xff); 1356 return emitByte(compiler, 0xff) - 1; 1357 } 1358 1359 // Creates a new constant for the current value and emits the bytecode to load 1360 // it from the constant table. 1361 static void emitConstant(Compiler* compiler, Value value) 1362 { 1363 int constant = addConstant(compiler, value); 1364 1365 // Compile the code to load the constant. 1366 emitShortArg(compiler, Code.CODE_CONSTANT, constant); 1367 } 1368 1369 // Create a new local variable with [name]. Assumes the current scope is local 1370 // and the name is unique. 1371 static int addLocal(Compiler* compiler, const char* name, int length) 1372 { 1373 Local* local = &compiler.locals[compiler.numLocals]; 1374 local.name = name; 1375 local.length = length; 1376 local.depth = compiler.scopeDepth; 1377 local.isUpvalue = false; 1378 return compiler.numLocals++; 1379 } 1380 1381 // Declares a variable in the current scope whose name is the given token. 1382 // 1383 // If [token] is `null`, uses the previously consumed token. Returns its symbol. 1384 static int declareVariable(Compiler* compiler, Token* token) 1385 { 1386 if (token == null) token = &compiler.parser.previous; 1387 1388 if (token.length > MAX_VARIABLE_NAME) 1389 { 1390 error(compiler, "Variable name cannot be longer than %d characters.", 1391 MAX_VARIABLE_NAME); 1392 } 1393 1394 // Top-level module scope. 1395 if (compiler.scopeDepth == -1) 1396 { 1397 int line = -1; 1398 int symbol = wrenDefineVariable(compiler.parser.vm, 1399 compiler.parser.module_, 1400 token.start, token.length, 1401 NULL_VAL, &line); 1402 1403 if (symbol == -1) 1404 { 1405 error(compiler, "Module variable is already defined."); 1406 } 1407 else if (symbol == -2) 1408 { 1409 error(compiler, "Too many module variables defined."); 1410 } 1411 else if (symbol == -3) 1412 { 1413 error(compiler, 1414 "Variable '%.*s' referenced before this definition (first use at line %d).", 1415 token.length, token.start, line); 1416 } 1417 1418 return symbol; 1419 } 1420 1421 // See if there is already a variable with this name declared in the current 1422 // scope. (Outer scopes are OK: those get shadowed.) 1423 for (int i = compiler.numLocals - 1; i >= 0; i--) 1424 { 1425 import core.stdc.string : memcmp; 1426 Local* local = &compiler.locals[i]; 1427 1428 // Once we escape this scope and hit an outer one, we can stop. 1429 if (local.depth < compiler.scopeDepth) break; 1430 1431 if (local.length == token.length && 1432 memcmp(local.name, token.start, token.length) == 0) 1433 { 1434 error(compiler, "Variable is already declared in this scope."); 1435 return i; 1436 } 1437 } 1438 1439 if (compiler.numLocals == MAX_LOCALS) 1440 { 1441 error(compiler, "Cannot declare more than %d variables in one scope.", 1442 MAX_LOCALS); 1443 return -1; 1444 } 1445 1446 return addLocal(compiler, token.start, token.length); 1447 } 1448 1449 // Parses a name token and declares a variable in the current scope with that 1450 // name. Returns its slot. 1451 static int declareNamedVariable(Compiler* compiler) 1452 { 1453 consume(compiler, TokenType.TOKEN_NAME, "Expect variable name."); 1454 return declareVariable(compiler, null); 1455 } 1456 1457 // Stores a variable with the previously defined symbol in the current scope. 1458 static void defineVariable(Compiler* compiler, int symbol) 1459 { 1460 // Store the variable. If it's a local, the result of the initializer is 1461 // in the correct slot on the stack already so we're done. 1462 if (compiler.scopeDepth >= 0) return; 1463 1464 // It's a module-level variable, so store the value in the module slot and 1465 // then discard the temporary for the initializer. 1466 emitShortArg(compiler, Code.CODE_STORE_MODULE_VAR, symbol); 1467 emitOp(compiler, Code.CODE_POP); 1468 } 1469 1470 // Starts a new local block scope. 1471 static void pushScope(Compiler* compiler) 1472 { 1473 compiler.scopeDepth++; 1474 } 1475 1476 // Generates code to discard local variables at [depth] or greater. Does *not* 1477 // actually undeclare variables or pop any scopes, though. This is called 1478 // directly when compiling "break" statements to ditch the local variables 1479 // before jumping out of the loop even though they are still in scope *past* 1480 // the break instruction. 1481 // 1482 // Returns the number of local variables that were eliminated. 1483 static int discardLocals(Compiler* compiler, int depth) 1484 { 1485 if (compiler.scopeDepth < -1) 1486 throw mallocNew!Error("Cannot exit top-level scope."); 1487 // assert(compiler.scopeDepth > -1, "Cannot exit top-level scope."); 1488 1489 int local = compiler.numLocals - 1; 1490 while (local >= 0 && compiler.locals[local].depth >= depth) 1491 { 1492 // If the local was closed over, make sure the upvalue gets closed when it 1493 // goes out of scope on the stack. We use emitByte() and not emitOp() here 1494 // because we don't want to track that stack effect of these pops since the 1495 // variables are still in scope after the break. 1496 if (compiler.locals[local].isUpvalue) 1497 { 1498 emitByte(compiler, Code.CODE_CLOSE_UPVALUE); 1499 } 1500 else 1501 { 1502 emitByte(compiler, Code.CODE_POP); 1503 } 1504 1505 1506 local--; 1507 } 1508 1509 return compiler.numLocals - local - 1; 1510 } 1511 1512 // Closes the last pushed block scope and discards any local variables declared 1513 // in that scope. This should only be called in a statement context where no 1514 // temporaries are still on the stack. 1515 static void popScope(Compiler* compiler) 1516 { 1517 int popped = discardLocals(compiler, compiler.scopeDepth); 1518 compiler.numLocals -= popped; 1519 compiler.numSlots -= popped; 1520 compiler.scopeDepth--; 1521 } 1522 1523 // Attempts to look up the name in the local variables of [compiler]. If found, 1524 // returns its index, otherwise returns -1. 1525 static int resolveLocal(Compiler* compiler, const char* name, int length) 1526 { 1527 import core.stdc.string : memcmp; 1528 // Look it up in the local scopes. Look in reverse order so that the most 1529 // nested variable is found first and shadows outer ones. 1530 for (int i = compiler.numLocals - 1; i >= 0; i--) 1531 { 1532 if (compiler.locals[i].length == length && 1533 memcmp(name, compiler.locals[i].name, length) == 0) 1534 { 1535 return i; 1536 } 1537 } 1538 1539 return -1; 1540 } 1541 1542 // Adds an upvalue to [compiler]'s function with the given properties. Does not 1543 // add one if an upvalue for that variable is already in the list. Returns the 1544 // index of the upvalue. 1545 static int addUpvalue(Compiler* compiler, bool isLocal, int index) 1546 { 1547 // Look for an existing one. 1548 for (int i = 0; i < compiler.fn.numUpvalues; i++) 1549 { 1550 CompilerUpvalue* upvalue = &compiler.upvalues[i]; 1551 if (upvalue.index == index && upvalue.isLocal == isLocal) return i; 1552 } 1553 1554 // If we got here, it's a new upvalue. 1555 compiler.upvalues[compiler.fn.numUpvalues].isLocal = isLocal; 1556 compiler.upvalues[compiler.fn.numUpvalues].index = index; 1557 return compiler.fn.numUpvalues++; 1558 } 1559 1560 // Attempts to look up [name] in the functions enclosing the one being compiled 1561 // by [compiler]. If found, it adds an upvalue for it to this compiler's list 1562 // of upvalues (unless it's already in there) and returns its index. If not 1563 // found, returns -1. 1564 // 1565 // If the name is found outside of the immediately enclosing function, this 1566 // will flatten the closure and add upvalues to all of the intermediate 1567 // functions so that it gets walked down to this one. 1568 // 1569 // If it reaches a method boundary, this stops and returns -1 since methods do 1570 // not close over local variables. 1571 static int findUpvalue(Compiler* compiler, const char* name, int length) 1572 { 1573 // If we are at the top level, we didn't find it. 1574 if (compiler.parent == null) return -1; 1575 1576 // If we hit the method boundary (and the name isn't a static field), then 1577 // stop looking for it. We'll instead treat it as a self send. 1578 if (name[0] != '_' && compiler.parent.enclosingClass != null) return -1; 1579 1580 // See if it's a local variable in the immediately enclosing function. 1581 int local = resolveLocal(compiler.parent, name, length); 1582 if (local != -1) 1583 { 1584 // Mark the local as an upvalue so we know to close it when it goes out of 1585 // scope. 1586 compiler.parent.locals[local].isUpvalue = true; 1587 1588 return addUpvalue(compiler, true, local); 1589 } 1590 1591 // See if it's an upvalue in the immediately enclosing function. In other 1592 // words, if it's a local variable in a non-immediately enclosing function. 1593 // This "flattens" closures automatically: it adds upvalues to all of the 1594 // intermediate functions to get from the function where a local is declared 1595 // all the way into the possibly deeply nested function that is closing over 1596 // it. 1597 int upvalue = findUpvalue(compiler.parent, name, length); 1598 if (upvalue != -1) 1599 { 1600 return addUpvalue(compiler, false, upvalue); 1601 } 1602 1603 // If we got here, we walked all the way up the parent chain and couldn't 1604 // find it. 1605 return -1; 1606 } 1607 1608 // Look up [name] in the current scope to see what variable it refers to. 1609 // Returns the variable either in local scope, or the enclosing function's 1610 // upvalue list. Does not search the module scope. Returns a variable with 1611 // index -1 if not found. 1612 static Variable resolveNonmodule(Compiler* compiler, 1613 const char* name, int length) 1614 { 1615 // Look it up in the local scopes. 1616 Variable variable; 1617 variable.scope_ = Scope.SCOPE_LOCAL; 1618 variable.index = resolveLocal(compiler, name, length); 1619 if (variable.index != -1) return variable; 1620 1621 // Tt's not a local, so guess that it's an upvalue. 1622 variable.scope_ = Scope.SCOPE_UPVALUE; 1623 variable.index = findUpvalue(compiler, name, length); 1624 return variable; 1625 } 1626 1627 // Look up [name] in the current scope to see what variable it refers to. 1628 // Returns the variable either in module scope, local scope, or the enclosing 1629 // function's upvalue list. Returns a variable with index -1 if not found. 1630 static Variable resolveName(Compiler* compiler, const char* name, int length) 1631 { 1632 Variable variable = resolveNonmodule(compiler, name, length); 1633 if (variable.index != -1) return variable; 1634 1635 variable.scope_ = Scope.SCOPE_MODULE; 1636 variable.index = wrenSymbolTableFind(&compiler.parser.module_.variableNames, 1637 name, length); 1638 return variable; 1639 } 1640 1641 static void loadLocal(Compiler* compiler, int slot) 1642 { 1643 if (slot <= 8) 1644 { 1645 emitOp(compiler, cast(Code)(Code.CODE_LOAD_LOCAL_0 + slot)); 1646 return; 1647 } 1648 1649 emitByteArg(compiler, Code.CODE_LOAD_LOCAL, slot); 1650 } 1651 1652 // Finishes [compiler], which is compiling a function, method, or chunk of top 1653 // level code. If there is a parent compiler, then this emits code in the 1654 // parent compiler to load the resulting function. 1655 static ObjFn* endCompiler(Compiler* compiler, 1656 const char* debugName, int debugNameLength) 1657 { 1658 // If we hit an error, don't finish the function since it's borked anyway. 1659 if (compiler.parser.hasError) 1660 { 1661 compiler.parser.vm.compiler = compiler.parent; 1662 return null; 1663 } 1664 1665 // Mark the end of the bytecode. Since it may contain multiple early returns, 1666 // we can't rely on CODE_RETURN to tell us we're at the end. 1667 emitOp(compiler, Code.CODE_END); 1668 1669 wrenFunctionBindName(compiler.parser.vm, compiler.fn, 1670 debugName, debugNameLength); 1671 1672 // In the function that contains this one, load the resulting function object. 1673 if (compiler.parent != null) 1674 { 1675 int constant = addConstant(compiler.parent, OBJ_VAL(compiler.fn)); 1676 1677 // Wrap the function in a closure. We do this even if it has no upvalues so 1678 // that the VM can uniformly assume all called objects are closures. This 1679 // makes creating a function a little slower, but makes invoking them 1680 // faster. Given that functions are invoked more often than they are 1681 // created, this is a win. 1682 emitShortArg(compiler.parent, Code.CODE_CLOSURE, constant); 1683 1684 // Emit arguments for each upvalue to know whether to capture a local or 1685 // an upvalue. 1686 for (int i = 0; i < compiler.fn.numUpvalues; i++) 1687 { 1688 emitByte(compiler.parent, compiler.upvalues[i].isLocal ? 1 : 0); 1689 emitByte(compiler.parent, compiler.upvalues[i].index); 1690 } 1691 } 1692 1693 // Pop this compiler off the stack. 1694 compiler.parser.vm.compiler = compiler.parent; 1695 1696 static if (WREN_DEBUG_DUMP_COMPILED_CODE) { 1697 wrenDumpCode(compiler.parser.vm, compiler.fn); 1698 } 1699 1700 return compiler.fn; 1701 } 1702 1703 // Grammar --------------------------------------------------------------------- 1704 enum Precedence 1705 { 1706 PREC_NONE, 1707 PREC_LOWEST, 1708 PREC_ASSIGNMENT, // = 1709 PREC_CONDITIONAL, // ?: 1710 PREC_LOGICAL_OR, // || 1711 PREC_LOGICAL_AND, // && 1712 PREC_EQUALITY, // == != 1713 PREC_IS, // is 1714 PREC_COMPARISON, // < > <= >= 1715 PREC_BITWISE_OR, // | 1716 PREC_BITWISE_XOR, // ^ 1717 PREC_BITWISE_AND, // & 1718 PREC_BITWISE_SHIFT, // << >> 1719 PREC_RANGE, // .. ... 1720 PREC_TERM, // + - 1721 PREC_FACTOR, // * / % 1722 PREC_UNARY, // unary - ! ~ 1723 PREC_CALL, // . () [] 1724 PREC_PRIMARY 1725 } 1726 1727 alias GrammarFn = void function(Compiler*, bool canAssign); 1728 1729 alias SignatureFn = void function(Compiler*, Signature* signature); 1730 1731 struct GrammarRule 1732 { 1733 GrammarFn prefix; 1734 GrammarFn infix; 1735 SignatureFn method; 1736 Precedence precedence; 1737 const(char)* name; 1738 } 1739 1740 // Replaces the placeholder argument for a previous CODE_JUMP or CODE_JUMP_IF 1741 // instruction with an offset that jumps to the current end of bytecode. 1742 static void patchJump(Compiler* compiler, int offset) 1743 { 1744 // -2 to adjust for the bytecode for the jump offset itself. 1745 int jump = compiler.fn.code.count - offset - 2; 1746 if (jump > MAX_JUMP) error(compiler, "Too much code to jump over."); 1747 1748 compiler.fn.code.data[offset] = (jump >> 8) & 0xff; 1749 compiler.fn.code.data[offset + 1] = jump & 0xff; 1750 } 1751 1752 // Parses a block body, after the initial "{" has been consumed. 1753 // 1754 // Returns true if it was a expression body, false if it was a statement body. 1755 // (More precisely, returns true if a value was left on the stack. An empty 1756 // block returns false.) 1757 static bool finishBlock(Compiler* compiler) 1758 { 1759 // Empty blocks do nothing. 1760 if (match(compiler, TokenType.TOKEN_RIGHT_BRACE)) return false; 1761 1762 // If there's no line after the "{", it's a single-expression body. 1763 if (!matchLine(compiler)) 1764 { 1765 expression(compiler); 1766 consume(compiler, TokenType.TOKEN_RIGHT_BRACE, "Expect '}' at end of block."); 1767 return true; 1768 } 1769 1770 // Empty blocks (with just a newline inside) do nothing. 1771 if (match(compiler, TokenType.TOKEN_RIGHT_BRACE)) return false; 1772 1773 // Compile the definition list. 1774 do 1775 { 1776 definition(compiler); 1777 consumeLine(compiler, "Expect newline after statement."); 1778 } 1779 while (peek(compiler) != TokenType.TOKEN_RIGHT_BRACE && peek(compiler) != TokenType.TOKEN_EOF); 1780 1781 consume(compiler, TokenType.TOKEN_RIGHT_BRACE, "Expect '}' at end of block."); 1782 return false; 1783 } 1784 1785 // Parses a method or function body, after the initial "{" has been consumed. 1786 // 1787 // If [Compiler.isInitializer] is `true`, this is the body of a constructor 1788 // initializer. In that case, this adds the code to ensure it returns `this`. 1789 static void finishBody(Compiler* compiler) 1790 { 1791 bool isExpressionBody = finishBlock(compiler); 1792 1793 if (compiler.isInitializer) 1794 { 1795 // If the initializer body evaluates to a value, discard it. 1796 if (isExpressionBody) emitOp(compiler, Code.CODE_POP); 1797 1798 // The receiver is always stored in the first local slot. 1799 emitOp(compiler, Code.CODE_LOAD_LOCAL_0); 1800 } 1801 else if (!isExpressionBody) 1802 { 1803 // Implicitly return null in statement bodies. 1804 emitOp(compiler, Code.CODE_NULL); 1805 } 1806 1807 emitOp(compiler, Code.CODE_RETURN); 1808 } 1809 1810 // The VM can only handle a certain number of parameters, so check that we 1811 // haven't exceeded that and give a usable error. 1812 static void validateNumParameters(Compiler* compiler, int numArgs) 1813 { 1814 if (numArgs == MAX_PARAMETERS + 1) 1815 { 1816 // Only show an error at exactly max + 1 so that we can keep parsing the 1817 // parameters and minimize cascaded errors. 1818 error(compiler, "Methods cannot have more than %d parameters.", 1819 MAX_PARAMETERS); 1820 } 1821 } 1822 1823 // Parses the rest of a comma-separated parameter list after the opening 1824 // delimeter. Updates `arity` in [signature] with the number of parameters. 1825 static void finishParameterList(Compiler* compiler, Signature* signature) 1826 { 1827 do 1828 { 1829 ignoreNewlines(compiler); 1830 validateNumParameters(compiler, ++signature.arity); 1831 1832 // Define a local variable in the method for the parameter. 1833 declareNamedVariable(compiler); 1834 } 1835 while (match(compiler, TokenType.TOKEN_COMMA)); 1836 } 1837 1838 // Gets the symbol for a method [name] with [length]. 1839 static int methodSymbol(Compiler* compiler, const char* name, int length) 1840 { 1841 return wrenSymbolTableEnsure(compiler.parser.vm, 1842 &compiler.parser.vm.methodNames, name, length); 1843 } 1844 1845 // Appends characters to [name] (and updates [length]) for [numParams] "_" 1846 // surrounded by [leftBracket] and [rightBracket]. 1847 static void signatureParameterList(char* name, int* length, 1848 int numParams, char leftBracket, char rightBracket) 1849 { 1850 name[(*length)++] = leftBracket; 1851 1852 // This function may be called with too many parameters. When that happens, 1853 // a compile error has already been reported, but we need to make sure we 1854 // don't overflow the string too, hence the MAX_PARAMETERS check. 1855 for (int i = 0; i < numParams && i < MAX_PARAMETERS; i++) 1856 { 1857 if (i > 0) name[(*length)++] = ','; 1858 name[(*length)++] = '_'; 1859 } 1860 name[(*length)++] = rightBracket; 1861 } 1862 1863 // Fills [name] with the stringified version of [signature] and updates 1864 // [length] to the resulting length. 1865 static void signatureToString(Signature* signature, 1866 char* name, int* length) 1867 { 1868 import core.stdc.string : memcpy; 1869 *length = 0; 1870 1871 // Build the full name from the signature. 1872 memcpy(name + *length, signature.name, signature.length); 1873 *length += signature.length; 1874 1875 switch (signature.type) with(SignatureType) 1876 { 1877 case SIG_METHOD: 1878 signatureParameterList(name, length, signature.arity, '(', ')'); 1879 break; 1880 1881 case SIG_GETTER: 1882 // The signature is just the name. 1883 break; 1884 1885 case SIG_SETTER: 1886 name[(*length)++] = '='; 1887 signatureParameterList(name, length, 1, '(', ')'); 1888 break; 1889 1890 case SIG_SUBSCRIPT: 1891 signatureParameterList(name, length, signature.arity, '[', ']'); 1892 break; 1893 1894 case SIG_SUBSCRIPT_SETTER: 1895 signatureParameterList(name, length, signature.arity - 1, '[', ']'); 1896 name[(*length)++] = '='; 1897 signatureParameterList(name, length, 1, '(', ')'); 1898 break; 1899 1900 case SIG_INITIALIZER: 1901 memcpy(name, "init ".ptr, 5); 1902 memcpy(name + 5, signature.name, signature.length); 1903 *length = 5 + signature.length; 1904 signatureParameterList(name, length, signature.arity, '(', ')'); 1905 break; 1906 1907 default: 1908 throw mallocNew!Error("Unexpected signature type hit"); 1909 } 1910 1911 name[*length] = '\0'; 1912 } 1913 1914 // Gets the symbol for a method with [signature]. 1915 static int signatureSymbol(Compiler* compiler, Signature* signature) 1916 { 1917 // Build the full name from the signature. 1918 char[MAX_METHOD_SIGNATURE] name = 0; 1919 int length; 1920 signatureToString(signature, name.ptr, &length); 1921 1922 return methodSymbol(compiler, name.ptr, length); 1923 } 1924 1925 // Returns a signature with [type] whose name is from the last consumed token. 1926 static Signature signatureFromToken(Compiler* compiler, SignatureType type) 1927 { 1928 Signature signature; 1929 1930 // Get the token for the method name. 1931 Token* token = &compiler.parser.previous; 1932 signature.name = token.start; 1933 signature.length = token.length; 1934 signature.type = type; 1935 signature.arity = 0; 1936 1937 if (signature.length > MAX_METHOD_NAME) 1938 { 1939 error(compiler, "Method names cannot be longer than %d characters.", 1940 MAX_METHOD_NAME); 1941 signature.length = MAX_METHOD_NAME; 1942 } 1943 1944 return signature; 1945 } 1946 1947 // Parses a comma-separated list of arguments. Modifies [signature] to include 1948 // the arity of the argument list. 1949 static void finishArgumentList(Compiler* compiler, Signature* signature) 1950 { 1951 do 1952 { 1953 ignoreNewlines(compiler); 1954 validateNumParameters(compiler, ++signature.arity); 1955 expression(compiler); 1956 } 1957 while (match(compiler, TokenType.TOKEN_COMMA)); 1958 1959 // Allow a newline before the closing delimiter. 1960 ignoreNewlines(compiler); 1961 } 1962 1963 // Compiles a method call with [signature] using [instruction]. 1964 static void callSignature(Compiler* compiler, Code instruction, 1965 Signature* signature) 1966 { 1967 int symbol = signatureSymbol(compiler, signature); 1968 emitShortArg(compiler, cast(Code)(instruction + signature.arity), symbol); 1969 1970 if (instruction == Code.CODE_SUPER_0) 1971 { 1972 // Super calls need to be statically bound to the class's superclass. This 1973 // ensures we call the right method even when a method containing a super 1974 // call is inherited by another subclass. 1975 // 1976 // We bind it at class definition time by storing a reference to the 1977 // superclass in a constant. So, here, we create a slot in the constant 1978 // table and store NULL in it. When the method is bound, we'll look up the 1979 // superclass then and store it in the constant slot. 1980 emitShort(compiler, addConstant(compiler, NULL_VAL)); 1981 } 1982 } 1983 1984 // Compiles a method call with [numArgs] for a method with [name] with [length]. 1985 static void callMethod(Compiler* compiler, int numArgs, const char* name, 1986 int length) 1987 { 1988 int symbol = methodSymbol(compiler, name, length); 1989 emitShortArg(compiler, cast(Code)(Code.CODE_CALL_0 + numArgs), symbol); 1990 } 1991 1992 // Compiles an (optional) argument list for a method call with [methodSignature] 1993 // and then calls it. 1994 static void methodCall(Compiler* compiler, Code instruction, 1995 Signature* signature) 1996 { 1997 import core.stdc.string : memmove; 1998 // Make a new signature that contains the updated arity and type based on 1999 // the arguments we find. 2000 Signature called = { signature.name, signature.length, SignatureType.SIG_GETTER, 0 }; 2001 2002 // Parse the argument list, if any. 2003 if (match(compiler, TokenType.TOKEN_LEFT_PAREN)) 2004 { 2005 called.type = SignatureType.SIG_METHOD; 2006 2007 // Allow new line before an empty argument list 2008 ignoreNewlines(compiler); 2009 2010 // Allow empty an argument list. 2011 if (peek(compiler) != TokenType.TOKEN_RIGHT_PAREN) 2012 { 2013 finishArgumentList(compiler, &called); 2014 } 2015 consume(compiler, TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after arguments."); 2016 } 2017 2018 // Parse the block argument, if any. 2019 if (match(compiler, TokenType.TOKEN_LEFT_BRACE)) 2020 { 2021 // Include the block argument in the arity. 2022 called.type = SignatureType.SIG_METHOD; 2023 called.arity++; 2024 2025 Compiler fnCompiler; 2026 initCompiler(&fnCompiler, compiler.parser, compiler, false); 2027 2028 // Make a dummy signature to track the arity. 2029 Signature fnSignature = { "", 0, SignatureType.SIG_METHOD, 0 }; 2030 2031 // Parse the parameter list, if any. 2032 if (match(compiler, TokenType.TOKEN_PIPE)) 2033 { 2034 finishParameterList(&fnCompiler, &fnSignature); 2035 consume(compiler, TokenType.TOKEN_PIPE, "Expect '|' after function parameters."); 2036 } 2037 2038 fnCompiler.fn.arity = fnSignature.arity; 2039 2040 finishBody(&fnCompiler); 2041 2042 // Name the function based on the method its passed to. 2043 char[MAX_METHOD_SIGNATURE + 15] blockName; 2044 int blockLength; 2045 signatureToString(&called, blockName.ptr, &blockLength); 2046 memmove(blockName.ptr + blockLength, " block argument".ptr, 16); 2047 2048 endCompiler(&fnCompiler, blockName.ptr, blockLength + 15); 2049 } 2050 2051 // TODO: Allow Grace-style mixfix methods? 2052 2053 // If this is a super() call for an initializer, make sure we got an actual 2054 // argument list. 2055 if (signature.type == SignatureType.SIG_INITIALIZER) 2056 { 2057 if (called.type != SignatureType.SIG_METHOD) 2058 { 2059 error(compiler, "A superclass constructor must have an argument list."); 2060 } 2061 2062 called.type = SignatureType.SIG_INITIALIZER; 2063 } 2064 2065 callSignature(compiler, instruction, &called); 2066 } 2067 2068 // Compiles a call whose name is the previously consumed token. This includes 2069 // getters, method calls with arguments, and setter calls. 2070 static void namedCall(Compiler* compiler, bool canAssign, Code instruction) 2071 { 2072 // Get the token for the method name. 2073 Signature signature = signatureFromToken(compiler, SignatureType.SIG_GETTER); 2074 2075 if (canAssign && match(compiler, TokenType.TOKEN_EQ)) 2076 { 2077 ignoreNewlines(compiler); 2078 2079 // Build the setter signature. 2080 signature.type = SignatureType.SIG_SETTER; 2081 signature.arity = 1; 2082 2083 // Compile the assigned value. 2084 expression(compiler); 2085 callSignature(compiler, instruction, &signature); 2086 } 2087 else 2088 { 2089 methodCall(compiler, instruction, &signature); 2090 allowLineBeforeDot(compiler); 2091 } 2092 } 2093 2094 // Emits the code to load [variable] onto the stack. 2095 static void loadVariable(Compiler* compiler, Variable variable) 2096 { 2097 switch (variable.scope_) with(Scope) 2098 { 2099 case SCOPE_LOCAL: 2100 loadLocal(compiler, variable.index); 2101 break; 2102 case SCOPE_UPVALUE: 2103 emitByteArg(compiler, Code.CODE_LOAD_UPVALUE, variable.index); 2104 break; 2105 case SCOPE_MODULE: 2106 emitShortArg(compiler, Code.CODE_LOAD_MODULE_VAR, variable.index); 2107 break; 2108 default: 2109 throw mallocNew!Error("Unreachable code hit"); 2110 } 2111 } 2112 2113 // Loads the receiver of the currently enclosing method. Correctly handles 2114 // functions defined inside methods. 2115 static void loadThis(Compiler* compiler) 2116 { 2117 loadVariable(compiler, resolveNonmodule(compiler, "this", 4)); 2118 } 2119 2120 // Pushes the value for a module-level variable implicitly imported from core. 2121 static void loadCoreVariable(Compiler* compiler, const(char)* name) 2122 { 2123 import core.stdc.string : strlen; 2124 int symbol = wrenSymbolTableFind(&compiler.parser.module_.variableNames, 2125 name, strlen(name)); 2126 // assert(symbol != -1, "Should have already defined core name."); 2127 if (symbol == -1) 2128 throw mallocNew!Error("Should have already defined core name."); 2129 emitShortArg(compiler, Code.CODE_LOAD_MODULE_VAR, symbol); 2130 } 2131 2132 // A parenthesized expression. 2133 static void grouping(Compiler* compiler, bool canAssign) 2134 { 2135 expression(compiler); 2136 consume(compiler, TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after expression."); 2137 } 2138 2139 // A list literal. 2140 static void list(Compiler* compiler, bool canAssign) 2141 { 2142 // Instantiate a new list. 2143 loadCoreVariable(compiler, "List"); 2144 callMethod(compiler, 0, "new()", 5); 2145 2146 // Compile the list elements. Each one compiles to a ".add()" call. 2147 do 2148 { 2149 ignoreNewlines(compiler); 2150 2151 // Stop if we hit the end of the list. 2152 if (peek(compiler) == TokenType.TOKEN_RIGHT_BRACKET) break; 2153 2154 // The element. 2155 expression(compiler); 2156 callMethod(compiler, 1, "addCore_(_)", 11); 2157 } while (match(compiler, TokenType.TOKEN_COMMA)); 2158 2159 // Allow newlines before the closing ']'. 2160 ignoreNewlines(compiler); 2161 consume(compiler, TokenType.TOKEN_RIGHT_BRACKET, "Expect ']' after list elements."); 2162 } 2163 2164 // A map literal. 2165 static void map(Compiler* compiler, bool canAssign) 2166 { 2167 // Instantiate a new map. 2168 loadCoreVariable(compiler, "Map"); 2169 callMethod(compiler, 0, "new()", 5); 2170 2171 // Compile the map elements. Each one is compiled to just invoke the 2172 // subscript setter on the map. 2173 do 2174 { 2175 ignoreNewlines(compiler); 2176 2177 // Stop if we hit the end of the map. 2178 if (peek(compiler) == TokenType.TOKEN_RIGHT_BRACE) break; 2179 2180 // The key. 2181 parsePrecedence(compiler, Precedence.PREC_UNARY); 2182 consume(compiler, TokenType.TOKEN_COLON, "Expect ':' after map key."); 2183 ignoreNewlines(compiler); 2184 2185 // The value. 2186 expression(compiler); 2187 callMethod(compiler, 2, "addCore_(_,_)", 13); 2188 } while (match(compiler, TokenType.TOKEN_COMMA)); 2189 2190 // Allow newlines before the closing '}'. 2191 ignoreNewlines(compiler); 2192 consume(compiler, TokenType.TOKEN_RIGHT_BRACE, "Expect '}' after map entries."); 2193 } 2194 2195 // Unary operators like `-foo`. 2196 static void unaryOp(Compiler* compiler, bool canAssign) 2197 { 2198 GrammarRule* rule = getRule(compiler.parser.previous.type); 2199 2200 ignoreNewlines(compiler); 2201 2202 // Compile the argument. 2203 parsePrecedence(compiler, cast(Precedence)(Precedence.PREC_UNARY + 1)); 2204 2205 // Call the operator method on the left-hand side. 2206 callMethod(compiler, 0, rule.name, 1); 2207 } 2208 2209 static void boolean(Compiler* compiler, bool canAssign) 2210 { 2211 emitOp(compiler, 2212 compiler.parser.previous.type == TokenType.TOKEN_FALSE ? Code.CODE_FALSE : Code.CODE_TRUE); 2213 } 2214 2215 // Walks the compiler chain to find the compiler for the nearest class 2216 // enclosing this one. Returns NULL if not currently inside a class definition. 2217 static Compiler* getEnclosingClassCompiler(Compiler* compiler) 2218 { 2219 while (compiler != null) 2220 { 2221 if (compiler.enclosingClass != null) return compiler; 2222 compiler = compiler.parent; 2223 } 2224 2225 return null; 2226 } 2227 2228 // Walks the compiler chain to find the nearest class enclosing this one. 2229 // Returns NULL if not currently inside a class definition. 2230 static ClassInfo* getEnclosingClass(Compiler* compiler) 2231 { 2232 compiler = getEnclosingClassCompiler(compiler); 2233 return compiler == null ? null : compiler.enclosingClass; 2234 } 2235 2236 static void field(Compiler* compiler, bool canAssign) 2237 { 2238 // Initialize it with a fake value so we can keep parsing and minimize the 2239 // number of cascaded errors. 2240 int field = MAX_FIELDS; 2241 2242 ClassInfo* enclosingClass = getEnclosingClass(compiler); 2243 2244 if (enclosingClass == null) 2245 { 2246 error(compiler, "Cannot reference a field outside of a class definition."); 2247 } 2248 else if (enclosingClass.isForeign) 2249 { 2250 error(compiler, "Cannot define fields in a foreign class."); 2251 } 2252 else if (enclosingClass.inStatic) 2253 { 2254 error(compiler, "Cannot use an instance field in a static method."); 2255 } 2256 else 2257 { 2258 // Look up the field, or implicitly define it. 2259 field = wrenSymbolTableEnsure(compiler.parser.vm, &enclosingClass.fields, 2260 compiler.parser.previous.start, 2261 compiler.parser.previous.length); 2262 2263 if (field >= MAX_FIELDS) 2264 { 2265 error(compiler, "A class can only have %d fields.", MAX_FIELDS); 2266 } 2267 } 2268 2269 // If there's an "=" after a field name, it's an assignment. 2270 bool isLoad = true; 2271 if (canAssign && match(compiler, TokenType.TOKEN_EQ)) 2272 { 2273 // Compile the right-hand side. 2274 expression(compiler); 2275 isLoad = false; 2276 } 2277 2278 // If we're directly inside a method, use a more optimal instruction. 2279 if (compiler.parent != null && 2280 compiler.parent.enclosingClass == enclosingClass) 2281 { 2282 emitByteArg(compiler, isLoad ? Code.CODE_LOAD_FIELD_THIS : Code.CODE_STORE_FIELD_THIS, 2283 field); 2284 } 2285 else 2286 { 2287 loadThis(compiler); 2288 emitByteArg(compiler, isLoad ? Code.CODE_LOAD_FIELD : Code.CODE_STORE_FIELD, field); 2289 } 2290 2291 allowLineBeforeDot(compiler); 2292 } 2293 2294 // Compiles a read or assignment to [variable]. 2295 static void bareName(Compiler* compiler, bool canAssign, Variable variable) 2296 { 2297 // If there's an "=" after a bare name, it's a variable assignment. 2298 if (canAssign && match(compiler, TokenType.TOKEN_EQ)) 2299 { 2300 // Compile the right-hand side. 2301 expression(compiler); 2302 2303 // Emit the store instruction. 2304 switch (variable.scope_) 2305 { 2306 case Scope.SCOPE_LOCAL: 2307 emitByteArg(compiler, Code.CODE_STORE_LOCAL, variable.index); 2308 break; 2309 case Scope.SCOPE_UPVALUE: 2310 emitByteArg(compiler, Code.CODE_STORE_UPVALUE, variable.index); 2311 break; 2312 case Scope.SCOPE_MODULE: 2313 emitShortArg(compiler, Code.CODE_STORE_MODULE_VAR, variable.index); 2314 break; 2315 default: 2316 throw mallocNew!Error("Unreachable code hit."); 2317 // assert(0, "Unreachable"); 2318 } 2319 return; 2320 } 2321 2322 // Emit the load instruction. 2323 loadVariable(compiler, variable); 2324 2325 allowLineBeforeDot(compiler); 2326 } 2327 2328 static void staticField(Compiler* compiler, bool canAssign) 2329 { 2330 Compiler* classCompiler = getEnclosingClassCompiler(compiler); 2331 if (classCompiler == null) 2332 { 2333 error(compiler, "Cannot use a static field outside of a class definition."); 2334 return; 2335 } 2336 2337 // Look up the name in the scope chain. 2338 Token* token = &compiler.parser.previous; 2339 2340 // If this is the first time we've seen this static field, implicitly 2341 // define it as a variable in the scope surrounding the class definition. 2342 if (resolveLocal(classCompiler, token.start, token.length) == -1) 2343 { 2344 int symbol = declareVariable(classCompiler, null); 2345 2346 // Implicitly initialize it to null. 2347 emitOp(classCompiler, Code.CODE_NULL); 2348 defineVariable(classCompiler, symbol); 2349 } 2350 2351 // It definitely exists now, so resolve it properly. This is different from 2352 // the above resolveLocal() call because we may have already closed over it 2353 // as an upvalue. 2354 Variable variable = resolveName(compiler, token.start, token.length); 2355 bareName(compiler, canAssign, variable); 2356 } 2357 2358 // Compiles a variable name or method call with an implicit receiver. 2359 static void name(Compiler* compiler, bool canAssign) 2360 { 2361 // Look for the name in the scope chain up to the nearest enclosing method. 2362 Token* token = &compiler.parser.previous; 2363 2364 Variable variable = resolveNonmodule(compiler, token.start, token.length); 2365 if (variable.index != -1) 2366 { 2367 bareName(compiler, canAssign, variable); 2368 return; 2369 } 2370 2371 // TODO: The fact that we return above here if the variable is known and parse 2372 // an optional argument list below if not means that the grammar is not 2373 // context-free. A line of code in a method like "someName(foo)" is a parse 2374 // error if "someName" is a defined variable in the surrounding scope and not 2375 // if it isn't. Fix this. One option is to have "someName(foo)" always 2376 // resolve to a self-call if there is an argument list, but that makes 2377 // getters a little confusing. 2378 2379 // If we're inside a method and the name is lowercase, treat it as a method 2380 // on this. 2381 if (wrenIsLocalName(token.start) && getEnclosingClass(compiler) != null) 2382 { 2383 loadThis(compiler); 2384 namedCall(compiler, canAssign, Code.CODE_CALL_0); 2385 return; 2386 } 2387 2388 // Otherwise, look for a module-level variable with the name. 2389 variable.scope_ = Scope.SCOPE_MODULE; 2390 variable.index = wrenSymbolTableFind(&compiler.parser.module_.variableNames, 2391 token.start, token.length); 2392 if (variable.index == -1) 2393 { 2394 // Implicitly define a module-level variable in 2395 // the hopes that we get a real definition later. 2396 variable.index = wrenDeclareVariable(compiler.parser.vm, 2397 compiler.parser.module_, 2398 token.start, token.length, 2399 token.line); 2400 2401 if (variable.index == -2) 2402 { 2403 error(compiler, "Too many module variables defined."); 2404 } 2405 } 2406 2407 bareName(compiler, canAssign, variable); 2408 } 2409 2410 static void null_(Compiler* compiler, bool canAssign) 2411 { 2412 emitOp(compiler, Code.CODE_NULL); 2413 } 2414 2415 // A number or string literal. 2416 static void literal(Compiler* compiler, bool canAssign) 2417 { 2418 emitConstant(compiler, compiler.parser.previous.value); 2419 } 2420 2421 // A string literal that contains interpolated expressions. 2422 // 2423 // Interpolation is syntactic sugar for calling ".join()" on a list. So the 2424 // string: 2425 // 2426 // "a %(b + c) d" 2427 // 2428 // is compiled roughly like: 2429 // 2430 // ["a ", b + c, " d"].join() 2431 static void stringInterpolation(Compiler* compiler, bool canAssign) 2432 { 2433 // Instantiate a new list. 2434 loadCoreVariable(compiler, "List"); 2435 callMethod(compiler, 0, "new()", 5); 2436 2437 do 2438 { 2439 // The opening string part. 2440 literal(compiler, false); 2441 callMethod(compiler, 1, "addCore_(_)", 11); 2442 2443 // The interpolated expression. 2444 ignoreNewlines(compiler); 2445 expression(compiler); 2446 callMethod(compiler, 1, "addCore_(_)", 11); 2447 2448 ignoreNewlines(compiler); 2449 } while (match(compiler, TokenType.TOKEN_INTERPOLATION)); 2450 2451 // The trailing string part. 2452 consume(compiler, TokenType.TOKEN_STRING, "Expect end of string interpolation."); 2453 literal(compiler, false); 2454 callMethod(compiler, 1, "addCore_(_)", 11); 2455 2456 // The list of interpolated parts. 2457 callMethod(compiler, 0, "join()", 6); 2458 } 2459 2460 static void super_(Compiler* compiler, bool canAssign) 2461 { 2462 ClassInfo* enclosingClass = getEnclosingClass(compiler); 2463 if (enclosingClass == null) 2464 { 2465 error(compiler, "Cannot use 'super' outside of a method."); 2466 } 2467 2468 loadThis(compiler); 2469 2470 // TODO: Super operator calls. 2471 // TODO: There's no syntax for invoking a superclass constructor with a 2472 // different name from the enclosing one. Figure that out. 2473 2474 // See if it's a named super call, or an unnamed one. 2475 if (match(compiler, TokenType.TOKEN_DOT)) 2476 { 2477 // Compile the superclass call. 2478 consume(compiler, TokenType.TOKEN_NAME, "Expect method name after 'super.'."); 2479 namedCall(compiler, canAssign, Code.CODE_SUPER_0); 2480 } 2481 else if (enclosingClass != null) 2482 { 2483 // No explicit name, so use the name of the enclosing method. Make sure we 2484 // check that enclosingClass isn't NULL first. We've already reported the 2485 // error, but we don't want to crash here. 2486 methodCall(compiler, Code.CODE_SUPER_0, enclosingClass.signature); 2487 } 2488 } 2489 2490 static void this_(Compiler* compiler, bool canAssign) 2491 { 2492 if (getEnclosingClass(compiler) == null) 2493 { 2494 error(compiler, "Cannot use 'this' outside of a method."); 2495 return; 2496 } 2497 2498 loadThis(compiler); 2499 } 2500 2501 // Subscript or "array indexing" operator like `foo[bar]`. 2502 static void subscript(Compiler* compiler, bool canAssign) 2503 { 2504 Signature signature = { "", 0, SignatureType.SIG_SUBSCRIPT, 0 }; 2505 2506 // Parse the argument list. 2507 finishArgumentList(compiler, &signature); 2508 consume(compiler, TokenType.TOKEN_RIGHT_BRACKET, "Expect ']' after arguments."); 2509 2510 allowLineBeforeDot(compiler); 2511 2512 if (canAssign && match(compiler, TokenType.TOKEN_EQ)) 2513 { 2514 signature.type = SignatureType.SIG_SUBSCRIPT_SETTER; 2515 2516 // Compile the assigned value. 2517 validateNumParameters(compiler, ++signature.arity); 2518 expression(compiler); 2519 } 2520 2521 callSignature(compiler, Code.CODE_CALL_0, &signature); 2522 } 2523 2524 static void call(Compiler* compiler, bool canAssign) 2525 { 2526 ignoreNewlines(compiler); 2527 consume(compiler, TokenType.TOKEN_NAME, "Expect method name after '.'."); 2528 namedCall(compiler, canAssign, Code.CODE_CALL_0); 2529 } 2530 2531 static void and_(Compiler* compiler, bool canAssign) 2532 { 2533 ignoreNewlines(compiler); 2534 2535 // Skip the right argument if the left is false. 2536 int jump = emitJump(compiler, Code.CODE_AND); 2537 parsePrecedence(compiler, Precedence.PREC_LOGICAL_AND); 2538 patchJump(compiler, jump); 2539 } 2540 2541 static void or_(Compiler* compiler, bool canAssign) 2542 { 2543 ignoreNewlines(compiler); 2544 2545 // Skip the right argument if the left is true. 2546 int jump = emitJump(compiler, Code.CODE_OR); 2547 parsePrecedence(compiler, Precedence.PREC_LOGICAL_OR); 2548 patchJump(compiler, jump); 2549 } 2550 2551 static void conditional(Compiler* compiler, bool canAssign) 2552 { 2553 // Ignore newline after '?'. 2554 ignoreNewlines(compiler); 2555 2556 // Jump to the else branch if the condition is false. 2557 int ifJump = emitJump(compiler, Code.CODE_JUMP_IF); 2558 2559 // Compile the then branch. 2560 parsePrecedence(compiler, Precedence.PREC_CONDITIONAL); 2561 2562 consume(compiler, TokenType.TOKEN_COLON, 2563 "Expect ':' after then branch of conditional operator."); 2564 ignoreNewlines(compiler); 2565 2566 // Jump over the else branch when the if branch is taken. 2567 int elseJump = emitJump(compiler, Code.CODE_JUMP); 2568 2569 // Compile the else branch. 2570 patchJump(compiler, ifJump); 2571 2572 parsePrecedence(compiler, Precedence.PREC_ASSIGNMENT); 2573 2574 // Patch the jump over the else. 2575 patchJump(compiler, elseJump); 2576 } 2577 2578 void infixOp(Compiler* compiler, bool canAssign) 2579 { 2580 import core.stdc.string : strlen; 2581 GrammarRule* rule = getRule(compiler.parser.previous.type); 2582 2583 // An infix operator cannot end an expression. 2584 ignoreNewlines(compiler); 2585 2586 // Compile the right-hand side. 2587 parsePrecedence(compiler, cast(Precedence)(rule.precedence + 1)); 2588 2589 // Call the operator method on the left-hand side. 2590 Signature signature = { rule.name, cast(int)strlen(rule.name), SignatureType.SIG_METHOD, 1 }; 2591 callSignature(compiler, Code.CODE_CALL_0, &signature); 2592 } 2593 2594 // Compiles a method signature for an infix operator. 2595 void infixSignature(Compiler* compiler, Signature* signature) 2596 { 2597 // Add the RHS parameter. 2598 signature.type = SignatureType.SIG_METHOD; 2599 signature.arity = 1; 2600 2601 // Parse the parameter name. 2602 consume(compiler, TokenType.TOKEN_LEFT_PAREN, "Expect '(' after operator name."); 2603 declareNamedVariable(compiler); 2604 consume(compiler, TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after parameter name."); 2605 } 2606 2607 // Compiles a method signature for an unary operator (i.e. "!"). 2608 void unarySignature(Compiler* compiler, Signature* signature) 2609 { 2610 // Do nothing. The name is already complete. 2611 signature.type = SignatureType.SIG_GETTER; 2612 } 2613 2614 // Compiles a method signature for an operator that can either be unary or 2615 // infix (i.e. "-"). 2616 void mixedSignature(Compiler* compiler, Signature* signature) 2617 { 2618 signature.type = SignatureType.SIG_GETTER; 2619 2620 // If there is a parameter, it's an infix operator, otherwise it's unary. 2621 if (match(compiler, TokenType.TOKEN_LEFT_PAREN)) 2622 { 2623 // Add the RHS parameter. 2624 signature.type = SignatureType.SIG_METHOD; 2625 signature.arity = 1; 2626 2627 // Parse the parameter name. 2628 declareNamedVariable(compiler); 2629 consume(compiler, TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after parameter name."); 2630 } 2631 } 2632 2633 // Compiles an optional setter parameter in a method [signature]. 2634 // 2635 // Returns `true` if it was a setter. 2636 static bool maybeSetter(Compiler* compiler, Signature* signature) 2637 { 2638 // See if it's a setter. 2639 if (!match(compiler, TokenType.TOKEN_EQ)) return false; 2640 2641 // It's a setter. 2642 if (signature.type == SignatureType.SIG_SUBSCRIPT) 2643 { 2644 signature.type = SignatureType.SIG_SUBSCRIPT_SETTER; 2645 } 2646 else 2647 { 2648 signature.type = SignatureType.SIG_SETTER; 2649 } 2650 2651 // Parse the value parameter. 2652 consume(compiler, TokenType.TOKEN_LEFT_PAREN, "Expect '(' after '='."); 2653 declareNamedVariable(compiler); 2654 consume(compiler, TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after parameter name."); 2655 2656 signature.arity++; 2657 2658 return true; 2659 } 2660 2661 // Compiles a method signature for a subscript operator. 2662 void subscriptSignature(Compiler* compiler, Signature* signature) 2663 { 2664 signature.type = SignatureType.SIG_SUBSCRIPT; 2665 2666 // The signature currently has "[" as its name since that was the token that 2667 // matched it. Clear that out. 2668 signature.length = 0; 2669 2670 // Parse the parameters inside the subscript. 2671 finishParameterList(compiler, signature); 2672 consume(compiler, TokenType.TOKEN_RIGHT_BRACKET, "Expect ']' after parameters."); 2673 2674 maybeSetter(compiler, signature); 2675 } 2676 2677 // Parses an optional parenthesized parameter list. Updates `type` and `arity` 2678 // in [signature] to match what was parsed. 2679 static void parameterList(Compiler* compiler, Signature* signature) 2680 { 2681 // The parameter list is optional. 2682 if (!match(compiler, TokenType.TOKEN_LEFT_PAREN)) return; 2683 2684 signature.type = SignatureType.SIG_METHOD; 2685 2686 // Allow new line before an empty argument list 2687 ignoreNewlines(compiler); 2688 2689 // Allow an empty parameter list. 2690 if (match(compiler, TokenType.TOKEN_RIGHT_PAREN)) return; 2691 2692 finishParameterList(compiler, signature); 2693 consume(compiler, TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after parameters."); 2694 } 2695 2696 // Compiles a method signature for a named method or setter. 2697 void namedSignature(Compiler* compiler, Signature* signature) 2698 { 2699 signature.type = SignatureType.SIG_GETTER; 2700 2701 // If it's a setter, it can't also have a parameter list. 2702 if (maybeSetter(compiler, signature)) return; 2703 2704 // Regular named method with an optional parameter list. 2705 parameterList(compiler, signature); 2706 } 2707 2708 // Compiles a method signature for a constructor. 2709 void constructorSignature(Compiler* compiler, Signature* signature) 2710 { 2711 consume(compiler, TokenType.TOKEN_NAME, "Expect constructor name after 'construct'."); 2712 2713 // Capture the name. 2714 *signature = signatureFromToken(compiler, SignatureType.SIG_INITIALIZER); 2715 2716 if (match(compiler, TokenType.TOKEN_EQ)) 2717 { 2718 error(compiler, "A constructor cannot be a setter."); 2719 } 2720 2721 if (!match(compiler, TokenType.TOKEN_LEFT_PAREN)) 2722 { 2723 error(compiler, "A constructor cannot be a getter."); 2724 return; 2725 } 2726 2727 // Allow an empty parameter list. 2728 if (match(compiler, TokenType.TOKEN_RIGHT_PAREN)) return; 2729 2730 finishParameterList(compiler, signature); 2731 consume(compiler, TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after parameters."); 2732 } 2733 2734 enum UNUSED = GrammarRule(null, null, null, Precedence.PREC_NONE, null); 2735 enum PREFIX(alias fn) = GrammarRule(&fn, null, null, Precedence.PREC_NONE, null); 2736 enum INFIX(Precedence prec, alias fn) = GrammarRule(null, &fn, null, prec, null); 2737 enum INFIX_OPERATOR(Precedence prec, const(char)* name) = GrammarRule(null, &infixOp, &infixSignature, prec, name); 2738 enum PREFIX_OPERATOR(const(char)* name) = GrammarRule(&unaryOp, null, &unarySignature, Precedence.PREC_NONE, name); 2739 enum OPERATOR(const(char)* name) = GrammarRule(&unaryOp, &infixOp, &mixedSignature, Precedence.PREC_TERM, name); 2740 2741 static immutable GrammarRule[] rules = [ 2742 /* TOKEN_LEFT_PAREN */ PREFIX!(grouping), 2743 /* TOKEN_RIGHT_PAREN */ UNUSED, 2744 /* TOKEN_LEFT_BRACKET */ GrammarRule(&list, &subscript, &subscriptSignature, Precedence.PREC_CALL, null), 2745 /* TOKEN_RIGHT_BRACKET */ UNUSED, 2746 /* TOKEN_LEFT_BRACE */ PREFIX!(map), 2747 /* TOKEN_RIGHT_BRACE */ UNUSED, 2748 /* TOKEN_COLON */ UNUSED, 2749 /* TOKEN_DOT */ INFIX!(Precedence.PREC_CALL, call), 2750 /* TOKEN_DOTDOT */ INFIX_OPERATOR!(Precedence.PREC_RANGE, ".."), 2751 /* TOKEN_DOTDOTDOT */ INFIX_OPERATOR!(Precedence.PREC_RANGE, "..."), 2752 /* TOKEN_COMMA */ UNUSED, 2753 /* TOKEN_STAR */ INFIX_OPERATOR!(Precedence.PREC_FACTOR, "*"), 2754 /* TOKEN_SLASH */ INFIX_OPERATOR!(Precedence.PREC_FACTOR, "/"), 2755 /* TOKEN_PERCENT */ INFIX_OPERATOR!(Precedence.PREC_FACTOR, "%"), 2756 /* TOKEN_HASH */ UNUSED, 2757 /* TOKEN_PLUS */ INFIX_OPERATOR!(Precedence.PREC_TERM, "+"), 2758 /* TOKEN_MINUS */ OPERATOR!("-"), 2759 /* TOKEN_LTLT */ INFIX_OPERATOR!(Precedence.PREC_BITWISE_SHIFT, "<<"), 2760 /* TOKEN_GTGT */ INFIX_OPERATOR!(Precedence.PREC_BITWISE_SHIFT, ">>"), 2761 /* TOKEN_PIPE */ INFIX_OPERATOR!(Precedence.PREC_BITWISE_OR, "|"), 2762 /* TOKEN_PIPEPIPE */ INFIX!(Precedence.PREC_LOGICAL_OR, or_), 2763 /* TOKEN_CARET */ INFIX_OPERATOR!(Precedence.PREC_BITWISE_XOR, "^"), 2764 /* TOKEN_AMP */ INFIX_OPERATOR!(Precedence.PREC_BITWISE_AND, "&"), 2765 /* TOKEN_AMPAMP */ INFIX!(Precedence.PREC_LOGICAL_AND, and_), 2766 /* TOKEN_BANG */ PREFIX_OPERATOR!("!"), 2767 /* TOKEN_TILDE */ PREFIX_OPERATOR!("~"), 2768 /* TOKEN_QUESTION */ INFIX!(Precedence.PREC_ASSIGNMENT, conditional), 2769 /* TOKEN_EQ */ UNUSED, 2770 /* TOKEN_LT */ INFIX_OPERATOR!(Precedence.PREC_COMPARISON, "<"), 2771 /* TOKEN_GT */ INFIX_OPERATOR!(Precedence.PREC_COMPARISON, ">"), 2772 /* TOKEN_LTEQ */ INFIX_OPERATOR!(Precedence.PREC_COMPARISON, "<="), 2773 /* TOKEN_GTEQ */ INFIX_OPERATOR!(Precedence.PREC_COMPARISON, ">="), 2774 /* TOKEN_EQEQ */ INFIX_OPERATOR!(Precedence.PREC_EQUALITY, "=="), 2775 /* TOKEN_BANGEQ */ INFIX_OPERATOR!(Precedence.PREC_EQUALITY, "!="), 2776 /* TOKEN_BREAK */ UNUSED, 2777 /* TOKEN_CONTINUE */ UNUSED, 2778 /* TOKEN_CLASS */ UNUSED, 2779 /* TOKEN_CONSTRUCT */ GrammarRule(null, null, &constructorSignature, Precedence.PREC_NONE, null), 2780 /* TOKEN_ELSE */ UNUSED, 2781 /* TOKEN_FALSE */ PREFIX!(boolean), 2782 /* TOKEN_FOR */ UNUSED, 2783 /* TOKEN_FOREIGN */ UNUSED, 2784 /* TOKEN_IF */ UNUSED, 2785 /* TOKEN_IMPORT */ UNUSED, 2786 /* TOKEN_AS */ UNUSED, 2787 /* TOKEN_IN */ UNUSED, 2788 /* TOKEN_IS */ INFIX_OPERATOR!(Precedence.PREC_IS, "is"), 2789 /* TOKEN_NULL */ PREFIX!(null_), 2790 /* TOKEN_RETURN */ UNUSED, 2791 /* TOKEN_STATIC */ UNUSED, 2792 /* TOKEN_SUPER */ PREFIX!(super_), 2793 /* TOKEN_THIS */ PREFIX!(this_), 2794 /* TOKEN_TRUE */ PREFIX!(boolean), 2795 /* TOKEN_VAR */ UNUSED, 2796 /* TOKEN_WHILE */ UNUSED, 2797 /* TOKEN_FIELD */ PREFIX!(field), 2798 /* TOKEN_STATIC_FIELD */ PREFIX!(staticField), 2799 /* TOKEN_NAME */ GrammarRule(&name, null, &namedSignature, Precedence.PREC_NONE, null), 2800 /* TOKEN_NUMBER */ PREFIX!(literal), 2801 /* TOKEN_STRING */ PREFIX!(literal), 2802 /* TOKEN_INTERPOLATION */ PREFIX!(stringInterpolation), 2803 /* TOKEN_LINE */ UNUSED, 2804 /* TOKEN_ERROR */ UNUSED, 2805 /* TOKEN_EOF */ UNUSED 2806 ]; 2807 2808 // Gets the [GrammarRule] associated with tokens of [type]. 2809 static GrammarRule* getRule(TokenType type) 2810 { 2811 return cast(typeof(return))&rules[type]; 2812 } 2813 2814 // The main entrypoint for the top-down operator precedence parser. 2815 void parsePrecedence(Compiler* compiler, Precedence precedence) 2816 { 2817 nextToken(compiler.parser); 2818 GrammarFn prefix = rules[compiler.parser.previous.type].prefix; 2819 2820 if (prefix == null) 2821 { 2822 error(compiler, "Expected expression."); 2823 return; 2824 } 2825 2826 // Track if the precendence of the surrounding expression is low enough to 2827 // allow an assignment inside this one. We can't compile an assignment like 2828 // a normal expression because it requires us to handle the LHS specially -- 2829 // it needs to be an lvalue, not an rvalue. So, for each of the kinds of 2830 // expressions that are valid lvalues -- names, subscripts, fields, etc. -- 2831 // we pass in whether or not it appears in a context loose enough to allow 2832 // "=". If so, it will parse the "=" itself and handle it appropriately. 2833 bool canAssign = precedence <= Precedence.PREC_CONDITIONAL; 2834 prefix(compiler, canAssign); 2835 2836 while (precedence <= rules[compiler.parser.current.type].precedence) 2837 { 2838 nextToken(compiler.parser); 2839 GrammarFn infix = rules[compiler.parser.previous.type].infix; 2840 infix(compiler, canAssign); 2841 } 2842 } 2843 2844 // Parses an expression. Unlike statements, expressions leave a resulting value 2845 // on the stack. 2846 void expression(Compiler* compiler) 2847 { 2848 parsePrecedence(compiler, Precedence.PREC_LOWEST); 2849 } 2850 2851 // Returns the number of bytes for the arguments to the instruction 2852 // at [ip] in [fn]'s bytecode. 2853 static int getByteCountForArguments(const ubyte* bytecode, 2854 const Value* constants, int ip) 2855 { 2856 Code instruction = cast(Code)bytecode[ip]; 2857 switch (instruction) with(Code) 2858 { 2859 case CODE_NULL: 2860 case CODE_FALSE: 2861 case CODE_TRUE: 2862 case CODE_POP: 2863 case CODE_CLOSE_UPVALUE: 2864 case CODE_RETURN: 2865 case CODE_END: 2866 case CODE_LOAD_LOCAL_0: 2867 case CODE_LOAD_LOCAL_1: 2868 case CODE_LOAD_LOCAL_2: 2869 case CODE_LOAD_LOCAL_3: 2870 case CODE_LOAD_LOCAL_4: 2871 case CODE_LOAD_LOCAL_5: 2872 case CODE_LOAD_LOCAL_6: 2873 case CODE_LOAD_LOCAL_7: 2874 case CODE_LOAD_LOCAL_8: 2875 case CODE_CONSTRUCT: 2876 case CODE_FOREIGN_CONSTRUCT: 2877 case CODE_FOREIGN_CLASS: 2878 case CODE_END_MODULE: 2879 case CODE_END_CLASS: 2880 return 0; 2881 2882 case CODE_LOAD_LOCAL: 2883 case CODE_STORE_LOCAL: 2884 case CODE_LOAD_UPVALUE: 2885 case CODE_STORE_UPVALUE: 2886 case CODE_LOAD_FIELD_THIS: 2887 case CODE_STORE_FIELD_THIS: 2888 case CODE_LOAD_FIELD: 2889 case CODE_STORE_FIELD: 2890 case CODE_CLASS: 2891 return 1; 2892 2893 case CODE_CONSTANT: 2894 case CODE_LOAD_MODULE_VAR: 2895 case CODE_STORE_MODULE_VAR: 2896 case CODE_CALL_0: 2897 case CODE_CALL_1: 2898 case CODE_CALL_2: 2899 case CODE_CALL_3: 2900 case CODE_CALL_4: 2901 case CODE_CALL_5: 2902 case CODE_CALL_6: 2903 case CODE_CALL_7: 2904 case CODE_CALL_8: 2905 case CODE_CALL_9: 2906 case CODE_CALL_10: 2907 case CODE_CALL_11: 2908 case CODE_CALL_12: 2909 case CODE_CALL_13: 2910 case CODE_CALL_14: 2911 case CODE_CALL_15: 2912 case CODE_CALL_16: 2913 case CODE_JUMP: 2914 case CODE_LOOP: 2915 case CODE_JUMP_IF: 2916 case CODE_AND: 2917 case CODE_OR: 2918 case CODE_METHOD_INSTANCE: 2919 case CODE_METHOD_STATIC: 2920 case CODE_IMPORT_MODULE: 2921 case CODE_IMPORT_VARIABLE: 2922 return 2; 2923 2924 case CODE_SUPER_0: 2925 case CODE_SUPER_1: 2926 case CODE_SUPER_2: 2927 case CODE_SUPER_3: 2928 case CODE_SUPER_4: 2929 case CODE_SUPER_5: 2930 case CODE_SUPER_6: 2931 case CODE_SUPER_7: 2932 case CODE_SUPER_8: 2933 case CODE_SUPER_9: 2934 case CODE_SUPER_10: 2935 case CODE_SUPER_11: 2936 case CODE_SUPER_12: 2937 case CODE_SUPER_13: 2938 case CODE_SUPER_14: 2939 case CODE_SUPER_15: 2940 case CODE_SUPER_16: 2941 return 4; 2942 2943 case CODE_CLOSURE: 2944 { 2945 int constant = (bytecode[ip + 1] << 8) | bytecode[ip + 2]; 2946 ObjFn* loadedFn = AS_FN(constants[constant]); 2947 2948 // There are two bytes for the constant, then two for each upvalue. 2949 return 2 + (loadedFn.numUpvalues * 2); 2950 } 2951 default: 2952 throw mallocNew!Error("Unreachable code hit"); 2953 // assert(0, "Unreachable"); 2954 } 2955 } 2956 2957 // Marks the beginning of a loop. Keeps track of the current instruction so we 2958 // know what to loop back to at the end of the body. 2959 static void startLoop(Compiler* compiler, Loop* loop) 2960 { 2961 loop.enclosing = compiler.loop; 2962 loop.start = compiler.fn.code.count - 1; 2963 loop.scopeDepth = compiler.scopeDepth; 2964 compiler.loop = loop; 2965 } 2966 2967 // Emits the [CODE_JUMP_IF] instruction used to test the loop condition and 2968 // potentially exit the loop. Keeps track of the instruction so we can patch it 2969 // later once we know where the end of the body is. 2970 static void testExitLoop(Compiler* compiler) 2971 { 2972 compiler.loop.exitJump = emitJump(compiler, Code.CODE_JUMP_IF); 2973 } 2974 2975 // Compiles the body of the loop and tracks its extent so that contained "break" 2976 // statements can be handled correctly. 2977 static void loopBody(Compiler* compiler) 2978 { 2979 compiler.loop.body_ = compiler.fn.code.count; 2980 statement(compiler); 2981 } 2982 2983 // Ends the current innermost loop. Patches up all jumps and breaks now that 2984 // we know where the end of the loop is. 2985 static void endLoop(Compiler* compiler) 2986 { 2987 // We don't check for overflow here since the forward jump over the loop body 2988 // will report an error for the same problem. 2989 int loopOffset = compiler.fn.code.count - compiler.loop.start + 2; 2990 emitShortArg(compiler, Code.CODE_LOOP, loopOffset); 2991 2992 patchJump(compiler, compiler.loop.exitJump); 2993 2994 // Find any break placeholder instructions (which will be CODE_END in the 2995 // bytecode) and replace them with real jumps. 2996 int i = compiler.loop.body_; 2997 while (i < compiler.fn.code.count) 2998 { 2999 if (compiler.fn.code.data[i] == Code.CODE_END) 3000 { 3001 compiler.fn.code.data[i] = Code.CODE_JUMP; 3002 patchJump(compiler, i + 1); 3003 i += 3; 3004 } 3005 else 3006 { 3007 // Skip this instruction and its arguments. 3008 i += 1 + getByteCountForArguments(compiler.fn.code.data, 3009 compiler.fn.constants.data, i); 3010 } 3011 } 3012 3013 compiler.loop = compiler.loop.enclosing; 3014 } 3015 3016 static void forStatement(Compiler* compiler) 3017 { 3018 // A for statement like: 3019 // 3020 // for (i in sequence.expression) { 3021 // System.print(i) 3022 // } 3023 // 3024 // Is compiled to bytecode almost as if the source looked like this: 3025 // 3026 // { 3027 // var seq_ = sequence.expression 3028 // var iter_ 3029 // while (iter_ = seq_.iterate(iter_)) { 3030 // var i = seq_.iteratorValue(iter_) 3031 // System.print(i) 3032 // } 3033 // } 3034 // 3035 // It's not exactly this, because the synthetic variables `seq_` and `iter_` 3036 // actually get names that aren't valid Wren identfiers, but that's the basic 3037 // idea. 3038 // 3039 // The important parts are: 3040 // - The sequence expression is only evaluated once. 3041 // - The .iterate() method is used to advance the iterator and determine if 3042 // it should exit the loop. 3043 // - The .iteratorValue() method is used to get the value at the current 3044 // iterator position. 3045 3046 // Create a scope for the hidden local variables used for the iterator. 3047 pushScope(compiler); 3048 3049 consume(compiler, TokenType.TOKEN_LEFT_PAREN, "Expect '(' after 'for'."); 3050 consume(compiler, TokenType.TOKEN_NAME, "Expect for loop variable name."); 3051 3052 // Remember the name of the loop variable. 3053 const char* name = compiler.parser.previous.start; 3054 int length = compiler.parser.previous.length; 3055 3056 consume(compiler, TokenType.TOKEN_IN, "Expect 'in' after loop variable."); 3057 ignoreNewlines(compiler); 3058 3059 // Evaluate the sequence expression and store it in a hidden local variable. 3060 // The space in the variable name ensures it won't collide with a user-defined 3061 // variable. 3062 expression(compiler); 3063 3064 // Verify that there is space to hidden local variables. 3065 // Note that we expect only two addLocal calls next to each other in the 3066 // following code. 3067 if (compiler.numLocals + 2 > MAX_LOCALS) 3068 { 3069 error(compiler, "Cannot declare more than %d variables in one scope. (Not enough space for for-loops internal variables)", 3070 MAX_LOCALS); 3071 return; 3072 } 3073 int seqSlot = addLocal(compiler, "seq ", 4); 3074 3075 // Create another hidden local for the iterator object. 3076 null_(compiler, false); 3077 int iterSlot = addLocal(compiler, "iter ", 5); 3078 3079 consume(compiler, TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after loop expression."); 3080 3081 Loop loop; 3082 startLoop(compiler, &loop); 3083 3084 // Advance the iterator by calling the ".iterate" method on the sequence. 3085 loadLocal(compiler, seqSlot); 3086 loadLocal(compiler, iterSlot); 3087 3088 // Update and test the iterator. 3089 callMethod(compiler, 1, "iterate(_)", 10); 3090 emitByteArg(compiler, Code.CODE_STORE_LOCAL, iterSlot); 3091 testExitLoop(compiler); 3092 3093 // Get the current value in the sequence by calling ".iteratorValue". 3094 loadLocal(compiler, seqSlot); 3095 loadLocal(compiler, iterSlot); 3096 callMethod(compiler, 1, "iteratorValue(_)", 16); 3097 3098 // Bind the loop variable in its own scope. This ensures we get a fresh 3099 // variable each iteration so that closures for it don't all see the same one. 3100 pushScope(compiler); 3101 addLocal(compiler, name, length); 3102 3103 loopBody(compiler); 3104 3105 // Loop variable. 3106 popScope(compiler); 3107 3108 endLoop(compiler); 3109 3110 // Hidden variables. 3111 popScope(compiler); 3112 } 3113 3114 static void ifStatement(Compiler* compiler) 3115 { 3116 // Compile the condition. 3117 consume(compiler, TokenType.TOKEN_LEFT_PAREN, "Expect '(' after 'if'."); 3118 expression(compiler); 3119 consume(compiler, TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after if condition."); 3120 3121 // Jump to the else branch if the condition is false. 3122 int ifJump = emitJump(compiler, Code.CODE_JUMP_IF); 3123 3124 // Compile the then branch. 3125 statement(compiler); 3126 3127 // Compile the else branch if there is one. 3128 if (match(compiler, TokenType.TOKEN_ELSE)) 3129 { 3130 // Jump over the else branch when the if branch is taken. 3131 int elseJump = emitJump(compiler, Code.CODE_JUMP); 3132 patchJump(compiler, ifJump); 3133 3134 statement(compiler); 3135 3136 // Patch the jump over the else. 3137 patchJump(compiler, elseJump); 3138 } 3139 else 3140 { 3141 patchJump(compiler, ifJump); 3142 } 3143 } 3144 3145 static void whileStatement(Compiler* compiler) 3146 { 3147 Loop loop; 3148 startLoop(compiler, &loop); 3149 3150 // Compile the condition. 3151 consume(compiler, TokenType.TOKEN_LEFT_PAREN, "Expect '(' after 'while'."); 3152 expression(compiler); 3153 consume(compiler, TokenType.TOKEN_RIGHT_PAREN, "Expect ')' after while condition."); 3154 3155 testExitLoop(compiler); 3156 loopBody(compiler); 3157 endLoop(compiler); 3158 } 3159 3160 // Compiles a simple statement. These can only appear at the top-level or 3161 // within curly blocks. Simple statements exclude variable binding statements 3162 // like "var" and "class" which are not allowed directly in places like the 3163 // branches of an "if" statement. 3164 // 3165 // Unlike expressions, statements do not leave a value on the stack. 3166 void statement(Compiler* compiler) 3167 { 3168 if (match(compiler, TokenType.TOKEN_BREAK)) 3169 { 3170 if (compiler.loop == null) 3171 { 3172 error(compiler, "Cannot use 'break' outside of a loop."); 3173 return; 3174 } 3175 3176 // Since we will be jumping out of the scope, make sure any locals in it 3177 // are discarded first. 3178 discardLocals(compiler, compiler.loop.scopeDepth + 1); 3179 3180 // Emit a placeholder instruction for the jump to the end of the body. When 3181 // we're done compiling the loop body and know where the end is, we'll 3182 // replace these with `CODE_JUMP` instructions with appropriate offsets. 3183 // We use `CODE_END` here because that can't occur in the middle of 3184 // bytecode. 3185 emitJump(compiler, Code.CODE_END); 3186 } 3187 else if (match(compiler, TokenType.TOKEN_CONTINUE)) 3188 { 3189 if (compiler.loop == null) 3190 { 3191 error(compiler, "Cannot use 'continue' outside of a loop."); 3192 return; 3193 } 3194 3195 // Since we will be jumping out of the scope, make sure any locals in it 3196 // are discarded first. 3197 discardLocals(compiler, compiler.loop.scopeDepth + 1); 3198 3199 // emit a jump back to the top of the loop 3200 int loopOffset = compiler.fn.code.count - compiler.loop.start + 2; 3201 emitShortArg(compiler, Code.CODE_LOOP, loopOffset); 3202 } 3203 else if (match(compiler, TokenType.TOKEN_FOR)) 3204 { 3205 forStatement(compiler); 3206 } 3207 else if (match(compiler, TokenType.TOKEN_IF)) 3208 { 3209 ifStatement(compiler); 3210 } 3211 else if (match(compiler, TokenType.TOKEN_RETURN)) 3212 { 3213 // Compile the return value. 3214 if (peek(compiler) == TokenType.TOKEN_LINE) 3215 { 3216 // If there's no expression after return, initializers should 3217 // return 'this' and regular methods should return null 3218 Code result = compiler.isInitializer ? Code.CODE_LOAD_LOCAL_0 : Code.CODE_NULL; 3219 emitOp(compiler, result); 3220 } 3221 else 3222 { 3223 if (compiler.isInitializer) 3224 { 3225 error(compiler, "A constructor cannot return a value."); 3226 } 3227 3228 expression(compiler); 3229 } 3230 3231 emitOp(compiler, Code.CODE_RETURN); 3232 } 3233 else if (match(compiler, TokenType.TOKEN_WHILE)) 3234 { 3235 whileStatement(compiler); 3236 } 3237 else if (match(compiler, TokenType.TOKEN_LEFT_BRACE)) 3238 { 3239 // Block statement. 3240 pushScope(compiler); 3241 if (finishBlock(compiler)) 3242 { 3243 // Block was an expression, so discard it. 3244 emitOp(compiler, Code.CODE_POP); 3245 } 3246 popScope(compiler); 3247 } 3248 else 3249 { 3250 // Expression statement. 3251 expression(compiler); 3252 emitOp(compiler, Code.CODE_POP); 3253 } 3254 } 3255 3256 // Creates a matching constructor method for an initializer with [signature] 3257 // and [initializerSymbol]. 3258 // 3259 // Construction is a two-stage process in Wren that involves two separate 3260 // methods. There is a static method that allocates a new instance of the class. 3261 // It then invokes an initializer method on the new instance, forwarding all of 3262 // the constructor arguments to it. 3263 // 3264 // The allocator method always has a fixed implementation: 3265 // 3266 // CODE_CONSTRUCT - Replace the class in slot 0 with a new instance of it. 3267 // CODE_CALL - Invoke the initializer on the new instance. 3268 // 3269 // This creates that method and calls the initializer with [initializerSymbol]. 3270 static void createConstructor(Compiler* compiler, Signature* signature, 3271 int initializerSymbol) 3272 { 3273 Compiler methodCompiler; 3274 initCompiler(&methodCompiler, compiler.parser, compiler, true); 3275 3276 // Allocate the instance. 3277 emitOp(&methodCompiler, compiler.enclosingClass.isForeign 3278 ? Code.CODE_FOREIGN_CONSTRUCT : Code.CODE_CONSTRUCT); 3279 3280 // Run its initializer. 3281 emitShortArg(&methodCompiler, cast(Code)(Code.CODE_CALL_0 + signature.arity), 3282 initializerSymbol); 3283 3284 // Return the instance. 3285 emitOp(&methodCompiler, Code.CODE_RETURN); 3286 3287 endCompiler(&methodCompiler, "", 0); 3288 } 3289 3290 // Loads the enclosing class onto the stack and then binds the function already 3291 // on the stack as a method on that class. 3292 static void defineMethod(Compiler* compiler, Variable classVariable, 3293 bool isStatic, int methodSymbol) 3294 { 3295 // Load the class. We have to do this for each method because we can't 3296 // keep the class on top of the stack. If there are static fields, they 3297 // will be locals above the initial variable slot for the class on the 3298 // stack. To skip past those, we just load the class each time right before 3299 // defining a method. 3300 loadVariable(compiler, classVariable); 3301 3302 // Define the method. 3303 Code instruction = isStatic ? Code.CODE_METHOD_STATIC : Code.CODE_METHOD_INSTANCE; 3304 emitShortArg(compiler, instruction, methodSymbol); 3305 } 3306 3307 // Declares a method in the enclosing class with [signature]. 3308 // 3309 // Reports an error if a method with that signature is already declared. 3310 // Returns the symbol for the method. 3311 static int declareMethod(Compiler* compiler, Signature* signature, 3312 const(char)* name, int length) 3313 { 3314 int symbol = signatureSymbol(compiler, signature); 3315 3316 // See if the class has already declared method with this signature. 3317 ClassInfo* classInfo = compiler.enclosingClass; 3318 IntBuffer* methods = classInfo.inStatic 3319 ? &classInfo.staticMethods : &classInfo.methods; 3320 for (int i = 0; i < methods.count; i++) 3321 { 3322 if (methods.data[i] == symbol) 3323 { 3324 const(char)* staticPrefix = classInfo.inStatic ? "static " : ""; 3325 error(compiler, "Class %s already defines a %smethod '%s'.", 3326 compiler.enclosingClass.name.value.ptr, staticPrefix, name); 3327 break; 3328 } 3329 } 3330 3331 wrenIntBufferWrite(compiler.parser.vm, methods, symbol); 3332 return symbol; 3333 } 3334 3335 static Value consumeLiteral(Compiler* compiler, const char* message) 3336 { 3337 if(match(compiler, TokenType.TOKEN_FALSE)) return FALSE_VAL; 3338 if(match(compiler, TokenType.TOKEN_TRUE)) return TRUE_VAL; 3339 if(match(compiler, TokenType.TOKEN_NUMBER)) return compiler.parser.previous.value; 3340 if(match(compiler, TokenType.TOKEN_STRING)) return compiler.parser.previous.value; 3341 if(match(compiler, TokenType.TOKEN_NAME)) return compiler.parser.previous.value; 3342 3343 error(compiler, message); 3344 nextToken(compiler.parser); 3345 return NULL_VAL; 3346 } 3347 3348 static bool matchAttribute(Compiler* compiler) { 3349 3350 if(match(compiler, TokenType.TOKEN_HASH)) 3351 { 3352 compiler.numAttributes++; 3353 bool runtimeAccess = match(compiler, TokenType.TOKEN_BANG); 3354 if(match(compiler, TokenType.TOKEN_NAME)) 3355 { 3356 Value group = compiler.parser.previous.value; 3357 TokenType ahead = peek(compiler); 3358 if(ahead == TokenType.TOKEN_EQ || ahead == TokenType.TOKEN_LINE) 3359 { 3360 Value key = group; 3361 Value value = NULL_VAL; 3362 if(match(compiler, TokenType.TOKEN_EQ)) 3363 { 3364 value = consumeLiteral(compiler, "Expect a Bool, Num, String or Identifier literal for an attribute value."); 3365 } 3366 if(runtimeAccess) addToAttributeGroup(compiler, NULL_VAL, key, value); 3367 } 3368 else if(match(compiler, TokenType.TOKEN_LEFT_PAREN)) 3369 { 3370 ignoreNewlines(compiler); 3371 if(match(compiler, TokenType.TOKEN_RIGHT_PAREN)) 3372 { 3373 error(compiler, "Expected attributes in group, group cannot be empty."); 3374 } 3375 else 3376 { 3377 while(peek(compiler) != TokenType.TOKEN_RIGHT_PAREN) 3378 { 3379 consume(compiler, TokenType.TOKEN_NAME, "Expect name for attribute key."); 3380 Value key = compiler.parser.previous.value; 3381 Value value = NULL_VAL; 3382 if(match(compiler, TokenType.TOKEN_EQ)) 3383 { 3384 value = consumeLiteral(compiler, "Expect a Bool, Num, String or Identifier literal for an attribute value."); 3385 } 3386 if(runtimeAccess) addToAttributeGroup(compiler, group, key, value); 3387 ignoreNewlines(compiler); 3388 if(!match(compiler, TokenType.TOKEN_COMMA)) break; 3389 ignoreNewlines(compiler); 3390 } 3391 ignoreNewlines(compiler); 3392 consume(compiler, TokenType.TOKEN_RIGHT_PAREN, 3393 "Expected ')' after grouped attributes."); 3394 } 3395 } 3396 else 3397 { 3398 error(compiler, "Expect an equal, newline or grouping after an attribute key."); 3399 } 3400 } 3401 else 3402 { 3403 error(compiler, "Expect an attribute definition after #."); 3404 } 3405 consumeLine(compiler, "Expect newline after attribute."); 3406 return true; 3407 } 3408 3409 return false; 3410 } 3411 3412 // Compiles a method definition inside a class body. 3413 // 3414 // Returns `true` if it compiled successfully, or `false` if the method couldn't 3415 // be parsed. 3416 static bool method(Compiler* compiler, Variable classVariable) 3417 { 3418 // Parse any attributes before the method and store them 3419 if(matchAttribute(compiler)) { 3420 return method(compiler, classVariable); 3421 } 3422 3423 // TODO: What about foreign constructors? 3424 bool isForeign = match(compiler, TokenType.TOKEN_FOREIGN); 3425 bool isStatic = match(compiler, TokenType.TOKEN_STATIC); 3426 compiler.enclosingClass.inStatic = isStatic; 3427 3428 SignatureFn signatureFn = rules[compiler.parser.current.type].method; 3429 nextToken(compiler.parser); 3430 3431 if (signatureFn == null) 3432 { 3433 error(compiler, "Expect method definition."); 3434 return false; 3435 } 3436 3437 // Build the method signature. 3438 Signature signature = signatureFromToken(compiler, SignatureType.SIG_GETTER); 3439 compiler.enclosingClass.signature = &signature; 3440 3441 Compiler methodCompiler; 3442 initCompiler(&methodCompiler, compiler.parser, compiler, true); 3443 3444 // Compile the method signature. 3445 signatureFn(&methodCompiler, &signature); 3446 3447 methodCompiler.isInitializer = signature.type == SignatureType.SIG_INITIALIZER; 3448 3449 if (isStatic && signature.type == SignatureType.SIG_INITIALIZER) 3450 { 3451 error(compiler, "A constructor cannot be static."); 3452 } 3453 3454 // Include the full signature in debug messages in stack traces. 3455 char[MAX_METHOD_SIGNATURE] fullSignature = 0; 3456 int length; 3457 signatureToString(&signature, fullSignature.ptr, &length); 3458 3459 // Copy any attributes the compiler collected into the enclosing class 3460 copyMethodAttributes(compiler, isForeign, isStatic, fullSignature.ptr, length); 3461 3462 // Check for duplicate methods. Doesn't matter that it's already been 3463 // defined, error will discard bytecode anyway. 3464 // Check if the method table already contains this symbol 3465 int methodSymbol = declareMethod(compiler, &signature, fullSignature.ptr, length); 3466 3467 if (isForeign) 3468 { 3469 // Define a constant for the signature. 3470 emitConstant(compiler, wrenNewStringLength(compiler.parser.vm, 3471 fullSignature.ptr, length)); 3472 3473 // We don't need the function we started compiling in the parameter list 3474 // any more. 3475 methodCompiler.parser.vm.compiler = methodCompiler.parent; 3476 } 3477 else 3478 { 3479 consume(compiler, TokenType.TOKEN_LEFT_BRACE, "Expect '{' to begin method body."); 3480 finishBody(&methodCompiler); 3481 endCompiler(&methodCompiler, fullSignature.ptr, length); 3482 } 3483 3484 // Define the method. For a constructor, this defines the instance 3485 // initializer method. 3486 defineMethod(compiler, classVariable, isStatic, methodSymbol); 3487 3488 if (signature.type == SignatureType.SIG_INITIALIZER) 3489 { 3490 // Also define a matching constructor method on the metaclass. 3491 signature.type = SignatureType.SIG_METHOD; 3492 int constructorSymbol = signatureSymbol(compiler, &signature); 3493 3494 createConstructor(compiler, &signature, methodSymbol); 3495 defineMethod(compiler, classVariable, true, constructorSymbol); 3496 } 3497 3498 return true; 3499 } 3500 3501 // Compiles a class definition. Assumes the "class" token has already been 3502 // consumed (along with a possibly preceding "foreign" token). 3503 static void classDefinition(Compiler* compiler, bool isForeign) 3504 { 3505 // Create a variable to store the class in. 3506 Variable classVariable; 3507 classVariable.scope_ = compiler.scopeDepth == -1 ? Scope.SCOPE_MODULE : Scope.SCOPE_LOCAL; 3508 classVariable.index = declareNamedVariable(compiler); 3509 3510 // Create shared class name value 3511 Value classNameString = wrenNewStringLength(compiler.parser.vm, 3512 compiler.parser.previous.start, compiler.parser.previous.length); 3513 3514 // Create class name string to track method duplicates 3515 ObjString* className = AS_STRING(classNameString); 3516 3517 // Make a string constant for the name. 3518 emitConstant(compiler, classNameString); 3519 3520 // Load the superclass (if there is one). 3521 if (match(compiler, TokenType.TOKEN_IS)) 3522 { 3523 parsePrecedence(compiler, Precedence.PREC_CALL); 3524 } 3525 else 3526 { 3527 // Implicitly inherit from Object. 3528 loadCoreVariable(compiler, "Object"); 3529 } 3530 3531 // Store a placeholder for the number of fields argument. We don't know the 3532 // count until we've compiled all the methods to see which fields are used. 3533 int numFieldsInstruction = -1; 3534 if (isForeign) 3535 { 3536 emitOp(compiler, Code.CODE_FOREIGN_CLASS); 3537 } 3538 else 3539 { 3540 numFieldsInstruction = emitByteArg(compiler, Code.CODE_CLASS, 255); 3541 } 3542 3543 // Store it in its name. 3544 defineVariable(compiler, classVariable.index); 3545 3546 // Push a local variable scope. Static fields in a class body are hoisted out 3547 // into local variables declared in this scope. Methods that use them will 3548 // have upvalues referencing them. 3549 pushScope(compiler); 3550 3551 ClassInfo classInfo; 3552 classInfo.isForeign = isForeign; 3553 classInfo.name = className; 3554 3555 // Allocate attribute maps if necessary. 3556 // A method will allocate the methods one if needed 3557 classInfo.classAttributes = compiler.attributes.count > 0 3558 ? wrenNewMap(compiler.parser.vm) 3559 : null; 3560 classInfo.methodAttributes = null; 3561 // Copy any existing attributes into the class 3562 copyAttributes(compiler, classInfo.classAttributes); 3563 3564 // Set up a symbol table for the class's fields. We'll initially compile 3565 // them to slots starting at zero. When the method is bound to the class, the 3566 // bytecode will be adjusted by [wrenBindMethod] to take inherited fields 3567 // into account. 3568 wrenSymbolTableInit(&classInfo.fields); 3569 3570 // Set up symbol buffers to track duplicate static and instance methods. 3571 wrenIntBufferInit(&classInfo.methods); 3572 wrenIntBufferInit(&classInfo.staticMethods); 3573 compiler.enclosingClass = &classInfo; 3574 3575 // Compile the method definitions. 3576 consume(compiler, TokenType.TOKEN_LEFT_BRACE, "Expect '{' after class declaration."); 3577 matchLine(compiler); 3578 3579 while (!match(compiler, TokenType.TOKEN_RIGHT_BRACE)) 3580 { 3581 if (!method(compiler, classVariable)) break; 3582 3583 // Don't require a newline after the last definition. 3584 if (match(compiler, TokenType.TOKEN_RIGHT_BRACE)) break; 3585 3586 consumeLine(compiler, "Expect newline after definition in class."); 3587 } 3588 3589 // If any attributes are present, 3590 // instantiate a ClassAttributes instance for the class 3591 // and send it over to CODE_END_CLASS 3592 bool hasAttr = classInfo.classAttributes != null || 3593 classInfo.methodAttributes != null; 3594 if(hasAttr) { 3595 emitClassAttributes(compiler, &classInfo); 3596 loadVariable(compiler, classVariable); 3597 // At the moment, we don't have other uses for CODE_END_CLASS, 3598 // so we put it inside this condition. Later, we can always 3599 // emit it and use it as needed. 3600 emitOp(compiler, Code.CODE_END_CLASS); 3601 } 3602 3603 // Update the class with the number of fields. 3604 if (!isForeign) 3605 { 3606 compiler.fn.code.data[numFieldsInstruction] = 3607 cast(ubyte)classInfo.fields.count; 3608 } 3609 3610 // Clear symbol tables for tracking field and method names. 3611 wrenSymbolTableClear(compiler.parser.vm, &classInfo.fields); 3612 wrenIntBufferClear(compiler.parser.vm, &classInfo.methods); 3613 wrenIntBufferClear(compiler.parser.vm, &classInfo.staticMethods); 3614 compiler.enclosingClass = null; 3615 popScope(compiler); 3616 } 3617 3618 // Compiles an "import" statement. 3619 // 3620 // An import compiles to a series of instructions. Given: 3621 // 3622 // import "foo" for Bar, Baz 3623 // 3624 // We compile a single IMPORT_MODULE "foo" instruction to load the module 3625 // itself. When that finishes executing the imported module, it leaves the 3626 // ObjModule in vm.lastModule. Then, for Bar and Baz, we: 3627 // 3628 // * Declare a variable in the current scope with that name. 3629 // * Emit an IMPORT_VARIABLE instruction to load the variable's value from the 3630 // other module. 3631 // * Compile the code to store that value in the variable in this scope. 3632 static void import_(Compiler* compiler) 3633 { 3634 ignoreNewlines(compiler); 3635 consume(compiler, TokenType.TOKEN_STRING, "Expect a string after 'import'."); 3636 int moduleConstant = addConstant(compiler, compiler.parser.previous.value); 3637 3638 // Load the module. 3639 emitShortArg(compiler, Code.CODE_IMPORT_MODULE, moduleConstant); 3640 3641 // Discard the unused result value from calling the module body's closure. 3642 emitOp(compiler, Code.CODE_POP); 3643 3644 // The for clause is optional. 3645 if (!match(compiler, TokenType.TOKEN_FOR)) return; 3646 3647 // Compile the comma-separated list of variables to import. 3648 do 3649 { 3650 ignoreNewlines(compiler); 3651 3652 consume(compiler, TokenType.TOKEN_NAME, "Expect variable name."); 3653 3654 // We need to hold onto the source variable, 3655 // in order to reference it in the import later 3656 Token sourceVariableToken = compiler.parser.previous; 3657 3658 // Define a string constant for the original variable name. 3659 int sourceVariableConstant = addConstant(compiler, 3660 wrenNewStringLength(compiler.parser.vm, 3661 sourceVariableToken.start, 3662 sourceVariableToken.length)); 3663 3664 // Store the symbol we care about for the variable 3665 int slot = -1; 3666 if(match(compiler, TokenType.TOKEN_AS)) 3667 { 3668 //import "module" for Source as Dest 3669 //Use 'Dest' as the name by declaring a new variable for it. 3670 //This parses a name after the 'as' and defines it. 3671 slot = declareNamedVariable(compiler); 3672 } 3673 else 3674 { 3675 //import "module" for Source 3676 //Uses 'Source' as the name directly 3677 slot = declareVariable(compiler, &sourceVariableToken); 3678 } 3679 3680 // Load the variable from the other module. 3681 emitShortArg(compiler, Code.CODE_IMPORT_VARIABLE, sourceVariableConstant); 3682 3683 // Store the result in the variable here. 3684 defineVariable(compiler, slot); 3685 } while (match(compiler, TokenType.TOKEN_COMMA)); 3686 } 3687 3688 // Compiles a "var" variable definition statement. 3689 static void variableDefinition(Compiler* compiler) 3690 { 3691 // Grab its name, but don't declare it yet. A (local) variable shouldn't be 3692 // in scope in its own initializer. 3693 consume(compiler, TokenType.TOKEN_NAME, "Expect variable name."); 3694 Token nameToken = compiler.parser.previous; 3695 3696 // Compile the initializer. 3697 if (match(compiler, TokenType.TOKEN_EQ)) 3698 { 3699 ignoreNewlines(compiler); 3700 expression(compiler); 3701 } 3702 else 3703 { 3704 // Default initialize it to null. 3705 null_(compiler, false); 3706 } 3707 3708 // Now put it in scope. 3709 int symbol = declareVariable(compiler, &nameToken); 3710 defineVariable(compiler, symbol); 3711 } 3712 3713 // Compiles a "definition". These are the statements that bind new variables. 3714 // They can only appear at the top level of a block and are prohibited in places 3715 // like the non-curly body of an if or while. 3716 void definition(Compiler* compiler) 3717 { 3718 if(matchAttribute(compiler)) { 3719 definition(compiler); 3720 return; 3721 } 3722 3723 if (match(compiler, TokenType.TOKEN_CLASS)) 3724 { 3725 classDefinition(compiler, false); 3726 return; 3727 } 3728 else if (match(compiler, TokenType.TOKEN_FOREIGN)) 3729 { 3730 consume(compiler, TokenType.TOKEN_CLASS, "Expect 'class' after 'foreign'."); 3731 classDefinition(compiler, true); 3732 return; 3733 } 3734 3735 disallowAttributes(compiler); 3736 3737 if (match(compiler, TokenType.TOKEN_IMPORT)) 3738 { 3739 import_(compiler); 3740 } 3741 else if (match(compiler, TokenType.TOKEN_VAR)) 3742 { 3743 variableDefinition(compiler); 3744 } 3745 else 3746 { 3747 statement(compiler); 3748 } 3749 } 3750 3751 ObjFn* wrenCompile(WrenVM* vm, ObjModule* module_, const(char)* source, 3752 bool isExpression, bool printErrors) 3753 { 3754 import core.stdc.string : strncmp; 3755 // Skip the UTF-8 BOM if there is one. 3756 if (strncmp(source, "\xEF\xBB\xBF", 3) == 0) source += 3; 3757 3758 Parser parser; 3759 parser.vm = vm; 3760 parser.module_ = module_; 3761 parser.source = source; 3762 3763 parser.tokenStart = source; 3764 parser.currentChar = source; 3765 parser.currentLine = 1; 3766 parser.numParens = 0; 3767 3768 // Zero-init the current token. This will get copied to previous when 3769 // nextToken() is called below. 3770 parser.next.type = TokenType.TOKEN_ERROR; 3771 parser.next.start = source; 3772 parser.next.length = 0; 3773 parser.next.line = 0; 3774 parser.next.value = UNDEFINED_VAL; 3775 3776 parser.printErrors = printErrors; 3777 parser.hasError = false; 3778 3779 // Read the first token into next 3780 nextToken(&parser); 3781 // Copy next . current 3782 nextToken(&parser); 3783 3784 int numExistingVariables = module_.variables.count; 3785 3786 Compiler compiler; 3787 initCompiler(&compiler, &parser, null, false); 3788 ignoreNewlines(&compiler); 3789 3790 if (isExpression) 3791 { 3792 expression(&compiler); 3793 consume(&compiler, TokenType.TOKEN_EOF, "Expect end of expression."); 3794 } 3795 else 3796 { 3797 while (!match(&compiler, TokenType.TOKEN_EOF)) 3798 { 3799 definition(&compiler); 3800 3801 // If there is no newline, it must be the end of file on the same line. 3802 if (!matchLine(&compiler)) 3803 { 3804 consume(&compiler, TokenType.TOKEN_EOF, "Expect end of file."); 3805 break; 3806 } 3807 } 3808 3809 emitOp(&compiler, Code.CODE_END_MODULE); 3810 } 3811 3812 emitOp(&compiler, Code.CODE_RETURN); 3813 3814 // See if there are any implicitly declared module-level variables that never 3815 // got an explicit definition. They will have values that are numbers 3816 // indicating the line where the variable was first used. 3817 for (int i = numExistingVariables; i < parser.module_.variables.count; i++) 3818 { 3819 if (IS_NUM(parser.module_.variables.data[i])) 3820 { 3821 // Synthesize a token for the original use site. 3822 parser.previous.type = TokenType.TOKEN_NAME; 3823 parser.previous.start = parser.module_.variableNames.data[i].value.ptr; 3824 parser.previous.length = parser.module_.variableNames.data[i].length; 3825 parser.previous.line = cast(int)AS_NUM(parser.module_.variables.data[i]); 3826 error(&compiler, "Variable is used but not defined."); 3827 } 3828 } 3829 3830 return endCompiler(&compiler, "(script)", 8); 3831 } 3832 3833 void wrenBindMethodCode(ObjClass* classObj, ObjFn* fn) 3834 { 3835 int ip = 0; 3836 for (;;) 3837 { 3838 Code instruction = cast(Code)fn.code.data[ip]; 3839 switch (instruction) with(Code) 3840 { 3841 case CODE_LOAD_FIELD: 3842 case CODE_STORE_FIELD: 3843 case CODE_LOAD_FIELD_THIS: 3844 case CODE_STORE_FIELD_THIS: 3845 // Shift this class's fields down past the inherited ones. We don't 3846 // check for overflow here because we'll see if the number of fields 3847 // overflows when the subclass is created. 3848 fn.code.data[ip + 1] += classObj.superclass.numFields; 3849 break; 3850 3851 case CODE_SUPER_0: 3852 case CODE_SUPER_1: 3853 case CODE_SUPER_2: 3854 case CODE_SUPER_3: 3855 case CODE_SUPER_4: 3856 case CODE_SUPER_5: 3857 case CODE_SUPER_6: 3858 case CODE_SUPER_7: 3859 case CODE_SUPER_8: 3860 case CODE_SUPER_9: 3861 case CODE_SUPER_10: 3862 case CODE_SUPER_11: 3863 case CODE_SUPER_12: 3864 case CODE_SUPER_13: 3865 case CODE_SUPER_14: 3866 case CODE_SUPER_15: 3867 case CODE_SUPER_16: 3868 { 3869 // Fill in the constant slot with a reference to the superclass. 3870 int constant = (fn.code.data[ip + 3] << 8) | fn.code.data[ip + 4]; 3871 fn.constants.data[constant] = OBJ_VAL(classObj.superclass); 3872 break; 3873 } 3874 3875 case CODE_CLOSURE: 3876 { 3877 // Bind the nested closure too. 3878 int constant = (fn.code.data[ip + 1] << 8) | fn.code.data[ip + 2]; 3879 wrenBindMethodCode(classObj, AS_FN(fn.constants.data[constant])); 3880 break; 3881 } 3882 3883 case CODE_END: 3884 return; 3885 3886 default: 3887 // Other instructions are unaffected, so just skip over them. 3888 break; 3889 } 3890 ip += 1 + getByteCountForArguments(fn.code.data, fn.constants.data, ip); 3891 } 3892 } 3893 3894 void wrenMarkCompiler(WrenVM* vm, Compiler* compiler) 3895 { 3896 wrenGrayValue(vm, compiler.parser.current.value); 3897 wrenGrayValue(vm, compiler.parser.previous.value); 3898 wrenGrayValue(vm, compiler.parser.next.value); 3899 3900 // Walk up the parent chain to mark the outer compilers too. The VM only 3901 // tracks the innermost one. 3902 do 3903 { 3904 wrenGrayObj(vm, cast(Obj*)compiler.fn); 3905 wrenGrayObj(vm, cast(Obj*)compiler.constants); 3906 wrenGrayObj(vm, cast(Obj*)compiler.attributes); 3907 3908 if (compiler.enclosingClass != null) 3909 { 3910 wrenBlackenSymbolTable(vm, &compiler.enclosingClass.fields); 3911 3912 if(compiler.enclosingClass.methodAttributes != null) 3913 { 3914 wrenGrayObj(vm, cast(Obj*)compiler.enclosingClass.methodAttributes); 3915 } 3916 if(compiler.enclosingClass.classAttributes != null) 3917 { 3918 wrenGrayObj(vm, cast(Obj*)compiler.enclosingClass.classAttributes); 3919 } 3920 } 3921 3922 compiler = compiler.parent; 3923 } 3924 while (compiler != null); 3925 } 3926 3927 // Helpers for Attributes 3928 3929 // Throw an error if any attributes were found preceding, 3930 // and clear the attributes so the error doesn't keep happening. 3931 static void disallowAttributes(Compiler* compiler) 3932 { 3933 if (compiler.numAttributes > 0) 3934 { 3935 error(compiler, "Attributes can only specified before a class or a method"); 3936 wrenMapClear(compiler.parser.vm, compiler.attributes); 3937 compiler.numAttributes = 0; 3938 } 3939 } 3940 // Add an attribute to a given group in the compiler attribues map 3941 static void addToAttributeGroup(Compiler* compiler, 3942 Value group, Value key, Value value) 3943 { 3944 WrenVM* vm = compiler.parser.vm; 3945 3946 if(IS_OBJ(group)) wrenPushRoot(vm, AS_OBJ(group)); 3947 if(IS_OBJ(key)) wrenPushRoot(vm, AS_OBJ(key)); 3948 if(IS_OBJ(value)) wrenPushRoot(vm, AS_OBJ(value)); 3949 3950 Value groupMapValue = wrenMapGet(compiler.attributes, group); 3951 if(IS_UNDEFINED(groupMapValue)) 3952 { 3953 groupMapValue = OBJ_VAL(wrenNewMap(vm)); 3954 wrenMapSet(vm, compiler.attributes, group, groupMapValue); 3955 } 3956 3957 //we store them as a map per so we can maintain duplicate keys 3958 //group = { key:[value, ...], } 3959 ObjMap* groupMap = AS_MAP(groupMapValue); 3960 3961 //var keyItems = group[key] 3962 //if(!keyItems) keyItems = group[key] = [] 3963 Value keyItemsValue = wrenMapGet(groupMap, key); 3964 if(IS_UNDEFINED(keyItemsValue)) 3965 { 3966 keyItemsValue = OBJ_VAL(wrenNewList(vm, 0)); 3967 wrenMapSet(vm, groupMap, key, keyItemsValue); 3968 } 3969 3970 //keyItems.add(value) 3971 ObjList* keyItems = AS_LIST(keyItemsValue); 3972 wrenValueBufferWrite(vm, &keyItems.elements, value); 3973 3974 if(IS_OBJ(group)) wrenPopRoot(vm); 3975 if(IS_OBJ(key)) wrenPopRoot(vm); 3976 if(IS_OBJ(value)) wrenPopRoot(vm); 3977 } 3978 3979 3980 // Emit the attributes in the give map onto the stack 3981 static void emitAttributes(Compiler* compiler, ObjMap* attributes) 3982 { 3983 // Instantiate a new map for the attributes 3984 loadCoreVariable(compiler, "Map"); 3985 callMethod(compiler, 0, "new()", 5); 3986 3987 // The attributes are stored as group = { key:[value, value, ...] } 3988 // so our first level is the group map 3989 for(uint groupIdx = 0; groupIdx < attributes.capacity; groupIdx++) 3990 { 3991 const MapEntry* groupEntry = &attributes.entries[groupIdx]; 3992 if(IS_UNDEFINED(groupEntry.key)) continue; 3993 //group key 3994 emitConstant(compiler, groupEntry.key); 3995 3996 //group value is gonna be a map 3997 loadCoreVariable(compiler, "Map"); 3998 callMethod(compiler, 0, "new()", 5); 3999 4000 ObjMap* groupItems = AS_MAP(groupEntry.value); 4001 for(uint itemIdx = 0; itemIdx < groupItems.capacity; itemIdx++) 4002 { 4003 const MapEntry* itemEntry = &groupItems.entries[itemIdx]; 4004 if(IS_UNDEFINED(itemEntry.key)) continue; 4005 4006 emitConstant(compiler, itemEntry.key); 4007 // Attribute key value, key = [] 4008 loadCoreVariable(compiler, "List"); 4009 callMethod(compiler, 0, "new()", 5); 4010 // Add the items to the key list 4011 ObjList* items = AS_LIST(itemEntry.value); 4012 for(int itemIdx2 = 0; itemIdx2 < items.elements.count; ++itemIdx2) 4013 { 4014 emitConstant(compiler, items.elements.data[itemIdx2]); 4015 callMethod(compiler, 1, "addCore_(_)", 11); 4016 } 4017 // Add the list to the map 4018 callMethod(compiler, 2, "addCore_(_,_)", 13); 4019 } 4020 4021 // Add the key/value to the map 4022 callMethod(compiler, 2, "addCore_(_,_)", 13); 4023 } 4024 4025 } 4026 4027 // Methods are stored as method <. attributes, so we have to have 4028 // an indirection to resolve for methods 4029 static void emitAttributeMethods(Compiler* compiler, ObjMap* attributes) 4030 { 4031 // Instantiate a new map for the attributes 4032 loadCoreVariable(compiler, "Map"); 4033 callMethod(compiler, 0, "new()", 5); 4034 4035 for(uint methodIdx = 0; methodIdx < attributes.capacity; methodIdx++) 4036 { 4037 const MapEntry* methodEntry = &attributes.entries[methodIdx]; 4038 if(IS_UNDEFINED(methodEntry.key)) continue; 4039 emitConstant(compiler, methodEntry.key); 4040 ObjMap* attributeMap = AS_MAP(methodEntry.value); 4041 emitAttributes(compiler, attributeMap); 4042 callMethod(compiler, 2, "addCore_(_,_)", 13); 4043 } 4044 } 4045 4046 4047 // Emit the final ClassAttributes that exists at runtime 4048 static void emitClassAttributes(Compiler* compiler, ClassInfo* classInfo) 4049 { 4050 loadCoreVariable(compiler, "ClassAttributes"); 4051 4052 classInfo.classAttributes 4053 ? emitAttributes(compiler, classInfo.classAttributes) 4054 : null_(compiler, false); 4055 4056 classInfo.methodAttributes 4057 ? emitAttributeMethods(compiler, classInfo.methodAttributes) 4058 : null_(compiler, false); 4059 4060 callMethod(compiler, 2, "new(_,_)", 8); 4061 } 4062 4063 // Copy the current attributes stored in the compiler into a destination map 4064 // This also resets the counter, since the intent is to consume the attributes 4065 static void copyAttributes(Compiler* compiler, ObjMap* into) 4066 { 4067 compiler.numAttributes = 0; 4068 4069 if(compiler.attributes.count == 0) return; 4070 if(into == null) return; 4071 4072 WrenVM* vm = compiler.parser.vm; 4073 4074 // Note we copy the actual values as is since we'll take ownership 4075 // and clear the original map 4076 for(uint attrIdx = 0; attrIdx < compiler.attributes.capacity; attrIdx++) 4077 { 4078 const MapEntry* attrEntry = &compiler.attributes.entries[attrIdx]; 4079 if(IS_UNDEFINED(attrEntry.key)) continue; 4080 wrenMapSet(vm, into, attrEntry.key, attrEntry.value); 4081 } 4082 4083 wrenMapClear(vm, compiler.attributes); 4084 } 4085 4086 // Copy the current attributes stored in the compiler into the method specific 4087 // attributes for the current enclosingClass. 4088 // This also resets the counter, since the intent is to consume the attributes 4089 static void copyMethodAttributes(Compiler* compiler, bool isForeign, 4090 bool isStatic, const char* fullSignature, int length) 4091 { 4092 import core.stdc.stdio : sprintf; 4093 compiler.numAttributes = 0; 4094 4095 if(compiler.attributes.count == 0) return; 4096 4097 WrenVM* vm = compiler.parser.vm; 4098 4099 // Make a map for this method to copy into 4100 ObjMap* methodAttr = wrenNewMap(vm); 4101 wrenPushRoot(vm, cast(Obj*)methodAttr); 4102 copyAttributes(compiler, methodAttr); 4103 4104 // Include 'foreign static ' in front as needed 4105 int fullLength = length; 4106 if(isForeign) fullLength += 8; 4107 if(isStatic) fullLength += 7; 4108 char[MAX_METHOD_SIGNATURE + 8 + 7] fullSignatureWithPrefix = 0; 4109 const char* foreignPrefix = isForeign ? "foreign " : ""; 4110 const char* staticPrefix = isStatic ? "static " : ""; 4111 sprintf(fullSignatureWithPrefix.ptr, "%s%s%.*s", foreignPrefix, staticPrefix, 4112 length, fullSignature); 4113 fullSignatureWithPrefix[fullLength] = '\0'; 4114 4115 if(compiler.enclosingClass.methodAttributes == null) { 4116 compiler.enclosingClass.methodAttributes = wrenNewMap(vm); 4117 } 4118 4119 // Store the method attributes in the class map 4120 Value key = wrenNewStringLength(vm, fullSignatureWithPrefix.ptr, fullLength); 4121 wrenMapSet(vm, compiler.enclosingClass.methodAttributes, key, OBJ_VAL(methodAttr)); 4122 4123 wrenPopRoot(vm); 4124 }