605 lines
10 KiB
Go
605 lines
10 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
)
|
|
|
|
type Parser struct {
|
|
tokens []Token
|
|
current int
|
|
errors []error
|
|
}
|
|
|
|
type ParseError struct {
|
|
token Token
|
|
message string
|
|
}
|
|
|
|
func (pe *ParseError) Error() string {
|
|
return fmt.Sprintf("ParseError [%d][%s]: %s", pe.token.line, pe.token.typ, pe.message)
|
|
}
|
|
|
|
func newParser(tokens []Token) *Parser {
|
|
return &Parser{
|
|
tokens: tokens,
|
|
current: 0,
|
|
}
|
|
}
|
|
|
|
// program -> declaration* EOF
|
|
func (p *Parser) parse() ([]Stmt, error) {
|
|
defer p.recover()
|
|
|
|
stmts := []Stmt{}
|
|
|
|
for !p.isAtEnd() {
|
|
|
|
if stmt := p.declaration(); stmt != nil {
|
|
stmts = append(stmts, stmt)
|
|
}
|
|
}
|
|
|
|
return stmts, errors.Join(p.errors...)
|
|
}
|
|
|
|
// declaration -> varDecl | funDecl | statement
|
|
func (p *Parser) declaration() Stmt {
|
|
defer p.synchronize()
|
|
if p.match(VAR) {
|
|
return p.varDecl()
|
|
}
|
|
|
|
if p.match(FUN) {
|
|
return p.function("function")
|
|
}
|
|
|
|
return p.statement()
|
|
}
|
|
|
|
// varDecl -> "var" IDENTIFIER ("=" expression)? ";"
|
|
func (p *Parser) varDecl() Stmt {
|
|
name := p.consume(IDENTIFIER, "Expect identifier for variable")
|
|
|
|
var initializer Expr = nil
|
|
if p.match(EQUAL) {
|
|
initializer = p.expression()
|
|
}
|
|
|
|
p.consume(SEMICOLON, "Expect ';' after expression.")
|
|
|
|
return &VarStmt{name, initializer}
|
|
}
|
|
|
|
// funDecl -> "fun" function
|
|
// function -> IDENTIFIER "(" parameters? ")" blockStmt
|
|
// parameters -> IDENTIFIER ( "," IDENTIFIER )*
|
|
func (p *Parser) function(kind string) Stmt {
|
|
name := p.consume(IDENTIFIER, fmt.Sprintf("Expect %s name.", kind))
|
|
|
|
p.consume(LEFT_PAREN, fmt.Sprintf("Expect '(' after %s name.", kind))
|
|
|
|
args := []Token{}
|
|
for !p.check(RIGHT_PAREN) {
|
|
args = append(
|
|
args,
|
|
p.consume(
|
|
IDENTIFIER,
|
|
fmt.Sprintf("Expect %s argument.", kind),
|
|
),
|
|
)
|
|
|
|
if p.check(COMMA) {
|
|
p.advance()
|
|
}
|
|
}
|
|
|
|
p.consume(RIGHT_PAREN, fmt.Sprintf("Expect ')' after %s name.", kind))
|
|
p.consume(LEFT_BRACE, fmt.Sprintf("Expect '{' after %s arguments.", kind))
|
|
|
|
body := p.block()
|
|
|
|
return &FunStmt{name, args, body}
|
|
}
|
|
|
|
// statement -> exprStmt
|
|
//
|
|
// | whileStmt
|
|
// | forStmt
|
|
// | printStmt
|
|
// | blockStmt
|
|
// | breakStmt
|
|
// | ifStmt
|
|
// | returnStmt
|
|
// | env
|
|
func (p *Parser) statement() Stmt {
|
|
if p.match(PRINT) {
|
|
return p.printStmt()
|
|
}
|
|
|
|
if p.match(LEFT_BRACE) {
|
|
return p.blockStmt()
|
|
}
|
|
|
|
if p.match(IF) {
|
|
return p.ifStmt()
|
|
}
|
|
|
|
if p.match(ENV) {
|
|
return p.envStmt()
|
|
}
|
|
|
|
if p.match(WHILE) {
|
|
return p.whileStmt()
|
|
}
|
|
|
|
if p.match(FOR) {
|
|
return p.forStmt()
|
|
}
|
|
|
|
if p.match(BREAK) {
|
|
return p.breakStmt()
|
|
}
|
|
|
|
if p.match(RETURN) {
|
|
return p.returnStmt()
|
|
}
|
|
|
|
return p.exprStmt()
|
|
}
|
|
|
|
// exprStmt -> expression ";"
|
|
func (p *Parser) exprStmt() Stmt {
|
|
expr := p.expression()
|
|
p.consume(SEMICOLON, "Expect ';' after expression.")
|
|
|
|
if expr == nil {
|
|
return nil
|
|
}
|
|
|
|
return &ExprStmt{expr}
|
|
}
|
|
|
|
// printStmt -> "print" expression ";"
|
|
func (p *Parser) printStmt() Stmt {
|
|
expr := p.expression()
|
|
|
|
if expr == nil {
|
|
p.panic(&ParseError{p.previous(), "Expect expression after 'print'"})
|
|
}
|
|
|
|
p.consume(SEMICOLON, "Expect ';' after expression.")
|
|
return &PrintStmt{expr}
|
|
}
|
|
|
|
func (p *Parser) block() []Stmt {
|
|
|
|
stmts := []Stmt{}
|
|
for !p.check(RIGHT_BRACE) && !p.isAtEnd() {
|
|
stmts = append(stmts, p.declaration())
|
|
}
|
|
|
|
p.consume(RIGHT_BRACE, "Unclosed block: Expected '}'.")
|
|
|
|
return stmts
|
|
}
|
|
|
|
// blockStmt -> "{" statement* "}"
|
|
func (p *Parser) blockStmt() *BlockStmt {
|
|
return &BlockStmt{p.block()}
|
|
}
|
|
|
|
// breakStmt -> break ";"
|
|
func (p *Parser) breakStmt() Stmt {
|
|
p.consume(SEMICOLON, "Expect ';' after break.")
|
|
return &BreakStmt{}
|
|
}
|
|
|
|
// if -> "if" "(" expression ")" statement ("else" statement)?
|
|
func (p *Parser) ifStmt() Stmt {
|
|
name := p.previous()
|
|
p.consume(LEFT_PAREN, "Expect '(' after 'if'.")
|
|
expr := p.expression()
|
|
p.consume(RIGHT_PAREN, "Expect ')' after 'if' condition.")
|
|
then := p.statement()
|
|
|
|
var or Stmt = nil
|
|
if p.match(ELSE) {
|
|
or = p.statement()
|
|
}
|
|
|
|
return &IfStmt{name, expr, then, or}
|
|
}
|
|
|
|
// while -> "while" "(" expression ")" statement
|
|
func (p *Parser) whileStmt() Stmt {
|
|
p.consume(LEFT_PAREN, "Expect '(' after 'while'.")
|
|
cond := p.expression()
|
|
p.consume(RIGHT_PAREN, "Expect ')' after 'while' expression.")
|
|
body := p.statement()
|
|
|
|
return &WhileStmt{cond, body}
|
|
}
|
|
|
|
// for -> "for" ( "(" ( varDecl | exprStmt | ";" ) expression? ";" expression ")" )? statement
|
|
func (p *Parser) forStmt() Stmt {
|
|
|
|
if p.check(LEFT_BRACE) {
|
|
return &WhileStmt{&Literal{true}, p.statement()}
|
|
}
|
|
|
|
p.consume(LEFT_PAREN, "Expect '(' after 'for'.")
|
|
|
|
var init Stmt
|
|
|
|
if p.match(SEMICOLON) {
|
|
init = nil
|
|
} else if p.match(VAR) {
|
|
init = p.varDecl()
|
|
} else {
|
|
init = p.exprStmt()
|
|
}
|
|
|
|
var cond Expr
|
|
|
|
if !p.check(SEMICOLON) {
|
|
cond = p.expression()
|
|
}
|
|
|
|
p.consume(SEMICOLON, "Expect ';' after for loop condition;")
|
|
|
|
var incr Expr
|
|
|
|
if !p.check(RIGHT_PAREN) {
|
|
incr = p.expression()
|
|
}
|
|
|
|
p.consume(RIGHT_PAREN, "Expect ')' after for clauses;")
|
|
|
|
var body = p.statement()
|
|
|
|
if incr != nil {
|
|
body = &BlockStmt{[]Stmt{body, &ExprStmt{incr}}}
|
|
}
|
|
|
|
if cond == nil {
|
|
cond = &Literal{true}
|
|
}
|
|
|
|
body = &WhileStmt{cond, body}
|
|
|
|
if init != nil {
|
|
body = &BlockStmt{[]Stmt{init, body}}
|
|
}
|
|
|
|
return body
|
|
}
|
|
|
|
// env -> "env" ";"
|
|
func (p *Parser) envStmt() Stmt {
|
|
p.consume(SEMICOLON, "Expect ';' after 'env'.")
|
|
return &EnvStmt{}
|
|
}
|
|
|
|
// return -> "return" expression ";"
|
|
func (p *Parser) returnStmt() Stmt {
|
|
ret := p.expression()
|
|
p.consume(SEMICOLON, "Expect ';' after return;")
|
|
return &ReturnStmt{ret}
|
|
}
|
|
|
|
// expression -> assignment
|
|
func (p *Parser) expression() Expr {
|
|
return p.assignment()
|
|
}
|
|
|
|
// assignment -> IDENTIFIER "=" assignment | or
|
|
func (p *Parser) assignment() Expr {
|
|
expr := p.or()
|
|
|
|
if p.match(EQUAL) {
|
|
eq := p.previous()
|
|
val := p.assignment()
|
|
|
|
if variable, ok := expr.(*Variable); ok {
|
|
return &Assign{variable.name, val}
|
|
}
|
|
|
|
p.panic(&ParseError{eq, "Invalid assignment target."})
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
// or -> and ( "or" and )*
|
|
func (p *Parser) or() Expr {
|
|
left := p.and()
|
|
|
|
for p.match(OR) {
|
|
or := p.previous()
|
|
right := p.and()
|
|
left = &Logical{left, or, right}
|
|
}
|
|
|
|
return left
|
|
}
|
|
|
|
// and -> equality ( "and" equality )*
|
|
func (p *Parser) and() Expr {
|
|
left := p.equality()
|
|
|
|
for p.match(AND) {
|
|
or := p.previous()
|
|
right := p.equality()
|
|
|
|
left = &Logical{left, or, right}
|
|
}
|
|
|
|
return left
|
|
}
|
|
|
|
// 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 = &Binary{exp, 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.call()
|
|
}
|
|
|
|
// call -> primary ( "(" arguments? ")" )*
|
|
func (p *Parser) call() Expr {
|
|
expr := p.primary()
|
|
|
|
for {
|
|
if p.match(LEFT_PAREN) {
|
|
expr = p.arguments(expr)
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
|
|
return expr
|
|
}
|
|
|
|
// arguments -> expression ( "," expression )*
|
|
func (p *Parser) arguments(callee Expr) Expr {
|
|
arguments := []Expr{}
|
|
|
|
if !p.check(RIGHT_PAREN) {
|
|
for {
|
|
arguments = append(arguments, p.expression())
|
|
|
|
if !p.match(COMMA) {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
paren := p.consume(RIGHT_PAREN, "Expect ')' after arguments.")
|
|
|
|
return &Call{callee, paren, arguments}
|
|
}
|
|
|
|
// primary -> IDENTIFIER
|
|
//
|
|
// | NUMBER
|
|
// | STRING
|
|
// | "true"
|
|
// | "false"
|
|
// | "nil"
|
|
// | "(" expression ")"
|
|
// | lambda
|
|
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(FUN) {
|
|
return p.lambda()
|
|
}
|
|
|
|
if p.match(NUMBER, STRING) {
|
|
return &Literal{p.previous().literal}
|
|
}
|
|
|
|
if p.match(IDENTIFIER) {
|
|
return &Variable{p.previous()}
|
|
}
|
|
|
|
if p.match(LEFT_PAREN) {
|
|
expr := p.expression()
|
|
p.consume(RIGHT_PAREN, "Expect ')' after expression")
|
|
return &Grouping{expr}
|
|
}
|
|
|
|
p.panic(&ParseError{p.peek(), "Expect expression"})
|
|
|
|
return nil
|
|
}
|
|
|
|
func (p *Parser) lambda() Expr {
|
|
name := p.previous()
|
|
|
|
p.consume(LEFT_PAREN, "Expect '(' before lambda arguments.")
|
|
|
|
args := []Token{}
|
|
for !p.check(RIGHT_PAREN) {
|
|
args = append(
|
|
args,
|
|
p.consume(
|
|
IDENTIFIER,
|
|
"Expect lambda argument.",
|
|
),
|
|
)
|
|
|
|
if p.check(COMMA) {
|
|
p.advance()
|
|
}
|
|
}
|
|
|
|
p.consume(RIGHT_PAREN, "Expect ')' after lambda arguments.")
|
|
p.consume(LEFT_BRACE, "Expect '{' before lambda body.")
|
|
|
|
body := p.block()
|
|
|
|
return &Lambda{name, args, body}
|
|
}
|
|
|
|
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()
|
|
}
|
|
|
|
p.panic(&ParseError{p.peek(), mes})
|
|
|
|
return Token{}
|
|
}
|
|
|
|
func (p *Parser) synchronize() {
|
|
err := recover()
|
|
|
|
pe := p.isParseError(err)
|
|
|
|
if pe == nil {
|
|
return
|
|
}
|
|
|
|
p.advance()
|
|
|
|
for !p.isAtEnd() {
|
|
if p.previous().typ == SEMICOLON {
|
|
return
|
|
}
|
|
|
|
switch p.peek().typ {
|
|
case CLASS, FOR, FUN, IF, PRINT, RETURN, VAR, WHILE, ENV:
|
|
return
|
|
}
|
|
|
|
p.advance()
|
|
}
|
|
|
|
}
|
|
|
|
func (p *Parser) recover() {
|
|
p.isParseError(recover())
|
|
}
|
|
|
|
func (p *Parser) panic(pe *ParseError) {
|
|
p.errors = append(p.errors, pe)
|
|
panic(pe)
|
|
}
|
|
|
|
func (p *Parser) isParseError(err any) *ParseError {
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
pe, ok := err.(*ParseError)
|
|
|
|
if !ok {
|
|
panic(err)
|
|
}
|
|
|
|
return pe
|
|
}
|