const util = require('util') const selfClosingTags = require('./util/selfClosingTags') module.exports = class Compiler { constructor(tree, context) { this.tree = tree this.context = context this.pos = 0 this.result = '' this.standardLibrary = { cond: function(predicate, left, right) { if (predicate) { let compiler = new Compiler([left], this.context) return compiler.compile() } else { let compiler = new Compiler([right], this.context) return compiler.compile() } }, } } compile() { this.tree.forEach(node => { switch (node.constructor.name) { case 'Application': this.result += this.application(node) break; case 'String': this.result += node.value; break; case 'Identifier': this.result += this.lookup(node) break; } }) return this.result } application(node) { if (this.standardLibrary[node.functionName.name]) { return this.standardLibrary[node.functionName.name](...node.args) } return this.htmlElement(node) } htmlElement(node) { let result = `<${node.functionName.name}` node.args.filter(arg => arg.constructor.name === 'Attribute').forEach(arg => { result += ` ${arg.name}` let compiler = new Compiler([arg.value], this.context) let attrValue = compiler.compile() if (attrValue) { result += `="${attrValue}"` } }) result += '>' node.args.filter(arg => arg.constructor.name !== 'Attribute').forEach(arg => { let compiler = new Compiler([arg], this.context) result += compiler.compile() }) if (!selfClosingTags.includes(node.functionName.name)) { result += `` } return result } lookup(identifier) { let result = this.context[identifier.name] if (!result) { throw `Undefined variable '${identifier.name}'` } return result } currentNode() { return this.tree[this.pos] } }