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()
|
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 {
|
type ReturnStatement struct {
|
||||||
Token token.Token // the 'return' token
|
Token token.Token // the 'return' token
|
||||||
ReturnValue Expression
|
ReturnValue Expression
|
||||||
@@ -284,23 +259,23 @@ type StringLiteral struct {
|
|||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sl StringLiteral) TokenLiteral() string {
|
func (sl *StringLiteral) TokenLiteral() string {
|
||||||
return sl.Token.Literal
|
return sl.Token.Literal
|
||||||
}
|
}
|
||||||
func (sl StringLiteral) String() string {
|
func (sl *StringLiteral) String() string {
|
||||||
return sl.Token.Literal
|
return sl.Token.Literal
|
||||||
}
|
}
|
||||||
func (sl StringLiteral) expressionNode() {}
|
func (sl *StringLiteral) expressionNode() {}
|
||||||
|
|
||||||
type ArrayLiteral struct {
|
type ArrayLiteral struct {
|
||||||
Token token.Token // the '[' token
|
Token token.Token // the '[' token
|
||||||
Elements []Expression
|
Elements []Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
func (al ArrayLiteral) TokenLiteral() string {
|
func (al *ArrayLiteral) TokenLiteral() string {
|
||||||
return al.Token.Literal
|
return al.Token.Literal
|
||||||
}
|
}
|
||||||
func (al ArrayLiteral) String() string {
|
func (al *ArrayLiteral) String() string {
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
elements := []string{}
|
elements := []string{}
|
||||||
@@ -314,7 +289,7 @@ func (al ArrayLiteral) String() string {
|
|||||||
|
|
||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
func (al ArrayLiteral) expressionNode() {}
|
func (al *ArrayLiteral) expressionNode() {}
|
||||||
|
|
||||||
type IndexExpression struct {
|
type IndexExpression struct {
|
||||||
Token token.Token // The [ token
|
Token token.Token // The [ token
|
||||||
@@ -322,11 +297,11 @@ type IndexExpression struct {
|
|||||||
Index Expression
|
Index Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ie IndexExpression) TokenLiteral() string {
|
func (ie *IndexExpression) TokenLiteral() string {
|
||||||
return ie.Token.Literal
|
return ie.Token.Literal
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ie IndexExpression) String() string {
|
func (ie *IndexExpression) String() string {
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
out.WriteString("(")
|
out.WriteString("(")
|
||||||
@@ -338,18 +313,18 @@ func (ie IndexExpression) String() string {
|
|||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ie IndexExpression) expressionNode() {}
|
func (ie *IndexExpression) expressionNode() {}
|
||||||
|
|
||||||
type HashLiteral struct {
|
type HashLiteral struct {
|
||||||
Token token.Token // the '{' token
|
Token token.Token // the '{' token
|
||||||
Pairs map[Expression]Expression
|
Pairs map[Expression]Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hl HashLiteral) TokenLiteral() string {
|
func (hl *HashLiteral) TokenLiteral() string {
|
||||||
return hl.Token.Literal
|
return hl.Token.Literal
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hl HashLiteral) String() string {
|
func (hl *HashLiteral) String() string {
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
pairs := []string{}
|
pairs := []string{}
|
||||||
@@ -364,7 +339,7 @@ func (hl HashLiteral) String() string {
|
|||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hl HashLiteral) expressionNode() {}
|
func (hl *HashLiteral) expressionNode() {}
|
||||||
|
|
||||||
type WhileExpression struct {
|
type WhileExpression struct {
|
||||||
Token token.Token // The 'while' token
|
Token token.Token // The 'while' token
|
||||||
@@ -389,6 +364,28 @@ func (we *WhileExpression) String() string {
|
|||||||
return out.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:
|
// AssignmentExpression represents an assignment expression of the form:
|
||||||
// x = 1 or xs[1] = 2
|
// x = 1 or xs[1] = 2
|
||||||
type AssignmentExpression struct {
|
type AssignmentExpression struct {
|
||||||
@@ -397,10 +394,10 @@ type AssignmentExpression struct {
|
|||||||
Value Expression
|
Value Expression
|
||||||
}
|
}
|
||||||
|
|
||||||
func (as AssignmentExpression) TokenLiteral() string {
|
func (as *AssignmentExpression) TokenLiteral() string {
|
||||||
return as.Token.Literal
|
return as.Token.Literal
|
||||||
}
|
}
|
||||||
func (as AssignmentExpression) String() string {
|
func (as *AssignmentExpression) String() string {
|
||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
|
|
||||||
out.WriteString(as.Left.String())
|
out.WriteString(as.Left.String())
|
||||||
@@ -409,7 +406,7 @@ func (as AssignmentExpression) String() string {
|
|||||||
|
|
||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
func (as AssignmentExpression) expressionNode() {}
|
func (as *AssignmentExpression) expressionNode() {}
|
||||||
|
|
||||||
// Comment a comment
|
// Comment a comment
|
||||||
type Comment struct {
|
type Comment struct {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package ast
|
package ast
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"monkey/token"
|
"monkey/token"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
@@ -8,21 +9,22 @@ import (
|
|||||||
func TestString(t *testing.T) {
|
func TestString(t *testing.T) {
|
||||||
program := &Program{
|
program := &Program{
|
||||||
Statements: []Statement{
|
Statements: []Statement{
|
||||||
&LetStatement{
|
&ExpressionStatement{
|
||||||
Token: token.Token{Type: token.LET, Literal: "let"},
|
Token: token.Token{Type: token.IDENT, Literal: "myVar"},
|
||||||
Name: &Identifier{
|
Expression: &BindExpression{
|
||||||
Token: token.Token{Type: token.IDENT, Literal: "myVar"},
|
Token: token.Token{Type: token.BIND, Literal: ":="},
|
||||||
Value: "myVar",
|
Left: &Identifier{
|
||||||
},
|
Token: token.Token{Type: token.IDENT, Literal: "myVar"},
|
||||||
Value: &Identifier{
|
Value: "myVar",
|
||||||
Token: token.Token{Type: token.IDENT, Literal: "anotherVar"},
|
},
|
||||||
Value: "anotherVar",
|
Value: &Identifier{
|
||||||
|
Token: token.Token{Type: token.IDENT, Literal: "anotherVar"},
|
||||||
|
Value: "anotherVar",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if program.String() != "let myVar = anotherVar;" {
|
assert.Equal(t, "myVar:=anotherVar", program.String())
|
||||||
t.Errorf("program.String wrong. got=%q", program.String())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -292,23 +292,36 @@ func (c *Compiler) Compile(node ast.Node) error {
|
|||||||
return fmt.Errorf("expected identifier or index expression got=%s", node.Left)
|
return fmt.Errorf("expected identifier or index expression got=%s", node.Left)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.LetStatement:
|
case *ast.BindExpression:
|
||||||
symbol, ok := c.symbolTable.Resolve(node.Name.Value)
|
var symbol Symbol
|
||||||
if !ok {
|
|
||||||
symbol = c.symbolTable.Define(node.Name.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.l++
|
if ident, ok := node.Left.(*ast.Identifier); ok {
|
||||||
err := c.Compile(node.Value)
|
symbol, ok = c.symbolTable.Resolve(ident.Value)
|
||||||
c.l--
|
if !ok {
|
||||||
if err != nil {
|
symbol = c.symbolTable.Define(ident.Value)
|
||||||
return err
|
} 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++
|
||||||
|
err := c.Compile(node.Value)
|
||||||
|
c.l--
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if symbol.Scope == GlobalScope {
|
||||||
|
c.emit(code.OpSetGlobal, symbol.Index)
|
||||||
|
} else {
|
||||||
|
c.emit(code.OpSetLocal, symbol.Index)
|
||||||
|
}
|
||||||
|
|
||||||
if symbol.Scope == GlobalScope {
|
|
||||||
c.emit(code.OpSetGlobal, symbol.Index)
|
|
||||||
} else {
|
} else {
|
||||||
c.emit(code.OpSetLocal, symbol.Index)
|
return fmt.Errorf("expected identifier got=%s", node.Left)
|
||||||
}
|
}
|
||||||
|
|
||||||
case *ast.Identifier:
|
case *ast.Identifier:
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type Instructions string
|
||||||
|
|
||||||
type compilerTestCase struct {
|
type compilerTestCase struct {
|
||||||
input string
|
input string
|
||||||
expectedConstants []interface{}
|
expectedConstants []interface{}
|
||||||
@@ -210,146 +212,61 @@ func TestBooleanExpressions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestConditionals(t *testing.T) {
|
func TestConditionals(t *testing.T) {
|
||||||
tests := []compilerTestCase{
|
tests := []compilerTestCase2{
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
if (true) { 10 }; 3333;
|
if (true) { 10 }; 3333;
|
||||||
`,
|
`,
|
||||||
expectedConstants: []interface{}{10, 3333},
|
constants: []interface{}{10, 3333},
|
||||||
expectedInstructions: []code.Instructions{
|
instructions: "0000 LoadTrue\n0001 JumpIfFalse 10\n0004 LoadConstant 0\n0007 Jump 11\n0010 LoadNull\n0011 Pop\n0012 LoadConstant 1\n0015 Pop\n",
|
||||||
// 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),
|
|
||||||
},
|
|
||||||
}, {
|
}, {
|
||||||
input: `
|
input: `
|
||||||
if (true) { 10 } else { 20 }; 3333;
|
if (true) { 10 } else { 20 }; 3333;
|
||||||
`,
|
`,
|
||||||
expectedConstants: []interface{}{10, 20, 3333},
|
constants: []interface{}{10, 20, 3333},
|
||||||
expectedInstructions: []code.Instructions{
|
instructions: "0000 LoadTrue\n0001 JumpIfFalse 10\n0004 LoadConstant 0\n0007 Jump 13\n0010 LoadConstant 1\n0013 Pop\n0014 LoadConstant 2\n0017 Pop\n",
|
||||||
// 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),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let x = 0; if (true) { x = 1; }; if (false) { x = 2; }
|
let x = 0; if (true) { x = 1; }; if (false) { x = 2; }
|
||||||
`,
|
`,
|
||||||
expectedConstants: []interface{}{0, 1, 2},
|
constants: []interface{}{0, 1, 2},
|
||||||
expectedInstructions: []code.Instructions{
|
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",
|
||||||
// 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),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
runCompilerTests(t, tests)
|
runCompilerTests2(t, tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGlobalLetStatements(t *testing.T) {
|
func TestGlobalBindExpressions(t *testing.T) {
|
||||||
tests := []compilerTestCase{
|
tests := []compilerTestCase2{
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let one = 1;
|
one := 1;
|
||||||
let two = 2;
|
two := 2;
|
||||||
`,
|
`,
|
||||||
expectedConstants: []interface{}{1, 2},
|
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadConstant 1\n0010 BindGlobal 1\n0013 Pop\n",
|
||||||
expectedInstructions: []code.Instructions{
|
|
||||||
code.Make(code.OpConstant, 0),
|
|
||||||
code.Make(code.OpSetGlobal, 0),
|
|
||||||
code.Make(code.OpConstant, 1),
|
|
||||||
code.Make(code.OpSetGlobal, 1),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let one = 1;
|
one := 1;
|
||||||
one;
|
one;
|
||||||
`,
|
`,
|
||||||
expectedConstants: []interface{}{1},
|
constants: []interface{}{1},
|
||||||
expectedInstructions: []code.Instructions{
|
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadGlobal 0\n0010 Pop\n",
|
||||||
code.Make(code.OpConstant, 0),
|
|
||||||
code.Make(code.OpSetGlobal, 0),
|
|
||||||
code.Make(code.OpGetGlobal, 0),
|
|
||||||
code.Make(code.OpPop),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let one = 1;
|
one := 1;
|
||||||
let two = one;
|
two := one;
|
||||||
two;
|
two;
|
||||||
`,
|
`,
|
||||||
expectedConstants: []interface{}{1},
|
constants: []interface{}{1},
|
||||||
expectedInstructions: []code.Instructions{
|
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadGlobal 0\n0010 BindGlobal 1\n0013 Pop\n0014 LoadGlobal 1\n0017 Pop\n",
|
||||||
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),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
runCompilerTests(t, tests)
|
runCompilerTests2(t, tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStringExpressions(t *testing.T) {
|
func TestStringExpressions(t *testing.T) {
|
||||||
@@ -735,95 +652,53 @@ func TestCompilerScopes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestFunctionCalls(t *testing.T) {
|
func TestFunctionCalls(t *testing.T) {
|
||||||
tests := []compilerTestCase{
|
tests := []compilerTestCase2{
|
||||||
{
|
{
|
||||||
input: `fn() { return 24 }();`,
|
input: `fn() { return 24 }();`,
|
||||||
expectedConstants: []interface{}{
|
constants: []interface{}{
|
||||||
24,
|
24,
|
||||||
[]code.Instructions{
|
Instructions("0000 LoadConstant 0\n0003 Return\n"),
|
||||||
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 MakeClosure 1 0\n0004 Call 0\n0006 Pop\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let noArg = fn() { return 24 };
|
noArg := fn() { return 24 };
|
||||||
noArg();
|
noArg();
|
||||||
`,
|
`,
|
||||||
expectedConstants: []interface{}{
|
constants: []interface{}{
|
||||||
24,
|
24,
|
||||||
[]code.Instructions{
|
Instructions("0000 SetSelf 0\n0002 LoadConstant 0\n0005 Return\n"),
|
||||||
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 LoadGlobal 0\n0003 MakeClosure 1 1\n0007 BindGlobal 0\n0010 Pop\n0011 LoadGlobal 0\n0014 Call 0\n0016 Pop\n",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let oneArg = fn(a) { return a };
|
oneArg := fn(a) { return a };
|
||||||
oneArg(24);
|
oneArg(24);
|
||||||
`,
|
`,
|
||||||
expectedConstants: []interface{}{
|
constants: []interface{}{
|
||||||
[]code.Instructions{
|
Instructions("0000 SetSelf 0\n0002 LoadLocal 0\n0004 Return\n"),
|
||||||
code.Make(code.OpGetLocal, 0),
|
|
||||||
code.Make(code.OpReturn),
|
|
||||||
},
|
|
||||||
24,
|
24,
|
||||||
},
|
},
|
||||||
expectedInstructions: []code.Instructions{
|
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",
|
||||||
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),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let manyArg = fn(a, b, c) { a; b; return c };
|
manyArg := fn(a, b, c) { a; b; return c };
|
||||||
manyArg(24, 25, 26);
|
manyArg(24, 25, 26);
|
||||||
`,
|
`,
|
||||||
expectedConstants: []interface{}{
|
constants: []interface{}{
|
||||||
[]code.Instructions{
|
Instructions("0000 SetSelf 0\n0002 LoadLocal 0\n0004 Pop\n0005 LoadLocal 1\n0007 Pop\n0008 LoadLocal 2\n0010 Return\n"),
|
||||||
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),
|
|
||||||
},
|
|
||||||
24,
|
24,
|
||||||
25,
|
25,
|
||||||
26,
|
26,
|
||||||
},
|
},
|
||||||
expectedInstructions: []code.Instructions{
|
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",
|
||||||
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),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
runCompilerTests(t, tests)
|
runCompilerTests2(t, tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssignmentExpressions(t *testing.T) {
|
func TestAssignmentExpressions(t *testing.T) {
|
||||||
@@ -1240,10 +1115,11 @@ func testConstants2(t *testing.T, expected []interface{}, actual []object.Object
|
|||||||
for i, constant := range expected {
|
for i, constant := range expected {
|
||||||
switch constant := constant.(type) {
|
switch constant := constant.(type) {
|
||||||
|
|
||||||
case []code.Instructions:
|
case Instructions:
|
||||||
fn, ok := actual[i].(*object.CompiledFunction)
|
assert.Equal(
|
||||||
assert.True(ok)
|
string(constant),
|
||||||
assert.Equal(constant, fn.Instructions.String())
|
actual[i].(*object.CompiledFunction).Instructions.String(),
|
||||||
|
)
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
assert.Equal(constant, actual[i].(*object.String).Value)
|
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}
|
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:
|
case *ast.AssignmentExpression:
|
||||||
left := Eval(node.Left, env)
|
left := Eval(node.Left, env)
|
||||||
if isError(left) {
|
if isError(left) {
|
||||||
@@ -94,18 +111,6 @@ func Eval(node ast.Node, env *object.Environment) object.Object {
|
|||||||
|
|
||||||
return NULL
|
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:
|
case *ast.Identifier:
|
||||||
return evalIdentifier(node, env)
|
return evalIdentifier(node, env)
|
||||||
|
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ func TestReturnStatements(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
`
|
`
|
||||||
let f = fn(x) {
|
f := fn(x) {
|
||||||
return x;
|
return x;
|
||||||
x + 10;
|
x + 10;
|
||||||
};
|
};
|
||||||
@@ -156,8 +156,8 @@ func TestReturnStatements(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
`
|
`
|
||||||
let f = fn(x) {
|
f := fn(x) {
|
||||||
let result = x + 10;
|
result := x + 10;
|
||||||
return result;
|
return result;
|
||||||
return 10;
|
return 10;
|
||||||
};
|
};
|
||||||
@@ -249,7 +249,7 @@ func TestIndexAssignmentStatements(t *testing.T) {
|
|||||||
input string
|
input string
|
||||||
expected int64
|
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 {
|
for _, tt := range tests {
|
||||||
@@ -263,16 +263,16 @@ func TestAssignmentStatements(t *testing.T) {
|
|||||||
input string
|
input string
|
||||||
expected interface{}
|
expected interface{}
|
||||||
}{
|
}{
|
||||||
{"let a = 0; a = 5;", nil},
|
{"a := 0; a = 5;", nil},
|
||||||
{"let a = 0; a = 5; a;", 5},
|
{"a := 0; a = 5; a;", 5},
|
||||||
{"let a = 0; a = 5 * 5;", nil},
|
{"a := 0; a = 5 * 5;", nil},
|
||||||
{"let a = 0; a = 5 * 5; a;", 25},
|
{"a := 0; a = 5 * 5; a;", 25},
|
||||||
{"let a = 0; a = 5; let b = 0; b = a;", nil},
|
{"a := 0; a = 5; b := 0; b = a;", nil},
|
||||||
{"let a = 0; a = 5; let b = 0; b = a; b;", 5},
|
{"a := 0; a = 5; b := 0; b = a; b;", 5},
|
||||||
{"let a = 0; a = 5; let b = 0; b = a; let c = 0; c = a + b + 5;", nil},
|
{"a := 0; a = 5; b := 0; b = a; 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},
|
{"a := 0; a = 5; b := 0; b = a; c := 0; c = a + b + 5; c;", 15},
|
||||||
{"let a = 5; let b = a; a = 0;", nil},
|
{"a := 5; b := a; a = 0;", nil},
|
||||||
{"let a = 5; let b = a; a = 0; b;", 5},
|
{"a := 5; b := a; a = 0; b;", 5},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
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 {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
expected int64
|
expected int64
|
||||||
}{
|
}{
|
||||||
{"let a = 5; a;", 5},
|
{"a := 5; a;", 5},
|
||||||
{"let a = 5 * 5; a;", 25},
|
{"a := 5 * 5; a;", 25},
|
||||||
{"let a = 5; let b = a; b;", 5},
|
{"a := 5; b := a; b;", 5},
|
||||||
{"let a = 5; let b = a; let c = a + b + 5; c;", 15},
|
{"a := 5; b := a; c := a + b + 5; c;", 15},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@@ -331,11 +331,11 @@ func TestFunctionApplication(t *testing.T) {
|
|||||||
input string
|
input string
|
||||||
expected int64
|
expected int64
|
||||||
}{
|
}{
|
||||||
{"let identity = fn(x) { x; }; identity(5);", 5},
|
{"identity := fn(x) { x; }; identity(5);", 5},
|
||||||
{"let identity = fn(x) { return x; }; identity(5);", 5},
|
{"identity := fn(x) { return x; }; identity(5);", 5},
|
||||||
{"let double = fn(x) { x * 2; }; double(5);", 10},
|
{"double := fn(x) { x * 2; }; double(5);", 10},
|
||||||
{"let add = fn(x, y) { x + y; }; add(5, 5);", 10},
|
{"add := fn(x, y) { x + y; }; add(5, 5);", 10},
|
||||||
{"let add = fn(x, y) { x + y; }; add(5 + 5, add(5, 5));", 20},
|
{"add := fn(x, y) { x + y; }; add(5 + 5, add(5, 5));", 20},
|
||||||
{"fn(x) { x; }(5)", 5},
|
{"fn(x) { x; }(5)", 5},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,11 +346,11 @@ func TestFunctionApplication(t *testing.T) {
|
|||||||
|
|
||||||
func TestClosures(t *testing.T) {
|
func TestClosures(t *testing.T) {
|
||||||
input := `
|
input := `
|
||||||
let newAdder = fn(x) {
|
newAdder := fn(x) {
|
||||||
fn(y) { x + y };
|
fn(y) { x + y };
|
||||||
};
|
};
|
||||||
|
|
||||||
let addTwo = newAdder(2);
|
addTwo := newAdder(2);
|
||||||
addTwo(2);`
|
addTwo(2);`
|
||||||
|
|
||||||
testIntegerObject(t, testEval(input), 4)
|
testIntegerObject(t, testEval(input), 4)
|
||||||
@@ -496,7 +496,7 @@ func TestArrayIndexExpressions(t *testing.T) {
|
|||||||
3,
|
3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"let i = 0; [1][i];",
|
"i := 0; [1][i];",
|
||||||
1,
|
1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -504,15 +504,15 @@ func TestArrayIndexExpressions(t *testing.T) {
|
|||||||
3,
|
3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"let myArray = [1, 2, 3]; myArray[2];",
|
"myArray := [1, 2, 3]; myArray[2];",
|
||||||
3,
|
3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"let myArray = [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];",
|
"myArray := [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];",
|
||||||
6,
|
6,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"let myArray = [1, 2, 3]; let i = myArray[0]; myArray[i]",
|
"myArray := [1, 2, 3]; i := myArray[0]; myArray[i]",
|
||||||
2,
|
2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -538,7 +538,7 @@ func TestArrayIndexExpressions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestHashLiterals(t *testing.T) {
|
func TestHashLiterals(t *testing.T) {
|
||||||
input := `let two = "two";
|
input := `two := "two";
|
||||||
{
|
{
|
||||||
"one": 10 - 9,
|
"one": 10 - 9,
|
||||||
two: 1 + 1,
|
two: 1 + 1,
|
||||||
@@ -621,7 +621,7 @@ func TestHashIndexExpressions(t *testing.T) {
|
|||||||
nil,
|
nil,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`let key = "foo"; {"foo": 5}[key]`,
|
`key := "foo"; {"foo": 5}[key]`,
|
||||||
5,
|
5,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -660,14 +660,14 @@ func TestWhileExpressions(t *testing.T) {
|
|||||||
expected interface{}
|
expected interface{}
|
||||||
}{
|
}{
|
||||||
{"while (false) { }", nil},
|
{"while (false) { }", nil},
|
||||||
{"let n = 0; while (n < 10) { let n = n + 1 }; n", 10},
|
{"n := 0; while (n < 10) { n := n + 1 }; n", 10},
|
||||||
{"let n = 10; while (n > 0) { let n = n - 1 }; n", 0},
|
{"n := 10; while (n > 0) { n := n - 1 }; n", 0},
|
||||||
{"let n = 0; while (n < 10) { let n = n + 1 }", nil},
|
{"n := 0; while (n < 10) { n := n + 1 }", nil},
|
||||||
{"let n = 10; while (n > 0) { let n = n - 1 }", nil},
|
{"n := 10; while (n > 0) { n := n - 1 }", nil},
|
||||||
{"let n = 0; while (n < 10) { n = n + 1 }; n", 10},
|
{"n := 0; while (n < 10) { n = n + 1 }; n", 10},
|
||||||
{"let n = 10; while (n > 0) { n = n - 1 }; n", 0},
|
{"n := 10; while (n > 0) { n = n - 1 }; n", 0},
|
||||||
{"let n = 0; while (n < 10) { n = n + 1 }", nil},
|
{"n := 0; while (n < 10) { n = n + 1 }", nil},
|
||||||
{"let n = 10; while (n > 0) { n = n - 1 }", nil},
|
{"n := 10; while (n > 0) { n = n - 1 }", nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@@ -778,7 +778,7 @@ func TestStringIndexExpressions(t *testing.T) {
|
|||||||
"c",
|
"c",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`let i = 0; "abc"[i];`,
|
`i := 0; "abc"[i];`,
|
||||||
"a",
|
"a",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -786,7 +786,7 @@ func TestStringIndexExpressions(t *testing.T) {
|
|||||||
"c",
|
"c",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`let myString = "abc"; myString[0] + myString[1] + myString[2];`,
|
`myString := "abc"; myString[0] + myString[1] + myString[2];`,
|
||||||
"abc",
|
"abc",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!./monkey-lang
|
#!./monkey-lang
|
||||||
|
|
||||||
let fill = fn(x, i) {
|
fill := fn(x, i) {
|
||||||
let xs = []
|
xs := []
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
xs = push(xs, x)
|
xs = push(xs, x)
|
||||||
i = i - 1
|
i = i - 1
|
||||||
@@ -9,17 +9,17 @@ let fill = fn(x, i) {
|
|||||||
return xs
|
return xs
|
||||||
}
|
}
|
||||||
|
|
||||||
let buildJumpMap = fn(program) {
|
buildJumpMap := fn(program) {
|
||||||
let stack = []
|
stack := []
|
||||||
let map = {}
|
map := {}
|
||||||
|
|
||||||
let n = 0
|
n := 0
|
||||||
while (n < len(program)) {
|
while (n < len(program)) {
|
||||||
if (program[n] == "[") {
|
if (program[n] == "[") {
|
||||||
stack = push(stack, n)
|
stack = push(stack, n)
|
||||||
}
|
}
|
||||||
if (program[n] == "]") {
|
if (program[n] == "]") {
|
||||||
let start = pop(stack)
|
start := pop(stack)
|
||||||
map[start] = n
|
map[start] = n
|
||||||
map[n] = start
|
map[n] = start
|
||||||
}
|
}
|
||||||
@@ -29,13 +29,13 @@ let buildJumpMap = fn(program) {
|
|||||||
return map
|
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]
|
return ascii_table[s]
|
||||||
}
|
}
|
||||||
|
|
||||||
let chr = fn(x) {
|
chr := fn(x) {
|
||||||
if (x < 0) {
|
if (x < 0) {
|
||||||
return "??"
|
return "??"
|
||||||
}
|
}
|
||||||
@@ -45,27 +45,27 @@ let chr = fn(x) {
|
|||||||
return ascii_table[x]
|
return ascii_table[x]
|
||||||
}
|
}
|
||||||
|
|
||||||
let read = fn() {
|
read := fn() {
|
||||||
let buf = input()
|
buf := input()
|
||||||
if (len(buf) > 0) {
|
if (len(buf) > 0) {
|
||||||
return ord(buf[0])
|
return ord(buf[0])
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
let write = fn(x) {
|
write := fn(x) {
|
||||||
print(chr(x))
|
print(chr(x))
|
||||||
}
|
}
|
||||||
|
|
||||||
let VM = fn(program) {
|
VM := fn(program) {
|
||||||
let jumps = buildJumpMap(program)
|
jumps := buildJumpMap(program)
|
||||||
|
|
||||||
let ip = 0
|
ip := 0
|
||||||
let dp = 0
|
dp := 0
|
||||||
let memory = fill(0, 32)
|
memory := fill(0, 32)
|
||||||
|
|
||||||
while (ip < len(program)) {
|
while (ip < len(program)) {
|
||||||
let op = program[ip]
|
op := program[ip]
|
||||||
|
|
||||||
if (op == ">") {
|
if (op == ">") {
|
||||||
dp = dp + 1
|
dp = dp + 1
|
||||||
@@ -108,9 +108,9 @@ let VM = fn(program) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Hello World
|
// Hello World
|
||||||
let program = "++++++++ [ >++++ [ >++ >+++ >+++ >+ <<<<- ] >+ >+ >- >>+ [<] <- ] >>. >---. +++++++..+++. >>. <-. <. +++.------.--------. >>+. >++."
|
program := "++++++++ [ >++++ [ >++ >+++ >+++ >+ <<<<- ] >+ >+ >- >>+ [<] <- ] >>. >---. +++++++..+++. >>. <-. <. +++.------.--------. >>+. >++."
|
||||||
|
|
||||||
// 2 + 5
|
// 2 + 5
|
||||||
// let program = "++> +++++ [<+>-] ++++++++ [<++++++>-] < ."
|
// program := "++> +++++ [<+>-] ++++++++ [<++++++>-] < ."
|
||||||
|
|
||||||
VM(program)
|
VM(program)
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
let name = "Monkey";
|
name := "Monkey";
|
||||||
let age = 1;
|
age := 1;
|
||||||
let inspirations = ["Scheme", "Lisp", "JavaScript", "Clojure"];
|
inspirations := ["Scheme", "Lisp", "JavaScript", "Clojure"];
|
||||||
let book = {
|
book := {
|
||||||
"title": "Writing A Compiler In Go",
|
"title": "Writing A Compiler In Go",
|
||||||
"author": "Thorsten Ball",
|
"author": "Thorsten Ball",
|
||||||
"prequel": "Writing An Interpreter In Go"
|
"prequel": "Writing An Interpreter In Go"
|
||||||
};
|
};
|
||||||
|
|
||||||
let printBookName = fn(book) {
|
printBookName := fn(book) {
|
||||||
let title = book["title"];
|
title := book["title"];
|
||||||
let author = book["author"];
|
author := book["author"];
|
||||||
print(author + " - " + title);
|
print(author + " - " + title);
|
||||||
};
|
};
|
||||||
|
|
||||||
printBookName(book);
|
printBookName(book);
|
||||||
|
|
||||||
let fibonacci = fn(x) {
|
fibonacci := fn(x) {
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
return 0
|
return 0
|
||||||
} else {
|
} else {
|
||||||
@@ -27,8 +27,8 @@ let fibonacci = fn(x) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let map = fn(arr, f) {
|
map := fn(arr, f) {
|
||||||
let iter = fn(arr, accumulated) {
|
iter := fn(arr, accumulated) {
|
||||||
if (len(arr) == 0) {
|
if (len(arr) == 0) {
|
||||||
return accumulated
|
return accumulated
|
||||||
} else {
|
} else {
|
||||||
@@ -39,5 +39,5 @@ let map = fn(arr, f) {
|
|||||||
return iter(arr, []);
|
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);
|
map(numbers, fibonacci);
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
let fact = fn(n) {
|
fact := fn(n) {
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
let fact = fn(n, a) {
|
fact := fn(n, a) {
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
let fib = fn(x) {
|
fib := fn(x) {
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
let fib = fn(n) {
|
fib := fn(n) {
|
||||||
if (n < 3) {
|
if (n < 3) {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
let a = 1
|
a := 1
|
||||||
let b = 1
|
b := 1
|
||||||
let c = 0
|
c := 0
|
||||||
let i = 0
|
i := 0
|
||||||
while (i < n - 2) {
|
while (i < n - 2) {
|
||||||
c = a + b
|
c = a + b
|
||||||
b = a
|
b = a
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
let fib = fn(n, a, b) {
|
fib := fn(n, a, b) {
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
let name = input("What is your name? ")
|
name := input("What is your name? ")
|
||||||
|
|
||||||
print("Hello " + name)
|
print("Hello " + name)
|
||||||
@@ -134,7 +134,14 @@ func (l *Lexer) NextToken() token.Token {
|
|||||||
case ']':
|
case ']':
|
||||||
tok = newToken(token.RBRACKET, l.ch)
|
tok = newToken(token.RBRACKET, l.ch)
|
||||||
case ':':
|
case ':':
|
||||||
tok = newToken(token.COLON, l.ch)
|
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:
|
default:
|
||||||
if isLetter(l.ch) {
|
if isLetter(l.ch) {
|
||||||
tok.Literal = l.readIdentifier()
|
tok.Literal = l.readIdentifier()
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import (
|
|||||||
|
|
||||||
func TestNextToken(t *testing.T) {
|
func TestNextToken(t *testing.T) {
|
||||||
input := `#!./monkey-lang
|
input := `#!./monkey-lang
|
||||||
let five = 5;
|
five := 5;
|
||||||
let ten = 10;
|
ten := 10;
|
||||||
|
|
||||||
let add = fn(x, y) {
|
add := fn(x, y) {
|
||||||
x + y;
|
x + y;
|
||||||
};
|
};
|
||||||
|
|
||||||
# this is a comment
|
# this is a comment
|
||||||
let result = add(five, ten);
|
result := add(five, ten);
|
||||||
!-/*5;
|
!-/*5;
|
||||||
5 < 10 > 5;
|
5 < 10 > 5;
|
||||||
|
|
||||||
@@ -40,19 +40,16 @@ func TestNextToken(t *testing.T) {
|
|||||||
expectedLiteral string
|
expectedLiteral string
|
||||||
}{
|
}{
|
||||||
{token.COMMENT, "!./monkey-lang"},
|
{token.COMMENT, "!./monkey-lang"},
|
||||||
{token.LET, "let"},
|
|
||||||
{token.IDENT, "five"},
|
{token.IDENT, "five"},
|
||||||
{token.ASSIGN, "="},
|
{token.BIND, ":="},
|
||||||
{token.INT, "5"},
|
{token.INT, "5"},
|
||||||
{token.SEMICOLON, ";"},
|
{token.SEMICOLON, ";"},
|
||||||
{token.LET, "let"},
|
|
||||||
{token.IDENT, "ten"},
|
{token.IDENT, "ten"},
|
||||||
{token.ASSIGN, "="},
|
{token.BIND, ":="},
|
||||||
{token.INT, "10"},
|
{token.INT, "10"},
|
||||||
{token.SEMICOLON, ";"},
|
{token.SEMICOLON, ";"},
|
||||||
{token.LET, "let"},
|
|
||||||
{token.IDENT, "add"},
|
{token.IDENT, "add"},
|
||||||
{token.ASSIGN, "="},
|
{token.BIND, ":="},
|
||||||
{token.FUNCTION, "fn"},
|
{token.FUNCTION, "fn"},
|
||||||
{token.LPAREN, "("},
|
{token.LPAREN, "("},
|
||||||
{token.IDENT, "x"},
|
{token.IDENT, "x"},
|
||||||
@@ -67,9 +64,8 @@ func TestNextToken(t *testing.T) {
|
|||||||
{token.RBRACE, "}"},
|
{token.RBRACE, "}"},
|
||||||
{token.SEMICOLON, ";"},
|
{token.SEMICOLON, ";"},
|
||||||
{token.COMMENT, " this is a comment"},
|
{token.COMMENT, " this is a comment"},
|
||||||
{token.LET, "let"},
|
|
||||||
{token.IDENT, "result"},
|
{token.IDENT, "result"},
|
||||||
{token.ASSIGN, "="},
|
{token.BIND, ":="},
|
||||||
{token.IDENT, "add"},
|
{token.IDENT, "add"},
|
||||||
{token.LPAREN, "("},
|
{token.LPAREN, "("},
|
||||||
{token.IDENT, "five"},
|
{token.IDENT, "five"},
|
||||||
@@ -163,9 +159,9 @@ func TestNextToken(t *testing.T) {
|
|||||||
|
|
||||||
func TestStringEscapes(t *testing.T) {
|
func TestStringEscapes(t *testing.T) {
|
||||||
input := `#!./monkey-lang
|
input := `#!./monkey-lang
|
||||||
let a = "\"foo\""
|
a := "\"foo\""
|
||||||
let b = "\x00\x0a\x7f"
|
b := "\x00\x0a\x7f"
|
||||||
let c = "\r\n\t"
|
c := "\r\n\t"
|
||||||
`
|
`
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
@@ -173,17 +169,14 @@ let c = "\r\n\t"
|
|||||||
expectedLiteral string
|
expectedLiteral string
|
||||||
}{
|
}{
|
||||||
{token.COMMENT, "!./monkey-lang"},
|
{token.COMMENT, "!./monkey-lang"},
|
||||||
{token.LET, "let"},
|
|
||||||
{token.IDENT, "a"},
|
{token.IDENT, "a"},
|
||||||
{token.ASSIGN, "="},
|
{token.BIND, ":="},
|
||||||
{token.STRING, "\"foo\""},
|
{token.STRING, "\"foo\""},
|
||||||
{token.LET, "let"},
|
|
||||||
{token.IDENT, "b"},
|
{token.IDENT, "b"},
|
||||||
{token.ASSIGN, "="},
|
{token.BIND, ":="},
|
||||||
{token.STRING, "\x00\n\u007f"},
|
{token.STRING, "\x00\n\u007f"},
|
||||||
{token.LET, "let"},
|
|
||||||
{token.IDENT, "c"},
|
{token.IDENT, "c"},
|
||||||
{token.ASSIGN, "="},
|
{token.BIND, ":="},
|
||||||
{token.STRING, "\r\n\t"},
|
{token.STRING, "\r\n\t"},
|
||||||
{token.EOF, ""},
|
{token.EOF, ""},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
_ int = iota
|
_ int = iota
|
||||||
LOWEST
|
LOWEST
|
||||||
ASSIGN // =
|
ASSIGN // := or =
|
||||||
EQUALS // ==
|
EQUALS // ==
|
||||||
LESSGREATER // > or <
|
LESSGREATER // > or <
|
||||||
SUM // +
|
SUM // +
|
||||||
@@ -22,6 +22,7 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var precedences = map[token.TokenType]int{
|
var precedences = map[token.TokenType]int{
|
||||||
|
token.BIND: ASSIGN,
|
||||||
token.ASSIGN: ASSIGN,
|
token.ASSIGN: ASSIGN,
|
||||||
token.EQ: EQUALS,
|
token.EQ: EQUALS,
|
||||||
token.NOT_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.GTE, p.parseInfixExpression)
|
||||||
p.registerInfix(token.LPAREN, p.parseCallExpression)
|
p.registerInfix(token.LPAREN, p.parseCallExpression)
|
||||||
p.registerInfix(token.LBRACKET, p.parseIndexExpression)
|
p.registerInfix(token.LBRACKET, p.parseIndexExpression)
|
||||||
|
p.registerInfix(token.BIND, p.parseBindExpression)
|
||||||
p.registerInfix(token.ASSIGN, p.parseAssignmentExpression)
|
p.registerInfix(token.ASSIGN, p.parseAssignmentExpression)
|
||||||
p.registerInfix(token.DOT, p.parseSelectorExpression)
|
p.registerInfix(token.DOT, p.parseSelectorExpression)
|
||||||
|
|
||||||
@@ -161,8 +163,6 @@ func (p *Parser) parseStatement() ast.Statement {
|
|||||||
switch p.curToken.Type {
|
switch p.curToken.Type {
|
||||||
case token.COMMENT:
|
case token.COMMENT:
|
||||||
return p.parseComment()
|
return p.parseComment()
|
||||||
case token.LET:
|
|
||||||
return p.parseLetStatement()
|
|
||||||
case token.RETURN:
|
case token.RETURN:
|
||||||
return p.parseReturnStatement()
|
return p.parseReturnStatement()
|
||||||
default:
|
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 {
|
func (p *Parser) parseReturnStatement() *ast.ReturnStatement {
|
||||||
stmt := &ast.ReturnStatement{Token: p.curToken}
|
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}
|
index := &ast.StringLiteral{Token: p.curToken, Value: p.curToken.Literal}
|
||||||
return &ast.IndexExpression{Left: expression, Index: index}
|
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"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLetStatements(t *testing.T) {
|
func TestBindExpressions(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
input string
|
input string
|
||||||
expectedIdentifier string
|
expected string
|
||||||
expectedValue interface{}
|
|
||||||
}{
|
}{
|
||||||
{"let x = 5;", "x", 5},
|
{"x := 5;", "x:=5"},
|
||||||
{"let y = true;", "y", true},
|
{"y := true;", "y:=true"},
|
||||||
{"let foobar = y;", "foobar", "y"},
|
{"foobar := y;", "foobar:=y"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
@@ -25,20 +24,7 @@ func TestLetStatements(t *testing.T) {
|
|||||||
program := p.ParseProgram()
|
program := p.ParseProgram()
|
||||||
checkParserErrors(t, p)
|
checkParserErrors(t, p)
|
||||||
|
|
||||||
if len(program.Statements) != 1 {
|
assert.Equal(t, tt.expected, program.String())
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -994,35 +980,17 @@ func TestParsingHashLiteralsWithExpressions(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFunctionLiteralWithName(t *testing.T) {
|
func TestFunctionDefinitionParsing(t *testing.T) {
|
||||||
input := `let myFunction = fn() { };`
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
input := `add := fn(x, y) { x + y; }`
|
||||||
|
|
||||||
l := lexer.New(input)
|
l := lexer.New(input)
|
||||||
p := New(l)
|
p := New(l)
|
||||||
program := p.ParseProgram()
|
program := p.ParseProgram()
|
||||||
checkParserErrors(t, p)
|
checkParserErrors(t, p)
|
||||||
|
|
||||||
if len(program.Statements) != 1 {
|
assert.Equal("add:=fn<add>(x, y) (x + y)", program.String())
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestWhileExpression(t *testing.T) {
|
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{},
|
func testInfixExpression(t *testing.T, exp ast.Expression, left interface{},
|
||||||
operator string, right interface{}) bool {
|
operator string, right interface{}) bool {
|
||||||
|
|
||||||
@@ -1249,7 +1191,7 @@ func TestComments(t *testing.T) {
|
|||||||
{"#!monkey", "!monkey"},
|
{"#!monkey", "!monkey"},
|
||||||
{"# foo", " foo"},
|
{"# foo", " foo"},
|
||||||
{" # foo", " foo"},
|
{" # foo", " foo"},
|
||||||
{" // let x = 1", " let x = 1"},
|
{" // x := 1", " x := 1"},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
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]
|
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 = 2
|
||||||
x = x + 1
|
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)
|
len(xs)
|
||||||
|
|
||||||
first(xs)
|
first(xs)
|
||||||
@@ -9,5 +9,5 @@ pop(xs)
|
|||||||
|
|
||||||
len("foo")
|
len("foo")
|
||||||
|
|
||||||
let x = input()
|
x := input()
|
||||||
print(x)
|
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)
|
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)
|
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"]
|
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) {
|
if (x == 1) {
|
||||||
x = 2
|
x = 2
|
||||||
x
|
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.foo == 1, "d.foo != 1")
|
||||||
assert(d.bar == 2, "d.bar != 2")
|
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"
|
s + " " + "world"
|
||||||
@@ -20,6 +20,7 @@ const (
|
|||||||
STRING = "STRING"
|
STRING = "STRING"
|
||||||
|
|
||||||
// Operators
|
// Operators
|
||||||
|
BIND = ":="
|
||||||
ASSIGN = "="
|
ASSIGN = "="
|
||||||
PLUS = "+"
|
PLUS = "+"
|
||||||
MINUS = "-"
|
MINUS = "-"
|
||||||
@@ -50,7 +51,6 @@ const (
|
|||||||
|
|
||||||
// Keywords
|
// Keywords
|
||||||
FUNCTION = "FUNCTION"
|
FUNCTION = "FUNCTION"
|
||||||
LET = "LET"
|
|
||||||
TRUE = "TRUE"
|
TRUE = "TRUE"
|
||||||
FALSE = "FALSE"
|
FALSE = "FALSE"
|
||||||
NULL = "NULL"
|
NULL = "NULL"
|
||||||
@@ -62,7 +62,6 @@ const (
|
|||||||
|
|
||||||
var keywords = map[string]TokenType{
|
var keywords = map[string]TokenType{
|
||||||
"fn": FUNCTION,
|
"fn": FUNCTION,
|
||||||
"let": LET,
|
|
||||||
"true": TRUE,
|
"true": TRUE,
|
||||||
"false": FALSE,
|
"false": FALSE,
|
||||||
"if": IF,
|
"if": IF,
|
||||||
|
|||||||
@@ -13,12 +13,13 @@ syntax case match
|
|||||||
|
|
||||||
syntax keyword xType true false null
|
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 xFunction len input print first last rest push pop exit assert
|
||||||
|
|
||||||
syntax keyword xOperator == != < > !
|
syntax keyword xOperator == != < > !
|
||||||
syntax keyword xOperator + - * / =
|
syntax keyword xOperator + - * /
|
||||||
|
syntax keyword xOperator := =
|
||||||
|
|
||||||
syntax region xString start=/"/ skip=/\\./ end=/"/
|
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
|
vm.globals[globalIndex] = ref
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err := vm.push(Null)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
case code.OpAssignGlobal:
|
case code.OpAssignGlobal:
|
||||||
globalIndex := code.ReadUint16(ins[ip+1:])
|
globalIndex := code.ReadUint16(ins[ip+1:])
|
||||||
vm.currentFrame().ip += 2
|
vm.currentFrame().ip += 2
|
||||||
@@ -284,7 +289,17 @@ func (vm *VM) Run() error {
|
|||||||
|
|
||||||
frame := vm.currentFrame()
|
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:
|
case code.OpGetLocal:
|
||||||
localIndex := code.ReadUint8(ins[ip+1:])
|
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 (1 > 2) { 10 }", Null},
|
||||||
{"if (false) { 10 }", Null},
|
{"if (false) { 10 }", Null},
|
||||||
{"if ((if (false) { 10 })) { 10 } else { 20 }", 20},
|
{"if ((if (false) { 10 })) { 10 } else { 20 }", 20},
|
||||||
{"if (true) { let a = 5; }", Null},
|
{"if (true) { a := 5; }", Null},
|
||||||
{"if (true) { 10; let a = 5; }", Null},
|
{"if (true) { 10; a := 5; }", Null},
|
||||||
{"if (false) { 10 } else { let b = 5; }", Null},
|
{"if (false) { 10 } else { b := 5; }", Null},
|
||||||
{"if (false) { 10 } else { 10; let b = 5; }", Null},
|
{"if (false) { 10 } else { 10; b := 5; }", Null},
|
||||||
{"if (true) { let a = 5; } else { 10 }", Null},
|
{"if (true) { a := 5; } else { 10 }", Null},
|
||||||
{"let x = 0; if (true) { x = 1; }; if (false) { x = 2; }; x", 1},
|
{"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 }", 10},
|
||||||
{"if (1 > 2) { 10 } else if (1 == 2) { 20 } else { 30 }", 30},
|
{"if (1 > 2) { 10 } else if (1 == 2) { 20 } else { 30 }", 30},
|
||||||
}
|
}
|
||||||
@@ -274,11 +274,11 @@ func TestConditionals(t *testing.T) {
|
|||||||
runVmTests(t, tests)
|
runVmTests(t, tests)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGlobalLetStatements(t *testing.T) {
|
func TestGlobalBindExpressions(t *testing.T) {
|
||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{"let one = 1; one", 1},
|
{"one := 1; one", 1},
|
||||||
{"let one = 1; let two = 2; one + two", 3},
|
{"one := 1; two := 2; one + two", 3},
|
||||||
{"let one = 1; let two = one + one; one + two", 3},
|
{"one := 1; two := one + one; one + two", 3},
|
||||||
}
|
}
|
||||||
|
|
||||||
runVmTests(t, tests)
|
runVmTests(t, tests)
|
||||||
@@ -364,24 +364,24 @@ func TestCallingFunctionsWithoutArguments(t *testing.T) {
|
|||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let fivePlusTen = fn() { return 5 + 10; };
|
fivePlusTen := fn() { return 5 + 10; };
|
||||||
fivePlusTen();
|
fivePlusTen();
|
||||||
`,
|
`,
|
||||||
expected: 15,
|
expected: 15,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let one = fn() { return 1; };
|
one := fn() { return 1; };
|
||||||
let two = fn() { return 2; };
|
two := fn() { return 2; };
|
||||||
one() + two()
|
one() + two()
|
||||||
`,
|
`,
|
||||||
expected: 3,
|
expected: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let a = fn() { return 1 };
|
a := fn() { return 1 };
|
||||||
let b = fn() { return a() + 1 };
|
b := fn() { return a() + 1 };
|
||||||
let c = fn() { return b() + 1 };
|
c:= fn() { return b() + 1 };
|
||||||
c();
|
c();
|
||||||
`,
|
`,
|
||||||
expected: 3,
|
expected: 3,
|
||||||
@@ -395,14 +395,14 @@ func TestFunctionsWithReturnStatements(t *testing.T) {
|
|||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let earlyExit = fn() { return 99; 100; };
|
earlyExit := fn() { return 99; 100; };
|
||||||
earlyExit();
|
earlyExit();
|
||||||
`,
|
`,
|
||||||
expected: 99,
|
expected: 99,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let earlyExit = fn() { return 99; return 100; };
|
earlyExit := fn() { return 99; return 100; };
|
||||||
earlyExit();
|
earlyExit();
|
||||||
`,
|
`,
|
||||||
expected: 99,
|
expected: 99,
|
||||||
@@ -416,15 +416,15 @@ func TestFunctionsWithoutReturnValue(t *testing.T) {
|
|||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let noReturn = fn() { };
|
noReturn := fn() { };
|
||||||
noReturn();
|
noReturn();
|
||||||
`,
|
`,
|
||||||
expected: Null,
|
expected: Null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let noReturn = fn() { };
|
noReturn := fn() { };
|
||||||
let noReturnTwo = fn() { noReturn(); };
|
noReturnTwo := fn() { noReturn(); };
|
||||||
noReturn();
|
noReturn();
|
||||||
noReturnTwo();
|
noReturnTwo();
|
||||||
`,
|
`,
|
||||||
@@ -439,16 +439,16 @@ func TestFirstClassFunctions(t *testing.T) {
|
|||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let returnsOne = fn() { return 1; };
|
returnsOne := fn() { return 1; };
|
||||||
let returnsOneReturner = fn() { return returnsOne; };
|
returnsOneReturner := fn() { return returnsOne; };
|
||||||
returnsOneReturner()();
|
returnsOneReturner()();
|
||||||
`,
|
`,
|
||||||
expected: 1,
|
expected: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let returnsOneReturner = fn() {
|
returnsOneReturner := fn() {
|
||||||
let returnsOne = fn() { return 1; };
|
returnsOne := fn() { return 1; };
|
||||||
return returnsOne;
|
return returnsOne;
|
||||||
};
|
};
|
||||||
returnsOneReturner()();
|
returnsOneReturner()();
|
||||||
@@ -464,43 +464,43 @@ func TestCallingFunctionsWithBindings(t *testing.T) {
|
|||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
//{
|
//{
|
||||||
// input: `
|
// input: `
|
||||||
// let one = fn() { let one = 1; return one };
|
// one := fn() { one := 1; return one };
|
||||||
// one();
|
// one();
|
||||||
// `,
|
// `,
|
||||||
// expected: 1,
|
// expected: 1,
|
||||||
//},
|
//},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let oneAndTwo = fn() { let one = 1; let two = 2; return one + two; };
|
oneAndTwo := fn() { one := 1; two := 2; return one + two; };
|
||||||
oneAndTwo();
|
oneAndTwo();
|
||||||
`,
|
`,
|
||||||
expected: 3,
|
expected: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let oneAndTwo = fn() { let one = 1; let two = 2; return one + two; };
|
oneAndTwo := fn() { one := 1; two := 2; return one + two; };
|
||||||
let threeAndFour = fn() { let three = 3; let four = 4; return three + four; };
|
threeAndFour := fn() { three := 3; four := 4; return three + four; };
|
||||||
oneAndTwo() + threeAndFour();
|
oneAndTwo() + threeAndFour();
|
||||||
`,
|
`,
|
||||||
expected: 10,
|
expected: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let firstFoobar = fn() { let foobar = 50; return foobar; };
|
firstFoobar := fn() { foobar := 50; return foobar; };
|
||||||
let secondFoobar = fn() { let foobar = 100; return foobar; };
|
secondFoobar := fn() { foobar := 100; return foobar; };
|
||||||
firstFoobar() + secondFoobar();
|
firstFoobar() + secondFoobar();
|
||||||
`,
|
`,
|
||||||
expected: 150,
|
expected: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let globalSeed = 50;
|
globalSeed := 50;
|
||||||
let minusOne = fn() {
|
minusOne := fn() {
|
||||||
let num = 1;
|
num := 1;
|
||||||
return globalSeed - num;
|
return globalSeed - num;
|
||||||
}
|
}
|
||||||
let minusTwo = fn() {
|
minusTwo := fn() {
|
||||||
let num = 2;
|
num := 2;
|
||||||
return globalSeed - num;
|
return globalSeed - num;
|
||||||
}
|
}
|
||||||
minusOne() + minusTwo();
|
minusOne() + minusTwo();
|
||||||
@@ -516,22 +516,22 @@ func TestCallingFunctionsWithArgumentsAndBindings(t *testing.T) {
|
|||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let identity = fn(a) { return a; };
|
identity := fn(a) { return a; };
|
||||||
identity(4);
|
identity(4);
|
||||||
`,
|
`,
|
||||||
expected: 4,
|
expected: 4,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let sum = fn(a, b) { return a + b; };
|
sum := fn(a, b) { return a + b; };
|
||||||
sum(1, 2);
|
sum(1, 2);
|
||||||
`,
|
`,
|
||||||
expected: 3,
|
expected: 3,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let sum = fn(a, b) {
|
sum := fn(a, b) {
|
||||||
let c = a + b;
|
c := a + b;
|
||||||
return c;
|
return c;
|
||||||
};
|
};
|
||||||
sum(1, 2);
|
sum(1, 2);
|
||||||
@@ -540,8 +540,8 @@ func TestCallingFunctionsWithArgumentsAndBindings(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let sum = fn(a, b) {
|
sum := fn(a, b) {
|
||||||
let c = a + b;
|
c := a + b;
|
||||||
return c;
|
return c;
|
||||||
};
|
};
|
||||||
sum(1, 2) + sum(3, 4);`,
|
sum(1, 2) + sum(3, 4);`,
|
||||||
@@ -549,11 +549,11 @@ func TestCallingFunctionsWithArgumentsAndBindings(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let sum = fn(a, b) {
|
sum := fn(a, b) {
|
||||||
let c = a + b;
|
c := a + b;
|
||||||
return c;
|
return c;
|
||||||
};
|
};
|
||||||
let outer = fn() {
|
outer := fn() {
|
||||||
return sum(1, 2) + sum(3, 4);
|
return sum(1, 2) + sum(3, 4);
|
||||||
};
|
};
|
||||||
outer();
|
outer();
|
||||||
@@ -562,14 +562,14 @@ func TestCallingFunctionsWithArgumentsAndBindings(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let globalNum = 10;
|
globalNum := 10;
|
||||||
|
|
||||||
let sum = fn(a, b) {
|
sum := fn(a, b) {
|
||||||
let c = a + b;
|
c := a + b;
|
||||||
return c + globalNum;
|
return c + globalNum;
|
||||||
};
|
};
|
||||||
|
|
||||||
let outer = fn() {
|
outer := fn() {
|
||||||
return sum(1, 2) + sum(3, 4) + globalNum;
|
return sum(1, 2) + sum(3, 4) + globalNum;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -697,72 +697,72 @@ func TestClosures(t *testing.T) {
|
|||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let newClosure = fn(a) {
|
newClosure := fn(a) {
|
||||||
return fn() { return a; };
|
return fn() { return a; };
|
||||||
};
|
};
|
||||||
let closure = newClosure(99);
|
closure := newClosure(99);
|
||||||
closure();
|
closure();
|
||||||
`,
|
`,
|
||||||
expected: 99,
|
expected: 99,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let newAdder = fn(a, b) {
|
newAdder := fn(a, b) {
|
||||||
return fn(c) { return a + b + c };
|
return fn(c) { return a + b + c };
|
||||||
};
|
};
|
||||||
let adder = newAdder(1, 2);
|
adder := newAdder(1, 2);
|
||||||
adder(8);
|
adder(8);
|
||||||
`,
|
`,
|
||||||
expected: 11,
|
expected: 11,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let newAdder = fn(a, b) {
|
newAdder := fn(a, b) {
|
||||||
let c = a + b;
|
c := a + b;
|
||||||
return fn(d) { return c + d };
|
return fn(d) { return c + d };
|
||||||
};
|
};
|
||||||
let adder = newAdder(1, 2);
|
adder := newAdder(1, 2);
|
||||||
adder(8);
|
adder(8);
|
||||||
`,
|
`,
|
||||||
expected: 11,
|
expected: 11,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let newAdderOuter = fn(a, b) {
|
newAdderOuter := fn(a, b) {
|
||||||
let c = a + b;
|
c := a + b;
|
||||||
return fn(d) {
|
return fn(d) {
|
||||||
let e = d + c;
|
e := d + c;
|
||||||
return fn(f) { return e + f; };
|
return fn(f) { return e + f; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let newAdderInner = newAdderOuter(1, 2)
|
newAdderInner := newAdderOuter(1, 2)
|
||||||
let adder = newAdderInner(3);
|
adder := newAdderInner(3);
|
||||||
adder(8);
|
adder(8);
|
||||||
`,
|
`,
|
||||||
expected: 14,
|
expected: 14,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let a = 1;
|
a := 1;
|
||||||
let newAdderOuter = fn(b) {
|
newAdderOuter := fn(b) {
|
||||||
return fn(c) {
|
return fn(c) {
|
||||||
return fn(d) { return a + b + c + d };
|
return fn(d) { return a + b + c + d };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
let newAdderInner = newAdderOuter(2)
|
newAdderInner := newAdderOuter(2)
|
||||||
let adder = newAdderInner(3);
|
adder := newAdderInner(3);
|
||||||
adder(8);
|
adder(8);
|
||||||
`,
|
`,
|
||||||
expected: 14,
|
expected: 14,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let newClosure = fn(a, b) {
|
newClosure := fn(a, b) {
|
||||||
let one = fn() { return a; };
|
one := fn() { return a; };
|
||||||
let two = fn() { return b; };
|
two := fn() { return b; };
|
||||||
return fn() { return one() + two(); };
|
return fn() { return one() + two(); };
|
||||||
};
|
};
|
||||||
let closure = newClosure(9, 90);
|
closure := newClosure(9, 90);
|
||||||
closure();
|
closure();
|
||||||
`,
|
`,
|
||||||
expected: 99,
|
expected: 99,
|
||||||
@@ -776,7 +776,7 @@ func TestRecursiveFunctions(t *testing.T) {
|
|||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let countDown = fn(x) {
|
countDown := fn(x) {
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -789,14 +789,14 @@ func TestRecursiveFunctions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let countDown = fn(x) {
|
countDown := fn(x) {
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
return countDown(x - 1);
|
return countDown(x - 1);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let wrapper = fn() {
|
wrapper := fn() {
|
||||||
return countDown(1);
|
return countDown(1);
|
||||||
};
|
};
|
||||||
wrapper();
|
wrapper();
|
||||||
@@ -805,8 +805,8 @@ func TestRecursiveFunctions(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let wrapper = fn() {
|
wrapper := fn() {
|
||||||
let countDown = fn(x) {
|
countDown := fn(x) {
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -828,7 +828,7 @@ func TestRecursiveFibonacci(t *testing.T) {
|
|||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let fibonacci = fn(x) {
|
fibonacci := fn(x) {
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -851,14 +851,14 @@ func TestRecursiveFibonacci(t *testing.T) {
|
|||||||
func TestIterations(t *testing.T) {
|
func TestIterations(t *testing.T) {
|
||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{"while (false) { }", nil},
|
{"while (false) { }", nil},
|
||||||
{"let n = 0; while (n < 10) { let n = n + 1 }; n", 10},
|
{"n := 0; while (n < 10) { n := n + 1 }; n", 10},
|
||||||
{"let n = 10; while (n > 0) { let n = n - 1 }; n", 0},
|
{"n := 10; while (n > 0) { n := n - 1 }; n", 0},
|
||||||
{"let n = 0; while (n < 10) { let n = n + 1 }", nil},
|
{"n := 0; while (n < 10) { n := n + 1 }", nil},
|
||||||
{"let n = 10; while (n > 0) { let n = n - 1 }", nil},
|
{"n := 10; while (n > 0) { n := n - 1 }", nil},
|
||||||
{"let n = 0; while (n < 10) { n = n + 1 }; n", 10},
|
{"n := 0; while (n < 10) { n = n + 1 }; n", 10},
|
||||||
{"let n = 10; while (n > 0) { n = n - 1 }; n", 0},
|
{"n := 10; while (n > 0) { n = n - 1 }; n", 0},
|
||||||
{"let n = 0; while (n < 10) { n = n + 1 }", nil},
|
{"n := 0; while (n < 10) { n = n + 1 }", nil},
|
||||||
{"let n = 10; while (n > 0) { n = n - 1 }", nil},
|
{"n := 10; while (n > 0) { n = n - 1 }", nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
runVmTests(t, tests)
|
runVmTests(t, tests)
|
||||||
@@ -866,7 +866,7 @@ func TestIterations(t *testing.T) {
|
|||||||
|
|
||||||
func TestIndexAssignmentStatements(t *testing.T) {
|
func TestIndexAssignmentStatements(t *testing.T) {
|
||||||
tests := []vmTestCase{
|
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)
|
runVmTests(t, tests)
|
||||||
@@ -874,20 +874,20 @@ func TestIndexAssignmentStatements(t *testing.T) {
|
|||||||
|
|
||||||
func TestAssignmentExpressions(t *testing.T) {
|
func TestAssignmentExpressions(t *testing.T) {
|
||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{"let a = 0; a = 5;", nil},
|
{"a := 0; a = 5;", nil},
|
||||||
{"let a = 0; a = 5; a;", 5},
|
{"a := 0; a = 5; a;", 5},
|
||||||
{"let a = 0; a = 5 * 5;", nil},
|
{"a := 0; a = 5 * 5;", nil},
|
||||||
{"let a = 0; a = 5 * 5; a;", 25},
|
{"a := 0; a = 5 * 5; a;", 25},
|
||||||
{"let a = 0; a = 5; let b = 0; b = a;", nil},
|
{"a := 0; a = 5; b := 0; b = a;", nil},
|
||||||
{"let a = 0; a = 5; let b = 0; b = a; b;", 5},
|
{"a := 0; a = 5; b := 0; b = a; b;", 5},
|
||||||
{"let a = 0; a = 5; let b = 0; b = a; let c = 0; c = a + b + 5;", nil},
|
{"a := 0; a = 5; b := 0; b = a; 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},
|
{"a := 0; a = 5; b := 0; b = a; c := 0; c = a + b + 5; c;", 15},
|
||||||
{"let a = 5; let b = a; a = 0;", nil},
|
{"a := 5; b := a; a = 0;", nil},
|
||||||
{"let a = 5; let b = a; a = 0; b;", 5},
|
{"a := 5; b := a; a = 0; b;", 5},
|
||||||
{"let one = 0; one = 1", nil},
|
{"one := 0; one = 1", nil},
|
||||||
{"let one = 0; one = 1; one", 1},
|
{"one := 0; one = 1; one", 1},
|
||||||
{"let one = 0; one = 1; let two = 0; two = 2; one + two", 3},
|
{"one := 0; one = 1; two := 0; two = 2; one + two", 3},
|
||||||
{"let one = 0; one = 1; let two = 0; two = one + one; one + two", 3},
|
{"one := 0; one = 1; two := 0; two = one + one; one + two", 3},
|
||||||
}
|
}
|
||||||
|
|
||||||
runVmTests(t, tests)
|
runVmTests(t, tests)
|
||||||
@@ -897,7 +897,7 @@ func TestTailCalls(t *testing.T) {
|
|||||||
tests := []vmTestCase{
|
tests := []vmTestCase{
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let fact = fn(n, a) {
|
fact := fn(n, a) {
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
@@ -912,7 +912,7 @@ func TestTailCalls(t *testing.T) {
|
|||||||
// without tail recursion optimization this will cause a stack overflow
|
// without tail recursion optimization this will cause a stack overflow
|
||||||
{
|
{
|
||||||
input: `
|
input: `
|
||||||
let iter = fn(n, max) {
|
iter := fn(n, max) {
|
||||||
if (n == max) {
|
if (n == max) {
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
@@ -971,14 +971,14 @@ func TestIntegration(t *testing.T) {
|
|||||||
func BenchmarkFibonacci(b *testing.B) {
|
func BenchmarkFibonacci(b *testing.B) {
|
||||||
tests := map[string]string{
|
tests := map[string]string{
|
||||||
"iterative": `
|
"iterative": `
|
||||||
let fib = fn(n) {
|
fib := fn(n) {
|
||||||
if (n < 3) {
|
if (n < 3) {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
let a = 1
|
a := 1
|
||||||
let b = 1
|
b := 1
|
||||||
let c = 0
|
c := 0
|
||||||
let i = 0
|
i := 0
|
||||||
while (i < n - 2) {
|
while (i < n - 2) {
|
||||||
c = a + b
|
c = a + b
|
||||||
b = a
|
b = a
|
||||||
@@ -991,7 +991,7 @@ func BenchmarkFibonacci(b *testing.B) {
|
|||||||
fib(35)
|
fib(35)
|
||||||
`,
|
`,
|
||||||
"recursive": `
|
"recursive": `
|
||||||
let fib = fn(x) {
|
fib := fn(x) {
|
||||||
if (x == 0) {
|
if (x == 0) {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@@ -1004,7 +1004,7 @@ func BenchmarkFibonacci(b *testing.B) {
|
|||||||
fib(35)
|
fib(35)
|
||||||
`,
|
`,
|
||||||
"tail-recursive": `
|
"tail-recursive": `
|
||||||
let fib = fn(n, a, b) {
|
fib := fn(n, a, b) {
|
||||||
if (n == 0) {
|
if (n == 0) {
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user