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