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>
14 Redistribution and use in source and binary forms, with or without
15 modification, are permitted provided that the following conditions are met:
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.
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.
34 /*eslint no-undefined:0, no-use-before-define: 0*/
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");
48 var Token = tokenInfo.Token,
49 TokenName = tokenInfo.TokenName,
50 FnExprTokens = tokenInfo.FnExprTokens,
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.
75 function assert(condition, message) {
76 /* istanbul ignore if */
78 throw new Error("ASSERT: " + message);
84 function addComment(type, value, start, end, loc) {
87 assert(typeof start === "number", "Comment must have valid position");
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
93 if (state.lastCommentStart >= start) {
96 state.lastCommentStart = start;
103 comment.range = [start, end];
108 extra.comments.push(comment);
110 if (extra.attachComment) {
111 commentAttachment.addComment(comment);
115 function skipSingleLineComment(offset) {
116 var start, loc, ch, comment;
118 start = index - offset;
122 column: index - lineStart - offset
126 while (index < length) {
127 ch = source.charCodeAt(index);
129 if (syntax.isLineTerminator(ch)) {
130 if (extra.comments) {
131 comment = source.slice(start + offset, index - 1);
134 column: index - lineStart - 1
136 addComment("Line", comment, start, index - 1, loc);
138 if (ch === 13 && source.charCodeAt(index) === 10) {
147 if (extra.comments) {
148 comment = source.slice(start + offset, index);
151 column: index - lineStart
153 addComment("Line", comment, start, index, loc);
157 function skipMultiLineComment() {
158 var start, loc, ch, comment;
160 if (extra.comments) {
165 column: index - lineStart - 2
170 while (index < length) {
171 ch = source.charCodeAt(index);
172 if (syntax.isLineTerminator(ch)) {
173 if (ch === 0x0D && source.charCodeAt(index + 1) === 0x0A) {
179 if (index >= length) {
180 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
182 } else if (ch === 0x2A) {
183 // Block comment ends with "*/".
184 if (source.charCodeAt(index + 1) === 0x2F) {
187 if (extra.comments) {
188 comment = source.slice(start + 2, index - 2);
191 column: index - lineStart
193 addComment("Block", comment, start, index, loc);
203 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
206 function skipComment() {
209 start = (index === 0);
210 while (index < length) {
211 ch = source.charCodeAt(index);
213 if (syntax.isWhiteSpace(ch)) {
215 } else if (syntax.isLineTerminator(ch)) {
217 if (ch === 0x0D && source.charCodeAt(index) === 0x0A) {
223 } else if (ch === 0x2F) { // U+002F is "/"
224 ch = source.charCodeAt(index + 1);
228 skipSingleLineComment(2);
230 } else if (ch === 0x2A) { // U+002A is "*"
233 skipMultiLineComment();
237 } else if (start && ch === 0x2D) { // U+002D is "-"
239 if ((source.charCodeAt(index + 1) === 0x2D) && (source.charCodeAt(index + 2) === 0x3E)) {
240 // "-->" is a single-line comment
242 skipSingleLineComment(3);
246 } else if (ch === 0x3C) { // U+003C is "<"
247 if (source.slice(index + 1, index + 4) === "!--") {
252 skipSingleLineComment(4);
262 function scanHexEscape(prefix) {
263 var i, len, ch, code = 0;
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());
274 return String.fromCharCode(code);
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.
283 function scanUnicodeCodePointEscape() {
284 var ch, code, cu1, cu2;
289 // At least one hex digit is required.
291 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
294 while (index < length) {
295 ch = source[index++];
296 if (!syntax.isHexDigit(ch)) {
299 code = code * 16 + "0123456789abcdef".indexOf(ch.toLowerCase());
302 if (code > 0x10FFFF || ch !== "}") {
303 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
307 if (code <= 0xFFFF) {
308 return String.fromCharCode(code);
310 cu1 = ((code - 0x10000) >> 10) + 0xD800;
311 cu2 = ((code - 0x10000) & 1023) + 0xDC00;
312 return String.fromCharCode(cu1, cu2);
315 function getEscapedIdentifier() {
318 ch = source.charCodeAt(index++);
319 id = String.fromCharCode(ch);
321 // "\u" (U+005C, U+0075) denotes an escaped character.
323 if (source.charCodeAt(index) !== 0x75) {
324 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
327 ch = scanHexEscape("u");
328 if (!ch || ch === "\\" || !syntax.isIdentifierStart(ch.charCodeAt(0))) {
329 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
334 while (index < length) {
335 ch = source.charCodeAt(index);
336 if (!syntax.isIdentifierPart(ch)) {
340 id += String.fromCharCode(ch);
342 // "\u" (U+005C, U+0075) denotes an escaped character.
344 id = id.substr(0, id.length - 1);
345 if (source.charCodeAt(index) !== 0x75) {
346 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
349 ch = scanHexEscape("u");
350 if (!ch || ch === "\\" || !syntax.isIdentifierPart(ch.charCodeAt(0))) {
351 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
360 function getIdentifier() {
364 while (index < length) {
365 ch = source.charCodeAt(index);
367 // Blackslash (U+005C) marks Unicode escape sequence.
369 return getEscapedIdentifier();
371 if (syntax.isIdentifierPart(ch)) {
378 return source.slice(start, index);
381 function scanIdentifier() {
386 // Backslash (U+005C) starts an escaped character.
387 id = (source.charCodeAt(index) === 0x5C) ? getEscapedIdentifier() : getIdentifier();
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;
400 type = Token.Identifier;
406 lineNumber: lineNumber,
407 lineStart: lineStart,
408 range: [start, index]
415 function scanPunctuator() {
417 code = source.charCodeAt(index),
425 // Check for most common single-character punctuators.
426 case 40: // ( open bracket
427 case 41: // ) close bracket
428 case 59: // ; semicolon
437 if (extra.tokenize && code === 40) {
438 extra.openParenToken = extra.tokens.length;
442 type: Token.Punctuator,
443 value: String.fromCharCode(code),
444 lineNumber: lineNumber,
445 lineStart: lineStart,
446 range: [start, index]
449 case 123: // { open curly brace
450 case 125: // } close curly brace
453 if (extra.tokenize && code === 123) {
454 extra.openCurlyToken = extra.tokens.length;
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;
463 state.curlyStack.push("{");
465 state.curlyStack.pop();
470 type: Token.Punctuator,
471 value: String.fromCharCode(code),
472 lineNumber: lineNumber,
473 lineStart: lineStart,
474 range: [start, index]
478 code2 = source.charCodeAt(index + 1);
480 // "=" (char #61) marks an assignment or comparison operator.
495 type: Token.Punctuator,
496 value: String.fromCharCode(code) + String.fromCharCode(code2),
497 lineNumber: lineNumber,
498 lineStart: lineStart,
499 range: [start, index]
507 if (source.charCodeAt(index) === 61) {
511 type: Token.Punctuator,
512 value: source.slice(start, index),
513 lineNumber: lineNumber,
514 lineStart: lineStart,
515 range: [start, index]
524 // Peek more characters.
526 ch2 = source[index + 1];
527 ch3 = source[index + 2];
528 ch4 = source[index + 3];
530 // 4-character punctuator: >>>=
532 if (ch1 === ">" && ch2 === ">" && ch3 === ">") {
536 type: Token.Punctuator,
538 lineNumber: lineNumber,
539 lineStart: lineStart,
540 range: [start, index]
545 // 3-character punctuators: === !== >>> <<= >>=
547 if (ch1 === ">" && ch2 === ">" && ch3 === ">") {
550 type: Token.Punctuator,
552 lineNumber: lineNumber,
553 lineStart: lineStart,
554 range: [start, index]
558 if (ch1 === "<" && ch2 === "<" && ch3 === "=") {
561 type: Token.Punctuator,
563 lineNumber: lineNumber,
564 lineStart: lineStart,
565 range: [start, index]
569 if (ch1 === ">" && ch2 === ">" && ch3 === "=") {
572 type: Token.Punctuator,
574 lineNumber: lineNumber,
575 lineStart: lineStart,
576 range: [start, index]
580 // The ... operator (spread, restParams, JSX, etc.)
581 if (extra.ecmaFeatures.spread ||
582 extra.ecmaFeatures.restParams ||
583 (extra.ecmaFeatures.jsx && state.inJSXSpreadAttribute)
585 if (ch1 === "." && ch2 === "." && ch3 === ".") {
588 type: Token.Punctuator,
590 lineNumber: lineNumber,
591 lineStart: lineStart,
592 range: [start, index]
597 // Other 2-character punctuators: ++ -- << >> && ||
598 if (ch1 === ch2 && ("+-<>&|".indexOf(ch1) >= 0)) {
601 type: Token.Punctuator,
603 lineNumber: lineNumber,
604 lineStart: lineStart,
605 range: [start, index]
609 // the => for arrow functions
610 if (extra.ecmaFeatures.arrowFunctions) {
611 if (ch1 === "=" && ch2 === ">") {
614 type: Token.Punctuator,
616 lineNumber: lineNumber,
617 lineStart: lineStart,
618 range: [start, index]
623 if ("<>=!+-*%&|^/".indexOf(ch1) >= 0) {
626 type: Token.Punctuator,
628 lineNumber: lineNumber,
629 lineStart: lineStart,
630 range: [start, index]
637 type: Token.Punctuator,
639 lineNumber: lineNumber,
640 lineStart: lineStart,
641 range: [start, index]
645 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
648 // 7.8.3 Numeric Literals
650 function scanHexLiteral(start) {
653 while (index < length) {
654 if (!syntax.isHexDigit(source[index])) {
657 number += source[index++];
660 if (number.length === 0) {
661 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
664 if (syntax.isIdentifierStart(source.charCodeAt(index))) {
665 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
669 type: Token.NumericLiteral,
670 value: parseInt("0x" + number, 16),
671 lineNumber: lineNumber,
672 lineStart: lineStart,
673 range: [start, index]
677 function scanBinaryLiteral(start) {
680 while (index < length) {
682 if (ch !== "0" && ch !== "1") {
685 number += source[index++];
688 if (number.length === 0) {
690 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
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");
703 type: Token.NumericLiteral,
704 value: parseInt(number, 2),
705 lineNumber: lineNumber,
706 lineStart: lineStart,
707 range: [start, index]
711 function scanOctalLiteral(prefix, start) {
714 if (syntax.isOctalDigit(prefix)) {
716 number = "0" + source[index++];
723 while (index < length) {
724 if (!syntax.isOctalDigit(source[index])) {
727 number += source[index++];
730 if (!octal && number.length === 0) {
732 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
735 if (syntax.isIdentifierStart(source.charCodeAt(index)) || syntax.isDecimalDigit(source.charCodeAt(index))) {
736 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
740 type: Token.NumericLiteral,
741 value: parseInt(number, 8),
743 lineNumber: lineNumber,
744 lineStart: lineStart,
745 range: [start, index]
749 function scanNumericLiteral() {
750 var number, start, ch;
753 assert(syntax.isDecimalDigit(ch.charCodeAt(0)) || (ch === "."),
754 "Numeric literal must start with a decimal digit or a decimal point");
759 number = source[index++];
762 // Hex number starts with "0x".
763 // Octal number starts with "0".
764 if (number === "0") {
765 if (ch === "x" || ch === "X") {
767 return scanHexLiteral(start);
770 // Binary number in ES6 starts with '0b'
771 if (extra.ecmaFeatures.binaryLiterals) {
772 if (ch === "b" || ch === "B") {
774 return scanBinaryLiteral(start);
778 if ((extra.ecmaFeatures.octalLiterals && (ch === "o" || ch === "O")) || syntax.isOctalDigit(ch)) {
779 return scanOctalLiteral(ch, start);
782 // decimal number starts with "0" such as "09" is illegal.
783 if (ch && syntax.isDecimalDigit(ch.charCodeAt(0))) {
784 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
788 while (syntax.isDecimalDigit(source.charCodeAt(index))) {
789 number += source[index++];
795 number += source[index++];
796 while (syntax.isDecimalDigit(source.charCodeAt(index))) {
797 number += source[index++];
802 if (ch === "e" || ch === "E") {
803 number += source[index++];
806 if (ch === "+" || ch === "-") {
807 number += source[index++];
809 if (syntax.isDecimalDigit(source.charCodeAt(index))) {
810 while (syntax.isDecimalDigit(source.charCodeAt(index))) {
811 number += source[index++];
814 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
818 if (syntax.isIdentifierStart(source.charCodeAt(index))) {
819 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
823 type: Token.NumericLiteral,
824 value: parseFloat(number),
825 lineNumber: lineNumber,
826 lineStart: lineStart,
827 range: [start, index]
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.
838 function scanEscapeSequence(ch) {
845 // An escape sequence cannot be empty
847 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
850 if (syntax.isLineTerminator(ch.charCodeAt(0))) {
852 if (ch === "\r" && source[index] === "\n") {
857 } else if (ch === "u" && source[index] === "{") {
858 // Handle ES6 extended unicode code point escape sequences.
859 if (extra.ecmaFeatures.unicodeCodePointEscapes) {
861 escapedCh = scanUnicodeCodePointEscape();
863 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
865 } else if (ch === "u" || ch === "x") {
866 // Handle other unicode and hex codes normally
868 unescaped = scanHexEscape(ch);
870 escapedCh = unescaped;
875 } else if (ch === "n") {
877 } else if (ch === "r") {
879 } else if (ch === "t") {
881 } else if (ch === "b") {
883 } else if (ch === "f") {
885 } else if (ch === "v") {
887 } else if (syntax.isOctalDigit(ch)) {
888 code = "01234567".indexOf(ch);
890 // \0 is not octal escape sequence
895 if (index < length && syntax.isOctalDigit(source[index])) {
897 code = code * 8 + "01234567".indexOf(source[index++]);
899 // 3 digits are only allowed when string starts with 0, 1, 2, 3
900 if ("0123".indexOf(ch) >= 0 &&
902 syntax.isOctalDigit(source[index])) {
903 code = code * 8 + "01234567".indexOf(source[index++]);
906 escapedCh = String.fromCharCode(code);
917 function scanStringLiteral() {
923 startLineNumber = lineNumber,
924 startLineStart = lineStart,
925 quote = source[index];
927 assert((quote === "'" || quote === "\""),
928 "String literal must starts with a quote");
932 while (index < length) {
933 ch = source[index++];
935 if (syntax.isLineTerminator(ch.charCodeAt(0))) {
937 } else if (ch === quote) {
940 } else if (ch === "\\") {
941 ch = source[index++];
942 escapedSequence = scanEscapeSequence(ch);
943 str += escapedSequence.ch;
944 octal = escapedSequence.octal || octal;
951 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
955 type: Token.StringLiteral,
958 startLineNumber: startLineNumber,
959 startLineStart: startLineStart,
960 lineNumber: lineNumber,
961 lineStart: lineStart,
962 range: [start, index]
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.
973 function scanTemplate() {
980 head = (source[index] === "`");
984 while (index < length) {
985 ch = source[index++];
991 } else if (ch === "$") {
992 if (source[index] === "{") {
998 } else if (ch === "\\") {
999 ch = source[index++];
1000 escapedSequence = scanEscapeSequence(ch);
1002 if (escapedSequence.octal) {
1003 throwError({}, Messages.TemplateOctalLiteral);
1006 cooked += escapedSequence.ch;
1008 } else if (syntax.isLineTerminator(ch.charCodeAt(0))) {
1010 if (ch === "\r" && source[index] === "\n") {
1021 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
1024 if (index > state.curlyLastIndex) {
1025 state.curlyLastIndex = index;
1028 state.curlyStack.push("template");
1032 state.curlyStack.pop();
1037 type: Token.Template,
1040 raw: source.slice(start + 1, index - ((tail) ? 1 : 2))
1044 lineNumber: lineNumber,
1045 lineStart: lineStart,
1046 range: [start, index]
1050 function testRegExp(pattern, flags) {
1052 validFlags = "gmsi";
1054 if (extra.ecmaFeatures.regexYFlag) {
1058 if (extra.ecmaFeatures.regexUFlag) {
1062 if (!RegExp("^[" + validFlags + "]*$").test(flags)) {
1063 throwError({}, Messages.InvalidRegExpFlag);
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
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.
1077 .replace(/\\u\{([0-9a-fA-F]+)\}/g, function ($0, $1) {
1078 if (parseInt($1, 16) <= 0x10FFFF) {
1081 throwError({}, Messages.InvalidRegExp);
1083 .replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, "x");
1086 // First, detect invalid regular expressions.
1090 throwError({}, Messages.InvalidRegExp);
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
1097 return new RegExp(pattern, flags);
1098 } catch (exception) {
1103 function scanRegExpBody() {
1104 var ch, str, classMarker, terminated, body;
1107 assert(ch === "/", "Regular expression literal must start with a slash");
1108 str = source[index++];
1110 classMarker = false;
1112 while (index < length) {
1113 ch = source[index++];
1116 ch = source[index++];
1118 if (syntax.isLineTerminator(ch.charCodeAt(0))) {
1119 throwError({}, Messages.UnterminatedRegExp);
1122 } else if (syntax.isLineTerminator(ch.charCodeAt(0))) {
1123 throwError({}, Messages.UnterminatedRegExp);
1124 } else if (classMarker) {
1126 classMarker = false;
1132 } else if (ch === "[") {
1139 throwError({}, Messages.UnterminatedRegExp);
1142 // Exclude leading and trailing slash.
1143 body = str.substr(1, str.length - 2);
1150 function scanRegExpFlags() {
1151 var ch, str, flags, restore;
1155 while (index < length) {
1157 if (!syntax.isIdentifierPart(ch.charCodeAt(0))) {
1162 if (ch === "\\" && index < length) {
1167 ch = scanHexEscape("u");
1170 for (str += "\\u"; restore < index; ++restore) {
1171 str += source[restore];
1178 throwErrorTolerant({}, Messages.UnexpectedToken, "ILLEGAL");
1181 throwErrorTolerant({}, Messages.UnexpectedToken, "ILLEGAL");
1195 function scanRegExp() {
1196 var start, body, flags, value;
1202 body = scanRegExpBody();
1203 flags = scanRegExpFlags();
1204 value = testRegExp(body.value, flags.value);
1206 if (extra.tokenize) {
1208 type: Token.RegularExpression,
1211 pattern: body.value,
1214 lineNumber: lineNumber,
1215 lineStart: lineStart,
1216 range: [start, index]
1221 literal: body.literal + flags.literal,
1224 pattern: body.value,
1227 range: [start, index]
1231 function collectRegex() {
1232 var pos, loc, regex, token;
1240 column: index - lineStart
1244 regex = scanRegExp();
1247 column: index - lineStart
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 === "/=") {
1263 type: "RegularExpression",
1264 value: regex.literal,
1266 range: [pos, index],
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;
1281 function advanceSlash() {
1284 // Using the following algorithm:
1285 // https://github.com/mozilla/sweet.js/wiki/design
1286 prevToken = extra.tokens[extra.tokens.length - 1];
1288 // Nothing before that: it cannot be a division.
1289 return collectRegex();
1291 if (prevToken.type === "Punctuator") {
1292 if (prevToken.value === "]") {
1293 return scanPunctuator();
1295 if (prevToken.value === ")") {
1296 checkToken = extra.tokens[extra.openParenToken - 1];
1298 checkToken.type === "Keyword" &&
1299 (checkToken.value === "if" ||
1300 checkToken.value === "while" ||
1301 checkToken.value === "for" ||
1302 checkToken.value === "with")) {
1303 return collectRegex();
1305 return scanPunctuator();
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];
1315 return scanPunctuator();
1317 } else if (extra.tokens[extra.openCurlyToken - 4] &&
1318 extra.tokens[extra.openCurlyToken - 4].type === "Keyword") {
1320 checkToken = extra.tokens[extra.openCurlyToken - 5];
1322 return collectRegex();
1325 return scanPunctuator();
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();
1333 // It is a declaration.
1334 return collectRegex();
1336 return collectRegex();
1338 if (prevToken.type === "Keyword") {
1339 return collectRegex();
1341 return scanPunctuator();
1344 function advance() {
1346 allowJSX = extra.ecmaFeatures.jsx,
1347 allowTemplateStrings = extra.ecmaFeatures.templateStrings;
1350 * If JSX isn't allowed or JSX is allowed and we're not inside an JSX child,
1351 * then skip any comments.
1353 if (!allowJSX || !state.inJSXChild) {
1357 if (index >= length) {
1360 lineNumber: lineNumber,
1361 lineStart: lineStart,
1362 range: [index, index]
1366 // if inside an JSX child, then abort regular tokenization
1367 if (allowJSX && state.inJSXChild) {
1368 return advanceJSXChild();
1371 ch = source.charCodeAt(index);
1373 // Very common: ( and ) and ;
1374 if (ch === 0x28 || ch === 0x29 || ch === 0x3B) {
1375 return scanPunctuator();
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();
1384 return scanStringLiteral();
1387 if (allowJSX && state.inJSXTag && syntax.isJSXIdentifierStart(ch)) {
1388 return scanJSXIdentifier();
1391 // Template strings start with backtick (U+0096) or closing curly brace (125) and backtick.
1392 if (allowTemplateStrings) {
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();
1401 if (syntax.isIdentifierStart(ch)) {
1402 return scanIdentifier();
1405 // Dot (.) U+002E can also start a floating-point number, hence the need
1406 // to check the next character.
1408 if (syntax.isDecimalDigit(source.charCodeAt(index + 1))) {
1409 return scanNumericLiteral();
1411 return scanPunctuator();
1414 if (syntax.isDecimalDigit(ch)) {
1415 return scanNumericLiteral();
1418 // Slash (/) U+002F can also start a regex.
1419 if (extra.tokenize && ch === 0x2F) {
1420 return advanceSlash();
1423 return scanPunctuator();
1426 function collectToken() {
1427 var loc, token, range, value, entry,
1428 allowJSX = extra.ecmaFeatures.jsx;
1430 /* istanbul ignore else */
1431 if (!allowJSX || !state.inJSXChild) {
1438 column: index - lineStart
1445 column: index - lineStart
1448 if (token.type !== Token.EOF) {
1449 range = [token.range[0], token.range[1]];
1450 value = source.slice(token.range[0], token.range[1]);
1452 type: TokenName[token.type],
1459 pattern: token.regex.pattern,
1460 flags: token.regex.flags
1463 extra.tokens.push(entry);
1473 index = token.range[1];
1474 lineNumber = token.lineNumber;
1475 lineStart = token.lineStart;
1477 lookahead = (typeof extra.tokens !== "undefined") ? collectToken() : advance();
1479 index = token.range[1];
1480 lineNumber = token.lineNumber;
1481 lineStart = token.lineStart;
1495 lookahead = (typeof extra.tokens !== "undefined") ? collectToken() : advance();
1502 function lookahead2() {
1503 var adv, pos, line, start, result;
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;
1513 // Scan for the next immediate token.
1514 /* istanbul ignore if */
1515 if (lookahead === null) {
1518 index = lookahead.range[1];
1519 lineNumber = lookahead.lineNumber;
1520 lineStart = lookahead.lineStart;
1522 // Grab the token right after.
1532 //------------------------------------------------------------------------------
1534 //------------------------------------------------------------------------------
1536 function getQualifiedJSXName(object) {
1537 if (object.type === astNodeTypes.JSXIdentifier) {
1540 if (object.type === astNodeTypes.JSXNamespacedName) {
1541 return object.namespace.name + ":" + object.name.name;
1543 /* istanbul ignore else */
1544 if (object.type === astNodeTypes.JSXMemberExpression) {
1546 getQualifiedJSXName(object.object) + "." +
1547 getQualifiedJSXName(object.property)
1550 /* istanbul ignore next */
1551 throwUnexpected(object);
1554 function scanJSXIdentifier() {
1555 var ch, start, value = "";
1558 while (index < length) {
1559 ch = source.charCodeAt(index);
1560 if (!syntax.isJSXIdentifierPart(ch)) {
1563 value += source[index++];
1567 type: Token.JSXIdentifier,
1569 lineNumber: lineNumber,
1570 lineStart: lineStart,
1571 range: [start, index]
1575 function scanJSXEntity() {
1576 var ch, str = "", start = index, count = 0, code;
1578 assert(ch === "&", "Entity must start with an ampersand");
1580 while (index < length && count++ < 10) {
1581 ch = source[index++];
1588 // Well-formed entity (ending was found).
1591 if (str[0] === "#") {
1592 if (str[1] === "x") {
1593 code = +("0" + str.substr(1));
1595 // Removing leading zeros in order to avoid treating as octal in old browsers.
1596 code = +str.substr(1).replace(Regex.LeadingZeros, "");
1600 return String.fromCharCode(code);
1602 /* istanbul ignore else */
1603 } else if (XHTMLEntities[str]) {
1604 return XHTMLEntities[str];
1608 // Treat non-entity sequences as regular text.
1613 function scanJSXText(stopChars) {
1614 var ch, str = "", start;
1616 while (index < length) {
1618 if (stopChars.indexOf(ch) !== -1) {
1622 str += scanJSXEntity();
1625 if (ch === "\r" && source[index] === "\n") {
1630 if (syntax.isLineTerminator(ch.charCodeAt(0))) {
1638 type: Token.JSXText,
1640 lineNumber: lineNumber,
1641 lineStart: lineStart,
1642 range: [start, index]
1646 function scanJSXStringLiteral() {
1647 var innerToken, quote, start;
1649 quote = source[index];
1650 assert((quote === "\"" || quote === "'"),
1651 "String literal must starts with a quote");
1656 innerToken = scanJSXText([quote]);
1658 if (quote !== source[index]) {
1659 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
1664 innerToken.range = [start, index];
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.
1673 function advanceJSXChild() {
1674 var ch = source.charCodeAt(index);
1676 // { (123) and < (60)
1677 if (ch !== 123 && ch !== 60) {
1678 return scanJSXText(["<", "{"]);
1681 return scanPunctuator();
1684 function parseJSXIdentifier() {
1685 var token, marker = markerCreate();
1687 if (lookahead.type !== Token.JSXIdentifier) {
1688 throwUnexpected(lookahead);
1692 return markerApply(marker, astNodeFactory.createJSXIdentifier(token.value));
1695 function parseJSXNamespacedName() {
1696 var namespace, name, marker = markerCreate();
1698 namespace = parseJSXIdentifier();
1700 name = parseJSXIdentifier();
1702 return markerApply(marker, astNodeFactory.createJSXNamespacedName(namespace, name));
1705 function parseJSXMemberExpression() {
1706 var marker = markerCreate(),
1707 expr = parseJSXIdentifier();
1709 while (match(".")) {
1711 expr = markerApply(marker, astNodeFactory.createJSXMemberExpression(expr, parseJSXIdentifier()));
1717 function parseJSXElementName() {
1718 if (lookahead2().value === ":") {
1719 return parseJSXNamespacedName();
1721 if (lookahead2().value === ".") {
1722 return parseJSXMemberExpression();
1725 return parseJSXIdentifier();
1728 function parseJSXAttributeName() {
1729 if (lookahead2().value === ":") {
1730 return parseJSXNamespacedName();
1733 return parseJSXIdentifier();
1736 function parseJSXAttributeValue() {
1739 value = parseJSXExpressionContainer();
1740 if (value.expression.type === astNodeTypes.JSXEmptyExpression) {
1743 "JSX attributes must only be assigned a non-empty " +
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));
1753 throwError({}, Messages.InvalidJSXAttributeValue);
1758 function parseJSXEmptyExpression() {
1759 var marker = markerCreatePreserveWhitespace();
1760 while (source.charAt(index) !== "}") {
1763 return markerApply(marker, astNodeFactory.createJSXEmptyExpression());
1766 function parseJSXExpressionContainer() {
1767 var expression, origInJSXChild, origInJSXTag, marker = markerCreate();
1769 origInJSXChild = state.inJSXChild;
1770 origInJSXTag = state.inJSXTag;
1771 state.inJSXChild = false;
1772 state.inJSXTag = false;
1777 expression = parseJSXEmptyExpression();
1779 expression = parseExpression();
1782 state.inJSXChild = origInJSXChild;
1783 state.inJSXTag = origInJSXTag;
1787 return markerApply(marker, astNodeFactory.createJSXExpressionContainer(expression));
1790 function parseJSXSpreadAttribute() {
1791 var expression, origInJSXChild, origInJSXTag, marker = markerCreate();
1793 origInJSXChild = state.inJSXChild;
1794 origInJSXTag = state.inJSXTag;
1795 state.inJSXChild = false;
1796 state.inJSXTag = false;
1797 state.inJSXSpreadAttribute = true;
1802 state.inJSXSpreadAttribute = false;
1804 expression = parseAssignmentExpression();
1806 state.inJSXChild = origInJSXChild;
1807 state.inJSXTag = origInJSXTag;
1811 return markerApply(marker, astNodeFactory.createJSXSpreadAttribute(expression));
1814 function parseJSXAttribute() {
1818 return parseJSXSpreadAttribute();
1821 marker = markerCreate();
1823 name = parseJSXAttributeName();
1825 // HTML empty attribute
1828 return markerApply(marker, astNodeFactory.createJSXAttribute(name, parseJSXAttributeValue()));
1831 return markerApply(marker, astNodeFactory.createJSXAttribute(name));
1834 function parseJSXChild() {
1837 token = parseJSXExpressionContainer();
1838 } else if (lookahead.type === Token.JSXText) {
1839 marker = markerCreatePreserveWhitespace();
1840 token = markerApply(marker, astNodeFactory.createLiteralFromSource(lex(), source));
1842 token = parseJSXElement();
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;
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;
1862 return markerApply(marker, astNodeFactory.createJSXClosingElement(name));
1865 function parseJSXOpeningElement() {
1866 var name, attributes = [], selfClosing = false, origInJSXChild,
1867 origInJSXTag, marker = markerCreate();
1869 origInJSXChild = state.inJSXChild;
1870 origInJSXTag = state.inJSXTag;
1871 state.inJSXChild = false;
1872 state.inJSXTag = true;
1876 name = parseJSXElementName();
1878 while (index < length &&
1879 lookahead.value !== "/" &&
1880 lookahead.value !== ">") {
1881 attributes.push(parseJSXAttribute());
1884 state.inJSXTag = origInJSXTag;
1886 if (lookahead.value === "/") {
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;
1895 state.inJSXChild = true;
1898 return markerApply(marker, astNodeFactory.createJSXOpeningElement(name, attributes, selfClosing));
1901 function parseJSXElement() {
1902 var openingElement, closingElement = null, children = [], origInJSXChild, origInJSXTag, marker = markerCreate();
1904 origInJSXChild = state.inJSXChild;
1905 origInJSXTag = state.inJSXTag;
1906 openingElement = parseJSXOpeningElement();
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 === "/") {
1914 state.inJSXChild = true;
1915 children.push(parseJSXChild());
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));
1926 * When (erroneously) writing two adjacent tags like
1928 * var x = <div>one</div><div>two</div>;
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.)
1936 if (!origInJSXChild && match("<")) {
1937 throwError(lookahead, Messages.AdjacentJSXElements);
1940 return markerApply(marker, astNodeFactory.createJSXElement(openingElement, closingElement, children));
1943 //------------------------------------------------------------------------------
1945 //------------------------------------------------------------------------------
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.
1956 function markerApply(marker, node) {
1958 // add range information to the node if present
1960 node.range = [marker.offset, index];
1963 // add location information the node if present
1972 column: index - lineStart
1975 // Attach extra.source information to the location, if present
1977 node.loc.source = extra.source;
1981 // attach leading and trailing comments if requested
1982 if (extra.attachComment) {
1983 commentAttachment.processComment(node);
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.
1996 function markerCreate() {
1998 if (!extra.loc && !extra.range) {
2007 col: index - lineStart
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.
2019 function markerCreatePreserveWhitespace() {
2021 if (!extra.loc && !extra.range) {
2028 col: index - lineStart
2033 //------------------------------------------------------------------------------
2034 // Syntax Tree Delegate
2035 //------------------------------------------------------------------------------
2037 // Return true if there is a line terminator before the next token.
2039 function peekLineTerminator() {
2040 var pos, line, start, found;
2046 found = lineNumber !== line;
2054 // Throw an exception
2056 function throwError(token, messageFormat) {
2059 args = Array.prototype.slice.call(arguments, 2),
2060 msg = messageFormat.replace(
2062 function (whole, index) {
2063 assert(index < args.length, "Message reference must be in range");
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;
2074 error = new Error("Line " + lineNumber + ": " + msg);
2075 error.index = index;
2076 error.lineNumber = lineNumber;
2077 error.column = index - lineStart + 1;
2080 error.description = msg;
2084 function throwErrorTolerant() {
2086 throwError.apply(null, arguments);
2089 extra.errors.push(e);
2097 // Throw an exception because of the token.
2099 function throwUnexpected(token) {
2101 if (token.type === Token.EOF) {
2102 throwError(token, Messages.UnexpectedEOS);
2105 if (token.type === Token.NumericLiteral) {
2106 throwError(token, Messages.UnexpectedNumber);
2109 if (token.type === Token.StringLiteral || token.type === Token.JSXText) {
2110 throwError(token, Messages.UnexpectedString);
2113 if (token.type === Token.Identifier) {
2114 throwError(token, Messages.UnexpectedIdentifier);
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);
2124 throwError(token, Messages.UnexpectedToken, token.value);
2127 if (token.type === Token.Template) {
2128 throwError(token, Messages.UnexpectedTemplate, token.value.raw);
2131 // BooleanLiteral, NullLiteral, or Punctuator.
2132 throwError(token, Messages.UnexpectedToken, token.value);
2135 // Expect the next token to match the specified punctuator.
2136 // If not, an exception will be thrown.
2138 function expect(value) {
2140 if (token.type !== Token.Punctuator || token.value !== value) {
2141 throwUnexpected(token);
2145 // Expect the next token to match the specified keyword.
2146 // If not, an exception will be thrown.
2148 function expectKeyword(keyword) {
2150 if (token.type !== Token.Keyword || token.value !== keyword) {
2151 throwUnexpected(token);
2155 // Return true if the next token matches the specified punctuator.
2157 function match(value) {
2158 return lookahead.type === Token.Punctuator && lookahead.value === value;
2161 // Return true if the next token matches the specified keyword
2163 function matchKeyword(keyword) {
2164 return lookahead.type === Token.Keyword && lookahead.value === keyword;
2167 // Return true if the next token matches the specified contextual keyword
2168 // (where an identifier is sometimes a keyword depending on the context)
2170 function matchContextualKeyword(keyword) {
2171 return lookahead.type === Token.Identifier && lookahead.value === keyword;
2174 // Return true if the next token is an assignment operator
2176 function matchAssign() {
2179 if (lookahead.type !== Token.Punctuator) {
2182 op = lookahead.value;
2183 return op === "=" ||
2197 function consumeSemicolon() {
2200 // Catch the very common case first: immediately a semicolon (U+003B).
2201 if (source.charCodeAt(index) === 0x3B || match(";")) {
2208 if (lineNumber !== line) {
2212 if (lookahead.type !== Token.EOF && !match("}")) {
2213 throwUnexpected(lookahead);
2217 // Return true if provided expression is LeftHandSideExpression
2219 function isLeftHandSide(expr) {
2220 return expr.type === astNodeTypes.Identifier || expr.type === astNodeTypes.MemberExpression;
2223 // 11.1.4 Array Initialiser
2225 function parseArrayInitialiser() {
2227 marker = markerCreate(),
2232 while (!match("]")) {
2234 lex(); // only get here when you have [a,,] or similar
2235 elements.push(null);
2237 tmp = parseSpreadOrAssignmentExpression();
2239 if (!(match("]"))) {
2240 expect(","); // handles the common case of comma-separated values
2247 return markerApply(marker, astNodeFactory.createArrayExpression(elements));
2250 // 11.1.5 Object Initialiser
2252 function parsePropertyFunction(paramInfo, options) {
2253 var previousStrict = strict,
2254 previousYieldAllowed = state.yieldAllowed,
2255 generator = options ? options.generator : false,
2258 state.yieldAllowed = generator;
2261 * Esprima uses parseConciseBody() here, which is incorrect. Object literal
2262 * methods must have braces.
2264 body = parseFunctionSourceElements();
2266 if (strict && paramInfo.firstRestricted) {
2267 throwErrorTolerant(paramInfo.firstRestricted, Messages.StrictParamName);
2270 if (strict && paramInfo.stricted) {
2271 throwErrorTolerant(paramInfo.stricted, paramInfo.message);
2274 strict = previousStrict;
2275 state.yieldAllowed = previousYieldAllowed;
2277 return markerApply(options.marker, astNodeFactory.createFunctionExpression(
2284 body.type !== astNodeTypes.BlockStatement
2288 function parsePropertyMethodFunction(options) {
2289 var previousStrict = strict,
2290 marker = markerCreate(),
2296 params = parseParams();
2298 if (params.stricted) {
2299 throwErrorTolerant(params.stricted, params.message);
2302 method = parsePropertyFunction(params, {
2303 generator: options ? options.generator : false,
2307 strict = previousStrict;
2312 function parseObjectPropertyKey() {
2313 var marker = markerCreate(),
2315 allowObjectLiteralComputed = extra.ecmaFeatures.objectLiteralComputedProperties,
2319 // Note: This function is called only from parseObjectProperty(), where
2320 // EOF and Punctuator tokens are already filtered out.
2322 switch (token.type) {
2323 case Token.StringLiteral:
2324 case Token.NumericLiteral:
2325 if (strict && token.octal) {
2326 throwErrorTolerant(token, Messages.StrictOctalLiteral);
2328 return markerApply(marker, astNodeFactory.createLiteralFromSource(token, source));
2330 case Token.Identifier:
2331 case Token.BooleanLiteral:
2332 case Token.NullLiteral:
2334 return markerApply(marker, astNodeFactory.createIdentifier(token.value));
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);
2351 throwUnexpected(token);
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:
2363 case Token.Punctuator:
2364 return lookahead.value === "[";
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;
2378 if (token.type === Token.Identifier) {
2379 // check for `get` and `set`;
2381 if (token.value === "get" && lookaheadPropertyName()) {
2383 computed = match("[");
2384 key = parseObjectPropertyKey();
2385 methodMarker = markerCreate();
2389 value = parsePropertyFunction({
2393 firstRestricted: null,
2397 marker: methodMarker
2400 return markerApply(marker, astNodeFactory.createProperty("get", key, value, false, false, computed));
2402 } else if (token.value === "set" && lookaheadPropertyName()) {
2403 computed = match("[");
2404 key = parseObjectPropertyKey();
2405 methodMarker = markerCreate();
2413 firstRestricted: null,
2414 paramSet: new StringMap(),
2418 throwErrorTolerant(lookahead, Messages.UnexpectedToken, lookahead.value);
2420 parseParam(options);
2421 if (options.defaultCount === 0) {
2422 options.defaults = [];
2427 value = parsePropertyFunction(options, { marker: methodMarker });
2428 return markerApply(marker, astNodeFactory.createProperty("set", key, value, false, false, computed));
2433 value = parsePropertyMethodFunction();
2434 return markerApply(marker, astNodeFactory.createProperty("init", key, value, true, false, computed));
2437 // Not a MethodDefinition.
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.
2447 function parseGeneratorProperty(key, marker) {
2449 var computed = (lookahead.type === Token.Punctuator && lookahead.value === "[");
2452 throwUnexpected(lex());
2457 astNodeFactory.createProperty(
2460 parsePropertyMethodFunction({ generator: true }),
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();
2479 computed = (token.value === "[" && token.type === Token.Punctuator);
2481 if (token.type === Token.Identifier || (allowComputed && computed)) {
2483 id = parseObjectPropertyKey();
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.
2489 if (token.value === "get" &&
2490 !(match(":") || match("(") || match(",") || match("}"))) {
2491 computed = (lookahead.value === "[");
2492 key = parseObjectPropertyKey();
2493 methodMarker = markerCreate();
2499 astNodeFactory.createProperty(
2502 parsePropertyFunction({
2505 marker: methodMarker
2514 if (token.value === "set" &&
2515 !(match(":") || match("(") || match(",") || match("}"))) {
2516 computed = (lookahead.value === "[");
2517 key = parseObjectPropertyKey();
2518 methodMarker = markerCreate();
2526 firstRestricted: null,
2527 paramSet: new StringMap(),
2532 throwErrorTolerant(lookahead, Messages.UnexpectedToken, lookahead.value);
2534 parseParam(options);
2535 if (options.defaultCount === 0) {
2536 options.defaults = [];
2544 astNodeFactory.createProperty(
2547 parsePropertyFunction(options, {
2548 marker: methodMarker
2557 // normal property (key:value)
2562 astNodeFactory.createProperty(
2565 parseAssignmentExpression(),
2573 // method shorthand (key(){...})
2574 if (allowMethod && match("(")) {
2577 astNodeFactory.createProperty(
2580 parsePropertyMethodFunction({ generator: false }),
2588 // destructuring defaults (shorthand syntax)
2589 if (allowDestructuring && match("=")) {
2591 var value = parseAssignmentExpression();
2592 var prop = markerApply(marker, astNodeFactory.createAssignmentExpression("=", id, value));
2593 prop.type = astNodeTypes.AssignmentPattern;
2594 var fullProperty = astNodeFactory.createProperty(
2602 return markerApply(marker, fullProperty);
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.
2611 if (computed || (!allowShorthand && !allowDestructuring)) {
2612 throwUnexpected(lookahead);
2615 // shorthand property
2618 astNodeFactory.createProperty(
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);
2637 id = parseObjectPropertyKey();
2639 return parseGeneratorProperty(id, marker);
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.
2648 key = parseObjectPropertyKey();
2650 // check for property value
2655 astNodeFactory.createProperty(
2658 parseAssignmentExpression(),
2667 if (allowMethod && match("(")) {
2670 astNodeFactory.createProperty(
2673 parsePropertyMethodFunction(),
2681 // no other options, this is bad
2682 throwUnexpected(lex());
2685 function getFieldName(key) {
2686 var toString = String;
2687 if (key.type === astNodeTypes.Identifier) {
2690 return toString(key.value);
2693 function parseObjectInitialiser() {
2694 var marker = markerCreate(),
2695 allowDuplicates = extra.ecmaFeatures.objectLiteralDuplicateProperties,
2702 previousInObjectLiteral = state.inObjectLiteral,
2703 kindMap = new StringMap();
2705 state.inObjectLiteral = true;
2709 while (!match("}")) {
2711 property = parseObjectProperty();
2713 if (!property.computed) {
2715 name = getFieldName(property.key);
2716 propertyFn = (property.kind === "get") ? PropertyKind.Get : PropertyKind.Set;
2717 kind = (property.kind === "init") ? PropertyKind.Data : propertyFn;
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);
2732 if (kind === PropertyKind.Data) {
2733 throwErrorTolerant({}, Messages.AccessorDataProperty);
2734 } else if (storedKind & kind) {
2735 throwErrorTolerant({}, Messages.AccessorGetSet);
2738 kindMap.set(name, storedKind | kind);
2740 kindMap.set(name, kind);
2744 properties.push(property);
2753 state.inObjectLiteral = previousInObjectLiteral;
2755 return markerApply(marker, astNodeFactory.createObjectExpression(properties));
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
2766 function parseTemplateElement(option) {
2769 if (lookahead.type !== Token.Template || (option.head && !lookahead.head)) {
2770 throwError({}, Messages.UnexpectedToken, "ILLEGAL");
2773 marker = markerCreate();
2778 astNodeFactory.createTemplateElement(
2780 raw: token.value.raw,
2781 cooked: token.value.cooked
2789 * Parse a template string literal and return its ASTNode representation
2790 * @returns {ASTNode} The template literal node with marker info applied
2793 function parseTemplateLiteral() {
2794 var quasi, quasis, expressions, marker = markerCreate();
2796 quasi = parseTemplateElement({ head: true });
2800 while (!quasi.tail) {
2801 expressions.push(parseExpression());
2802 quasi = parseTemplateElement({ head: false });
2806 return markerApply(marker, astNodeFactory.createTemplateLiteral(quasis, expressions));
2809 // 11.1.6 The Grouping Operator
2811 function parseGroupExpression() {
2816 ++state.parenthesisCount;
2818 expr = parseExpression();
2826 // 11.1 Primary Expressions
2828 function parsePrimaryExpression() {
2829 var type, token, expr,
2831 allowJSX = extra.ecmaFeatures.jsx,
2832 allowClasses = extra.ecmaFeatures.classes,
2833 allowSuper = allowClasses || extra.ecmaFeatures.superInFunctions;
2836 return parseGroupExpression();
2840 return parseArrayInitialiser();
2844 return parseObjectInitialiser();
2847 if (allowJSX && match("<")) {
2848 return parseJSXElement();
2851 type = lookahead.type;
2852 marker = markerCreate();
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);
2860 expr = astNodeFactory.createLiteralFromSource(lex(), source);
2861 } else if (type === Token.Keyword) {
2862 if (matchKeyword("function")) {
2863 return parseFunctionExpression();
2866 if (allowSuper && matchKeyword("super") && state.inFunctionBody) {
2867 marker = markerCreate();
2869 return markerApply(marker, astNodeFactory.createIdentifier("super"));
2872 if (matchKeyword("this")) {
2873 marker = markerCreate();
2875 return markerApply(marker, astNodeFactory.createThisExpression());
2878 if (allowClasses && matchKeyword("class")) {
2879 return parseClassExpression();
2882 throwUnexpected(lex());
2883 } else if (type === Token.BooleanLiteral) {
2885 token.value = (token.value === "true");
2886 expr = astNodeFactory.createLiteralFromSource(token, source);
2887 } else if (type === Token.NullLiteral) {
2890 expr = astNodeFactory.createLiteralFromSource(token, source);
2891 } else if (match("/") || match("/=")) {
2892 if (typeof extra.tokens !== "undefined") {
2893 expr = astNodeFactory.createLiteralFromSource(collectRegex(), source);
2895 expr = astNodeFactory.createLiteralFromSource(scanRegExp(), source);
2898 } else if (type === Token.Template) {
2899 return parseTemplateLiteral();
2901 throwUnexpected(lex());
2904 return markerApply(marker, expr);
2907 // 11.2 Left-Hand-Side Expressions
2909 function parseArguments() {
2915 while (index < length) {
2916 arg = parseSpreadOrAssignmentExpression();
2932 function parseSpreadOrAssignmentExpression() {
2934 var marker = markerCreate();
2936 return markerApply(marker, astNodeFactory.createSpreadElement(parseAssignmentExpression()));
2938 return parseAssignmentExpression();
2941 function parseNonComputedProperty() {
2943 marker = markerCreate();
2947 if (!isIdentifierName(token)) {
2948 throwUnexpected(token);
2951 return markerApply(marker, astNodeFactory.createIdentifier(token.value));
2954 function parseNonComputedMember() {
2957 return parseNonComputedProperty();
2960 function parseComputedMember() {
2965 expr = parseExpression();
2972 function parseNewExpression() {
2974 marker = markerCreate();
2976 expectKeyword("new");
2977 callee = parseLeftHandSideExpression();
2978 args = match("(") ? parseArguments() : [];
2980 return markerApply(marker, astNodeFactory.createNewExpression(callee, args));
2983 function parseLeftHandSideExpressionAllowCall() {
2985 previousAllowIn = state.allowIn,
2986 marker = markerCreate();
2988 state.allowIn = true;
2989 expr = matchKeyword("new") ? parseNewExpression() : parsePrimaryExpression();
2990 state.allowIn = previousAllowIn;
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)) {
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()));
3002 expr = markerApply(marker, astNodeFactory.createTaggedTemplateExpression(expr, parseTemplateLiteral()));
3009 function parseLeftHandSideExpression() {
3011 previousAllowIn = state.allowIn,
3012 marker = markerCreate();
3014 expr = matchKeyword("new") ? parseNewExpression() : parsePrimaryExpression();
3015 state.allowIn = previousAllowIn;
3017 // only start parsing template literal if the lookahead is a head (beginning with `)
3018 while (match(".") || match("[") || (lookahead.type === Token.Template && lookahead.head)) {
3020 expr = markerApply(marker, astNodeFactory.createMemberExpression("[", expr, parseComputedMember()));
3021 } else if (match(".")) {
3022 expr = markerApply(marker, astNodeFactory.createMemberExpression(".", expr, parseNonComputedMember()));
3024 expr = markerApply(marker, astNodeFactory.createTaggedTemplateExpression(expr, parseTemplateLiteral()));
3032 // 11.3 Postfix Expressions
3034 function parsePostfixExpression() {
3036 marker = markerCreate();
3038 expr = parseLeftHandSideExpressionAllowCall();
3040 if (lookahead.type === Token.Punctuator) {
3041 if ((match("++") || match("--")) && !peekLineTerminator()) {
3043 if (strict && expr.type === astNodeTypes.Identifier && syntax.isRestrictedWord(expr.name)) {
3044 throwErrorTolerant({}, Messages.StrictLHSPostfix);
3047 if (!isLeftHandSide(expr)) {
3048 throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
3052 expr = markerApply(marker, astNodeFactory.createPostfixExpression(token.value, expr));
3059 // 11.4 Unary Operators
3061 function parseUnaryExpression() {
3065 if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) {
3066 expr = parsePostfixExpression();
3067 } else if (match("++") || match("--")) {
3068 marker = markerCreate();
3070 expr = parseUnaryExpression();
3072 if (strict && expr.type === astNodeTypes.Identifier && syntax.isRestrictedWord(expr.name)) {
3073 throwErrorTolerant({}, Messages.StrictLHSPrefix);
3076 if (!isLeftHandSide(expr)) {
3077 throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
3080 expr = astNodeFactory.createUnaryExpression(token.value, expr);
3081 expr = markerApply(marker, expr);
3082 } else if (match("+") || match("-") || match("~") || match("!")) {
3083 marker = markerCreate();
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();
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);
3098 expr = parsePostfixExpression();
3104 function binaryPrecedence(token, allowIn) {
3107 if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
3111 switch (token.value) {
3148 prec = allowIn ? 7 : 0;
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,
3186 previousAllowIn = state.allowIn;
3187 state.allowIn = true;
3189 marker = markerCreate();
3190 left = parseUnaryExpression();
3193 prec = binaryPrecedence(token, previousAllowIn);
3200 markers = [marker, markerCreate()];
3201 right = parseUnaryExpression();
3203 stack = [left, token, right];
3205 while ((prec = binaryPrecedence(lookahead, previousAllowIn)) > 0) {
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;
3212 expr = astNodeFactory.createBinaryExpression(operator, left, right);
3214 marker = markers.pop();
3215 markerApply(marker, expr);
3217 markers.push(marker);
3224 markers.push(markerCreate());
3225 expr = parseUnaryExpression();
3229 state.allowIn = previousAllowIn;
3231 // Final reduce to clean-up the stack.
3232 i = stack.length - 1;
3236 expr = astNodeFactory.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
3238 marker = markers.pop();
3239 markerApply(marker, expr);
3245 // 11.12 Conditional Operator
3247 function parseConditionalExpression() {
3248 var expr, previousAllowIn, consequent, alternate,
3249 marker = markerCreate();
3251 expr = parseBinaryExpression();
3255 previousAllowIn = state.allowIn;
3256 state.allowIn = true;
3257 consequent = parseAssignmentExpression();
3258 state.allowIn = previousAllowIn;
3260 alternate = parseAssignmentExpression();
3262 expr = astNodeFactory.createConditionalExpression(expr, consequent, alternate);
3263 markerApply(marker, expr);
3269 // [ES6] 14.2 Arrow Function
3271 function parseConciseBody() {
3273 return parseFunctionSourceElements();
3275 return parseAssignmentExpression();
3278 function reinterpretAsCoverFormalsList(expressions) {
3279 var i, len, param, params, defaults, defaultCount, options, rest;
3286 paramSet: new StringMap()
3289 for (i = 0, len = expressions.length; i < len; i += 1) {
3290 param = expressions[i];
3291 if (param.type === astNodeTypes.Identifier) {
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);
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);
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);
3310 validateParam(options, param.left, param.left.name);
3316 if (options.message === Messages.StrictParamDupe) {
3318 strict ? options.stricted : options.firstRestricted,
3323 // must be here so it's not an array of [null, null]
3324 if (defaultCount === 0) {
3332 stricted: options.stricted,
3333 firstRestricted: options.firstRestricted,
3334 message: options.message
3338 function parseArrowFunctionExpression(options, marker) {
3339 var previousStrict, body;
3342 previousStrict = strict;
3344 body = parseConciseBody();
3346 if (strict && options.firstRestricted) {
3347 throwError(options.firstRestricted, options.message);
3349 if (strict && options.stricted) {
3350 throwErrorTolerant(options.stricted, options.message);
3353 strict = previousStrict;
3354 return markerApply(marker, astNodeFactory.createArrowFunctionExpression(
3359 body.type !== astNodeTypes.BlockStatement
3363 // 11.13 Assignment Operators
3365 // 12.14.5 AssignmentPattern
3367 function reinterpretAsAssignmentBindingPattern(expr) {
3368 var i, len, property, element,
3369 allowDestructuring = extra.ecmaFeatures.destructuring;
3371 if (!allowDestructuring) {
3372 throwUnexpected(lex());
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);
3382 reinterpretAsAssignmentBindingPattern(property.value);
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 */
3390 reinterpretAsAssignmentBindingPattern(element);
3393 } else if (expr.type === astNodeTypes.Identifier) {
3394 if (syntax.isRestrictedWord(expr.name)) {
3395 throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
3397 } else if (expr.type === astNodeTypes.SpreadElement) {
3398 reinterpretAsAssignmentBindingPattern(expr.argument);
3399 if (expr.argument.type === astNodeTypes.ObjectPattern) {
3400 throwErrorTolerant({}, Messages.ObjectPatternAsSpread);
3402 } else if (expr.type === "AssignmentExpression" && expr.operator === "=") {
3403 expr.type = astNodeTypes.AssignmentPattern;
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
3411 throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
3416 // 13.2.3 BindingPattern
3418 function reinterpretAsDestructuredParameter(options, expr) {
3419 var i, len, property, element,
3420 allowDestructuring = extra.ecmaFeatures.destructuring;
3422 if (!allowDestructuring) {
3423 throwUnexpected(lex());
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);
3433 reinterpretAsDestructuredParameter(options, property.value);
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];
3440 reinterpretAsDestructuredParameter(options, element);
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);
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);
3458 function parseAssignmentExpression() {
3459 var token, left, right, node, params,
3461 startsWithParen = false,
3462 oldParenthesisCount = state.parenthesisCount,
3463 allowGenerators = extra.ecmaFeatures.generators;
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();
3472 marker = markerCreate();
3475 token = lookahead2();
3476 if ((token.value === ")" && token.type === Token.Punctuator) || token.value === "...") {
3477 params = parseParams();
3479 throwUnexpected(lex());
3481 return parseArrowFunctionExpression(params, marker);
3483 startsWithParen = true;
3486 // revert to the previous lookahead style object
3488 node = left = parseConditionalExpression();
3491 (state.parenthesisCount === oldParenthesisCount ||
3492 state.parenthesisCount === (oldParenthesisCount + 1))) {
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());
3502 params = reinterpretAsCoverFormalsList([ node ]);
3503 } else if (node.type === astNodeTypes.SequenceExpression) {
3504 params = reinterpretAsCoverFormalsList(node.expressions);
3508 return parseArrowFunctionExpression(params, marker);
3512 if (matchAssign()) {
3515 if (strict && left.type === astNodeTypes.Identifier && syntax.isRestrictedWord(left.name)) {
3516 throwErrorTolerant(token, Messages.StrictLHSAssignment);
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);
3527 right = parseAssignmentExpression();
3528 node = markerApply(marker, astNodeFactory.createAssignmentExpression(token.value, left, right));
3534 // 11.14 Comma Operator
3536 function parseExpression() {
3537 var marker = markerCreate(),
3538 expr = parseAssignmentExpression(),
3539 expressions = [ expr ],
3540 sequence, spreadFound;
3543 while (index < length) {
3548 expr = parseSpreadOrAssignmentExpression();
3549 expressions.push(expr);
3551 if (expr.type === astNodeTypes.SpreadElement) {
3554 throwError({}, Messages.ElementAfterSpreadElement);
3560 sequence = markerApply(marker, astNodeFactory.createSequenceExpression(expressions));
3563 if (spreadFound && lookahead2().value !== "=>") {
3564 throwError({}, Messages.IllegalSpread);
3567 return sequence || expr;
3572 function parseStatementList() {
3576 while (index < length) {
3580 statement = parseSourceElement();
3581 if (typeof statement === "undefined") {
3584 list.push(statement);
3590 function parseBlock() {
3592 marker = markerCreate();
3596 block = parseStatementList();
3600 return markerApply(marker, astNodeFactory.createBlockStatement(block));
3603 // 12.2 Variable Statement
3605 function parseVariableIdentifier() {
3607 marker = markerCreate();
3611 if (token.type !== Token.Identifier) {
3612 if (strict && token.type === Token.Keyword && syntax.isStrictModeReservedWord(token.value)) {
3613 throwErrorTolerant(token, Messages.StrictReservedWord);
3615 throwUnexpected(token);
3619 return markerApply(marker, astNodeFactory.createIdentifier(token.value));
3622 function parseVariableDeclaration(kind) {
3624 marker = markerCreate(),
3627 id = parseObjectInitialiser();
3628 reinterpretAsAssignmentBindingPattern(id);
3629 } else if (match("[")) {
3630 id = parseArrayInitialiser();
3631 reinterpretAsAssignmentBindingPattern(id);
3633 /* istanbul ignore next */
3634 id = state.allowKeyword ? parseNonComputedProperty() : parseVariableIdentifier();
3636 if (strict && syntax.isRestrictedWord(id.name)) {
3637 throwErrorTolerant({}, Messages.StrictVarName);
3641 // TODO: Verify against feature flags
3642 if (kind === "const") {
3644 throwError({}, Messages.NoUnintializedConst);
3647 init = parseAssignmentExpression();
3648 } else if (match("=")) {
3650 init = parseAssignmentExpression();
3653 return markerApply(marker, astNodeFactory.createVariableDeclarator(id, init));
3656 function parseVariableDeclarationList(kind) {
3660 list.push(parseVariableDeclaration(kind));
3665 } while (index < length);
3670 function parseVariableStatement() {
3673 expectKeyword("var");
3675 declarations = parseVariableDeclarationList();
3679 return astNodeFactory.createVariableDeclaration(declarations, "var");
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) {
3688 marker = markerCreate();
3690 expectKeyword(kind);
3692 declarations = parseVariableDeclarationList(kind);
3696 return markerApply(marker, astNodeFactory.createVariableDeclaration(declarations, kind));
3699 // 12.3 Empty Statement
3701 function parseEmptyStatement() {
3703 return astNodeFactory.createEmptyStatement();
3706 // 12.4 Expression Statement
3708 function parseExpressionStatement() {
3709 var expr = parseExpression();
3711 return astNodeFactory.createExpressionStatement(expr);
3714 // 12.5 If statement
3716 function parseIfStatement() {
3717 var test, consequent, alternate;
3719 expectKeyword("if");
3723 test = parseExpression();
3727 consequent = parseStatement();
3729 if (matchKeyword("else")) {
3731 alternate = parseStatement();
3736 return astNodeFactory.createIfStatement(test, consequent, alternate);
3739 // 12.6 Iteration Statements
3741 function parseDoWhileStatement() {
3742 var body, test, oldInIteration;
3744 expectKeyword("do");
3746 oldInIteration = state.inIteration;
3747 state.inIteration = true;
3749 body = parseStatement();
3751 state.inIteration = oldInIteration;
3753 expectKeyword("while");
3757 test = parseExpression();
3765 return astNodeFactory.createDoWhileStatement(test, body);
3768 function parseWhileStatement() {
3769 var test, body, oldInIteration;
3771 expectKeyword("while");
3775 test = parseExpression();
3779 oldInIteration = state.inIteration;
3780 state.inIteration = true;
3782 body = parseStatement();
3784 state.inIteration = oldInIteration;
3786 return astNodeFactory.createWhileStatement(test, body);
3789 function parseForVariableDeclaration() {
3790 var token, declarations,
3791 marker = markerCreate();
3794 declarations = parseVariableDeclarationList();
3796 return markerApply(marker, astNodeFactory.createVariableDeclaration(declarations, token.value));
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;
3804 init = test = update = null;
3806 expectKeyword("for");
3814 if (matchKeyword("var") ||
3815 (allowBlockBindings && (matchKeyword("let") || matchKeyword("const")))
3817 state.allowIn = false;
3818 init = parseForVariableDeclaration();
3819 state.allowIn = true;
3821 if (init.declarations.length === 1) {
3822 if (matchKeyword("in") || (allowForOf && matchContextualKeyword("of"))) {
3823 operator = lookahead;
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)) {
3829 right = parseExpression();
3836 state.allowIn = false;
3837 init = parseExpression();
3838 state.allowIn = true;
3840 if (allowForOf && matchContextualKeyword("of")) {
3843 right = parseExpression();
3845 } else if (matchKeyword("in")) {
3846 // LeftHandSideExpression
3847 if (!isLeftHandSide(init)) {
3848 throwErrorTolerant({}, Messages.InvalidLHSInForIn);
3853 right = parseExpression();
3858 if (typeof left === "undefined") {
3863 if (typeof left === "undefined") {
3866 test = parseExpression();
3871 update = parseExpression();
3877 oldInIteration = state.inIteration;
3878 state.inIteration = true;
3880 if (!(opts !== undefined && opts.ignoreBody)) {
3881 body = parseStatement();
3884 state.inIteration = oldInIteration;
3886 if (typeof left === "undefined") {
3887 return astNodeFactory.createForStatement(init, test, update, body);
3890 if (extra.ecmaFeatures.forOf && operator.value === "of") {
3891 return astNodeFactory.createForOfStatement(left, right, body);
3894 return astNodeFactory.createForInStatement(left, right, body);
3897 // 12.7 The continue statement
3899 function parseContinueStatement() {
3902 expectKeyword("continue");
3904 // Optimize the most common form: "continue;".
3905 if (source.charCodeAt(index) === 0x3B) {
3908 if (!state.inIteration) {
3909 throwError({}, Messages.IllegalContinue);
3912 return astNodeFactory.createContinueStatement(null);
3915 if (peekLineTerminator()) {
3916 if (!state.inIteration) {
3917 throwError({}, Messages.IllegalContinue);
3920 return astNodeFactory.createContinueStatement(null);
3923 if (lookahead.type === Token.Identifier) {
3924 label = parseVariableIdentifier();
3926 if (!state.labelSet.has(label.name)) {
3927 throwError({}, Messages.UnknownLabel, label.name);
3933 if (label === null && !state.inIteration) {
3934 throwError({}, Messages.IllegalContinue);
3937 return astNodeFactory.createContinueStatement(label);
3940 // 12.8 The break statement
3942 function parseBreakStatement() {
3945 expectKeyword("break");
3947 // Catch the very common case first: immediately a semicolon (U+003B).
3948 if (source.charCodeAt(index) === 0x3B) {
3951 if (!(state.inIteration || state.inSwitch)) {
3952 throwError({}, Messages.IllegalBreak);
3955 return astNodeFactory.createBreakStatement(null);
3958 if (peekLineTerminator()) {
3959 if (!(state.inIteration || state.inSwitch)) {
3960 throwError({}, Messages.IllegalBreak);
3963 return astNodeFactory.createBreakStatement(null);
3966 if (lookahead.type === Token.Identifier) {
3967 label = parseVariableIdentifier();
3969 if (!state.labelSet.has(label.name)) {
3970 throwError({}, Messages.UnknownLabel, label.name);
3976 if (label === null && !(state.inIteration || state.inSwitch)) {
3977 throwError({}, Messages.IllegalBreak);
3980 return astNodeFactory.createBreakStatement(label);
3983 // 12.9 The return statement
3985 function parseReturnStatement() {
3986 var argument = null;
3988 expectKeyword("return");
3990 if (!state.inFunctionBody && !extra.ecmaFeatures.globalReturn) {
3991 throwErrorTolerant({}, Messages.IllegalReturn);
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();
3999 return astNodeFactory.createReturnStatement(argument);
4003 if (peekLineTerminator()) {
4004 return astNodeFactory.createReturnStatement(null);
4008 if (!match("}") && lookahead.type !== Token.EOF) {
4009 argument = parseExpression();
4015 return astNodeFactory.createReturnStatement(argument);
4018 // 12.10 The with statement
4020 function parseWithStatement() {
4024 // TODO(ikarienator): Should we update the test cases instead?
4026 throwErrorTolerant({}, Messages.StrictModeWith);
4029 expectKeyword("with");
4033 object = parseExpression();
4037 body = parseStatement();
4039 return astNodeFactory.createWithStatement(object, body);
4042 // 12.10 The swith statement
4044 function parseSwitchCase() {
4045 var test, consequent = [], statement,
4046 marker = markerCreate();
4048 if (matchKeyword("default")) {
4052 expectKeyword("case");
4053 test = parseExpression();
4057 while (index < length) {
4058 if (match("}") || matchKeyword("default") || matchKeyword("case")) {
4061 statement = parseSourceElement();
4062 consequent.push(statement);
4065 return markerApply(marker, astNodeFactory.createSwitchCase(test, consequent));
4068 function parseSwitchStatement() {
4069 var discriminant, cases, clause, oldInSwitch, defaultFound;
4071 expectKeyword("switch");
4075 discriminant = parseExpression();
4085 return astNodeFactory.createSwitchStatement(discriminant, cases);
4088 oldInSwitch = state.inSwitch;
4089 state.inSwitch = true;
4090 defaultFound = false;
4092 while (index < length) {
4096 clause = parseSwitchCase();
4097 if (clause.test === null) {
4099 throwError({}, Messages.MultipleDefaultsInSwitch);
4101 defaultFound = true;
4106 state.inSwitch = oldInSwitch;
4110 return astNodeFactory.createSwitchStatement(discriminant, cases);
4113 // 12.13 The throw statement
4115 function parseThrowStatement() {
4118 expectKeyword("throw");
4120 if (peekLineTerminator()) {
4121 throwError({}, Messages.NewlineAfterThrow);
4124 argument = parseExpression();
4128 return astNodeFactory.createThrowStatement(argument);
4131 // 12.14 The try statement
4133 function parseCatchClause() {
4135 marker = markerCreate(),
4136 allowDestructuring = extra.ecmaFeatures.destructuring,
4138 paramSet: new StringMap()
4141 expectKeyword("catch");
4145 throwUnexpected(lookahead);
4149 if (!allowDestructuring) {
4150 throwUnexpected(lookahead);
4152 param = parseArrayInitialiser();
4153 reinterpretAsDestructuredParameter(options, param);
4154 } else if (match("{")) {
4156 if (!allowDestructuring) {
4157 throwUnexpected(lookahead);
4159 param = parseObjectInitialiser();
4160 reinterpretAsDestructuredParameter(options, param);
4162 param = parseVariableIdentifier();
4166 if (strict && param.name && syntax.isRestrictedWord(param.name)) {
4167 throwErrorTolerant({}, Messages.StrictCatchVariable);
4171 body = parseBlock();
4172 return markerApply(marker, astNodeFactory.createCatchClause(param, body));
4175 function parseTryStatement() {
4176 var block, handler = null, finalizer = null;
4178 expectKeyword("try");
4180 block = parseBlock();
4182 if (matchKeyword("catch")) {
4183 handler = parseCatchClause();
4186 if (matchKeyword("finally")) {
4188 finalizer = parseBlock();
4191 if (!handler && !finalizer) {
4192 throwError({}, Messages.NoCatchOrFinally);
4195 return astNodeFactory.createTryStatement(block, handler, finalizer);
4198 // 12.15 The debugger statement
4200 function parseDebuggerStatement() {
4201 expectKeyword("debugger");
4205 return astNodeFactory.createDebuggerStatement();
4210 function parseStatement() {
4211 var type = lookahead.type,
4216 if (type === Token.EOF) {
4217 throwUnexpected(lookahead);
4220 if (type === Token.Punctuator && lookahead.value === "{") {
4221 return parseBlock();
4224 marker = markerCreate();
4226 if (type === Token.Punctuator) {
4227 switch (lookahead.value) {
4229 return markerApply(marker, parseEmptyStatement());
4231 return parseBlock();
4233 return markerApply(marker, parseExpressionStatement());
4239 marker = markerCreate();
4241 if (type === Token.Keyword) {
4242 switch (lookahead.value) {
4244 return markerApply(marker, parseBreakStatement());
4246 return markerApply(marker, parseContinueStatement());
4248 return markerApply(marker, parseDebuggerStatement());
4250 return markerApply(marker, parseDoWhileStatement());
4252 return markerApply(marker, parseForStatement());
4254 return markerApply(marker, parseFunctionDeclaration());
4256 return markerApply(marker, parseIfStatement());
4258 return markerApply(marker, parseReturnStatement());
4260 return markerApply(marker, parseSwitchStatement());
4262 return markerApply(marker, parseThrowStatement());
4264 return markerApply(marker, parseTryStatement());
4266 return markerApply(marker, parseVariableStatement());
4268 return markerApply(marker, parseWhileStatement());
4270 return markerApply(marker, parseWithStatement());
4276 marker = markerCreate();
4277 expr = parseExpression();
4279 // 12.12 Labelled Statements
4280 if ((expr.type === astNodeTypes.Identifier) && match(":")) {
4283 if (state.labelSet.has(expr.name)) {
4284 throwError({}, Messages.Redeclaration, "Label", expr.name);
4287 state.labelSet.set(expr.name, true);
4288 labeledBody = parseStatement();
4289 state.labelSet.delete(expr.name);
4290 return markerApply(marker, astNodeFactory.createLabeledStatement(expr, labeledBody));
4295 return markerApply(marker, astNodeFactory.createExpressionStatement(expr));
4298 // 13 Function Definition
4300 // function parseConciseBody() {
4301 // if (match("{")) {
4302 // return parseFunctionSourceElements();
4304 // return parseAssignmentExpression();
4307 function parseFunctionSourceElements() {
4308 var sourceElement, sourceElements = [], token, directive, firstRestricted,
4309 oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, oldParenthesisCount,
4310 marker = markerCreate();
4314 while (index < length) {
4315 if (lookahead.type !== Token.StringLiteral) {
4320 sourceElement = parseSourceElement();
4321 sourceElements.push(sourceElement);
4322 if (sourceElement.expression.type !== astNodeTypes.Literal) {
4323 // this is not directive
4326 directive = source.slice(token.range[0] + 1, token.range[1] - 1);
4327 if (directive === "use strict") {
4330 if (firstRestricted) {
4331 throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
4334 if (!firstRestricted && token.octal) {
4335 firstRestricted = token;
4340 oldLabelSet = state.labelSet;
4341 oldInIteration = state.inIteration;
4342 oldInSwitch = state.inSwitch;
4343 oldInFunctionBody = state.inFunctionBody;
4344 oldParenthesisCount = state.parenthesizedCount;
4346 state.labelSet = new StringMap();
4347 state.inIteration = false;
4348 state.inSwitch = false;
4349 state.inFunctionBody = true;
4351 while (index < length) {
4357 sourceElement = parseSourceElement();
4359 if (typeof sourceElement === "undefined") {
4363 sourceElements.push(sourceElement);
4368 state.labelSet = oldLabelSet;
4369 state.inIteration = oldInIteration;
4370 state.inSwitch = oldInSwitch;
4371 state.inFunctionBody = oldInFunctionBody;
4372 state.parenthesizedCount = oldParenthesisCount;
4374 return markerApply(marker, astNodeFactory.createBlockStatement(sourceElements));
4377 function validateParam(options, param, name) {
4380 if (syntax.isRestrictedWord(name)) {
4381 options.stricted = param;
4382 options.message = Messages.StrictParamName;
4385 if (options.paramSet.has(name)) {
4386 options.stricted = param;
4387 options.message = Messages.StrictParamDupe;
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;
4401 options.paramSet.set(name, true);
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;
4412 if (token.value === "...") {
4413 if (!allowRestParams) {
4414 throwUnexpected(lookahead);
4421 if (!allowDestructuring) {
4422 throwUnexpected(lookahead);
4424 param = parseArrayInitialiser();
4425 reinterpretAsDestructuredParameter(options, param);
4426 } else if (match("{")) {
4428 throwError({}, Messages.ObjectPatternAsRestParameter);
4430 if (!allowDestructuring) {
4431 throwUnexpected(lookahead);
4433 param = parseObjectInitialiser();
4434 reinterpretAsDestructuredParameter(options, param);
4436 param = parseVariableIdentifier();
4437 validateParam(options, token, token.value);
4442 throwErrorTolerant(lookahead, Messages.DefaultRestParameter);
4444 if (allowDefaultParams || allowDestructuring) {
4446 def = parseAssignmentExpression();
4447 ++options.defaultCount;
4449 throwUnexpected(lookahead);
4455 throwError({}, Messages.ParameterAfterRestParameter);
4457 options.rest = param;
4461 options.params.push(param);
4462 options.defaults.push(def ? def : null); // TODO: determine if null or undefined (see: #55)
4468 function parseParams(firstRestricted) {
4476 firstRestricted: firstRestricted
4482 options.paramSet = new StringMap();
4483 while (index < length) {
4484 if (!parseParam(options)) {
4493 if (options.defaultCount === 0) {
4494 options.defaults = [];
4498 params: options.params,
4499 defaults: options.defaults,
4501 stricted: options.stricted,
4502 firstRestricted: options.firstRestricted,
4503 message: options.message
4507 function parseFunctionDeclaration(identifierIsOptional) {
4508 var id = null, body, token, tmp, firstRestricted, message, previousStrict, previousYieldAllowed, generator,
4509 marker = markerCreate(),
4510 allowGenerators = extra.ecmaFeatures.generators;
4512 expectKeyword("function");
4515 if (allowGenerators && match("*")) {
4520 if (!identifierIsOptional || !match("(")) {
4524 id = parseVariableIdentifier();
4527 if (syntax.isRestrictedWord(token.value)) {
4528 throwErrorTolerant(token, Messages.StrictFunctionName);
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;
4541 tmp = parseParams(firstRestricted);
4542 firstRestricted = tmp.firstRestricted;
4544 message = tmp.message;
4547 previousStrict = strict;
4548 previousYieldAllowed = state.yieldAllowed;
4549 state.yieldAllowed = generator;
4551 body = parseFunctionSourceElements();
4553 if (strict && firstRestricted) {
4554 throwError(firstRestricted, message);
4556 if (strict && tmp.stricted) {
4557 throwErrorTolerant(tmp.stricted, message);
4559 strict = previousStrict;
4560 state.yieldAllowed = previousYieldAllowed;
4564 astNodeFactory.createFunctionDeclaration(
4576 function parseFunctionExpression() {
4577 var token, id = null, firstRestricted, message, tmp, body, previousStrict, previousYieldAllowed, generator,
4578 marker = markerCreate(),
4579 allowGenerators = extra.ecmaFeatures.generators;
4581 expectKeyword("function");
4585 if (allowGenerators && match("*")) {
4592 id = parseVariableIdentifier();
4594 if (syntax.isRestrictedWord(token.value)) {
4595 throwErrorTolerant(token, Messages.StrictFunctionName);
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;
4608 tmp = parseParams(firstRestricted);
4609 firstRestricted = tmp.firstRestricted;
4611 message = tmp.message;
4614 previousStrict = strict;
4615 previousYieldAllowed = state.yieldAllowed;
4616 state.yieldAllowed = generator;
4618 body = parseFunctionSourceElements();
4620 if (strict && firstRestricted) {
4621 throwError(firstRestricted, message);
4623 if (strict && tmp.stricted) {
4624 throwErrorTolerant(tmp.stricted, message);
4626 strict = previousStrict;
4627 state.yieldAllowed = previousYieldAllowed;
4631 astNodeFactory.createFunctionExpression(
4643 function parseYieldExpression() {
4644 var yieldToken, delegateFlag, expr, marker = markerCreate();
4647 assert(yieldToken.value === "yield", "Called parseYieldExpression with non-yield lookahead.");
4649 if (!state.yieldAllowed) {
4650 throwErrorTolerant({}, Messages.IllegalYield);
4653 delegateFlag = false;
4656 delegateFlag = true;
4659 expr = parseAssignmentExpression();
4661 return markerApply(marker, astNodeFactory.createYieldExpression(expr, delegateFlag));
4664 // Modules grammar from:
4665 // people.mozilla.org/~jorendorff/es6-draft.html
4667 function parseModuleSpecifier() {
4668 var marker = markerCreate(),
4671 if (lookahead.type !== Token.StringLiteral) {
4672 throwError({}, Messages.InvalidModuleSpecifier);
4674 specifier = astNodeFactory.createLiteralFromSource(lex(), source);
4675 return markerApply(marker, specifier);
4678 function parseExportSpecifier() {
4679 var exported, local, marker = markerCreate();
4680 if (matchKeyword("default")) {
4682 local = markerApply(marker, astNodeFactory.createIdentifier("default"));
4683 // export {default} from "something";
4685 local = parseVariableIdentifier();
4687 if (matchContextualKeyword("as")) {
4689 exported = parseNonComputedProperty();
4691 return markerApply(marker, astNodeFactory.createExportSpecifier(local, exported));
4694 function parseExportNamedDeclaration() {
4695 var declaration = null,
4696 isExportFromIdentifier,
4697 src = null, specifiers = [],
4698 marker = markerCreate();
4700 expectKeyword("export");
4702 // non-default export
4703 if (lookahead.type === Token.Keyword) {
4705 // export var f = 1;
4706 switch (lookahead.value) {
4712 declaration = parseSourceElement();
4713 return markerApply(marker, astNodeFactory.createExportNamedDeclaration(declaration, specifiers, null));
4722 isExportFromIdentifier = isExportFromIdentifier || matchKeyword("default");
4723 specifiers.push(parseExportSpecifier());
4724 } while (match(",") && lex());
4728 if (matchContextualKeyword("from")) {
4730 // export {default} from "foo";
4731 // export {foo} from "foo";
4733 src = parseModuleSpecifier();
4735 } else if (isExportFromIdentifier) {
4737 // export {default}; // missing fromClause
4738 throwError({}, lookahead.value ?
4739 Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value);
4745 return markerApply(marker, astNodeFactory.createExportNamedDeclaration(declaration, specifiers, src));
4748 function parseExportDefaultDeclaration() {
4749 var declaration = null,
4751 possibleIdentifierToken,
4752 allowClasses = extra.ecmaFeatures.classes,
4753 marker = markerCreate();
4756 // export default ...
4757 expectKeyword("export");
4758 expectKeyword("default");
4760 if (matchKeyword("function") || matchKeyword("class")) {
4761 possibleIdentifierToken = lookahead2();
4762 if (possibleIdentifierToken.type === Token.Identifier) {
4764 // export default function foo () {}
4765 // export default class foo {}
4766 declaration = parseSourceElement();
4767 return markerApply(marker, astNodeFactory.createExportDefaultDeclaration(declaration));
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));
4781 if (matchContextualKeyword("from")) {
4782 throwError({}, Messages.UnexpectedToken, lookahead.value);
4786 // export default {};
4787 // export default [];
4788 // export default (1 + 2);
4790 expression = parseObjectInitialiser();
4791 } else if (match("[")) {
4792 expression = parseArrayInitialiser();
4794 expression = parseAssignmentExpression();
4797 return markerApply(marker, astNodeFactory.createExportDefaultDeclaration(expression));
4801 function parseExportAllDeclaration() {
4803 marker = markerCreate();
4806 // export * from "foo";
4807 expectKeyword("export");
4809 if (!matchContextualKeyword("from")) {
4810 throwError({}, lookahead.value ?
4811 Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value);
4814 src = parseModuleSpecifier();
4817 return markerApply(marker, astNodeFactory.createExportAllDeclaration(src));
4820 function parseExportDeclaration() {
4821 if (state.inFunctionBody) {
4822 throwError({}, Messages.IllegalExportDeclaration);
4824 var declarationType = lookahead2().value;
4825 if (declarationType === "default") {
4826 return parseExportDefaultDeclaration();
4827 } else if (declarationType === "*") {
4828 return parseExportAllDeclaration();
4830 return parseExportNamedDeclaration();
4834 function parseImportSpecifier() {
4835 // import {<foo as bar>} ...;
4836 var local, imported, marker = markerCreate();
4838 imported = parseNonComputedProperty();
4839 if (matchContextualKeyword("as")) {
4841 local = parseVariableIdentifier();
4844 return markerApply(marker, astNodeFactory.createImportSpecifier(local, imported));
4847 function parseNamedImports() {
4848 var specifiers = [];
4849 // {foo, bar as bas}
4853 specifiers.push(parseImportSpecifier());
4854 } while (match(",") && lex());
4860 function parseImportDefaultSpecifier() {
4861 // import <foo> ...;
4862 var local, marker = markerCreate();
4864 local = parseNonComputedProperty();
4866 return markerApply(marker, astNodeFactory.createImportDefaultSpecifier(local));
4869 function parseImportNamespaceSpecifier() {
4870 // import <* as foo> ...;
4871 var local, marker = markerCreate();
4874 if (!matchContextualKeyword("as")) {
4875 throwError({}, Messages.NoAsAfterImportNamespace);
4878 local = parseNonComputedProperty();
4880 return markerApply(marker, astNodeFactory.createImportNamespaceSpecifier(local));
4883 function parseImportDeclaration() {
4884 var specifiers, src, marker = markerCreate();
4886 if (state.inFunctionBody) {
4887 throwError({}, Messages.IllegalImportDeclaration);
4890 expectKeyword("import");
4893 if (lookahead.type === Token.StringLiteral) {
4896 src = parseModuleSpecifier();
4898 return markerApply(marker, astNodeFactory.createImportDeclaration(specifiers, src));
4901 if (!matchKeyword("default") && isIdentifierName(lookahead)) {
4905 specifiers.push(parseImportDefaultSpecifier());
4912 // import foo, * as foo
4914 specifiers.push(parseImportNamespaceSpecifier());
4915 } else if (match("{")) {
4917 // import foo, {bar}
4919 specifiers = specifiers.concat(parseNamedImports());
4922 if (!matchContextualKeyword("from")) {
4923 throwError({}, lookahead.value ?
4924 Messages.UnexpectedToken : Messages.MissingFromClause, lookahead.value);
4927 src = parseModuleSpecifier();
4930 return markerApply(marker, astNodeFactory.createImportDeclaration(specifiers, src));
4933 // 14 Functions and classes
4935 // 14.1 Functions is defined above (13 in ES5)
4936 // 14.2 Arrow Functions Definitions is defined in (7.3 assignments)
4938 // 14.3 Method Definitions
4941 // 14.5 Class Definitions
4943 function parseClassBody() {
4944 var hasConstructor = false, generator = false,
4945 allowGenerators = extra.ecmaFeatures.generators,
4946 token, isStatic, body = [], method, computed, key;
4948 var existingProps = {},
4949 topMarker = markerCreate(),
4952 existingProps.static = new StringMap();
4953 existingProps.prototype = new StringMap();
4957 while (!match("}")) {
4959 // extra semicolons are fine
4967 generator = match("*");
4968 computed = match("[");
4969 marker = markerCreate();
4972 if (!allowGenerators) {
4973 throwUnexpected(lookahead);
4978 key = parseObjectPropertyKey();
4980 // static generator methods
4981 if (key.name === "static" && match("*")) {
4982 if (!allowGenerators) {
4983 throwUnexpected(lookahead);
4989 if (key.name === "static" && lookaheadPropertyName()) {
4992 computed = match("[");
4993 key = parseObjectPropertyKey();
4997 method = parseGeneratorProperty(key, marker);
4999 method = tryParseMethodDefinition(token, key, computed, marker, generator);
5003 method.static = isStatic;
5004 if (method.kind === "init") {
5005 method.kind = "method";
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);
5013 if (hasConstructor) {
5014 throwUnexpected(token, Messages.DuplicateConstructor);
5016 hasConstructor = true;
5018 method.kind = "constructor";
5021 if (!method.computed && (method.key.name || method.key.value.toString()) === "prototype") {
5022 throwUnexpected(token, Messages.StaticPrototype);
5025 method.type = astNodeTypes.MethodDefinition;
5026 delete method.method;
5027 delete method.shorthand;
5030 throwUnexpected(lookahead);
5035 return markerApply(topMarker, astNodeFactory.createClassBody(body));
5038 function parseClassExpression() {
5039 var id = null, superClass = null, marker = markerCreate(),
5040 previousStrict = strict, classBody;
5042 // classes run in strict mode
5045 expectKeyword("class");
5047 if (lookahead.type === Token.Identifier) {
5048 id = parseVariableIdentifier();
5051 if (matchKeyword("extends")) {
5053 superClass = parseLeftHandSideExpressionAllowCall();
5056 classBody = parseClassBody();
5057 strict = previousStrict;
5059 return markerApply(marker, astNodeFactory.createClassExpression(id, superClass, classBody));
5062 function parseClassDeclaration(identifierIsOptional) {
5063 var id = null, superClass = null, marker = markerCreate(),
5064 previousStrict = strict, classBody;
5066 // classes run in strict mode
5069 expectKeyword("class");
5071 if (!identifierIsOptional || lookahead.type === Token.Identifier) {
5072 id = parseVariableIdentifier();
5075 if (matchKeyword("extends")) {
5077 superClass = parseLeftHandSideExpressionAllowCall();
5080 classBody = parseClassBody();
5081 strict = previousStrict;
5083 return markerApply(marker, astNodeFactory.createClassDeclaration(id, superClass, classBody));
5088 function parseSourceElement() {
5090 var allowClasses = extra.ecmaFeatures.classes,
5091 allowModules = extra.ecmaFeatures.modules,
5092 allowBlockBindings = extra.ecmaFeatures.blockBindings;
5094 if (lookahead.type === Token.Keyword) {
5095 switch (lookahead.value) {
5097 if (!allowModules) {
5098 throwErrorTolerant({}, Messages.IllegalExportDeclaration);
5100 return parseExportDeclaration();
5102 if (!allowModules) {
5103 throwErrorTolerant({}, Messages.IllegalImportDeclaration);
5105 return parseImportDeclaration();
5107 return parseFunctionDeclaration();
5110 return parseClassDeclaration();
5115 if (allowBlockBindings) {
5116 return parseConstLetDeclaration(lookahead.value);
5120 return parseStatement();
5124 if (lookahead.type !== Token.EOF) {
5125 return parseStatement();
5129 function parseSourceElements() {
5130 var sourceElement, sourceElements = [], token, directive, firstRestricted;
5132 while (index < length) {
5134 if (token.type !== Token.StringLiteral) {
5138 sourceElement = parseSourceElement();
5139 sourceElements.push(sourceElement);
5140 if (sourceElement.expression.type !== astNodeTypes.Literal) {
5141 // this is not directive
5144 directive = source.slice(token.range[0] + 1, token.range[1] - 1);
5145 if (directive === "use strict") {
5147 if (firstRestricted) {
5148 throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
5151 if (!firstRestricted && token.octal) {
5152 firstRestricted = token;
5157 while (index < length) {
5158 sourceElement = parseSourceElement();
5159 /* istanbul ignore if */
5160 if (typeof sourceElement === "undefined") {
5163 sourceElements.push(sourceElement);
5165 return sourceElements;
5168 function parseProgram() {
5171 isModule = !!extra.ecmaFeatures.modules;
5175 marker = markerCreate();
5178 body = parseSourceElements();
5179 return markerApply(marker, astNodeFactory.createProgram(body, isModule ? "module" : "script"));
5182 function filterTokenLocation() {
5183 var i, entry, token, tokens = [];
5185 for (i = 0; i < extra.tokens.length; ++i) {
5186 entry = extra.tokens[i];
5193 pattern: entry.regex.pattern,
5194 flags: entry.regex.flags
5198 token.range = entry.range;
5201 token.loc = entry.loc;
5206 extra.tokens = tokens;
5209 //------------------------------------------------------------------------------
5211 //------------------------------------------------------------------------------
5213 function tokenize(code, options) {
5218 if (typeof code !== "string" && !(code instanceof String)) {
5219 code = toString(code);
5224 lineNumber = (source.length > 0) ? 1 : 0;
5226 length = source.length;
5231 parenthesisCount: 0,
5232 inFunctionBody: false,
5235 lastCommentStart: -1,
5236 yieldAllowed: false,
5239 inJSXSpreadAttribute: false,
5245 ecmaFeatures: defaultFeatures
5248 // Options matching.
5249 options = options || {};
5251 // Of course we collect tokens here.
5252 options.tokens = true;
5254 extra.tokenize = true;
5256 // The following two fields are necessary to compute the Regex tokens.
5257 extra.openParenToken = -1;
5258 extra.openCurlyToken = -1;
5260 extra.range = (typeof options.range === "boolean") && options.range;
5261 extra.loc = (typeof options.loc === "boolean") && options.loc;
5263 if (typeof options.comment === "boolean" && options.comment) {
5264 extra.comments = [];
5266 if (typeof options.tolerant === "boolean" && options.tolerant) {
5270 // apply parsing flags
5271 if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") {
5272 extra.ecmaFeatures = options.ecmaFeatures;
5277 if (lookahead.type === Token.EOF) {
5278 return extra.tokens;
5282 while (lookahead.type !== Token.EOF) {
5285 } catch (lexError) {
5287 extra.errors.push(lexError);
5288 // We have to break on the first error
5289 // to avoid infinite loops.
5297 filterTokenLocation();
5298 tokens = extra.tokens;
5300 if (typeof extra.comments !== "undefined") {
5301 tokens.comments = extra.comments;
5303 if (typeof extra.errors !== "undefined") {
5304 tokens.errors = extra.errors;
5314 //------------------------------------------------------------------------------
5316 //------------------------------------------------------------------------------
5318 function parse(code, options) {
5319 var program, toString;
5322 if (typeof code !== "string" && !(code instanceof String)) {
5323 code = toString(code);
5328 lineNumber = (source.length > 0) ? 1 : 0;
5330 length = source.length;
5334 labelSet: new StringMap(),
5335 parenthesisCount: 0,
5336 inFunctionBody: false,
5339 lastCommentStart: -1,
5340 yieldAllowed: false,
5343 inJSXSpreadAttribute: false,
5349 ecmaFeatures: Object.create(defaultFeatures)
5352 // for template strings
5353 state.curlyStack = [];
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;
5360 if (extra.loc && options.source !== null && options.source !== undefined) {
5361 extra.source = toString(options.source);
5364 if (typeof options.tokens === "boolean" && options.tokens) {
5367 if (typeof options.comment === "boolean" && options.comment) {
5368 extra.comments = [];
5370 if (typeof options.tolerant === "boolean" && options.tolerant) {
5373 if (extra.attachComment) {
5375 extra.comments = [];
5376 commentAttachment.reset();
5379 if (options.sourceType === "module") {
5380 extra.ecmaFeatures = {
5381 arrowFunctions: true,
5382 blockBindings: true,
5385 templateStrings: true,
5386 binaryLiterals: true,
5387 octalLiterals: true,
5388 unicodeCodePointEscapes: true,
5389 superInFunctions: true,
5390 defaultParams: true,
5393 objectLiteralComputedProperties: true,
5394 objectLiteralShorthandMethods: true,
5395 objectLiteralShorthandProperties: true,
5396 objectLiteralDuplicateProperties: true,
5398 destructuring: true,
5404 // apply parsing flags after sourceType to allow overriding
5405 if (options.ecmaFeatures && typeof options.ecmaFeatures === "object") {
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];
5413 extra.ecmaFeatures = options.ecmaFeatures;
5420 program = parseProgram();
5421 if (typeof extra.comments !== "undefined") {
5422 program.comments = extra.comments;
5424 if (typeof extra.tokens !== "undefined") {
5425 filterTokenLocation();
5426 program.tokens = extra.tokens;
5428 if (typeof extra.errors !== "undefined") {
5429 program.errors = extra.errors;
5440 //------------------------------------------------------------------------------
5442 //------------------------------------------------------------------------------
5444 exports.version = require("./package.json").version;
5446 exports.tokenize = tokenize;
5448 exports.parse = parse;
5451 /* istanbul ignore next */
5452 exports.Syntax = (function () {
5453 var name, types = {};
5455 if (typeof Object.create === "function") {
5456 types = Object.create(null);
5459 for (name in astNodeTypes) {
5460 if (astNodeTypes.hasOwnProperty(name)) {
5461 types[name] = astNodeTypes[name];
5465 if (typeof Object.freeze === "function") {
5466 Object.freeze(types);