Skip to content

Commit

Permalink
identifier expression parser
Browse files Browse the repository at this point in the history
  • Loading branch information
54L1M committed Jan 31, 2024
1 parent 7731813 commit f7454cb
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 2 deletions.
44 changes: 43 additions & 1 deletion parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,32 @@ import (
"github.com/54L1m/mil-lang/token"
)

const (
_ int = iota
LOWEST
EQUALS // =
LESSGREATER // < OR >
SUM // +
PRODUCT // *
PREFIX // -X OR !X
CALL // myFunction(X)
)

type (
prefixParseFn func() ast.Expression
infixParseFn func(ast.Expression) ast.Expression
)

type Parser struct {
l *lexer.Lexer

errors []string

curToken token.Token
peekToken token.Token

prefixParseFns map[token.TokenType]prefixParseFn
infixParseFns map[token.TokenType]infixParseFn
}

func New(l *lexer.Lexer) *Parser {
Expand All @@ -21,6 +40,9 @@ func New(l *lexer.Lexer) *Parser {
errors: []string{},
}

p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier)

p.nextToken()
p.nextToken()
return p
Expand Down Expand Up @@ -48,11 +70,31 @@ func (p *Parser) parseStatement() ast.Statement {
case token.RETURN:
return p.parseReturnStatement()
default:
return nil
return p.parseExpressionStatement()

}
}

// parsing expressions
func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement {
stmt := &ast.ExpressionStatement{Token: p.curToken}
stmt.Expression = p.parseExpression(LOWEST)

if p.peekTokenIs(token.SEMICOLON) {
p.nextToken()
}
return stmt
}

func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
return nil
}
leftExp := prefix()
return leftExp
}

// parsing var statements
func (p *Parser) parseVarStatement() *ast.VarStatement {
stmt := &ast.VarStatement{Token: p.curToken}
Expand Down
17 changes: 16 additions & 1 deletion parser/parser_helpers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package parser

import "github.com/54L1m/mil-lang/token"
import (
"github.com/54L1m/mil-lang/ast"
"github.com/54L1m/mil-lang/token"
)

func (p *Parser) nextToken() {
p.curToken = p.peekToken
Expand All @@ -24,3 +27,15 @@ func (p *Parser) expectPeek(t token.TokenType) bool {
return false
}
}

func (p *Parser) parseIdentifier() ast.Expression {
return &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
}

func (p *Parser) registerPrefix(tokenType token.TokenType, fn prefixParseFn) {
p.prefixParseFns[tokenType] = fn
}

func (p *Parser) registerInfix(tokenType token.TokenType, fn infixParseFn) {
p.infixParseFns[tokenType] = fn
}
28 changes: 28 additions & 0 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,34 @@ func TestReturnStatement(t *testing.T) {

}

func TestIdentifierExpression(t *testing.T) {
input := "foobar;"
l := lexer.New(input)
p := New(l)
program := p.ParseProgram()
checkParseErrors(t, p)
if len(program.Statements) != 1 {
t.Fatalf("program has not enough statements. got=%d",
len(program.Statements))
}
stmt, ok := program.Statements[0].(*ast.ExpressionStatement)
if !ok {
t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T",
program.Statements[0])
}
ident, ok := stmt.Expression.(*ast.Identifier)
if !ok {
t.Fatalf("exp not *ast.Identifier. got=%T", stmt.Expression)
}
if ident.Value != "foobar" {
t.Errorf("ident.Value not %s. got=%s", "foobar", ident.Value)
}
if ident.TokenLiteral() != "foobar" {
t.Errorf("ident.TokenLiteral not %s. got=%s", "foobar",
ident.TokenLiteral())
}
}

func testVarStatement(t *testing.T, s ast.Statement, name string) bool {
if s.TokenLiteral() != "var" {
t.Errorf("s.TokenLiteral not 'var'. got=%q", s.TokenLiteral())
Expand Down

0 comments on commit f7454cb

Please sign in to comment.