bind expression (:=) instead of let
This commit is contained in:
@@ -11,7 +11,7 @@ import (
|
||||
const (
|
||||
_ int = iota
|
||||
LOWEST
|
||||
ASSIGN // =
|
||||
ASSIGN // := or =
|
||||
EQUALS // ==
|
||||
LESSGREATER // > or <
|
||||
SUM // +
|
||||
@@ -22,6 +22,7 @@ const (
|
||||
)
|
||||
|
||||
var precedences = map[token.TokenType]int{
|
||||
token.BIND: ASSIGN,
|
||||
token.ASSIGN: ASSIGN,
|
||||
token.EQ: EQUALS,
|
||||
token.NOT_EQ: EQUALS,
|
||||
@@ -89,6 +90,7 @@ func New(l *lexer.Lexer) *Parser {
|
||||
p.registerInfix(token.GTE, p.parseInfixExpression)
|
||||
p.registerInfix(token.LPAREN, p.parseCallExpression)
|
||||
p.registerInfix(token.LBRACKET, p.parseIndexExpression)
|
||||
p.registerInfix(token.BIND, p.parseBindExpression)
|
||||
p.registerInfix(token.ASSIGN, p.parseAssignmentExpression)
|
||||
p.registerInfix(token.DOT, p.parseSelectorExpression)
|
||||
|
||||
@@ -161,8 +163,6 @@ func (p *Parser) parseStatement() ast.Statement {
|
||||
switch p.curToken.Type {
|
||||
case token.COMMENT:
|
||||
return p.parseComment()
|
||||
case token.LET:
|
||||
return p.parseLetStatement()
|
||||
case token.RETURN:
|
||||
return p.parseReturnStatement()
|
||||
default:
|
||||
@@ -170,34 +170,6 @@ func (p *Parser) parseStatement() ast.Statement {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseLetStatement() *ast.LetStatement {
|
||||
stmt := &ast.LetStatement{Token: p.curToken}
|
||||
|
||||
if !p.expectPeek(token.IDENT) {
|
||||
return nil
|
||||
}
|
||||
|
||||
stmt.Name = &ast.Identifier{Token: p.curToken, Value: p.curToken.Literal}
|
||||
|
||||
if !p.expectPeek(token.ASSIGN) {
|
||||
return nil
|
||||
}
|
||||
|
||||
p.nextToken()
|
||||
|
||||
stmt.Value = p.parseExpression(LOWEST)
|
||||
|
||||
if fl, ok := stmt.Value.(*ast.FunctionLiteral); ok {
|
||||
fl.Name = stmt.Name.Value
|
||||
}
|
||||
|
||||
if p.peekTokenIs(token.SEMICOLON) {
|
||||
p.nextToken()
|
||||
}
|
||||
|
||||
return stmt
|
||||
}
|
||||
|
||||
func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
|
||||
stmt := &ast.ReturnStatement{Token: p.curToken}
|
||||
|
||||
@@ -578,3 +550,26 @@ func (p *Parser) parseSelectorExpression(expression ast.Expression) ast.Expressi
|
||||
index := &ast.StringLiteral{Token: p.curToken, Value: p.curToken.Literal}
|
||||
return &ast.IndexExpression{Left: expression, Index: index}
|
||||
}
|
||||
|
||||
func (p *Parser) parseBindExpression(expression ast.Expression) ast.Expression {
|
||||
switch node := expression.(type) {
|
||||
case *ast.Identifier:
|
||||
default:
|
||||
msg := fmt.Sprintf("expected identifier opn left but got %T %#v", node, expression)
|
||||
p.errors = append(p.errors, msg)
|
||||
return nil
|
||||
}
|
||||
|
||||
be := &ast.BindExpression{Token: p.curToken, Left: expression}
|
||||
|
||||
p.nextToken()
|
||||
|
||||
be.Value = p.parseExpression(LOWEST)
|
||||
|
||||
if fl, ok := be.Value.(*ast.FunctionLiteral); ok {
|
||||
ident := be.Left.(*ast.Identifier)
|
||||
fl.Name = ident.Value
|
||||
}
|
||||
|
||||
return be
|
||||
}
|
||||
|
||||
@@ -8,15 +8,14 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLetStatements(t *testing.T) {
|
||||
func TestBindExpressions(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expectedIdentifier string
|
||||
expectedValue interface{}
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"let x = 5;", "x", 5},
|
||||
{"let y = true;", "y", true},
|
||||
{"let foobar = y;", "foobar", "y"},
|
||||
{"x := 5;", "x:=5"},
|
||||
{"y := true;", "y:=true"},
|
||||
{"foobar := y;", "foobar:=y"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -25,20 +24,7 @@ func TestLetStatements(t *testing.T) {
|
||||
program := p.ParseProgram()
|
||||
checkParserErrors(t, p)
|
||||
|
||||
if len(program.Statements) != 1 {
|
||||
t.Fatalf("program.Statements does not contain 1 statements. got=%d",
|
||||
len(program.Statements))
|
||||
}
|
||||
|
||||
stmt := program.Statements[0]
|
||||
if !testLetStatement(t, stmt, tt.expectedIdentifier) {
|
||||
return
|
||||
}
|
||||
|
||||
val := stmt.(*ast.LetStatement).Value
|
||||
if !testLiteralExpression(t, val, tt.expectedValue) {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.expected, program.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -994,35 +980,17 @@ func TestParsingHashLiteralsWithExpressions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestFunctionLiteralWithName(t *testing.T) {
|
||||
input := `let myFunction = fn() { };`
|
||||
func TestFunctionDefinitionParsing(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
input := `add := 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.Body does not contain %d statements. got=%d\n",
|
||||
1, len(program.Statements))
|
||||
}
|
||||
|
||||
stmt, ok := program.Statements[0].(*ast.LetStatement)
|
||||
if !ok {
|
||||
t.Fatalf("program.Statements[0] is not ast.LetStatement. got=%T",
|
||||
program.Statements[0])
|
||||
}
|
||||
|
||||
function, ok := stmt.Value.(*ast.FunctionLiteral)
|
||||
if !ok {
|
||||
t.Fatalf("stmt.Value is not ast.FunctionLiteral. got=%T",
|
||||
stmt.Value)
|
||||
}
|
||||
|
||||
if function.Name != "myFunction" {
|
||||
t.Fatalf("function literal name wrong. want 'myFunction', got=%q\n",
|
||||
function.Name)
|
||||
}
|
||||
assert.Equal("add:=fn<add>(x, y) (x + y)", program.String())
|
||||
}
|
||||
|
||||
func TestWhileExpression(t *testing.T) {
|
||||
@@ -1094,32 +1062,6 @@ func TestAssignmentExpressions(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testLetStatement(t *testing.T, s ast.Statement, name string) bool {
|
||||
if s.TokenLiteral() != "let" {
|
||||
t.Errorf("s.TokenLiteral not 'let'. got=%q", s.TokenLiteral())
|
||||
return false
|
||||
}
|
||||
|
||||
letStmt, ok := s.(*ast.LetStatement)
|
||||
if !ok {
|
||||
t.Errorf("s not *ast.LetStatement. got=%T", s)
|
||||
return false
|
||||
}
|
||||
|
||||
if letStmt.Name.Value != name {
|
||||
t.Errorf("letStmt.Name.Value not '%s'. got=%s", name, letStmt.Name.Value)
|
||||
return false
|
||||
}
|
||||
|
||||
if letStmt.Name.TokenLiteral() != name {
|
||||
t.Errorf("letStmt.Name.TokenLiteral() not '%s'. got=%s",
|
||||
name, letStmt.Name.TokenLiteral())
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func testInfixExpression(t *testing.T, exp ast.Expression, left interface{},
|
||||
operator string, right interface{}) bool {
|
||||
|
||||
@@ -1249,7 +1191,7 @@ func TestComments(t *testing.T) {
|
||||
{"#!monkey", "!monkey"},
|
||||
{"# foo", " foo"},
|
||||
{" # foo", " foo"},
|
||||
{" // let x = 1", " let x = 1"},
|
||||
{" // x := 1", " x := 1"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
|
||||
Reference in New Issue
Block a user