From cf754a7a34db11e6c91e00a35f5168c5aa271eb8 Mon Sep 17 00:00:00 2001 From: Ad5001 Date: Mon, 9 Oct 2023 18:37:28 +0200 Subject: [PATCH] Better handling of constants in simplified expressions. --- .../ad5001/LogarithmPlotter/js/expr-eval.js | 23 ++++++++++--------- .../ad5001/LogarithmPlotter/js/math/common.js | 7 +++++- .../LogarithmPlotter/js/math/expression.js | 4 ++-- .../eu/ad5001/LogarithmPlotter/js/utils.js | 16 +++++++++++++ README.md | 2 +- 5 files changed, 37 insertions(+), 15 deletions(-) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js index e118ec3..ae689e8 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js @@ -521,7 +521,7 @@ Expression.prototype.substitute = function (variable, expr) { }; Expression.prototype.evaluate = function (values) { - values = values || {}; + values = Object.assign({}, values, this.parser.consts) return evaluate(this.tokens, this, values); }; @@ -541,8 +541,9 @@ Expression.prototype.variables = function (options) { var vars = []; getSymbols(this.tokens, vars, options); var functions = this.functions; + var consts = this.parser.consts return vars.filter(function (name) { - return !(name in functions); + return !(name in functions) && !(name in consts); }); }; @@ -580,7 +581,7 @@ function TokenStream(parser, expression) { this.unaryOps = parser.unaryOps; this.binaryOps = parser.binaryOps; this.ternaryOps = parser.ternaryOps; - this.consts = parser.consts; + this.builtinConsts = parser.builtinConsts; this.expression = expression; this.savedPosition = 0; this.savedCurrent = null; @@ -700,8 +701,8 @@ TokenStream.prototype.isConst = function () { } if (i > startPos) { var str = this.expression.substring(startPos, i); - if (str in this.consts) { - this.current = this.newToken(TNUMBER, this.consts[str]); + if (str in this.builtinConsts) { + this.current = this.newToken(TNUMBER, this.builtinConsts[str]); this.pos += str.length; return true; } @@ -1792,12 +1793,12 @@ class Parser { join: arrayJoin }; - this.consts = { - E: Math.E, - PI: Math.PI, - 'true': true, - 'false': false - }; + // These constants will automatically be replaced the MOMENT they are parsed. + // (Original consts from the parser) + this.builtinConsts = {}; + // These consts will only be replaced when the expression is evaluated. + this.consts = {} + } parse(expr) { diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js index 59297d8..297887b 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/common.js @@ -26,13 +26,17 @@ const DERIVATION_PRECISION = 0.1 var evalVariables = { // Variables not provided by expr-eval.js, needs to be provided manually "pi": Math.PI, + "PI": Math.PI, "π": Math.PI, "inf": Infinity, "infinity": Infinity, "Infinity": Infinity, "∞": Infinity, "e": Math.E, - "E": Math.E + "E": Math.E, + "true": true, + "false": false + } var currentVars = {} @@ -54,3 +58,4 @@ parser.functions.derivative = function(f, variable, x) { f = parser.parse(f).toJSFunction(variable, currentVars) return (f(x+DERIVATION_PRECISION/2)-f(x-DERIVATION_PRECISION/2))/DERIVATION_PRECISION } + diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js index 9c9ae49..01b25b9 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/math/expression.js @@ -27,8 +27,8 @@ */ class Expression { constructor(expr) { - this.expr = expr - this.calc = C.parser.parse(expr).simplify() + this.expr = Utils.exponentsToExpression(expr) + this.calc = C.parser.parse(this.expr).simplify() this.cached = this.isConstant() this.cachedValue = this.cached && this.allRequirementsFullfilled() ? this.calc.evaluate(C.currentObjectsByName) : null this.latexMarkup = Latex.expression(this.calc.tokens) diff --git a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/utils.js b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/utils.js index 9121661..8ca3a69 100644 --- a/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/utils.js +++ b/LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/utils.js @@ -62,6 +62,11 @@ var powerpos = { "z": "ᶻ" } +var exponents = [ + "⁰","¹","²","³","⁴","⁵","⁶","⁷","⁸","⁹" +] +var exponentReg = new RegExp('(['+exponents.join('')+']+)', 'g') + var indicepos = { "-": "₋", "+": "₊", @@ -351,3 +356,14 @@ function getRandomColor() { function escapeHTML(str) { return str.replace(/&/g,'&').replace(//g,'>') ; } + + + +/** + * Parses exponents and replaces them with expression values + * @param {string} expression - The expression to replace in. + * @return {string} The parsed expression + */ +function exponentsToExpression(expression) { + return expression.replace(exponentReg, (m, exp) => '^' + exp.split('').map((x) => exponents.indexOf(x)).join('')) +} diff --git a/README.md b/README.md index e75daba..ba1f3b1 100644 --- a/README.md +++ b/README.md @@ -79,4 +79,4 @@ Language files translations located at LogarithmPlotter/i18n are licensed under LogarithmPlotter includes [expr-eval](https://github.com/silentmatt/expr-eval) a port of [ndef.parser](https://web.archive.org/web/20111023001618/http://www.undefined.ch/mparser/index.html) by Raphael Graf <r@undefined.ch>, ported to javascript by Matthew Crumley <email@matthewcrumley.com> (http://silentmatt.com/), and then to QMLJS by Ad5001. -The code is licensed under the [MIT License](https://raw.githubusercontent.com/silentmatt/expr-eval/master/LICENSE.txt). +The specific file (LogarithmPlotter/qml/eu/ad5001/LogarithmPlotter/js/expr-eval.js) is licensed under the [MIT License](https://raw.githubusercontent.com/silentmatt/expr-eval/master/LICENSE.txt).