UNPKG

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