44c222f71ba81eea23933f7d07bc209cfe615343
[platform/framework/web/crosswalk-tizen.git] /
1 /*
2 Copyright (C) 2015 Fred K. Schott <fkschott@gmail.com>
3 Copyright (C) 2013 Ariya Hidayat <ariya.hidayat@gmail.com>
4 Copyright (C) 2013 Thaddee Tyl <thaddee.tyl@gmail.com>
5 Copyright (C) 2013 Mathias Bynens <mathias@qiwi.be>
6 Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
7 Copyright (C) 2012 Mathias Bynens <mathias@qiwi.be>
8 Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl>
9 Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com>
10 Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
11 Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com>
12 Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
13
14 Redistribution and use in source and binary forms, with or without
15 modification, are permitted provided that the following conditions are met:
16
17 * Redistributions of source code must retain the above copyright
18   notice, this list of conditions and the following disclaimer.
19 * Redistributions in binary form must reproduce the above copyright
20   notice, this list of conditions and the following disclaimer in the
21   documentation and/or other materials provided with the distribution.
22
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
27 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34 /*eslint no-undefined:0, no-use-before-define: 0*/
35
36 "use strict";
37
38 var syntax = require("./lib/syntax"),
39     tokenInfo = require("./lib/token-info"),
40     astNodeTypes = require("./lib/ast-node-types"),
41     astNodeFactory = require("./lib/ast-node-factory"),
42     defaultFeatures = require("./lib/features"),
43     Messages = require("./lib/messages"),
44     XHTMLEntities = require("./lib/xhtml-entities"),
45     StringMap = require("./lib/string-map"),
46     commentAttachment = require("./lib/comment-attachment");
47
48 var Token = tokenInfo.Token,
49     TokenName = tokenInfo.TokenName,
50     FnExprTokens = tokenInfo.FnExprTokens,
51     Regex = syntax.Regex,
52     PropertyKind,
53     source,
54     strict,
55     index,
56     lineNumber,
57     lineStart,
58     length,
59     lookahead,
60     state,
61     extra;
62
63 PropertyKind = {
64     Data: 1,
65     Get: 2,
66     Set: 4
67 };
68
69
70 // Ensure the condition is true, otherwise throw an error.
71 // This is only to have a better contract semantic, i.e. another safety net
72 // to catch a logic error. The condition shall be fulfilled in normal case.
73 // Do NOT use this to enforce a certain condition on any user input.
74
75 function assert(condition, message) {
76     /* istanbul ignore if */
77     if (!condition) {
78         throw new Error("ASSERT: " + message);
79     }
80 }
81
82 // 7.4 Comments
83
84 function addComment(type, value, start, end, loc) {
85     var comment;
86
87     assert(typeof start === "number", "Comment must have valid position");
88
89     // Because the way the actual token is scanned, often the comments
90     // (if any) are skipped twice during the lexical analysis.
91     // Thus, we need to skip adding a comment if the comment array already
92     // handled it.
93     if (state.lastCommentStart >= start) {
94         return;
95     }
96     state.lastCommentStart = start;
97
98     comment = {
99         type: type,
100         value: value
101     };
102     if (extra.range) {
103         comment.range = [start, end];
104     }
105     if (extra.loc) {
106         comment.loc = loc;
107     }
108     extra.comments.push(comment);
109
110     if (extra.attachComment) {
111         commentAttachment.addComment(comment);
112     }
113 }
114
115 function skipSingleLineComment(offset) {
116     var start, loc, ch, comment;
117
118     start = index - offset;
119     loc = {
120         start: {
121             line: lineNumber,
122             column: index - lineStart - offset
123         }
124     };
125
126     while (index < length) {
127         ch = source.charCodeAt(index);
128         ++index;
129         if (syntax.isLineTerminator(ch)) {
130             if (extra.comments) {
131                 comment = source.slice(start + offset, index - 1);
132                 loc.end = {
133                     line: lineNumber,
134                     column: index - lineStart - 1
135                 };
136                 addComment("Line", comment, start, index - 1, loc);
137             }
138             if (ch === 13 && source.charCodeAt(index) === 10) {
139                 ++index;
140             }
141             ++lineNumber;
142             lineStart = index;
143             return;
144         }
145     }
146
147     if (extra.comments) {
148         comment = source.slice(start + offset, index);
149         loc.end = {
150             line: lineNumber,
151             column: index - lineStart
152         };
153         addComment("Line", comment, start, index, loc);
154     }
155 }
156
157 function skipMultiLineComment() {
158     var start, loc, ch, comment;
159
160     if (extra.comments) {
161         start = index - 2;
162         loc = {
163             start: {
164                 line: lineNumber,
165                 column: index - lineStart - 2
166             }
167         };
168     }
169
170     while (index < length) {
171         ch = source.charCodeAt(index);
172         if (syntax.isLineTerminator(ch)) {
173             if (ch === 0x0D && source.charCodeAt(index + 1) === 0x0A) {
174                 ++index;
175             }
176             ++lineNumber;
177             ++index;
178             lineStart = index;
179             if (index >= length) {
180                 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
181             }
182         } else if (ch === 0x2A) {
183             // Block comment ends with "*/".
184             if (source.charCodeAt(index + 1) === 0x2F) {
185                 ++index;
186                 ++index;
187                 if (extra.comments) {
188                     comment = source.slice(start + 2, index - 2);
189                     loc.end = {
190                         line: lineNumber,
191                         column: index - lineStart
192                     };
193                     addComment("Block", comment, start, index, loc);
194                 }
195                 return;
196             }
197             ++index;
198         } else {
199             ++index;
200         }
201     }
202
203     throwError({}, Messages.UnexpectedToken, "ILLEGAL");
204 }
205
206 function skipComment() {
207     var ch, start;
208
209     start = (index === 0);
210     while (index < length) {
211         ch = source.charCodeAt(index);
212
213         if (syntax.isWhiteSpace(ch)) {
214             ++index;
215         } else if (syntax.isLineTerminator(ch)) {
216             ++index;
217             if (ch === 0x0D && source.charCodeAt(index) === 0x0A) {
218                 ++index;
219             }
220             ++lineNumber;
221             lineStart = index;
222             start = true;
223         } else if (ch === 0x2F) { // U+002F is "/"
224             ch = source.charCodeAt(index + 1);
225             if (ch === 0x2F) {
226                 ++index;
227                 ++index;
228                 skipSingleLineComment(2);
229                 start = true;
230             } else if (ch === 0x2A) {  // U+002A is "*"
231                 ++index;
232                 ++index;
233                 skipMultiLineComment();
234             } else {
235                 break;
236             }
237         } else if (start && ch === 0x2D) { // U+002D is "-"
238             // U+003E is ">"
239             if ((source.charCodeAt(index + 1) === 0x2D) && (source.charCodeAt(index + 2) === 0x3E)) {
240                 // "-->" is a single-line comment
241                 index += 3;
242                 skipSingleLineComment(3);
243             } else {
244                 break;
245             }
246         } else if (ch === 0x3C) { // U+003C is "<"
247             if (source.slice(index + 1, index + 4) === "!--") {
248                 ++index; // `<`
249                 ++index; // `!`
250                 ++index; // `-`
251                 ++index; // `-`
252                 skipSingleLineComment(4);
253             } else {
254                 break;
255             }
256         } else {
257             break;
258         }
259     }
260 }
261
262 function scanHexEscape(prefix) {
263     var i, len, ch, code = 0;
264
265     len = (prefix === "u") ? 4 : 2;
266     for (i = 0; i < len; ++i) {
267         if (index < length && syntax.isHexDigit(source[index])) {
268             ch = source[index++];
269             code = code * 16 + "0123456789abcdef".indexOf(ch.toLowerCase());
270         } else {
271             return "";
272         }
273     }
274     return String.fromCharCode(code);
275 }
276
277 /**
278  * Scans an extended unicode code point escape sequence from source. Throws an
279  * error if the sequence is empty or if the code point value is too large.
280  * @returns {string} The string created by the Unicode escape sequence.
281  * @private
282  */
283 function scanUnicodeCodePointEscape() {
284     var ch, code, cu1, cu2;
285
286     ch = source[index];
287     code = 0;
288
289     // At least one hex digit is required.
290     if (ch === "}") {
291         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
292     }
293
294     while (index < length) {
295         ch = source[index++];
296         if (!syntax.isHexDigit(ch)) {
297             break;
298         }
299         code = code * 16 + "0123456789abcdef".indexOf(ch.toLowerCase());
300     }
301
302     if (code > 0x10FFFF || ch !== "}") {
303         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
304     }
305
306     // UTF-16 Encoding
307     if (code <= 0xFFFF) {
308         return String.fromCharCode(code);
309     }
310     cu1 = ((code - 0x10000) >> 10) + 0xD800;
311     cu2 = ((code - 0x10000) & 1023) + 0xDC00;
312     return String.fromCharCode(cu1, cu2);
313 }
314
315 function getEscapedIdentifier() {
316     var ch, id;
317
318     ch = source.charCodeAt(index++);
319     id = String.fromCharCode(ch);
320
321     // "\u" (U+005C, U+0075) denotes an escaped character.
322     if (ch === 0x5C) {
323         if (source.charCodeAt(index) !== 0x75) {
324             throwError({}, Messages.UnexpectedToken, "ILLEGAL");
325         }
326         ++index;
327         ch = scanHexEscape("u");
328         if (!ch || ch === "\\" || !syntax.isIdentifierStart(ch.charCodeAt(0))) {
329             throwError({}, Messages.UnexpectedToken, "ILLEGAL");
330         }
331         id = ch;
332     }
333
334     while (index < length) {
335         ch = source.charCodeAt(index);
336         if (!syntax.isIdentifierPart(ch)) {
337             break;
338         }
339         ++index;
340         id += String.fromCharCode(ch);
341
342         // "\u" (U+005C, U+0075) denotes an escaped character.
343         if (ch === 0x5C) {
344             id = id.substr(0, id.length - 1);
345             if (source.charCodeAt(index) !== 0x75) {
346                 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
347             }
348             ++index;
349             ch = scanHexEscape("u");
350             if (!ch || ch === "\\" || !syntax.isIdentifierPart(ch.charCodeAt(0))) {
351                 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
352             }
353             id += ch;
354         }
355     }
356
357     return id;
358 }
359
360 function getIdentifier() {
361     var start, ch;
362
363     start = index++;
364     while (index < length) {
365         ch = source.charCodeAt(index);
366         if (ch === 0x5C) {
367             // Blackslash (U+005C) marks Unicode escape sequence.
368             index = start;
369             return getEscapedIdentifier();
370         }
371         if (syntax.isIdentifierPart(ch)) {
372             ++index;
373         } else {
374             break;
375         }
376     }
377
378     return source.slice(start, index);
379 }
380
381 function scanIdentifier() {
382     var start, id, type;
383
384     start = index;
385
386     // Backslash (U+005C) starts an escaped character.
387     id = (source.charCodeAt(index) === 0x5C) ? getEscapedIdentifier() : getIdentifier();
388
389     // There is no keyword or literal with only one character.
390     // Thus, it must be an identifier.
391     if (id.length === 1) {
392         type = Token.Identifier;
393     } else if (syntax.isKeyword(id, strict, extra.ecmaFeatures)) {
394         type = Token.Keyword;
395     } else if (id === "null") {
396         type = Token.NullLiteral;
397     } else if (id === "true" || id === "false") {
398         type = Token.BooleanLiteral;
399     } else {
400         type = Token.Identifier;
401     }
402
403     return {
404         type: type,
405         value: id,
406         lineNumber: lineNumber,
407         lineStart: lineStart,
408         range: [start, index]
409     };
410 }
411
412
413 // 7.7 Punctuators
414
415 function scanPunctuator() {
416     var start = index,
417         code = source.charCodeAt(index),
418         code2,
419         ch1 = source[index],
420         ch2,
421         ch3,
422         ch4;
423
424     switch (code) {
425         // Check for most common single-character punctuators.
426         case 40:   // ( open bracket
427         case 41:   // ) close bracket
428         case 59:   // ; semicolon
429         case 44:   // , comma
430         case 91:   // [
431         case 93:   // ]
432         case 58:   // :
433         case 63:   // ?
434         case 126:  // ~
435             ++index;
436
437             if (extra.tokenize && code === 40) {
438                 extra.openParenToken = extra.tokens.length;
439             }
440
441             return {
442                 type: Token.Punctuator,
443                 value: String.fromCharCode(code),
444                 lineNumber: lineNumber,
445                 lineStart: lineStart,
446                 range: [start, index]
447             };
448
449         case 123:  // { open curly brace
450         case 125:  // } close curly brace
451             ++index;
452
453             if (extra.tokenize && code === 123) {
454                 extra.openCurlyToken = extra.tokens.length;
455             }
456
457             // lookahead2 function can cause tokens to be scanned twice and in doing so
458             // would wreck the curly stack by pushing the same token onto the stack twice.
459             // curlyLastIndex ensures each token is pushed or popped exactly once
460             if (index > state.curlyLastIndex) {
461                 state.curlyLastIndex = index;
462                 if (code === 123) {
463                     state.curlyStack.push("{");
464                 } else {
465                     state.curlyStack.pop();
466                 }
467             }
468
469             return {
470                 type: Token.Punctuator,
471                 value: String.fromCharCode(code),
472                 lineNumber: lineNumber,
473                 lineStart: lineStart,
474                 range: [start, index]
475             };
476
477         default:
478             code2 = source.charCodeAt(index + 1);
479
480             // "=" (char #61) marks an assignment or comparison operator.
481             if (code2 === 61) {
482                 switch (code) {
483                     case 37:  // %
484                     case 38:  // &
485                     case 42:  // *:
486                     case 43:  // +
487                     case 45:  // -
488                     case 47:  // /
489                     case 60:  // <
490                     case 62:  // >
491                     case 94:  // ^
492                     case 124: // |
493                         index += 2;
494                         return {
495                             type: Token.Punctuator,
496                             value: String.fromCharCode(code) + String.fromCharCode(code2),
497                             lineNumber: lineNumber,
498                             lineStart: lineStart,
499                             range: [start, index]
500                         };
501
502                     case 33: // !
503                     case 61: // =
504                         index += 2;
505
506                         // !== and ===
507                         if (source.charCodeAt(index) === 61) {
508                             ++index;
509                         }
510                         return {
511                             type: Token.Punctuator,
512                             value: source.slice(start, index),
513                             lineNumber: lineNumber,
514                             lineStart: lineStart,
515                             range: [start, index]
516                         };
517                     default:
518                         break;
519                 }
520             }
521             break;
522     }
523
524     // Peek more characters.
525
526     ch2 = source[index + 1];
527     ch3 = source[index + 2];
528     ch4 = source[index + 3];
529
530     // 4-character punctuator: >>>=
531
532     if (ch1 === ">" && ch2 === ">" && ch3 === ">") {
533         if (ch4 === "=") {
534             index += 4;
535             return {
536                 type: Token.Punctuator,
537                 value: ">>>=",
538                 lineNumber: lineNumber,
539                 lineStart: lineStart,
540                 range: [start, index]
541             };
542         }
543     }
544
545     // 3-character punctuators: === !== >>> <<= >>=
546
547     if (ch1 === ">" && ch2 === ">" && ch3 === ">") {
548         index += 3;
549         return {
550             type: Token.Punctuator,
551             value: ">>>",
552             lineNumber: lineNumber,
553             lineStart: lineStart,
554             range: [start, index]
555         };
556     }
557
558     if (ch1 === "<" && ch2 === "<" && ch3 === "=") {
559         index += 3;
560         return {
561             type: Token.Punctuator,
562             value: "<<=",
563             lineNumber: lineNumber,
564             lineStart: lineStart,
565             range: [start, index]
566         };
567     }
568
569     if (ch1 === ">" && ch2 === ">" && ch3 === "=") {
570         index += 3;
571         return {
572             type: Token.Punctuator,
573             value: ">>=",
574             lineNumber: lineNumber,
575             lineStart: lineStart,
576             range: [start, index]
577         };
578     }
579
580     // The ... operator (spread, restParams, JSX, etc.)
581     if (extra.ecmaFeatures.spread ||
582         extra.ecmaFeatures.restParams ||
583         (extra.ecmaFeatures.jsx && state.inJSXSpreadAttribute)
584     ) {
585         if (ch1 === "." && ch2 === "." && ch3 === ".") {
586             index += 3;
587             return {
588                 type: Token.Punctuator,
589                 value: "...",
590                 lineNumber: lineNumber,
591                 lineStart: lineStart,
592                 range: [start, index]
593             };
594         }
595     }
596
597     // Other 2-character punctuators: ++ -- << >> && ||
598     if (ch1 === ch2 && ("+-<>&|".indexOf(ch1) >= 0)) {
599         index += 2;
600         return {
601             type: Token.Punctuator,
602             value: ch1 + ch2,
603             lineNumber: lineNumber,
604             lineStart: lineStart,
605             range: [start, index]
606         };
607     }
608
609     // the => for arrow functions
610     if (extra.ecmaFeatures.arrowFunctions) {
611         if (ch1 === "=" && ch2 === ">") {
612             index += 2;
613             return {
614                 type: Token.Punctuator,
615                 value: "=>",
616                 lineNumber: lineNumber,
617                 lineStart: lineStart,
618                 range: [start, index]
619             };
620         }
621     }
622
623     if ("<>=!+-*%&|^/".indexOf(ch1) >= 0) {
624         ++index;
625         return {
626             type: Token.Punctuator,
627             value: ch1,
628             lineNumber: lineNumber,
629             lineStart: lineStart,
630             range: [start, index]
631         };
632     }
633
634     if (ch1 === ".") {
635         ++index;
636         return {
637             type: Token.Punctuator,
638             value: ch1,
639             lineNumber: lineNumber,
640             lineStart: lineStart,
641             range: [start, index]
642         };
643     }
644
645     throwError({}, Messages.UnexpectedToken, "ILLEGAL");
646 }
647
648 // 7.8.3 Numeric Literals
649
650 function scanHexLiteral(start) {
651     var number = "";
652
653     while (index < length) {
654         if (!syntax.isHexDigit(source[index])) {
655             break;
656         }
657         number += source[index++];
658     }
659
660     if (number.length === 0) {
661         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
662     }
663
664     if (syntax.isIdentifierStart(source.charCodeAt(index))) {
665         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
666     }
667
668     return {
669         type: Token.NumericLiteral,
670         value: parseInt("0x" + number, 16),
671         lineNumber: lineNumber,
672         lineStart: lineStart,
673         range: [start, index]
674     };
675 }
676
677 function scanBinaryLiteral(start) {
678     var ch, number = "";
679
680     while (index < length) {
681         ch = source[index];
682         if (ch !== "0" && ch !== "1") {
683             break;
684         }
685         number += source[index++];
686     }
687
688     if (number.length === 0) {
689         // only 0b or 0B
690         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
691     }
692
693
694     if (index < length) {
695         ch = source.charCodeAt(index);
696         /* istanbul ignore else */
697         if (syntax.isIdentifierStart(ch) || syntax.isDecimalDigit(ch)) {
698             throwError({}, Messages.UnexpectedToken, "ILLEGAL");
699         }
700     }
701
702     return {
703         type: Token.NumericLiteral,
704         value: parseInt(number, 2),
705         lineNumber: lineNumber,
706         lineStart: lineStart,
707         range: [start, index]
708     };
709 }
710
711 function scanOctalLiteral(prefix, start) {
712     var number, octal;
713
714     if (syntax.isOctalDigit(prefix)) {
715         octal = true;
716         number = "0" + source[index++];
717     } else {
718         octal = false;
719         ++index;
720         number = "";
721     }
722
723     while (index < length) {
724         if (!syntax.isOctalDigit(source[index])) {
725             break;
726         }
727         number += source[index++];
728     }
729
730     if (!octal && number.length === 0) {
731         // only 0o or 0O
732         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
733     }
734
735     if (syntax.isIdentifierStart(source.charCodeAt(index)) || syntax.isDecimalDigit(source.charCodeAt(index))) {
736         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
737     }
738
739     return {
740         type: Token.NumericLiteral,
741         value: parseInt(number, 8),
742         octal: octal,
743         lineNumber: lineNumber,
744         lineStart: lineStart,
745         range: [start, index]
746     };
747 }
748
749 function scanNumericLiteral() {
750     var number, start, ch;
751
752     ch = source[index];
753     assert(syntax.isDecimalDigit(ch.charCodeAt(0)) || (ch === "."),
754         "Numeric literal must start with a decimal digit or a decimal point");
755
756     start = index;
757     number = "";
758     if (ch !== ".") {
759         number = source[index++];
760         ch = source[index];
761
762         // Hex number starts with "0x".
763         // Octal number starts with "0".
764         if (number === "0") {
765             if (ch === "x" || ch === "X") {
766                 ++index;
767                 return scanHexLiteral(start);
768             }
769
770             // Binary number in ES6 starts with '0b'
771             if (extra.ecmaFeatures.binaryLiterals) {
772                 if (ch === "b" || ch === "B") {
773                     ++index;
774                     return scanBinaryLiteral(start);
775                 }
776             }
777
778             if ((extra.ecmaFeatures.octalLiterals && (ch === "o" || ch === "O")) || syntax.isOctalDigit(ch)) {
779                 return scanOctalLiteral(ch, start);
780             }
781
782             // decimal number starts with "0" such as "09" is illegal.
783             if (ch && syntax.isDecimalDigit(ch.charCodeAt(0))) {
784                 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
785             }
786         }
787
788         while (syntax.isDecimalDigit(source.charCodeAt(index))) {
789             number += source[index++];
790         }
791         ch = source[index];
792     }
793
794     if (ch === ".") {
795         number += source[index++];
796         while (syntax.isDecimalDigit(source.charCodeAt(index))) {
797             number += source[index++];
798         }
799         ch = source[index];
800     }
801
802     if (ch === "e" || ch === "E") {
803         number += source[index++];
804
805         ch = source[index];
806         if (ch === "+" || ch === "-") {
807             number += source[index++];
808         }
809         if (syntax.isDecimalDigit(source.charCodeAt(index))) {
810             while (syntax.isDecimalDigit(source.charCodeAt(index))) {
811                 number += source[index++];
812             }
813         } else {
814             throwError({}, Messages.UnexpectedToken, "ILLEGAL");
815         }
816     }
817
818     if (syntax.isIdentifierStart(source.charCodeAt(index))) {
819         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
820     }
821
822     return {
823         type: Token.NumericLiteral,
824         value: parseFloat(number),
825         lineNumber: lineNumber,
826         lineStart: lineStart,
827         range: [start, index]
828     };
829 }
830
831 /**
832  * Scan a string escape sequence and return its special character.
833  * @param {string} ch The starting character of the given sequence.
834  * @returns {Object} An object containing the character and a flag
835  * if the escape sequence was an octal.
836  * @private
837  */
838 function scanEscapeSequence(ch) {
839     var code,
840         unescaped,
841         restore,
842         escapedCh,
843         octal = false;
844
845     // An escape sequence cannot be empty
846     if (!ch) {
847         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
848     }
849
850     if (syntax.isLineTerminator(ch.charCodeAt(0))) {
851         ++lineNumber;
852         if (ch === "\r" && source[index] === "\n") {
853             ++index;
854         }
855         lineStart = index;
856         escapedCh = "";
857     } else if (ch === "u" && source[index] === "{") {
858         // Handle ES6 extended unicode code point escape sequences.
859         if (extra.ecmaFeatures.unicodeCodePointEscapes) {
860             ++index;
861             escapedCh = scanUnicodeCodePointEscape();
862         } else {
863             throwError({}, Messages.UnexpectedToken, "ILLEGAL");
864         }
865     } else if (ch === "u" || ch === "x") {
866         // Handle other unicode and hex codes normally
867         restore = index;
868         unescaped = scanHexEscape(ch);
869         if (unescaped) {
870             escapedCh = unescaped;
871         } else {
872             index = restore;
873             escapedCh = ch;
874         }
875     } else if (ch === "n") {
876         escapedCh = "\n";
877     } else if (ch === "r") {
878         escapedCh = "\r";
879     } else if (ch === "t") {
880         escapedCh = "\t";
881     } else if (ch === "b") {
882         escapedCh = "\b";
883     } else if (ch === "f") {
884         escapedCh = "\f";
885     } else if (ch === "v") {
886         escapedCh = "\v";
887     } else if (syntax.isOctalDigit(ch)) {
888         code = "01234567".indexOf(ch);
889
890         // \0 is not octal escape sequence
891         if (code !== 0) {
892             octal = true;
893         }
894
895         if (index < length && syntax.isOctalDigit(source[index])) {
896             octal = true;
897             code = code * 8 + "01234567".indexOf(source[index++]);
898
899             // 3 digits are only allowed when string starts with 0, 1, 2, 3
900             if ("0123".indexOf(ch) >= 0 &&
901                     index < length &&
902                     syntax.isOctalDigit(source[index])) {
903                 code = code * 8 + "01234567".indexOf(source[index++]);
904             }
905         }
906         escapedCh = String.fromCharCode(code);
907     } else {
908         escapedCh = ch;
909     }
910
911     return {
912         ch: escapedCh,
913         octal: octal
914     };
915 }
916
917 function scanStringLiteral() {
918     var str = "",
919         ch,
920         escapedSequence,
921         octal = false,
922         start = index,
923         startLineNumber = lineNumber,
924         startLineStart = lineStart,
925         quote = source[index];
926
927     assert((quote === "'" || quote === "\""),
928         "String literal must starts with a quote");
929
930     ++index;
931
932     while (index < length) {
933         ch = source[index++];
934
935         if (syntax.isLineTerminator(ch.charCodeAt(0))) {
936             break;
937         } else if (ch === quote) {
938             quote = "";
939             break;
940         } else if (ch === "\\") {
941             ch = source[index++];
942             escapedSequence = scanEscapeSequence(ch);
943             str += escapedSequence.ch;
944             octal = escapedSequence.octal || octal;
945         } else {
946             str += ch;
947         }
948     }
949
950     if (quote !== "") {
951         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
952     }
953
954     return {
955         type: Token.StringLiteral,
956         value: str,
957         octal: octal,
958         startLineNumber: startLineNumber,
959         startLineStart: startLineStart,
960         lineNumber: lineNumber,
961         lineStart: lineStart,
962         range: [start, index]
963     };
964 }
965
966 /**
967  * Scan a template string and return a token. This scans both the first and
968  * subsequent pieces of a template string and assumes that the first backtick
969  * or the closing } have already been scanned.
970  * @returns {Token} The template string token.
971  * @private
972  */
973 function scanTemplate() {
974     var cooked = "",
975         ch,
976         escapedSequence,
977         start = index,
978         terminated = false,
979         tail = false,
980         head = (source[index] === "`");
981
982     ++index;
983
984     while (index < length) {
985         ch = source[index++];
986
987         if (ch === "`") {
988             tail = true;
989             terminated = true;
990             break;
991         } else if (ch === "$") {
992             if (source[index] === "{") {
993                 ++index;
994                 terminated = true;
995                 break;
996             }
997             cooked += ch;
998         } else if (ch === "\\") {
999             ch = source[index++];
1000             escapedSequence = scanEscapeSequence(ch);
1001
1002             if (escapedSequence.octal) {
1003                 throwError({}, Messages.TemplateOctalLiteral);
1004             }
1005
1006             cooked += escapedSequence.ch;
1007
1008         } else if (syntax.isLineTerminator(ch.charCodeAt(0))) {
1009             ++lineNumber;
1010             if (ch === "\r" && source[index] === "\n") {
1011                 ++index;
1012             }
1013             lineStart = index;
1014             cooked += "\n";
1015         } else {
1016             cooked += ch;
1017         }
1018     }
1019
1020     if (!terminated) {
1021         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
1022     }
1023
1024     if (index > state.curlyLastIndex) {
1025         state.curlyLastIndex = index;
1026
1027         if (!tail) {
1028             state.curlyStack.push("template");
1029         }
1030
1031         if (!head) {
1032             state.curlyStack.pop();
1033         }
1034     }
1035
1036     return {
1037         type: Token.Template,
1038         value: {
1039             cooked: cooked,
1040             raw: source.slice(start + 1, index - ((tail) ? 1 : 2))
1041         },
1042         head: head,
1043         tail: tail,
1044         lineNumber: lineNumber,
1045         lineStart: lineStart,
1046         range: [start, index]
1047     };
1048 }
1049
1050 function testRegExp(pattern, flags) {
1051     var tmp = pattern,
1052         validFlags = "gmsi";
1053
1054     if (extra.ecmaFeatures.regexYFlag) {
1055         validFlags += "y";
1056     }
1057
1058     if (extra.ecmaFeatures.regexUFlag) {
1059         validFlags += "u";
1060     }
1061
1062     if (!RegExp("^[" + validFlags + "]*$").test(flags)) {
1063         throwError({}, Messages.InvalidRegExpFlag);
1064     }
1065
1066
1067     if (flags.indexOf("u") >= 0) {
1068         // Replace each astral symbol and every Unicode code point
1069         // escape sequence with a single ASCII symbol to avoid throwing on
1070         // regular expressions that are only valid in combination with the
1071         // `/u` flag.
1072         // Note: replacing with the ASCII symbol `x` might cause false
1073         // negatives in unlikely scenarios. For example, `[\u{61}-b]` is a
1074         // perfectly valid pattern that is equivalent to `[a-b]`, but it
1075         // would be replaced by `[x-b]` which throws an error.
1076         tmp = tmp
1077             .replace(/\\u\{([0-9a-fA-F]+)\}/g, function ($0, $1) {
1078                 if (parseInt($1, 16) <= 0x10FFFF) {
1079                     return "x";
1080                 }
1081                 throwError({}, Messages.InvalidRegExp);
1082             })
1083             .replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x");
1084     }
1085
1086     // First, detect invalid regular expressions.
1087     try {
1088         RegExp(tmp);
1089     } catch (e) {
1090         throwError({}, Messages.InvalidRegExp);
1091     }
1092
1093     // Return a regular expression object for this pattern-flag pair, or
1094     // `null` in case the current environment doesn't support the flags it
1095     // uses.
1096     try {
1097         return new RegExp(pattern, flags);
1098     } catch (exception) {
1099         return null;
1100     }
1101 }
1102
1103 function scanRegExpBody() {
1104     var ch, str, classMarker, terminated, body;
1105
1106     ch = source[index];
1107     assert(ch === "/", "Regular expression literal must start with a slash");
1108     str = source[index++];
1109
1110     classMarker = false;
1111     terminated = false;
1112     while (index < length) {
1113         ch = source[index++];
1114         str += ch;
1115         if (ch === "\\") {
1116             ch = source[index++];
1117             // ECMA-262 7.8.5
1118             if (syntax.isLineTerminator(ch.charCodeAt(0))) {
1119                 throwError({}, Messages.UnterminatedRegExp);
1120             }
1121             str += ch;
1122         } else if (syntax.isLineTerminator(ch.charCodeAt(0))) {
1123             throwError({}, Messages.UnterminatedRegExp);
1124         } else if (classMarker) {
1125             if (ch === "]") {
1126                 classMarker = false;
1127             }
1128         } else {
1129             if (ch === "/") {
1130                 terminated = true;
1131                 break;
1132             } else if (ch === "[") {
1133                 classMarker = true;
1134             }
1135         }
1136     }
1137
1138     if (!terminated) {
1139         throwError({}, Messages.UnterminatedRegExp);
1140     }
1141
1142     // Exclude leading and trailing slash.
1143     body = str.substr(1, str.length - 2);
1144     return {
1145         value: body,
1146         literal: str
1147     };
1148 }
1149
1150 function scanRegExpFlags() {
1151     var ch, str, flags, restore;
1152
1153     str = "";
1154     flags = "";
1155     while (index < length) {
1156         ch = source[index];
1157         if (!syntax.isIdentifierPart(ch.charCodeAt(0))) {
1158             break;
1159         }
1160
1161         ++index;
1162         if (ch === "\\" && index < length) {
1163             ch = source[index];
1164             if (ch === "u") {
1165                 ++index;
1166                 restore = index;
1167                 ch = scanHexEscape("u");
1168                 if (ch) {
1169                     flags += ch;
1170                     for (str += "\\u"; restore < index; ++restore) {
1171                         str += source[restore];
1172                     }
1173                 } else {
1174                     index = restore;
1175                     flags += "u";
1176                     str += "\\u";
1177                 }
1178                 throwErrorTolerant({}, Messages.UnexpectedToken, "ILLEGAL");
1179             } else {
1180                 str += "\\";
1181                 throwErrorTolerant({}, Messages.UnexpectedToken, "ILLEGAL");
1182             }
1183         } else {
1184             flags += ch;
1185             str += ch;
1186         }
1187     }
1188
1189     return {
1190         value: flags,
1191         literal: str
1192     };
1193 }
1194
1195 function scanRegExp() {
1196     var start, body, flags, value;
1197
1198     lookahead = null;
1199     skipComment();
1200     start = index;
1201
1202     body = scanRegExpBody();
1203     flags = scanRegExpFlags();
1204     value = testRegExp(body.value, flags.value);
1205
1206     if (extra.tokenize) {
1207         return {
1208             type: Token.RegularExpression,
1209             value: value,
1210             regex: {
1211                 pattern: body.value,
1212                 flags: flags.value
1213             },
1214             lineNumber: lineNumber,
1215             lineStart: lineStart,
1216             range: [start, index]
1217         };
1218     }
1219
1220     return {
1221         literal: body.literal + flags.literal,
1222         value: value,
1223         regex: {
1224             pattern: body.value,
1225             flags: flags.value
1226         },
1227         range: [start, index]
1228     };
1229 }
1230
1231 function collectRegex() {
1232     var pos, loc, regex, token;
1233
1234     skipComment();
1235
1236     pos = index;
1237     loc = {
1238         start: {
1239             line: lineNumber,
1240             column: index - lineStart
1241         }
1242     };
1243
1244     regex = scanRegExp();
1245     loc.end = {
1246         line: lineNumber,
1247         column: index - lineStart
1248     };
1249
1250     /* istanbul ignore next */
1251     if (!extra.tokenize) {
1252         // Pop the previous token, which is likely "/" or "/="
1253         if (extra.tokens.length > 0) {
1254             token = extra.tokens[extra.tokens.length - 1];
1255             if (token.range[0] === pos && token.type === "Punctuator") {
1256                 if (token.value === "/" || token.value === "/=") {
1257                     extra.tokens.pop();
1258                 }
1259             }
1260         }
1261
1262         extra.tokens.push({
1263             type: "RegularExpression",
1264             value: regex.literal,
1265             regex: regex.regex,
1266             range: [pos, index],
1267             loc: loc
1268         });
1269     }
1270
1271     return regex;
1272 }
1273
1274 function isIdentifierName(token) {
1275     return token.type === Token.Identifier ||
1276         token.type === Token.Keyword ||
1277         token.type === Token.BooleanLiteral ||
1278         token.type === Token.NullLiteral;
1279 }
1280
1281 function advanceSlash() {
1282     var prevToken,
1283         checkToken;
1284     // Using the following algorithm:
1285     // https://github.com/mozilla/sweet.js/wiki/design
1286     prevToken = extra.tokens[extra.tokens.length - 1];
1287     if (!prevToken) {
1288         // Nothing before that: it cannot be a division.
1289         return collectRegex();
1290     }
1291     if (prevToken.type === "Punctuator") {
1292         if (prevToken.value === "]") {
1293             return scanPunctuator();
1294         }
1295         if (prevToken.value === ")") {
1296             checkToken = extra.tokens[extra.openParenToken - 1];
1297             if (checkToken &&
1298                     checkToken.type === "Keyword" &&
1299                     (checkToken.value === "if" ||
1300                      checkToken.value === "while" ||
1301                      checkToken.value === "for" ||
1302                      checkToken.value === "with")) {
1303                 return collectRegex();
1304             }
1305             return scanPunctuator();
1306         }
1307         if (prevToken.value === "}") {
1308             // Dividing a function by anything makes little sense,
1309             // but we have to check for that.
1310             if (extra.tokens[extra.openCurlyToken - 3] &&
1311                     extra.tokens[extra.openCurlyToken - 3].type === "Keyword") {
1312                 // Anonymous function.
1313                 checkToken = extra.tokens[extra.openCurlyToken - 4];
1314                 if (!checkToken) {
1315                     return scanPunctuator();
1316                 }
1317             } else if (extra.tokens[extra.openCurlyToken - 4] &&
1318                     extra.tokens[extra.openCurlyToken - 4].type === "Keyword") {
1319                 // Named function.
1320                 checkToken = extra.tokens[extra.openCurlyToken - 5];
1321                 if (!checkToken) {
1322                     return collectRegex();
1323                 }
1324             } else {
1325                 return scanPunctuator();
1326             }
1327             // checkToken determines whether the function is
1328             // a declaration or an expression.
1329             if (FnExprTokens.indexOf(checkToken.value) >= 0) {
1330                 // It is an expression.
1331                 return scanPunctuator();
1332             }
1333             // It is a declaration.
1334             return collectRegex();
1335         }
1336         return collectRegex();
1337     }
1338     if (prevToken.type === "Keyword") {
1339         return collectRegex();
1340     }
1341     return scanPunctuator();
1342 }
1343
1344 function advance() {
1345     var ch,
1346         allowJSX = extra.ecmaFeatures.jsx,
1347         allowTemplateStrings = extra.ecmaFeatures.templateStrings;
1348
1349     /*
1350      * If JSX isn't allowed or JSX is allowed and we're not inside an JSX child,
1351      * then skip any comments.
1352      */
1353     if (!allowJSX || !state.inJSXChild) {
1354         skipComment();
1355     }
1356
1357     if (index >= length) {
1358         return {
1359             type: Token.EOF,
1360             lineNumber: lineNumber,
1361             lineStart: lineStart,
1362             range: [index, index]
1363         };
1364     }
1365
1366     // if inside an JSX child, then abort regular tokenization
1367     if (allowJSX && state.inJSXChild) {
1368         return advanceJSXChild();
1369     }
1370
1371     ch = source.charCodeAt(index);
1372
1373     // Very common: ( and ) and ;
1374     if (ch === 0x28 || ch === 0x29 || ch === 0x3B) {
1375         return scanPunctuator();
1376     }
1377
1378     // String literal starts with single quote (U+0027) or double quote (U+0022).
1379     if (ch === 0x27 || ch === 0x22) {
1380         if (allowJSX && state.inJSXTag) {
1381             return scanJSXStringLiteral();
1382         }
1383
1384         return scanStringLiteral();
1385     }
1386
1387     if (allowJSX && state.inJSXTag && syntax.isJSXIdentifierStart(ch)) {
1388         return scanJSXIdentifier();
1389     }
1390
1391     // Template strings start with backtick (U+0096) or closing curly brace (125) and backtick.
1392     if (allowTemplateStrings) {
1393
1394         // template strings start with backtick (96) or open curly (125) but only if the open
1395         // curly closes a previously opened curly from a template.
1396         if (ch === 96 || (ch === 125 && state.curlyStack[state.curlyStack.length - 1] === "template")) {
1397             return scanTemplate();
1398         }
1399     }
1400
1401     if (syntax.isIdentifierStart(ch)) {
1402         return scanIdentifier();
1403     }
1404
1405     // Dot (.) U+002E can also start a floating-point number, hence the need
1406     // to check the next character.
1407     if (ch === 0x2E) {
1408         if (syntax.isDecimalDigit(source.charCodeAt(index + 1))) {
1409             return scanNumericLiteral();
1410         }
1411         return scanPunctuator();
1412     }
1413
1414     if (syntax.isDecimalDigit(ch)) {
1415         return scanNumericLiteral();
1416     }
1417
1418     // Slash (/) U+002F can also start a regex.
1419     if (extra.tokenize && ch === 0x2F) {
1420         return advanceSlash();
1421     }
1422
1423     return scanPunctuator();
1424 }
1425
1426 function collectToken() {
1427     var loc, token, range, value, entry,
1428         allowJSX = extra.ecmaFeatures.jsx;
1429
1430     /* istanbul ignore else */
1431     if (!allowJSX || !state.inJSXChild) {
1432         skipComment();
1433     }
1434
1435     loc = {
1436         start: {
1437             line: lineNumber,
1438             column: index - lineStart
1439         }
1440     };
1441
1442     token = advance();
1443     loc.end = {
1444         line: lineNumber,
1445         column: index - lineStart
1446     };
1447
1448     if (token.type !== Token.EOF) {
1449         range = [token.range[0], token.range[1]];
1450         value = source.slice(token.range[0], token.range[1]);
1451         entry = {
1452             type: TokenName[token.type],
1453             value: value,
1454             range: range,
1455             loc: loc
1456         };
1457         if (token.regex) {
1458             entry.regex = {
1459                 pattern: token.regex.pattern,
1460                 flags: token.regex.flags
1461             };
1462         }
1463         extra.tokens.push(entry);
1464     }
1465
1466     return token;
1467 }
1468
1469 function lex() {
1470     var token;
1471
1472     token = lookahead;
1473     index = token.range[1];
1474     lineNumber = token.lineNumber;
1475     lineStart = token.lineStart;
1476
1477     lookahead = (typeof extra.tokens !== "undefined") ? collectToken() : advance();
1478
1479     index = token.range[1];
1480     lineNumber = token.lineNumber;
1481     lineStart = token.lineStart;
1482
1483     return token;
1484 }
1485
1486 function peek() {
1487     var pos,
1488         line,
1489         start;
1490
1491     pos = index;
1492     line = lineNumber;
1493     start = lineStart;
1494
1495     lookahead = (typeof extra.tokens !== "undefined") ? collectToken() : advance();
1496
1497     index = pos;
1498     lineNumber = line;
1499     lineStart = start;
1500 }
1501
1502 function lookahead2() {
1503     var adv, pos, line, start, result;
1504
1505     // If we are collecting the tokens, don't grab the next one yet.
1506     /* istanbul ignore next */
1507     adv = (typeof extra.advance === "function") ? extra.advance : advance;
1508
1509     pos = index;
1510     line = lineNumber;
1511     start = lineStart;
1512
1513     // Scan for the next immediate token.
1514     /* istanbul ignore if */
1515     if (lookahead === null) {
1516         lookahead = adv();
1517     }
1518     index = lookahead.range[1];
1519     lineNumber = lookahead.lineNumber;
1520     lineStart = lookahead.lineStart;
1521
1522     // Grab the token right after.
1523     result = adv();
1524     index = pos;
1525     lineNumber = line;
1526     lineStart = start;
1527
1528     return result;
1529 }
1530
1531
1532 //------------------------------------------------------------------------------
1533 // JSX
1534 //------------------------------------------------------------------------------
1535
1536 function getQualifiedJSXName(object) {
1537     if (object.type === astNodeTypes.JSXIdentifier) {
1538         return object.name;
1539     }
1540     if (object.type === astNodeTypes.JSXNamespacedName) {
1541         return object.namespace.name + ":" + object.name.name;
1542     }
1543     /* istanbul ignore else */
1544     if (object.type === astNodeTypes.JSXMemberExpression) {
1545         return (
1546             getQualifiedJSXName(object.object) + "." +
1547             getQualifiedJSXName(object.property)
1548         );
1549     }
1550     /* istanbul ignore next */
1551     throwUnexpected(object);
1552 }
1553
1554 function scanJSXIdentifier() {
1555     var ch, start, value = "";
1556
1557     start = index;
1558     while (index < length) {
1559         ch = source.charCodeAt(index);
1560         if (!syntax.isJSXIdentifierPart(ch)) {
1561             break;
1562         }
1563         value += source[index++];
1564     }
1565
1566     return {
1567         type: Token.JSXIdentifier,
1568         value: value,
1569         lineNumber: lineNumber,
1570         lineStart: lineStart,
1571         range: [start, index]
1572     };
1573 }
1574
1575 function scanJSXEntity() {
1576     var ch, str = "", start = index, count = 0, code;
1577     ch = source[index];
1578     assert(ch === "&", "Entity must start with an ampersand");
1579     index++;
1580     while (index < length && count++ < 10) {
1581         ch = source[index++];
1582         if (ch === ";") {
1583             break;
1584         }
1585         str += ch;
1586     }
1587
1588     // Well-formed entity (ending was found).
1589     if (ch === ";") {
1590         // Numeric entity.
1591         if (str[0] === "#") {
1592             if (str[1] === "x") {
1593                 code = +("0" + str.substr(1));
1594             } else {
1595                 // Removing leading zeros in order to avoid treating as octal in old browsers.
1596                 code = +str.substr(1).replace(Regex.LeadingZeros, "");
1597             }
1598
1599             if (!isNaN(code)) {
1600                 return String.fromCharCode(code);
1601             }
1602         /* istanbul ignore else */
1603         } else if (XHTMLEntities[str]) {
1604             return XHTMLEntities[str];
1605         }
1606     }
1607
1608     // Treat non-entity sequences as regular text.
1609     index = start + 1;
1610     return "&";
1611 }
1612
1613 function scanJSXText(stopChars) {
1614     var ch, str = "", start;
1615     start = index;
1616     while (index < length) {
1617         ch = source[index];
1618         if (stopChars.indexOf(ch) !== -1) {
1619             break;
1620         }
1621         if (ch === "&") {
1622             str += scanJSXEntity();
1623         } else {
1624             index++;
1625             if (ch === "\r" && source[index] === "\n") {
1626                 str += ch;
1627                 ch = source[index];
1628                 index++;
1629             }
1630             if (syntax.isLineTerminator(ch.charCodeAt(0))) {
1631                 ++lineNumber;
1632                 lineStart = index;
1633             }
1634             str += ch;
1635         }
1636     }
1637     return {
1638         type: Token.JSXText,
1639         value: str,
1640         lineNumber: lineNumber,
1641         lineStart: lineStart,
1642         range: [start, index]
1643     };
1644 }
1645
1646 function scanJSXStringLiteral() {
1647     var innerToken, quote, start;
1648
1649     quote = source[index];
1650     assert((quote === "\"" || quote === "'"),
1651         "String literal must starts with a quote");
1652
1653     start = index;
1654     ++index;
1655
1656     innerToken = scanJSXText([quote]);
1657
1658     if (quote !== source[index]) {
1659         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
1660     }
1661
1662     ++index;
1663
1664     innerToken.range = [start, index];
1665
1666     return innerToken;
1667 }
1668
1669 /*
1670  * Between JSX opening and closing tags (e.g. <foo>HERE</foo>), anything that
1671  * is not another JSX tag and is not an expression wrapped by {} is text.
1672  */
1673 function advanceJSXChild() {
1674     var ch = source.charCodeAt(index);
1675
1676     // { (123) and < (60)
1677     if (ch !== 123 && ch !== 60) {
1678         return scanJSXText(["<", "{"]);
1679     }
1680
1681     return scanPunctuator();
1682 }
1683
1684 function parseJSXIdentifier() {
1685     var token, marker = markerCreate();
1686
1687     if (lookahead.type !== Token.JSXIdentifier) {
1688         throwUnexpected(lookahead);
1689     }
1690
1691     token = lex();
1692     return markerApply(marker, astNodeFactory.createJSXIdentifier(token.value));
1693 }
1694
1695 function parseJSXNamespacedName() {
1696     var namespace, name, marker = markerCreate();
1697
1698     namespace = parseJSXIdentifier();
1699     expect(":");
1700     name = parseJSXIdentifier();
1701
1702     return markerApply(marker, astNodeFactory.createJSXNamespacedName(namespace, name));
1703 }
1704
1705 function parseJSXMemberExpression() {
1706     var marker = markerCreate(),
1707         expr = parseJSXIdentifier();
1708
1709     while (match(".")) {
1710         lex();
1711         expr = markerApply(marker, astNodeFactory.createJSXMemberExpression(expr, parseJSXIdentifier()));
1712     }
1713
1714     return expr;
1715 }
1716
1717 function parseJSXElementName() {
1718     if (lookahead2().value === ":") {
1719         return parseJSXNamespacedName();
1720     }
1721     if (lookahead2().value === ".") {
1722         return parseJSXMemberExpression();
1723     }
1724
1725     return parseJSXIdentifier();
1726 }
1727
1728 function parseJSXAttributeName() {
1729     if (lookahead2().value === ":") {
1730         return parseJSXNamespacedName();
1731     }
1732
1733     return parseJSXIdentifier();
1734 }
1735
1736 function parseJSXAttributeValue() {
1737     var value, marker;
1738     if (match("{")) {
1739         value = parseJSXExpressionContainer();
1740         if (value.expression.type === astNodeTypes.JSXEmptyExpression) {
1741             throwError(
1742                 value,
1743                 "JSX attributes must only be assigned a non-empty " +
1744                     "expression"
1745             );
1746         }
1747     } else if (match("<")) {
1748         value = parseJSXElement();
1749     } else if (lookahead.type === Token.JSXText) {
1750         marker = markerCreate();
1751         value = markerApply(marker, astNodeFactory.createLiteralFromSource(lex(), source));
1752     } else {
1753         throwError({}, Messages.InvalidJSXAttributeValue);
1754     }
1755     return value;
1756 }
1757
1758 function parseJSXEmptyExpression() {
1759     var marker = markerCreatePreserveWhitespace();
1760     while (source.charAt(index) !== "}") {
1761         index++;
1762     }
1763     return markerApply(marker, astNodeFactory.createJSXEmptyExpression());
1764 }
1765
1766 function parseJSXExpressionContainer() {
1767     var expression, origInJSXChild, origInJSXTag, marker = markerCreate();
1768
1769     origInJSXChild = state.inJSXChild;
1770     origInJSXTag = state.inJSXTag;
1771     state.inJSXChild = false;
1772     state.inJSXTag = false;
1773
1774     expect("{");
1775
1776     if (match("}")) {
1777         expression = parseJSXEmptyExpression();
1778     } else {
1779         expression = parseExpression();
1780     }
1781
1782     state.inJSXChild = origInJSXChild;
1783     state.inJSXTag = origInJSXTag;
1784
1785     expect("}");
1786
1787     return markerApply(marker, astNodeFactory.createJSXExpressionContainer(expression));
1788 }
1789
1790 function parseJSXSpreadAttribute() {
1791     var expression, origInJSXChild, origInJSXTag, marker = markerCreate();
1792
1793     origInJSXChild = state.inJSXChild;
1794     origInJSXTag = state.inJSXTag;
1795     state.inJSXChild = false;
1796     state.inJSXTag = false;
1797     state.inJSXSpreadAttribute = true;
1798
1799     expect("{");
1800     expect("...");
1801
1802     state.inJSXSpreadAttribute = false;
1803
1804     expression = parseAssignmentExpression();
1805
1806     state.inJSXChild = origInJSXChild;
1807     state.inJSXTag = origInJSXTag;
1808
1809     expect("}");
1810
1811     return markerApply(marker, astNodeFactory.createJSXSpreadAttribute(expression));
1812 }
1813
1814 function parseJSXAttribute() {
1815     var name, marker;
1816
1817     if (match("{")) {
1818         return parseJSXSpreadAttribute();
1819     }
1820
1821     marker = markerCreate();
1822
1823     name = parseJSXAttributeName();
1824
1825     // HTML empty attribute
1826     if (match("=")) {
1827         lex();
1828         return markerApply(marker, astNodeFactory.createJSXAttribute(name, parseJSXAttributeValue()));
1829     }
1830
1831     return markerApply(marker, astNodeFactory.createJSXAttribute(name));
1832 }
1833
1834 function parseJSXChild() {
1835     var token, marker;
1836     if (match("{")) {
1837         token = parseJSXExpressionContainer();
1838     } else if (lookahead.type === Token.JSXText) {
1839         marker = markerCreatePreserveWhitespace();
1840         token = markerApply(marker, astNodeFactory.createLiteralFromSource(lex(), source));
1841     } else {
1842         token = parseJSXElement();
1843     }
1844     return token;
1845 }
1846
1847 function parseJSXClosingElement() {
1848     var name, origInJSXChild, origInJSXTag, marker = markerCreate();
1849     origInJSXChild = state.inJSXChild;
1850     origInJSXTag = state.inJSXTag;
1851     state.inJSXChild = false;
1852     state.inJSXTag = true;
1853     expect("<");
1854     expect("/");
1855     name = parseJSXElementName();
1856     // Because advance() (called by lex() called by expect()) expects there
1857     // to be a valid token after >, it needs to know whether to look for a
1858     // standard JS token or an JSX text node
1859     state.inJSXChild = origInJSXChild;
1860     state.inJSXTag = origInJSXTag;
1861     expect(">");
1862     return markerApply(marker, astNodeFactory.createJSXClosingElement(name));
1863 }
1864
1865 function parseJSXOpeningElement() {
1866     var name, attributes = [], selfClosing = false, origInJSXChild,
1867         origInJSXTag, marker = markerCreate();
1868
1869     origInJSXChild = state.inJSXChild;
1870     origInJSXTag = state.inJSXTag;
1871     state.inJSXChild = false;
1872     state.inJSXTag = true;
1873
1874     expect("<");
1875
1876     name = parseJSXElementName();
1877
1878     while (index < length &&
1879             lookahead.value !== "/" &&
1880             lookahead.value !== ">") {
1881         attributes.push(parseJSXAttribute());
1882     }
1883
1884     state.inJSXTag = origInJSXTag;
1885
1886     if (lookahead.value === "/") {
1887         expect("/");
1888         // Because advance() (called by lex() called by expect()) expects
1889         // there to be a valid token after >, it needs to know whether to
1890         // look for a standard JS token or an JSX text node
1891         state.inJSXChild = origInJSXChild;
1892         expect(">");
1893         selfClosing = true;
1894     } else {
1895         state.inJSXChild = true;
1896         expect(">");
1897     }
1898     return markerApply(marker, astNodeFactory.createJSXOpeningElement(name, attributes, selfClosing));
1899 }
1900
1901 function parseJSXElement() {
1902     var openingElement, closingElement = null, children = [], origInJSXChild, origInJSXTag, marker = markerCreate();
1903
1904     origInJSXChild = state.inJSXChild;
1905     origInJSXTag = state.inJSXTag;
1906     openingElement = parseJSXOpeningElement();
1907
1908     if (!openingElement.selfClosing) {
1909         while (index < length) {
1910             state.inJSXChild = false; // Call lookahead2() with inJSXChild = false because </ should not be considered in the child
1911             if (lookahead.value === "<" && lookahead2().value === "/") {
1912                 break;
1913             }
1914             state.inJSXChild = true;
1915             children.push(parseJSXChild());
1916         }
1917         state.inJSXChild = origInJSXChild;
1918         state.inJSXTag = origInJSXTag;
1919         closingElement = parseJSXClosingElement();
1920         if (getQualifiedJSXName(closingElement.name) !== getQualifiedJSXName(openingElement.name)) {
1921             throwError({}, Messages.ExpectedJSXClosingTag, getQualifiedJSXName(openingElement.name));
1922         }
1923     }
1924
1925     /*
1926      * When (erroneously) writing two adjacent tags like
1927      *
1928      *     var x = <div>one</div><div>two</div>;
1929      *
1930      * the default error message is a bit incomprehensible. Since it"s
1931      * rarely (never?) useful to write a less-than sign after an JSX
1932      * element, we disallow it here in the parser in order to provide a
1933      * better error message. (In the rare case that the less-than operator
1934      * was intended, the left tag can be wrapped in parentheses.)
1935      */
1936     if (!origInJSXChild && match("<")) {
1937         throwError(lookahead, Messages.AdjacentJSXElements);
1938     }
1939
1940     return markerApply(marker, astNodeFactory.createJSXElement(openingElement, closingElement, children));
1941 }
1942
1943 //------------------------------------------------------------------------------
1944 // Location markers
1945 //------------------------------------------------------------------------------
1946
1947 /**
1948  * Applies location information to the given node by using the given marker.
1949  * The marker indicates the point at which the node is said to have to begun
1950  * in the source code.
1951  * @param {Object} marker The marker to use for the node.
1952  * @param {ASTNode} node The AST node to apply location information to.
1953  * @returns {ASTNode} The node that was passed in.
1954  * @private
1955  */
1956 function markerApply(marker, node) {
1957
1958     // add range information to the node if present
1959     if (extra.range) {
1960         node.range = [marker.offset, index];
1961     }
1962
1963     // add location information the node if present
1964     if (extra.loc) {
1965         node.loc = {
1966             start: {
1967                 line: marker.line,
1968                 column: marker.col
1969             },
1970             end: {
1971                 line: lineNumber,
1972                 column: index - lineStart
1973             }
1974         };
1975         // Attach extra.source information to the location, if present
1976         if (extra.source) {
1977             node.loc.source = extra.source;
1978         }
1979     }
1980
1981     // attach leading and trailing comments if requested
1982     if (extra.attachComment) {
1983         commentAttachment.processComment(node);
1984     }
1985
1986     return node;
1987 }
1988
1989 /**
1990  * Creates a location marker in the source code. Location markers are used for
1991  * tracking where tokens and nodes appear in the source code.
1992  * @returns {Object} A marker object or undefined if the parser doesn't have
1993  *      any location information.
1994  * @private
1995  */
1996 function markerCreate() {
1997
1998     if (!extra.loc && !extra.range) {
1999         return undefined;
2000     }
2001
2002     skipComment();
2003
2004     return {
2005         offset: index,
2006         line: lineNumber,
2007         col: index - lineStart
2008     };
2009 }
2010
2011 /**
2012  * Creates a location marker in the source code. Location markers are used for
2013  * tracking where tokens and nodes appear in the source code. This method
2014  * doesn't skip comments or extra whitespace which is important for JSX.
2015  * @returns {Object} A marker object or undefined if the parser doesn't have
2016  *      any location information.
2017  * @private
2018  */
2019 function markerCreatePreserveWhitespace() {
2020
2021     if (!extra.loc && !extra.range) {
2022         return undefined;
2023     }
2024
2025     return {
2026         offset: index,
2027         line: lineNumber,
2028         col: index - lineStart
2029     };
2030 }
2031
2032
2033 //------------------------------------------------------------------------------
2034 // Syntax Tree Delegate
2035 //------------------------------------------------------------------------------
2036
2037 // Return true if there is a line terminator before the next token.
2038
2039 function peekLineTerminator() {
2040     var pos, line, start, found;
2041
2042     pos = index;
2043     line = lineNumber;
2044     start = lineStart;
2045     skipComment();
2046     found = lineNumber !== line;
2047     index = pos;
2048     lineNumber = line;
2049     lineStart = start;
2050
2051     return found;
2052 }
2053
2054 // Throw an exception
2055
2056 function throwError(token, messageFormat) {
2057
2058     var error,
2059         args = Array.prototype.slice.call(arguments, 2),
2060         msg = messageFormat.replace(
2061             /%(\d)/g,
2062             function (whole, index) {
2063                 assert(index < args.length, "Message reference must be in range");
2064                 return args[index];
2065             }
2066         );
2067
2068     if (typeof token.lineNumber === "number") {
2069         error = new Error("Line " + token.lineNumber + ": " + msg);
2070         error.index = token.range[0];
2071         error.lineNumber = token.lineNumber;
2072         error.column = token.range[0] - lineStart + 1;
2073     } else {
2074         error = new Error("Line " + lineNumber + ": " + msg);
2075         error.index = index;
2076         error.lineNumber = lineNumber;
2077         error.column = index - lineStart + 1;
2078     }
2079
2080     error.description = msg;
2081     throw error;
2082 }
2083
2084 function throwErrorTolerant() {
2085     try {
2086         throwError.apply(null, arguments);
2087     } catch (e) {
2088         if (extra.errors) {
2089             extra.errors.push(e);
2090         } else {
2091             throw e;
2092         }
2093     }
2094 }
2095
2096
2097 // Throw an exception because of the token.
2098
2099 function throwUnexpected(token) {
2100
2101     if (token.type === Token.EOF) {
2102         throwError(token, Messages.UnexpectedEOS);
2103     }
2104
2105     if (token.type === Token.NumericLiteral) {
2106         throwError(token, Messages.UnexpectedNumber);
2107     }
2108
2109     if (token.type === Token.StringLiteral || token.type === Token.JSXText) {
2110         throwError(token, Messages.UnexpectedString);
2111     }
2112
2113     if (token.type === Token.Identifier) {
2114         throwError(token, Messages.UnexpectedIdentifier);
2115     }
2116
2117     if (token.type === Token.Keyword) {
2118         if (syntax.isFutureReservedWord(token.value)) {
2119             throwError(token, Messages.UnexpectedReserved);
2120         } else if (strict && syntax.isStrictModeReservedWord(token.value)) {
2121             throwErrorTolerant(token, Messages.StrictReservedWord);
2122             return;
2123         }
2124         throwError(token, Messages.UnexpectedToken, token.value);
2125     }
2126
2127     if (token.type === Token.Template) {
2128         throwError(token, Messages.UnexpectedTemplate, token.value.raw);
2129     }
2130
2131     // BooleanLiteral, NullLiteral, or Punctuator.
2132     throwError(token, Messages.UnexpectedToken, token.value);
2133 }
2134
2135 // Expect the next token to match the specified punctuator.
2136 // If not, an exception will be thrown.
2137
2138 function expect(value) {
2139     var token = lex();
2140     if (token.type !== Token.Punctuator || token.value !== value) {
2141         throwUnexpected(token);
2142     }
2143 }
2144
2145 // Expect the next token to match the specified keyword.
2146 // If not, an exception will be thrown.
2147
2148 function expectKeyword(keyword) {
2149     var token = lex();
2150     if (token.type !== Token.Keyword || token.value !== keyword) {
2151         throwUnexpected(token);
2152     }
2153 }
2154
2155 // Return true if the next token matches the specified punctuator.
2156
2157 function match(value) {
2158     return lookahead.type === Token.Punctuator && lookahead.value === value;
2159 }
2160
2161 // Return true if the next token matches the specified keyword
2162
2163 function matchKeyword(keyword) {
2164     return lookahead.type === Token.Keyword && lookahead.value === keyword;
2165 }
2166
2167 // Return true if the next token matches the specified contextual keyword
2168 // (where an identifier is sometimes a keyword depending on the context)
2169
2170 function matchContextualKeyword(keyword) {
2171     return lookahead.type === Token.Identifier && lookahead.value === keyword;
2172 }
2173
2174 // Return true if the next token is an assignment operator
2175
2176 function matchAssign() {
2177     var op;
2178
2179     if (lookahead.type !== Token.Punctuator) {
2180         return false;
2181     }
2182     op = lookahead.value;
2183     return op === "=" ||
2184         op === "*=" ||
2185         op === "/=" ||
2186         op === "%=" ||
2187         op === "+=" ||
2188         op === "-=" ||
2189         op === "<<=" ||
2190         op === ">>=" ||
2191         op === ">>>=" ||
2192         op === "&=" ||
2193         op === "^=" ||
2194         op === "|=";
2195 }
2196
2197 function consumeSemicolon() {
2198     var line;
2199
2200     // Catch the very common case first: immediately a semicolon (U+003B).
2201     if (source.charCodeAt(index) === 0x3B || match(";")) {
2202         lex();
2203         return;
2204     }
2205
2206     line = lineNumber;
2207     skipComment();
2208     if (lineNumber !== line) {
2209         return;
2210     }
2211
2212     if (lookahead.type !== Token.EOF && !match("}")) {
2213         throwUnexpected(lookahead);
2214     }
2215 }
2216
2217 // Return true if provided expression is LeftHandSideExpression
2218
2219 function isLeftHandSide(expr) {
2220     return expr.type === astNodeTypes.Identifier || expr.type === astNodeTypes.MemberExpression;
2221 }
2222
2223 // 11.1.4 Array Initialiser
2224
2225 function parseArrayInitialiser() {
2226     var elements = [],
2227         marker = markerCreate(),
2228         tmp;
2229
2230     expect("[");
2231
2232     while (!match("]")) {
2233         if (match(",")) {
2234             lex(); // only get here when you have [a,,] or similar
2235             elements.push(null);
2236         } else {
2237             tmp = parseSpreadOrAssignmentExpression();
2238             elements.push(tmp);
2239             if (!(match("]"))) {
2240                 expect(","); // handles the common case of comma-separated values
2241             }
2242         }
2243     }
2244
2245     expect("]");
2246
2247     return markerApply(marker, astNodeFactory.createArrayExpression(elements));
2248 }
2249
2250 // 11.1.5 Object Initialiser
2251
2252 function parsePropertyFunction(paramInfo, options) {
2253     var previousStrict = strict,
2254         previousYieldAllowed = state.yieldAllowed,
2255         generator = options ? options.generator : false,
2256         body;
2257
2258     state.yieldAllowed = generator;
2259
2260     /*
2261      * Esprima uses parseConciseBody() here, which is incorrect. Object literal
2262      * methods must have braces.
2263      */
2264     body = parseFunctionSourceElements();
2265
2266     if (strict && paramInfo.firstRestricted) {
2267         throwErrorTolerant(paramInfo.firstRestricted, Messages.StrictParamName);
2268     }
2269
2270     if (strict && paramInfo.stricted) {
2271         throwErrorTolerant(paramInfo.stricted, paramInfo.message);
2272     }
2273
2274     strict = previousStrict;
2275     state.yieldAllowed = previousYieldAllowed;
2276
2277     return markerApply(options.marker, astNodeFactory.createFunctionExpression(
2278         null,
2279         paramInfo.params,
2280         paramInfo.defaults,
2281         body,
2282         paramInfo.rest,
2283         generator,
2284         body.type !== astNodeTypes.BlockStatement
2285     ));
2286 }
2287
2288 function parsePropertyMethodFunction(options) {
2289     var previousStrict = strict,
2290         marker = markerCreate(),
2291         params,
2292         method;
2293
2294     strict = true;
2295
2296     params = parseParams();
2297
2298     if (params.stricted) {
2299         throwErrorTolerant(params.stricted, params.message);
2300     }
2301
2302     method = parsePropertyFunction(params, {
2303         generator: options ? options.generator : false,
2304         marker: marker
2305     });
2306
2307     strict = previousStrict;
2308
2309     return method;
2310 }
2311
2312 function parseObjectPropertyKey() {
2313     var marker = markerCreate(),
2314         token = lex(),
2315         allowObjectLiteralComputed = extra.ecmaFeatures.objectLiteralComputedProperties,
2316         expr,
2317         result;
2318
2319     // Note: This function is called only from parseObjectProperty(), where
2320     // EOF and Punctuator tokens are already filtered out.
2321
2322     switch (token.type) {
2323         case Token.StringLiteral:
2324         case Token.NumericLiteral:
2325             if (strict && token.octal) {
2326                 throwErrorTolerant(token, Messages.StrictOctalLiteral);
2327             }
2328             return markerApply(marker, astNodeFactory.createLiteralFromSource(token, source));
2329
2330         case Token.Identifier:
2331         case Token.BooleanLiteral:
2332         case Token.NullLiteral:
2333         case Token.Keyword:
2334             return markerApply(marker, astNodeFactory.createIdentifier(token.value));
2335
2336         case Token.Punctuator:
2337             if ((!state.inObjectLiteral || allowObjectLiteralComputed) &&
2338                     token.value === "[") {
2339                 // For computed properties we should skip the [ and ], and
2340                 // capture in marker only the assignment expression itself.
2341                 marker = markerCreate();
2342                 expr = parseAssignmentExpression();
2343                 result = markerApply(marker, expr);
2344                 expect("]");
2345                 return result;
2346             }
2347
2348         // no default
2349     }
2350
2351     throwUnexpected(token);
2352 }
2353
2354 function lookaheadPropertyName() {
2355     switch (lookahead.type) {
2356         case Token.Identifier:
2357         case Token.StringLiteral:
2358         case Token.BooleanLiteral:
2359         case Token.NullLiteral:
2360         case Token.NumericLiteral:
2361         case Token.Keyword:
2362             return true;
2363         case Token.Punctuator:
2364             return lookahead.value === "[";
2365         // no default
2366     }
2367     return false;
2368 }
2369
2370 // This function is to try to parse a MethodDefinition as defined in 14.3. But in the case of object literals,
2371 // it might be called at a position where there is in fact a short hand identifier pattern or a data property.
2372 // This can only be determined after we consumed up to the left parentheses.
2373 // In order to avoid back tracking, it returns `null` if the position is not a MethodDefinition and the caller
2374 // is responsible to visit other options.
2375 function tryParseMethodDefinition(token, key, computed, marker) {
2376     var value, options, methodMarker;
2377
2378     if (token.type === Token.Identifier) {
2379         // check for `get` and `set`;
2380
2381         if (token.value === "get" && lookaheadPropertyName()) {
2382
2383             computed = match("[");
2384             key = parseObjectPropertyKey();
2385             methodMarker = markerCreate();
2386             expect("(");
2387             expect(")");
2388
2389             value = parsePropertyFunction({
2390                 params: [],
2391                 defaults: [],
2392                 stricted: null,
2393                 firstRestricted: null,
2394                 message: null,
2395                 rest: null
2396             }, {
2397                 marker: methodMarker
2398             });
2399
2400             return markerApply(marker, astNodeFactory.createProperty("get", key, value, false, false, computed));
2401
2402         } else if (token.value === "set" && lookaheadPropertyName()) {
2403             computed = match("[");
2404             key = parseObjectPropertyKey();
2405             methodMarker = markerCreate();
2406             expect("(");
2407
2408             options = {
2409                 params: [],
2410                 defaultCount: 0,
2411                 defaults: [],
2412                 stricted: null,
2413                 firstRestricted: null,
2414                 paramSet: new StringMap(),
2415                 rest: null
2416             };
2417             if (match(")")) {
2418                 throwErrorTolerant(lookahead, Messages.UnexpectedToken, lookahead.value);
2419             } else {
2420                 parseParam(options);
2421                 if (options.defaultCount === 0) {
2422                     options.defaults = [];
2423                 }
2424             }
2425             expect(")");
2426
2427             value = parsePropertyFunction(options, { marker: methodMarker });
2428             return markerApply(marker, astNodeFactory.createProperty("set", key, value, false, false, computed));
2429         }
2430     }
2431
2432     if (match("(")) {
2433         value = parsePropertyMethodFunction();
2434         return markerApply(marker, astNodeFactory.createProperty("init", key, value, true, false, computed));
2435     }
2436
2437     // Not a MethodDefinition.
2438     return null;
2439 }
2440
2441 /**
2442  * Parses Generator Properties
2443  * @param {ASTNode} key The property key (usually an identifier).
2444  * @param {Object} marker The marker to use for the node.
2445  * @returns {ASTNode} The generator property node.
2446  */
2447 function parseGeneratorProperty(key, marker) {
2448
2449     var computed = (lookahead.type === Token.Punctuator && lookahead.value === "[");
2450
2451     if (!match("(")) {
2452         throwUnexpected(lex());
2453     }
2454
2455     return markerApply(
2456         marker,
2457         astNodeFactory.createProperty(
2458             "init",
2459             key,
2460             parsePropertyMethodFunction({ generator: true }),
2461             true,
2462             false,
2463             computed
2464         )
2465     );
2466 }
2467
2468 // TODO(nzakas): Update to match Esprima
2469 function parseObjectProperty() {
2470     var token, key, id, computed, methodMarker, options;
2471     var allowComputed = extra.ecmaFeatures.objectLiteralComputedProperties,
2472         allowMethod = extra.ecmaFeatures.objectLiteralShorthandMethods,
2473         allowShorthand = extra.ecmaFeatures.objectLiteralShorthandProperties,
2474         allowGenerators = extra.ecmaFeatures.generators,
2475         allowDestructuring = extra.ecmaFeatures.destructuring,
2476         marker = markerCreate();
2477
2478     token = lookahead;
2479     computed = (token.value === "[" && token.type === Token.Punctuator);
2480
2481     if (token.type === Token.Identifier || (allowComputed && computed)) {
2482
2483         id = parseObjectPropertyKey();
2484
2485         /*
2486          * Check for getters and setters. Be careful! "get" and "set" are legal
2487          * method names. It's only a getter or setter if followed by a space.
2488          */
2489         if (token.value === "get" &&
2490                 !(match(":") || match("(") || match(",") || match("}"))) {
2491             computed = (lookahead.value === "[");
2492             key = parseObjectPropertyKey();
2493             methodMarker = markerCreate();
2494             expect("(");
2495             expect(")");
2496
2497             return markerApply(
2498                 marker,
2499                 astNodeFactory.createProperty(
2500                     "get",
2501                     key,
2502                     parsePropertyFunction({
2503                         generator: false
2504                     }, {
2505                         marker: methodMarker
2506                     }),
2507                     false,
2508                     false,
2509                     computed
2510                 )
2511             );
2512         }
2513
2514         if (token.value === "set" &&
2515                 !(match(":") || match("(") || match(",") || match("}"))) {
2516             computed = (lookahead.value === "[");
2517             key = parseObjectPropertyKey();
2518             methodMarker = markerCreate();
2519             expect("(");
2520
2521             options = {
2522                 params: [],
2523                 defaultCount: 0,
2524                 defaults: [],
2525                 stricted: null,
2526                 firstRestricted: null,
2527                 paramSet: new StringMap(),
2528                 rest: null
2529             };
2530
2531             if (match(")")) {
2532                 throwErrorTolerant(lookahead, Messages.UnexpectedToken, lookahead.value);
2533             } else {
2534                 parseParam(options);
2535                 if (options.defaultCount === 0) {
2536                     options.defaults = [];
2537                 }
2538             }
2539
2540             expect(")");
2541
2542             return markerApply(
2543                 marker,
2544                 astNodeFactory.createProperty(
2545                     "set",
2546                     key,
2547                     parsePropertyFunction(options, {
2548                         marker: methodMarker
2549                     }),
2550                     false,
2551                     false,
2552                     computed
2553                 )
2554             );
2555         }
2556
2557         // normal property (key:value)
2558         if (match(":")) {
2559             lex();
2560             return markerApply(
2561                 marker,
2562                 astNodeFactory.createProperty(
2563                     "init",
2564                     id,
2565                     parseAssignmentExpression(),
2566                     false,
2567                     false,
2568                     computed
2569                 )
2570             );
2571         }
2572
2573         // method shorthand (key(){...})
2574         if (allowMethod && match("(")) {
2575             return markerApply(
2576                 marker,
2577                 astNodeFactory.createProperty(
2578                     "init",
2579                     id,
2580                     parsePropertyMethodFunction({ generator: false }),
2581                     true,
2582                     false,
2583                     computed
2584                 )
2585             );
2586         }
2587
2588         // destructuring defaults (shorthand syntax)
2589         if (allowDestructuring && match("=")) {
2590             lex();
2591             var value = parseAssignmentExpression();
2592             var prop = markerApply(marker, astNodeFactory.createAssignmentExpression("=", id, value));
2593             prop.type = astNodeTypes.AssignmentPattern;
2594             var fullProperty = astNodeFactory.createProperty(
2595                 "init",
2596                 id,
2597                 prop,
2598                 false,
2599                 true, // shorthand
2600                 computed
2601             );
2602             return markerApply(marker, fullProperty);
2603         }
2604
2605         /*
2606          * Only other possibility is that this is a shorthand property. Computed
2607          * properties cannot use shorthand notation, so that's a syntax error.
2608          * If shorthand properties aren't allow, then this is an automatic
2609          * syntax error. Destructuring is another case with a similar shorthand syntax.
2610          */
2611         if (computed || (!allowShorthand && !allowDestructuring)) {
2612             throwUnexpected(lookahead);
2613         }
2614
2615         // shorthand property
2616         return markerApply(
2617             marker,
2618             astNodeFactory.createProperty(
2619                 "init",
2620                 id,
2621                 id,
2622                 false,
2623                 true,
2624                 false
2625             )
2626         );
2627     }
2628
2629     // only possibility in this branch is a shorthand generator
2630     if (token.type === Token.EOF || token.type === Token.Punctuator) {
2631         if (!allowGenerators || !match("*") || !allowMethod) {
2632             throwUnexpected(token);
2633         }
2634
2635         lex();
2636
2637         id = parseObjectPropertyKey();
2638
2639         return parseGeneratorProperty(id, marker);
2640
2641     }
2642
2643     /*
2644      * If we've made it here, then that means the property name is represented
2645      * by a string (i.e, { "foo": 2}). The only options here are normal
2646      * property with a colon or a method.
2647      */
2648     key = parseObjectPropertyKey();
2649
2650     // check for property value
2651     if (match(":")) {
2652         lex();
2653         return markerApply(
2654             marker,
2655             astNodeFactory.createProperty(
2656                 "init",
2657                 key,
2658                 parseAssignmentExpression(),
2659                 false,
2660                 false,
2661                 false
2662             )
2663         );
2664     }
2665
2666     // check for method
2667     if (allowMethod && match("(")) {
2668         return markerApply(
2669             marker,
2670             astNodeFactory.createProperty(
2671                 "init",
2672                 key,
2673                 parsePropertyMethodFunction(),
2674                 true,
2675                 false,
2676                 false
2677             )
2678         );
2679     }
2680
2681     // no other options, this is bad
2682     throwUnexpected(lex());
2683 }
2684
2685 function getFieldName(key) {
2686     var toString = String;
2687     if (key.type === astNodeTypes.Identifier) {
2688         return key.name;
2689     }
2690     return toString(key.value);
2691 }
2692
2693 function parseObjectInitialiser() {
2694     var marker = markerCreate(),
2695         allowDuplicates = extra.ecmaFeatures.objectLiteralDuplicateProperties,
2696         properties = [],
2697         property,
2698         name,
2699         propertyFn,
2700         kind,
2701         storedKind,
2702         previousInObjectLiteral = state.inObjectLiteral,
2703         kindMap = new StringMap();
2704
2705     state.inObjectLiteral = true;
2706
2707     expect("{");
2708
2709     while (!match("}")) {
2710
2711         property = parseObjectProperty();
2712
2713         if (!property.computed) {
2714
2715             name = getFieldName(property.key);
2716             propertyFn = (property.kind === "get") ? PropertyKind.Get : PropertyKind.Set;
2717             kind = (property.kind === "init") ? PropertyKind.Data : propertyFn;
2718
2719             if (kindMap.has(name)) {
2720                 storedKind = kindMap.get(name);
2721                 if (storedKind === PropertyKind.Data) {
2722                     if (kind === PropertyKind.Data && name === "__proto__" && allowDuplicates) {
2723                         // Duplicate '__proto__' literal properties are forbidden in ES 6
2724                         throwErrorTolerant({}, Messages.DuplicatePrototypeProperty);
2725                     } else if (strict && kind === PropertyKind.Data && !allowDuplicates) {
2726                         // Duplicate literal properties are only forbidden in ES 5 strict mode
2727                         throwErrorTolerant({}, Messages.StrictDuplicateProperty);
2728                     } else if (kind !== PropertyKind.Data) {
2729                         throwErrorTolerant({}, Messages.AccessorDataProperty);
2730                     }
2731                 } else {
2732                     if (kind === PropertyKind.Data) {
2733                         throwErrorTolerant({}, Messages.AccessorDataProperty);
2734                     } else if (storedKind & kind) {
2735                         throwErrorTolerant({}, Messages.AccessorGetSet);
2736                     }
2737                 }
2738                 kindMap.set(name, storedKind | kind);
2739             } else {
2740                 kindMap.set(name, kind);
2741             }
2742         }
2743
2744         properties.push(property);
2745
2746         if (!match("}")) {
2747             expect(",");
2748         }
2749     }
2750
2751     expect("}");
2752
2753     state.inObjectLiteral = previousInObjectLiteral;
2754
2755     return markerApply(marker, astNodeFactory.createObjectExpression(properties));
2756 }
2757
2758 /**
2759  * Parse a template string element and return its ASTNode representation
2760  * @param {Object} option Parsing & scanning options
2761  * @param {Object} option.head True if this element is the first in the
2762  *                               template string, false otherwise.
2763  * @returns {ASTNode} The template element node with marker info applied
2764  * @private
2765  */
2766 function parseTemplateElement(option) {
2767     var marker, token;
2768
2769     if (lookahead.type !== Token.Template || (option.head && !lookahead.head)) {
2770         throwError({}, Messages.UnexpectedToken, "ILLEGAL");
2771     }
2772
2773     marker = markerCreate();
2774     token = lex();
2775
2776     return markerApply(
2777         marker,
2778         astNodeFactory.createTemplateElement(
2779             {
2780                 raw: token.value.raw,
2781                 cooked: token.value.cooked
2782             },
2783             token.tail
2784         )
2785     );
2786 }
2787
2788 /**
2789  * Parse a template string literal and return its ASTNode representation
2790  * @returns {ASTNode} The template literal node with marker info applied
2791  * @private
2792  */
2793 function parseTemplateLiteral() {
2794     var quasi, quasis, expressions, marker = markerCreate();
2795
2796     quasi = parseTemplateElement({ head: true });
2797     quasis = [ quasi ];
2798     expressions = [];
2799
2800     while (!quasi.tail) {
2801         expressions.push(parseExpression());
2802         quasi = parseTemplateElement({ head: false });
2803         quasis.push(quasi);
2804     }
2805
2806     return markerApply(marker, astNodeFactory.createTemplateLiteral(quasis, expressions));
2807 }
2808
2809 // 11.1.6 The Grouping Operator
2810
2811 function parseGroupExpression() {
2812     var expr;
2813
2814     expect("(");
2815
2816     ++state.parenthesisCount;
2817
2818     expr = parseExpression();
2819
2820     expect(")");
2821
2822     return expr;
2823 }
2824
2825
2826 // 11.1 Primary Expressions
2827
2828 function parsePrimaryExpression() {
2829     var type, token, expr,
2830         marker,
2831         allowJSX = extra.ecmaFeatures.jsx,
2832         allowClasses = extra.ecmaFeatures.classes,
2833         allowSuper = allowClasses || extra.ecmaFeatures.superInFunctions;
2834
2835     if (match("(")) {
2836         return parseGroupExpression();
2837     }
2838
2839     if (match("[")) {
2840         return parseArrayInitialiser();
2841     }
2842
2843     if (match("{")) {
2844         return parseObjectInitialiser();
2845     }
2846
2847     if (allowJSX && match("<")) {
2848         return parseJSXElement();
2849     }
2850
2851     type = lookahead.type;
2852     marker = markerCreate();
2853
2854     if (type === Token.Identifier) {
2855         expr = astNodeFactory.createIdentifier(lex().value);
2856     } else if (type === Token.StringLiteral || type === Token.NumericLiteral) {
2857         if (strict && lookahead.octal) {
2858             throwErrorTolerant(lookahead, Messages.StrictOctalLiteral);
2859         }
2860         expr = astNodeFactory.createLiteralFromSource(lex(), source);
2861     } else if (type === Token.Keyword) {
2862         if (matchKeyword("function")) {
2863             return parseFunctionExpression();
2864         }
2865
2866         if (allowSuper && matchKeyword("super") && state.inFunctionBody) {
2867             marker = markerCreate();
2868             lex();
2869             return markerApply(marker, astNodeFactory.createIdentifier("super"));
2870         }
2871
2872         if (matchKeyword("this")) {
2873             marker = markerCreate();
2874             lex();
2875             return markerApply(marker, astNodeFactory.createThisExpression());
2876         }
2877
2878         if (allowClasses && matchKeyword("class")) {
2879             return parseClassExpression();
2880         }
2881
2882         throwUnexpected(lex());
2883     } else if (type === Token.BooleanLiteral) {
2884         token = lex();
2885         token.value = (token.value === "true");
2886         expr = astNodeFactory.createLiteralFromSource(token, source);
2887     } else if (type === Token.NullLiteral) {
2888         token = lex();
2889         token.value = null;
2890         expr = astNodeFactory.createLiteralFromSource(token, source);
2891     } else if (match("/") || match("/=")) {
2892         if (typeof extra.tokens !== "undefined") {
2893             expr = astNodeFactory.createLiteralFromSource(collectRegex(), source);
2894         } else {
2895             expr = astNodeFactory.createLiteralFromSource(scanRegExp(), source);
2896         }
2897         peek();
2898     } else if (type === Token.Template) {
2899         return parseTemplateLiteral();
2900     } else {
2901        throwUnexpected(lex());
2902     }
2903
2904     return markerApply(marker, expr);
2905 }
2906
2907 // 11.2 Left-Hand-Side Expressions
2908
2909 function parseArguments() {
2910     var args = [], arg;
2911
2912     expect("(");
2913
2914     if (!match(")")) {
2915         while (index < length) {
2916             arg = parseSpreadOrAssignmentExpression();
2917             args.push(arg);
2918
2919             if (match(")")) {
2920                 break;
2921             }
2922
2923             expect(",");
2924         }
2925     }
2926
2927     expect(")");
2928
2929     return args;
2930 }
2931
2932 function parseSpreadOrAssignmentExpression() {
2933     if (match("...")) {
2934         var marker = markerCreate();
2935         lex();
2936         return markerApply(marker, astNodeFactory.createSpreadElement(parseAssignmentExpression()));
2937     }
2938     return parseAssignmentExpression();
2939 }
2940
2941 function parseNonComputedProperty() {
2942     var token,
2943         marker = markerCreate();
2944
2945     token = lex();
2946
2947     if (!isIdentifierName(token)) {
2948         throwUnexpected(token);
2949     }
2950
2951     return markerApply(marker, astNodeFactory.createIdentifier(token.value));
2952 }
2953
2954 function parseNonComputedMember() {
2955     expect(".");
2956
2957     return parseNonComputedProperty();
2958 }
2959
2960 function parseComputedMember() {
2961     var expr;
2962
2963     expect("[");
2964
2965     expr = parseExpression();
2966
2967     expect("]");
2968
2969     return expr;
2970 }
2971
2972 function parseNewExpression() {
2973     var callee, args,
2974         marker = markerCreate();
2975
2976     expectKeyword("new");
2977     callee = parseLeftHandSideExpression();
2978     args = match("(") ? parseArguments() : [];
2979
2980     return markerApply(marker, astNodeFactory.createNewExpression(callee, args));
2981 }
2982
2983 function parseLeftHandSideExpressionAllowCall() {
2984     var expr, args,
2985         previousAllowIn = state.allowIn,
2986         marker = markerCreate();
2987
2988     state.allowIn = true;
2989     expr = matchKeyword("new") ? parseNewExpression() : parsePrimaryExpression();
2990     state.allowIn = previousAllowIn;
2991
2992     // only start parsing template literal if the lookahead is a head (beginning with `)
2993     while (match(".") || match("[") || match("(") || (lookahead.type === Token.Template && lookahead.head)) {
2994         if (match("(")) {
2995             args = parseArguments();
2996             expr = markerApply(marker, astNodeFactory.createCallExpression(expr, args));
2997         } else if (match("[")) {
2998             expr = markerApply(marker, astNodeFactory.createMemberExpression("[", expr, parseComputedMember()));
2999         } else if (match(".")) {
3000             expr = markerApply(marker, astNodeFactory.createMemberExpression(".", expr, parseNonComputedMember()));
3001         } else {
3002             expr = markerApply(marker, astNodeFactory.createTaggedTemplateExpression(expr, parseTemplateLiteral()));
3003         }
3004     }
3005
3006     return expr;
3007 }
3008
3009 function parseLeftHandSideExpression() {
3010     var expr,
3011         previousAllowIn = state.allowIn,
3012         marker = markerCreate();
3013
3014     expr = matchKeyword("new") ? parseNewExpression() : parsePrimaryExpression();
3015     state.allowIn = previousAllowIn;
3016
3017     // only start parsing template literal if the lookahead is a head (beginning with `)
3018     while (match(".") || match("[") || (lookahead.type === Token.Template && lookahead.head)) {
3019         if (match("[")) {
3020             expr = markerApply(marker, astNodeFactory.createMemberExpression("[", expr, parseComputedMember()));
3021         } else if (match(".")) {
3022             expr = markerApply(marker, astNodeFactory.createMemberExpression(".", expr, parseNonComputedMember()));
3023         } else {
3024             expr = markerApply(marker, astNodeFactory.createTaggedTemplateExpression(expr, parseTemplateLiteral()));
3025         }
3026     }
3027
3028     return expr;
3029 }
3030
3031
3032 // 11.3 Postfix Expressions
3033
3034 function parsePostfixExpression() {
3035     var expr, token,
3036         marker = markerCreate();
3037
3038     expr = parseLeftHandSideExpressionAllowCall();
3039
3040     if (lookahead.type === Token.Punctuator) {
3041         if ((match("++") || match("--")) && !peekLineTerminator()) {
3042             // 11.3.1, 11.3.2
3043             if (strict && expr.type === astNodeTypes.Identifier && syntax.isRestrictedWord(expr.name)) {
3044                 throwErrorTolerant({}, Messages.StrictLHSPostfix);
3045             }
3046
3047             if (!isLeftHandSide(expr)) {
3048                 throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
3049             }
3050
3051             token = lex();
3052             expr = markerApply(marker, astNodeFactory.createPostfixExpression(token.value, expr));
3053         }
3054     }
3055
3056     return expr;
3057 }
3058
3059 // 11.4 Unary Operators
3060
3061 function parseUnaryExpression() {
3062     var token, expr,
3063         marker;
3064
3065     if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) {
3066         expr = parsePostfixExpression();
3067     } else if (match("++") || match("--")) {
3068         marker = markerCreate();
3069         token = lex();
3070         expr = parseUnaryExpression();
3071         // 11.4.4, 11.4.5
3072         if (strict && expr.type === astNodeTypes.Identifier && syntax.isRestrictedWord(expr.name)) {
3073             throwErrorTolerant({}, Messages.StrictLHSPrefix);
3074         }
3075
3076         if (!isLeftHandSide(expr)) {
3077             throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
3078         }
3079
3080         expr = astNodeFactory.createUnaryExpression(token.value, expr);
3081         expr = markerApply(marker, expr);
3082     } else if (match("+") || match("-") || match("~") || match("!")) {
3083         marker = markerCreate();
3084         token = lex();
3085         expr = parseUnaryExpression();
3086         expr = astNodeFactory.createUnaryExpression(token.value, expr);
3087         expr = markerApply(marker, expr);
3088     } else if (matchKeyword("delete") || matchKeyword("void") || matchKeyword("typeof")) {
3089         marker = markerCreate();
3090         token = lex();
3091         expr = parseUnaryExpression();
3092         expr = astNodeFactory.createUnaryExpression(token.value, expr);
3093         expr = markerApply(marker, expr);
3094         if (strict && expr.operator === "delete" && expr.argument.type === astNodeTypes.Identifier) {
3095             throwErrorTolerant({}, Messages.StrictDelete);
3096         }
3097     } else {
3098         expr = parsePostfixExpression();
3099     }
3100
3101     return expr;
3102 }
3103
3104 function binaryPrecedence(token, allowIn) {
3105     var prec = 0;
3106
3107     if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
3108         return 0;
3109     }
3110
3111     switch (token.value) {
3112     case "||":
3113         prec = 1;
3114         break;
3115
3116     case "&&":
3117         prec = 2;
3118         break;
3119
3120     case "|":
3121         prec = 3;
3122         break;
3123
3124     case "^":
3125         prec = 4;
3126         break;
3127
3128     case "&":
3129         prec = 5;
3130         break;
3131
3132     case "==":
3133     case "!=":
3134     case "===":
3135     case "!==":
3136         prec = 6;
3137         break;
3138
3139     case "<":
3140     case ">":
3141     case "<=":
3142     case ">=":
3143     case "instanceof":
3144         prec = 7;
3145         break;
3146
3147     case "in":
3148         prec = allowIn ? 7 : 0;
3149         break;
3150
3151     case "<<":
3152     case ">>":
3153     case ">>>":
3154         prec = 8;
3155         break;
3156
3157     case "+":
3158     case "-":
3159         prec = 9;
3160         break;
3161
3162     case "*":
3163     case "/":
3164     case "%":
3165         prec = 11;
3166         break;
3167
3168     default:
3169         break;
3170     }
3171
3172     return prec;
3173 }
3174
3175 // 11.5 Multiplicative Operators
3176 // 11.6 Additive Operators
3177 // 11.7 Bitwise Shift Operators
3178 // 11.8 Relational Operators
3179 // 11.9 Equality Operators
3180 // 11.10 Binary Bitwise Operators
3181 // 11.11 Binary Logical Operators
3182 function parseBinaryExpression() {
3183     var expr, token, prec, previousAllowIn, stack, right, operator, left, i,
3184         marker, markers;
3185
3186     previousAllowIn = state.allowIn;
3187     state.allowIn = true;
3188
3189     marker = markerCreate();
3190     left = parseUnaryExpression();
3191
3192     token = lookahead;
3193     prec = binaryPrecedence(token, previousAllowIn);
3194     if (prec === 0) {
3195         return left;
3196     }
3197     token.prec = prec;
3198     lex();
3199
3200     markers = [marker, markerCreate()];
3201     right = parseUnaryExpression();
3202
3203     stack = [left, token, right];
3204
3205     while ((prec = binaryPrecedence(lookahead, previousAllowIn)) > 0) {
3206
3207         // Reduce: make a binary expression from the three topmost entries.
3208         while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
3209             right = stack.pop();
3210             operator = stack.pop().value;
3211             left = stack.pop();
3212             expr = astNodeFactory.createBinaryExpression(operator, left, right);
3213             markers.pop();
3214             marker = markers.pop();
3215             markerApply(marker, expr);
3216             stack.push(expr);
3217             markers.push(marker);
3218         }
3219
3220         // Shift.
3221         token = lex();
3222         token.prec = prec;
3223         stack.push(token);
3224         markers.push(markerCreate());
3225         expr = parseUnaryExpression();
3226         stack.push(expr);
3227     }
3228
3229     state.allowIn = previousAllowIn;
3230
3231     // Final reduce to clean-up the stack.
3232     i = stack.length - 1;
3233     expr = stack[i];
3234     markers.pop();
3235     while (i > 1) {
3236         expr = astNodeFactory.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
3237         i -= 2;
3238         marker = markers.pop();
3239         markerApply(marker, expr);
3240     }
3241
3242     return expr;
3243 }
3244
3245 // 11.12 Conditional Operator
3246
3247 function parseConditionalExpression() {
3248     var expr, previousAllowIn, consequent, alternate,
3249         marker = markerCreate();
3250
3251     expr = parseBinaryExpression();
3252
3253     if (match("?")) {
3254         lex();
3255         previousAllowIn = state.allowIn;
3256         state.allowIn = true;
3257         consequent = parseAssignmentExpression();
3258         state.allowIn = previousAllowIn;
3259         expect(":");
3260         alternate = parseAssignmentExpression();
3261
3262         expr = astNodeFactory.createConditionalExpression(expr, consequent, alternate);
3263         markerApply(marker, expr);
3264     }
3265
3266     return expr;
3267 }
3268
3269 // [ES6] 14.2 Arrow Function
3270
3271 function parseConciseBody() {
3272     if (match("{")) {
3273         return parseFunctionSourceElements();
3274     }
3275     return parseAssignmentExpression();
3276 }
3277
3278 function reinterpretAsCoverFormalsList(expressions) {
3279     var i, len, param, params, defaults, defaultCount, options, rest;
3280
3281     params = [];
3282     defaults = [];
3283     defaultCount = 0;
3284     rest = null;
3285     options = {
3286         paramSet: new StringMap()
3287     };
3288
3289     for (i = 0, len = expressions.length; i < len; i += 1) {
3290         param = expressions[i];
3291         if (param.type === astNodeTypes.Identifier) {
3292             params.push(param);
3293             defaults.push(null);
3294             validateParam(options, param, param.name);
3295         }  else if (param.type === astNodeTypes.ObjectExpression || param.type === astNodeTypes.ArrayExpression) {
3296             reinterpretAsDestructuredParameter(options, param);
3297             params.push(param);
3298             defaults.push(null);
3299         } else if (param.type === astNodeTypes.SpreadElement) {
3300             assert(i === len - 1, "It is guaranteed that SpreadElement is last element by parseExpression");
3301             if (param.argument.type !== astNodeTypes.Identifier) {
3302                 throwError({}, Messages.InvalidLHSInFormalsList);
3303             }
3304             reinterpretAsDestructuredParameter(options, param.argument);
3305             rest = param.argument;
3306         } else if (param.type === astNodeTypes.AssignmentExpression) {
3307             params.push(param.left);
3308             defaults.push(param.right);
3309             ++defaultCount;
3310             validateParam(options, param.left, param.left.name);
3311         } else {
3312             return null;
3313         }
3314     }
3315
3316     if (options.message === Messages.StrictParamDupe) {
3317         throwError(
3318             strict ? options.stricted : options.firstRestricted,
3319             options.message
3320         );
3321     }
3322
3323     // must be here so it's not an array of [null, null]
3324     if (defaultCount === 0) {
3325         defaults = [];
3326     }
3327
3328     return {
3329         params: params,
3330         defaults: defaults,
3331         rest: rest,
3332         stricted: options.stricted,
3333         firstRestricted: options.firstRestricted,
3334         message: options.message
3335     };
3336 }
3337
3338 function parseArrowFunctionExpression(options, marker) {
3339     var previousStrict, body;
3340
3341     expect("=>");
3342     previousStrict = strict;
3343
3344     body = parseConciseBody();
3345
3346     if (strict && options.firstRestricted) {
3347         throwError(options.firstRestricted, options.message);
3348     }
3349     if (strict && options.stricted) {
3350         throwErrorTolerant(options.stricted, options.message);
3351     }
3352
3353     strict = previousStrict;
3354     return markerApply(marker, astNodeFactory.createArrowFunctionExpression(
3355         options.params,
3356         options.defaults,
3357         body,
3358         options.rest,
3359         body.type !== astNodeTypes.BlockStatement
3360     ));
3361 }
3362
3363 // 11.13 Assignment Operators
3364
3365 // 12.14.5 AssignmentPattern
3366
3367 function reinterpretAsAssignmentBindingPattern(expr) {
3368     var i, len, property, element,
3369         allowDestructuring = extra.ecmaFeatures.destructuring;
3370
3371     if (!allowDestructuring) {
3372         throwUnexpected(lex());
3373     }
3374
3375     if (expr.type === astNodeTypes.ObjectExpression) {
3376         expr.type = astNodeTypes.ObjectPattern;
3377         for (i = 0, len = expr.properties.length; i < len; i += 1) {
3378             property = expr.properties[i];
3379             if (property.kind !== "init") {
3380                 throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
3381             }
3382             reinterpretAsAssignmentBindingPattern(property.value);
3383         }
3384     } else if (expr.type === astNodeTypes.ArrayExpression) {
3385         expr.type = astNodeTypes.ArrayPattern;
3386         for (i = 0, len = expr.elements.length; i < len; i += 1) {
3387             element = expr.elements[i];
3388             /* istanbul ignore else */
3389             if (element) {
3390                 reinterpretAsAssignmentBindingPattern(element);
3391             }
3392         }
3393     } else if (expr.type === astNodeTypes.Identifier) {
3394         if (syntax.isRestrictedWord(expr.name)) {
3395             throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
3396         }
3397     } else if (expr.type === astNodeTypes.SpreadElement) {
3398         reinterpretAsAssignmentBindingPattern(expr.argument);
3399         if (expr.argument.type === astNodeTypes.ObjectPattern) {
3400             throwErrorTolerant({}, Messages.ObjectPatternAsSpread);
3401         }
3402     } else if (expr.type === "AssignmentExpression" && expr.operator === "=") {
3403         expr.type = astNodeTypes.AssignmentPattern;
3404     } else {
3405         /* istanbul ignore else */
3406         if (expr.type !== astNodeTypes.MemberExpression &&
3407             expr.type !== astNodeTypes.CallExpression &&
3408             expr.type !== astNodeTypes.NewExpression &&
3409             expr.type !== astNodeTypes.AssignmentPattern
3410         ) {
3411             throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
3412         }
3413     }
3414 }
3415
3416 // 13.2.3 BindingPattern
3417
3418 function reinterpretAsDestructuredParameter(options, expr) {
3419     var i, len, property, element,
3420         allowDestructuring = extra.ecmaFeatures.destructuring;
3421
3422     if (!allowDestructuring) {
3423         throwUnexpected(lex());
3424     }
3425
3426     if (expr.type === astNodeTypes.ObjectExpression) {
3427         expr.type = astNodeTypes.ObjectPattern;
3428         for (i = 0, len = expr.properties.length; i < len; i += 1) {
3429             property = expr.properties[i];
3430             if (property.kind !== "init") {
3431                 throwErrorTolerant({}, Messages.InvalidLHSInFormalsList);
3432             }
3433             reinterpretAsDestructuredParameter(options, property.value);
3434         }
3435     } else if (expr.type === astNodeTypes.ArrayExpression) {
3436         expr.type = astNodeTypes.ArrayPattern;
3437         for (i = 0, len = expr.elements.length; i < len; i += 1) {
3438             element = expr.elements[i];
3439             if (element) {
3440                 reinterpretAsDestructuredParameter(options, element);
3441             }
3442         }
3443     } else if (expr.type === astNodeTypes.Identifier) {
3444         validateParam(options, expr, expr.name);
3445     } else if (expr.type === astNodeTypes.SpreadElement) {
3446         // BindingRestElement only allows BindingIdentifier
3447         if (expr.argument.type !== astNodeTypes.Identifier) {
3448             throwErrorTolerant({}, Messages.InvalidLHSInFormalsList);
3449         }
3450         validateParam(options, expr.argument, expr.argument.name);
3451     } else if (expr.type === astNodeTypes.AssignmentExpression && expr.operator === "=") {
3452         expr.type = astNodeTypes.AssignmentPattern;
3453     } else if (expr.type !== astNodeTypes.AssignmentPattern) {
3454         throwError({}, Messages.InvalidLHSInFormalsList);
3455     }
3456 }
3457
3458 function parseAssignmentExpression() {
3459     var token, left, right, node, params,
3460         marker,
3461         startsWithParen = false,
3462         oldParenthesisCount = state.parenthesisCount,
3463         allowGenerators = extra.ecmaFeatures.generators;
3464
3465     // Note that 'yield' is treated as a keyword in strict mode, but a
3466     // contextual keyword (identifier) in non-strict mode, so we need
3467     // to use matchKeyword and matchContextualKeyword appropriately.
3468     if (allowGenerators && ((state.yieldAllowed && matchContextualKeyword("yield")) || (strict && matchKeyword("yield")))) {
3469         return parseYieldExpression();
3470     }
3471
3472     marker = markerCreate();
3473
3474     if (match("(")) {
3475         token = lookahead2();
3476         if ((token.value === ")" && token.type === Token.Punctuator) || token.value === "...") {
3477             params = parseParams();
3478             if (!match("=>")) {
3479                 throwUnexpected(lex());
3480             }
3481             return parseArrowFunctionExpression(params, marker);
3482         }
3483         startsWithParen = true;
3484     }
3485
3486     // revert to the previous lookahead style object
3487     token = lookahead;
3488     node = left = parseConditionalExpression();
3489
3490     if (match("=>") &&
3491             (state.parenthesisCount === oldParenthesisCount ||
3492             state.parenthesisCount === (oldParenthesisCount + 1))) {
3493
3494         if (node.type === astNodeTypes.Identifier) {
3495             params = reinterpretAsCoverFormalsList([ node ]);
3496         } else if (node.type === astNodeTypes.AssignmentExpression ||
3497             node.type === astNodeTypes.ArrayExpression ||
3498             node.type === astNodeTypes.ObjectExpression) {
3499             if (!startsWithParen) {
3500                 throwUnexpected(lex());
3501             }
3502             params = reinterpretAsCoverFormalsList([ node ]);
3503         } else if (node.type === astNodeTypes.SequenceExpression) {
3504             params = reinterpretAsCoverFormalsList(node.expressions);
3505         }
3506
3507         if (params) {
3508             return parseArrowFunctionExpression(params, marker);
3509         }
3510     }
3511
3512     if (matchAssign()) {
3513
3514         // 11.13.1
3515         if (strict && left.type === astNodeTypes.Identifier && syntax.isRestrictedWord(left.name)) {
3516             throwErrorTolerant(token, Messages.StrictLHSAssignment);
3517         }
3518
3519         // ES.next draf 11.13 Runtime Semantics step 1
3520         if (match("=") && (node.type === astNodeTypes.ObjectExpression || node.type === astNodeTypes.ArrayExpression)) {
3521             reinterpretAsAssignmentBindingPattern(node);
3522         } else if (!isLeftHandSide(node)) {
3523             throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
3524         }
3525
3526         token = lex();
3527         right = parseAssignmentExpression();
3528         node = markerApply(marker, astNodeFactory.createAssignmentExpression(token.value, left, right));
3529     }
3530
3531     return node;
3532 }
3533
3534 // 11.14 Comma Operator
3535
3536 function parseExpression() {
3537     var marker = markerCreate(),
3538         expr = parseAssignmentExpression(),
3539         expressions = [ expr ],
3540         sequence, spreadFound;
3541
3542     if (match(",")) {
3543         while (index < length) {
3544             if (!match(",")) {
3545                 break;
3546             }
3547             lex();
3548             expr = parseSpreadOrAssignmentExpression();
3549             expressions.push(expr);
3550
3551             if (expr.type === astNodeTypes.SpreadElement) {
3552                 spreadFound = true;
3553                 if (!match(")")) {
3554                     throwError({}, Messages.ElementAfterSpreadElement);
3555                 }
3556                 break;
3557             }
3558         }
3559
3560         sequence = markerApply(marker, astNodeFactory.createSequenceExpression(expressions));
3561     }
3562
3563     if (spreadFound && lookahead2().value !== "=>") {
3564         throwError({}, Messages.IllegalSpread);
3565     }
3566
3567     return sequence || expr;
3568 }
3569
3570 // 12.1 Block
3571
3572 function parseStatementList() {
3573     var list = [],
3574         statement;
3575
3576     while (index < length) {
3577         if (match("}")) {
3578             break;
3579         }
3580         statement = parseSourceElement();
3581         if (typeof statement === "undefined") {
3582             break;
3583         }
3584         list.push(statement);
3585     }
3586
3587     return list;
3588 }
3589
3590 function parseBlock() {
3591     var block,
3592         marker = markerCreate();
3593
3594     expect("{");
3595
3596     block = parseStatementList();
3597
3598     expect("}");
3599
3600     return markerApply(marker, astNodeFactory.createBlockStatement(block));
3601 }
3602
3603 // 12.2 Variable Statement
3604
3605 function parseVariableIdentifier() {
3606     var token,
3607         marker = markerCreate();
3608
3609     token = lex();
3610
3611     if (token.type !== Token.Identifier) {
3612         if (strict && token.type === Token.Keyword && syntax.isStrictModeReservedWord(token.value)) {
3613             throwErrorTolerant(token, Messages.StrictReservedWord);
3614         } else {
3615             throwUnexpected(token);
3616         }
3617     }
3618
3619     return markerApply(marker, astNodeFactory.createIdentifier(token.value));
3620 }
3621
3622 function parseVariableDeclaration(kind) {
3623     var id,
3624         marker = markerCreate(),
3625         init = null;
3626     if (match("{")) {
3627         id = parseObjectInitialiser();
3628         reinterpretAsAssignmentBindingPattern(id);
3629     } else if (match("[")) {
3630         id = parseArrayInitialiser();
3631         reinterpretAsAssignmentBindingPattern(id);
3632     } else {
3633         /* istanbul ignore next */
3634         id = state.allowKeyword ? parseNonComputedProperty() : parseVariableIdentifier();
3635         // 12.2.1
3636         if (strict && syntax.isRestrictedWord(id.name)) {
3637             throwErrorTolerant({}, Messages.StrictVarName);
3638         }
3639     }
3640
3641     // TODO: Verify against feature flags
3642     if (kind === "const") {
3643         if (!match("=")) {
3644             throwError({}, Messages.NoUnintializedConst);
3645         }
3646         expect("=");
3647         init = parseAssignmentExpression();
3648     } else if (match("=")) {
3649         lex();
3650         init = parseAssignmentExpression();
3651     }
3652
3653     return markerApply(marker, astNodeFactory.createVariableDeclarator(id, init));
3654 }
3655
3656 function parseVariableDeclarationList(kind) {
3657     var list = [];
3658
3659     do {
3660         list.push(parseVariableDeclaration(kind));
3661         if (!match(",")) {
3662             break;
3663         }
3664         lex();
3665     } while (index < length);
3666
3667     return list;
3668 }
3669
3670 function parseVariableStatement() {
3671     var declarations;
3672
3673     expectKeyword("var");
3674
3675     declarations = parseVariableDeclarationList();
3676
3677     consumeSemicolon();
3678
3679     return astNodeFactory.createVariableDeclaration(declarations, "var");
3680 }
3681
3682 // kind may be `const` or `let`
3683 // Both are experimental and not in the specification yet.
3684 // see http://wiki.ecmascript.org/doku.php?id=harmony:const
3685 // and http://wiki.ecmascript.org/doku.php?id=harmony:let
3686 function parseConstLetDeclaration(kind) {
3687     var declarations,
3688         marker = markerCreate();
3689
3690     expectKeyword(kind);
3691
3692     declarations = parseVariableDeclarationList(kind);
3693
3694     consumeSemicolon();
3695
3696     return markerApply(marker, astNodeFactory.createVariableDeclaration(declarations, kind));
3697 }
3698
3699 // 12.3 Empty Statement
3700
3701 function parseEmptyStatement() {
3702     expect(";");
3703     return astNodeFactory.createEmptyStatement();
3704 }
3705
3706 // 12.4 Expression Statement
3707
3708 function parseExpressionStatement() {
3709     var expr = parseExpression();
3710     consumeSemicolon();
3711     return astNodeFactory.createExpressionStatement(expr);
3712 }
3713
3714 // 12.5 If statement
3715
3716 function parseIfStatement() {
3717     var test, consequent, alternate;
3718
3719     expectKeyword("if");
3720
3721     expect("(");
3722
3723     test = parseExpression();
3724
3725     expect(")");
3726
3727     consequent = parseStatement();
3728
3729     if (matchKeyword("else")) {
3730         lex();
3731         alternate = parseStatement();
3732     } else {
3733         alternate = null;
3734     }
3735
3736     return astNodeFactory.createIfStatement(test, consequent, alternate);
3737 }
3738
3739 // 12.6 Iteration Statements
3740
3741 function parseDoWhileStatement() {
3742     var body, test, oldInIteration;
3743
3744     expectKeyword("do");
3745
3746     oldInIteration = state.inIteration;
3747     state.inIteration = true;
3748
3749     body = parseStatement();
3750
3751     state.inIteration = oldInIteration;
3752
3753     expectKeyword("while");
3754
3755     expect("(");
3756
3757     test = parseExpression();
3758
3759     expect(")");
3760
3761     if (match(";")) {
3762         lex();
3763     }
3764
3765     return astNodeFactory.createDoWhileStatement(test, body);
3766 }
3767
3768 function parseWhileStatement() {
3769     var test, body, oldInIteration;
3770
3771     expectKeyword("while");
3772
3773     expect("(");
3774
3775     test = parseExpression();
3776
3777     expect(")");
3778
3779     oldInIteration = state.inIteration;
3780     state.inIteration = true;
3781
3782     body = parseStatement();
3783
3784     state.inIteration = oldInIteration;
3785
3786     return astNodeFactory.createWhileStatement(test, body);
3787 }
3788
3789 function parseForVariableDeclaration() {
3790     var token, declarations,
3791         marker = markerCreate();
3792
3793     token = lex();
3794     declarations = parseVariableDeclarationList();
3795
3796     return markerApply(marker, astNodeFactory.createVariableDeclaration(declarations, token.value));
3797 }
3798
3799 function parseForStatement(opts) {
3800     var init, test, update, left, right, body, operator, oldInIteration;
3801     var allowForOf = extra.ecmaFeatures.forOf,
3802         allowBlockBindings = extra.ecmaFeatures.blockBindings;
3803
3804     init = test = update = null;
3805
3806     expectKeyword("for");
3807
3808     expect("(");
3809
3810     if (match(";")) {
3811         lex();
3812     } else {
3813
3814         if (matchKeyword("var") ||
3815             (allowBlockBindings && (matchKeyword("let") || matchKeyword("const")))
3816         ) {
3817             state.allowIn = false;
3818             init = parseForVariableDeclaration();
3819             state.allowIn = true;
3820
3821             if (init.declarations.length === 1) {
3822                 if (matchKeyword("in") || (allowForOf && matchContextualKeyword("of"))) {
3823                     operator = lookahead;
3824
3825                     // TODO: is "var" check here really needed? wasn"t in 1.2.2
3826                     if (!((operator.value === "in" || init.kind !== "var") && init.declarations[0].init)) {
3827                         lex();
3828                         left = init;
3829                         right = parseExpression();
3830                         init = null;
3831                     }
3832                 }
3833             }
3834
3835         } else {
3836             state.allowIn = false;
3837             init = parseExpression();
3838             state.allowIn = true;
3839
3840             if (allowForOf && matchContextualKeyword("of")) {
3841                 operator = lex();
3842                 left = init;
3843                 right = parseExpression();
3844                 init = null;
3845             } else if (matchKeyword("in")) {
3846                 // LeftHandSideExpression
3847                 if (!isLeftHandSide(init)) {
3848                     throwErrorTolerant({}, Messages.InvalidLHSInForIn);
3849                 }
3850
3851                 operator = lex();
3852                 left = init;
3853                 right = parseExpression();
3854                 init = null;
3855             }
3856         }
3857
3858         if (typeof left === "undefined") {
3859             expect(";");
3860         }
3861     }
3862
3863     if (typeof left === "undefined") {
3864
3865         if (!match(";")) {
3866             test = parseExpression();
3867         }
3868         expect(";");
3869
3870         if (!match(")")) {
3871             update = parseExpression();
3872         }
3873     }
3874
3875     expect(")");
3876
3877     oldInIteration = state.inIteration;
3878     state.inIteration = true;
3879
3880     if (!(opts !== undefined && opts.ignoreBody)) {
3881         body = parseStatement();
3882     }
3883
3884     state.inIteration = oldInIteration;
3885
3886     if (typeof left === "undefined") {
3887         return astNodeFactory.createForStatement(init, test, update, body);
3888     }
3889
3890     if (extra.ecmaFeatures.forOf && operator.value === "of") {
3891         return astNodeFactory.createForOfStatement(left, right, body);
3892     }
3893
3894     return astNodeFactory.createForInStatement(left, right, body);
3895 }
3896
3897 // 12.7 The continue statement
3898
3899 function parseContinueStatement() {
3900     var label = null;
3901
3902     expectKeyword("continue");
3903
3904     // Optimize the most common form: "continue;".
3905     if (source.charCodeAt(index) === 0x3B) {
3906         lex();
3907
3908         if (!state.inIteration) {
3909             throwError({}, Messages.IllegalContinue);
3910         }
3911
3912         return astNodeFactory.createContinueStatement(null);
3913     }
3914
3915     if (peekLineTerminator()) {
3916         if (!state.inIteration) {
3917             throwError({}, Messages.IllegalContinue);
3918         }
3919
3920         return astNodeFactory.createContinueStatement(null);
3921     }
3922
3923     if (lookahead.type === Token.Identifier) {
3924         label = parseVariableIdentifier();
3925
3926         if (!state.labelSet.has(label.name)) {
3927             throwError({}, Messages.UnknownLabel, label.name);
3928         }
3929     }
3930
3931     consumeSemicolon();
3932
3933     if (label === null && !state.inIteration) {
3934         throwError({}, Messages.IllegalContinue);
3935     }
3936
3937     return astNodeFactory.createContinueStatement(label);
3938 }
3939
3940 // 12.8 The break statement
3941
3942 function parseBreakStatement() {
3943     var label = null;
3944
3945     expectKeyword("break");
3946
3947     // Catch the very common case first: immediately a semicolon (U+003B).
3948     if (source.charCodeAt(index) === 0x3B) {
3949         lex();
3950
3951         if (!(state.inIteration || state.inSwitch)) {
3952             throwError({}, Messages.IllegalBreak);
3953         }
3954
3955         return astNodeFactory.createBreakStatement(null);
3956     }
3957
3958     if (peekLineTerminator()) {
3959         if (!(state.inIteration || state.inSwitch)) {
3960             throwError({}, Messages.IllegalBreak);
3961         }
3962
3963         return astNodeFactory.createBreakStatement(null);
3964     }
3965
3966     if (lookahead.type === Token.Identifier) {
3967         label = parseVariableIdentifier();
3968
3969         if (!state.labelSet.has(label.name)) {
3970             throwError({}, Messages.UnknownLabel, label.name);
3971         }
3972     }
3973
3974     consumeSemicolon();
3975
3976     if (label === null && !(state.inIteration || state.inSwitch)) {
3977         throwError({}, Messages.IllegalBreak);
3978     }
3979
3980     return astNodeFactory.createBreakStatement(label);
3981 }
3982
3983 // 12.9 The return statement
3984
3985 function parseReturnStatement() {
3986     var argument = null;
3987
3988     expectKeyword("return");
3989
3990     if (!state.inFunctionBody && !extra.ecmaFeatures.globalReturn) {
3991         throwErrorTolerant({}, Messages.IllegalReturn);
3992     }
3993
3994     // "return" followed by a space and an identifier is very common.
3995     if (source.charCodeAt(index) === 0x20) {
3996         if (syntax.isIdentifierStart(source.charCodeAt(index + 1))) {
3997             argument = parseExpression();
3998             consumeSemicolon();
3999             return astNodeFactory.createReturnStatement(argument);
4000         }
4001     }
4002
4003     if (peekLineTerminator()) {
4004         return astNodeFactory.createReturnStatement(null);
4005     }
4006
4007     if (!match(";")) {
4008         if (!match("}") && lookahead.type !== Token.EOF) {
4009             argument = parseExpression();
4010         }
4011     }
4012
4013     consumeSemicolon();
4014
4015     return astNodeFactory.createReturnStatement(argument);
4016 }
4017
4018 // 12.10 The with statement
4019
4020 function parseWithStatement() {
4021     var object, body;
4022
4023     if (strict) {
4024         // TODO(ikarienator): Should we update the test cases instead?
4025         skipComment();
4026         throwErrorTolerant({}, Messages.StrictModeWith);
4027     }
4028
4029     expectKeyword("with");
4030
4031     expect("(");
4032
4033     object = parseExpression();
4034
4035     expect(")");
4036
4037     body = parseStatement();
4038
4039     return astNodeFactory.createWithStatement(object, body);
4040 }
4041
4042 // 12.10 The swith statement
4043
4044 function parseSwitchCase() {
4045     var test, consequent = [], statement,
4046         marker = markerCreate();
4047
4048     if (matchKeyword("default")) {
4049         lex();
4050         test = null;
4051     } else {
4052         expectKeyword("case");
4053         test = parseExpression();
4054     }
4055     expect(":");
4056
4057     while (index < length) {
4058         if (match("}") || matchKeyword("default") || matchKeyword("case")) {
4059             break;
4060         }
4061         statement = parseSourceElement();
4062         consequent.push(statement);
4063     }
4064
4065     return markerApply(marker, astNodeFactory.createSwitchCase(test, consequent));
4066 }
4067
4068 function parseSwitchStatement() {
4069     var discriminant, cases, clause, oldInSwitch, defaultFound;
4070
4071     expectKeyword("switch");
4072
4073     expect("(");
4074
4075     discriminant = parseExpression();
4076
4077     expect(")");
4078
4079     expect("{");
4080
4081     cases = [];
4082
4083     if (match("}")) {
4084         lex();
4085         return astNodeFactory.createSwitchStatement(discriminant, cases);
4086     }
4087
4088     oldInSwitch = state.inSwitch;
4089     state.inSwitch = true;
4090     defaultFound = false;
4091
4092     while (index < length) {
4093         if (match("}")) {
4094             break;
4095         }
4096         clause = parseSwitchCase();
4097         if (clause.test === null) {
4098             if (defaultFound) {
4099                 throwError({}, Messages.MultipleDefaultsInSwitch);
4100             }
4101             defaultFound = true;
4102         }
4103         cases.push(clause);
4104     }
4105
4106     state.inSwitch = oldInSwitch;
4107
4108     expect("}");
4109
4110     return astNodeFactory.createSwitchStatement(discriminant, cases);
4111 }
4112
4113 // 12.13 The throw statement
4114
4115 function parseThrowStatement() {
4116     var argument;
4117
4118     expectKeyword("throw");
4119
4120     if (peekLineTerminator()) {
4121         throwError({}, Messages.NewlineAfterThrow);
4122     }
4123
4124     argument = parseExpression();
4125
4126     consumeSemicolon();
4127
4128     return astNodeFactory.createThrowStatement(argument);
4129 }
4130
4131 // 12.14 The try statement
4132
4133 function parseCatchClause() {
4134     var param, body,
4135         marker = markerCreate(),
4136         allowDestructuring = extra.ecmaFeatures.destructuring,
4137         options = {
4138             paramSet: new StringMap()
4139         };
4140
4141     expectKeyword("catch");
4142
4143     expect("(");
4144     if (match(")")) {
4145         throwUnexpected(lookahead);
4146     }
4147
4148     if (match("[")) {
4149         if (!allowDestructuring) {
4150             throwUnexpected(lookahead);
4151         }
4152         param = parseArrayInitialiser();
4153         reinterpretAsDestructuredParameter(options, param);
4154     } else if (match("{")) {
4155
4156         if (!allowDestructuring) {
4157             throwUnexpected(lookahead);
4158         }
4159         param = parseObjectInitialiser();
4160         reinterpretAsDestructuredParameter(options, param);
4161     } else {
4162         param = parseVariableIdentifier();
4163     }
4164
4165     // 12.14.1
4166     if (strict && param.name && syntax.isRestrictedWord(param.name)) {
4167         throwErrorTolerant({}, Messages.StrictCatchVariable);
4168     }
4169
4170     expect(")");
4171     body = parseBlock();
4172     return markerApply(marker, astNodeFactory.createCatchClause(param, body));
4173 }
4174
4175 function parseTryStatement() {
4176     var block, handler = null, finalizer = null;
4177
4178     expectKeyword("try");
4179
4180     block = parseBlock();
4181
4182     if (matchKeyword("catch")) {
4183         handler = parseCatchClause();
4184     }
4185
4186     if (matchKeyword("finally")) {
4187         lex();
4188         finalizer = parseBlock();
4189     }
4190
4191     if (!handler && !finalizer) {
4192         throwError({}, Messages.NoCatchOrFinally);
4193     }
4194
4195     return astNodeFactory.createTryStatement(block, handler, finalizer);
4196 }
4197
4198 // 12.15 The debugger statement
4199
4200 function parseDebuggerStatement() {
4201     expectKeyword("debugger");
4202
4203     consumeSemicolon();
4204
4205     return astNodeFactory.createDebuggerStatement();
4206 }
4207
4208 // 12 Statements
4209
4210 function parseStatement() {
4211     var type = lookahead.type,
4212         expr,
4213         labeledBody,
4214         marker;
4215
4216     if (type === Token.EOF) {
4217         throwUnexpected(lookahead);
4218     }
4219
4220     if (type === Token.Punctuator && lookahead.value === "{") {
4221         return parseBlock();
4222     }
4223
4224     marker = markerCreate();
4225
4226     if (type === Token.Punctuator) {
4227         switch (lookahead.value) {
4228             case ";":
4229                 return markerApply(marker, parseEmptyStatement());
4230             case "{":
4231                 return parseBlock();
4232             case "(":
4233                 return markerApply(marker, parseExpressionStatement());
4234             default:
4235                 break;
4236         }
4237     }
4238
4239     marker = markerCreate();
4240
4241     if (type === Token.Keyword) {
4242         switch (lookahead.value) {
4243             case "break":
4244                 return markerApply(marker, parseBreakStatement());
4245             case "continue":
4246                 return markerApply(marker, parseContinueStatement());
4247             case "debugger":
4248                 return markerApply(marker, parseDebuggerStatement());
4249             case "do":
4250                 return markerApply(marker, parseDoWhileStatement());
4251             case "for":
4252                 return markerApply(marker, parseForStatement());
4253             case "function":
4254                 return markerApply(marker, parseFunctionDeclaration());
4255             case "if":
4256                 return markerApply(marker, parseIfStatement());
4257             case "return":
4258                 return markerApply(marker, parseReturnStatement());
4259             case "switch":
4260                 return markerApply(marker, parseSwitchStatement());
4261             case "throw":
4262                 return markerApply(marker, parseThrowStatement());
4263             case "try":
4264                 return markerApply(marker, parseTryStatement());
4265             case "var":
4266                 return markerApply(marker, parseVariableStatement());
4267             case "while":
4268                 return markerApply(marker, parseWhileStatement());
4269             case "with":
4270                 return markerApply(marker, parseWithStatement());
4271             default:
4272                 break;
4273         }
4274     }
4275
4276     marker = markerCreate();
4277     expr = parseExpression();
4278
4279     // 12.12 Labelled Statements
4280     if ((expr.type === astNodeTypes.Identifier) && match(":")) {
4281         lex();
4282
4283         if (state.labelSet.has(expr.name)) {
4284             throwError({}, Messages.Redeclaration, "Label", expr.name);
4285         }
4286
4287         state.labelSet.set(expr.name, true);
4288         labeledBody = parseStatement();
4289         state.labelSet.delete(expr.name);
4290         return markerApply(marker, astNodeFactory.createLabeledStatement(expr, labeledBody));
4291     }
4292
4293     consumeSemicolon();
4294
4295     return markerApply(marker, astNodeFactory.createExpressionStatement(expr));
4296 }
4297
4298 // 13 Function Definition
4299
4300 // function parseConciseBody() {
4301 //     if (match("{")) {
4302 //         return parseFunctionSourceElements();
4303 //     }
4304 //     return parseAssignmentExpression();
4305 // }
4306
4307 function parseFunctionSourceElements() {
4308     var sourceElement, sourceElements = [], token, directive, firstRestricted,
4309         oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, oldParenthesisCount,
4310         marker = markerCreate();
4311
4312     expect("{");
4313
4314     while (index < length) {
4315         if (lookahead.type !== Token.StringLiteral) {
4316             break;
4317         }
4318         token = lookahead;
4319
4320         sourceElement = parseSourceElement();
4321         sourceElements.push(sourceElement);
4322         if (sourceElement.expression.type !== astNodeTypes.Literal) {
4323             // this is not directive
4324             break;
4325         }
4326         directive = source.slice(token.range[0] + 1, token.range[1] - 1);
4327         if (directive === "use strict") {
4328             strict = true;
4329
4330             if (firstRestricted) {
4331                 throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
4332             }
4333         } else {
4334             if (!firstRestricted && token.octal) {
4335                 firstRestricted = token;
4336             }
4337         }
4338     }
4339
4340     oldLabelSet = state.labelSet;
4341     oldInIteration = state.inIteration;
4342     oldInSwitch = state.inSwitch;
4343     oldInFunctionBody = state.inFunctionBody;
4344     oldParenthesisCount = state.parenthesizedCount;
4345
4346     state.labelSet = new StringMap();
4347     state.inIteration = false;
4348     state.inSwitch = false;
4349     state.inFunctionBody = true;
4350
4351     while (index < length) {
4352
4353         if (match("}")) {
4354             break;
4355         }
4356
4357         sourceElement = parseSourceElement();
4358
4359         if (typeof sourceElement === "undefined") {
4360             break;
4361         }
4362
4363         sourceElements.push(sourceElement);
4364     }
4365
4366     expect("}");
4367
4368     state.labelSet = oldLabelSet;
4369     state.inIteration = oldInIteration;
4370     state.inSwitch = oldInSwitch;
4371     state.inFunctionBody = oldInFunctionBody;
4372     state.parenthesizedCount = oldParenthesisCount;
4373
4374     return markerApply(marker, astNodeFactory.createBlockStatement(sourceElements));
4375 }
4376
4377 function validateParam(options, param, name) {
4378
4379     if (strict) {
4380         if (syntax.isRestrictedWord(name)) {
4381             options.stricted = param;
4382             options.message = Messages.StrictParamName;
4383         }
4384
4385         if (options.paramSet.has(name)) {
4386             options.stricted = param;
4387             options.message = Messages.StrictParamDupe;
4388         }
4389     } else if (!options.firstRestricted) {
4390         if (syntax.isRestrictedWord(name)) {
4391             options.firstRestricted = param;
4392             options.message = Messages.StrictParamName;
4393         } else if (syntax.isStrictModeReservedWord(name)) {
4394             options.firstRestricted = param;
4395             options.message = Messages.StrictReservedWord;
4396         } else if (options.paramSet.has(name)) {
4397             options.firstRestricted = param;
4398             options.message = Messages.StrictParamDupe;
4399         }
4400     }
4401     options.paramSet.set(name, true);
4402 }
4403
4404 function parseParam(options) {
4405     var token, rest, param, def,
4406         allowRestParams = extra.ecmaFeatures.restParams,
4407         allowDestructuring = extra.ecmaFeatures.destructuring,
4408         allowDefaultParams = extra.ecmaFeatures.defaultParams;
4409
4410
4411     token = lookahead;
4412     if (token.value === "...") {
4413         if (!allowRestParams) {
4414             throwUnexpected(lookahead);
4415         }
4416         token = lex();
4417         rest = true;
4418     }
4419
4420     if (match("[")) {
4421         if (!allowDestructuring) {
4422             throwUnexpected(lookahead);
4423         }
4424         param = parseArrayInitialiser();
4425         reinterpretAsDestructuredParameter(options, param);
4426     } else if (match("{")) {
4427         if (rest) {
4428             throwError({}, Messages.ObjectPatternAsRestParameter);
4429         }
4430         if (!allowDestructuring) {
4431             throwUnexpected(lookahead);
4432         }
4433         param = parseObjectInitialiser();
4434         reinterpretAsDestructuredParameter(options, param);
4435     } else {
4436         param = parseVariableIdentifier();
4437         validateParam(options, token, token.value);
4438     }
4439
4440     if (match("=")) {
4441         if (rest) {
4442             throwErrorTolerant(lookahead, Messages.DefaultRestParameter);
4443         }
4444         if (allowDefaultParams || allowDestructuring) {
4445             lex();
4446             def = parseAssignmentExpression();
4447             ++options.defaultCount;
4448         } else {
4449             throwUnexpected(lookahead);
4450         }
4451     }
4452
4453     if (rest) {
4454         if (!match(")")) {
4455             throwError({}, Messages.ParameterAfterRestParameter);
4456         }
4457         options.rest = param;
4458         return false;
4459     }
4460
4461     options.params.push(param);
4462     options.defaults.push(def ? def : null); // TODO: determine if null or undefined (see: #55)
4463
4464     return !match(")");
4465 }
4466
4467
4468 function parseParams(firstRestricted) {
4469     var options;
4470
4471     options = {
4472         params: [],
4473         defaultCount: 0,
4474         defaults: [],
4475         rest: null,
4476         firstRestricted: firstRestricted
4477     };
4478
4479     expect("(");
4480
4481     if (!match(")")) {
4482         options.paramSet = new StringMap();
4483         while (index < length) {
4484             if (!parseParam(options)) {
4485                 break;
4486             }
4487             expect(",");
4488         }
4489     }
4490
4491     expect(")");
4492
4493     if (options.defaultCount === 0) {
4494         options.defaults = [];
4495     }
4496
4497     return {
4498         params: options.params,
4499         defaults: options.defaults,
4500         rest: options.rest,
4501         stricted: options.stricted,
4502         firstRestricted: options.firstRestricted,
4503         message: options.message
4504     };
4505 }
4506
4507 function parseFunctionDeclaration(identifierIsOptional) {
4508         var id = null, body, token, tmp, firstRestricted, message, previousStrict, previousYieldAllowed, generator,
4509             marker = markerCreate(),
4510             allowGenerators = extra.ecmaFeatures.generators;
4511
4512         expectKeyword("function");
4513
4514         generator = false;
4515         if (allowGenerators && match("*")) {
4516             lex();
4517             generator = true;
4518         }
4519
4520         if (!identifierIsOptional || !match("(")) {
4521
4522             token = lookahead;
4523
4524             id = parseVariableIdentifier();
4525
4526             if (strict) {
4527                 if (syntax.isRestrictedWord(token.value)) {
4528                     throwErrorTolerant(token, Messages.StrictFunctionName);
4529                 }
4530             } else {
4531                 if (syntax.isRestrictedWord(token.value)) {
4532                     firstRestricted = token;
4533                     message = Messages.StrictFunctionName;
4534                 } else if (syntax.isStrictModeReservedWord(token.value)) {
4535                     firstRestricted = token;
4536                     message = Messages.StrictReservedWord;
4537                 }
4538             }
4539         }
4540
4541         tmp = parseParams(firstRestricted);
4542         firstRestricted = tmp.firstRestricted;
4543         if (tmp.message) {
4544             message = tmp.message;
4545         }
4546
4547         previousStrict = strict;
4548         previousYieldAllowed = state.yieldAllowed;
4549         state.yieldAllowed = generator;
4550
4551         body = parseFunctionSourceElements();
4552
4553         if (strict && firstRestricted) {
4554             throwError(firstRestricted, message);
4555         }
4556         if (strict && tmp.stricted) {
4557             throwErrorTolerant(tmp.stricted, message);
4558         }
4559         strict = previousStrict;
4560         state.yieldAllowed = previousYieldAllowed;
4561
4562         return markerApply(
4563             marker,
4564             astNodeFactory.createFunctionDeclaration(
4565                 id,
4566                 tmp.params,
4567                 tmp.defaults,
4568                 body,
4569                 tmp.rest,
4570                 generator,
4571                 false
4572             )
4573         );
4574     }
4575
4576 function parseFunctionExpression() {
4577     var token, id = null, firstRestricted, message, tmp, body, previousStrict, previousYieldAllowed, generator,
4578         marker = markerCreate(),
4579         allowGenerators = extra.ecmaFeatures.generators;
4580
4581     expectKeyword("function");
4582
4583     generator = false;
4584
4585     if (allowGenerators && match("*")) {
4586         lex();
4587         generator = true;
4588     }
4589
4590     if (!match("(")) {
4591         token = lookahead;
4592         id = parseVariableIdentifier();
4593         if (strict) {
4594             if (syntax.isRestrictedWord(token.value)) {
4595                 throwErrorTolerant(token, Messages.StrictFunctionName);
4596             }
4597         } else {
4598             if (syntax.isRestrictedWord(token.value)) {
4599                 firstRestricted = token;
4600                 message = Messages.StrictFunctionName;
4601             } else if (syntax.isStrictModeReservedWord(token.value)) {
4602                 firstRestricted = token;
4603                 message = Messages.StrictReservedWord;
4604             }
4605         }
4606     }
4607
4608     tmp = parseParams(firstRestricted);
4609     firstRestricted = tmp.firstRestricted;
4610     if (tmp.message) {
4611         message = tmp.message;
4612     }
4613
4614     previousStrict = strict;
4615     previousYieldAllowed = state.yieldAllowed;
4616     state.yieldAllowed = generator;
4617
4618     body = parseFunctionSourceElements();
4619
4620     if (strict && firstRestricted) {
4621         throwError(firstRestricted, message);
4622     }
4623     if (strict && tmp.stricted) {
4624         throwErrorTolerant(tmp.stricted, message);
4625     }
4626     strict = previousStrict;
4627     state.yieldAllowed = previousYieldAllowed;
4628
4629     return markerApply(
4630         marker,
4631         astNodeFactory.createFunctionExpression(
4632             id,
4633             tmp.params,
4634             tmp.defaults,
4635             body,
4636             tmp.rest,
4637             generator,
4638             false
4639         )
4640     );
4641 }
4642
4643 function parseYieldExpression() {
4644     var yieldToken, delegateFlag, expr, marker = markerCreate();
4645
4646     yieldToken = lex();
4647     assert(yieldToken.value === "yield", "Called parseYieldExpression with non-yield lookahead.");
4648
4649     if (!state.yieldAllowed) {
4650         throwErrorTolerant({}, Messages.IllegalYield);
4651     }
4652
4653     delegateFlag = false;
4654     if (match("*")) {
4655         lex();
4656         delegateFlag = true;
4657     }
4658
4659     expr = parseAssignmentExpression();
4660
4661     return markerApply(marker, astNodeFactory.createYieldExpression(expr, delegateFlag));
4662 }
4663
4664 // Modules grammar from:
4665 // people.mozilla.org/~jorendorff/es6-draft.html
4666
4667 function parseModuleSpecifier() {
4668     var marker = markerCreate(),
4669         specifier;
4670
4671     if (lookahead.type !== Token.StringLiteral) {
4672         throwError({}, Messages.InvalidModuleSpecifier);
4673     }
4674     specifier = astNodeFactory.createLiteralFromSource(lex(), source);
4675     return markerApply(marker, specifier);
4676 }
4677
4678 function parseExportSpecifier() {
4679     var exported, local, marker = markerCreate();
4680     if (matchKeyword("default")) {
4681         lex();
4682         local = markerApply(marker, astNodeFactory.createIdentifier("default"));
4683         // export {default} from "something";
4684     } else {
4685         local = parseVariableIdentifier();
4686     }
4687     if (matchContextualKeyword("as")) {
4688         lex();
4689         exported = parseNonComputedProperty();
4690     }
4691     return markerApply(marker, astNodeFactory.createExportSpecifier(local, exported));
4692 }
4693
4694 function parseExportNamedDeclaration() {
4695     var declaration = null,
4696         isExportFromIdentifier,
4697         src = null, specifiers = [],
4698         marker = markerCreate();
4699
4700     expectKeyword("export");
4701
4702     // non-default export
4703     if (lookahead.type === Token.Keyword) {
4704         // covers:
4705         // export var f = 1;
4706         switch (lookahead.value) {
4707             case "let":
4708             case "const":
4709             case "var":
4710             case "class":
4711             case "function":
4712                 declaration = parseSourceElement();
4713                 return markerApply(marker, astNodeFactory.createExportNamedDeclaration(declaration, specifiers, null));
4714             default:
4715                 break;
4716         }
4717     }
4718
4719     expect("{");
4720     if (!match("}")) {
4721         do {
4722             isExportFromIdentifier = isExportFromIdentifier || matchKeyword("default");
4723             specifiers.push(parseExportSpecifier());
4724         } while (match(",") && lex());
4725     }
4726     expect("}");
4727
4728     if (matchContextualKeyword("from")) {
4729         // covering:
4730         // export {default} from "foo";
4731         // export {foo} from "foo";
4732         lex();
4733         src = parseModuleSpecifier();
4734         consumeSemicolon();
4735     } else if (isExportFromIdentifier) {
4736         // covering:
4737         // export {default}; // missing fromClause
4738         throwError({}, lookahead.value ?
4739                 Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value);
4740     } else {
4741         // cover
4742         // export {foo};
4743         consumeSemicolon();
4744     }
4745     return markerApply(marker, astNodeFactory.createExportNamedDeclaration(declaration, specifiers, src));
4746 }
4747
4748 function parseExportDefaultDeclaration() {
4749     var declaration = null,
4750         expression = null,
4751         possibleIdentifierToken,
4752         allowClasses = extra.ecmaFeatures.classes,
4753         marker = markerCreate();
4754
4755     // covers:
4756     // export default ...
4757     expectKeyword("export");
4758     expectKeyword("default");
4759
4760     if (matchKeyword("function") || matchKeyword("class")) {
4761         possibleIdentifierToken = lookahead2();
4762         if (possibleIdentifierToken.type === Token.Identifier) {
4763             // covers:
4764             // export default function foo () {}
4765             // export default class foo {}
4766             declaration = parseSourceElement();
4767             return markerApply(marker, astNodeFactory.createExportDefaultDeclaration(declaration));
4768         }
4769         // covers:
4770         // export default function () {}
4771         // export default class {}
4772         if (lookahead.value === "function") {
4773             declaration = parseFunctionDeclaration(true);
4774             return markerApply(marker, astNodeFactory.createExportDefaultDeclaration(declaration));
4775         } else if (allowClasses && lookahead.value === "class") {
4776             declaration = parseClassDeclaration(true);
4777             return markerApply(marker, astNodeFactory.createExportDefaultDeclaration(declaration));
4778         }
4779     }
4780
4781     if (matchContextualKeyword("from")) {
4782         throwError({}, Messages.UnexpectedToken, lookahead.value);
4783     }
4784
4785     // covers:
4786     // export default {};
4787     // export default [];
4788     // export default (1 + 2);
4789     if (match("{")) {
4790         expression = parseObjectInitialiser();
4791     } else if (match("[")) {
4792         expression = parseArrayInitialiser();
4793     } else {
4794         expression = parseAssignmentExpression();
4795     }
4796     consumeSemicolon();
4797     return markerApply(marker, astNodeFactory.createExportDefaultDeclaration(expression));
4798 }
4799
4800
4801 function parseExportAllDeclaration() {
4802     var src,
4803         marker = markerCreate();
4804
4805     // covers:
4806     // export * from "foo";
4807     expectKeyword("export");
4808     expect("*");
4809     if (!matchContextualKeyword("from")) {
4810         throwError({}, lookahead.value ?
4811                 Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value);
4812     }
4813     lex();
4814     src = parseModuleSpecifier();
4815     consumeSemicolon();
4816
4817     return markerApply(marker, astNodeFactory.createExportAllDeclaration(src));
4818 }
4819
4820 function parseExportDeclaration() {
4821     if (state.inFunctionBody) {
4822         throwError({}, Messages.IllegalExportDeclaration);
4823     }
4824     var declarationType = lookahead2().value;
4825     if (declarationType === "default") {
4826         return parseExportDefaultDeclaration();
4827     } else if (declarationType === "*") {
4828         return parseExportAllDeclaration();
4829     } else {
4830         return parseExportNamedDeclaration();
4831     }
4832 }
4833
4834 function parseImportSpecifier() {
4835     // import {<foo as bar>} ...;
4836     var local, imported, marker = markerCreate();
4837
4838     imported = parseNonComputedProperty();
4839     if (matchContextualKeyword("as")) {
4840         lex();
4841         local = parseVariableIdentifier();
4842     }
4843
4844     return markerApply(marker, astNodeFactory.createImportSpecifier(local, imported));
4845 }
4846
4847 function parseNamedImports() {
4848     var specifiers = [];
4849     // {foo, bar as bas}
4850     expect("{");
4851     if (!match("}")) {
4852         do {
4853             specifiers.push(parseImportSpecifier());
4854         } while (match(",") && lex());
4855     }
4856     expect("}");
4857     return specifiers;
4858 }
4859
4860 function parseImportDefaultSpecifier() {
4861     // import <foo> ...;
4862     var local, marker = markerCreate();
4863
4864     local = parseNonComputedProperty();
4865
4866     return markerApply(marker, astNodeFactory.createImportDefaultSpecifier(local));
4867 }
4868
4869 function parseImportNamespaceSpecifier() {
4870     // import <* as foo> ...;
4871     var local, marker = markerCreate();
4872
4873     expect("*");
4874     if (!matchContextualKeyword("as")) {
4875         throwError({}, Messages.NoAsAfterImportNamespace);
4876     }
4877     lex();
4878     local = parseNonComputedProperty();
4879
4880     return markerApply(marker, astNodeFactory.createImportNamespaceSpecifier(local));
4881 }
4882
4883 function parseImportDeclaration() {
4884     var specifiers, src, marker = markerCreate();
4885
4886     if (state.inFunctionBody) {
4887         throwError({}, Messages.IllegalImportDeclaration);
4888     }
4889
4890     expectKeyword("import");
4891     specifiers = [];
4892
4893     if (lookahead.type === Token.StringLiteral) {
4894         // covers:
4895         // import "foo";
4896         src = parseModuleSpecifier();
4897         consumeSemicolon();
4898         return markerApply(marker, astNodeFactory.createImportDeclaration(specifiers, src));
4899     }
4900
4901     if (!matchKeyword("default") && isIdentifierName(lookahead)) {
4902         // covers:
4903         // import foo
4904         // import foo, ...
4905         specifiers.push(parseImportDefaultSpecifier());
4906         if (match(",")) {
4907             lex();
4908         }
4909     }
4910     if (match("*")) {
4911         // covers:
4912         // import foo, * as foo
4913         // import * as foo
4914         specifiers.push(parseImportNamespaceSpecifier());
4915     } else if (match("{")) {
4916         // covers:
4917         // import foo, {bar}
4918         // import {bar}
4919         specifiers = specifiers.concat(parseNamedImports());
4920     }
4921
4922     if (!matchContextualKeyword("from")) {
4923         throwError({}, lookahead.value ?
4924                 Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value);
4925     }
4926     lex();
4927     src = parseModuleSpecifier();
4928     consumeSemicolon();
4929
4930     return markerApply(marker, astNodeFactory.createImportDeclaration(specifiers, src));
4931 }
4932
4933 // 14 Functions and classes
4934
4935 // 14.1 Functions is defined above (13 in ES5)
4936 // 14.2 Arrow Functions Definitions is defined in (7.3 assignments)
4937
4938 // 14.3 Method Definitions
4939 // 14.3.7
4940
4941 // 14.5 Class Definitions
4942
4943 function parseClassBody() {
4944     var hasConstructor = false, generator = false,
4945         allowGenerators = extra.ecmaFeatures.generators,
4946         token, isStatic, body = [], method, computed, key;
4947
4948     var existingProps = {},
4949         topMarker = markerCreate(),
4950         marker;
4951
4952     existingProps.static = new StringMap();
4953     existingProps.prototype = new StringMap();
4954
4955     expect("{");
4956
4957     while (!match("}")) {
4958
4959         // extra semicolons are fine
4960         if (match(";")) {
4961             lex();
4962             continue;
4963         }
4964
4965         token = lookahead;
4966         isStatic = false;
4967         generator = match("*");
4968         computed = match("[");
4969         marker = markerCreate();
4970
4971         if (generator) {
4972             if (!allowGenerators) {
4973                 throwUnexpected(lookahead);
4974             }
4975             lex();
4976         }
4977
4978         key = parseObjectPropertyKey();
4979
4980         // static generator methods
4981         if (key.name === "static" && match("*")) {
4982             if (!allowGenerators) {
4983                 throwUnexpected(lookahead);
4984             }
4985             generator = true;
4986             lex();
4987         }
4988
4989         if (key.name === "static" && lookaheadPropertyName()) {
4990             token = lookahead;
4991             isStatic = true;
4992             computed = match("[");
4993             key = parseObjectPropertyKey();
4994         }
4995
4996         if (generator) {
4997             method = parseGeneratorProperty(key, marker);
4998         } else {
4999             method = tryParseMethodDefinition(token, key, computed, marker, generator);
5000         }
5001
5002         if (method) {
5003             method.static = isStatic;
5004             if (method.kind === "init") {
5005                 method.kind = "method";
5006             }
5007
5008             if (!isStatic) {
5009                 if (!method.computed && (method.key.name || method.key.value.toString()) === "constructor") {
5010                     if (method.kind !== "method" || !method.method || method.value.generator) {
5011                         throwUnexpected(token, Messages.ConstructorSpecialMethod);
5012                     }
5013                     if (hasConstructor) {
5014                         throwUnexpected(token, Messages.DuplicateConstructor);
5015                     } else {
5016                         hasConstructor = true;
5017                     }
5018                     method.kind = "constructor";
5019                 }
5020             } else {
5021                 if (!method.computed && (method.key.name || method.key.value.toString()) === "prototype") {
5022                     throwUnexpected(token, Messages.StaticPrototype);
5023                 }
5024             }
5025             method.type = astNodeTypes.MethodDefinition;
5026             delete method.method;
5027             delete method.shorthand;
5028             body.push(method);
5029         } else {
5030             throwUnexpected(lookahead);
5031         }
5032     }
5033
5034     lex();
5035     return markerApply(topMarker, astNodeFactory.createClassBody(body));
5036 }
5037
5038 function parseClassExpression() {
5039     var id = null, superClass = null, marker = markerCreate(),
5040         previousStrict = strict, classBody;
5041
5042     // classes run in strict mode
5043     strict = true;
5044
5045     expectKeyword("class");
5046
5047     if (lookahead.type === Token.Identifier) {
5048         id = parseVariableIdentifier();
5049     }
5050
5051     if (matchKeyword("extends")) {
5052         lex();
5053         superClass = parseLeftHandSideExpressionAllowCall();
5054     }
5055
5056     classBody = parseClassBody();
5057     strict = previousStrict;
5058
5059     return markerApply(marker, astNodeFactory.createClassExpression(id, superClass, classBody));
5060 }
5061
5062 function parseClassDeclaration(identifierIsOptional) {
5063     var id = null, superClass = null, marker = markerCreate(),
5064         previousStrict = strict, classBody;
5065
5066     // classes run in strict mode
5067     strict = true;
5068
5069     expectKeyword("class");
5070
5071     if (!identifierIsOptional || lookahead.type === Token.Identifier) {
5072         id = parseVariableIdentifier();
5073     }
5074
5075     if (matchKeyword("extends")) {
5076         lex();
5077         superClass = parseLeftHandSideExpressionAllowCall();
5078     }
5079
5080     classBody = parseClassBody();
5081     strict = previousStrict;
5082
5083     return markerApply(marker, astNodeFactory.createClassDeclaration(id, superClass, classBody));
5084 }
5085
5086 // 15 Program
5087
5088 function parseSourceElement() {
5089
5090     var allowClasses = extra.ecmaFeatures.classes,
5091         allowModules = extra.ecmaFeatures.modules,
5092         allowBlockBindings = extra.ecmaFeatures.blockBindings;
5093
5094     if (lookahead.type === Token.Keyword) {
5095         switch (lookahead.value) {
5096             case "export":
5097                 if (!allowModules) {
5098                     throwErrorTolerant({}, Messages.IllegalExportDeclaration);
5099                 }
5100                 return parseExportDeclaration();
5101             case "import":
5102                 if (!allowModules) {
5103                     throwErrorTolerant({}, Messages.IllegalImportDeclaration);
5104                 }
5105                 return parseImportDeclaration();
5106             case "function":
5107                 return parseFunctionDeclaration();
5108             case "class":
5109                 if (allowClasses) {
5110                     return parseClassDeclaration();
5111                 }
5112                 break;
5113             case "const":
5114             case "let":
5115                 if (allowBlockBindings) {
5116                     return parseConstLetDeclaration(lookahead.value);
5117                 }
5118                 /* falls through */
5119             default:
5120                 return parseStatement();
5121         }
5122     }
5123
5124     if (lookahead.type !== Token.EOF) {
5125         return parseStatement();
5126     }
5127 }
5128
5129 function parseSourceElements() {
5130     var sourceElement, sourceElements = [], token, directive, firstRestricted;
5131
5132     while (index < length) {
5133         token = lookahead;
5134         if (token.type !== Token.StringLiteral) {
5135             break;
5136         }
5137
5138         sourceElement = parseSourceElement();
5139         sourceElements.push(sourceElement);
5140         if (sourceElement.expression.type !== astNodeTypes.Literal) {
5141             // this is not directive
5142             break;
5143         }
5144         directive = source.slice(token.range[0] + 1, token.range[1] - 1);
5145         if (directive === "use strict") {
5146             strict = true;
5147             if (firstRestricted) {
5148                 throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
5149             }
5150         } else {
5151             if (!firstRestricted && token.octal) {
5152                 firstRestricted = token;
5153             }
5154         }
5155     }
5156
5157     while (index < length) {
5158         sourceElement = parseSourceElement();
5159         /* istanbul ignore if */
5160         if (typeof sourceElement === "undefined") {
5161             break;
5162         }
5163         sourceElements.push(sourceElement);
5164     }
5165     return sourceElements;
5166 }
5167
5168 function parseProgram() {
5169     var body,
5170         marker,
5171         isModule = !!extra.ecmaFeatures.modules;
5172
5173     skipComment();
5174     peek();
5175     marker = markerCreate();
5176     strict = isModule;
5177
5178     body = parseSourceElements();
5179     return markerApply(marker, astNodeFactory.createProgram(body, isModule ? "module" : "script"));
5180 }
5181
5182 function filterTokenLocation() {
5183     var i, entry, token, tokens = [];
5184
5185     for (i = 0; i < extra.tokens.length; ++i) {
5186         entry = extra.tokens[i];
5187         token = {
5188             type: entry.type,
5189             value: entry.value
5190         };
5191         if (entry.regex) {
5192             token.regex = {
5193                 pattern: entry.regex.pattern,
5194                 flags: entry.regex.flags
5195             };
5196         }
5197         if (extra.range) {
5198             token.range = entry.range;
5199         }
5200         if (extra.loc) {
5201             token.loc = entry.loc;
5202         }
5203         tokens.push(token);
5204     }
5205
5206     extra.tokens = tokens;
5207 }
5208
5209 //------------------------------------------------------------------------------
5210 // Tokenizer
5211 //------------------------------------------------------------------------------
5212
5213 function tokenize(code, options) {
5214     var toString,
5215         tokens;
5216
5217     toString = String;
5218     if (typeof code !== "string" && !(code instanceof String)) {
5219         code = toString(code);
5220     }
5221
5222     source = code;
5223     index = 0;
5224     lineNumber = (source.length > 0) ? 1 : 0;
5225     lineStart = 0;
5226     length = source.length;
5227     lookahead = null;
5228     state = {
5229         allowIn: true,
5230         labelSet: {},
5231         parenthesisCount: 0,
5232         inFunctionBody: false,
5233         inIteration: false,
5234         inSwitch: false,
5235         lastCommentStart: -1,
5236         yieldAllowed: false,
5237         curlyStack: [],
5238         curlyLastIndex: 0,
5239         inJSXSpreadAttribute: false,
5240         inJSXChild: false,
5241         inJSXTag: false
5242     };
5243
5244     extra = {
5245         ecmaFeatures: defaultFeatures
5246     };
5247
5248     // Options matching.
5249     options = options || {};
5250
5251     // Of course we collect tokens here.
5252     options.tokens = true;
5253     extra.tokens = [];
5254     extra.tokenize = true;
5255
5256     // The following two fields are necessary to compute the Regex tokens.
5257     extra.openParenToken = -1;
5258     extra.openCurlyToken = -1;
5259
5260     extra.range = (typeof options.range === "boolean") && options.range;
5261     extra.loc = (typeof options.loc === "boolean") && options.loc;
5262
5263     if (typeof options.comment === "boolean" && options.comment) {
5264         extra.comments = [];
5265     }
5266     if (typeof options.tolerant === "boolean" && options.tolerant) {
5267         extra.errors = [];
5268     }
5269
5270     // apply parsing flags
5271     if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") {
5272         extra.ecmaFeatures = options.ecmaFeatures;
5273     }
5274
5275     try {
5276         peek();
5277         if (lookahead.type === Token.EOF) {
5278             return extra.tokens;
5279         }
5280
5281         lex();
5282         while (lookahead.type !== Token.EOF) {
5283             try {
5284                 lex();
5285             } catch (lexError) {
5286                 if (extra.errors) {
5287                     extra.errors.push(lexError);
5288                     // We have to break on the first error
5289                     // to avoid infinite loops.
5290                     break;
5291                 } else {
5292                     throw lexError;
5293                 }
5294             }
5295         }
5296
5297         filterTokenLocation();
5298         tokens = extra.tokens;
5299
5300         if (typeof extra.comments !== "undefined") {
5301             tokens.comments = extra.comments;
5302         }
5303         if (typeof extra.errors !== "undefined") {
5304             tokens.errors = extra.errors;
5305         }
5306     } catch (e) {
5307         throw e;
5308     } finally {
5309         extra = {};
5310     }
5311     return tokens;
5312 }
5313
5314 //------------------------------------------------------------------------------
5315 // Parser
5316 //------------------------------------------------------------------------------
5317
5318 function parse(code, options) {
5319     var program, toString;
5320
5321     toString = String;
5322     if (typeof code !== "string" && !(code instanceof String)) {
5323         code = toString(code);
5324     }
5325
5326     source = code;
5327     index = 0;
5328     lineNumber = (source.length > 0) ? 1 : 0;
5329     lineStart = 0;
5330     length = source.length;
5331     lookahead = null;
5332     state = {
5333         allowIn: true,
5334         labelSet: new StringMap(),
5335         parenthesisCount: 0,
5336         inFunctionBody: false,
5337         inIteration: false,
5338         inSwitch: false,
5339         lastCommentStart: -1,
5340         yieldAllowed: false,
5341         curlyStack: [],
5342         curlyLastIndex: 0,
5343         inJSXSpreadAttribute: false,
5344         inJSXChild: false,
5345         inJSXTag: false
5346     };
5347
5348     extra = {
5349         ecmaFeatures: Object.create(defaultFeatures)
5350     };
5351
5352     // for template strings
5353     state.curlyStack = [];
5354
5355     if (typeof options !== "undefined") {
5356         extra.range = (typeof options.range === "boolean") && options.range;
5357         extra.loc = (typeof options.loc === "boolean") && options.loc;
5358         extra.attachComment = (typeof options.attachComment === "boolean") && options.attachComment;
5359
5360         if (extra.loc && options.source !== null && options.source !== undefined) {
5361             extra.source = toString(options.source);
5362         }
5363
5364         if (typeof options.tokens === "boolean" && options.tokens) {
5365             extra.tokens = [];
5366         }
5367         if (typeof options.comment === "boolean" && options.comment) {
5368             extra.comments = [];
5369         }
5370         if (typeof options.tolerant === "boolean" && options.tolerant) {
5371             extra.errors = [];
5372         }
5373         if (extra.attachComment) {
5374             extra.range = true;
5375             extra.comments = [];
5376             commentAttachment.reset();
5377         }
5378
5379         if (options.sourceType === "module") {
5380             extra.ecmaFeatures = {
5381                 arrowFunctions: true,
5382                 blockBindings: true,
5383                 regexUFlag: true,
5384                 regexYFlag: true,
5385                 templateStrings: true,
5386                 binaryLiterals: true,
5387                 octalLiterals: true,
5388                 unicodeCodePointEscapes: true,
5389                 superInFunctions: true,
5390                 defaultParams: true,
5391                 restParams: true,
5392                 forOf: true,
5393                 objectLiteralComputedProperties: true,
5394                 objectLiteralShorthandMethods: true,
5395                 objectLiteralShorthandProperties: true,
5396                 objectLiteralDuplicateProperties: true,
5397                 generators: true,
5398                 destructuring: true,
5399                 classes: true,
5400                 modules: true
5401             };
5402         }
5403
5404         // apply parsing flags after sourceType to allow overriding
5405         if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") {
5406
5407             // if it's a module, augment the ecmaFeatures
5408             if (options.sourceType === "module") {
5409                 Object.keys(options.ecmaFeatures).forEach(function(key) {
5410                     extra.ecmaFeatures[key] = options.ecmaFeatures[key];
5411                 });
5412             } else {
5413                 extra.ecmaFeatures = options.ecmaFeatures;
5414             }
5415         }
5416
5417     }
5418
5419     try {
5420         program = parseProgram();
5421         if (typeof extra.comments !== "undefined") {
5422             program.comments = extra.comments;
5423         }
5424         if (typeof extra.tokens !== "undefined") {
5425             filterTokenLocation();
5426             program.tokens = extra.tokens;
5427         }
5428         if (typeof extra.errors !== "undefined") {
5429             program.errors = extra.errors;
5430         }
5431     } catch (e) {
5432         throw e;
5433     } finally {
5434         extra = {};
5435     }
5436
5437     return program;
5438 }
5439
5440 //------------------------------------------------------------------------------
5441 // Public
5442 //------------------------------------------------------------------------------
5443
5444 exports.version = require("./package.json").version;
5445
5446 exports.tokenize = tokenize;
5447
5448 exports.parse = parse;
5449
5450 // Deep copy.
5451 /* istanbul ignore next */
5452 exports.Syntax = (function () {
5453     var name, types = {};
5454
5455     if (typeof Object.create === "function") {
5456         types = Object.create(null);
5457     }
5458
5459     for (name in astNodeTypes) {
5460         if (astNodeTypes.hasOwnProperty(name)) {
5461             types[name] = astNodeTypes[name];
5462         }
5463     }
5464
5465     if (typeof Object.freeze === "function") {
5466         Object.freeze(types);
5467     }
5468
5469     return types;
5470 }());