Skip to content

Commit

Permalink
2.6: Add parser of identifier
Browse files Browse the repository at this point in the history
  • Loading branch information
nibral committed Jan 12, 2019
1 parent 64b4e6f commit 763c848
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 10 deletions.
63 changes: 56 additions & 7 deletions ast/ast.go
@@ -1,9 +1,13 @@
package ast

import "monkey_interpreter/token"
import (
"bytes"
"monkey_interpreter/token"
)

type Node interface {
TokenLiteral() string
String() string
}

type Statement interface {
Expand All @@ -28,6 +32,25 @@ func (p *Program) TokenLiteral() string {
}
}

func (p *Program) String() string {
var out bytes.Buffer

for _, s := range p.Statements {
out.WriteString(s.String())
}

return out.String()
}

type Identifier struct {
Token token.Token // the token.IDENT token
Value string
}

func (i *Identifier) expressionNode() {}
func (i *Identifier) TokenLiteral() string { return i.Token.Literal }
func (i *Identifier) String() string { return i.Value }

type LetStatement struct {
Token token.Token // the token.LET token
Name *Identifier
Expand All @@ -36,14 +59,15 @@ type LetStatement struct {

func (ls *LetStatement) statementNode() {}
func (ls *LetStatement) TokenLiteral() string { return ls.Token.Literal }
func (ls *LetStatement) String() string {
var out bytes.Buffer

type Identifier struct {
Token token.Token // the token.IDENT token
Value string
}
out.WriteString(ls.TokenLiteral() + " ")
out.WriteString(ls.Name.String())
out.WriteString(" = ")

func (i *Identifier) statementNode() {}
func (i *Identifier) TokenLiteral() string { return i.Token.Literal }
return out.String()
}

type ReturnStatement struct {
Token token.Token // the token.RETURN token
Expand All @@ -52,3 +76,28 @@ type ReturnStatement struct {

func (rs *ReturnStatement) statementNode() {}
func (rs *ReturnStatement) TokenLiteral() string { return rs.Token.Literal }
func (rs *ReturnStatement) String() string {
var out bytes.Buffer

out.WriteString(rs.TokenLiteral() + " ")

if rs.ReturnValue != nil {
out.WriteString(rs.ReturnValue.String())
}

return out.String()
}

type ExpressionStatement struct {
Token token.Token // the first token of the expression
Expression Expression
}

func (es *ExpressionStatement) statementNode() {}
func (es *ExpressionStatement) TokenLiteral() string { return es.Token.Literal }
func (es *ExpressionStatement) String() string {
if es.Expression != nil {
return es.Expression.String()
}
return ""
}
35 changes: 35 additions & 0 deletions ast/ast_test.go
@@ -0,0 +1,35 @@
package ast

import (
"monkey_interpreter/token"
"testing"
)

func TestString(t *testing.T) {
program := &Program{
Statements: []Statement{
&LetStatement{
Token: token.Token{
Type: token.LET,
Literal: "let",
},
Name: &Identifier{
Token: token.Token{
Type: token.IDENT,
Literal: "myVar",
},
},
Value: &Identifier{
Token: token.Token{
Type: token.IDENT,
Literal: "anotherVar",
},
},
},
},
}

if program.String() != "lat myVar = anotherVar;" {
t.Errorf("program.String() wrong. got=%q", program.String())
}
}
61 changes: 60 additions & 1 deletion parser/parser.go
Expand Up @@ -7,13 +7,32 @@ import (
"monkey_interpreter/token"
)

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

type (
prefixParseFn func() ast.Expression
infixParseFn func(expression 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 @@ -26,9 +45,20 @@ func New(l *lexer.Lexer) *Parser {
p.nextToken()
p.nextToken()

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

return p
}

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
}

func (p *Parser) Errors() []string {
return p.errors
}
Expand Down Expand Up @@ -65,7 +95,7 @@ func (p *Parser) parseStatement() ast.Statement {
case token.RETURN:
return p.parseReturnStatement()
default:
return nil
return p.parseExpressionStatement()
}
}

Expand Down Expand Up @@ -121,3 +151,32 @@ func (p *Parser) parseReturnStatement() *ast.ReturnStatement {

return stmt
}

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
}

func (p *Parser) parseIdentifier() ast.Expression {
return &ast.Identifier{
Token: p.curToken,
Value: p.curToken.Literal,
}
}
30 changes: 28 additions & 2 deletions parser/parser_test.go
Expand Up @@ -14,7 +14,6 @@ let foobar = 838383;
`
l := lexer.New(input)
p := New(l)

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

Expand Down Expand Up @@ -85,7 +84,6 @@ return 993322;

l := lexer.New(input)
p := New(l)

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

Expand All @@ -105,3 +103,31 @@ return 993322;
}
}
}

func TestIdentifierExpression(t *testing.T) {
input := `foobar;`

l := lexer.New(input)
p := New(l)
program := p.ParseProgram()
checkParserErrors(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())
}
}

0 comments on commit 763c848

Please sign in to comment.