Improved error handling for most calculations
This commit is contained in:
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import VARIABLE_NAME_REGEX from '/imports/constants/VARIABLE_NAME_REGEX.js';
|
||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||
import { parse, CompilationContext } from '/imports/parser/parser.js';
|
||||
import {
|
||||
parse,
|
||||
CompilationContext,
|
||||
prettifyParseError,
|
||||
} from '/imports/parser/parser.js';
|
||||
import AccessorNode from '/imports/parser/parseTree/AccessorNode.js';
|
||||
import SymbolNode from '/imports/parser/parseTree/SymbolNode.js';
|
||||
/*
|
||||
@@ -35,19 +39,19 @@ let ConstantSchema = new SimpleSchema({
|
||||
}
|
||||
let string = calc.value;
|
||||
// Evaluate the calculation with no scope
|
||||
let {result, errors} = parseString(string);
|
||||
// Any errors will result in a failure
|
||||
if (errors.length) return errors;
|
||||
let {result, context} = parseString(string);
|
||||
// Any existing errors will result in an early failure
|
||||
if (context.errors.length) return context.errors;
|
||||
// Ban variables in constants if necessary
|
||||
result && result.traverse(node => {
|
||||
if (node instanceof SymbolNode || node instanceof AccessorNode){
|
||||
errors.push({
|
||||
context.storeError()({
|
||||
type: 'error',
|
||||
message: 'Variables can\'t be used to define a constant'
|
||||
});
|
||||
}
|
||||
});
|
||||
return errors;
|
||||
return context.errors;
|
||||
}
|
||||
},
|
||||
'errors.$':{
|
||||
@@ -56,9 +60,9 @@ let ConstantSchema = new SimpleSchema({
|
||||
});
|
||||
|
||||
function parseString(string, fn = 'compile'){
|
||||
let errors = [];
|
||||
let context = new CompilationContext();
|
||||
if (!string){
|
||||
return {result: string, errors};
|
||||
return {result: string, errors: []};
|
||||
}
|
||||
|
||||
// Parse the string using mathjs
|
||||
@@ -66,18 +70,12 @@ function parseString(string, fn = 'compile'){
|
||||
try {
|
||||
node = parse(string);
|
||||
} catch (e) {
|
||||
let message = e.toString().split('.')[0];
|
||||
errors.push({type: 'error', message});
|
||||
return {result: string, errors};
|
||||
let message = prettifyParseError(e);
|
||||
context.storeError({type: 'error', message});
|
||||
return {result: string, errors: context.errors};
|
||||
}
|
||||
// Parsing incomplete
|
||||
if (node === null){
|
||||
errors.push({type: 'warning', message: 'Unexpected end of input'});
|
||||
return {result: string, errors};
|
||||
}
|
||||
let context = new CompilationContext();
|
||||
let result = node[fn]({/*empty scope*/}, context);
|
||||
return {result, errors: context.errors}
|
||||
return {result, context}
|
||||
}
|
||||
|
||||
export { ConstantSchema };
|
||||
|
||||
Reference in New Issue
Block a user