Files
DiceCloud/app/imports/api/creature/computation/afterComputation/evaluateString.js
2020-04-23 14:26:05 +02:00

108 lines
2.8 KiB
JavaScript

import * as math from 'mathjs';
export default function evaluateString(string, scope){
let errors = [];
if (!string){
errors.push('No string provided');
return {result: string, errors};
}
if (!scope) errors.push('No scope provided');
// Parse the string using mathjs
let calc;
try {
calc = math.parse(string);
} catch (e) {
errors.push(e);
return {result: string, errors};
}
// Replace all bare symbols with symbol.value
let transformedCalc = calc.transform(replaceBareSymbolsWithValueAccessor);
// Evaluate the expression to a number or return with substitutions
try {
let result = transformedCalc.evaluate(scope);
return {result, errors};
} catch (e1){
errors.push(e1);
try {
result = simplifyWithAccessors(calc, scope).toHTML();
return {result, errors};
} catch (e2){
errors.push(e2);
return {result: calc.toHTML(), errors};
}
}
}
function replaceBareSymbolsWithValueAccessor(node, path, parent) {
if (node.isSymbolNode && path !== 'object') {
const object = new math.SymbolNode(node.name);
const address = new math.ConstantNode('value');
const index = new math.IndexNode([address]);
return new math.AccessorNode(object, index);
} else {
return node;
}
}
function simplifyWithAccessors(calc, scope){
let noAccessorCalc = calc.transform(substituteAccessors(scope));
return math.simplify(noAccessorCalc);
}
// returns a function to replace all accessors with either their resolved value
// or a symbol to simplify with
function substituteAccessors(scope){
return function(node, path, parent){
if (node.isAccessorNode){
try {
return evaluateAccessor(node, scope);
} catch (e) {
console.log(typeof e);
return replaceAccessorWithSymbol(node);
}
} else {
return node;
}
}
}
// Throws error if symbol is undefined in scope
function evaluateAccessor(node, scope){
let value = node.evaluate(scope);
if (value === undefined){
throw 'Undefined symbol'
}
return new math.ConstantNode(value);
}
function replaceAccessorWithSymbol(node){
let symbolNode = new math.SymbolNode(node.toString());
return symbolNode;
}
function overrideSymbolNodeHTML(symbolNode){
let safeName = escape(symbolNode.name);
symbolNode.toHTML = function(){
console.log('running custom tohtml function')
return `<span class="math-symbol math-substitution-failed">${safeName}</span>`
}
return symbolNode;
}
// Escape special HTML characters
// Copied directly from math.js source to help with overriding toHTML
function escape (value) {
let text = String(value)
text = text.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
return text
}