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 @@
+
+ {{ fn.name }}
+
+
+
+
+
+
+ {{ example.input }}
+
+
+
+
+ {{ example.result }}
+