Improved error handling for most calculations

This commit is contained in:
Stefan Zermatten
2021-02-12 11:00:44 +02:00
parent fed87f0a84
commit 2b345c1f77
12 changed files with 80 additions and 201 deletions

View File

@@ -4,6 +4,6 @@ export default function embedInlineCalculations(string, calculations){
let index = 0;
return string.replace(/\{([^{}]*)\}/g, () => {
let comp = calculations && calculations[index++];
return comp && comp.result;
return comp && comp.result ? comp.result : string;
});
}

View File

@@ -18,11 +18,6 @@ export default function evaluateString(string, scope, fn = 'compile'){
errors.push(e);
return {result: string, errors};
}
// Parsing failed
if (node === null){
errors.push('...');
return {result: string, errors};
}
let context = new CompilationContext();
let result = node[fn](scope, context);
if (result instanceof ConstantNode){

View File

@@ -1,4 +1,5 @@
import evaluateCalculation from '/imports/api/creature/computation/engine/evaluateCalculation.js';
import ErrorNode from '/imports/parser/parseTree/ErrorNode.js';
import { union } from 'lodash';
export default function computeInlineCalculations(prop, memo){
@@ -21,9 +22,12 @@ function computeInlineCalcsForField(prop, memo, field){
context,
dependencies,
} = evaluateCalculation({string: calculation, prop, memo, fn: 'compile'});
if (result instanceof ErrorNode){
result = `<code>${result.toString()}</code>`;
}
let computation = {
calculation,
result: result.toString(),
result: result && result.toString(),
};
if (context.errors.length){
computation.errors = context.errors;

View File

@@ -1,8 +1,9 @@
import computeStat from '/imports/api/creature/computation/engine/computeStat.js';
import { parse, CompilationContext } from '/imports/parser/parser.js';
import { prettifyParseError, parse, CompilationContext } from '/imports/parser/parser.js';
import SymbolNode from '/imports/parser/parseTree/SymbolNode.js';
import AccessorNode from '/imports/parser/parseTree/AccessorNode.js';
import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
import ErrorNode from '/imports/parser/parseTree/ErrorNode.js';
import findAncestorByType from '/imports/api/creature/computation/engine/findAncestorByType.js';
import { union } from 'lodash';
@@ -14,10 +15,10 @@ export default function evaluateCalculation({
fn = 'reduce',
}){
let dependencies = [];
let errors = [];
let context = new CompilationContext();
if (!string) return {
context: {errors},
result: new ConstantNode({value: string, type: 'string'}),
context,
dependencies,
};
// Parse the string
@@ -25,34 +26,24 @@ export default function evaluateCalculation({
try {
calc = parse(string);
} catch (e) {
errors.push({
type: 'parsing',
message: e.message || e
});
let error = prettifyParseError(e);
return {
context: {errors},
result: new ConstantNode({value: string, type: 'string'}),
dependencies,
};
}
if (!calc){
return {
context: {errors},
result: new ConstantNode({value: calc, type: 'string'}),
result: new ErrorNode({context, error}),
context,
dependencies,
};
}
// Replace constants with their parsed constant
let replaceResults = replaceConstants({
calc, memo, prop, dependencies, errors
calc, memo, prop, dependencies, context
});
dependencies = replaceResults.dependencies;
calc = replaceResults.calc;
if (replaceResults.failed){
return {
context: {errors},
result: new ConstantNode({value: string, type: 'string'}),
context,
dependencies,
};
}
@@ -61,13 +52,12 @@ export default function evaluateCalculation({
dependencies = computeSymbols({calc, memo, prop, dependencies})
// Evaluate
let context = new CompilationContext();
let result = calc[fn](memo.statsByVariableName, context);
return {result, context, dependencies};
}
// Replace constants in the calc with the right ParseNodes
function replaceConstants({calc, memo, prop, dependencies, errors}){
function replaceConstants({calc, memo, prop, dependencies, context}){
let constFailed = [];
calc = calc.replaceNodes(node => {
if (!(node instanceof SymbolNode)) return;
@@ -101,16 +91,16 @@ function replaceConstants({calc, memo, prop, dependencies, errors}){
}
});
constFailed.forEach(name => {
errors.push({
context.storeError({
type: 'error',
message: `${name} is a constant property with parsing errors`
});
});
return {
failed: !!constFailed.length,
dependencies,
calc,
};
let failed = !!constFailed.length;
if (failed){
calc = new ErrorNode({error: 'Failed to replace constants'});
}
return { failed, dependencies, calc };
}
// Ensure all symbol nodes are defined and computed

View File

@@ -4,7 +4,11 @@ import LogContentSchema from '/imports/api/creature/log/LogContentSchema.js';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import {assertEditPermission} from '/imports/api/creature/creaturePermissions.js';
import { parse, CompilationContext } from '/imports/parser/parser.js';
import {
parse,
CompilationContext,
prettifyParseError
} from '/imports/parser/parser.js';
const PER_CREATURE_LOG_LIMIT = 100;
if (Meteor.isServer){
@@ -149,13 +153,15 @@ const logRoll = new ValidatedMethod({
avatarPicture: 1,
}});
assertEditPermission(creature, this.userId);
let parsedResult = parse(roll);
let logContent;
if (parsedResult === null) {
logContent = [{error: 'Unexpected end of input'}];
let logContent = []
let parsedResult = undefined;
try {
parsedResult = parse(roll);
} catch (e){
let error = prettifyParseError(e);
logContent.push({error});
}
else try {
logContent = [];
if (parsedResult) try {
let rollContext = new CompilationContext();
let compiled = parsedResult.compile(creature.variables, rollContext);
let compiledString = compiled.toString();