thorgot/parser/parser.go
Robin Bärtschi 5db30a2a84
All checks were successful
Go / build (push) Successful in 20s
added statement parsing, missing tests and expression
2024-11-14 15:42:39 +01:00

164 lines
2.8 KiB
Go

package parser
import (
"fmt"
"git.robaertschi.xyz/robaertschi/thorgot/ast"
"git.robaertschi.xyz/robaertschi/thorgot/lexer"
"git.robaertschi.xyz/robaertschi/thorgot/token"
)
type Parser struct {
lexer lexer.Lexer
curToken token.Token
peekToken token.Token
Errors []error
}
func New(lexer lexer.Lexer) Parser {
p := Parser{}
p.nextToken()
p.nextToken()
return p
}
func (p *Parser) error(err error) ast.StatementNode {
p.Errors = append(p.Errors, err)
return nil
}
func (p *Parser) expectPeek(t token.TokenType) bool {
if p.peekToken.Type == t {
p.nextToken()
return true
} else {
p.peekError(t)
return false
}
}
func (p *Parser) peekTokenIs(t token.TokenType) bool {
return p.peekToken.Type == t
}
func (p *Parser) peekError(t token.TokenType) {
err := fmt.Errorf("Expected token %v to be %v", t, p.peekToken.Type)
p.Errors = append(p.Errors, err)
}
func (p *Parser) nextToken() {
p.curToken = p.peekToken
p.peekToken = p.lexer.NextToken()
}
func (p *Parser) ParseProgram() ast.Program {
program := ast.Program{}
program.Statements = make([]ast.StatementNode, 0)
for p.curToken.Type != token.Eof {
stmt := p.parseStatement()
if stmt != nil {
program.Statements = append(program.Statements, stmt)
}
}
return program
}
func (p *Parser) parseStatement() ast.StatementNode {
switch p.curToken.Type {
case token.Fn:
return p.parseFunction()
}
return p.error(fmt.Errorf("Invalid token %v found with literal %v", p.curToken.Type, p.curToken.Literal))
}
func (p *Parser) parseFunctionArguments() []ast.FunctionArgument {
args := make([]ast.FunctionArgument, 0)
for p.peekTokenIs(token.Identifier) {
p.nextToken()
name := p.curToken.Literal
if !p.expectPeek(token.Identifier) {
return nil
}
args = append(args, ast.FunctionArgument{Name: name, Type: ast.Type(p.curToken.Literal)})
if !p.peekTokenIs(token.Comma) {
break
}
p.nextToken()
}
if !p.expectPeek(token.RParen) {
return nil
}
return args
}
func (p *Parser) parseFunction() *ast.Function {
f := &ast.Function{Token: p.curToken}
if !p.expectPeek(token.Identifier) {
return nil
}
f.Name = p.curToken.Literal
if !p.expectPeek(token.LParen) {
return nil
}
args := p.parseFunctionArguments()
if args == nil {
return nil
}
f.Arguments = args
if p.peekTokenIs(token.Identifier) {
p.nextToken()
f.ReturnType = ast.Type(p.curToken.Literal)
f.HasReturnType = true
}
if !p.expectPeek(token.LBrace) {
return nil
}
// parse block
f.Block = p.parseBlock()
if f.Block == nil {
return nil
}
return f
}
func (p *Parser) parseBlock() *ast.Block {
b := &ast.Block{Token: p.curToken}
// skip {
p.nextToken()
for p.curToken.Type != token.RBrace {
stmt := p.parseStatement()
if stmt == nil {
return nil
}
b.Statements = append(b.Statements, stmt)
}
p.nextToken()
return b
}