Commit a48c2c0e a48c2c0ee2fd10ff8d5fc65683723720fa73298d by Nicolas Perriault

added syntax checking for test files using esprima.js

1 parent 30e703d6
...@@ -279,9 +279,16 @@ ...@@ -279,9 +279,16 @@
279 throw "Can only exec() files with .js or .coffee extensions"; 279 throw "Can only exec() files with .js or .coffee extensions";
280 } 280 }
281 if (fileExt(file) === "coffee") { 281 if (fileExt(file) === "coffee") {
282 phantom.injectJs(file); 282 phantom.injectJs(file); // FIXME: syntax validation?
283 } else { 283 } else {
284 eval(fs.read(file)); 284 var testContents = fs.read(file);
285 var parsed;
286 try {
287 parsed = esprima.parse(testContents);
288 } catch(e) {
289 throw "Unable to parse test file " + file + ": " + e.toString();
290 }
291 eval(testContents);
285 } 292 }
286 }; 293 };
287 294
......
1 /*
2 Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
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 <COPYRIGHT HOLDER> 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, parseBlock: 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 lineNumber,
38 length,
39 buffer,
40 comments;
41
42 Token = {
43 BooleanLiteral: 1,
44 EOF: 2,
45 Identifier: 3,
46 Keyword: 4,
47 NullLiteral: 5,
48 NumericLiteral: 6,
49 Punctuator: 7,
50 StringLiteral: 8
51 };
52
53 Syntax = {
54 AssignmentExpression: 'AssignmentExpression',
55 ArrayExpression: 'ArrayExpression',
56 BlockStatement: 'BlockStatement',
57 BinaryExpression: 'BinaryExpression',
58 BreakStatement: 'BreakStatement',
59 CallExpression: 'CallExpression',
60 CatchClause: 'CatchClause',
61 Comment: 'Comment',
62 ConditionalExpression: 'ConditionalExpression',
63 ContinueStatement: 'ContinueStatement',
64 DoWhileStatement: 'DoWhileStatement',
65 DebuggerStatement: 'DebuggerStatement',
66 EmptyStatement: 'EmptyStatement',
67 ExpressionStatement: 'ExpressionStatement',
68 ForStatement: 'ForStatement',
69 ForInStatement: 'ForInStatement',
70 FunctionDeclaration: 'FunctionDeclaration',
71 FunctionExpression: 'FunctionExpression',
72 Identifier: 'Identifier',
73 IfStatement: 'IfStatement',
74 Literal: 'Literal',
75 LabeledStatement: 'LabeledStatement',
76 LogicalExpression: 'LogicalExpression',
77 MemberExpression: 'MemberExpression',
78 NewExpression: 'NewExpression',
79 ObjectExpression: 'ObjectExpression',
80 Program: 'Program',
81 ReturnStatement: 'ReturnStatement',
82 SequenceExpression: 'SequenceExpression',
83 SwitchStatement: 'SwitchStatement',
84 SwitchCase: 'SwitchCase',
85 ThisExpression: 'ThisExpression',
86 ThrowStatement: 'ThrowStatement',
87 TryStatement: 'TryStatement',
88 UnaryExpression: 'UnaryExpression',
89 UpdateExpression: 'UpdateExpression',
90 VariableDeclaration: 'VariableDeclaration',
91 WhileStatement: 'WhileStatement',
92 WithStatement: 'WithStatement'
93 };
94
95 if (typeof Object.freeze === 'function') {
96 Object.freeze(Token);
97 Object.freeze(Syntax);
98 }
99
100 function isDecimalDigit(ch) {
101 return '0123456789'.indexOf(ch) >= 0;
102 }
103
104 function isHexDigit(ch) {
105 return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
106 }
107
108 // TODO: really handle Unicode category Lu, LI, Lt, Lm, Lo, NI
109 function isUnicodeLetter(ch) {
110 return (ch >= 'a' && ch <= 'z') ||
111 (ch >= 'A' && ch <= 'Z');
112 }
113
114 // TODO: really handle Unicode category Nd
115 function isUnicodeDigit(ch) {
116 return (ch >= '0') && (ch <= '9');
117 }
118
119 // 7.2 White Space
120
121 function isWhiteSpace(ch) {
122 // TODO Unicode "space separator"
123 return (ch === ' ') || (ch === '\u0009') || (ch === '\u000B') ||
124 (ch === '\u000C') || (ch === '\u00A0') || (ch === '\uFEFF');
125 }
126
127 // 7.3 Line Terminators
128
129 function isLineTerminator(ch) {
130 return (ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029');
131 }
132
133 // 7.6 Identifier Names and Identifiers
134
135 function isIdentifierStart(ch) {
136 // TODO UnicodeEscapeSequence
137 return (ch === '$') || (ch === '_') || isUnicodeLetter(ch);
138 }
139
140 function isIdentifierPart(ch) {
141 // TODO UnicodeCombiningMark UnicodeConnectorPunctuation and ZWNJ and ZWJ
142 return isIdentifierStart(ch) || isUnicodeDigit(ch);
143 }
144
145 // 7.6.1.1 Keywords
146 // 7.6.1.2 Future Reserved Words
147
148 function isKeyword(id) {
149 switch (id) {
150
151 // Keywords.
152 case 'break':
153 case 'case':
154 case 'catch':
155 case 'continue':
156 case 'debugger':
157 case 'default':
158 case 'delete':
159 case 'do':
160 case 'else':
161 case 'finally':
162 case 'for':
163 case 'function':
164 case 'if':
165 case 'in':
166 case 'instanceof':
167 case 'new':
168 case 'return':
169 case 'switch':
170 case 'this':
171 case 'throw':
172 case 'try':
173 case 'typeof':
174 case 'var':
175 case 'void':
176 case 'while':
177 case 'with':
178
179 // Future reserved words.
180 case 'class':
181 case 'const':
182 case 'enum':
183 case 'export':
184 case 'extends':
185 case 'import':
186 case 'super':
187
188 // strict mode
189 case 'implements':
190 case 'interface':
191 case 'let':
192 case 'package':
193 case 'private':
194 case 'protected':
195 case 'public':
196 case 'static':
197 case 'yield':
198 return true;
199 }
200
201 return false;
202 }
203
204 // Return the next character and move forward.
205
206 function nextChar() {
207 var ch = '\x00',
208 idx = index;
209 if (idx < length) {
210 ch = source[idx];
211 index += 1;
212 }
213 return ch;
214 }
215
216 // 7.4 Comments
217
218 function skipComment() {
219 var ch, blockComment, lineComment;
220
221 blockComment = false;
222 lineComment = false;
223
224 while (index < length) {
225 ch = source[index];
226
227 if (lineComment) {
228 nextChar();
229 if (isLineTerminator(ch)) {
230 lineComment = false;
231 lineNumber += 1;
232 }
233 } else if (blockComment) {
234 nextChar();
235 if (ch === '*') {
236 ch = source[index];
237 if (ch === '/') {
238 nextChar();
239 blockComment = false;
240 }
241 } else if (isLineTerminator(ch)) {
242 lineNumber += 1;
243 }
244 } else if (ch === '/') {
245 ch = source[index + 1];
246 if (ch === '/') {
247 nextChar();
248 nextChar();
249 lineComment = true;
250 } else if (ch === '*') {
251 nextChar();
252 nextChar();
253 blockComment = true;
254 } else {
255 break;
256 }
257 } else if (isWhiteSpace(ch)) {
258 nextChar();
259 } else if (isLineTerminator(ch)) {
260 nextChar();
261 lineNumber += 1;
262 } else {
263 break;
264 }
265 }
266 }
267
268 function scanIdentifier() {
269 var ch, id;
270
271 ch = source[index];
272 if (!isIdentifierStart(ch)) {
273 return;
274 }
275
276 id = nextChar();
277 while (index < length) {
278 ch = source[index];
279 if (!isIdentifierPart(ch)) {
280 break;
281 }
282 id += nextChar();
283 }
284
285 // There is no keyword or literal with only one character.
286 // Thus, it must be an identifier.
287 if (id.length === 1) {
288 return {
289 type: Token.Identifier,
290 value: id
291 };
292 }
293
294 if (isKeyword(id)) {
295 return {
296 type: Token.Keyword,
297 value: id
298 };
299 }
300
301 // 7.8.1 Null Literals
302
303 if (id === 'null') {
304 return {
305 type: Token.NullLiteral
306 };
307 }
308
309 // 7.8.2 Boolean Literals
310
311 if (id === 'true' || id === 'false') {
312 return {
313 type: Token.BooleanLiteral,
314 value: id
315 };
316 }
317
318 return {
319 type: Token.Identifier,
320 value: id
321 };
322 }
323
324 // 7.7 Punctuators
325
326 function scanPunctuator() {
327 var ch1 = source[index],
328 ch2,
329 ch3,
330 ch4;
331
332 // Check for most common single-character punctuators.
333
334 if (ch1 === ';' || ch1 === '{' || ch1 === '}') {
335 nextChar();
336 return {
337 type: Token.Punctuator,
338 value: ch1
339 };
340 }
341
342 if (ch1 === ',' || ch1 === '(' || ch1 === ')') {
343 nextChar();
344 return {
345 type: Token.Punctuator,
346 value: ch1
347 };
348 }
349
350 // Dot (.) can also start a floating-point number, hence the need
351 // to check the next character.
352
353 ch2 = source[index + 1];
354 if (ch1 === '.' && !isDecimalDigit(ch2)) {
355 return {
356 type: Token.Punctuator,
357 value: nextChar()
358 };
359 }
360
361 // Peek more characters.
362
363 ch3 = source[index + 2];
364 ch4 = source[index + 3];
365
366 // 4-character punctuator: >>>=
367
368 if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
369 if (ch4 === '=') {
370 nextChar();
371 nextChar();
372 nextChar();
373 nextChar();
374 return {
375 type: Token.Punctuator,
376 value: '>>>='
377 };
378 }
379 }
380
381 // 3-character punctuators: === !== >>> <<= >>=
382
383 if (ch1 === '=' && ch2 === '=' && ch3 === '=') {
384 nextChar();
385 nextChar();
386 nextChar();
387 return {
388 type: Token.Punctuator,
389 value: '==='
390 };
391 }
392
393 if (ch1 === '!' && ch2 === '=' && ch3 === '=') {
394 nextChar();
395 nextChar();
396 nextChar();
397 return {
398 type: Token.Punctuator,
399 value: '!=='
400 };
401 }
402
403 if (ch1 === '>' && ch2 === '>' && ch3 === '>') {
404 nextChar();
405 nextChar();
406 nextChar();
407 return {
408 type: Token.Punctuator,
409 value: '>>>'
410 };
411 }
412
413 if (ch1 === '<' && ch2 === '<' && ch3 === '=') {
414 nextChar();
415 nextChar();
416 nextChar();
417 return {
418 type: Token.Punctuator,
419 value: '<<='
420 };
421 }
422
423 if (ch1 === '>' && ch2 === '>' && ch3 === '=') {
424 nextChar();
425 nextChar();
426 nextChar();
427 return {
428 type: Token.Punctuator,
429 value: '>>='
430 };
431 }
432
433 // 2-character punctuators: <= >= == != ++ -- << >> && ||
434 // += -= *= %= &= |= ^= /=
435
436 if (ch2 === '=') {
437 if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
438 nextChar();
439 nextChar();
440 return {
441 type: Token.Punctuator,
442 value: ch1 + ch2
443 };
444 }
445 }
446
447 if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) {
448 if ('+-<>&|'.indexOf(ch2) >= 0) {
449 nextChar();
450 nextChar();
451 return {
452 type: Token.Punctuator,
453 value: ch1 + ch2
454 };
455 }
456 }
457
458 // The remaining 1-character punctuators.
459
460 if ('[]<>+-*%&|^!~?:=/'.indexOf(ch1) >= 0) {
461 return {
462 type: Token.Punctuator,
463 value: nextChar()
464 };
465 }
466 }
467
468 // 7.8.3 Numeric Literals
469
470 function scanNumericLiteral() {
471 var number, ch;
472
473 ch = source[index];
474 if (!isDecimalDigit(ch) && (ch !== '.')) {
475 return;
476 }
477
478 number = '';
479 if (ch !== '.') {
480 number = nextChar();
481 ch = source[index];
482
483 // Hex number starts with '0x'.
484 if (ch === 'x' || ch === 'X') {
485 number += nextChar();
486 while (index < length) {
487 ch = source[index];
488 if (!isHexDigit(ch)) {
489 break;
490 }
491 number += nextChar();
492 }
493 return {
494 type: Token.NumericLiteral,
495 value: parseInt(number, 16)
496 };
497 }
498
499 while (index < length) {
500 ch = source[index];
501 if (!isDecimalDigit(ch)) {
502 break;
503 }
504 number += nextChar();
505 }
506 }
507
508 if (ch === '.') {
509 number += nextChar();
510 while (index < length) {
511 ch = source[index];
512 if (!isDecimalDigit(ch)) {
513 break;
514 }
515 number += nextChar();
516 }
517 }
518
519 if (ch === 'e' || ch === 'E') {
520 number += nextChar();
521 ch = source[index];
522 if (ch === '+' || ch === '-' || isDecimalDigit(ch)) {
523 number += nextChar();
524 while (index < length) {
525 ch = source[index];
526 if (!isDecimalDigit(ch)) {
527 break;
528 }
529 number += nextChar();
530 }
531 } else {
532 ch = 'character ' + ch;
533 if (index >= length) {
534 ch = '<end>';
535 }
536 throw new Error('Line ' + lineNumber + ': Unexpected ' + ch +
537 ' after the exponent sign');
538 }
539 }
540
541 return {
542 type: Token.NumericLiteral,
543 value: parseFloat(number)
544 };
545 }
546
547 // 7.8.4 String Literals
548
549 // TODO Unicode
550 function scanStringLiteral() {
551 var str = '', quote, ch;
552
553 quote = source[index];
554 if (quote !== '\'' && quote !== '"') {
555 return;
556 }
557 nextChar();
558
559 while (index < length) {
560 ch = nextChar();
561
562 if (ch === quote) {
563 quote = '';
564 break;
565 } else if (ch === '\\') {
566 ch = nextChar();
567 if (!isLineTerminator(ch)) {
568 str += '\\';
569 str += ch;
570 }
571 } else {
572 str += ch;
573 }
574 }
575
576 if (quote !== '') {
577 throw new Error('Line ' + lineNumber + ': Unterminated string constant');
578 }
579
580 return {
581 type: Token.StringLiteral,
582 value: str
583 };
584 }
585
586 function scanRegExp() {
587 var str = '', ch, classMarker = false;
588
589 buffer = null;
590 skipComment();
591
592 ch = source[index];
593 if (ch !== '/') {
594 return;
595 }
596 str = nextChar();
597
598 while (index < length) {
599 ch = nextChar();
600 str += ch;
601 if (classMarker) {
602 if (ch === ']') {
603 classMarker = false;
604 }
605 } else {
606 if (ch === '\\') {
607 str += nextChar();
608 }
609 if (ch === '/') {
610 break;
611 }
612 if (ch === '[') {
613 classMarker = true;
614 }
615 if (isLineTerminator(ch)) {
616 throw new Error('Line ' + lineNumber +
617 ': Unexpected line terminator in a regular expression');
618 }
619 }
620 }
621
622 while (index < length) {
623 ch = source[index];
624 if (!isIdentifierPart(ch)) {
625 break;
626 }
627 str += nextChar();
628 }
629
630 return str;
631 }
632
633 function advance() {
634 var ch, token;
635
636 if (index >= length) {
637 return {
638 type: Token.EOF
639 };
640 }
641
642 token = scanPunctuator();
643 if (typeof token !== 'undefined') {
644 return token;
645 }
646
647 ch = source[index];
648
649 if (ch === '\'' || ch === '"') {
650 return scanStringLiteral();
651 }
652
653 if (ch === '.' || isDecimalDigit(ch)) {
654 return scanNumericLiteral();
655 }
656
657 token = scanIdentifier();
658 if (typeof token !== 'undefined') {
659 return token;
660 }
661
662 throw new Error('Line ' + lineNumber +
663 ': Unknown token from character ' + nextChar());
664 }
665
666 function lex() {
667 var pos, token;
668
669 if (buffer) {
670 index = buffer.range[1];
671 lineNumber = buffer.lineNumber;
672 token = buffer;
673 buffer = null;
674 return token;
675 }
676
677 buffer = null;
678 skipComment();
679
680 pos = index;
681 token = advance();
682 token.range = [pos, index];
683 token.lineNumber = lineNumber;
684
685 return token;
686 }
687
688 function lookahead() {
689 var pos, line, token;
690
691 if (buffer !== null) {
692 return buffer;
693 }
694
695 pos = index;
696 line = lineNumber;
697 token = lex();
698 index = pos;
699 lineNumber = line;
700
701 buffer = token;
702 return buffer;
703 }
704
705 // Return true if there is a line terminator before the next token.
706
707 function peekLineTerminator() {
708 var pos, line, found;
709
710 pos = index;
711 line = lineNumber;
712 skipComment();
713 found = lineNumber !== line;
714 index = pos;
715 lineNumber = line;
716
717 return found;
718 }
719
720 // Throw an exception because of the token.
721
722 function throwUnexpected(token) {
723 var s;
724
725 if (token.type === Token.EOF) {
726 throw new Error('Line ' + lineNumber + ': Unexpected <EOF>');
727 }
728
729 s = token.value;
730 if (s.length > 10) {
731 s = s.substr(0, 10) + '...';
732 }
733 throw new Error('Line ' + lineNumber + ': Unexpected token ' + s);
734 }
735
736 // Expect the next token to match the specified punctuator.
737 // If not, an exception will be thrown.
738
739 function expect(value) {
740 var token = lex();
741 if (token.type !== Token.Punctuator || token.value !== value) {
742 throwUnexpected(token);
743 }
744 }
745
746 // Expect the next token to match the specified keyword.
747 // If not, an exception will be thrown.
748
749 function expectKeyword(keyword) {
750 var token = lex();
751 if (token.type !== Token.Keyword || token.value !== keyword) {
752 throwUnexpected(token);
753 }
754 }
755
756 // Return true if the next token matches the specified punctuator.
757
758 function match(value) {
759 var token = lookahead();
760 return token.type === Token.Punctuator && token.value === value;
761 }
762
763 // Return true if the next token matches the specified keyword
764
765 function matchKeyword(keyword) {
766 var token = lookahead();
767 return token.type === Token.Keyword && token.value === keyword;
768 }
769
770 // Return true if the next token is an assignment operator
771
772 function matchAssign() {
773 var token = lookahead(),
774 op = token.value;
775
776 if (token.type !== Token.Punctuator) {
777 return false;
778 }
779 return op === '=' ||
780 op === '*=' ||
781 op === '/=' ||
782 op === '%=' ||
783 op === '+=' ||
784 op === '-=' ||
785 op === '<<=' ||
786 op === '>>=' ||
787 op === '>>>=' ||
788 op === '&=' ||
789 op === '^=' ||
790 op === '|=';
791 }
792
793
794 function consumeSemicolon() {
795 var token, line;
796
797 // Catch the very common case first.
798 if (source[index] === ';') {
799 lex();
800 return;
801 }
802
803 line = lineNumber;
804 skipComment();
805 if (lineNumber !== line) {
806 return;
807 }
808
809 if (match(';')) {
810 lex();
811 return;
812 }
813
814 token = lookahead();
815 if (token.type !== Token.EOF && !match('}')) {
816 throwUnexpected(token);
817 }
818 return;
819 }
820
821 // 11.1.4 Array Initialiser
822
823 function parseArrayInitialiser() {
824 var elements = [],
825 undef;
826
827 expect('[');
828
829 while (index < length) {
830 if (match(']')) {
831 lex();
832 break;
833 }
834
835 if (match(',')) {
836 lex();
837 elements.push(undef);
838 } else {
839 elements.push(parseAssignmentExpression());
840
841 if (match(']')) {
842 lex();
843 break;
844 }
845
846 expect(',');
847 }
848 }
849
850 return {
851 type: Syntax.ArrayExpression,
852 elements: elements
853 };
854 }
855
856 // 11.1.5 Object Initialiser
857
858 function parseObjectInitialiser() {
859 var token, expr, properties = [], property;
860
861 expect('{');
862
863 // TODO handle 'get' and 'set'
864 while (index < length) {
865 token = lex();
866 if (token.type === Token.Punctuator && token.value === '}') {
867 break;
868 }
869
870 property = {};
871 switch (token.type) {
872 case Token.Identifier:
873 property.key = {
874 type: Syntax.Identifier,
875 name: token.value
876 };
877
878 // Property Assignment: Getter and Setter.
879
880 if (token.value === 'get' && !match(':')) {
881 token = lex();
882 if (token.type !== Token.Identifier) {
883 throwUnexpected(token);
884 }
885 expect('(');
886 expect(')');
887 property = {
888 key: {
889 type: Syntax.Identifier,
890 name: token.value
891 },
892 value: {
893 type: Syntax.FunctionExpression,
894 id: null,
895 params: [],
896 body: parseBlock()
897 },
898 kind: 'get'
899 };
900 break;
901 }
902
903 if (token.value === 'set' && !match(':')) {
904 token = lex();
905 if (token.type !== Token.Identifier) {
906 throwUnexpected(token);
907 }
908 property.key = {
909 type: Syntax.Identifier,
910 name: token.value
911 };
912 expect('(');
913 token = lex();
914 if (token.type !== Token.Identifier) {
915 throwUnexpected(token);
916 }
917 expect(')');
918 property.value = {
919 type: Syntax.FunctionExpression,
920 id: null,
921 params: [{
922 type: Syntax.Identifier,
923 name: token.value
924 }],
925 body: parseBlock()
926 };
927 property.kind = 'set';
928 break;
929 }
930
931 expect(':');
932 property.value = parseAssignmentExpression();
933 break;
934
935 case Token.StringLiteral:
936 case Token.NumericLiteral:
937 property.key = {
938 type: Syntax.Literal,
939 value: token.value
940 };
941 expect(':');
942 property.value = parseAssignmentExpression();
943 break;
944
945 default:
946 throwUnexpected(token);
947 }
948 properties.push(property);
949
950 token = lookahead();
951 if (token.type === Token.Punctuator && token.value === '}') {
952 lex();
953 break;
954 }
955 expect(',');
956 }
957
958 return {
959 type: Syntax.ObjectExpression,
960 properties: properties
961 };
962 }
963
964 // 11.1 Primary Expressions
965
966 function parsePrimaryExpression() {
967 var token, expr;
968
969 if (match('[')) {
970 return parseArrayInitialiser();
971 }
972
973 if (match('{')) {
974 return parseObjectInitialiser();
975 }
976
977 if (match('(')) {
978 lex();
979 expr = parseExpression();
980 expect(')');
981 return expr.expression;
982 }
983
984 if (matchKeyword('function')) {
985 return parseFunctionExpression();
986 }
987
988 if (matchKeyword('this')) {
989 lex();
990 return {
991 type: Syntax.ThisExpression
992 };
993 }
994
995 if (match('/') || match('/=')) {
996 return {
997 type: Syntax.Literal,
998 value: scanRegExp()
999 };
1000 }
1001
1002 token = lex();
1003
1004 if (token.type === Token.Identifier) {
1005 return {
1006 type: Syntax.Identifier,
1007 name: token.value
1008 };
1009 }
1010
1011 if (token.type === Token.BooleanLiteral) {
1012 return {
1013 type: Syntax.Literal,
1014 value: (token.value === 'true')
1015 };
1016 }
1017
1018 if (token.type === Token.NullLiteral) {
1019 return {
1020 type: Syntax.Literal,
1021 value: null
1022 };
1023 }
1024
1025 if (token.type === Token.NumericLiteral) {
1026 return {
1027 type: Syntax.Literal,
1028 value: token.value
1029 };
1030 }
1031
1032 if (token.type === Token.StringLiteral) {
1033 return {
1034 type: Syntax.Literal,
1035 value: token.value
1036 };
1037 }
1038
1039 return;
1040 }
1041
1042 // 11.2 Left-Hand-Side Expressions
1043
1044 function parseArguments() {
1045 var args = [];
1046
1047 expect('(');
1048
1049 if (!match(')')) {
1050 while (index < length) {
1051 args.push(parseAssignmentExpression());
1052 if (match(')')) {
1053 break;
1054 }
1055 expect(',');
1056 }
1057 }
1058
1059 expect(')');
1060
1061 return args;
1062 }
1063
1064 function parseMemberExpression() {
1065 var expr, token, property;
1066
1067 expr = parsePrimaryExpression();
1068
1069 while (index < length) {
1070 if (match('.')) {
1071 lex();
1072 token = lex();
1073 if (token.type !== Token.Identifier) {
1074 throw new Error('Line ' + lineNumber +
1075 ': Expecting an identifier after dot (.)');
1076 }
1077 property = {
1078 type: Syntax.Identifier,
1079 name: token.value
1080 };
1081 expr = {
1082 type: Syntax.MemberExpression,
1083 computed: false,
1084 object: expr,
1085 property: property
1086 };
1087 } else if (match('[')) {
1088 lex();
1089 property = parseExpression();
1090 if (property.type === Syntax.ExpressionStatement) {
1091 property = property.expression;
1092 }
1093 expr = {
1094 type: Syntax.MemberExpression,
1095 computed: true,
1096 object: expr,
1097 property: property
1098 };
1099 expect(']');
1100 } else if (match('(')) {
1101 expr = {
1102 type: Syntax.CallExpression,
1103 callee: expr,
1104 'arguments': parseArguments()
1105 };
1106 } else {
1107 break;
1108 }
1109 }
1110
1111 return expr;
1112 }
1113
1114 function parseLeftHandSideExpression() {
1115 var useNew, expr, args;
1116
1117 useNew = matchKeyword('new');
1118 if (useNew) {
1119 // Read the keyword.
1120 lex();
1121 expr = parseLeftHandSideExpression();
1122 } else {
1123 expr = parseMemberExpression();
1124 }
1125
1126 if (match('(')) {
1127 args = parseArguments();
1128 }
1129
1130 if (useNew) {
1131
1132 // Force to have at least an empty argument list.
1133 if (typeof args === 'undefined') {
1134 args = [];
1135 }
1136
1137 // e.g. "new x()" thus adopt the CallExpression of "x()".
1138 if (expr.type === Syntax.CallExpression) {
1139 args = expr['arguments'];
1140 expr = expr.callee;
1141 }
1142
1143 return {
1144 type: Syntax.NewExpression,
1145 callee: expr,
1146 'arguments': args
1147 };
1148 }
1149
1150 if (typeof args !== 'undefined') {
1151 return {
1152 type: Syntax.CallExpression,
1153 callee: expr,
1154 'arguments': args
1155 };
1156 }
1157
1158 return expr;
1159 }
1160
1161 // 11.3 Postfix Expressions
1162
1163 function parsePostfixExpression() {
1164 var expr = parseLeftHandSideExpression();
1165
1166 if ((match('++') || match('--')) && !peekLineTerminator()) {
1167 expr = {
1168 type: Syntax.UpdateExpression,
1169 operator: lex().value,
1170 argument: expr,
1171 prefix: false
1172 };
1173 }
1174
1175 return expr;
1176 }
1177
1178 // 11.4 Unary Operators
1179
1180 function parseUnaryExpression() {
1181
1182 if (match('++') || match('--')) {
1183 return {
1184 type: Syntax.UpdateExpression,
1185 operator: lex().value,
1186 argument: parseUnaryExpression(),
1187 prefix: true
1188 };
1189 }
1190
1191 if (match('+') || match('-') || match('~') || match('!')) {
1192 return {
1193 type: Syntax.UnaryExpression,
1194 operator: lex().value,
1195 argument: parseUnaryExpression()
1196 };
1197 }
1198
1199 if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
1200 return {
1201 type: Syntax.UnaryExpression,
1202 operator: lex().value,
1203 argument: parseUnaryExpression()
1204 };
1205 }
1206
1207 return parsePostfixExpression();
1208 }
1209
1210 // 11.5 Multiplicative Operators
1211
1212 function parseMultiplicativeExpression() {
1213 var expr = parseUnaryExpression();
1214
1215 while (match('*') || match('/') || match('%')) {
1216 expr = {
1217 type: Syntax.BinaryExpression,
1218 operator: lex().value,
1219 left: expr,
1220 right: parseUnaryExpression()
1221 };
1222 }
1223
1224 return expr;
1225 }
1226
1227 // 11.6 Additive Operators
1228
1229 function parseAdditiveExpression() {
1230 var expr = parseMultiplicativeExpression();
1231
1232 while (match('+') || match('-')) {
1233 expr = {
1234 type: Syntax.BinaryExpression,
1235 operator: lex().value,
1236 left: expr,
1237 right: parseMultiplicativeExpression()
1238 };
1239 }
1240
1241 return expr;
1242 }
1243
1244 // 11.7 Bitwise Shift Operators
1245
1246 function parseShiftExpression() {
1247 var expr = parseAdditiveExpression();
1248
1249 while (match('<<') || match('>>') || match('>>>')) {
1250 expr = {
1251 type: Syntax.BinaryExpression,
1252 operator: lex().value,
1253 left: expr,
1254 right: parseAdditiveExpression()
1255 };
1256 }
1257
1258 return expr;
1259 }
1260
1261 // 11.8 Relational Operators
1262
1263 function parseRelationalExpression() {
1264 var expr = parseShiftExpression();
1265
1266 if (match('<') || match('>') || match('<=') || match('>=')) {
1267 expr = {
1268 type: Syntax.BinaryExpression,
1269 operator: lex().value,
1270 left: expr,
1271 right: parseRelationalExpression()
1272 };
1273 } else if (matchKeyword('in')) {
1274 lex();
1275 expr = {
1276 type: Syntax.BinaryExpression,
1277 operator: 'in',
1278 left: expr,
1279 right: parseRelationalExpression()
1280 };
1281 } else if (matchKeyword('instanceof')) {
1282 lex();
1283 expr = {
1284 type: Syntax.BinaryExpression,
1285 operator: 'instanceof',
1286 left: expr,
1287 right: parseRelationalExpression()
1288 };
1289 }
1290
1291 return expr;
1292 }
1293
1294 // 11.9 Equality Operators
1295
1296 function parseEqualityExpression() {
1297 var expr = parseRelationalExpression();
1298
1299 while (match('==') || match('!=') || match('===') || match('!==')) {
1300 expr = {
1301 type: Syntax.BinaryExpression,
1302 operator: lex().value,
1303 left: expr,
1304 right: parseRelationalExpression()
1305 };
1306 }
1307
1308 return expr;
1309 }
1310
1311 // 11.10 Binary Bitwise Operators
1312
1313 function parseBitwiseANDExpression() {
1314 var expr = parseEqualityExpression();
1315
1316 while (match('&')) {
1317 lex();
1318 expr = {
1319 type: Syntax.BinaryExpression,
1320 operator: '&',
1321 left: expr,
1322 right: parseEqualityExpression()
1323 };
1324 }
1325
1326 return expr;
1327 }
1328
1329 function parseBitwiseORExpression() {
1330 var expr = parseBitwiseANDExpression();
1331
1332 while (match('|')) {
1333 lex();
1334 expr = {
1335 type: Syntax.BinaryExpression,
1336 operator: '|',
1337 left: expr,
1338 right: parseBitwiseANDExpression()
1339 };
1340 }
1341
1342 return expr;
1343 }
1344
1345 function parseBitwiseXORExpression() {
1346 var expr = parseBitwiseORExpression();
1347
1348 while (match('^')) {
1349 lex();
1350 expr = {
1351 type: Syntax.BinaryExpression,
1352 operator: '^',
1353 left: expr,
1354 right: parseBitwiseORExpression()
1355 };
1356 }
1357
1358 return expr;
1359 }
1360
1361 // 11.11 Binary Logical Operators
1362
1363 function parseLogicalANDExpression() {
1364 var expr = parseBitwiseXORExpression();
1365
1366 while (match('&&')) {
1367 lex();
1368 expr = {
1369 type: Syntax.LogicalExpression,
1370 operator: '&&',
1371 left: expr,
1372 right: parseBitwiseXORExpression()
1373 };
1374 }
1375
1376 return expr;
1377 }
1378
1379 function parseLogicalORExpression() {
1380 var expr = parseLogicalANDExpression();
1381
1382 while (match('||')) {
1383 lex();
1384 expr = {
1385 type: Syntax.LogicalExpression,
1386 operator: '||',
1387 left: expr,
1388 right: parseLogicalANDExpression()
1389 };
1390 }
1391
1392 return expr;
1393 }
1394
1395 // 11.12 Conditional Operator
1396
1397 function parseConditionalExpression() {
1398 var token, expr;
1399
1400 token = lookahead();
1401 expr = parseLogicalORExpression();
1402 if (typeof expr === 'undefined') {
1403 throwUnexpected(token);
1404 }
1405
1406 if (match('?')) {
1407 lex();
1408 expr = {
1409 type: Syntax.ConditionalExpression,
1410 test: expr
1411 };
1412 expr.consequent = parseAssignmentExpression();
1413 expect(':');
1414 expr.alternate = parseAssignmentExpression();
1415 }
1416
1417 return expr;
1418 }
1419
1420 // 11.13 Assignment Operators
1421
1422 function parseAssignmentExpression() {
1423
1424 var expr = parseConditionalExpression();
1425
1426 if (matchAssign()) {
1427 expr = {
1428 type: Syntax.AssignmentExpression,
1429 operator: lex().value,
1430 left: expr,
1431 right: parseAssignmentExpression()
1432 };
1433 }
1434
1435 return expr;
1436 }
1437
1438 // 11.14 Comma Operator
1439
1440 function parseExpression() {
1441 var expr = parseAssignmentExpression();
1442
1443 if (match(',')) {
1444 expr = {
1445 type: Syntax.SequenceExpression,
1446 expressions: [ expr ]
1447 };
1448
1449 while (index < length) {
1450 if (!match(',')) {
1451 break;
1452 }
1453 lex();
1454 expr.expressions.push(parseAssignmentExpression());
1455 }
1456 }
1457
1458 return {
1459 type: Syntax.ExpressionStatement,
1460 expression: expr
1461 };
1462 }
1463
1464 // 12.1 Block
1465
1466 function parseStatementList() {
1467 var list = [],
1468 statement;
1469
1470 while (index < length) {
1471 if (match('}')) {
1472 break;
1473 }
1474 statement = parseStatement();
1475 if (typeof statement === 'undefined') {
1476 break;
1477 }
1478 list.push(statement);
1479 }
1480
1481 return list;
1482 }
1483
1484 function parseBlock() {
1485 var block;
1486
1487 expect('{');
1488
1489 block = parseStatementList();
1490
1491 expect('}');
1492
1493 return {
1494 type: Syntax.BlockStatement,
1495 body: block
1496 };
1497 }
1498
1499 // 12.2 Variable Statement
1500
1501 function parseVariableDeclaration() {
1502 var token, id, init;
1503
1504 token = lex();
1505 if (token.type !== Token.Identifier) {
1506 throw new Error('Line ' + lineNumber + ': Expected an identifier');
1507 }
1508
1509 id = {
1510 type: Syntax.Identifier,
1511 name: token.value
1512 };
1513
1514 init = null;
1515 if (match('=')) {
1516 lex();
1517 init = parseAssignmentExpression();
1518 }
1519
1520 return {
1521 id: id,
1522 init: init
1523 };
1524 }
1525
1526 function parseVariableDeclarationList() {
1527 var list = [];
1528
1529 while (index < length) {
1530 list.push(parseVariableDeclaration());
1531 if (!match(',')) {
1532 break;
1533 }
1534 lex();
1535 }
1536
1537 return list;
1538 }
1539
1540 function parseVariableStatement() {
1541 var declarations;
1542
1543 expectKeyword('var');
1544
1545 declarations = parseVariableDeclarationList();
1546
1547 consumeSemicolon();
1548
1549 return {
1550 type: Syntax.VariableDeclaration,
1551 declarations: declarations,
1552 kind: 'var'
1553 };
1554 }
1555
1556 // http://wiki.ecmascript.org/doku.php?id=harmony:let.
1557 // Warning: This is experimental and not in the specification yet.
1558
1559 function parseLetStatement() {
1560 var declarations;
1561
1562 expectKeyword('let');
1563
1564 declarations = parseVariableDeclarationList();
1565
1566 consumeSemicolon();
1567
1568 return {
1569 type: Syntax.VariableDeclaration,
1570 declarations: declarations,
1571 kind: 'let'
1572 };
1573 }
1574
1575 // 12.3 Empty Statement
1576
1577 function parseEmptyStatement() {
1578 expect(';');
1579
1580 return {
1581 type: Syntax.EmptyStatement
1582 };
1583 }
1584
1585 // 12.4 Expression Statement
1586
1587 function parseExpressionStatement() {
1588 var expr = parseExpression();
1589
1590 consumeSemicolon();
1591
1592 return expr;
1593 }
1594
1595 // 12.5 If statement
1596
1597 function parseIfStatement() {
1598 var test, consequent, alternate;
1599
1600 expectKeyword('if');
1601
1602 expect('(');
1603
1604 test = parseExpression().expression;
1605
1606 expect(')');
1607
1608 consequent = parseStatement();
1609
1610 if (matchKeyword('else')) {
1611 lex();
1612 alternate = parseStatement();
1613 } else {
1614 alternate = null;
1615 }
1616
1617 return {
1618 type: Syntax.IfStatement,
1619 test: test,
1620 consequent: consequent,
1621 alternate: alternate
1622 };
1623 }
1624
1625 // 12.6 Iteration Statements
1626
1627 function parseDoWhileStatement() {
1628 var body, test;
1629
1630 expectKeyword('do');
1631
1632 body = parseStatement();
1633
1634 expectKeyword('while');
1635
1636 expect('(');
1637
1638 test = parseExpression().expression;
1639
1640 expect(')');
1641
1642 consumeSemicolon();
1643
1644 return {
1645 type: Syntax.DoWhileStatement,
1646 body: body,
1647 test: test
1648 };
1649 }
1650
1651 function parseWhileStatement() {
1652 var test, body;
1653
1654 expectKeyword('while');
1655
1656 expect('(');
1657
1658 test = parseExpression().expression;
1659
1660 expect(')');
1661
1662 body = parseStatement();
1663
1664 return {
1665 type: Syntax.WhileStatement,
1666 test: test,
1667 body: body
1668 };
1669 }
1670
1671 function parseForStatement() {
1672 var kind, init, test, update, left, right, body;
1673
1674 init = test = update = null;
1675
1676 expectKeyword('for');
1677
1678 expect('(');
1679
1680 if (match(';')) {
1681 lex();
1682 } else {
1683 if (matchKeyword('var') || matchKeyword('let')) {
1684 kind = lex().value;
1685 init = {
1686 type: Syntax.VariableDeclaration,
1687 declarations: parseVariableDeclarationList(),
1688 kind: kind
1689 };
1690
1691 if (matchKeyword('in')) {
1692 lex();
1693 left = init;
1694 right = parseExpression().expression;
1695 init = null;
1696 }
1697 } else {
1698 init = parseExpression().expression;
1699 }
1700
1701 if (typeof left === 'undefined') {
1702 if (init.hasOwnProperty('operator') && init.operator === 'in') {
1703 left = init.left;
1704 right = init.right;
1705 init = null;
1706 } else {
1707 expect(';');
1708 }
1709 }
1710 }
1711
1712 if (typeof left === 'undefined') {
1713
1714 if (!match(';')) {
1715 test = parseExpression().expression;
1716 }
1717 expect(';');
1718
1719 if (!match(')')) {
1720 update = parseExpression().expression;
1721 }
1722 }
1723
1724 expect(')');
1725
1726 body = parseStatement();
1727
1728 if (typeof left === 'undefined') {
1729 return {
1730 type: Syntax.ForStatement,
1731 init: init,
1732 test: test,
1733 update: update,
1734 body: body
1735 };
1736 }
1737
1738 return {
1739 type: Syntax.ForInStatement,
1740 left: left,
1741 right: right,
1742 body: body,
1743 each: false
1744 };
1745 }
1746
1747 // 12.7 The continue statement
1748
1749 function parseContinueStatement() {
1750 var token, label = null;
1751
1752 expectKeyword('continue');
1753
1754 // Optimize the most common form: 'continue;'.
1755 if (source[index] === ';') {
1756 lex();
1757 return {
1758 type: Syntax.ContinueStatement,
1759 label: null
1760 };
1761 }
1762
1763 if (peekLineTerminator()) {
1764 return {
1765 type: Syntax.ContinueStatement,
1766 label: null
1767 };
1768 }
1769
1770 token = lookahead();
1771 if (token.type === Token.Identifier) {
1772 lex();
1773 label = {
1774 type: Syntax.Identifier,
1775 name: token.value
1776 };
1777 }
1778
1779 consumeSemicolon();
1780
1781 return {
1782 type: Syntax.ContinueStatement,
1783 label: label
1784 };
1785 }
1786
1787 // 12.8 The break statement
1788
1789 function parseBreakStatement() {
1790 var token, label = null;
1791
1792 expectKeyword('break');
1793
1794 // Optimize the most common form: 'break;'.
1795 if (source[index] === ';') {
1796 lex();
1797 return {
1798 type: Syntax.BreakStatement,
1799 label: null
1800 };
1801 }
1802
1803 if (peekLineTerminator()) {
1804 return {
1805 type: Syntax.BreakStatement,
1806 label: null
1807 };
1808 }
1809
1810 token = lookahead();
1811 if (token.type === Token.Identifier) {
1812 lex();
1813 label = {
1814 type: Syntax.Identifier,
1815 name: token.value
1816 };
1817 }
1818
1819 consumeSemicolon();
1820
1821 return {
1822 type: Syntax.BreakStatement,
1823 label: label
1824 };
1825 }
1826
1827 // 12.9 The return statement
1828
1829 function parseReturnStatement() {
1830 var token, argument = null;
1831
1832 expectKeyword('return');
1833
1834 // 'return' followed by a space and an identifier is very common.
1835 if (source[index] === ' ') {
1836 if (isIdentifierStart(source[index + 1])) {
1837 argument = parseExpression().expression;
1838 consumeSemicolon();
1839 return {
1840 type: Syntax.ReturnStatement,
1841 argument: argument
1842 };
1843 }
1844 }
1845
1846 if (peekLineTerminator()) {
1847 return {
1848 type: Syntax.ReturnStatement,
1849 argument: null
1850 };
1851 }
1852
1853 if (!match(';')) {
1854 token = lookahead();
1855 if (!match('}') && token.type !== Token.EOF) {
1856 argument = parseExpression().expression;
1857 }
1858 }
1859
1860 consumeSemicolon();
1861
1862 return {
1863 type: Syntax.ReturnStatement,
1864 argument: argument
1865 };
1866 }
1867
1868 // 12.10 The with statement
1869
1870 function parseWithStatement() {
1871 var object, body;
1872
1873 expectKeyword('with');
1874
1875 expect('(');
1876
1877 object = parseExpression().expression;
1878
1879 expect(')');
1880
1881 body = parseStatement();
1882
1883 return {
1884 type: Syntax.WithStatement,
1885 object: object,
1886 body: body
1887 };
1888 }
1889
1890 // 12.10 The swith statement
1891
1892 function parseSwitchConsequent() {
1893 var consequent = [],
1894 statement;
1895
1896 while (index < length) {
1897 if (match('}') || matchKeyword('default') || matchKeyword('case')) {
1898 break;
1899 }
1900 statement = parseStatement();
1901 if (typeof statement === 'undefined') {
1902 break;
1903 }
1904 consequent.push(statement);
1905 }
1906
1907 return consequent;
1908 }
1909
1910 function parseSwitchStatement() {
1911 var discriminant, cases, test, consequent, statement;
1912
1913 expectKeyword('switch');
1914
1915 expect('(');
1916
1917 discriminant = parseExpression().expression;
1918
1919 expect(')');
1920
1921 expect('{');
1922
1923 if (match('}')) {
1924 lex();
1925 return {
1926 type: Syntax.SwitchStatement,
1927 discriminant: discriminant
1928 };
1929 }
1930
1931 cases = [];
1932
1933 while (index < length) {
1934 if (match('}')) {
1935 break;
1936 }
1937
1938 if (matchKeyword('default')) {
1939 lex();
1940 test = null;
1941 } else {
1942 expectKeyword('case');
1943 test = parseExpression().expression;
1944 }
1945 expect(':');
1946
1947 cases.push({
1948 type: Syntax.SwitchCase,
1949 test: test,
1950 consequent: parseSwitchConsequent()
1951 });
1952 }
1953
1954 expect('}');
1955
1956 return {
1957 type: Syntax.SwitchStatement,
1958 discriminant: discriminant,
1959 cases: cases
1960 };
1961 }
1962
1963 // 12.13 The throw statement
1964
1965 function parseThrowStatement() {
1966 var argument;
1967
1968 expectKeyword('throw');
1969
1970 if (peekLineTerminator()) {
1971 throw new Error('Line ' + lineNumber +
1972 ': Unexpected line terminator after throw');
1973 }
1974
1975 argument = parseExpression().expression;
1976
1977 consumeSemicolon();
1978
1979 return {
1980 type: Syntax.ThrowStatement,
1981 argument: argument
1982 };
1983 }
1984
1985 // 12.14 The try statement
1986
1987 function parseTryStatement() {
1988 var block, handlers = [], param, finalizer = null;
1989
1990 expectKeyword('try');
1991
1992 block = parseBlock();
1993
1994 if (matchKeyword('catch')) {
1995 lex();
1996 expect('(');
1997 if (!match(')')) {
1998 param = parseExpression().expression;
1999 }
2000 expect(')');
2001
2002 handlers.push({
2003 type: Syntax.CatchClause,
2004 param: param,
2005 guard: null,
2006 body: parseBlock()
2007 });
2008 }
2009
2010 if (matchKeyword('finally')) {
2011 lex();
2012 finalizer = parseBlock();
2013 }
2014
2015 return {
2016 type: Syntax.TryStatement,
2017 block: block,
2018 handlers: handlers,
2019 finalizer: finalizer
2020 };
2021 }
2022
2023 // 12.15 The debugger statement
2024
2025 function parseDebuggerStatement() {
2026 expectKeyword('debugger');
2027
2028 consumeSemicolon();
2029
2030 return {
2031 type: Syntax.DebuggerStatement
2032 };
2033 }
2034
2035 // 12 Statements
2036
2037 function parseStatement() {
2038 var token = lookahead(),
2039 stat;
2040
2041 if (token.type === Token.EOF) {
2042 return;
2043 }
2044
2045 if (token.type === Token.Punctuator) {
2046 switch (token.value) {
2047 case ';':
2048 return parseEmptyStatement();
2049 case '{':
2050 return parseBlock();
2051 case '(':
2052 return parseExpressionStatement();
2053 default:
2054 break;
2055 }
2056 }
2057
2058 if (token.type === Token.Keyword) {
2059 switch (token.value) {
2060 case 'break':
2061 return parseBreakStatement();
2062 case 'continue':
2063 return parseContinueStatement();
2064 case 'debugger':
2065 return parseDebuggerStatement();
2066 case 'do':
2067 return parseDoWhileStatement();
2068 case 'for':
2069 return parseForStatement();
2070 case 'if':
2071 return parseIfStatement();
2072 case 'let':
2073 return parseLetStatement();
2074 case 'return':
2075 return parseReturnStatement();
2076 case 'switch':
2077 return parseSwitchStatement();
2078 case 'throw':
2079 return parseThrowStatement();
2080 case 'try':
2081 return parseTryStatement();
2082 case 'var':
2083 return parseVariableStatement();
2084 case 'while':
2085 return parseWhileStatement();
2086 case 'with':
2087 return parseWithStatement();
2088 default:
2089 break;
2090 }
2091 }
2092
2093 stat = parseExpression();
2094
2095 if (stat.expression.type === Syntax.FunctionExpression) {
2096 if (stat.expression.id !== null) {
2097 return {
2098 type: Syntax.FunctionDeclaration,
2099 id: stat.expression.id,
2100 params: stat.expression.params,
2101 body: stat.expression.body
2102 };
2103 }
2104 }
2105
2106 // 12.12 Labelled Statements
2107 if ((stat.expression.type === Syntax.Identifier) && match(':')) {
2108 lex();
2109 return {
2110 type: Syntax.LabeledStatement,
2111 label: stat.expression,
2112 body: parseStatement()
2113 };
2114 }
2115
2116 consumeSemicolon();
2117
2118 return stat;
2119 }
2120
2121 // 13 Function Definition
2122
2123 function parseFunctionDeclaration() {
2124 var token, id = null, params = [], body;
2125
2126 expectKeyword('function');
2127
2128 token = lex();
2129 if (token.type !== Token.Identifier) {
2130 throwUnexpected(token);
2131 }
2132 id = {
2133 type: Syntax.Identifier,
2134 name: token.value
2135 };
2136
2137 expect('(');
2138
2139 if (!match(')')) {
2140 while (index < length) {
2141 token = lex();
2142 if (token.type !== Token.Identifier) {
2143 throwUnexpected(token);
2144 }
2145 params.push({
2146 type: Syntax.Identifier,
2147 name: token.value
2148 });
2149 if (match(')')) {
2150 break;
2151 }
2152 expect(',');
2153 }
2154 }
2155
2156 expect(')');
2157
2158 body = parseBlock();
2159
2160 return {
2161 type: Syntax.FunctionDeclaration,
2162 id: id,
2163 params: params,
2164 body: body
2165 };
2166 }
2167
2168 function parseFunctionExpression() {
2169 var token, id = null, params = [], body;
2170
2171 expectKeyword('function');
2172
2173 if (!match('(')) {
2174 token = lex();
2175 if (token.type !== Token.Identifier) {
2176 throwUnexpected(token);
2177 }
2178 id = {
2179 type: Syntax.Identifier,
2180 name: token.value
2181 };
2182 }
2183
2184 expect('(');
2185
2186 if (!match(')')) {
2187 while (index < length) {
2188 token = lex();
2189 if (token.type !== Token.Identifier) {
2190 throwUnexpected(token);
2191 }
2192 params.push({
2193 type: Syntax.Identifier,
2194 name: token.value
2195 });
2196 if (match(')')) {
2197 break;
2198 }
2199 expect(',');
2200 }
2201 }
2202
2203 expect(')');
2204
2205 body = parseBlock();
2206
2207 return {
2208 type: Syntax.FunctionExpression,
2209 id: id,
2210 params: params,
2211 body: body
2212 };
2213 }
2214
2215 // 14 Program
2216
2217 function parseSourceElement() {
2218 var token;
2219
2220 token = lookahead();
2221 if (token.type === Token.EOF) {
2222 return;
2223 }
2224
2225 if (matchKeyword('function')) {
2226 return parseFunctionDeclaration();
2227 }
2228
2229 return parseStatement();
2230 }
2231
2232 function parseSourceElements() {
2233 var sourceElement, sourceElements = [];
2234
2235 while (index < length) {
2236 sourceElement = parseSourceElement();
2237 if (typeof sourceElement === 'undefined') {
2238 break;
2239 }
2240 sourceElements.push(sourceElement);
2241 }
2242 return sourceElements;
2243 }
2244
2245 function parseProgram() {
2246 return {
2247 type: Syntax.Program,
2248 body: parseSourceElements()
2249 };
2250 }
2251
2252 // The following functions are needed only when the option to preserve
2253 // the comments is active.
2254
2255 function scanComment() {
2256 var comment, ch, start, blockComment, lineComment;
2257
2258 comment = '';
2259 blockComment = false;
2260 lineComment = false;
2261
2262 while (index < length) {
2263 ch = source[index];
2264
2265 if (lineComment) {
2266 ch = nextChar();
2267 if (isLineTerminator(ch)) {
2268 lineComment = false;
2269 lineNumber += 1;
2270 comments.push({
2271 range: [start, index - 1],
2272 type: 'Line',
2273 value: comment
2274 });
2275 comment = '';
2276 } else {
2277 comment += ch;
2278 }
2279 } else if (blockComment) {
2280 ch = nextChar();
2281 comment += ch;
2282 if (ch === '*') {
2283 ch = source[index];
2284 if (ch === '/') {
2285 comment = comment.substr(0, comment.length - 1);
2286 blockComment = false;
2287 nextChar();
2288 comments.push({
2289 range: [start, index - 1],
2290 type: 'Block',
2291 value: comment
2292 });
2293 comment = '';
2294 }
2295 } else if (isLineTerminator(ch)) {
2296 lineNumber += 1;
2297 }
2298 } else if (ch === '/') {
2299 ch = source[index + 1];
2300 if (ch === '/') {
2301 start = index;
2302 nextChar();
2303 nextChar();
2304 lineComment = true;
2305 } else if (ch === '*') {
2306 start = index;
2307 nextChar();
2308 nextChar();
2309 blockComment = true;
2310 } else {
2311 break;
2312 }
2313 } else if (isWhiteSpace(ch)) {
2314 nextChar();
2315 } else if (isLineTerminator(ch)) {
2316 nextChar();
2317 lineNumber += 1;
2318 } else {
2319 break;
2320 }
2321 }
2322
2323 if (comment.length > 0) {
2324 comments.push({
2325 range: [start, index],
2326 type: (blockComment) ? 'Block' : 'Line',
2327 value: comment
2328 });
2329 }
2330 }
2331
2332 exports.parse = function (code, options) {
2333
2334 var program,
2335 comment = false,
2336 original = {};
2337
2338 if (typeof options !== 'undefined') {
2339 if (typeof options.comment === 'boolean') {
2340 comment = options.comment;
2341 }
2342 }
2343
2344 source = code;
2345 index = 0;
2346 lineNumber = (source.length > 0) ? 1 : 0;
2347 length = source.length;
2348 buffer = null;
2349
2350 if (comment) {
2351 // Run-time patching.
2352 comments = [];
2353 original.skipComment = skipComment;
2354 skipComment = scanComment;
2355 }
2356
2357 program = parseProgram();
2358
2359 if (comment) {
2360 program.comments = comments;
2361 skipComment = original.skipComment;
2362 comments = [];
2363 }
2364
2365 return program;
2366 };
2367
2368 // Sync with package.json.
2369 exports.version = '0.9.4';
2370
2371 }(typeof exports === 'undefined' ? (esprima = {}) : exports));
...\ No newline at end of file ...\ No newline at end of file
1 phantom.injectJs('casper.js'); 1 phantom.injectJs('casper.js');
2 phantom.injectJs('lib/vendors/esprima.js');
2 3
3 var fs = require('fs'); 4 var fs = require('fs');
4 var casper = new phantom.Casper({ 5 var casper = new phantom.Casper({
......