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 }) } }