const AST = require('./ast') const Env = require('./env') module.exports = class Evaluator { eval(tree, env) { this.env = env let evaluatedTree = [] tree.forEach(node => { let evaluatedNode = this.evalNode(node) if (evaluatedNode) { evaluatedTree.push(evaluatedNode) } }) return evaluatedTree } evalNode(node, env) { if (!env) env = this.env switch (node.constructor) { case AST.Boolean: case AST.Number: case AST.String: return node case AST.Identifier: return env.get(node.name) case AST.Definition: this.env.set(node.symbol.name, node.value) return false case AST.Application: switch (node.function.constructor) { case AST.Identifier: return this.evalNode( new AST.Application({ function: env.get(node.function.name), args: node.args, }), ) case AST.Lambda: let innerEnv = new Env(env) node.function.parameters.forEach((param, index) => { innerEnv.set(param.name, node.args[index]) }) return this.evalNode(node.function.body, innerEnv) case Function: let args = node.args.map(arg => { return this.evalNode(arg) }) args.unshift(this) return node.function.call(...args) } } return node } }