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;