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.
209 lines
5.0 KiB
Go
209 lines
5.0 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/ast"
|
|
"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() ast.Expr {
|
|
return p.expression()
|
|
}
|
|
|
|
// expression → equality ;
|
|
func (p *Parser) expression() ast.Expr {
|
|
return p.equality()
|
|
}
|
|
|
|
// equality → comparison ( ( "!=" | "==" ) comparison )* ;
|
|
func (p *Parser) equality() ast.Expr {
|
|
expr := p.comparison()
|
|
|
|
for p.match(token.BANG_EQUAL, token.EQUAL_EQUAL) {
|
|
operator := p.previous()
|
|
right := p.comparison()
|
|
expr = &ast.BinaryExpr{Left: expr, Operator: operator, Right: right}
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
// comparison → term ( ( ">" | ">=" | "<" | "<=" ) term )* ;
|
|
func (p *Parser) comparison() ast.Expr {
|
|
expr := p.term()
|
|
|
|
for p.match(token.GREATER, token.GREATER_EQUAL, token.LESS, token.LESS_EQUAL) {
|
|
operator := p.previous()
|
|
right := p.term()
|
|
expr = &ast.BinaryExpr{Left: expr, Operator: operator, Right: right}
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
// term → factor ( ( "-" | "+" ) factor )* ;
|
|
func (p *Parser) term() ast.Expr {
|
|
expr := p.factor()
|
|
|
|
for p.match(token.MINUS, token.PLUS) {
|
|
operator := p.previous()
|
|
right := p.factor()
|
|
expr = &ast.BinaryExpr{Left: expr, Operator: operator, Right: right}
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
// factor → unary ( ( "/" | "*" ) unary )* ;
|
|
func (p *Parser) factor() ast.Expr {
|
|
expr := p.unary()
|
|
|
|
for p.match(token.SLASH, token.STAR) {
|
|
operator := p.previous()
|
|
right := p.unary()
|
|
expr = &ast.BinaryExpr{Left: expr, Operator: operator, Right: right}
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
// unary → ( "!" | "-" ) unary | primary ;
|
|
func (p *Parser) unary() ast.Expr {
|
|
if p.match(token.BANG, token.MINUS) {
|
|
operator := p.previous()
|
|
right := p.unary()
|
|
return &ast.UnaryExpr{Operator: operator, Right: right}
|
|
}
|
|
|
|
return p.primary()
|
|
}
|
|
|
|
// primary → NUMBER | STRING | "true" | "false" | "nil" | "(" expression ")" ;
|
|
func (p *Parser) primary() ast.Expr {
|
|
switch {
|
|
case p.match(token.FALSE):
|
|
return &ast.LiteralExpr{Value: false}
|
|
case p.match(token.TRUE):
|
|
return &ast.LiteralExpr{Value: true}
|
|
case p.match(token.NIL):
|
|
return &ast.LiteralExpr{Value: nil}
|
|
case p.match(token.NUMBER, token.STRING):
|
|
return &ast.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 &ast.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) *ast.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) *ast.ErrorExpr {
|
|
p.errLogger.ErrorAtToken(t, message)
|
|
return &ast.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()
|
|
}
|
|
}
|