Mako/parser/parser.go
2025-05-07 09:08:51 -05:00

550 lines
11 KiB
Go

package parser
import (
"git.sharkk.net/Sharkk/Mako/scanner"
"git.sharkk.net/Sharkk/Mako/types"
)
// Parser manages the state needed for parsing
type Parser struct {
scanner *scanner.Scanner
tokens []types.Token // Token buffer for lookahead
current int // Index into tokens
hadError bool
panicMode bool
}
// New creates a new parser for the given source
func New(source string) *Parser {
p := &Parser{
scanner: scanner.New(source),
}
// Fill the token buffer with at least 2 tokens
p.tokens = append(p.tokens, p.scanner.NextToken())
p.tokens = append(p.tokens, p.scanner.NextToken())
return p
}
// Parse parses the source and returns the statements
func (p *Parser) Parse() []types.Statement {
var statements []types.Statement
for !p.check(types.EOF) {
statements = append(statements, p.declaration())
}
return statements
}
// advance moves to the next token
func (p *Parser) advance() {
p.current++
// Ensure we always have at least 2 tokens in the buffer
if p.current >= len(p.tokens)-1 {
p.tokens = append(p.tokens, p.scanner.NextToken())
}
// Skip error tokens
for p.currentToken().Type == types.ERROR {
p.error(p.currentToken().Lexeme)
p.current++
// Ensure we have enough tokens
if p.current >= len(p.tokens)-1 {
p.tokens = append(p.tokens, p.scanner.NextToken())
}
}
}
// currentToken returns the current token
func (p *Parser) currentToken() types.Token {
return p.tokens[p.current]
}
// previousToken returns the previous token
func (p *Parser) previousToken() types.Token {
return p.tokens[p.current-1]
}
// peek returns the next token without consuming it
func (p *Parser) peek() types.Token {
return p.tokens[p.current+1]
}
// check checks if the current token is of the given type
func (p *Parser) check(t types.TokenType) bool {
return p.currentToken().Type == t
}
// match checks if the current token is of the given type
// If it is, it consumes the token and returns true
func (p *Parser) match(types ...types.TokenType) bool {
for _, t := range types {
if p.check(t) {
p.advance()
return true
}
}
return false
}
// consume consumes the current token if it matches the given type
// Otherwise, it reports an error
func (p *Parser) consume(t types.TokenType, message string) types.Token {
if p.check(t) {
token := p.currentToken()
p.advance()
return token
}
p.error(message)
return p.currentToken()
}
// error reports a parse error
func (p *Parser) error(message string) *types.MakoError {
p.hadError = true
if p.panicMode {
return nil
}
p.panicMode = true
token := p.currentToken()
return types.NewError(message, token.Line, token.Column)
}
// synchronize skips tokens until it finds a statement boundary
func (p *Parser) synchronize() {
p.panicMode = false
for !p.check(types.EOF) {
if p.previousToken().Type == types.END {
return
}
switch p.currentToken().Type {
case types.FN, types.IF, types.RETURN, types.ECHO:
return
}
p.advance()
}
}
// declaration parses a declaration
func (p *Parser) declaration() types.Statement {
// Only treat it as a function declaration if there's an identifier after 'fn'
if p.check(types.FN) && p.peek().Type == types.IDENTIFIER {
p.advance() // Consume the FN token
return p.function()
}
stmt := p.statement()
if p.hadError {
p.synchronize()
}
return stmt
}
// function parses a function declaration
func (p *Parser) function() types.Statement {
name := p.consume(types.IDENTIFIER, "Expected function name.")
p.consume(types.LEFT_PAREN, "Expected '(' after function name.")
var params []types.Token
isVariadic := false
// Parse parameter list
if !p.check(types.RIGHT_PAREN) {
for {
if p.match(types.ELLIPSIS) {
isVariadic = true
break
}
param := p.consume(types.IDENTIFIER, "Expected parameter name.")
params = append(params, param)
if !p.match(types.COMMA) {
break
}
if p.match(types.ELLIPSIS) {
isVariadic = true
break
}
}
}
p.consume(types.RIGHT_PAREN, "Expected ')' after parameters.")
// Parse function body
var body []types.Statement
for !p.check(types.END) && !p.check(types.EOF) {
body = append(body, p.declaration())
}
p.consume(types.END, "Expected 'end' after function body.")
return types.FunctionStmt{
Name: name,
Params: params,
IsVariadic: isVariadic,
Body: body,
}
}
// statement parses a statement
func (p *Parser) statement() types.Statement {
if p.match(types.IF) {
return p.ifStatement()
}
if p.match(types.RETURN) {
return p.returnStatement()
}
if p.match(types.ECHO) {
return p.echoStatement()
}
// Check for assignment
if p.check(types.IDENTIFIER) && p.peek().Type == types.EQUAL {
name := p.currentToken()
p.advance() // Consume the identifier
p.advance() // Consume the equals sign
value := p.expression()
return types.AssignStmt{
Name: name,
Value: value,
}
}
return p.expressionStatement()
}
// ifStatement parses an if statement
func (p *Parser) ifStatement() types.Statement {
condition := p.expression()
p.consume(types.THEN, "Expected 'then' after if condition.")
var thenBranch []types.Statement
for !p.check(types.END) && !p.check(types.ELSEIF) && !p.check(types.ELSE) && !p.check(types.EOF) {
thenBranch = append(thenBranch, p.declaration())
}
var elseIfs []struct {
Condition types.Expression
Body []types.Statement
}
// Parse 'elseif' branches
for p.match(types.ELSEIF) {
elseifCondition := p.expression()
p.consume(types.THEN, "Expected 'then' after elseif condition.")
var body []types.Statement
for !p.check(types.END) && !p.check(types.ELSEIF) && !p.check(types.ELSE) && !p.check(types.EOF) {
body = append(body, p.declaration())
}
elseIfs = append(elseIfs, struct {
Condition types.Expression
Body []types.Statement
}{
Condition: elseifCondition,
Body: body,
})
}
var elseBranch []types.Statement
// Parse 'else' branch
if p.match(types.ELSE) {
for !p.check(types.END) && !p.check(types.EOF) {
elseBranch = append(elseBranch, p.declaration())
}
}
p.consume(types.END, "Expected 'end' after if statement.")
return types.IfStmt{
Condition: condition,
ThenBranch: thenBranch,
ElseIfs: elseIfs,
ElseBranch: elseBranch,
}
}
// returnStatement parses a return statement
func (p *Parser) returnStatement() types.Statement {
keyword := p.previousToken()
var value types.Expression
if !p.check(types.END) && !p.check(types.EOF) {
value = p.expression()
}
return types.ReturnStmt{
Keyword: keyword,
Value: value,
}
}
// echoStatement parses an echo statement
func (p *Parser) echoStatement() types.Statement {
keyword := p.previousToken()
value := p.expression()
return types.EchoStmt{
Keyword: keyword,
Value: value,
}
}
// expressionStatement parses an expression statement
func (p *Parser) expressionStatement() types.Statement {
expr := p.expression()
return types.ExpressionStmt{Expression: expr}
}
// expression parses an expression
func (p *Parser) expression() types.Expression {
return p.or()
}
// or parses a logical OR expression
func (p *Parser) or() types.Expression {
expr := p.and()
for p.match(types.OR) {
operator := p.previousToken()
right := p.and()
expr = types.BinaryExpr{
Left: expr,
Operator: operator,
Right: right,
}
}
return expr
}
// and parses a logical AND expression
func (p *Parser) and() types.Expression {
expr := p.equality()
for p.match(types.AND) {
operator := p.previousToken()
right := p.equality()
expr = types.BinaryExpr{
Left: expr,
Operator: operator,
Right: right,
}
}
return expr
}
// equality parses an equality expression
func (p *Parser) equality() types.Expression {
expr := p.comparison()
for p.match(types.EQUAL_EQUAL, types.BANG_EQUAL) {
operator := p.previousToken()
right := p.comparison()
expr = types.BinaryExpr{
Left: expr,
Operator: operator,
Right: right,
}
}
return expr
}
// comparison parses a comparison expression
func (p *Parser) comparison() types.Expression {
expr := p.term()
for p.match(types.LESS, types.LESS_EQUAL, types.GREATER, types.GREATER_EQUAL) {
operator := p.previousToken()
right := p.term()
expr = types.BinaryExpr{
Left: expr,
Operator: operator,
Right: right,
}
}
return expr
}
// term parses a term expression
func (p *Parser) term() types.Expression {
expr := p.factor()
for p.match(types.PLUS, types.MINUS) {
operator := p.previousToken()
right := p.factor()
expr = types.BinaryExpr{
Left: expr,
Operator: operator,
Right: right,
}
}
return expr
}
// factor parses a factor expression
func (p *Parser) factor() types.Expression {
expr := p.unary()
for p.match(types.STAR, types.SLASH) {
operator := p.previousToken()
right := p.unary()
expr = types.BinaryExpr{
Left: expr,
Operator: operator,
Right: right,
}
}
return expr
}
// unary parses a unary expression
func (p *Parser) unary() types.Expression {
if p.match(types.MINUS) {
operator := p.previousToken()
right := p.unary()
return types.UnaryExpr{
Operator: operator,
Right: right,
}
}
return p.call()
}
// call parses a function call
func (p *Parser) call() types.Expression {
expr := p.primary()
for p.match(types.LEFT_PAREN) {
var arguments []types.Expression
if !p.check(types.RIGHT_PAREN) {
for {
arguments = append(arguments, p.expression())
if !p.match(types.COMMA) {
break
}
}
}
paren := p.consume(types.RIGHT_PAREN, "Expected ')' after arguments.")
expr = types.CallExpr{
Callee: expr,
Paren: paren,
Arguments: arguments,
}
}
return expr
}
// primary parses a primary expression
func (p *Parser) primary() types.Expression {
if p.match(types.NIL) {
return types.LiteralExpr{Value: nil}
}
if p.match(types.TRUE) {
return types.LiteralExpr{Value: true}
}
if p.match(types.FALSE) {
return types.LiteralExpr{Value: false}
}
if p.match(types.NUMBER, types.STRING) {
return types.LiteralExpr{Value: p.previousToken().Literal}
}
if p.match(types.IDENTIFIER) {
return types.VariableExpr{Name: p.previousToken()}
}
if p.match(types.LEFT_PAREN) {
expr := p.expression()
p.consume(types.RIGHT_PAREN, "Expected ')' after expression.")
return expr
}
// Anonymous function expression
if p.match(types.FN) {
p.consume(types.LEFT_PAREN, "Expected '(' after 'fn'.")
var params []types.Token
isVariadic := false
// Parse parameter list
if !p.check(types.RIGHT_PAREN) {
for {
if p.match(types.ELLIPSIS) {
isVariadic = true
break
}
param := p.consume(types.IDENTIFIER, "Expected parameter name.")
params = append(params, param)
if !p.match(types.COMMA) {
break
}
if p.match(types.ELLIPSIS) {
isVariadic = true
break
}
}
}
p.consume(types.RIGHT_PAREN, "Expected ')' after parameters.")
// Parse function body
var body []types.Statement
for !p.check(types.END) && !p.check(types.EOF) {
body = append(body, p.declaration())
}
p.consume(types.END, "Expected 'end' after function body.")
return types.FunctionExpr{
Params: params,
IsVariadic: isVariadic,
Body: body,
}
}
p.error("Expected expression.")
return nil
}