Reputation: 571
I want to store arbitrary mathematical expression with basic operations (+, -, *, /, ^, sqrt, grouping, etc…) and id placeholders as a string.
const json = {
“formula”: “{{a45bc2a1-ed82-4ccd-a455-f7959e875aad}}+({{f6c2ef2b-a4fa-4cfb-b62d-d0d7c3e266d9}}*{{335563ad-a715-47b9-8e54-2b8553768168}})”
}
Ids are mapped to arrays such as:
const map = {
“a45bc2a1-ed82-4ccd-a455-f7959e875aad”: [1, 2, 3, 4, 5],
“f6c2ef2b-a4fa-4cfb-b62d-d0d7c3e266d9”: [10, 20, 30, 40, 50],
“335563ad-a715-47b9-8e54-2b8553768168”: [1, 2, 3, 4, 5]
}
How can I achieve this? eval() doesn’t work with vectors and I don’t know how to parse arbitrary formula to do element-wise operations
Result should be:
[11, 42, 93, 164, 255]
Upvotes: 1
Views: 175
Reputation: 106
I played around with this a while today and got a version working that does not require defining a formal AST, and only uses eval() to instantiate an arrow function that calculates each index of the output based on the input lists and an incrementing input index.
'use strict';
const json={
formula: '{{a45bc2a1-ed82-4ccd-a455-f7959e875aad}}+({{f6c2ef2b-a4fa-4cfb-b62d-d0d7c3e266d9}}*{{335563ad-a715-47b9-8e54-2b8553768168}})'
}
const map = {
'a45bc2a1-ed82-4ccd-a455-f7959e875aad': [1, 2, 3, 4, 5],
'f6c2ef2b-a4fa-4cfb-b62d-d0d7c3e266d9': [10, 20, 30, 40, 50],
'335563ad-a715-47b9-8e54-2b8553768168': [1, 2, 3, 4, 5]
}
function parseFormula (formula) {
const template = /{{([^}]*)}}/;
const idList = [];
for (let i = 0; template.test(formula); i++) {
idList.push(formula.match(template)[0].replace(/[{}]/g, ''));
formula = formula.replace(template, `input[${i}][i]`);
}
return {idList, formula};
}
function calculateFormula(map, parsed) {
const input = [];
const result = [];
const lambda = eval(`(input, i) => ${parsed.formula}`)
parsed.idList.forEach(id => input.push(map[id]));
for (let i = 0; i < input[0].length; i++) {
result.push(lambda(input, i));
}
return result;
}
const parsed = parseFormula(json.formula);
console.log(parsed);
console.log(calculateFormula(map, parsed));
Output:
{
idList: [
'a45bc2a1-ed82-4ccd-a455-f7959e875aad',
'f6c2ef2b-a4fa-4cfb-b62d-d0d7c3e266d9',
'335563ad-a715-47b9-8e54-2b8553768168'
],
formula: 'input[0][i]+(input[1][i]*input[2][i])'
}
[ 11, 42, 93, 164, 255 ]
Upvotes: 0
Reputation: 215029
Since you're, in fact, developing a programming language, you'll going to need a compiler (=translate string expressions into an AST) and a runtime (=evaluate AST given inputs and predefined bindings). Here's some code to get you started. It only supports a limited grammar expression = term | term op expression
and doesn't handle any errors:
// "compiler"
function parse(str) {
return expr([...str])
}
function expr(chars) {
let node = term(chars)
if (chars.length)
node = {
op: chars.shift(),
left: node,
right: expr(chars)
}
return node
}
function term(chars) {
let str = ''
while (chars.length && chars[0].match(/\w/))
str += chars.shift()
return {value: str}
}
// "runtime"
ops = {
'+': (a, b) => a + b,
'-': (a, b) => a - b,
}
function evaluate(node, bindings) {
if (node.value)
return bindings[node.value]
return eval_op(
node.op,
evaluate(node.left, bindings),
evaluate(node.right, bindings))
}
function eval_op(op, left, right) {
let fn = ops[op]
return left.map((_, i) => fn(left[i], right[i]))
}
// demo
input = 'abc+def+xyz'
bindings = {
'abc': [1, 2, 3],
'def': [4, 5, 6],
'xyz': [7, 8, 9],
}
ast = parse(input)
console.log('AST', ast)
res = evaluate(ast, bindings)
console.log('RESULT', res)
Of course, there's a lot more work involved to turn this sketch into real code. I'd suggest you learn a bit about formal grammars, parsers and parser generators.
Upvotes: 1
Reputation: 782166
Loop over the array indexes, and replace the placeholders with the current index in the corresponding element of map
. Then call eval()
.
let len = Object.values(json.formula)[0].length;
for (let i = 0; i < len; i++) {
let formula = json.formula.replace(/{{([-\w]+)}}/g, (m, placeholder) => map[placeholder][i];
console.log(eval(formula));
}
Upvotes: 0