Index: sky/framework/sky-element/third_party/esprima/esprima.sky |
diff --git a/sky/framework/sky-element/third_party/esprima/esprima.sky b/sky/framework/sky-element/third_party/esprima/esprima.sky |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0ac5dedc9a98f23c157bbfa67bd04d2489081aba |
--- /dev/null |
+++ b/sky/framework/sky-element/third_party/esprima/esprima.sky |
@@ -0,0 +1,1042 @@ |
+<script> |
+ |
+/* |
+ Copyright (C) 2013 Ariya Hidayat <ariya.hidayat@gmail.com> |
+ Copyright (C) 2013 Thaddee Tyl <thaddee.tyl@gmail.com> |
+ Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com> |
+ Copyright (C) 2012 Mathias Bynens <mathias@qiwi.be> |
+ Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl> |
+ Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com> |
+ Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com> |
+ Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com> |
+ Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com> |
+ |
+ Redistribution and use in source and binary forms, with or without |
+ modification, are permitted provided that the following conditions are met: |
+ |
+ * Redistributions of source code must retain the above copyright |
+ notice, this list of conditions and the following disclaimer. |
+ * Redistributions in binary form must reproduce the above copyright |
+ notice, this list of conditions and the following disclaimer in the |
+ documentation and/or other materials provided with the distribution. |
+ |
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
+ ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY |
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+*/ |
+ |
+var Token, |
+ TokenName, |
+ Syntax, |
+ Messages, |
+ source, |
+ index, |
+ length, |
+ delegate, |
+ lookahead, |
+ state; |
+ |
+Token = { |
+ BooleanLiteral: 1, |
+ EOF: 2, |
+ Identifier: 3, |
+ Keyword: 4, |
+ NullLiteral: 5, |
+ NumericLiteral: 6, |
+ Punctuator: 7, |
+ StringLiteral: 8 |
+}; |
+ |
+TokenName = {}; |
+TokenName[Token.BooleanLiteral] = 'Boolean'; |
+TokenName[Token.EOF] = '<end>'; |
+TokenName[Token.Identifier] = 'Identifier'; |
+TokenName[Token.Keyword] = 'Keyword'; |
+TokenName[Token.NullLiteral] = 'Null'; |
+TokenName[Token.NumericLiteral] = 'Numeric'; |
+TokenName[Token.Punctuator] = 'Punctuator'; |
+TokenName[Token.StringLiteral] = 'String'; |
+ |
+Syntax = { |
+ ArrayExpression: 'ArrayExpression', |
+ BinaryExpression: 'BinaryExpression', |
+ CallExpression: 'CallExpression', |
+ ConditionalExpression: 'ConditionalExpression', |
+ EmptyStatement: 'EmptyStatement', |
+ ExpressionStatement: 'ExpressionStatement', |
+ Identifier: 'Identifier', |
+ Literal: 'Literal', |
+ LabeledStatement: 'LabeledStatement', |
+ LogicalExpression: 'LogicalExpression', |
+ MemberExpression: 'MemberExpression', |
+ ObjectExpression: 'ObjectExpression', |
+ Program: 'Program', |
+ Property: 'Property', |
+ ThisExpression: 'ThisExpression', |
+ UnaryExpression: 'UnaryExpression' |
+}; |
+ |
+// Error messages should be identical to V8. |
+Messages = { |
+ UnexpectedToken: 'Unexpected token %0', |
+ UnknownLabel: 'Undefined label \'%0\'', |
+ Redeclaration: '%0 \'%1\' has already been declared' |
+}; |
+ |
+// Ensure the condition is true, otherwise throw an error. |
+// This is only to have a better contract semantic, i.e. another safety net |
+// to catch a logic error. The condition shall be fulfilled in normal case. |
+// Do NOT use this to enforce a certain condition on any user input. |
+ |
+function assert(condition, message) { |
+ if (!condition) { |
+ throw new Error('ASSERT: ' + message); |
+ } |
+} |
+ |
+function isDecimalDigit(ch) { |
+ return (ch >= 48 && ch <= 57); // 0..9 |
+} |
+ |
+ |
+// 7.2 White Space |
+ |
+function isWhiteSpace(ch) { |
+ 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 === 10) || (ch === 13) || (ch === 0x2028) || (ch === 0x2029); |
+} |
+ |
+// 7.6 Identifier Names and Identifiers |
+ |
+function isIdentifierStart(ch) { |
+ return (ch === 36) || (ch === 95) || // $ (dollar) and _ (underscore) |
+ (ch >= 65 && ch <= 90) || // A..Z |
+ (ch >= 97 && ch <= 122); // a..z |
+} |
+ |
+function isIdentifierPart(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 |
+} |
+ |
+// 7.6.1.1 Keywords |
+ |
+function isKeyword(id) { |
+ return (id === 'this') |
+} |
+ |
+// 7.4 Comments |
+ |
+function skipWhitespace() { |
+ while (index < length && isWhiteSpace(source.charCodeAt(index))) { |
+ ++index; |
+ } |
+} |
+ |
+function getIdentifier() { |
+ var start, ch; |
+ |
+ start = index++; |
+ while (index < length) { |
+ ch = source.charCodeAt(index); |
+ if (isIdentifierPart(ch)) { |
+ ++index; |
+ } else { |
+ break; |
+ } |
+ } |
+ |
+ return source.slice(start, index); |
+} |
+ |
+function scanIdentifier() { |
+ var start, id, type; |
+ |
+ start = index; |
+ |
+ id = getIdentifier(); |
+ |
+ // There is no keyword or literal with only one character. |
+ // Thus, it must be an identifier. |
+ if (id.length === 1) { |
+ 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: type, |
+ value: id, |
+ range: [start, index] |
+ }; |
+} |
+ |
+ |
+// 7.7 Punctuators |
+ |
+function scanPunctuator() { |
+ var start = index, |
+ code = source.charCodeAt(index), |
+ code2, |
+ ch1 = source[index], |
+ ch2; |
+ |
+ switch (code) { |
+ |
+ // Check for most common single-character punctuators. |
+ case 46: // . dot |
+ 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: // ? |
+ ++index; |
+ return { |
+ type: Token.Punctuator, |
+ value: String.fromCharCode(code), |
+ range: [start, index] |
+ }; |
+ |
+ default: |
+ code2 = source.charCodeAt(index + 1); |
+ |
+ // '=' (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 124: // | |
+ index += 2; |
+ return { |
+ type: Token.Punctuator, |
+ value: String.fromCharCode(code) + String.fromCharCode(code2), |
+ 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), |
+ range: [start, index] |
+ }; |
+ default: |
+ break; |
+ } |
+ } |
+ break; |
+ } |
+ |
+ // Peek more characters. |
+ |
+ ch2 = source[index + 1]; |
+ |
+ // Other 2-character punctuators: && || |
+ |
+ if (ch1 === ch2 && ('&|'.indexOf(ch1) >= 0)) { |
+ index += 2; |
+ return { |
+ type: Token.Punctuator, |
+ value: ch1 + ch2, |
+ range: [start, index] |
+ }; |
+ } |
+ |
+ if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) { |
+ ++index; |
+ return { |
+ type: Token.Punctuator, |
+ value: ch1, |
+ range: [start, index] |
+ }; |
+ } |
+ |
+ throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); |
+} |
+ |
+// 7.8.3 Numeric Literals |
+function scanNumericLiteral() { |
+ var number, start, ch; |
+ |
+ ch = source[index]; |
+ assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'), |
+ 'Numeric literal must start with a decimal digit or a decimal point'); |
+ |
+ start = index; |
+ number = ''; |
+ if (ch !== '.') { |
+ number = source[index++]; |
+ ch = source[index]; |
+ |
+ // Hex number starts with '0x'. |
+ // Octal number starts with '0'. |
+ if (number === '0') { |
+ // decimal number starts with '0' such as '09' is illegal. |
+ if (ch && isDecimalDigit(ch.charCodeAt(0))) { |
+ throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); |
+ } |
+ } |
+ |
+ while (isDecimalDigit(source.charCodeAt(index))) { |
+ number += source[index++]; |
+ } |
+ ch = source[index]; |
+ } |
+ |
+ if (ch === '.') { |
+ number += source[index++]; |
+ while (isDecimalDigit(source.charCodeAt(index))) { |
+ number += source[index++]; |
+ } |
+ ch = source[index]; |
+ } |
+ |
+ if (ch === 'e' || ch === 'E') { |
+ number += source[index++]; |
+ |
+ ch = source[index]; |
+ if (ch === '+' || ch === '-') { |
+ number += source[index++]; |
+ } |
+ if (isDecimalDigit(source.charCodeAt(index))) { |
+ while (isDecimalDigit(source.charCodeAt(index))) { |
+ number += source[index++]; |
+ } |
+ } else { |
+ throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); |
+ } |
+ } |
+ |
+ if (isIdentifierStart(source.charCodeAt(index))) { |
+ throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); |
+ } |
+ |
+ return { |
+ type: Token.NumericLiteral, |
+ value: parseFloat(number), |
+ range: [start, index] |
+ }; |
+} |
+ |
+// 7.8.4 String Literals |
+ |
+function scanStringLiteral() { |
+ var str = '', quote, start, ch, octal = false; |
+ |
+ quote = source[index]; |
+ assert((quote === '\'' || quote === '"'), |
+ 'String literal must starts with a quote'); |
+ |
+ start = index; |
+ ++index; |
+ |
+ while (index < length) { |
+ ch = source[index++]; |
+ |
+ if (ch === quote) { |
+ quote = ''; |
+ break; |
+ } else if (ch === '\\') { |
+ ch = source[index++]; |
+ if (!ch || !isLineTerminator(ch.charCodeAt(0))) { |
+ switch (ch) { |
+ case 'n': |
+ str += '\n'; |
+ break; |
+ case 'r': |
+ str += '\r'; |
+ break; |
+ case 't': |
+ str += '\t'; |
+ break; |
+ case 'b': |
+ str += '\b'; |
+ break; |
+ case 'f': |
+ str += '\f'; |
+ break; |
+ case 'v': |
+ str += '\x0B'; |
+ break; |
+ |
+ default: |
+ str += ch; |
+ break; |
+ } |
+ } else { |
+ if (ch === '\r' && source[index] === '\n') { |
+ ++index; |
+ } |
+ } |
+ } else if (isLineTerminator(ch.charCodeAt(0))) { |
+ break; |
+ } else { |
+ str += ch; |
+ } |
+ } |
+ |
+ if (quote !== '') { |
+ throwError({}, Messages.UnexpectedToken, 'ILLEGAL'); |
+ } |
+ |
+ return { |
+ type: Token.StringLiteral, |
+ value: str, |
+ octal: octal, |
+ range: [start, index] |
+ }; |
+} |
+ |
+function isIdentifierName(token) { |
+ return token.type === Token.Identifier || |
+ token.type === Token.Keyword || |
+ token.type === Token.BooleanLiteral || |
+ token.type === Token.NullLiteral; |
+} |
+ |
+function advance() { |
+ var ch; |
+ |
+ skipWhitespace(); |
+ |
+ if (index >= length) { |
+ return { |
+ type: Token.EOF, |
+ range: [index, index] |
+ }; |
+ } |
+ |
+ ch = source.charCodeAt(index); |
+ |
+ // Very common: ( and ) and ; |
+ if (ch === 40 || ch === 41 || ch === 58) { |
+ return scanPunctuator(); |
+ } |
+ |
+ // String literal starts with single quote (#39) or double quote (#34). |
+ if (ch === 39 || ch === 34) { |
+ return scanStringLiteral(); |
+ } |
+ |
+ 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(); |
+ } |
+ |
+ return scanPunctuator(); |
+} |
+ |
+function lex() { |
+ var token; |
+ |
+ token = lookahead; |
+ index = token.range[1]; |
+ |
+ lookahead = advance(); |
+ |
+ index = token.range[1]; |
+ |
+ return token; |
+} |
+ |
+function peek() { |
+ var pos; |
+ |
+ pos = index; |
+ lookahead = advance(); |
+ index = pos; |
+} |
+ |
+// Throw an exception |
+ |
+function throwError(token, messageFormat) { |
+ var error, |
+ args = Array.prototype.slice.call(arguments, 2), |
+ msg = messageFormat.replace( |
+ /%(\d)/g, |
+ function (whole, index) { |
+ assert(index < args.length, 'Message reference must be in range'); |
+ return args[index]; |
+ } |
+ ); |
+ |
+ error = new Error(msg); |
+ error.index = index; |
+ error.description = msg; |
+ throw error; |
+} |
+ |
+// Throw an exception because of the token. |
+ |
+function throwUnexpected(token) { |
+ throwError(token, Messages.UnexpectedToken, token.value); |
+} |
+ |
+// Expect the next token to match the specified punctuator. |
+// If not, an exception will be thrown. |
+ |
+function expect(value) { |
+ var token = lex(); |
+ if (token.type !== Token.Punctuator || token.value !== value) { |
+ throwUnexpected(token); |
+ } |
+} |
+ |
+// Return true if the next token matches the specified punctuator. |
+ |
+function match(value) { |
+ return lookahead.type === Token.Punctuator && lookahead.value === value; |
+} |
+ |
+// Return true if the next token matches the specified keyword |
+ |
+function matchKeyword(keyword) { |
+ return lookahead.type === Token.Keyword && lookahead.value === keyword; |
+} |
+ |
+function consumeSemicolon() { |
+ // Catch the very common case first: immediately a semicolon (char #59). |
+ if (source.charCodeAt(index) === 59) { |
+ lex(); |
+ return; |
+ } |
+ |
+ skipWhitespace(); |
+ |
+ if (match(';')) { |
+ lex(); |
+ return; |
+ } |
+ |
+ if (lookahead.type !== Token.EOF && !match('}')) { |
+ throwUnexpected(lookahead); |
+ } |
+} |
+ |
+// 11.1.4 Array Initialiser |
+ |
+function parseArrayInitialiser() { |
+ var elements = []; |
+ |
+ expect('['); |
+ |
+ while (!match(']')) { |
+ if (match(',')) { |
+ lex(); |
+ elements.push(null); |
+ } else { |
+ elements.push(parseExpression()); |
+ |
+ if (!match(']')) { |
+ expect(','); |
+ } |
+ } |
+ } |
+ |
+ expect(']'); |
+ |
+ return delegate.createArrayExpression(elements); |
+} |
+ |
+// 11.1.5 Object Initialiser |
+ |
+function parseObjectPropertyKey() { |
+ var token; |
+ |
+ skipWhitespace(); |
+ token = lex(); |
+ |
+ // Note: This function is called only from parseObjectProperty(), where |
+ // EOF and Punctuator tokens are already filtered out. |
+ if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) { |
+ return delegate.createLiteral(token); |
+ } |
+ |
+ return delegate.createIdentifier(token.value); |
+} |
+ |
+function parseObjectProperty() { |
+ var token, key; |
+ |
+ token = lookahead; |
+ skipWhitespace(); |
+ |
+ if (token.type === Token.EOF || token.type === Token.Punctuator) { |
+ throwUnexpected(token); |
+ } |
+ |
+ key = parseObjectPropertyKey(); |
+ expect(':'); |
+ return delegate.createProperty('init', key, parseExpression()); |
+} |
+ |
+function parseObjectInitialiser() { |
+ var properties = []; |
+ |
+ expect('{'); |
+ |
+ while (!match('}')) { |
+ properties.push(parseObjectProperty()); |
+ |
+ if (!match('}')) { |
+ expect(','); |
+ } |
+ } |
+ |
+ expect('}'); |
+ |
+ return delegate.createObjectExpression(properties); |
+} |
+ |
+// 11.1.6 The Grouping Operator |
+ |
+function parseGroupExpression() { |
+ var expr; |
+ |
+ expect('('); |
+ |
+ expr = parseExpression(); |
+ |
+ expect(')'); |
+ |
+ return expr; |
+} |
+ |
+ |
+// 11.1 Primary Expressions |
+ |
+function parsePrimaryExpression() { |
+ var type, token, expr; |
+ |
+ if (match('(')) { |
+ return parseGroupExpression(); |
+ } |
+ |
+ type = lookahead.type; |
+ |
+ if (type === Token.Identifier) { |
+ expr = delegate.createIdentifier(lex().value); |
+ } else if (type === Token.StringLiteral || type === Token.NumericLiteral) { |
+ expr = delegate.createLiteral(lex()); |
+ } else if (type === Token.Keyword) { |
+ if (matchKeyword('this')) { |
+ lex(); |
+ expr = delegate.createThisExpression(); |
+ } |
+ } else if (type === Token.BooleanLiteral) { |
+ token = lex(); |
+ token.value = (token.value === 'true'); |
+ expr = delegate.createLiteral(token); |
+ } else if (type === Token.NullLiteral) { |
+ token = lex(); |
+ token.value = null; |
+ expr = delegate.createLiteral(token); |
+ } else if (match('[')) { |
+ expr = parseArrayInitialiser(); |
+ } else if (match('{')) { |
+ expr = parseObjectInitialiser(); |
+ } |
+ |
+ if (expr) { |
+ return expr; |
+ } |
+ |
+ throwUnexpected(lex()); |
+} |
+ |
+// 11.2 Left-Hand-Side Expressions |
+ |
+function parseArguments() { |
+ var args = []; |
+ |
+ expect('('); |
+ |
+ if (!match(')')) { |
+ while (index < length) { |
+ args.push(parseExpression()); |
+ if (match(')')) { |
+ break; |
+ } |
+ expect(','); |
+ } |
+ } |
+ |
+ expect(')'); |
+ |
+ return args; |
+} |
+ |
+function parseNonComputedProperty() { |
+ var token; |
+ |
+ token = lex(); |
+ |
+ if (!isIdentifierName(token)) { |
+ throwUnexpected(token); |
+ } |
+ |
+ return delegate.createIdentifier(token.value); |
+} |
+ |
+function parseNonComputedMember() { |
+ expect('.'); |
+ |
+ return parseNonComputedProperty(); |
+} |
+ |
+function parseComputedMember() { |
+ var expr; |
+ |
+ expect('['); |
+ |
+ expr = parseExpression(); |
+ |
+ expect(']'); |
+ |
+ return expr; |
+} |
+ |
+function parseLeftHandSideExpression() { |
+ var expr, args, property; |
+ |
+ expr = parsePrimaryExpression(); |
+ |
+ while (true) { |
+ if (match('[')) { |
+ property = parseComputedMember(); |
+ expr = delegate.createMemberExpression('[', expr, property); |
+ } else if (match('.')) { |
+ property = parseNonComputedMember(); |
+ expr = delegate.createMemberExpression('.', expr, property); |
+ } else if (match('(')) { |
+ args = parseArguments(); |
+ expr = delegate.createCallExpression(expr, args); |
+ } else { |
+ break; |
+ } |
+ } |
+ |
+ return expr; |
+} |
+ |
+// 11.3 Postfix Expressions |
+ |
+var parsePostfixExpression = parseLeftHandSideExpression; |
+ |
+// 11.4 Unary Operators |
+ |
+function parseUnaryExpression() { |
+ var token, expr; |
+ |
+ if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) { |
+ expr = parsePostfixExpression(); |
+ } else if (match('+') || match('-') || match('!')) { |
+ token = lex(); |
+ expr = parseUnaryExpression(); |
+ expr = delegate.createUnaryExpression(token.value, expr); |
+ } else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) { |
+ throwError({}, Messages.UnexpectedToken); |
+ } else { |
+ expr = parsePostfixExpression(); |
+ } |
+ |
+ return expr; |
+} |
+ |
+function binaryPrecedence(token) { |
+ 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 '==': |
+ case '!=': |
+ case '===': |
+ case '!==': |
+ prec = 6; |
+ break; |
+ |
+ case '<': |
+ case '>': |
+ case '<=': |
+ case '>=': |
+ case 'instanceof': |
+ prec = 7; |
+ break; |
+ |
+ case 'in': |
+ prec = 7; |
+ break; |
+ |
+ case '+': |
+ case '-': |
+ prec = 9; |
+ break; |
+ |
+ case '*': |
+ case '/': |
+ case '%': |
+ prec = 11; |
+ break; |
+ |
+ default: |
+ break; |
+ } |
+ |
+ return prec; |
+} |
+ |
+// 11.5 Multiplicative Operators |
+// 11.6 Additive Operators |
+// 11.7 Bitwise Shift Operators |
+// 11.8 Relational Operators |
+// 11.9 Equality Operators |
+// 11.10 Binary Bitwise Operators |
+// 11.11 Binary Logical Operators |
+ |
+function parseBinaryExpression() { |
+ var expr, token, prec, stack, right, operator, left, i; |
+ |
+ left = parseUnaryExpression(); |
+ |
+ token = lookahead; |
+ prec = binaryPrecedence(token); |
+ if (prec === 0) { |
+ return left; |
+ } |
+ token.prec = prec; |
+ lex(); |
+ |
+ right = parseUnaryExpression(); |
+ |
+ stack = [left, token, right]; |
+ |
+ while ((prec = binaryPrecedence(lookahead)) > 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); |
+ stack.push(expr); |
+ } |
+ |
+ // Shift. |
+ token = lex(); |
+ token.prec = prec; |
+ stack.push(token); |
+ expr = parseUnaryExpression(); |
+ stack.push(expr); |
+ } |
+ |
+ // Final reduce to clean-up the stack. |
+ i = stack.length - 1; |
+ expr = stack[i]; |
+ while (i > 1) { |
+ expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr); |
+ i -= 2; |
+ } |
+ |
+ return expr; |
+} |
+ |
+ |
+// 11.12 Conditional Operator |
+ |
+function parseConditionalExpression() { |
+ var expr, consequent, alternate; |
+ |
+ expr = parseBinaryExpression(); |
+ |
+ if (match('?')) { |
+ lex(); |
+ consequent = parseConditionalExpression(); |
+ expect(':'); |
+ alternate = parseConditionalExpression(); |
+ |
+ expr = delegate.createConditionalExpression(expr, consequent, alternate); |
+ } |
+ |
+ return expr; |
+} |
+ |
+// Simplification since we do not support AssignmentExpression. |
+var parseExpression = parseConditionalExpression; |
+ |
+// Polymer Syntax extensions |
+ |
+// Filter :: |
+// Identifier |
+// Identifier "(" ")" |
+// Identifier "(" FilterArguments ")" |
+ |
+function parseFilter() { |
+ var identifier, args; |
+ |
+ identifier = lex(); |
+ |
+ if (identifier.type !== Token.Identifier) { |
+ throwUnexpected(identifier); |
+ } |
+ |
+ args = match('(') ? parseArguments() : []; |
+ |
+ return delegate.createFilter(identifier.value, args); |
+} |
+ |
+// Filters :: |
+// "|" Filter |
+// Filters "|" Filter |
+ |
+function parseFilters() { |
+ while (match('|')) { |
+ lex(); |
+ parseFilter(); |
+ } |
+} |
+ |
+// TopLevel :: |
+// LabelledExpressions |
+// AsExpression |
+// InExpression |
+// FilterExpression |
+ |
+// AsExpression :: |
+// FilterExpression as Identifier |
+ |
+// InExpression :: |
+// Identifier, Identifier in FilterExpression |
+// Identifier in FilterExpression |
+ |
+// FilterExpression :: |
+// Expression |
+// Expression Filters |
+ |
+function parseTopLevel() { |
+ skipWhitespace(); |
+ peek(); |
+ |
+ var expr = parseExpression(); |
+ if (expr) { |
+ if (lookahead.value === ',' || lookahead.value == 'in' && |
+ expr.type === Syntax.Identifier) { |
+ parseInExpression(expr); |
+ } else { |
+ parseFilters(); |
+ if (lookahead.value === 'as') { |
+ parseAsExpression(expr); |
+ } else { |
+ delegate.createTopLevel(expr); |
+ } |
+ } |
+ } |
+ |
+ if (lookahead.type !== Token.EOF) { |
+ throwUnexpected(lookahead); |
+ } |
+} |
+ |
+function parseAsExpression(expr) { |
+ lex(); // as |
+ var identifier = lex().value; |
+ delegate.createAsExpression(expr, identifier); |
+} |
+ |
+function parseInExpression(identifier) { |
+ var indexName; |
+ if (lookahead.value === ',') { |
+ lex(); |
+ if (lookahead.type !== Token.Identifier) |
+ throwUnexpected(lookahead); |
+ indexName = lex().value; |
+ } |
+ |
+ lex(); // in |
+ var expr = parseExpression(); |
+ parseFilters(); |
+ delegate.createInExpression(identifier.name, indexName, expr); |
+} |
+ |
+function parse(code, inDelegate) { |
+ delegate = inDelegate; |
+ source = code; |
+ index = 0; |
+ length = source.length; |
+ lookahead = null; |
+ state = { |
+ labelSet: {} |
+ }; |
+ |
+ return parseTopLevel(); |
+} |
+ |
+module.exports = { |
+ parse: parse |
+}; |
+ |
+</script> |