Referencing a missing variable in an effect now returns zero, not an error

This commit is contained in:
Thaum Rystra
2020-05-28 19:58:52 +02:00
parent d31f980002
commit 7f2401da81
8 changed files with 198 additions and 50 deletions

View File

@@ -3,8 +3,14 @@ import evaluateCalculation from '/imports/api/creature/computation/evaluateCalcu
export default class EffectAggregator{
constructor(stat, memo){
if (stat.baseValueCalculation){
this.statBaseValue = evaluateCalculation(stat.baseValueCalculation, memo);
this.base = +this.statBaseValue;
let {value, errors} = evaluateCalculation(stat.baseValueCalculation, memo);
this.statBaseValue = value;
if (errors.length){
this.baseValueErrors = errors;
} else {
delete this.baseValueErrors;
}
this.base = this.statBaseValue;
} else {
this.base = 0;
}

View File

@@ -20,6 +20,7 @@ export default function computeEffect(effect, memo){
applyToggles(effect, memo);
// Determine result of effect calculation
delete effect.errors;
if (!effect.calculation){
if(effect.operation === 'add' || effect.operation === 'base'){
effect.result = 0;
@@ -31,7 +32,11 @@ export default function computeEffect(effect, memo){
} else if(_.contains(['advantage', 'disadvantage', 'fail'], effect.operation)){
effect.result = 1;
} else {
effect.result = evaluateCalculation(effect.calculation, memo);
let {value, errors} = evaluateCalculation(effect.calculation, memo);
effect.result = value;
if (errors.length){
effect.errors = errors;
}
}
effect.computationDetails.computed = true;
effect.computationDetails.busyComputing = false;

View File

@@ -25,7 +25,13 @@ export default function computeToggle(toggle, memo){
} else if (Number.isFinite(+toggle.condition)){
toggle.toggleResult = !!+toggle.condition;
} else {
toggle.toggleResult = evaluateCalculation(toggle.condition, memo);
let {value, errors} = evaluateCalculation(toggle.condition, memo);
toggle.toggleResult = value;
if (errors.length){
toggle.errors = errors;
} else {
delete toggle.errors;
}
}
toggle.computationDetails.computed = true;
toggle.computationDetails.busyComputing = false;

View File

@@ -1,15 +1,20 @@
import bareSymbolSubtitutor from '/imports/api/creature/computation/utility/bareSymbolSubtitutor.js';
import computeStat from '/imports/api/creature/computation/computeStat.js';
import math from '/imports/math.js';
/* Convert a calculation into a constant output and errors*/
export default function evaluateCalculation(string, memo){
if (!string) return string;
let errors = [];
// Parse the string using mathjs
let calc;
try {
calc = math.parse(string);
} catch (e) {
return string;
errors.push({
type: 'parsing',
message: e.message || e
});
return {errors, value: string};
}
// Ensure all symbol nodes are defined and coputed
calc.traverse(node => {
@@ -20,12 +25,73 @@ export default function evaluateCalculation(string, memo){
}
}
});
// Ensure any bare symbols are value accessors instead
let substitutedCalc = calc.transform(bareSymbolSubtitutor(memo.statsByVariableName));
// Replace all symbols with their subtitution
let substitutedCalc = calc.transform(
symbolSubtitutor(memo.statsByVariableName, errors)
);
// Evaluate the expression to a number or return with substitutions
try {
return substitutedCalc.evaluate(memo.statsByVariableName);
let value = substitutedCalc.evaluate(memo.statsByVariableName);
return {errors, value};
} catch (e){
return substitutedCalc.toString();
errors.push({
type: 'evaluation',
message: e.message || e
});
let value = substitutedCalc.toString();
return {errors, value};
}
}
// returns a function to replace all symbols with either their resolved value
// or zero, keeping the errors
function symbolSubtitutor(scope, errors){
return function(node){
// mark symbol nodes that are children of function nodes to be skipped
if (node.isFunctionNode){
let fn = node.fn;
if (fn && fn.isSymbolNode){
fn.skipReplacement = true;
}
return node;
} else if (node.isSymbolNode && node.skipReplacement !== true){
//bare symbols of name "stat", should search for stat.value
let stat = scope[node.name];
if (stat){
if (stat.value === undefined){
errors.push({
type: 'subsitution',
message: `${node.name} does not have a value, set to 0`
});
return new math.ConstantNode(0);
} else {
return new math.ConstantNode(stat.value);
}
} else {
try {
return new math.ConstantNode(node.evaluate(scope));
} catch (e) {
errors.push({
type: 'subsitution',
message: `${node.name} not found, set to 0`
});
return new math.ConstantNode(0);
}
}
} else if (node.isAccessorNode){
try {
let value = node.evaluate(scope);
if (value === undefined) throw 'Not found';
return new math.ConstantNode(value);
} catch (e) {
errors.push({
type: 'subsitution',
message: `${node.toString()} not found, set to 0`
});
return new math.ConstantNode(0);
}
} else {
return node;
}
}
}