added syntax checking for test files using esprima.js
Showing
3 changed files
with
2381 additions
and
2 deletions
... | @@ -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 | ... | ... |
lib/vendors/esprima.js
0 → 100644
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 |
-
Please register or sign in to post a comment