parse calls, returns, and expressions
This commit is contained in:
239
ast/ast.go
239
ast/ast.go
@@ -3,18 +3,22 @@ package ast
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"monkey/token"
|
"monkey/token"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// The base Node interface
|
||||||
type Node interface {
|
type Node interface {
|
||||||
TokenLiteral() string
|
TokenLiteral() string
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All statement nodes implement this
|
||||||
type Statement interface {
|
type Statement interface {
|
||||||
Node
|
Node
|
||||||
statementNode()
|
statementNode()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// All expression nodes implement this
|
||||||
type Expression interface {
|
type Expression interface {
|
||||||
Node
|
Node
|
||||||
expressionNode()
|
expressionNode()
|
||||||
@@ -42,67 +46,15 @@ func (p *Program) String() string {
|
|||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
type Identifier struct {
|
// Statements
|
||||||
Token token.Token // the token.Ident token
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
type LetStatement struct {
|
type LetStatement struct {
|
||||||
Token token.Token // the token.Let token
|
Token token.Token // the token.LET token
|
||||||
Name *Identifier
|
Name *Identifier
|
||||||
Value Expression
|
Value Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReturnStatement struct {
|
func (ls *LetStatement) statementNode() {}
|
||||||
Token token.Token // the 'return token
|
func (ls *LetStatement) TokenLiteral() string { return ls.Token.Literal }
|
||||||
ReturnValue Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
type ExpressionStatement struct {
|
|
||||||
Token token.Token // the first token of the expression
|
|
||||||
Expression Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
type IntegerLiteral struct {
|
|
||||||
Token token.Token
|
|
||||||
Value int64
|
|
||||||
}
|
|
||||||
|
|
||||||
type PrefixExpression struct {
|
|
||||||
Token token.Token
|
|
||||||
Operator string
|
|
||||||
Right Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
type InfixExpression struct {
|
|
||||||
Token token.Token
|
|
||||||
Left Expression
|
|
||||||
Operator string
|
|
||||||
Right Expression
|
|
||||||
}
|
|
||||||
|
|
||||||
type Boolean struct {
|
|
||||||
Token token.Token
|
|
||||||
Value bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type BlockStatement struct {
|
|
||||||
Token token.Token // the { token
|
|
||||||
Statements []Statement
|
|
||||||
}
|
|
||||||
|
|
||||||
type IfExpression struct {
|
|
||||||
Token token.Token // The 'if' token
|
|
||||||
Condition Expression
|
|
||||||
Consequence *BlockStatement
|
|
||||||
Alternative *BlockStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ls *LetStatement) statementNode() {
|
|
||||||
}
|
|
||||||
func (ls *LetStatement) TokenLiteral() string {
|
|
||||||
return ls.Token.Literal
|
|
||||||
}
|
|
||||||
func (ls *LetStatement) String() string {
|
func (ls *LetStatement) String() string {
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
@@ -119,18 +71,13 @@ func (ls *LetStatement) String() string {
|
|||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Identifier) expressionNode() {
|
type ReturnStatement struct {
|
||||||
|
Token token.Token // the 'return' token
|
||||||
|
ReturnValue Expression
|
||||||
}
|
}
|
||||||
func (i *Identifier) TokenLiteral() string {
|
|
||||||
return i.Token.Literal
|
|
||||||
}
|
|
||||||
func (i *Identifier) String() string { return i.Value }
|
|
||||||
|
|
||||||
func (rs *ReturnStatement) statementNode() {
|
func (rs *ReturnStatement) statementNode() {}
|
||||||
}
|
func (rs *ReturnStatement) TokenLiteral() string { return rs.Token.Literal }
|
||||||
func (rs *ReturnStatement) TokenLiteral() string {
|
|
||||||
return rs.Token.Literal
|
|
||||||
}
|
|
||||||
func (rs *ReturnStatement) String() string {
|
func (rs *ReturnStatement) String() string {
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
@@ -145,32 +92,73 @@ func (rs *ReturnStatement) String() string {
|
|||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *ExpressionStatement) statementNode() {
|
type ExpressionStatement struct {
|
||||||
}
|
Token token.Token // the first token of the expression
|
||||||
func (es *ExpressionStatement) TokenLiteral() string {
|
Expression Expression
|
||||||
return es.Token.Literal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (es *ExpressionStatement) statementNode() {}
|
||||||
|
func (es *ExpressionStatement) TokenLiteral() string { return es.Token.Literal }
|
||||||
func (es *ExpressionStatement) String() string {
|
func (es *ExpressionStatement) String() string {
|
||||||
if es.Expression != nil {
|
if es.Expression != nil {
|
||||||
return es.Expression.String()
|
return es.Expression.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (il *IntegerLiteral) expressionNode() {
|
type BlockStatement struct {
|
||||||
|
Token token.Token // the { token
|
||||||
|
Statements []Statement
|
||||||
}
|
}
|
||||||
func (il *IntegerLiteral) TokenLiteral() string {
|
|
||||||
return il.Token.Literal
|
func (bs *BlockStatement) statementNode() {}
|
||||||
|
func (bs *BlockStatement) TokenLiteral() string { return bs.Token.Literal }
|
||||||
|
func (bs *BlockStatement) String() string {
|
||||||
|
var out bytes.Buffer
|
||||||
|
|
||||||
|
for _, s := range bs.Statements {
|
||||||
|
out.WriteString(s.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return out.String()
|
||||||
}
|
}
|
||||||
func (il *IntegerLiteral) String() string {
|
|
||||||
return il.Token.Literal
|
// Expressions
|
||||||
|
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 Boolean struct {
|
||||||
|
Token token.Token
|
||||||
|
Value bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Boolean) expressionNode() {}
|
||||||
|
func (b *Boolean) TokenLiteral() string { return b.Token.Literal }
|
||||||
|
func (b *Boolean) String() string { return b.Token.Literal }
|
||||||
|
|
||||||
|
type IntegerLiteral struct {
|
||||||
|
Token token.Token
|
||||||
|
Value int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (il *IntegerLiteral) expressionNode() {}
|
||||||
|
func (il *IntegerLiteral) TokenLiteral() string { return il.Token.Literal }
|
||||||
|
func (il *IntegerLiteral) String() string { return il.Token.Literal }
|
||||||
|
|
||||||
|
type PrefixExpression struct {
|
||||||
|
Token token.Token // The prefix token, e.g. !
|
||||||
|
Operator string
|
||||||
|
Right Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pe *PrefixExpression) expressionNode() {}
|
func (pe *PrefixExpression) expressionNode() {}
|
||||||
func (pe *PrefixExpression) TokenLiteral() string {
|
func (pe *PrefixExpression) TokenLiteral() string { return pe.Token.Literal }
|
||||||
return pe.Token.Literal
|
|
||||||
}
|
|
||||||
func (pe *PrefixExpression) String() string {
|
func (pe *PrefixExpression) String() string {
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
@@ -182,10 +170,15 @@ func (pe *PrefixExpression) String() string {
|
|||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ie *InfixExpression) expressionNode() {}
|
type InfixExpression struct {
|
||||||
func (ie *InfixExpression) TokenLiteral() string {
|
Token token.Token // The operator token, e.g. +
|
||||||
return ie.Token.Literal
|
Left Expression
|
||||||
|
Operator string
|
||||||
|
Right Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ie *InfixExpression) expressionNode() {}
|
||||||
|
func (ie *InfixExpression) TokenLiteral() string { return ie.Token.Literal }
|
||||||
func (ie *InfixExpression) String() string {
|
func (ie *InfixExpression) String() string {
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
@@ -198,32 +191,16 @@ func (ie *InfixExpression) String() string {
|
|||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b Boolean) TokenLiteral() string {
|
type IfExpression struct {
|
||||||
return b.Token.Literal
|
Token token.Token // The 'if' token
|
||||||
|
Condition Expression
|
||||||
|
Consequence *BlockStatement
|
||||||
|
Alternative *BlockStatement
|
||||||
}
|
}
|
||||||
func (b Boolean) String() string {
|
|
||||||
return b.Token.Literal
|
|
||||||
}
|
|
||||||
func (b Boolean) expressionNode() {}
|
|
||||||
|
|
||||||
func (bs BlockStatement) TokenLiteral() string {
|
func (ie *IfExpression) expressionNode() {}
|
||||||
return bs.Token.Literal
|
func (ie *IfExpression) TokenLiteral() string { return ie.Token.Literal }
|
||||||
}
|
func (ie *IfExpression) String() string {
|
||||||
func (bs BlockStatement) String() string {
|
|
||||||
var out bytes.Buffer
|
|
||||||
|
|
||||||
for _, s := range bs.Statements {
|
|
||||||
out.WriteString(s.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
return out.String()
|
|
||||||
}
|
|
||||||
func (bs BlockStatement) statementNode() {}
|
|
||||||
|
|
||||||
func (ie IfExpression) TokenLiteral() string {
|
|
||||||
return ie.Token.Literal
|
|
||||||
}
|
|
||||||
func (ie IfExpression) String() string {
|
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
out.WriteString("if")
|
out.WriteString("if")
|
||||||
@@ -238,4 +215,52 @@ func (ie IfExpression) String() string {
|
|||||||
|
|
||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
func (ie IfExpression) expressionNode() {}
|
|
||||||
|
type FunctionLiteral struct {
|
||||||
|
Token token.Token // The 'fn' token
|
||||||
|
Parameters []*Identifier
|
||||||
|
Body *BlockStatement
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fl *FunctionLiteral) expressionNode() {}
|
||||||
|
func (fl *FunctionLiteral) TokenLiteral() string { return fl.Token.Literal }
|
||||||
|
func (fl *FunctionLiteral) String() string {
|
||||||
|
var out bytes.Buffer
|
||||||
|
|
||||||
|
params := []string{}
|
||||||
|
for _, p := range fl.Parameters {
|
||||||
|
params = append(params, p.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
out.WriteString(fl.TokenLiteral())
|
||||||
|
out.WriteString("(")
|
||||||
|
out.WriteString(strings.Join(params, ", "))
|
||||||
|
out.WriteString(") ")
|
||||||
|
out.WriteString(fl.Body.String())
|
||||||
|
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
type CallExpression struct {
|
||||||
|
Token token.Token // The '(' token
|
||||||
|
Function Expression // Identifier or FunctionLiteral
|
||||||
|
Arguments []Expression
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ce *CallExpression) expressionNode() {}
|
||||||
|
func (ce *CallExpression) TokenLiteral() string { return ce.Token.Literal }
|
||||||
|
func (ce *CallExpression) String() string {
|
||||||
|
var out bytes.Buffer
|
||||||
|
|
||||||
|
args := []string{}
|
||||||
|
for _, a := range ce.Arguments {
|
||||||
|
args = append(args, a.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
out.WriteString(ce.Function.String())
|
||||||
|
out.WriteString("(")
|
||||||
|
out.WriteString(strings.Join(args, ", "))
|
||||||
|
out.WriteString(")")
|
||||||
|
|
||||||
|
return out.String()
|
||||||
|
}
|
||||||
|
|||||||
261
parser/parser.go
261
parser/parser.go
@@ -17,6 +17,7 @@ const (
|
|||||||
PRODUCT // *
|
PRODUCT // *
|
||||||
PREFIX // -X or !X
|
PREFIX // -X or !X
|
||||||
CALL // myFunction(X)
|
CALL // myFunction(X)
|
||||||
|
INDEX // array[index]
|
||||||
)
|
)
|
||||||
|
|
||||||
var precedences = map[token.TokenType]int{
|
var precedences = map[token.TokenType]int{
|
||||||
@@ -28,8 +29,14 @@ var precedences = map[token.TokenType]int{
|
|||||||
token.MINUS: SUM,
|
token.MINUS: SUM,
|
||||||
token.SLASH: PRODUCT,
|
token.SLASH: PRODUCT,
|
||||||
token.ASTERISK: PRODUCT,
|
token.ASTERISK: PRODUCT,
|
||||||
|
token.LPAREN: CALL,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type (
|
||||||
|
prefixParseFn func() ast.Expression
|
||||||
|
infixParseFn func(ast.Expression) ast.Expression
|
||||||
|
)
|
||||||
|
|
||||||
type Parser struct {
|
type Parser struct {
|
||||||
l *lexer.Lexer
|
l *lexer.Lexer
|
||||||
errors []string
|
errors []string
|
||||||
@@ -41,16 +48,6 @@ type Parser struct {
|
|||||||
infixParseFns map[token.TokenType]infixParseFn
|
infixParseFns map[token.TokenType]infixParseFn
|
||||||
}
|
}
|
||||||
|
|
||||||
type (
|
|
||||||
prefixParseFn func() ast.Expression
|
|
||||||
infixParseFn func(expression ast.Expression) ast.Expression
|
|
||||||
)
|
|
||||||
|
|
||||||
func (p *Parser) nextToken() {
|
|
||||||
p.curToken = p.peekToken
|
|
||||||
p.peekToken = p.l.NextToken()
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(l *lexer.Lexer) *Parser {
|
func New(l *lexer.Lexer) *Parser {
|
||||||
p := &Parser{
|
p := &Parser{
|
||||||
l: l,
|
l: l,
|
||||||
@@ -66,6 +63,7 @@ func New(l *lexer.Lexer) *Parser {
|
|||||||
p.registerPrefix(token.FALSE, p.parseBoolean)
|
p.registerPrefix(token.FALSE, p.parseBoolean)
|
||||||
p.registerPrefix(token.LPAREN, p.parseGroupedExpression)
|
p.registerPrefix(token.LPAREN, p.parseGroupedExpression)
|
||||||
p.registerPrefix(token.IF, p.parseIfExpression)
|
p.registerPrefix(token.IF, p.parseIfExpression)
|
||||||
|
p.registerPrefix(token.FUNCTION, p.parseFunctionLiteral)
|
||||||
|
|
||||||
p.infixParseFns = make(map[token.TokenType]infixParseFn)
|
p.infixParseFns = make(map[token.TokenType]infixParseFn)
|
||||||
p.registerInfix(token.PLUS, p.parseInfixExpression)
|
p.registerInfix(token.PLUS, p.parseInfixExpression)
|
||||||
@@ -77,13 +75,53 @@ func New(l *lexer.Lexer) *Parser {
|
|||||||
p.registerInfix(token.LT, p.parseInfixExpression)
|
p.registerInfix(token.LT, p.parseInfixExpression)
|
||||||
p.registerInfix(token.GT, p.parseInfixExpression)
|
p.registerInfix(token.GT, p.parseInfixExpression)
|
||||||
|
|
||||||
// Read two token, so curToken and peekToken are both set
|
p.registerInfix(token.LPAREN, p.parseCallExpression)
|
||||||
|
|
||||||
|
// Read two tokens, so curToken and peekToken are both set
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Parser) nextToken() {
|
||||||
|
p.curToken = p.peekToken
|
||||||
|
p.peekToken = p.l.NextToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) curTokenIs(t token.TokenType) bool {
|
||||||
|
return p.curToken.Type == t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) peekTokenIs(t token.TokenType) bool {
|
||||||
|
return p.peekToken.Type == t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) expectPeek(t token.TokenType) bool {
|
||||||
|
if p.peekTokenIs(t) {
|
||||||
|
p.nextToken()
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
p.peekError(t)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) Errors() []string {
|
||||||
|
return p.errors
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) peekError(t token.TokenType) {
|
||||||
|
msg := fmt.Sprintf("expected next token to be %s, got %s instead",
|
||||||
|
t, p.peekToken.Type)
|
||||||
|
p.errors = append(p.errors, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) noPrefixParseFnError(t token.TokenType) {
|
||||||
|
msg := fmt.Sprintf("no prefix parse function for %s found", t)
|
||||||
|
p.errors = append(p.errors, msg)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Parser) ParseProgram() *ast.Program {
|
func (p *Parser) ParseProgram() *ast.Program {
|
||||||
program := &ast.Program{}
|
program := &ast.Program{}
|
||||||
program.Statements = []ast.Statement{}
|
program.Statements = []ast.Statement{}
|
||||||
@@ -106,7 +144,7 @@ func (p *Parser) parseStatement() ast.Statement {
|
|||||||
case token.RETURN:
|
case token.RETURN:
|
||||||
return p.parseReturnStatement()
|
return p.parseReturnStatement()
|
||||||
default:
|
default:
|
||||||
return p.parseExpressionsStatement()
|
return p.parseExpressionStatement()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,65 +155,38 @@ func (p *Parser) parseLetStatement() *ast.LetStatement {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt.Name = &ast.Identifier{
|
stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
|
||||||
Token: p.curToken,
|
|
||||||
Value: p.curToken.Literal,
|
|
||||||
}
|
|
||||||
|
|
||||||
if !p.expectPeek(token.ASSIGN) {
|
if !p.expectPeek(token.ASSIGN) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: We're skipping expressions until we
|
p.nextToken()
|
||||||
// encounter a semicolon
|
|
||||||
for !p.curTokenIs(token.SEMICOLON) {
|
stmt.Value = p.parseExpression(LOWEST)
|
||||||
|
|
||||||
|
if p.peekTokenIs(token.SEMICOLON) {
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
return stmt
|
return stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) expectPeek(t token.TokenType) bool {
|
|
||||||
if p.peekTokenIs(t) {
|
|
||||||
p.nextToken()
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
p.peekError(t)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) curTokenIs(t token.TokenType) bool {
|
|
||||||
return p.curToken.Type == t
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) peekTokenIs(t token.TokenType) bool {
|
|
||||||
return p.peekToken.Type == t
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) Errors() []string {
|
|
||||||
return p.errors
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) peekError(t token.TokenType) {
|
|
||||||
msg := fmt.Sprintf("expected next token to be %s, got %s instead", t, p.peekToken.Type)
|
|
||||||
p.errors = append(p.errors, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
|
func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
|
||||||
stmt := &ast.ReturnStatement{Token: p.curToken}
|
stmt := &ast.ReturnStatement{Token: p.curToken}
|
||||||
|
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
|
|
||||||
// TODO: We're skipping the expression until we encounter a semicolon
|
stmt.ReturnValue = p.parseExpression(LOWEST)
|
||||||
for !p.curTokenIs(token.SEMICOLON) {
|
|
||||||
|
if p.peekTokenIs(token.SEMICOLON) {
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
}
|
}
|
||||||
|
|
||||||
return stmt
|
return stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseExpressionsStatement() *ast.ExpressionStatement {
|
func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement {
|
||||||
stmt := &ast.ExpressionStatement{Token: p.curToken}
|
stmt := &ast.ExpressionStatement{Token: p.curToken}
|
||||||
|
|
||||||
stmt.Expression = p.parseExpression(LOWEST)
|
stmt.Expression = p.parseExpression(LOWEST)
|
||||||
@@ -209,6 +220,26 @@ func (p *Parser) parseExpression(precedence int) ast.Expression {
|
|||||||
return leftExp
|
return leftExp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Parser) peekPrecedence() int {
|
||||||
|
if p, ok := precedences[p.peekToken.Type]; ok {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
return LOWEST
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) curPrecedence() int {
|
||||||
|
if p, ok := precedences[p.curToken.Type]; ok {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
return LOWEST
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseIdentifier() ast.Expression {
|
||||||
|
return &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Parser) parseIntegerLiteral() ast.Expression {
|
func (p *Parser) parseIntegerLiteral() ast.Expression {
|
||||||
lit := &ast.IntegerLiteral{Token: p.curToken}
|
lit := &ast.IntegerLiteral{Token: p.curToken}
|
||||||
|
|
||||||
@@ -224,26 +255,6 @@ func (p *Parser) parseIntegerLiteral() ast.Expression {
|
|||||||
return lit
|
return lit
|
||||||
}
|
}
|
||||||
|
|
||||||
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) parseIdentifier() ast.Expression {
|
|
||||||
return &ast.Identifier{
|
|
||||||
Token: p.curToken,
|
|
||||||
Value: p.curToken.Literal,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) noPrefixParseFnError(t token.TokenType) {
|
|
||||||
msg := fmt.Sprintf("no prefix parse function for %s found", t)
|
|
||||||
p.errors = append(p.errors, msg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) parsePrefixExpression() ast.Expression {
|
func (p *Parser) parsePrefixExpression() ast.Expression {
|
||||||
expression := &ast.PrefixExpression{
|
expression := &ast.PrefixExpression{
|
||||||
Token: p.curToken,
|
Token: p.curToken,
|
||||||
@@ -257,22 +268,6 @@ func (p *Parser) parsePrefixExpression() ast.Expression {
|
|||||||
return expression
|
return expression
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) peekPrecedence() int {
|
|
||||||
if p, ok := precedences[p.peekToken.Type]; ok {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
return LOWEST
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) curPrecedence() int {
|
|
||||||
if p, ok := precedences[p.curToken.Type]; ok {
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
|
|
||||||
return LOWEST
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
|
func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
|
||||||
expression := &ast.InfixExpression{
|
expression := &ast.InfixExpression{
|
||||||
Token: p.curToken,
|
Token: p.curToken,
|
||||||
@@ -288,10 +283,7 @@ func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseBoolean() ast.Expression {
|
func (p *Parser) parseBoolean() ast.Expression {
|
||||||
return &ast.Boolean{
|
return &ast.Boolean{Token: p.curToken, Value: p.curTokenIs(token.TRUE)}
|
||||||
Token: p.curToken,
|
|
||||||
Value: p.curTokenIs(token.TRUE),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseGroupedExpression() ast.Expression {
|
func (p *Parser) parseGroupedExpression() ast.Expression {
|
||||||
@@ -299,7 +291,7 @@ func (p *Parser) parseGroupedExpression() ast.Expression {
|
|||||||
|
|
||||||
exp := p.parseExpression(LOWEST)
|
exp := p.parseExpression(LOWEST)
|
||||||
|
|
||||||
if p.expectPeek(token.RPAREN) {
|
if !p.expectPeek(token.RPAREN) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,7 +316,7 @@ func (p *Parser) parseIfExpression() ast.Expression {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
expression.Consequence = p.parseBlockExpression()
|
expression.Consequence = p.parseBlockStatement()
|
||||||
|
|
||||||
if p.peekTokenIs(token.ELSE) {
|
if p.peekTokenIs(token.ELSE) {
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
@@ -333,13 +325,13 @@ func (p *Parser) parseIfExpression() ast.Expression {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
expression.Alternative = p.parseBlockExpression()
|
expression.Alternative = p.parseBlockStatement()
|
||||||
}
|
}
|
||||||
|
|
||||||
return expression
|
return expression
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseBlockExpression() *ast.BlockStatement {
|
func (p *Parser) parseBlockStatement() *ast.BlockStatement {
|
||||||
block := &ast.BlockStatement{Token: p.curToken}
|
block := &ast.BlockStatement{Token: p.curToken}
|
||||||
block.Statements = []ast.Statement{}
|
block.Statements = []ast.Statement{}
|
||||||
|
|
||||||
@@ -355,3 +347,86 @@ func (p *Parser) parseBlockExpression() *ast.BlockStatement {
|
|||||||
|
|
||||||
return block
|
return block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseFunctionLiteral() ast.Expression {
|
||||||
|
lit := &ast.FunctionLiteral{Token: p.curToken}
|
||||||
|
|
||||||
|
if !p.expectPeek(token.LPAREN) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lit.Parameters = p.parseFunctionParameters()
|
||||||
|
|
||||||
|
if !p.expectPeek(token.LBRACE) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lit.Body = p.parseBlockStatement()
|
||||||
|
|
||||||
|
return lit
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseFunctionParameters() []*ast.Identifier {
|
||||||
|
identifiers := []*ast.Identifier{}
|
||||||
|
|
||||||
|
if p.peekTokenIs(token.RPAREN) {
|
||||||
|
p.nextToken()
|
||||||
|
return identifiers
|
||||||
|
}
|
||||||
|
|
||||||
|
p.nextToken()
|
||||||
|
|
||||||
|
ident := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
|
||||||
|
identifiers = append(identifiers, ident)
|
||||||
|
|
||||||
|
for p.peekTokenIs(token.COMMA) {
|
||||||
|
p.nextToken()
|
||||||
|
p.nextToken()
|
||||||
|
ident := &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
|
||||||
|
identifiers = append(identifiers, ident)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.expectPeek(token.RPAREN) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return identifiers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression {
|
||||||
|
exp := &ast.CallExpression{Token: p.curToken, Function: function}
|
||||||
|
exp.Arguments = p.parseExpressionList(token.RPAREN)
|
||||||
|
return exp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Parser) parseExpressionList(end token.TokenType) []ast.Expression {
|
||||||
|
list := []ast.Expression{}
|
||||||
|
|
||||||
|
if p.peekTokenIs(end) {
|
||||||
|
p.nextToken()
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
p.nextToken()
|
||||||
|
list = append(list, p.parseExpression(LOWEST))
|
||||||
|
|
||||||
|
for p.peekTokenIs(token.COMMA) {
|
||||||
|
p.nextToken()
|
||||||
|
p.nextToken()
|
||||||
|
list = append(list, p.parseExpression(LOWEST))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.expectPeek(end) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return list
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -356,6 +356,43 @@ func TestOperatorPrecedenceParsing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestBooleanExpression(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
expectedBoolean bool
|
||||||
|
}{
|
||||||
|
{"true;", true},
|
||||||
|
{"false;", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
l := lexer.New(tt.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])
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean, ok := stmt.Expression.(*ast.Boolean)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("exp not *ast.Boolean. got=%T", stmt.Expression)
|
||||||
|
}
|
||||||
|
if boolean.Value != tt.expectedBoolean {
|
||||||
|
t.Errorf("boolean.Value not %t. got=%t", tt.expectedBoolean,
|
||||||
|
boolean.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestIfExpression(t *testing.T) {
|
func TestIfExpression(t *testing.T) {
|
||||||
input := `if (x < y) { x }`
|
input := `if (x < y) { x }`
|
||||||
|
|
||||||
@@ -464,6 +501,175 @@ func TestIfElseExpression(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFunctionLiteralParsing(t *testing.T) {
|
||||||
|
input := `fn(x, y) { x + y; }`
|
||||||
|
|
||||||
|
l := lexer.New(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])
|
||||||
|
}
|
||||||
|
|
||||||
|
function, ok := stmt.Expression.(*ast.FunctionLiteral)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("stmt.Expression is not ast.FunctionLiteral. got=%T",
|
||||||
|
stmt.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(function.Parameters) != 2 {
|
||||||
|
t.Fatalf("function literal parameters wrong. want 2, got=%d\n",
|
||||||
|
len(function.Parameters))
|
||||||
|
}
|
||||||
|
|
||||||
|
testLiteralExpression(t, function.Parameters[0], "x")
|
||||||
|
testLiteralExpression(t, function.Parameters[1], "y")
|
||||||
|
|
||||||
|
if len(function.Body.Statements) != 1 {
|
||||||
|
t.Fatalf("function.Body.Statements has not 1 statements. got=%d\n",
|
||||||
|
len(function.Body.Statements))
|
||||||
|
}
|
||||||
|
|
||||||
|
bodyStmt, ok := function.Body.Statements[0].(*ast.ExpressionStatement)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("function body stmt is not ast.ExpressionStatement. got=%T",
|
||||||
|
function.Body.Statements[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
testInfixExpression(t, bodyStmt.Expression, "x", "+", "y")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFunctionParameterParsing(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
expectedParams []string
|
||||||
|
}{
|
||||||
|
{input: "fn() {};", expectedParams: []string{}},
|
||||||
|
{input: "fn(x) {};", expectedParams: []string{"x"}},
|
||||||
|
{input: "fn(x, y, z) {};", expectedParams: []string{"x", "y", "z"}},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
l := lexer.New(tt.input)
|
||||||
|
p := New(l)
|
||||||
|
program := p.ParseProgram()
|
||||||
|
checkParserErrors(t, p)
|
||||||
|
|
||||||
|
stmt := program.Statements[0].(*ast.ExpressionStatement)
|
||||||
|
function := stmt.Expression.(*ast.FunctionLiteral)
|
||||||
|
|
||||||
|
if len(function.Parameters) != len(tt.expectedParams) {
|
||||||
|
t.Errorf("length parameters wrong. want %d, got=%d\n",
|
||||||
|
len(tt.expectedParams), len(function.Parameters))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, ident := range tt.expectedParams {
|
||||||
|
testLiteralExpression(t, function.Parameters[i], ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCallExpressionParsing(t *testing.T) {
|
||||||
|
input := "add(1, 2 * 3, 4 + 5);"
|
||||||
|
|
||||||
|
l := lexer.New(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("stmt is not ast.ExpressionStatement. got=%T",
|
||||||
|
program.Statements[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
exp, ok := stmt.Expression.(*ast.CallExpression)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("stmt.Expression is not ast.CallExpression. got=%T",
|
||||||
|
stmt.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testIdentifier(t, exp.Function, "add") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(exp.Arguments) != 3 {
|
||||||
|
t.Fatalf("wrong length of arguments. got=%d", len(exp.Arguments))
|
||||||
|
}
|
||||||
|
|
||||||
|
testLiteralExpression(t, exp.Arguments[0], 1)
|
||||||
|
testInfixExpression(t, exp.Arguments[1], 2, "*", 3)
|
||||||
|
testInfixExpression(t, exp.Arguments[2], 4, "+", 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCallExpressionParameterParsing(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
input string
|
||||||
|
expectedIdent string
|
||||||
|
expectedArgs []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
input: "add();",
|
||||||
|
expectedIdent: "add",
|
||||||
|
expectedArgs: []string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "add(1);",
|
||||||
|
expectedIdent: "add",
|
||||||
|
expectedArgs: []string{"1"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: "add(1, 2 * 3, 4 + 5);",
|
||||||
|
expectedIdent: "add",
|
||||||
|
expectedArgs: []string{"1", "(2 * 3)", "(4 + 5)"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
l := lexer.New(tt.input)
|
||||||
|
p := New(l)
|
||||||
|
program := p.ParseProgram()
|
||||||
|
checkParserErrors(t, p)
|
||||||
|
|
||||||
|
stmt := program.Statements[0].(*ast.ExpressionStatement)
|
||||||
|
exp, ok := stmt.Expression.(*ast.CallExpression)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("stmt.Expression is not ast.CallExpression. got=%T",
|
||||||
|
stmt.Expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !testIdentifier(t, exp.Function, tt.expectedIdent) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(exp.Arguments) != len(tt.expectedArgs) {
|
||||||
|
t.Fatalf("wrong number of arguments. want=%d, got=%d",
|
||||||
|
len(tt.expectedArgs), len(exp.Arguments))
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, arg := range tt.expectedArgs {
|
||||||
|
if exp.Arguments[i].String() != arg {
|
||||||
|
t.Errorf("argument %d wrong. want=%q, got=%q", i,
|
||||||
|
arg, exp.Arguments[i].String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
|
func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
|
||||||
if s.TokenLiteral() != "let" {
|
if s.TokenLiteral() != "let" {
|
||||||
t.Errorf("s.TokenLiteral not 'let'. got=%q", s.TokenLiteral())
|
t.Errorf("s.TokenLiteral not 'let'. got=%q", s.TokenLiteral())
|
||||||
@@ -534,27 +740,6 @@ func testLiteralExpression(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func testBooleanLiteral(t *testing.T, exp ast.Expression, value bool) bool {
|
|
||||||
bo, ok := exp.(*ast.Boolean)
|
|
||||||
if !ok {
|
|
||||||
t.Errorf("exp *ast.Boolean. got=%T", exp)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if bo.Value != value {
|
|
||||||
t.Errorf("bo.Value not %t. got=%t", value, bo.Value)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if bo.TokenLiteral() != fmt.Sprintf("%t", value) {
|
|
||||||
t.Errorf("intboeg.TokenLiteral not %t. got=%s", value,
|
|
||||||
bo.TokenLiteral())
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
|
func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool {
|
||||||
integ, ok := il.(*ast.IntegerLiteral)
|
integ, ok := il.(*ast.IntegerLiteral)
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -597,6 +782,27 @@ func testIdentifier(t *testing.T, exp ast.Expression, value string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testBooleanLiteral(t *testing.T, exp ast.Expression, value bool) bool {
|
||||||
|
bo, ok := exp.(*ast.Boolean)
|
||||||
|
if !ok {
|
||||||
|
t.Errorf("exp not *ast.Boolean. got=%T", exp)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if bo.Value != value {
|
||||||
|
t.Errorf("bo.Value not %t. got=%t", value, bo.Value)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if bo.TokenLiteral() != fmt.Sprintf("%t", value) {
|
||||||
|
t.Errorf("bo.TokenLiteral not %t. got=%s",
|
||||||
|
value, bo.TokenLiteral())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func checkParserErrors(t *testing.T, p *Parser) {
|
func checkParserErrors(t *testing.T, p *Parser) {
|
||||||
errors := p.Errors()
|
errors := p.Errors()
|
||||||
if len(errors) == 0 {
|
if len(errors) == 0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user