| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860 |
- (function (global, factory) {
- (global = global || self, factory(global.exprEval = {}));
- }(this, (function (exports) {
- 'use strict';
- var INUMBER = 'INUMBER';
- var IOP1 = 'IOP1';
- var IOP2 = 'IOP2';
- var IOP3 = 'IOP3';
- var IVAR = 'IVAR';
- var IVARNAME = 'IVARNAME';
- var IFUNCALL = 'IFUNCALL';
- var IFUNDEF = 'IFUNDEF';
- var IEXPR = 'IEXPR';
- var IEXPREVAL = 'IEXPREVAL';
- var IMEMBER = 'IMEMBER';
- var IENDSTATEMENT = 'IENDSTATEMENT';
- var IARRAY = 'IARRAY';
- function Instruction(type, value) {
- this.type = type;
- this.value = (value !== undefined && value !== null) ? value : 0;
- }
- Instruction.prototype.toString = function () {
- switch (this.type) {
- case INUMBER:
- case IOP1:
- case IOP2:
- case IOP3:
- case IVAR:
- case IVARNAME:
- case IENDSTATEMENT:
- return this.value;
- case IFUNCALL:
- return 'CALL ' + this.value;
- case IFUNDEF:
- return 'DEF ' + this.value;
- case IARRAY:
- return 'ARRAY ' + this.value;
- case IMEMBER:
- return '.' + this.value;
- default:
- return 'Invalid Instruction';
- }
- };
- function unaryInstruction(value) {
- return new Instruction(IOP1, value);
- }
- function binaryInstruction(value) {
- return new Instruction(IOP2, value);
- }
- function ternaryInstruction(value) {
- return new Instruction(IOP3, value);
- }
- function simplify(tokens, unaryOps, binaryOps, ternaryOps, values) {
- var nstack = [];
- var newexpression = [];
- var n1, n2, n3;
- var f;
- for (var i = 0; i < tokens.length; i++) {
- var item = tokens[i];
- var type = item.type;
- if (type === INUMBER || type === IVARNAME) {
- if (Array.isArray(item.value)) {
- nstack.push.apply(nstack, simplify(item.value.map(function (x) {
- return new Instruction(INUMBER, x);
- }).concat(new Instruction(IARRAY, item.value.length)), unaryOps, binaryOps, ternaryOps, values));
- } else {
- nstack.push(item);
- }
- } else if (type === IVAR && values.hasOwnProperty(item.value)) {
- item = new Instruction(INUMBER, values[item.value]);
- nstack.push(item);
- } else if (type === IOP2 && nstack.length > 1) {
- n2 = nstack.pop();
- n1 = nstack.pop();
- f = binaryOps[item.value];
- item = new Instruction(INUMBER, f(n1.value, n2.value));
- nstack.push(item);
- } else if (type === IOP3 && nstack.length > 2) {
- n3 = nstack.pop();
- n2 = nstack.pop();
- n1 = nstack.pop();
- if (item.value === '?') {
- nstack.push(n1.value ? n2.value : n3.value);
- } else {
- f = ternaryOps[item.value];
- item = new Instruction(INUMBER, f(n1.value, n2.value, n3.value));
- nstack.push(item);
- }
- } else if (type === IOP1 && nstack.length > 0) {
- n1 = nstack.pop();
- f = unaryOps[item.value];
- item = new Instruction(INUMBER, f(n1.value));
- nstack.push(item);
- } else if (type === IEXPR) {
- while (nstack.length > 0) {
- newexpression.push(nstack.shift());
- }
- newexpression.push(new Instruction(IEXPR, simplify(item.value, unaryOps, binaryOps, ternaryOps, values)));
- } else if (type === IMEMBER && nstack.length > 0) {
- n1 = nstack.pop();
- nstack.push(new Instruction(INUMBER, n1.value[item.value]));
- } /* else if (type === IARRAY && nstack.length >= item.value) {
- var length = item.value;
- while (length-- > 0) {
- newexpression.push(nstack.pop());
- }
- newexpression.push(new Instruction(IARRAY, item.value));
- } */ else {
- while (nstack.length > 0) {
- newexpression.push(nstack.shift());
- }
- newexpression.push(item);
- }
- }
- while (nstack.length > 0) {
- newexpression.push(nstack.shift());
- }
- return newexpression;
- }
- function substitute(tokens, variable, expr) {
- var newexpression = [];
- for (var i = 0; i < tokens.length; i++) {
- var item = tokens[i];
- var type = item.type;
- if (type === IVAR && item.value === variable) {
- for (var j = 0; j < expr.tokens.length; j++) {
- var expritem = expr.tokens[j];
- var replitem;
- if (expritem.type === IOP1) {
- replitem = unaryInstruction(expritem.value);
- } else if (expritem.type === IOP2) {
- replitem = binaryInstruction(expritem.value);
- } else if (expritem.type === IOP3) {
- replitem = ternaryInstruction(expritem.value);
- } else {
- replitem = new Instruction(expritem.type, expritem.value);
- }
- newexpression.push(replitem);
- }
- } else if (type === IEXPR) {
- newexpression.push(new Instruction(IEXPR, substitute(item.value, variable, expr)));
- } else {
- newexpression.push(item);
- }
- }
- return newexpression;
- }
- function evaluate(tokens, expr, values) {
- var nstack = [];
- var n1, n2, n3;
- var f, args, argCount;
- if (isExpressionEvaluator(tokens)) {
- return resolveExpression(tokens, values);
- }
- var numTokens = tokens.length;
- for (var i = 0; i < numTokens; i++) {
- var item = tokens[i];
- var type = item.type;
- if (type === INUMBER || type === IVARNAME) {
- nstack.push(item.value);
- } else if (type === IOP2) {
- n2 = nstack.pop();
- n1 = nstack.pop();
- if (item.value === 'and') {
- nstack.push(n1 ? !!evaluate(n2, expr, values) : false);
- } else if (item.value === 'or') {
- nstack.push(n1 ? true : !!evaluate(n2, expr, values));
- } else if (item.value === '=') {
- f = expr.binaryOps[item.value];
- nstack.push(f(n1, evaluate(n2, expr, values), values));
- } else {
- f = expr.binaryOps[item.value];
- nstack.push(f(resolveExpression(n1, values), resolveExpression(n2, values)));
- }
- } else if (type === IOP3) {
- n3 = nstack.pop();
- n2 = nstack.pop();
- n1 = nstack.pop();
- if (item.value === '?') {
- nstack.push(evaluate(n1 ? n2 : n3, expr, values));
- } else {
- f = expr.ternaryOps[item.value];
- nstack.push(f(resolveExpression(n1, values), resolveExpression(n2, values), resolveExpression(n3, values)));
- }
- } else if (type === IVAR) {
- if (item.value in expr.functions) {
- nstack.push(expr.functions[item.value]);
- } else if (item.value in expr.unaryOps && expr.parser.isOperatorEnabled(item.value)) {
- nstack.push(expr.unaryOps[item.value]);
- } else {
- var v = values[item.value];
- if (v !== undefined) {
- nstack.push(v);
- } else {
- throw new Error('undefined variable: ' + item.value);
- }
- }
- } else if (type === IOP1) {
- n1 = nstack.pop();
- f = expr.unaryOps[item.value];
- nstack.push(f(resolveExpression(n1, values)));
- } else if (type === IFUNCALL) {
- argCount = item.value;
- args = [];
- while (argCount-- > 0) {
- args.unshift(resolveExpression(nstack.pop(), values));
- }
- f = nstack.pop();
- if (f.apply && f.call) {
- nstack.push(f.apply(undefined, args));
- } else {
- throw new Error(f + ' is not a function');
- }
- } else if (type === IFUNDEF) {
- // Create closure to keep references to arguments and expression
- nstack.push((function () {
- var n2 = nstack.pop();
- var args = [];
- var argCount = item.value;
- while (argCount-- > 0) {
- args.unshift(nstack.pop());
- }
- var n1 = nstack.pop();
- var f = function () {
- var scope = Object.assign({}, values);
- for (var i = 0, len = args.length; i < len; i++) {
- scope[args[i]] = arguments[i];
- }
- return evaluate(n2, expr, scope);
- };
- // f.name = n1
- Object.defineProperty(f, 'name', {
- value: n1,
- writable: false
- });
- values[n1] = f;
- return f;
- })());
- } else if (type === IEXPR) {
- nstack.push(createExpressionEvaluator(item, expr));
- } else if (type === IEXPREVAL) {
- nstack.push(item);
- } else if (type === IMEMBER) {
- n1 = nstack.pop();
- nstack.push(n1[item.value]);
- } else if (type === IENDSTATEMENT) {
- nstack.pop();
- } else if (type === IARRAY) {
- argCount = item.value;
- args = [];
- while (argCount-- > 0) {
- args.unshift(nstack.pop());
- }
- nstack.push(args);
- } else {
- throw new Error('invalid Expression');
- }
- }
- if (nstack.length > 1) {
- throw new Error('invalid Expression (parity)');
- }
- // Explicitly return zero to avoid test issues caused by -0
- return nstack[0] === 0 ? 0 : resolveExpression(nstack[0], values);
- }
- function createExpressionEvaluator(token, expr, values) {
- if (isExpressionEvaluator(token)) return token;
- return {
- type: IEXPREVAL,
- value: function (scope) {
- return evaluate(token.value, expr, scope);
- }
- };
- }
- function isExpressionEvaluator(n) {
- return n && n.type === IEXPREVAL;
- }
- function resolveExpression(n, values) {
- return isExpressionEvaluator(n) ? n.value(values) : n;
- }
- function expressionToString(tokens, toJS) {
- var nstack = [];
- var n1, n2, n3;
- var f, args, argCount;
- for (var i = 0; i < tokens.length; i++) {
- var item = tokens[i];
- var type = item.type;
- if (type === INUMBER) {
- if (typeof item.value === 'number' && item.value < 0) {
- nstack.push('(' + item.value + ')');
- } else if (Array.isArray(item.value)) {
- nstack.push('[' + item.value.map(escapeValue).join(', ') + ']');
- } else {
- nstack.push(escapeValue(item.value));
- }
- } else if (type === IOP2) {
- n2 = nstack.pop();
- n1 = nstack.pop();
- f = item.value;
- if (toJS) {
- if (f === '^') {
- nstack.push('Math.pow(' + n1 + ', ' + n2 + ')');
- } else if (f === 'and') {
- nstack.push('(!!' + n1 + ' && !!' + n2 + ')');
- } else if (f === 'or') {
- nstack.push('(!!' + n1 + ' || !!' + n2 + ')');
- } else if (f === '||') {
- nstack.push('(function(a,b){ return Array.isArray(a) && Array.isArray(b) ? a.concat(b) : String(a) + String(b); }((' + n1 + '),(' + n2 + ')))');
- } else if (f === '==') {
- nstack.push('(' + n1 + ' === ' + n2 + ')');
- } else if (f === '!=') {
- nstack.push('(' + n1 + ' !== ' + n2 + ')');
- } else if (f === '[') {
- nstack.push(n1 + '[(' + n2 + ') | 0]');
- } else {
- nstack.push('(' + n1 + ' ' + f + ' ' + n2 + ')');
- }
- } else {
- if (f === '[') {
- nstack.push(n1 + '[' + n2 + ']');
- } else {
- nstack.push('(' + n1 + ' ' + f + ' ' + n2 + ')');
- }
- }
- } else if (type === IOP3) {
- n3 = nstack.pop();
- n2 = nstack.pop();
- n1 = nstack.pop();
- f = item.value;
- if (f === '?') {
- nstack.push('(' + n1 + ' ? ' + n2 + ' : ' + n3 + ')');
- } else {
- throw new Error('invalid Expression');
- }
- } else if (type === IVAR || type === IVARNAME) {
- nstack.push(item.value);
- } else if (type === IOP1) {
- n1 = nstack.pop();
- f = item.value;
- if (f === '-' || f === '+') {
- nstack.push('(' + f + n1 + ')');
- } else if (toJS) {
- if (f === 'not') {
- nstack.push('(' + '!' + n1 + ')');
- } else if (f === '!') {
- nstack.push('fac(' + n1 + ')');
- } else {
- nstack.push(f + '(' + n1 + ')');
- }
- } else if (f === '!') {
- nstack.push('(' + n1 + '!)');
- } else {
- nstack.push('(' + f + ' ' + n1 + ')');
- }
- } else if (type === IFUNCALL) {
- argCount = item.value;
- args = [];
- while (argCount-- > 0) {
- args.unshift(nstack.pop());
- }
- f = nstack.pop();
- nstack.push(f + '(' + args.join(', ') + ')');
- } else if (type === IFUNDEF) {
- n2 = nstack.pop();
- argCount = item.value;
- args = [];
- while (argCount-- > 0) {
- args.unshift(nstack.pop());
- }
- n1 = nstack.pop();
- if (toJS) {
- nstack.push('(' + n1 + ' = function(' + args.join(', ') + ') { return ' + n2 + ' })');
- } else {
- nstack.push('(' + n1 + '(' + args.join(', ') + ') = ' + n2 + ')');
- }
- } else if (type === IMEMBER) {
- n1 = nstack.pop();
- nstack.push(n1 + '.' + item.value);
- } else if (type === IARRAY) {
- argCount = item.value;
- args = [];
- while (argCount-- > 0) {
- args.unshift(nstack.pop());
- }
- nstack.push('[' + args.join(', ') + ']');
- } else if (type === IEXPR) {
- nstack.push('(' + expressionToString(item.value, toJS) + ')');
- } else if (type === IENDSTATEMENT); else {
- throw new Error('invalid Expression');
- }
- }
- if (nstack.length > 1) {
- if (toJS) {
- nstack = [nstack.join(',')];
- } else {
- nstack = [nstack.join(';')];
- }
- }
- return String(nstack[0]);
- }
- function escapeValue(v) {
- if (typeof v === 'string') {
- return JSON.stringify(v).replace(/\u2028/g, '\\u2028').replace(/\u2029/g, '\\u2029');
- }
- return v;
- }
- function contains(array, obj) {
- for (var i = 0; i < array.length; i++) {
- if (array[i] === obj) {
- return true;
- }
- }
- return false;
- }
- function getSymbols(tokens, symbols, options) {
- options = options || {};
- var withMembers = !!options.withMembers;
- var prevVar = null;
- for (var i = 0; i < tokens.length; i++) {
- var item = tokens[i];
- if (item.type === IVAR || item.type === IVARNAME) {
- if (!withMembers && !contains(symbols, item.value)) {
- symbols.push(item.value);
- } else if (prevVar !== null) {
- if (!contains(symbols, prevVar)) {
- symbols.push(prevVar);
- }
- prevVar = item.value;
- } else {
- prevVar = item.value;
- }
- } else if (item.type === IMEMBER && withMembers && prevVar !== null) {
- prevVar += '.' + item.value;
- } else if (item.type === IEXPR) {
- getSymbols(item.value, symbols, options);
- } else if (prevVar !== null) {
- if (!contains(symbols, prevVar)) {
- symbols.push(prevVar);
- }
- prevVar = null;
- }
- }
- if (prevVar !== null && !contains(symbols, prevVar)) {
- symbols.push(prevVar);
- }
- }
- function Expression(tokens, parser) {
- this.tokens = tokens;
- this.parser = parser;
- this.unaryOps = parser.unaryOps;
- this.binaryOps = parser.binaryOps;
- this.ternaryOps = parser.ternaryOps;
- this.functions = parser.functions;
- }
- Expression.prototype.simplify = function (values) {
- values = values || {};
- return new Expression(simplify(this.tokens, this.unaryOps, this.binaryOps, this.ternaryOps, values), this.parser);
- };
- Expression.prototype.substitute = function (variable, expr) {
- if (!(expr instanceof Expression)) {
- expr = this.parser.parse(String(expr));
- }
- return new Expression(substitute(this.tokens, variable, expr), this.parser);
- };
- Expression.prototype.evaluate = function (values) {
- values = values || {};
- return evaluate(this.tokens, this, values);
- };
- Expression.prototype.toString = function () {
- return expressionToString(this.tokens, false);
- };
- Expression.prototype.symbols = function (options) {
- options = options || {};
- var vars = [];
- getSymbols(this.tokens, vars, options);
- return vars;
- };
- Expression.prototype.variables = function (options) {
- options = options || {};
- var vars = [];
- getSymbols(this.tokens, vars, options);
- var functions = this.functions;
- return vars.filter(function (name) {
- return !(name in functions);
- });
- };
- Expression.prototype.toJSFunction = function (param, variables) {
- var expr = this;
- var f = new Function(param, 'with(this.functions) with (this.ternaryOps) with (this.binaryOps) with (this.unaryOps) { return ' + expressionToString(this.simplify(variables).tokens, true) + '; }'); // eslint-disable-line no-new-func
- return function () {
- return f.apply(expr, arguments);
- };
- };
- var TEOF = 'TEOF';
- var TOP = 'TOP';
- var TNUMBER = 'TNUMBER';
- var TSTRING = 'TSTRING';
- var TPAREN = 'TPAREN';
- var TBRACKET = 'TBRACKET';
- var TCOMMA = 'TCOMMA';
- var TNAME = 'TNAME';
- var TSEMICOLON = 'TSEMICOLON';
- function Token(type, value, index) {
- this.type = type;
- this.value = value;
- this.index = index;
- }
- Token.prototype.toString = function () {
- return this.type + ': ' + this.value;
- };
- function TokenStream(parser, expression) {
- this.pos = 0;
- this.current = null;
- this.unaryOps = parser.unaryOps;
- this.binaryOps = parser.binaryOps;
- this.ternaryOps = parser.ternaryOps;
- this.consts = parser.consts;
- this.expression = expression;
- this.savedPosition = 0;
- this.savedCurrent = null;
- this.options = parser.options;
- this.parser = parser;
- }
- TokenStream.prototype.newToken = function (type, value, pos) {
- return new Token(type, value, pos != null ? pos : this.pos);
- };
- TokenStream.prototype.save = function () {
- this.savedPosition = this.pos;
- this.savedCurrent = this.current;
- };
- TokenStream.prototype.restore = function () {
- this.pos = this.savedPosition;
- this.current = this.savedCurrent;
- };
- TokenStream.prototype.next = function () {
- if (this.pos >= this.expression.length) {
- return this.newToken(TEOF, 'EOF');
- }
- if (this.isWhitespace() || this.isComment()) {
- return this.next();
- } else if (this.isRadixInteger() ||
- this.isNumber() ||
- this.isOperator() ||
- this.isString() ||
- this.isParen() ||
- this.isBracket() ||
- this.isComma() ||
- this.isSemicolon() ||
- this.isNamedOp() ||
- this.isConst() ||
- this.isName()) {
- return this.current;
- } else {
- this.parseError('Unknown character "' + this.expression.charAt(this.pos) + '"');
- }
- };
- TokenStream.prototype.isString = function () {
- var r = false;
- var startPos = this.pos;
- var quote = this.expression.charAt(startPos);
- if (quote === '\'' || quote === '"') {
- var index = this.expression.indexOf(quote, startPos + 1);
- while (index >= 0 && this.pos < this.expression.length) {
- this.pos = index + 1;
- if (this.expression.charAt(index - 1) !== '\\') {
- var rawString = this.expression.substring(startPos + 1, index);
- this.current = this.newToken(TSTRING, this.unescape(rawString), startPos);
- r = true;
- break;
- }
- index = this.expression.indexOf(quote, index + 1);
- }
- }
- return r;
- };
- TokenStream.prototype.isParen = function () {
- var c = this.expression.charAt(this.pos);
- if (c === '(' || c === ')') {
- this.current = this.newToken(TPAREN, c);
- this.pos++;
- return true;
- }
- return false;
- };
- TokenStream.prototype.isBracket = function () {
- var c = this.expression.charAt(this.pos);
- if ((c === '[' || c === ']') && this.isOperatorEnabled('[')) {
- this.current = this.newToken(TBRACKET, c);
- this.pos++;
- return true;
- }
- return false;
- };
- TokenStream.prototype.isComma = function () {
- var c = this.expression.charAt(this.pos);
- if (c === ',') {
- this.current = this.newToken(TCOMMA, ',');
- this.pos++;
- return true;
- }
- return false;
- };
- TokenStream.prototype.isSemicolon = function () {
- var c = this.expression.charAt(this.pos);
- if (c === ';') {
- this.current = this.newToken(TSEMICOLON, ';');
- this.pos++;
- return true;
- }
- return false;
- };
- TokenStream.prototype.isConst = function () {
- var startPos = this.pos;
- var i = startPos;
- for (; i < this.expression.length; i++) {
- var c = this.expression.charAt(i);
- if (c.toUpperCase() === c.toLowerCase()) {
- if (i === this.pos || (c !== '_' && c !== '.' && (c < '0' || c > '9'))) {
- break;
- }
- }
- }
- if (i > startPos) {
- var str = this.expression.substring(startPos, i);
- if (str in this.consts) {
- this.current = this.newToken(TNUMBER, this.consts[str]);
- this.pos += str.length;
- return true;
- }
- }
- return false;
- };
- TokenStream.prototype.isNamedOp = function () {
- var startPos = this.pos;
- var i = startPos;
- for (; i < this.expression.length; i++) {
- var c = this.expression.charAt(i);
- if (c.toUpperCase() === c.toLowerCase()) {
- if (i === this.pos || (c !== '_' && (c < '0' || c > '9'))) {
- break;
- }
- }
- }
- if (i > startPos) {
- var str = this.expression.substring(startPos, i);
- if (this.isOperatorEnabled(str) && (str in this.binaryOps || str in this.unaryOps || str in this.ternaryOps)) {
- this.current = this.newToken(TOP, str);
- this.pos += str.length;
- return true;
- }
- }
- return false;
- };
- TokenStream.prototype.isName = function () {
- var startPos = this.pos;
- var i = startPos;
- var hasLetter = false;
- for (; i < this.expression.length; i++) {
- var c = this.expression.charAt(i);
- if (c.toUpperCase() === c.toLowerCase()) {
- if (i === this.pos && (c === '$' || c === '_')) {
- if (c === '_') {
- hasLetter = true;
- }
- continue;
- } else if (i === this.pos || !hasLetter || (c !== '_' && (c < '0' || c > '9'))) {
- break;
- }
- } else {
- hasLetter = true;
- }
- }
- if (hasLetter) {
- var str = this.expression.substring(startPos, i);
- this.current = this.newToken(TNAME, str);
- this.pos += str.length;
- return true;
- }
- return false;
- };
- TokenStream.prototype.isWhitespace = function () {
- var r = false;
- var c = this.expression.charAt(this.pos);
- while (c === ' ' || c === '\t' || c === '\n' || c === '\r') {
- r = true;
- this.pos++;
- if (this.pos >= this.expression.length) {
- break;
- }
- c = this.expression.charAt(this.pos);
- }
- return r;
- };
- var codePointPattern = /^[0-9a-f]{4}$/i;
- TokenStream.prototype.unescape = function (v) {
- var index = v.indexOf('\\');
- if (index < 0) {
- return v;
- }
- var buffer = v.substring(0, index);
- while (index >= 0) {
- var c = v.charAt(++index);
- switch (c) {
- case '\'':
- buffer += '\'';
- break;
- case '"':
- buffer += '"';
- break;
- case '\\':
- buffer += '\\';
- break;
- case '/':
- buffer += '/';
- break;
- case 'b':
- buffer += '\b';
- break;
- case 'f':
- buffer += '\f';
- break;
- case 'n':
- buffer += '\n';
- break;
- case 'r':
- buffer += '\r';
- break;
- case 't':
- buffer += '\t';
- break;
- case 'u':
- // interpret the following 4 characters as the hex of the unicode code point
- var codePoint = v.substring(index + 1, index + 5);
- if (!codePointPattern.test(codePoint)) {
- this.parseError('Illegal escape sequence: \\u' + codePoint);
- }
- buffer += String.fromCharCode(parseInt(codePoint, 16));
- index += 4;
- break;
- default:
- throw this.parseError('Illegal escape sequence: "\\' + c + '"');
- }
- ++index;
- var backslash = v.indexOf('\\', index);
- buffer += v.substring(index, backslash < 0 ? v.length : backslash);
- index = backslash;
- }
- return buffer;
- };
- TokenStream.prototype.isComment = function () {
- var c = this.expression.charAt(this.pos);
- if (c === '/' && this.expression.charAt(this.pos + 1) === '*') {
- this.pos = this.expression.indexOf('*/', this.pos) + 2;
- if (this.pos === 1) {
- this.pos = this.expression.length;
- }
- return true;
- }
- return false;
- };
- TokenStream.prototype.isRadixInteger = function () {
- var pos = this.pos;
- if (pos >= this.expression.length - 2 || this.expression.charAt(pos) !== '0') {
- return false;
- }
- ++pos;
- var radix;
- var validDigit;
- if (this.expression.charAt(pos) === 'x') {
- radix = 16;
- validDigit = /^[0-9a-f]$/i;
- ++pos;
- } else if (this.expression.charAt(pos) === 'b') {
- radix = 2;
- validDigit = /^[01]$/i;
- ++pos;
- } else {
- return false;
- }
- var valid = false;
- var startPos = pos;
- while (pos < this.expression.length) {
- var c = this.expression.charAt(pos);
- if (validDigit.test(c)) {
- pos++;
- valid = true;
- } else {
- break;
- }
- }
- if (valid) {
- this.current = this.newToken(TNUMBER, parseInt(this.expression.substring(startPos, pos), radix));
- this.pos = pos;
- }
- return valid;
- };
- TokenStream.prototype.isNumber = function () {
- var valid = false;
- var pos = this.pos;
- var startPos = pos;
- var resetPos = pos;
- var foundDot = false;
- var foundDigits = false;
- var c;
- while (pos < this.expression.length) {
- c = this.expression.charAt(pos);
- if ((c >= '0' && c <= '9') || (!foundDot && c === '.')) {
- if (c === '.') {
- foundDot = true;
- } else {
- foundDigits = true;
- }
- pos++;
- valid = foundDigits;
- } else {
- break;
- }
- }
- if (valid) {
- resetPos = pos;
- }
- if (c === 'e' || c === 'E') {
- pos++;
- var acceptSign = true;
- var validExponent = false;
- while (pos < this.expression.length) {
- c = this.expression.charAt(pos);
- if (acceptSign && (c === '+' || c === '-')) {
- acceptSign = false;
- } else if (c >= '0' && c <= '9') {
- validExponent = true;
- acceptSign = false;
- } else {
- break;
- }
- pos++;
- }
- if (!validExponent) {
- pos = resetPos;
- }
- }
- if (valid) {
- this.current = this.newToken(TNUMBER, parseFloat(this.expression.substring(startPos, pos)));
- this.pos = pos;
- } else {
- this.pos = resetPos;
- }
- return valid;
- };
- TokenStream.prototype.isOperator = function () {
- var startPos = this.pos;
- var c = this.expression.charAt(this.pos);
- if (c === '+' || c === '-' || c === '*' || c === '/' || c === '%' || c === '^' || c === '?' || c === ':' || c === '.') {
- this.current = this.newToken(TOP, c);
- } else if (c === '∙' || c === '•') {
- this.current = this.newToken(TOP, '*');
- } else if (c === '>') {
- if (this.expression.charAt(this.pos + 1) === '=') {
- this.current = this.newToken(TOP, '>=');
- this.pos++;
- } else {
- this.current = this.newToken(TOP, '>');
- }
- } else if (c === '<') {
- if (this.expression.charAt(this.pos + 1) === '=') {
- this.current = this.newToken(TOP, '<=');
- this.pos++;
- } else {
- this.current = this.newToken(TOP, '<');
- }
- } else if (c === '|') {
- if (this.expression.charAt(this.pos + 1) === '|') {
- this.current = this.newToken(TOP, '||');
- this.pos++;
- } else {
- return false;
- }
- } else if (c === '=') {
- if (this.expression.charAt(this.pos + 1) === '=') {
- this.current = this.newToken(TOP, '==');
- this.pos++;
- } else {
- this.current = this.newToken(TOP, c);
- }
- } else if (c === '!') {
- if (this.expression.charAt(this.pos + 1) === '=') {
- this.current = this.newToken(TOP, '!=');
- this.pos++;
- } else {
- this.current = this.newToken(TOP, c);
- }
- } else {
- return false;
- }
- this.pos++;
- if (this.isOperatorEnabled(this.current.value)) {
- return true;
- } else {
- this.pos = startPos;
- return false;
- }
- };
- TokenStream.prototype.isOperatorEnabled = function (op) {
- return this.parser.isOperatorEnabled(op);
- };
- TokenStream.prototype.getCoordinates = function () {
- var line = 0;
- var column;
- var newline = -1;
- do {
- line++;
- column = this.pos - newline;
- newline = this.expression.indexOf('\n', newline + 1);
- } while (newline >= 0 && newline < this.pos);
- return {
- line: line,
- column: column
- };
- };
- TokenStream.prototype.parseError = function (msg) {
- var coords = this.getCoordinates();
- throw new Error('parse error [' + coords.line + ':' + coords.column + ']: ' + msg);
- };
- function ParserState(parser, tokenStream, options) {
- this.parser = parser;
- this.tokens = tokenStream;
- this.current = null;
- this.nextToken = null;
- this.next();
- this.savedCurrent = null;
- this.savedNextToken = null;
- this.allowMemberAccess = options.allowMemberAccess !== false;
- }
- ParserState.prototype.next = function () {
- this.current = this.nextToken;
- return (this.nextToken = this.tokens.next());
- };
- ParserState.prototype.tokenMatches = function (token, value) {
- if (typeof value === 'undefined') {
- return true;
- } else if (Array.isArray(value)) {
- return contains(value, token.value);
- } else if (typeof value === 'function') {
- return value(token);
- } else {
- return token.value === value;
- }
- };
- ParserState.prototype.save = function () {
- this.savedCurrent = this.current;
- this.savedNextToken = this.nextToken;
- this.tokens.save();
- };
- ParserState.prototype.restore = function () {
- this.tokens.restore();
- this.current = this.savedCurrent;
- this.nextToken = this.savedNextToken;
- };
- ParserState.prototype.accept = function (type, value) {
- if (this.nextToken.type === type && this.tokenMatches(this.nextToken, value)) {
- this.next();
- return true;
- }
- return false;
- };
- ParserState.prototype.expect = function (type, value) {
- if (!this.accept(type, value)) {
- var coords = this.tokens.getCoordinates();
- throw new Error('parse error [' + coords.line + ':' + coords.column + ']: Expected ' + (value || type));
- }
- };
- ParserState.prototype.parseAtom = function (instr) {
- var unaryOps = this.tokens.unaryOps;
- function isPrefixOperator(token) {
- return token.value in unaryOps;
- }
- if (this.accept(TNAME) || this.accept(TOP, isPrefixOperator)) {
- instr.push(new Instruction(IVAR, this.current.value));
- } else if (this.accept(TNUMBER)) {
- instr.push(new Instruction(INUMBER, this.current.value));
- } else if (this.accept(TSTRING)) {
- instr.push(new Instruction(INUMBER, this.current.value));
- } else if (this.accept(TPAREN, '(')) {
- this.parseExpression(instr);
- this.expect(TPAREN, ')');
- } else if (this.accept(TBRACKET, '[')) {
- if (this.accept(TBRACKET, ']')) {
- instr.push(new Instruction(IARRAY, 0));
- } else {
- var argCount = this.parseArrayList(instr);
- instr.push(new Instruction(IARRAY, argCount));
- }
- } else {
- throw new Error('unexpected ' + this.nextToken);
- }
- };
- ParserState.prototype.parseExpression = function (instr) {
- var exprInstr = [];
- if (this.parseUntilEndStatement(instr, exprInstr)) {
- return;
- }
- this.parseVariableAssignmentExpression(exprInstr);
- if (this.parseUntilEndStatement(instr, exprInstr)) {
- return;
- }
- this.pushExpression(instr, exprInstr);
- };
- ParserState.prototype.pushExpression = function (instr, exprInstr) {
- for (var i = 0, len = exprInstr.length; i < len; i++) {
- instr.push(exprInstr[i]);
- }
- };
- ParserState.prototype.parseUntilEndStatement = function (instr, exprInstr) {
- if (!this.accept(TSEMICOLON)) return false;
- if (this.nextToken && this.nextToken.type !== TEOF && !(this.nextToken.type === TPAREN && this.nextToken.value === ')')) {
- exprInstr.push(new Instruction(IENDSTATEMENT));
- }
- if (this.nextToken.type !== TEOF) {
- this.parseExpression(exprInstr);
- }
- instr.push(new Instruction(IEXPR, exprInstr));
- return true;
- };
- ParserState.prototype.parseArrayList = function (instr) {
- var argCount = 0;
- while (!this.accept(TBRACKET, ']')) {
- this.parseExpression(instr);
- ++argCount;
- while (this.accept(TCOMMA)) {
- this.parseExpression(instr);
- ++argCount;
- }
- }
- return argCount;
- };
- ParserState.prototype.parseVariableAssignmentExpression = function (instr) {
- this.parseConditionalExpression(instr);
- while (this.accept(TOP, '=')) {
- var varName = instr.pop();
- var varValue = [];
- var lastInstrIndex = instr.length - 1;
- if (varName.type === IFUNCALL) {
- if (!this.tokens.isOperatorEnabled('()=')) {
- throw new Error('function definition is not permitted');
- }
- for (var i = 0, len = varName.value + 1; i < len; i++) {
- var index = lastInstrIndex - i;
- if (instr[index].type === IVAR) {
- instr[index] = new Instruction(IVARNAME, instr[index].value);
- }
- }
- this.parseVariableAssignmentExpression(varValue);
- instr.push(new Instruction(IEXPR, varValue));
- instr.push(new Instruction(IFUNDEF, varName.value));
- continue;
- }
- if (varName.type !== IVAR && varName.type !== IMEMBER) {
- throw new Error('expected variable for assignment');
- }
- this.parseVariableAssignmentExpression(varValue);
- instr.push(new Instruction(IVARNAME, varName.value));
- instr.push(new Instruction(IEXPR, varValue));
- instr.push(binaryInstruction('='));
- }
- };
- ParserState.prototype.parseConditionalExpression = function (instr) {
- this.parseOrExpression(instr);
- while (this.accept(TOP, '?')) {
- var trueBranch = [];
- var falseBranch = [];
- this.parseConditionalExpression(trueBranch);
- this.expect(TOP, ':');
- this.parseConditionalExpression(falseBranch);
- instr.push(new Instruction(IEXPR, trueBranch));
- instr.push(new Instruction(IEXPR, falseBranch));
- instr.push(ternaryInstruction('?'));
- }
- };
- ParserState.prototype.parseOrExpression = function (instr) {
- this.parseAndExpression(instr);
- while (this.accept(TOP, 'or')) {
- var falseBranch = [];
- this.parseAndExpression(falseBranch);
- instr.push(new Instruction(IEXPR, falseBranch));
- instr.push(binaryInstruction('or'));
- }
- };
- ParserState.prototype.parseAndExpression = function (instr) {
- this.parseComparison(instr);
- while (this.accept(TOP, 'and')) {
- var trueBranch = [];
- this.parseComparison(trueBranch);
- instr.push(new Instruction(IEXPR, trueBranch));
- instr.push(binaryInstruction('and'));
- }
- };
- var COMPARISON_OPERATORS = ['==', '!=', '<', '<=', '>=', '>', 'in'];
- ParserState.prototype.parseComparison = function (instr) {
- this.parseAddSub(instr);
- while (this.accept(TOP, COMPARISON_OPERATORS)) {
- var op = this.current;
- this.parseAddSub(instr);
- instr.push(binaryInstruction(op.value));
- }
- };
- var ADD_SUB_OPERATORS = ['+', '-', '||'];
- ParserState.prototype.parseAddSub = function (instr) {
- this.parseTerm(instr);
- while (this.accept(TOP, ADD_SUB_OPERATORS)) {
- var op = this.current;
- this.parseTerm(instr);
- instr.push(binaryInstruction(op.value));
- }
- };
- var TERM_OPERATORS = ['*', '/', '%'];
- ParserState.prototype.parseTerm = function (instr) {
- this.parseFactor(instr);
- while (this.accept(TOP, TERM_OPERATORS)) {
- var op = this.current;
- this.parseFactor(instr);
- instr.push(binaryInstruction(op.value));
- }
- };
- ParserState.prototype.parseFactor = function (instr) {
- var unaryOps = this.tokens.unaryOps;
- function isPrefixOperator(token) {
- return token.value in unaryOps;
- }
- this.save();
- if (this.accept(TOP, isPrefixOperator)) {
- if (this.current.value !== '-' && this.current.value !== '+') {
- if (this.nextToken.type === TPAREN && this.nextToken.value === '(') {
- this.restore();
- this.parseExponential(instr);
- return;
- } else if (this.nextToken.type === TSEMICOLON || this.nextToken.type === TCOMMA || this.nextToken.type === TEOF || (this.nextToken.type === TPAREN && this.nextToken.value === ')')) {
- this.restore();
- this.parseAtom(instr);
- return;
- }
- }
- var op = this.current;
- this.parseFactor(instr);
- instr.push(unaryInstruction(op.value));
- } else {
- this.parseExponential(instr);
- }
- };
- ParserState.prototype.parseExponential = function (instr) {
- this.parsePostfixExpression(instr);
- while (this.accept(TOP, '^')) {
- this.parseFactor(instr);
- instr.push(binaryInstruction('^'));
- }
- };
- ParserState.prototype.parsePostfixExpression = function (instr) {
- this.parseFunctionCall(instr);
- while (this.accept(TOP, '!')) {
- instr.push(unaryInstruction('!'));
- }
- };
- ParserState.prototype.parseFunctionCall = function (instr) {
- var unaryOps = this.tokens.unaryOps;
- function isPrefixOperator(token) {
- return token.value in unaryOps;
- }
- if (this.accept(TOP, isPrefixOperator)) {
- var op = this.current;
- this.parseAtom(instr);
- instr.push(unaryInstruction(op.value));
- } else {
- this.parseMemberExpression(instr);
- while (this.accept(TPAREN, '(')) {
- if (this.accept(TPAREN, ')')) {
- instr.push(new Instruction(IFUNCALL, 0));
- } else {
- var argCount = this.parseArgumentList(instr);
- instr.push(new Instruction(IFUNCALL, argCount));
- }
- }
- }
- };
- ParserState.prototype.parseArgumentList = function (instr) {
- var argCount = 0;
- while (!this.accept(TPAREN, ')')) {
- this.parseExpression(instr);
- ++argCount;
- while (this.accept(TCOMMA)) {
- this.parseExpression(instr);
- ++argCount;
- }
- }
- return argCount;
- };
- ParserState.prototype.parseMemberExpression = function (instr) {
- this.parseAtom(instr);
- while (this.accept(TOP, '.') || this.accept(TBRACKET, '[')) {
- var op = this.current;
- if (op.value === '.') {
- if (!this.allowMemberAccess) {
- throw new Error('unexpected ".", member access is not permitted');
- }
- this.expect(TNAME);
- instr.push(new Instruction(IMEMBER, this.current.value));
- } else if (op.value === '[') {
- if (!this.tokens.isOperatorEnabled('[')) {
- throw new Error('unexpected "[]", arrays are disabled');
- }
- this.parseExpression(instr);
- this.expect(TBRACKET, ']');
- instr.push(binaryInstruction('['));
- } else {
- throw new Error('unexpected symbol: ' + op.value);
- }
- }
- };
- function add(a, b) {
- return Number(a) + Number(b);
- }
- function sub(a, b) {
- return a - b;
- }
- function mul(a, b) {
- return a * b;
- }
- function div(a, b) {
- return a / b;
- }
- function mod(a, b) {
- return a % b;
- }
- function concat(a, b) {
- if (Array.isArray(a) && Array.isArray(b)) {
- return a.concat(b);
- }
- return '' + a + b;
- }
- function equal(a, b) {
- return a === b;
- }
- function notEqual(a, b) {
- return a !== b;
- }
- function greaterThan(a, b) {
- return a > b;
- }
- function lessThan(a, b) {
- return a < b;
- }
- function greaterThanEqual(a, b) {
- return a >= b;
- }
- function lessThanEqual(a, b) {
- return a <= b;
- }
- function andOperator(a, b) {
- return Boolean(a && b);
- }
- function orOperator(a, b) {
- return Boolean(a || b);
- }
- function inOperator(a, b) {
- return contains(b, a);
- }
- function sinh(a) {
- return ((Math.exp(a) - Math.exp(-a)) / 2);
- }
- function cosh(a) {
- return ((Math.exp(a) + Math.exp(-a)) / 2);
- }
- function tanh(a) {
- if (a === Infinity) return 1;
- if (a === -Infinity) return -1;
- return (Math.exp(a) - Math.exp(-a)) / (Math.exp(a) + Math.exp(-a));
- }
- function asinh(a) {
- if (a === -Infinity) return a;
- return Math.log(a + Math.sqrt((a * a) + 1));
- }
- function acosh(a) {
- return Math.log(a + Math.sqrt((a * a) - 1));
- }
- function atanh(a) {
- return (Math.log((1 + a) / (1 - a)) / 2);
- }
- function log10(a) {
- return Math.log(a) * Math.LOG10E;
- }
- function neg(a) {
- return -a;
- }
- function not(a) {
- return !a;
- }
- function trunc(a) {
- return a < 0 ? Math.ceil(a) : Math.floor(a);
- }
- function random(a) {
- return Math.random() * (a || 1);
- }
- function factorial(a) { // a!
- return gamma(a + 1);
- }
- function isInteger(value) {
- return isFinite(value) && (value === Math.round(value));
- }
- var GAMMA_G = 4.7421875;
- var GAMMA_P = [
- 0.99999999999999709182,
- 57.156235665862923517, -59.597960355475491248,
- 14.136097974741747174, -0.49191381609762019978,
- 0.33994649984811888699e-4,
- 0.46523628927048575665e-4, -0.98374475304879564677e-4,
- 0.15808870322491248884e-3, -0.21026444172410488319e-3,
- 0.21743961811521264320e-3, -0.16431810653676389022e-3,
- 0.84418223983852743293e-4, -0.26190838401581408670e-4,
- 0.36899182659531622704e-5
- ];
- // Gamma function from math.js
- function gamma(n) {
- var t, x;
- if (isInteger(n)) {
- if (n <= 0) {
- return isFinite(n) ? Infinity : NaN;
- }
- if (n > 171) {
- return Infinity; // Will overflow
- }
- var value = n - 2;
- var res = n - 1;
- while (value > 1) {
- res *= value;
- value--;
- }
- if (res === 0) {
- res = 1; // 0! is per definition 1
- }
- return res;
- }
- if (n < 0.5) {
- return Math.PI / (Math.sin(Math.PI * n) * gamma(1 - n));
- }
- if (n >= 171.35) {
- return Infinity; // will overflow
- }
- if (n > 85.0) { // Extended Stirling Approx
- var twoN = n * n;
- var threeN = twoN * n;
- var fourN = threeN * n;
- var fiveN = fourN * n;
- return Math.sqrt(2 * Math.PI / n) * Math.pow((n / Math.E), n) *
- (1 + (1 / (12 * n)) + (1 / (288 * twoN)) - (139 / (51840 * threeN)) -
- (571 / (2488320 * fourN)) + (163879 / (209018880 * fiveN)) +
- (5246819 / (75246796800 * fiveN * n)));
- }
- --n;
- x = GAMMA_P[0];
- for (var i = 1; i < GAMMA_P.length; ++i) {
- x += GAMMA_P[i] / (n + i);
- }
- t = n + GAMMA_G + 0.5;
- return Math.sqrt(2 * Math.PI) * Math.pow(t, n + 0.5) * Math.exp(-t) * x;
- }
- function stringOrArrayLength(s) {
- if (Array.isArray(s)) {
- return s.length;
- }
- return String(s).length;
- }
- function hypot() {
- var sum = 0;
- var larg = 0;
- for (var i = 0; i < arguments.length; i++) {
- var arg = Math.abs(arguments[i]);
- var div;
- if (larg < arg) {
- div = larg / arg;
- sum = (sum * div * div) + 1;
- larg = arg;
- } else if (arg > 0) {
- div = arg / larg;
- sum += div * div;
- } else {
- sum += arg;
- }
- }
- return larg === Infinity ? Infinity : larg * Math.sqrt(sum);
- }
- function condition(cond, yep, nope) {
- return cond ? yep : nope;
- }
- /**
- * Decimal adjustment of a number.
- * From @escopecz.
- *
- * @param {Number} value The number.
- * @param {Integer} exp The exponent (the 10 logarithm of the adjustment base).
- * @return {Number} The adjusted value.
- */
- function roundTo(value, exp) {
- // If the exp is undefined or zero...
- if (typeof exp === 'undefined' || +exp === 0) {
- return Math.round(value);
- }
- value = +value;
- exp = -(+exp);
- // If the value is not a number or the exp is not an integer...
- if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
- return NaN;
- }
- // Shift
- value = value.toString().split('e');
- value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
- // Shift back
- value = value.toString().split('e');
- return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
- }
- function setVar(name, value, variables) {
- if (variables) variables[name] = value;
- return value;
- }
- function arrayIndex(array, index) {
- return array[index | 0];
- }
- function max(array) {
- if (arguments.length === 1 && Array.isArray(array)) {
- return Math.max.apply(Math, array);
- } else {
- return Math.max.apply(Math, arguments);
- }
- }
- function min(array) {
- if (arguments.length === 1 && Array.isArray(array)) {
- return Math.min.apply(Math, array);
- } else {
- return Math.min.apply(Math, arguments);
- }
- }
- function arrayMap(f, a) {
- if (typeof f !== 'function') {
- throw new Error('First argument to map is not a function');
- }
- if (!Array.isArray(a)) {
- throw new Error('Second argument to map is not an array');
- }
- return a.map(function (x, i) {
- return f(x, i);
- });
- }
- function arrayFold(f, init, a) {
- if (typeof f !== 'function') {
- throw new Error('First argument to fold is not a function');
- }
- if (!Array.isArray(a)) {
- throw new Error('Second argument to fold is not an array');
- }
- return a.reduce(function (acc, x, i) {
- return f(acc, x, i);
- }, init);
- }
- function arrayFilter(f, a) {
- if (typeof f !== 'function') {
- throw new Error('First argument to filter is not a function');
- }
- if (!Array.isArray(a)) {
- throw new Error('Second argument to filter is not an array');
- }
- return a.filter(function (x, i) {
- return f(x, i);
- });
- }
- function stringOrArrayIndexOf(target, s) {
- if (!(Array.isArray(s) || typeof s === 'string')) {
- throw new Error('Second argument to indexOf is not a string or array');
- }
- return s.indexOf(target);
- }
- function arrayJoin(sep, a) {
- if (!Array.isArray(a)) {
- throw new Error('Second argument to join is not an array');
- }
- return a.join(sep);
- }
- function sign(x) {
- return ((x > 0) - (x < 0)) || +x;
- }
- var ONE_THIRD = 1 / 3;
- function cbrt(x) {
- return x < 0 ? -Math.pow(-x, ONE_THIRD) : Math.pow(x, ONE_THIRD);
- }
- function expm1(x) {
- return Math.exp(x) - 1;
- }
- function log1p(x) {
- return Math.log(1 + x);
- }
- function log2(x) {
- return Math.log(x) / Math.LN2;
- }
- function sum(array) {
- if (!Array.isArray(array)) {
- throw new Error('Sum argument is not an array');
- }
- return array.reduce(function (total, value) {
- return total + Number(value);
- }, 0);
- }
- function Parser(options) {
- this.options = options || {};
- this.unaryOps = {
- sin: Math.sin,
- cos: Math.cos,
- tan: Math.tan,
- asin: Math.asin,
- acos: Math.acos,
- atan: Math.atan,
- sinh: Math.sinh || sinh,
- cosh: Math.cosh || cosh,
- tanh: Math.tanh || tanh,
- asinh: Math.asinh || asinh,
- acosh: Math.acosh || acosh,
- atanh: Math.atanh || atanh,
- sqrt: Math.sqrt,
- cbrt: Math.cbrt || cbrt,
- log: Math.log,
- log2: Math.log2 || log2,
- ln: Math.log,
- lg: Math.log10 || log10,
- log10: Math.log10 || log10,
- expm1: Math.expm1 || expm1,
- log1p: Math.log1p || log1p,
- abs: Math.abs,
- ceil: Math.ceil,
- floor: Math.floor,
- round: Math.round,
- trunc: Math.trunc || trunc,
- '-': neg,
- '+': Number,
- exp: Math.exp,
- not: not,
- length: stringOrArrayLength,
- '!': factorial,
- sign: Math.sign || sign
- };
- this.binaryOps = {
- '+': add,
- '-': sub,
- '*': mul,
- '/': div,
- '%': mod,
- '^': Math.pow,
- '||': concat,
- '==': equal,
- '!=': notEqual,
- '>': greaterThan,
- '<': lessThan,
- '>=': greaterThanEqual,
- '<=': lessThanEqual,
- and: andOperator,
- or: orOperator,
- 'in': inOperator,
- '=': setVar,
- '[': arrayIndex
- };
- this.ternaryOps = {
- '?': condition
- };
- this.functions = {
- random: random,
- fac: factorial,
- min: min,
- max: max,
- hypot: Math.hypot || hypot,
- pyt: Math.hypot || hypot, // backward compat
- pow: Math.pow,
- atan2: Math.atan2,
- 'if': condition,
- gamma: gamma,
- roundTo: roundTo,
- map: arrayMap,
- fold: arrayFold,
- filter: arrayFilter,
- indexOf: stringOrArrayIndexOf,
- join: arrayJoin,
- sum: sum
- };
- this.consts = {
- E: Math.E,
- PI: Math.PI,
- 'true': true,
- 'false': false
- };
- }
- Parser.prototype.parse = function (expr) {
- var instr = [];
- var parserState = new ParserState(
- this,
- new TokenStream(this, expr),
- { allowMemberAccess: this.options.allowMemberAccess }
- );
- parserState.parseExpression(instr);
- parserState.expect(TEOF, 'EOF');
- return new Expression(instr, this);
- };
- Parser.prototype.evaluate = function (expr, variables) {
- return this.parse(expr).evaluate(variables);
- };
- var sharedParser = new Parser();
- Parser.parse = function (expr) {
- return sharedParser.parse(expr);
- };
- Parser.evaluate = function (expr, variables) {
- return sharedParser.parse(expr).evaluate(variables);
- };
- var optionNameMap = {
- '+': 'add',
- '-': 'subtract',
- '*': 'multiply',
- '/': 'divide',
- '%': 'remainder',
- '^': 'power',
- '!': 'factorial',
- '<': 'comparison',
- '>': 'comparison',
- '<=': 'comparison',
- '>=': 'comparison',
- '==': 'comparison',
- '!=': 'comparison',
- '||': 'concatenate',
- 'and': 'logical',
- 'or': 'logical',
- 'not': 'logical',
- '?': 'conditional',
- ':': 'conditional',
- '=': 'assignment',
- '[': 'array',
- '()=': 'fndef'
- };
- function getOptionName(op) {
- return optionNameMap.hasOwnProperty(op) ? optionNameMap[op] : op;
- }
- Parser.prototype.isOperatorEnabled = function (op) {
- var optionName = getOptionName(op);
- var operators = this.options.operators || {};
- return !(optionName in operators) || !!operators[optionName];
- };
- /*!
- Based on ndef.parser, by Raphael Graf([email protected])
- http://www.undefined.ch/mparser/index.html
- Ported to JavaScript and modified by Matthew Crumley ([email protected], http://silentmatt.com/)
- You are free to use and modify this code in anyway you find useful. Please leave this comment in the code
- to acknowledge its original source. If you feel like it, I enjoy hearing about projects that use my code,
- but don't feel like you have to let me know or ask permission.
- */
- // Backwards compatibility
- var index = {
- Parser: Parser,
- Expression: Expression
- };
- exports.Expression = Expression;
- exports.Parser = Parser;
- exports.default = index;
- })));
|