Moved parse tree classes out of grammar.js started working on compilation. Broke the build
This commit is contained in:
1
app/imports/parser/compute.js
Normal file
1
app/imports/parser/compute.js
Normal file
@@ -0,0 +1 @@
|
||||
// Takes a parse tree and computes it down as far as possible into a real number
|
||||
@@ -31,15 +31,6 @@
|
||||
});
|
||||
|
||||
function nuller() { return null; }
|
||||
|
||||
class OperatorNode {
|
||||
constructor({left, right, operator, fn}) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
this.fn = fn;
|
||||
this.operator = operator;
|
||||
}
|
||||
}
|
||||
function operator([left, _1, operator, _2, right], fn){
|
||||
return new OperatorNode({
|
||||
left,
|
||||
@@ -48,26 +39,15 @@
|
||||
fn
|
||||
});
|
||||
}
|
||||
|
||||
class SymbolNode {
|
||||
constructor(name){
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
class ConstantNode {
|
||||
constructor(value, type){
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
%}
|
||||
|
||||
# Use the Moo lexer
|
||||
@lexer lexer
|
||||
|
||||
ifStatement ->
|
||||
"if" _ "(" _ expression _ ")" _ ifStatement _ "else" _ ifStatement {% d => ({condition: d[4], true: d[8], false: d[12]}) %}
|
||||
"if" _ "(" _ expression _ ")" _ ifStatement _ "else" _ ifStatement {%
|
||||
d => new ifNode({condition: d[4], consequent: d[8], alternative: d[12]})
|
||||
%}
|
||||
| expression {% id %}
|
||||
|
||||
expression ->
|
||||
@@ -127,13 +107,13 @@ valueExpression ->
|
||||
|
||||
# A number or a function of a number
|
||||
number ->
|
||||
%number {% d => new ConstantNode(d[0].value, 'number') %}
|
||||
%number {% d => new ConstantNode({value: d[0].value, type 'number'}) %}
|
||||
|
||||
name ->
|
||||
%name {% d => new SymbolNode(d[0].value) %}
|
||||
|
||||
string ->
|
||||
%string {% d => new ConstantNode(d[0].value, 'string') %}
|
||||
%string {% d => new ConstantNode({value: d[0].value, type: 'string'}) %}
|
||||
|
||||
_ ->
|
||||
null
|
||||
|
||||
96
app/imports/parser/parseTree.js
Normal file
96
app/imports/parser/parseTree.js
Normal file
@@ -0,0 +1,96 @@
|
||||
// All the classes that make up a parse tree
|
||||
|
||||
class ParseNode {
|
||||
// Compiling a node must return a ConstantNode
|
||||
compile(){
|
||||
throw new Meteor.Error('Compile not implemented on ' + this);
|
||||
}
|
||||
compileToSingleValue(){
|
||||
return this.compile();
|
||||
}
|
||||
}
|
||||
|
||||
function sum(total, num) {
|
||||
return total + num;
|
||||
}
|
||||
|
||||
class ConstantNode extends ParseNode {
|
||||
constructor({value, type, errors}){
|
||||
// string, number, boolean, numberArray, uncompiledNode
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
if (errors) this.errors = errors;
|
||||
}
|
||||
compile(){
|
||||
return this;
|
||||
}
|
||||
compileToSingleValue(){
|
||||
if (this.type !== 'numberArray') return this;
|
||||
return this.value.reduce(sum, 0);
|
||||
}
|
||||
}
|
||||
|
||||
class SymbolNode extends ParseNode {
|
||||
constructor({name}){
|
||||
this.name = name;
|
||||
}
|
||||
compile(scope){
|
||||
let value = scope[this.name];
|
||||
let type = typeof value;
|
||||
if (type === 'string' || type === 'number' || type === 'boolean'){
|
||||
return new ConstantNode({value, type});
|
||||
} else if (type === 'undefined'){
|
||||
return new ConstantNode({
|
||||
value: this.name,
|
||||
type: 'uncompiledNode',
|
||||
errors: [`${this.name} could not be resolved`]
|
||||
});
|
||||
} else {
|
||||
throw new Meteor.Error(`Unexpected case: ${this.name} resolved to ${value}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ifNode extends ParseNode {
|
||||
constructor({condition, consequent, alternative}){
|
||||
this.condition = condition;
|
||||
this.consequent = consequent;
|
||||
this.alternative = alternative;
|
||||
}
|
||||
compile(){
|
||||
let condition = this.condition.compile();
|
||||
let consequent = this.consequent.compile();
|
||||
let alternative = this.alternative.compile();
|
||||
if (
|
||||
condition.type === 'uncompiledNode' ||
|
||||
consequent.type === 'uncompiledNode' ||
|
||||
alternative.type === 'uncompiledNode'
|
||||
){
|
||||
// Handle uncompiled child nodes
|
||||
return new ConstantNode({
|
||||
value: `if (${condition.value}) ${consequent.value} else ${alternative.value}`,
|
||||
type: 'uncompiledNode',
|
||||
errors: [
|
||||
...condition.errors,
|
||||
...consequent.errors,
|
||||
...alternative.errors,
|
||||
],
|
||||
});
|
||||
} else {
|
||||
if (condition.value){
|
||||
return consequent;
|
||||
} else {
|
||||
return alternative;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OperatorNode extends ParseNode {
|
||||
constructor({left, right, operator, fn}) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
this.fn = fn;
|
||||
this.operator = operator;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user