StackGenVis: Alignment of Data, Algorithms, and Models for Stacking Ensemble Learning Using Performance Metrics
https://doi.org/10.1109/TVCG.2020.3030352
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1838 lines
57 KiB
1838 lines
57 KiB
(function (global, factory) {
|
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('vega-util')) :
|
|
typeof define === 'function' && define.amd ? define(['exports', 'vega-util'], factory) :
|
|
(global = global || self, factory(global.vega = {}, global.vega));
|
|
}(this, (function (exports, vegaUtil) { 'use strict';
|
|
|
|
var RawCode = 'RawCode';
|
|
var Literal = 'Literal';
|
|
var Property = 'Property';
|
|
var Identifier = 'Identifier';
|
|
|
|
var ArrayExpression = 'ArrayExpression';
|
|
var BinaryExpression = 'BinaryExpression';
|
|
var CallExpression = 'CallExpression';
|
|
var ConditionalExpression = 'ConditionalExpression';
|
|
var LogicalExpression = 'LogicalExpression';
|
|
var MemberExpression = 'MemberExpression';
|
|
var ObjectExpression = 'ObjectExpression';
|
|
var UnaryExpression = 'UnaryExpression';
|
|
|
|
function ASTNode(type) {
|
|
this.type = type;
|
|
}
|
|
|
|
ASTNode.prototype.visit = function(visitor) {
|
|
var node = this, c, i, n;
|
|
|
|
if (visitor(node)) return 1;
|
|
|
|
for (c=children(node), i=0, n=c.length; i<n; ++i) {
|
|
if (c[i].visit(visitor)) return 1;
|
|
}
|
|
};
|
|
|
|
function children(node) {
|
|
switch (node.type) {
|
|
case ArrayExpression:
|
|
return node.elements;
|
|
case BinaryExpression:
|
|
case LogicalExpression:
|
|
return [node.left, node.right];
|
|
case CallExpression:
|
|
var args = node.arguments.slice();
|
|
args.unshift(node.callee);
|
|
return args;
|
|
case ConditionalExpression:
|
|
return [node.test, node.consequent, node.alternate];
|
|
case MemberExpression:
|
|
return [node.object, node.property];
|
|
case ObjectExpression:
|
|
return node.properties;
|
|
case Property:
|
|
return [node.key, node.value];
|
|
case UnaryExpression:
|
|
return [node.argument];
|
|
case Identifier:
|
|
case Literal:
|
|
case RawCode:
|
|
default:
|
|
return [];
|
|
}
|
|
}
|
|
|
|
/*
|
|
The following expression parser is based on Esprima (http://esprima.org/).
|
|
Original header comment and license for Esprima is included here:
|
|
|
|
Copyright (C) 2013 Ariya Hidayat <ariya.hidayat@gmail.com>
|
|
Copyright (C) 2013 Thaddee Tyl <thaddee.tyl@gmail.com>
|
|
Copyright (C) 2013 Mathias Bynens <mathias@qiwi.be>
|
|
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 TokenName,
|
|
source,
|
|
index,
|
|
length,
|
|
lookahead;
|
|
|
|
var TokenBooleanLiteral = 1,
|
|
TokenEOF = 2,
|
|
TokenIdentifier = 3,
|
|
TokenKeyword = 4,
|
|
TokenNullLiteral = 5,
|
|
TokenNumericLiteral = 6,
|
|
TokenPunctuator = 7,
|
|
TokenStringLiteral = 8,
|
|
TokenRegularExpression = 9;
|
|
|
|
TokenName = {};
|
|
TokenName[TokenBooleanLiteral] = 'Boolean';
|
|
TokenName[TokenEOF] = '<end>';
|
|
TokenName[TokenIdentifier] = 'Identifier';
|
|
TokenName[TokenKeyword] = 'Keyword';
|
|
TokenName[TokenNullLiteral] = 'Null';
|
|
TokenName[TokenNumericLiteral] = 'Numeric';
|
|
TokenName[TokenPunctuator] = 'Punctuator';
|
|
TokenName[TokenStringLiteral] = 'String';
|
|
TokenName[TokenRegularExpression] = 'RegularExpression';
|
|
|
|
var SyntaxArrayExpression = 'ArrayExpression',
|
|
SyntaxBinaryExpression = 'BinaryExpression',
|
|
SyntaxCallExpression = 'CallExpression',
|
|
SyntaxConditionalExpression = 'ConditionalExpression',
|
|
SyntaxIdentifier = 'Identifier',
|
|
SyntaxLiteral = 'Literal',
|
|
SyntaxLogicalExpression = 'LogicalExpression',
|
|
SyntaxMemberExpression = 'MemberExpression',
|
|
SyntaxObjectExpression = 'ObjectExpression',
|
|
SyntaxProperty = 'Property',
|
|
SyntaxUnaryExpression = 'UnaryExpression';
|
|
|
|
// Error messages should be identical to V8.
|
|
var MessageUnexpectedToken = 'Unexpected token %0',
|
|
MessageUnexpectedNumber = 'Unexpected number',
|
|
MessageUnexpectedString = 'Unexpected string',
|
|
MessageUnexpectedIdentifier = 'Unexpected identifier',
|
|
MessageUnexpectedReserved = 'Unexpected reserved word',
|
|
MessageUnexpectedEOS = 'Unexpected end of input',
|
|
MessageInvalidRegExp = 'Invalid regular expression',
|
|
MessageUnterminatedRegExp = 'Invalid regular expression: missing /',
|
|
MessageStrictOctalLiteral = 'Octal literals are not allowed in strict mode.',
|
|
MessageStrictDuplicateProperty = 'Duplicate data property in object literal not allowed in strict mode';
|
|
|
|
var ILLEGAL = 'ILLEGAL',
|
|
DISABLED = 'Disabled.';
|
|
|
|
// See also tools/generate-unicode-regex.py.
|
|
var RegexNonAsciiIdentifierStart = new RegExp("[\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0620-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0800-\\u0815\\u081A\\u0824\\u0828\\u0840-\\u0858\\u08A0-\\u08B2\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971-\\u0980\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0CF1\\u0CF2\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D\\u0D4E\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC-\\u0EDF\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8C\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1A20-\\u1A54\\u1AA7\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1BBA-\\u1BE5\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1CE9-\\u1CEC\\u1CEE-\\u1CF1\\u1CF5\\u1CF6\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u209C\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CEE\\u2CF2\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005-\\u3007\\u3021-\\u3029\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA66E\\uA67F-\\uA69D\\uA6A0-\\uA6EF\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA8F2-\\uA8F7\\uA8FB\\uA90A-\\uA925\\uA930-\\uA946\\uA960-\\uA97C\\uA984-\\uA9B2\\uA9CF\\uA9E0-\\uA9E4\\uA9E6-\\uA9EF\\uA9FA-\\uA9FE\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAA60-\\uAA76\\uAA7A\\uAA7E-\\uAAAF\\uAAB1\\uAAB5\\uAAB6\\uAAB9-\\uAABD\\uAAC0\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEA\\uAAF2-\\uAAF4\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABE2\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),
|
|
// eslint-disable-next-line no-misleading-character-class
|
|
RegexNonAsciiIdentifierPart = new RegExp("[\\xAA\\xB5\\xBA\\xC0-\\xD6\\xD8-\\xF6\\xF8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0300-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u037F\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u0483-\\u0487\\u048A-\\u052F\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0610-\\u061A\\u0620-\\u0669\\u066E-\\u06D3\\u06D5-\\u06DC\\u06DF-\\u06E8\\u06EA-\\u06FC\\u06FF\\u0710-\\u074A\\u074D-\\u07B1\\u07C0-\\u07F5\\u07FA\\u0800-\\u082D\\u0840-\\u085B\\u08A0-\\u08B2\\u08E4-\\u0963\\u0966-\\u096F\\u0971-\\u0983\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BC-\\u09C4\\u09C7\\u09C8\\u09CB-\\u09CE\\u09D7\\u09DC\\u09DD\\u09DF-\\u09E3\\u09E6-\\u09F1\\u0A01-\\u0A03\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A3C\\u0A3E-\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A59-\\u0A5C\\u0A5E\\u0A66-\\u0A75\\u0A81-\\u0A83\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABC-\\u0AC5\\u0AC7-\\u0AC9\\u0ACB-\\u0ACD\\u0AD0\\u0AE0-\\u0AE3\\u0AE6-\\u0AEF\\u0B01-\\u0B03\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3C-\\u0B44\\u0B47\\u0B48\\u0B4B-\\u0B4D\\u0B56\\u0B57\\u0B5C\\u0B5D\\u0B5F-\\u0B63\\u0B66-\\u0B6F\\u0B71\\u0B82\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BBE-\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCD\\u0BD0\\u0BD7\\u0BE6-\\u0BEF\\u0C00-\\u0C03\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C39\\u0C3D-\\u0C44\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C58\\u0C59\\u0C60-\\u0C63\\u0C66-\\u0C6F\\u0C81-\\u0C83\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBC-\\u0CC4\\u0CC6-\\u0CC8\\u0CCA-\\u0CCD\\u0CD5\\u0CD6\\u0CDE\\u0CE0-\\u0CE3\\u0CE6-\\u0CEF\\u0CF1\\u0CF2\\u0D01-\\u0D03\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D3A\\u0D3D-\\u0D44\\u0D46-\\u0D48\\u0D4A-\\u0D4E\\u0D57\\u0D60-\\u0D63\\u0D66-\\u0D6F\\u0D7A-\\u0D7F\\u0D82\\u0D83\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0DCA\\u0DCF-\\u0DD4\\u0DD6\\u0DD8-\\u0DDF\\u0DE6-\\u0DEF\\u0DF2\\u0DF3\\u0E01-\\u0E3A\\u0E40-\\u0E4E\\u0E50-\\u0E59\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB9\\u0EBB-\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EC8-\\u0ECD\\u0ED0-\\u0ED9\\u0EDC-\\u0EDF\\u0F00\\u0F18\\u0F19\\u0F20-\\u0F29\\u0F35\\u0F37\\u0F39\\u0F3E-\\u0F47\\u0F49-\\u0F6C\\u0F71-\\u0F84\\u0F86-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u1000-\\u1049\\u1050-\\u109D\\u10A0-\\u10C5\\u10C7\\u10CD\\u10D0-\\u10FA\\u10FC-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u135D-\\u135F\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u167F\\u1681-\\u169A\\u16A0-\\u16EA\\u16EE-\\u16F8\\u1700-\\u170C\\u170E-\\u1714\\u1720-\\u1734\\u1740-\\u1753\\u1760-\\u176C\\u176E-\\u1770\\u1772\\u1773\\u1780-\\u17D3\\u17D7\\u17DC\\u17DD\\u17E0-\\u17E9\\u180B-\\u180D\\u1810-\\u1819\\u1820-\\u1877\\u1880-\\u18AA\\u18B0-\\u18F5\\u1900-\\u191E\\u1920-\\u192B\\u1930-\\u193B\\u1946-\\u196D\\u1970-\\u1974\\u1980-\\u19AB\\u19B0-\\u19C9\\u19D0-\\u19D9\\u1A00-\\u1A1B\\u1A20-\\u1A5E\\u1A60-\\u1A7C\\u1A7F-\\u1A89\\u1A90-\\u1A99\\u1AA7\\u1AB0-\\u1ABD\\u1B00-\\u1B4B\\u1B50-\\u1B59\\u1B6B-\\u1B73\\u1B80-\\u1BF3\\u1C00-\\u1C37\\u1C40-\\u1C49\\u1C4D-\\u1C7D\\u1CD0-\\u1CD2\\u1CD4-\\u1CF6\\u1CF8\\u1CF9\\u1D00-\\u1DF5\\u1DFC-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u200C\\u200D\\u203F\\u2040\\u2054\\u2071\\u207F\\u2090-\\u209C\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2160-\\u2188\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2CE4\\u2CEB-\\u2CF3\\u2D00-\\u2D25\\u2D27\\u2D2D\\u2D30-\\u2D67\\u2D6F\\u2D7F-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2DE0-\\u2DFF\\u2E2F\\u3005-\\u3007\\u3021-\\u302F\\u3031-\\u3035\\u3038-\\u303C\\u3041-\\u3096\\u3099\\u309A\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31BA\\u31F0-\\u31FF\\u3400-\\u4DB5\\u4E00-\\u9FCC\\uA000-\\uA48C\\uA4D0-\\uA4FD\\uA500-\\uA60C\\uA610-\\uA62B\\uA640-\\uA66F\\uA674-\\uA67D\\uA67F-\\uA69D\\uA69F-\\uA6F1\\uA717-\\uA71F\\uA722-\\uA788\\uA78B-\\uA78E\\uA790-\\uA7AD\\uA7B0\\uA7B1\\uA7F7-\\uA827\\uA840-\\uA873\\uA880-\\uA8C4\\uA8D0-\\uA8D9\\uA8E0-\\uA8F7\\uA8FB\\uA900-\\uA92D\\uA930-\\uA953\\uA960-\\uA97C\\uA980-\\uA9C0\\uA9CF-\\uA9D9\\uA9E0-\\uA9FE\\uAA00-\\uAA36\\uAA40-\\uAA4D\\uAA50-\\uAA59\\uAA60-\\uAA76\\uAA7A-\\uAAC2\\uAADB-\\uAADD\\uAAE0-\\uAAEF\\uAAF2-\\uAAF6\\uAB01-\\uAB06\\uAB09-\\uAB0E\\uAB11-\\uAB16\\uAB20-\\uAB26\\uAB28-\\uAB2E\\uAB30-\\uAB5A\\uAB5C-\\uAB5F\\uAB64\\uAB65\\uABC0-\\uABEA\\uABEC\\uABED\\uABF0-\\uABF9\\uAC00-\\uD7A3\\uD7B0-\\uD7C6\\uD7CB-\\uD7FB\\uF900-\\uFA6D\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE00-\\uFE0F\\uFE20-\\uFE2D\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF10-\\uFF19\\uFF21-\\uFF3A\\uFF3F\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]");
|
|
|
|
// 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) {
|
|
/* istanbul ignore next */
|
|
if (!condition) {
|
|
throw new Error('ASSERT: ' + message);
|
|
}
|
|
}
|
|
|
|
function isDecimalDigit(ch) {
|
|
return (ch >= 0x30 && ch <= 0x39); // 0..9
|
|
}
|
|
|
|
function isHexDigit(ch) {
|
|
return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
|
|
}
|
|
|
|
function isOctalDigit(ch) {
|
|
return '01234567'.indexOf(ch) >= 0;
|
|
}
|
|
|
|
// 7.2 White Space
|
|
|
|
function isWhiteSpace(ch) {
|
|
return (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
|
|
(ch >= 0x1680 && [0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(ch) >= 0);
|
|
}
|
|
|
|
// 7.3 Line Terminators
|
|
|
|
function isLineTerminator(ch) {
|
|
return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029);
|
|
}
|
|
|
|
// 7.6 Identifier Names and Identifiers
|
|
|
|
function isIdentifierStart(ch) {
|
|
return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore)
|
|
(ch >= 0x41 && ch <= 0x5A) || // A..Z
|
|
(ch >= 0x61 && ch <= 0x7A) || // a..z
|
|
(ch === 0x5C) || // \ (backslash)
|
|
((ch >= 0x80) && RegexNonAsciiIdentifierStart.test(String.fromCharCode(ch)));
|
|
}
|
|
|
|
function isIdentifierPart(ch) {
|
|
return (ch === 0x24) || (ch === 0x5F) || // $ (dollar) and _ (underscore)
|
|
(ch >= 0x41 && ch <= 0x5A) || // A..Z
|
|
(ch >= 0x61 && ch <= 0x7A) || // a..z
|
|
(ch >= 0x30 && ch <= 0x39) || // 0..9
|
|
(ch === 0x5C) || // \ (backslash)
|
|
((ch >= 0x80) && RegexNonAsciiIdentifierPart.test(String.fromCharCode(ch)));
|
|
}
|
|
|
|
// 7.6.1.1 Keywords
|
|
|
|
var keywords = {
|
|
'if':1, 'in':1, 'do':1,
|
|
'var':1, 'for':1, 'new':1, 'try':1, 'let':1,
|
|
'this':1, 'else':1, 'case':1, 'void':1, 'with':1, 'enum':1,
|
|
'while':1, 'break':1, 'catch':1, 'throw':1, 'const':1, 'yield':1, 'class':1, 'super':1,
|
|
'return':1, 'typeof':1, 'delete':1, 'switch':1, 'export':1, 'import':1, 'public':1, 'static':1,
|
|
'default':1, 'finally':1, 'extends':1, 'package':1, 'private':1,
|
|
'function':1, 'continue':1, 'debugger':1,
|
|
'interface':1, 'protected':1,
|
|
'instanceof':1, 'implements':1
|
|
};
|
|
|
|
function skipComment() {
|
|
var ch;
|
|
|
|
while (index < length) {
|
|
ch = source.charCodeAt(index);
|
|
|
|
if (isWhiteSpace(ch) || isLineTerminator(ch)) {
|
|
++index;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
function scanHexEscape(prefix) {
|
|
var i, len, ch, code = 0;
|
|
|
|
len = (prefix === 'u') ? 4 : 2;
|
|
for (i = 0; i < len; ++i) {
|
|
if (index < length && isHexDigit(source[index])) {
|
|
ch = source[index++];
|
|
code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
|
|
} else {
|
|
throwError({}, MessageUnexpectedToken, ILLEGAL);
|
|
}
|
|
}
|
|
return String.fromCharCode(code);
|
|
}
|
|
|
|
function scanUnicodeCodePointEscape() {
|
|
var ch, code, cu1, cu2;
|
|
|
|
ch = source[index];
|
|
code = 0;
|
|
|
|
// At least, one hex digit is required.
|
|
if (ch === '}') {
|
|
throwError({}, MessageUnexpectedToken, ILLEGAL);
|
|
}
|
|
|
|
while (index < length) {
|
|
ch = source[index++];
|
|
if (!isHexDigit(ch)) {
|
|
break;
|
|
}
|
|
code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
|
|
}
|
|
|
|
if (code > 0x10FFFF || ch !== '}') {
|
|
throwError({}, MessageUnexpectedToken, 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' (U+005C, U+0075) denotes an escaped character.
|
|
if (ch === 0x5C) {
|
|
if (source.charCodeAt(index) !== 0x75) {
|
|
throwError({}, MessageUnexpectedToken, ILLEGAL);
|
|
}
|
|
++index;
|
|
ch = scanHexEscape('u');
|
|
if (!ch || ch === '\\' || !isIdentifierStart(ch.charCodeAt(0))) {
|
|
throwError({}, MessageUnexpectedToken, ILLEGAL);
|
|
}
|
|
id = ch;
|
|
}
|
|
|
|
while (index < length) {
|
|
ch = source.charCodeAt(index);
|
|
if (!isIdentifierPart(ch)) {
|
|
break;
|
|
}
|
|
++index;
|
|
id += String.fromCharCode(ch);
|
|
|
|
// '\u' (U+005C, U+0075) denotes an escaped character.
|
|
if (ch === 0x5C) {
|
|
id = id.substr(0, id.length - 1);
|
|
if (source.charCodeAt(index) !== 0x75) {
|
|
throwError({}, MessageUnexpectedToken, ILLEGAL);
|
|
}
|
|
++index;
|
|
ch = scanHexEscape('u');
|
|
if (!ch || ch === '\\' || !isIdentifierPart(ch.charCodeAt(0))) {
|
|
throwError({}, MessageUnexpectedToken, ILLEGAL);
|
|
}
|
|
id += ch;
|
|
}
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
function getIdentifier() {
|
|
var start, ch;
|
|
|
|
start = index++;
|
|
while (index < length) {
|
|
ch = source.charCodeAt(index);
|
|
if (ch === 0x5C) {
|
|
// Blackslash (U+005C) 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 (U+005C) starts an escaped character.
|
|
id = (source.charCodeAt(index) === 0x5C) ? getEscapedIdentifier() : getIdentifier();
|
|
|
|
// There is no keyword or literal with only one character.
|
|
// Thus, it must be an identifier.
|
|
if (id.length === 1) {
|
|
type = TokenIdentifier;
|
|
} else if (keywords.hasOwnProperty(id)) { // eslint-disable-line no-prototype-builtins
|
|
type = TokenKeyword;
|
|
} else if (id === 'null') {
|
|
type = TokenNullLiteral;
|
|
} else if (id === 'true' || id === 'false') {
|
|
type = TokenBooleanLiteral;
|
|
} else {
|
|
type = TokenIdentifier;
|
|
}
|
|
|
|
return {
|
|
type: type,
|
|
value: id,
|
|
start: start,
|
|
end: index
|
|
};
|
|
}
|
|
|
|
// 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.
|
|
case 0x2E: // . dot
|
|
case 0x28: // ( open bracket
|
|
case 0x29: // ) close bracket
|
|
case 0x3B: // ; semicolon
|
|
case 0x2C: // , comma
|
|
case 0x7B: // { open curly brace
|
|
case 0x7D: // } close curly brace
|
|
case 0x5B: // [
|
|
case 0x5D: // ]
|
|
case 0x3A: // :
|
|
case 0x3F: // ?
|
|
case 0x7E: // ~
|
|
++index;
|
|
return {
|
|
type: TokenPunctuator,
|
|
value: String.fromCharCode(code),
|
|
start: start,
|
|
end: index
|
|
};
|
|
|
|
default:
|
|
code2 = source.charCodeAt(index + 1);
|
|
|
|
// '=' (U+003D) marks an assignment or comparison operator.
|
|
if (code2 === 0x3D) {
|
|
switch (code) {
|
|
case 0x2B: // +
|
|
case 0x2D: // -
|
|
case 0x2F: // /
|
|
case 0x3C: // <
|
|
case 0x3E: // >
|
|
case 0x5E: // ^
|
|
case 0x7C: // |
|
|
case 0x25: // %
|
|
case 0x26: // &
|
|
case 0x2A: // *
|
|
index += 2;
|
|
return {
|
|
type: TokenPunctuator,
|
|
value: String.fromCharCode(code) + String.fromCharCode(code2),
|
|
start: start,
|
|
end: index
|
|
};
|
|
|
|
case 0x21: // !
|
|
case 0x3D: // =
|
|
index += 2;
|
|
|
|
// !== and ===
|
|
if (source.charCodeAt(index) === 0x3D) {
|
|
++index;
|
|
}
|
|
return {
|
|
type: TokenPunctuator,
|
|
value: source.slice(start, index),
|
|
start: start,
|
|
end: index
|
|
};
|
|
}
|
|
}
|
|
}
|
|
|
|
// 4-character punctuator: >>>=
|
|
|
|
ch4 = source.substr(index, 4);
|
|
|
|
if (ch4 === '>>>=') {
|
|
index += 4;
|
|
return {
|
|
type: TokenPunctuator,
|
|
value: ch4,
|
|
start: start,
|
|
end: index
|
|
};
|
|
}
|
|
|
|
// 3-character punctuators: === !== >>> <<= >>=
|
|
|
|
ch3 = ch4.substr(0, 3);
|
|
|
|
if (ch3 === '>>>' || ch3 === '<<=' || ch3 === '>>=') {
|
|
index += 3;
|
|
return {
|
|
type: TokenPunctuator,
|
|
value: ch3,
|
|
start: start,
|
|
end: index
|
|
};
|
|
}
|
|
|
|
// Other 2-character punctuators: ++ -- << >> && ||
|
|
ch2 = ch3.substr(0, 2);
|
|
|
|
if ((ch1 === ch2[1] && ('+-<>&|'.indexOf(ch1) >= 0)) || ch2 === '=>') {
|
|
index += 2;
|
|
return {
|
|
type: TokenPunctuator,
|
|
value: ch2,
|
|
start: start,
|
|
end: index
|
|
};
|
|
}
|
|
|
|
// 1-character punctuators: < > = ! + - * % & | ^ /
|
|
|
|
if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
|
|
++index;
|
|
return {
|
|
type: TokenPunctuator,
|
|
value: ch1,
|
|
start: start,
|
|
end: index
|
|
};
|
|
}
|
|
|
|
throwError({}, MessageUnexpectedToken, 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({}, MessageUnexpectedToken, ILLEGAL);
|
|
}
|
|
|
|
if (isIdentifierStart(source.charCodeAt(index))) {
|
|
throwError({}, MessageUnexpectedToken, ILLEGAL);
|
|
}
|
|
|
|
return {
|
|
type: TokenNumericLiteral,
|
|
value: parseInt('0x' + number, 16),
|
|
start: start,
|
|
end: index
|
|
};
|
|
}
|
|
|
|
function scanOctalLiteral(start) {
|
|
var number = '0' + source[index++];
|
|
while (index < length) {
|
|
if (!isOctalDigit(source[index])) {
|
|
break;
|
|
}
|
|
number += source[index++];
|
|
}
|
|
|
|
if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) {
|
|
throwError({}, MessageUnexpectedToken, ILLEGAL);
|
|
}
|
|
|
|
return {
|
|
type: TokenNumericLiteral,
|
|
value: parseInt(number, 8),
|
|
octal: true,
|
|
start: start,
|
|
end: index
|
|
};
|
|
}
|
|
|
|
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') {
|
|
if (ch === 'x' || ch === 'X') {
|
|
++index;
|
|
return scanHexLiteral(start);
|
|
}
|
|
if (isOctalDigit(ch)) {
|
|
return scanOctalLiteral(start);
|
|
}
|
|
|
|
// decimal number starts with '0' such as '09' is illegal.
|
|
if (ch && isDecimalDigit(ch.charCodeAt(0))) {
|
|
throwError({}, MessageUnexpectedToken, 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({}, MessageUnexpectedToken, ILLEGAL);
|
|
}
|
|
}
|
|
|
|
if (isIdentifierStart(source.charCodeAt(index))) {
|
|
throwError({}, MessageUnexpectedToken, ILLEGAL);
|
|
}
|
|
|
|
return {
|
|
type: TokenNumericLiteral,
|
|
value: parseFloat(number),
|
|
start: start,
|
|
end: index
|
|
};
|
|
}
|
|
|
|
// 7.8.4 String Literals
|
|
|
|
function scanStringLiteral() {
|
|
var str = '',
|
|
quote, start, ch, code, 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 'u':
|
|
case 'x':
|
|
if (source[index] === '{') {
|
|
++index;
|
|
str += scanUnicodeCodePointEscape();
|
|
} else {
|
|
str += scanHexEscape(ch);
|
|
}
|
|
break;
|
|
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:
|
|
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++]);
|
|
}
|
|
}
|
|
str += String.fromCharCode(code);
|
|
} else {
|
|
str += ch;
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
if (ch === '\r' && source[index] === '\n') {
|
|
++index;
|
|
}
|
|
}
|
|
} else if (isLineTerminator(ch.charCodeAt(0))) {
|
|
break;
|
|
} else {
|
|
str += ch;
|
|
}
|
|
}
|
|
|
|
if (quote !== '') {
|
|
throwError({}, MessageUnexpectedToken, ILLEGAL);
|
|
}
|
|
|
|
return {
|
|
type: TokenStringLiteral,
|
|
value: str,
|
|
octal: octal,
|
|
start: start,
|
|
end: index
|
|
};
|
|
}
|
|
|
|
function testRegExp(pattern, flags) {
|
|
var tmp = pattern;
|
|
|
|
if (flags.indexOf('u') >= 0) {
|
|
// Replace each astral symbol and every Unicode code point
|
|
// escape sequence with a single ASCII symbol to avoid throwing on
|
|
// regular expressions that are only valid in combination with the
|
|
// `/u` flag.
|
|
// Note: replacing with the ASCII symbol `x` might cause false
|
|
// negatives in unlikely scenarios. For example, `[\u{61}-b]` is a
|
|
// perfectly valid pattern that is equivalent to `[a-b]`, but it
|
|
// would be replaced by `[x-b]` which throws an error.
|
|
tmp = tmp
|
|
.replace(/\\u\{([0-9a-fA-F]+)\}/g, function($0, $1) {
|
|
if (parseInt($1, 16) <= 0x10FFFF) {
|
|
return 'x';
|
|
}
|
|
throwError({}, MessageInvalidRegExp);
|
|
})
|
|
.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, 'x');
|
|
}
|
|
|
|
// First, detect invalid regular expressions.
|
|
try {
|
|
new RegExp(tmp);
|
|
} catch (e) {
|
|
throwError({}, MessageInvalidRegExp);
|
|
}
|
|
|
|
// Return a regular expression object for this pattern-flag pair, or
|
|
// `null` in case the current environment doesn't support the flags it
|
|
// uses.
|
|
try {
|
|
return new RegExp(pattern, flags);
|
|
} catch (exception) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function scanRegExpBody() {
|
|
var ch, str, classMarker, terminated, body;
|
|
|
|
ch = source[index];
|
|
assert(ch === '/', 'Regular expression literal must start with a slash');
|
|
str = source[index++];
|
|
|
|
classMarker = false;
|
|
terminated = false;
|
|
while (index < length) {
|
|
ch = source[index++];
|
|
str += ch;
|
|
if (ch === '\\') {
|
|
ch = source[index++];
|
|
// ECMA-262 7.8.5
|
|
if (isLineTerminator(ch.charCodeAt(0))) {
|
|
throwError({}, MessageUnterminatedRegExp);
|
|
}
|
|
str += ch;
|
|
} else if (isLineTerminator(ch.charCodeAt(0))) {
|
|
throwError({}, MessageUnterminatedRegExp);
|
|
} else if (classMarker) {
|
|
if (ch === ']') {
|
|
classMarker = false;
|
|
}
|
|
} else {
|
|
if (ch === '/') {
|
|
terminated = true;
|
|
break;
|
|
} else if (ch === '[') {
|
|
classMarker = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!terminated) {
|
|
throwError({}, MessageUnterminatedRegExp);
|
|
}
|
|
|
|
// Exclude leading and trailing slash.
|
|
body = str.substr(1, str.length - 2);
|
|
return {
|
|
value: body,
|
|
literal: str
|
|
};
|
|
}
|
|
|
|
function scanRegExpFlags() {
|
|
var ch, str, flags;
|
|
|
|
str = '';
|
|
flags = '';
|
|
while (index < length) {
|
|
ch = source[index];
|
|
if (!isIdentifierPart(ch.charCodeAt(0))) {
|
|
break;
|
|
}
|
|
|
|
++index;
|
|
if (ch === '\\' && index < length) {
|
|
throwError({}, MessageUnexpectedToken, ILLEGAL);
|
|
} else {
|
|
flags += ch;
|
|
str += ch;
|
|
}
|
|
}
|
|
|
|
if (flags.search(/[^gimuy]/g) >= 0) {
|
|
throwError({}, MessageInvalidRegExp, flags);
|
|
}
|
|
|
|
return {
|
|
value: flags,
|
|
literal: str
|
|
};
|
|
}
|
|
|
|
function scanRegExp() {
|
|
var start, body, flags, value;
|
|
|
|
lookahead = null;
|
|
skipComment();
|
|
start = index;
|
|
|
|
body = scanRegExpBody();
|
|
flags = scanRegExpFlags();
|
|
value = testRegExp(body.value, flags.value);
|
|
|
|
return {
|
|
literal: body.literal + flags.literal,
|
|
value: value,
|
|
regex: {
|
|
pattern: body.value,
|
|
flags: flags.value
|
|
},
|
|
start: start,
|
|
end: index
|
|
};
|
|
}
|
|
|
|
function isIdentifierName(token) {
|
|
return token.type === TokenIdentifier ||
|
|
token.type === TokenKeyword ||
|
|
token.type === TokenBooleanLiteral ||
|
|
token.type === TokenNullLiteral;
|
|
}
|
|
|
|
function advance() {
|
|
var ch;
|
|
|
|
skipComment();
|
|
|
|
if (index >= length) {
|
|
return {
|
|
type: TokenEOF,
|
|
start: index,
|
|
end: index
|
|
};
|
|
}
|
|
|
|
ch = source.charCodeAt(index);
|
|
|
|
if (isIdentifierStart(ch)) {
|
|
return scanIdentifier();
|
|
}
|
|
|
|
// Very common: ( and ) and ;
|
|
if (ch === 0x28 || ch === 0x29 || ch === 0x3B) {
|
|
return scanPunctuator();
|
|
}
|
|
|
|
// String literal starts with single quote (U+0027) or double quote (U+0022).
|
|
if (ch === 0x27 || ch === 0x22) {
|
|
return scanStringLiteral();
|
|
}
|
|
|
|
|
|
// Dot (.) U+002E can also start a floating-point number, hence the need
|
|
// to check the next character.
|
|
if (ch === 0x2E) {
|
|
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.end;
|
|
|
|
lookahead = advance();
|
|
|
|
index = token.end;
|
|
|
|
return token;
|
|
}
|
|
|
|
function peek() {
|
|
var pos;
|
|
|
|
pos = index;
|
|
|
|
lookahead = advance();
|
|
index = pos;
|
|
}
|
|
|
|
function finishArrayExpression(elements) {
|
|
var node = new ASTNode(SyntaxArrayExpression);
|
|
node.elements = elements;
|
|
return node;
|
|
}
|
|
|
|
function finishBinaryExpression(operator, left, right) {
|
|
var node = new ASTNode((operator === '||' || operator === '&&') ? SyntaxLogicalExpression : SyntaxBinaryExpression);
|
|
node.operator = operator;
|
|
node.left = left;
|
|
node.right = right;
|
|
return node;
|
|
}
|
|
|
|
function finishCallExpression(callee, args) {
|
|
var node = new ASTNode(SyntaxCallExpression);
|
|
node.callee = callee;
|
|
node.arguments = args;
|
|
return node;
|
|
}
|
|
|
|
function finishConditionalExpression(test, consequent, alternate) {
|
|
var node = new ASTNode(SyntaxConditionalExpression);
|
|
node.test = test;
|
|
node.consequent = consequent;
|
|
node.alternate = alternate;
|
|
return node;
|
|
}
|
|
|
|
function finishIdentifier(name) {
|
|
var node = new ASTNode(SyntaxIdentifier);
|
|
node.name = name;
|
|
return node;
|
|
}
|
|
|
|
function finishLiteral(token) {
|
|
var node = new ASTNode(SyntaxLiteral);
|
|
node.value = token.value;
|
|
node.raw = source.slice(token.start, token.end);
|
|
if (token.regex) {
|
|
if (node.raw === '//') {
|
|
node.raw = '/(?:)/';
|
|
}
|
|
node.regex = token.regex;
|
|
}
|
|
return node;
|
|
}
|
|
|
|
function finishMemberExpression(accessor, object, property) {
|
|
var node = new ASTNode(SyntaxMemberExpression);
|
|
node.computed = accessor === '[';
|
|
node.object = object;
|
|
node.property = property;
|
|
if (!node.computed) property.member = true;
|
|
return node;
|
|
}
|
|
|
|
function finishObjectExpression(properties) {
|
|
var node = new ASTNode(SyntaxObjectExpression);
|
|
node.properties = properties;
|
|
return node;
|
|
}
|
|
|
|
function finishProperty(kind, key, value) {
|
|
var node = new ASTNode(SyntaxProperty);
|
|
node.key = key;
|
|
node.value = value;
|
|
node.kind = kind;
|
|
return node;
|
|
}
|
|
|
|
function finishUnaryExpression(operator, argument) {
|
|
var node = new ASTNode(SyntaxUnaryExpression);
|
|
node.operator = operator;
|
|
node.argument = argument;
|
|
node.prefix = true;
|
|
return node;
|
|
}
|
|
|
|
// 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) {
|
|
if (token.type === TokenEOF) {
|
|
throwError(token, MessageUnexpectedEOS);
|
|
}
|
|
|
|
if (token.type === TokenNumericLiteral) {
|
|
throwError(token, MessageUnexpectedNumber);
|
|
}
|
|
|
|
if (token.type === TokenStringLiteral) {
|
|
throwError(token, MessageUnexpectedString);
|
|
}
|
|
|
|
if (token.type === TokenIdentifier) {
|
|
throwError(token, MessageUnexpectedIdentifier);
|
|
}
|
|
|
|
if (token.type === TokenKeyword) {
|
|
throwError(token, MessageUnexpectedReserved);
|
|
}
|
|
|
|
// BooleanLiteral, NullLiteral, or Punctuator.
|
|
throwError(token, MessageUnexpectedToken, 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 !== TokenPunctuator || token.value !== value) {
|
|
throwUnexpected(token);
|
|
}
|
|
}
|
|
|
|
// Return true if the next token matches the specified punctuator.
|
|
|
|
function match(value) {
|
|
return lookahead.type === TokenPunctuator && lookahead.value === value;
|
|
}
|
|
|
|
// Return true if the next token matches the specified keyword
|
|
|
|
function matchKeyword(keyword) {
|
|
return lookahead.type === TokenKeyword && lookahead.value === keyword;
|
|
}
|
|
|
|
// 11.1.4 Array Initialiser
|
|
|
|
function parseArrayInitialiser() {
|
|
var elements = [];
|
|
|
|
index = lookahead.start;
|
|
expect('[');
|
|
|
|
while (!match(']')) {
|
|
if (match(',')) {
|
|
lex();
|
|
elements.push(null);
|
|
} else {
|
|
elements.push(parseConditionalExpression());
|
|
|
|
if (!match(']')) {
|
|
expect(',');
|
|
}
|
|
}
|
|
}
|
|
|
|
lex();
|
|
|
|
return finishArrayExpression(elements);
|
|
}
|
|
|
|
// 11.1.5 Object Initialiser
|
|
|
|
function parseObjectPropertyKey() {
|
|
var token;
|
|
|
|
index = lookahead.start;
|
|
token = lex();
|
|
|
|
// Note: This function is called only from parseObjectProperty(), where
|
|
// EOF and Punctuator tokens are already filtered out.
|
|
|
|
if (token.type === TokenStringLiteral || token.type === TokenNumericLiteral) {
|
|
if (token.octal) {
|
|
throwError(token, MessageStrictOctalLiteral);
|
|
}
|
|
return finishLiteral(token);
|
|
}
|
|
|
|
return finishIdentifier(token.value);
|
|
}
|
|
|
|
function parseObjectProperty() {
|
|
var token, key, id, value;
|
|
|
|
index = lookahead.start;
|
|
token = lookahead;
|
|
|
|
if (token.type === TokenIdentifier) {
|
|
id = parseObjectPropertyKey();
|
|
expect(':');
|
|
value = parseConditionalExpression();
|
|
return finishProperty('init', id, value);
|
|
}
|
|
if (token.type === TokenEOF || token.type === TokenPunctuator) {
|
|
throwUnexpected(token);
|
|
} else {
|
|
key = parseObjectPropertyKey();
|
|
expect(':');
|
|
value = parseConditionalExpression();
|
|
return finishProperty('init', key, value);
|
|
}
|
|
}
|
|
|
|
function parseObjectInitialiser() {
|
|
var properties = [],
|
|
property, name, key, map = {},
|
|
toString = String;
|
|
|
|
index = lookahead.start;
|
|
expect('{');
|
|
|
|
while (!match('}')) {
|
|
property = parseObjectProperty();
|
|
|
|
if (property.key.type === SyntaxIdentifier) {
|
|
name = property.key.name;
|
|
} else {
|
|
name = toString(property.key.value);
|
|
}
|
|
|
|
key = '$' + name;
|
|
if (Object.prototype.hasOwnProperty.call(map, key)) {
|
|
throwError({}, MessageStrictDuplicateProperty);
|
|
} else {
|
|
map[key] = true;
|
|
}
|
|
|
|
properties.push(property);
|
|
|
|
if (!match('}')) {
|
|
expect(',');
|
|
}
|
|
}
|
|
|
|
expect('}');
|
|
|
|
return finishObjectExpression(properties);
|
|
}
|
|
|
|
// 11.1.6 The Grouping Operator
|
|
|
|
function parseGroupExpression() {
|
|
var expr;
|
|
|
|
expect('(');
|
|
|
|
expr = parseExpression();
|
|
|
|
expect(')');
|
|
|
|
return expr;
|
|
}
|
|
|
|
|
|
// 11.1 Primary Expressions
|
|
|
|
var legalKeywords = {
|
|
"if": 1,
|
|
"this": 1
|
|
};
|
|
|
|
function parsePrimaryExpression() {
|
|
var type, token, expr;
|
|
|
|
if (match('(')) {
|
|
return parseGroupExpression();
|
|
}
|
|
|
|
if (match('[')) {
|
|
return parseArrayInitialiser();
|
|
}
|
|
|
|
if (match('{')) {
|
|
return parseObjectInitialiser();
|
|
}
|
|
|
|
type = lookahead.type;
|
|
index = lookahead.start;
|
|
|
|
|
|
if (type === TokenIdentifier || legalKeywords[lookahead.value]) {
|
|
expr = finishIdentifier(lex().value);
|
|
} else if (type === TokenStringLiteral || type === TokenNumericLiteral) {
|
|
if (lookahead.octal) {
|
|
throwError(lookahead, MessageStrictOctalLiteral);
|
|
}
|
|
expr = finishLiteral(lex());
|
|
} else if (type === TokenKeyword) {
|
|
throw new Error(DISABLED);
|
|
} else if (type === TokenBooleanLiteral) {
|
|
token = lex();
|
|
token.value = (token.value === 'true');
|
|
expr = finishLiteral(token);
|
|
} else if (type === TokenNullLiteral) {
|
|
token = lex();
|
|
token.value = null;
|
|
expr = finishLiteral(token);
|
|
} else if (match('/') || match('/=')) {
|
|
expr = finishLiteral(scanRegExp());
|
|
peek();
|
|
} else {
|
|
throwUnexpected(lex());
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// 11.2 Left-Hand-Side Expressions
|
|
|
|
function parseArguments() {
|
|
var args = [];
|
|
|
|
expect('(');
|
|
|
|
if (!match(')')) {
|
|
while (index < length) {
|
|
args.push(parseConditionalExpression());
|
|
if (match(')')) {
|
|
break;
|
|
}
|
|
expect(',');
|
|
}
|
|
}
|
|
|
|
expect(')');
|
|
|
|
return args;
|
|
}
|
|
|
|
function parseNonComputedProperty() {
|
|
var token;
|
|
index = lookahead.start;
|
|
token = lex();
|
|
|
|
if (!isIdentifierName(token)) {
|
|
throwUnexpected(token);
|
|
}
|
|
|
|
return finishIdentifier(token.value);
|
|
}
|
|
|
|
function parseNonComputedMember() {
|
|
expect('.');
|
|
|
|
return parseNonComputedProperty();
|
|
}
|
|
|
|
function parseComputedMember() {
|
|
var expr;
|
|
|
|
expect('[');
|
|
|
|
expr = parseExpression();
|
|
|
|
expect(']');
|
|
|
|
return expr;
|
|
}
|
|
|
|
function parseLeftHandSideExpressionAllowCall() {
|
|
var expr, args, property;
|
|
|
|
expr = parsePrimaryExpression();
|
|
|
|
for (;;) {
|
|
if (match('.')) {
|
|
property = parseNonComputedMember();
|
|
expr = finishMemberExpression('.', expr, property);
|
|
} else if (match('(')) {
|
|
args = parseArguments();
|
|
expr = finishCallExpression(expr, args);
|
|
} else if (match('[')) {
|
|
property = parseComputedMember();
|
|
expr = finishMemberExpression('[', expr, property);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// 11.3 Postfix Expressions
|
|
|
|
function parsePostfixExpression() {
|
|
var expr = parseLeftHandSideExpressionAllowCall();
|
|
|
|
if (lookahead.type === TokenPunctuator) {
|
|
if ((match('++') || match('--'))) {
|
|
throw new Error(DISABLED);
|
|
}
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// 11.4 Unary Operators
|
|
|
|
function parseUnaryExpression() {
|
|
var token, expr;
|
|
|
|
if (lookahead.type !== TokenPunctuator && lookahead.type !== TokenKeyword) {
|
|
expr = parsePostfixExpression();
|
|
} else if (match('++') || match('--')) {
|
|
throw new Error(DISABLED);
|
|
} else if (match('+') || match('-') || match('~') || match('!')) {
|
|
token = lex();
|
|
expr = parseUnaryExpression();
|
|
expr = finishUnaryExpression(token.value, expr);
|
|
} else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
|
|
throw new Error(DISABLED);
|
|
} else {
|
|
expr = parsePostfixExpression();
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
function binaryPrecedence(token) {
|
|
var prec = 0;
|
|
|
|
if (token.type !== TokenPunctuator && token.type !== TokenKeyword) {
|
|
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':
|
|
case 'in':
|
|
prec = 7;
|
|
break;
|
|
|
|
case '<<':
|
|
case '>>':
|
|
case '>>>':
|
|
prec = 8;
|
|
break;
|
|
|
|
case '+':
|
|
case '-':
|
|
prec = 9;
|
|
break;
|
|
|
|
case '*':
|
|
case '/':
|
|
case '%':
|
|
prec = 11;
|
|
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 marker, markers, expr, token, prec, stack, right, operator, left, i;
|
|
|
|
marker = lookahead;
|
|
left = parseUnaryExpression();
|
|
|
|
token = lookahead;
|
|
prec = binaryPrecedence(token);
|
|
if (prec === 0) {
|
|
return left;
|
|
}
|
|
token.prec = prec;
|
|
lex();
|
|
|
|
markers = [marker, lookahead];
|
|
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();
|
|
markers.pop();
|
|
expr = finishBinaryExpression(operator, left, right);
|
|
stack.push(expr);
|
|
}
|
|
|
|
// Shift.
|
|
token = lex();
|
|
token.prec = prec;
|
|
stack.push(token);
|
|
markers.push(lookahead);
|
|
expr = parseUnaryExpression();
|
|
stack.push(expr);
|
|
}
|
|
|
|
// Final reduce to clean-up the stack.
|
|
i = stack.length - 1;
|
|
expr = stack[i];
|
|
markers.pop();
|
|
while (i > 1) {
|
|
markers.pop();
|
|
expr = finishBinaryExpression(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 = finishConditionalExpression(expr, consequent, alternate);
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
// 11.14 Comma Operator
|
|
|
|
function parseExpression() {
|
|
var expr = parseConditionalExpression();
|
|
|
|
if (match(',')) {
|
|
throw new Error(DISABLED); // no sequence expressions
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
function parser(code) {
|
|
source = code;
|
|
index = 0;
|
|
length = source.length;
|
|
lookahead = null;
|
|
|
|
peek();
|
|
|
|
var expr = parseExpression();
|
|
|
|
if (lookahead.type !== TokenEOF) {
|
|
throw new Error("Unexpect token after expression.");
|
|
}
|
|
return expr;
|
|
}
|
|
|
|
var Constants = {
|
|
NaN: 'NaN',
|
|
E: 'Math.E',
|
|
LN2: 'Math.LN2',
|
|
LN10: 'Math.LN10',
|
|
LOG2E: 'Math.LOG2E',
|
|
LOG10E: 'Math.LOG10E',
|
|
PI: 'Math.PI',
|
|
SQRT1_2: 'Math.SQRT1_2',
|
|
SQRT2: 'Math.SQRT2',
|
|
MIN_VALUE: 'Number.MIN_VALUE',
|
|
MAX_VALUE: 'Number.MAX_VALUE'
|
|
};
|
|
|
|
function Functions(codegen) {
|
|
|
|
function fncall(name, args, cast, type) {
|
|
var obj = codegen(args[0]);
|
|
if (cast) {
|
|
obj = cast + '(' + obj + ')';
|
|
if (cast.lastIndexOf('new ', 0) === 0) obj = '(' + obj + ')';
|
|
}
|
|
return obj + '.' + name + (type < 0 ? '' : type === 0 ?
|
|
'()' :
|
|
'(' + args.slice(1).map(codegen).join(',') + ')');
|
|
}
|
|
|
|
function fn(name, cast, type) {
|
|
return function(args) {
|
|
return fncall(name, args, cast, type);
|
|
};
|
|
}
|
|
|
|
var DATE = 'new Date',
|
|
STRING = 'String',
|
|
REGEXP = 'RegExp';
|
|
|
|
return {
|
|
// MATH functions
|
|
isNaN: 'Number.isNaN',
|
|
isFinite: 'Number.isFinite',
|
|
abs: 'Math.abs',
|
|
acos: 'Math.acos',
|
|
asin: 'Math.asin',
|
|
atan: 'Math.atan',
|
|
atan2: 'Math.atan2',
|
|
ceil: 'Math.ceil',
|
|
cos: 'Math.cos',
|
|
exp: 'Math.exp',
|
|
floor: 'Math.floor',
|
|
log: 'Math.log',
|
|
max: 'Math.max',
|
|
min: 'Math.min',
|
|
pow: 'Math.pow',
|
|
random: 'Math.random',
|
|
round: 'Math.round',
|
|
sin: 'Math.sin',
|
|
sqrt: 'Math.sqrt',
|
|
tan: 'Math.tan',
|
|
|
|
clamp: function(args) {
|
|
if (args.length < 3) vegaUtil.error('Missing arguments to clamp function.');
|
|
if (args.length > 3) vegaUtil.error('Too many arguments to clamp function.');
|
|
var a = args.map(codegen);
|
|
return 'Math.max('+a[1]+', Math.min('+a[2]+','+a[0]+'))';
|
|
},
|
|
|
|
// DATE functions
|
|
now: 'Date.now',
|
|
utc: 'Date.UTC',
|
|
datetime: DATE,
|
|
date: fn('getDate', DATE, 0),
|
|
day: fn('getDay', DATE, 0),
|
|
year: fn('getFullYear', DATE, 0),
|
|
month: fn('getMonth', DATE, 0),
|
|
hours: fn('getHours', DATE, 0),
|
|
minutes: fn('getMinutes', DATE, 0),
|
|
seconds: fn('getSeconds', DATE, 0),
|
|
milliseconds: fn('getMilliseconds', DATE, 0),
|
|
time: fn('getTime', DATE, 0),
|
|
timezoneoffset: fn('getTimezoneOffset', DATE, 0),
|
|
utcdate: fn('getUTCDate', DATE, 0),
|
|
utcday: fn('getUTCDay', DATE, 0),
|
|
utcyear: fn('getUTCFullYear', DATE, 0),
|
|
utcmonth: fn('getUTCMonth', DATE, 0),
|
|
utchours: fn('getUTCHours', DATE, 0),
|
|
utcminutes: fn('getUTCMinutes', DATE, 0),
|
|
utcseconds: fn('getUTCSeconds', DATE, 0),
|
|
utcmilliseconds: fn('getUTCMilliseconds', DATE, 0),
|
|
|
|
// sequence functions
|
|
length: fn('length', null, -1),
|
|
join: fn('join', null),
|
|
indexof: fn('indexOf', null),
|
|
lastindexof: fn('lastIndexOf', null),
|
|
slice: fn('slice', null),
|
|
|
|
reverse: function(args) {
|
|
return '('+codegen(args[0])+').slice().reverse()';
|
|
},
|
|
|
|
// STRING functions
|
|
parseFloat: 'parseFloat',
|
|
parseInt: 'parseInt',
|
|
upper: fn('toUpperCase', STRING, 0),
|
|
lower: fn('toLowerCase', STRING, 0),
|
|
substring: fn('substring', STRING),
|
|
split: fn('split', STRING),
|
|
replace: fn('replace', STRING),
|
|
trim: fn('trim', STRING, 0),
|
|
|
|
// REGEXP functions
|
|
regexp: REGEXP,
|
|
test: fn('test', REGEXP),
|
|
|
|
// Control Flow functions
|
|
if: function(args) {
|
|
if (args.length < 3) vegaUtil.error('Missing arguments to if function.');
|
|
if (args.length > 3) vegaUtil.error('Too many arguments to if function.');
|
|
var a = args.map(codegen);
|
|
return '('+a[0]+'?'+a[1]+':'+a[2]+')';
|
|
}
|
|
};
|
|
}
|
|
|
|
function stripQuotes(s) {
|
|
var n = s && s.length - 1;
|
|
return n && (
|
|
(s[0]==='"' && s[n]==='"') ||
|
|
(s[0]==='\'' && s[n]==='\'')
|
|
) ? s.slice(1, -1) : s;
|
|
}
|
|
|
|
function codegen(opt) {
|
|
opt = opt || {};
|
|
|
|
var whitelist = opt.whitelist ? vegaUtil.toSet(opt.whitelist) : {},
|
|
blacklist = opt.blacklist ? vegaUtil.toSet(opt.blacklist) : {},
|
|
constants = opt.constants || Constants,
|
|
functions = (opt.functions || Functions)(visit),
|
|
globalvar = opt.globalvar,
|
|
fieldvar = opt.fieldvar,
|
|
globals = {},
|
|
fields = {},
|
|
memberDepth = 0;
|
|
|
|
var outputGlobal = vegaUtil.isFunction(globalvar)
|
|
? globalvar
|
|
: function (id) { return globalvar + '["' + id + '"]'; };
|
|
|
|
function visit(ast) {
|
|
if (vegaUtil.isString(ast)) return ast;
|
|
var generator = Generators[ast.type];
|
|
if (generator == null) vegaUtil.error('Unsupported type: ' + ast.type);
|
|
return generator(ast);
|
|
}
|
|
|
|
var Generators = {
|
|
Literal: function(n) {
|
|
return n.raw;
|
|
},
|
|
|
|
Identifier: function(n) {
|
|
var id = n.name;
|
|
if (memberDepth > 0) {
|
|
return id;
|
|
} else if (vegaUtil.hasOwnProperty(blacklist, id)) {
|
|
return vegaUtil.error('Illegal identifier: ' + id);
|
|
} else if (vegaUtil.hasOwnProperty(constants, id)) {
|
|
return constants[id];
|
|
} else if (vegaUtil.hasOwnProperty(whitelist, id)) {
|
|
return id;
|
|
} else {
|
|
globals[id] = 1;
|
|
return outputGlobal(id);
|
|
}
|
|
},
|
|
|
|
MemberExpression: function(n) {
|
|
var d = !n.computed;
|
|
var o = visit(n.object);
|
|
if (d) memberDepth += 1;
|
|
var p = visit(n.property);
|
|
if (o === fieldvar) {
|
|
// strip quotes to sanitize field name (#1653)
|
|
fields[stripQuotes(p)] = 1;
|
|
}
|
|
if (d) memberDepth -= 1;
|
|
return o + (d ? '.'+p : '['+p+']');
|
|
},
|
|
|
|
CallExpression: function(n) {
|
|
if (n.callee.type !== 'Identifier') {
|
|
vegaUtil.error('Illegal callee type: ' + n.callee.type);
|
|
}
|
|
var callee = n.callee.name;
|
|
var args = n.arguments;
|
|
var fn = vegaUtil.hasOwnProperty(functions, callee) && functions[callee];
|
|
if (!fn) vegaUtil.error('Unrecognized function: ' + callee);
|
|
return vegaUtil.isFunction(fn)
|
|
? fn(args)
|
|
: fn + '(' + args.map(visit).join(',') + ')';
|
|
},
|
|
|
|
ArrayExpression: function(n) {
|
|
return '[' + n.elements.map(visit).join(',') + ']';
|
|
},
|
|
|
|
BinaryExpression: function(n) {
|
|
return '(' + visit(n.left) + n.operator + visit(n.right) + ')';
|
|
},
|
|
|
|
UnaryExpression: function(n) {
|
|
return '(' + n.operator + visit(n.argument) + ')';
|
|
},
|
|
|
|
ConditionalExpression: function(n) {
|
|
return '(' + visit(n.test) +
|
|
'?' + visit(n.consequent) +
|
|
':' + visit(n.alternate) +
|
|
')';
|
|
},
|
|
|
|
LogicalExpression: function(n) {
|
|
return '(' + visit(n.left) + n.operator + visit(n.right) + ')';
|
|
},
|
|
|
|
ObjectExpression: function(n) {
|
|
return '{' + n.properties.map(visit).join(',') + '}';
|
|
},
|
|
|
|
Property: function(n) {
|
|
memberDepth += 1;
|
|
var k = visit(n.key);
|
|
memberDepth -= 1;
|
|
return k + ':' + visit(n.value);
|
|
}
|
|
};
|
|
|
|
function codegen(ast) {
|
|
var result = {
|
|
code: visit(ast),
|
|
globals: Object.keys(globals),
|
|
fields: Object.keys(fields)
|
|
};
|
|
globals = {};
|
|
fields = {};
|
|
return result;
|
|
}
|
|
|
|
codegen.functions = functions;
|
|
codegen.constants = constants;
|
|
|
|
return codegen;
|
|
}
|
|
|
|
exports.ASTNode = ASTNode;
|
|
exports.ArrayExpression = ArrayExpression;
|
|
exports.BinaryExpression = BinaryExpression;
|
|
exports.CallExpression = CallExpression;
|
|
exports.ConditionalExpression = ConditionalExpression;
|
|
exports.Identifier = Identifier;
|
|
exports.Literal = Literal;
|
|
exports.LogicalExpression = LogicalExpression;
|
|
exports.MemberExpression = MemberExpression;
|
|
exports.ObjectExpression = ObjectExpression;
|
|
exports.Property = Property;
|
|
exports.RawCode = RawCode;
|
|
exports.UnaryExpression = UnaryExpression;
|
|
exports.codegen = codegen;
|
|
exports.constants = Constants;
|
|
exports.functions = Functions;
|
|
exports.parse = parser;
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
})));
|
|
|