bind expression (:=) instead of let
This commit is contained in:
77
ast/ast.go
77
ast/ast.go
@@ -47,31 +47,6 @@ func (p *Program) String() string {
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// Statements
|
||||
type LetStatement struct {
|
||||
Token token.Token // the token.LET token
|
||||
Name *Identifier
|
||||
Value Expression
|
||||
}
|
||||
|
||||
func (ls *LetStatement) statementNode() {}
|
||||
func (ls *LetStatement) TokenLiteral() string { return ls.Token.Literal }
|
||||
func (ls *LetStatement) String() string {
|
||||
var out bytes.Buffer
|
||||
|
||||
out.WriteString(ls.TokenLiteral() + " ")
|
||||
out.WriteString(ls.Name.String())
|
||||
out.WriteString(" = ")
|
||||
|
||||
if ls.Value != nil {
|
||||
out.WriteString(ls.Value.String())
|
||||
}
|
||||
|
||||
out.WriteString(";")
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
type ReturnStatement struct {
|
||||
Token token.Token // the 'return' token
|
||||
ReturnValue Expression
|
||||
@@ -284,23 +259,23 @@ type StringLiteral struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
func (sl StringLiteral) TokenLiteral() string {
|
||||
func (sl *StringLiteral) TokenLiteral() string {
|
||||
return sl.Token.Literal
|
||||
}
|
||||
func (sl StringLiteral) String() string {
|
||||
func (sl *StringLiteral) String() string {
|
||||
return sl.Token.Literal
|
||||
}
|
||||
func (sl StringLiteral) expressionNode() {}
|
||||
func (sl *StringLiteral) expressionNode() {}
|
||||
|
||||
type ArrayLiteral struct {
|
||||
Token token.Token // the '[' token
|
||||
Elements []Expression
|
||||
}
|
||||
|
||||
func (al ArrayLiteral) TokenLiteral() string {
|
||||
func (al *ArrayLiteral) TokenLiteral() string {
|
||||
return al.Token.Literal
|
||||
}
|
||||
func (al ArrayLiteral) String() string {
|
||||
func (al *ArrayLiteral) String() string {
|
||||
var out bytes.Buffer
|
||||
|
||||
elements := []string{}
|
||||
@@ -314,7 +289,7 @@ func (al ArrayLiteral) String() string {
|
||||
|
||||
return out.String()
|
||||
}
|
||||
func (al ArrayLiteral) expressionNode() {}
|
||||
func (al *ArrayLiteral) expressionNode() {}
|
||||
|
||||
type IndexExpression struct {
|
||||
Token token.Token // The [ token
|
||||
@@ -322,11 +297,11 @@ type IndexExpression struct {
|
||||
Index Expression
|
||||
}
|
||||
|
||||
func (ie IndexExpression) TokenLiteral() string {
|
||||
func (ie *IndexExpression) TokenLiteral() string {
|
||||
return ie.Token.Literal
|
||||
}
|
||||
|
||||
func (ie IndexExpression) String() string {
|
||||
func (ie *IndexExpression) String() string {
|
||||
var out bytes.Buffer
|
||||
|
||||
out.WriteString("(")
|
||||
@@ -338,18 +313,18 @@ func (ie IndexExpression) String() string {
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func (ie IndexExpression) expressionNode() {}
|
||||
func (ie *IndexExpression) expressionNode() {}
|
||||
|
||||
type HashLiteral struct {
|
||||
Token token.Token // the '{' token
|
||||
Pairs map[Expression]Expression
|
||||
}
|
||||
|
||||
func (hl HashLiteral) TokenLiteral() string {
|
||||
func (hl *HashLiteral) TokenLiteral() string {
|
||||
return hl.Token.Literal
|
||||
}
|
||||
|
||||
func (hl HashLiteral) String() string {
|
||||
func (hl *HashLiteral) String() string {
|
||||
var out bytes.Buffer
|
||||
|
||||
pairs := []string{}
|
||||
@@ -364,7 +339,7 @@ func (hl HashLiteral) String() string {
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func (hl HashLiteral) expressionNode() {}
|
||||
func (hl *HashLiteral) expressionNode() {}
|
||||
|
||||
type WhileExpression struct {
|
||||
Token token.Token // The 'while' token
|
||||
@@ -389,6 +364,28 @@ func (we *WhileExpression) String() string {
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// BindExpression represents an assignment expression of the form:
|
||||
// x := 1
|
||||
type BindExpression struct {
|
||||
Token token.Token // the := token
|
||||
Left Expression
|
||||
Value Expression
|
||||
}
|
||||
|
||||
func (be *BindExpression) TokenLiteral() string {
|
||||
return be.Token.Literal
|
||||
}
|
||||
func (be *BindExpression) String() string {
|
||||
var out bytes.Buffer
|
||||
|
||||
out.WriteString(be.Left.String())
|
||||
out.WriteString(be.TokenLiteral())
|
||||
out.WriteString(be.Value.String())
|
||||
|
||||
return out.String()
|
||||
}
|
||||
func (be *BindExpression) expressionNode() {}
|
||||
|
||||
// AssignmentExpression represents an assignment expression of the form:
|
||||
// x = 1 or xs[1] = 2
|
||||
type AssignmentExpression struct {
|
||||
@@ -397,10 +394,10 @@ type AssignmentExpression struct {
|
||||
Value Expression
|
||||
}
|
||||
|
||||
func (as AssignmentExpression) TokenLiteral() string {
|
||||
func (as *AssignmentExpression) TokenLiteral() string {
|
||||
return as.Token.Literal
|
||||
}
|
||||
func (as AssignmentExpression) String() string {
|
||||
func (as *AssignmentExpression) String() string {
|
||||
var out bytes.Buffer
|
||||
|
||||
out.WriteString(as.Left.String())
|
||||
@@ -409,7 +406,7 @@ func (as AssignmentExpression) String() string {
|
||||
|
||||
return out.String()
|
||||
}
|
||||
func (as AssignmentExpression) expressionNode() {}
|
||||
func (as *AssignmentExpression) expressionNode() {}
|
||||
|
||||
// Comment a comment
|
||||
type Comment struct {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ast
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"monkey/token"
|
||||
"testing"
|
||||
)
|
||||
@@ -8,9 +9,11 @@ import (
|
||||
func TestString(t *testing.T) {
|
||||
program := &Program{
|
||||
Statements: []Statement{
|
||||
&LetStatement{
|
||||
Token: token.Token{Type: token.LET, Literal: "let"},
|
||||
Name: &Identifier{
|
||||
&ExpressionStatement{
|
||||
Token: token.Token{Type: token.IDENT, Literal: "myVar"},
|
||||
Expression: &BindExpression{
|
||||
Token: token.Token{Type: token.BIND, Literal: ":="},
|
||||
Left: &Identifier{
|
||||
Token: token.Token{Type: token.IDENT, Literal: "myVar"},
|
||||
Value: "myVar",
|
||||
},
|
||||
@@ -20,9 +23,8 @@ func TestString(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if program.String() != "let myVar = anotherVar;" {
|
||||
t.Errorf("program.String wrong. got=%q", program.String())
|
||||
}
|
||||
assert.Equal(t, "myVar:=anotherVar", program.String())
|
||||
}
|
||||
|
||||
@@ -292,10 +292,19 @@ func (c *Compiler) Compile(node ast.Node) error {
|
||||
return fmt.Errorf("expected identifier or index expression got=%s", node.Left)
|
||||
}
|
||||
|
||||
case *ast.LetStatement:
|
||||
symbol, ok := c.symbolTable.Resolve(node.Name.Value)
|
||||
case *ast.BindExpression:
|
||||
var symbol Symbol
|
||||
|
||||
if ident, ok := node.Left.(*ast.Identifier); ok {
|
||||
symbol, ok = c.symbolTable.Resolve(ident.Value)
|
||||
if !ok {
|
||||
symbol = c.symbolTable.Define(node.Name.Value)
|
||||
symbol = c.symbolTable.Define(ident.Value)
|
||||
} else {
|
||||
// Local shadowing of previously defined "free" variable in a
|
||||
// function now being rebound to a locally scoped variable.
|
||||
if symbol.Scope == FreeScope {
|
||||
symbol = c.symbolTable.Define(ident.Value)
|
||||
}
|
||||
}
|
||||
|
||||
c.l++
|
||||
@@ -311,6 +320,10 @@ func (c *Compiler) Compile(node ast.Node) error {
|
||||
c.emit(code.OpSetLocal, symbol.Index)
|
||||
}
|
||||
|
||||
} else {
|
||||
return fmt.Errorf("expected identifier got=%s", node.Left)
|
||||
}
|
||||
|
||||
case *ast.Identifier:
|
||||
symbol, ok := c.symbolTable.Resolve(node.Value)
|
||||
if !ok {
|
||||
|
||||
@@ -11,6 +11,8 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type Instructions string
|
||||
|
||||
type compilerTestCase struct {
|
||||
input string
|
||||
expectedConstants []interface{}
|
||||
@@ -210,146 +212,61 @@ func TestBooleanExpressions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConditionals(t *testing.T) {
|
||||
tests := []compilerTestCase{
|
||||
tests := []compilerTestCase2{
|
||||
{
|
||||
input: `
|
||||
if (true) { 10 }; 3333;
|
||||
`,
|
||||
expectedConstants: []interface{}{10, 3333},
|
||||
expectedInstructions: []code.Instructions{
|
||||
// 0000
|
||||
code.Make(code.OpTrue),
|
||||
// 0001
|
||||
code.Make(code.OpJumpNotTruthy, 10),
|
||||
// 0004
|
||||
code.Make(code.OpConstant, 0),
|
||||
// 0007
|
||||
code.Make(code.OpJump, 11),
|
||||
// 0010
|
||||
code.Make(code.OpNull),
|
||||
// 0011
|
||||
code.Make(code.OpPop),
|
||||
// 0012
|
||||
code.Make(code.OpConstant, 1),
|
||||
// 0015
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
constants: []interface{}{10, 3333},
|
||||
instructions: "0000 LoadTrue\n0001 JumpIfFalse 10\n0004 LoadConstant 0\n0007 Jump 11\n0010 LoadNull\n0011 Pop\n0012 LoadConstant 1\n0015 Pop\n",
|
||||
}, {
|
||||
input: `
|
||||
if (true) { 10 } else { 20 }; 3333;
|
||||
`,
|
||||
expectedConstants: []interface{}{10, 20, 3333},
|
||||
expectedInstructions: []code.Instructions{
|
||||
// 0000
|
||||
code.Make(code.OpTrue),
|
||||
// 0001
|
||||
code.Make(code.OpJumpNotTruthy, 10),
|
||||
// 0004
|
||||
code.Make(code.OpConstant, 0),
|
||||
// 0008
|
||||
code.Make(code.OpJump, 13),
|
||||
// 0011
|
||||
code.Make(code.OpConstant, 1),
|
||||
// 0013
|
||||
code.Make(code.OpPop),
|
||||
// 0014
|
||||
code.Make(code.OpConstant, 2),
|
||||
// 0018
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
constants: []interface{}{10, 20, 3333},
|
||||
instructions: "0000 LoadTrue\n0001 JumpIfFalse 10\n0004 LoadConstant 0\n0007 Jump 13\n0010 LoadConstant 1\n0013 Pop\n0014 LoadConstant 2\n0017 Pop\n",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let x = 0; if (true) { x = 1; }; if (false) { x = 2; }
|
||||
`,
|
||||
expectedConstants: []interface{}{0, 1, 2},
|
||||
expectedInstructions: []code.Instructions{
|
||||
// 0000
|
||||
code.Make(code.OpConstant, 0),
|
||||
// 0003
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
// 0006
|
||||
code.Make(code.OpTrue),
|
||||
// 0007
|
||||
code.Make(code.OpJumpNotTruthy, 19),
|
||||
// 0010
|
||||
code.Make(code.OpConstant, 1),
|
||||
// 0013
|
||||
code.Make(code.OpAssignGlobal, 0),
|
||||
// 0018
|
||||
code.Make(code.OpJump, 20),
|
||||
// 0019
|
||||
code.Make(code.OpNull),
|
||||
// 0020
|
||||
code.Make(code.OpPop),
|
||||
// 0021
|
||||
code.Make(code.OpFalse),
|
||||
// 0022
|
||||
code.Make(code.OpJumpNotTruthy, 34),
|
||||
// 0024
|
||||
code.Make(code.OpConstant, 2),
|
||||
// 0028
|
||||
code.Make(code.OpAssignGlobal, 0),
|
||||
// 0032
|
||||
code.Make(code.OpJump, 35),
|
||||
// 0035
|
||||
code.Make(code.OpNull),
|
||||
// 0036
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
constants: []interface{}{0, 1, 2},
|
||||
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadTrue\n0008 JumpIfFalse 20\n0011 LoadConstant 1\n0014 AssignGlobal 0\n0017 Jump 21\n0020 LoadNull\n0021 Pop\n0022 LoadFalse\n0023 JumpIfFalse 35\n0026 LoadConstant 2\n0029 AssignGlobal 0\n0032 Jump 36\n0035 LoadNull\n0036 Pop\n",
|
||||
},
|
||||
}
|
||||
|
||||
runCompilerTests(t, tests)
|
||||
runCompilerTests2(t, tests)
|
||||
}
|
||||
|
||||
func TestGlobalLetStatements(t *testing.T) {
|
||||
tests := []compilerTestCase{
|
||||
func TestGlobalBindExpressions(t *testing.T) {
|
||||
tests := []compilerTestCase2{
|
||||
{
|
||||
input: `
|
||||
let one = 1;
|
||||
let two = 2;
|
||||
one := 1;
|
||||
two := 2;
|
||||
`,
|
||||
expectedConstants: []interface{}{1, 2},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpConstant, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpConstant, 1),
|
||||
code.Make(code.OpSetGlobal, 1),
|
||||
},
|
||||
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadConstant 1\n0010 BindGlobal 1\n0013 Pop\n",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let one = 1;
|
||||
one := 1;
|
||||
one;
|
||||
`,
|
||||
expectedConstants: []interface{}{1},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpConstant, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpGetGlobal, 0),
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
constants: []interface{}{1},
|
||||
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadGlobal 0\n0010 Pop\n",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let one = 1;
|
||||
let two = one;
|
||||
one := 1;
|
||||
two := one;
|
||||
two;
|
||||
`,
|
||||
expectedConstants: []interface{}{1},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpConstant, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpGetGlobal, 0),
|
||||
code.Make(code.OpSetGlobal, 1),
|
||||
code.Make(code.OpGetGlobal, 1),
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
constants: []interface{}{1},
|
||||
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadGlobal 0\n0010 BindGlobal 1\n0013 Pop\n0014 LoadGlobal 1\n0017 Pop\n",
|
||||
},
|
||||
}
|
||||
|
||||
runCompilerTests(t, tests)
|
||||
runCompilerTests2(t, tests)
|
||||
}
|
||||
|
||||
func TestStringExpressions(t *testing.T) {
|
||||
@@ -735,95 +652,53 @@ func TestCompilerScopes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFunctionCalls(t *testing.T) {
|
||||
tests := []compilerTestCase{
|
||||
tests := []compilerTestCase2{
|
||||
{
|
||||
input: `fn() { return 24 }();`,
|
||||
expectedConstants: []interface{}{
|
||||
constants: []interface{}{
|
||||
24,
|
||||
[]code.Instructions{
|
||||
code.Make(code.OpConstant, 0), // The literal "24"
|
||||
code.Make(code.OpReturn),
|
||||
},
|
||||
},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpClosure, 1, 0), // The compiled function
|
||||
code.Make(code.OpCall, 0),
|
||||
code.Make(code.OpPop),
|
||||
Instructions("0000 LoadConstant 0\n0003 Return\n"),
|
||||
},
|
||||
instructions: "0000 MakeClosure 1 0\n0004 Call 0\n0006 Pop\n",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let noArg = fn() { return 24 };
|
||||
noArg := fn() { return 24 };
|
||||
noArg();
|
||||
`,
|
||||
expectedConstants: []interface{}{
|
||||
constants: []interface{}{
|
||||
24,
|
||||
[]code.Instructions{
|
||||
code.Make(code.OpConstant, 0), // The literal "24"
|
||||
code.Make(code.OpReturn),
|
||||
},
|
||||
},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpClosure, 1, 0), // The compiled function
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpGetGlobal, 0),
|
||||
code.Make(code.OpCall, 0),
|
||||
code.Make(code.OpPop),
|
||||
Instructions("0000 SetSelf 0\n0002 LoadConstant 0\n0005 Return\n"),
|
||||
},
|
||||
instructions: "0000 LoadGlobal 0\n0003 MakeClosure 1 1\n0007 BindGlobal 0\n0010 Pop\n0011 LoadGlobal 0\n0014 Call 0\n0016 Pop\n",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let oneArg = fn(a) { return a };
|
||||
oneArg := fn(a) { return a };
|
||||
oneArg(24);
|
||||
`,
|
||||
expectedConstants: []interface{}{
|
||||
[]code.Instructions{
|
||||
code.Make(code.OpGetLocal, 0),
|
||||
code.Make(code.OpReturn),
|
||||
},
|
||||
constants: []interface{}{
|
||||
Instructions("0000 SetSelf 0\n0002 LoadLocal 0\n0004 Return\n"),
|
||||
24,
|
||||
},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpClosure, 0, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpGetGlobal, 0),
|
||||
code.Make(code.OpConstant, 1),
|
||||
code.Make(code.OpCall, 1),
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
instructions: "0000 LoadGlobal 0\n0003 MakeClosure 0 1\n0007 BindGlobal 0\n0010 Pop\n0011 LoadGlobal 0\n0014 LoadConstant 1\n0017 Call 1\n0019 Pop\n",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let manyArg = fn(a, b, c) { a; b; return c };
|
||||
manyArg := fn(a, b, c) { a; b; return c };
|
||||
manyArg(24, 25, 26);
|
||||
`,
|
||||
expectedConstants: []interface{}{
|
||||
[]code.Instructions{
|
||||
code.Make(code.OpGetLocal, 0),
|
||||
code.Make(code.OpPop),
|
||||
code.Make(code.OpGetLocal, 1),
|
||||
code.Make(code.OpPop),
|
||||
code.Make(code.OpGetLocal, 2),
|
||||
code.Make(code.OpReturn),
|
||||
},
|
||||
constants: []interface{}{
|
||||
Instructions("0000 SetSelf 0\n0002 LoadLocal 0\n0004 Pop\n0005 LoadLocal 1\n0007 Pop\n0008 LoadLocal 2\n0010 Return\n"),
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpClosure, 0, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpGetGlobal, 0),
|
||||
code.Make(code.OpConstant, 1),
|
||||
code.Make(code.OpConstant, 2),
|
||||
code.Make(code.OpConstant, 3),
|
||||
code.Make(code.OpCall, 3),
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
instructions: "0000 LoadGlobal 0\n0003 MakeClosure 0 1\n0007 BindGlobal 0\n0010 Pop\n0011 LoadGlobal 0\n0014 LoadConstant 1\n0017 LoadConstant 2\n0020 LoadConstant 3\n0023 Call 3\n0025 Pop\n",
|
||||
},
|
||||
}
|
||||
|
||||
runCompilerTests(t, tests)
|
||||
runCompilerTests2(t, tests)
|
||||
}
|
||||
|
||||
func TestAssignmentExpressions(t *testing.T) {
|
||||
@@ -1240,10 +1115,11 @@ func testConstants2(t *testing.T, expected []interface{}, actual []object.Object
|
||||
for i, constant := range expected {
|
||||
switch constant := constant.(type) {
|
||||
|
||||
case []code.Instructions:
|
||||
fn, ok := actual[i].(*object.CompiledFunction)
|
||||
assert.True(ok)
|
||||
assert.Equal(constant, fn.Instructions.String())
|
||||
case Instructions:
|
||||
assert.Equal(
|
||||
string(constant),
|
||||
actual[i].(*object.CompiledFunction).Instructions.String(),
|
||||
)
|
||||
|
||||
case string:
|
||||
assert.Equal(constant, actual[i].(*object.String).Value)
|
||||
|
||||
@@ -45,6 +45,23 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
|
||||
}
|
||||
return &object.ReturnValue{Value: val}
|
||||
|
||||
case *ast.BindExpression:
|
||||
value := Eval(node.Value, env)
|
||||
if isError(value) {
|
||||
return value
|
||||
}
|
||||
|
||||
if ident, ok := node.Left.(*ast.Identifier); ok {
|
||||
if immutable, ok := value.(object.Immutable); ok {
|
||||
env.Set(ident.Value, immutable.Clone())
|
||||
} else {
|
||||
env.Set(ident.Value, value)
|
||||
}
|
||||
|
||||
return NULL
|
||||
}
|
||||
return newError("expected identifier on left got=%T", node.Left)
|
||||
|
||||
case *ast.AssignmentExpression:
|
||||
left := Eval(node.Left, env)
|
||||
if isError(left) {
|
||||
@@ -94,18 +111,6 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
|
||||
|
||||
return NULL
|
||||
|
||||
case *ast.LetStatement:
|
||||
val := Eval(node.Value, env)
|
||||
if isError(val) {
|
||||
return val
|
||||
}
|
||||
|
||||
if immutable, ok := val.(object.Immutable); ok {
|
||||
env.Set(node.Name.Value, immutable.Clone())
|
||||
} else {
|
||||
env.Set(node.Name.Value, val)
|
||||
}
|
||||
|
||||
case *ast.Identifier:
|
||||
return evalIdentifier(node, env)
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ func TestReturnStatements(t *testing.T) {
|
||||
},
|
||||
{
|
||||
`
|
||||
let f = fn(x) {
|
||||
f := fn(x) {
|
||||
return x;
|
||||
x + 10;
|
||||
};
|
||||
@@ -156,8 +156,8 @@ func TestReturnStatements(t *testing.T) {
|
||||
},
|
||||
{
|
||||
`
|
||||
let f = fn(x) {
|
||||
let result = x + 10;
|
||||
f := fn(x) {
|
||||
result := x + 10;
|
||||
return result;
|
||||
return 10;
|
||||
};
|
||||
@@ -249,7 +249,7 @@ func TestIndexAssignmentStatements(t *testing.T) {
|
||||
input string
|
||||
expected int64
|
||||
}{
|
||||
{"let xs = [1, 2, 3]; xs[1] = 4; xs[1];", 4},
|
||||
{"xs := [1, 2, 3]; xs[1] = 4; xs[1];", 4},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -263,16 +263,16 @@ func TestAssignmentStatements(t *testing.T) {
|
||||
input string
|
||||
expected interface{}
|
||||
}{
|
||||
{"let a = 0; a = 5;", nil},
|
||||
{"let a = 0; a = 5; a;", 5},
|
||||
{"let a = 0; a = 5 * 5;", nil},
|
||||
{"let a = 0; a = 5 * 5; a;", 25},
|
||||
{"let a = 0; a = 5; let b = 0; b = a;", nil},
|
||||
{"let a = 0; a = 5; let b = 0; b = a; b;", 5},
|
||||
{"let a = 0; a = 5; let b = 0; b = a; let c = 0; c = a + b + 5;", nil},
|
||||
{"let a = 0; a = 5; let b = 0; b = a; let c = 0; c = a + b + 5; c;", 15},
|
||||
{"let a = 5; let b = a; a = 0;", nil},
|
||||
{"let a = 5; let b = a; a = 0; b;", 5},
|
||||
{"a := 0; a = 5;", nil},
|
||||
{"a := 0; a = 5; a;", 5},
|
||||
{"a := 0; a = 5 * 5;", nil},
|
||||
{"a := 0; a = 5 * 5; a;", 25},
|
||||
{"a := 0; a = 5; b := 0; b = a;", nil},
|
||||
{"a := 0; a = 5; b := 0; b = a; b;", 5},
|
||||
{"a := 0; a = 5; b := 0; b = a; c := 0; c = a + b + 5;", nil},
|
||||
{"a := 0; a = 5; b := 0; b = a; c := 0; c = a + b + 5; c;", 15},
|
||||
{"a := 5; b := a; a = 0;", nil},
|
||||
{"a := 5; b := a; a = 0; b;", 5},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -286,15 +286,15 @@ func TestAssignmentStatements(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLetStatements(t *testing.T) {
|
||||
func TestBindExpressions(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
expected int64
|
||||
}{
|
||||
{"let a = 5; a;", 5},
|
||||
{"let a = 5 * 5; a;", 25},
|
||||
{"let a = 5; let b = a; b;", 5},
|
||||
{"let a = 5; let b = a; let c = a + b + 5; c;", 15},
|
||||
{"a := 5; a;", 5},
|
||||
{"a := 5 * 5; a;", 25},
|
||||
{"a := 5; b := a; b;", 5},
|
||||
{"a := 5; b := a; c := a + b + 5; c;", 15},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -331,11 +331,11 @@ func TestFunctionApplication(t *testing.T) {
|
||||
input string
|
||||
expected int64
|
||||
}{
|
||||
{"let identity = fn(x) { x; }; identity(5);", 5},
|
||||
{"let identity = fn(x) { return x; }; identity(5);", 5},
|
||||
{"let double = fn(x) { x * 2; }; double(5);", 10},
|
||||
{"let add = fn(x, y) { x + y; }; add(5, 5);", 10},
|
||||
{"let add = fn(x, y) { x + y; }; add(5 + 5, add(5, 5));", 20},
|
||||
{"identity := fn(x) { x; }; identity(5);", 5},
|
||||
{"identity := fn(x) { return x; }; identity(5);", 5},
|
||||
{"double := fn(x) { x * 2; }; double(5);", 10},
|
||||
{"add := fn(x, y) { x + y; }; add(5, 5);", 10},
|
||||
{"add := fn(x, y) { x + y; }; add(5 + 5, add(5, 5));", 20},
|
||||
{"fn(x) { x; }(5)", 5},
|
||||
}
|
||||
|
||||
@@ -346,11 +346,11 @@ func TestFunctionApplication(t *testing.T) {
|
||||
|
||||
func TestClosures(t *testing.T) {
|
||||
input := `
|
||||
let newAdder = fn(x) {
|
||||
newAdder := fn(x) {
|
||||
fn(y) { x + y };
|
||||
};
|
||||
|
||||
let addTwo = newAdder(2);
|
||||
addTwo := newAdder(2);
|
||||
addTwo(2);`
|
||||
|
||||
testIntegerObject(t, testEval(input), 4)
|
||||
@@ -496,7 +496,7 @@ func TestArrayIndexExpressions(t *testing.T) {
|
||||
3,
|
||||
},
|
||||
{
|
||||
"let i = 0; [1][i];",
|
||||
"i := 0; [1][i];",
|
||||
1,
|
||||
},
|
||||
{
|
||||
@@ -504,15 +504,15 @@ func TestArrayIndexExpressions(t *testing.T) {
|
||||
3,
|
||||
},
|
||||
{
|
||||
"let myArray = [1, 2, 3]; myArray[2];",
|
||||
"myArray := [1, 2, 3]; myArray[2];",
|
||||
3,
|
||||
},
|
||||
{
|
||||
"let myArray = [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];",
|
||||
"myArray := [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];",
|
||||
6,
|
||||
},
|
||||
{
|
||||
"let myArray = [1, 2, 3]; let i = myArray[0]; myArray[i]",
|
||||
"myArray := [1, 2, 3]; i := myArray[0]; myArray[i]",
|
||||
2,
|
||||
},
|
||||
{
|
||||
@@ -538,7 +538,7 @@ func TestArrayIndexExpressions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestHashLiterals(t *testing.T) {
|
||||
input := `let two = "two";
|
||||
input := `two := "two";
|
||||
{
|
||||
"one": 10 - 9,
|
||||
two: 1 + 1,
|
||||
@@ -621,7 +621,7 @@ func TestHashIndexExpressions(t *testing.T) {
|
||||
nil,
|
||||
},
|
||||
{
|
||||
`let key = "foo"; {"foo": 5}[key]`,
|
||||
`key := "foo"; {"foo": 5}[key]`,
|
||||
5,
|
||||
},
|
||||
{
|
||||
@@ -660,14 +660,14 @@ func TestWhileExpressions(t *testing.T) {
|
||||
expected interface{}
|
||||
}{
|
||||
{"while (false) { }", nil},
|
||||
{"let n = 0; while (n < 10) { let n = n + 1 }; n", 10},
|
||||
{"let n = 10; while (n > 0) { let n = n - 1 }; n", 0},
|
||||
{"let n = 0; while (n < 10) { let n = n + 1 }", nil},
|
||||
{"let n = 10; while (n > 0) { let n = n - 1 }", nil},
|
||||
{"let n = 0; while (n < 10) { n = n + 1 }; n", 10},
|
||||
{"let n = 10; while (n > 0) { n = n - 1 }; n", 0},
|
||||
{"let n = 0; while (n < 10) { n = n + 1 }", nil},
|
||||
{"let n = 10; while (n > 0) { n = n - 1 }", nil},
|
||||
{"n := 0; while (n < 10) { n := n + 1 }; n", 10},
|
||||
{"n := 10; while (n > 0) { n := n - 1 }; n", 0},
|
||||
{"n := 0; while (n < 10) { n := n + 1 }", nil},
|
||||
{"n := 10; while (n > 0) { n := n - 1 }", nil},
|
||||
{"n := 0; while (n < 10) { n = n + 1 }; n", 10},
|
||||
{"n := 10; while (n > 0) { n = n - 1 }; n", 0},
|
||||
{"n := 0; while (n < 10) { n = n + 1 }", nil},
|
||||
{"n := 10; while (n > 0) { n = n - 1 }", nil},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
@@ -778,7 +778,7 @@ func TestStringIndexExpressions(t *testing.T) {
|
||||
"c",
|
||||
},
|
||||
{
|
||||
`let i = 0; "abc"[i];`,
|
||||
`i := 0; "abc"[i];`,
|
||||
"a",
|
||||
},
|
||||
{
|
||||
@@ -786,7 +786,7 @@ func TestStringIndexExpressions(t *testing.T) {
|
||||
"c",
|
||||
},
|
||||
{
|
||||
`let myString = "abc"; myString[0] + myString[1] + myString[2];`,
|
||||
`myString := "abc"; myString[0] + myString[1] + myString[2];`,
|
||||
"abc",
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!./monkey-lang
|
||||
|
||||
let fill = fn(x, i) {
|
||||
let xs = []
|
||||
fill := fn(x, i) {
|
||||
xs := []
|
||||
while (i > 0) {
|
||||
xs = push(xs, x)
|
||||
i = i - 1
|
||||
@@ -9,17 +9,17 @@ let fill = fn(x, i) {
|
||||
return xs
|
||||
}
|
||||
|
||||
let buildJumpMap = fn(program) {
|
||||
let stack = []
|
||||
let map = {}
|
||||
buildJumpMap := fn(program) {
|
||||
stack := []
|
||||
map := {}
|
||||
|
||||
let n = 0
|
||||
n := 0
|
||||
while (n < len(program)) {
|
||||
if (program[n] == "[") {
|
||||
stack = push(stack, n)
|
||||
}
|
||||
if (program[n] == "]") {
|
||||
let start = pop(stack)
|
||||
start := pop(stack)
|
||||
map[start] = n
|
||||
map[n] = start
|
||||
}
|
||||
@@ -29,13 +29,13 @@ let buildJumpMap = fn(program) {
|
||||
return map
|
||||
}
|
||||
|
||||
let ascii_table = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
|
||||
ascii_table := "\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
|
||||
|
||||
let ord = fn(s) {
|
||||
ord := fn(s) {
|
||||
return ascii_table[s]
|
||||
}
|
||||
|
||||
let chr = fn(x) {
|
||||
chr := fn(x) {
|
||||
if (x < 0) {
|
||||
return "??"
|
||||
}
|
||||
@@ -45,27 +45,27 @@ let chr = fn(x) {
|
||||
return ascii_table[x]
|
||||
}
|
||||
|
||||
let read = fn() {
|
||||
let buf = input()
|
||||
read := fn() {
|
||||
buf := input()
|
||||
if (len(buf) > 0) {
|
||||
return ord(buf[0])
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
let write = fn(x) {
|
||||
write := fn(x) {
|
||||
print(chr(x))
|
||||
}
|
||||
|
||||
let VM = fn(program) {
|
||||
let jumps = buildJumpMap(program)
|
||||
VM := fn(program) {
|
||||
jumps := buildJumpMap(program)
|
||||
|
||||
let ip = 0
|
||||
let dp = 0
|
||||
let memory = fill(0, 32)
|
||||
ip := 0
|
||||
dp := 0
|
||||
memory := fill(0, 32)
|
||||
|
||||
while (ip < len(program)) {
|
||||
let op = program[ip]
|
||||
op := program[ip]
|
||||
|
||||
if (op == ">") {
|
||||
dp = dp + 1
|
||||
@@ -108,9 +108,9 @@ let VM = fn(program) {
|
||||
}
|
||||
|
||||
// Hello World
|
||||
let program = "++++++++ [ >++++ [ >++ >+++ >+++ >+ <<<<- ] >+ >+ >- >>+ [<] <- ] >>. >---. +++++++..+++. >>. <-. <. +++.------.--------. >>+. >++."
|
||||
program := "++++++++ [ >++++ [ >++ >+++ >+++ >+ <<<<- ] >+ >+ >- >>+ [<] <- ] >>. >---. +++++++..+++. >>. <-. <. +++.------.--------. >>+. >++."
|
||||
|
||||
// 2 + 5
|
||||
// let program = "++> +++++ [<+>-] ++++++++ [<++++++>-] < ."
|
||||
// program := "++> +++++ [<+>-] ++++++++ [<++++++>-] < ."
|
||||
|
||||
VM(program)
|
||||
@@ -1,21 +1,21 @@
|
||||
let name = "Monkey";
|
||||
let age = 1;
|
||||
let inspirations = ["Scheme", "Lisp", "JavaScript", "Clojure"];
|
||||
let book = {
|
||||
name := "Monkey";
|
||||
age := 1;
|
||||
inspirations := ["Scheme", "Lisp", "JavaScript", "Clojure"];
|
||||
book := {
|
||||
"title": "Writing A Compiler In Go",
|
||||
"author": "Thorsten Ball",
|
||||
"prequel": "Writing An Interpreter In Go"
|
||||
};
|
||||
|
||||
let printBookName = fn(book) {
|
||||
let title = book["title"];
|
||||
let author = book["author"];
|
||||
printBookName := fn(book) {
|
||||
title := book["title"];
|
||||
author := book["author"];
|
||||
print(author + " - " + title);
|
||||
};
|
||||
|
||||
printBookName(book);
|
||||
|
||||
let fibonacci = fn(x) {
|
||||
fibonacci := fn(x) {
|
||||
if (x == 0) {
|
||||
return 0
|
||||
} else {
|
||||
@@ -27,8 +27,8 @@ let fibonacci = fn(x) {
|
||||
}
|
||||
};
|
||||
|
||||
let map = fn(arr, f) {
|
||||
let iter = fn(arr, accumulated) {
|
||||
map := fn(arr, f) {
|
||||
iter := fn(arr, accumulated) {
|
||||
if (len(arr) == 0) {
|
||||
return accumulated
|
||||
} else {
|
||||
@@ -39,5 +39,5 @@ let map = fn(arr, f) {
|
||||
return iter(arr, []);
|
||||
};
|
||||
|
||||
let numbers = [1, 1 + 1, 4 - 1, 2 * 2, 2 + 3, 12 / 2];
|
||||
numbers := [1, 1 + 1, 4 - 1, 2 * 2, 2 + 3, 12 / 2];
|
||||
map(numbers, fibonacci);
|
||||
@@ -1,4 +1,4 @@
|
||||
let fact = fn(n) {
|
||||
fact := fn(n) {
|
||||
if (n == 0) {
|
||||
return 1
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
let fact = fn(n, a) {
|
||||
fact := fn(n, a) {
|
||||
if (n == 0) {
|
||||
return a
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
let fib = fn(x) {
|
||||
fib := fn(x) {
|
||||
if (x == 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
let fib = fn(n) {
|
||||
fib := fn(n) {
|
||||
if (n < 3) {
|
||||
return 1
|
||||
}
|
||||
let a = 1
|
||||
let b = 1
|
||||
let c = 0
|
||||
let i = 0
|
||||
a := 1
|
||||
b := 1
|
||||
c := 0
|
||||
i := 0
|
||||
while (i < n - 2) {
|
||||
c = a + b
|
||||
b = a
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
let fib = fn(n, a, b) {
|
||||
fib := fn(n, a, b) {
|
||||
if (n == 0) {
|
||||
return a
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
let name = input("What is your name? ")
|
||||
name := input("What is your name? ")
|
||||
|
||||
print("Hello " + name)
|
||||
@@ -134,7 +134,14 @@ func (l *Lexer) NextToken() token.Token {
|
||||
case ']':
|
||||
tok = newToken(token.RBRACKET, l.ch)
|
||||
case ':':
|
||||
if l.peekChar() == '=' {
|
||||
ch := l.ch
|
||||
l.readChar()
|
||||
literal := string(ch) + string(l.ch)
|
||||
tok = token.Token{Type: token.BIND, Literal: literal}
|
||||
} else {
|
||||
tok = newToken(token.COLON, l.ch)
|
||||
}
|
||||
default:
|
||||
if isLetter(l.ch) {
|
||||
tok.Literal = l.readIdentifier()
|
||||
|
||||
@@ -7,15 +7,15 @@ import (
|
||||
|
||||
func TestNextToken(t *testing.T) {
|
||||
input := `#!./monkey-lang
|
||||
let five = 5;
|
||||
let ten = 10;
|
||||
five := 5;
|
||||
ten := 10;
|
||||
|
||||
let add = fn(x, y) {
|
||||
add := fn(x, y) {
|
||||
x + y;
|
||||
};
|
||||
|
||||
# this is a comment
|
||||
let result = add(five, ten);
|
||||
result := add(five, ten);
|
||||
!-/*5;
|
||||
5 < 10 > 5;
|
||||
|
||||
@@ -40,19 +40,16 @@ func TestNextToken(t *testing.T) {
|
||||
expectedLiteral string
|
||||
}{
|
||||
{token.COMMENT, "!./monkey-lang"},
|
||||
{token.LET, "let"},
|
||||
{token.IDENT, "five"},
|
||||
{token.ASSIGN, "="},
|
||||
{token.BIND, ":="},
|
||||
{token.INT, "5"},
|
||||
{token.SEMICOLON, ";"},
|
||||
{token.LET, "let"},
|
||||
{token.IDENT, "ten"},
|
||||
{token.ASSIGN, "="},
|
||||
{token.BIND, ":="},
|
||||
{token.INT, "10"},
|
||||
{token.SEMICOLON, ";"},
|
||||
{token.LET, "let"},
|
||||
{token.IDENT, "add"},
|
||||
{token.ASSIGN, "="},
|
||||
{token.BIND, ":="},
|
||||
{token.FUNCTION, "fn"},
|
||||
{token.LPAREN, "("},
|
||||
{token.IDENT, "x"},
|
||||
@@ -67,9 +64,8 @@ func TestNextToken(t *testing.T) {
|
||||
{token.RBRACE, "}"},
|
||||
{token.SEMICOLON, ";"},
|
||||
{token.COMMENT, " this is a comment"},
|
||||
{token.LET, "let"},
|
||||
{token.IDENT, "result"},
|
||||
{token.ASSIGN, "="},
|
||||
{token.BIND, ":="},
|
||||
{token.IDENT, "add"},
|
||||
{token.LPAREN, "("},
|
||||
{token.IDENT, "five"},
|
||||
@@ -163,9 +159,9 @@ func TestNextToken(t *testing.T) {
|
||||
|
||||
func TestStringEscapes(t *testing.T) {
|
||||
input := `#!./monkey-lang
|
||||
let a = "\"foo\""
|
||||
let b = "\x00\x0a\x7f"
|
||||
let c = "\r\n\t"
|
||||
a := "\"foo\""
|
||||
b := "\x00\x0a\x7f"
|
||||
c := "\r\n\t"
|
||||
`
|
||||
|
||||
tests := []struct {
|
||||
@@ -173,17 +169,14 @@ let c = "\r\n\t"
|
||||
expectedLiteral string
|
||||
}{
|
||||
{token.COMMENT, "!./monkey-lang"},
|
||||
{token.LET, "let"},
|
||||
{token.IDENT, "a"},
|
||||
{token.ASSIGN, "="},
|
||||
{token.BIND, ":="},
|
||||
{token.STRING, "\"foo\""},
|
||||
{token.LET, "let"},
|
||||
{token.IDENT, "b"},
|
||||
{token.ASSIGN, "="},
|
||||
{token.BIND, ":="},
|
||||
{token.STRING, "\x00\n\u007f"},
|
||||
{token.LET, "let"},
|
||||
{token.IDENT, "c"},
|
||||
{token.ASSIGN, "="},
|
||||
{token.BIND, ":="},
|
||||
{token.STRING, "\r\n\t"},
|
||||
{token.EOF, ""},
|
||||
}
|
||||
|
||||
@@ -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{}
|
||||
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 {
|
||||
|
||||
2
testdata/arrays.monkey
vendored
2
testdata/arrays.monkey
vendored
@@ -1,2 +1,2 @@
|
||||
let xs = [1, 2, 3]
|
||||
xs := [1, 2, 3]
|
||||
xs[0] + xs[1] + xs[2]
|
||||
2
testdata/assign.monkey
vendored
2
testdata/assign.monkey
vendored
@@ -1,3 +1,3 @@
|
||||
let x = 1
|
||||
x := 1
|
||||
x = 2
|
||||
x = x + 1
|
||||
3
testdata/binding.monkey
vendored
Normal file
3
testdata/binding.monkey
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
x := 1
|
||||
y := 2
|
||||
z := x
|
||||
4
testdata/builtins.monkey
vendored
4
testdata/builtins.monkey
vendored
@@ -1,4 +1,4 @@
|
||||
let xs = [1, 2, 3]
|
||||
xs := [1, 2, 3]
|
||||
len(xs)
|
||||
|
||||
first(xs)
|
||||
@@ -9,5 +9,5 @@ pop(xs)
|
||||
|
||||
len("foo")
|
||||
|
||||
let x = input()
|
||||
x := input()
|
||||
print(x)
|
||||
2
testdata/closures.monkey
vendored
2
testdata/closures.monkey
vendored
@@ -1,2 +1,2 @@
|
||||
let f = fn(x) { fn() { x + 1 } }
|
||||
f := fn(x) { fn() { x + 1 } }
|
||||
f(2)
|
||||
2
testdata/functions.monkey
vendored
2
testdata/functions.monkey
vendored
@@ -1,2 +1,2 @@
|
||||
let f = fn(x, y) { x * y };
|
||||
f := fn(x, y) { x * y };
|
||||
f(2, 4)
|
||||
2
testdata/hashes.monkey
vendored
2
testdata/hashes.monkey
vendored
@@ -1,2 +1,2 @@
|
||||
let d = {"a": 1, "b": 2}
|
||||
d := {"a": 1, "b": 2}
|
||||
d["a"] + d["b"]
|
||||
2
testdata/if.monkey
vendored
2
testdata/if.monkey
vendored
@@ -1,4 +1,4 @@
|
||||
let x = 1
|
||||
x := 1
|
||||
if (x == 1) {
|
||||
x = 2
|
||||
x
|
||||
|
||||
3
testdata/let.monkey
vendored
3
testdata/let.monkey
vendored
@@ -1,3 +0,0 @@
|
||||
let x = 1
|
||||
let y = 2
|
||||
let z = x
|
||||
1
testdata/popbug1.monkey
vendored
1
testdata/popbug1.monkey
vendored
@@ -1 +0,0 @@
|
||||
let n = 2; let x = 0; while (n > 0) { if (n > 1) { x = x + 1 }; n = n - 1; }; x;
|
||||
1
testdata/popbug2.monkey
vendored
1
testdata/popbug2.monkey
vendored
@@ -1 +0,0 @@
|
||||
let n = 2; let x = 0; while (n > 0) { if (n > 1) { x = x + 1 }; let n = n - 1; }; x;
|
||||
1
testdata/popbug3.monkey
vendored
1
testdata/popbug3.monkey
vendored
@@ -1 +0,0 @@
|
||||
let x = 1; if (x == 1) { let x = 2 }
|
||||
@@ -1,4 +1,4 @@
|
||||
let d = {"foo": 1, "bar": 2}
|
||||
d := {"foo": 1, "bar": 2}
|
||||
|
||||
assert(d.foo == 1, "d.foo != 1")
|
||||
assert(d.bar == 2, "d.bar != 2")
|
||||
2
testdata/strings.monkey
vendored
2
testdata/strings.monkey
vendored
@@ -1,2 +1,2 @@
|
||||
let s = "hello"
|
||||
s := "hello"
|
||||
s + " " + "world"
|
||||
@@ -20,6 +20,7 @@ const (
|
||||
STRING = "STRING"
|
||||
|
||||
// Operators
|
||||
BIND = ":="
|
||||
ASSIGN = "="
|
||||
PLUS = "+"
|
||||
MINUS = "-"
|
||||
@@ -50,7 +51,6 @@ const (
|
||||
|
||||
// Keywords
|
||||
FUNCTION = "FUNCTION"
|
||||
LET = "LET"
|
||||
TRUE = "TRUE"
|
||||
FALSE = "FALSE"
|
||||
NULL = "NULL"
|
||||
@@ -62,7 +62,6 @@ const (
|
||||
|
||||
var keywords = map[string]TokenType{
|
||||
"fn": FUNCTION,
|
||||
"let": LET,
|
||||
"true": TRUE,
|
||||
"false": FALSE,
|
||||
"if": IF,
|
||||
|
||||
@@ -13,12 +13,13 @@ syntax case match
|
||||
|
||||
syntax keyword xType true false null
|
||||
|
||||
syntax keyword xKeyword let fn if else return while
|
||||
syntax keyword xKeyword fn if else return while
|
||||
|
||||
syntax keyword xFunction len input print first last rest push pop exit assert
|
||||
|
||||
syntax keyword xOperator == != < > !
|
||||
syntax keyword xOperator + - * / =
|
||||
syntax keyword xOperator + - * /
|
||||
syntax keyword xOperator := =
|
||||
|
||||
syntax region xString start=/"/ skip=/\\./ end=/"/
|
||||
|
||||
|
||||
17
vm/vm.go
17
vm/vm.go
@@ -181,6 +181,11 @@ func (vm *VM) Run() error {
|
||||
vm.globals[globalIndex] = ref
|
||||
}
|
||||
|
||||
err := vm.push(Null)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case code.OpAssignGlobal:
|
||||
globalIndex := code.ReadUint16(ins[ip+1:])
|
||||
vm.currentFrame().ip += 2
|
||||
@@ -284,7 +289,17 @@ func (vm *VM) Run() error {
|
||||
|
||||
frame := vm.currentFrame()
|
||||
|
||||
vm.stack[frame.basePointer+int(localIndex)] = vm.pop()
|
||||
ref := vm.pop()
|
||||
if immutable, ok := ref.(object.Immutable); ok {
|
||||
vm.stack[frame.basePointer+int(localIndex)] = immutable.Clone()
|
||||
} else {
|
||||
vm.stack[frame.basePointer+int(localIndex)] = ref
|
||||
}
|
||||
|
||||
err := vm.push(Null)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case code.OpGetLocal:
|
||||
localIndex := code.ReadUint8(ins[ip+1:])
|
||||
|
||||
214
vm/vm_test.go
214
vm/vm_test.go
@@ -261,12 +261,12 @@ func TestConditionals(t *testing.T) {
|
||||
{"if (1 > 2) { 10 }", Null},
|
||||
{"if (false) { 10 }", Null},
|
||||
{"if ((if (false) { 10 })) { 10 } else { 20 }", 20},
|
||||
{"if (true) { let a = 5; }", Null},
|
||||
{"if (true) { 10; let a = 5; }", Null},
|
||||
{"if (false) { 10 } else { let b = 5; }", Null},
|
||||
{"if (false) { 10 } else { 10; let b = 5; }", Null},
|
||||
{"if (true) { let a = 5; } else { 10 }", Null},
|
||||
{"let x = 0; if (true) { x = 1; }; if (false) { x = 2; }; x", 1},
|
||||
{"if (true) { a := 5; }", Null},
|
||||
{"if (true) { 10; a := 5; }", Null},
|
||||
{"if (false) { 10 } else { b := 5; }", Null},
|
||||
{"if (false) { 10 } else { 10; b := 5; }", Null},
|
||||
{"if (true) { a := 5; } else { 10 }", Null},
|
||||
{"x := 0; if (true) { x = 1; }; if (false) { x = 2; }; x", 1},
|
||||
{"if (1 < 2) { 10 } else if (1 == 2) { 20 }", 10},
|
||||
{"if (1 > 2) { 10 } else if (1 == 2) { 20 } else { 30 }", 30},
|
||||
}
|
||||
@@ -274,11 +274,11 @@ func TestConditionals(t *testing.T) {
|
||||
runVmTests(t, tests)
|
||||
}
|
||||
|
||||
func TestGlobalLetStatements(t *testing.T) {
|
||||
func TestGlobalBindExpressions(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{"let one = 1; one", 1},
|
||||
{"let one = 1; let two = 2; one + two", 3},
|
||||
{"let one = 1; let two = one + one; one + two", 3},
|
||||
{"one := 1; one", 1},
|
||||
{"one := 1; two := 2; one + two", 3},
|
||||
{"one := 1; two := one + one; one + two", 3},
|
||||
}
|
||||
|
||||
runVmTests(t, tests)
|
||||
@@ -364,24 +364,24 @@ func TestCallingFunctionsWithoutArguments(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let fivePlusTen = fn() { return 5 + 10; };
|
||||
fivePlusTen := fn() { return 5 + 10; };
|
||||
fivePlusTen();
|
||||
`,
|
||||
expected: 15,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let one = fn() { return 1; };
|
||||
let two = fn() { return 2; };
|
||||
one := fn() { return 1; };
|
||||
two := fn() { return 2; };
|
||||
one() + two()
|
||||
`,
|
||||
expected: 3,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let a = fn() { return 1 };
|
||||
let b = fn() { return a() + 1 };
|
||||
let c = fn() { return b() + 1 };
|
||||
a := fn() { return 1 };
|
||||
b := fn() { return a() + 1 };
|
||||
c:= fn() { return b() + 1 };
|
||||
c();
|
||||
`,
|
||||
expected: 3,
|
||||
@@ -395,14 +395,14 @@ func TestFunctionsWithReturnStatements(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let earlyExit = fn() { return 99; 100; };
|
||||
earlyExit := fn() { return 99; 100; };
|
||||
earlyExit();
|
||||
`,
|
||||
expected: 99,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let earlyExit = fn() { return 99; return 100; };
|
||||
earlyExit := fn() { return 99; return 100; };
|
||||
earlyExit();
|
||||
`,
|
||||
expected: 99,
|
||||
@@ -416,15 +416,15 @@ func TestFunctionsWithoutReturnValue(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let noReturn = fn() { };
|
||||
noReturn := fn() { };
|
||||
noReturn();
|
||||
`,
|
||||
expected: Null,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let noReturn = fn() { };
|
||||
let noReturnTwo = fn() { noReturn(); };
|
||||
noReturn := fn() { };
|
||||
noReturnTwo := fn() { noReturn(); };
|
||||
noReturn();
|
||||
noReturnTwo();
|
||||
`,
|
||||
@@ -439,16 +439,16 @@ func TestFirstClassFunctions(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let returnsOne = fn() { return 1; };
|
||||
let returnsOneReturner = fn() { return returnsOne; };
|
||||
returnsOne := fn() { return 1; };
|
||||
returnsOneReturner := fn() { return returnsOne; };
|
||||
returnsOneReturner()();
|
||||
`,
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let returnsOneReturner = fn() {
|
||||
let returnsOne = fn() { return 1; };
|
||||
returnsOneReturner := fn() {
|
||||
returnsOne := fn() { return 1; };
|
||||
return returnsOne;
|
||||
};
|
||||
returnsOneReturner()();
|
||||
@@ -464,43 +464,43 @@ func TestCallingFunctionsWithBindings(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
//{
|
||||
// input: `
|
||||
// let one = fn() { let one = 1; return one };
|
||||
// one := fn() { one := 1; return one };
|
||||
// one();
|
||||
// `,
|
||||
// expected: 1,
|
||||
//},
|
||||
{
|
||||
input: `
|
||||
let oneAndTwo = fn() { let one = 1; let two = 2; return one + two; };
|
||||
oneAndTwo := fn() { one := 1; two := 2; return one + two; };
|
||||
oneAndTwo();
|
||||
`,
|
||||
expected: 3,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let oneAndTwo = fn() { let one = 1; let two = 2; return one + two; };
|
||||
let threeAndFour = fn() { let three = 3; let four = 4; return three + four; };
|
||||
oneAndTwo := fn() { one := 1; two := 2; return one + two; };
|
||||
threeAndFour := fn() { three := 3; four := 4; return three + four; };
|
||||
oneAndTwo() + threeAndFour();
|
||||
`,
|
||||
expected: 10,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let firstFoobar = fn() { let foobar = 50; return foobar; };
|
||||
let secondFoobar = fn() { let foobar = 100; return foobar; };
|
||||
firstFoobar := fn() { foobar := 50; return foobar; };
|
||||
secondFoobar := fn() { foobar := 100; return foobar; };
|
||||
firstFoobar() + secondFoobar();
|
||||
`,
|
||||
expected: 150,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let globalSeed = 50;
|
||||
let minusOne = fn() {
|
||||
let num = 1;
|
||||
globalSeed := 50;
|
||||
minusOne := fn() {
|
||||
num := 1;
|
||||
return globalSeed - num;
|
||||
}
|
||||
let minusTwo = fn() {
|
||||
let num = 2;
|
||||
minusTwo := fn() {
|
||||
num := 2;
|
||||
return globalSeed - num;
|
||||
}
|
||||
minusOne() + minusTwo();
|
||||
@@ -516,22 +516,22 @@ func TestCallingFunctionsWithArgumentsAndBindings(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let identity = fn(a) { return a; };
|
||||
identity := fn(a) { return a; };
|
||||
identity(4);
|
||||
`,
|
||||
expected: 4,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let sum = fn(a, b) { return a + b; };
|
||||
sum := fn(a, b) { return a + b; };
|
||||
sum(1, 2);
|
||||
`,
|
||||
expected: 3,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let sum = fn(a, b) {
|
||||
let c = a + b;
|
||||
sum := fn(a, b) {
|
||||
c := a + b;
|
||||
return c;
|
||||
};
|
||||
sum(1, 2);
|
||||
@@ -540,8 +540,8 @@ func TestCallingFunctionsWithArgumentsAndBindings(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let sum = fn(a, b) {
|
||||
let c = a + b;
|
||||
sum := fn(a, b) {
|
||||
c := a + b;
|
||||
return c;
|
||||
};
|
||||
sum(1, 2) + sum(3, 4);`,
|
||||
@@ -549,11 +549,11 @@ func TestCallingFunctionsWithArgumentsAndBindings(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let sum = fn(a, b) {
|
||||
let c = a + b;
|
||||
sum := fn(a, b) {
|
||||
c := a + b;
|
||||
return c;
|
||||
};
|
||||
let outer = fn() {
|
||||
outer := fn() {
|
||||
return sum(1, 2) + sum(3, 4);
|
||||
};
|
||||
outer();
|
||||
@@ -562,14 +562,14 @@ func TestCallingFunctionsWithArgumentsAndBindings(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let globalNum = 10;
|
||||
globalNum := 10;
|
||||
|
||||
let sum = fn(a, b) {
|
||||
let c = a + b;
|
||||
sum := fn(a, b) {
|
||||
c := a + b;
|
||||
return c + globalNum;
|
||||
};
|
||||
|
||||
let outer = fn() {
|
||||
outer := fn() {
|
||||
return sum(1, 2) + sum(3, 4) + globalNum;
|
||||
};
|
||||
|
||||
@@ -697,72 +697,72 @@ func TestClosures(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let newClosure = fn(a) {
|
||||
newClosure := fn(a) {
|
||||
return fn() { return a; };
|
||||
};
|
||||
let closure = newClosure(99);
|
||||
closure := newClosure(99);
|
||||
closure();
|
||||
`,
|
||||
expected: 99,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let newAdder = fn(a, b) {
|
||||
newAdder := fn(a, b) {
|
||||
return fn(c) { return a + b + c };
|
||||
};
|
||||
let adder = newAdder(1, 2);
|
||||
adder := newAdder(1, 2);
|
||||
adder(8);
|
||||
`,
|
||||
expected: 11,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let newAdder = fn(a, b) {
|
||||
let c = a + b;
|
||||
newAdder := fn(a, b) {
|
||||
c := a + b;
|
||||
return fn(d) { return c + d };
|
||||
};
|
||||
let adder = newAdder(1, 2);
|
||||
adder := newAdder(1, 2);
|
||||
adder(8);
|
||||
`,
|
||||
expected: 11,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let newAdderOuter = fn(a, b) {
|
||||
let c = a + b;
|
||||
newAdderOuter := fn(a, b) {
|
||||
c := a + b;
|
||||
return fn(d) {
|
||||
let e = d + c;
|
||||
e := d + c;
|
||||
return fn(f) { return e + f; };
|
||||
};
|
||||
};
|
||||
let newAdderInner = newAdderOuter(1, 2)
|
||||
let adder = newAdderInner(3);
|
||||
newAdderInner := newAdderOuter(1, 2)
|
||||
adder := newAdderInner(3);
|
||||
adder(8);
|
||||
`,
|
||||
expected: 14,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let a = 1;
|
||||
let newAdderOuter = fn(b) {
|
||||
a := 1;
|
||||
newAdderOuter := fn(b) {
|
||||
return fn(c) {
|
||||
return fn(d) { return a + b + c + d };
|
||||
};
|
||||
};
|
||||
let newAdderInner = newAdderOuter(2)
|
||||
let adder = newAdderInner(3);
|
||||
newAdderInner := newAdderOuter(2)
|
||||
adder := newAdderInner(3);
|
||||
adder(8);
|
||||
`,
|
||||
expected: 14,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let newClosure = fn(a, b) {
|
||||
let one = fn() { return a; };
|
||||
let two = fn() { return b; };
|
||||
newClosure := fn(a, b) {
|
||||
one := fn() { return a; };
|
||||
two := fn() { return b; };
|
||||
return fn() { return one() + two(); };
|
||||
};
|
||||
let closure = newClosure(9, 90);
|
||||
closure := newClosure(9, 90);
|
||||
closure();
|
||||
`,
|
||||
expected: 99,
|
||||
@@ -776,7 +776,7 @@ func TestRecursiveFunctions(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let countDown = fn(x) {
|
||||
countDown := fn(x) {
|
||||
if (x == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -789,14 +789,14 @@ func TestRecursiveFunctions(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let countDown = fn(x) {
|
||||
countDown := fn(x) {
|
||||
if (x == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return countDown(x - 1);
|
||||
}
|
||||
};
|
||||
let wrapper = fn() {
|
||||
wrapper := fn() {
|
||||
return countDown(1);
|
||||
};
|
||||
wrapper();
|
||||
@@ -805,8 +805,8 @@ func TestRecursiveFunctions(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let wrapper = fn() {
|
||||
let countDown = fn(x) {
|
||||
wrapper := fn() {
|
||||
countDown := fn(x) {
|
||||
if (x == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -828,7 +828,7 @@ func TestRecursiveFibonacci(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let fibonacci = fn(x) {
|
||||
fibonacci := fn(x) {
|
||||
if (x == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -851,14 +851,14 @@ func TestRecursiveFibonacci(t *testing.T) {
|
||||
func TestIterations(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{"while (false) { }", nil},
|
||||
{"let n = 0; while (n < 10) { let n = n + 1 }; n", 10},
|
||||
{"let n = 10; while (n > 0) { let n = n - 1 }; n", 0},
|
||||
{"let n = 0; while (n < 10) { let n = n + 1 }", nil},
|
||||
{"let n = 10; while (n > 0) { let n = n - 1 }", nil},
|
||||
{"let n = 0; while (n < 10) { n = n + 1 }; n", 10},
|
||||
{"let n = 10; while (n > 0) { n = n - 1 }; n", 0},
|
||||
{"let n = 0; while (n < 10) { n = n + 1 }", nil},
|
||||
{"let n = 10; while (n > 0) { n = n - 1 }", nil},
|
||||
{"n := 0; while (n < 10) { n := n + 1 }; n", 10},
|
||||
{"n := 10; while (n > 0) { n := n - 1 }; n", 0},
|
||||
{"n := 0; while (n < 10) { n := n + 1 }", nil},
|
||||
{"n := 10; while (n > 0) { n := n - 1 }", nil},
|
||||
{"n := 0; while (n < 10) { n = n + 1 }; n", 10},
|
||||
{"n := 10; while (n > 0) { n = n - 1 }; n", 0},
|
||||
{"n := 0; while (n < 10) { n = n + 1 }", nil},
|
||||
{"n := 10; while (n > 0) { n = n - 1 }", nil},
|
||||
}
|
||||
|
||||
runVmTests(t, tests)
|
||||
@@ -866,7 +866,7 @@ func TestIterations(t *testing.T) {
|
||||
|
||||
func TestIndexAssignmentStatements(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{"let xs = [1, 2, 3]; xs[1] = 4; xs[1];", 4},
|
||||
{"xs := [1, 2, 3]; xs[1] = 4; xs[1];", 4},
|
||||
}
|
||||
|
||||
runVmTests(t, tests)
|
||||
@@ -874,20 +874,20 @@ func TestIndexAssignmentStatements(t *testing.T) {
|
||||
|
||||
func TestAssignmentExpressions(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{"let a = 0; a = 5;", nil},
|
||||
{"let a = 0; a = 5; a;", 5},
|
||||
{"let a = 0; a = 5 * 5;", nil},
|
||||
{"let a = 0; a = 5 * 5; a;", 25},
|
||||
{"let a = 0; a = 5; let b = 0; b = a;", nil},
|
||||
{"let a = 0; a = 5; let b = 0; b = a; b;", 5},
|
||||
{"let a = 0; a = 5; let b = 0; b = a; let c = 0; c = a + b + 5;", nil},
|
||||
{"let a = 0; a = 5; let b = 0; b = a; let c = 0; c = a + b + 5; c;", 15},
|
||||
{"let a = 5; let b = a; a = 0;", nil},
|
||||
{"let a = 5; let b = a; a = 0; b;", 5},
|
||||
{"let one = 0; one = 1", nil},
|
||||
{"let one = 0; one = 1; one", 1},
|
||||
{"let one = 0; one = 1; let two = 0; two = 2; one + two", 3},
|
||||
{"let one = 0; one = 1; let two = 0; two = one + one; one + two", 3},
|
||||
{"a := 0; a = 5;", nil},
|
||||
{"a := 0; a = 5; a;", 5},
|
||||
{"a := 0; a = 5 * 5;", nil},
|
||||
{"a := 0; a = 5 * 5; a;", 25},
|
||||
{"a := 0; a = 5; b := 0; b = a;", nil},
|
||||
{"a := 0; a = 5; b := 0; b = a; b;", 5},
|
||||
{"a := 0; a = 5; b := 0; b = a; c := 0; c = a + b + 5;", nil},
|
||||
{"a := 0; a = 5; b := 0; b = a; c := 0; c = a + b + 5; c;", 15},
|
||||
{"a := 5; b := a; a = 0;", nil},
|
||||
{"a := 5; b := a; a = 0; b;", 5},
|
||||
{"one := 0; one = 1", nil},
|
||||
{"one := 0; one = 1; one", 1},
|
||||
{"one := 0; one = 1; two := 0; two = 2; one + two", 3},
|
||||
{"one := 0; one = 1; two := 0; two = one + one; one + two", 3},
|
||||
}
|
||||
|
||||
runVmTests(t, tests)
|
||||
@@ -897,7 +897,7 @@ func TestTailCalls(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let fact = fn(n, a) {
|
||||
fact := fn(n, a) {
|
||||
if (n == 0) {
|
||||
return a
|
||||
}
|
||||
@@ -912,7 +912,7 @@ func TestTailCalls(t *testing.T) {
|
||||
// without tail recursion optimization this will cause a stack overflow
|
||||
{
|
||||
input: `
|
||||
let iter = fn(n, max) {
|
||||
iter := fn(n, max) {
|
||||
if (n == max) {
|
||||
return n
|
||||
}
|
||||
@@ -971,14 +971,14 @@ func TestIntegration(t *testing.T) {
|
||||
func BenchmarkFibonacci(b *testing.B) {
|
||||
tests := map[string]string{
|
||||
"iterative": `
|
||||
let fib = fn(n) {
|
||||
fib := fn(n) {
|
||||
if (n < 3) {
|
||||
return 1
|
||||
}
|
||||
let a = 1
|
||||
let b = 1
|
||||
let c = 0
|
||||
let i = 0
|
||||
a := 1
|
||||
b := 1
|
||||
c := 0
|
||||
i := 0
|
||||
while (i < n - 2) {
|
||||
c = a + b
|
||||
b = a
|
||||
@@ -991,7 +991,7 @@ func BenchmarkFibonacci(b *testing.B) {
|
||||
fib(35)
|
||||
`,
|
||||
"recursive": `
|
||||
let fib = fn(x) {
|
||||
fib := fn(x) {
|
||||
if (x == 0) {
|
||||
return 0
|
||||
}
|
||||
@@ -1004,7 +1004,7 @@ func BenchmarkFibonacci(b *testing.B) {
|
||||
fib(35)
|
||||
`,
|
||||
"tail-recursive": `
|
||||
let fib = fn(n, a, b) {
|
||||
fib := fn(n, a, b) {
|
||||
if (n == 0) {
|
||||
return a
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user