diff --git a/app/imports/parser/grammar.js b/app/imports/parser/grammar.js
index 7ab4c9ed..c158d3ea 100644
--- a/app/imports/parser/grammar.js
+++ b/app/imports/parser/grammar.js
@@ -2,11 +2,16 @@
// http://github.com/Hardmath123/nearley
function id(x) { return x[0]; }
+ import ArrayNode from '/imports/parser/parseTree/ArrayNode.js';
import CallNode from '/imports/parser/parseTree/CallNode.js';
import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
- import IfNode from '/imports/parser/parseTree/IfNode.js';
+ import IfNode from '/imports/parser/parseTree/IfNode.js';
+ import IndexNode from '/imports/parser/parseTree/IndexNode.js';
import OperatorNode from '/imports/parser/parseTree/OperatorNode.js';
- import SymbolNode from '/imports/parser/parseTree/SymbolNode.js';
+ import ParenthesisNode from '/imports/parser/parseTree/ParenthesisNode.js';
+ import RollNode from '/imports/parser/parseTree/RollNode.js';
+ import SymbolNode from '/imports/parser/parseTree/SymbolNode.js';
+
import moo from 'moo';
const lexer = moo.compile({
@@ -25,7 +30,8 @@ function id(x) { return x[0]; }
match: /\s+/,
lineBreaks: true,
},
- separators: [',', '.'],
+ separator: [',', ';'],
+ period: ['.'],
multiplicativeOperator: ['*', '/'],
exponentOperator: ['^'],
additiveOperator: ['+', '-'],
@@ -35,7 +41,7 @@ function id(x) { return x[0]; }
stringDelimiters: ['\"', '\''],
equalityOperator: ['=', '==', '===', '!=', '!=='],
relationalOperator: ['>', '<', '>=', '<='],
- brackets: ['(', ')', '{', '}'],
+ brackets: ['(', ')', '{', '}', '[', ']'],
});
function nuller() { return null; }
@@ -49,11 +55,11 @@ function id(x) { return x[0]; }
}
let Lexer = lexer;
let ParserRules = [
- {"name": "ifStatement", "symbols": [{"literal":"if"}, "_", {"literal":"("}, "_", "expression", "_", {"literal":")"}, "_", "ifStatement", "_", {"literal":"else"}, "_", "ifStatement"], "postprocess":
+ {"name": "expression", "symbols": ["ifStatement"], "postprocess": d => d[0]},
+ {"name": "ifStatement", "symbols": ["_", "expression", "_", {"literal":"?"}, "_", "expression", "_", {"literal":":"}, "_", "expression"], "postprocess":
d => new IfNode({condition: d[4], consequent: d[8], alternative: d[12]})
},
- {"name": "ifStatement", "symbols": ["expression"], "postprocess": id},
- {"name": "expression", "symbols": ["equalityExpression"], "postprocess": d => d[0]},
+ {"name": "ifStatement", "symbols": ["equalityExpression"], "postprocess": id},
{"name": "equalityExpression", "symbols": ["equalityExpression", "_", (lexer.has("equalityOperator") ? {type: "equalityOperator"} : equalityOperator), "_", "relationalExpression"], "postprocess": d => operator(d, 'equality')},
{"name": "equalityExpression", "symbols": ["relationalExpression"], "postprocess": id},
{"name": "relationalExpression", "symbols": ["relationalExpression", "_", (lexer.has("relationalOperator") ? {type: "relationalOperator"} : relationalOperator), "_", "orExpression"], "postprocess": d => operator(d, 'relation')},
@@ -66,33 +72,47 @@ let ParserRules = [
{"name": "additiveExpression", "symbols": ["multiplicativeExpression"], "postprocess": id},
{"name": "multiplicativeExpression", "symbols": ["multiplicativeExpression", "_", (lexer.has("multiplicativeOperator") ? {type: "multiplicativeOperator"} : multiplicativeOperator), "_", "rollExpression"], "postprocess": d => operator(d, 'multiply')},
{"name": "multiplicativeExpression", "symbols": ["rollExpression"], "postprocess": id},
- {"name": "rollExpression", "symbols": ["rollExpression", "_", {"literal":"d"}, "_", "exponentExpression"], "postprocess": d => operator(d, 'roll')},
- {"name": "rollExpression", "symbols": ["exponentExpression"], "postprocess": id},
+ {"name": "rollExpression", "symbols": ["rollExpression", "_", {"literal":"d"}, "_", "exponentExpression"], "postprocess": d => new RollNode({left: d[0], right: d[4]})},
+ {"name": "rollExpression", "symbols": ["singleRollExpression"], "postprocess": id},
+ {"name": "singleRollExpression", "symbols": [{"literal":"d"}, "_", "exponentExpression"], "postprocess": d => new RollNode({left: new ConstantNode({value: 1, type: 'number'}), right: d[2]})},
+ {"name": "singleRollExpression", "symbols": ["exponentExpression"], "postprocess": id},
{"name": "exponentExpression", "symbols": ["callExpression", "_", (lexer.has("exponentOperator") ? {type: "exponentOperator"} : exponentOperator), "_", "exponentExpression"], "postprocess": d => operator(d, 'exponent')},
{"name": "exponentExpression", "symbols": ["callExpression"], "postprocess": id},
{"name": "callExpression", "symbols": ["name", "_", "arguments"], "postprocess":
d => new CallNode ({type: "call", fn: d[0], arguments: d[2]})
},
- {"name": "callExpression", "symbols": ["parenthesizedExpression"], "postprocess": id},
+ {"name": "callExpression", "symbols": ["indexExpression"], "postprocess": id},
{"name": "arguments$ebnf$1$subexpression$1", "symbols": ["expression"], "postprocess": d => d[0]},
{"name": "arguments$ebnf$1", "symbols": ["arguments$ebnf$1$subexpression$1"], "postprocess": id},
{"name": "arguments$ebnf$1", "symbols": [], "postprocess": function(d) {return null;}},
{"name": "arguments$ebnf$2", "symbols": []},
- {"name": "arguments$ebnf$2$subexpression$1", "symbols": ["_", {"literal":","}, "_", "expression"], "postprocess": d => d[3]},
+ {"name": "arguments$ebnf$2$subexpression$1", "symbols": ["_", (lexer.has("separator") ? {type: "separator"} : separator), "_", "expression"], "postprocess": d => d[3]},
{"name": "arguments$ebnf$2", "symbols": ["arguments$ebnf$2", "arguments$ebnf$2$subexpression$1"], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}},
{"name": "arguments", "symbols": [{"literal":"("}, "_", "arguments$ebnf$1", "arguments$ebnf$2", "_", {"literal":")"}], "postprocess":
d => [d[2], ...d[3]]
+ },
+ {"name": "indexExpression", "symbols": ["arrayExpression", {"literal":"["}, "_", "expression", "_", {"literal":"]"}], "postprocess": d => new IndexNode ({array: d[0], index: d[3]})},
+ {"name": "indexExpression", "symbols": ["arrayExpression"], "postprocess": id},
+ {"name": "arrayExpression$ebnf$1$subexpression$1", "symbols": ["expression"], "postprocess": d => d[0]},
+ {"name": "arrayExpression$ebnf$1", "symbols": ["arrayExpression$ebnf$1$subexpression$1"], "postprocess": id},
+ {"name": "arrayExpression$ebnf$1", "symbols": [], "postprocess": function(d) {return null;}},
+ {"name": "arrayExpression$ebnf$2", "symbols": []},
+ {"name": "arrayExpression$ebnf$2$subexpression$1", "symbols": ["_", (lexer.has("separator") ? {type: "separator"} : separator), "_", "expression"], "postprocess": d => d[3]},
+ {"name": "arrayExpression$ebnf$2", "symbols": ["arrayExpression$ebnf$2", "arrayExpression$ebnf$2$subexpression$1"], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}},
+ {"name": "arrayExpression", "symbols": [{"literal":"["}, "_", "arrayExpression$ebnf$1", "arrayExpression$ebnf$2", "_", {"literal":"]"}], "postprocess":
+ d => new ArrayNode({values: [d[2], ...d[3]]})
},
- {"name": "parenthesizedExpression", "symbols": [{"literal":"("}, "_", "expression", "_", {"literal":")"}], "postprocess": d => d[2]},
+ {"name": "arrayExpression", "symbols": ["parenthesizedExpression"], "postprocess": id},
+ {"name": "parenthesizedExpression", "symbols": [{"literal":"("}, "_", "expression", "_", {"literal":")"}], "postprocess": d => new ParenthesisNode({content: d[2]})},
{"name": "parenthesizedExpression", "symbols": ["valueExpression"], "postprocess": id},
{"name": "valueExpression", "symbols": ["name"], "postprocess": id},
{"name": "valueExpression", "symbols": ["number"], "postprocess": id},
{"name": "valueExpression", "symbols": ["string"], "postprocess": id},
- {"name": "number", "symbols": [(lexer.has("number") ? {type: "number"} : number)], "postprocess": d => new ConstantNode({value: d[0].value, type: 'number'})},
+ {"name": "number", "symbols": [(lexer.has("number") ? {type: "number"} : number)], "postprocess": d => new ConstantNode({value: +d[0].value, type: 'number'})},
{"name": "name", "symbols": [(lexer.has("name") ? {type: "name"} : name)], "postprocess": d => new SymbolNode({name: d[0].value})},
{"name": "string", "symbols": [(lexer.has("string") ? {type: "string"} : string)], "postprocess": d => new ConstantNode({value: d[0].value, type: 'string'})},
{"name": "_", "symbols": []},
{"name": "_", "symbols": [(lexer.has("space") ? {type: "space"} : space)], "postprocess": nuller}
];
-let ParserStart = "ifStatement";
+let ParserStart = "expression";
export default { Lexer, ParserRules, ParserStart };
diff --git a/app/imports/parser/grammar.ne b/app/imports/parser/grammar.ne
index 22563eb0..3f6c7fc6 100644
--- a/app/imports/parser/grammar.ne
+++ b/app/imports/parser/grammar.ne
@@ -1,10 +1,15 @@
@preprocessor esmodule
@{%
+ import ArrayNode from '/imports/parser/parseTree/ArrayNode.js';
import CallNode from '/imports/parser/parseTree/CallNode.js';
import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
- import IfNode from '/imports/parser/parseTree/IfNode.js';
+ import IfNode from '/imports/parser/parseTree/IfNode.js';
+ import IndexNode from '/imports/parser/parseTree/IndexNode.js';
import OperatorNode from '/imports/parser/parseTree/OperatorNode.js';
- import SymbolNode from '/imports/parser/parseTree/SymbolNode.js';
+ import ParenthesisNode from '/imports/parser/parseTree/ParenthesisNode.js';
+ import RollNode from '/imports/parser/parseTree/RollNode.js';
+ import SymbolNode from '/imports/parser/parseTree/SymbolNode.js';
+
import moo from 'moo';
const lexer = moo.compile({
@@ -23,7 +28,8 @@
match: /\s+/,
lineBreaks: true,
},
- separators: [',', '.'],
+ separator: [',', ';'],
+ period: ['.'],
multiplicativeOperator: ['*', '/'],
exponentOperator: ['^'],
additiveOperator: ['+', '-'],
@@ -33,7 +39,7 @@
stringDelimiters: ['\"', '\''],
equalityOperator: ['=', '==', '===', '!=', '!=='],
relationalOperator: ['>', '<', '>=', '<='],
- brackets: ['(', ')', '{', '}'],
+ brackets: ['(', ')', '{', '}', '[', ']'],
});
function nuller() { return null; }
@@ -50,14 +56,14 @@
# Use the Moo lexer
@lexer lexer
+expression ->
+ ifStatement {% d => d[0] %}
+
ifStatement ->
- "if" _ "(" _ expression _ ")" _ ifStatement _ "else" _ ifStatement {%
+ _ expression _ "?" _ expression _ ":" _ expression {%
d => new IfNode({condition: d[4], consequent: d[8], alternative: d[12]})
%}
-| expression {% id %}
-
-expression ->
- equalityExpression {% d => d[0] %}
+| equalityExpression {% id %}
equalityExpression ->
equalityExpression _ %equalityOperator _ relationalExpression {% d => operator(d, 'equality') %}
@@ -84,7 +90,11 @@ multiplicativeExpression ->
| rollExpression {% id %}
rollExpression ->
- rollExpression _ "d" _ exponentExpression {% d => operator(d, 'roll') %}
+ rollExpression _ "d" _ exponentExpression {% d => new RollNode({left: d[0], right: d[4]}) %}
+| singleRollExpression {% id %}
+
+singleRollExpression ->
+ "d" _ exponentExpression {% d => new RollNode({left: new ConstantNode({value: 1, type: 'number'}), right: d[2]}) %}
| exponentExpression {% id %}
exponentExpression ->
@@ -95,15 +105,25 @@ callExpression ->
name _ arguments {%
d => new CallNode ({type: "call", fn: d[0], arguments: d[2]})
%}
-| parenthesizedExpression {% id %}
+| indexExpression {% id %}
arguments ->
- "(" _ (expression {% d => d[0] %}):? ( _ "," _ expression {% d => d[3] %} ):* _ ")" {%
- d => [d[2], ...d[3]]
+"(" _ (expression {% d => d[0] %}):? ( _ %separator _ expression {% d => d[3] %} ):* _ ")" {%
+ d => [d[2], ...d[3]]
%}
+indexExpression ->
+ arrayExpression "[" _ expression _ "]" {% d => new IndexNode ({array: d[0], index: d[3]}) %}
+| arrayExpression {% id %}
+
+arrayExpression ->
+ "[" _ (expression {% d => d[0] %}):? ( _ %separator _ expression {% d => d[3] %} ):* _ "]" {%
+ d => new ArrayNode({values: [d[2], ...d[3]]})
+ %}
+| parenthesizedExpression {% id %}
+
parenthesizedExpression ->
- "(" _ expression _ ")" {% d => d[2] %}
+ "(" _ expression _ ")" {% d => new ParenthesisNode({content: d[2]}) %}
| valueExpression {% id %}
valueExpression ->
@@ -113,7 +133,7 @@ valueExpression ->
# A number or a function of a number
number ->
- %number {% d => new ConstantNode({value: d[0].value, type: 'number'}) %}
+ %number {% d => new ConstantNode({value: +d[0].value, type: 'number'}) %}
name ->
%name {% d => new SymbolNode({name: d[0].value}) %}
diff --git a/app/imports/parser/parseTree/ArrayNode.js b/app/imports/parser/parseTree/ArrayNode.js
new file mode 100644
index 00000000..43f592ad
--- /dev/null
+++ b/app/imports/parser/parseTree/ArrayNode.js
@@ -0,0 +1,15 @@
+import ParseNode from '/imports/parser/parseTree/ParseNode.js';
+
+export default class ArrayNode extends ParseNode {
+ constructor({values}) {
+ super();
+ this.values = values;
+ }
+ compile(){
+ let values = this.values.map(node => node.compile());
+ return new ArrayNode({values});
+ }
+ toString(){
+ return `[${this.values.map(node => node.toString()).join(', ')}]`;
+ }
+}
diff --git a/app/imports/parser/parseTree/ConstantNode.js b/app/imports/parser/parseTree/ConstantNode.js
index caa910f0..4d3198fd 100644
--- a/app/imports/parser/parseTree/ConstantNode.js
+++ b/app/imports/parser/parseTree/ConstantNode.js
@@ -11,11 +11,13 @@ export default class ConstantNode extends ParseNode {
compile(){
return this;
}
- reduce(){
- if (this.type === 'numberArray'){
- return this.value.reduce((total, num) => total + num, 0);
- } else {
- return this;
- }
+ toString(){
+ return `${this.value}`;
+ }
+ get isNumber(){
+ return this.type === 'number';
+ }
+ get isInteger(){
+ return this.isNumberNode && Number.isInteger(this.value);
}
}
diff --git a/app/imports/parser/parseTree/ErrorNode.js b/app/imports/parser/parseTree/ErrorNode.js
new file mode 100644
index 00000000..bbef94c3
--- /dev/null
+++ b/app/imports/parser/parseTree/ErrorNode.js
@@ -0,0 +1,15 @@
+import ParseNode from '/imports/parser/parseTree/ParseNode.js';
+
+export default class ErrorNode extends ParseNode {
+ constructor({node, error}) {
+ super();
+ this.node = node;
+ this.error = error;
+ }
+ compile(){
+ return this;
+ }
+ toString(){
+ return '###';
+ }
+}
diff --git a/app/imports/parser/parseTree/IfNode.js b/app/imports/parser/parseTree/IfNode.js
index 29bca520..a801e4bd 100644
--- a/app/imports/parser/parseTree/IfNode.js
+++ b/app/imports/parser/parseTree/IfNode.js
@@ -8,6 +8,10 @@ export default class IfNode extends ParseNode {
this.consequent = consequent;
this.alternative = alternative;
}
+ toString(){
+ let {condition, consequent, alternative} = this;
+ return `${condition.toString()} ? ${consequent.toString()} : ${alternative.toString()}`
+ }
compile(){
let condition = this.condition.compile();
let consequent = this.consequent.compile();
@@ -19,7 +23,7 @@ export default class IfNode extends ParseNode {
){
// Handle unresolved condition
return new ConstantNode({
- value: `if (${condition.value}) ${consequent.value} else ${alternative.value}`,
+ value: `${condition.value}) ${consequent.value} else ${alternative.value}`,
type: 'uncompiledNode',
errors: [
...condition.errors,
diff --git a/app/imports/parser/parseTree/IndexNode.js b/app/imports/parser/parseTree/IndexNode.js
new file mode 100644
index 00000000..158da8c6
--- /dev/null
+++ b/app/imports/parser/parseTree/IndexNode.js
@@ -0,0 +1,25 @@
+import ParseNode from '/imports/parser/parseTree/ParseNode.js';
+
+export default class IndexNode extends ParseNode {
+ constructor({array, index}) {
+ super();
+ this.array = array;
+ this.index = index;
+ }
+ compile(){
+ let index = this.index.compile();
+ if (index.constructor.name === 'ConstantNode' && index.type === 'number'){
+ let selection = this.array.values[index.value];
+ if (selection){
+ return selection.compile();
+ }
+ }
+ return new IndexNode({
+ array: this.array.compile(),
+ index: this.index.compile(),
+ });
+ }
+ toString(){
+ return `${this.array.toString()}[${this.index.toString()}]`;
+ }
+}
diff --git a/app/imports/parser/parseTree/OperatorNode.js b/app/imports/parser/parseTree/OperatorNode.js
index cd4d9146..fa75f315 100644
--- a/app/imports/parser/parseTree/OperatorNode.js
+++ b/app/imports/parser/parseTree/OperatorNode.js
@@ -1,4 +1,5 @@
import ParseNode from '/imports/parser/parseTree/ParseNode.js';
+import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
export default class OperatorNode extends ParseNode {
constructor({left, right, operator, fn}) {
@@ -8,4 +9,45 @@ export default class OperatorNode extends ParseNode {
this.fn = fn;
this.operator = operator;
}
+ compile(){
+ let leftNode = this.left.compile();
+ let rightNode = this.right.compile();
+ let left, right;
+ if (leftNode.type !== 'number' || rightNode.type !== 'number'){
+ return new OperatorNode({
+ left: leftNode,
+ right: rightNode,
+ operator: this.operator,
+ fn: this.fn
+ });
+ } else {
+ left = leftNode.value;
+ right = rightNode.value;
+ }
+ let result;
+ switch(this.operator){
+ case '+': result = left + right; break;
+ case '-': result = left - right; break;
+ case '*': result = left * right; break;
+ case '/': result = left / right; break;
+ case '^': result = Math.pow(left, right); break;
+ case '&':
+ case '&&': result = left && right; break;
+ case '|':
+ case '||': result = left || right; break;
+ case '=':
+ case '==':
+ case '===': result = left == right; break;
+ case '!=':
+ case '>': result = left > right; break;
+ case '<': result = left < right; break;
+ case '>=': result = left >= right; break;
+ case '<=': result = left <= right; break;
+ }
+ return new ConstantNode({value: result, type: typeof result});
+ }
+ toString(){
+ let {left, right, operator} = this;
+ return `${left.toString()} ${operator} ${right.toString()}`;
+ }
}
diff --git a/app/imports/parser/parseTree/ParenthesisNode.js b/app/imports/parser/parseTree/ParenthesisNode.js
new file mode 100644
index 00000000..22da9c4b
--- /dev/null
+++ b/app/imports/parser/parseTree/ParenthesisNode.js
@@ -0,0 +1,19 @@
+import ParseNode from '/imports/parser/parseTree/ParseNode.js';
+
+export default class ParenthesisNode extends ParseNode {
+ constructor({content}) {
+ super();
+ this.content = content;
+ }
+ compile(){
+ let content = this.content.compile();
+ if (content.constructor.name === 'ConstantNode'){
+ return content;
+ } else {
+ return new ParenthesisNode({content});
+ }
+ }
+ toString(){
+ return `(${this.content.toString()})`;
+ }
+}
diff --git a/app/imports/parser/parseTree/ParseNode.js b/app/imports/parser/parseTree/ParseNode.js
index 0e213587..0d8686d3 100644
--- a/app/imports/parser/parseTree/ParseNode.js
+++ b/app/imports/parser/parseTree/ParseNode.js
@@ -1,7 +1,10 @@
export default class ParseNode {
- // Compiling a node must return a ConstantNode
compile(){
- throw new Meteor.Error('Compile not implemented on ' + this);
+ // Returns a ParseNode, a ConstantNode if possible
+ throw new Meteor.Error('Compile not implemented on ' + this.constructor.name);
+ }
+ toString(){
+ throw new Meteor.Error('toString not implemented on ' + this.constructor.name);
}
// Compile, but turn rolls into arrays
roll(){
@@ -9,6 +12,6 @@ export default class ParseNode {
}
// Compile, turn rolls into arrays, and reduce those arrays into single values
reduce(){
- return this.compileAndRoll()
+ return this.roll();
}
}
diff --git a/app/imports/parser/parseTree/RollArrayNode.js b/app/imports/parser/parseTree/RollArrayNode.js
new file mode 100644
index 00000000..d8d60236
--- /dev/null
+++ b/app/imports/parser/parseTree/RollArrayNode.js
@@ -0,0 +1,17 @@
+import ParseNode from '/imports/parser/parseTree/ParseNode.js';
+
+export default class RollArrayNode extends ParseNode {
+ constructor({values}) {
+ super();
+ this.values = values;
+ }
+ compile(){
+ return this;
+ }
+ toString(){
+ return `[${this.values.join(', ')}]`;
+ }
+ reduce(){
+ //TODO sum and return values
+ }
+}
diff --git a/app/imports/parser/parseTree/RollNode.js b/app/imports/parser/parseTree/RollNode.js
index e69de29b..d2f99379 100644
--- a/app/imports/parser/parseTree/RollNode.js
+++ b/app/imports/parser/parseTree/RollNode.js
@@ -0,0 +1,57 @@
+import ParseNode from '/imports/parser/parseTree/ParseNode.js';
+import ArrayNode from '/imports/parser/parseTree/ArrayNode.js';
+import ErrorNode from '/imports/parser/parseTree/ErrorNode.js';
+
+export default class RollNode extends ParseNode {
+ constructor({left, right}) {
+ super();
+ this.left = left;
+ this.right = right;
+ }
+ compile(){
+ let left = this.left.compile();
+ let right = this.right.compile();
+ return new RollNode({left, right});
+ }
+ toString(){
+ if (
+ this.left.isNumberNode && this.left.value === 1
+ ){
+ return `d${this.right.toString()}`;
+ } else {
+ return `${this.left.toString()}d${this.right.toString()}`;
+ }
+ }
+ roll(){
+ let left = this.left.reduce();
+ let right = this.right.reduce();
+ if (!left.isInteger){
+ return new ErrorNode({
+ node: this,
+ error: 'Number of dice is not an integer'
+ });
+ }
+ if (!right.isInteger){
+ return new ErrorNode({
+ node: this,
+ error: 'Dice size is not an integer'
+ });
+ }
+ let number = left.value;
+ if (number > 100) return new ErrorNode({
+ node: this,
+ error: 'Can\'t roll more than 100 dice at once'
+ });
+ let diceSize = right.value;
+ let randomSrc = DDP.randomStream('diceRoller');
+ let rolls = [];
+ for (let i = 0; i < number; i++){
+ let roll = ~~(randomSrc.fraction() * diceSize) + 1
+ rolls.push(roll);
+ }
+ return new ArrayNode({values: rolls});
+ }
+ reduce(){
+ this.roll().reduce();
+ }
+}
diff --git a/app/imports/parser/parseTree/SymbolNode.js b/app/imports/parser/parseTree/SymbolNode.js
index 52ce13fa..cb855b72 100644
--- a/app/imports/parser/parseTree/SymbolNode.js
+++ b/app/imports/parser/parseTree/SymbolNode.js
@@ -6,6 +6,9 @@ export default class SymbolNode extends ParseNode {
super();
this.name = name;
}
+ toString(){
+ return `${this.name}`
+ }
compile(scope){
let value = scope && scope[this.name];
let type = typeof value;
diff --git a/app/imports/ui/pages/Parser.vue b/app/imports/ui/pages/Parser.vue
new file mode 100644
index 00000000..abcc4e4b
--- /dev/null
+++ b/app/imports/ui/pages/Parser.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/imports/ui/router.js b/app/imports/ui/router.js
index 8e0fc7c5..89c7d0e3 100644
--- a/app/imports/ui/router.js
+++ b/app/imports/ui/router.js
@@ -23,6 +23,7 @@ import PatreonLevelTooLow from '/imports/ui/pages/PatreonLevelTooLow.vue';
import Tabletops from '/imports/ui/pages/Tabletops.vue';
import Tabletop from '/imports/ui/pages/Tabletop.vue';
import TabletopToolbar from '/imports/ui/tabletop/TabletopToolbar.vue';
+import Parser from '/imports/ui/pages/Parser.vue';
let userSubscription = Meteor.subscribe('user');
@@ -109,6 +110,14 @@ RouterFactory.configure(factory => {
title: 'Character List',
},
beforeEnter: ensureLoggedIn,
+ },{
+ path: '/parser',
+ components: {
+ default: Parser,
+ },
+ meta: {
+ title: 'Parser',
+ },
},{
path: '/library',
components: {