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, prettifyParseError, } from '/imports/parser/parser.js'; import AccessorNode from '/imports/parser/parseTree/AccessorNode.js'; import SymbolNode from '/imports/parser/parseTree/SymbolNode.js'; import STORAGE_LIMITS from '/imports/constants/STORAGE_LIMITS.js'; /* * Constants are primitive values that can be used elsewhere in computations */ let ConstantSchema = new SimpleSchema({ name: { type: String, optional: true, max: STORAGE_LIMITS.name, }, // The technical, lowercase, single-word name used in formulae variableName: { type: String, regEx: VARIABLE_NAME_REGEX, min: 2, defaultValue: 'newConstant', max: STORAGE_LIMITS.variableName, }, // The input value to be parsed, must return a constant node or an array // of constant nodes to be valid calculation: { type: String, optional: true, max: STORAGE_LIMITS.calculation, }, errors: { type: Array, maxCount: STORAGE_LIMITS.errorCount, autoValue(){ let calc = this.field('calculation'); if (!calc.isSet && this.isModifier) { this.unset() return; } let string = calc.value; if (!string) return []; // Evaluate the calculation with no scope let {result, context} = parseString(string); // Any existing errors will result in an early failure if (context && context.errors.length) return context.errors; // Ban variables in constants if necessary result && result.traverse(node => { if (node instanceof SymbolNode || node instanceof AccessorNode){ context.storeError({ type: 'error', message: 'Variables can\'t be used to define a constant' }); } }); return context && context.errors || []; } }, 'errors.$':{ type: ErrorSchema, }, }); function parseString(string, fn = 'compile'){ let context = new CompilationContext(); if (!string){ return {result: string, context}; } // Parse the string using mathjs let node; try { node = parse(string); } catch (e) { let message = prettifyParseError(e); context.storeError({type: 'error', message}); return {context}; } let result = node[fn]({/*empty scope*/}, context); return {result, context} } export { ConstantSchema };