UNPKG

48.3 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/extends";
2import { factory } from '../utils/factory.js';
3import { isAccessorNode, isConstantNode, isFunctionNode, isOperatorNode, isSymbolNode } from '../utils/is.js';
4import { deepMap } from '../utils/collection.js';
5import { hasOwnProperty } from '../utils/object.js';
6var name = 'parse';
7var dependencies = ['typed', 'numeric', 'config', 'AccessorNode', 'ArrayNode', 'AssignmentNode', 'BlockNode', 'ConditionalNode', 'ConstantNode', 'FunctionAssignmentNode', 'FunctionNode', 'IndexNode', 'ObjectNode', 'OperatorNode', 'ParenthesisNode', 'RangeNode', 'RelationalNode', 'SymbolNode'];
8export 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
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,
627 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,
639 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});
\No newline at end of file