You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
4.9 KiB
Go

/* Description: This file contains the recursivde descent parser implementation.
* The parser is responsible for parsing the tokens generated by the scanner.
*
* The grammar is as follows:
* expression → equality ;
* equality → comparison ( ( "!=" | "==" ) comparison )* ;
* comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
* term → factor ( ( "-" | "+" ) factor )* ;
* factor → unary ( ( "/" | "*" ) unary )* ;
* unary → ( "!" | "-" ) unary
* | primary ;
* primary → NUMBER | STRING | "true" | "false" | "nil"
* | "(" expression ")" ;
*/
package parser
import (
"golox/errors"
"golox/token"
)
// Parser is a recursive descent parser.
type Parser struct {
tokens []token.Token
current int
errLogger errors.Logger
}
// New creates a new Parser.
func New(tokens []token.Token, el errors.Logger) *Parser {
return &Parser{tokens: tokens, current: 0, errLogger: el}
}
// Parse parses the tokens and returns the AST.
func (p *Parser) Parse() Expr {
return p.expression()
}
// expression → equality ;
func (p *Parser) expression() Expr {
return p.equality()
}
// equality → comparison ( ( "!=" | "==" ) comparison )* ;
func (p *Parser) equality() Expr {
expr := p.comparison()
for p.match(token.BANG_EQUAL, token.EQUAL_EQUAL) {
operator := p.previous()
right := p.comparison()
expr = &BinaryExpr{Left: expr, Operator: operator, Right: right}
}
return expr
}
// comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
func (p *Parser) comparison() Expr {
expr := p.term()
for p.match(token.GREATER, token.GREATER_EQUAL, token.LESS, token.LESS_EQUAL) {
operator := p.previous()
right := p.term()
expr = &BinaryExpr{Left: expr, Operator: operator, Right: right}
}
return expr
}
// term → factor ( ( "-" | "+" ) factor )* ;
func (p *Parser) term() Expr {
expr := p.factor()
for p.match(token.MINUS, token.PLUS) {
operator := p.previous()
right := p.factor()
expr = &BinaryExpr{Left: expr, Operator: operator, Right: right}
}
return expr
}
// factor → unary ( ( "/" | "*" ) unary )* ;
func (p *Parser) factor() Expr {
expr := p.unary()
for p.match(token.SLASH, token.STAR) {
operator := p.previous()
right := p.unary()
expr = &BinaryExpr{Left: expr, Operator: operator, Right: right}
}
return expr
}
// unary → ( "!" | "-" ) unary | primary ;
func (p *Parser) unary() Expr {
if p.match(token.BANG, token.MINUS) {
operator := p.previous()
right := p.unary()
return &UnaryExpr{Operator: operator, Right: right}
}
return p.primary()
}
// primary → NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" ;
func (p *Parser) primary() Expr {
switch {
case p.match(token.FALSE):
return &LiteralExpr{Value: false}
case p.match(token.TRUE):
return &LiteralExpr{Value: true}
case p.match(token.NIL):
return &LiteralExpr{Value: nil}
case p.match(token.NUMBER, token.STRING):
return &LiteralExpr{Value: p.previous().Literal}
case p.match(token.LEFT_PAREN):
expr := p.expression()
err := p.consume(token.RIGHT_PAREN, "Expect ')' after expression.")
if err != nil {
return err
}
return &GroupingExpr{Expression: expr}
}
return p.newErrorExpr(p.peek(), "Expect expression.")
}
// match checks if the current token is any of the given types.
func (p *Parser) match(types ...token.TokenType) bool {
for _, t := range types {
if p.check(t) {
p.advance()
return true
}
}
return false
}
// consume consumes the current token if it is of the given type.
func (p *Parser) consume(tt token.TokenType, message string) *ErrorExpr {
if p.check(tt) {
p.advance()
return nil
}
return p.newErrorExpr(p.peek(), message)
}
// check checks if the current token is of the given type.
func (p *Parser) check(tt token.TokenType) bool {
if p.isAtEnd() {
return false
}
return p.peek().Type == tt
}
// advance advances the current token and returns the previous token.
func (p *Parser) advance() token.Token {
if !p.isAtEnd() {
p.current++
}
return p.previous()
}
// isAtEnd checks if the parser has reached the end of the tokens.
func (p *Parser) isAtEnd() bool {
return p.peek().Type == token.EOF
}
// peek returns the current token.
func (p *Parser) peek() token.Token {
return p.tokens[p.current]
}
// previous returns the previous token.
func (p *Parser) previous() token.Token {
return p.tokens[p.current-1]
}
// newErrorExpr creates a new ErrorExpr and reports the error.
func (p *Parser) newErrorExpr(t token.Token, message string) *ErrorExpr {
p.errLogger.ErrorAtToken(t, message)
return &ErrorExpr{Value: message}
}
// synchronize synchronizes the parser after an error.
func (p *Parser) synchronize() {
p.advance()
for !p.isAtEnd() {
if p.previous().Type == token.SEMICOLON {
return
}
switch p.peek().Type {
case token.CLASS, token.FUN, token.VAR, token.FOR, token.IF, token.WHILE, token.PRINT, token.RETURN:
return
}
p.advance()
}
}