simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

parse.js (48357B)


      1 import _extends from "@babel/runtime/helpers/extends";
      2 import { factory } from '../utils/factory.js';
      3 import { isAccessorNode, isConstantNode, isFunctionNode, isOperatorNode, isSymbolNode } from '../utils/is.js';
      4 import { deepMap } from '../utils/collection.js';
      5 import { hasOwnProperty } from '../utils/object.js';
      6 var name = 'parse';
      7 var dependencies = ['typed', 'numeric', 'config', 'AccessorNode', 'ArrayNode', 'AssignmentNode', 'BlockNode', 'ConditionalNode', 'ConstantNode', 'FunctionAssignmentNode', 'FunctionNode', 'IndexNode', 'ObjectNode', 'OperatorNode', 'ParenthesisNode', 'RangeNode', 'RelationalNode', 'SymbolNode'];
      8 export var createParse = /* #__PURE__ */factory(name, dependencies, _ref => {
      9   var {
     10     typed,
     11     numeric,
     12     config,
     13     AccessorNode,
     14     ArrayNode,
     15     AssignmentNode,
     16     BlockNode,
     17     ConditionalNode,
     18     ConstantNode,
     19     FunctionAssignmentNode,
     20     FunctionNode,
     21     IndexNode,
     22     ObjectNode,
     23     OperatorNode,
     24     ParenthesisNode,
     25     RangeNode,
     26     RelationalNode,
     27     SymbolNode
     28   } = _ref;
     29 
     30   /**
     31    * Parse an expression. Returns a node tree, which can be evaluated by
     32    * invoking node.evaluate().
     33    *
     34    * Note the evaluating arbitrary expressions may involve security risks,
     35    * see [https://mathjs.org/docs/expressions/security.html](https://mathjs.org/docs/expressions/security.html) for more information.
     36    *
     37    * Syntax:
     38    *
     39    *     math.parse(expr)
     40    *     math.parse(expr, options)
     41    *     math.parse([expr1, expr2, expr3, ...])
     42    *     math.parse([expr1, expr2, expr3, ...], options)
     43    *
     44    * Example:
     45    *
     46    *     const node1 = math.parse('sqrt(3^2 + 4^2)')
     47    *     node1.compile().evaluate() // 5
     48    *
     49    *     let scope = {a:3, b:4}
     50    *     const node2 = math.parse('a * b') // 12
     51    *     const code2 = node2.compile()
     52    *     code2.evaluate(scope) // 12
     53    *     scope.a = 5
     54    *     code2.evaluate(scope) // 20
     55    *
     56    *     const nodes = math.parse(['a = 3', 'b = 4', 'a * b'])
     57    *     nodes[2].compile().evaluate() // 12
     58    *
     59    * See also:
     60    *
     61    *     evaluate, compile
     62    *
     63    * @param {string | string[] | Matrix} expr          Expression to be parsed
     64    * @param {{nodes: Object<string, Node>}} [options]  Available options:
     65    *                                                   - `nodes` a set of custom nodes
     66    * @return {Node | Node[]} node
     67    * @throws {Error}
     68    */
     69   var parse = typed(name, {
     70     string: function string(expression) {
     71       return parseStart(expression, {});
     72     },
     73     'Array | Matrix': function ArrayMatrix(expressions) {
     74       return parseMultiple(expressions, {});
     75     },
     76     'string, Object': function stringObject(expression, options) {
     77       var extraNodes = options.nodes !== undefined ? options.nodes : {};
     78       return parseStart(expression, extraNodes);
     79     },
     80     'Array | Matrix, Object': parseMultiple
     81   });
     82 
     83   function parseMultiple(expressions) {
     84     var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
     85     var extraNodes = options.nodes !== undefined ? options.nodes : {}; // parse an array or matrix with expressions
     86 
     87     return deepMap(expressions, function (elem) {
     88       if (typeof elem !== 'string') throw new TypeError('String expected');
     89       return parseStart(elem, extraNodes);
     90     });
     91   } // token types enumeration
     92 
     93 
     94   var TOKENTYPE = {
     95     NULL: 0,
     96     DELIMITER: 1,
     97     NUMBER: 2,
     98     SYMBOL: 3,
     99     UNKNOWN: 4
    100   }; // map with all delimiters
    101 
    102   var DELIMITERS = {
    103     ',': true,
    104     '(': true,
    105     ')': true,
    106     '[': true,
    107     ']': true,
    108     '{': true,
    109     '}': true,
    110     '"': true,
    111     '\'': true,
    112     ';': true,
    113     '+': true,
    114     '-': true,
    115     '*': true,
    116     '.*': true,
    117     '/': true,
    118     './': true,
    119     '%': true,
    120     '^': true,
    121     '.^': true,
    122     '~': true,
    123     '!': true,
    124     '&': true,
    125     '|': true,
    126     '^|': true,
    127     '=': true,
    128     ':': true,
    129     '?': true,
    130     '==': true,
    131     '!=': true,
    132     '<': true,
    133     '>': true,
    134     '<=': true,
    135     '>=': true,
    136     '<<': true,
    137     '>>': true,
    138     '>>>': true
    139   }; // map with all named delimiters
    140 
    141   var NAMED_DELIMITERS = {
    142     mod: true,
    143     to: true,
    144     in: true,
    145     and: true,
    146     xor: true,
    147     or: true,
    148     not: true
    149   };
    150   var CONSTANTS = {
    151     true: true,
    152     false: false,
    153     null: null,
    154     undefined: undefined
    155   };
    156   var NUMERIC_CONSTANTS = ['NaN', 'Infinity'];
    157 
    158   function initialState() {
    159     return {
    160       extraNodes: {},
    161       // current extra nodes, must be careful not to mutate
    162       expression: '',
    163       // current expression
    164       comment: '',
    165       // last parsed comment
    166       index: 0,
    167       // current index in expr
    168       token: '',
    169       // current token
    170       tokenType: TOKENTYPE.NULL,
    171       // type of the token
    172       nestingLevel: 0,
    173       // level of nesting inside parameters, used to ignore newline characters
    174       conditionalLevel: null // when a conditional is being parsed, the level of the conditional is stored here
    175 
    176     };
    177   }
    178   /**
    179    * View upto `length` characters of the expression starting at the current character.
    180    *
    181    * @param {Object} state
    182    * @param {number} [length=1] Number of characters to view
    183    * @returns {string}
    184    * @private
    185    */
    186 
    187 
    188   function currentString(state, length) {
    189     return state.expression.substr(state.index, length);
    190   }
    191   /**
    192    * View the current character. Returns '' if end of expression is reached.
    193    *
    194    * @param {Object} state
    195    * @returns {string}
    196    * @private
    197    */
    198 
    199 
    200   function currentCharacter(state) {
    201     return currentString(state, 1);
    202   }
    203   /**
    204    * Get the next character from the expression.
    205    * The character is stored into the char c. If the end of the expression is
    206    * reached, the function puts an empty string in c.
    207    * @private
    208    */
    209 
    210 
    211   function next(state) {
    212     state.index++;
    213   }
    214   /**
    215    * Preview the previous character from the expression.
    216    * @return {string} cNext
    217    * @private
    218    */
    219 
    220 
    221   function prevCharacter(state) {
    222     return state.expression.charAt(state.index - 1);
    223   }
    224   /**
    225    * Preview the next character from the expression.
    226    * @return {string} cNext
    227    * @private
    228    */
    229 
    230 
    231   function nextCharacter(state) {
    232     return state.expression.charAt(state.index + 1);
    233   }
    234   /**
    235    * Get next token in the current string expr.
    236    * The token and token type are available as token and tokenType
    237    * @private
    238    */
    239 
    240 
    241   function getToken(state) {
    242     state.tokenType = TOKENTYPE.NULL;
    243     state.token = '';
    244     state.comment = ''; // skip over ignored characters:
    245 
    246     while (true) {
    247       // comments:
    248       if (currentCharacter(state) === '#') {
    249         while (currentCharacter(state) !== '\n' && currentCharacter(state) !== '') {
    250           state.comment += currentCharacter(state);
    251           next(state);
    252         }
    253       } // whitespace: space, tab, and newline when inside parameters
    254 
    255 
    256       if (parse.isWhitespace(currentCharacter(state), state.nestingLevel)) {
    257         next(state);
    258       } else {
    259         break;
    260       }
    261     } // check for end of expression
    262 
    263 
    264     if (currentCharacter(state) === '') {
    265       // token is still empty
    266       state.tokenType = TOKENTYPE.DELIMITER;
    267       return;
    268     } // check for new line character
    269 
    270 
    271     if (currentCharacter(state) === '\n' && !state.nestingLevel) {
    272       state.tokenType = TOKENTYPE.DELIMITER;
    273       state.token = currentCharacter(state);
    274       next(state);
    275       return;
    276     }
    277 
    278     var c1 = currentCharacter(state);
    279     var c2 = currentString(state, 2);
    280     var c3 = currentString(state, 3);
    281 
    282     if (c3.length === 3 && DELIMITERS[c3]) {
    283       state.tokenType = TOKENTYPE.DELIMITER;
    284       state.token = c3;
    285       next(state);
    286       next(state);
    287       next(state);
    288       return;
    289     } // check for delimiters consisting of 2 characters
    290 
    291 
    292     if (c2.length === 2 && DELIMITERS[c2]) {
    293       state.tokenType = TOKENTYPE.DELIMITER;
    294       state.token = c2;
    295       next(state);
    296       next(state);
    297       return;
    298     } // check for delimiters consisting of 1 character
    299 
    300 
    301     if (DELIMITERS[c1]) {
    302       state.tokenType = TOKENTYPE.DELIMITER;
    303       state.token = c1;
    304       next(state);
    305       return;
    306     } // check for a number
    307 
    308 
    309     if (parse.isDigitDot(c1)) {
    310       state.tokenType = TOKENTYPE.NUMBER; // check for binary, octal, or hex
    311 
    312       var _c = currentString(state, 2);
    313 
    314       if (_c === '0b' || _c === '0o' || _c === '0x') {
    315         state.token += currentCharacter(state);
    316         next(state);
    317         state.token += currentCharacter(state);
    318         next(state);
    319 
    320         while (parse.isHexDigit(currentCharacter(state))) {
    321           state.token += currentCharacter(state);
    322           next(state);
    323         }
    324 
    325         if (currentCharacter(state) === '.') {
    326           // this number has a radix point
    327           state.token += '.';
    328           next(state); // get the digits after the radix
    329 
    330           while (parse.isHexDigit(currentCharacter(state))) {
    331             state.token += currentCharacter(state);
    332             next(state);
    333           }
    334         } else if (currentCharacter(state) === 'i') {
    335           // this number has a word size suffix
    336           state.token += 'i';
    337           next(state); // get the word size
    338 
    339           while (parse.isDigit(currentCharacter(state))) {
    340             state.token += currentCharacter(state);
    341             next(state);
    342           }
    343         }
    344 
    345         return;
    346       } // get number, can have a single dot
    347 
    348 
    349       if (currentCharacter(state) === '.') {
    350         state.token += currentCharacter(state);
    351         next(state);
    352 
    353         if (!parse.isDigit(currentCharacter(state))) {
    354           // this is no number, it is just a dot (can be dot notation)
    355           state.tokenType = TOKENTYPE.DELIMITER;
    356           return;
    357         }
    358       } else {
    359         while (parse.isDigit(currentCharacter(state))) {
    360           state.token += currentCharacter(state);
    361           next(state);
    362         }
    363 
    364         if (parse.isDecimalMark(currentCharacter(state), nextCharacter(state))) {
    365           state.token += currentCharacter(state);
    366           next(state);
    367         }
    368       }
    369 
    370       while (parse.isDigit(currentCharacter(state))) {
    371         state.token += currentCharacter(state);
    372         next(state);
    373       } // check for exponential notation like "2.3e-4", "1.23e50" or "2e+4"
    374 
    375 
    376       if (currentCharacter(state) === 'E' || currentCharacter(state) === 'e') {
    377         if (parse.isDigit(nextCharacter(state)) || nextCharacter(state) === '-' || nextCharacter(state) === '+') {
    378           state.token += currentCharacter(state);
    379           next(state);
    380 
    381           if (currentCharacter(state) === '+' || currentCharacter(state) === '-') {
    382             state.token += currentCharacter(state);
    383             next(state);
    384           } // Scientific notation MUST be followed by an exponent
    385 
    386 
    387           if (!parse.isDigit(currentCharacter(state))) {
    388             throw createSyntaxError(state, 'Digit expected, got "' + currentCharacter(state) + '"');
    389           }
    390 
    391           while (parse.isDigit(currentCharacter(state))) {
    392             state.token += currentCharacter(state);
    393             next(state);
    394           }
    395 
    396           if (parse.isDecimalMark(currentCharacter(state), nextCharacter(state))) {
    397             throw createSyntaxError(state, 'Digit expected, got "' + currentCharacter(state) + '"');
    398           }
    399         } else if (nextCharacter(state) === '.') {
    400           next(state);
    401           throw createSyntaxError(state, 'Digit expected, got "' + currentCharacter(state) + '"');
    402         }
    403       }
    404 
    405       return;
    406     } // check for variables, functions, named operators
    407 
    408 
    409     if (parse.isAlpha(currentCharacter(state), prevCharacter(state), nextCharacter(state))) {
    410       while (parse.isAlpha(currentCharacter(state), prevCharacter(state), nextCharacter(state)) || parse.isDigit(currentCharacter(state))) {
    411         state.token += currentCharacter(state);
    412         next(state);
    413       }
    414 
    415       if (hasOwnProperty(NAMED_DELIMITERS, state.token)) {
    416         state.tokenType = TOKENTYPE.DELIMITER;
    417       } else {
    418         state.tokenType = TOKENTYPE.SYMBOL;
    419       }
    420 
    421       return;
    422     } // something unknown is found, wrong characters -> a syntax error
    423 
    424 
    425     state.tokenType = TOKENTYPE.UNKNOWN;
    426 
    427     while (currentCharacter(state) !== '') {
    428       state.token += currentCharacter(state);
    429       next(state);
    430     }
    431 
    432     throw createSyntaxError(state, 'Syntax error in part "' + state.token + '"');
    433   }
    434   /**
    435    * Get next token and skip newline tokens
    436    */
    437 
    438 
    439   function getTokenSkipNewline(state) {
    440     do {
    441       getToken(state);
    442     } while (state.token === '\n'); // eslint-disable-line no-unmodified-loop-condition
    443 
    444   }
    445   /**
    446    * Open parameters.
    447    * New line characters will be ignored until closeParams(state) is called
    448    */
    449 
    450 
    451   function openParams(state) {
    452     state.nestingLevel++;
    453   }
    454   /**
    455    * Close parameters.
    456    * New line characters will no longer be ignored
    457    */
    458 
    459 
    460   function closeParams(state) {
    461     state.nestingLevel--;
    462   }
    463   /**
    464    * Checks whether the current character `c` is a valid alpha character:
    465    *
    466    * - A latin letter (upper or lower case) Ascii: a-z, A-Z
    467    * - An underscore                        Ascii: _
    468    * - A dollar sign                        Ascii: $
    469    * - A latin letter with accents          Unicode: \u00C0 - \u02AF
    470    * - A greek letter                       Unicode: \u0370 - \u03FF
    471    * - A mathematical alphanumeric symbol   Unicode: \u{1D400} - \u{1D7FF} excluding invalid code points
    472    *
    473    * The previous and next characters are needed to determine whether
    474    * this character is part of a unicode surrogate pair.
    475    *
    476    * @param {string} c      Current character in the expression
    477    * @param {string} cPrev  Previous character
    478    * @param {string} cNext  Next character
    479    * @return {boolean}
    480    */
    481 
    482 
    483   parse.isAlpha = function isAlpha(c, cPrev, cNext) {
    484     return parse.isValidLatinOrGreek(c) || parse.isValidMathSymbol(c, cNext) || parse.isValidMathSymbol(cPrev, c);
    485   };
    486   /**
    487    * Test whether a character is a valid latin, greek, or letter-like character
    488    * @param {string} c
    489    * @return {boolean}
    490    */
    491 
    492 
    493   parse.isValidLatinOrGreek = function isValidLatinOrGreek(c) {
    494     return /^[a-zA-Z_$\u00C0-\u02AF\u0370-\u03FF\u2100-\u214F]$/.test(c);
    495   };
    496   /**
    497    * Test whether two given 16 bit characters form a surrogate pair of a
    498    * unicode math symbol.
    499    *
    500    * https://unicode-table.com/en/
    501    * https://www.wikiwand.com/en/Mathematical_operators_and_symbols_in_Unicode
    502    *
    503    * Note: In ES6 will be unicode aware:
    504    * https://stackoverflow.com/questions/280712/javascript-unicode-regexes
    505    * https://mathiasbynens.be/notes/es6-unicode-regex
    506    *
    507    * @param {string} high
    508    * @param {string} low
    509    * @return {boolean}
    510    */
    511 
    512 
    513   parse.isValidMathSymbol = function isValidMathSymbol(high, low) {
    514     return /^[\uD835]$/.test(high) && /^[\uDC00-\uDFFF]$/.test(low) && /^[^\uDC55\uDC9D\uDCA0\uDCA1\uDCA3\uDCA4\uDCA7\uDCA8\uDCAD\uDCBA\uDCBC\uDCC4\uDD06\uDD0B\uDD0C\uDD15\uDD1D\uDD3A\uDD3F\uDD45\uDD47-\uDD49\uDD51\uDEA6\uDEA7\uDFCC\uDFCD]$/.test(low);
    515   };
    516   /**
    517    * Check whether given character c is a white space character: space, tab, or enter
    518    * @param {string} c
    519    * @param {number} nestingLevel
    520    * @return {boolean}
    521    */
    522 
    523 
    524   parse.isWhitespace = function isWhitespace(c, nestingLevel) {
    525     // TODO: also take '\r' carriage return as newline? Or does that give problems on mac?
    526     return c === ' ' || c === '\t' || c === '\n' && nestingLevel > 0;
    527   };
    528   /**
    529    * Test whether the character c is a decimal mark (dot).
    530    * This is the case when it's not the start of a delimiter '.*', './', or '.^'
    531    * @param {string} c
    532    * @param {string} cNext
    533    * @return {boolean}
    534    */
    535 
    536 
    537   parse.isDecimalMark = function isDecimalMark(c, cNext) {
    538     return c === '.' && cNext !== '/' && cNext !== '*' && cNext !== '^';
    539   };
    540   /**
    541    * checks if the given char c is a digit or dot
    542    * @param {string} c   a string with one character
    543    * @return {boolean}
    544    */
    545 
    546 
    547   parse.isDigitDot = function isDigitDot(c) {
    548     return c >= '0' && c <= '9' || c === '.';
    549   };
    550   /**
    551    * checks if the given char c is a digit
    552    * @param {string} c   a string with one character
    553    * @return {boolean}
    554    */
    555 
    556 
    557   parse.isDigit = function isDigit(c) {
    558     return c >= '0' && c <= '9';
    559   };
    560   /**
    561    * checks if the given char c is a hex digit
    562    * @param {string} c   a string with one character
    563    * @return {boolean}
    564    */
    565 
    566 
    567   parse.isHexDigit = function isHexDigit(c) {
    568     return c >= '0' && c <= '9' || c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F';
    569   };
    570   /**
    571    * Start of the parse levels below, in order of precedence
    572    * @return {Node} node
    573    * @private
    574    */
    575 
    576 
    577   function parseStart(expression, extraNodes) {
    578     var state = initialState();
    579 
    580     _extends(state, {
    581       expression,
    582       extraNodes
    583     });
    584 
    585     getToken(state);
    586     var node = parseBlock(state); // check for garbage at the end of the expression
    587     // an expression ends with a empty character '' and tokenType DELIMITER
    588 
    589     if (state.token !== '') {
    590       if (state.tokenType === TOKENTYPE.DELIMITER) {
    591         // user entered a not existing operator like "//"
    592         // TODO: give hints for aliases, for example with "<>" give as hint " did you mean !== ?"
    593         throw createError(state, 'Unexpected operator ' + state.token);
    594       } else {
    595         throw createSyntaxError(state, 'Unexpected part "' + state.token + '"');
    596       }
    597     }
    598 
    599     return node;
    600   }
    601   /**
    602    * Parse a block with expressions. Expressions can be separated by a newline
    603    * character '\n', or by a semicolon ';'. In case of a semicolon, no output
    604    * of the preceding line is returned.
    605    * @return {Node} node
    606    * @private
    607    */
    608 
    609 
    610   function parseBlock(state) {
    611     var node;
    612     var blocks = [];
    613     var visible;
    614 
    615     if (state.token !== '' && state.token !== '\n' && state.token !== ';') {
    616       node = parseAssignment(state);
    617       node.comment = state.comment;
    618     } // TODO: simplify this loop
    619 
    620 
    621     while (state.token === '\n' || state.token === ';') {
    622       // eslint-disable-line no-unmodified-loop-condition
    623       if (blocks.length === 0 && node) {
    624         visible = state.token !== ';';
    625         blocks.push({
    626           node: node,
    627           visible: visible
    628         });
    629       }
    630 
    631       getToken(state);
    632 
    633       if (state.token !== '\n' && state.token !== ';' && state.token !== '') {
    634         node = parseAssignment(state);
    635         node.comment = state.comment;
    636         visible = state.token !== ';';
    637         blocks.push({
    638           node: node,
    639           visible: visible
    640         });
    641       }
    642     }
    643 
    644     if (blocks.length > 0) {
    645       return new BlockNode(blocks);
    646     } else {
    647       if (!node) {
    648         node = new ConstantNode(undefined);
    649         node.comment = state.comment;
    650       }
    651 
    652       return node;
    653     }
    654   }
    655   /**
    656    * Assignment of a function or variable,
    657    * - can be a variable like 'a=2.3'
    658    * - or a updating an existing variable like 'matrix(2,3:5)=[6,7,8]'
    659    * - defining a function like 'f(x) = x^2'
    660    * @return {Node} node
    661    * @private
    662    */
    663 
    664 
    665   function parseAssignment(state) {
    666     var name, args, value, valid;
    667     var node = parseConditional(state);
    668 
    669     if (state.token === '=') {
    670       if (isSymbolNode(node)) {
    671         // parse a variable assignment like 'a = 2/3'
    672         name = node.name;
    673         getTokenSkipNewline(state);
    674         value = parseAssignment(state);
    675         return new AssignmentNode(new SymbolNode(name), value);
    676       } else if (isAccessorNode(node)) {
    677         // parse a matrix subset assignment like 'A[1,2] = 4'
    678         getTokenSkipNewline(state);
    679         value = parseAssignment(state);
    680         return new AssignmentNode(node.object, node.index, value);
    681       } else if (isFunctionNode(node) && isSymbolNode(node.fn)) {
    682         // parse function assignment like 'f(x) = x^2'
    683         valid = true;
    684         args = [];
    685         name = node.name;
    686         node.args.forEach(function (arg, index) {
    687           if (isSymbolNode(arg)) {
    688             args[index] = arg.name;
    689           } else {
    690             valid = false;
    691           }
    692         });
    693 
    694         if (valid) {
    695           getTokenSkipNewline(state);
    696           value = parseAssignment(state);
    697           return new FunctionAssignmentNode(name, args, value);
    698         }
    699       }
    700 
    701       throw createSyntaxError(state, 'Invalid left hand side of assignment operator =');
    702     }
    703 
    704     return node;
    705   }
    706   /**
    707    * conditional operation
    708    *
    709    *     condition ? truePart : falsePart
    710    *
    711    * Note: conditional operator is right-associative
    712    *
    713    * @return {Node} node
    714    * @private
    715    */
    716 
    717 
    718   function parseConditional(state) {
    719     var node = parseLogicalOr(state);
    720 
    721     while (state.token === '?') {
    722       // eslint-disable-line no-unmodified-loop-condition
    723       // set a conditional level, the range operator will be ignored as long
    724       // as conditionalLevel === state.nestingLevel.
    725       var prev = state.conditionalLevel;
    726       state.conditionalLevel = state.nestingLevel;
    727       getTokenSkipNewline(state);
    728       var condition = node;
    729       var trueExpr = parseAssignment(state);
    730       if (state.token !== ':') throw createSyntaxError(state, 'False part of conditional expression expected');
    731       state.conditionalLevel = null;
    732       getTokenSkipNewline(state);
    733       var falseExpr = parseAssignment(state); // Note: check for conditional operator again, right associativity
    734 
    735       node = new ConditionalNode(condition, trueExpr, falseExpr); // restore the previous conditional level
    736 
    737       state.conditionalLevel = prev;
    738     }
    739 
    740     return node;
    741   }
    742   /**
    743    * logical or, 'x or y'
    744    * @return {Node} node
    745    * @private
    746    */
    747 
    748 
    749   function parseLogicalOr(state) {
    750     var node = parseLogicalXor(state);
    751 
    752     while (state.token === 'or') {
    753       // eslint-disable-line no-unmodified-loop-condition
    754       getTokenSkipNewline(state);
    755       node = new OperatorNode('or', 'or', [node, parseLogicalXor(state)]);
    756     }
    757 
    758     return node;
    759   }
    760   /**
    761    * logical exclusive or, 'x xor y'
    762    * @return {Node} node
    763    * @private
    764    */
    765 
    766 
    767   function parseLogicalXor(state) {
    768     var node = parseLogicalAnd(state);
    769 
    770     while (state.token === 'xor') {
    771       // eslint-disable-line no-unmodified-loop-condition
    772       getTokenSkipNewline(state);
    773       node = new OperatorNode('xor', 'xor', [node, parseLogicalAnd(state)]);
    774     }
    775 
    776     return node;
    777   }
    778   /**
    779    * logical and, 'x and y'
    780    * @return {Node} node
    781    * @private
    782    */
    783 
    784 
    785   function parseLogicalAnd(state) {
    786     var node = parseBitwiseOr(state);
    787 
    788     while (state.token === 'and') {
    789       // eslint-disable-line no-unmodified-loop-condition
    790       getTokenSkipNewline(state);
    791       node = new OperatorNode('and', 'and', [node, parseBitwiseOr(state)]);
    792     }
    793 
    794     return node;
    795   }
    796   /**
    797    * bitwise or, 'x | y'
    798    * @return {Node} node
    799    * @private
    800    */
    801 
    802 
    803   function parseBitwiseOr(state) {
    804     var node = parseBitwiseXor(state);
    805 
    806     while (state.token === '|') {
    807       // eslint-disable-line no-unmodified-loop-condition
    808       getTokenSkipNewline(state);
    809       node = new OperatorNode('|', 'bitOr', [node, parseBitwiseXor(state)]);
    810     }
    811 
    812     return node;
    813   }
    814   /**
    815    * bitwise exclusive or (xor), 'x ^| y'
    816    * @return {Node} node
    817    * @private
    818    */
    819 
    820 
    821   function parseBitwiseXor(state) {
    822     var node = parseBitwiseAnd(state);
    823 
    824     while (state.token === '^|') {
    825       // eslint-disable-line no-unmodified-loop-condition
    826       getTokenSkipNewline(state);
    827       node = new OperatorNode('^|', 'bitXor', [node, parseBitwiseAnd(state)]);
    828     }
    829 
    830     return node;
    831   }
    832   /**
    833    * bitwise and, 'x & y'
    834    * @return {Node} node
    835    * @private
    836    */
    837 
    838 
    839   function parseBitwiseAnd(state) {
    840     var node = parseRelational(state);
    841 
    842     while (state.token === '&') {
    843       // eslint-disable-line no-unmodified-loop-condition
    844       getTokenSkipNewline(state);
    845       node = new OperatorNode('&', 'bitAnd', [node, parseRelational(state)]);
    846     }
    847 
    848     return node;
    849   }
    850   /**
    851    * Parse a chained conditional, like 'a > b >= c'
    852    * @return {Node} node
    853    */
    854 
    855 
    856   function parseRelational(state) {
    857     var params = [parseShift(state)];
    858     var conditionals = [];
    859     var operators = {
    860       '==': 'equal',
    861       '!=': 'unequal',
    862       '<': 'smaller',
    863       '>': 'larger',
    864       '<=': 'smallerEq',
    865       '>=': 'largerEq'
    866     };
    867 
    868     while (hasOwnProperty(operators, state.token)) {
    869       // eslint-disable-line no-unmodified-loop-condition
    870       var cond = {
    871         name: state.token,
    872         fn: operators[state.token]
    873       };
    874       conditionals.push(cond);
    875       getTokenSkipNewline(state);
    876       params.push(parseShift(state));
    877     }
    878 
    879     if (params.length === 1) {
    880       return params[0];
    881     } else if (params.length === 2) {
    882       return new OperatorNode(conditionals[0].name, conditionals[0].fn, params);
    883     } else {
    884       return new RelationalNode(conditionals.map(c => c.fn), params);
    885     }
    886   }
    887   /**
    888    * Bitwise left shift, bitwise right arithmetic shift, bitwise right logical shift
    889    * @return {Node} node
    890    * @private
    891    */
    892 
    893 
    894   function parseShift(state) {
    895     var node, name, fn, params;
    896     node = parseConversion(state);
    897     var operators = {
    898       '<<': 'leftShift',
    899       '>>': 'rightArithShift',
    900       '>>>': 'rightLogShift'
    901     };
    902 
    903     while (hasOwnProperty(operators, state.token)) {
    904       name = state.token;
    905       fn = operators[name];
    906       getTokenSkipNewline(state);
    907       params = [node, parseConversion(state)];
    908       node = new OperatorNode(name, fn, params);
    909     }
    910 
    911     return node;
    912   }
    913   /**
    914    * conversion operators 'to' and 'in'
    915    * @return {Node} node
    916    * @private
    917    */
    918 
    919 
    920   function parseConversion(state) {
    921     var node, name, fn, params;
    922     node = parseRange(state);
    923     var operators = {
    924       to: 'to',
    925       in: 'to' // alias of 'to'
    926 
    927     };
    928 
    929     while (hasOwnProperty(operators, state.token)) {
    930       name = state.token;
    931       fn = operators[name];
    932       getTokenSkipNewline(state);
    933 
    934       if (name === 'in' && state.token === '') {
    935         // end of expression -> this is the unit 'in' ('inch')
    936         node = new OperatorNode('*', 'multiply', [node, new SymbolNode('in')], true);
    937       } else {
    938         // operator 'a to b' or 'a in b'
    939         params = [node, parseRange(state)];
    940         node = new OperatorNode(name, fn, params);
    941       }
    942     }
    943 
    944     return node;
    945   }
    946   /**
    947    * parse range, "start:end", "start:step:end", ":", "start:", ":end", etc
    948    * @return {Node} node
    949    * @private
    950    */
    951 
    952 
    953   function parseRange(state) {
    954     var node;
    955     var params = [];
    956 
    957     if (state.token === ':') {
    958       // implicit start=1 (one-based)
    959       node = new ConstantNode(1);
    960     } else {
    961       // explicit start
    962       node = parseAddSubtract(state);
    963     }
    964 
    965     if (state.token === ':' && state.conditionalLevel !== state.nestingLevel) {
    966       // we ignore the range operator when a conditional operator is being processed on the same level
    967       params.push(node); // parse step and end
    968 
    969       while (state.token === ':' && params.length < 3) {
    970         // eslint-disable-line no-unmodified-loop-condition
    971         getTokenSkipNewline(state);
    972 
    973         if (state.token === ')' || state.token === ']' || state.token === ',' || state.token === '') {
    974           // implicit end
    975           params.push(new SymbolNode('end'));
    976         } else {
    977           // explicit end
    978           params.push(parseAddSubtract(state));
    979         }
    980       }
    981 
    982       if (params.length === 3) {
    983         // params = [start, step, end]
    984         node = new RangeNode(params[0], params[2], params[1]); // start, end, step
    985       } else {
    986         // length === 2
    987         // params = [start, end]
    988         node = new RangeNode(params[0], params[1]); // start, end
    989       }
    990     }
    991 
    992     return node;
    993   }
    994   /**
    995    * add or subtract
    996    * @return {Node} node
    997    * @private
    998    */
    999 
   1000 
   1001   function parseAddSubtract(state) {
   1002     var node, name, fn, params;
   1003     node = parseMultiplyDivide(state);
   1004     var operators = {
   1005       '+': 'add',
   1006       '-': 'subtract'
   1007     };
   1008 
   1009     while (hasOwnProperty(operators, state.token)) {
   1010       name = state.token;
   1011       fn = operators[name];
   1012       getTokenSkipNewline(state);
   1013       var rightNode = parseMultiplyDivide(state);
   1014 
   1015       if (rightNode.isPercentage) {
   1016         params = [node, new OperatorNode('*', 'multiply', [node, rightNode])];
   1017       } else {
   1018         params = [node, rightNode];
   1019       }
   1020 
   1021       node = new OperatorNode(name, fn, params);
   1022     }
   1023 
   1024     return node;
   1025   }
   1026   /**
   1027    * multiply, divide, modulus
   1028    * @return {Node} node
   1029    * @private
   1030    */
   1031 
   1032 
   1033   function parseMultiplyDivide(state) {
   1034     var node, last, name, fn;
   1035     node = parseImplicitMultiplication(state);
   1036     last = node;
   1037     var operators = {
   1038       '*': 'multiply',
   1039       '.*': 'dotMultiply',
   1040       '/': 'divide',
   1041       './': 'dotDivide'
   1042     };
   1043 
   1044     while (true) {
   1045       if (hasOwnProperty(operators, state.token)) {
   1046         // explicit operators
   1047         name = state.token;
   1048         fn = operators[name];
   1049         getTokenSkipNewline(state);
   1050         last = parseImplicitMultiplication(state);
   1051         node = new OperatorNode(name, fn, [node, last]);
   1052       } else {
   1053         break;
   1054       }
   1055     }
   1056 
   1057     return node;
   1058   }
   1059   /**
   1060    * implicit multiplication
   1061    * @return {Node} node
   1062    * @private
   1063    */
   1064 
   1065 
   1066   function parseImplicitMultiplication(state) {
   1067     var node, last;
   1068     node = parseRule2(state);
   1069     last = node;
   1070 
   1071     while (true) {
   1072       if (state.tokenType === TOKENTYPE.SYMBOL || state.token === 'in' && isConstantNode(node) || state.tokenType === TOKENTYPE.NUMBER && !isConstantNode(last) && (!isOperatorNode(last) || last.op === '!') || state.token === '(') {
   1073         // parse implicit multiplication
   1074         //
   1075         // symbol:      implicit multiplication like '2a', '(2+3)a', 'a b'
   1076         // number:      implicit multiplication like '(2+3)2'
   1077         // parenthesis: implicit multiplication like '2(3+4)', '(3+4)(1+2)'
   1078         last = parseRule2(state);
   1079         node = new OperatorNode('*', 'multiply', [node, last], true
   1080         /* implicit */
   1081         );
   1082       } else {
   1083         break;
   1084       }
   1085     }
   1086 
   1087     return node;
   1088   }
   1089   /**
   1090    * Infamous "rule 2" as described in https://github.com/josdejong/mathjs/issues/792#issuecomment-361065370
   1091    * Explicit division gets higher precedence than implicit multiplication
   1092    * when the division matches this pattern: [number] / [number] [symbol]
   1093    * @return {Node} node
   1094    * @private
   1095    */
   1096 
   1097 
   1098   function parseRule2(state) {
   1099     var node = parsePercentage(state);
   1100     var last = node;
   1101     var tokenStates = [];
   1102 
   1103     while (true) {
   1104       // Match the "number /" part of the pattern "number / number symbol"
   1105       if (state.token === '/' && isConstantNode(last)) {
   1106         // Look ahead to see if the next token is a number
   1107         tokenStates.push(_extends({}, state));
   1108         getTokenSkipNewline(state); // Match the "number / number" part of the pattern
   1109 
   1110         if (state.tokenType === TOKENTYPE.NUMBER) {
   1111           // Look ahead again
   1112           tokenStates.push(_extends({}, state));
   1113           getTokenSkipNewline(state); // Match the "symbol" part of the pattern, or a left parenthesis
   1114 
   1115           if (state.tokenType === TOKENTYPE.SYMBOL || state.token === '(') {
   1116             // We've matched the pattern "number / number symbol".
   1117             // Rewind once and build the "number / number" node; the symbol will be consumed later
   1118             _extends(state, tokenStates.pop());
   1119 
   1120             tokenStates.pop();
   1121             last = parsePercentage(state);
   1122             node = new OperatorNode('/', 'divide', [node, last]);
   1123           } else {
   1124             // Not a match, so rewind
   1125             tokenStates.pop();
   1126 
   1127             _extends(state, tokenStates.pop());
   1128 
   1129             break;
   1130           }
   1131         } else {
   1132           // Not a match, so rewind
   1133           _extends(state, tokenStates.pop());
   1134 
   1135           break;
   1136         }
   1137       } else {
   1138         break;
   1139       }
   1140     }
   1141 
   1142     return node;
   1143   }
   1144   /**
   1145    * percentage or mod
   1146    * @return {Node} node
   1147    * @private
   1148    */
   1149 
   1150 
   1151   function parsePercentage(state) {
   1152     var node, name, fn, params;
   1153     node = parseUnary(state);
   1154     var operators = {
   1155       '%': 'mod',
   1156       mod: 'mod'
   1157     };
   1158 
   1159     while (hasOwnProperty(operators, state.token)) {
   1160       name = state.token;
   1161       fn = operators[name];
   1162       getTokenSkipNewline(state);
   1163 
   1164       if (name === '%' && state.tokenType === TOKENTYPE.DELIMITER && state.token !== '(') {
   1165         // If the expression contains only %, then treat that as /100
   1166         node = new OperatorNode('/', 'divide', [node, new ConstantNode(100)], false, true);
   1167       } else {
   1168         params = [node, parseUnary(state)];
   1169         node = new OperatorNode(name, fn, params);
   1170       }
   1171     }
   1172 
   1173     return node;
   1174   }
   1175   /**
   1176    * Unary plus and minus, and logical and bitwise not
   1177    * @return {Node} node
   1178    * @private
   1179    */
   1180 
   1181 
   1182   function parseUnary(state) {
   1183     var name, params, fn;
   1184     var operators = {
   1185       '-': 'unaryMinus',
   1186       '+': 'unaryPlus',
   1187       '~': 'bitNot',
   1188       not: 'not'
   1189     };
   1190 
   1191     if (hasOwnProperty(operators, state.token)) {
   1192       fn = operators[state.token];
   1193       name = state.token;
   1194       getTokenSkipNewline(state);
   1195       params = [parseUnary(state)];
   1196       return new OperatorNode(name, fn, params);
   1197     }
   1198 
   1199     return parsePow(state);
   1200   }
   1201   /**
   1202    * power
   1203    * Note: power operator is right associative
   1204    * @return {Node} node
   1205    * @private
   1206    */
   1207 
   1208 
   1209   function parsePow(state) {
   1210     var node, name, fn, params;
   1211     node = parseLeftHandOperators(state);
   1212 
   1213     if (state.token === '^' || state.token === '.^') {
   1214       name = state.token;
   1215       fn = name === '^' ? 'pow' : 'dotPow';
   1216       getTokenSkipNewline(state);
   1217       params = [node, parseUnary(state)]; // Go back to unary, we can have '2^-3'
   1218 
   1219       node = new OperatorNode(name, fn, params);
   1220     }
   1221 
   1222     return node;
   1223   }
   1224   /**
   1225    * Left hand operators: factorial x!, ctranspose x'
   1226    * @return {Node} node
   1227    * @private
   1228    */
   1229 
   1230 
   1231   function parseLeftHandOperators(state) {
   1232     var node, name, fn, params;
   1233     node = parseCustomNodes(state);
   1234     var operators = {
   1235       '!': 'factorial',
   1236       '\'': 'ctranspose'
   1237     };
   1238 
   1239     while (hasOwnProperty(operators, state.token)) {
   1240       name = state.token;
   1241       fn = operators[name];
   1242       getToken(state);
   1243       params = [node];
   1244       node = new OperatorNode(name, fn, params);
   1245       node = parseAccessors(state, node);
   1246     }
   1247 
   1248     return node;
   1249   }
   1250   /**
   1251    * Parse a custom node handler. A node handler can be used to process
   1252    * nodes in a custom way, for example for handling a plot.
   1253    *
   1254    * A handler must be passed as second argument of the parse function.
   1255    * - must extend math.Node
   1256    * - must contain a function _compile(defs: Object) : string
   1257    * - must contain a function find(filter: Object) : Node[]
   1258    * - must contain a function toString() : string
   1259    * - the constructor is called with a single argument containing all parameters
   1260    *
   1261    * For example:
   1262    *
   1263    *     nodes = {
   1264    *       'plot': PlotHandler
   1265    *     }
   1266    *
   1267    * The constructor of the handler is called as:
   1268    *
   1269    *     node = new PlotHandler(params)
   1270    *
   1271    * The handler will be invoked when evaluating an expression like:
   1272    *
   1273    *     node = math.parse('plot(sin(x), x)', nodes)
   1274    *
   1275    * @return {Node} node
   1276    * @private
   1277    */
   1278 
   1279 
   1280   function parseCustomNodes(state) {
   1281     var params = [];
   1282 
   1283     if (state.tokenType === TOKENTYPE.SYMBOL && hasOwnProperty(state.extraNodes, state.token)) {
   1284       var CustomNode = state.extraNodes[state.token];
   1285       getToken(state); // parse parameters
   1286 
   1287       if (state.token === '(') {
   1288         params = [];
   1289         openParams(state);
   1290         getToken(state);
   1291 
   1292         if (state.token !== ')') {
   1293           params.push(parseAssignment(state)); // parse a list with parameters
   1294 
   1295           while (state.token === ',') {
   1296             // eslint-disable-line no-unmodified-loop-condition
   1297             getToken(state);
   1298             params.push(parseAssignment(state));
   1299           }
   1300         }
   1301 
   1302         if (state.token !== ')') {
   1303           throw createSyntaxError(state, 'Parenthesis ) expected');
   1304         }
   1305 
   1306         closeParams(state);
   1307         getToken(state);
   1308       } // create a new custom node
   1309       // noinspection JSValidateTypes
   1310 
   1311 
   1312       return new CustomNode(params);
   1313     }
   1314 
   1315     return parseSymbol(state);
   1316   }
   1317   /**
   1318    * parse symbols: functions, variables, constants, units
   1319    * @return {Node} node
   1320    * @private
   1321    */
   1322 
   1323 
   1324   function parseSymbol(state) {
   1325     var node, name;
   1326 
   1327     if (state.tokenType === TOKENTYPE.SYMBOL || state.tokenType === TOKENTYPE.DELIMITER && state.token in NAMED_DELIMITERS) {
   1328       name = state.token;
   1329       getToken(state);
   1330 
   1331       if (hasOwnProperty(CONSTANTS, name)) {
   1332         // true, false, null, ...
   1333         node = new ConstantNode(CONSTANTS[name]);
   1334       } else if (NUMERIC_CONSTANTS.indexOf(name) !== -1) {
   1335         // NaN, Infinity
   1336         node = new ConstantNode(numeric(name, 'number'));
   1337       } else {
   1338         node = new SymbolNode(name);
   1339       } // parse function parameters and matrix index
   1340 
   1341 
   1342       node = parseAccessors(state, node);
   1343       return node;
   1344     }
   1345 
   1346     return parseDoubleQuotesString(state);
   1347   }
   1348   /**
   1349    * parse accessors:
   1350    * - function invocation in round brackets (...), for example sqrt(2)
   1351    * - index enclosed in square brackets [...], for example A[2,3]
   1352    * - dot notation for properties, like foo.bar
   1353    * @param {Object} state
   1354    * @param {Node} node    Node on which to apply the parameters. If there
   1355    *                       are no parameters in the expression, the node
   1356    *                       itself is returned
   1357    * @param {string[]} [types]  Filter the types of notations
   1358    *                            can be ['(', '[', '.']
   1359    * @return {Node} node
   1360    * @private
   1361    */
   1362 
   1363 
   1364   function parseAccessors(state, node, types) {
   1365     var params;
   1366 
   1367     while ((state.token === '(' || state.token === '[' || state.token === '.') && (!types || types.indexOf(state.token) !== -1)) {
   1368       // eslint-disable-line no-unmodified-loop-condition
   1369       params = [];
   1370 
   1371       if (state.token === '(') {
   1372         if (isSymbolNode(node) || isAccessorNode(node)) {
   1373           // function invocation like fn(2, 3) or obj.fn(2, 3)
   1374           openParams(state);
   1375           getToken(state);
   1376 
   1377           if (state.token !== ')') {
   1378             params.push(parseAssignment(state)); // parse a list with parameters
   1379 
   1380             while (state.token === ',') {
   1381               // eslint-disable-line no-unmodified-loop-condition
   1382               getToken(state);
   1383               params.push(parseAssignment(state));
   1384             }
   1385           }
   1386 
   1387           if (state.token !== ')') {
   1388             throw createSyntaxError(state, 'Parenthesis ) expected');
   1389           }
   1390 
   1391           closeParams(state);
   1392           getToken(state);
   1393           node = new FunctionNode(node, params);
   1394         } else {
   1395           // implicit multiplication like (2+3)(4+5) or sqrt(2)(1+2)
   1396           // don't parse it here but let it be handled by parseImplicitMultiplication
   1397           // with correct precedence
   1398           return node;
   1399         }
   1400       } else if (state.token === '[') {
   1401         // index notation like variable[2, 3]
   1402         openParams(state);
   1403         getToken(state);
   1404 
   1405         if (state.token !== ']') {
   1406           params.push(parseAssignment(state)); // parse a list with parameters
   1407 
   1408           while (state.token === ',') {
   1409             // eslint-disable-line no-unmodified-loop-condition
   1410             getToken(state);
   1411             params.push(parseAssignment(state));
   1412           }
   1413         }
   1414 
   1415         if (state.token !== ']') {
   1416           throw createSyntaxError(state, 'Parenthesis ] expected');
   1417         }
   1418 
   1419         closeParams(state);
   1420         getToken(state);
   1421         node = new AccessorNode(node, new IndexNode(params));
   1422       } else {
   1423         // dot notation like variable.prop
   1424         getToken(state);
   1425 
   1426         if (state.tokenType !== TOKENTYPE.SYMBOL) {
   1427           throw createSyntaxError(state, 'Property name expected after dot');
   1428         }
   1429 
   1430         params.push(new ConstantNode(state.token));
   1431         getToken(state);
   1432         var dotNotation = true;
   1433         node = new AccessorNode(node, new IndexNode(params, dotNotation));
   1434       }
   1435     }
   1436 
   1437     return node;
   1438   }
   1439   /**
   1440    * Parse a double quotes string.
   1441    * @return {Node} node
   1442    * @private
   1443    */
   1444 
   1445 
   1446   function parseDoubleQuotesString(state) {
   1447     var node, str;
   1448 
   1449     if (state.token === '"') {
   1450       str = parseDoubleQuotesStringToken(state); // create constant
   1451 
   1452       node = new ConstantNode(str); // parse index parameters
   1453 
   1454       node = parseAccessors(state, node);
   1455       return node;
   1456     }
   1457 
   1458     return parseSingleQuotesString(state);
   1459   }
   1460   /**
   1461    * Parse a string surrounded by double quotes "..."
   1462    * @return {string}
   1463    */
   1464 
   1465 
   1466   function parseDoubleQuotesStringToken(state) {
   1467     var str = '';
   1468 
   1469     while (currentCharacter(state) !== '' && currentCharacter(state) !== '"') {
   1470       if (currentCharacter(state) === '\\') {
   1471         // escape character, immediately process the next
   1472         // character to prevent stopping at a next '\"'
   1473         str += currentCharacter(state);
   1474         next(state);
   1475       }
   1476 
   1477       str += currentCharacter(state);
   1478       next(state);
   1479     }
   1480 
   1481     getToken(state);
   1482 
   1483     if (state.token !== '"') {
   1484       throw createSyntaxError(state, 'End of string " expected');
   1485     }
   1486 
   1487     getToken(state);
   1488     return JSON.parse('"' + str + '"'); // unescape escaped characters
   1489   }
   1490   /**
   1491    * Parse a single quotes string.
   1492    * @return {Node} node
   1493    * @private
   1494    */
   1495 
   1496 
   1497   function parseSingleQuotesString(state) {
   1498     var node, str;
   1499 
   1500     if (state.token === '\'') {
   1501       str = parseSingleQuotesStringToken(state); // create constant
   1502 
   1503       node = new ConstantNode(str); // parse index parameters
   1504 
   1505       node = parseAccessors(state, node);
   1506       return node;
   1507     }
   1508 
   1509     return parseMatrix(state);
   1510   }
   1511   /**
   1512    * Parse a string surrounded by single quotes '...'
   1513    * @return {string}
   1514    */
   1515 
   1516 
   1517   function parseSingleQuotesStringToken(state) {
   1518     var str = '';
   1519 
   1520     while (currentCharacter(state) !== '' && currentCharacter(state) !== '\'') {
   1521       if (currentCharacter(state) === '\\') {
   1522         // escape character, immediately process the next
   1523         // character to prevent stopping at a next '\''
   1524         str += currentCharacter(state);
   1525         next(state);
   1526       }
   1527 
   1528       str += currentCharacter(state);
   1529       next(state);
   1530     }
   1531 
   1532     getToken(state);
   1533 
   1534     if (state.token !== '\'') {
   1535       throw createSyntaxError(state, 'End of string \' expected');
   1536     }
   1537 
   1538     getToken(state);
   1539     return JSON.parse('"' + str + '"'); // unescape escaped characters
   1540   }
   1541   /**
   1542    * parse the matrix
   1543    * @return {Node} node
   1544    * @private
   1545    */
   1546 
   1547 
   1548   function parseMatrix(state) {
   1549     var array, params, rows, cols;
   1550 
   1551     if (state.token === '[') {
   1552       // matrix [...]
   1553       openParams(state);
   1554       getToken(state);
   1555 
   1556       if (state.token !== ']') {
   1557         // this is a non-empty matrix
   1558         var row = parseRow(state);
   1559 
   1560         if (state.token === ';') {
   1561           // 2 dimensional array
   1562           rows = 1;
   1563           params = [row]; // the rows of the matrix are separated by dot-comma's
   1564 
   1565           while (state.token === ';') {
   1566             // eslint-disable-line no-unmodified-loop-condition
   1567             getToken(state);
   1568             params[rows] = parseRow(state);
   1569             rows++;
   1570           }
   1571 
   1572           if (state.token !== ']') {
   1573             throw createSyntaxError(state, 'End of matrix ] expected');
   1574           }
   1575 
   1576           closeParams(state);
   1577           getToken(state); // check if the number of columns matches in all rows
   1578 
   1579           cols = params[0].items.length;
   1580 
   1581           for (var r = 1; r < rows; r++) {
   1582             if (params[r].items.length !== cols) {
   1583               throw createError(state, 'Column dimensions mismatch ' + '(' + params[r].items.length + ' !== ' + cols + ')');
   1584             }
   1585           }
   1586 
   1587           array = new ArrayNode(params);
   1588         } else {
   1589           // 1 dimensional vector
   1590           if (state.token !== ']') {
   1591             throw createSyntaxError(state, 'End of matrix ] expected');
   1592           }
   1593 
   1594           closeParams(state);
   1595           getToken(state);
   1596           array = row;
   1597         }
   1598       } else {
   1599         // this is an empty matrix "[ ]"
   1600         closeParams(state);
   1601         getToken(state);
   1602         array = new ArrayNode([]);
   1603       }
   1604 
   1605       return parseAccessors(state, array);
   1606     }
   1607 
   1608     return parseObject(state);
   1609   }
   1610   /**
   1611    * Parse a single comma-separated row from a matrix, like 'a, b, c'
   1612    * @return {ArrayNode} node
   1613    */
   1614 
   1615 
   1616   function parseRow(state) {
   1617     var params = [parseAssignment(state)];
   1618     var len = 1;
   1619 
   1620     while (state.token === ',') {
   1621       // eslint-disable-line no-unmodified-loop-condition
   1622       getToken(state); // parse expression
   1623 
   1624       params[len] = parseAssignment(state);
   1625       len++;
   1626     }
   1627 
   1628     return new ArrayNode(params);
   1629   }
   1630   /**
   1631    * parse an object, enclosed in angle brackets{...}, for example {value: 2}
   1632    * @return {Node} node
   1633    * @private
   1634    */
   1635 
   1636 
   1637   function parseObject(state) {
   1638     if (state.token === '{') {
   1639       openParams(state);
   1640       var key;
   1641       var properties = {};
   1642 
   1643       do {
   1644         getToken(state);
   1645 
   1646         if (state.token !== '}') {
   1647           // parse key
   1648           if (state.token === '"') {
   1649             key = parseDoubleQuotesStringToken(state);
   1650           } else if (state.token === '\'') {
   1651             key = parseSingleQuotesStringToken(state);
   1652           } else if (state.tokenType === TOKENTYPE.SYMBOL || state.tokenType === TOKENTYPE.DELIMITER && state.token in NAMED_DELIMITERS) {
   1653             key = state.token;
   1654             getToken(state);
   1655           } else {
   1656             throw createSyntaxError(state, 'Symbol or string expected as object key');
   1657           } // parse key/value separator
   1658 
   1659 
   1660           if (state.token !== ':') {
   1661             throw createSyntaxError(state, 'Colon : expected after object key');
   1662           }
   1663 
   1664           getToken(state); // parse key
   1665 
   1666           properties[key] = parseAssignment(state);
   1667         }
   1668       } while (state.token === ','); // eslint-disable-line no-unmodified-loop-condition
   1669 
   1670 
   1671       if (state.token !== '}') {
   1672         throw createSyntaxError(state, 'Comma , or bracket } expected after object value');
   1673       }
   1674 
   1675       closeParams(state);
   1676       getToken(state);
   1677       var node = new ObjectNode(properties); // parse index parameters
   1678 
   1679       node = parseAccessors(state, node);
   1680       return node;
   1681     }
   1682 
   1683     return parseNumber(state);
   1684   }
   1685   /**
   1686    * parse a number
   1687    * @return {Node} node
   1688    * @private
   1689    */
   1690 
   1691 
   1692   function parseNumber(state) {
   1693     var numberStr;
   1694 
   1695     if (state.tokenType === TOKENTYPE.NUMBER) {
   1696       // this is a number
   1697       numberStr = state.token;
   1698       getToken(state);
   1699       return new ConstantNode(numeric(numberStr, config.number));
   1700     }
   1701 
   1702     return parseParentheses(state);
   1703   }
   1704   /**
   1705    * parentheses
   1706    * @return {Node} node
   1707    * @private
   1708    */
   1709 
   1710 
   1711   function parseParentheses(state) {
   1712     var node; // check if it is a parenthesized expression
   1713 
   1714     if (state.token === '(') {
   1715       // parentheses (...)
   1716       openParams(state);
   1717       getToken(state);
   1718       node = parseAssignment(state); // start again
   1719 
   1720       if (state.token !== ')') {
   1721         throw createSyntaxError(state, 'Parenthesis ) expected');
   1722       }
   1723 
   1724       closeParams(state);
   1725       getToken(state);
   1726       node = new ParenthesisNode(node);
   1727       node = parseAccessors(state, node);
   1728       return node;
   1729     }
   1730 
   1731     return parseEnd(state);
   1732   }
   1733   /**
   1734    * Evaluated when the expression is not yet ended but expected to end
   1735    * @return {Node} res
   1736    * @private
   1737    */
   1738 
   1739 
   1740   function parseEnd(state) {
   1741     if (state.token === '') {
   1742       // syntax error or unexpected end of expression
   1743       throw createSyntaxError(state, 'Unexpected end of expression');
   1744     } else {
   1745       throw createSyntaxError(state, 'Value expected');
   1746     }
   1747   }
   1748   /**
   1749    * Shortcut for getting the current row value (one based)
   1750    * Returns the line of the currently handled expression
   1751    * @private
   1752    */
   1753 
   1754   /* TODO: implement keeping track on the row number
   1755   function row () {
   1756     return null
   1757   }
   1758   */
   1759 
   1760   /**
   1761    * Shortcut for getting the current col value (one based)
   1762    * Returns the column (position) where the last state.token starts
   1763    * @private
   1764    */
   1765 
   1766 
   1767   function col(state) {
   1768     return state.index - state.token.length + 1;
   1769   }
   1770   /**
   1771    * Create an error
   1772    * @param {Object} state
   1773    * @param {string} message
   1774    * @return {SyntaxError} instantiated error
   1775    * @private
   1776    */
   1777 
   1778 
   1779   function createSyntaxError(state, message) {
   1780     var c = col(state);
   1781     var error = new SyntaxError(message + ' (char ' + c + ')');
   1782     error.char = c;
   1783     return error;
   1784   }
   1785   /**
   1786    * Create an error
   1787    * @param {Object} state
   1788    * @param {string} message
   1789    * @return {Error} instantiated error
   1790    * @private
   1791    */
   1792 
   1793 
   1794   function createError(state, message) {
   1795     var c = col(state);
   1796     var error = new SyntaxError(message + ' (char ' + c + ')');
   1797     error.char = c;
   1798     return error;
   1799   }
   1800 
   1801   return parse;
   1802 });