Skip to content

Commit

Permalink
feat: adds prefix operators
Browse files Browse the repository at this point in the history
  • Loading branch information
Tezzish committed Sep 20, 2024
1 parent d21cef4 commit 24c587d
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 0 deletions.
25 changes: 25 additions & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func (p *Program) String() string {
return out.String()
}

// for expressions
type ExpressionStatement struct {
Token token.Token
Expression Expression
Expand All @@ -56,6 +57,27 @@ func (es *ExpressionStatement) String() string {
return ""
}

// for Prefix expresions etc. -15
type PrefixExpression struct {
Token token.Token // the token of the prefix eg. !
Operator string
Right Expression
}

func (pe *PrefixExpression) expressionNode() {}
func (pe *PrefixExpression) TokenLiteral() string { return pe.Token.Literal }
func (pe *PrefixExpression) String() string {
var out bytes.Buffer

out.WriteString("(")
out.WriteString(pe.Operator)
out.WriteString(pe.Right.String())
out.WriteString(")")

return out.String()
}

// for name identifiers
type Identifier struct {
Token token.Token
Value string
Expand All @@ -67,6 +89,7 @@ func (i *Identifier) String() string {
return i.Value
}

// for integers
type IntegerLiteral struct {
Token token.Token
Value int64
Expand All @@ -76,6 +99,7 @@ func (num *IntegerLiteral) expressionNode() {}
func (num *IntegerLiteral) TokenLiteral() string { return num.Token.Literal }
func (num *IntegerLiteral) String() string { return num.Token.Literal }

// for let statements
type LetStatement struct {
Token token.Token
Name *Identifier
Expand All @@ -101,6 +125,7 @@ func (ls *LetStatement) String() string {
return out.String()
}

// for return statements
type ReturnStatement struct {
Token token.Token
ReturnValue Expression
Expand Down
18 changes: 18 additions & 0 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ func New(l *lexer.Lexer) *Parser {
p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
p.registerPrefix(token.IDENT, p.parseIdentifier)
p.registerPrefix(token.INT, p.parseIntegerLiteral)
p.registerPrefix(token.BANG, p.parsePrefixExpression)
p.registerPrefix(token.MINUS, p.parsePrefixExpression)

return p
}
Expand Down Expand Up @@ -166,6 +168,7 @@ func (p *Parser) parseReturnStatment() *ast.ReturnStatement {
func (p *Parser) parseExpression(precedence int) ast.Expression {
prefix := p.prefixParseFns[p.curToken.Type]
if prefix == nil {
p.noPrefixParseFnError(p.curToken.Type)
return nil
}

Expand All @@ -186,6 +189,21 @@ func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement {
return stmt
}

func (p *Parser) parsePrefixExpression() ast.Expression {
expression := &ast.PrefixExpression{
Token: p.curToken,
Operator: p.curToken.Literal,
}
p.nextToken()
expression.Right = p.parseExpression(PREFIX)
return expression
}

func (p *Parser) noPrefixParseFnError(t token.TokenType) {
msg := fmt.Sprintf("no prefix parse function for %s found", t)
p.errors = append(p.errors, msg)
}

type (
prefixParseFn func() ast.Expression
infixParseFn func(ast.Expression) ast.Expression
Expand Down
60 changes: 60 additions & 0 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package parser
import (
"Go-interpreter/ast"
"Go-interpreter/lexer"
"fmt"
"testing"
)

Expand Down Expand Up @@ -158,3 +159,62 @@ func TestIntegerLiteralExpression(t *testing.T) {
literal.TokenLiteral())
}
}

func TestParsingPrefixExressions(t *testing.T) {
prefixTests := []struct {
input string
operator string
integerValue int64
}{
{"!5", "!", 5},
{"-15", "-", 15},
}

for _, tt := range prefixTests {
l := lexer.New(tt.input)
p := New(l)

program := p.ParseProgram()
checkParserErrors(t, p)

if len(program.Statements) != 1 {
t.Fatalf("program.Statements does not contain %d statements. got=%d\n", 1, 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])
}

exp, ok := stmt.Expression.(*ast.PrefixExpression)
if !ok {
t.Fatalf("stmt is not ast.PrefixExpression. got=%T", stmt.Expression)
}

if exp.Operator != tt.operator {
t.Fatalf("exp.Operator is not '%s'. got=%T", tt.operator, exp.Operator)
}

if !testIntegerLiteral(t, exp.Right, tt.integerValue) {
return
}
}
}

func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
integ, ok := il.(*ast.IntegerLiteral)
if !ok {
t.Errorf("il not *ast.IntegerLiteral. got=%T", il)
return false
}
if integ.Value != value {
t.Errorf("integ.Value not %d. got=%d", value, integ.Value)
return false
}
if integ.TokenLiteral() != fmt.Sprintf("%d", value) {
t.Errorf("integ.TokenLiteral not %d. got=%s", value,
integ.TokenLiteral())
return false
}
return true
}

0 comments on commit 24c587d

Please sign in to comment.