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.

294 lines
7.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:
* program → statement* EOF ;
* statement → exprStmt
* | printStmt ;
* exprStmt → expression ";" ;
* printStmt → "print" expression ";" ;
* 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.Stmt {
stmts := []ast.Stmt{}
for !p.isAtEnd() {
stmt := p.declaration()
if _, ok := stmt.(*ast.ErrorStmt); ok {
p.synchronize()
} else {
stmts = append(stmts, stmt)
}
}
return stmts
}
// declaration → varDecl | statement ;
func (p *Parser) declaration() ast.Stmt {
if p.match(token.VAR) {
return p.varDeclaration()
}
return p.statement()
}
// varDecl → "var" IDENTIFIER ( "=" expression )? ";" ;
func (p *Parser) varDeclaration() ast.Stmt {
err := p.consume(token.IDENTIFIER, "Expect variable name.")
if err != nil {
return p.fromErrorExpr(err)
}
name := p.previous()
var initializer ast.Expr
if p.match(token.EQUAL) {
initializer = p.expression()
}
err = p.consume(token.SEMICOLON, "Expect ';' after variable declaration.")
if err != nil {
return p.fromErrorExpr(err)
}
return &ast.VarStmt{Name: name, Initializer: initializer}
}
// statement → exprStmt | printStmt ;
func (p *Parser) statement() ast.Stmt {
if p.match(token.PRINT) {
return p.printStatement()
}
return p.expressionStatement()
}
// exprStmt → expression ";" ;
func (p *Parser) expressionStatement() ast.Stmt {
expr := p.expression()
err := p.consume(token.SEMICOLON, "Expect ';' after expression.")
if err != nil {
return p.fromErrorExpr(err)
}
return &ast.ExpressionStmt{Expression: expr}
}
// printStmt → "print" expression ";" ;
func (p *Parser) printStatement() ast.Stmt {
expr := p.expression()
err := p.consume(token.SEMICOLON, "Expect ';' after value.")
if err != nil {
return p.fromErrorExpr(err)
}
return &ast.PrintStmt{Expression: expr}
}
// 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 ")" | IDENTIFIER;
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.IDENTIFIER):
return &ast.VariableExpr{Name: p.previous()}
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}
}
// fromErrorExpr creates an ErrorStmt from an ErrorExpr.
func (p *Parser) fromErrorExpr(ee *ast.ErrorExpr) *ast.ErrorStmt {
return &ast.ErrorStmt{Value: ee.Value}
}
// synchronize synchronizes the parser after an error.
// It skips tokens until it finds a statement boundary.
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()
}
}