206 lines
3.3 KiB
Go
206 lines
3.3 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
)
|
|
|
|
type Parser struct {
|
|
tokens []Token
|
|
current int
|
|
}
|
|
|
|
type ParseError struct {
|
|
token Token
|
|
message string
|
|
}
|
|
|
|
func (pe ParseError) Error() string {
|
|
return fmt.Sprintf("%s: %s", pe.token.lexeme, pe.message)
|
|
}
|
|
|
|
func newParser(tokens []Token) *Parser {
|
|
return &Parser{
|
|
tokens: tokens,
|
|
current: 0,
|
|
}
|
|
}
|
|
|
|
func (p *Parser) parse() Expr {
|
|
defer p.recover()
|
|
return p.expression()
|
|
}
|
|
|
|
func (p *Parser) recover() {
|
|
if err := recover(); err != nil {
|
|
pe := err.(ParseError)
|
|
printError(pe.token, pe.message)
|
|
}
|
|
}
|
|
|
|
// expression -> equality
|
|
func (p *Parser) expression() Expr {
|
|
return p.equality()
|
|
}
|
|
|
|
// equality -> comparison ( ( "==" | "!=" ) comparison )*
|
|
func (p *Parser) equality() Expr {
|
|
expr := p.comparison()
|
|
|
|
for p.match(EQUAL_EQUAL, BANG_EQUAL) {
|
|
op := p.previous()
|
|
right := p.comparison()
|
|
expr = &Binary{expr, op, right}
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
// comparison -> term ( ( ">" | ">=" | "<" | "<=" ) term )*
|
|
func (p *Parser) comparison() Expr {
|
|
expr := p.term()
|
|
|
|
for p.match(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL) {
|
|
op := p.previous()
|
|
right := p.term()
|
|
expr = &Binary{expr, op, right}
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
// term -> factor ( ( "-" | "+" ) factor )*
|
|
func (p *Parser) term() Expr {
|
|
expr := p.factor()
|
|
|
|
for p.match(MINUS, PLUS) {
|
|
op := p.previous()
|
|
right := p.factor()
|
|
expr = &Binary{expr, op, right}
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
// factor -> unary ( ( "/" | "*" ) unary )*
|
|
func (p *Parser) factor() Expr {
|
|
exp := p.unary()
|
|
|
|
for p.match(SLASH, STAR) {
|
|
op := p.previous()
|
|
right := p.unary()
|
|
exp = &Unary{op, right}
|
|
}
|
|
|
|
return exp
|
|
}
|
|
|
|
// unary -> ( "!" | "-" ) unary | primary
|
|
func (p *Parser) unary() Expr {
|
|
if p.match(BANG, MINUS) {
|
|
op := p.previous()
|
|
right := p.unary()
|
|
return &Unary{op, right}
|
|
}
|
|
|
|
return p.primary()
|
|
}
|
|
|
|
// primary -> NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")"
|
|
func (p *Parser) primary() Expr {
|
|
switch {
|
|
case p.match(FALSE):
|
|
return &Literal{false}
|
|
case p.match(TRUE):
|
|
return &Literal{true}
|
|
case p.match(NIL):
|
|
return &Literal{nil}
|
|
}
|
|
|
|
if p.match(NUMBER, STRING) {
|
|
return &Literal{p.previous().literal}
|
|
}
|
|
|
|
if p.match(LEFT_PAREN) {
|
|
expr := p.expression()
|
|
p.consume(RIGHT_PAREN, "Expect ')' after expression")
|
|
return &Grouping{expr}
|
|
}
|
|
|
|
panic(ParseError{p.peek(), "Expect expression"})
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Parser) previous() Token {
|
|
return p.tokens[p.current-1]
|
|
}
|
|
|
|
func (p *Parser) peek() Token {
|
|
return p.tokens[p.current]
|
|
}
|
|
|
|
func (p *Parser) isAtEnd() bool {
|
|
return p.peek().typ == EOF
|
|
}
|
|
|
|
func (p *Parser) advance() Token {
|
|
if !p.isAtEnd() {
|
|
p.current++
|
|
}
|
|
|
|
return p.previous()
|
|
}
|
|
|
|
func (p *Parser) check(typ TokenType) bool {
|
|
if p.isAtEnd() {
|
|
return false
|
|
}
|
|
|
|
return p.peek().typ == typ
|
|
}
|
|
|
|
func (p *Parser) match(types ...TokenType) bool {
|
|
|
|
for _, typ := range types {
|
|
if p.check(typ) {
|
|
p.advance()
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
func (p *Parser) consume(typ TokenType, mes string) Token {
|
|
if p.check(typ) {
|
|
return p.advance()
|
|
}
|
|
|
|
panic(ParseError{p.peek(), mes})
|
|
|
|
return Token{}
|
|
}
|
|
|
|
func (p *Parser) synchronize() {
|
|
p.advance()
|
|
|
|
for !p.isAtEnd() {
|
|
if p.previous().typ == SEMICOLON {
|
|
return
|
|
}
|
|
|
|
switch p.peek().typ {
|
|
case CLASS:
|
|
case FOR:
|
|
case FUN:
|
|
case IF:
|
|
case PRINT:
|
|
case RETURN:
|
|
case VAR:
|
|
case WHILE:
|
|
return
|
|
}
|
|
|
|
p.advance()
|
|
}
|
|
}
|