Moved parse tree classes out of grammar.js started working on compilation. Broke the build

This commit is contained in:
Stefan Zermatten
2019-03-29 14:08:09 +02:00
parent caf7f3efb9
commit d21827106c
3 changed files with 102 additions and 25 deletions

View File

@@ -0,0 +1 @@
// Takes a parse tree and computes it down as far as possible into a real number

View File

@@ -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

View 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;
}
}