├── 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 |
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 |
--------------------------------------------------------------------------------