Emmy as JavaScript library (3)
<script src="https://kloimhardt.github.io/blog/js/emmy.js/build/emmy_bundle.js"></script>
var div = emmy.div;
var replaceMath = (txt) => txt.replace(/\//g,"div");
var insertCommas = (txt) =>
txt.replace(/(\w+)/g,'"$1",')
.replace(/\,\s+\]/g," ]");
var makeBrackets = (txt) =>
txt.trim()
.replace(/\(/g,"[ ")
.replace(/\)/g," ],")
.replace(/,$/,"");
var textToJson = (txt) =>
JSON.parse(insertCommas(replaceMath(makeBrackets(txt))));
var jsonToJs = (j) =>
j.constructor == Array
? jsonToJs(j[0]) + "(" + j.slice(1).map(jsonToJs) + ")"
: j;
var swapFirst = (j) =>
j.constructor == Array
? [j[1], j[0]].concat(j.slice(2)).map(swapFirst)
: j;
var expressionToJs = (expr) =>
expr[expr.length-1] === " "
? jsonToJs(swapFirst(textToJson(expr))) + ".toString();"
: "'add blank'";
<script> var oldEval = window.eval; var newEval = (txt) => txt[0] === "(" ? oldEval(expressionToJs(txt)) : oldEval(txt); window.eval = newEval; </script>
((1 / 2) / 2)
var mul = emmy.mul;
mul(3, div(1, 2)).toString();
var _replaceMath = (txt) => // old definition
txt.replace(/\//g,"div");
var __replaceMath = (txt) => // extended version
_replaceMath(txt).replace(/\*/g,"mul");
var replaceMath = __replaceMath;
(3 * (1 / 2))
var sub = emmy.sub;
var add = emmy.add;
var expt = emmy.expt;
var replaceMath = (txt) => // new
__replaceMath(txt)
.replace(/\-/g,"sub")
.replace(/\+/g,"add")
.replace(/\^/g,"expt");
add(1, mul(sub(div(2, 3), 4), 5)).toString();
(1 + (((2 / 3) - 4) * 5))
expt(2,3);
var _jsonToJs = (j) => // old definition
j.constructor == Array
? jsonToJs(j[0]) + "(" + j.slice(1).map(jsonToJs) + ")"
: j;
var jsonToJs = (j) => // new
j.constructor == Array
? jsonToJs(j[0]) + "(" + j.slice(1).map(jsonToJs) + ")"
: _jsonToJs(j);
expressionToJs("(2 ^ 3) ");
(2 ^ 3)
Up to now, the numbers in our mathematical expressions, like 1/2, 2^3, always came in pairs. If there is only one argument, like in cos(0), we would need to apply an awkward notation.
expressionToJs("(0 cos) ");
This notation is unacceptable and needs fixing. As an intermediate step, we write a dedicated procedure for importing functions
<script> var loadEnv = (name) => { window[name] = emmy[name]; return name; } </script>
and us it.
var names = ["sin", "cos"];
names.map(loadEnv);
cos.call(null, 0);
var _swapFirst = (j) => // old
j.constructor == Array
? [j[1], j[0]].concat(j.slice(2)).map(swapFirst)
: j;
var swapFirst = (j) =>
j.constructor == Array
&& ["div", "mul", "sub", "add", "expt"].includes(j[1])
? [j[1], j[0]].concat(j.slice(2)).map(swapFirst)
: j.constructor == Array
? j.map(swapFirst)
: _swapFirst(j);
jsonToJs(swapFirst(textToJson("(cos 0)")));
Acceptable, we can write
(1 + (cos 0))
as well as
(+ 1 (cos 0))
if not happy yet, proposal
var shiftParen = (txt) =>
names.reduce((s,r) => s.replace(r+"(","("+r+" "), txt);
var _textToJson = (txt) => // old
JSON.parse(insertCommas(replaceMath(makeBrackets(txt))));
var textToJson = (txt) =>
_textToJson(shiftParen(txt));
shiftParen("sin(cos(0))", names);
better
(1 + cos(sin(0)))
but square has to be written like this
(sin((314 / 100)) ^ 2)
Needs lots of work to improve, so stick without last step.
((sin ^ 2) (314 / 100))
In this last step we did not square a number, but we squared a function and then applied it to a number,
Before moving on, practice a bit.