Advertisement
Echo89

Math Expression Parser v1.1

Nov 5th, 2016
130
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. const Decimal = require('decimal.js');
  2. Decimal.config({ precision: 20 });
  3.  
  4. const PI_5000 = '3.14159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912983367336244065664308602139494639522473719070217986094370277053921717629317675238467481846766940513200056812714526356082778577134275778960917363717872146844090122495343014654958537105079227968925892354201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859502445945534690830264252230825334468503526193118817101000313783875288658753320838142061717766914730359825349042875546873115956286388235378759375195778185778053217122680661300192787661119590921642019893809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151557485724245415069595082953311686172785588907509838175463746493931925506040092770167113900984882401285836160356370766010471018194295559619894676783744944825537977472684710404753464620804668425906949129331367702898915210475216205696602405803815019351125338243003558764024749647326391419927260426992279678235478163600934172164121992458631503028618297455570674983850549458858692699569092721079750930295532116534498720275596023648066549911988183479775356636980742654252786255181841757467289097777279380008164706001614524919217321721477235014144197356854816136115735255213347574184946843852332390739414333454776241686251898356948556209921922218427255025425688767179049460165346680498862723279178608578438382796797668145410095388378636095068006422512520511739298489608412848862694560424196528502221066118630674427862203919494504712371378696095636437191728746776465757396241389086583264599581339047802759009946576407895126946839835259570982582262052248940772671947826848260147699090264013639443745530506820349625245174939965143142980919065925093722169646151570985838741059788595977297549893016175392846813826868386894277415599185592524595395943104997252468084598727364469584865383673622262609912460805124388439045124413654976278079771569143599770012961608944169486855584840635342207222582848864815845602850601684273945226746767889525213852254995466672782398645659611635488623057745649803559363456817432411251507606947945109659609402522887971089314566913686722874894056010150330861792868092087476091782493858900971490967598526136554978189312978482168299894872265880485756401427047755513237964145152374623436454285844479526586782105114135473573952311342716610213596953623144295248493718711014576540359027993440374200731057853906219838744780847848968332144571386875194350643021845319104848100537061468067491927819119793995206141966342875444064374512371819217999839101591956181467514269123974894090718649423196156794520809514655022523160388193014209376213785595663893778708303906979207734672218256259966150142150306803844773454920260541466592520149744285073251866600213243408819071048633173464965145390579626856100550810665879699816357473638405257145910289706414011097120628043903975951567715770042033786993600723055876317635942187312514712053292819182618612586732157919841484882916447060957527069572209175671167229109816909152801735067127485832228718352093539657251210835791513698820914442100675103346711031412671113699086585163983150197016515116851714376576183515565088490998985998238734552833163550764791853589322618548963213293308985706420467525907091548141654985946163718027098199430992448895757128289059232332609729971208443357326548938239119325974636673058360414281388303203824903758985243744170291327656180937734440307074692112019130203303801976211011004492932151608424448596376698389522868478312355265821314495768572624334418930396864262434107732269780280731891544110104468232527162010526522721116603966655730925471105578537634668206531098965269186205647693125705863566201855810072936065987648611791045334885034611365768675324944166803962657978771855608455296541266540853061434443185867697514566140680070023787765913440171274947042056223053899456131407112700040785473326993908145466464588079727082668306343285878569830523580893306575740679545716377525420211495576158140025012622859413021647155097925923099079654737612551765675135751782966645477917450112996148903046399471329621073404375189573596145890193897131117904297828564750320319869151402870808599048010941214722131794764777262241425485454033215718530614228813758504306332175182979866223717215916077166925474873898665494945011465406284336639379003976926567214638530673609657120918076383271664162748888007869256029022847210403172118608204190004229661711963779213375751149595015660496318629472654736425230817703675159067350235072835405670403867435136222247715891504953098444893330963408780769325993978054193414473774418426312986080998886874132604721',
  5.       TAU_5000 = '6.2831853071795864769252867665590057683943387987502116419498891846156328125724179972560696506842341359642961730265646132941876892191011644634507188162569622349005682054038770422111192892458979098607639288576219513318668922569512964675735663305424038182912971338469206972209086532964267872145204982825474491740132126311763497630418419256585081834307287357851807200226610610976409330427682939038830232188661145407315191839061843722347638652235862102370961489247599254991347037715054497824558763660238982596673467248813132861720427898927904494743814043597218874055410784343525863535047693496369353388102640011362542905271216555715426855155792183472743574429368818024499068602930991707421015845593785178470840399122242580439217280688363196272595495426199210374144226999999967459560999021194634656321926371900489189106938166052850446165066893700705238623763420200062756775057731750664167628412343553382946071965069808575109374623191257277647075751875039155637155610643424536132260038557532223918184328403978761905144021309717265577318723067636559364606039040706037059379915472451988277824994435505669582630311497144849083013919016590662337234557117781501967635092749298786385101208018554033422780196976480257167232071274153202094203638859111923978935356748988965107595494536942080950692924160933685181389825866273540579783042095043241139320481160763003870225067648600711752804949929465278283985452085398455935647095632720186834432824398491726300605723659491114134996770109891771738539913818544215950186059106423306899744055119204729613309982397636695955071327396148530850557251036368351493457819555455876001632941200322904983843464344295447002828839471370963227223147051042669514836989368770466478147882866690955248337250379671389711241984384443685451005085137753435809892033069336099772544655835721715687676559359533629082019077675727219013601284502504102347859697921682569772538912084839305700444213223726134885572440783898900942474275739219127287438345749355293151479248277817316652919916267809560551801989315281579025389367967051914196516452410449788154534389565369652029539818052802727888749106101364069925049034987993028628596183813185018744433929230314197167748211957719195459509978603235078569362765373677378855483119837118504919079188620999450493616919745472893916973076734724452521982492161024877687809024882730995255615954313828719954002592321788833897371116968127068441444516569772963169120570120336854789045349353577905042770450999093334556479729131922327097724611549129960711872691363486482250301521389589021931921880504577594217862913382737344574978811202030066172358573618417495218356498771780194298193519705227310995637862595696433659978974453176097151280285409551102647592829030474924687290857168895905317356421022827094714790462268543322042719390724628859049698743742202915308071805598688074840146211570781243967748956169569793666428914277375038870128604369063820969620107412293613498385563823958799041228393268575088812874902474363843599967820318391236293502853824794978818143729884639231358904161902931004504632077638602841875242757119132778755741660781395841546934443651251993230028430061360768954690984052108293318504029948857014650373320042648681763814209726634692993029078115925371220110162133175939963271494727681051429182057941282802219424125608780795190313543154008406757398720144611175263527188437462502942410658563836523722517346431583968296976583289412191505413914441835134233445821963381830560347013425497166445743670418707931450242167158302739764182888420135020669342206282534222739817317032796630039403303023370342875315236703113017698199797199647746910566632710152958370717864523709792642658661797141284093505181418309628330997189232743605419639886198489779151425657811846466521945994241688671465309787647823865194927334611672082856277660640764980751797048748834058265531236187546888061414938422403826040660760395242202200898586430321684889719275339677904573695662471053164262899153714524866883786079372852486821546453956056146378308822020893646505432402105304544223320793331146185094221115707526933641306219793053837241129538625141172713240371162014587213197529722358209066977006922273153735064988833360792531595754371121691059308253308170612286888637173539502913228136014004757553182688034254989408411244610779891226281422540008157094665398781629093292917615945416533661268657175713966104716178661315148135909143275505084042299115231628005002524571882604329431019585184619815930947522510353135027150356593329095583490022599229780609279894265924214680875037914719229178038779426223580859565712950064063973830280574161719809602188242944426358952955452448285097090806643143706122845762751700861266435036595973244743443183215433385094974779733098989002293081256867327875800795385313442927706134721931424183615276654332832549777601573851205804569442080634423721640838000845932342392755842675150229919003132099263725894530947285046163540735031813470047014567081134080773487027244449543178300990619688978666192681756153865198795610838682894754883685262597216199777374826520944',
  6.       NUMERIC_CHARSET = '01234567890.',
  7.       ALPHA_CHARSET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_',
  8.       OPERATOR_CHARSET = '+-/*^%',
  9.       HEX_CHARSET = '0123456789ABCDEFabcdef',
  10.       OCTAL_CHARSET = '01234567',
  11.       WHITE_SPACE_REGEX = /\s/;
  12.  
  13. const MathFunctions = {
  14.     fact: function (value) {
  15.         if(arguments.length !== 1) {
  16.             throw new Error("function 'fact' requires exactly one argument");
  17.         }
  18.  
  19.         var iter,
  20.             multiplier,
  21.             returnValue = value;
  22.  
  23.         for(multiplier = value - 1; multiplier > 0; --multiplier) {
  24.             returnValue = returnValue.times(multiplier);
  25.         }
  26.  
  27.         return returnValue;
  28.     },
  29.     rad: function(n) {
  30.         if(arguments.length !== 1) {
  31.             throw new Error("function 'rad' requires exactly one argument");
  32.         }
  33.  
  34.         return n.times(Math.PI).dividedBy(180);
  35.     },
  36.     deg: function(n) {
  37.         if(arguments.length !== 1) {
  38.             throw new Error("function 'rad' requires exactly one argument");
  39.         }
  40.  
  41.         return n.times(180).dividedBy(Math.PI);
  42.     },
  43.     rand: function(min, max) {
  44.         return new Decimal(Math.random()).times(max.minus(min).add(1)).add(min).floor();
  45.         //return Math.floor(Math.random() * (max - min + 1) + min);
  46.     },
  47.     randf: function() {
  48.         return new Decimal(Math.random());
  49.     },
  50.     round: function(n, toMultiple) {
  51.         if(typeof toMultiple !== 'undefined') {
  52.             return n.dividedBy(toMultiple).round().times(toMultiple);
  53.         } else {
  54.             return n.round();
  55.         }
  56.     },
  57.     ceil: function(n, toMultiple) {
  58.         if(typeof toMultiple !== 'undefined') {
  59.             return n.dividedBy(toMultiple).ceil().times(toMultiple);
  60.         } else {
  61.             return n.ceil();
  62.         }
  63.     },
  64.     floor: function(n, toMultiple) {
  65.         if(typeof toMultiple !== 'undefined') {
  66.             return n.dividedBy(toMultiple).floor().times(toMultiple);
  67.         } else {
  68.             return n.floor();
  69.         }
  70.     },
  71.     sign: function(n) {
  72.         return new Decimal(n.s);
  73.     },
  74.     cmp: function(a, b) {
  75.         return new Decimal(a.cmp(b));
  76.     }
  77. },
  78. allowedMathFunctions = [
  79.     'abs',
  80.     'acos',
  81.     'acosh',
  82.     'asin',
  83.     'asinh',
  84.     'atan',
  85.     'atanh',
  86.     'atan2',
  87.     'cbrt',
  88.     'cos',
  89.     'exp',
  90.     'hypot',
  91.     'ln',
  92.     'log',
  93.     'log10',
  94.     'log2',
  95.     'max',
  96.     'min',
  97.     'pow',
  98.     'sin',
  99.     'sinh',
  100.     'sqrt',
  101.     'tan',
  102.     'tanh'
  103. ];
  104.  
  105. const Helpers = {
  106.     isNumeric: char => NUMERIC_CHARSET.indexOf(char) !== -1,
  107.     isAlpha: char => ALPHA_CHARSET.indexOf(char) !== -1,
  108.     isOperator: char => OPERATOR_CHARSET.indexOf(char) !== -1,
  109.     isMathFunction: keyword => typeof MathFunctions[keyword] === 'function' || allowedMathFunctions.indexOf(keyword) !== -1,
  110.     isWhitespace: char => WHITE_SPACE_REGEX.test(char),
  111.     radians: angle => angle * Math.PI / 180,
  112.     isHexChar: char => HEX_CHARSET.indexOf(char) !== -1,
  113.     isBinChar: char => char === '0' || char === '1',
  114.     isOctalChar: char => OCTAL_CHARSET.indexOf(char) !== -1
  115. };
  116.  
  117. const OperatorFunctions = {
  118.     '+': (left, right) => left.add(right),
  119.     '-': (left, right) => left.minus(right),
  120.     '/': (left, right) => left.dividedBy(right),
  121.     '*': (left, right) => left.times(right),
  122.     '%': (left, right) => left.modulo(right),
  123.     '^': (left, right) => left.pow(right)
  124. };
  125.  
  126. function clone(obj) {
  127.     if(typeof obj === 'object') {
  128.         if(obj.constructor.name === 'Decimal') {
  129.             return obj;
  130.         } else if(obj.constructor.name === 'Array' && typeof obj.length === 'number') {
  131.             var len = obj.length,
  132.                 cln = [ ],
  133.                 iter;
  134.  
  135.             for(iter = 0; iter < len; ++iter) {
  136.                 cln.push(clone(obj[iter]));
  137.             }
  138.  
  139.             return cln;
  140.         } else {
  141.             let key,
  142.                 cln = { };
  143.  
  144.             for(key in obj) {
  145.                 if(obj.hasOwnProperty(key)) {
  146.                     cln[key] = clone(obj[key]);
  147.                 }
  148.             }
  149.  
  150.             return cln;
  151.         }
  152.     }
  153.  
  154.     return obj;
  155. }
  156.  
  157. function getListPrimitiveArray(itms) {
  158.     var items = itms,
  159.         itemsLength = items.length,
  160.         iter,
  161.         primitives = [ ];
  162.  
  163.     for(iter = 0; iter < itemsLength; ++iter) {
  164.         primitives.push(items[iter][1]);
  165.     }
  166.  
  167.     return primitives;
  168. }
  169.  
  170. /**
  171.  * ExpressionParser - A JavaScript utility for parsing and evaluating expressions
  172.  *
  173.  * @author Caelan Stewart
  174.  */
  175. function ExpressionParser(variables, macros) {
  176.     'use strict';
  177.    
  178.     var defaultVariables = {
  179.             pi: new Decimal(PI_5000),
  180.             PI: new Decimal(PI_5000),
  181.             tau: new Decimal(TAU_5000),
  182.             TAU: new Decimal(TAU_5000),
  183.             e: new Decimal(Math.E),
  184.             E: new Decimal(Math.E),
  185.             randf: () => new Decimal(Math.random()),
  186.             NaN: Decimal.asin(2)
  187.         };
  188.  
  189.     if(Object.prototype.toString.call(variables) === '[object Object]') {
  190.         this.variables = variables;
  191.     } else {
  192.         this.variables = defaultVariables;
  193.     }
  194.  
  195.     if(Object.prototype.toString.call(macros) === '[object Object]') {
  196.         this.macros = macros;
  197.     } else {
  198.         this.macros = { };
  199.     }
  200.  
  201.     this.eventCallbacks = {
  202.         'variable-set': [ ],
  203.         'macro-set': [ ]
  204.     }
  205.  
  206.     // Create an array of the names of the default variables
  207.     // them being overwritten.
  208.     this.readOnlyVariables = Object.keys(defaultVariables);
  209.  
  210.     this.settings = {
  211.         sciNotation: true
  212.     };
  213. };
  214.  
  215. /**
  216.  * Sets an event listener. To find any event types, search for
  217.  * usages of the 'trigger' method.
  218.  *
  219.  * @param {string} type - The event type
  220.  * @param {Function} callback - A callable type to be executed when
  221.  * the given event type is triggered.
  222.  */
  223. ExpressionParser.prototype.on = function(type, callback) {
  224.     if(typeof type !== 'string') {
  225.         throw new Error('Event type must be a string');
  226.     }
  227.  
  228.     if(!this.eventCallbacks.hasOwnProperty(type)) {
  229.         throw new Error('Invalid event type "' + type + '"');
  230.     }
  231.  
  232.     if(typeof callback !== 'function') {
  233.         throw new Error('Callback must be a function');
  234.     }
  235.  
  236.     this.eventCallbacks[type].push(callback);
  237. };
  238.  
  239. /**
  240.  * Removes a given event listener
  241.  *
  242.  * @param {string} type - The type of event
  243.  * @param {Function} callback - The exact callback provided when
  244.  * the event listener was set.
  245.  */
  246. ExpressionParser.prototype.off = function(type, callback) {
  247.     if(typeof type !== 'string') {
  248.         throw new Error('Event type must be a string');
  249.     }
  250.  
  251.     if(!this.eventCallbacks.hasOwnProperty(type)) {
  252.         throw new Error('Invalid event type "' + type + '"');
  253.     }
  254.  
  255.     if(typeof callback !== 'undefined' && typeof callback !== 'function') {
  256.         throw new Error('Callback must be a function');
  257.     }
  258.  
  259.     // Clear all events of this type
  260.     if(typeof callback === 'undefined') {
  261.         this.eventCallbacks[type] = [ ];
  262.     }
  263.  
  264.     let callbacks = this.eventCallbacks[type],
  265.         len = callbacks.length,
  266.         index;
  267.  
  268.     for(index = 0; index < len; ++index) {
  269.         if(callback === callbacks[iter]) {
  270.             callbacks.splice(index, 1);
  271.         }
  272.     }
  273. };
  274.  
  275. /**
  276.  * Triggers listeners of a given type, and passes arguments to
  277.  * the callbacks.
  278.  *
  279.  * @param {string} type - The type of event
  280.  * @param {array} argArray - An array of arguments to be passed to
  281.  * the event listener callback functions.
  282.  */
  283. ExpressionParser.prototype.trigger = function(type, argArray) {
  284.     if(typeof type !== 'string') {
  285.         throw new Error('Event type must be a string');
  286.     }
  287.  
  288.     if(!this.eventCallbacks.hasOwnProperty(type)) {
  289.         throw new Error('Invalid event type "' + type + '"');
  290.     }
  291.  
  292.     this.eventCallbacks[type].forEach(callback => callback.apply(null, argArray));
  293. };
  294.  
  295. /**
  296.  * Sets a variable of a given name to a given value.
  297.  *
  298.  * @param {string} name - The name of the variable
  299.  * @param {string|Decimal} value - The value of the variable. If a
  300.  * string is passed, the string is fed into a new instance of Decimal.
  301.  * If a Decimal object is passed, it is set as is.
  302.  * @param {boolean} triggerEvent - Should this setting of a variable
  303.  * trigger the 'variable-set' event listeners?
  304.  */
  305. ExpressionParser.prototype.setVariable = function(name, value, triggerEvent) {
  306.     'use strict';
  307.  
  308.     if(this.readOnlyVariables.indexOf(name) !== -1) {
  309.         throw new Error('Cannot set read only variable "' + name + '"');
  310.     }
  311.  
  312.     if(typeof value === 'string') {
  313.         value = new Decimal(value);
  314.     }
  315.  
  316.     if(triggerEvent !== false) {
  317.         this.trigger('variable-set', [name, value]);
  318.     }
  319.  
  320.     this.variables[name] = value;
  321. };
  322.  
  323. /**
  324.  * Gets the value of a variable of a given name.
  325.  *
  326.  * @param {string} name - The name of the variable to fetch
  327.  */
  328. ExpressionParser.prototype.getVariable = function(name) {
  329.     'use strict';
  330.  
  331.     if(this.isVariable(name)) {
  332.         let variable = this.variables[name];
  333.  
  334.         if(typeof variable === 'function') {
  335.             variable = variable();
  336.         }
  337.  
  338.         if(typeof variable === 'number' || typeof variable === 'string') {
  339.             variable = new Decimal(variable);
  340.         }
  341.  
  342.         return variable;
  343.     }
  344.  
  345.     return undefined;
  346. };
  347.  
  348. /**
  349.  * Checks to see if a variable of a given name exists.
  350.  *
  351.  * @param {string} name - The name of the variable to check for.
  352.  *
  353.  * @return {boolean} - 'true' if the variable exists, 'false' if it does not.
  354.  */
  355. ExpressionParser.prototype.isVariable = function(name) {
  356.     'use strict';
  357.  
  358.     return this.variables.hasOwnProperty(name);
  359. };
  360.  
  361. /**
  362.  * Sets a macro.
  363.  *
  364.  * @param {string} name - The name of the macro
  365.  * @param {array} argList - An argument list, in the format of an array of tokens.
  366.  * @param {array} exprTokens - The expression, in the format of an array of tokens.
  367.  * @param {boolean} triggerEvent - Should the setting of this macro trigger the
  368.  * 'macro-set' event listener type?
  369.  */
  370. ExpressionParser.prototype.setMacro = function(name, argList, exprTkns, triggerEvent) {
  371.     'use strict';
  372.  
  373.     this.macros[name] = {
  374.         argList: argList,
  375.         exprTkns: exprTkns
  376.     };
  377.  
  378.     if(triggerEvent !== false) {
  379.         this.trigger('macro-set', [name, argList, exprTkns]);
  380.     }
  381. };
  382.  
  383. /**
  384.  * Runs a macro of a given name, passing a list of arguments
  385.  * for the expression.
  386.  *
  387.  * @param {string} name - The name of the macro to execute.
  388.  * @param {array} list - An array of values to be used as the
  389.  * argument list for the execution of the macro's expression.
  390.  *
  391.  * @return {array} - Returns the last token left after the
  392.  * execution of the macro's expression.
  393.  */
  394. ExpressionParser.prototype.runMacro = function(name, list) {
  395.     'use strict';
  396.  
  397.     if(name in this.macros) {
  398.         let macro = this.macros[name],
  399.             vars = { },
  400.             ep;
  401.  
  402.         if(list.length !== macro.argList.length) {
  403.             throw new Error('Macro "' + name + '" takes ' + macro.argList.length + ' arguments');
  404.         }
  405.  
  406.         ep = new ExpressionParser(clone(this.variables), clone(this.macros));
  407.  
  408.         macro.argList.forEach((arg, index) => {
  409.             ep.setVariable(arg[1], list[index]);
  410.         });
  411.  
  412.         let tkns = ep.parseTokens(clone(macro.exprTkns));
  413.  
  414.         if(tkns.length === 1 && tkns[0][0] === 'number') {
  415.             return tkns[0][1];
  416.         }
  417.     }
  418.  
  419.     return [];
  420. };
  421.  
  422. /**
  423.  * Parses an expression.
  424.  *
  425.  * @param {string} expression - The expression to parse.
  426.  *
  427.  * @return {string|Decimal} - A string if scientific notation
  428.  * is off. An instance of Decimal if it is.
  429.  */
  430. ExpressionParser.prototype.parse = function(expression) {
  431.     'use strict';
  432.  
  433.     var tokens = this.tokenize(expression);
  434.  
  435.     tokens = this.parseTokens(tokens);
  436.  
  437.     var tokensLength = tokens.length,
  438.         iter,
  439.         value = null,
  440.         last_number = null;
  441.  
  442.     for(iter = 0; iter < tokensLength; ++iter) {
  443.         // Get the value
  444.         if(tokens[iter][0] === 'number') {
  445.             value = tokens[iter][1];
  446.         }
  447.     }
  448.  
  449.     return this.settings.sciNotation ? value : value.toFixed();
  450. };
  451.  
  452. /**
  453.  * Parses a token stack.
  454.  *
  455.  * @param {array} - An array of tokens
  456.  *
  457.  * @return {array} - The resulting array of tokens after being evaluated and reduced down to a single token.
  458.  */
  459. ExpressionParser.prototype.parseTokens = function(tkns) {
  460.     'use strict';
  461.  
  462.     var tokens = tkns;
  463.  
  464.     tokens = this.parseArgLists(tokens);
  465.     tokens = this.parseMacros(tokens);
  466.     tokens = this.parseLists(tokens);
  467.     tokens = this.parseBrackets(tokens);
  468.     tokens = this.parseVariables(tokens);
  469.     tokens = this.parseNegatives(tokens);
  470.     tokens = this.parseFunctions(tokens);
  471.     tokens = this.parseOperations(tokens);
  472.     tokens = this.parseAssignments(tokens);
  473.    
  474.     // If there is more than one token, an operator was most likely missed
  475.     if(tokens.length > 1) {
  476.         throw new Error('Expected operator, got "' + tokens[1][1] + '"');
  477.     }
  478.  
  479.     return tokens;
  480. };
  481.  
  482. /**
  483.  * Parses assignments.
  484.  */
  485. ExpressionParser.prototype.parseAssignments = function(tkns) {
  486.     'use strict';
  487.  
  488.     var tokens = tkns,
  489.         tokensLength = tokens.length,
  490.         bracketDepth = 0,
  491.         bracketIndex = 0,
  492.         iter;
  493.  
  494.     for(iter = tokensLength - 1; iter >= 0; --iter) {
  495.         if(
  496.             tokens[iter][0] === 'keyword' &&
  497.             iter + 2 < tokensLength &&
  498.             tokens[iter + 1][0] === 'assignment' &&
  499.             tokens[iter + 2][0] === 'number'
  500.  
  501.         ) {
  502.             let varName = tokens[iter][1],
  503.                 value = tokens[iter + 2][1];
  504.  
  505.             this.setVariable(varName, value);
  506.  
  507.             tokensLength -= tokens.splice(iter, 2).length;
  508.         }
  509.     }
  510.  
  511.     return tokens;
  512. };
  513.  
  514. ExpressionParser.prototype.parseMacros = function(tkns) {
  515.     'use strict';
  516.  
  517.     var tokens = tkns,
  518.         tokensLength = tokens.length,
  519.         bracketDepth = 0,
  520.         bracketIndex = 0,
  521.         iter;
  522.  
  523.     for(iter = 0; iter < tokensLength; ++iter) {
  524.         if(
  525.             tokens[iter][0] === 'keyword' &&
  526.             iter + 2 < tokensLength &&
  527.             tokens[iter + 1][0] === 'arglist' &&
  528.             tokens[iter + 2][0] === 'assignment'
  529.         ) {
  530.             if(Helpers.isMathFunction(tokens[iter][1])) {
  531.                 throw new Error('Keyword "' + tokens[iter][1] + '" is reserved');
  532.             }
  533.  
  534.             if(iter !== 0) {
  535.                 throw new Error('Macro definition must be solitary');
  536.             }
  537.  
  538.             let exprTkns = tokens.slice(iter + 3);
  539.  
  540.             if(!exprTkns.length) {
  541.                 throw new Error('Macro is missing an expression');
  542.             }
  543.  
  544.             this.setMacro(tokens[iter][1], tokens[iter + 1][1], exprTkns);
  545.  
  546.             // Only one macro definition per parse
  547.             return [ ];
  548.         }
  549.     }
  550.  
  551.     return tokens;
  552. };
  553.  
  554. ExpressionParser.prototype.parseArgLists = function(tkns) {
  555.     'use strict';
  556.  
  557.     var tokens = tkns,
  558.         tokensLength = tokens.length,
  559.         braceDepth = 0,
  560.         braceIndex = 0,
  561.         iter,
  562.         flag_list = true,
  563.         items = [ ],
  564.         listIndex = 0;
  565.  
  566.     for(iter = 0; iter < tokensLength; ++iter) {
  567.         if(tokens[iter][0] === 'brace') {
  568.  
  569.             if(tokens[iter][1] === '}' && braceDepth === 0) {
  570.                 throw new Error('Invalid brace syntax');
  571.             }
  572.  
  573.             if(braceDepth > 0) {
  574.                 if(tokens[iter][1] === '}') {
  575.                     --braceDepth;
  576.                 }
  577.  
  578.                 if(braceDepth === 0 && flag_list) {
  579.                     let argTokens = tokens.slice(listIndex + 1, iter);
  580.  
  581.                     if(argTokens.length !== 1 || argTokens[0][0] !== 'keyword') {
  582.                         throw new Error('Invalid argument list syntax');
  583.                     }
  584.  
  585.                     items.push(argTokens[0]);
  586.  
  587.                     let leftSide = tokens.slice(0, braceIndex),
  588.                         rightSide = tokens.slice(iter + 1),
  589.                         replace = [['arglist', items]];
  590.  
  591.                     tokens = leftSide.concat(replace, rightSide);
  592.                     iter += tokens.length - tokensLength;
  593.                     tokensLength = tokens.length;
  594.                     flag_list = false;
  595.                 }
  596.             }
  597.  
  598.             if(tokens[iter][1] === '{') {
  599.                 if(braceDepth === 0) {
  600.                     braceIndex = iter;
  601.                     listIndex = iter;
  602.                 }
  603.  
  604.                 braceDepth++;
  605.             }
  606.         }
  607.  
  608.         if(tokens[iter][0] === 'comma' && braceDepth === 1) {
  609.             if( // Check for correct comma syntax
  610.                 tokens[iter - 1][0] !== 'comma' &&
  611.                 (
  612.                     tokens[iter - 1][0] !== 'brace' ||
  613.                     (
  614.                         tokens[iter - 1][0] === 'brace' &&
  615.                         tokens[iter - 1][1] !== '{'
  616.                     )
  617.                 ) &&
  618.                 (
  619.                     (
  620.                         iter + 1 < tokensLength &&
  621.                         tokens[iter + 1][0] !== 'comma'
  622.                     ) ||
  623.                     iter + 1 === tokensLength
  624.                 )
  625.             ) {
  626.                 if(items === null) {
  627.                     items = [ ];
  628.                 }
  629.  
  630.                 let argTokens = tokens.slice(listIndex + 1, iter);
  631.  
  632.                 listIndex = iter;
  633.  
  634.                 if(argTokens.length !== 1 || argTokens[0][0] !== 'keyword') {
  635.                     throw new Error('Invalid argument list syntax');
  636.                 }
  637.  
  638.                 items.push(argTokens[0]);
  639.  
  640.                 flag_list = true;
  641.  
  642.                 continue;
  643.             } else {
  644.                 throw new Error('Unexpected comma');
  645.             }
  646.         }
  647.     }
  648.  
  649.     return tokens;
  650. };
  651.  
  652. ExpressionParser.prototype.parseLists = function(tkns) {
  653.     'use strict';
  654.  
  655.     var tokens = tkns,
  656.         tokensLength = tokens.length,
  657.         bracketDepth = 0,
  658.         bracketIndex = 0,
  659.         iter,
  660.         flag_list = false,
  661.         items = null,
  662.         listIndex = 0;
  663.  
  664.     for(iter = 0; iter < tokensLength; ++iter) {
  665.         if(tokens[iter][0] === 'bracket') {
  666.  
  667.             if(tokens[iter][1] === ')' && bracketDepth === 0) {
  668.                 throw new Error('Invalid bracket syntax');
  669.             }
  670.  
  671.             if(bracketDepth > 0) {
  672.                 if(tokens[iter][1] === ')') {
  673.                     --bracketDepth;
  674.                 }
  675.  
  676.                 if(bracketDepth === 0 && flag_list) {
  677.                     let argTokens = tokens.slice(listIndex + 1, iter);
  678.  
  679.                     if(argTokens.length) {
  680.                         argTokens = this.parseTokens(clone(argTokens));
  681.  
  682.                         items.push(argTokens[0]);
  683.                     }
  684.  
  685.                     let leftSide = tokens.slice(0, bracketIndex),
  686.                         rightSide = tokens.slice(iter + 1),
  687.                         replace = [['list', items]];
  688.  
  689.                     tokens = leftSide.concat(replace, rightSide);
  690.                     iter += tokens.length - tokensLength;
  691.                     tokensLength = tokens.length;
  692.                     flag_list = false;
  693.                 }
  694.             }
  695.  
  696.             if(tokens[iter][1] === '(') {
  697.                 if(bracketDepth === 0) {
  698.                     bracketIndex = iter;
  699.                     listIndex = iter;
  700.  
  701.                     if(
  702.                         iter - 1 >= 0 &&
  703.                         tokens[iter - 1][0] === 'keyword'
  704.                     ) {
  705.                         flag_list = true;
  706.                         items = [ ];
  707.                     }
  708.                 }
  709.  
  710.                 bracketDepth++;
  711.             }
  712.         }
  713.  
  714.         if(tokens[iter][0] === 'comma' && bracketDepth === 1) {
  715.             if( // Check for correct comma syntax
  716.                 tokens[iter - 1][0] !== 'comma' &&
  717.                 (
  718.                     tokens[iter - 1][0] !== 'bracket' ||
  719.                     (
  720.                         tokens[iter - 1][0] === 'bracket' &&
  721.                         tokens[iter - 1][1] !== '('
  722.                     )
  723.                 ) &&
  724.                 (
  725.                     (
  726.                         iter + 1 < tokensLength &&
  727.                         tokens[iter + 1][0] !== 'comma'
  728.                     ) ||
  729.                     iter + 1 === tokensLength
  730.                 )
  731.             ) {
  732.                 if(items === null) {
  733.                     items = [ ];
  734.                 }
  735.  
  736.                 let argTokens = tokens.slice(listIndex + 1, iter);
  737.  
  738.                 listIndex = iter;
  739.  
  740.                 if(argTokens.length) {
  741.                     argTokens = this.parseTokens(clone(argTokens));
  742.                 }
  743.  
  744.                 items.push(argTokens[0]);
  745.                 flag_list = true;
  746.  
  747.                 continue;
  748.             } else {
  749.                 throw new Error('Unexpected comma');
  750.             }
  751.         }
  752.     }
  753.  
  754.     return tokens;
  755. };
  756.  
  757. ExpressionParser.prototype.parseBrackets = function(tkns) {
  758.     'use strict';
  759.  
  760.     var tokens = tkns,
  761.         tokensLength = tokens.length,
  762.         bracketDepth = 0,
  763.         bracketIndex = 0,
  764.         iter;
  765.  
  766.     for(iter = 0; iter < tokensLength; ++iter) {
  767.         if(tokens[iter][0] === 'bracket') {
  768.             if(tokens[iter][1] === ')' && bracketDepth === 0) {
  769.                 throw new Error('Invalid bracket syntax');
  770.             }
  771.  
  772.             if(bracketDepth > 0) {
  773.                 if(tokens[iter][1] === ')') {
  774.                     --bracketDepth;
  775.                 }
  776.  
  777.                 if(bracketDepth === 0) {
  778.                     let leftSide = tokens.slice(0, bracketIndex),
  779.                         rightSide = tokens.slice(iter + 1),
  780.                         parsed;
  781.  
  782.                     parsed = this.parseTokens(clone(tokens.slice(bracketIndex + 1, iter)));
  783.  
  784.                     tokens = leftSide.concat(parsed, rightSide);
  785.                     iter += tokens.length - tokensLength;
  786.                     tokensLength = tokens.length;
  787.                 }
  788.             }
  789.  
  790.             if(tokens[iter][1] === '(') {
  791.                 if(bracketDepth === 0) {
  792.                     bracketIndex = iter;
  793.                 }
  794.  
  795.                 ++bracketDepth;
  796.             }
  797.         }
  798.     }
  799.  
  800.     if(bracketDepth > 0) {
  801.         throw new Error('Invalid bracket syntax');
  802.     }
  803.  
  804.     return tokens;
  805. };
  806.  
  807. ExpressionParser.prototype.parseNegatives = function(tkns) {
  808.     'use strict';
  809.  
  810.     var tokens = tkns,
  811.         tokensLength = tokens.length,
  812.         iter;
  813.  
  814.     for(iter = 0; iter < tokensLength; ++iter) {
  815.         // Logic for a negative number
  816.         if(
  817.             tokens[iter][0] === 'operator' &&
  818.             (
  819.                 tokens[iter][1] === '-' ||          // Check it's a minus symbol
  820.                 tokens[iter][1] === '+'             // Or a plus symbol
  821.             ) &&
  822.             (
  823.                 iter - 1 < 0 ||                     // Either there is no previous token...
  824.                 tokens[iter - 1][0] !== 'number'    // Or it's not a number
  825.             ) &&
  826.             iter + 1 < tokensLength &&              // Check there is a proceeding token
  827.             tokens[iter + 1][0] === 'number'        // And it's a number
  828.         ) {
  829.             // Make the next number a negative
  830.             tokens[iter + 1][1] = tokens[iter][1] === '-' ? tokens[iter + 1][1].negated() : tokens[iter + 1][1];
  831.             // Remove this token from stack
  832.             tokens.splice(iter, 1);
  833.             --tokensLength;
  834.             --iter;
  835.             continue;
  836.         }
  837.     }
  838.  
  839.     return tokens;
  840. };
  841.  
  842. ExpressionParser.prototype.parseVariables = function(tkns) {
  843.     'use strict';
  844.  
  845.     var tokens = tkns,
  846.         tokensLength = tokens.length,
  847.         iter;
  848.  
  849.     for(iter = 0; iter < tokensLength; ++iter) {
  850.         if(tokens[iter][0] === 'keyword') {
  851.             if(
  852.                 (
  853.                     iter === tokensLength - 1 ||            // Either this is the last token
  854.                     tokens[iter + 1][0] !== 'assignment'    // Or the next token is not an assignment
  855.                 ) &&
  856.                 (
  857.                     iter === tokensLength - 1 ||
  858.                     (
  859.                         tokens[iter + 1][0] !== 'list' &&
  860.                         tokens[iter + 1][0] !== 'number'
  861.                     )
  862.                 )
  863.             ) {
  864.                 // Check variable exists
  865.                 if(this.isVariable(tokens[iter][1])) {
  866.                     if(
  867.                         iter - 1 >= 0 &&
  868.                         tokens[iter - 1][0] === 'number'
  869.                     ) {
  870.                         tokens[iter][0] = 'number';
  871.                         tokens[iter][1] = this.getVariable(tokens[iter][1]).times(tokens[iter - 1][1]);
  872.  
  873.                         let leftSide = tokens.slice(0, iter - 1),
  874.                             rightSide = tokens.slice(iter);
  875.  
  876.                         tokens = leftSide.concat(rightSide);
  877.  
  878.                         --iter;
  879.                         --tokensLength;
  880.                     } else {
  881.                         tokens[iter][0] = 'number';
  882.                         tokens[iter][1] = this.getVariable(tokens[iter][1]);
  883.                     }
  884.  
  885.                     continue;
  886.                 } else {
  887.                     throw new Error('Undefined variable "' + tokens[iter][1] + '"');
  888.                 }
  889.             }
  890.         }
  891.     }
  892.  
  893.     return tokens;
  894. };
  895.  
  896. ExpressionParser.prototype.parseFunctions = function(tkns) {
  897.     'use strict';
  898.  
  899.     var tokens = tkns,
  900.         tokensLength = tokens.length,
  901.         iter;
  902.  
  903.     for(iter = 0; iter < tokensLength; ++iter) {
  904.         if(
  905.             tokens[iter][0] === 'keyword' &&
  906.             iter + 1 < tokensLength &&
  907.             (
  908.                 tokens[iter + 1][0] === 'number' ||
  909.                 tokens[iter + 1][0] === 'list'
  910.             )
  911.         ) {
  912.             if(
  913.                 tokens[iter][1] in MathFunctions ||
  914.                 allowedMathFunctions.indexOf(tokens[iter][1]) !== -1 ||
  915.                 tokens[iter][1] in this.macros
  916.             ) {
  917.                 let mathFunction,
  918.                     context,
  919.                     macro,
  920.                     primitives;
  921.  
  922.  
  923.                 if(tokens[iter + 1][0] !== 'list') {
  924.                     tokens[iter + 1] = [
  925.                         'list',
  926.                         [
  927.                             tokens[iter + 1]
  928.                         ]
  929.                     ];
  930.                 }
  931.  
  932.                 primitives = getListPrimitiveArray(tokens[iter + 1][1]);
  933.  
  934.                 if(allowedMathFunctions.indexOf(tokens[iter][1]) !== -1) {
  935.                     tokens[iter + 1] = [
  936.                         'number',
  937.                         Decimal[tokens[iter][1]].apply(Decimal, primitives)
  938.                     ];
  939.                 } else if(tokens[iter][1] in MathFunctions) {
  940.                     tokens[iter + 1] = [
  941.                         'number',
  942.                         MathFunctions[tokens[iter][1]].apply(MathFunctions, primitives)
  943.                     ];
  944.                 } else if(tokens[iter][1] in this.macros) {
  945.                     tokens[iter + 1] = [
  946.                         'number',
  947.                         this.runMacro(tokens[iter][1], primitives)
  948.                     ];
  949.                 }
  950.  
  951.                 // Remove this token from stack
  952.                 tokens.splice(iter, 1);
  953.                 --tokensLength;
  954.                 --iter;
  955.             } else {
  956.                 throw new Error('Undefined function "' + tokens[iter][1] + '"');
  957.             }
  958.         }
  959.     }
  960.  
  961.     return tokens;
  962. };
  963.  
  964. ExpressionParser.prototype.parseOperations = function(tkns) {
  965.     'use strict';
  966.  
  967.     // Order of operations
  968.     var operators = ['^', '*', '/', '%', '+', '-'],
  969.         tokens = tkns,
  970.         self = this;
  971.  
  972.     operators.forEach(operator => tokens = self.parseOperator(tokens, operator));
  973.  
  974.     return tokens;
  975. };
  976.  
  977. ExpressionParser.prototype.parseOperator = function(tkns, oprtr) {
  978.     'use strict';
  979.  
  980.     var tokens = tkns,
  981.         operator = oprtr,
  982.         tokensLength = tokens.length,
  983.         iter;
  984.  
  985.     for(iter = 0; iter < tokensLength; ++iter) {
  986.         var token = tokens[iter],
  987.             token_type = token[0],
  988.             token_value = token[1];
  989.  
  990.         if(token_type === 'operator' && token_value === operator) {
  991.             if(
  992.                 iter - 1 >= 0 &&                        // Check there is a previous token
  993.                 iter + 1 < tokensLength &&              // Check there is a next token
  994.                 tokens[iter - 1][0] === 'number' &&     // Check the previous token is a number
  995.                 tokens[iter + 1][0] === 'number'        // Check the next token is a number
  996.             ) {
  997.                 tokens[iter + 1][1] = OperatorFunctions[token_value](tokens[iter - 1][1], tokens[iter + 1][1]);
  998.  
  999.                 let leftSide = tokens.slice(0, iter - 1),
  1000.                     rightSide = tokens.slice(iter + 1);
  1001.  
  1002.                 // Replace sub-expression with the result value
  1003.                 tokens = leftSide.concat(rightSide);
  1004.                 iter += tokens.length - tokensLength;
  1005.                 tokensLength = tokens.length;
  1006.  
  1007.                 continue;
  1008.             } else {
  1009.                 throw new Error('unexpected operator "' + tokens[iter][1] + '"');
  1010.             }
  1011.         }
  1012.     }
  1013.  
  1014.     return tokens;
  1015. };
  1016.  
  1017. /**
  1018.  * Split expression into tokens
  1019.  */
  1020. ExpressionParser.prototype.tokenize = function(expr) {
  1021.     'use strict';
  1022.  
  1023.     // TOKENIZER VARS
  1024.     var expression = expr + ' ', // Append space so that the last character before that space is tokenised
  1025.         expressionLength = expression.length,
  1026.         iter,
  1027.         tokens = [ ],
  1028.         buffer = '';
  1029.  
  1030.     // FLAGS
  1031.     var flag_numeric = false,
  1032.         flag_keyword = false,
  1033.         flag_operator = false,
  1034.         flag_sciNotation = false,
  1035.         flag_sciNotationHasSign = false,
  1036.         flag_hex = false,
  1037.         flag_bin = false,
  1038.         flag_oct = false;
  1039.  
  1040.     // Iterate through expression
  1041.     for(iter = 0; iter < expressionLength; ++iter) {
  1042.         let char = expression.charAt(iter),
  1043.             char_isNumeric = Helpers.isNumeric(char),
  1044.             char_isOperator = Helpers.isOperator(char),
  1045.             char_isAlpha = Helpers.isAlpha(char);
  1046.  
  1047.         if(flag_keyword) {
  1048.             // We've reached the end of the keyword
  1049.             if(!char_isAlpha && !char_isNumeric) {
  1050.                 flag_keyword = false;
  1051.                 tokens.push(['keyword', buffer]);
  1052.                 buffer = '';
  1053.             } else {
  1054.                 buffer += char;
  1055.                 continue;
  1056.             }
  1057.         }
  1058.  
  1059.         if(flag_hex) {
  1060.             if(Helpers.isHexChar(char)) {
  1061.                 buffer += char;
  1062.                 continue;
  1063.             } else {
  1064.                 tokens.push(['number', new Decimal(buffer)]);
  1065.                 buffer = '';
  1066.                 flag_hex = false;
  1067.             }
  1068.         }
  1069.  
  1070.         if(flag_bin) {
  1071.             if(Helpers.isBinChar(char)) {
  1072.                 buffer += char;
  1073.                 continue;
  1074.             } else {
  1075.                 tokens.push(['number', new Decimal(buffer)]);
  1076.                 buffer = '';
  1077.                 flag_bin = false;
  1078.             }
  1079.         }
  1080.  
  1081.         if(flag_oct) {
  1082.             if(Helpers.isOctalChar(char)) {
  1083.                 buffer += char;
  1084.                 continue;
  1085.             } else {
  1086.                 tokens.push(['number', new Decimal(buffer)]);
  1087.                 buffer = '';
  1088.                 flag_oct = false;
  1089.             }
  1090.         }
  1091.  
  1092.         if(flag_sciNotation) {
  1093.             if(!char_isNumeric) {
  1094.                 if((char === '+' || char === '-') && !flag_sciNotationHasSign) {
  1095.                     let lastChar = buffer.charAt(buffer.length - 1);
  1096.  
  1097.                     // There is no sign in the buffer already, so add it
  1098.                     if(lastChar === 'e') {
  1099.                         flag_sciNotationHasSign = true;
  1100.                         buffer += char;
  1101.                         continue;
  1102.                     }
  1103.  
  1104.                     // We already have a sign, and its the last char (ERROR)
  1105.                     else if(lastChar === '+' || lastChar === '-') {
  1106.                         throw new Error('Unexpected char "' + char + '"');
  1107.                     }
  1108.                 } else {
  1109.                     tokens.push(['number', new Decimal(buffer)]);
  1110.                     buffer = '';
  1111.                     flag_sciNotation = false;
  1112.                     flag_sciNotationHasSign = false;
  1113.                 }
  1114.             } else {
  1115.                 flag_sciNotationHasSign = true;
  1116.                 buffer += char;
  1117.                 continue;
  1118.             }
  1119.         }
  1120.  
  1121.         if(flag_numeric) {
  1122.             if(!char_isNumeric) {
  1123.                 // Possible scientific notation
  1124.                 if(char.toLowerCase() === 'e') {
  1125.                     let nextChar = expression.charAt(iter + 1);
  1126.  
  1127.                     // CHeck if next char is valid in sci notation syntax
  1128.                     if(Helpers.isNumeric(nextChar) || nextChar === '+' || nextChar === '-') {
  1129.                         flag_numeric = false;
  1130.                         flag_sciNotation = true;
  1131.  
  1132.                         buffer += char;
  1133.                         continue;
  1134.                     }
  1135.  
  1136.                     // If the next char is not valid sci notation syntax, treat
  1137.                     // the 'e' as a separate token.
  1138.                     else {
  1139.                         flag_numeric = false;
  1140.                         flag_keyword = true;
  1141.  
  1142.                         tokens.push(['number', new Decimal(buffer)]);
  1143.  
  1144.                         buffer = char;
  1145.                         continue;
  1146.                     }
  1147.                 }
  1148.  
  1149.                 // Possible hex notation
  1150.                 if(char.toLowerCase() === 'x') {
  1151.                     let nextChar = expression.charAt(iter + 1);
  1152.  
  1153.                     // If next character is valid hex syntax
  1154.                     if(Helpers.isHexChar(nextChar)) {
  1155.                         flag_numeric = false;
  1156.                         flag_hex = true;
  1157.  
  1158.                         buffer += char;
  1159.                         continue;
  1160.                     }
  1161.  
  1162.                     // Else this is not valid hex syntax, so treat the 'x'
  1163.                     // as separate token.
  1164.                     else {
  1165.                         flag_numeric = false;
  1166.                         flag_keyword = true;
  1167.  
  1168.                         tokens.push(['number', new Decimal(buffer)]);
  1169.  
  1170.                         buffer = char;
  1171.                         continue;
  1172.                     }
  1173.                 }
  1174.  
  1175.                 // Possible hex notation
  1176.                 if(char.toLowerCase() === 'b') {
  1177.                     let nextChar = expression.charAt(iter + 1);
  1178.  
  1179.                     // If next character is valid hex syntax
  1180.                     if(Helpers.isBinChar(nextChar)) {
  1181.                         flag_numeric = false;
  1182.                         flag_bin = true;
  1183.  
  1184.                         buffer += char;
  1185.                         continue;
  1186.                     }
  1187.  
  1188.                     // Else this is not valid hex syntax, so treat the 'b'
  1189.                     // as separate token.
  1190.                     else {
  1191.                         flag_numeric = false;
  1192.                         flag_keyword = true;
  1193.  
  1194.                         tokens.push(['number', new Decimal(buffer)]);
  1195.  
  1196.                         buffer = char;
  1197.                         continue;
  1198.                     }
  1199.                 }
  1200.  
  1201.                 // Possible octal notation
  1202.                 if(char.toLowerCase() === 'o') {
  1203.                     let nextChar = expression.charAt(iter + 1);
  1204.  
  1205.                     // If next character is valid octal syntax
  1206.                     if(Helpers.isOctalChar(nextChar)) {
  1207.                         flag_numeric = false;
  1208.                         flag_oct = true;
  1209.  
  1210.                         buffer += char;
  1211.                         continue;
  1212.                     }
  1213.  
  1214.                     // Else this is not valid octal syntax, so treat the 'o'
  1215.                     // as separate token.
  1216.                     else {
  1217.                         flag_numeric = false;
  1218.                         flag_keyword = true;
  1219.  
  1220.                         tokens.push(['number', new Decimal(buffer)]);
  1221.  
  1222.                         buffer = char;
  1223.                         continue;
  1224.                     }
  1225.                 }
  1226.  
  1227.                 flag_numeric = false;
  1228.                 tokens.push(['number', new Decimal(buffer)]);
  1229.                 buffer = '';
  1230.             }
  1231.         }
  1232.  
  1233.         if(char_isNumeric) {                     // Check for a number
  1234.             flag_numeric = true;
  1235.             buffer += char;
  1236.         } else if(char_isAlpha) {                // Check for a keyword
  1237.             flag_keyword = true;
  1238.             buffer += char;
  1239.         } else if(char_isOperator) {             // Check for an operator
  1240.             tokens.push(['operator', char]);
  1241.         } else if(char === '(' || char === ')') {                // Check for parentheses
  1242.             tokens.push(['bracket', char]);
  1243.         } else if(char === '=') {                // Check for assignment
  1244.             tokens.push(['assignment', char]);
  1245.         } else if(char === ',') {
  1246.             tokens.push(['comma', char]);
  1247.         } else if(char === '{' || char === '}') {
  1248.             tokens.push(['brace', char]);
  1249.         } else if(!Helpers.isWhitespace(char)) { // Check for whitespace
  1250.             throw new Error('Unexpected char "' + char + '"');
  1251.         }
  1252.     }
  1253.  
  1254.     return tokens;
  1255. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement