├── .babelrc
├── .eslintignore
├── .eslintrc
├── .gitignore
├── .npmignore
├── .travis.yml
├── LICENSE
├── README.md
├── docs
├── index.html
├── index.js
└── math-parser.js
├── index.js
├── lib
├── __test__
│ ├── __snapshots__
│ │ └── parser.test.js.snap
│ ├── parser.test.js
│ ├── print.test.js
│ └── toTex.test.js
├── parse.js
├── print.js
└── toTex.js
├── package.json
├── webpack.config.js
└── yarn.lock
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015"],
3 | "plugins": [
4 | "transform-flow-strip-types",
5 | "transform-object-rest-spread"
6 | ]
7 | }
8 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | docs
2 | dist
3 |
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint:recommended",
4 | "plugin:flowtype/recommended"
5 | ],
6 | "plugins": [
7 | "flowtype"
8 | ],
9 | "rules": {
10 | "no-constant-condition": 0,
11 | "indent": ["error", 4],
12 | "linebreak-style": ["error", "unix"],
13 | "quotes": ["error", "single"],
14 | "semi": ["error", "never"],
15 | "no-nested-ternary": "error",
16 | "space-infix-ops": "error"
17 | },
18 | "env": {
19 | "browser": true,
20 | "es6": true,
21 | "node": true,
22 | "mocha": true
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | .eslintcache
4 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | lib
2 | docs
3 | test
4 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "node"
4 | notifications:
5 | email: false
6 | script:
7 | - npm test
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Kevin Barabash
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # math-parser
2 |
3 | [](https://travis-ci.org/semantic-math/math-parser)
4 |
5 | Parse math strings to an AST suitable for symbolic manipulation.
6 |
7 | ## Features
8 |
9 | - flattened add/sub operations
10 | - flattened mul/div operations
11 | - equations and inequalities
12 | - absolute value, e.g. `||x - y| - |y - z||`
13 | - functions, e.g. `f(x, y, g(u, v))`
14 | - multi-char identifiers, e.g. `atan2(dy, dx)`
15 | - implicit multiplication, e.g. `(x)(y)`, `2x`, `a b`
16 |
17 | ## Demo
18 |
19 | [demo](https://semantic-math.github.io/math-parser/)
20 |
21 | ## Getting Started
22 |
23 | - `yarn install`
24 | - `npm run build`
25 | - `node demo.js`
26 |
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
33 | math-parser demo
34 |
35 |
36 |
37 |
38 |
math-parser demo
39 |
40 |
41 |
42 |
43 |
44 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/docs/index.js:
--------------------------------------------------------------------------------
1 | window.addEventListener('load', function() {
2 |
3 | const parse = module.exports.parse
4 |
5 | const params = {}
6 |
7 | location.search.slice(1).split('&').forEach(part => {
8 | const [key, value] = part.split('=')
9 | params[key] = decodeURIComponent(value)
10 | })
11 |
12 | const input = document.getElementById('input')
13 | const output = document.getElementById('output')
14 | const permalink = document.getElementById('permalink')
15 |
16 | if ('math' in params) {
17 | input.value = params.math
18 | }
19 |
20 | const update = function() {
21 | try {
22 | const ast = parse(input.value)
23 | output.textContent = JSON.stringify(ast, null, 2)
24 | } catch (e) {
25 | output.textContent = e.message
26 | }
27 | }
28 |
29 | update()
30 |
31 | input.addEventListener('input', update)
32 |
33 | permalink.addEventListener('click', function() {
34 | const math = encodeURIComponent(input.value)
35 | window.location = `?math=${math}`
36 | })
37 | })
38 |
--------------------------------------------------------------------------------
/docs/math-parser.js:
--------------------------------------------------------------------------------
1 | module.exports =
2 | /******/ (function(modules) { // webpackBootstrap
3 | /******/ // The module cache
4 | /******/ var installedModules = {};
5 |
6 | /******/ // The require function
7 | /******/ function __webpack_require__(moduleId) {
8 |
9 | /******/ // Check if module is in cache
10 | /******/ if(installedModules[moduleId])
11 | /******/ return installedModules[moduleId].exports;
12 |
13 | /******/ // Create a new module (and put it into the cache)
14 | /******/ var module = installedModules[moduleId] = {
15 | /******/ i: moduleId,
16 | /******/ l: false,
17 | /******/ exports: {}
18 | /******/ };
19 |
20 | /******/ // Execute the module function
21 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
22 |
23 | /******/ // Flag the module as loaded
24 | /******/ module.l = true;
25 |
26 | /******/ // Return the exports of the module
27 | /******/ return module.exports;
28 | /******/ }
29 |
30 |
31 | /******/ // expose the modules object (__webpack_modules__)
32 | /******/ __webpack_require__.m = modules;
33 |
34 | /******/ // expose the module cache
35 | /******/ __webpack_require__.c = installedModules;
36 |
37 | /******/ // identity function for calling harmony imports with the correct context
38 | /******/ __webpack_require__.i = function(value) { return value; };
39 |
40 | /******/ // define getter function for harmony exports
41 | /******/ __webpack_require__.d = function(exports, name, getter) {
42 | /******/ if(!__webpack_require__.o(exports, name)) {
43 | /******/ Object.defineProperty(exports, name, {
44 | /******/ configurable: false,
45 | /******/ enumerable: true,
46 | /******/ get: getter
47 | /******/ });
48 | /******/ }
49 | /******/ };
50 |
51 | /******/ // getDefaultExport function for compatibility with non-harmony modules
52 | /******/ __webpack_require__.n = function(module) {
53 | /******/ var getter = module && module.__esModule ?
54 | /******/ function getDefault() { return module['default']; } :
55 | /******/ function getModuleExports() { return module; };
56 | /******/ __webpack_require__.d(getter, 'a', getter);
57 | /******/ return getter;
58 | /******/ };
59 |
60 | /******/ // Object.prototype.hasOwnProperty.call
61 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
62 |
63 | /******/ // __webpack_public_path__
64 | /******/ __webpack_require__.p = "";
65 |
66 | /******/ // Load entry module and return exports
67 | /******/ return __webpack_require__(__webpack_require__.s = 5);
68 | /******/ })
69 | /************************************************************************/
70 | /******/ ([
71 | /* 0 */
72 | /***/ (function(module, exports) {
73 |
74 | module.exports =
75 | /******/ (function(modules) { // webpackBootstrap
76 | /******/ // The module cache
77 | /******/ var installedModules = {};
78 | /******/
79 | /******/ // The require function
80 | /******/ function __webpack_require__(moduleId) {
81 | /******/
82 | /******/ // Check if module is in cache
83 | /******/ if(installedModules[moduleId]) {
84 | /******/ return installedModules[moduleId].exports;
85 | /******/ }
86 | /******/ // Create a new module (and put it into the cache)
87 | /******/ var module = installedModules[moduleId] = {
88 | /******/ i: moduleId,
89 | /******/ l: false,
90 | /******/ exports: {}
91 | /******/ };
92 | /******/
93 | /******/ // Execute the module function
94 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
95 | /******/
96 | /******/ // Flag the module as loaded
97 | /******/ module.l = true;
98 | /******/
99 | /******/ // Return the exports of the module
100 | /******/ return module.exports;
101 | /******/ }
102 | /******/
103 | /******/
104 | /******/ // expose the modules object (__webpack_modules__)
105 | /******/ __webpack_require__.m = modules;
106 | /******/
107 | /******/ // expose the module cache
108 | /******/ __webpack_require__.c = installedModules;
109 | /******/
110 | /******/ // identity function for calling harmony imports with the correct context
111 | /******/ __webpack_require__.i = function(value) { return value; };
112 | /******/
113 | /******/ // define getter function for harmony exports
114 | /******/ __webpack_require__.d = function(exports, name, getter) {
115 | /******/ if(!__webpack_require__.o(exports, name)) {
116 | /******/ Object.defineProperty(exports, name, {
117 | /******/ configurable: false,
118 | /******/ enumerable: true,
119 | /******/ get: getter
120 | /******/ });
121 | /******/ }
122 | /******/ };
123 | /******/
124 | /******/ // getDefaultExport function for compatibility with non-harmony modules
125 | /******/ __webpack_require__.n = function(module) {
126 | /******/ var getter = module && module.__esModule ?
127 | /******/ function getDefault() { return module['default']; } :
128 | /******/ function getModuleExports() { return module; };
129 | /******/ __webpack_require__.d(getter, 'a', getter);
130 | /******/ return getter;
131 | /******/ };
132 | /******/
133 | /******/ // Object.prototype.hasOwnProperty.call
134 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
135 | /******/
136 | /******/ // __webpack_public_path__
137 | /******/ __webpack_require__.p = "";
138 | /******/
139 | /******/ // Load entry module and return exports
140 | /******/ return __webpack_require__(__webpack_require__.s = 2);
141 | /******/ })
142 | /************************************************************************/
143 | /******/ ([
144 | /* 0 */
145 | /***/ (function(module, exports, __webpack_require__) {
146 |
147 | "use strict";
148 |
149 |
150 | Object.defineProperty(exports, "__esModule", {
151 | value: true
152 | });
153 |
154 | var _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; };
155 |
156 | /**
157 | * Functions to build nodes
158 | */
159 |
160 | var apply = exports.apply = function apply(op, args) {
161 | var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
162 | return _extends({
163 | type: 'Apply',
164 | op: op,
165 | args: args
166 | }, options);
167 | };
168 |
169 | // Operations
170 |
171 | var neg = exports.neg = function neg(arg) {
172 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
173 | return apply('neg', [arg], options);
174 | }; // options: wasMinus
175 | var add = exports.add = function add() {
176 | for (var _len = arguments.length, terms = Array(_len), _key = 0; _key < _len; _key++) {
177 | terms[_key] = arguments[_key];
178 | }
179 |
180 | return apply('add', terms);
181 | };
182 | var sub = exports.sub = function sub(minuend, subtrahend) {
183 | return apply('add', [minuend, neg(subtrahend, { wasMinus: true })]);
184 | };
185 | var mul = exports.mul = function mul() {
186 | for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
187 | args[_key2] = arguments[_key2];
188 | }
189 |
190 | return apply('mul', args);
191 | };
192 | var implicitMul = exports.implicitMul = function implicitMul() {
193 | for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
194 | args[_key3] = arguments[_key3];
195 | }
196 |
197 | return apply('mul', args, { implicit: true });
198 | };
199 | var div = exports.div = function div(numerator, denominator) {
200 | return apply('div', [numerator, denominator]);
201 | };
202 | var pow = exports.pow = function pow(base, exponent) {
203 | return apply('pow', [base, exponent]);
204 | };
205 | var abs = exports.abs = function abs(arg) {
206 | return apply('abs', [arg]);
207 | };
208 | var fact = exports.fact = function fact(arg) {
209 | return apply('fact', [arg]);
210 | };
211 | var nthRoot = exports.nthRoot = function nthRoot(radicand, index) {
212 | return apply('nthRoot', [radicand, index || number('2')]);
213 | };
214 |
215 | // Relations
216 |
217 | var eq = exports.eq = function eq() {
218 | for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
219 | args[_key4] = arguments[_key4];
220 | }
221 |
222 | return apply('eq', args);
223 | };
224 | var ne = exports.ne = function ne() {
225 | for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
226 | args[_key5] = arguments[_key5];
227 | }
228 |
229 | return apply('ne', args);
230 | };
231 | var lt = exports.lt = function lt() {
232 | for (var _len6 = arguments.length, args = Array(_len6), _key6 = 0; _key6 < _len6; _key6++) {
233 | args[_key6] = arguments[_key6];
234 | }
235 |
236 | return apply('lt', args);
237 | };
238 | var le = exports.le = function le() {
239 | for (var _len7 = arguments.length, args = Array(_len7), _key7 = 0; _key7 < _len7; _key7++) {
240 | args[_key7] = arguments[_key7];
241 | }
242 |
243 | return apply('le', args);
244 | };
245 | var gt = exports.gt = function gt() {
246 | for (var _len8 = arguments.length, args = Array(_len8), _key8 = 0; _key8 < _len8; _key8++) {
247 | args[_key8] = arguments[_key8];
248 | }
249 |
250 | return apply('gt', args);
251 | };
252 | var ge = exports.ge = function ge() {
253 | for (var _len9 = arguments.length, args = Array(_len9), _key9 = 0; _key9 < _len9; _key9++) {
254 | args[_key9] = arguments[_key9];
255 | }
256 |
257 | return apply('ge', args);
258 | };
259 |
260 | var identifier = exports.identifier = function identifier(name) {
261 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
262 | return _extends({
263 | type: 'Identifier',
264 | name: name
265 | }, options);
266 | };
267 |
268 | var number = exports.number = function number(value) {
269 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
270 | return _extends({
271 | type: 'Number',
272 | value: value
273 | }, options);
274 | };
275 |
276 | var parens = exports.parens = function parens(body) {
277 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
278 | return _extends({
279 | type: 'Parentheses',
280 | body: body
281 | }, options);
282 | };
283 |
284 | // deprecated aliases
285 | var parensNode = exports.parensNode = parens;
286 | var numberNode = exports.numberNode = number;
287 | var identifierNode = exports.identifierNode = identifier;
288 | var applyNode = exports.applyNode = apply;
289 |
290 | /***/ }),
291 | /* 1 */
292 | /***/ (function(module, exports, __webpack_require__) {
293 |
294 | "use strict";
295 |
296 |
297 | Object.defineProperty(exports, "__esModule", {
298 | value: true
299 | });
300 | /**
301 | * Functions to query properties of nodes
302 | */
303 |
304 | var isIdentifier = exports.isIdentifier = function isIdentifier(node) {
305 | return node && node.type === 'Identifier';
306 | };
307 | var isApply = exports.isApply = function isApply(node) {
308 | return node && node.type === 'Apply';
309 | };
310 |
311 | var isOperation = exports.isOperation = function isOperation(node) {
312 | return isApply(node) && !isNumber(node);
313 | };
314 | var isFunction = exports.isFunction = function isFunction(node) {
315 | return isApply(node) && isIdentifier(node.op);
316 | };
317 |
318 | // TODO: curry it?
319 | var _isOp = function _isOp(op, node) {
320 | return isApply(node) && node.op === op;
321 | };
322 |
323 | var isAdd = exports.isAdd = function isAdd(node) {
324 | return _isOp('add', node);
325 | };
326 | var isMul = exports.isMul = function isMul(node) {
327 | return _isOp('mul', node);
328 | };
329 | var isDiv = exports.isDiv = function isDiv(node) {
330 | return _isOp('div', node);
331 | };
332 | var isPow = exports.isPow = function isPow(node) {
333 | return _isOp('pow', node);
334 | };
335 | var isNeg = exports.isNeg = function isNeg(node) {
336 | return _isOp('neg', node);
337 | };
338 | var isPos = exports.isPos = function isPos(node) {
339 | return _isOp('pos', node);
340 | };
341 | var isAbs = exports.isAbs = function isAbs(node) {
342 | return _isOp('abs', node);
343 | };
344 | var isFact = exports.isFact = function isFact(node) {
345 | return _isOp('fact', node);
346 | };
347 | var isNthRoot = exports.isNthRoot = function isNthRoot(node) {
348 | return _isOp('nthRoot', node);
349 | };
350 |
351 | var relationIdentifierMap = {
352 | 'eq': '=',
353 | 'lt': '<',
354 | 'le': '<=',
355 | 'gt': '>',
356 | 'ge': '>=',
357 | 'ne': '!='
358 | };
359 |
360 | var isRel = exports.isRel = function isRel(node) {
361 | return isApply(node) && node.op in relationIdentifierMap;
362 | };
363 |
364 | var isNumber = exports.isNumber = function isNumber(node) {
365 | if (node.type === 'Number') {
366 | return true;
367 | } else if (isNeg(node)) {
368 | return isNumber(node.args[0]);
369 | } else {
370 | return false;
371 | }
372 | };
373 |
374 | // check if it's a number before trying to get its value
375 | var getValue = exports.getValue = function getValue(node) {
376 | if (node.type === 'Number') {
377 | return parseFloat(node.value);
378 | } else if (isNeg(node)) {
379 | return -getValue(node.args[0]);
380 | } else if (isPos(node)) {
381 | return getValue(node.args[0]);
382 | }
383 | };
384 |
385 | /***/ }),
386 | /* 2 */
387 | /***/ (function(module, exports, __webpack_require__) {
388 |
389 | "use strict";
390 |
391 |
392 | Object.defineProperty(exports, "__esModule", {
393 | value: true
394 | });
395 | exports.query = exports.build = undefined;
396 |
397 | var _build = __webpack_require__(0);
398 |
399 | var build = _interopRequireWildcard(_build);
400 |
401 | var _query = __webpack_require__(1);
402 |
403 | var query = _interopRequireWildcard(_query);
404 |
405 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
406 |
407 | exports.build = build;
408 | exports.query = query;
409 |
410 | /***/ })
411 | /******/ ]);
412 |
413 | /***/ }),
414 | /* 1 */
415 | /***/ (function(module, exports, __webpack_require__) {
416 |
417 | "use strict";
418 |
419 |
420 | Object.defineProperty(exports, "__esModule", {
421 | value: true
422 | });
423 |
424 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
425 |
426 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
427 |
428 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /**
429 | * Parses a math string to an AST.
430 | *
431 | * Notes:
432 | * - The output AST tries to conform to the math-ast spec, but some aspects may
433 | * be a little off. This will be fixed in future versions.
434 | * - The input syntax covers the parts of the mathjs syntax being used by
435 | * mathsteps
436 | *
437 | * TODO:
438 | * - Better adherence to and more comprehensive coverage of the math-ast spec.
439 | * - Specify what the syntax is, e.g. operator precedence, implicit multiplication,
440 | * etc.
441 | */
442 |
443 |
444 | exports.default = parse;
445 |
446 | var _mathNodes = __webpack_require__(0);
447 |
448 | var _mathTraverse = __webpack_require__(4);
449 |
450 | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
451 |
452 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
453 |
454 | function isIdentifierToken(token) {
455 | return token && /^[a-zA-Z][a-zA-Z0-9]*$/.test(token.value);
456 | }
457 |
458 | function isNumberToken(token) {
459 | return token && /^(\d*\.\d+|\d+\.\d*|\d+)$/.test(token.value);
460 | }
461 |
462 | var tokenPattern = /\.\.\.|[a-zA-Z][a-zA-Z0-9]*|<=|>=|!=|[\<\>\!\=\(\)\+\-\/\*\^\<\>|\,\#\_]|\d*\.\d+|\d+\.\d*|\d+/;
463 |
464 | var relationTokenMap = {
465 | '=': 'eq',
466 | '<': 'lt',
467 | '<=': 'le',
468 | '>': 'gt',
469 | '>=': 'ge',
470 | '!=': 'ne'
471 | };
472 |
473 | function isIdentifier(node) {
474 | return node.type === 'Identifier';
475 | }
476 |
477 | function isRelation(node) {
478 | return node.type === 'Apply' && Object.values(relationTokenMap).includes(node.op);
479 | }
480 |
481 | function isExpression(node) {
482 | return ['System', 'List', 'Sequence'].indexOf(node.type) === -1 || isRelation(node);
483 | }
484 |
485 | function matches(token, value) {
486 | return token && token.value === value;
487 | }
488 |
489 | var Parser = function () {
490 | function Parser() {
491 | _classCallCheck(this, Parser);
492 | }
493 |
494 | _createClass(Parser, [{
495 | key: 'consume',
496 | value: function consume(expectedValue) {
497 | var token = this.currentToken();
498 | if (expectedValue !== undefined) {
499 | if (!matches(token, expectedValue)) {
500 | throw new Error('expected \'' + expectedValue + '\' received \'' + token.value + '\'');
501 | }
502 | }
503 | this.i++;
504 | return token;
505 | }
506 | }, {
507 | key: 'currentToken',
508 | value: function currentToken() {
509 | return this.tokens[this.i];
510 | }
511 | }, {
512 | key: 'parse',
513 | value: function parse(input) {
514 | this.i = 0;
515 | this.tokens = [];
516 | this.integrals = 0;
517 |
518 | var regex = new RegExp(tokenPattern, 'g');
519 |
520 | var index = 0;
521 | var match = void 0;
522 |
523 | while ((match = regex.exec(input)) != null) {
524 | var start = match.index;
525 | var end = regex.lastIndex;
526 |
527 | this.tokens.push({
528 | value: match[0],
529 | start: start,
530 | end: end
531 | });
532 |
533 | if (index !== start) {
534 | var skipped = input.slice(index, start).trim();
535 | if (skipped !== '') {
536 | throw new Error('\'' + skipped + '\' not recognized');
537 | }
538 | }
539 |
540 | index = end;
541 | }
542 |
543 | if (index !== input.length) {
544 | var _skipped = input.slice(index, input.length).trim();
545 | if (_skipped !== '') {
546 | throw new Error('\'' + _skipped + '\' not recognized');
547 | }
548 | }
549 |
550 | var result = this.list();
551 |
552 | if (this.i < this.tokens.length) {
553 | throw new Error('extra input not recognized');
554 | }
555 |
556 | return result;
557 | }
558 | }, {
559 | key: 'list',
560 | value: function list() {
561 | var items = [this.relationsOrRelationOrExpression()];
562 |
563 | while (true) {
564 | var token = this.currentToken();
565 |
566 | if (matches(token, ',')) {
567 | this.consume(',');
568 | items.push(this.relationsOrRelationOrExpression());
569 | } else {
570 | break;
571 | }
572 | }
573 |
574 | if (items.length > 1) {
575 | if (items.every(function (item) {
576 | return isRelation(item);
577 | })) {
578 | return {
579 | type: 'System', // of equations
580 | relations: items
581 | };
582 | } else if (items.every(isExpression)) {
583 | return {
584 | type: 'Sequence',
585 | items: items
586 | };
587 | } else {
588 | return {
589 | type: 'List',
590 | items: items
591 | };
592 | }
593 | } else {
594 | return items[0];
595 | }
596 | }
597 | }, {
598 | key: 'relationsOrRelationOrExpression',
599 | value: function relationsOrRelationOrExpression() {
600 | var relations = [];
601 | var args = [];
602 |
603 | var left = void 0;
604 | var right = void 0;
605 |
606 | left = this.expression();
607 |
608 | while (true) {
609 | var token = this.currentToken();
610 |
611 | if (token && token.value in relationTokenMap) {
612 | this.consume();
613 | right = this.expression();
614 | var rel = relationTokenMap[token.value];
615 | relations.push(_mathNodes.build.applyNode(rel, [left, right]));
616 | args.push(left);
617 | left = right;
618 | } else {
619 | break;
620 | }
621 | }
622 | args.push(right);
623 |
624 | if (relations.length > 1) {
625 | var _ret = function () {
626 | relations.reverse();
627 |
628 | var output = {
629 | type: 'Apply',
630 | op: 'and',
631 | args: []
632 | };
633 |
634 | var current = {
635 | type: 'Apply',
636 | op: relations[0].op,
637 | args: []
638 | };
639 |
640 | relations.forEach(function (item) {
641 | if (item.op !== current.op) {
642 | current.args.unshift(item.args[1]);
643 |
644 | output.args.unshift(current);
645 |
646 | current = {
647 | type: 'Apply',
648 | op: item.op,
649 | args: [item.args[1]]
650 | };
651 | } else {
652 | current.args.unshift(item.args[1]);
653 | }
654 | });
655 |
656 | current.args.unshift(relations[relations.length - 1].args[0]);
657 | output.args.unshift(current);
658 |
659 | if (output.args.length === 1) {
660 | return {
661 | v: output.args[0]
662 | };
663 | }
664 |
665 | return {
666 | v: output
667 | };
668 | }();
669 |
670 | if ((typeof _ret === 'undefined' ? 'undefined' : _typeof(_ret)) === "object") return _ret.v;
671 | } else if (relations.length > 0) {
672 | return relations[0];
673 | } else {
674 | return left;
675 | }
676 | }
677 | }, {
678 | key: 'expression',
679 | value: function expression() {
680 | var args = [];
681 |
682 | args.push(this.explicitMul());
683 |
684 | while (true) {
685 | var token = this.currentToken();
686 |
687 | if (matches(token, '-')) {
688 | this.consume('-');
689 | args.push(_mathNodes.build.applyNode('neg', [this.explicitMul()], { wasMinus: true }));
690 | } else if (matches(token, '+')) {
691 | this.consume('+');
692 | args.push(this.explicitMul());
693 | } else {
694 | break;
695 | }
696 | }
697 |
698 | if (args.length > 1) {
699 | return _mathNodes.build.applyNode('add', args.map(function (term) {
700 | return term.addParens ? _mathNodes.build.parensNode(term) : term;
701 | }));
702 | } else {
703 | if (args[0].addParens) {
704 | return _mathNodes.build.parensNode(args[0]);
705 | } else {
706 | return args[0];
707 | }
708 | }
709 | }
710 | }, {
711 | key: 'explicitMul',
712 | value: function explicitMul() {
713 | var factors = [];
714 |
715 | factors.push(this.implicitMul());
716 |
717 | while (true) {
718 | if (matches(this.currentToken(), '*')) {
719 | this.consume('*');
720 | factors.push(this.implicitMul());
721 | } else {
722 | break;
723 | }
724 | }
725 |
726 | return factors.length > 1 ? _mathNodes.build.applyNode('mul', factors) : factors[0];
727 | }
728 |
729 | /**
730 | * Parse the following forms of implicit multiplication:
731 | * - a b c
732 | * - (a)(b)(c)
733 | *
734 | * Note: (a)b(c) is actually: 'a' times function 'b' evaluated at 'c'
735 | *
736 | * If the multiplication was detected, a single parsed factor is returned.
737 | */
738 |
739 | }, {
740 | key: 'implicitMul',
741 | value: function implicitMul() {
742 | var factors = [];
743 |
744 | factors.push(this.division());
745 |
746 | while (true) {
747 | var token = this.currentToken();
748 |
749 | if (matches(token, '(') || matches(token, '#') || isIdentifierToken(token) || isNumberToken(token)) {
750 | var factor = this.division();
751 | if (factor) {
752 | factors.push(factor);
753 | } else {
754 | break;
755 | }
756 | } else {
757 | break;
758 | }
759 |
760 | if (this.i > this.tokens.length) {
761 | break;
762 | }
763 | }
764 |
765 | return factors.length > 1 ? _mathNodes.build.applyNode('mul', factors, { implicit: true }) : factors[0];
766 | }
767 | }, {
768 | key: 'division',
769 | value: function division() {
770 | var numerator = void 0;
771 | var denominator = void 0;
772 | var frac = void 0;
773 |
774 | numerator = this.factor();
775 |
776 | while (true) {
777 | var token = this.currentToken();
778 |
779 | if (matches(token, '/')) {
780 | this.consume('/');
781 | denominator = this.factor();
782 | if (frac) {
783 | frac = _mathNodes.build.applyNode('div', [frac, denominator]);
784 | } else {
785 | frac = _mathNodes.build.applyNode('div', [numerator, denominator]);
786 | }
787 | } else {
788 | break;
789 | }
790 | }
791 |
792 | return frac || numerator;
793 | }
794 |
795 | /**
796 | * Parse any of the following:
797 | * - unary operations, e.g. +, -
798 | * - numbers
799 | * - identifiers
800 | * - parenthesis
801 | * - absolute value function, e.g. |x|
802 | * - exponents, e.g. x^2
803 | */
804 |
805 | }, {
806 | key: 'factor',
807 | value: function factor() {
808 | var token = this.currentToken();
809 |
810 | if (matches(token, '...')) {
811 | this.consume('...');
812 | return {
813 | type: 'Ellipsis'
814 | };
815 | }
816 |
817 | var signs = [];
818 |
819 | // handle multiple unary operators
820 | while (matches(token, '+') || matches(token, '-')) {
821 | signs.push(token);
822 | this.consume(token.value);
823 | token = this.currentToken();
824 | }
825 |
826 | var base = void 0,
827 | exp = void 0;
828 | var addParens = false;
829 |
830 | if (matches(token, '#') || isIdentifierToken(token)) {
831 | var node = this.identifierOrPlaceholder();
832 |
833 | if (this.integrals > 0 && isIdentifier(node) && /d[a-z]+/.test(node.name)) {
834 | // backup
835 | this.integrals--;
836 | return;
837 | }
838 |
839 | if (matches(this.currentToken(), '(')) {
840 | this.consume('(');
841 | var args = this.argumentList();
842 | token = this.consume(')');
843 | if (node.name === 'nthRoot') {
844 | if (args.length < 1 || args.length > 2) {
845 | throw new Error('nthRoot takes 1 or 2 args');
846 | } else {
847 | base = _mathNodes.build.nthRoot.apply(_mathNodes.build, _toConsumableArray(args));
848 | }
849 | } else if (node.name === 'int') {
850 | if (args.length >= 2 && args.length <= 4) {
851 | base = _mathNodes.build.apply('int', args);
852 | } else {
853 | throw new Error('integral takes between 2 and 4 args');
854 | }
855 | } else {
856 | if (_mathNodes.query.isIdentifier(node) && node.name === 'abs') {
857 | base = _mathNodes.build.apply('abs', args);
858 | } else {
859 | base = _mathNodes.build.apply(node, args);
860 | }
861 | }
862 | } else {
863 | // TODO(kevinb) valid the constraint type against the node
864 | // e.g. if it's a 'Number' then it can't have a subscript
865 | base = node;
866 | }
867 | } else if (isNumberToken(token)) {
868 | this.consume(token.value);
869 | base = _mathNodes.build.numberNode(token.value);
870 | } else if (matches(token, '(')) {
871 | this.consume('(');
872 | base = this.expression();
873 | token = this.consume(')');
874 | addParens = true;
875 | if (base.type === 'Number' || isIdentifier(base)) {
876 | base = _mathNodes.build.parensNode(base);
877 | addParens = false;
878 | }
879 | } else if (matches(token, '|')) {
880 | this.consume('|');
881 | base = this.expression();
882 | token = this.consume('|');
883 |
884 | base = _mathNodes.build.applyNode('abs', [base]);
885 | }
886 |
887 | if (matches(this.currentToken(), '!')) {
888 | this.consume('!');
889 | // print will add parentheses back in if a 'fact' node wraps the
890 | // expression.
891 | addParens = false;
892 | base = _mathNodes.build.applyNode('fact', [base]);
893 | }
894 |
895 | var factor = base;
896 |
897 | // TODO handle exponents separately
898 | if (matches(this.currentToken(), '^')) {
899 | this.consume('^');
900 | exp = this.factor();
901 | factor = _mathNodes.build.applyNode('pow', [base, exp]);
902 | addParens = false;
903 | }
904 |
905 | // Reverse the signs so that we process them from the sign nearest
906 | // to the factor to the furthest.
907 | signs.reverse();
908 |
909 | signs.forEach(function (sign) {
910 | if (sign.value === '+') {
911 | factor = _mathNodes.build.applyNode('pos', [factor]);
912 | } else {
913 | factor = _mathNodes.build.applyNode('neg', [factor]);
914 | }
915 | addParens = false;
916 | });
917 |
918 | if (addParens) {
919 | factor.addParens = addParens;
920 | }
921 |
922 | if (_mathNodes.query.isPow(factor)) {
923 | var _factor$args = _slicedToArray(factor.args, 2),
924 | _base = _factor$args[0],
925 | exponent = _factor$args[1];
926 |
927 | if (_mathNodes.query.isIdentifier(_base) && _base.name === 'int') {
928 | this.integrals++;
929 | var body = this.expression();
930 |
931 | // backup to get the token we ignore
932 | this.i--;
933 | token = this.currentToken();
934 | this.consume(token.value);
935 |
936 | var result = {
937 | type: 'Apply',
938 | op: 'int',
939 | args: [body, _mathNodes.build.identifier(token.value)],
940 | limits: [_base.subscript, exponent]
941 | };
942 |
943 | return result;
944 | }
945 | } else if (_mathNodes.query.isIdentifier(factor) && factor.name === 'int') {
946 | this.integrals++;
947 | var _body = this.expression();
948 |
949 | // backup to get the token we ignore
950 | this.i--;
951 | token = this.currentToken();
952 | this.consume(token.value);
953 |
954 | var _result = {
955 | type: 'Apply',
956 | op: 'int',
957 | args: [_body, _mathNodes.build.identifier(token.value)]
958 | };
959 |
960 | return _result;
961 | }
962 |
963 | return factor;
964 | }
965 | }, {
966 | key: 'identifierOrPlaceholder',
967 | value: function identifierOrPlaceholder() {
968 | var token = this.currentToken();
969 |
970 | var isPlaceholder = false;
971 | if (matches(token, '#')) {
972 | isPlaceholder = true;
973 | this.consume('#');
974 | token = this.currentToken();
975 | }
976 |
977 | if (!isIdentifierToken(token)) {
978 | throw new Error('\'#\' must be followed by an identifier');
979 | }
980 |
981 | var result = this.identifier();
982 |
983 | if (isPlaceholder) {
984 | result.type = 'Placeholder';
985 | }
986 |
987 | return result;
988 | }
989 | }, {
990 | key: 'identifier',
991 | value: function identifier() {
992 | var token = this.currentToken();
993 |
994 | var name = token.value;
995 | var result = _mathNodes.build.identifierNode(name);
996 | this.consume(name);
997 |
998 | token = this.currentToken();
999 |
1000 | // This only handles very simple subscripts, e.g. a_0, a_n
1001 | // It doesn't handle: a_-1, a_(m+n), etc.
1002 | // The precedence of subscripts is very high: a_0^2 => (a_0)^2
1003 | if (matches(token, '_')) {
1004 | this.consume('_');
1005 |
1006 | token = this.currentToken();
1007 |
1008 | if (isNumberToken(token)) {
1009 | result.subscript = _mathNodes.build.numberNode(token.value);
1010 | this.consume(token.value);
1011 | } else if (isIdentifierToken(token)) {
1012 | result.subscript = _mathNodes.build.identifierNode(token.value);
1013 | this.consume(token.value);
1014 | } else {
1015 | throw new Error('Can\'t handle \'' + token.value + '\' as a subscript');
1016 | }
1017 | }
1018 |
1019 | return result;
1020 | }
1021 | }, {
1022 | key: 'argumentList',
1023 | value: function argumentList() {
1024 | var args = [this.expression()];
1025 | while (true) {
1026 | var token = this.currentToken();
1027 | if (!matches(token, ',')) {
1028 | break;
1029 | }
1030 | this.consume(',');
1031 | args.push(this.expression());
1032 | }
1033 | return args;
1034 | }
1035 | }]);
1036 |
1037 | return Parser;
1038 | }();
1039 |
1040 | var parser = new Parser();
1041 |
1042 | function parse(math) {
1043 | var ast = parser.parse(math);
1044 | (0, _mathTraverse.traverse)(ast, {
1045 | leave: function leave(node) {
1046 | if (node.hasOwnProperty('addParens')) {
1047 | delete node.addParens;
1048 | }
1049 | }
1050 | });
1051 | return ast;
1052 | }
1053 |
1054 | /***/ }),
1055 | /* 2 */
1056 | /***/ (function(module, exports, __webpack_require__) {
1057 |
1058 | "use strict";
1059 |
1060 |
1061 | Object.defineProperty(exports, "__esModule", {
1062 | value: true
1063 | });
1064 |
1065 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); /**
1066 | * print - return a string representation of the nodes.
1067 | */
1068 |
1069 |
1070 | exports.default = print;
1071 |
1072 | var _mathNodes = __webpack_require__(0);
1073 |
1074 | var relationIdentifierMap = {
1075 | 'eq': '=',
1076 | 'lt': '<',
1077 | 'le': '<=',
1078 | 'gt': '>',
1079 | 'ge': '>=',
1080 | 'ne': '!='
1081 | };
1082 |
1083 | var printApply = function printApply(node, parent) {
1084 | var op = node.op,
1085 | args = node.args;
1086 |
1087 |
1088 | if (op === 'add') {
1089 | var result = print(args[0], node);
1090 | for (var i = 1; i < args.length; i++) {
1091 | var arg = args[i];
1092 | if (_mathNodes.query.isNeg(arg) && arg.wasMinus) {
1093 | result += ' - ' + print(arg.args[0], node);
1094 | } else {
1095 | result += ' + ' + print(arg, node);
1096 | }
1097 | }
1098 | return parent && !_mathNodes.query.isRel(parent) && parent.type !== 'Parentheses' ? '(' + result + ')' : result;
1099 | } else if (op === 'mul') {
1100 | var _result = void 0;
1101 | // print coefficients with no spaces when possible - e.g. 2x not 2 x
1102 | // if there are more than 3 args, we can't do 2xy because if that was
1103 | // reparsed it'd treat xy as one symbol
1104 | if (node.implicit && node.args.length === 2 && _mathNodes.query.isNumber(node.args[0]) && _mathNodes.query.isIdentifier(node.args[1])) {
1105 | _result = args.map(function (arg) {
1106 | return print(arg, node);
1107 | }).join('');
1108 | } else if (node.implicit) {
1109 | _result = args.map(function (arg) {
1110 | return print(arg, node);
1111 | }).join(' ');
1112 | } else {
1113 | _result = args.map(function (arg) {
1114 | return print(arg, node);
1115 | }).join(' * ');
1116 | }
1117 |
1118 | if (_mathNodes.query.isMul(parent)) {
1119 | if (node.implicit && !parent.implicit) {
1120 | return _result;
1121 | } else {
1122 | return '(' + _result + ')';
1123 | }
1124 | } else if (_mathNodes.query.isPow(parent) || _mathNodes.query.isDiv(parent)) {
1125 | return '(' + _result + ')';
1126 | } else {
1127 | return _result;
1128 | }
1129 | } else if (op === 'div') {
1130 | var _result2 = '';
1131 | // this lets us print things like 2/3 and x/5 instead of 2 / 3 and x / 5
1132 | // (but the spaces are helpful for reading more complicated fractions)
1133 | if ((_mathNodes.query.isIdentifier(args[0]) || _mathNodes.query.isNumber(args[0])) && (_mathNodes.query.isIdentifier(args[1]) || _mathNodes.query.isNumber(args[1]))) {
1134 | _result2 = print(args[0]) + '/' + print(args[1]);
1135 | } else {
1136 | _result2 += print(args[0], node);
1137 | _result2 += ' / ';
1138 | if (_mathNodes.query.isDiv(args[1])) {
1139 | _result2 += '(' + print(args[1], node) + ')';
1140 | } else {
1141 | _result2 += print(args[1], node);
1142 | }
1143 | }
1144 | return _mathNodes.query.isPow(parent) ? '(' + _result2 + ')' : _result2;
1145 | } else if (op === 'pow') {
1146 | var _node$args = _slicedToArray(node.args, 2),
1147 | base = _node$args[0],
1148 | exp = _node$args[1];
1149 |
1150 | return _mathNodes.query.isNeg(base) ? '(' + print(base, node) + ')^' + print(exp, node) : print(base, node) + '^' + print(exp, node);
1151 | } else if (op === 'neg') {
1152 | return '-' + print(args[0], node);
1153 | } else if (op === 'pos') {
1154 | return '+' + print(args[0], node);
1155 | } else if (op === 'pn') {
1156 | throw new Error('we don\'t handle \'pn\' operations yet');
1157 | } else if (op === 'np') {
1158 | throw new Error('we don\'t handle \'np\' operations yet');
1159 | } else if (op === 'fact') {
1160 | if (args[0].op === 'pow' || args[0].op === 'mul' || args[0].op === 'div') {
1161 | return '(' + print(args[0], node) + ')!';
1162 | } else {
1163 | return print(args[0], node) + '!';
1164 | }
1165 | } else if (op === 'nthRoot') {
1166 | return 'nthRoot(' + args.map(function (arg) {
1167 | return print(arg, node);
1168 | }).join(', ') + ')';
1169 | } else if (op === 'int') {
1170 | return 'int(' + args.map(function (arg) {
1171 | return print(arg, node);
1172 | }).join(', ') + ')';
1173 | } else if (op === 'abs') {
1174 | return '|' + print(args[0]) + '|';
1175 | } else if (op in relationIdentifierMap) {
1176 | var symbol = relationIdentifierMap[op];
1177 | return args.map(function (arg) {
1178 | return print(arg, node);
1179 | }).join(' ' + symbol + ' ');
1180 | } else {
1181 | return print(op) + '(' + args.map(function (arg) {
1182 | return print(arg, node);
1183 | }).join(', ') + ')';
1184 | }
1185 | };
1186 |
1187 | function print(node) {
1188 | var parent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
1189 |
1190 | switch (node.type) {
1191 | // regular non-leaf nodes
1192 | case 'Apply':
1193 | return printApply(node, parent);
1194 |
1195 | // irregular non-leaf nodes
1196 | case 'Parentheses':
1197 | return '(' + print(node.body, node) + ')';
1198 |
1199 | case 'Sequence':
1200 | return node.items.map(print).join(', ');
1201 |
1202 | // leaf nodes
1203 | case 'Identifier':
1204 | if (node.subscript) {
1205 | return node.name + '_' + print(node.subscript);
1206 | } else {
1207 | return node.name;
1208 | }
1209 |
1210 | case 'Placeholder':
1211 | if (node.subscript) {
1212 | return '#' + node.name + '_' + print(node.subscript);
1213 | } else {
1214 | return '#' + node.name;
1215 | }
1216 |
1217 | case 'Number':
1218 | return node.value;
1219 |
1220 | case 'Ellipsis':
1221 | return '...';
1222 |
1223 | default:
1224 | console.log(node); // eslint-disable-line no-console
1225 | throw new Error('unrecognized node');
1226 | }
1227 | }
1228 |
1229 | /***/ }),
1230 | /* 3 */
1231 | /***/ (function(module, exports, __webpack_require__) {
1232 |
1233 | "use strict";
1234 |
1235 |
1236 | Object.defineProperty(exports, "__esModule", {
1237 | value: true
1238 | });
1239 |
1240 | var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); /**
1241 | * toTex - return a string representation of the nodes in LaTeX
1242 | */
1243 |
1244 | exports.default = toTex;
1245 |
1246 | var _mathNodes = __webpack_require__(0);
1247 |
1248 | var relationIdentifierMap = {
1249 | 'eq': '=',
1250 | 'lt': '<',
1251 | 'le': '<=',
1252 | 'gt': '>',
1253 | 'ge': '>=',
1254 | 'ne': '!='
1255 | };
1256 |
1257 | var applyToTex = function applyToTex(node, parent) {
1258 | var op = node.op,
1259 | args = node.args;
1260 |
1261 |
1262 | if (op === 'add') {
1263 | //e.g a + (-a) => a - a
1264 | var result = toTex(node.args[0], node);
1265 | for (var i = 1; i < node.args.length; i++) {
1266 | var arg = node.args[i];
1267 | if (_mathNodes.query.isNeg(arg) && arg.wasMinus) {
1268 | result += ' - ' + toTex(arg.args[0], node);
1269 | } else {
1270 | result += ' + ' + toTex(arg, node);
1271 | }
1272 | }
1273 | return parent ? '\\left(' + result + '\\right)' : result;
1274 | } else if (op === 'mul') {
1275 | if (node.implicit) {
1276 | //e.g 2 x
1277 | return node.args.map(function (arg) {
1278 | return toTex(arg, node);
1279 | }).join(' ');
1280 | } else {
1281 | //e.g 2 * x
1282 | return node.args.map(function (arg) {
1283 | return toTex(arg, node);
1284 | }).join(' \\times ');
1285 | }
1286 | } else if (op === 'div') {
1287 | var _result = '';
1288 | _result += '\\frac';
1289 | //add parentheses when numerator or denominator has multiple terms
1290 | //e.g latex fractions: \frac{a}{b} => a/b
1291 | _result += '{' + toTex(node.args[0], node) + '}';
1292 | _result += '{' + toTex(node.args[1], node) + '}';
1293 | return _result;
1294 | } else if (op === 'pow') {
1295 | return toTex(node.args[0], node) + '^{' + toTex(node.args[1], node) + '}';
1296 | } else if (op === 'neg') {
1297 | return '-' + toTex(args[0], node);
1298 | } else if (op === 'pos') {
1299 | return '+' + toTex(args[0], node);
1300 | } else if (op === 'pn') {
1301 | throw new Error('we don\'t handle \'pn\' operations yet');
1302 | } else if (op === 'np') {
1303 | throw new Error('we don\'t handle \'np\' operations yet');
1304 | } else if (op === 'fact') {
1305 | throw new Error('we dont handle fact operations yet');
1306 | } else if (op === 'nthRoot') {
1307 | var _node$args = _slicedToArray(node.args, 2),
1308 | radicand = _node$args[0],
1309 | index = _node$args[1];
1310 |
1311 | var base = void 0,
1312 | exponent = void 0;
1313 | var _result2 = void 0;
1314 |
1315 | if (_mathNodes.query.isPow(radicand)) {
1316 | var _radicand$args = _slicedToArray(radicand.args, 2);
1317 |
1318 | base = _radicand$args[0];
1319 | exponent = _radicand$args[1];
1320 |
1321 | _result2 = index == '' || _mathNodes.query.getValue(index) == 2 ? '\\sqrt{' + toTex(base, node) + '^' + toTex(exponent, node) + '}' : '\\sqrt[' + toTex(index, node) + ']{' + toTex(base, node) + '^' + toTex(exponent, node) + '}';
1322 | } else {
1323 | base = radicand;
1324 | _result2 = index == '' || _mathNodes.query.getValue(index) == 2 ? '\\sqrt{' + toTex(base, node) + '}' : '\\sqrt[' + toTex(index, node) + ']{' + toTex(base, node) + '}';
1325 | }
1326 | return _result2;
1327 | } else if (op === 'int') {
1328 | var _result3 = void 0;
1329 | if (node.args.length == 2) {
1330 | var _node$args2 = _slicedToArray(node.args, 2),
1331 | input = _node$args2[0],
1332 | variable = _node$args2[1];
1333 |
1334 | _result3 = '\\int ' + toTex(input, node) + ' d' + toTex(variable, node);
1335 | } else if (node.args.length == 3) {
1336 | var _node$args3 = _slicedToArray(node.args, 3),
1337 | _input = _node$args3[0],
1338 | _variable = _node$args3[1],
1339 | domain = _node$args3[2];
1340 |
1341 | _result3 = '\\int_' + toTex(domain, node) + ' ' + toTex(_input, node) + ' d' + toTex(_variable, node);
1342 | } else if (node.args.length == 4) {
1343 | var _node$args4 = _slicedToArray(node.args, 4),
1344 | _input2 = _node$args4[0],
1345 | _variable2 = _node$args4[1],
1346 | upper = _node$args4[2],
1347 | lower = _node$args4[3];
1348 |
1349 | _result3 = '\\int_{' + toTex(upper, node) + '}^{' + toTex(lower, node) + '} ' + toTex(_input2, node) + ' d' + toTex(_variable2, node);
1350 | }
1351 | return _result3;
1352 | } else if (op in relationIdentifierMap) {
1353 | var symbol = relationIdentifierMap[op];
1354 | return args.map(function (arg) {
1355 | return toTex(arg, node);
1356 | }).join(' ' + symbol + ' ');
1357 | } else {
1358 | return op + '(' + args.map(function (arg) {
1359 | return toTex(arg, node);
1360 | }).join(', ') + ')';
1361 | }
1362 | };
1363 |
1364 | function toTex(node) {
1365 | var parent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
1366 |
1367 | switch (node.type) {
1368 | // regular non-leaf nodes
1369 | case 'Apply':
1370 | return applyToTex(node, parent);
1371 |
1372 | // irregular node-leaf nodes
1373 | case 'Parentheses':
1374 | return 'left(' + toTex(node.body, node) + '\right)';
1375 |
1376 | //leaf nodes
1377 | case 'Identifier':
1378 | return node.name;
1379 | case 'Number':
1380 | return node.value;
1381 |
1382 | default:
1383 | console.log(node); // eslint-disable-line no-console
1384 | throw new Error('unrecognized node');
1385 | }
1386 | }
1387 |
1388 | /***/ }),
1389 | /* 4 */
1390 | /***/ (function(module, exports) {
1391 |
1392 | module.exports =
1393 | /******/ (function(modules) { // webpackBootstrap
1394 | /******/ // The module cache
1395 | /******/ var installedModules = {};
1396 | /******/
1397 | /******/ // The require function
1398 | /******/ function __webpack_require__(moduleId) {
1399 | /******/
1400 | /******/ // Check if module is in cache
1401 | /******/ if(installedModules[moduleId]) {
1402 | /******/ return installedModules[moduleId].exports;
1403 | /******/ }
1404 | /******/ // Create a new module (and put it into the cache)
1405 | /******/ var module = installedModules[moduleId] = {
1406 | /******/ i: moduleId,
1407 | /******/ l: false,
1408 | /******/ exports: {}
1409 | /******/ };
1410 | /******/
1411 | /******/ // Execute the module function
1412 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
1413 | /******/
1414 | /******/ // Flag the module as loaded
1415 | /******/ module.l = true;
1416 | /******/
1417 | /******/ // Return the exports of the module
1418 | /******/ return module.exports;
1419 | /******/ }
1420 | /******/
1421 | /******/
1422 | /******/ // expose the modules object (__webpack_modules__)
1423 | /******/ __webpack_require__.m = modules;
1424 | /******/
1425 | /******/ // expose the module cache
1426 | /******/ __webpack_require__.c = installedModules;
1427 | /******/
1428 | /******/ // identity function for calling harmony imports with the correct context
1429 | /******/ __webpack_require__.i = function(value) { return value; };
1430 | /******/
1431 | /******/ // define getter function for harmony exports
1432 | /******/ __webpack_require__.d = function(exports, name, getter) {
1433 | /******/ if(!__webpack_require__.o(exports, name)) {
1434 | /******/ Object.defineProperty(exports, name, {
1435 | /******/ configurable: false,
1436 | /******/ enumerable: true,
1437 | /******/ get: getter
1438 | /******/ });
1439 | /******/ }
1440 | /******/ };
1441 | /******/
1442 | /******/ // getDefaultExport function for compatibility with non-harmony modules
1443 | /******/ __webpack_require__.n = function(module) {
1444 | /******/ var getter = module && module.__esModule ?
1445 | /******/ function getDefault() { return module['default']; } :
1446 | /******/ function getModuleExports() { return module; };
1447 | /******/ __webpack_require__.d(getter, 'a', getter);
1448 | /******/ return getter;
1449 | /******/ };
1450 | /******/
1451 | /******/ // Object.prototype.hasOwnProperty.call
1452 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
1453 | /******/
1454 | /******/ // __webpack_public_path__
1455 | /******/ __webpack_require__.p = "";
1456 | /******/
1457 | /******/ // Load entry module and return exports
1458 | /******/ return __webpack_require__(__webpack_require__.s = 2);
1459 | /******/ })
1460 | /************************************************************************/
1461 | /******/ ([
1462 | /* 0 */
1463 | /***/ (function(module, exports, __webpack_require__) {
1464 |
1465 | "use strict";
1466 |
1467 |
1468 | /**
1469 | * replace - visit all nodes in the tree with the ability to replace them.
1470 | *
1471 | * This function may modify the node passed in and/or any of its descendants.
1472 | *
1473 | * If neither 'enter' nor 'leave' return a value, the node is unchanged.
1474 | * If 'enter' returns a new node, the children of the new node will be traversed
1475 | * instead of the old one. If both 'enter' and 'leave' return values, the
1476 | * value returned by 'leave' is the node that will end up in the new AST.
1477 | */
1478 |
1479 | function replace(node, _ref) {
1480 | var enter = _ref.enter,
1481 | leave = _ref.leave;
1482 |
1483 | var rep = enter && enter(node) || node;
1484 |
1485 | switch (rep.type) {
1486 | // regular non-leaf nodes
1487 | case 'Apply':
1488 | for (var i = 0; i < rep.args.length; i++) {
1489 | var arg = rep.args[i];
1490 | rep.args[i] = replace(arg, { enter: enter, leave: leave });
1491 | }
1492 | break;
1493 |
1494 | // Skip leaf nodes because they're handled by the enter/leave calls at
1495 | // the start/end of replace.
1496 | case 'Identifier':
1497 | case 'Number':
1498 | case 'Ellipsis':
1499 | break;
1500 |
1501 | // irregular non-leaf nodes
1502 | case 'Parentheses':
1503 | rep.body = replace(rep.body, { enter: enter, leave: leave });
1504 | break;
1505 |
1506 | case 'List':
1507 | case 'Sequence':
1508 | for (var _i = 0; _i < rep.args.length; _i++) {
1509 | var item = rep.items[_i];
1510 | rep.items[_i] = replace(item, { enter: enter, leave: leave });
1511 | }
1512 | break;
1513 |
1514 | case 'System':
1515 | for (var _i2 = 0; _i2 < rep.relations.length; _i2++) {
1516 | var rel = rep.relations[_i2];
1517 | rep.relations[_i2] = replace(rel, { enter: enter, leave: leave });
1518 | }
1519 | break;
1520 |
1521 | case 'Placeholder':
1522 | // TODO(kevinb) handle children of the placeholder
1523 | // e.g. we there might #a_0 could match x_0, y_0, z_0, etc.
1524 | break;
1525 |
1526 | default:
1527 | throw new Error('unrecognized node');
1528 | }
1529 |
1530 | return leave && leave(rep) || rep;
1531 | }
1532 |
1533 | module.exports = replace;
1534 |
1535 | /***/ }),
1536 | /* 1 */
1537 | /***/ (function(module, exports, __webpack_require__) {
1538 |
1539 | "use strict";
1540 |
1541 |
1542 | function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
1543 |
1544 | /**
1545 | * traverse - walk all of the nodes in a tree.
1546 | */
1547 |
1548 | function traverse(node, _ref) {
1549 | var _ref$enter = _ref.enter,
1550 | enter = _ref$enter === undefined ? function () {} : _ref$enter,
1551 | _ref$leave = _ref.leave,
1552 | leave = _ref$leave === undefined ? function () {} : _ref$leave;
1553 | var path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
1554 |
1555 | switch (node.type) {
1556 | // regular non-leaf nodes
1557 | case 'Apply':
1558 | enter(node, path);
1559 | node.args.forEach(function (arg, index) {
1560 | return traverse(arg, { enter: enter, leave: leave }, [].concat(_toConsumableArray(path), ['args', index]));
1561 | });
1562 | leave(node, path);
1563 | break;
1564 |
1565 | // leaf nodes
1566 | case 'Identifier':
1567 | case 'Number':
1568 | case 'Ellipsis':
1569 | enter(node, path);
1570 | leave(node, path);
1571 | break;
1572 |
1573 | // irregular non-leaf nodes
1574 | case 'Parentheses':
1575 | enter(node, path);
1576 | traverse(node.body, { enter: enter, leave: leave }, [].concat(_toConsumableArray(path), ['body']));
1577 | leave(node, path);
1578 | break;
1579 |
1580 | case 'List':
1581 | case 'Sequence':
1582 | enter(node, path);
1583 | node.items.forEach(function (item, index) {
1584 | return traverse(item, { enter: enter, leave: leave }, [].concat(_toConsumableArray(path), ['items', index]));
1585 | });
1586 | leave(node, path);
1587 | break;
1588 |
1589 | case 'System':
1590 | enter(node, path);
1591 | node.relations.forEach(function (rel, index) {
1592 | return traverse(rel, { enter: enter, leave: leave }, [].concat(_toConsumableArray(path), ['relations', index]));
1593 | });
1594 | leave(node, path);
1595 | break;
1596 |
1597 | case 'Placeholder':
1598 | // TODO(kevinb) handle children of the placeholder
1599 | // e.g. we there might #a_0 could match x_0, y_0, z_0, etc.
1600 | enter(node, path);
1601 | leave(node, path);
1602 | break;
1603 |
1604 | default:
1605 | throw new Error('unrecognized node: ' + node.type);
1606 | }
1607 | }
1608 |
1609 | module.exports = traverse;
1610 |
1611 | /***/ }),
1612 | /* 2 */
1613 | /***/ (function(module, exports, __webpack_require__) {
1614 |
1615 | "use strict";
1616 |
1617 |
1618 | module.exports = {
1619 | replace: __webpack_require__(0),
1620 | traverse: __webpack_require__(1)
1621 | };
1622 |
1623 | /***/ })
1624 | /******/ ]);
1625 |
1626 | /***/ }),
1627 | /* 5 */
1628 | /***/ (function(module, exports, __webpack_require__) {
1629 |
1630 | "use strict";
1631 |
1632 |
1633 | Object.defineProperty(exports, "__esModule", {
1634 | value: true
1635 | });
1636 | exports.toTex = exports.print = exports.parse = undefined;
1637 |
1638 | var _parse = __webpack_require__(1);
1639 |
1640 | var _parse2 = _interopRequireDefault(_parse);
1641 |
1642 | var _print = __webpack_require__(2);
1643 |
1644 | var _print2 = _interopRequireDefault(_print);
1645 |
1646 | var _toTex = __webpack_require__(3);
1647 |
1648 | var _toTex2 = _interopRequireDefault(_toTex);
1649 |
1650 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
1651 |
1652 | exports.parse = _parse2.default;
1653 | exports.print = _print2.default;
1654 | exports.toTex = _toTex2.default;
1655 |
1656 | /***/ })
1657 | /******/ ]);
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import parse from './lib/parse'
2 | import print from './lib/print'
3 | import toTex from './lib/toTex'
4 |
5 | export {
6 | parse,
7 | print,
8 | toTex,
9 | }
10 |
--------------------------------------------------------------------------------
/lib/__test__/__snapshots__/parser.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Parser.parse abs ||a - b| - |b - c|| 1`] = `
4 | {
5 | "type": "Apply",
6 | "op": "abs",
7 | "args": [
8 | {
9 | "type": "Apply",
10 | "op": "add",
11 | "args": [
12 | {
13 | "type": "Apply",
14 | "op": "abs",
15 | "args": [
16 | {
17 | "type": "Apply",
18 | "op": "add",
19 | "args": [
20 | {
21 | "type": "Identifier",
22 | "name": "a"
23 | },
24 | {
25 | "wasMinus": true,
26 | "type": "Apply",
27 | "op": "neg",
28 | "args": [
29 | {
30 | "type": "Identifier",
31 | "name": "b"
32 | }
33 | ]
34 | }
35 | ]
36 | }
37 | ]
38 | },
39 | {
40 | "wasMinus": true,
41 | "type": "Apply",
42 | "op": "neg",
43 | "args": [
44 | {
45 | "type": "Apply",
46 | "op": "abs",
47 | "args": [
48 | {
49 | "type": "Apply",
50 | "op": "add",
51 | "args": [
52 | {
53 | "type": "Identifier",
54 | "name": "b"
55 | },
56 | {
57 | "wasMinus": true,
58 | "type": "Apply",
59 | "op": "neg",
60 | "args": [
61 | {
62 | "type": "Identifier",
63 | "name": "c"
64 | }
65 | ]
66 | }
67 | ]
68 | }
69 | ]
70 | }
71 | ]
72 | }
73 | ]
74 | }
75 | ]
76 | }
77 | `;
78 |
79 | exports[`Parser.parse abs |a - b| 1`] = `
80 | {
81 | "type": "Apply",
82 | "op": "abs",
83 | "args": [
84 | {
85 | "type": "Apply",
86 | "op": "add",
87 | "args": [
88 | {
89 | "type": "Identifier",
90 | "name": "a"
91 | },
92 | {
93 | "wasMinus": true,
94 | "type": "Apply",
95 | "op": "neg",
96 | "args": [
97 | {
98 | "type": "Identifier",
99 | "name": "b"
100 | }
101 | ]
102 | }
103 | ]
104 | }
105 | ]
106 | }
107 | `;
108 |
109 | exports[`Parser.parse abs abs(-1) 1`] = `
110 | {
111 | "type": "Apply",
112 | "op": "abs",
113 | "args": [
114 | {
115 | "type": "Apply",
116 | "op": "neg",
117 | "args": [
118 | {
119 | "value": "1",
120 | "type": "Number"
121 | }
122 | ]
123 | }
124 | ]
125 | }
126 | `;
127 |
128 | exports[`Parser.parse addition/subtraction 1 - -2 1`] = `
129 | {
130 | "type": "Apply",
131 | "op": "add",
132 | "args": [
133 | {
134 | "value": "1",
135 | "type": "Number"
136 | },
137 | {
138 | "wasMinus": true,
139 | "type": "Apply",
140 | "op": "neg",
141 | "args": [
142 | {
143 | "type": "Apply",
144 | "op": "neg",
145 | "args": [
146 | {
147 | "value": "2",
148 | "type": "Number"
149 | }
150 | ]
151 | }
152 | ]
153 | }
154 | ]
155 | }
156 | `;
157 |
158 | exports[`Parser.parse addition/subtraction 1 - 2 1`] = `
159 | {
160 | "type": "Apply",
161 | "op": "add",
162 | "args": [
163 | {
164 | "value": "1",
165 | "type": "Number"
166 | },
167 | {
168 | "wasMinus": true,
169 | "type": "Apply",
170 | "op": "neg",
171 | "args": [
172 | {
173 | "value": "2",
174 | "type": "Number"
175 | }
176 | ]
177 | }
178 | ]
179 | }
180 | `;
181 |
182 | exports[`Parser.parse addition/subtraction a + -b - c 1`] = `
183 | {
184 | "type": "Apply",
185 | "op": "add",
186 | "args": [
187 | {
188 | "type": "Identifier",
189 | "name": "a"
190 | },
191 | {
192 | "type": "Apply",
193 | "op": "neg",
194 | "args": [
195 | {
196 | "type": "Identifier",
197 | "name": "b"
198 | }
199 | ]
200 | },
201 | {
202 | "wasMinus": true,
203 | "type": "Apply",
204 | "op": "neg",
205 | "args": [
206 | {
207 | "type": "Identifier",
208 | "name": "c"
209 | }
210 | ]
211 | }
212 | ]
213 | }
214 | `;
215 |
216 | exports[`Parser.parse addition/subtraction a + b + c 1`] = `
217 | {
218 | "type": "Apply",
219 | "op": "add",
220 | "args": [
221 | {
222 | "type": "Identifier",
223 | "name": "a"
224 | },
225 | {
226 | "type": "Identifier",
227 | "name": "b"
228 | },
229 | {
230 | "type": "Identifier",
231 | "name": "c"
232 | }
233 | ]
234 | }
235 | `;
236 |
237 | exports[`Parser.parse addition/subtraction a - b - -c 1`] = `
238 | {
239 | "type": "Apply",
240 | "op": "add",
241 | "args": [
242 | {
243 | "type": "Identifier",
244 | "name": "a"
245 | },
246 | {
247 | "wasMinus": true,
248 | "type": "Apply",
249 | "op": "neg",
250 | "args": [
251 | {
252 | "type": "Identifier",
253 | "name": "b"
254 | }
255 | ]
256 | },
257 | {
258 | "wasMinus": true,
259 | "type": "Apply",
260 | "op": "neg",
261 | "args": [
262 | {
263 | "type": "Apply",
264 | "op": "neg",
265 | "args": [
266 | {
267 | "type": "Identifier",
268 | "name": "c"
269 | }
270 | ]
271 | }
272 | ]
273 | }
274 | ]
275 | }
276 | `;
277 |
278 | exports[`Parser.parse addition/subtraction a - b - c 1`] = `
279 | {
280 | "type": "Apply",
281 | "op": "add",
282 | "args": [
283 | {
284 | "type": "Identifier",
285 | "name": "a"
286 | },
287 | {
288 | "wasMinus": true,
289 | "type": "Apply",
290 | "op": "neg",
291 | "args": [
292 | {
293 | "type": "Identifier",
294 | "name": "b"
295 | }
296 | ]
297 | },
298 | {
299 | "wasMinus": true,
300 | "type": "Apply",
301 | "op": "neg",
302 | "args": [
303 | {
304 | "type": "Identifier",
305 | "name": "c"
306 | }
307 | ]
308 | }
309 | ]
310 | }
311 | `;
312 |
313 | exports[`Parser.parse division (a*b)/(c*d) 1`] = `
314 | {
315 | "type": "Apply",
316 | "op": "div",
317 | "args": [
318 | {
319 | "type": "Apply",
320 | "op": "mul",
321 | "args": [
322 | {
323 | "type": "Identifier",
324 | "name": "a"
325 | },
326 | {
327 | "type": "Identifier",
328 | "name": "b"
329 | }
330 | ]
331 | },
332 | {
333 | "type": "Apply",
334 | "op": "mul",
335 | "args": [
336 | {
337 | "type": "Identifier",
338 | "name": "c"
339 | },
340 | {
341 | "type": "Identifier",
342 | "name": "d"
343 | }
344 | ]
345 | }
346 | ]
347 | }
348 | `;
349 |
350 | exports[`Parser.parse division 2(x+1)/4 1`] = `
351 | {
352 | "type": "Apply",
353 | "op": "mul",
354 | "implicit": true,
355 | "args": [
356 | {
357 | "value": "2",
358 | "type": "Number"
359 | },
360 | {
361 | "type": "Apply",
362 | "op": "div",
363 | "args": [
364 | {
365 | "type": "Apply",
366 | "op": "add",
367 | "args": [
368 | {
369 | "type": "Identifier",
370 | "name": "x"
371 | },
372 | {
373 | "value": "1",
374 | "type": "Number"
375 | }
376 | ]
377 | },
378 | {
379 | "value": "4",
380 | "type": "Number"
381 | }
382 | ]
383 | }
384 | ]
385 | }
386 | `;
387 |
388 | exports[`Parser.parse division 2x/4 1`] = `
389 | {
390 | "type": "Apply",
391 | "op": "mul",
392 | "implicit": true,
393 | "args": [
394 | {
395 | "value": "2",
396 | "type": "Number"
397 | },
398 | {
399 | "type": "Apply",
400 | "op": "div",
401 | "args": [
402 | {
403 | "type": "Identifier",
404 | "name": "x"
405 | },
406 | {
407 | "value": "4",
408 | "type": "Number"
409 | }
410 | ]
411 | }
412 | ]
413 | }
414 | `;
415 |
416 | exports[`Parser.parse division a b c/d 1`] = `
417 | {
418 | "type": "Apply",
419 | "op": "mul",
420 | "implicit": true,
421 | "args": [
422 | {
423 | "type": "Identifier",
424 | "name": "a"
425 | },
426 | {
427 | "type": "Identifier",
428 | "name": "b"
429 | },
430 | {
431 | "type": "Apply",
432 | "op": "div",
433 | "args": [
434 | {
435 | "type": "Identifier",
436 | "name": "c"
437 | },
438 | {
439 | "type": "Identifier",
440 | "name": "d"
441 | }
442 | ]
443 | }
444 | ]
445 | }
446 | `;
447 |
448 | exports[`Parser.parse division a*b*c/d 1`] = `
449 | {
450 | "type": "Apply",
451 | "op": "mul",
452 | "args": [
453 | {
454 | "type": "Identifier",
455 | "name": "a"
456 | },
457 | {
458 | "type": "Identifier",
459 | "name": "b"
460 | },
461 | {
462 | "type": "Apply",
463 | "op": "div",
464 | "args": [
465 | {
466 | "type": "Identifier",
467 | "name": "c"
468 | },
469 | {
470 | "type": "Identifier",
471 | "name": "d"
472 | }
473 | ]
474 | }
475 | ]
476 | }
477 | `;
478 |
479 | exports[`Parser.parse division a/b*c/d 1`] = `
480 | {
481 | "type": "Apply",
482 | "op": "mul",
483 | "args": [
484 | {
485 | "type": "Apply",
486 | "op": "div",
487 | "args": [
488 | {
489 | "type": "Identifier",
490 | "name": "a"
491 | },
492 | {
493 | "type": "Identifier",
494 | "name": "b"
495 | }
496 | ]
497 | },
498 | {
499 | "type": "Apply",
500 | "op": "div",
501 | "args": [
502 | {
503 | "type": "Identifier",
504 | "name": "c"
505 | },
506 | {
507 | "type": "Identifier",
508 | "name": "d"
509 | }
510 | ]
511 | }
512 | ]
513 | }
514 | `;
515 |
516 | exports[`Parser.parse division a/b/c 1`] = `
517 | {
518 | "type": "Apply",
519 | "op": "div",
520 | "args": [
521 | {
522 | "type": "Apply",
523 | "op": "div",
524 | "args": [
525 | {
526 | "type": "Identifier",
527 | "name": "a"
528 | },
529 | {
530 | "type": "Identifier",
531 | "name": "b"
532 | }
533 | ]
534 | },
535 | {
536 | "type": "Identifier",
537 | "name": "c"
538 | }
539 | ]
540 | }
541 | `;
542 |
543 | exports[`Parser.parse division a^2/b^2 1`] = `
544 | {
545 | "type": "Apply",
546 | "op": "div",
547 | "args": [
548 | {
549 | "type": "Apply",
550 | "op": "pow",
551 | "args": [
552 | {
553 | "type": "Identifier",
554 | "name": "a"
555 | },
556 | {
557 | "value": "2",
558 | "type": "Number"
559 | }
560 | ]
561 | },
562 | {
563 | "type": "Apply",
564 | "op": "pow",
565 | "args": [
566 | {
567 | "type": "Identifier",
568 | "name": "b"
569 | },
570 | {
571 | "value": "2",
572 | "type": "Number"
573 | }
574 | ]
575 | }
576 | ]
577 | }
578 | `;
579 |
580 | exports[`Parser.parse ellipsis #a_0#x + ... + #a_n#x 1`] = `
581 | {
582 | "type": "Apply",
583 | "op": "add",
584 | "args": [
585 | {
586 | "type": "Apply",
587 | "op": "mul",
588 | "implicit": true,
589 | "args": [
590 | {
591 | "type": "Placeholder",
592 | "subscript": {
593 | "value": "0",
594 | "type": "Number"
595 | },
596 | "name": "a"
597 | },
598 | {
599 | "type": "Placeholder",
600 | "name": "x"
601 | }
602 | ]
603 | },
604 | {
605 | "type": "Ellipsis"
606 | },
607 | {
608 | "type": "Apply",
609 | "op": "mul",
610 | "implicit": true,
611 | "args": [
612 | {
613 | "type": "Placeholder",
614 | "subscript": {
615 | "type": "Identifier",
616 | "name": "n"
617 | },
618 | "name": "a"
619 | },
620 | {
621 | "type": "Placeholder",
622 | "name": "x"
623 | }
624 | ]
625 | }
626 | ]
627 | }
628 | `;
629 |
630 | exports[`Parser.parse ellipsis 1 * ... * n 1`] = `
631 | {
632 | "type": "Apply",
633 | "op": "mul",
634 | "args": [
635 | {
636 | "value": "1",
637 | "type": "Number"
638 | },
639 | {
640 | "type": "Ellipsis"
641 | },
642 | {
643 | "type": "Identifier",
644 | "name": "n"
645 | }
646 | ]
647 | }
648 | `;
649 |
650 | exports[`Parser.parse ellipsis 1, 2, ..., n 1`] = `
651 | {
652 | "type": "Sequence",
653 | "items": [
654 | {
655 | "value": "1",
656 | "type": "Number"
657 | },
658 | {
659 | "value": "2",
660 | "type": "Number"
661 | },
662 | {
663 | "type": "Ellipsis"
664 | },
665 | {
666 | "type": "Identifier",
667 | "name": "n"
668 | }
669 | ]
670 | }
671 | `;
672 |
673 | exports[`Parser.parse ellipsis a + ... + z 1`] = `
674 | {
675 | "type": "Apply",
676 | "op": "add",
677 | "args": [
678 | {
679 | "type": "Identifier",
680 | "name": "a"
681 | },
682 | {
683 | "type": "Ellipsis"
684 | },
685 | {
686 | "type": "Identifier",
687 | "name": "z"
688 | }
689 | ]
690 | }
691 | `;
692 |
693 | exports[`Parser.parse factorial (2 * n)! 1`] = `
694 | {
695 | "type": "Apply",
696 | "op": "fact",
697 | "args": [
698 | {
699 | "type": "Apply",
700 | "op": "mul",
701 | "args": [
702 | {
703 | "value": "2",
704 | "type": "Number"
705 | },
706 | {
707 | "type": "Identifier",
708 | "name": "n"
709 | }
710 | ]
711 | }
712 | ]
713 | }
714 | `;
715 |
716 | exports[`Parser.parse factorial (5^2)! 1`] = `
717 | {
718 | "type": "Apply",
719 | "op": "fact",
720 | "args": [
721 | {
722 | "type": "Apply",
723 | "op": "pow",
724 | "args": [
725 | {
726 | "value": "5",
727 | "type": "Number"
728 | },
729 | {
730 | "value": "2",
731 | "type": "Number"
732 | }
733 | ]
734 | }
735 | ]
736 | }
737 | `;
738 |
739 | exports[`Parser.parse factorial 2 * n! 1`] = `
740 | {
741 | "type": "Apply",
742 | "op": "mul",
743 | "args": [
744 | {
745 | "value": "2",
746 | "type": "Number"
747 | },
748 | {
749 | "type": "Apply",
750 | "op": "fact",
751 | "args": [
752 | {
753 | "type": "Identifier",
754 | "name": "n"
755 | }
756 | ]
757 | }
758 | ]
759 | }
760 | `;
761 |
762 | exports[`Parser.parse factorial 5! 1`] = `
763 | {
764 | "type": "Apply",
765 | "op": "fact",
766 | "args": [
767 | {
768 | "value": "5",
769 | "type": "Number"
770 | }
771 | ]
772 | }
773 | `;
774 |
775 | exports[`Parser.parse factorial 5!^2 1`] = `
776 | {
777 | "type": "Apply",
778 | "op": "pow",
779 | "args": [
780 | {
781 | "type": "Apply",
782 | "op": "fact",
783 | "args": [
784 | {
785 | "value": "5",
786 | "type": "Number"
787 | }
788 | ]
789 | },
790 | {
791 | "value": "2",
792 | "type": "Number"
793 | }
794 | ]
795 | }
796 | `;
797 |
798 | exports[`Parser.parse factorial x! 1`] = `
799 | {
800 | "type": "Apply",
801 | "op": "fact",
802 | "args": [
803 | {
804 | "type": "Identifier",
805 | "name": "x"
806 | }
807 | ]
808 | }
809 | `;
810 |
811 | exports[`Parser.parse factorial x^2! 1`] = `
812 | {
813 | "type": "Apply",
814 | "op": "pow",
815 | "args": [
816 | {
817 | "type": "Identifier",
818 | "name": "x"
819 | },
820 | {
821 | "type": "Apply",
822 | "op": "fact",
823 | "args": [
824 | {
825 | "value": "2",
826 | "type": "Number"
827 | }
828 | ]
829 | }
830 | ]
831 | }
832 | `;
833 |
834 | exports[`Parser.parse functions f(a+b) 1`] = `
835 | {
836 | "type": "Apply",
837 | "op": {
838 | "type": "Identifier",
839 | "name": "f"
840 | },
841 | "args": [
842 | {
843 | "type": "Apply",
844 | "op": "add",
845 | "args": [
846 | {
847 | "type": "Identifier",
848 | "name": "a"
849 | },
850 | {
851 | "type": "Identifier",
852 | "name": "b"
853 | }
854 | ]
855 | }
856 | ]
857 | }
858 | `;
859 |
860 | exports[`Parser.parse functions f(a,b) 1`] = `
861 | {
862 | "type": "Apply",
863 | "op": {
864 | "type": "Identifier",
865 | "name": "f"
866 | },
867 | "args": [
868 | {
869 | "type": "Identifier",
870 | "name": "a"
871 | },
872 | {
873 | "type": "Identifier",
874 | "name": "b"
875 | }
876 | ]
877 | }
878 | `;
879 |
880 | exports[`Parser.parse functions f(f(a)) 1`] = `
881 | {
882 | "type": "Apply",
883 | "op": {
884 | "type": "Identifier",
885 | "name": "f"
886 | },
887 | "args": [
888 | {
889 | "type": "Apply",
890 | "op": {
891 | "type": "Identifier",
892 | "name": "f"
893 | },
894 | "args": [
895 | {
896 | "type": "Identifier",
897 | "name": "a"
898 | }
899 | ]
900 | }
901 | ]
902 | }
903 | `;
904 |
905 | exports[`Parser.parse integrals int x^2 dx 1`] = `
906 | {
907 | "type": "Apply",
908 | "op": "int",
909 | "args": [
910 | {
911 | "type": "Apply",
912 | "op": "pow",
913 | "args": [
914 | {
915 | "type": "Identifier",
916 | "name": "x"
917 | },
918 | {
919 | "value": "2",
920 | "type": "Number"
921 | }
922 | ]
923 | },
924 | {
925 | "type": "Identifier",
926 | "name": "dx"
927 | }
928 | ]
929 | }
930 | `;
931 |
932 | exports[`Parser.parse integrals int_0^1 int_0^1 x^2 + y^2 dx dy 1`] = `
933 | {
934 | "type": "Apply",
935 | "op": "int",
936 | "limits": [
937 | {
938 | "value": "0",
939 | "type": "Number"
940 | },
941 | {
942 | "value": "1",
943 | "type": "Number"
944 | }
945 | ],
946 | "args": [
947 | {
948 | "type": "Apply",
949 | "op": "int",
950 | "limits": [
951 | {
952 | "value": "0",
953 | "type": "Number"
954 | },
955 | {
956 | "value": "1",
957 | "type": "Number"
958 | }
959 | ],
960 | "args": [
961 | {
962 | "type": "Apply",
963 | "op": "add",
964 | "args": [
965 | {
966 | "type": "Apply",
967 | "op": "pow",
968 | "args": [
969 | {
970 | "type": "Identifier",
971 | "name": "x"
972 | },
973 | {
974 | "value": "2",
975 | "type": "Number"
976 | }
977 | ]
978 | },
979 | {
980 | "type": "Apply",
981 | "op": "pow",
982 | "args": [
983 | {
984 | "type": "Identifier",
985 | "name": "y"
986 | },
987 | {
988 | "value": "2",
989 | "type": "Number"
990 | }
991 | ]
992 | }
993 | ]
994 | },
995 | {
996 | "type": "Identifier",
997 | "name": "dx"
998 | }
999 | ]
1000 | },
1001 | {
1002 | "type": "Identifier",
1003 | "name": "dy"
1004 | }
1005 | ]
1006 | }
1007 | `;
1008 |
1009 | exports[`Parser.parse integrals int_0^1 int_0^1 x^2 y^2 dx dy 1`] = `
1010 | {
1011 | "type": "Apply",
1012 | "op": "int",
1013 | "limits": [
1014 | {
1015 | "value": "0",
1016 | "type": "Number"
1017 | },
1018 | {
1019 | "value": "1",
1020 | "type": "Number"
1021 | }
1022 | ],
1023 | "args": [
1024 | {
1025 | "type": "Apply",
1026 | "op": "int",
1027 | "limits": [
1028 | {
1029 | "value": "0",
1030 | "type": "Number"
1031 | },
1032 | {
1033 | "value": "1",
1034 | "type": "Number"
1035 | }
1036 | ],
1037 | "args": [
1038 | {
1039 | "type": "Apply",
1040 | "op": "mul",
1041 | "implicit": true,
1042 | "args": [
1043 | {
1044 | "type": "Apply",
1045 | "op": "pow",
1046 | "args": [
1047 | {
1048 | "type": "Identifier",
1049 | "name": "x"
1050 | },
1051 | {
1052 | "value": "2",
1053 | "type": "Number"
1054 | }
1055 | ]
1056 | },
1057 | {
1058 | "type": "Apply",
1059 | "op": "pow",
1060 | "args": [
1061 | {
1062 | "type": "Identifier",
1063 | "name": "y"
1064 | },
1065 | {
1066 | "value": "2",
1067 | "type": "Number"
1068 | }
1069 | ]
1070 | }
1071 | ]
1072 | },
1073 | {
1074 | "type": "Identifier",
1075 | "name": "dx"
1076 | }
1077 | ]
1078 | },
1079 | {
1080 | "type": "Identifier",
1081 | "name": "dy"
1082 | }
1083 | ]
1084 | }
1085 | `;
1086 |
1087 | exports[`Parser.parse integrals int_0^1 x^2 2x dx 1`] = `
1088 | {
1089 | "type": "Apply",
1090 | "op": "int",
1091 | "limits": [
1092 | {
1093 | "value": "0",
1094 | "type": "Number"
1095 | },
1096 | {
1097 | "value": "1",
1098 | "type": "Number"
1099 | }
1100 | ],
1101 | "args": [
1102 | {
1103 | "type": "Apply",
1104 | "op": "mul",
1105 | "implicit": true,
1106 | "args": [
1107 | {
1108 | "type": "Apply",
1109 | "op": "pow",
1110 | "args": [
1111 | {
1112 | "type": "Identifier",
1113 | "name": "x"
1114 | },
1115 | {
1116 | "value": "2",
1117 | "type": "Number"
1118 | }
1119 | ]
1120 | },
1121 | {
1122 | "value": "2",
1123 | "type": "Number"
1124 | },
1125 | {
1126 | "type": "Identifier",
1127 | "name": "x"
1128 | }
1129 | ]
1130 | },
1131 | {
1132 | "type": "Identifier",
1133 | "name": "dx"
1134 | }
1135 | ]
1136 | }
1137 | `;
1138 |
1139 | exports[`Parser.parse integrals int_0^1 x^2 dx 1`] = `
1140 | {
1141 | "type": "Apply",
1142 | "op": "int",
1143 | "limits": [
1144 | {
1145 | "value": "0",
1146 | "type": "Number"
1147 | },
1148 | {
1149 | "value": "1",
1150 | "type": "Number"
1151 | }
1152 | ],
1153 | "args": [
1154 | {
1155 | "type": "Apply",
1156 | "op": "pow",
1157 | "args": [
1158 | {
1159 | "type": "Identifier",
1160 | "name": "x"
1161 | },
1162 | {
1163 | "value": "2",
1164 | "type": "Number"
1165 | }
1166 | ]
1167 | },
1168 | {
1169 | "type": "Identifier",
1170 | "name": "dx"
1171 | }
1172 | ]
1173 | }
1174 | `;
1175 |
1176 | exports[`Parser.parse multiplication (a)(b)(c) 1`] = `
1177 | {
1178 | "type": "Apply",
1179 | "op": "mul",
1180 | "implicit": true,
1181 | "args": [
1182 | {
1183 | "type": "Parentheses",
1184 | "body": {
1185 | "type": "Identifier",
1186 | "name": "a"
1187 | }
1188 | },
1189 | {
1190 | "type": "Parentheses",
1191 | "body": {
1192 | "type": "Identifier",
1193 | "name": "b"
1194 | }
1195 | },
1196 | {
1197 | "type": "Parentheses",
1198 | "body": {
1199 | "type": "Identifier",
1200 | "name": "c"
1201 | }
1202 | }
1203 | ]
1204 | }
1205 | `;
1206 |
1207 | exports[`Parser.parse multiplication (a)b(c) 1`] = `
1208 | {
1209 | "type": "Apply",
1210 | "op": "mul",
1211 | "implicit": true,
1212 | "args": [
1213 | {
1214 | "type": "Parentheses",
1215 | "body": {
1216 | "type": "Identifier",
1217 | "name": "a"
1218 | }
1219 | },
1220 | {
1221 | "type": "Apply",
1222 | "op": {
1223 | "type": "Identifier",
1224 | "name": "b"
1225 | },
1226 | "args": [
1227 | {
1228 | "type": "Identifier",
1229 | "name": "c"
1230 | }
1231 | ]
1232 | }
1233 | ]
1234 | }
1235 | `;
1236 |
1237 | exports[`Parser.parse multiplication a b * b * b c 1`] = `
1238 | {
1239 | "type": "Apply",
1240 | "op": "mul",
1241 | "args": [
1242 | {
1243 | "type": "Apply",
1244 | "op": "mul",
1245 | "implicit": true,
1246 | "args": [
1247 | {
1248 | "type": "Identifier",
1249 | "name": "a"
1250 | },
1251 | {
1252 | "type": "Identifier",
1253 | "name": "b"
1254 | }
1255 | ]
1256 | },
1257 | {
1258 | "type": "Identifier",
1259 | "name": "b"
1260 | },
1261 | {
1262 | "type": "Apply",
1263 | "op": "mul",
1264 | "implicit": true,
1265 | "args": [
1266 | {
1267 | "type": "Identifier",
1268 | "name": "b"
1269 | },
1270 | {
1271 | "type": "Identifier",
1272 | "name": "c"
1273 | }
1274 | ]
1275 | }
1276 | ]
1277 | }
1278 | `;
1279 |
1280 | exports[`Parser.parse multiplication a b c 1`] = `
1281 | {
1282 | "type": "Apply",
1283 | "op": "mul",
1284 | "implicit": true,
1285 | "args": [
1286 | {
1287 | "type": "Identifier",
1288 | "name": "a"
1289 | },
1290 | {
1291 | "type": "Identifier",
1292 | "name": "b"
1293 | },
1294 | {
1295 | "type": "Identifier",
1296 | "name": "c"
1297 | }
1298 | ]
1299 | }
1300 | `;
1301 |
1302 | exports[`Parser.parse multiplication a*b c 1`] = `
1303 | {
1304 | "type": "Apply",
1305 | "op": "mul",
1306 | "args": [
1307 | {
1308 | "type": "Identifier",
1309 | "name": "a"
1310 | },
1311 | {
1312 | "type": "Apply",
1313 | "op": "mul",
1314 | "implicit": true,
1315 | "args": [
1316 | {
1317 | "type": "Identifier",
1318 | "name": "b"
1319 | },
1320 | {
1321 | "type": "Identifier",
1322 | "name": "c"
1323 | }
1324 | ]
1325 | }
1326 | ]
1327 | }
1328 | `;
1329 |
1330 | exports[`Parser.parse multiplication a*b*c 1`] = `
1331 | {
1332 | "type": "Apply",
1333 | "op": "mul",
1334 | "args": [
1335 | {
1336 | "type": "Identifier",
1337 | "name": "a"
1338 | },
1339 | {
1340 | "type": "Identifier",
1341 | "name": "b"
1342 | },
1343 | {
1344 | "type": "Identifier",
1345 | "name": "c"
1346 | }
1347 | ]
1348 | }
1349 | `;
1350 |
1351 | exports[`Parser.parse nthRoot nthRoot(-27, 3) 1`] = `
1352 | {
1353 | "type": "Apply",
1354 | "op": "nthRoot",
1355 | "args": [
1356 | {
1357 | "type": "Apply",
1358 | "op": "neg",
1359 | "args": [
1360 | {
1361 | "value": "27",
1362 | "type": "Number"
1363 | }
1364 | ]
1365 | },
1366 | {
1367 | "value": "3",
1368 | "type": "Number"
1369 | }
1370 | ]
1371 | }
1372 | `;
1373 |
1374 | exports[`Parser.parse nthRoot nthRoot(x) 1`] = `
1375 | {
1376 | "type": "Apply",
1377 | "op": "nthRoot",
1378 | "args": [
1379 | {
1380 | "type": "Identifier",
1381 | "name": "x"
1382 | },
1383 | {
1384 | "value": "2",
1385 | "type": "Number"
1386 | }
1387 | ]
1388 | }
1389 | `;
1390 |
1391 | exports[`Parser.parse nthRoot nthRoot(x, 2) 1`] = `
1392 | {
1393 | "type": "Apply",
1394 | "op": "nthRoot",
1395 | "args": [
1396 | {
1397 | "type": "Identifier",
1398 | "name": "x"
1399 | },
1400 | {
1401 | "value": "2",
1402 | "type": "Number"
1403 | }
1404 | ]
1405 | }
1406 | `;
1407 |
1408 | exports[`Parser.parse parentheses ((1 + 2)) 1`] = `
1409 | {
1410 | "type": "Parentheses",
1411 | "body": {
1412 | "type": "Parentheses",
1413 | "body": {
1414 | "type": "Apply",
1415 | "op": "add",
1416 | "args": [
1417 | {
1418 | "value": "1",
1419 | "type": "Number"
1420 | },
1421 | {
1422 | "value": "2",
1423 | "type": "Number"
1424 | }
1425 | ]
1426 | }
1427 | }
1428 | }
1429 | `;
1430 |
1431 | exports[`Parser.parse parentheses ((a * b)) 1`] = `
1432 | {
1433 | "type": "Parentheses",
1434 | "body": {
1435 | "type": "Parentheses",
1436 | "body": {
1437 | "type": "Apply",
1438 | "op": "mul",
1439 | "args": [
1440 | {
1441 | "type": "Identifier",
1442 | "name": "a"
1443 | },
1444 | {
1445 | "type": "Identifier",
1446 | "name": "b"
1447 | }
1448 | ]
1449 | }
1450 | }
1451 | }
1452 | `;
1453 |
1454 | exports[`Parser.parse parentheses (1 + 2) 1`] = `
1455 | {
1456 | "type": "Parentheses",
1457 | "body": {
1458 | "type": "Apply",
1459 | "op": "add",
1460 | "args": [
1461 | {
1462 | "value": "1",
1463 | "type": "Number"
1464 | },
1465 | {
1466 | "value": "2",
1467 | "type": "Number"
1468 | }
1469 | ]
1470 | }
1471 | }
1472 | `;
1473 |
1474 | exports[`Parser.parse parentheses (a * b) 1`] = `
1475 | {
1476 | "type": "Parentheses",
1477 | "body": {
1478 | "type": "Apply",
1479 | "op": "mul",
1480 | "args": [
1481 | {
1482 | "type": "Identifier",
1483 | "name": "a"
1484 | },
1485 | {
1486 | "type": "Identifier",
1487 | "name": "b"
1488 | }
1489 | ]
1490 | }
1491 | }
1492 | `;
1493 |
1494 | exports[`Parser.parse parentheses (a)(b) 1`] = `
1495 | {
1496 | "type": "Apply",
1497 | "op": "mul",
1498 | "implicit": true,
1499 | "args": [
1500 | {
1501 | "type": "Parentheses",
1502 | "body": {
1503 | "type": "Identifier",
1504 | "name": "a"
1505 | }
1506 | },
1507 | {
1508 | "type": "Parentheses",
1509 | "body": {
1510 | "type": "Identifier",
1511 | "name": "b"
1512 | }
1513 | }
1514 | ]
1515 | }
1516 | `;
1517 |
1518 | exports[`Parser.parse parentheses (x + y) - (a + b) 1`] = `
1519 | {
1520 | "type": "Apply",
1521 | "op": "add",
1522 | "args": [
1523 | {
1524 | "type": "Parentheses",
1525 | "body": {
1526 | "type": "Apply",
1527 | "op": "add",
1528 | "args": [
1529 | {
1530 | "type": "Identifier",
1531 | "name": "x"
1532 | },
1533 | {
1534 | "type": "Identifier",
1535 | "name": "y"
1536 | }
1537 | ]
1538 | }
1539 | },
1540 | {
1541 | "wasMinus": true,
1542 | "type": "Apply",
1543 | "op": "neg",
1544 | "args": [
1545 | {
1546 | "type": "Apply",
1547 | "op": "add",
1548 | "args": [
1549 | {
1550 | "type": "Identifier",
1551 | "name": "a"
1552 | },
1553 | {
1554 | "type": "Identifier",
1555 | "name": "b"
1556 | }
1557 | ]
1558 | }
1559 | ]
1560 | }
1561 | ]
1562 | }
1563 | `;
1564 |
1565 | exports[`Parser.parse parentheses 5 + ((3 * 6)) 1`] = `
1566 | {
1567 | "type": "Apply",
1568 | "op": "add",
1569 | "args": [
1570 | {
1571 | "value": "5",
1572 | "type": "Number"
1573 | },
1574 | {
1575 | "type": "Parentheses",
1576 | "body": {
1577 | "type": "Parentheses",
1578 | "body": {
1579 | "type": "Apply",
1580 | "op": "mul",
1581 | "args": [
1582 | {
1583 | "value": "3",
1584 | "type": "Number"
1585 | },
1586 | {
1587 | "value": "6",
1588 | "type": "Number"
1589 | }
1590 | ]
1591 | }
1592 | }
1593 | }
1594 | ]
1595 | }
1596 | `;
1597 |
1598 | exports[`Parser.parse parentheses 5 + (3 * 6) 1`] = `
1599 | {
1600 | "type": "Apply",
1601 | "op": "add",
1602 | "args": [
1603 | {
1604 | "value": "5",
1605 | "type": "Number"
1606 | },
1607 | {
1608 | "type": "Parentheses",
1609 | "body": {
1610 | "type": "Apply",
1611 | "op": "mul",
1612 | "args": [
1613 | {
1614 | "value": "3",
1615 | "type": "Number"
1616 | },
1617 | {
1618 | "value": "6",
1619 | "type": "Number"
1620 | }
1621 | ]
1622 | }
1623 | }
1624 | ]
1625 | }
1626 | `;
1627 |
1628 | exports[`Parser.parse parentheses a * (b + c) 1`] = `
1629 | {
1630 | "type": "Apply",
1631 | "op": "mul",
1632 | "args": [
1633 | {
1634 | "type": "Identifier",
1635 | "name": "a"
1636 | },
1637 | {
1638 | "type": "Apply",
1639 | "op": "add",
1640 | "args": [
1641 | {
1642 | "type": "Identifier",
1643 | "name": "b"
1644 | },
1645 | {
1646 | "type": "Identifier",
1647 | "name": "c"
1648 | }
1649 | ]
1650 | }
1651 | ]
1652 | }
1653 | `;
1654 |
1655 | exports[`Parser.parse placeholders #a #b / #c 1`] = `
1656 | {
1657 | "type": "Apply",
1658 | "op": "mul",
1659 | "implicit": true,
1660 | "args": [
1661 | {
1662 | "type": "Placeholder",
1663 | "name": "a"
1664 | },
1665 | {
1666 | "type": "Apply",
1667 | "op": "div",
1668 | "args": [
1669 | {
1670 | "type": "Placeholder",
1671 | "name": "b"
1672 | },
1673 | {
1674 | "type": "Placeholder",
1675 | "name": "c"
1676 | }
1677 | ]
1678 | }
1679 | ]
1680 | }
1681 | `;
1682 |
1683 | exports[`Parser.parse placeholders #a #f(#x) 1`] = `
1684 | {
1685 | "type": "Apply",
1686 | "op": "mul",
1687 | "implicit": true,
1688 | "args": [
1689 | {
1690 | "type": "Placeholder",
1691 | "name": "a"
1692 | },
1693 | {
1694 | "type": "Apply",
1695 | "op": {
1696 | "type": "Placeholder",
1697 | "name": "f"
1698 | },
1699 | "args": [
1700 | {
1701 | "type": "Placeholder",
1702 | "name": "x"
1703 | }
1704 | ]
1705 | }
1706 | ]
1707 | }
1708 | `;
1709 |
1710 | exports[`Parser.parse placeholders #a 1`] = `
1711 | {
1712 | "type": "Placeholder",
1713 | "name": "a"
1714 | }
1715 | `;
1716 |
1717 | exports[`Parser.parse placeholders #eval(#a + #b) 1`] = `
1718 | {
1719 | "type": "Apply",
1720 | "op": {
1721 | "type": "Placeholder",
1722 | "name": "eval"
1723 | },
1724 | "args": [
1725 | {
1726 | "type": "Apply",
1727 | "op": "add",
1728 | "args": [
1729 | {
1730 | "type": "Placeholder",
1731 | "name": "a"
1732 | },
1733 | {
1734 | "type": "Placeholder",
1735 | "name": "b"
1736 | }
1737 | ]
1738 | }
1739 | ]
1740 | }
1741 | `;
1742 |
1743 | exports[`Parser.parse placeholders #f(#x) 1`] = `
1744 | {
1745 | "type": "Apply",
1746 | "op": {
1747 | "type": "Placeholder",
1748 | "name": "f"
1749 | },
1750 | "args": [
1751 | {
1752 | "type": "Placeholder",
1753 | "name": "x"
1754 | }
1755 | ]
1756 | }
1757 | `;
1758 |
1759 | exports[`Parser.parse powers (-2)^x 1`] = `
1760 | {
1761 | "type": "Apply",
1762 | "op": "pow",
1763 | "args": [
1764 | {
1765 | "type": "Apply",
1766 | "op": "neg",
1767 | "args": [
1768 | {
1769 | "value": "2",
1770 | "type": "Number"
1771 | }
1772 | ]
1773 | },
1774 | {
1775 | "type": "Identifier",
1776 | "name": "x"
1777 | }
1778 | ]
1779 | }
1780 | `;
1781 |
1782 | exports[`Parser.parse powers -1^-2 1`] = `
1783 | {
1784 | "type": "Apply",
1785 | "op": "neg",
1786 | "args": [
1787 | {
1788 | "type": "Apply",
1789 | "op": "pow",
1790 | "args": [
1791 | {
1792 | "value": "1",
1793 | "type": "Number"
1794 | },
1795 | {
1796 | "type": "Apply",
1797 | "op": "neg",
1798 | "args": [
1799 | {
1800 | "value": "2",
1801 | "type": "Number"
1802 | }
1803 | ]
1804 | }
1805 | ]
1806 | }
1807 | ]
1808 | }
1809 | `;
1810 |
1811 | exports[`Parser.parse powers -a^-b 1`] = `
1812 | {
1813 | "type": "Apply",
1814 | "op": "neg",
1815 | "args": [
1816 | {
1817 | "type": "Apply",
1818 | "op": "pow",
1819 | "args": [
1820 | {
1821 | "type": "Identifier",
1822 | "name": "a"
1823 | },
1824 | {
1825 | "type": "Apply",
1826 | "op": "neg",
1827 | "args": [
1828 | {
1829 | "type": "Identifier",
1830 | "name": "b"
1831 | }
1832 | ]
1833 | }
1834 | ]
1835 | }
1836 | ]
1837 | }
1838 | `;
1839 |
1840 | exports[`Parser.parse powers a^-1.23 1`] = `
1841 | {
1842 | "type": "Apply",
1843 | "op": "pow",
1844 | "args": [
1845 | {
1846 | "type": "Identifier",
1847 | "name": "a"
1848 | },
1849 | {
1850 | "type": "Apply",
1851 | "op": "neg",
1852 | "args": [
1853 | {
1854 | "value": "1.23",
1855 | "type": "Number"
1856 | }
1857 | ]
1858 | }
1859 | ]
1860 | }
1861 | `;
1862 |
1863 | exports[`Parser.parse powers a^b^c 1`] = `
1864 | {
1865 | "type": "Apply",
1866 | "op": "pow",
1867 | "args": [
1868 | {
1869 | "type": "Identifier",
1870 | "name": "a"
1871 | },
1872 | {
1873 | "type": "Apply",
1874 | "op": "pow",
1875 | "args": [
1876 | {
1877 | "type": "Identifier",
1878 | "name": "b"
1879 | },
1880 | {
1881 | "type": "Identifier",
1882 | "name": "c"
1883 | }
1884 | ]
1885 | }
1886 | ]
1887 | }
1888 | `;
1889 |
1890 | exports[`Parser.parse relations (binary) a != b 1`] = `
1891 | {
1892 | "type": "Apply",
1893 | "op": "ne",
1894 | "args": [
1895 | {
1896 | "type": "Identifier",
1897 | "name": "a"
1898 | },
1899 | {
1900 | "type": "Identifier",
1901 | "name": "b"
1902 | }
1903 | ]
1904 | }
1905 | `;
1906 |
1907 | exports[`Parser.parse relations (binary) a < b 1`] = `
1908 | {
1909 | "type": "Apply",
1910 | "op": "lt",
1911 | "args": [
1912 | {
1913 | "type": "Identifier",
1914 | "name": "a"
1915 | },
1916 | {
1917 | "type": "Identifier",
1918 | "name": "b"
1919 | }
1920 | ]
1921 | }
1922 | `;
1923 |
1924 | exports[`Parser.parse relations (binary) a <= b 1`] = `
1925 | {
1926 | "type": "Apply",
1927 | "op": "le",
1928 | "args": [
1929 | {
1930 | "type": "Identifier",
1931 | "name": "a"
1932 | },
1933 | {
1934 | "type": "Identifier",
1935 | "name": "b"
1936 | }
1937 | ]
1938 | }
1939 | `;
1940 |
1941 | exports[`Parser.parse relations (binary) a = b 1`] = `
1942 | {
1943 | "type": "Apply",
1944 | "op": "eq",
1945 | "args": [
1946 | {
1947 | "type": "Identifier",
1948 | "name": "a"
1949 | },
1950 | {
1951 | "type": "Identifier",
1952 | "name": "b"
1953 | }
1954 | ]
1955 | }
1956 | `;
1957 |
1958 | exports[`Parser.parse relations (binary) a > b 1`] = `
1959 | {
1960 | "type": "Apply",
1961 | "op": "gt",
1962 | "args": [
1963 | {
1964 | "type": "Identifier",
1965 | "name": "a"
1966 | },
1967 | {
1968 | "type": "Identifier",
1969 | "name": "b"
1970 | }
1971 | ]
1972 | }
1973 | `;
1974 |
1975 | exports[`Parser.parse relations (binary) a >= b 1`] = `
1976 | {
1977 | "type": "Apply",
1978 | "op": "ge",
1979 | "args": [
1980 | {
1981 | "type": "Identifier",
1982 | "name": "a"
1983 | },
1984 | {
1985 | "type": "Identifier",
1986 | "name": "b"
1987 | }
1988 | ]
1989 | }
1990 | `;
1991 |
1992 | exports[`Parser.parse relations (n-ary) a != b != c 1`] = `
1993 | {
1994 | "type": "Apply",
1995 | "op": "ne",
1996 | "args": [
1997 | {
1998 | "type": "Identifier",
1999 | "name": "a"
2000 | },
2001 | {
2002 | "type": "Identifier",
2003 | "name": "b"
2004 | },
2005 | {
2006 | "type": "Identifier",
2007 | "name": "c"
2008 | }
2009 | ]
2010 | }
2011 | `;
2012 |
2013 | exports[`Parser.parse relations (n-ary) a + b != c + d != e + f 1`] = `
2014 | {
2015 | "type": "Apply",
2016 | "op": "ne",
2017 | "args": [
2018 | {
2019 | "type": "Apply",
2020 | "op": "add",
2021 | "args": [
2022 | {
2023 | "type": "Identifier",
2024 | "name": "a"
2025 | },
2026 | {
2027 | "type": "Identifier",
2028 | "name": "b"
2029 | }
2030 | ]
2031 | },
2032 | {
2033 | "type": "Apply",
2034 | "op": "add",
2035 | "args": [
2036 | {
2037 | "type": "Identifier",
2038 | "name": "c"
2039 | },
2040 | {
2041 | "type": "Identifier",
2042 | "name": "d"
2043 | }
2044 | ]
2045 | },
2046 | {
2047 | "type": "Apply",
2048 | "op": "add",
2049 | "args": [
2050 | {
2051 | "type": "Identifier",
2052 | "name": "e"
2053 | },
2054 | {
2055 | "type": "Identifier",
2056 | "name": "f"
2057 | }
2058 | ]
2059 | }
2060 | ]
2061 | }
2062 | `;
2063 |
2064 | exports[`Parser.parse relations (n-ary) a + b = c + d = e + f 1`] = `
2065 | {
2066 | "type": "Apply",
2067 | "op": "eq",
2068 | "args": [
2069 | {
2070 | "type": "Apply",
2071 | "op": "add",
2072 | "args": [
2073 | {
2074 | "type": "Identifier",
2075 | "name": "a"
2076 | },
2077 | {
2078 | "type": "Identifier",
2079 | "name": "b"
2080 | }
2081 | ]
2082 | },
2083 | {
2084 | "type": "Apply",
2085 | "op": "add",
2086 | "args": [
2087 | {
2088 | "type": "Identifier",
2089 | "name": "c"
2090 | },
2091 | {
2092 | "type": "Identifier",
2093 | "name": "d"
2094 | }
2095 | ]
2096 | },
2097 | {
2098 | "type": "Apply",
2099 | "op": "add",
2100 | "args": [
2101 | {
2102 | "type": "Identifier",
2103 | "name": "e"
2104 | },
2105 | {
2106 | "type": "Identifier",
2107 | "name": "f"
2108 | }
2109 | ]
2110 | }
2111 | ]
2112 | }
2113 | `;
2114 |
2115 | exports[`Parser.parse relations (n-ary) a + b > c + d > e + f 1`] = `
2116 | {
2117 | "type": "Apply",
2118 | "op": "gt",
2119 | "args": [
2120 | {
2121 | "type": "Apply",
2122 | "op": "add",
2123 | "args": [
2124 | {
2125 | "type": "Identifier",
2126 | "name": "a"
2127 | },
2128 | {
2129 | "type": "Identifier",
2130 | "name": "b"
2131 | }
2132 | ]
2133 | },
2134 | {
2135 | "type": "Apply",
2136 | "op": "add",
2137 | "args": [
2138 | {
2139 | "type": "Identifier",
2140 | "name": "c"
2141 | },
2142 | {
2143 | "type": "Identifier",
2144 | "name": "d"
2145 | }
2146 | ]
2147 | },
2148 | {
2149 | "type": "Apply",
2150 | "op": "add",
2151 | "args": [
2152 | {
2153 | "type": "Identifier",
2154 | "name": "e"
2155 | },
2156 | {
2157 | "type": "Identifier",
2158 | "name": "f"
2159 | }
2160 | ]
2161 | }
2162 | ]
2163 | }
2164 | `;
2165 |
2166 | exports[`Parser.parse relations (n-ary) a = b = c >= d <= e <= f 1`] = `
2167 | {
2168 | "type": "Apply",
2169 | "op": "and",
2170 | "args": [
2171 | {
2172 | "type": "Apply",
2173 | "op": "eq",
2174 | "args": [
2175 | {
2176 | "type": "Identifier",
2177 | "name": "a"
2178 | },
2179 | {
2180 | "type": "Identifier",
2181 | "name": "b"
2182 | },
2183 | {
2184 | "type": "Identifier",
2185 | "name": "c"
2186 | }
2187 | ]
2188 | },
2189 | {
2190 | "type": "Apply",
2191 | "op": "ge",
2192 | "args": [
2193 | {
2194 | "type": "Identifier",
2195 | "name": "c"
2196 | },
2197 | {
2198 | "type": "Identifier",
2199 | "name": "d"
2200 | }
2201 | ]
2202 | },
2203 | {
2204 | "type": "Apply",
2205 | "op": "le",
2206 | "args": [
2207 | {
2208 | "type": "Identifier",
2209 | "name": "d"
2210 | },
2211 | {
2212 | "type": "Identifier",
2213 | "name": "e"
2214 | },
2215 | {
2216 | "type": "Identifier",
2217 | "name": "f"
2218 | }
2219 | ]
2220 | }
2221 | ]
2222 | }
2223 | `;
2224 |
2225 | exports[`Parser.parse relations (n-ary) a = b = c 1`] = `
2226 | {
2227 | "type": "Apply",
2228 | "op": "eq",
2229 | "args": [
2230 | {
2231 | "type": "Identifier",
2232 | "name": "a"
2233 | },
2234 | {
2235 | "type": "Identifier",
2236 | "name": "b"
2237 | },
2238 | {
2239 | "type": "Identifier",
2240 | "name": "c"
2241 | }
2242 | ]
2243 | }
2244 | `;
2245 |
2246 | exports[`Parser.parse relations (n-ary) a = b >= c != d < e - f 1`] = `
2247 | {
2248 | "type": "Apply",
2249 | "op": "and",
2250 | "args": [
2251 | {
2252 | "type": "Apply",
2253 | "op": "eq",
2254 | "args": [
2255 | {
2256 | "type": "Identifier",
2257 | "name": "a"
2258 | },
2259 | {
2260 | "type": "Identifier",
2261 | "name": "b"
2262 | }
2263 | ]
2264 | },
2265 | {
2266 | "type": "Apply",
2267 | "op": "ge",
2268 | "args": [
2269 | {
2270 | "type": "Identifier",
2271 | "name": "b"
2272 | },
2273 | {
2274 | "type": "Identifier",
2275 | "name": "c"
2276 | }
2277 | ]
2278 | },
2279 | {
2280 | "type": "Apply",
2281 | "op": "ne",
2282 | "args": [
2283 | {
2284 | "type": "Identifier",
2285 | "name": "c"
2286 | },
2287 | {
2288 | "type": "Identifier",
2289 | "name": "d"
2290 | }
2291 | ]
2292 | },
2293 | {
2294 | "type": "Apply",
2295 | "op": "lt",
2296 | "args": [
2297 | {
2298 | "type": "Identifier",
2299 | "name": "d"
2300 | },
2301 | {
2302 | "type": "Apply",
2303 | "op": "add",
2304 | "args": [
2305 | {
2306 | "type": "Identifier",
2307 | "name": "e"
2308 | },
2309 | {
2310 | "wasMinus": true,
2311 | "type": "Apply",
2312 | "op": "neg",
2313 | "args": [
2314 | {
2315 | "type": "Identifier",
2316 | "name": "f"
2317 | }
2318 | ]
2319 | }
2320 | ]
2321 | }
2322 | ]
2323 | }
2324 | ]
2325 | }
2326 | `;
2327 |
2328 | exports[`Parser.parse relations (n-ary) a > b > c 1`] = `
2329 | {
2330 | "type": "Apply",
2331 | "op": "gt",
2332 | "args": [
2333 | {
2334 | "type": "Identifier",
2335 | "name": "a"
2336 | },
2337 | {
2338 | "type": "Identifier",
2339 | "name": "b"
2340 | },
2341 | {
2342 | "type": "Identifier",
2343 | "name": "c"
2344 | }
2345 | ]
2346 | }
2347 | `;
2348 |
2349 | exports[`Parser.parse sequences 1, 1, 2, 3, 5 1`] = `
2350 | {
2351 | "type": "Sequence",
2352 | "items": [
2353 | {
2354 | "value": "1",
2355 | "type": "Number"
2356 | },
2357 | {
2358 | "value": "1",
2359 | "type": "Number"
2360 | },
2361 | {
2362 | "value": "2",
2363 | "type": "Number"
2364 | },
2365 | {
2366 | "value": "3",
2367 | "type": "Number"
2368 | },
2369 | {
2370 | "value": "5",
2371 | "type": "Number"
2372 | }
2373 | ]
2374 | }
2375 | `;
2376 |
2377 | exports[`Parser.parse sequences a, a^3, a^5 1`] = `
2378 | {
2379 | "type": "Sequence",
2380 | "items": [
2381 | {
2382 | "type": "Identifier",
2383 | "name": "a"
2384 | },
2385 | {
2386 | "type": "Apply",
2387 | "op": "pow",
2388 | "args": [
2389 | {
2390 | "type": "Identifier",
2391 | "name": "a"
2392 | },
2393 | {
2394 | "value": "3",
2395 | "type": "Number"
2396 | }
2397 | ]
2398 | },
2399 | {
2400 | "type": "Apply",
2401 | "op": "pow",
2402 | "args": [
2403 | {
2404 | "type": "Identifier",
2405 | "name": "a"
2406 | },
2407 | {
2408 | "value": "5",
2409 | "type": "Number"
2410 | }
2411 | ]
2412 | }
2413 | ]
2414 | }
2415 | `;
2416 |
2417 | exports[`Parser.parse sequences r_1, r_2, r_3 1`] = `
2418 | {
2419 | "type": "Sequence",
2420 | "items": [
2421 | {
2422 | "type": "Identifier",
2423 | "subscript": {
2424 | "value": "1",
2425 | "type": "Number"
2426 | },
2427 | "name": "r"
2428 | },
2429 | {
2430 | "type": "Identifier",
2431 | "subscript": {
2432 | "value": "2",
2433 | "type": "Number"
2434 | },
2435 | "name": "r"
2436 | },
2437 | {
2438 | "type": "Identifier",
2439 | "subscript": {
2440 | "value": "3",
2441 | "type": "Number"
2442 | },
2443 | "name": "r"
2444 | }
2445 | ]
2446 | }
2447 | `;
2448 |
2449 | exports[`Parser.parse sequences x, x + 1, x + 3 1`] = `
2450 | {
2451 | "type": "Sequence",
2452 | "items": [
2453 | {
2454 | "type": "Identifier",
2455 | "name": "x"
2456 | },
2457 | {
2458 | "type": "Apply",
2459 | "op": "add",
2460 | "args": [
2461 | {
2462 | "type": "Identifier",
2463 | "name": "x"
2464 | },
2465 | {
2466 | "value": "1",
2467 | "type": "Number"
2468 | }
2469 | ]
2470 | },
2471 | {
2472 | "type": "Apply",
2473 | "op": "add",
2474 | "args": [
2475 | {
2476 | "type": "Identifier",
2477 | "name": "x"
2478 | },
2479 | {
2480 | "value": "3",
2481 | "type": "Number"
2482 | }
2483 | ]
2484 | }
2485 | ]
2486 | }
2487 | `;
2488 |
2489 | exports[`Parser.parse subscripts #a_0 1`] = `
2490 | {
2491 | "type": "Placeholder",
2492 | "subscript": {
2493 | "value": "0",
2494 | "type": "Number"
2495 | },
2496 | "name": "a"
2497 | }
2498 | `;
2499 |
2500 | exports[`Parser.parse subscripts #a_a3 1`] = `
2501 | {
2502 | "type": "Placeholder",
2503 | "subscript": {
2504 | "type": "Identifier",
2505 | "name": "a3"
2506 | },
2507 | "name": "a"
2508 | }
2509 | `;
2510 |
2511 | exports[`Parser.parse subscripts a_0 1`] = `
2512 | {
2513 | "type": "Identifier",
2514 | "subscript": {
2515 | "value": "0",
2516 | "type": "Number"
2517 | },
2518 | "name": "a"
2519 | }
2520 | `;
2521 |
2522 | exports[`Parser.parse subscripts a_0^2 1`] = `
2523 | {
2524 | "type": "Apply",
2525 | "op": "pow",
2526 | "args": [
2527 | {
2528 | "type": "Identifier",
2529 | "subscript": {
2530 | "value": "0",
2531 | "type": "Number"
2532 | },
2533 | "name": "a"
2534 | },
2535 | {
2536 | "value": "2",
2537 | "type": "Number"
2538 | }
2539 | ]
2540 | }
2541 | `;
2542 |
2543 | exports[`Parser.parse subscripts a_123 1`] = `
2544 | {
2545 | "type": "Identifier",
2546 | "subscript": {
2547 | "value": "123",
2548 | "type": "Number"
2549 | },
2550 | "name": "a"
2551 | }
2552 | `;
2553 |
2554 | exports[`Parser.parse subscripts a_n 1`] = `
2555 | {
2556 | "type": "Identifier",
2557 | "subscript": {
2558 | "type": "Identifier",
2559 | "name": "n"
2560 | },
2561 | "name": "a"
2562 | }
2563 | `;
2564 |
2565 | exports[`Parser.parse subscripts a_xyz 1`] = `
2566 | {
2567 | "type": "Identifier",
2568 | "subscript": {
2569 | "type": "Identifier",
2570 | "name": "xyz"
2571 | },
2572 | "name": "a"
2573 | }
2574 | `;
2575 |
2576 | exports[`Parser.parse systems of equations a = b, b = c, c = d 1`] = `
2577 | {
2578 | "type": "System",
2579 | "relations": [
2580 | {
2581 | "type": "Apply",
2582 | "op": "eq",
2583 | "args": [
2584 | {
2585 | "type": "Identifier",
2586 | "name": "a"
2587 | },
2588 | {
2589 | "type": "Identifier",
2590 | "name": "b"
2591 | }
2592 | ]
2593 | },
2594 | {
2595 | "type": "Apply",
2596 | "op": "eq",
2597 | "args": [
2598 | {
2599 | "type": "Identifier",
2600 | "name": "b"
2601 | },
2602 | {
2603 | "type": "Identifier",
2604 | "name": "c"
2605 | }
2606 | ]
2607 | },
2608 | {
2609 | "type": "Apply",
2610 | "op": "eq",
2611 | "args": [
2612 | {
2613 | "type": "Identifier",
2614 | "name": "c"
2615 | },
2616 | {
2617 | "type": "Identifier",
2618 | "name": "d"
2619 | }
2620 | ]
2621 | }
2622 | ]
2623 | }
2624 | `;
2625 |
2626 | exports[`Parser.parse systems of equations x + 1 = 2, x^2 + 2x + 1 = 0 1`] = `
2627 | {
2628 | "type": "System",
2629 | "relations": [
2630 | {
2631 | "type": "Apply",
2632 | "op": "eq",
2633 | "args": [
2634 | {
2635 | "type": "Apply",
2636 | "op": "add",
2637 | "args": [
2638 | {
2639 | "type": "Identifier",
2640 | "name": "x"
2641 | },
2642 | {
2643 | "value": "1",
2644 | "type": "Number"
2645 | }
2646 | ]
2647 | },
2648 | {
2649 | "value": "2",
2650 | "type": "Number"
2651 | }
2652 | ]
2653 | },
2654 | {
2655 | "type": "Apply",
2656 | "op": "eq",
2657 | "args": [
2658 | {
2659 | "type": "Apply",
2660 | "op": "add",
2661 | "args": [
2662 | {
2663 | "type": "Apply",
2664 | "op": "pow",
2665 | "args": [
2666 | {
2667 | "type": "Identifier",
2668 | "name": "x"
2669 | },
2670 | {
2671 | "value": "2",
2672 | "type": "Number"
2673 | }
2674 | ]
2675 | },
2676 | {
2677 | "type": "Apply",
2678 | "op": "mul",
2679 | "implicit": true,
2680 | "args": [
2681 | {
2682 | "value": "2",
2683 | "type": "Number"
2684 | },
2685 | {
2686 | "type": "Identifier",
2687 | "name": "x"
2688 | }
2689 | ]
2690 | },
2691 | {
2692 | "value": "1",
2693 | "type": "Number"
2694 | }
2695 | ]
2696 | },
2697 | {
2698 | "value": "0",
2699 | "type": "Number"
2700 | }
2701 | ]
2702 | }
2703 | ]
2704 | }
2705 | `;
2706 |
2707 | exports[`Parser.parse systems of equations x + 2 = y, 3x - 5 = 2y 1`] = `
2708 | {
2709 | "type": "System",
2710 | "relations": [
2711 | {
2712 | "type": "Apply",
2713 | "op": "eq",
2714 | "args": [
2715 | {
2716 | "type": "Apply",
2717 | "op": "add",
2718 | "args": [
2719 | {
2720 | "type": "Identifier",
2721 | "name": "x"
2722 | },
2723 | {
2724 | "value": "2",
2725 | "type": "Number"
2726 | }
2727 | ]
2728 | },
2729 | {
2730 | "type": "Identifier",
2731 | "name": "y"
2732 | }
2733 | ]
2734 | },
2735 | {
2736 | "type": "Apply",
2737 | "op": "eq",
2738 | "args": [
2739 | {
2740 | "type": "Apply",
2741 | "op": "add",
2742 | "args": [
2743 | {
2744 | "type": "Apply",
2745 | "op": "mul",
2746 | "implicit": true,
2747 | "args": [
2748 | {
2749 | "value": "3",
2750 | "type": "Number"
2751 | },
2752 | {
2753 | "type": "Identifier",
2754 | "name": "x"
2755 | }
2756 | ]
2757 | },
2758 | {
2759 | "wasMinus": true,
2760 | "type": "Apply",
2761 | "op": "neg",
2762 | "args": [
2763 | {
2764 | "value": "5",
2765 | "type": "Number"
2766 | }
2767 | ]
2768 | }
2769 | ]
2770 | },
2771 | {
2772 | "type": "Apply",
2773 | "op": "mul",
2774 | "implicit": true,
2775 | "args": [
2776 | {
2777 | "value": "2",
2778 | "type": "Number"
2779 | },
2780 | {
2781 | "type": "Identifier",
2782 | "name": "y"
2783 | }
2784 | ]
2785 | }
2786 | ]
2787 | }
2788 | ]
2789 | }
2790 | `;
2791 |
2792 | exports[`Parser.parse unary operators +2 1`] = `
2793 | {
2794 | "type": "Apply",
2795 | "op": "pos",
2796 | "args": [
2797 | {
2798 | "value": "2",
2799 | "type": "Number"
2800 | }
2801 | ]
2802 | }
2803 | `;
2804 |
2805 | exports[`Parser.parse unary operators +a 1`] = `
2806 | {
2807 | "type": "Apply",
2808 | "op": "pos",
2809 | "args": [
2810 | {
2811 | "type": "Identifier",
2812 | "name": "a"
2813 | }
2814 | ]
2815 | }
2816 | `;
2817 |
2818 | exports[`Parser.parse unary operators --2 1`] = `
2819 | {
2820 | "type": "Apply",
2821 | "op": "neg",
2822 | "args": [
2823 | {
2824 | "type": "Apply",
2825 | "op": "neg",
2826 | "args": [
2827 | {
2828 | "value": "2",
2829 | "type": "Number"
2830 | }
2831 | ]
2832 | }
2833 | ]
2834 | }
2835 | `;
2836 |
2837 | exports[`Parser.parse unary operators --a 1`] = `
2838 | {
2839 | "type": "Apply",
2840 | "op": "neg",
2841 | "args": [
2842 | {
2843 | "type": "Apply",
2844 | "op": "neg",
2845 | "args": [
2846 | {
2847 | "type": "Identifier",
2848 | "name": "a"
2849 | }
2850 | ]
2851 | }
2852 | ]
2853 | }
2854 | `;
2855 |
2856 | exports[`Parser.parse unary operators -2 1`] = `
2857 | {
2858 | "type": "Apply",
2859 | "op": "neg",
2860 | "args": [
2861 | {
2862 | "value": "2",
2863 | "type": "Number"
2864 | }
2865 | ]
2866 | }
2867 | `;
2868 |
2869 | exports[`Parser.parse unary operators -a 1`] = `
2870 | {
2871 | "type": "Apply",
2872 | "op": "neg",
2873 | "args": [
2874 | {
2875 | "type": "Identifier",
2876 | "name": "a"
2877 | }
2878 | ]
2879 | }
2880 | `;
2881 |
--------------------------------------------------------------------------------
/lib/__test__/parser.test.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert'
2 | import stringify from 'json-stable-stringify'
3 |
4 | import parse from '../parse'
5 |
6 | const reverseAlphabetical = (a, b) => a.key < b.key ? 1 : -1
7 |
8 | const serializer = {
9 | print(val) {
10 | return stringify(val, {cmp: reverseAlphabetical, space: ' '})
11 | },
12 | test() {
13 | return true
14 | },
15 | }
16 |
17 | expect.addSnapshotSerializer(serializer) // eslint-disable-line
18 |
19 | const suite = (name, cases) => {
20 | describe(name, () => {
21 | cases.forEach((c) => {
22 | it(c, () => {
23 | const ast = parse(c)
24 | expect(ast).toMatchSnapshot() // eslint-disable-line
25 | })
26 | })
27 | })
28 | }
29 |
30 | suite.only = (name, cases) => {
31 | describe.only(name, () => {
32 | cases.forEach((c) => {
33 | it(c, () => {
34 | const ast = parse(c)
35 | expect(ast).toMatchSnapshot() // eslint-disable-line
36 | })
37 | })
38 | })
39 | }
40 |
41 | describe('Parser.parse', () => {
42 | it('should fail with invalid tokens', () => {
43 | assert.throws(() => parse('a ; b'))
44 | assert.throws(() => parse('; a b'))
45 | assert.throws(() => parse('a b ;'))
46 | })
47 |
48 | suite('addition/subtraction', [
49 | 'a + b + c',
50 | 'a - b - c',
51 | 'a + -b - c',
52 | 'a - b - -c',
53 | '1 - 2',
54 | '1 - -2',
55 | ])
56 |
57 | suite('multiplication', [
58 | 'a b c',
59 | 'a*b c', // mix of explicit and implicit multiplication
60 | 'a b * b * b c',
61 | 'a*b*c',
62 | '(a)(b)(c)',
63 | '(a)b(c)', // a times function b evaluated at c
64 | ])
65 |
66 | suite('division', [
67 | 'a/b/c', // (a/b) / c
68 | 'a*b*c/d', // a * b * (c/d)
69 | 'a b c/d', // a * b * (c/d)
70 | 'a/b*c/d', // (a/b) * (c/d)
71 | '(a*b)/(c*d)', // (a*b) / (c*d)
72 | 'a^2/b^2',
73 | '2x/4',
74 | '2(x+1)/4',
75 | ])
76 |
77 | suite('powers', [
78 | 'a^b^c',
79 | '-a^-b',
80 | '-1^-2',
81 | 'a^-1.23',
82 | '(-2)^x',
83 | ])
84 |
85 | suite('functions', [
86 | // 'f()',
87 | 'f(a,b)',
88 | 'f(a+b)',
89 | 'f(f(a))',
90 | // 'cos^2(x)',
91 | ])
92 |
93 | suite('nthRoot', [
94 | 'nthRoot(x)',
95 | 'nthRoot(x, 2)',
96 | 'nthRoot(-27, 3)',
97 | ])
98 | // TODO: add failing tests for:
99 | // - nthRoot()
100 | // - nthRoot(1, 2, 3)
101 |
102 | suite('abs', [
103 | '|a - b|',
104 | '||a - b| - |b - c||',
105 | 'abs(-1)',
106 | ])
107 |
108 | suite('parentheses', [
109 | 'a * (b + c)',
110 | '(x + y) - (a + b)',
111 | '(a)(b)',
112 | '5 + (3 * 6)',
113 | '5 + ((3 * 6))',
114 | '(1 + 2)',
115 | '((1 + 2))',
116 | '(a * b)',
117 | '((a * b))',
118 | ])
119 |
120 | suite('unary operators', [
121 | '-a',
122 | '-2',
123 | '--a',
124 | '--2',
125 | '+a',
126 | '+2',
127 | ])
128 |
129 | suite('relations (binary)', [
130 | 'a = b',
131 | 'a > b',
132 | 'a >= b',
133 | 'a < b',
134 | 'a <= b',
135 | 'a != b',
136 | ])
137 |
138 | suite('relations (n-ary)', [
139 | 'a = b = c',
140 | 'a + b = c + d = e + f',
141 | 'a > b > c',
142 | 'a + b > c + d > e + f',
143 | 'a != b != c',
144 | 'a + b != c + d != e + f',
145 | 'a = b >= c != d < e - f',
146 | 'a = b = c >= d <= e <= f',
147 | ])
148 |
149 | suite('systems of equations', [
150 | 'x + 2 = y, 3x - 5 = 2y',
151 | 'x + 1 = 2, x^2 + 2x + 1 = 0',
152 | 'a = b, b = c, c = d'
153 | ])
154 |
155 | suite('sequences', [
156 | '1, 1, 2, 3, 5',
157 | 'x, x + 1, x + 3',
158 | 'a, a^3, a^5',
159 | 'r_1, r_2, r_3',
160 | ])
161 |
162 | suite('placeholders', [
163 | '#a',
164 | '#a #b / #c',
165 | '#f(#x)',
166 | '#a #f(#x)',
167 | '#eval(#a + #b)',
168 | ])
169 |
170 | suite('subscripts', [
171 | 'a_0',
172 | 'a_123',
173 | 'a_n',
174 | 'a_xyz',
175 | 'a_0^2',
176 | '#a_0',
177 | '#a_a3',
178 | // 'a_-1',
179 | // 'a_(m+n),
180 | // 'a_b_c',
181 | // 'f_n(x)',
182 | ])
183 |
184 | suite('ellipsis', [
185 | 'a + ... + z',
186 | '1 * ... * n',
187 | // 'a ... z', // implicit multiplication
188 | '1, 2, ..., n',
189 | '#a_0#x + ... + #a_n#x',
190 | ])
191 |
192 | suite('factorial', [
193 | '5!',
194 | 'x!',
195 | '5!^2',
196 | '(5^2)!',
197 | 'x^2!',
198 | '2 * n!',
199 | '(2 * n)!',
200 | ])
201 |
202 | suite('integrals', [
203 | 'int x^2 dx',
204 | 'int_0^1 x^2 dx',
205 | 'int_0^1 x^2 2x dx',
206 | 'int_0^1 int_0^1 x^2 y^2 dx dy',
207 | 'int_0^1 int_0^1 x^2 + y^2 dx dy',
208 | ])
209 | })
210 |
211 | // TODO: add tests verify different ways of writing the same thing, e.g.
212 | // a*b/c/d/ === a*b/(c*d)
213 |
--------------------------------------------------------------------------------
/lib/__test__/print.test.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert'
2 |
3 | import parse from '../parse'
4 | import print from '../print'
5 |
6 | describe('print', () => {
7 | describe('wasMinus', () => {
8 | const tests = [
9 | '1 + -2',
10 | '1 - 2',
11 | '1 - -2',
12 | 'a - b',
13 | 'a + -b',
14 | ]
15 |
16 | tests.forEach(t =>
17 | it(t, () => assert.equal(print(parse(t)),t))
18 | )
19 | })
20 |
21 | describe('relations', () => {
22 | const tests = [
23 | 'a + b = c - d',
24 | 'a + b > c - d',
25 | 'a + b >= c - d',
26 | 'a + b < c - d',
27 | 'a + b <= c - d',
28 | 'a + b != c - d',
29 | ]
30 |
31 | tests.forEach(t =>
32 | it(t, () => assert.equal(print(parse(t)),t))
33 | )
34 | })
35 |
36 | describe('fractions', () => {
37 | const tests = [
38 | ['(x + 1) / 1', '(x + 1) / 1'],
39 | ['2/3 + 5', '2/3 + 5'],
40 | ['2/x + 5', '2/x + 5'],
41 | ['y/x + 5', 'y/x + 5'],
42 | ['y/5 + 5', 'y/5 + 5'],
43 | ['1/2/3', '1/2 / 3'], // (1/2) / 3
44 | ['1*2/3', '1 * 2/3'], // 1 * (2/3)
45 | ['(1*2)/3', '(1 * 2) / 3'],
46 | ['a/(b/c)', 'a / (b/c)'],
47 | ]
48 |
49 | tests.forEach(t =>
50 | it(`${t[0]} => ${t[1]}`, () =>
51 | assert.equal(print(parse(t[0])),t[1])
52 | )
53 | )
54 | })
55 |
56 | describe('exponents', () => {
57 | const tests = [
58 | ['x^2', 'x^2'],
59 | ['x^(x / 2)','x^(x/2)'],
60 | ['x^(y + 1)', 'x^(y + 1)'],
61 | ['x^(x / (x + 2))', 'x^(x / (x + 2))'],
62 | ['x^((x + 1)/(2 * 2))', 'x^((x + 1) / (2 * 2))'],
63 | ['x^(x + x + (x + y))', 'x^(x + x + (x + y))'],
64 | ['(y+1)^((x + 1) + 2)', '(y + 1)^((x + 1) + 2)'],
65 | ['-2^x', '-2^x'],
66 | ['(-2)^x', '(-2)^x'],
67 | ['(-2)^-1', '(-2)^-1'],
68 | ]
69 |
70 | tests.forEach(t =>
71 | it(`${t[0]} => ${t[1]}`, () =>
72 | assert.equal(print(parse(t[0])),t[1])
73 | )
74 | )
75 | })
76 |
77 | describe('order of operations', () => {
78 | const tests = [
79 | '1 * (2 * (3 * 4))',
80 | '((1 * 2) * 3) * 4',
81 | '1 + (2 + (3 + 4))',
82 | '((1 + 2) + 3) + 4',
83 | '2 x y + 3 x y',
84 | '(2 x y) / (3 x y)',
85 | '(x y)^(2 * 3)',
86 | '(x/y)^(2/3)',
87 | '2x * 3 x y',
88 | '(2x) (3 x y)',
89 | ]
90 |
91 | tests.forEach(t =>
92 | it(t, () => assert.equal(print(parse(t)),t))
93 | )
94 | })
95 |
96 | describe('subscripts', () => {
97 | const tests = [
98 | 'a_0',
99 | 'a_123',
100 | 'a_n',
101 | 'a_xyz',
102 | 'a_0^2',
103 | '#a_0',
104 | // 'a_-1',
105 | // 'a_(m+n),
106 | // 'a_b_c',
107 | // 'f_n(x)',
108 | ]
109 |
110 | tests.forEach(t =>
111 | it(t, () => assert.equal(print(parse(t)), t))
112 | )
113 | })
114 |
115 | describe('ellipsis', () => {
116 | const tests = [
117 | 'a + ... + z',
118 | '1 * ... * n',
119 | // 'a ... z', // implicit multiplication
120 | '1, 2, ..., n',
121 | '#a_0 #x + ... + #a_n #x',
122 | ]
123 |
124 | tests.forEach(t =>
125 | it(t, () => assert.equal(print(parse(t)), t))
126 | )
127 | })
128 |
129 | describe('functions', () => {
130 | const tests = [
131 | 'f(x)',
132 | 'gcd(1, 2, 3)',
133 | '#eval(#a_0, ...)',
134 | '#eval(lcm(#a_0, ...))',
135 | ]
136 |
137 | tests.forEach(t =>
138 | it(t, () => assert.equal(print(parse(t)), t))
139 | )
140 | })
141 |
142 | describe('parentheses', () => {
143 | const tests = [
144 | '5 + (3 * 6)',
145 | '5 + ((3 * 6))',
146 | '5 + (((3 * 6)))',
147 | '(1 + 2)',
148 | '((1 + 2))',
149 | '(((1 + 2)))',
150 | '(a * b)',
151 | '((a * b))',
152 | '(((a * b)))',
153 | ]
154 |
155 | tests.forEach(t =>
156 | it(t, () => assert.equal(print(parse(t)), t))
157 | )
158 | })
159 |
160 | describe('nthRoot', () => {
161 | const tests = [
162 | ['nthRoot(x)', 'nthRoot(x, 2)'],
163 | ['nthRoot(x, 3)', 'nthRoot(x, 3)'],
164 | ['nthRoot(8, 3)', 'nthRoot(8, 3)'],
165 | ['nthRoot(x^2, 4)', 'nthRoot(x^2, 4)'],
166 | ]
167 |
168 | tests.forEach(t =>
169 | it(t[0], () => assert.equal(print(parse(t[0])), t[1]))
170 | )
171 | })
172 |
173 | describe('factorial', () => {
174 | const tests = [
175 | '5!',
176 | 'n!',
177 | 'x^2!',
178 | '(x + 1)!',
179 | '(5 * 2)!',
180 | '(8/2)!',
181 | '(x^2)!',
182 | ]
183 |
184 | tests.forEach(t =>
185 | it(t, () => assert.equal(print(parse(t)), t))
186 | )
187 | })
188 |
189 | describe('abs', () => {
190 | const tests = [
191 | '|-1|',
192 | '|x|',
193 | '|x + 1|',
194 | '|x| - |y|',
195 | '||x| - |y||',
196 | ]
197 |
198 | tests.forEach(t =>
199 | it(t, () => assert.equal(print(parse(t)), t))
200 | )
201 | })
202 |
203 | describe('integral', () => {
204 | const tests = [
205 | ['int(x^2, x)', 'int(x^2, x)'],
206 | ['int(x^2, x, D)', 'int(x^2, x, D)'],
207 | ['int(x^2, x, a, b)', 'int(x^2, x, a, b)'],
208 | ['int(int(x^2, x, a, b), y, c, d)', 'int(int(x^2, x, a, b), y, c, d)'],
209 | ]
210 |
211 | tests.forEach(t =>
212 | it(t[0], () => assert.equal(print(parse(t[0])), t[1]))
213 | )
214 | })
215 |
216 | })
217 |
218 |
--------------------------------------------------------------------------------
/lib/__test__/toTex.test.js:
--------------------------------------------------------------------------------
1 | import assert from 'assert'
2 |
3 | import parse from '../parse'
4 | import toTex from '../toTex.js'
5 |
6 | describe('toTex', () => {
7 | it('handles wasMinus correctly', () => {
8 | const tests = [
9 | // arithmetic
10 | ['1 + -2', '1 + -2'],
11 | ['1 - -2', '1 - -2'],
12 | ['a - b', 'a - b'],
13 | ['a + -b', 'a + -b'],
14 | ['1 * 2', '1 \\times 2'],
15 | // implicit multiplication
16 | ['x * 2x', 'x \\times 2 x'],
17 | ['-3', '-3'],
18 | // brackets
19 | //['[2 , 3]', '']
20 | ]
21 |
22 | tests.forEach(t => assert.equal(toTex(parse(t[0])), t[1]))
23 | })
24 |
25 | it('handles fractions correctly', () => {
26 | const tests = [
27 | ['1/2', '\\frac{1}{2}'],
28 | ['1/2/3', '\\frac{\\frac{1}{2}}{3}'],
29 | ['x/2', '\\frac{x}{2}'],
30 | ['(x+2)/(x+3)', '\\frac{\\left(x + 2\\right)}{\\left(x + 3\\right)}'],
31 | ]
32 |
33 | tests.forEach(t => assert.equal(toTex(parse(t[0])), t[1]))
34 | })
35 |
36 | it('handles equations correctly', () => {
37 | const tests = [
38 | ['x = 5/2', 'x = \\frac{5}{2}'],
39 | ['x = 3 * (2/x)', 'x = 3 \\times \\frac{2}{x}'],
40 | ['3 + x = 3/x', '\\left(3 + x\\right) = \\frac{3}{x}'],
41 | ]
42 |
43 | tests.forEach(t => assert.equal(toTex(parse(t[0])), t[1]))
44 | })
45 |
46 | it('relations', () => {
47 | const tests = [
48 | 'a = b',
49 | 'a > b',
50 | 'a >= b',
51 | 'a < b',
52 | 'a <= b',
53 | 'a != b',
54 | ]
55 |
56 | tests.forEach(test => assert.equal(toTex(parse(test)), test))
57 | })
58 |
59 | it('functions', () => {
60 | const tests = [
61 | // nthRoot
62 | ['nthRoot(x^1, 2)', '\\sqrt{x^1}'],
63 | ['nthRoot(y^-6, -3)', '\\sqrt[-3]{y^-6}'],
64 | ['nthRoot(2, 2)', '\\sqrt{2}'],
65 | ['nthRoot(x^2)', '\\sqrt{x^2}'],
66 | ['nthRoot(x^2 y^2, -2)', '\\sqrt[-2]{x^{2} y^{2}}'],
67 |
68 | // integrals
69 | ['int(x^2, x)', '\\int x^{2} dx'],
70 | ['int(x^2, x, D)', '\\int_D x^{2} dx'],
71 | ['int(x^2, x, a, b)', '\\int_{a}^{b} x^{2} dx'],
72 | ['int(int(x^2 + y^2, x, a, b), y, c, d)', '\\int_{c}^{d} \\int_{a}^{b} \\left(x^{2} + y^{2}\\right) dx dy']
73 |
74 | ]
75 |
76 | tests.forEach(t => assert.equal(toTex(parse(t[0])), t[1]))
77 | })
78 | })
79 |
--------------------------------------------------------------------------------
/lib/parse.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Parses a math string to an AST.
3 | *
4 | * Notes:
5 | * - The output AST tries to conform to the math-ast spec, but some aspects may
6 | * be a little off. This will be fixed in future versions.
7 | * - The input syntax covers the parts of the mathjs syntax being used by
8 | * mathsteps
9 | *
10 | * TODO:
11 | * - Better adherence to and more comprehensive coverage of the math-ast spec.
12 | * - Specify what the syntax is, e.g. operator precedence, implicit multiplication,
13 | * etc.
14 | */
15 | import {build, query} from 'math-nodes'
16 | import {traverse} from 'math-traverse'
17 |
18 | function isIdentifierToken(token) {
19 | return token && /^[a-zA-Z][a-zA-Z0-9]*$/.test(token.value)
20 | }
21 |
22 | function isNumberToken(token) {
23 | return token && /^(\d*\.\d+|\d+\.\d*|\d+)$/.test(token.value)
24 | }
25 |
26 | const tokenPattern =
27 | /\.\.\.|[a-zA-Z][a-zA-Z0-9]*|<=|>=|!=|[\<\>\!\=\(\)\+\-\/\*\^\<\>|\,\#\_]|\d*\.\d+|\d+\.\d*|\d+/
28 |
29 | const relationTokenMap = {
30 | '=': 'eq',
31 | '<': 'lt',
32 | '<=': 'le',
33 | '>': 'gt',
34 | '>=': 'ge',
35 | '!=': 'ne',
36 | }
37 |
38 | function isIdentifier(node) {
39 | return node.type === 'Identifier'
40 | }
41 |
42 | function isRelation(node) {
43 | return node.type === 'Apply' && Object.values(relationTokenMap).includes(node.op)
44 | }
45 |
46 | function isExpression(node) {
47 | return ['System', 'List', 'Sequence'].indexOf(node.type) === -1 ||
48 | isRelation(node)
49 | }
50 |
51 | function matches(token, value) {
52 | return token && token.value === value
53 | }
54 |
55 | class Parser {
56 | consume(expectedValue) {
57 | const token = this.currentToken()
58 | if (expectedValue !== undefined) {
59 | if (!matches(token, expectedValue)) {
60 | throw new Error(
61 | `expected '${expectedValue}' received '${token.value}'`)
62 | }
63 | }
64 | this.i++
65 | return token
66 | }
67 |
68 | currentToken() {
69 | return this.tokens[this.i]
70 | }
71 |
72 | parse(input) {
73 | this.i = 0
74 | this.tokens = []
75 | this.integrals = 0
76 |
77 | const regex = new RegExp(tokenPattern, 'g')
78 |
79 | let index = 0
80 | let match
81 |
82 | while ((match = regex.exec(input)) != null) {
83 | const start = match.index
84 | const end = regex.lastIndex
85 |
86 | this.tokens.push({
87 | value: match[0],
88 | start: start,
89 | end: end,
90 | })
91 |
92 | if (index !== start) {
93 | const skipped = input.slice(index, start).trim()
94 | if (skipped !== '') {
95 | throw new Error(`'${skipped}' not recognized`)
96 | }
97 | }
98 |
99 | index = end
100 | }
101 |
102 | if (index !== input.length) {
103 | const skipped = input.slice(index, input.length).trim()
104 | if (skipped !== '') {
105 | throw new Error(`'${skipped}' not recognized`)
106 | }
107 | }
108 |
109 | const result = this.list()
110 |
111 | if (this.i < this.tokens.length) {
112 | throw new Error('extra input not recognized')
113 | }
114 |
115 | return result
116 | }
117 |
118 | list() {
119 | const items = [this.relationsOrRelationOrExpression()]
120 |
121 | while (true) {
122 | const token = this.currentToken()
123 |
124 | if (matches(token, ',')) {
125 | this.consume(',')
126 | items.push(this.relationsOrRelationOrExpression())
127 | } else {
128 | break
129 | }
130 | }
131 |
132 | if (items.length > 1) {
133 | if (items.every((item) => isRelation(item))) {
134 | return {
135 | type: 'System', // of equations
136 | relations: items,
137 | }
138 | } else if (items.every(isExpression)) {
139 | return {
140 | type: 'Sequence',
141 | items,
142 | }
143 | } else {
144 | return {
145 | type: 'List',
146 | items,
147 | }
148 | }
149 | } else {
150 | return items[0]
151 | }
152 | }
153 |
154 | relationsOrRelationOrExpression() {
155 | const relations = []
156 | const args = []
157 |
158 | let left
159 | let right
160 |
161 | left = this.expression()
162 |
163 | while (true) {
164 | const token = this.currentToken()
165 |
166 | if (token && token.value in relationTokenMap) {
167 | this.consume()
168 | right = this.expression()
169 | const rel = relationTokenMap[token.value]
170 | relations.push(build.applyNode(rel, [left, right]))
171 | args.push(left)
172 | left = right
173 | } else {
174 | break
175 | }
176 | }
177 | args.push(right)
178 |
179 | if (relations.length > 1) {
180 | relations.reverse()
181 |
182 | let output = {
183 | type: 'Apply',
184 | op: 'and',
185 | args: []
186 | }
187 |
188 | let current = {
189 | type: 'Apply',
190 | op: relations[0].op,
191 | args: []
192 | }
193 |
194 | relations.forEach(function (item) {
195 | if (item.op !== current.op) {
196 | current.args.unshift(item.args[1])
197 |
198 | output.args.unshift(current)
199 |
200 | current = {
201 | type: 'Apply',
202 | op: item.op,
203 | args: [item.args[1]]
204 | }
205 | }
206 | else {
207 | current.args.unshift(item.args[1])
208 | }
209 | })
210 |
211 | current.args.unshift(relations[relations.length - 1].args[0])
212 | output.args.unshift(current)
213 |
214 | if (output.args.length === 1) {
215 | return output.args[0]
216 | }
217 |
218 | return output
219 | } else if (relations.length > 0) {
220 | return relations[0]
221 | } else {
222 | return left
223 | }
224 | }
225 |
226 | expression() {
227 | const args = []
228 |
229 | args.push(this.explicitMul())
230 |
231 | while (true) {
232 | const token = this.currentToken()
233 |
234 | if (matches(token, '-')) {
235 | this.consume('-')
236 | args.push(
237 | build.applyNode('neg', [this.explicitMul()], {wasMinus: true})
238 | )
239 | } else if (matches(token, '+')) {
240 | this.consume('+')
241 | args.push(this.explicitMul())
242 | } else {
243 | break
244 | }
245 | }
246 |
247 | if (args.length > 1) {
248 | return build.applyNode('add', args.map(term =>
249 | term.addParens ? build.parensNode(term) : term))
250 | } else {
251 | if (args[0].addParens) {
252 | return build.parensNode(args[0])
253 | } else {
254 | return args[0]
255 | }
256 | }
257 | }
258 |
259 | explicitMul() {
260 | const factors = []
261 |
262 | factors.push(this.implicitMul())
263 |
264 | while (true) {
265 | if (matches(this.currentToken(), '*')) {
266 | this.consume('*')
267 | factors.push(this.implicitMul())
268 | } else {
269 | break
270 | }
271 | }
272 |
273 | return factors.length > 1
274 | ? build.applyNode('mul', factors)
275 | : factors[0]
276 | }
277 |
278 | /**
279 | * Parse the following forms of implicit multiplication:
280 | * - a b c
281 | * - (a)(b)(c)
282 | *
283 | * Note: (a)b(c) is actually: 'a' times function 'b' evaluated at 'c'
284 | *
285 | * If the multiplication was detected, a single parsed factor is returned.
286 | */
287 | implicitMul() {
288 | let factors = []
289 |
290 | factors.push(this.division())
291 |
292 | while (true) {
293 | let token = this.currentToken()
294 |
295 | if (matches(token, '(') || matches(token, '#') || isIdentifierToken(token) || isNumberToken(token)) {
296 | const factor = this.division()
297 | if (factor) {
298 | factors.push(factor)
299 | } else {
300 | break
301 | }
302 | } else {
303 | break
304 | }
305 |
306 | if (this.i > this.tokens.length) {
307 | break
308 | }
309 | }
310 |
311 | return factors.length > 1
312 | ? build.applyNode('mul', factors, {implicit: true})
313 | : factors[0]
314 | }
315 |
316 | division() {
317 | let numerator
318 | let denominator
319 | let frac
320 |
321 | numerator = this.factor()
322 |
323 | while (true) {
324 | const token = this.currentToken()
325 |
326 | if (matches(token, '/')) {
327 | this.consume('/')
328 | denominator = this.factor()
329 | if (frac) {
330 | frac = build.applyNode('div', [frac, denominator])
331 | } else {
332 | frac = build.applyNode('div', [numerator, denominator])
333 | }
334 | } else {
335 | break
336 | }
337 | }
338 |
339 | return frac || numerator
340 | }
341 |
342 | /**
343 | * Parse any of the following:
344 | * - unary operations, e.g. +, -
345 | * - numbers
346 | * - identifiers
347 | * - parenthesis
348 | * - absolute value function, e.g. |x|
349 | * - exponents, e.g. x^2
350 | */
351 | factor() {
352 | let token = this.currentToken()
353 |
354 | if (matches(token, '...')) {
355 | this.consume('...')
356 | return {
357 | type: 'Ellipsis',
358 | }
359 | }
360 |
361 | let signs = []
362 |
363 | // handle multiple unary operators
364 | while (matches(token, '+') || matches(token, '-')) {
365 | signs.push(token)
366 | this.consume(token.value)
367 | token = this.currentToken()
368 | }
369 |
370 | let base, exp
371 | let addParens = false
372 |
373 | if (matches(token, '#') || isIdentifierToken(token)) {
374 | const node = this.identifierOrPlaceholder()
375 |
376 | if (this.integrals > 0 && isIdentifier(node) && /d[a-z]+/.test(node.name)) {
377 | // backup
378 | this.integrals--
379 | return
380 | }
381 |
382 | if (matches(this.currentToken(), '(')) {
383 | this.consume('(')
384 | const args = this.argumentList()
385 | token = this.consume(')')
386 | if (node.name === 'nthRoot') {
387 | if (args.length < 1 || args.length > 2) {
388 | throw new Error('nthRoot takes 1 or 2 args')
389 | } else {
390 | base = build.nthRoot(...args)
391 | }
392 | } else if (node.name === 'int') {
393 | if (args.length >= 2 && args.length <= 4) {
394 | base = build.apply('int', args)
395 | } else {
396 | throw new Error('integral takes between 2 and 4 args')
397 | }
398 | } else {
399 | if (query.isIdentifier(node) && node.name === 'abs') {
400 | base = build.apply('abs', args)
401 | } else {
402 | base = build.apply(node, args)
403 | }
404 | }
405 | } else {
406 | // TODO(kevinb) valid the constraint type against the node
407 | // e.g. if it's a 'Number' then it can't have a subscript
408 | base = node
409 | }
410 | } else if (isNumberToken(token)) {
411 | this.consume(token.value)
412 | base = build.numberNode(token.value)
413 | } else if (matches(token, '(')) {
414 | this.consume('(')
415 | base = this.expression()
416 | token = this.consume(')')
417 | addParens = true
418 | if (base.type === 'Number' || isIdentifier(base)) {
419 | base = build.parensNode(base)
420 | addParens = false
421 | }
422 | } else if (matches(token, '|')) {
423 | this.consume('|')
424 | base = this.expression()
425 | token = this.consume('|')
426 |
427 | base = build.applyNode('abs', [base])
428 | }
429 |
430 | if (matches(this.currentToken(), '!')) {
431 | this.consume('!')
432 | // print will add parentheses back in if a 'fact' node wraps the
433 | // expression.
434 | addParens = false
435 | base = build.applyNode('fact', [base])
436 | }
437 |
438 | let factor = base
439 |
440 | // TODO handle exponents separately
441 | if (matches(this.currentToken(), '^')) {
442 | this.consume('^')
443 | exp = this.factor()
444 | factor = build.applyNode('pow', [base, exp])
445 | addParens = false
446 | }
447 |
448 | // Reverse the signs so that we process them from the sign nearest
449 | // to the factor to the furthest.
450 | signs.reverse()
451 |
452 | signs.forEach((sign) => {
453 | if (sign.value === '+') {
454 | factor = build.applyNode('pos', [factor])
455 | } else {
456 | factor = build.applyNode('neg', [factor])
457 | }
458 | addParens = false
459 | })
460 |
461 | if (addParens) {
462 | factor.addParens = addParens
463 | }
464 |
465 | if (query.isPow(factor)) {
466 | const [base, exponent] = factor.args
467 | if (query.isIdentifier(base) && base.name === 'int') {
468 | this.integrals++
469 | const body = this.expression()
470 |
471 | // backup to get the token we ignore
472 | this.i--
473 | token = this.currentToken()
474 | this.consume(token.value)
475 |
476 | const result = {
477 | type: 'Apply',
478 | op: 'int',
479 | args: [body, build.identifier(token.value)],
480 | limits: [base.subscript, exponent],
481 | }
482 |
483 | return result
484 | }
485 | } else if (query.isIdentifier(factor) && factor.name === 'int') {
486 | this.integrals++
487 | const body = this.expression()
488 |
489 | // backup to get the token we ignore
490 | this.i--
491 | token = this.currentToken()
492 | this.consume(token.value)
493 |
494 | const result = {
495 | type: 'Apply',
496 | op: 'int',
497 | args: [body, build.identifier(token.value)],
498 | }
499 |
500 | return result
501 | }
502 |
503 | return factor
504 | }
505 |
506 | identifierOrPlaceholder() {
507 | let token = this.currentToken()
508 |
509 | let isPlaceholder = false
510 | if (matches(token, '#')) {
511 | isPlaceholder = true
512 | this.consume('#')
513 | token = this.currentToken()
514 | }
515 |
516 | if (!isIdentifierToken(token)) {
517 | throw new Error('\'#\' must be followed by an identifier')
518 | }
519 |
520 | const result = this.identifier()
521 |
522 | if (isPlaceholder) {
523 | result.type = 'Placeholder'
524 | }
525 |
526 | return result
527 | }
528 |
529 | identifier() {
530 | let token = this.currentToken()
531 |
532 | const name = token.value
533 | const result = build.identifierNode(name)
534 | this.consume(name)
535 |
536 | token = this.currentToken()
537 |
538 | // This only handles very simple subscripts, e.g. a_0, a_n
539 | // It doesn't handle: a_-1, a_(m+n), etc.
540 | // The precedence of subscripts is very high: a_0^2 => (a_0)^2
541 | if (matches(token, '_')) {
542 | this.consume('_')
543 |
544 | token = this.currentToken()
545 |
546 | if (isNumberToken(token)) {
547 | result.subscript = build.numberNode(token.value)
548 | this.consume(token.value)
549 | } else if (isIdentifierToken(token)) {
550 | result.subscript = build.identifierNode(token.value)
551 | this.consume(token.value)
552 | } else {
553 | throw new Error(`Can't handle '${token.value}' as a subscript`)
554 | }
555 | }
556 |
557 | return result
558 | }
559 |
560 | argumentList() {
561 | const args = [this.expression()]
562 | while (true) {
563 | const token = this.currentToken()
564 | if (!matches(token, ',')) {
565 | break
566 | }
567 | this.consume(',')
568 | args.push(this.expression())
569 | }
570 | return args
571 | }
572 | }
573 |
574 | const parser = new Parser()
575 |
576 | export default function parse(math) {
577 | const ast = parser.parse(math)
578 | traverse(ast, {
579 | leave(node) {
580 | if (node.hasOwnProperty('addParens')) {
581 | delete node.addParens
582 | }
583 | },
584 | })
585 | return ast
586 | }
587 |
--------------------------------------------------------------------------------
/lib/print.js:
--------------------------------------------------------------------------------
1 | /**
2 | * print - return a string representation of the nodes.
3 | */
4 | import {query} from 'math-nodes'
5 |
6 | const relationIdentifierMap = {
7 | 'eq': '=',
8 | 'lt': '<',
9 | 'le': '<=',
10 | 'gt': '>',
11 | 'ge': '>=',
12 | 'ne': '!=',
13 | }
14 |
15 | const printApply = (node, parent) => {
16 | const {op, args} = node
17 |
18 | if (op === 'add') {
19 | let result = print(args[0], node)
20 | for (let i = 1; i < args.length; i++) {
21 | const arg = args[i]
22 | if (query.isNeg(arg) && arg.wasMinus) {
23 | result += ` - ${print(arg.args[0], node)}`
24 | } else {
25 | result += ` + ${print(arg, node)}`
26 | }
27 | }
28 | return parent && !query.isRel(parent) && parent.type !== 'Parentheses'
29 | ? `(${result})`
30 | : result
31 | } else if (op === 'mul') {
32 | let result
33 | // print coefficients with no spaces when possible - e.g. 2x not 2 x
34 | // if there are more than 3 args, we can't do 2xy because if that was
35 | // reparsed it'd treat xy as one symbol
36 | if (node.implicit && node.args.length === 2 &&
37 | query.isNumber(node.args[0]) && query.isIdentifier(node.args[1])) {
38 | result = args.map(arg => print(arg, node)).join('')
39 | } else if (node.implicit) {
40 | result = args.map(arg => print(arg, node)).join(' ')
41 | } else {
42 | result = args.map(arg => print(arg, node)).join(' * ')
43 | }
44 |
45 | if (query.isMul(parent)) {
46 | if (node.implicit && !parent.implicit) {
47 | return result
48 | } else {
49 | return `(${result})`
50 | }
51 | } else if (query.isPow(parent) || query.isDiv(parent)) {
52 | return `(${result})`
53 | } else {
54 | return result
55 | }
56 | } else if (op === 'div') {
57 | let result = ''
58 | // this lets us print things like 2/3 and x/5 instead of 2 / 3 and x / 5
59 | // (but the spaces are helpful for reading more complicated fractions)
60 | if ((query.isIdentifier(args[0]) || query.isNumber(args[0]))
61 | && (query.isIdentifier(args[1]) || query.isNumber(args[1]))) {
62 | result = `${print(args[0])}/${print(args[1])}`
63 | } else {
64 | result += print(args[0], node)
65 | result += ' / '
66 | if (query.isDiv(args[1])) {
67 | result += `(${print(args[1], node)})`
68 | } else {
69 | result += print(args[1], node)
70 | }
71 | }
72 | return query.isPow(parent)
73 | ? `(${result})`
74 | : result
75 | } else if (op === 'pow') {
76 | const [base, exp] = node.args
77 | return query.isNeg(base)
78 | ? `(${print(base, node)})^${print(exp, node)}`
79 | : `${print(base, node)}^${print(exp, node)}`
80 | } else if (op === 'neg') {
81 | return `-${print(args[0], node)}`
82 | } else if (op === 'pos') {
83 | return `+${print(args[0], node)}`
84 | } else if (op === 'pn') {
85 | throw new Error('we don\'t handle \'pn\' operations yet')
86 | } else if (op === 'np') {
87 | throw new Error('we don\'t handle \'np\' operations yet')
88 | } else if (op === 'fact') {
89 | if (args[0].op === 'pow' || args[0].op === 'mul' || args[0].op === 'div') {
90 | return `(${print(args[0], node)})!`
91 | } else {
92 | return `${print(args[0], node)}!`
93 | }
94 | } else if (op === 'nthRoot') {
95 | return `nthRoot(${args.map(arg => print(arg, node)).join(', ')})`
96 | } else if (op === 'int') {
97 | return `int(${args.map(arg => print(arg, node)).join(', ')})`
98 | } else if (op === 'abs') {
99 | return `|${print(args[0])}|`
100 | } else if (op in relationIdentifierMap) {
101 | const symbol = relationIdentifierMap[op]
102 | return args.map(arg => print(arg, node)).join(` ${symbol} `)
103 | } else {
104 | return `${print(op)}(${args.map(arg => print(arg, node)).join(', ')})`
105 | }
106 | }
107 |
108 | export default function print(node, parent = null) {
109 | switch (node.type) {
110 | // regular non-leaf nodes
111 | case 'Apply':
112 | return printApply(node, parent)
113 |
114 | // irregular non-leaf nodes
115 | case 'Parentheses':
116 | return `(${print(node.body, node)})`
117 |
118 | case 'Sequence':
119 | return node.items.map(print).join(', ')
120 |
121 | // leaf nodes
122 | case 'Identifier':
123 | if (node.subscript) {
124 | return `${node.name}_${print(node.subscript)}`
125 | } else {
126 | return node.name
127 | }
128 |
129 | case 'Placeholder':
130 | if (node.subscript) {
131 | return `#${node.name}_${print(node.subscript)}`
132 | } else {
133 | return `#${node.name}`
134 | }
135 |
136 | case 'Number':
137 | return node.value
138 |
139 | case 'Ellipsis':
140 | return '...'
141 |
142 | default:
143 | console.log(node) // eslint-disable-line no-console
144 | throw new Error('unrecognized node')
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/lib/toTex.js:
--------------------------------------------------------------------------------
1 | /**
2 | * toTex - return a string representation of the nodes in LaTeX
3 | */
4 |
5 | import {query} from 'math-nodes'
6 |
7 | const relationIdentifierMap = {
8 | 'eq': '=',
9 | 'lt': '<',
10 | 'le': '<=',
11 | 'gt': '>',
12 | 'ge': '>=',
13 | 'ne': '!=',
14 | }
15 |
16 | const applyToTex = (node, parent) => {
17 | const {op, args} = node
18 |
19 | if (op === 'add') {
20 | //e.g a + (-a) => a - a
21 | let result = toTex(node.args[0], node)
22 | for (let i = 1; i < node.args.length; i++){
23 | const arg = node.args[i]
24 | if (query.isNeg(arg) && arg.wasMinus){
25 | result += ` - ${toTex(arg.args[0], node)}`
26 | } else {
27 | result += ` + ${toTex(arg, node)}`
28 | }
29 | }
30 | return parent ? `\\left(${result}\\right)` : result
31 | } else if (op === 'mul') {
32 | if (node.implicit) {
33 | //e.g 2 x
34 | return node.args.map(arg => toTex(arg, node)).join(' ')
35 | } else {
36 | //e.g 2 * x
37 | return node.args.map(arg => toTex(arg, node)).join(' \\times ')
38 | }
39 | } else if (op === 'div') {
40 | let result = ''
41 | result += '\\frac'
42 | //add parentheses when numerator or denominator has multiple terms
43 | //e.g latex fractions: \frac{a}{b} => a/b
44 | result += `{${toTex(node.args[0], node)}}`
45 | result += `{${toTex(node.args[1], node)}}`
46 | return result
47 | } else if (op === 'pow') {
48 | return `${toTex(node.args[0], node)}^{${toTex(node.args[1], node)}}`
49 | } else if (op === 'neg') {
50 | return `-${toTex(args[0], node)}`
51 | } else if (op === 'pos') {
52 | return `+${toTex(args[0], node)}`
53 | } else if (op === 'pn') {
54 | throw new Error('we don\'t handle \'pn\' operations yet')
55 | } else if (op === 'np') {
56 | throw new Error('we don\'t handle \'np\' operations yet')
57 | } else if (op === 'fact') {
58 | throw new Error('we dont handle fact operations yet')
59 | } else if (op === 'nthRoot') {
60 | const [radicand, index] = node.args
61 |
62 | let base, exponent
63 | let result
64 |
65 | if (query.isPow(radicand)) {
66 | [base, exponent] = radicand.args
67 | result = index == '' || query.getValue(index) == 2
68 | ? `\\sqrt{${toTex(base, node)}^${toTex(exponent, node)}}`
69 | : `\\sqrt[${toTex(index, node)}]{${toTex(base, node)}^${toTex(exponent, node)}}`
70 | } else {
71 | base = radicand
72 | result = index == '' || query.getValue(index) == 2
73 | ? `\\sqrt{${toTex(base, node)}}`
74 | : `\\sqrt[${toTex(index, node)}]{${toTex(base, node)}}`
75 | }
76 | return result
77 | } else if (op === 'int') {
78 | let result
79 | if (node.args.length == 2) {
80 | const [input, variable] = node.args
81 | result = `\\int ${toTex(input, node)} d${toTex(variable, node)}`
82 | } else if (node.args.length == 3) {
83 | const [input, variable, domain] = node.args
84 | result = `\\int_${toTex(domain, node)} ${toTex(input, node)} d${toTex(variable, node)}`
85 | } else if (node.args.length == 4) {
86 | const [input, variable, upper, lower] = node.args
87 | result = `\\int_{${toTex(upper, node)}}^{${toTex(lower, node)}} ${toTex(input, node)} d${toTex(variable, node)}`
88 | }
89 | return result
90 | } else if (op in relationIdentifierMap) {
91 | const symbol = relationIdentifierMap[op]
92 | return args.map(arg => toTex(arg, node)).join(` ${symbol} `)
93 | } else {
94 | return `${op}(${args.map(arg => toTex(arg, node)).join(', ')})`
95 | }
96 | }
97 |
98 | export default function toTex(node, parent = null){
99 | switch(node.type){
100 | // regular non-leaf nodes
101 | case 'Apply':
102 | return applyToTex(node, parent)
103 |
104 | // irregular node-leaf nodes
105 | case 'Parentheses':
106 | return `\left(${toTex(node.body, node)}\right)`
107 |
108 | //leaf nodes
109 | case 'Identifier':
110 | return node.name
111 | case 'Number':
112 | return node.value
113 |
114 | default:
115 | console.log(node) // eslint-disable-line no-console
116 | throw new Error('unrecognized node')
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "math-parser",
3 | "version": "0.13.0",
4 | "description": "Parse math strings to an AST suitable for symbolic manipulation.",
5 | "main": "dist/math-parser.js",
6 | "scripts": {
7 | "prepublish": "webpack",
8 | "watch": "webpack -w",
9 | "test": "jest",
10 | "lint": "eslint . --cache"
11 | },
12 | "pre-commit": [
13 | "lint"
14 | ],
15 | "jest": {
16 | "setupTestFrameworkScriptFile": "/node_modules/babel-polyfill/dist/polyfill.js",
17 | "transform": {
18 | "^.+\\.jsx?$": "babel-jest"
19 | }
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/kevinbarabash/math-parser.git"
24 | },
25 | "author": "Kevin Barabash ",
26 | "license": "MIT",
27 | "bugs": {
28 | "url": "https://github.com/kevinbarabash/math-parser/issues"
29 | },
30 | "homepage": "https://github.com/kevinbarabash/math-parser#readme",
31 | "devDependencies": {
32 | "babel-core": "^6.22.1",
33 | "babel-eslint": "^7.1.1",
34 | "babel-loader": "^6.2.10",
35 | "babel-plugin-transform-flow-strip-types": "^6.22.0",
36 | "babel-plugin-transform-object-rest-spread": "^6.22.0",
37 | "babel-polyfill": "^6.23.0",
38 | "babel-preset-es2015": "^6.22.0",
39 | "eslint": "^3.15.0",
40 | "eslint-plugin-flowtype": "^2.30.0",
41 | "flow-bin": "^0.38.0",
42 | "jest": "^20.0.3",
43 | "json-stable-stringify": "^1.0.1",
44 | "pre-commit": "^1.2.2",
45 | "webpack": "^2.2.1"
46 | },
47 | "dependencies": {
48 | "math-nodes": "^0.1.7",
49 | "math-traverse": "^0.2.2"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | module.exports = {
4 | entry: './index.js',
5 | output: {
6 | path: path.join(__dirname, 'dist'),
7 | filename: 'math-parser.js',
8 | libraryTarget: 'commonjs2'
9 | },
10 | module: {
11 | loaders: [
12 | {
13 | test: /\.js$/,
14 | exclude: /node_modules/,
15 | loader: 'babel-loader',
16 | }
17 | ]
18 | }
19 | }
20 |
--------------------------------------------------------------------------------