parse.js 48 KB

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