Parser now works with variables passed into scope
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
// http://github.com/Hardmath123/nearley
|
// http://github.com/Hardmath123/nearley
|
||||||
function id(x) { return x[0]; }
|
function id(x) { return x[0]; }
|
||||||
|
|
||||||
|
import AccessorNode from '/imports/parser/parseTree/AccessorNode.js';
|
||||||
import ArrayNode from '/imports/parser/parseTree/ArrayNode.js';
|
import ArrayNode from '/imports/parser/parseTree/ArrayNode.js';
|
||||||
import CallNode from '/imports/parser/parseTree/CallNode.js';
|
import CallNode from '/imports/parser/parseTree/CallNode.js';
|
||||||
import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
|
import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
|
||||||
@@ -23,7 +24,7 @@ function id(x) { return x[0]; }
|
|||||||
name: {
|
name: {
|
||||||
match: /[a-zA-Z]+\w*?/,
|
match: /[a-zA-Z]+\w*?/,
|
||||||
type: moo.keywords({
|
type: moo.keywords({
|
||||||
'keywords': ['if', 'else', 'd'],
|
'keywords': ['d'],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
space: {
|
space: {
|
||||||
@@ -32,6 +33,7 @@ function id(x) { return x[0]; }
|
|||||||
},
|
},
|
||||||
separator: [',', ';'],
|
separator: [',', ';'],
|
||||||
period: ['.'],
|
period: ['.'],
|
||||||
|
ternaryOperator: ['?', ':'],
|
||||||
multiplicativeOperator: ['*', '/'],
|
multiplicativeOperator: ['*', '/'],
|
||||||
exponentOperator: ['^'],
|
exponentOperator: ['^'],
|
||||||
additiveOperator: ['+', '-'],
|
additiveOperator: ['+', '-'],
|
||||||
@@ -56,8 +58,8 @@ function id(x) { return x[0]; }
|
|||||||
let Lexer = lexer;
|
let Lexer = lexer;
|
||||||
let ParserRules = [
|
let ParserRules = [
|
||||||
{"name": "expression", "symbols": ["ifStatement"], "postprocess": d => d[0]},
|
{"name": "expression", "symbols": ["ifStatement"], "postprocess": d => d[0]},
|
||||||
{"name": "ifStatement", "symbols": ["_", "expression", "_", {"literal":"?"}, "_", "expression", "_", {"literal":":"}, "_", "expression"], "postprocess":
|
{"name": "ifStatement", "symbols": ["_", "equalityExpression", "_", {"literal":"?"}, "_", "equalityExpression", "_", {"literal":":"}, "_", "ifStatement"], "postprocess":
|
||||||
d => new IfNode({condition: d[4], consequent: d[8], alternative: d[12]})
|
d => new IfNode({condition: d[1], consequent: d[5], alternative: d[9]})
|
||||||
},
|
},
|
||||||
{"name": "ifStatement", "symbols": ["equalityExpression"], "postprocess": id},
|
{"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": ["equalityExpression", "_", (lexer.has("equalityOperator") ? {type: "equalityOperator"} : equalityOperator), "_", "relationalExpression"], "postprocess": d => operator(d, 'equality')},
|
||||||
@@ -100,11 +102,17 @@ let ParserRules = [
|
|||||||
{"name": "arrayExpression$ebnf$2$subexpression$1", "symbols": ["_", (lexer.has("separator") ? {type: "separator"} : separator), "_", "expression"], "postprocess": d => d[3]},
|
{"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$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":
|
{"name": "arrayExpression", "symbols": [{"literal":"["}, "_", "arrayExpression$ebnf$1", "arrayExpression$ebnf$2", "_", {"literal":"]"}], "postprocess":
|
||||||
d => new ArrayNode({values: [d[2], ...d[3]]})
|
d => new ArrayNode({values: d[2] ? [d[2], ...d[3]] : []})
|
||||||
},
|
},
|
||||||
{"name": "arrayExpression", "symbols": ["parenthesizedExpression"], "postprocess": id},
|
{"name": "arrayExpression", "symbols": ["parenthesizedExpression"], "postprocess": id},
|
||||||
{"name": "parenthesizedExpression", "symbols": [{"literal":"("}, "_", "expression", "_", {"literal":")"}], "postprocess": d => new ParenthesisNode({content: d[2]})},
|
{"name": "parenthesizedExpression", "symbols": [{"literal":"("}, "_", "expression", "_", {"literal":")"}], "postprocess": d => new ParenthesisNode({content: d[2]})},
|
||||||
{"name": "parenthesizedExpression", "symbols": ["valueExpression"], "postprocess": id},
|
{"name": "parenthesizedExpression", "symbols": ["accessorExpression"], "postprocess": id},
|
||||||
|
{"name": "accessorExpression$ebnf$1$subexpression$1", "symbols": [{"literal":"."}, "name"], "postprocess": d => d[1].name},
|
||||||
|
{"name": "accessorExpression$ebnf$1", "symbols": ["accessorExpression$ebnf$1$subexpression$1"]},
|
||||||
|
{"name": "accessorExpression$ebnf$1$subexpression$2", "symbols": [{"literal":"."}, "name"], "postprocess": d => d[1].name},
|
||||||
|
{"name": "accessorExpression$ebnf$1", "symbols": ["accessorExpression$ebnf$1", "accessorExpression$ebnf$1$subexpression$2"], "postprocess": function arrpush(d) {return d[0].concat([d[1]]);}},
|
||||||
|
{"name": "accessorExpression", "symbols": ["name", "accessorExpression$ebnf$1"], "postprocess": d=> new AccessorNode({name: d[0], path: d[1]})},
|
||||||
|
{"name": "accessorExpression", "symbols": ["valueExpression"], "postprocess": id},
|
||||||
{"name": "valueExpression", "symbols": ["name"], "postprocess": id},
|
{"name": "valueExpression", "symbols": ["name"], "postprocess": id},
|
||||||
{"name": "valueExpression", "symbols": ["number"], "postprocess": id},
|
{"name": "valueExpression", "symbols": ["number"], "postprocess": id},
|
||||||
{"name": "valueExpression", "symbols": ["string"], "postprocess": id},
|
{"name": "valueExpression", "symbols": ["string"], "postprocess": id},
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@preprocessor esmodule
|
@preprocessor esmodule
|
||||||
@{%
|
@{%
|
||||||
|
import AccessorNode from '/imports/parser/parseTree/AccessorNode.js';
|
||||||
import ArrayNode from '/imports/parser/parseTree/ArrayNode.js';
|
import ArrayNode from '/imports/parser/parseTree/ArrayNode.js';
|
||||||
import CallNode from '/imports/parser/parseTree/CallNode.js';
|
import CallNode from '/imports/parser/parseTree/CallNode.js';
|
||||||
import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
|
import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
|
||||||
@@ -21,7 +22,7 @@
|
|||||||
name: {
|
name: {
|
||||||
match: /[a-zA-Z]+\w*?/,
|
match: /[a-zA-Z]+\w*?/,
|
||||||
type: moo.keywords({
|
type: moo.keywords({
|
||||||
'keywords': ['if', 'else', 'd'],
|
'keywords': ['d'],
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
space: {
|
space: {
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
},
|
},
|
||||||
separator: [',', ';'],
|
separator: [',', ';'],
|
||||||
period: ['.'],
|
period: ['.'],
|
||||||
|
ternaryOperator: ['?', ':'],
|
||||||
multiplicativeOperator: ['*', '/'],
|
multiplicativeOperator: ['*', '/'],
|
||||||
exponentOperator: ['^'],
|
exponentOperator: ['^'],
|
||||||
additiveOperator: ['+', '-'],
|
additiveOperator: ['+', '-'],
|
||||||
@@ -60,8 +62,8 @@ expression ->
|
|||||||
ifStatement {% d => d[0] %}
|
ifStatement {% d => d[0] %}
|
||||||
|
|
||||||
ifStatement ->
|
ifStatement ->
|
||||||
_ expression _ "?" _ expression _ ":" _ expression {%
|
_ equalityExpression _ "?" _ equalityExpression _ ":" _ ifStatement {%
|
||||||
d => new IfNode({condition: d[4], consequent: d[8], alternative: d[12]})
|
d => new IfNode({condition: d[1], consequent: d[5], alternative: d[9]})
|
||||||
%}
|
%}
|
||||||
| equalityExpression {% id %}
|
| equalityExpression {% id %}
|
||||||
|
|
||||||
@@ -118,12 +120,16 @@ indexExpression ->
|
|||||||
|
|
||||||
arrayExpression ->
|
arrayExpression ->
|
||||||
"[" _ (expression {% d => d[0] %}):? ( _ %separator _ expression {% d => d[3] %} ):* _ "]" {%
|
"[" _ (expression {% d => d[0] %}):? ( _ %separator _ expression {% d => d[3] %} ):* _ "]" {%
|
||||||
d => new ArrayNode({values: [d[2], ...d[3]]})
|
d => new ArrayNode({values: d[2] ? [d[2], ...d[3]] : []})
|
||||||
%}
|
%}
|
||||||
| parenthesizedExpression {% id %}
|
| parenthesizedExpression {% id %}
|
||||||
|
|
||||||
parenthesizedExpression ->
|
parenthesizedExpression ->
|
||||||
"(" _ expression _ ")" {% d => new ParenthesisNode({content: d[2]}) %}
|
"(" _ expression _ ")" {% d => new ParenthesisNode({content: d[2]}) %}
|
||||||
|
| accessorExpression {% id %}
|
||||||
|
|
||||||
|
accessorExpression ->
|
||||||
|
name ( "." name {% d => d[1].name %} ):+ {% d=> new AccessorNode({name: d[0], path: d[1]}) %}
|
||||||
| valueExpression {% id %}
|
| valueExpression {% id %}
|
||||||
|
|
||||||
valueExpression ->
|
valueExpression ->
|
||||||
|
|||||||
46
app/imports/parser/parseTree/AccessorNode.js
Normal file
46
app/imports/parser/parseTree/AccessorNode.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import ParseNode from '/imports/parser/parseTree/ParseNode.js';
|
||||||
|
import ErrorNode from '/imports/parser/parseTree/ErrorNode.js';
|
||||||
|
import ConstantNode from '/imports/parser/parseTree/ConstantNode.js';
|
||||||
|
|
||||||
|
export default class AccessorNode extends ParseNode {
|
||||||
|
constructor({name, path}) {
|
||||||
|
super(...arguments);
|
||||||
|
this.name = name;
|
||||||
|
this.path = path;
|
||||||
|
}
|
||||||
|
compile(scope){
|
||||||
|
let value = scope && scope[this.name];
|
||||||
|
// For objects, get their value
|
||||||
|
this.path.forEach(name => {
|
||||||
|
if (value === undefined) return;
|
||||||
|
value = value[name];
|
||||||
|
});
|
||||||
|
let type = typeof value;
|
||||||
|
if (type === 'string' || type === 'number' || type === 'boolean'){
|
||||||
|
return new ConstantNode({value, type, previousNodes: [this]});
|
||||||
|
} else if (type === 'undefined'){
|
||||||
|
return new AccessorNode({
|
||||||
|
name: this.name,
|
||||||
|
path: this.path,
|
||||||
|
previousNodes: [this],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Meteor.Error(`Unexpected case: ${this.name} resolved to ${value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reduce(scope){
|
||||||
|
let result = this.compile(scope);
|
||||||
|
if (result instanceof AccessorNode){
|
||||||
|
return new ErrorNode({
|
||||||
|
node: result,
|
||||||
|
error: `${this.toString()} could not be resolved`,
|
||||||
|
previousNodes: [result],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toString(){
|
||||||
|
return `${this.name}.${this.path.join('.')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -22,13 +22,12 @@ export default class IfNode extends ParseNode {
|
|||||||
}
|
}
|
||||||
resolve(fn, scope){
|
resolve(fn, scope){
|
||||||
let condition = this.condition[fn](scope);
|
let condition = this.condition[fn](scope);
|
||||||
let consequent = this.consequent[fn](scope);
|
|
||||||
let alternative = this.alternative[fn](scope);
|
|
||||||
this.resolve(condition, consequent, alternative);
|
|
||||||
if (condition.value){
|
if (condition.value){
|
||||||
|
let consequent = this.consequent[fn](scope);
|
||||||
consequent.inheritDetails([condition, this]);
|
consequent.inheritDetails([condition, this]);
|
||||||
return consequent;
|
return this.consequent[fn](scope);
|
||||||
} else {
|
} else {
|
||||||
|
let alternative = this.alternative[fn](scope);
|
||||||
alternative.inheritDetails([condition, this]);
|
alternative.inheritDetails([condition, this]);
|
||||||
return alternative;
|
return alternative;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,15 +13,32 @@ export default class SymbolNode extends ParseNode {
|
|||||||
compile(scope){
|
compile(scope){
|
||||||
let value = scope && scope[this.name];
|
let value = scope && scope[this.name];
|
||||||
let type = typeof value;
|
let type = typeof value;
|
||||||
|
// For objects, get their value
|
||||||
|
if (type === 'object'){
|
||||||
|
value = value.value;
|
||||||
|
type = typeof value;
|
||||||
|
}
|
||||||
if (type === 'string' || type === 'number' || type === 'boolean'){
|
if (type === 'string' || type === 'number' || type === 'boolean'){
|
||||||
return new ConstantNode({value, type, previousNodes: [this]});
|
return new ConstantNode({value, type, previousNodes: [this]});
|
||||||
} else if (type === 'undefined'){
|
} else if (type === 'undefined'){
|
||||||
return new ErrorNode({
|
return new SymbolNode({
|
||||||
node: this,
|
name: this.name,
|
||||||
error: `${this.name} could not be resolved`,
|
previousNodes: [this],
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
throw new Meteor.Error(`Unexpected case: ${this.name} resolved to ${value}`);
|
throw new Meteor.Error(`Unexpected case: ${this.name} resolved to ${value}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
reduce(scope){
|
||||||
|
let result = this.compile(scope);
|
||||||
|
if (result instanceof SymbolNode){
|
||||||
|
return new ErrorNode({
|
||||||
|
node: result,
|
||||||
|
error: `${this.toString()} could not be resolved`,
|
||||||
|
previousNodes: [result],
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ export default {
|
|||||||
this.output = JSON.stringify(output, null, 2);
|
this.output = JSON.stringify(output, null, 2);
|
||||||
if (!output) return;
|
if (!output) return;
|
||||||
this.string = output;
|
this.string = output;
|
||||||
let scope = {cat: 1, dog: 2, mouse: 3};
|
let scope = {strength: {value: 16}, hitpoints: {value: 32, currentValue: 8}, mouse: 3};
|
||||||
this.compiled = output.compile(scope);
|
this.compiled = output.compile(scope);
|
||||||
this.rolled = output.roll(scope);
|
this.rolled = output.roll(scope);
|
||||||
this.reduced = output.reduce(scope);
|
this.reduced = output.reduce(scope);
|
||||||
|
|||||||
Reference in New Issue
Block a user