diff --git a/app/imports/parser/functions.js b/app/imports/parser/functions.js new file mode 100644 index 00000000..57378390 --- /dev/null +++ b/app/imports/parser/functions.js @@ -0,0 +1,83 @@ +export default { + 'abs': { + comment: 'Returns the absolute value of a number', + examples: [ + {input: 'abs(9)', result: '9'}, + {input: 'abs(-3)', result: '3'}, + ], + argumentType: 'number', + resultType: 'number', + fn: Math.abs, + }, + 'sqrt': { + comment: 'Returns the square root of a number', + examples: [ + {input: 'sqrt(16)', result: '4'}, + {input: 'sqrt(10)', result: '3.1622776601683795'}, + ], + argumentType: 'number', + resultType: 'number', + fn: Math.sqrt, + }, + 'max': { + comment: 'Returns the largest of the given numbers', + examples: [{input: 'min(12, 6, 3, 168)', result: '168'}], + argumentType: 'number', + resultType: 'number', + fn: Math.max, + }, + 'min': { + comment: 'Returns the smallest of the given numbers', + examples: [{input: 'min(12, 6, 3, 168)', result: '3'}], + argumentType: 'number', + resultType: 'number', + fn: Math.min, + }, + 'round': { + comment: 'Returns the value of a number rounded to the nearest integer', + examples: [ + {input: 'round(5.95)', result: '6'}, + {input: 'round(5.5)', result: '6'}, + {input: 'round(5.05)', result: '5'}, + ], + argumentType: 'number', + resultType: 'number', + fn: Math.round, + }, + 'floor': { + comment: 'Rounds a number down to the next smallest integer', + examples: [ + {input: 'floor(5.95)', result: '5'}, + {input: 'floor(5.05)', result: '5'}, + {input: 'floor(5)', result: '5'}, + {input: 'floor(-5.5)', result: '-6'}, + ], + argumentType: 'number', + resultType: 'number', + fn: Math.floor, + }, + 'ceil': { + comment: 'Rounds a number up to the next largest integer', + examples: [ + {input: 'ceil(5.95)', result: '6'}, + {input: 'ceil(5.05)', result: '6'}, + {input: 'ceil(5)', result: '5'}, + {input: 'ceil(-5.5)', result: '-5'}, + ], + argumentType: 'number', + resultType: 'number', + fn: Math.ceil, + }, + 'trunc': { + comment: 'Returns the integer part of a number by removing any fractional digits', + examples: [ + {input: 'trunc(5.95)', result: '5'}, + {input: 'trunc(5.05)', result: '5'}, + {input: 'trunc(5)', result: '5'}, + {input: 'trunc(-5.5)', result: '-5'}, + ], + argumentType: 'number', + resultType: 'number', + fn: Math.trunc, + }, +} diff --git a/app/imports/parser/parseTree/AccessorNode.js b/app/imports/parser/parseTree/AccessorNode.js index 2f594e50..f64eb793 100644 --- a/app/imports/parser/parseTree/AccessorNode.js +++ b/app/imports/parser/parseTree/AccessorNode.js @@ -27,9 +27,9 @@ export default class AccessorNode extends ParseNode { } } reduce(scope, context){ - let result = this.compile(scope); + let result = this.compile(scope, context); if (result instanceof AccessorNode){ - context.storeError({ + if (context) context.storeError({ type: 'info', message: `${result.toString()} not found, set to 0` }); diff --git a/app/imports/parser/parseTree/CallNode.js b/app/imports/parser/parseTree/CallNode.js index 31522dfa..3580950b 100644 --- a/app/imports/parser/parseTree/CallNode.js +++ b/app/imports/parser/parseTree/CallNode.js @@ -1,6 +1,7 @@ import ParseNode from '/imports/parser/parseTree/ParseNode.js'; import ErrorNode from '/imports/parser/parseTree/ErrorNode.js'; import ConstantNode from '/imports/parser/parseTree/ConstantNode.js'; +import functions from '/imports/parser/functions.js'; export default class CallNode extends ParseNode { constructor({functionName, args}) { @@ -15,7 +16,7 @@ export default class CallNode extends ParseNode { error: `${this.functionName} is not a function`, context, }); - let args = castArgsToType({fn, scope, args: this.args, type: func.argumentType}); + let args = castArgsToType({fn, scope, context, args: this.args, type: func.argumentType}); if (args.failed){ if (fn === 'reduce'){ return new ErrorNode({ @@ -51,17 +52,8 @@ export default class CallNode extends ParseNode { } } -const functions = { - 'min': { - comment: 'Returns the lowest of the given numbers', - argumentType: 'number', - resultType: 'number', - fn: Math.min, - }, -} - -function castArgsToType({fn, scope, args, type}){ - let resolvedArgs = args.map(node => node[fn](scope)) +function castArgsToType({fn, scope, context, args, type}){ + let resolvedArgs = args.map(node => node[fn](scope, context)) let result = []; if (type === 'number'){ resolvedArgs.forEach(node => { diff --git a/app/imports/parser/parseTree/IfNode.js b/app/imports/parser/parseTree/IfNode.js index d088bae7..0224fd0f 100644 --- a/app/imports/parser/parseTree/IfNode.js +++ b/app/imports/parser/parseTree/IfNode.js @@ -12,15 +12,15 @@ export default class IfNode extends ParseNode { let {condition, consequent, alternative} = this; return `${condition.toString()} ? ${consequent.toString()} : ${alternative.toString()}` } - resolve(fn, scope){ - let condition = this.condition[fn](scope); + resolve(fn, scope, context){ + let condition = this.condition[fn](scope, context); if (condition instanceof ConstantNode){ if (condition.value){ - let consequent = this.consequent[fn](scope); + let consequent = this.consequent[fn](scope, context); consequent.inheritDetails([condition, this]); return this.consequent[fn](scope); } else { - let alternative = this.alternative[fn](scope); + let alternative = this.alternative[fn](scope, context); alternative.inheritDetails([condition, this]); return alternative; } diff --git a/app/imports/parser/parseTree/IndexNode.js b/app/imports/parser/parseTree/IndexNode.js index 4359ae56..592bf8d0 100644 --- a/app/imports/parser/parseTree/IndexNode.js +++ b/app/imports/parser/parseTree/IndexNode.js @@ -6,19 +6,19 @@ export default class IndexNode extends ParseNode { this.array = array; this.index = index; } - resolve(fn, scope){ - let index = this.index[fn](scope); + resolve(fn, scope, context){ + let index = this.index[fn](scope, context); if (index.isInteger){ let selection = this.array.values[index.value - 1]; if (selection){ - let result = selection[fn](scope); + let result = selection[fn](scope, context); result.inheritDetails([index, this]); return result; } } return new IndexNode({ - array: this.array[fn](scope), - index: this.index[fn](scope), + array: this.array[fn](scope, context), + index: this.index[fn](scope, context), previousNodes: [this], }); } diff --git a/app/imports/parser/parseTree/OperatorNode.js b/app/imports/parser/parseTree/OperatorNode.js index 14935a0f..5fee48c4 100644 --- a/app/imports/parser/parseTree/OperatorNode.js +++ b/app/imports/parser/parseTree/OperatorNode.js @@ -9,9 +9,9 @@ export default class OperatorNode extends ParseNode { this.fn = fn; this.operator = operator; } - resolve(fn, scope){ - let leftNode = this.left[fn](scope); - let rightNode = this.right[fn](scope); + resolve(fn, scope, context){ + let leftNode = this.left[fn](scope, context); + let rightNode = this.right[fn](scope, context); let left, right; if (leftNode.type !== 'number' || rightNode.type !== 'number'){ return new OperatorNode({ diff --git a/app/imports/parser/parseTree/ParenthesisNode.js b/app/imports/parser/parseTree/ParenthesisNode.js index afb8acac..facca06b 100644 --- a/app/imports/parser/parseTree/ParenthesisNode.js +++ b/app/imports/parser/parseTree/ParenthesisNode.js @@ -5,8 +5,8 @@ export default class ParenthesisNode extends ParseNode { super(...arguments); this.content = content; } - resolve(fn, scope){ - let content = this.content[fn](scope); + resolve(fn, scope, context){ + let content = this.content[fn](scope, context); if ( content.constructor.name === 'IfNode' || content.constructor.name === 'OperatorNode' diff --git a/app/imports/parser/parseTree/RollArrayNode.js b/app/imports/parser/parseTree/RollArrayNode.js index fe21abd3..978c20d6 100644 --- a/app/imports/parser/parseTree/RollArrayNode.js +++ b/app/imports/parser/parseTree/RollArrayNode.js @@ -17,7 +17,6 @@ export default class RollArrayNode extends ParseNode { return new ConstantNode({ value: total, type: 'number', - previousNodes: [this], }); } } diff --git a/app/imports/parser/parseTree/RollNode.js b/app/imports/parser/parseTree/RollNode.js index 070ac165..d57bd734 100644 --- a/app/imports/parser/parseTree/RollNode.js +++ b/app/imports/parser/parseTree/RollNode.js @@ -8,9 +8,9 @@ export default class RollNode extends ParseNode { this.left = left; this.right = right; } - compile(scope){ - let left = this.left.compile(scope); - let right = this.right.compile(scope); + compile(scope, context){ + let left = this.left.compile(scope, context); + let right = this.right.compile(scope, context); return new RollNode({left, right, previousNodes: [this]}); } toString(){ @@ -23,8 +23,8 @@ export default class RollNode extends ParseNode { } } roll(scope, context){ - let left = this.left.reduce(scope); - let right = this.right.reduce(scope); + let left = this.left.reduce(scope, context); + let right = this.right.reduce(scope, context); if (!left.isInteger){ return new ErrorNode({ node: this, @@ -56,7 +56,7 @@ export default class RollNode extends ParseNode { } return new RollArrayNode({values}); } - reduce(scope){ - return this.roll(scope).reduce(scope); + reduce(scope, context){ + return this.roll(scope, context).reduce(scope, context); } } diff --git a/app/imports/parser/parseTree/SymbolNode.js b/app/imports/parser/parseTree/SymbolNode.js index 73becc6f..b65a5523 100644 --- a/app/imports/parser/parseTree/SymbolNode.js +++ b/app/imports/parser/parseTree/SymbolNode.js @@ -1,6 +1,5 @@ import ParseNode from '/imports/parser/parseTree/ParseNode.js'; import ConstantNode from '/imports/parser/parseTree/ConstantNode.js'; -import ErrorNode from '/imports/parser/parseTree/ErrorNode.js'; export default class SymbolNode extends ParseNode { constructor({name}){ @@ -19,23 +18,25 @@ export default class SymbolNode extends ParseNode { type = typeof value; } if (type === 'string' || type === 'number' || type === 'boolean'){ - return new ConstantNode({value, type, previousNodes: [this]}); + return new ConstantNode({value, type}); } else if (type === 'undefined'){ return new SymbolNode({ name: this.name, - previousNodes: [this], }); } else { throw new Meteor.Error(`Unexpected case: ${this.name} resolved to ${value}`); } } - reduce(scope){ + reduce(scope, context){ let result = this.compile(scope); if (result instanceof SymbolNode){ - return new ErrorNode({ - node: result, - error: `${this.toString()} could not be resolved`, - previousNodes: [result], + if (context) context.storeError({ + type: 'info', + message: `${result.toString()} not found, set to 0` + }); + return new ConstantNode({ + type: 'number', + value: 0, }); } else { return result; diff --git a/app/imports/ui/documentation/FunctionReference.vue b/app/imports/ui/documentation/FunctionReference.vue new file mode 100644 index 00000000..202fa60b --- /dev/null +++ b/app/imports/ui/documentation/FunctionReference.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/app/imports/ui/pages/Parser.vue b/app/imports/ui/pages/Parser.vue index cb3ddd92..74a73945 100644 --- a/app/imports/ui/pages/Parser.vue +++ b/app/imports/ui/pages/Parser.vue @@ -2,7 +2,16 @@
- + + + refresh + + +