229 lines
4.8 KiB
Go
229 lines
4.8 KiB
Go
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
)
|
|
|
|
// Parser implements a recursive descent Pratt parser
|
|
type Parser struct {
|
|
lexer *Lexer
|
|
|
|
curToken Token
|
|
peekToken Token
|
|
|
|
prefixParseFns map[TokenType]func() Expression
|
|
infixParseFns map[TokenType]func(Expression) Expression
|
|
|
|
errors []string
|
|
}
|
|
|
|
// NewParser creates a new parser instance
|
|
func NewParser(lexer *Lexer) *Parser {
|
|
p := &Parser{
|
|
lexer: lexer,
|
|
errors: []string{},
|
|
}
|
|
|
|
p.prefixParseFns = make(map[TokenType]func() Expression)
|
|
p.registerPrefix(IDENT, p.parseIdentifier)
|
|
p.registerPrefix(NUMBER, p.parseNumberLiteral)
|
|
p.registerPrefix(STRING, p.parseStringLiteral)
|
|
p.registerPrefix(LPAREN, p.parseGroupedExpression)
|
|
|
|
p.infixParseFns = make(map[TokenType]func(Expression) Expression)
|
|
p.registerInfix(PLUS, p.parseInfixExpression)
|
|
p.registerInfix(MINUS, p.parseInfixExpression)
|
|
p.registerInfix(SLASH, p.parseInfixExpression)
|
|
p.registerInfix(STAR, p.parseInfixExpression)
|
|
|
|
// Read two tokens, so curToken and peekToken are both set
|
|
p.nextToken()
|
|
p.nextToken()
|
|
|
|
return p
|
|
}
|
|
|
|
// registerPrefix registers a prefix parse function
|
|
func (p *Parser) registerPrefix(tokenType TokenType, fn func() Expression) {
|
|
p.prefixParseFns[tokenType] = fn
|
|
}
|
|
|
|
// registerInfix registers an infix parse function
|
|
func (p *Parser) registerInfix(tokenType TokenType, fn func(Expression) Expression) {
|
|
p.infixParseFns[tokenType] = fn
|
|
}
|
|
|
|
// nextToken advances to the next token
|
|
func (p *Parser) nextToken() {
|
|
p.curToken = p.peekToken
|
|
p.peekToken = p.lexer.NextToken()
|
|
}
|
|
|
|
// ParseProgram parses the entire program
|
|
func (p *Parser) ParseProgram() *Program {
|
|
program := &Program{}
|
|
program.Statements = []Statement{}
|
|
|
|
for !p.curTokenIs(EOF) {
|
|
stmt := p.parseStatement()
|
|
if stmt != nil {
|
|
program.Statements = append(program.Statements, stmt)
|
|
}
|
|
p.nextToken()
|
|
}
|
|
|
|
return program
|
|
}
|
|
|
|
// parseStatement parses a statement
|
|
func (p *Parser) parseStatement() Statement {
|
|
if p.curTokenIs(IDENT) && p.peekTokenIs(ASSIGN) {
|
|
return p.parseAssignStatement()
|
|
}
|
|
|
|
// Skip unknown statements for now
|
|
return nil
|
|
}
|
|
|
|
// parseAssignStatement parses variable assignment
|
|
func (p *Parser) parseAssignStatement() *AssignStatement {
|
|
stmt := &AssignStatement{}
|
|
|
|
if !p.curTokenIs(IDENT) {
|
|
return nil
|
|
}
|
|
|
|
stmt.Name = &Identifier{Value: p.curToken.Literal}
|
|
|
|
if !p.expectPeek(ASSIGN) {
|
|
return nil
|
|
}
|
|
|
|
p.nextToken()
|
|
|
|
stmt.Value = p.parseExpression(LOWEST)
|
|
|
|
return stmt
|
|
}
|
|
|
|
// parseExpression parses expressions using Pratt parsing
|
|
func (p *Parser) parseExpression(precedence Precedence) Expression {
|
|
prefix := p.prefixParseFns[p.curToken.Type]
|
|
if prefix == nil {
|
|
p.noPrefixParseFnError(p.curToken.Type)
|
|
return nil
|
|
}
|
|
|
|
leftExp := prefix()
|
|
|
|
for !p.peekTokenIs(EOF) && precedence < p.peekPrecedence() {
|
|
infix := p.infixParseFns[p.peekToken.Type]
|
|
if infix == nil {
|
|
return leftExp
|
|
}
|
|
|
|
p.nextToken()
|
|
leftExp = infix(leftExp)
|
|
}
|
|
|
|
return leftExp
|
|
}
|
|
|
|
// Expression parsing functions
|
|
func (p *Parser) parseIdentifier() Expression {
|
|
return &Identifier{Value: p.curToken.Literal}
|
|
}
|
|
|
|
func (p *Parser) parseNumberLiteral() Expression {
|
|
lit := &NumberLiteral{}
|
|
|
|
value, err := strconv.ParseFloat(p.curToken.Literal, 64)
|
|
if err != nil {
|
|
msg := fmt.Sprintf("could not parse %q as float", p.curToken.Literal)
|
|
p.errors = append(p.errors, msg)
|
|
return nil
|
|
}
|
|
|
|
lit.Value = value
|
|
return lit
|
|
}
|
|
|
|
func (p *Parser) parseStringLiteral() Expression {
|
|
return &StringLiteral{Value: p.curToken.Literal}
|
|
}
|
|
|
|
func (p *Parser) parseGroupedExpression() Expression {
|
|
p.nextToken()
|
|
|
|
exp := p.parseExpression(LOWEST)
|
|
|
|
if !p.expectPeek(RPAREN) {
|
|
return nil
|
|
}
|
|
|
|
return exp
|
|
}
|
|
|
|
func (p *Parser) parseInfixExpression(left Expression) Expression {
|
|
expression := &InfixExpression{
|
|
Left: left,
|
|
Operator: p.curToken.Literal,
|
|
}
|
|
|
|
precedence := p.curPrecedence()
|
|
p.nextToken()
|
|
expression.Right = p.parseExpression(precedence)
|
|
|
|
return expression
|
|
}
|
|
|
|
// Helper methods
|
|
func (p *Parser) curTokenIs(t TokenType) bool {
|
|
return p.curToken.Type == t
|
|
}
|
|
|
|
func (p *Parser) peekTokenIs(t TokenType) bool {
|
|
return p.peekToken.Type == t
|
|
}
|
|
|
|
func (p *Parser) expectPeek(t TokenType) bool {
|
|
if p.peekTokenIs(t) {
|
|
p.nextToken()
|
|
return true
|
|
} else {
|
|
p.peekError(t)
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (p *Parser) peekError(t TokenType) {
|
|
msg := fmt.Sprintf("expected next token to be %v, got %v instead",
|
|
t, p.peekToken.Type)
|
|
p.errors = append(p.errors, msg)
|
|
}
|
|
|
|
func (p *Parser) noPrefixParseFnError(t TokenType) {
|
|
msg := fmt.Sprintf("no prefix parse function for %v found", t)
|
|
p.errors = append(p.errors, msg)
|
|
}
|
|
|
|
func (p *Parser) peekPrecedence() Precedence {
|
|
if p, ok := precedences[p.peekToken.Type]; ok {
|
|
return p
|
|
}
|
|
return LOWEST
|
|
}
|
|
|
|
func (p *Parser) curPrecedence() Precedence {
|
|
if p, ok := precedences[p.curToken.Type]; ok {
|
|
return p
|
|
}
|
|
return LOWEST
|
|
}
|
|
|
|
// Errors returns all parsing errors
|
|
func (p *Parser) Errors() []string {
|
|
return p.errors
|
|
}
|