├── README.md ├── package.json ├── assets └── style.css ├── index.html ├── LICENSE.BSD └── esprima.js /README.md: -------------------------------------------------------------------------------- 1 | Esprima ([esprima.org](http://esprima.org)) is an experimental ECMAScript 2 | parsing infrastructure for multipurpose analysis. 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esprima", 3 | "description": "ECMAScript parsing infrastructure for multipurpose analysis tool", 4 | "homepage": "http://esprima.org", 5 | "main": "esprima.js", 6 | "version": "0.0.0", 7 | "maintainers": [{ 8 | "name": "Ariya Hidayat", 9 | "email": "ariya.hidayat@gmail.com", 10 | "web": "http://ariya.ofilabs.com" 11 | }], 12 | "repository": { 13 | "type": "git", 14 | "url": "http://github.com/ariya/esprima.git" 15 | }, 16 | "licenses": [{ 17 | "type": "BSD", 18 | "url": "http://github.com/ariya/esprima/raw/master/LICENSE.BSD" 19 | }] 20 | } 21 | -------------------------------------------------------------------------------- /assets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #ffffff; 3 | min-width: 960px; 4 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 5 | font-size: 13px; 6 | font-weight: normal; 7 | line-height: 18px; 8 | color: #444; 9 | margin: 0; 10 | } 11 | 12 | p { 13 | font-size: 13px; 14 | font-weight: normal; 15 | line-height: 18px; 16 | margin-bottom: 9px; 17 | } 18 | 19 | h1 { 20 | margin-bottom: 18px; 21 | font-size: 30px; 22 | line-height: 36px; 23 | font-weight: bold; 24 | color: #444; 25 | } 26 | 27 | h1 small { 28 | font-size: 18px; 29 | color: #ccc; 30 | } 31 | 32 | .container { 33 | margin-left: auto; 34 | margin-right: auto; 35 | width: 960px; 36 | } 37 | 38 | .footer { 39 | margin-top: 25px; 40 | color: #555; 41 | text-align: center; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | esprima 6 | 7 | 8 | 9 |
10 |

esprima ECMAScript parsing infrastructure for multipurpose analysis

11 |

Coming soon!

12 |

Meanwhile, check the project page at esprima.googlecode.com.

13 |

 

14 |

 

15 |

 

16 |

 

17 |

 

18 |

 

19 |

 

20 | 23 |
24 | Fork me on GitHub 25 | 26 | 27 | -------------------------------------------------------------------------------- /LICENSE.BSD: -------------------------------------------------------------------------------- 1 | Redistribution and use in source and binary forms, with or without 2 | modification, are permitted provided that the following conditions are met: 3 | 4 | * Redistributions of source code must retain the above copyright 5 | notice, this list of conditions and the following disclaimer. 6 | * Redistributions in binary form must reproduce the above copyright 7 | notice, this list of conditions and the following disclaimer in the 8 | documentation and/or other materials provided with the distribution. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 11 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 12 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 13 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 14 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 15 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 16 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 17 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 18 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 19 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | -------------------------------------------------------------------------------- /esprima.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (C) 2011 Ariya Hidayat 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 17 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | */ 24 | 25 | /*global esprima:true, exports:true, 26 | parseAssignmentExpression: true, parseExpression: true, 27 | parseFunctionDeclaration: true, parseFunctionExpression: true, 28 | parseStatement: true */ 29 | 30 | (function (exports) { 31 | 'use strict'; 32 | 33 | var Token, 34 | Syntax, 35 | source, 36 | index; 37 | 38 | Token = { 39 | BooleanLiteral: 'BooleanLiteral', 40 | EOF: 'EOF', 41 | FutureReservedWord: 'FutureReservedWord', 42 | Identifier: 'Identifier', 43 | Keyword: 'Keyword', 44 | NullLiteral: 'NullLiteral', 45 | NumericLiteral: 'NumericLiteral', 46 | Punctuator: 'Punctuator', 47 | StringLiteral: 'StringLiteral' 48 | }; 49 | 50 | Syntax = { 51 | AssignmentExpression: 'AssignmentExpression', 52 | BlockStatement: 'BlockStatement', 53 | BinaryExpression: 'BinaryExpression', 54 | BreakStatement: 'BreakStatement', 55 | CallExpression: 'CallExpression', 56 | ConditionalExpression: 'ConditionalExpression', 57 | ContinueStatement: 'ContinueStatement', 58 | DoWhileStatement: 'DoWhileStatement', 59 | DebuggerStatement: 'DebuggerStatement', 60 | EmptyStatement: 'EmptyStatement', 61 | ExpressionStatement: 'ExpressionStatement', 62 | ForStatement: 'ForStatement', 63 | ForInStatement: 'ForInStatement', 64 | FunctionDeclaration: 'FunctionDeclaration', 65 | FunctionExpression: 'FunctionExpression', 66 | Identifier: 'Identifier', 67 | IfStatement: 'IfStatement', 68 | Literal: 'Literal', 69 | MemberExpression: 'MemberExpression', 70 | NewExpression: 'NewExpression', 71 | Program: 'Program', 72 | ReturnStatement: 'ReturnStatement', 73 | SequenceExpression: 'SequenceExpression', 74 | SwitchStatement: 'SwitchStatement', 75 | SwitchCase: 'SwitchCase', 76 | ThisExpression: 'ThisExpression', 77 | ThrowStatement: 'ThrowStatement', 78 | TryStatement: 'TryStatement', 79 | UnaryExpression: 'UnaryExpression', 80 | UpdateExpression: 'UpdateExpression', 81 | VariableDeclaration: 'VariableDeclaration', 82 | WhileStatement: 'WhileStatement', 83 | WithStatement: 'WithStatement' 84 | }; 85 | 86 | 87 | function isDecimalDigit(ch) { 88 | return '0123456789'.indexOf(ch) >= 0; 89 | } 90 | 91 | // TODO: really handle Unicode category Lu, LI, Lt, Lm, Lo, NI 92 | function isUnicodeLetter(ch) { 93 | return (ch >= 'a' && ch <= 'z') || 94 | (ch >= 'A' && ch <= 'Z'); 95 | } 96 | 97 | // TODO: really handle Unicode category Nd 98 | function isUnicodeDigit(ch) { 99 | return (ch >= '0') && (ch <= '9'); 100 | } 101 | 102 | // 7.2 White Space 103 | 104 | function isWhiteSpace(ch) { 105 | // TODO Unicode "space separator" 106 | return (ch === '\u0009') || (ch === '\u000B') || (ch === '\u000C') || 107 | (ch === ' ') || (ch === '\u00A0') || (ch === '\uFEFF'); 108 | } 109 | 110 | // 7.3 Line Terminators 111 | 112 | function isLineTerminator(ch) { 113 | return (ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029'); 114 | } 115 | 116 | // 7.6 Identifier Names and Identifiers 117 | 118 | function isIdentifierStart(ch) { 119 | // TODO UnicodeEscapeSequence 120 | return (ch === '$') || (ch === '_') || isUnicodeLetter(ch); 121 | } 122 | 123 | function isIdentifierPart(ch) { 124 | // TODO UnicodeCombiningMark UnicodeConnectorPunctuation and ZWNJ and ZWJ 125 | return isIdentifierStart(ch) || isUnicodeDigit(ch); 126 | } 127 | 128 | // 7.6.1.1 Keywords 129 | 130 | function isKeyword(id) { 131 | switch (id) { 132 | case 'break': 133 | case 'case': 134 | case 'catch': 135 | case 'continue': 136 | case 'debugger': 137 | case 'default': 138 | case 'delete': 139 | case 'do': 140 | case 'else': 141 | case 'finally': 142 | case 'for': 143 | case 'function': 144 | case 'if': 145 | case 'in': 146 | case 'instanceof': 147 | case 'new': 148 | case 'return': 149 | case 'switch': 150 | case 'this': 151 | case 'throw': 152 | case 'try': 153 | case 'typeof': 154 | case 'var': 155 | case 'void': 156 | case 'while': 157 | case 'with': 158 | return true; 159 | } 160 | return false; 161 | } 162 | 163 | // 7.6.1.2 Future Reserved Words 164 | 165 | function isFutureReservedWord(id) { 166 | switch (id) { 167 | case 'class': 168 | case 'const': 169 | case 'enum': 170 | case 'export': 171 | case 'extends': 172 | case 'import': 173 | case 'super': 174 | 175 | // strict mode 176 | case 'implements': 177 | case 'interface': 178 | case 'let': 179 | case 'package': 180 | case 'private': 181 | case 'protected': 182 | case 'public': 183 | case 'static': 184 | case 'yield': 185 | return true; 186 | } 187 | return false; 188 | } 189 | 190 | // Default for advance is '1', which means the next character right after. 191 | 192 | function peekChar(advance) { 193 | var idx; 194 | idx = (arguments.length > 0) ? (index + advance - 1) : index; 195 | return ((idx < source.length) ? source.charAt(idx) : '\x00'); 196 | } 197 | 198 | // Return the next character and move forward. 199 | 200 | function nextChar() { 201 | var ch = '\x00', 202 | idx = index; 203 | if (idx < source.length) { 204 | ch = source.charAt(idx); 205 | index += 1; 206 | } 207 | return ch; 208 | } 209 | 210 | // 7.4 Comments 211 | 212 | function skipComment() { 213 | var ch, blockComment, lineComment; 214 | 215 | blockComment = false; 216 | lineComment = false; 217 | 218 | while (index < source.length) { 219 | ch = peekChar(); 220 | 221 | if (lineComment) { 222 | nextChar(); 223 | if (isLineTerminator(ch)) { 224 | lineComment = false; 225 | } 226 | } else if (blockComment) { 227 | nextChar(); 228 | if (ch === '*') { 229 | ch = peekChar(); 230 | if (ch === '/') { 231 | nextChar(); 232 | blockComment = false; 233 | } 234 | } 235 | } else if (ch === '/') { 236 | ch = peekChar(2); 237 | if (ch === '/') { 238 | nextChar(); 239 | nextChar(); 240 | lineComment = true; 241 | } else if (ch === '*') { 242 | nextChar(); 243 | nextChar(); 244 | blockComment = true; 245 | } else { 246 | break; 247 | } 248 | } else if (isWhiteSpace(ch)) { 249 | nextChar(); 250 | } else if (isLineTerminator(ch)) { 251 | nextChar(); 252 | } else { 253 | break; 254 | } 255 | } 256 | } 257 | 258 | function scanIdentifier() { 259 | var ch, id; 260 | 261 | ch = peekChar(); 262 | if (!isIdentifierStart(ch)) { 263 | return; 264 | } 265 | 266 | id = nextChar(); 267 | while (true) { 268 | ch = peekChar(); 269 | if (isIdentifierPart(ch)) { 270 | id += nextChar(); 271 | } else { 272 | break; 273 | } 274 | } 275 | 276 | if (isKeyword(id)) { 277 | return { 278 | type: Token.Keyword, 279 | value: id 280 | }; 281 | } 282 | 283 | if (isFutureReservedWord(id)) { 284 | return { 285 | type: Token.FutureReservedKeyword, 286 | value: id 287 | }; 288 | } 289 | 290 | // 7.8.1 Null Literals 291 | 292 | if (id === 'null') { 293 | return { 294 | type: Token.NullLiteral 295 | }; 296 | } 297 | 298 | // 7.8.2 Boolean Literals 299 | 300 | if (id === 'true' || id === 'false') { 301 | return { 302 | type: Token.BooleanLiteral, 303 | value: id 304 | }; 305 | } 306 | 307 | return { 308 | type: Token.Identifier, 309 | value: id 310 | }; 311 | } 312 | 313 | // 7.7 Punctuators 314 | 315 | function scanPunctuator() { 316 | var ch1, ch2, ch3; 317 | 318 | ch1 = peekChar(1); 319 | ch2 = peekChar(2); 320 | ch3 = peekChar(3); 321 | 322 | // 4-character punctuator: >>>= 323 | 324 | if (ch1 === '>' && ch2 === '>' && ch3 === '>') { 325 | if (peekChar(4) === '=') { 326 | nextChar(); 327 | nextChar(); 328 | nextChar(); 329 | nextChar(); 330 | return { 331 | type: Token.Punctuator, 332 | value: '>>>=' 333 | }; 334 | } 335 | } 336 | 337 | // 3-character punctuators: === !== >>> <<= >>= 338 | 339 | if (ch1 === '=' && ch2 === '=' && ch3 === '=') { 340 | nextChar(); 341 | nextChar(); 342 | nextChar(); 343 | return { 344 | type: Token.Punctuator, 345 | value: '===' 346 | }; 347 | } 348 | 349 | if (ch1 === '!' && ch2 === '=' && ch3 === '=') { 350 | nextChar(); 351 | nextChar(); 352 | nextChar(); 353 | return { 354 | type: Token.Punctuator, 355 | value: '!==' 356 | }; 357 | } 358 | 359 | if (ch1 === '>' && ch2 === '>' && ch3 === '>') { 360 | nextChar(); 361 | nextChar(); 362 | nextChar(); 363 | return { 364 | type: Token.Punctuator, 365 | value: '>>>' 366 | }; 367 | } 368 | 369 | if (ch1 === '<' && ch2 === '<' && ch3 === '=') { 370 | nextChar(); 371 | nextChar(); 372 | nextChar(); 373 | return { 374 | type: Token.Punctuator, 375 | value: '<<=' 376 | }; 377 | } 378 | 379 | if (ch1 === '>' && ch2 === '>' && ch3 === '=') { 380 | nextChar(); 381 | nextChar(); 382 | nextChar(); 383 | return { 384 | type: Token.Punctuator, 385 | value: '>>=' 386 | }; 387 | } 388 | 389 | // 2-character punctuators: <= >= == != ++ -- << >> && || 390 | // += -= *= %= &= |= ^= /= 391 | 392 | if (ch2 === '=') { 393 | if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) { 394 | nextChar(); 395 | nextChar(); 396 | return { 397 | type: Token.Punctuator, 398 | value: ch1 + ch2 399 | }; 400 | } 401 | } 402 | 403 | if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) { 404 | if ('+-<>&|'.indexOf(ch2) >= 0) { 405 | nextChar(); 406 | nextChar(); 407 | return { 408 | type: Token.Punctuator, 409 | value: ch1 + ch2 410 | }; 411 | } 412 | } 413 | 414 | // 1-character punctuators: { } ( ) [ ] . ; , < > + - * % & | ^ ! ~ ? : = / 415 | 416 | if (ch1 === '.' && !isDecimalDigit(ch2)) { 417 | return { 418 | type: Token.Punctuator, 419 | value: nextChar() 420 | }; 421 | } 422 | 423 | if ('{}()[];,<>+-*%&|^!~?:=/'.indexOf(ch1) >= 0) { 424 | return { 425 | type: Token.Punctuator, 426 | value: nextChar() 427 | }; 428 | } 429 | } 430 | 431 | // 7.8.3 Numeric Literals 432 | 433 | // TODO: hex integer, etc 434 | 435 | function scanNumericLiteral() { 436 | var number, ch; 437 | 438 | ch = peekChar(); 439 | if (!isDecimalDigit(ch) && (ch !== '.')) { 440 | return; 441 | } 442 | 443 | number = ''; 444 | if (ch !== '.') { 445 | number = nextChar(); 446 | while (true) { 447 | ch = peekChar(); 448 | if (!isDecimalDigit(ch)) { 449 | break; 450 | } 451 | number += nextChar(); 452 | } 453 | } 454 | 455 | if (ch === '.') { 456 | number += nextChar(); 457 | while (true) { 458 | ch = peekChar(); 459 | if (!isDecimalDigit(ch)) { 460 | break; 461 | } 462 | number += nextChar(); 463 | } 464 | } 465 | 466 | if (ch === 'e' || ch === 'E') { 467 | number += nextChar(); 468 | ch = peekChar(); 469 | if (ch === '+' || ch === '-' || isDecimalDigit(ch)) { 470 | number += nextChar(); 471 | while (true) { 472 | ch = peekChar(); 473 | if (!isDecimalDigit(ch)) { 474 | break; 475 | } 476 | number += nextChar(); 477 | } 478 | } else { 479 | ch = 'character ' + ch; 480 | if (index >= source.length) { 481 | ch = ''; 482 | } 483 | throw { 484 | message: 'Unexpected ' + ch + ' after the exponent sign' 485 | }; 486 | } 487 | } 488 | 489 | if (number === '.') { 490 | throw { 491 | message: 'Expecting decimal digits after the dot sign' 492 | }; 493 | } 494 | 495 | return { 496 | type: Token.NumericLiteral, 497 | value: parseFloat(number) 498 | }; 499 | } 500 | 501 | // 7.8.4 String Literals 502 | 503 | // TODO Unicode, line continuiation 504 | function scanStringLiteral() { 505 | var str = '', quote, ch; 506 | 507 | quote = peekChar(); 508 | if (quote !== '\'' && quote !== '"') { 509 | return; 510 | } 511 | nextChar(); 512 | 513 | while (index < source.length) { 514 | ch = nextChar(); 515 | 516 | if (typeof ch === 'undefined') { 517 | throw { 518 | message: 'Unterminated string' 519 | }; 520 | } 521 | 522 | if (ch === quote) { 523 | break; 524 | } else if (ch === '\\') { 525 | str += ch; 526 | str += nextChar(); 527 | } else { 528 | str += ch; 529 | } 530 | } 531 | 532 | return { 533 | type: Token.StringLiteral, 534 | value: str 535 | }; 536 | } 537 | 538 | function scanRegExp() { 539 | var str = '', ch, classMarker = false; 540 | 541 | skipComment(); 542 | 543 | ch = peekChar(); 544 | if (ch !== '/') { 545 | return; 546 | } 547 | str = nextChar(); 548 | 549 | while (index < source.length) { 550 | ch = nextChar(); 551 | str += ch; 552 | if (classMarker) { 553 | if (ch === ']') { 554 | classMarker = false; 555 | } 556 | } else { 557 | if (ch === '\\') { 558 | str += nextChar(); 559 | } 560 | if (ch === '/') { 561 | break; 562 | } 563 | if (ch === '[') { 564 | classMarker = true; 565 | } 566 | if (isLineTerminator(ch)) { 567 | throw { 568 | message: 'Unexpected line terminator in a regular expression' 569 | }; 570 | } 571 | } 572 | } 573 | 574 | while (true) { 575 | ch = peekChar(); 576 | if (!isIdentifierPart(ch)) { 577 | break; 578 | } 579 | str += nextChar(); 580 | } 581 | 582 | return str; 583 | } 584 | 585 | function lex() { 586 | var ch, token; 587 | 588 | skipComment(); 589 | 590 | if (index >= source.length) { 591 | return { 592 | type: Token.EOF 593 | }; 594 | } 595 | 596 | token = scanPunctuator(); 597 | if (typeof token !== 'undefined') { 598 | return token; 599 | } 600 | 601 | ch = peekChar(); 602 | 603 | if (ch === '\'' || ch === '"') { 604 | return scanStringLiteral(); 605 | } 606 | 607 | if (ch === '.' || isDecimalDigit(ch)) { 608 | return scanNumericLiteral(); 609 | } 610 | 611 | token = scanIdentifier(); 612 | if (typeof token !== 'undefined') { 613 | return token; 614 | } 615 | 616 | throw { 617 | message: 'Unknown token from character ' + nextChar() 618 | }; 619 | } 620 | 621 | function lookahead() { 622 | var token, 623 | pos = index; 624 | 625 | pos = index; 626 | token = lex(); 627 | index = pos; 628 | 629 | return token; 630 | } 631 | 632 | // Throw an exception because of the token. 633 | 634 | function throwUnexpected(token) { 635 | var s; 636 | 637 | if (token.type === Token.EOF) { 638 | throw { 639 | message: 'Unexpected ' 640 | }; 641 | } 642 | 643 | s = token.value; 644 | if (s.length > 10) { 645 | s = s.substr(0, 10) + '...'; 646 | } 647 | throw { 648 | message: 'Unexpected token ' + s 649 | }; 650 | } 651 | 652 | // Expect the next token to match the specified one. 653 | // If not, an exception will be thrown. 654 | 655 | function expect(type, value) { 656 | var token; 657 | 658 | token = lex(); 659 | if (token.type !== type || token.value !== value) { 660 | throwUnexpected(token); 661 | } 662 | 663 | return token; 664 | } 665 | 666 | // Return true if the next token matches the specified punctuator. 667 | 668 | function match(value) { 669 | var token = lookahead(); 670 | return token.type === Token.Punctuator && token.value === value; 671 | } 672 | 673 | // Return true if the next token matches the specified keyword 674 | 675 | function matchKeyword(keyword) { 676 | var token = lookahead(); 677 | return token.type === Token.Keyword && token.value === keyword; 678 | } 679 | 680 | // Return true if the next token is an assignment operator 681 | 682 | function matchAssign() { 683 | var token = lookahead(), 684 | op = token.value; 685 | 686 | if (token.type !== Token.Punctuator) { 687 | return false; 688 | } 689 | return op === '=' || 690 | op === '*=' || 691 | op === '/=' || 692 | op === '%=' || 693 | op === '+=' || 694 | op === '-=' || 695 | op === '<<=' || 696 | op === '>>=' || 697 | op === '>>>=' || 698 | op === '&=' || 699 | op === '^=' || 700 | op === '|='; 701 | } 702 | 703 | 704 | function consumeSemicolon() { 705 | if (match(';')) { 706 | lex(); 707 | } 708 | } 709 | 710 | function isEOF() { 711 | return index >= source.length; 712 | } 713 | 714 | // 11.1.4 Array Initialiser 715 | 716 | function parseArrayInitialiser() { 717 | var elements = []; 718 | 719 | expect(Token.Punctuator, '['); 720 | 721 | while (!isEOF()) { 722 | if (match(']')) { 723 | lex(); 724 | break; 725 | } 726 | 727 | elements.push(parseAssignmentExpression()); 728 | 729 | if (match(']')) { 730 | lex(); 731 | break; 732 | } 733 | 734 | expect(Token.Punctuator, ','); 735 | } 736 | 737 | return { 738 | type: 'ArrayExpression', 739 | elements: elements 740 | }; 741 | } 742 | 743 | // 11.1.5 Object Initialiser 744 | 745 | function parseObjectInitialiser() { 746 | var token, expr, properties = [], name, value; 747 | 748 | function isPropertyName(t) { 749 | return t === Token.Identifier || 750 | t === Token.StringLiteral || 751 | t === Token.NumericLiteral; 752 | } 753 | 754 | expect(Token.Punctuator, '{'); 755 | 756 | // TODO handle 'get' and 'set' 757 | while (!isEOF()) { 758 | token = lex(); 759 | if (token.type === Token.Punctuator && token.value === '}') { 760 | break; 761 | } 762 | 763 | if (isPropertyName(token.type)) { 764 | name = token.value; 765 | expect(Token.Punctuator, ':'); 766 | value = parseAssignmentExpression(); 767 | 768 | properties.push({ 769 | type: 'Property', 770 | key: { 771 | type: 'Identifier', 772 | value: name 773 | }, 774 | value: value 775 | }); 776 | } else { 777 | throwUnexpected(token); 778 | } 779 | 780 | token = lookahead(); 781 | if (token.type === Token.Punctuator && token.value === '}') { 782 | lex(); 783 | break; 784 | } 785 | expect(Token.Punctuator, ','); 786 | } 787 | 788 | return { 789 | type: 'ObjectExpression', 790 | properties: properties 791 | }; 792 | } 793 | 794 | // 11.1 Primary Expressions 795 | 796 | function parsePrimaryExpression() { 797 | var token, expr; 798 | 799 | if (match('[')) { 800 | return parseArrayInitialiser(); 801 | } 802 | 803 | if (match('{')) { 804 | return parseObjectInitialiser(); 805 | } 806 | 807 | if (match('(')) { 808 | lex(); 809 | expr = parseExpression(); 810 | expect(Token.Punctuator, ')'); 811 | return expr.expression; 812 | } 813 | 814 | if (matchKeyword('function')) { 815 | return parseFunctionExpression(); 816 | } 817 | 818 | if (matchKeyword('this')) { 819 | lex(); 820 | return { 821 | type: Syntax.ThisExpression 822 | }; 823 | } 824 | 825 | if (match('/') || match('/=')) { 826 | return { 827 | type: Syntax.Literal, 828 | value: scanRegExp() 829 | }; 830 | } 831 | 832 | token = lex(); 833 | 834 | if (token.type === Token.Identifier) { 835 | return { 836 | type: Syntax.Identifier, 837 | name: token.value 838 | }; 839 | } 840 | 841 | if (token.type === Token.BooleanLiteral) { 842 | return { 843 | type: Syntax.Literal, 844 | value: (token.value === 'true') 845 | }; 846 | } 847 | 848 | if (token.type === Token.NullLiteral) { 849 | return { 850 | type: Syntax.Literal, 851 | value: null 852 | }; 853 | } 854 | 855 | if (token.type === Token.NumericLiteral) { 856 | return { 857 | type: Syntax.Literal, 858 | value: token.value 859 | }; 860 | } 861 | 862 | if (token.type === Token.StringLiteral) { 863 | return { 864 | type: Syntax.Literal, 865 | value: token.value 866 | }; 867 | } 868 | 869 | return; 870 | } 871 | 872 | // 11.2 Left-Hand-Side Expressions 873 | 874 | function parseArguments() { 875 | var args = []; 876 | 877 | expect(Token.Punctuator, '('); 878 | 879 | if (!match(')')) { 880 | while (!isEOF()) { 881 | args.push(parseAssignmentExpression()); 882 | if (match(')')) { 883 | break; 884 | } 885 | expect(Token.Punctuator, ','); 886 | } 887 | } 888 | 889 | expect(Token.Punctuator, ')'); 890 | 891 | return args; 892 | } 893 | 894 | function parseMemberExpression() { 895 | var expr, token, property; 896 | 897 | expr = parsePrimaryExpression(); 898 | 899 | while (!isEOF()) { 900 | if (match('.')) { 901 | lex(); 902 | token = lex(); 903 | if (token.type !== Token.Identifier) { 904 | throw { 905 | message: 'Expecting an identifier after dot (.)' 906 | }; 907 | } 908 | property = { 909 | type: Syntax.Identifier, 910 | name: token.value 911 | }; 912 | expr = { 913 | type: Syntax.MemberExpression, 914 | object: expr, 915 | property: property 916 | }; 917 | } else if (match('[')) { 918 | lex(); 919 | property = parseExpression(); 920 | if (property.type === 'ExpressionStatement') { 921 | property = property.expression; 922 | } 923 | expr = { 924 | type: Syntax.MemberExpression, 925 | object: expr, 926 | property: property 927 | }; 928 | expect(Token.Punctuator, ']'); 929 | } else if (match('(')) { 930 | expr = { 931 | type: Syntax.CallExpression, 932 | callee: expr, 933 | 'arguments': parseArguments() 934 | }; 935 | } else { 936 | break; 937 | } 938 | } 939 | 940 | return expr; 941 | } 942 | 943 | function parseLeftHandSideExpression() { 944 | var useNew, expr, args; 945 | 946 | useNew = matchKeyword('new'); 947 | if (useNew) { 948 | // Read the keyword. 949 | lex(); 950 | expr = parseLeftHandSideExpression(); 951 | } else { 952 | expr = parseMemberExpression(); 953 | } 954 | 955 | if (match('(')) { 956 | args = parseArguments(); 957 | } 958 | 959 | if (useNew) { 960 | 961 | // Force to have at least an empty argument list. 962 | if (typeof args === 'undefined') { 963 | args = []; 964 | } 965 | 966 | // e.g. "new x()" thus adopt the CallExpression of "x()". 967 | if (expr.type === Syntax.CallExpression) { 968 | args = expr['arguments']; 969 | expr = expr.callee; 970 | } 971 | 972 | return { 973 | type: Syntax.NewExpression, 974 | callee: expr, 975 | 'arguments': args 976 | }; 977 | } 978 | 979 | if (typeof args !== 'undefined') { 980 | return { 981 | type: Syntax.CallExpression, 982 | callee: expr, 983 | 'arguments': args 984 | }; 985 | } 986 | 987 | return expr; 988 | } 989 | 990 | // 11.3 Postfix Expressions 991 | 992 | function parsePostfixExpression() { 993 | var expr = parseLeftHandSideExpression(); 994 | 995 | if (match('++') || match('--')) { 996 | expr = { 997 | type: Syntax.UpdateExpression, 998 | operator: lex().value, 999 | argument: expr, 1000 | prefix: false 1001 | }; 1002 | } 1003 | 1004 | return expr; 1005 | } 1006 | 1007 | // 11.4 Unary Operators 1008 | 1009 | function parseUnaryExpression() { 1010 | 1011 | if (match('++') || match('--')) { 1012 | return { 1013 | type: Syntax.UpdateExpression, 1014 | operator: lex().value, 1015 | argument: parseUnaryExpression(), 1016 | prefix: true 1017 | }; 1018 | } 1019 | 1020 | if (match('+') || match('-') || match('~') || match('!')) { 1021 | return { 1022 | type: Syntax.UnaryExpression, 1023 | operator: lex().value, 1024 | argument: parseUnaryExpression() 1025 | }; 1026 | } 1027 | 1028 | if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { 1029 | return { 1030 | type: Syntax.UnaryExpression, 1031 | operator: lex().value, 1032 | argument: parseUnaryExpression() 1033 | }; 1034 | } 1035 | 1036 | return parsePostfixExpression(); 1037 | } 1038 | 1039 | // 11.5 Multiplicative Operators 1040 | 1041 | function parseMultiplicativeExpression() { 1042 | var expr = parseUnaryExpression(); 1043 | 1044 | if (match('*') || match('/') || match('%')) { 1045 | expr = { 1046 | type: Syntax.BinaryExpression, 1047 | operator: lex().value, 1048 | left: expr, 1049 | right: parseMultiplicativeExpression() 1050 | }; 1051 | } 1052 | 1053 | return expr; 1054 | } 1055 | 1056 | // 11.6 Additive Operators 1057 | 1058 | function parseAdditiveExpression() { 1059 | var expr = parseMultiplicativeExpression(); 1060 | 1061 | if (match('+') || match('-')) { 1062 | expr = { 1063 | type: Syntax.BinaryExpression, 1064 | operator: lex().value, 1065 | left: expr, 1066 | right: parseAdditiveExpression() 1067 | }; 1068 | } 1069 | 1070 | return expr; 1071 | } 1072 | 1073 | // 11.7 Bitwise Shift Operators 1074 | 1075 | function parseShiftExpression() { 1076 | var expr = parseAdditiveExpression(); 1077 | 1078 | if (match('<<') || match('>>') || match('>>>')) { 1079 | return { 1080 | type: Syntax.BinaryExpression, 1081 | operator: lex().value, 1082 | left: expr, 1083 | right: parseShiftExpression() 1084 | }; 1085 | } 1086 | 1087 | return expr; 1088 | } 1089 | 1090 | // 11.8 Relational Operators 1091 | 1092 | function parseRelationalExpression() { 1093 | var expr = parseShiftExpression(); 1094 | 1095 | if (match('<') || match('>') || match('<=') || match('>=')) { 1096 | expr = { 1097 | type: Syntax.BinaryExpression, 1098 | operator: lex().value, 1099 | left: expr, 1100 | right: parseRelationalExpression() 1101 | }; 1102 | } else if (matchKeyword('in')) { 1103 | lex(); 1104 | expr = { 1105 | type: Syntax.BinaryExpression, 1106 | operator: 'in', 1107 | left: expr, 1108 | right: parseRelationalExpression() 1109 | }; 1110 | } else if (matchKeyword('instanceof')) { 1111 | lex(); 1112 | expr = { 1113 | type: Syntax.BinaryExpression, 1114 | operator: 'instance of', 1115 | left: expr, 1116 | right: parseRelationalExpression() 1117 | }; 1118 | } 1119 | 1120 | return expr; 1121 | } 1122 | 1123 | // 11.9 Equality Operators 1124 | 1125 | function parseEqualityExpression() { 1126 | var expr = parseRelationalExpression(); 1127 | 1128 | if (match('==') || match('!=') || match('===') || match('!==')) { 1129 | expr = { 1130 | type: Syntax.BinaryExpression, 1131 | operator: lex().value, 1132 | left: expr, 1133 | right: parseEqualityExpression() 1134 | }; 1135 | } 1136 | 1137 | return expr; 1138 | } 1139 | 1140 | // 11.10 Binary Bitwise Operators 1141 | 1142 | function parseBitwiseANDExpression() { 1143 | var expr = parseEqualityExpression(); 1144 | 1145 | if (match('&')) { 1146 | lex(); 1147 | expr = { 1148 | type: Syntax.BinaryExpression, 1149 | operator: '&', 1150 | left: expr, 1151 | right: parseBitwiseANDExpression() 1152 | }; 1153 | } 1154 | 1155 | return expr; 1156 | } 1157 | 1158 | function parseBitwiseORExpression() { 1159 | var expr = parseBitwiseANDExpression(); 1160 | 1161 | if (match('|')) { 1162 | lex(); 1163 | expr = { 1164 | type: Syntax.BinaryExpression, 1165 | operator: '|', 1166 | left: expr, 1167 | right: parseBitwiseORExpression() 1168 | }; 1169 | } 1170 | 1171 | return expr; 1172 | } 1173 | 1174 | function parseBitwiseXORExpression() { 1175 | var expr = parseBitwiseORExpression(); 1176 | 1177 | if (match('^')) { 1178 | lex(); 1179 | expr = { 1180 | type: Syntax.BinaryExpression, 1181 | operator: '^', 1182 | left: expr, 1183 | right: parseBitwiseXORExpression() 1184 | }; 1185 | } 1186 | 1187 | return expr; 1188 | } 1189 | 1190 | // 11.11 Binary Logical Operators 1191 | 1192 | function parseLogicalANDExpression() { 1193 | var expr = parseBitwiseXORExpression(); 1194 | 1195 | if (match('&&')) { 1196 | lex(); 1197 | expr = { 1198 | type: Syntax.BinaryExpression, 1199 | operator: '&&', 1200 | left: expr, 1201 | right: parseLogicalANDExpression() 1202 | }; 1203 | } 1204 | 1205 | return expr; 1206 | } 1207 | 1208 | function parseLogicalORExpression() { 1209 | var expr = parseLogicalANDExpression(); 1210 | 1211 | if (match('||')) { 1212 | lex(); 1213 | expr = { 1214 | type: Syntax.BinaryExpression, 1215 | operator: '||', 1216 | left: expr, 1217 | right: parseLogicalORExpression() 1218 | }; 1219 | } 1220 | 1221 | return expr; 1222 | } 1223 | 1224 | // 11.12 Conditional Operator 1225 | 1226 | function parseConditionalExpression() { 1227 | var token, expr; 1228 | 1229 | token = lookahead(); 1230 | expr = parseLogicalORExpression(); 1231 | if (typeof expr === 'undefined') { 1232 | throwUnexpected(token); 1233 | } 1234 | 1235 | if (match('?')) { 1236 | lex(); 1237 | expr = { 1238 | type: Syntax.ConditionalExpression, 1239 | test: expr 1240 | }; 1241 | expr.consequent = parseAssignmentExpression(); 1242 | expect(Token.Punctuator, ':'); 1243 | expr.alternate = parseAssignmentExpression(); 1244 | } 1245 | 1246 | return expr; 1247 | } 1248 | 1249 | // 11.13 Assignment Operators 1250 | 1251 | function parseAssignmentExpression() { 1252 | 1253 | var expr = parseConditionalExpression(); 1254 | 1255 | if (matchAssign()) { 1256 | expr = { 1257 | type: Syntax.AssignmentExpression, 1258 | operator: lex().value, 1259 | left: expr, 1260 | right: parseAssignmentExpression() 1261 | }; 1262 | } 1263 | 1264 | return expr; 1265 | } 1266 | 1267 | // 11.14 Comma Operator 1268 | 1269 | function parseExpression() { 1270 | var expr = parseAssignmentExpression(); 1271 | 1272 | if (match(',')) { 1273 | expr = { 1274 | type: Syntax.SequenceExpression, 1275 | expressions: [ expr ] 1276 | }; 1277 | 1278 | while (!isEOF()) { 1279 | if (!match(',')) { 1280 | break; 1281 | } 1282 | lex(); 1283 | expr.expressions.push(parseAssignmentExpression()); 1284 | } 1285 | } 1286 | 1287 | return { 1288 | type: Syntax.ExpressionStatement, 1289 | expression: expr 1290 | }; 1291 | } 1292 | 1293 | // 12.1 Block 1294 | 1295 | function parseStatementList() { 1296 | var list = []; 1297 | 1298 | while (!isEOF()) { 1299 | if (match('}')) { 1300 | break; 1301 | } 1302 | list.push(parseStatement()); 1303 | } 1304 | 1305 | return list; 1306 | } 1307 | 1308 | function parseBlock() { 1309 | var block; 1310 | 1311 | expect(Token.Punctuator, '{'); 1312 | 1313 | block = parseStatementList(); 1314 | 1315 | expect(Token.Punctuator, '}'); 1316 | 1317 | return { 1318 | type: Syntax.BlockStatement, 1319 | body: block 1320 | }; 1321 | } 1322 | 1323 | // 12.2 Variable Statement 1324 | 1325 | function parseVariableDeclaration() { 1326 | var token, name; 1327 | 1328 | token = lex(); 1329 | if (token.type !== Token.Identifier) { 1330 | throw { 1331 | message: 'Expected an identifier' 1332 | }; 1333 | } 1334 | name = token.value; 1335 | 1336 | if (match('=')) { 1337 | lex(); 1338 | return { 1339 | type: Syntax.AssignmentExpression, 1340 | operator: '=', 1341 | left: { 1342 | type: Syntax.Identifier, 1343 | name: name 1344 | }, 1345 | right: parseAssignmentExpression() 1346 | }; 1347 | } 1348 | 1349 | return { 1350 | type: Syntax.Identifier, 1351 | name: name 1352 | }; 1353 | } 1354 | 1355 | function parseVariableDeclarationList() { 1356 | var list = []; 1357 | 1358 | while (!isEOF()) { 1359 | list.push(parseVariableDeclaration()); 1360 | if (!match(',')) { 1361 | break; 1362 | } 1363 | lex(); 1364 | } 1365 | 1366 | return list; 1367 | } 1368 | 1369 | function parseVariableStatement() { 1370 | var declarations; 1371 | 1372 | expect(Token.Keyword, 'var'); 1373 | 1374 | declarations = parseVariableDeclarationList(); 1375 | 1376 | consumeSemicolon(); 1377 | 1378 | return { 1379 | type: Syntax.VariableDeclaration, 1380 | declarations: declarations 1381 | }; 1382 | } 1383 | 1384 | // 12.3 Empty Statement 1385 | 1386 | function parseEmptyStatement() { 1387 | expect(Token.Punctuator, ';'); 1388 | 1389 | return { 1390 | type: Syntax.EmptyStatement 1391 | }; 1392 | } 1393 | 1394 | // 12.4 Expression Statement 1395 | 1396 | function parseExpressionStatement() { 1397 | var expr = parseExpression(); 1398 | 1399 | consumeSemicolon(); 1400 | 1401 | return expr; 1402 | } 1403 | 1404 | // 12.5 If statement 1405 | 1406 | function parseIfStatement() { 1407 | var test, consequent, alternate; 1408 | 1409 | expect(Token.Keyword, 'if'); 1410 | 1411 | expect(Token.Punctuator, '('); 1412 | 1413 | test = parseExpression().expression; 1414 | 1415 | expect(Token.Punctuator, ')'); 1416 | 1417 | consequent = parseStatement(); 1418 | 1419 | if (matchKeyword('else')) { 1420 | lex(); 1421 | alternate = parseStatement(); 1422 | } 1423 | 1424 | return { 1425 | type: Syntax.IfStatement, 1426 | test: test, 1427 | consequent: consequent, 1428 | alternate: alternate 1429 | }; 1430 | } 1431 | 1432 | // 12.6 Iteration Statements 1433 | 1434 | function parseDoWhileStatement() { 1435 | var body, test; 1436 | 1437 | expect(Token.Keyword, 'do'); 1438 | 1439 | body = parseStatement(); 1440 | 1441 | expect(Token.Keyword, 'while'); 1442 | 1443 | expect(Token.Punctuator, '('); 1444 | 1445 | test = parseExpression().expression; 1446 | 1447 | expect(Token.Punctuator, ')'); 1448 | 1449 | consumeSemicolon(); 1450 | 1451 | return { 1452 | type: Syntax.DoWhileStatement, 1453 | body: body, 1454 | test: test 1455 | }; 1456 | } 1457 | 1458 | function parseWhileStatement() { 1459 | var test, body; 1460 | 1461 | expect(Token.Keyword, 'while'); 1462 | 1463 | expect(Token.Punctuator, '('); 1464 | 1465 | test = parseExpression().expression; 1466 | 1467 | expect(Token.Punctuator, ')'); 1468 | 1469 | body = parseStatement(); 1470 | 1471 | return { 1472 | type: Syntax.WhileStatement, 1473 | test: test, 1474 | body: body 1475 | }; 1476 | } 1477 | 1478 | function parseForStatement() { 1479 | var init, test, update, left, right, body; 1480 | 1481 | init = test = update = null; 1482 | 1483 | expect(Token.Keyword, 'for'); 1484 | 1485 | expect(Token.Punctuator, '('); 1486 | 1487 | if (match(';')) { 1488 | lex(); 1489 | } else { 1490 | if (matchKeyword('var')) { 1491 | lex(); 1492 | init = parseVariableDeclarationList(); 1493 | if (init.length === 1) { 1494 | init = init[0]; 1495 | } else { 1496 | init = { 1497 | type: Syntax.SequenceExpression, 1498 | expressions: init 1499 | }; 1500 | } 1501 | if (matchKeyword('in')) { 1502 | lex(); 1503 | left = init; 1504 | right = parseExpression().expression; 1505 | init = null; 1506 | } 1507 | } else { 1508 | init = parseExpression().expression; 1509 | } 1510 | 1511 | if (typeof left === 'undefined') { 1512 | if (init.hasOwnProperty('operator') && init.operator === 'in') { 1513 | left = init.left; 1514 | right = init.right; 1515 | init = null; 1516 | } else { 1517 | expect(Token.Punctuator, ';'); 1518 | } 1519 | } 1520 | } 1521 | 1522 | if (typeof left === 'undefined') { 1523 | 1524 | if (!match(';')) { 1525 | test = parseExpression().expression; 1526 | } 1527 | expect(Token.Punctuator, ';'); 1528 | 1529 | if (!match(')')) { 1530 | update = parseExpression().expression; 1531 | } 1532 | } 1533 | 1534 | expect(Token.Punctuator, ')'); 1535 | 1536 | body = parseStatement(); 1537 | 1538 | if (typeof left === 'undefined') { 1539 | return { 1540 | type: Syntax.ForStatement, 1541 | init: init, 1542 | test: test, 1543 | update: update, 1544 | body: body 1545 | }; 1546 | } 1547 | 1548 | return { 1549 | type: Syntax.ForInStatement, 1550 | left: left, 1551 | right: right, 1552 | body: body 1553 | }; 1554 | } 1555 | 1556 | // 12.7 The continue statement 1557 | 1558 | function parseContinueStatement() { 1559 | var token, label = null; 1560 | 1561 | expect(Token.Keyword, 'continue'); 1562 | 1563 | token = lookahead(); 1564 | if (token.type === Token.Identifier) { 1565 | label = token.value; 1566 | } 1567 | 1568 | consumeSemicolon(); 1569 | 1570 | return { 1571 | type: Syntax.ContinueStatement, 1572 | label: label 1573 | }; 1574 | } 1575 | 1576 | // 12.8 The break statement 1577 | 1578 | function parseBreakStatement() { 1579 | var token, label = null; 1580 | 1581 | expect(Token.Keyword, 'break'); 1582 | 1583 | token = lookahead(); 1584 | if (token.type === Token.Identifier) { 1585 | label = token.value; 1586 | } 1587 | 1588 | consumeSemicolon(); 1589 | 1590 | return { 1591 | type: Syntax.BreakStatement, 1592 | label: label 1593 | }; 1594 | } 1595 | 1596 | // 12.9 The return statement 1597 | 1598 | function parseReturnStatement() { 1599 | var token, argument = null; 1600 | 1601 | expect(Token.Keyword, 'return'); 1602 | 1603 | if (!match(';')) { 1604 | token = lookahead(); 1605 | if (!match('}') && token.type !== Token.EOF) { 1606 | argument = parseExpression().expression; 1607 | } 1608 | } 1609 | 1610 | consumeSemicolon(); 1611 | 1612 | return { 1613 | type: Syntax.ReturnStatement, 1614 | argument: argument 1615 | }; 1616 | } 1617 | 1618 | // 12.10 The with statement 1619 | 1620 | function parseWithStatement() { 1621 | var object, body; 1622 | 1623 | expect(Token.Keyword, 'with'); 1624 | 1625 | expect(Token.Punctuator, '('); 1626 | 1627 | object = parseExpression().expression; 1628 | 1629 | expect(Token.Punctuator, ')'); 1630 | 1631 | body = parseStatement(); 1632 | 1633 | return { 1634 | type: Syntax.WithStatement, 1635 | object: object, 1636 | body: body 1637 | }; 1638 | } 1639 | 1640 | // 12.10 The swith statement 1641 | 1642 | function parseSwitchStatement() { 1643 | var discriminant, cases, test, consequent; 1644 | 1645 | expect(Token.Keyword, 'switch'); 1646 | 1647 | expect(Token.Punctuator, '('); 1648 | 1649 | discriminant = parseExpression().expression; 1650 | 1651 | expect(Token.Punctuator, ')'); 1652 | 1653 | expect(Token.Punctuator, '{'); 1654 | 1655 | if (match('}')) { 1656 | lex(); 1657 | return { 1658 | type: Syntax.SwitchStatement, 1659 | discriminant: discriminant 1660 | }; 1661 | } 1662 | 1663 | cases = []; 1664 | 1665 | while (!isEOF()) { 1666 | if (match('}')) { 1667 | break; 1668 | } 1669 | 1670 | if (matchKeyword('default')) { 1671 | lex(); 1672 | test = null; 1673 | } else { 1674 | expect(Token.Keyword, 'case'); 1675 | test = parseExpression().expression; 1676 | } 1677 | expect(Token.Punctuator, ':'); 1678 | 1679 | consequent = { 1680 | type: Syntax.BlockStatement, 1681 | body: [] 1682 | }; 1683 | 1684 | while (!isEOF()) { 1685 | if (match('}') || matchKeyword('default') || matchKeyword('case')) { 1686 | break; 1687 | } 1688 | consequent.body.push(parseStatement()); 1689 | } 1690 | 1691 | cases.push({ 1692 | type: Syntax.SwitchCase, 1693 | test: test, 1694 | consequent: [ consequent ] 1695 | }); 1696 | } 1697 | 1698 | expect(Token.Punctuator, '}'); 1699 | 1700 | return { 1701 | type: Syntax.SwitchStatement, 1702 | discriminant: discriminant, 1703 | cases: cases 1704 | }; 1705 | } 1706 | 1707 | // 12.13 The throw statement 1708 | 1709 | function parseThrowStatement() { 1710 | var token, argument = null; 1711 | 1712 | expect(Token.Keyword, 'throw'); 1713 | 1714 | if (!match(';')) { 1715 | token = lookahead(); 1716 | if (token.type !== Token.EOF) { 1717 | argument = parseExpression().expression; 1718 | } 1719 | } 1720 | 1721 | consumeSemicolon(); 1722 | 1723 | return { 1724 | type: Syntax.ThrowStatement, 1725 | argument: argument 1726 | }; 1727 | } 1728 | 1729 | // 12.14 The try statement 1730 | 1731 | function parseTryStatement() { 1732 | var block, handler = null, guard, finalizer = null; 1733 | 1734 | expect(Token.Keyword, 'try'); 1735 | 1736 | block = parseBlock(); 1737 | 1738 | if (matchKeyword('catch')) { 1739 | lex(); 1740 | expect(Token.Punctuator, '('); 1741 | if (!match(')')) { 1742 | guard = parseExpression().expression; 1743 | } 1744 | expect(Token.Punctuator, ')'); 1745 | handler = parseBlock(); 1746 | handler.guard = guard; 1747 | } 1748 | 1749 | if (matchKeyword('finally')) { 1750 | lex(); 1751 | finalizer = parseBlock(); 1752 | } 1753 | 1754 | return { 1755 | type: Syntax.TryStatement, 1756 | block: block, 1757 | handler: handler, 1758 | finalizer: finalizer 1759 | }; 1760 | } 1761 | 1762 | // 12.15 The debugger statement 1763 | 1764 | function parseDebuggerStatement() { 1765 | expect(Token.Keyword, 'debugger'); 1766 | 1767 | consumeSemicolon(); 1768 | 1769 | return { 1770 | type: Syntax.DebuggerStatement 1771 | }; 1772 | } 1773 | 1774 | // 12 Statements 1775 | 1776 | function parseStatement() { 1777 | var token = lookahead(); 1778 | 1779 | if (token.type === Token.EOF) { 1780 | return; 1781 | } 1782 | 1783 | if (token.type === Token.Punctuator) { 1784 | switch (token.value) { 1785 | case ';': 1786 | return parseEmptyStatement(); 1787 | case '{': 1788 | return parseBlock(); 1789 | default: 1790 | break; 1791 | } 1792 | } 1793 | 1794 | if (token.type === Token.Keyword) { 1795 | switch (token.value) { 1796 | case 'break': 1797 | return parseBreakStatement(); 1798 | case 'continue': 1799 | return parseContinueStatement(); 1800 | case 'debugger': 1801 | return parseDebuggerStatement(); 1802 | case 'do': 1803 | return parseDoWhileStatement(); 1804 | case 'for': 1805 | return parseForStatement(); 1806 | case 'if': 1807 | return parseIfStatement(); 1808 | case 'return': 1809 | return parseReturnStatement(); 1810 | case 'switch': 1811 | return parseSwitchStatement(); 1812 | case 'throw': 1813 | return parseThrowStatement(); 1814 | case 'try': 1815 | return parseTryStatement(); 1816 | case 'var': 1817 | return parseVariableStatement(); 1818 | case 'while': 1819 | return parseWhileStatement(); 1820 | case 'with': 1821 | return parseWithStatement(); 1822 | default: 1823 | break; 1824 | } 1825 | } 1826 | 1827 | return parseExpressionStatement(); 1828 | } 1829 | 1830 | // 13 Function Definition 1831 | 1832 | function parseFunctionDeclaration() { 1833 | var token, id = null, params = [], body; 1834 | 1835 | expect(Token.Keyword, 'function'); 1836 | 1837 | token = lex(); 1838 | if (token.type !== 'Identifier') { 1839 | throwUnexpected(token); 1840 | } 1841 | id = token.value; 1842 | 1843 | expect(Token.Punctuator, '('); 1844 | 1845 | if (!match(')')) { 1846 | while (!isEOF()) { 1847 | token = lex(); 1848 | if (token.type !== 'Identifier') { 1849 | throwUnexpected(token); 1850 | } 1851 | params.push({ 1852 | type: 'Identifier', 1853 | name: token.value 1854 | }); 1855 | if (match(')')) { 1856 | break; 1857 | } 1858 | expect(Token.Punctuator, ','); 1859 | } 1860 | } 1861 | 1862 | expect(Token.Punctuator, ')'); 1863 | 1864 | body = parseBlock(); 1865 | 1866 | return { 1867 | type: Syntax.FunctionDeclaration, 1868 | id: id, 1869 | params: params, 1870 | body: body 1871 | }; 1872 | } 1873 | 1874 | function parseFunctionExpression() { 1875 | var token, id = null, params = [], body; 1876 | 1877 | expect(Token.Keyword, 'function'); 1878 | 1879 | if (!match('(')) { 1880 | token = lex(); 1881 | if (token.type !== 'Identifier') { 1882 | throwUnexpected(token); 1883 | } 1884 | id = token.value; 1885 | } 1886 | 1887 | expect(Token.Punctuator, '('); 1888 | 1889 | if (!match(')')) { 1890 | while (!isEOF()) { 1891 | token = lex(); 1892 | if (token.type !== 'Identifier') { 1893 | throwUnexpected(token); 1894 | } 1895 | params.push({ 1896 | type: 'Identifier', 1897 | name: token.value 1898 | }); 1899 | if (match(')')) { 1900 | break; 1901 | } 1902 | expect(Token.Punctuator, ','); 1903 | } 1904 | } 1905 | 1906 | expect(Token.Punctuator, ')'); 1907 | 1908 | body = parseBlock(); 1909 | 1910 | return { 1911 | type: Syntax.FunctionExpression, 1912 | id: id, 1913 | params: params, 1914 | body: body 1915 | }; 1916 | } 1917 | 1918 | // 14 Program 1919 | 1920 | function parseSourceElement() { 1921 | var token; 1922 | 1923 | token = lookahead(); 1924 | if (token.type === Token.EOF) { 1925 | return; 1926 | } 1927 | 1928 | if (matchKeyword('function')) { 1929 | return parseFunctionDeclaration(); 1930 | } 1931 | 1932 | return parseStatement(); 1933 | } 1934 | 1935 | function parseSourceElements() { 1936 | var sourceElement, sourceElements = []; 1937 | 1938 | while (!isEOF()) { 1939 | sourceElement = parseSourceElement(); 1940 | if (typeof sourceElement === 'undefined') { 1941 | break; 1942 | } 1943 | sourceElements.push(sourceElement); 1944 | } 1945 | return sourceElements; 1946 | } 1947 | 1948 | function parseProgram() { 1949 | return { 1950 | type: Syntax.Program, 1951 | body: parseSourceElements() 1952 | }; 1953 | } 1954 | 1955 | exports.parse = function (code) { 1956 | source = code; 1957 | index = 0; 1958 | return parseProgram(); 1959 | }; 1960 | 1961 | }(typeof exports === 'undefined' ? (esprima = {}) : exports)); 1962 | --------------------------------------------------------------------------------