From e72f1663b1ba35e2c94b0aca244806ceb47460b3 Mon Sep 17 00:00:00 2001 From: Jeff Williams Date: Sun, 6 Apr 2014 08:57:27 -0700 Subject: [PATCH] use Esprima's harmony branch (#555) --- lib/jsdoc/src/astnode.js | 6 +- node_modules/esprima/esprima.js | 4451 +++++++++++++++++++---------- node_modules/esprima/package.json | 46 +- package.json | 2 +- 4 files changed, 2899 insertions(+), 1606 deletions(-) diff --git a/lib/jsdoc/src/astnode.js b/lib/jsdoc/src/astnode.js index da0dd16e..9a9b43d3 100644 --- a/lib/jsdoc/src/astnode.js +++ b/lib/jsdoc/src/astnode.js @@ -203,12 +203,14 @@ var nodeToString = exports.nodeToString = function(node) { // valid postfix operator (such as -- or ++) is not a UnaryExpression. str = nodeToString(node.argument); - if (node.prefix) { + // workaround for https://code.google.com/p/esprima/issues/detail?id=526 + if (node.prefix === true || node.prefix === undefined) { str = node.operator + str; } else { // this shouldn't happen - throw new Error('Found a UnaryExpression with a postfix operator: %j', node); + throw new Error( util.format('Found a UnaryExpression with a postfix operator: %j', + node) ); } break; diff --git a/node_modules/esprima/esprima.js b/node_modules/esprima/esprima.js index f1320daf..4ccd8483 100644 --- a/node_modules/esprima/esprima.js +++ b/node_modules/esprima/esprima.js @@ -1,4 +1,6 @@ /* + Copyright (C) 2013 Ariya Hidayat + Copyright (C) 2013 Thaddee Tyl Copyright (C) 2012 Ariya Hidayat Copyright (C) 2012 Mathias Bynens Copyright (C) 2012 Joost-Wim Boekesteijn @@ -30,12 +32,18 @@ /*jslint bitwise:true plusplus:true */ /*global esprima:true, define:true, exports:true, window: true, -throwError: true, createLiteral: true, generateStatement: true, -parseAssignmentExpression: true, parseBlock: true, parseExpression: true, +throwError: true, generateStatement: true, peek: true, +parseAssignmentExpression: true, parseBlock: true, +parseClassExpression: true, parseClassDeclaration: true, parseExpression: true, +parseForStatement: true, parseFunctionDeclaration: true, parseFunctionExpression: true, parseFunctionSourceElements: true, parseVariableIdentifier: true, -parseLeftHandSideExpression: true, -parseStatement: true, parseSourceElement: true */ +parseImportSpecifier: true, +parseLeftHandSideExpression: true, parseParams: true, validateParam: true, +parseSpreadOrAssignmentExpression: true, +parseStatement: true, parseSourceElement: true, parseModuleBlock: true, parseConciseBody: true, +parseYieldExpression: true +*/ (function (root, factory) { 'use strict'; @@ -54,17 +62,21 @@ parseStatement: true, parseSourceElement: true */ var Token, TokenName, + FnExprTokens, Syntax, PropertyKind, Messages, Regex, + SyntaxTreeDelegate, + ClassPropertyType, source, strict, index, lineNumber, lineStart, length, - buffer, + delegate, + lookahead, state, extra; @@ -76,7 +88,9 @@ parseStatement: true, parseSourceElement: true */ NullLiteral: 5, NumericLiteral: 6, Punctuator: 7, - StringLiteral: 8 + StringLiteral: 8, + RegularExpression: 9, + Template: 10 }; TokenName = {}; @@ -88,39 +102,72 @@ parseStatement: true, parseSourceElement: true */ TokenName[Token.NumericLiteral] = 'Numeric'; TokenName[Token.Punctuator] = 'Punctuator'; TokenName[Token.StringLiteral] = 'String'; + TokenName[Token.RegularExpression] = 'RegularExpression'; + + // A function following one of those tokens is an expression. + FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new', + 'return', 'case', 'delete', 'throw', 'void', + // assignment operators + '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=', + '&=', '|=', '^=', ',', + // binary/unary operators + '+', '-', '*', '/', '%', '++', '--', '<<', '>>', '>>>', '&', + '|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=', + '<=', '<', '>', '!=', '!==']; Syntax = { - AssignmentExpression: 'AssignmentExpression', ArrayExpression: 'ArrayExpression', - BlockStatement: 'BlockStatement', + ArrayPattern: 'ArrayPattern', + ArrowFunctionExpression: 'ArrowFunctionExpression', + AssignmentExpression: 'AssignmentExpression', BinaryExpression: 'BinaryExpression', + BlockStatement: 'BlockStatement', BreakStatement: 'BreakStatement', CallExpression: 'CallExpression', CatchClause: 'CatchClause', + ClassBody: 'ClassBody', + ClassDeclaration: 'ClassDeclaration', + ClassExpression: 'ClassExpression', + ClassHeritage: 'ClassHeritage', + ComprehensionBlock: 'ComprehensionBlock', + ComprehensionExpression: 'ComprehensionExpression', ConditionalExpression: 'ConditionalExpression', ContinueStatement: 'ContinueStatement', - DoWhileStatement: 'DoWhileStatement', DebuggerStatement: 'DebuggerStatement', + DoWhileStatement: 'DoWhileStatement', EmptyStatement: 'EmptyStatement', + ExportDeclaration: 'ExportDeclaration', + ExportBatchSpecifier: 'ExportBatchSpecifier', + ExportSpecifier: 'ExportSpecifier', ExpressionStatement: 'ExpressionStatement', - ForStatement: 'ForStatement', ForInStatement: 'ForInStatement', + ForOfStatement: 'ForOfStatement', + ForStatement: 'ForStatement', FunctionDeclaration: 'FunctionDeclaration', FunctionExpression: 'FunctionExpression', Identifier: 'Identifier', IfStatement: 'IfStatement', - Literal: 'Literal', + ImportDeclaration: 'ImportDeclaration', + ImportSpecifier: 'ImportSpecifier', LabeledStatement: 'LabeledStatement', + Literal: 'Literal', LogicalExpression: 'LogicalExpression', MemberExpression: 'MemberExpression', + MethodDefinition: 'MethodDefinition', + ModuleDeclaration: 'ModuleDeclaration', NewExpression: 'NewExpression', ObjectExpression: 'ObjectExpression', + ObjectPattern: 'ObjectPattern', Program: 'Program', Property: 'Property', ReturnStatement: 'ReturnStatement', SequenceExpression: 'SequenceExpression', - SwitchStatement: 'SwitchStatement', + SpreadElement: 'SpreadElement', SwitchCase: 'SwitchCase', + SwitchStatement: 'SwitchStatement', + TaggedTemplateExpression: 'TaggedTemplateExpression', + TemplateElement: 'TemplateElement', + TemplateLiteral: 'TemplateLiteral', ThisExpression: 'ThisExpression', ThrowStatement: 'ThrowStatement', TryStatement: 'TryStatement', @@ -129,7 +176,8 @@ parseStatement: true, parseSourceElement: true */ VariableDeclaration: 'VariableDeclaration', VariableDeclarator: 'VariableDeclarator', WhileStatement: 'WhileStatement', - WithStatement: 'WithStatement' + WithStatement: 'WithStatement', + YieldExpression: 'YieldExpression' }; PropertyKind = { @@ -138,6 +186,11 @@ parseStatement: true, parseSourceElement: true */ Set: 4 }; + ClassPropertyType = { + 'static': 'static', + prototype: 'prototype' + }; + // Error messages should be identical to V8. Messages = { UnexpectedToken: 'Unexpected token %0', @@ -145,11 +198,13 @@ parseStatement: true, parseSourceElement: true */ UnexpectedString: 'Unexpected string', UnexpectedIdentifier: 'Unexpected identifier', UnexpectedReserved: 'Unexpected reserved word', + UnexpectedTemplate: 'Unexpected quasi %0', UnexpectedEOS: 'Unexpected end of input', NewlineAfterThrow: 'Illegal newline after throw', InvalidRegExp: 'Invalid regular expression', UnterminatedRegExp: 'Invalid regular expression: missing /', InvalidLHSInAssignment: 'Invalid left-hand side in assignment', + InvalidLHSInFormalsList: 'Invalid left-hand side in formals list', InvalidLHSInForIn: 'Invalid left-hand side in for-in', MultipleDefaultsInSwitch: 'More than one default clause in switch statement', NoCatchOrFinally: 'Missing catch or finally after try', @@ -157,12 +212,20 @@ parseStatement: true, parseSourceElement: true */ Redeclaration: '%0 \'%1\' has already been declared', IllegalContinue: 'Illegal continue statement', IllegalBreak: 'Illegal break statement', + IllegalDuplicateClassProperty: 'Illegal duplicate property in class definition', IllegalReturn: 'Illegal return statement', + IllegalYield: 'Illegal yield expression', + IllegalSpread: 'Illegal spread element', StrictModeWith: 'Strict mode code may not include a with statement', StrictCatchVariable: 'Catch variable may not be eval or arguments in strict mode', StrictVarName: 'Variable name may not be eval or arguments in strict mode', StrictParamName: 'Parameter name eval or arguments is not allowed in strict mode', StrictParamDupe: 'Strict mode function may not have duplicate parameter names', + ParameterAfterRestParameter: 'Rest parameter must be final parameter of an argument list', + DefaultRestParameter: 'Rest parameter can not have a default value', + ElementAfterSpreadElement: 'Spread must be the final element of an element list', + ObjectPatternAsRestParameter: 'Invalid rest parameter', + ObjectPatternAsSpread: 'Invalid spread argument', StrictFunctionName: 'Function name may not be eval or arguments in strict mode', StrictOctalLiteral: 'Octal literals are not allowed in strict mode.', StrictDelete: 'Delete of an unqualified identifier in strict mode.', @@ -172,7 +235,15 @@ parseStatement: true, parseSourceElement: true */ StrictLHSAssignment: 'Assignment to eval or arguments is not allowed in strict mode', StrictLHSPostfix: 'Postfix increment/decrement may not have eval or arguments operand in strict mode', StrictLHSPrefix: 'Prefix increment/decrement may not have eval or arguments operand in strict mode', - StrictReservedWord: 'Use of future reserved word in strict mode' + StrictReservedWord: 'Use of future reserved word in strict mode', + NewlineAfterModule: 'Illegal newline after module', + NoFromAfterImport: 'Missing from after import', + InvalidModuleSpecifier: 'Invalid module specifier', + NestedModule: 'Module declaration can not be nested', + NoUnintializedConst: 'Const must be initialized', + ComprehensionRequiresBlock: 'Comprehension must have at least one block', + ComprehensionError: 'Comprehension Error', + EachNotAllowed: 'Each is not supported' }; // See also tools/generate-unicode-regex.py. @@ -192,18 +263,8 @@ parseStatement: true, parseSourceElement: true */ } } - function sliceSource(from, to) { - return source.slice(from, to); - } - - if (typeof 'esprima'[0] === 'undefined') { - sliceSource = function sliceArraySource(from, to) { - return source.slice(from, to).join(''); - }; - } - function isDecimalDigit(ch) { - return '0123456789'.indexOf(ch) >= 0; + return (ch >= 48 && ch <= 57); // 0..9 } function isHexDigit(ch) { @@ -218,39 +279,43 @@ parseStatement: true, parseSourceElement: true */ // 7.2 White Space function isWhiteSpace(ch) { - return (ch === ' ') || (ch === '\u0009') || (ch === '\u000B') || - (ch === '\u000C') || (ch === '\u00A0') || - (ch.charCodeAt(0) >= 0x1680 && - '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(ch) >= 0); + return (ch === 32) || // space + (ch === 9) || // tab + (ch === 0xB) || + (ch === 0xC) || + (ch === 0xA0) || + (ch >= 0x1680 && '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\uFEFF'.indexOf(String.fromCharCode(ch)) > 0); } // 7.3 Line Terminators function isLineTerminator(ch) { - return (ch === '\n' || ch === '\r' || ch === '\u2028' || ch === '\u2029'); + return (ch === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029); } // 7.6 Identifier Names and Identifiers function isIdentifierStart(ch) { - return (ch === '$') || (ch === '_') || (ch === '\\') || - (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || - ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierStart.test(ch)); + return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore) + (ch >= 65 && ch <= 90) || // A..Z + (ch >= 97 && ch <= 122) || // a..z + (ch === 92) || // \ (backslash) + ((ch >= 0x80) && Regex.NonAsciiIdentifierStart.test(String.fromCharCode(ch))); } function isIdentifierPart(ch) { - return (ch === '$') || (ch === '_') || (ch === '\\') || - (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || - ((ch >= '0') && (ch <= '9')) || - ((ch.charCodeAt(0) >= 0x80) && Regex.NonAsciiIdentifierPart.test(ch)); + return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore) + (ch >= 65 && ch <= 90) || // A..Z + (ch >= 97 && ch <= 122) || // a..z + (ch >= 48 && ch <= 57) || // 0..9 + (ch === 92) || // \ (backslash) + ((ch >= 0x80) && Regex.NonAsciiIdentifierPart.test(String.fromCharCode(ch))); } // 7.6.1.2 Future Reserved Words function isFutureReservedWord(id) { switch (id) { - - // Future reserved words. case 'class': case 'enum': case 'export': @@ -258,15 +323,13 @@ parseStatement: true, parseSourceElement: true */ case 'import': case 'super': return true; + default: + return false; } - - return false; } function isStrictModeReservedWord(id) { switch (id) { - - // Strict Mode reserved words. case 'implements': case 'interface': case 'package': @@ -277,9 +340,9 @@ parseStatement: true, parseSourceElement: true */ case 'yield': case 'let': return true; + default: + return false; } - - return false; } function isRestrictedWord(id) { @@ -289,55 +352,40 @@ parseStatement: true, parseSourceElement: true */ // 7.6.1.1 Keywords function isKeyword(id) { - var keyword = false; - switch (id.length) { - case 2: - keyword = (id === 'if') || (id === 'in') || (id === 'do'); - break; - case 3: - keyword = (id === 'var') || (id === 'for') || (id === 'new') || (id === 'try'); - break; - case 4: - keyword = (id === 'this') || (id === 'else') || (id === 'case') || (id === 'void') || (id === 'with'); - break; - case 5: - keyword = (id === 'while') || (id === 'break') || (id === 'catch') || (id === 'throw'); - break; - case 6: - keyword = (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch'); - break; - case 7: - keyword = (id === 'default') || (id === 'finally'); - break; - case 8: - keyword = (id === 'function') || (id === 'continue') || (id === 'debugger'); - break; - case 10: - keyword = (id === 'instanceof'); - break; - } - - if (keyword) { - return true; - } - - switch (id) { - // Future reserved words. - // 'const' is specialized as Keyword in V8. - case 'const': - return true; - - // For compatiblity to SpiderMonkey and ES.next - case 'yield': - case 'let': - return true; - } - if (strict && isStrictModeReservedWord(id)) { return true; } - return isFutureReservedWord(id); + // 'const' is specialized as Keyword in V8. + // 'yield' is only treated as a keyword in strict mode. + // 'let' is for compatiblity with SpiderMonkey and ES.next. + // Some others are from future reserved words. + + switch (id.length) { + case 2: + return (id === 'if') || (id === 'in') || (id === 'do'); + case 3: + return (id === 'var') || (id === 'for') || (id === 'new') || + (id === 'try') || (id === 'let'); + case 4: + return (id === 'this') || (id === 'else') || (id === 'case') || + (id === 'void') || (id === 'with') || (id === 'enum'); + case 5: + return (id === 'while') || (id === 'break') || (id === 'catch') || + (id === 'throw') || (id === 'const') || + (id === 'class') || (id === 'super'); + case 6: + return (id === 'return') || (id === 'typeof') || (id === 'delete') || + (id === 'switch') || (id === 'export') || (id === 'import'); + case 7: + return (id === 'default') || (id === 'finally') || (id === 'extends'); + case 8: + return (id === 'function') || (id === 'continue') || (id === 'debugger'); + case 10: + return (id === 'instanceof'); + default: + return false; + } } // 7.4 Comments @@ -349,13 +397,13 @@ parseStatement: true, parseSourceElement: true */ lineComment = false; while (index < length) { - ch = source[index]; + ch = source.charCodeAt(index); if (lineComment) { - ch = source[index++]; + ++index; if (isLineTerminator(ch)) { lineComment = false; - if (ch === '\r' && source[index] === '\n') { + if (ch === 13 && source.charCodeAt(index) === 10) { ++index; } ++lineNumber; @@ -363,7 +411,7 @@ parseStatement: true, parseSourceElement: true */ } } else if (blockComment) { if (isLineTerminator(ch)) { - if (ch === '\r' && source[index + 1] === '\n') { + if (ch === 13 && source.charCodeAt(index + 1) === 10) { ++index; } ++lineNumber; @@ -373,24 +421,27 @@ parseStatement: true, parseSourceElement: true */ throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } } else { - ch = source[index++]; + ch = source.charCodeAt(index++); if (index >= length) { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } - if (ch === '*') { - ch = source[index]; - if (ch === '/') { + // Block comment ends with '*/' (char #42, char #47). + if (ch === 42) { + ch = source.charCodeAt(index); + if (ch === 47) { ++index; blockComment = false; } } } - } else if (ch === '/') { - ch = source[index + 1]; - if (ch === '/') { + } else if (ch === 47) { + ch = source.charCodeAt(index + 1); + // Line comment starts with '//' (char #47, char #47). + if (ch === 47) { index += 2; lineComment = true; - } else if (ch === '*') { + } else if (ch === 42) { + // Block comment starts with '/*' (char #47, char #42). index += 2; blockComment = true; if (index >= length) { @@ -403,7 +454,7 @@ parseStatement: true, parseSourceElement: true */ ++index; } else if (isLineTerminator(ch)) { ++index; - if (ch === '\r' && source[index] === '\n') { + if (ch === 13 && source.charCodeAt(index) === 10) { ++index; } ++lineNumber; @@ -429,111 +480,128 @@ parseStatement: true, parseSourceElement: true */ return String.fromCharCode(code); } - function scanIdentifier() { - var ch, start, id, restore; + function scanUnicodeCodePointEscape() { + var ch, code, cu1, cu2; ch = source[index]; - if (!isIdentifierStart(ch)) { - return; - } + code = 0; - start = index; - if (ch === '\\') { - ++index; - if (source[index] !== 'u') { - return; - } - ++index; - restore = index; - ch = scanHexEscape('u'); - if (ch) { - if (ch === '\\' || !isIdentifierStart(ch)) { - return; - } - id = ch; - } else { - index = restore; - id = 'u'; - } - } else { - id = source[index++]; + // At least, one hex digit is required. + if (ch === '}') { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } while (index < length) { - ch = source[index]; + ch = source[index++]; + if (!isHexDigit(ch)) { + break; + } + code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase()); + } + + if (code > 0x10FFFF || ch !== '}') { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + + // UTF-16 Encoding + if (code <= 0xFFFF) { + return String.fromCharCode(code); + } + cu1 = ((code - 0x10000) >> 10) + 0xD800; + cu2 = ((code - 0x10000) & 1023) + 0xDC00; + return String.fromCharCode(cu1, cu2); + } + + function getEscapedIdentifier() { + var ch, id; + + ch = source.charCodeAt(index++); + id = String.fromCharCode(ch); + + // '\u' (char #92, char #117) denotes an escaped character. + if (ch === 92) { + if (source.charCodeAt(index) !== 117) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + ++index; + ch = scanHexEscape('u'); + if (!ch || ch === '\\' || !isIdentifierStart(ch.charCodeAt(0))) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + id = ch; + } + + while (index < length) { + ch = source.charCodeAt(index); if (!isIdentifierPart(ch)) { break; } - if (ch === '\\') { - ++index; - if (source[index] !== 'u') { - return; + ++index; + id += String.fromCharCode(ch); + + // '\u' (char #92, char #117) denotes an escaped character. + if (ch === 92) { + id = id.substr(0, id.length - 1); + if (source.charCodeAt(index) !== 117) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } ++index; - restore = index; ch = scanHexEscape('u'); - if (ch) { - if (ch === '\\' || !isIdentifierPart(ch)) { - return; - } - id += ch; - } else { - index = restore; - id += 'u'; + if (!ch || ch === '\\' || !isIdentifierPart(ch.charCodeAt(0))) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } - } else { - id += source[index++]; + id += ch; } } + return id; + } + + function getIdentifier() { + var start, ch; + + start = index++; + while (index < length) { + ch = source.charCodeAt(index); + if (ch === 92) { + // Blackslash (char #92) marks Unicode escape sequence. + index = start; + return getEscapedIdentifier(); + } + if (isIdentifierPart(ch)) { + ++index; + } else { + break; + } + } + + return source.slice(start, index); + } + + function scanIdentifier() { + var start, id, type; + + start = index; + + // Backslash (char #92) starts an escaped character. + id = (source.charCodeAt(index) === 92) ? getEscapedIdentifier() : getIdentifier(); + // There is no keyword or literal with only one character. // Thus, it must be an identifier. if (id.length === 1) { - return { - type: Token.Identifier, - value: id, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - if (isKeyword(id)) { - return { - type: Token.Keyword, - value: id, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - // 7.8.1 Null Literals - - if (id === 'null') { - return { - type: Token.NullLiteral, - value: id, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - // 7.8.2 Boolean Literals - - if (id === 'true' || id === 'false') { - return { - type: Token.BooleanLiteral, - value: id, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; + type = Token.Identifier; + } else if (isKeyword(id)) { + type = Token.Keyword; + } else if (id === 'null') { + type = Token.NullLiteral; + } else if (id === 'true' || id === 'false') { + type = Token.BooleanLiteral; + } else { + type = Token.Identifier; } return { - type: Token.Identifier, + type: type, value: id, lineNumber: lineNumber, lineStart: lineStart, @@ -541,55 +609,97 @@ parseStatement: true, parseSourceElement: true */ }; } + // 7.7 Punctuators function scanPunctuator() { var start = index, + code = source.charCodeAt(index), + code2, ch1 = source[index], ch2, ch3, ch4; + switch (code) { // Check for most common single-character punctuators. - - if (ch1 === ';' || ch1 === '{' || ch1 === '}') { + case 40: // ( open bracket + case 41: // ) close bracket + case 59: // ; semicolon + case 44: // , comma + case 123: // { open curly brace + case 125: // } close curly brace + case 91: // [ + case 93: // ] + case 58: // : + case 63: // ? + case 126: // ~ ++index; + if (extra.tokenize) { + if (code === 40) { + extra.openParenToken = extra.tokens.length; + } else if (code === 123) { + extra.openCurlyToken = extra.tokens.length; + } + } return { type: Token.Punctuator, - value: ch1, + value: String.fromCharCode(code), lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; - } - if (ch1 === ',' || ch1 === '(' || ch1 === ')') { - ++index; - return { - type: Token.Punctuator, - value: ch1, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } + default: + code2 = source.charCodeAt(index + 1); - // Dot (.) can also start a floating-point number, hence the need - // to check the next character. + // '=' (char #61) marks an assignment or comparison operator. + if (code2 === 61) { + switch (code) { + case 37: // % + case 38: // & + case 42: // *: + case 43: // + + case 45: // - + case 47: // / + case 60: // < + case 62: // > + case 94: // ^ + case 124: // | + index += 2; + return { + type: Token.Punctuator, + value: String.fromCharCode(code) + String.fromCharCode(code2), + lineNumber: lineNumber, + lineStart: lineStart, + range: [start, index] + }; - ch2 = source[index + 1]; - if (ch1 === '.' && !isDecimalDigit(ch2)) { - return { - type: Token.Punctuator, - value: source[index++], - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; + case 33: // ! + case 61: // = + index += 2; + + // !== and === + if (source.charCodeAt(index) === 61) { + ++index; + } + return { + type: Token.Punctuator, + value: source.slice(start, index), + lineNumber: lineNumber, + lineStart: lineStart, + range: [start, index] + }; + default: + break; + } + } + break; } // Peek more characters. + ch2 = source[index + 1]; ch3 = source[index + 2]; ch4 = source[index + 3]; @@ -610,28 +720,6 @@ parseStatement: true, parseSourceElement: true */ // 3-character punctuators: === !== >>> <<= >>= - if (ch1 === '=' && ch2 === '=' && ch3 === '=') { - index += 3; - return { - type: Token.Punctuator, - value: '===', - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - - if (ch1 === '!' && ch2 === '=' && ch3 === '=') { - index += 3; - return { - type: Token.Punctuator, - value: '!==', - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - if (ch1 === '>' && ch2 === '>' && ch3 === '>') { index += 3; return { @@ -665,55 +753,138 @@ parseStatement: true, parseSourceElement: true */ }; } - // 2-character punctuators: <= >= == != ++ -- << >> && || - // += -= *= %= &= |= ^= /= - - if (ch2 === '=') { - if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) { - index += 2; - return { - type: Token.Punctuator, - value: ch1 + ch2, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - } - - if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) { - if ('+-<>&|'.indexOf(ch2) >= 0) { - index += 2; - return { - type: Token.Punctuator, - value: ch1 + ch2, - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } - } - - // The remaining 1-character punctuators. - - if ('[]<>+-*%&|^!~?:=/'.indexOf(ch1) >= 0) { + if (ch1 === '.' && ch2 === '.' && ch3 === '.') { + index += 3; return { type: Token.Punctuator, - value: source[index++], + value: '...', lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } + + // Other 2-character punctuators: ++ -- << >> && || + + if (ch1 === ch2 && ('+-<>&|'.indexOf(ch1) >= 0)) { + index += 2; + return { + type: Token.Punctuator, + value: ch1 + ch2, + lineNumber: lineNumber, + lineStart: lineStart, + range: [start, index] + }; + } + + if (ch1 === '=' && ch2 === '>') { + index += 2; + return { + type: Token.Punctuator, + value: '=>', + lineNumber: lineNumber, + lineStart: lineStart, + range: [start, index] + }; + } + + if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) { + ++index; + return { + type: Token.Punctuator, + value: ch1, + lineNumber: lineNumber, + lineStart: lineStart, + range: [start, index] + }; + } + + if (ch1 === '.') { + ++index; + return { + type: Token.Punctuator, + value: ch1, + lineNumber: lineNumber, + lineStart: lineStart, + range: [start, index] + }; + } + + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } // 7.8.3 Numeric Literals + function scanHexLiteral(start) { + var number = ''; + + while (index < length) { + if (!isHexDigit(source[index])) { + break; + } + number += source[index++]; + } + + if (number.length === 0) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + + if (isIdentifierStart(source.charCodeAt(index))) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + + return { + type: Token.NumericLiteral, + value: parseInt('0x' + number, 16), + lineNumber: lineNumber, + lineStart: lineStart, + range: [start, index] + }; + } + + function scanOctalLiteral(prefix, start) { + var number, octal; + + if (isOctalDigit(prefix)) { + octal = true; + number = '0' + source[index++]; + } else { + octal = false; + ++index; + number = ''; + } + + while (index < length) { + if (!isOctalDigit(source[index])) { + break; + } + number += source[index++]; + } + + if (!octal && number.length === 0) { + // only 0o or 0O + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + + if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + + return { + type: Token.NumericLiteral, + value: parseInt(number, 8), + octal: octal, + lineNumber: lineNumber, + lineStart: lineStart, + range: [start, index] + }; + } + function scanNumericLiteral() { - var number, start, ch; + var number, start, ch, octal; ch = source[index]; - assert(isDecimalDigit(ch) || (ch === '.'), + assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'), 'Numeric literal must start with a decimal digit or a decimal point'); start = index; @@ -724,85 +895,65 @@ parseStatement: true, parseSourceElement: true */ // Hex number starts with '0x'. // Octal number starts with '0'. + // Octal number in ES6 starts with '0o'. + // Binary number in ES6 starts with '0b'. if (number === '0') { if (ch === 'x' || ch === 'X') { - number += source[index++]; + ++index; + return scanHexLiteral(start); + } + if (ch === 'b' || ch === 'B') { + ++index; + number = ''; + while (index < length) { ch = source[index]; - if (!isHexDigit(ch)) { + if (ch !== '0' && ch !== '1') { break; } number += source[index++]; } - if (number.length <= 2) { - // only 0x + if (number.length === 0) { + // only 0b or 0B throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } if (index < length) { - ch = source[index]; - if (isIdentifierStart(ch)) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } - } - return { - type: Token.NumericLiteral, - value: parseInt(number, 16), - lineNumber: lineNumber, - lineStart: lineStart, - range: [start, index] - }; - } else if (isOctalDigit(ch)) { - number += source[index++]; - while (index < length) { - ch = source[index]; - if (!isOctalDigit(ch)) { - break; - } - number += source[index++]; - } - - if (index < length) { - ch = source[index]; + ch = source.charCodeAt(index); if (isIdentifierStart(ch) || isDecimalDigit(ch)) { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } } return { type: Token.NumericLiteral, - value: parseInt(number, 8), - octal: true, + value: parseInt(number, 2), lineNumber: lineNumber, lineStart: lineStart, range: [start, index] }; } - + if (ch === 'o' || ch === 'O' || isOctalDigit(ch)) { + return scanOctalLiteral(ch, start); + } // decimal number starts with '0' such as '09' is illegal. - if (isDecimalDigit(ch)) { + if (ch && isDecimalDigit(ch.charCodeAt(0))) { throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } } - while (index < length) { - ch = source[index]; - if (!isDecimalDigit(ch)) { - break; - } + while (isDecimalDigit(source.charCodeAt(index))) { number += source[index++]; } + ch = source[index]; } if (ch === '.') { number += source[index++]; - while (index < length) { - ch = source[index]; - if (!isDecimalDigit(ch)) { - break; - } + while (isDecimalDigit(source.charCodeAt(index))) { number += source[index++]; } + ch = source[index]; } if (ch === 'e' || ch === 'E') { @@ -812,31 +963,17 @@ parseStatement: true, parseSourceElement: true */ if (ch === '+' || ch === '-') { number += source[index++]; } - - ch = source[index]; - if (isDecimalDigit(ch)) { - number += source[index++]; - while (index < length) { - ch = source[index]; - if (!isDecimalDigit(ch)) { - break; - } + if (isDecimalDigit(source.charCodeAt(index))) { + while (isDecimalDigit(source.charCodeAt(index))) { number += source[index++]; } } else { - ch = 'character ' + ch; - if (index >= length) { - ch = ''; - } throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } } - if (index < length) { - ch = source[index]; - if (isIdentifierStart(ch)) { - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); - } + if (isIdentifierStart(source.charCodeAt(index))) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); } return { @@ -868,7 +1005,7 @@ parseStatement: true, parseSourceElement: true */ break; } else if (ch === '\\') { ch = source[index++]; - if (!isLineTerminator(ch)) { + if (!ch || !isLineTerminator(ch.charCodeAt(0))) { switch (ch) { case 'n': str += '\n'; @@ -881,13 +1018,18 @@ parseStatement: true, parseSourceElement: true */ break; case 'u': case 'x': - restore = index; - unescaped = scanHexEscape(ch); - if (unescaped) { - str += unescaped; + if (source[index] === '{') { + ++index; + str += scanUnicodeCodePointEscape(); } else { - index = restore; - str += ch; + restore = index; + unescaped = scanHexEscape(ch); + if (unescaped) { + str += unescaped; + } else { + index = restore; + str += ch; + } } break; case 'b': @@ -933,7 +1075,7 @@ parseStatement: true, parseSourceElement: true */ ++index; } } - } else if (isLineTerminator(ch)) { + } else if (isLineTerminator(ch.charCodeAt(0))) { break; } else { str += ch; @@ -954,10 +1096,152 @@ parseStatement: true, parseSourceElement: true */ }; } + function scanTemplate() { + var cooked = '', ch, start, terminated, tail, restore, unescaped, code, octal; + + terminated = false; + tail = false; + start = index; + + ++index; + + while (index < length) { + ch = source[index++]; + if (ch === '`') { + tail = true; + terminated = true; + break; + } else if (ch === '$') { + if (source[index] === '{') { + ++index; + terminated = true; + break; + } + cooked += ch; + } else if (ch === '\\') { + ch = source[index++]; + if (!isLineTerminator(ch.charCodeAt(0))) { + switch (ch) { + case 'n': + cooked += '\n'; + break; + case 'r': + cooked += '\r'; + break; + case 't': + cooked += '\t'; + break; + case 'u': + case 'x': + if (source[index] === '{') { + ++index; + cooked += scanUnicodeCodePointEscape(); + } else { + restore = index; + unescaped = scanHexEscape(ch); + if (unescaped) { + cooked += unescaped; + } else { + index = restore; + cooked += ch; + } + } + break; + case 'b': + cooked += '\b'; + break; + case 'f': + cooked += '\f'; + break; + case 'v': + cooked += '\v'; + break; + + default: + if (isOctalDigit(ch)) { + code = '01234567'.indexOf(ch); + + // \0 is not octal escape sequence + if (code !== 0) { + octal = true; + } + + if (index < length && isOctalDigit(source[index])) { + octal = true; + code = code * 8 + '01234567'.indexOf(source[index++]); + + // 3 digits are only allowed when string starts + // with 0, 1, 2, 3 + if ('0123'.indexOf(ch) >= 0 && + index < length && + isOctalDigit(source[index])) { + code = code * 8 + '01234567'.indexOf(source[index++]); + } + } + cooked += String.fromCharCode(code); + } else { + cooked += ch; + } + break; + } + } else { + ++lineNumber; + if (ch === '\r' && source[index] === '\n') { + ++index; + } + } + } else if (isLineTerminator(ch.charCodeAt(0))) { + ++lineNumber; + if (ch === '\r' && source[index] === '\n') { + ++index; + } + cooked += '\n'; + } else { + cooked += ch; + } + } + + if (!terminated) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + + return { + type: Token.Template, + value: { + cooked: cooked, + raw: source.slice(start + 1, index - ((tail) ? 1 : 2)) + }, + tail: tail, + octal: octal, + lineNumber: lineNumber, + lineStart: lineStart, + range: [start, index] + }; + } + + function scanTemplateElement(option) { + var startsWith, template; + + lookahead = null; + skipComment(); + + startsWith = (option.head) ? '`' : '}'; + + if (source[index] !== startsWith) { + throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + } + + template = scanTemplate(); + + peek(); + + return template; + } + function scanRegExp() { var str, ch, start, pattern, flags, value, classMarker = false, restore, terminated = false; - buffer = null; + lookahead = null; skipComment(); start = index; @@ -968,24 +1252,24 @@ parseStatement: true, parseSourceElement: true */ while (index < length) { ch = source[index++]; str += ch; - if (ch === '\\') { - ch = source[index++]; - // ECMA-262 7.8.5 - if (isLineTerminator(ch)) { - throwError({}, Messages.UnterminatedRegExp); - } - str += ch; - } else if (classMarker) { + if (classMarker) { if (ch === ']') { classMarker = false; } } else { - if (ch === '/') { + if (ch === '\\') { + ch = source[index++]; + // ECMA-262 7.8.5 + if (isLineTerminator(ch.charCodeAt(0))) { + throwError({}, Messages.UnterminatedRegExp); + } + str += ch; + } else if (ch === '/') { terminated = true; break; } else if (ch === '[') { classMarker = true; - } else if (isLineTerminator(ch)) { + } else if (isLineTerminator(ch.charCodeAt(0))) { throwError({}, Messages.UnterminatedRegExp); } } @@ -1001,7 +1285,7 @@ parseStatement: true, parseSourceElement: true */ flags = ''; while (index < length) { ch = source[index]; - if (!isIdentifierPart(ch)) { + if (!isIdentifierPart(ch.charCodeAt(0))) { break; } @@ -1014,8 +1298,7 @@ parseStatement: true, parseSourceElement: true */ ch = scanHexEscape('u'); if (ch) { flags += ch; - str += '\\u'; - for (; restore < index; ++restore) { + for (str += '\\u'; restore < index; ++restore) { str += source[restore]; } } else { @@ -1038,6 +1321,18 @@ parseStatement: true, parseSourceElement: true */ throwError({}, Messages.InvalidRegExp); } + peek(); + + + if (extra.tokenize) { + return { + type: Token.RegularExpression, + value: value, + lineNumber: lineNumber, + lineStart: lineStart, + range: [start, index] + }; + } return { literal: str, value: value, @@ -1052,8 +1347,68 @@ parseStatement: true, parseSourceElement: true */ token.type === Token.NullLiteral; } + function advanceSlash() { + var prevToken, + checkToken; + // Using the following algorithm: + // https://github.com/mozilla/sweet.js/wiki/design + prevToken = extra.tokens[extra.tokens.length - 1]; + if (!prevToken) { + // Nothing before that: it cannot be a division. + return scanRegExp(); + } + if (prevToken.type === 'Punctuator') { + if (prevToken.value === ')') { + checkToken = extra.tokens[extra.openParenToken - 1]; + if (checkToken && + checkToken.type === 'Keyword' && + (checkToken.value === 'if' || + checkToken.value === 'while' || + checkToken.value === 'for' || + checkToken.value === 'with')) { + return scanRegExp(); + } + return scanPunctuator(); + } + if (prevToken.value === '}') { + // Dividing a function by anything makes little sense, + // but we have to check for that. + if (extra.tokens[extra.openCurlyToken - 3] && + extra.tokens[extra.openCurlyToken - 3].type === 'Keyword') { + // Anonymous function. + checkToken = extra.tokens[extra.openCurlyToken - 4]; + if (!checkToken) { + return scanPunctuator(); + } + } else if (extra.tokens[extra.openCurlyToken - 4] && + extra.tokens[extra.openCurlyToken - 4].type === 'Keyword') { + // Named function. + checkToken = extra.tokens[extra.openCurlyToken - 5]; + if (!checkToken) { + return scanRegExp(); + } + } else { + return scanPunctuator(); + } + // checkToken determines whether the function is + // a declaration or an expression. + if (FnExprTokens.indexOf(checkToken.value) >= 0) { + // It is an expression. + return scanPunctuator(); + } + // It is a declaration. + return scanRegExp(); + } + return scanRegExp(); + } + if (prevToken.type === 'Keyword') { + return scanRegExp(); + } + return scanPunctuator(); + } + function advance() { - var ch, token; + var ch; skipComment(); @@ -1066,63 +1421,624 @@ parseStatement: true, parseSourceElement: true */ }; } - token = scanPunctuator(); - if (typeof token !== 'undefined') { - return token; + ch = source.charCodeAt(index); + + // Very common: ( and ) and ; + if (ch === 40 || ch === 41 || ch === 58) { + return scanPunctuator(); } - ch = source[index]; - - if (ch === '\'' || ch === '"') { + // String literal starts with single quote (#39) or double quote (#34). + if (ch === 39 || ch === 34) { return scanStringLiteral(); } - if (ch === '.' || isDecimalDigit(ch)) { + if (ch === 96) { + return scanTemplate(); + } + if (isIdentifierStart(ch)) { + return scanIdentifier(); + } + + // Dot (.) char #46 can also start a floating-point number, hence the need + // to check the next character. + if (ch === 46) { + if (isDecimalDigit(source.charCodeAt(index + 1))) { + return scanNumericLiteral(); + } + return scanPunctuator(); + } + + if (isDecimalDigit(ch)) { return scanNumericLiteral(); } - token = scanIdentifier(); - if (typeof token !== 'undefined') { - return token; + // Slash (/) char #47 can also start a regex. + if (extra.tokenize && ch === 47) { + return advanceSlash(); } - throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); + return scanPunctuator(); } function lex() { var token; - if (buffer) { - index = buffer.range[1]; - lineNumber = buffer.lineNumber; - lineStart = buffer.lineStart; - token = buffer; - buffer = null; - return token; - } + token = lookahead; + index = token.range[1]; + lineNumber = token.lineNumber; + lineStart = token.lineStart; - buffer = null; - return advance(); + lookahead = advance(); + + index = token.range[1]; + lineNumber = token.lineNumber; + lineStart = token.lineStart; + + return token; } - function lookahead() { + function peek() { var pos, line, start; - if (buffer !== null) { - return buffer; - } - pos = index; line = lineNumber; start = lineStart; - buffer = advance(); + lookahead = advance(); + index = pos; + lineNumber = line; + lineStart = start; + } + + function lookahead2() { + var adv, pos, line, start, result; + + // If we are collecting the tokens, don't grab the next one yet. + adv = (typeof extra.advance === 'function') ? extra.advance : advance; + + pos = index; + line = lineNumber; + start = lineStart; + + // Scan for the next immediate token. + if (lookahead === null) { + lookahead = adv(); + } + index = lookahead.range[1]; + lineNumber = lookahead.lineNumber; + lineStart = lookahead.lineStart; + + // Grab the token right after. + result = adv(); index = pos; lineNumber = line; lineStart = start; - return buffer; + return result; } + function markerCreate() { + if (!extra.loc && !extra.range) { + return undefined; + } + skipComment(); + return {offset: index, line: lineNumber, col: index - lineStart}; + } + + function markerApply(marker, node) { + if (extra.range) { + node.range = [marker.offset, index]; + } + if (extra.loc) { + node.loc = { + start: { + line: marker.line, + column: marker.col + }, + end: { + line: lineNumber, + column: index - lineStart + } + }; + node = delegate.postProcess(node); + } + return node; + } + + SyntaxTreeDelegate = { + + name: 'SyntaxTree', + + postProcess: function (node) { + return node; + }, + + createArrayExpression: function (elements) { + return { + type: Syntax.ArrayExpression, + elements: elements + }; + }, + + createAssignmentExpression: function (operator, left, right) { + return { + type: Syntax.AssignmentExpression, + operator: operator, + left: left, + right: right + }; + }, + + createBinaryExpression: function (operator, left, right) { + var type = (operator === '||' || operator === '&&') ? Syntax.LogicalExpression : + Syntax.BinaryExpression; + return { + type: type, + operator: operator, + left: left, + right: right + }; + }, + + createBlockStatement: function (body) { + return { + type: Syntax.BlockStatement, + body: body + }; + }, + + createBreakStatement: function (label) { + return { + type: Syntax.BreakStatement, + label: label + }; + }, + + createCallExpression: function (callee, args) { + return { + type: Syntax.CallExpression, + callee: callee, + 'arguments': args + }; + }, + + createCatchClause: function (param, body) { + return { + type: Syntax.CatchClause, + param: param, + body: body + }; + }, + + createConditionalExpression: function (test, consequent, alternate) { + return { + type: Syntax.ConditionalExpression, + test: test, + consequent: consequent, + alternate: alternate + }; + }, + + createContinueStatement: function (label) { + return { + type: Syntax.ContinueStatement, + label: label + }; + }, + + createDebuggerStatement: function () { + return { + type: Syntax.DebuggerStatement + }; + }, + + createDoWhileStatement: function (body, test) { + return { + type: Syntax.DoWhileStatement, + body: body, + test: test + }; + }, + + createEmptyStatement: function () { + return { + type: Syntax.EmptyStatement + }; + }, + + createExpressionStatement: function (expression) { + return { + type: Syntax.ExpressionStatement, + expression: expression + }; + }, + + createForStatement: function (init, test, update, body) { + return { + type: Syntax.ForStatement, + init: init, + test: test, + update: update, + body: body + }; + }, + + createForInStatement: function (left, right, body) { + return { + type: Syntax.ForInStatement, + left: left, + right: right, + body: body, + each: false + }; + }, + + createForOfStatement: function (left, right, body) { + return { + type: Syntax.ForOfStatement, + left: left, + right: right, + body: body + }; + }, + + createFunctionDeclaration: function (id, params, defaults, body, rest, generator, expression) { + return { + type: Syntax.FunctionDeclaration, + id: id, + params: params, + defaults: defaults, + body: body, + rest: rest, + generator: generator, + expression: expression + }; + }, + + createFunctionExpression: function (id, params, defaults, body, rest, generator, expression) { + return { + type: Syntax.FunctionExpression, + id: id, + params: params, + defaults: defaults, + body: body, + rest: rest, + generator: generator, + expression: expression + }; + }, + + createIdentifier: function (name) { + return { + type: Syntax.Identifier, + name: name + }; + }, + + createIfStatement: function (test, consequent, alternate) { + return { + type: Syntax.IfStatement, + test: test, + consequent: consequent, + alternate: alternate + }; + }, + + createLabeledStatement: function (label, body) { + return { + type: Syntax.LabeledStatement, + label: label, + body: body + }; + }, + + createLiteral: function (token) { + return { + type: Syntax.Literal, + value: token.value, + raw: source.slice(token.range[0], token.range[1]) + }; + }, + + createMemberExpression: function (accessor, object, property) { + return { + type: Syntax.MemberExpression, + computed: accessor === '[', + object: object, + property: property + }; + }, + + createNewExpression: function (callee, args) { + return { + type: Syntax.NewExpression, + callee: callee, + 'arguments': args + }; + }, + + createObjectExpression: function (properties) { + return { + type: Syntax.ObjectExpression, + properties: properties + }; + }, + + createPostfixExpression: function (operator, argument) { + return { + type: Syntax.UpdateExpression, + operator: operator, + argument: argument, + prefix: false + }; + }, + + createProgram: function (body) { + return { + type: Syntax.Program, + body: body + }; + }, + + createProperty: function (kind, key, value, method, shorthand) { + return { + type: Syntax.Property, + key: key, + value: value, + kind: kind, + method: method, + shorthand: shorthand + }; + }, + + createReturnStatement: function (argument) { + return { + type: Syntax.ReturnStatement, + argument: argument + }; + }, + + createSequenceExpression: function (expressions) { + return { + type: Syntax.SequenceExpression, + expressions: expressions + }; + }, + + createSwitchCase: function (test, consequent) { + return { + type: Syntax.SwitchCase, + test: test, + consequent: consequent + }; + }, + + createSwitchStatement: function (discriminant, cases) { + return { + type: Syntax.SwitchStatement, + discriminant: discriminant, + cases: cases + }; + }, + + createThisExpression: function () { + return { + type: Syntax.ThisExpression + }; + }, + + createThrowStatement: function (argument) { + return { + type: Syntax.ThrowStatement, + argument: argument + }; + }, + + createTryStatement: function (block, guardedHandlers, handlers, finalizer) { + return { + type: Syntax.TryStatement, + block: block, + guardedHandlers: guardedHandlers, + handlers: handlers, + finalizer: finalizer + }; + }, + + createUnaryExpression: function (operator, argument) { + if (operator === '++' || operator === '--') { + return { + type: Syntax.UpdateExpression, + operator: operator, + argument: argument, + prefix: true + }; + } + return { + type: Syntax.UnaryExpression, + operator: operator, + argument: argument + }; + }, + + createVariableDeclaration: function (declarations, kind) { + return { + type: Syntax.VariableDeclaration, + declarations: declarations, + kind: kind + }; + }, + + createVariableDeclarator: function (id, init) { + return { + type: Syntax.VariableDeclarator, + id: id, + init: init + }; + }, + + createWhileStatement: function (test, body) { + return { + type: Syntax.WhileStatement, + test: test, + body: body + }; + }, + + createWithStatement: function (object, body) { + return { + type: Syntax.WithStatement, + object: object, + body: body + }; + }, + + createTemplateElement: function (value, tail) { + return { + type: Syntax.TemplateElement, + value: value, + tail: tail + }; + }, + + createTemplateLiteral: function (quasis, expressions) { + return { + type: Syntax.TemplateLiteral, + quasis: quasis, + expressions: expressions + }; + }, + + createSpreadElement: function (argument) { + return { + type: Syntax.SpreadElement, + argument: argument + }; + }, + + createTaggedTemplateExpression: function (tag, quasi) { + return { + type: Syntax.TaggedTemplateExpression, + tag: tag, + quasi: quasi + }; + }, + + createArrowFunctionExpression: function (params, defaults, body, rest, expression) { + return { + type: Syntax.ArrowFunctionExpression, + id: null, + params: params, + defaults: defaults, + body: body, + rest: rest, + generator: false, + expression: expression + }; + }, + + createMethodDefinition: function (propertyType, kind, key, value) { + return { + type: Syntax.MethodDefinition, + key: key, + value: value, + kind: kind, + 'static': propertyType === ClassPropertyType.static + }; + }, + + createClassBody: function (body) { + return { + type: Syntax.ClassBody, + body: body + }; + }, + + createClassExpression: function (id, superClass, body) { + return { + type: Syntax.ClassExpression, + id: id, + superClass: superClass, + body: body + }; + }, + + createClassDeclaration: function (id, superClass, body) { + return { + type: Syntax.ClassDeclaration, + id: id, + superClass: superClass, + body: body + }; + }, + + createExportSpecifier: function (id, name) { + return { + type: Syntax.ExportSpecifier, + id: id, + name: name + }; + }, + + createExportBatchSpecifier: function () { + return { + type: Syntax.ExportBatchSpecifier + }; + }, + + createExportDeclaration: function (declaration, specifiers, source) { + return { + type: Syntax.ExportDeclaration, + declaration: declaration, + specifiers: specifiers, + source: source + }; + }, + + createImportSpecifier: function (id, name) { + return { + type: Syntax.ImportSpecifier, + id: id, + name: name + }; + }, + + createImportDeclaration: function (specifiers, kind, source) { + return { + type: Syntax.ImportDeclaration, + specifiers: specifiers, + kind: kind, + source: source + }; + }, + + createYieldExpression: function (argument, delegate) { + return { + type: Syntax.YieldExpression, + argument: argument, + delegate: delegate + }; + }, + + createModuleDeclaration: function (id, source, body) { + return { + type: Syntax.ModuleDeclaration, + id: id, + source: source, + body: body + }; + }, + + createComprehensionExpression: function (filter, blocks, body) { + return { + type: Syntax.ComprehensionExpression, + filter: filter, + blocks: blocks, + body: body + }; + } + + }; + // Return true if there is a line terminator before the next token. function peekLineTerminator() { @@ -1148,7 +2064,8 @@ parseStatement: true, parseSourceElement: true */ msg = messageFormat.replace( /%(\d)/g, function (whole, index) { - return args[index] || ''; + assert(index < args.length, 'Message reference must be in range'); + return args[index]; } ); @@ -1164,6 +2081,7 @@ parseStatement: true, parseSourceElement: true */ error.column = index - lineStart + 1; } + error.description = msg; throw error; } @@ -1209,6 +2127,10 @@ parseStatement: true, parseSourceElement: true */ throwError(token, Messages.UnexpectedToken, token.value); } + if (token.type === Token.Template) { + throwError(token, Messages.UnexpectedTemplate, token.value.raw); + } + // BooleanLiteral, NullLiteral, or Punctuator. throwError(token, Messages.UnexpectedToken, token.value); } @@ -1236,26 +2158,31 @@ parseStatement: true, parseSourceElement: true */ // Return true if the next token matches the specified punctuator. function match(value) { - var token = lookahead(); - return token.type === Token.Punctuator && token.value === value; + return lookahead.type === Token.Punctuator && lookahead.value === value; } // Return true if the next token matches the specified keyword function matchKeyword(keyword) { - var token = lookahead(); - return token.type === Token.Keyword && token.value === keyword; + return lookahead.type === Token.Keyword && lookahead.value === keyword; + } + + + // Return true if the next token matches the specified contextual keyword + + function matchContextualKeyword(keyword) { + return lookahead.type === Token.Identifier && lookahead.value === keyword; } // Return true if the next token is an assignment operator function matchAssign() { - var token = lookahead(), - op = token.value; + var op; - if (token.type !== Token.Punctuator) { + if (lookahead.type !== Token.Punctuator) { return false; } + op = lookahead.value; return op === '=' || op === '*=' || op === '/=' || @@ -1271,10 +2198,10 @@ parseStatement: true, parseSourceElement: true */ } function consumeSemicolon() { - var token, line; + var line; - // Catch the very common case first. - if (source[index] === ';') { + // Catch the very common case first: immediately a semicolon (char #59). + if (source.charCodeAt(index) === 59) { lex(); return; } @@ -1290,9 +2217,8 @@ parseStatement: true, parseSourceElement: true */ return; } - token = lookahead(); - if (token.type !== Token.EOF && !match('}')) { - throwUnexpected(token); + if (lookahead.type !== Token.EOF && !match('}')) { + throwUnexpected(lookahead); } } @@ -1302,60 +2228,134 @@ parseStatement: true, parseSourceElement: true */ return expr.type === Syntax.Identifier || expr.type === Syntax.MemberExpression; } + function isAssignableLeftHandSide(expr) { + return isLeftHandSide(expr) || expr.type === Syntax.ObjectPattern || expr.type === Syntax.ArrayPattern; + } + // 11.1.4 Array Initialiser function parseArrayInitialiser() { - var elements = []; + var elements = [], blocks = [], filter = null, tmp, possiblecomprehension = true, body, + marker = markerCreate(); expect('['); - while (!match(']')) { - if (match(',')) { + if (lookahead.value === 'for' && + lookahead.type === Token.Keyword) { + if (!possiblecomprehension) { + throwError({}, Messages.ComprehensionError); + } + matchKeyword('for'); + tmp = parseForStatement({ignoreBody: true}); + tmp.of = tmp.type === Syntax.ForOfStatement; + tmp.type = Syntax.ComprehensionBlock; + if (tmp.left.kind) { // can't be let or const + throwError({}, Messages.ComprehensionError); + } + blocks.push(tmp); + } else if (lookahead.value === 'if' && + lookahead.type === Token.Keyword) { + if (!possiblecomprehension) { + throwError({}, Messages.ComprehensionError); + } + expectKeyword('if'); + expect('('); + filter = parseExpression(); + expect(')'); + } else if (lookahead.value === ',' && + lookahead.type === Token.Punctuator) { + possiblecomprehension = false; // no longer allowed. lex(); elements.push(null); } else { - elements.push(parseAssignmentExpression()); - - if (!match(']')) { - expect(','); + tmp = parseSpreadOrAssignmentExpression(); + elements.push(tmp); + if (tmp && tmp.type === Syntax.SpreadElement) { + if (!match(']')) { + throwError({}, Messages.ElementAfterSpreadElement); + } + } else if (!(match(']') || matchKeyword('for') || matchKeyword('if'))) { + expect(','); // this lexes. + possiblecomprehension = false; } } } expect(']'); - return { - type: Syntax.ArrayExpression, - elements: elements - }; + if (filter && !blocks.length) { + throwError({}, Messages.ComprehensionRequiresBlock); + } + + if (blocks.length) { + if (elements.length !== 1) { + throwError({}, Messages.ComprehensionError); + } + return markerApply(marker, delegate.createComprehensionExpression(filter, blocks, elements[0])); + } + return markerApply(marker, delegate.createArrayExpression(elements)); } // 11.1.5 Object Initialiser - function parsePropertyFunction(param, first) { - var previousStrict, body; + function parsePropertyFunction(options) { + var previousStrict, previousYieldAllowed, params, defaults, body, + marker = markerCreate(); previousStrict = strict; - body = parseFunctionSourceElements(); - if (first && strict && isRestrictedWord(param[0].name)) { - throwErrorTolerant(first, Messages.StrictParamName); + previousYieldAllowed = state.yieldAllowed; + state.yieldAllowed = options.generator; + params = options.params || []; + defaults = options.defaults || []; + + body = parseConciseBody(); + if (options.name && strict && isRestrictedWord(params[0].name)) { + throwErrorTolerant(options.name, Messages.StrictParamName); } strict = previousStrict; + state.yieldAllowed = previousYieldAllowed; - return { - type: Syntax.FunctionExpression, - id: null, - params: param, - defaults: [], - body: body, - rest: null, - generator: false, - expression: false - }; + return markerApply(marker, delegate.createFunctionExpression( + null, + params, + defaults, + body, + options.rest || null, + options.generator, + body.type !== Syntax.BlockStatement + )); } + + function parsePropertyMethodFunction(options) { + var previousStrict, tmp, method; + + previousStrict = strict; + strict = true; + + tmp = parseParams(); + + if (tmp.stricted) { + throwErrorTolerant(tmp.stricted, tmp.message); + } + + + method = parsePropertyFunction({ + params: tmp.params, + defaults: tmp.defaults, + rest: tmp.rest, + generator: options.generator + }); + + strict = previousStrict; + + return method; + } + + function parseObjectPropertyKey() { - var token = lex(); + var marker = markerCreate(), + token = lex(); // Note: This function is called only from parseObjectProperty(), where // EOF and Punctuator tokens are already filtered out. @@ -1364,19 +2364,17 @@ parseStatement: true, parseSourceElement: true */ if (strict && token.octal) { throwErrorTolerant(token, Messages.StrictOctalLiteral); } - return createLiteral(token); + return markerApply(marker, delegate.createLiteral(token)); } - return { - type: Syntax.Identifier, - name: token.value - }; + return markerApply(marker, delegate.createIdentifier(token.value)); } function parseObjectProperty() { - var token, key, id, param; + var token, key, id, value, param, expr, + marker = markerCreate(); - token = lookahead(); + token = lookahead; if (token.type === Token.Identifier) { @@ -1384,64 +2382,57 @@ parseStatement: true, parseSourceElement: true */ // Property Assignment: Getter and Setter. - if (token.value === 'get' && !match(':')) { + if (token.value === 'get' && !(match(':') || match('('))) { key = parseObjectPropertyKey(); expect('('); expect(')'); - return { - type: Syntax.Property, - key: key, - value: parsePropertyFunction([]), - kind: 'get' - }; - } else if (token.value === 'set' && !match(':')) { + return markerApply(marker, delegate.createProperty('get', key, parsePropertyFunction({ generator: false }), false, false)); + } + if (token.value === 'set' && !(match(':') || match('('))) { key = parseObjectPropertyKey(); expect('('); - token = lookahead(); - if (token.type !== Token.Identifier) { - expect(')'); - throwErrorTolerant(token, Messages.UnexpectedToken, token.value); - return { - type: Syntax.Property, - key: key, - value: parsePropertyFunction([]), - kind: 'set' - }; - } else { - param = [ parseVariableIdentifier() ]; - expect(')'); - return { - type: Syntax.Property, - key: key, - value: parsePropertyFunction(param, token), - kind: 'set' - }; - } - } else { - expect(':'); - return { - type: Syntax.Property, - key: id, - value: parseAssignmentExpression(), - kind: 'init' - }; + token = lookahead; + param = [ parseVariableIdentifier() ]; + expect(')'); + return markerApply(marker, delegate.createProperty('set', key, parsePropertyFunction({ params: param, generator: false, name: token }), false, false)); } - } else if (token.type === Token.EOF || token.type === Token.Punctuator) { - throwUnexpected(token); - } else { - key = parseObjectPropertyKey(); - expect(':'); - return { - type: Syntax.Property, - key: key, - value: parseAssignmentExpression(), - kind: 'init' - }; + if (match(':')) { + lex(); + return markerApply(marker, delegate.createProperty('init', id, parseAssignmentExpression(), false, false)); + } + if (match('(')) { + return markerApply(marker, delegate.createProperty('init', id, parsePropertyMethodFunction({ generator: false }), true, false)); + } + return markerApply(marker, delegate.createProperty('init', id, id, false, true)); } + if (token.type === Token.EOF || token.type === Token.Punctuator) { + if (!match('*')) { + throwUnexpected(token); + } + lex(); + + id = parseObjectPropertyKey(); + + if (!match('(')) { + throwUnexpected(lex()); + } + + return markerApply(marker, delegate.createProperty('init', id, parsePropertyMethodFunction({ generator: true }), true, false)); + } + key = parseObjectPropertyKey(); + if (match(':')) { + lex(); + return markerApply(marker, delegate.createProperty('init', key, parseAssignmentExpression(), false, false)); + } + if (match('(')) { + return markerApply(marker, delegate.createProperty('init', key, parsePropertyMethodFunction({ generator: false }), true, false)); + } + throwUnexpected(lex()); } function parseObjectInitialiser() { - var properties = [], property, name, kind, map = {}, toString = String; + var properties = [], property, name, key, kind, map = {}, toString = String, + marker = markerCreate(); expect('{'); @@ -1454,8 +2445,10 @@ parseStatement: true, parseSourceElement: true */ name = toString(property.key.value); } kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set; - if (Object.prototype.hasOwnProperty.call(map, name)) { - if (map[name] === PropertyKind.Data) { + + key = '$' + name; + if (Object.prototype.hasOwnProperty.call(map, key)) { + if (map[key] === PropertyKind.Data) { if (strict && kind === PropertyKind.Data) { throwErrorTolerant({}, Messages.StrictDuplicateProperty); } else if (kind !== PropertyKind.Data) { @@ -1464,13 +2457,13 @@ parseStatement: true, parseSourceElement: true */ } else { if (kind === PropertyKind.Data) { throwErrorTolerant({}, Messages.AccessorDataProperty); - } else if (map[name] & kind) { + } else if (map[key] & kind) { throwErrorTolerant({}, Messages.AccessorGetSet); } } - map[name] |= kind; + map[key] |= kind; } else { - map[name] = kind; + map[key] = kind; } properties.push(property); @@ -1482,10 +2475,32 @@ parseStatement: true, parseSourceElement: true */ expect('}'); - return { - type: Syntax.ObjectExpression, - properties: properties - }; + return markerApply(marker, delegate.createObjectExpression(properties)); + } + + function parseTemplateElement(option) { + var marker = markerCreate(), + token = scanTemplateElement(option); + if (strict && token.octal) { + throwError(token, Messages.StrictOctalLiteral); + } + return markerApply(marker, delegate.createTemplateElement({ raw: token.value.raw, cooked: token.value.cooked }, token.tail)); + } + + function parseTemplateLiteral() { + var quasi, quasis, expressions, marker = markerCreate(); + + quasi = parseTemplateElement({ head: true }); + quasis = [ quasi ]; + expressions = []; + + while (!quasi.tail) { + expressions.push(parseExpression()); + quasi = parseTemplateElement({ head: false }); + quasis.push(quasi); + } + + return markerApply(marker, delegate.createTemplateLiteral(quasis, expressions)); } // 11.1.6 The Grouping Operator @@ -1495,6 +2510,8 @@ parseStatement: true, parseSourceElement: true */ expect('('); + ++state.parenthesizedCount; + expr = parseExpression(); expect(')'); @@ -1506,46 +2523,57 @@ parseStatement: true, parseSourceElement: true */ // 11.1 Primary Expressions function parsePrimaryExpression() { - var token = lookahead(), - type = token.type; + var marker, type, token, expr; + + type = lookahead.type; if (type === Token.Identifier) { - return { - type: Syntax.Identifier, - name: lex().value - }; + marker = markerCreate(); + return markerApply(marker, delegate.createIdentifier(lex().value)); } if (type === Token.StringLiteral || type === Token.NumericLiteral) { - if (strict && token.octal) { - throwErrorTolerant(token, Messages.StrictOctalLiteral); + if (strict && lookahead.octal) { + throwErrorTolerant(lookahead, Messages.StrictOctalLiteral); } - return createLiteral(lex()); + marker = markerCreate(); + return markerApply(marker, delegate.createLiteral(lex())); } if (type === Token.Keyword) { if (matchKeyword('this')) { + marker = markerCreate(); lex(); - return { - type: Syntax.ThisExpression - }; + return markerApply(marker, delegate.createThisExpression()); } if (matchKeyword('function')) { return parseFunctionExpression(); } + + if (matchKeyword('class')) { + return parseClassExpression(); + } + + if (matchKeyword('super')) { + marker = markerCreate(); + lex(); + return markerApply(marker, delegate.createIdentifier('super')); + } } if (type === Token.BooleanLiteral) { - lex(); + marker = markerCreate(); + token = lex(); token.value = (token.value === 'true'); - return createLiteral(token); + return markerApply(marker, delegate.createLiteral(token)); } if (type === Token.NullLiteral) { - lex(); + marker = markerCreate(); + token = lex(); token.value = null; - return createLiteral(token); + return markerApply(marker, delegate.createLiteral(token)); } if (match('[')) { @@ -1561,25 +2589,35 @@ parseStatement: true, parseSourceElement: true */ } if (match('/') || match('/=')) { - return createLiteral(scanRegExp()); + marker = markerCreate(); + return markerApply(marker, delegate.createLiteral(scanRegExp())); } - return throwUnexpected(lex()); + if (type === Token.Template) { + return parseTemplateLiteral(); + } + + throwUnexpected(lex()); } // 11.2 Left-Hand-Side Expressions function parseArguments() { - var args = []; + var args = [], arg; expect('('); if (!match(')')) { while (index < length) { - args.push(parseAssignmentExpression()); + arg = parseSpreadOrAssignmentExpression(); + args.push(arg); + if (match(')')) { break; + } else if (arg.type === Syntax.SpreadElement) { + throwError({}, Messages.ElementAfterSpreadElement); } + expect(','); } } @@ -1589,17 +2627,24 @@ parseStatement: true, parseSourceElement: true */ return args; } + function parseSpreadOrAssignmentExpression() { + if (match('...')) { + var marker = markerCreate(); + lex(); + return markerApply(marker, delegate.createSpreadElement(parseAssignmentExpression())); + } + return parseAssignmentExpression(); + } + function parseNonComputedProperty() { - var token = lex(); + var marker = markerCreate(), + token = lex(); if (!isIdentifierName(token)) { throwUnexpected(token); } - return { - type: Syntax.Identifier, - name: token.value - }; + return markerApply(marker, delegate.createIdentifier(token.value)); } function parseNonComputedMember() { @@ -1621,76 +2666,48 @@ parseStatement: true, parseSourceElement: true */ } function parseNewExpression() { - var expr; + var callee, args, marker = markerCreate(); expectKeyword('new'); + callee = parseLeftHandSideExpression(); + args = match('(') ? parseArguments() : []; - expr = { - type: Syntax.NewExpression, - callee: parseLeftHandSideExpression(), - 'arguments': [] - }; - - if (match('(')) { - expr['arguments'] = parseArguments(); - } - - return expr; + return markerApply(marker, delegate.createNewExpression(callee, args)); } function parseLeftHandSideExpressionAllowCall() { - var expr; + var expr, args, marker = markerCreate(); expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); - while (match('.') || match('[') || match('(')) { + while (match('.') || match('[') || match('(') || lookahead.type === Token.Template) { if (match('(')) { - expr = { - type: Syntax.CallExpression, - callee: expr, - 'arguments': parseArguments() - }; + args = parseArguments(); + expr = markerApply(marker, delegate.createCallExpression(expr, args)); } else if (match('[')) { - expr = { - type: Syntax.MemberExpression, - computed: true, - object: expr, - property: parseComputedMember() - }; + expr = markerApply(marker, delegate.createMemberExpression('[', expr, parseComputedMember())); + } else if (match('.')) { + expr = markerApply(marker, delegate.createMemberExpression('.', expr, parseNonComputedMember())); } else { - expr = { - type: Syntax.MemberExpression, - computed: false, - object: expr, - property: parseNonComputedMember() - }; + expr = markerApply(marker, delegate.createTaggedTemplateExpression(expr, parseTemplateLiteral())); } } return expr; } - function parseLeftHandSideExpression() { - var expr; + var expr, marker = markerCreate(); expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); - while (match('.') || match('[')) { + while (match('.') || match('[') || lookahead.type === Token.Template) { if (match('[')) { - expr = { - type: Syntax.MemberExpression, - computed: true, - object: expr, - property: parseComputedMember() - }; + expr = markerApply(marker, delegate.createMemberExpression('[', expr, parseComputedMember())); + } else if (match('.')) { + expr = markerApply(marker, delegate.createMemberExpression('.', expr, parseNonComputedMember())); } else { - expr = { - type: Syntax.MemberExpression, - computed: false, - object: expr, - property: parseNonComputedMember() - }; + expr = markerApply(marker, delegate.createTaggedTemplateExpression(expr, parseTemplateLiteral())); } } @@ -1700,10 +2717,11 @@ parseStatement: true, parseSourceElement: true */ // 11.3 Postfix Expressions function parsePostfixExpression() { - var expr = parseLeftHandSideExpressionAllowCall(), token; + var marker = markerCreate(), + expr = parseLeftHandSideExpressionAllowCall(), + token; - token = lookahead(); - if (token.type !== Token.Punctuator) { + if (lookahead.type !== Token.Punctuator) { return expr; } @@ -1712,16 +2730,13 @@ parseStatement: true, parseSourceElement: true */ if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { throwErrorTolerant({}, Messages.StrictLHSPostfix); } + if (!isLeftHandSide(expr)) { - throwErrorTolerant({}, Messages.InvalidLHSInAssignment); + throwError({}, Messages.InvalidLHSInAssignment); } - expr = { - type: Syntax.UpdateExpression, - operator: lex().value, - argument: expr, - prefix: false - }; + token = lex(); + expr = markerApply(marker, delegate.createPostfixExpression(token.value, expr)); } return expr; @@ -1730,14 +2745,14 @@ parseStatement: true, parseSourceElement: true */ // 11.4 Unary Operators function parseUnaryExpression() { - var token, expr; + var marker, token, expr; - token = lookahead(); - if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { + if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) { return parsePostfixExpression(); } if (match('++') || match('--')) { + marker = markerCreate(); token = lex(); expr = parseUnaryExpression(); // 11.4.4, 11.4.5 @@ -1746,35 +2761,24 @@ parseStatement: true, parseSourceElement: true */ } if (!isLeftHandSide(expr)) { - throwErrorTolerant({}, Messages.InvalidLHSInAssignment); + throwError({}, Messages.InvalidLHSInAssignment); } - expr = { - type: Syntax.UpdateExpression, - operator: token.value, - argument: expr, - prefix: true - }; - return expr; + return markerApply(marker, delegate.createUnaryExpression(token.value, expr)); } if (match('+') || match('-') || match('~') || match('!')) { - expr = { - type: Syntax.UnaryExpression, - operator: lex().value, - argument: parseUnaryExpression(), - prefix: true - }; - return expr; + marker = markerCreate(); + token = lex(); + expr = parseUnaryExpression(); + return markerApply(marker, delegate.createUnaryExpression(token.value, expr)); } if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { - expr = { - type: Syntax.UnaryExpression, - operator: lex().value, - argument: parseUnaryExpression(), - prefix: true - }; + marker = markerCreate(); + token = lex(); + expr = parseUnaryExpression(); + expr = markerApply(marker, delegate.createUnaryExpression(token.value, expr)); if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) { throwErrorTolerant({}, Messages.StrictDelete); } @@ -1784,186 +2788,154 @@ parseStatement: true, parseSourceElement: true */ return parsePostfixExpression(); } + function binaryPrecedence(token, allowIn) { + var prec = 0; + + if (token.type !== Token.Punctuator && token.type !== Token.Keyword) { + return 0; + } + + switch (token.value) { + case '||': + prec = 1; + break; + + case '&&': + prec = 2; + break; + + case '|': + prec = 3; + break; + + case '^': + prec = 4; + break; + + case '&': + prec = 5; + break; + + case '==': + case '!=': + case '===': + case '!==': + prec = 6; + break; + + case '<': + case '>': + case '<=': + case '>=': + case 'instanceof': + prec = 7; + break; + + case 'in': + prec = allowIn ? 7 : 0; + break; + + case '<<': + case '>>': + case '>>>': + prec = 8; + break; + + case '+': + case '-': + prec = 9; + break; + + case '*': + case '/': + case '%': + prec = 11; + break; + + default: + break; + } + + return prec; + } + // 11.5 Multiplicative Operators - - function parseMultiplicativeExpression() { - var expr = parseUnaryExpression(); - - while (match('*') || match('/') || match('%')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseUnaryExpression() - }; - } - - return expr; - } - // 11.6 Additive Operators - - function parseAdditiveExpression() { - var expr = parseMultiplicativeExpression(); - - while (match('+') || match('-')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseMultiplicativeExpression() - }; - } - - return expr; - } - // 11.7 Bitwise Shift Operators - - function parseShiftExpression() { - var expr = parseAdditiveExpression(); - - while (match('<<') || match('>>') || match('>>>')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseAdditiveExpression() - }; - } - - return expr; - } // 11.8 Relational Operators + // 11.9 Equality Operators + // 11.10 Binary Bitwise Operators + // 11.11 Binary Logical Operators - function parseRelationalExpression() { - var expr, previousAllowIn; + function parseBinaryExpression() { + var expr, token, prec, previousAllowIn, stack, right, operator, left, i, + marker, markers; previousAllowIn = state.allowIn; state.allowIn = true; - expr = parseShiftExpression(); + marker = markerCreate(); + left = parseUnaryExpression(); - while (match('<') || match('>') || match('<=') || match('>=') || (previousAllowIn && matchKeyword('in')) || matchKeyword('instanceof')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseShiftExpression() - }; + token = lookahead; + prec = binaryPrecedence(token, previousAllowIn); + if (prec === 0) { + return left; + } + token.prec = prec; + lex(); + + markers = [marker, markerCreate()]; + right = parseUnaryExpression(); + + stack = [left, token, right]; + + while ((prec = binaryPrecedence(lookahead, previousAllowIn)) > 0) { + + // Reduce: make a binary expression from the three topmost entries. + while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) { + right = stack.pop(); + operator = stack.pop().value; + left = stack.pop(); + expr = delegate.createBinaryExpression(operator, left, right); + markers.pop(); + marker = markers.pop(); + markerApply(marker, expr); + stack.push(expr); + markers.push(marker); + } + + // Shift. + token = lex(); + token.prec = prec; + stack.push(token); + markers.push(markerCreate()); + expr = parseUnaryExpression(); + stack.push(expr); } state.allowIn = previousAllowIn; - return expr; - } - // 11.9 Equality Operators - - function parseEqualityExpression() { - var expr = parseRelationalExpression(); - - while (match('==') || match('!=') || match('===') || match('!==')) { - expr = { - type: Syntax.BinaryExpression, - operator: lex().value, - left: expr, - right: parseRelationalExpression() - }; + // Final reduce to clean-up the stack. + i = stack.length - 1; + expr = stack[i]; + markers.pop(); + while (i > 1) { + expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr); + i -= 2; + marker = markers.pop(); + markerApply(marker, expr); } return expr; } - // 11.10 Binary Bitwise Operators - - function parseBitwiseANDExpression() { - var expr = parseEqualityExpression(); - - while (match('&')) { - lex(); - expr = { - type: Syntax.BinaryExpression, - operator: '&', - left: expr, - right: parseEqualityExpression() - }; - } - - return expr; - } - - function parseBitwiseXORExpression() { - var expr = parseBitwiseANDExpression(); - - while (match('^')) { - lex(); - expr = { - type: Syntax.BinaryExpression, - operator: '^', - left: expr, - right: parseBitwiseANDExpression() - }; - } - - return expr; - } - - function parseBitwiseORExpression() { - var expr = parseBitwiseXORExpression(); - - while (match('|')) { - lex(); - expr = { - type: Syntax.BinaryExpression, - operator: '|', - left: expr, - right: parseBitwiseXORExpression() - }; - } - - return expr; - } - - // 11.11 Binary Logical Operators - - function parseLogicalANDExpression() { - var expr = parseBitwiseORExpression(); - - while (match('&&')) { - lex(); - expr = { - type: Syntax.LogicalExpression, - operator: '&&', - left: expr, - right: parseBitwiseORExpression() - }; - } - - return expr; - } - - function parseLogicalORExpression() { - var expr = parseLogicalANDExpression(); - - while (match('||')) { - lex(); - expr = { - type: Syntax.LogicalExpression, - operator: '||', - left: expr, - right: parseLogicalANDExpression() - }; - } - - return expr; - } // 11.12 Conditional Operator function parseConditionalExpression() { - var expr, previousAllowIn, consequent; - - expr = parseLogicalORExpression(); + var expr, previousAllowIn, consequent, alternate, marker = markerCreate(); + expr = parseBinaryExpression(); if (match('?')) { lex(); @@ -1972,13 +2944,9 @@ parseStatement: true, parseSourceElement: true */ consequent = parseAssignmentExpression(); state.allowIn = previousAllowIn; expect(':'); + alternate = parseAssignmentExpression(); - expr = { - type: Syntax.ConditionalExpression, - test: expr, - consequent: consequent, - alternate: parseAssignmentExpression() - }; + expr = markerApply(marker, delegate.createConditionalExpression(expr, consequent, alternate)); } return expr; @@ -1986,29 +2954,212 @@ parseStatement: true, parseSourceElement: true */ // 11.13 Assignment Operators - function parseAssignmentExpression() { - var token, expr; + function reinterpretAsAssignmentBindingPattern(expr) { + var i, len, property, element; - token = lookahead(); + if (expr.type === Syntax.ObjectExpression) { + expr.type = Syntax.ObjectPattern; + for (i = 0, len = expr.properties.length; i < len; i += 1) { + property = expr.properties[i]; + if (property.kind !== 'init') { + throwError({}, Messages.InvalidLHSInAssignment); + } + reinterpretAsAssignmentBindingPattern(property.value); + } + } else if (expr.type === Syntax.ArrayExpression) { + expr.type = Syntax.ArrayPattern; + for (i = 0, len = expr.elements.length; i < len; i += 1) { + element = expr.elements[i]; + if (element) { + reinterpretAsAssignmentBindingPattern(element); + } + } + } else if (expr.type === Syntax.Identifier) { + if (isRestrictedWord(expr.name)) { + throwError({}, Messages.InvalidLHSInAssignment); + } + } else if (expr.type === Syntax.SpreadElement) { + reinterpretAsAssignmentBindingPattern(expr.argument); + if (expr.argument.type === Syntax.ObjectPattern) { + throwError({}, Messages.ObjectPatternAsSpread); + } + } else { + if (expr.type !== Syntax.MemberExpression && expr.type !== Syntax.CallExpression && expr.type !== Syntax.NewExpression) { + throwError({}, Messages.InvalidLHSInAssignment); + } + } + } + + + function reinterpretAsDestructuredParameter(options, expr) { + var i, len, property, element; + + if (expr.type === Syntax.ObjectExpression) { + expr.type = Syntax.ObjectPattern; + for (i = 0, len = expr.properties.length; i < len; i += 1) { + property = expr.properties[i]; + if (property.kind !== 'init') { + throwError({}, Messages.InvalidLHSInFormalsList); + } + reinterpretAsDestructuredParameter(options, property.value); + } + } else if (expr.type === Syntax.ArrayExpression) { + expr.type = Syntax.ArrayPattern; + for (i = 0, len = expr.elements.length; i < len; i += 1) { + element = expr.elements[i]; + if (element) { + reinterpretAsDestructuredParameter(options, element); + } + } + } else if (expr.type === Syntax.Identifier) { + validateParam(options, expr, expr.name); + } else { + if (expr.type !== Syntax.MemberExpression) { + throwError({}, Messages.InvalidLHSInFormalsList); + } + } + } + + function reinterpretAsCoverFormalsList(expressions) { + var i, len, param, params, defaults, defaultCount, options, rest; + + params = []; + defaults = []; + defaultCount = 0; + rest = null; + options = { + paramSet: {} + }; + + for (i = 0, len = expressions.length; i < len; i += 1) { + param = expressions[i]; + if (param.type === Syntax.Identifier) { + params.push(param); + defaults.push(null); + validateParam(options, param, param.name); + } else if (param.type === Syntax.ObjectExpression || param.type === Syntax.ArrayExpression) { + reinterpretAsDestructuredParameter(options, param); + params.push(param); + defaults.push(null); + } else if (param.type === Syntax.SpreadElement) { + assert(i === len - 1, 'It is guaranteed that SpreadElement is last element by parseExpression'); + reinterpretAsDestructuredParameter(options, param.argument); + rest = param.argument; + } else if (param.type === Syntax.AssignmentExpression) { + params.push(param.left); + defaults.push(param.right); + ++defaultCount; + validateParam(options, param.left, param.left.name); + } else { + return null; + } + } + + if (options.message === Messages.StrictParamDupe) { + throwError( + strict ? options.stricted : options.firstRestricted, + options.message + ); + } + + if (defaultCount === 0) { + defaults = []; + } + + return { + params: params, + defaults: defaults, + rest: rest, + stricted: options.stricted, + firstRestricted: options.firstRestricted, + message: options.message + }; + } + + function parseArrowFunctionExpression(options, marker) { + var previousStrict, previousYieldAllowed, body; + + expect('=>'); + + previousStrict = strict; + previousYieldAllowed = state.yieldAllowed; + state.yieldAllowed = false; + body = parseConciseBody(); + + if (strict && options.firstRestricted) { + throwError(options.firstRestricted, options.message); + } + if (strict && options.stricted) { + throwErrorTolerant(options.stricted, options.message); + } + + strict = previousStrict; + state.yieldAllowed = previousYieldAllowed; + + return markerApply(marker, delegate.createArrowFunctionExpression( + options.params, + options.defaults, + body, + options.rest, + body.type !== Syntax.BlockStatement + )); + } + + function parseAssignmentExpression() { + var marker, expr, token, params, oldParenthesizedCount; + + // Note that 'yield' is treated as a keyword in strict mode, but a + // contextual keyword (identifier) in non-strict mode, so we need + // to use matchKeyword and matchContextualKeyword appropriately. + if ((state.yieldAllowed && matchContextualKeyword('yield')) || (strict && matchKeyword('yield'))) { + return parseYieldExpression(); + } + + oldParenthesizedCount = state.parenthesizedCount; + + marker = markerCreate(); + + if (match('(')) { + token = lookahead2(); + if ((token.type === Token.Punctuator && token.value === ')') || token.value === '...') { + params = parseParams(); + if (!match('=>')) { + throwUnexpected(lex()); + } + return parseArrowFunctionExpression(params, marker); + } + } + + token = lookahead; expr = parseConditionalExpression(); - if (matchAssign()) { - // LeftHandSideExpression - if (!isLeftHandSide(expr)) { - throwErrorTolerant({}, Messages.InvalidLHSInAssignment); + if (match('=>') && + (state.parenthesizedCount === oldParenthesizedCount || + state.parenthesizedCount === (oldParenthesizedCount + 1))) { + if (expr.type === Syntax.Identifier) { + params = reinterpretAsCoverFormalsList([ expr ]); + } else if (expr.type === Syntax.SequenceExpression) { + params = reinterpretAsCoverFormalsList(expr.expressions); } + if (params) { + return parseArrowFunctionExpression(params, marker); + } + } + if (matchAssign()) { // 11.13.1 if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) { throwErrorTolerant(token, Messages.StrictLHSAssignment); } - expr = { - type: Syntax.AssignmentExpression, - operator: lex().value, - left: expr, - right: parseAssignmentExpression() - }; + // ES.next draf 11.13 Runtime Semantics step 1 + if (match('=') && (expr.type === Syntax.ObjectExpression || expr.type === Syntax.ArrayExpression)) { + reinterpretAsAssignmentBindingPattern(expr); + } else if (!isLeftHandSide(expr)) { + throwError({}, Messages.InvalidLHSInAssignment); + } + + expr = markerApply(marker, delegate.createAssignmentExpression(lex().value, expr, parseAssignmentExpression())); } return expr; @@ -2017,24 +3168,53 @@ parseStatement: true, parseSourceElement: true */ // 11.14 Comma Operator function parseExpression() { - var expr = parseAssignmentExpression(); + var marker, expr, expressions, sequence, coverFormalsList, spreadFound, oldParenthesizedCount; + + oldParenthesizedCount = state.parenthesizedCount; + + marker = markerCreate(); + expr = parseAssignmentExpression(); + expressions = [ expr ]; if (match(',')) { - expr = { - type: Syntax.SequenceExpression, - expressions: [ expr ] - }; - while (index < length) { if (!match(',')) { break; } + lex(); - expr.expressions.push(parseAssignmentExpression()); + expr = parseSpreadOrAssignmentExpression(); + expressions.push(expr); + + if (expr.type === Syntax.SpreadElement) { + spreadFound = true; + if (!match(')')) { + throwError({}, Messages.ElementAfterSpreadElement); + } + break; + } } + sequence = markerApply(marker, delegate.createSequenceExpression(expressions)); } - return expr; + + if (match('=>')) { + // Do not allow nested parentheses on the LHS of the =>. + if (state.parenthesizedCount === oldParenthesizedCount || state.parenthesizedCount === (oldParenthesizedCount + 1)) { + expr = expr.type === Syntax.SequenceExpression ? expr.expressions : expressions; + coverFormalsList = reinterpretAsCoverFormalsList(expr); + if (coverFormalsList) { + return parseArrowFunctionExpression(coverFormalsList, marker); + } + } + throwUnexpected(lex()); + } + + if (spreadFound && lookahead2().value !== '=>') { + throwError({}, Messages.IllegalSpread); + } + + return sequence || expr; } // 12.1 Block @@ -2058,7 +3238,7 @@ parseStatement: true, parseSourceElement: true */ } function parseBlock() { - var block; + var block, marker = markerCreate(); expect('{'); @@ -2066,37 +3246,44 @@ parseStatement: true, parseSourceElement: true */ expect('}'); - return { - type: Syntax.BlockStatement, - body: block - }; + return markerApply(marker, delegate.createBlockStatement(block)); } // 12.2 Variable Statement function parseVariableIdentifier() { - var token = lex(); + var marker = markerCreate(), + token = lex(); if (token.type !== Token.Identifier) { throwUnexpected(token); } - return { - type: Syntax.Identifier, - name: token.value - }; + return markerApply(marker, delegate.createIdentifier(token.value)); } function parseVariableDeclaration(kind) { - var id = parseVariableIdentifier(), + var id, + marker = markerCreate(), init = null; - - // 12.2.1 - if (strict && isRestrictedWord(id.name)) { - throwErrorTolerant({}, Messages.StrictVarName); + if (match('{')) { + id = parseObjectInitialiser(); + reinterpretAsAssignmentBindingPattern(id); + } else if (match('[')) { + id = parseArrayInitialiser(); + reinterpretAsAssignmentBindingPattern(id); + } else { + id = state.allowKeyword ? parseNonComputedProperty() : parseVariableIdentifier(); + // 12.2.1 + if (strict && isRestrictedWord(id.name)) { + throwErrorTolerant({}, Messages.StrictVarName); + } } if (kind === 'const') { + if (!match('=')) { + throwError({}, Messages.NoUnintializedConst); + } expect('='); init = parseAssignmentExpression(); } else if (match('=')) { @@ -2104,11 +3291,7 @@ parseStatement: true, parseSourceElement: true */ init = parseAssignmentExpression(); } - return { - type: Syntax.VariableDeclarator, - id: id, - init: init - }; + return markerApply(marker, delegate.createVariableDeclarator(id, init)); } function parseVariableDeclarationList(kind) { @@ -2126,7 +3309,7 @@ parseStatement: true, parseSourceElement: true */ } function parseVariableStatement() { - var declarations; + var declarations, marker = markerCreate(); expectKeyword('var'); @@ -2134,11 +3317,7 @@ parseStatement: true, parseSourceElement: true */ consumeSemicolon(); - return { - type: Syntax.VariableDeclaration, - declarations: declarations, - kind: 'var' - }; + return markerApply(marker, delegate.createVariableDeclaration(declarations, 'var')); } // kind may be `const` or `let` @@ -2146,7 +3325,7 @@ parseStatement: true, parseSourceElement: true */ // see http://wiki.ecmascript.org/doku.php?id=harmony:const // and http://wiki.ecmascript.org/doku.php?id=harmony:let function parseConstLetDeclaration(kind) { - var declarations; + var declarations, marker = markerCreate(); expectKeyword(kind); @@ -2154,40 +3333,185 @@ parseStatement: true, parseSourceElement: true */ consumeSemicolon(); - return { - type: Syntax.VariableDeclaration, - declarations: declarations, - kind: kind - }; + return markerApply(marker, delegate.createVariableDeclaration(declarations, kind)); + } + + // http://wiki.ecmascript.org/doku.php?id=harmony:modules + + function parseModuleDeclaration() { + var id, src, body, marker = markerCreate(); + + lex(); // 'module' + + if (peekLineTerminator()) { + throwError({}, Messages.NewlineAfterModule); + } + + switch (lookahead.type) { + + case Token.StringLiteral: + id = parsePrimaryExpression(); + body = parseModuleBlock(); + src = null; + break; + + case Token.Identifier: + id = parseVariableIdentifier(); + body = null; + if (!matchContextualKeyword('from')) { + throwUnexpected(lex()); + } + lex(); + src = parsePrimaryExpression(); + if (src.type !== Syntax.Literal) { + throwError({}, Messages.InvalidModuleSpecifier); + } + break; + } + + consumeSemicolon(); + return markerApply(marker, delegate.createModuleDeclaration(id, src, body)); + } + + function parseExportBatchSpecifier() { + var marker = markerCreate(); + expect('*'); + return markerApply(marker, delegate.createExportBatchSpecifier()); + } + + function parseExportSpecifier() { + var id, name = null, marker = markerCreate(); + + id = parseVariableIdentifier(); + if (matchContextualKeyword('as')) { + lex(); + name = parseNonComputedProperty(); + } + + return markerApply(marker, delegate.createExportSpecifier(id, name)); + } + + function parseExportDeclaration() { + var previousAllowKeyword, decl, def, src, specifiers, + marker = markerCreate(); + + expectKeyword('export'); + + if (lookahead.type === Token.Keyword) { + switch (lookahead.value) { + case 'let': + case 'const': + case 'var': + case 'class': + case 'function': + return markerApply(marker, delegate.createExportDeclaration(parseSourceElement(), null, null)); + } + } + + if (isIdentifierName(lookahead)) { + previousAllowKeyword = state.allowKeyword; + state.allowKeyword = true; + decl = parseVariableDeclarationList('let'); + state.allowKeyword = previousAllowKeyword; + return markerApply(marker, delegate.createExportDeclaration(decl, null, null)); + } + + specifiers = []; + src = null; + + if (match('*')) { + specifiers.push(parseExportBatchSpecifier()); + } else { + expect('{'); + do { + specifiers.push(parseExportSpecifier()); + } while (match(',') && lex()); + expect('}'); + } + + if (matchContextualKeyword('from')) { + lex(); + src = parsePrimaryExpression(); + if (src.type !== Syntax.Literal) { + throwError({}, Messages.InvalidModuleSpecifier); + } + } + + consumeSemicolon(); + + return markerApply(marker, delegate.createExportDeclaration(null, specifiers, src)); + } + + function parseImportDeclaration() { + var specifiers, kind, src, marker = markerCreate(); + + expectKeyword('import'); + specifiers = []; + + if (isIdentifierName(lookahead)) { + kind = 'default'; + specifiers.push(parseImportSpecifier()); + + if (!matchContextualKeyword('from')) { + throwError({}, Messages.NoFromAfterImport); + } + lex(); + } else if (match('{')) { + kind = 'named'; + lex(); + do { + specifiers.push(parseImportSpecifier()); + } while (match(',') && lex()); + expect('}'); + + if (!matchContextualKeyword('from')) { + throwError({}, Messages.NoFromAfterImport); + } + lex(); + } + + src = parsePrimaryExpression(); + if (src.type !== Syntax.Literal) { + throwError({}, Messages.InvalidModuleSpecifier); + } + + consumeSemicolon(); + + return markerApply(marker, delegate.createImportDeclaration(specifiers, kind, src)); + } + + function parseImportSpecifier() { + var id, name = null, marker = markerCreate(); + + id = parseNonComputedProperty(); + if (matchContextualKeyword('as')) { + lex(); + name = parseVariableIdentifier(); + } + + return markerApply(marker, delegate.createImportSpecifier(id, name)); } // 12.3 Empty Statement function parseEmptyStatement() { + var marker = markerCreate(); expect(';'); - - return { - type: Syntax.EmptyStatement - }; + return markerApply(marker, delegate.createEmptyStatement()); } // 12.4 Expression Statement function parseExpressionStatement() { - var expr = parseExpression(); - + var marker = markerCreate(), expr = parseExpression(); consumeSemicolon(); - - return { - type: Syntax.ExpressionStatement, - expression: expr - }; + return markerApply(marker, delegate.createExpressionStatement(expr)); } // 12.5 If statement function parseIfStatement() { - var test, consequent, alternate; + var test, consequent, alternate, marker = markerCreate(); expectKeyword('if'); @@ -2206,18 +3530,13 @@ parseStatement: true, parseSourceElement: true */ alternate = null; } - return { - type: Syntax.IfStatement, - test: test, - consequent: consequent, - alternate: alternate - }; + return markerApply(marker, delegate.createIfStatement(test, consequent, alternate)); } // 12.6 Iteration Statements function parseDoWhileStatement() { - var body, test, oldInIteration; + var body, test, oldInIteration, marker = markerCreate(); expectKeyword('do'); @@ -2240,15 +3559,11 @@ parseStatement: true, parseSourceElement: true */ lex(); } - return { - type: Syntax.DoWhileStatement, - body: body, - test: test - }; + return markerApply(marker, delegate.createDoWhileStatement(body, test)); } function parseWhileStatement() { - var test, body, oldInIteration; + var test, body, oldInIteration, marker = markerCreate(); expectKeyword('while'); @@ -2265,58 +3580,65 @@ parseStatement: true, parseSourceElement: true */ state.inIteration = oldInIteration; - return { - type: Syntax.WhileStatement, - test: test, - body: body - }; + return markerApply(marker, delegate.createWhileStatement(test, body)); } function parseForVariableDeclaration() { - var token = lex(); + var marker = markerCreate(), + token = lex(), + declarations = parseVariableDeclarationList(); - return { - type: Syntax.VariableDeclaration, - declarations: parseVariableDeclarationList(), - kind: token.value - }; + return markerApply(marker, delegate.createVariableDeclaration(declarations, token.value)); } - function parseForStatement() { - var init, test, update, left, right, body, oldInIteration; - + function parseForStatement(opts) { + var init, test, update, left, right, body, operator, oldInIteration, + marker = markerCreate(); init = test = update = null; - expectKeyword('for'); + // http://wiki.ecmascript.org/doku.php?id=proposals:iterators_and_generators&s=each + if (matchContextualKeyword('each')) { + throwError({}, Messages.EachNotAllowed); + } + expect('('); if (match(';')) { lex(); } else { - if (matchKeyword('var') || matchKeyword('let')) { + if (matchKeyword('var') || matchKeyword('let') || matchKeyword('const')) { state.allowIn = false; init = parseForVariableDeclaration(); state.allowIn = true; - if (init.declarations.length === 1 && matchKeyword('in')) { - lex(); - left = init; - right = parseExpression(); - init = null; + if (init.declarations.length === 1) { + if (matchKeyword('in') || matchContextualKeyword('of')) { + operator = lookahead; + if (!((operator.value === 'in' || init.kind !== 'var') && init.declarations[0].init)) { + lex(); + left = init; + right = parseExpression(); + init = null; + } + } } } else { state.allowIn = false; init = parseExpression(); state.allowIn = true; - if (matchKeyword('in')) { + if (matchContextualKeyword('of')) { + operator = lex(); + left = init; + right = parseExpression(); + init = null; + } else if (matchKeyword('in')) { // LeftHandSideExpression - if (!isLeftHandSide(init)) { - throwErrorTolerant({}, Messages.InvalidLHSInForIn); + if (!isAssignableLeftHandSide(init)) { + throwError({}, Messages.InvalidLHSInForIn); } - - lex(); + operator = lex(); left = init; right = parseExpression(); init = null; @@ -2345,48 +3667,38 @@ parseStatement: true, parseSourceElement: true */ oldInIteration = state.inIteration; state.inIteration = true; - body = parseStatement(); + if (!(opts !== undefined && opts.ignoreBody)) { + body = parseStatement(); + } state.inIteration = oldInIteration; if (typeof left === 'undefined') { - return { - type: Syntax.ForStatement, - init: init, - test: test, - update: update, - body: body - }; + return markerApply(marker, delegate.createForStatement(init, test, update, body)); } - return { - type: Syntax.ForInStatement, - left: left, - right: right, - body: body, - each: false - }; + if (operator.value === 'in') { + return markerApply(marker, delegate.createForInStatement(left, right, body)); + } + return markerApply(marker, delegate.createForOfStatement(left, right, body)); } // 12.7 The continue statement function parseContinueStatement() { - var token, label = null; + var label = null, key, marker = markerCreate(); expectKeyword('continue'); // Optimize the most common form: 'continue;'. - if (source[index] === ';') { + if (source.charCodeAt(index) === 59) { lex(); if (!state.inIteration) { throwError({}, Messages.IllegalContinue); } - return { - type: Syntax.ContinueStatement, - label: null - }; + return markerApply(marker, delegate.createContinueStatement(null)); } if (peekLineTerminator()) { @@ -2394,17 +3706,14 @@ parseStatement: true, parseSourceElement: true */ throwError({}, Messages.IllegalContinue); } - return { - type: Syntax.ContinueStatement, - label: null - }; + return markerApply(marker, delegate.createContinueStatement(null)); } - token = lookahead(); - if (token.type === Token.Identifier) { + if (lookahead.type === Token.Identifier) { label = parseVariableIdentifier(); - if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) { + key = '$' + label.name; + if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) { throwError({}, Messages.UnknownLabel, label.name); } } @@ -2415,31 +3724,25 @@ parseStatement: true, parseSourceElement: true */ throwError({}, Messages.IllegalContinue); } - return { - type: Syntax.ContinueStatement, - label: label - }; + return markerApply(marker, delegate.createContinueStatement(label)); } // 12.8 The break statement function parseBreakStatement() { - var token, label = null; + var label = null, key, marker = markerCreate(); expectKeyword('break'); - // Optimize the most common form: 'break;'. - if (source[index] === ';') { + // Catch the very common case first: immediately a semicolon (char #59). + if (source.charCodeAt(index) === 59) { lex(); if (!(state.inIteration || state.inSwitch)) { throwError({}, Messages.IllegalBreak); } - return { - type: Syntax.BreakStatement, - label: null - }; + return markerApply(marker, delegate.createBreakStatement(null)); } if (peekLineTerminator()) { @@ -2447,17 +3750,14 @@ parseStatement: true, parseSourceElement: true */ throwError({}, Messages.IllegalBreak); } - return { - type: Syntax.BreakStatement, - label: null - }; + return markerApply(marker, delegate.createBreakStatement(null)); } - token = lookahead(); - if (token.type === Token.Identifier) { + if (lookahead.type === Token.Identifier) { label = parseVariableIdentifier(); - if (!Object.prototype.hasOwnProperty.call(state.labelSet, label.name)) { + key = '$' + label.name; + if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) { throwError({}, Messages.UnknownLabel, label.name); } } @@ -2468,16 +3768,13 @@ parseStatement: true, parseSourceElement: true */ throwError({}, Messages.IllegalBreak); } - return { - type: Syntax.BreakStatement, - label: label - }; + return markerApply(marker, delegate.createBreakStatement(label)); } // 12.9 The return statement function parseReturnStatement() { - var token, argument = null; + var argument = null, marker = markerCreate(); expectKeyword('return'); @@ -2486,43 +3783,33 @@ parseStatement: true, parseSourceElement: true */ } // 'return' followed by a space and an identifier is very common. - if (source[index] === ' ') { - if (isIdentifierStart(source[index + 1])) { + if (source.charCodeAt(index) === 32) { + if (isIdentifierStart(source.charCodeAt(index + 1))) { argument = parseExpression(); consumeSemicolon(); - return { - type: Syntax.ReturnStatement, - argument: argument - }; + return markerApply(marker, delegate.createReturnStatement(argument)); } } if (peekLineTerminator()) { - return { - type: Syntax.ReturnStatement, - argument: null - }; + return markerApply(marker, delegate.createReturnStatement(null)); } if (!match(';')) { - token = lookahead(); - if (!match('}') && token.type !== Token.EOF) { + if (!match('}') && lookahead.type !== Token.EOF) { argument = parseExpression(); } } consumeSemicolon(); - return { - type: Syntax.ReturnStatement, - argument: argument - }; + return markerApply(marker, delegate.createReturnStatement(argument)); } // 12.10 The with statement function parseWithStatement() { - var object, body; + var object, body, marker = markerCreate(); if (strict) { throwErrorTolerant({}, Messages.StrictModeWith); @@ -2538,11 +3825,7 @@ parseStatement: true, parseSourceElement: true */ body = parseStatement(); - return { - type: Syntax.WithStatement, - object: object, - body: body - }; + return markerApply(marker, delegate.createWithStatement(object, body)); } // 12.10 The swith statement @@ -2550,7 +3833,8 @@ parseStatement: true, parseSourceElement: true */ function parseSwitchCase() { var test, consequent = [], - statement; + sourceElement, + marker = markerCreate(); if (matchKeyword('default')) { lex(); @@ -2565,22 +3849,18 @@ parseStatement: true, parseSourceElement: true */ if (match('}') || matchKeyword('default') || matchKeyword('case')) { break; } - statement = parseStatement(); - if (typeof statement === 'undefined') { + sourceElement = parseSourceElement(); + if (typeof sourceElement === 'undefined') { break; } - consequent.push(statement); + consequent.push(sourceElement); } - return { - type: Syntax.SwitchCase, - test: test, - consequent: consequent - }; + return markerApply(marker, delegate.createSwitchCase(test, consequent)); } function parseSwitchStatement() { - var discriminant, cases, clause, oldInSwitch, defaultFound; + var discriminant, cases, clause, oldInSwitch, defaultFound, marker = markerCreate(); expectKeyword('switch'); @@ -2596,11 +3876,7 @@ parseStatement: true, parseSourceElement: true */ if (match('}')) { lex(); - return { - type: Syntax.SwitchStatement, - discriminant: discriminant, - cases: cases - }; + return markerApply(marker, delegate.createSwitchStatement(discriminant, cases)); } oldInSwitch = state.inSwitch; @@ -2625,17 +3901,13 @@ parseStatement: true, parseSourceElement: true */ expect('}'); - return { - type: Syntax.SwitchStatement, - discriminant: discriminant, - cases: cases - }; + return markerApply(marker, delegate.createSwitchStatement(discriminant, cases)); } // 12.13 The throw statement function parseThrowStatement() { - var argument; + var argument, marker = markerCreate(); expectKeyword('throw'); @@ -2647,41 +3919,34 @@ parseStatement: true, parseSourceElement: true */ consumeSemicolon(); - return { - type: Syntax.ThrowStatement, - argument: argument - }; + return markerApply(marker, delegate.createThrowStatement(argument)); } // 12.14 The try statement function parseCatchClause() { - var param; + var param, body, marker = markerCreate(); expectKeyword('catch'); expect('('); if (match(')')) { - throwUnexpected(lookahead()); + throwUnexpected(lookahead); } - param = parseVariableIdentifier(); + param = parseExpression(); // 12.14.1 - if (strict && isRestrictedWord(param.name)) { + if (strict && param.type === Syntax.Identifier && isRestrictedWord(param.name)) { throwErrorTolerant({}, Messages.StrictCatchVariable); } expect(')'); - - return { - type: Syntax.CatchClause, - param: param, - body: parseBlock() - }; + body = parseBlock(); + return markerApply(marker, delegate.createCatchClause(param, body)); } function parseTryStatement() { - var block, handlers = [], finalizer = null; + var block, handlers = [], finalizer = null, marker = markerCreate(); expectKeyword('try'); @@ -2700,40 +3965,35 @@ parseStatement: true, parseSourceElement: true */ throwError({}, Messages.NoCatchOrFinally); } - return { - type: Syntax.TryStatement, - block: block, - guardedHandlers: [], - handlers: handlers, - finalizer: finalizer - }; + return markerApply(marker, delegate.createTryStatement(block, [], handlers, finalizer)); } // 12.15 The debugger statement function parseDebuggerStatement() { + var marker = markerCreate(); expectKeyword('debugger'); consumeSemicolon(); - return { - type: Syntax.DebuggerStatement - }; + return markerApply(marker, delegate.createDebuggerStatement()); } // 12 Statements function parseStatement() { - var token = lookahead(), + var type = lookahead.type, + marker, expr, - labeledBody; + labeledBody, + key; - if (token.type === Token.EOF) { - throwUnexpected(token); + if (type === Token.EOF) { + throwUnexpected(lookahead); } - if (token.type === Token.Punctuator) { - switch (token.value) { + if (type === Token.Punctuator) { + switch (lookahead.value) { case ';': return parseEmptyStatement(); case '{': @@ -2745,8 +4005,8 @@ parseStatement: true, parseSourceElement: true */ } } - if (token.type === Token.Keyword) { - switch (token.value) { + if (type === Token.Keyword) { + switch (lookahead.value) { case 'break': return parseBreakStatement(); case 'continue': @@ -2759,6 +4019,8 @@ parseStatement: true, parseSourceElement: true */ return parseForStatement(); case 'function': return parseFunctionDeclaration(); + case 'class': + return parseClassDeclaration(); case 'if': return parseIfStatement(); case 'return': @@ -2780,48 +4042,50 @@ parseStatement: true, parseSourceElement: true */ } } + marker = markerCreate(); expr = parseExpression(); // 12.12 Labelled Statements if ((expr.type === Syntax.Identifier) && match(':')) { lex(); - if (Object.prototype.hasOwnProperty.call(state.labelSet, expr.name)) { + key = '$' + expr.name; + if (Object.prototype.hasOwnProperty.call(state.labelSet, key)) { throwError({}, Messages.Redeclaration, 'Label', expr.name); } - state.labelSet[expr.name] = true; + state.labelSet[key] = true; labeledBody = parseStatement(); - delete state.labelSet[expr.name]; - - return { - type: Syntax.LabeledStatement, - label: expr, - body: labeledBody - }; + delete state.labelSet[key]; + return markerApply(marker, delegate.createLabeledStatement(expr, labeledBody)); } consumeSemicolon(); - return { - type: Syntax.ExpressionStatement, - expression: expr - }; + return markerApply(marker, delegate.createExpressionStatement(expr)); } // 13 Function Definition + function parseConciseBody() { + if (match('{')) { + return parseFunctionSourceElements(); + } + return parseAssignmentExpression(); + } + function parseFunctionSourceElements() { var sourceElement, sourceElements = [], token, directive, firstRestricted, - oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody; + oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, oldParenthesizedCount, + marker = markerCreate(); expect('{'); while (index < length) { - token = lookahead(); - if (token.type !== Token.StringLiteral) { + if (lookahead.type !== Token.StringLiteral) { break; } + token = lookahead; sourceElement = parseSourceElement(); sourceElements.push(sourceElement); @@ -2829,7 +4093,7 @@ parseStatement: true, parseSourceElement: true */ // this is not directive break; } - directive = sliceSource(token.range[0] + 1, token.range[1] - 1); + directive = source.slice(token.range[0] + 1, token.range[1] - 1); if (directive === 'use strict') { strict = true; if (firstRestricted) { @@ -2846,11 +4110,13 @@ parseStatement: true, parseSourceElement: true */ oldInIteration = state.inIteration; oldInSwitch = state.inSwitch; oldInFunctionBody = state.inFunctionBody; + oldParenthesizedCount = state.parenthesizedCount; state.labelSet = {}; state.inIteration = false; state.inSwitch = false; state.inFunctionBody = true; + state.parenthesizedCount = 0; while (index < length) { if (match('}')) { @@ -2869,19 +4135,129 @@ parseStatement: true, parseSourceElement: true */ state.inIteration = oldInIteration; state.inSwitch = oldInSwitch; state.inFunctionBody = oldInFunctionBody; + state.parenthesizedCount = oldParenthesizedCount; - return { - type: Syntax.BlockStatement, - body: sourceElements + return markerApply(marker, delegate.createBlockStatement(sourceElements)); + } + + function validateParam(options, param, name) { + var key = '$' + name; + if (strict) { + if (isRestrictedWord(name)) { + options.stricted = param; + options.message = Messages.StrictParamName; + } + if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) { + options.stricted = param; + options.message = Messages.StrictParamDupe; + } + } else if (!options.firstRestricted) { + if (isRestrictedWord(name)) { + options.firstRestricted = param; + options.message = Messages.StrictParamName; + } else if (isStrictModeReservedWord(name)) { + options.firstRestricted = param; + options.message = Messages.StrictReservedWord; + } else if (Object.prototype.hasOwnProperty.call(options.paramSet, key)) { + options.firstRestricted = param; + options.message = Messages.StrictParamDupe; + } + } + options.paramSet[key] = true; + } + + function parseParam(options) { + var token, rest, param, def; + + token = lookahead; + if (token.value === '...') { + token = lex(); + rest = true; + } + + if (match('[')) { + param = parseArrayInitialiser(); + reinterpretAsDestructuredParameter(options, param); + } else if (match('{')) { + if (rest) { + throwError({}, Messages.ObjectPatternAsRestParameter); + } + param = parseObjectInitialiser(); + reinterpretAsDestructuredParameter(options, param); + } else { + param = parseVariableIdentifier(); + validateParam(options, token, token.value); + if (match('=')) { + if (rest) { + throwErrorTolerant(lookahead, Messages.DefaultRestParameter); + } + lex(); + def = parseAssignmentExpression(); + ++options.defaultCount; + } + } + + if (rest) { + if (!match(')')) { + throwError({}, Messages.ParameterAfterRestParameter); + } + options.rest = param; + return false; + } + + options.params.push(param); + options.defaults.push(def); + return !match(')'); + } + + function parseParams(firstRestricted) { + var options, marker = markerCreate(); + + options = { + params: [], + defaultCount: 0, + defaults: [], + rest: null, + firstRestricted: firstRestricted }; + + expect('('); + + if (!match(')')) { + options.paramSet = {}; + while (index < length) { + if (!parseParam(options)) { + break; + } + expect(','); + } + } + + expect(')'); + + if (options.defaultCount === 0) { + options.defaults = []; + } + + return markerApply(marker, options); } function parseFunctionDeclaration() { - var id, param, params = [], body, token, stricted, firstRestricted, message, previousStrict, paramSet; + var id, body, token, tmp, firstRestricted, message, previousStrict, previousYieldAllowed, generator, + marker = markerCreate(); expectKeyword('function'); - token = lookahead(); + + generator = false; + if (match('*')) { + lex(); + generator = true; + } + + token = lookahead; + id = parseVariableIdentifier(); + if (strict) { if (isRestrictedWord(token.value)) { throwErrorTolerant(token, Messages.StrictFunctionName); @@ -2896,74 +4272,45 @@ parseStatement: true, parseSourceElement: true */ } } - expect('('); - - if (!match(')')) { - paramSet = {}; - while (index < length) { - token = lookahead(); - param = parseVariableIdentifier(); - if (strict) { - if (isRestrictedWord(token.value)) { - stricted = token; - message = Messages.StrictParamName; - } - if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) { - stricted = token; - message = Messages.StrictParamDupe; - } - } else if (!firstRestricted) { - if (isRestrictedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictParamName; - } else if (isStrictModeReservedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictReservedWord; - } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) { - firstRestricted = token; - message = Messages.StrictParamDupe; - } - } - params.push(param); - paramSet[param.name] = true; - if (match(')')) { - break; - } - expect(','); - } + tmp = parseParams(firstRestricted); + firstRestricted = tmp.firstRestricted; + if (tmp.message) { + message = tmp.message; } - expect(')'); - previousStrict = strict; + previousYieldAllowed = state.yieldAllowed; + state.yieldAllowed = generator; + body = parseFunctionSourceElements(); + if (strict && firstRestricted) { throwError(firstRestricted, message); } - if (strict && stricted) { - throwErrorTolerant(stricted, message); + if (strict && tmp.stricted) { + throwErrorTolerant(tmp.stricted, message); } strict = previousStrict; + state.yieldAllowed = previousYieldAllowed; - return { - type: Syntax.FunctionDeclaration, - id: id, - params: params, - defaults: [], - body: body, - rest: null, - generator: false, - expression: false - }; + return markerApply(marker, delegate.createFunctionDeclaration(id, tmp.params, tmp.defaults, body, tmp.rest, generator, false)); } function parseFunctionExpression() { - var token, id = null, stricted, firstRestricted, message, param, params = [], body, previousStrict, paramSet; + var token, id = null, firstRestricted, message, tmp, body, previousStrict, previousYieldAllowed, generator, + marker = markerCreate(); expectKeyword('function'); + generator = false; + + if (match('*')) { + lex(); + generator = true; + } + if (!match('(')) { - token = lookahead(); + token = lookahead; id = parseVariableIdentifier(); if (strict) { if (isRestrictedWord(token.value)) { @@ -2980,105 +4327,297 @@ parseStatement: true, parseSourceElement: true */ } } - expect('('); - - if (!match(')')) { - paramSet = {}; - while (index < length) { - token = lookahead(); - param = parseVariableIdentifier(); - if (strict) { - if (isRestrictedWord(token.value)) { - stricted = token; - message = Messages.StrictParamName; - } - if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) { - stricted = token; - message = Messages.StrictParamDupe; - } - } else if (!firstRestricted) { - if (isRestrictedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictParamName; - } else if (isStrictModeReservedWord(token.value)) { - firstRestricted = token; - message = Messages.StrictReservedWord; - } else if (Object.prototype.hasOwnProperty.call(paramSet, token.value)) { - firstRestricted = token; - message = Messages.StrictParamDupe; - } - } - params.push(param); - paramSet[param.name] = true; - if (match(')')) { - break; - } - expect(','); - } + tmp = parseParams(firstRestricted); + firstRestricted = tmp.firstRestricted; + if (tmp.message) { + message = tmp.message; } - expect(')'); - previousStrict = strict; + previousYieldAllowed = state.yieldAllowed; + state.yieldAllowed = generator; + body = parseFunctionSourceElements(); + if (strict && firstRestricted) { throwError(firstRestricted, message); } - if (strict && stricted) { - throwErrorTolerant(stricted, message); + if (strict && tmp.stricted) { + throwErrorTolerant(tmp.stricted, message); } strict = previousStrict; + state.yieldAllowed = previousYieldAllowed; - return { - type: Syntax.FunctionExpression, - id: id, - params: params, - defaults: [], - body: body, - rest: null, - generator: false, - expression: false - }; + return markerApply(marker, delegate.createFunctionExpression(id, tmp.params, tmp.defaults, body, tmp.rest, generator, false)); } - // 14 Program + function parseYieldExpression() { + var yieldToken, delegateFlag, expr, marker = markerCreate(); + + yieldToken = lex(); + assert(yieldToken.value === 'yield', 'Called parseYieldExpression with non-yield lookahead.'); + + if (!state.yieldAllowed) { + throwErrorTolerant({}, Messages.IllegalYield); + } + + delegateFlag = false; + if (match('*')) { + lex(); + delegateFlag = true; + } + + expr = parseAssignmentExpression(); + + return markerApply(marker, delegate.createYieldExpression(expr, delegateFlag)); + } + + // 14 Classes + + function parseMethodDefinition(existingPropNames) { + var token, key, param, propType, isValidDuplicateProp = false, + marker = markerCreate(); + + if (lookahead.value === 'static') { + propType = ClassPropertyType.static; + lex(); + } else { + propType = ClassPropertyType.prototype; + } + + if (match('*')) { + lex(); + return markerApply(marker, delegate.createMethodDefinition( + propType, + '', + parseObjectPropertyKey(), + parsePropertyMethodFunction({ generator: true }) + )); + } + + token = lookahead; + key = parseObjectPropertyKey(); + + if (token.value === 'get' && !match('(')) { + key = parseObjectPropertyKey(); + + // It is a syntax error if any other properties have a name + // duplicating this one unless they are a setter + if (existingPropNames[propType].hasOwnProperty(key.name)) { + isValidDuplicateProp = + // There isn't already a getter for this prop + existingPropNames[propType][key.name].get === undefined + // There isn't already a data prop by this name + && existingPropNames[propType][key.name].data === undefined + // The only existing prop by this name is a setter + && existingPropNames[propType][key.name].set !== undefined; + if (!isValidDuplicateProp) { + throwError(key, Messages.IllegalDuplicateClassProperty); + } + } else { + existingPropNames[propType][key.name] = {}; + } + existingPropNames[propType][key.name].get = true; + + expect('('); + expect(')'); + return markerApply(marker, delegate.createMethodDefinition( + propType, + 'get', + key, + parsePropertyFunction({ generator: false }) + )); + } + if (token.value === 'set' && !match('(')) { + key = parseObjectPropertyKey(); + + // It is a syntax error if any other properties have a name + // duplicating this one unless they are a getter + if (existingPropNames[propType].hasOwnProperty(key.name)) { + isValidDuplicateProp = + // There isn't already a setter for this prop + existingPropNames[propType][key.name].set === undefined + // There isn't already a data prop by this name + && existingPropNames[propType][key.name].data === undefined + // The only existing prop by this name is a getter + && existingPropNames[propType][key.name].get !== undefined; + if (!isValidDuplicateProp) { + throwError(key, Messages.IllegalDuplicateClassProperty); + } + } else { + existingPropNames[propType][key.name] = {}; + } + existingPropNames[propType][key.name].set = true; + + expect('('); + token = lookahead; + param = [ parseVariableIdentifier() ]; + expect(')'); + return markerApply(marker, delegate.createMethodDefinition( + propType, + 'set', + key, + parsePropertyFunction({ params: param, generator: false, name: token }) + )); + } + + // It is a syntax error if any other properties have the same name as a + // non-getter, non-setter method + if (existingPropNames[propType].hasOwnProperty(key.name)) { + throwError(key, Messages.IllegalDuplicateClassProperty); + } else { + existingPropNames[propType][key.name] = {}; + } + existingPropNames[propType][key.name].data = true; + + return markerApply(marker, delegate.createMethodDefinition( + propType, + '', + key, + parsePropertyMethodFunction({ generator: false }) + )); + } + + function parseClassElement(existingProps) { + if (match(';')) { + lex(); + return; + } + return parseMethodDefinition(existingProps); + } + + function parseClassBody() { + var classElement, classElements = [], existingProps = {}, marker = markerCreate(); + + existingProps[ClassPropertyType.static] = {}; + existingProps[ClassPropertyType.prototype] = {}; + + expect('{'); + + while (index < length) { + if (match('}')) { + break; + } + classElement = parseClassElement(existingProps); + + if (typeof classElement !== 'undefined') { + classElements.push(classElement); + } + } + + expect('}'); + + return markerApply(marker, delegate.createClassBody(classElements)); + } + + function parseClassExpression() { + var id, previousYieldAllowed, superClass = null, marker = markerCreate(); + + expectKeyword('class'); + + if (!matchKeyword('extends') && !match('{')) { + id = parseVariableIdentifier(); + } + + if (matchKeyword('extends')) { + expectKeyword('extends'); + previousYieldAllowed = state.yieldAllowed; + state.yieldAllowed = false; + superClass = parseAssignmentExpression(); + state.yieldAllowed = previousYieldAllowed; + } + + return markerApply(marker, delegate.createClassExpression(id, superClass, parseClassBody())); + } + + function parseClassDeclaration() { + var id, previousYieldAllowed, superClass = null, marker = markerCreate(); + + expectKeyword('class'); + + id = parseVariableIdentifier(); + + if (matchKeyword('extends')) { + expectKeyword('extends'); + previousYieldAllowed = state.yieldAllowed; + state.yieldAllowed = false; + superClass = parseAssignmentExpression(); + state.yieldAllowed = previousYieldAllowed; + } + + return markerApply(marker, delegate.createClassDeclaration(id, superClass, parseClassBody())); + } + + // 15 Program + + function matchModuleDeclaration() { + var id; + if (matchContextualKeyword('module')) { + id = lookahead2(); + return id.type === Token.StringLiteral || id.type === Token.Identifier; + } + return false; + } function parseSourceElement() { - var token = lookahead(); - - if (token.type === Token.Keyword) { - switch (token.value) { + if (lookahead.type === Token.Keyword) { + switch (lookahead.value) { case 'const': case 'let': - return parseConstLetDeclaration(token.value); + return parseConstLetDeclaration(lookahead.value); case 'function': return parseFunctionDeclaration(); + case 'export': + return parseExportDeclaration(); + case 'import': + return parseImportDeclaration(); default: return parseStatement(); } } - if (token.type !== Token.EOF) { + if (matchModuleDeclaration()) { + throwError({}, Messages.NestedModule); + } + + if (lookahead.type !== Token.EOF) { return parseStatement(); } } - function parseSourceElements() { + function parseProgramElement() { + if (lookahead.type === Token.Keyword) { + switch (lookahead.value) { + case 'export': + return parseExportDeclaration(); + case 'import': + return parseImportDeclaration(); + } + } + + if (matchModuleDeclaration()) { + return parseModuleDeclaration(); + } + + return parseSourceElement(); + } + + function parseProgramElements() { var sourceElement, sourceElements = [], token, directive, firstRestricted; while (index < length) { - token = lookahead(); + token = lookahead; if (token.type !== Token.StringLiteral) { break; } - sourceElement = parseSourceElement(); + sourceElement = parseProgramElement(); sourceElements.push(sourceElement); if (sourceElement.expression.type !== Syntax.Literal) { // this is not directive break; } - directive = sliceSource(token.range[0] + 1, token.range[1] - 1); + directive = source.slice(token.range[0] + 1, token.range[1] - 1); if (directive === 'use strict') { strict = true; if (firstRestricted) { @@ -3092,7 +4631,7 @@ parseStatement: true, parseSourceElement: true */ } while (index < length) { - sourceElement = parseSourceElement(); + sourceElement = parseProgramElement(); if (typeof sourceElement === 'undefined') { break; } @@ -3101,38 +4640,76 @@ parseStatement: true, parseSourceElement: true */ return sourceElements; } + function parseModuleElement() { + return parseSourceElement(); + } + + function parseModuleElements() { + var list = [], + statement; + + while (index < length) { + if (match('}')) { + break; + } + statement = parseModuleElement(); + if (typeof statement === 'undefined') { + break; + } + list.push(statement); + } + + return list; + } + + function parseModuleBlock() { + var block, marker = markerCreate(); + + expect('{'); + + block = parseModuleElements(); + + expect('}'); + + return markerApply(marker, delegate.createBlockStatement(block)); + } + function parseProgram() { - var program; + var body, marker = markerCreate(); strict = false; - program = { - type: Syntax.Program, - body: parseSourceElements() - }; - return program; + peek(); + body = parseProgramElements(); + return markerApply(marker, delegate.createProgram(body)); } // The following functions are needed only when the option to preserve // the comments is active. function addComment(type, value, start, end, loc) { + var comment; + assert(typeof start === 'number', 'Comment must have valid position'); // Because the way the actual token is scanned, often the comments // (if any) are skipped twice during the lexical analysis. // Thus, we need to skip adding a comment if the comment array already // handled it. - if (extra.comments.length > 0) { - if (extra.comments[extra.comments.length - 1].range[1] > start) { - return; - } + if (state.lastCommentStart >= start) { + return; } + state.lastCommentStart = start; - extra.comments.push({ + comment = { type: type, - value: value, - range: [start, end], - loc: loc - }); + value: value + }; + if (extra.range) { + comment.range = [start, end]; + } + if (extra.loc) { + comment.loc = loc; + } + extra.comments.push(comment); } function scanComment() { @@ -3147,7 +4724,7 @@ parseStatement: true, parseSourceElement: true */ if (lineComment) { ch = source[index++]; - if (isLineTerminator(ch)) { + if (isLineTerminator(ch.charCodeAt(0))) { loc.end = { line: lineNumber, column: index - lineStart - 1 @@ -3172,7 +4749,7 @@ parseStatement: true, parseSourceElement: true */ comment += ch; } } else if (blockComment) { - if (isLineTerminator(ch)) { + if (isLineTerminator(ch.charCodeAt(0))) { if (ch === '\r' && source[index + 1] === '\n') { ++index; comment += '\r\n'; @@ -3242,9 +4819,9 @@ parseStatement: true, parseSourceElement: true */ } else { break; } - } else if (isWhiteSpace(ch)) { + } else if (isWhiteSpace(ch.charCodeAt(0))) { ++index; - } else if (isLineTerminator(ch)) { + } else if (isLineTerminator(ch.charCodeAt(0))) { ++index; if (ch === '\r' && source[index] === '\n') { ++index; @@ -3257,27 +4834,6 @@ parseStatement: true, parseSourceElement: true */ } } - function filterCommentLocation() { - var i, entry, comment, comments = []; - - for (i = 0; i < extra.comments.length; ++i) { - entry = extra.comments[i]; - comment = { - type: entry.type, - value: entry.value - }; - if (extra.range) { - comment.range = entry.range; - } - if (extra.loc) { - comment.loc = entry.loc; - } - comments.push(comment); - } - - extra.comments = comments; - } - function collectToken() { var start, loc, token, range, value; @@ -3298,7 +4854,7 @@ parseStatement: true, parseSourceElement: true */ if (token.type !== Token.EOF) { range = [token.range[0], token.range[1]]; - value = sliceSource(token.range[0], token.range[1]); + value = source.slice(token.range[0], token.range[1]); extra.tokens.push({ type: TokenName[token.type], value: value, @@ -3329,22 +4885,24 @@ parseStatement: true, parseSourceElement: true */ column: index - lineStart }; - // Pop the previous token, which is likely '/' or '/=' - if (extra.tokens.length > 0) { - token = extra.tokens[extra.tokens.length - 1]; - if (token.range[0] === pos && token.type === 'Punctuator') { - if (token.value === '/' || token.value === '/=') { - extra.tokens.pop(); + if (!extra.tokenize) { + // Pop the previous token, which is likely '/' or '/=' + if (extra.tokens.length > 0) { + token = extra.tokens[extra.tokens.length - 1]; + if (token.range[0] === pos && token.type === 'Punctuator') { + if (token.value === '/' || token.value === '/=') { + extra.tokens.pop(); + } } } - } - extra.tokens.push({ - type: 'RegularExpression', - value: regex.literal, - range: [pos, index], - loc: loc - }); + extra.tokens.push({ + type: 'RegularExpression', + value: regex.literal, + range: [pos, index], + loc: loc + }); + } return regex; } @@ -3370,359 +4928,12 @@ parseStatement: true, parseSourceElement: true */ extra.tokens = tokens; } - function createLiteral(token) { - return { - type: Syntax.Literal, - value: token.value - }; - } - - function createRawLiteral(token) { - return { - type: Syntax.Literal, - value: token.value, - raw: sliceSource(token.range[0], token.range[1]) - }; - } - - function createLocationMarker() { - var marker = {}; - - marker.range = [index, index]; - marker.loc = { - start: { - line: lineNumber, - column: index - lineStart - }, - end: { - line: lineNumber, - column: index - lineStart - } - }; - - marker.end = function () { - this.range[1] = index; - this.loc.end.line = lineNumber; - this.loc.end.column = index - lineStart; - }; - - marker.applyGroup = function (node) { - if (extra.range) { - node.groupRange = [this.range[0], this.range[1]]; - } - if (extra.loc) { - node.groupLoc = { - start: { - line: this.loc.start.line, - column: this.loc.start.column - }, - end: { - line: this.loc.end.line, - column: this.loc.end.column - } - }; - } - }; - - marker.apply = function (node) { - if (extra.range) { - node.range = [this.range[0], this.range[1]]; - } - if (extra.loc) { - node.loc = { - start: { - line: this.loc.start.line, - column: this.loc.start.column - }, - end: { - line: this.loc.end.line, - column: this.loc.end.column - } - }; - } - }; - - return marker; - } - - function trackGroupExpression() { - var marker, expr; - - skipComment(); - marker = createLocationMarker(); - expect('('); - - expr = parseExpression(); - - expect(')'); - - marker.end(); - marker.applyGroup(expr); - - return expr; - } - - function trackLeftHandSideExpression() { - var marker, expr; - - skipComment(); - marker = createLocationMarker(); - - expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); - - while (match('.') || match('[')) { - if (match('[')) { - expr = { - type: Syntax.MemberExpression, - computed: true, - object: expr, - property: parseComputedMember() - }; - marker.end(); - marker.apply(expr); - } else { - expr = { - type: Syntax.MemberExpression, - computed: false, - object: expr, - property: parseNonComputedMember() - }; - marker.end(); - marker.apply(expr); - } - } - - return expr; - } - - function trackLeftHandSideExpressionAllowCall() { - var marker, expr; - - skipComment(); - marker = createLocationMarker(); - - expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression(); - - while (match('.') || match('[') || match('(')) { - if (match('(')) { - expr = { - type: Syntax.CallExpression, - callee: expr, - 'arguments': parseArguments() - }; - marker.end(); - marker.apply(expr); - } else if (match('[')) { - expr = { - type: Syntax.MemberExpression, - computed: true, - object: expr, - property: parseComputedMember() - }; - marker.end(); - marker.apply(expr); - } else { - expr = { - type: Syntax.MemberExpression, - computed: false, - object: expr, - property: parseNonComputedMember() - }; - marker.end(); - marker.apply(expr); - } - } - - return expr; - } - - function filterGroup(node) { - var n, i, entry; - - n = (Object.prototype.toString.apply(node) === '[object Array]') ? [] : {}; - for (i in node) { - if (node.hasOwnProperty(i) && i !== 'groupRange' && i !== 'groupLoc') { - entry = node[i]; - if (entry === null || typeof entry !== 'object' || entry instanceof RegExp) { - n[i] = entry; - } else { - n[i] = filterGroup(entry); - } - } - } - return n; - } - - function wrapTrackingFunction(range, loc) { - - return function (parseFunction) { - - function isBinary(node) { - return node.type === Syntax.LogicalExpression || - node.type === Syntax.BinaryExpression; - } - - function visit(node) { - var start, end; - - if (isBinary(node.left)) { - visit(node.left); - } - if (isBinary(node.right)) { - visit(node.right); - } - - if (range) { - if (node.left.groupRange || node.right.groupRange) { - start = node.left.groupRange ? node.left.groupRange[0] : node.left.range[0]; - end = node.right.groupRange ? node.right.groupRange[1] : node.right.range[1]; - node.range = [start, end]; - } else if (typeof node.range === 'undefined') { - start = node.left.range[0]; - end = node.right.range[1]; - node.range = [start, end]; - } - } - if (loc) { - if (node.left.groupLoc || node.right.groupLoc) { - start = node.left.groupLoc ? node.left.groupLoc.start : node.left.loc.start; - end = node.right.groupLoc ? node.right.groupLoc.end : node.right.loc.end; - node.loc = { - start: start, - end: end - }; - } else if (typeof node.loc === 'undefined') { - node.loc = { - start: node.left.loc.start, - end: node.right.loc.end - }; - } - } - } - - return function () { - var marker, node; - - skipComment(); - - marker = createLocationMarker(); - node = parseFunction.apply(null, arguments); - marker.end(); - - if (range && typeof node.range === 'undefined') { - marker.apply(node); - } - - if (loc && typeof node.loc === 'undefined') { - marker.apply(node); - } - - if (isBinary(node)) { - visit(node); - } - - return node; - }; - }; - } - function patch() { - - var wrapTracking; - if (extra.comments) { extra.skipComment = skipComment; skipComment = scanComment; } - if (extra.raw) { - extra.createLiteral = createLiteral; - createLiteral = createRawLiteral; - } - - if (extra.range || extra.loc) { - - extra.parseGroupExpression = parseGroupExpression; - extra.parseLeftHandSideExpression = parseLeftHandSideExpression; - extra.parseLeftHandSideExpressionAllowCall = parseLeftHandSideExpressionAllowCall; - parseGroupExpression = trackGroupExpression; - parseLeftHandSideExpression = trackLeftHandSideExpression; - parseLeftHandSideExpressionAllowCall = trackLeftHandSideExpressionAllowCall; - - wrapTracking = wrapTrackingFunction(extra.range, extra.loc); - - extra.parseAdditiveExpression = parseAdditiveExpression; - extra.parseAssignmentExpression = parseAssignmentExpression; - extra.parseBitwiseANDExpression = parseBitwiseANDExpression; - extra.parseBitwiseORExpression = parseBitwiseORExpression; - extra.parseBitwiseXORExpression = parseBitwiseXORExpression; - extra.parseBlock = parseBlock; - extra.parseFunctionSourceElements = parseFunctionSourceElements; - extra.parseCatchClause = parseCatchClause; - extra.parseComputedMember = parseComputedMember; - extra.parseConditionalExpression = parseConditionalExpression; - extra.parseConstLetDeclaration = parseConstLetDeclaration; - extra.parseEqualityExpression = parseEqualityExpression; - extra.parseExpression = parseExpression; - extra.parseForVariableDeclaration = parseForVariableDeclaration; - extra.parseFunctionDeclaration = parseFunctionDeclaration; - extra.parseFunctionExpression = parseFunctionExpression; - extra.parseLogicalANDExpression = parseLogicalANDExpression; - extra.parseLogicalORExpression = parseLogicalORExpression; - extra.parseMultiplicativeExpression = parseMultiplicativeExpression; - extra.parseNewExpression = parseNewExpression; - extra.parseNonComputedProperty = parseNonComputedProperty; - extra.parseObjectProperty = parseObjectProperty; - extra.parseObjectPropertyKey = parseObjectPropertyKey; - extra.parsePostfixExpression = parsePostfixExpression; - extra.parsePrimaryExpression = parsePrimaryExpression; - extra.parseProgram = parseProgram; - extra.parsePropertyFunction = parsePropertyFunction; - extra.parseRelationalExpression = parseRelationalExpression; - extra.parseStatement = parseStatement; - extra.parseShiftExpression = parseShiftExpression; - extra.parseSwitchCase = parseSwitchCase; - extra.parseUnaryExpression = parseUnaryExpression; - extra.parseVariableDeclaration = parseVariableDeclaration; - extra.parseVariableIdentifier = parseVariableIdentifier; - - parseAdditiveExpression = wrapTracking(extra.parseAdditiveExpression); - parseAssignmentExpression = wrapTracking(extra.parseAssignmentExpression); - parseBitwiseANDExpression = wrapTracking(extra.parseBitwiseANDExpression); - parseBitwiseORExpression = wrapTracking(extra.parseBitwiseORExpression); - parseBitwiseXORExpression = wrapTracking(extra.parseBitwiseXORExpression); - parseBlock = wrapTracking(extra.parseBlock); - parseFunctionSourceElements = wrapTracking(extra.parseFunctionSourceElements); - parseCatchClause = wrapTracking(extra.parseCatchClause); - parseComputedMember = wrapTracking(extra.parseComputedMember); - parseConditionalExpression = wrapTracking(extra.parseConditionalExpression); - parseConstLetDeclaration = wrapTracking(extra.parseConstLetDeclaration); - parseEqualityExpression = wrapTracking(extra.parseEqualityExpression); - parseExpression = wrapTracking(extra.parseExpression); - parseForVariableDeclaration = wrapTracking(extra.parseForVariableDeclaration); - parseFunctionDeclaration = wrapTracking(extra.parseFunctionDeclaration); - parseFunctionExpression = wrapTracking(extra.parseFunctionExpression); - parseLeftHandSideExpression = wrapTracking(parseLeftHandSideExpression); - parseLogicalANDExpression = wrapTracking(extra.parseLogicalANDExpression); - parseLogicalORExpression = wrapTracking(extra.parseLogicalORExpression); - parseMultiplicativeExpression = wrapTracking(extra.parseMultiplicativeExpression); - parseNewExpression = wrapTracking(extra.parseNewExpression); - parseNonComputedProperty = wrapTracking(extra.parseNonComputedProperty); - parseObjectProperty = wrapTracking(extra.parseObjectProperty); - parseObjectPropertyKey = wrapTracking(extra.parseObjectPropertyKey); - parsePostfixExpression = wrapTracking(extra.parsePostfixExpression); - parsePrimaryExpression = wrapTracking(extra.parsePrimaryExpression); - parseProgram = wrapTracking(extra.parseProgram); - parsePropertyFunction = wrapTracking(extra.parsePropertyFunction); - parseRelationalExpression = wrapTracking(extra.parseRelationalExpression); - parseStatement = wrapTracking(extra.parseStatement); - parseShiftExpression = wrapTracking(extra.parseShiftExpression); - parseSwitchCase = wrapTracking(extra.parseSwitchCase); - parseUnaryExpression = wrapTracking(extra.parseUnaryExpression); - parseVariableDeclaration = wrapTracking(extra.parseVariableDeclaration); - parseVariableIdentifier = wrapTracking(extra.parseVariableIdentifier); - } - if (typeof extra.tokens !== 'undefined') { extra.advance = advance; extra.scanRegExp = scanRegExp; @@ -3737,66 +4948,135 @@ parseStatement: true, parseSourceElement: true */ skipComment = extra.skipComment; } - if (extra.raw) { - createLiteral = extra.createLiteral; - } - - if (extra.range || extra.loc) { - parseAdditiveExpression = extra.parseAdditiveExpression; - parseAssignmentExpression = extra.parseAssignmentExpression; - parseBitwiseANDExpression = extra.parseBitwiseANDExpression; - parseBitwiseORExpression = extra.parseBitwiseORExpression; - parseBitwiseXORExpression = extra.parseBitwiseXORExpression; - parseBlock = extra.parseBlock; - parseFunctionSourceElements = extra.parseFunctionSourceElements; - parseCatchClause = extra.parseCatchClause; - parseComputedMember = extra.parseComputedMember; - parseConditionalExpression = extra.parseConditionalExpression; - parseConstLetDeclaration = extra.parseConstLetDeclaration; - parseEqualityExpression = extra.parseEqualityExpression; - parseExpression = extra.parseExpression; - parseForVariableDeclaration = extra.parseForVariableDeclaration; - parseFunctionDeclaration = extra.parseFunctionDeclaration; - parseFunctionExpression = extra.parseFunctionExpression; - parseGroupExpression = extra.parseGroupExpression; - parseLeftHandSideExpression = extra.parseLeftHandSideExpression; - parseLeftHandSideExpressionAllowCall = extra.parseLeftHandSideExpressionAllowCall; - parseLogicalANDExpression = extra.parseLogicalANDExpression; - parseLogicalORExpression = extra.parseLogicalORExpression; - parseMultiplicativeExpression = extra.parseMultiplicativeExpression; - parseNewExpression = extra.parseNewExpression; - parseNonComputedProperty = extra.parseNonComputedProperty; - parseObjectProperty = extra.parseObjectProperty; - parseObjectPropertyKey = extra.parseObjectPropertyKey; - parsePrimaryExpression = extra.parsePrimaryExpression; - parsePostfixExpression = extra.parsePostfixExpression; - parseProgram = extra.parseProgram; - parsePropertyFunction = extra.parsePropertyFunction; - parseRelationalExpression = extra.parseRelationalExpression; - parseStatement = extra.parseStatement; - parseShiftExpression = extra.parseShiftExpression; - parseSwitchCase = extra.parseSwitchCase; - parseUnaryExpression = extra.parseUnaryExpression; - parseVariableDeclaration = extra.parseVariableDeclaration; - parseVariableIdentifier = extra.parseVariableIdentifier; - } - if (typeof extra.scanRegExp === 'function') { advance = extra.advance; scanRegExp = extra.scanRegExp; } } - function stringToArray(str) { - var length = str.length, - result = [], - i; - for (i = 0; i < length; ++i) { - result[i] = str.charAt(i); + // This is used to modify the delegate. + + function extend(object, properties) { + var entry, result = {}; + + for (entry in object) { + if (object.hasOwnProperty(entry)) { + result[entry] = object[entry]; + } } + + for (entry in properties) { + if (properties.hasOwnProperty(entry)) { + result[entry] = properties[entry]; + } + } + return result; } + function tokenize(code, options) { + var toString, + token, + tokens; + + toString = String; + if (typeof code !== 'string' && !(code instanceof String)) { + code = toString(code); + } + + delegate = SyntaxTreeDelegate; + source = code; + index = 0; + lineNumber = (source.length > 0) ? 1 : 0; + lineStart = 0; + length = source.length; + lookahead = null; + state = { + allowKeyword: true, + allowIn: true, + labelSet: {}, + inFunctionBody: false, + inIteration: false, + inSwitch: false, + lastCommentStart: -1 + }; + + extra = {}; + + // Options matching. + options = options || {}; + + // Of course we collect tokens here. + options.tokens = true; + extra.tokens = []; + extra.tokenize = true; + // The following two fields are necessary to compute the Regex tokens. + extra.openParenToken = -1; + extra.openCurlyToken = -1; + + extra.range = (typeof options.range === 'boolean') && options.range; + extra.loc = (typeof options.loc === 'boolean') && options.loc; + + if (typeof options.comment === 'boolean' && options.comment) { + extra.comments = []; + } + if (typeof options.tolerant === 'boolean' && options.tolerant) { + extra.errors = []; + } + + if (length > 0) { + if (typeof source[0] === 'undefined') { + // Try first to convert to a string. This is good as fast path + // for old IE which understands string indexing for string + // literals only and not for string object. + if (code instanceof String) { + source = code.valueOf(); + } + } + } + + patch(); + + try { + peek(); + if (lookahead.type === Token.EOF) { + return extra.tokens; + } + + token = lex(); + while (lookahead.type !== Token.EOF) { + try { + token = lex(); + } catch (lexError) { + token = lookahead; + if (extra.errors) { + extra.errors.push(lexError); + // We have to break on the first error + // to avoid infinite loops. + break; + } else { + throw lexError; + } + } + } + + filterTokenLocation(); + tokens = extra.tokens; + if (typeof extra.comments !== 'undefined') { + tokens.comments = extra.comments; + } + if (typeof extra.errors !== 'undefined') { + tokens.errors = extra.errors; + } + } catch (e) { + throw e; + } finally { + unpatch(); + extra = {}; + } + return tokens; + } + function parse(code, options) { var program, toString; @@ -3805,25 +5085,39 @@ parseStatement: true, parseSourceElement: true */ code = toString(code); } + delegate = SyntaxTreeDelegate; source = code; index = 0; lineNumber = (source.length > 0) ? 1 : 0; lineStart = 0; length = source.length; - buffer = null; + lookahead = null; state = { + allowKeyword: false, allowIn: true, labelSet: {}, + parenthesizedCount: 0, inFunctionBody: false, inIteration: false, - inSwitch: false + inSwitch: false, + lastCommentStart: -1, + yieldAllowed: false }; extra = {}; if (typeof options !== 'undefined') { extra.range = (typeof options.range === 'boolean') && options.range; extra.loc = (typeof options.loc === 'boolean') && options.loc; - extra.raw = (typeof options.raw === 'boolean') && options.raw; + + if (extra.loc && options.source !== null && options.source !== undefined) { + delegate = extend(delegate, { + 'postProcess': function (node) { + node.loc.source = toString(options.source); + return node; + } + }); + } + if (typeof options.tokens === 'boolean' && options.tokens) { extra.tokens = []; } @@ -3843,11 +5137,6 @@ parseStatement: true, parseSourceElement: true */ if (code instanceof String) { source = code.valueOf(); } - - // Force accessing the characters via an array. - if (typeof source[0] === 'undefined') { - source = stringToArray(code); - } } } @@ -3855,7 +5144,6 @@ parseStatement: true, parseSourceElement: true */ try { program = parseProgram(); if (typeof extra.comments !== 'undefined') { - filterCommentLocation(); program.comments = extra.comments; } if (typeof extra.tokens !== 'undefined') { @@ -3865,9 +5153,6 @@ parseStatement: true, parseSourceElement: true */ if (typeof extra.errors !== 'undefined') { program.errors = extra.errors; } - if (extra.range || extra.loc) { - program.body = filterGroup(program.body); - } } catch (e) { throw e; } finally { @@ -3878,8 +5163,10 @@ parseStatement: true, parseSourceElement: true */ return program; } - // Sync with package.json. - exports.version = '1.0.4'; + // Sync with *.json manifests. + exports.version = '1.1.0-dev-harmony'; + + exports.tokenize = tokenize; exports.parse = parse; diff --git a/node_modules/esprima/package.json b/node_modules/esprima/package.json index 15596ad1..ac19d86d 100644 --- a/node_modules/esprima/package.json +++ b/node_modules/esprima/package.json @@ -7,16 +7,7 @@ "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" }, - "files": [ - "bin", - "test/run.js", - "test/runner.js", - "test/test.js", - "test/compat.js", - "test/reflect.js", - "esprima.js" - ], - "version": "1.0.4", + "version": "1.1.0-dev-harmony", "engines": { "node": ">=0.4.0" }, @@ -37,23 +28,36 @@ "url": "http://github.com/ariya/esprima/raw/master/LICENSE.BSD" } ], - "keywords": [ - "ast", - "ecmascript", - "javascript", - "parser", - "syntax" - ], + "devDependencies": { + "jslint": "~0.1.9", + "eslint": "~0.1.0", + "istanbul": "~0.1.27", + "complexity-report": "~0.6.1", + "regenerate": "~0.5.4", + "unicode-6.3.0": "~0.1.0", + "json-diff": "~0.3.1" + }, "scripts": { - "test": "node test/run.js", + "test": "npm run-script lint && node test/run.js && npm run-script coverage && npm run-script complexity", + "lint": "node tools/check-version.js && node_modules/eslint/bin/eslint.js esprima.js && node_modules/jslint/bin/jslint.js esprima.js", + "coverage": "npm run-script analyze-coverage && npm run-script check-coverage", + "analyze-coverage": "node node_modules/istanbul/lib/cli.js cover test/runner.js", + "check-coverage": "node node_modules/istanbul/lib/cli.js check-coverage --statement -8 --branch -28 --function 99.69", + "complexity": "npm run-script analyze-complexity && npm run-script check-complexity", + "analyze-complexity": "node tools/list-complexity.js", + "check-complexity": "node node_modules/complexity-report/src/cli.js --maxcc 31 --silent -l -w esprima.js", "benchmark": "node test/benchmarks.js", "benchmark-quick": "node test/benchmarks.js quick" }, - "readme": "**Esprima** ([esprima.org](http://esprima.org)) is a high performance,\nstandard-compliant [ECMAScript](http://www.ecma-international.org/publications/standards/Ecma-262.htm)\nparser written in ECMAScript (also popularly known as\n[JavaScript](http://en.wikipedia.org/wiki/JavaScript>JavaScript)).\nEsprima is created and maintained by [Ariya Hidayat](http://twitter.com/ariyahidayat),\nwith the help of [many contributors](https://github.com/ariya/esprima/contributors).\n\nEsprima runs on web browsers (IE 6+, Firefox 1+, Safari 3+, Chrome 1+, Konqueror 4.6+, Opera 8+) as well as\n[Node.js](http://nodejs.org).\n\n### Features\n\n- Full support for [ECMAScript 5.1](http://www.ecma-international.org/publications/standards/Ecma-262.htm)(ECMA-262)\n- Sensible [syntax tree format](http://esprima.org/doc/index.html#ast) compatible with Mozilla\n[Parser AST](https://developer.mozilla.org/en/SpiderMonkey/Parser_API)\n- Heavily tested (> 550 [unit tests](http://esprima.org/test/) with solid 100% statement coverage)\n- Optional tracking of syntax node location (index-based and line-column)\n- Experimental support for ES6/Harmony (module, class, destructuring, ...)\n\nEsprima is blazing fast (see the [benchmark suite](http://esprima.org/test/benchmarks.html)).\nIt is up to 3x faster than UglifyJS v1 and it is still [competitive](http://esprima.org/test/compare.html)\nwith the new generation of fast parsers.\n\n### Applications\n\nEsprima serves as the basis for many popular JavaScript development tools:\n\n- Code coverage analysis: [node-cover](https://github.com/itay/node-cover), [Istanbul](https://github.com/yahoo/Istanbul)\n- Documentation tool: [JFDoc](https://github.com/thejohnfreeman/jfdoc), [JSDuck](https://github.com/senchalabs/jsduck)\n- Language extension: [LLJS](http://mbebenita.github.com/LLJS/) (low-level JS),\n[Sweet.js](http://sweetjs.org/) (macro)\n- ES6/Harmony transpiler: [Six](https://github.com/matthewrobb/six), [Harmonizr](https://github.com/jdiamond/harmonizr)\n- Eclipse Orion smart editing ([outline view](https://github.com/aclement/esprima-outline), [content assist](http://contraptionsforprogramming.blogspot.com/2012/02/better-javascript-content-assist-in.html))\n- Source code modification: [Esmorph](https://github.com/ariya/esmorph), [Code Painter](https://github.com/fawek/codepainter),\n- Source transformation: [node-falafel](https://github.com/substack/node-falafel), [Esmangle](https://github.com/Constellation/esmangle), [escodegen](https://github.com/Constellation/escodegen)\n\n### Questions?\n- [Documentation](http://esprima.org/doc)\n- [Issue tracker](http://issues.esprima.org): [known problems](http://code.google.com/p/esprima/issues/list?q=Defect)\nand [future plans](http://code.google.com/p/esprima/issues/list?q=Enhancement)\n- [Mailing list](http://groups.google.com/group/esprima)\n- [Contribution guide](http://esprima.org/doc/index.html#contribution)\n\nFollow [@Esprima](http://twitter.com/Esprima) on Twitter to get the\ndevelopment updates.\nFeedback and contribution are welcomed!\n\n### License\n\nCopyright (C) 2012, 2011 [Ariya Hidayat](http://ariya.ofilabs.com/about)\n and other contributors.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n * Redistributions of source code must retain the above copyright\n notice, this list of conditions and the following disclaimer.\n\n * Redistributions in binary form must reproduce the above copyright\n notice, this list of conditions and the following disclaimer in the\n documentation and/or other materials provided with the distribution.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\nARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY\nDIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\nLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\nON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\nTHIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n\n", + "readme": "**Esprima** ([esprima.org](http://esprima.org), BSD license) is a high performance,\nstandard-compliant [ECMAScript](http://www.ecma-international.org/publications/standards/Ecma-262.htm)\nparser written in ECMAScript (also popularly known as\n[JavaScript](http://en.wikipedia.org/wiki/JavaScript>JavaScript)).\nEsprima is created and maintained by [Ariya Hidayat](http://twitter.com/ariyahidayat),\nwith the help of [many contributors](https://github.com/ariya/esprima/contributors).\n\n### Features\n\n- Full support for ECMAScript 5.1 ([ECMA-262](http://www.ecma-international.org/publications/standards/Ecma-262.htm))\n- Sensible [syntax tree format](http://esprima.org/doc/index.html#ast) compatible with Mozilla\n[Parser AST](https://developer.mozilla.org/en/SpiderMonkey/Parser_API)\n- Optional tracking of syntax node location (index-based and line-column)\n- Heavily tested (> 600 [unit tests](http://esprima.org/test/) with solid statement and branch coverage)\n- Experimental support for ES6/Harmony (module, class, destructuring, ...)\n\nEsprima serves as a **building block** for some JavaScript\nlanguage tools, from [code instrumentation](http://esprima.org/demo/functiontrace.html)\nto [editor autocompletion](http://esprima.org/demo/autocomplete.html).\n\nEsprima runs on many popular web browsers, as well as other ECMAScript platforms such as\n[Rhino](http://www.mozilla.org/rhino) and [Node.js](https://npmjs.org/package/esprima).\n\nFor more information, check the web site [esprima.org](http://esprima.org).\n", "readmeFilename": "README.md", "bugs": { "url": "https://github.com/ariya/esprima/issues" }, - "_id": "esprima@1.0.4", - "_from": "esprima@1.0.4" + "_id": "esprima@1.1.0-dev-harmony", + "dist": { + "shasum": "55953eb45725df0cc096c324f22f262a79c27e1f" + }, + "_from": "https://github.com/ariya/esprima/tarball/261b80e97c11cbc1097f425d1ea75e5a2e143491", + "_resolved": "https://github.com/ariya/esprima/tarball/261b80e97c11cbc1097f425d1ea75e5a2e143491" } diff --git a/package.json b/package.json index aa8be206..f93410f5 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "dependencies": { "async": "~0.1.22", "catharsis": "~0.7.1", - "esprima": "~1.0.4", + "esprima": "https://github.com/ariya/esprima/tarball/261b80e97c11cbc1097f425d1ea75e5a2e143491", "js2xmlparser": "~0.1.0", "marked": "~0.3.1", "taffydb": "https://github.com/hegemonic/taffydb/tarball/master",