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.
460 lines
11 KiB
Go
460 lines
11 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 | forStmt | ifStmt | printStmt | whileStmt | block ;
|
|
func (p *Parser) statement() ast.Stmt {
|
|
if p.match(token.FOR) {
|
|
return p.forStatement()
|
|
}
|
|
if p.match(token.IF) {
|
|
return p.ifStatement()
|
|
}
|
|
if p.match(token.PRINT) {
|
|
return p.printStatement()
|
|
}
|
|
if p.match(token.WHILE) {
|
|
return p.whileStatement()
|
|
}
|
|
if p.match(token.LEFT_BRACE) {
|
|
return p.blockStatement()
|
|
}
|
|
|
|
return p.expressionStatement()
|
|
}
|
|
|
|
// forStmt → "for" "(" ( varDecl | exprStmt | ";" ) expression? ";" expression? ")" statement ;
|
|
func (p *Parser) forStatement() ast.Stmt {
|
|
err := p.consume(token.LEFT_PAREN, "Expect '(' after 'for'.")
|
|
if err != nil {
|
|
return p.fromErrorExpr(err)
|
|
}
|
|
|
|
var initializer ast.Stmt
|
|
if p.match(token.SEMICOLON) {
|
|
initializer = nil
|
|
} else if p.match(token.VAR) {
|
|
initializer = p.varDeclaration()
|
|
} else {
|
|
initializer = p.expressionStatement()
|
|
}
|
|
|
|
var condition ast.Expr
|
|
if !p.check(token.SEMICOLON) {
|
|
condition = p.expression()
|
|
}
|
|
err = p.consume(token.SEMICOLON, "Expect ';' after loop condition.")
|
|
if err != nil {
|
|
return p.fromErrorExpr(err)
|
|
}
|
|
|
|
var increment ast.Expr
|
|
if !p.check(token.RIGHT_PAREN) {
|
|
increment = p.expression()
|
|
}
|
|
err = p.consume(token.RIGHT_PAREN, "Expect ')' after for clauses.")
|
|
if err != nil {
|
|
return p.fromErrorExpr(err)
|
|
}
|
|
|
|
body := p.statement()
|
|
|
|
if increment != nil {
|
|
body = &ast.BlockStmt{Statements: []ast.Stmt{body, &ast.ExpressionStmt{Expression: increment}}}
|
|
}
|
|
|
|
if condition == nil {
|
|
condition = &ast.LiteralExpr{Value: true}
|
|
}
|
|
body = &ast.WhileStmt{Condition: condition, Body: body}
|
|
|
|
if initializer != nil {
|
|
body = &ast.BlockStmt{Statements: []ast.Stmt{initializer, body}}
|
|
}
|
|
|
|
return body
|
|
}
|
|
|
|
// ifStmt → "if" "(" expression ")" statement ( "else" statement )? ;
|
|
func (p *Parser) ifStatement() ast.Stmt {
|
|
err := p.consume(token.LEFT_PAREN, "Expect '(' after 'if'.")
|
|
if err != nil {
|
|
return p.fromErrorExpr(err)
|
|
}
|
|
|
|
condition := p.expression()
|
|
|
|
err = p.consume(token.RIGHT_PAREN, "Expect ')' after if condition.")
|
|
if err != nil {
|
|
return p.fromErrorExpr(err)
|
|
}
|
|
|
|
thenBranch := p.statement()
|
|
var elseBranch ast.Stmt
|
|
if p.match(token.ELSE) {
|
|
elseBranch = p.statement()
|
|
}
|
|
|
|
return &ast.IfStmt{Condition: condition, ThenBranch: thenBranch, ElseBranch: elseBranch}
|
|
}
|
|
|
|
// 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}
|
|
}
|
|
|
|
// whileStmt → "while" "(" expression ")" statement ;
|
|
func (p *Parser) whileStatement() ast.Stmt {
|
|
err := p.consume(token.LEFT_PAREN, "Expect '(' after 'while'.")
|
|
if err != nil {
|
|
return p.fromErrorExpr(err)
|
|
}
|
|
|
|
condition := p.expression()
|
|
|
|
err = p.consume(token.RIGHT_PAREN, "Expect ')' after while condition.")
|
|
if err != nil {
|
|
return p.fromErrorExpr(err)
|
|
}
|
|
|
|
body := p.statement()
|
|
|
|
return &ast.WhileStmt{Condition: condition, Body: body}
|
|
}
|
|
|
|
// block → "{" declaration* "}" ;
|
|
func (p *Parser) blockStatement() ast.Stmt {
|
|
statements := []ast.Stmt{}
|
|
|
|
for !p.check(token.RIGHT_BRACE) && !p.isAtEnd() {
|
|
statements = append(statements, p.declaration())
|
|
}
|
|
|
|
err := p.consume(token.RIGHT_BRACE, "Expect '}' after block.")
|
|
if err != nil {
|
|
return p.fromErrorExpr(err)
|
|
}
|
|
|
|
return &ast.BlockStmt{Statements: statements}
|
|
}
|
|
|
|
// 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}
|
|
}
|
|
|
|
// expression → assignment ;
|
|
func (p *Parser) expression() ast.Expr {
|
|
return p.assignment()
|
|
}
|
|
|
|
// assignment → IDENTIFIER "=" assignment | logic_or ;
|
|
func (p *Parser) assignment() ast.Expr {
|
|
expr := p.or()
|
|
|
|
if p.match(token.EQUAL) {
|
|
equals := p.previous()
|
|
value := p.assignment()
|
|
|
|
if v, ok := expr.(*ast.VariableExpr); ok {
|
|
return &ast.AssignExpr{Name: v.Name, Value: value}
|
|
}
|
|
|
|
p.newErrorExpr(equals, "Invalid assignment target.")
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
// logic_or → logic_and ( "or" logic_and )* ;
|
|
func (p *Parser) or() ast.Expr {
|
|
expr := p.and()
|
|
|
|
for p.match(token.OR) {
|
|
operator := p.previous()
|
|
right := p.and()
|
|
expr = &ast.LogicalExpr{Left: expr, Operator: operator, Right: right}
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
// logic_and → equality ( "and" equality )* ;
|
|
func (p *Parser) and() ast.Expr {
|
|
expr := p.equality()
|
|
|
|
for p.match(token.AND) {
|
|
operator := p.previous()
|
|
right := p.equality()
|
|
expr = &ast.LogicalExpr{Left: expr, Operator: operator, Right: right}
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
// 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()
|
|
}
|
|
}
|