UNPKG

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