UNPKG

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