silentmatt / expr-eval

Mathematical expression evaluator in JavaScript
http://silentmatt.com/javascript-expression-evaluator/
MIT License
1.18k stars 239 forks source link

String representation of Expression in Node.js #256

Open n-riesco opened 3 years ago

n-riesco commented 3 years ago

This issue was originally reported in https://github.com/n-riesco/ijavascript/issues/240 .

The Node.js CLI doesn't use toString to show objects as a string. This makes the use of expr-eval in the Node.js CLI somewhat cumbersome. For example:

Click to expand example ``` $ node Welcome to Node.js v14.13.0. Type ".help" for more information. > var Parser = require('expr-eval').Parser; undefined > expr = Parser.parse("2 * x + 1"); Expression { tokens: [ Instruction { type: 'INUMBER', value: 2 }, Instruction { type: 'IVAR', value: 'x' }, Instruction { type: 'IOP2', value: '*' }, Instruction { type: 'INUMBER', value: 1 }, Instruction { type: 'IOP2', value: '+' } ], parser: Parser { options: {}, unaryOps: { sin: [Function: sin], cos: [Function: cos], tan: [Function: tan], asin: [Function: asin], acos: [Function: acos], atan: [Function: atan], sinh: [Function: sinh], cosh: [Function: cosh], tanh: [Function: tanh], asinh: [Function: asinh], acosh: [Function: acosh], atanh: [Function: atanh], sqrt: [Function: sqrt], cbrt: [Function: cbrt], log: [Function: log], log2: [Function: log2], ln: [Function: log], lg: [Function: log10], log10: [Function: log10], expm1: [Function: expm1], log1p: [Function: log1p], abs: [Function: abs], ceil: [Function: ceil], floor: [Function: floor], round: [Function: round], trunc: [Function: trunc], '-': [Function: neg], '+': [Function: Number], exp: [Function: exp], not: [Function: not], length: [Function: stringOrArrayLength], '!': [Function: factorial], sign: [Function: sign] }, binaryOps: { '+': [Function: add], '-': [Function: sub], '*': [Function: mul], '/': [Function: div], '%': [Function: mod], '^': [Function: pow], '||': [Function: concat], '==': [Function: equal], '!=': [Function: notEqual], '>': [Function: greaterThan], '<': [Function: lessThan], '>=': [Function: greaterThanEqual], '<=': [Function: lessThanEqual], and: [Function: andOperator], or: [Function: orOperator], in: [Function: inOperator], '=': [Function: setVar], '[': [Function: arrayIndex] }, ternaryOps: { '?': [Function: condition] }, functions: { random: [Function: random], fac: [Function: factorial], min: [Function: min], max: [Function: max], hypot: [Function: hypot], pyt: [Function: hypot], pow: [Function: pow], atan2: [Function: atan2], if: [Function: condition], gamma: [Function: gamma], roundTo: [Function: roundTo], map: [Function: arrayMap], fold: [Function: arrayFold], filter: [Function: arrayFilter], indexOf: [Function: stringOrArrayIndexOf], join: [Function: arrayJoin] }, consts: { E: 2.718281828459045, PI: 3.141592653589793, true: true, false: false } }, unaryOps: { sin: [Function: sin], cos: [Function: cos], tan: [Function: tan], asin: [Function: asin], acos: [Function: acos], atan: [Function: atan], sinh: [Function: sinh], cosh: [Function: cosh], tanh: [Function: tanh], asinh: [Function: asinh], acosh: [Function: acosh], atanh: [Function: atanh], sqrt: [Function: sqrt], cbrt: [Function: cbrt], log: [Function: log], log2: [Function: log2], ln: [Function: log], lg: [Function: log10], log10: [Function: log10], expm1: [Function: expm1], log1p: [Function: log1p], abs: [Function: abs], ceil: [Function: ceil], floor: [Function: floor], round: [Function: round], trunc: [Function: trunc], '-': [Function: neg], '+': [Function: Number], exp: [Function: exp], not: [Function: not], length: [Function: stringOrArrayLength], '!': [Function: factorial], sign: [Function: sign] }, binaryOps: { '+': [Function: add], '-': [Function: sub], '*': [Function: mul], '/': [Function: div], '%': [Function: mod], '^': [Function: pow], '||': [Function: concat], '==': [Function: equal], '!=': [Function: notEqual], '>': [Function: greaterThan], '<': [Function: lessThan], '>=': [Function: greaterThanEqual], '<=': [Function: lessThanEqual], and: [Function: andOperator], or: [Function: orOperator], in: [Function: inOperator], '=': [Function: setVar], '[': [Function: arrayIndex] }, ternaryOps: { '?': [Function: condition] }, functions: { random: [Function: random], fac: [Function: factorial], min: [Function: min], max: [Function: max], hypot: [Function: hypot], pyt: [Function: hypot], pow: [Function: pow], atan2: [Function: atan2], if: [Function: condition], gamma: [Function: gamma], roundTo: [Function: roundTo], map: [Function: arrayMap], fold: [Function: arrayFold], filter: [Function: arrayFilter], indexOf: [Function: stringOrArrayIndexOf], join: [Function: arrayJoin] } } > expr2 = expr.substitute("x", "4 * x"); Expression { tokens: [ Instruction { type: 'INUMBER', value: 2 }, Instruction { type: 'INUMBER', value: 4 }, Instruction { type: 'IVAR', value: 'x' }, Instruction { type: 'IOP2', value: '*' }, Instruction { type: 'IOP2', value: '*' }, Instruction { type: 'INUMBER', value: 1 }, Instruction { type: 'IOP2', value: '+' } ], parser: Parser { options: {}, unaryOps: { sin: [Function: sin], cos: [Function: cos], tan: [Function: tan], asin: [Function: asin], acos: [Function: acos], atan: [Function: atan], sinh: [Function: sinh], cosh: [Function: cosh], tanh: [Function: tanh], asinh: [Function: asinh], acosh: [Function: acosh], atanh: [Function: atanh], sqrt: [Function: sqrt], cbrt: [Function: cbrt], log: [Function: log], log2: [Function: log2], ln: [Function: log], lg: [Function: log10], log10: [Function: log10], expm1: [Function: expm1], log1p: [Function: log1p], abs: [Function: abs], ceil: [Function: ceil], floor: [Function: floor], round: [Function: round], trunc: [Function: trunc], '-': [Function: neg], '+': [Function: Number], exp: [Function: exp], not: [Function: not], length: [Function: stringOrArrayLength], '!': [Function: factorial], sign: [Function: sign] }, binaryOps: { '+': [Function: add], '-': [Function: sub], '*': [Function: mul], '/': [Function: div], '%': [Function: mod], '^': [Function: pow], '||': [Function: concat], '==': [Function: equal], '!=': [Function: notEqual], '>': [Function: greaterThan], '<': [Function: lessThan], '>=': [Function: greaterThanEqual], '<=': [Function: lessThanEqual], and: [Function: andOperator], or: [Function: orOperator], in: [Function: inOperator], '=': [Function: setVar], '[': [Function: arrayIndex] }, ternaryOps: { '?': [Function: condition] }, functions: { random: [Function: random], fac: [Function: factorial], min: [Function: min], max: [Function: max], hypot: [Function: hypot], pyt: [Function: hypot], pow: [Function: pow], atan2: [Function: atan2], if: [Function: condition], gamma: [Function: gamma], roundTo: [Function: roundTo], map: [Function: arrayMap], fold: [Function: arrayFold], filter: [Function: arrayFilter], indexOf: [Function: stringOrArrayIndexOf], join: [Function: arrayJoin] }, consts: { E: 2.718281828459045, PI: 3.141592653589793, true: true, false: false } }, unaryOps: { sin: [Function: sin], cos: [Function: cos], tan: [Function: tan], asin: [Function: asin], acos: [Function: acos], atan: [Function: atan], sinh: [Function: sinh], cosh: [Function: cosh], tanh: [Function: tanh], asinh: [Function: asinh], acosh: [Function: acosh], atanh: [Function: atanh], sqrt: [Function: sqrt], cbrt: [Function: cbrt], log: [Function: log], log2: [Function: log2], ln: [Function: log], lg: [Function: log10], log10: [Function: log10], expm1: [Function: expm1], log1p: [Function: log1p], abs: [Function: abs], ceil: [Function: ceil], floor: [Function: floor], round: [Function: round], trunc: [Function: trunc], '-': [Function: neg], '+': [Function: Number], exp: [Function: exp], not: [Function: not], length: [Function: stringOrArrayLength], '!': [Function: factorial], sign: [Function: sign] }, binaryOps: { '+': [Function: add], '-': [Function: sub], '*': [Function: mul], '/': [Function: div], '%': [Function: mod], '^': [Function: pow], '||': [Function: concat], '==': [Function: equal], '!=': [Function: notEqual], '>': [Function: greaterThan], '<': [Function: lessThan], '>=': [Function: greaterThanEqual], '<=': [Function: lessThanEqual], and: [Function: andOperator], or: [Function: orOperator], in: [Function: inOperator], '=': [Function: setVar], '[': [Function: arrayIndex] }, ternaryOps: { '?': [Function: condition] }, functions: { random: [Function: random], fac: [Function: factorial], min: [Function: min], max: [Function: max], hypot: [Function: hypot], pyt: [Function: hypot], pow: [Function: pow], atan2: [Function: atan2], if: [Function: condition], gamma: [Function: gamma], roundTo: [Function: roundTo], map: [Function: arrayMap], fold: [Function: arrayFold], filter: [Function: arrayFilter], indexOf: [Function: stringOrArrayIndexOf], join: [Function: arrayJoin] } } > expr2.evaluate({ x: 3 }); 25 ```

The Node,js CLI uses util.inspect instead, that can be customised by defining util.inspect.custom. For example:

$ node
Welcome to Node.js v14.13.0.
Type ".help" for more information.

> var Parser = require('expr-eval').Parser;
undefined

> require('expr-eval').Expression.prototype[Symbol.for('nodejs.util.inspect.custom')] = function inspect(_depth, _opts) { return this.toString(); };
[Function: inspect]

> expr = Parser.parse("2 * x + 1");
((2 * x) + 1)

I can submit a PR, if you're interested in having this functionality added to Expression.