const Error = require("./Error"); const AST = require("./ast"); const tokenTypes = require("./tokenTypes"); module.exports = class Parser { constructor(tokenStream) { this.tokenStream = tokenStream; } parse() { let tree = []; while (this.tokenStream.peek().type !== tokenTypes.EOF) { tree.push(this.expr()); if (this.tokenStream.error) { return { error: this.tokenStream.error }; } } return tree; } expr() { let token = this.tokenStream.peek(); switch (token.type) { case tokenTypes.ATTRIBUTE: return this.attribute(); case tokenTypes.BOOLEAN: return this.bool(); case tokenTypes.IDENTIFIER: return this.identifier(); case tokenTypes.NUMBER: return this.number(); case tokenTypes.QUOTE: return this.string(); case tokenTypes.SYMBOL: return this.symbol(); case tokenTypes.OPAREN: return this.form(); default: this.tokenStream.error = `Unexpected ${token.type} on line ${ token.line }`; break; } } form() { this.tokenStream.eat(tokenTypes.OPAREN); let node = new AST.Application(); node.functionName = this.identifier(); node.args = []; while (this.tokenStream.peek().type !== tokenTypes.CPAREN) { node.args.push(this.expr()); } this.tokenStream.eat(tokenTypes.CPAREN); return node; } attribute() { return new AST.Attribute({ name: this.tokenStream.eat(tokenTypes.ATTRIBUTE).value, value: this.expr() }); } bool() { return new AST.Boolean({ value: this.tokenStream.eat(tokenTypes.BOOLEAN).value }); } identifier() { return new AST.Identifier({ name: this.tokenStream.eat(tokenTypes.IDENTIFIER).value }); } number() { return new AST.Number({ value: this.tokenStream.eat(tokenTypes.NUMBER).value }); } string() { this.tokenStream.eat(tokenTypes.QUOTE); let value = ""; if (this.tokenStream.peek().type === tokenTypes.LITERAL) { value = this.tokenStream.eat(tokenTypes.LITERAL).value; } let node = new AST.String({ value: value }); this.tokenStream.eat(tokenTypes.QUOTE); return node; } symbol() { return new AST.Symbol({ value: this.tokenStream.eat(tokenTypes.SYMBOL).value }); } };