simple-squiggle

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

parse.js (49317B)


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