variable definition (operator ":=") is now required not optional
This commit is contained in:
parent
54bea46d19
commit
d870ebf72e
20 changed files with 473 additions and 232 deletions
|
@ -14,6 +14,42 @@ import (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
runFib(35)
|
runFib(35)
|
||||||
|
runFibTailCall(35)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runFibTailCall(n int) {
|
||||||
|
start := time.Now()
|
||||||
|
nativeResult := fibTC(n, 0)
|
||||||
|
nativeTime := time.Since(start)
|
||||||
|
|
||||||
|
input := `
|
||||||
|
fib := func(x, s) {
|
||||||
|
if x == 0 {
|
||||||
|
return s
|
||||||
|
} else if x == 1 {
|
||||||
|
return 1 + s
|
||||||
|
} else {
|
||||||
|
return fib(x-1, fib(x-2, s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
` + fmt.Sprintf("out = fib(%d, 0)", n)
|
||||||
|
|
||||||
|
parseTime, compileTime, runTime, result, err := runBench([]byte(input))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if nativeResult != int(result.(*objects.Int).Value) {
|
||||||
|
panic(fmt.Errorf("wrong result: %d != %d", nativeResult, int(result.(*objects.Int).Value)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("-------------------------------------")
|
||||||
|
fmt.Printf("fibonacci(%d) = %d (tail-call)\n", n, nativeResult)
|
||||||
|
fmt.Println("-------------------------------------")
|
||||||
|
fmt.Printf("Go: %s\n", nativeTime)
|
||||||
|
fmt.Printf("Parser: %s\n", parseTime)
|
||||||
|
fmt.Printf("Compile: %s\n", compileTime)
|
||||||
|
fmt.Printf("VM: %s\n", runTime)
|
||||||
}
|
}
|
||||||
|
|
||||||
func runFib(n int) {
|
func runFib(n int) {
|
||||||
|
@ -42,7 +78,8 @@ fib := func(x) {
|
||||||
panic(fmt.Errorf("wrong result: %d != %d", nativeResult, int(result.(*objects.Int).Value)))
|
panic(fmt.Errorf("wrong result: %d != %d", nativeResult, int(result.(*objects.Int).Value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("fib(%d) = %d\n", n, nativeResult)
|
fmt.Println("-------------------------------------")
|
||||||
|
fmt.Printf("fibonacci(%d) = %d\n", n, nativeResult)
|
||||||
fmt.Println("-------------------------------------")
|
fmt.Println("-------------------------------------")
|
||||||
fmt.Printf("Go: %s\n", nativeTime)
|
fmt.Printf("Go: %s\n", nativeTime)
|
||||||
fmt.Printf("Parser: %s\n", parseTime)
|
fmt.Printf("Parser: %s\n", parseTime)
|
||||||
|
@ -60,6 +97,16 @@ func fib(n int) int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func fibTC(n, s int) int {
|
||||||
|
if n == 0 {
|
||||||
|
return s
|
||||||
|
} else if n == 1 {
|
||||||
|
return 1 + s
|
||||||
|
} else {
|
||||||
|
return fibTC(n-1, fibTC(n-2, s))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func runBench(input []byte) (parseTime time.Duration, compileTime time.Duration, runTime time.Duration, result objects.Object, err error) {
|
func runBench(input []byte) (parseTime time.Duration, compileTime time.Duration, runTime time.Duration, result objects.Object, err error) {
|
||||||
var astFile *ast.File
|
var astFile *ast.File
|
||||||
parseTime, astFile, err = parse(input)
|
parseTime, astFile, err = parse(input)
|
||||||
|
|
|
@ -42,10 +42,6 @@ func (c *Compiler) compileAssign(lhs, rhs []ast.Expr, op token.Token) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
symbol = c.symbolTable.Define(ident)
|
symbol = c.symbolTable.Define(ident)
|
||||||
} else if op == token.Assign {
|
|
||||||
if !exists {
|
|
||||||
symbol = c.symbolTable.Define(ident)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if !exists {
|
if !exists {
|
||||||
return fmt.Errorf("unresolved reference '%s'", ident)
|
return fmt.Errorf("unresolved reference '%s'", ident)
|
||||||
|
@ -109,7 +105,11 @@ func (c *Compiler) compileAssign(lhs, rhs []ast.Expr, op token.Token) error {
|
||||||
if numSel > 0 {
|
if numSel > 0 {
|
||||||
c.emit(OpSetSelLocal, symbol.Index, numSel)
|
c.emit(OpSetSelLocal, symbol.Index, numSel)
|
||||||
} else {
|
} else {
|
||||||
c.emit(OpSetLocal, symbol.Index)
|
if op == token.Define {
|
||||||
|
c.emit(OpDefineLocal, symbol.Index)
|
||||||
|
} else {
|
||||||
|
c.emit(OpSetLocal, symbol.Index)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case ScopeFree:
|
case ScopeFree:
|
||||||
if numSel > 0 {
|
if numSel > 0 {
|
||||||
|
|
|
@ -74,7 +74,7 @@ func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
||||||
|
|
||||||
// for-in statement is compiled like following:
|
// for-in statement is compiled like following:
|
||||||
//
|
//
|
||||||
// for :it = iterator(iterable); :it.next(); {
|
// for :it := iterator(iterable); :it.next(); {
|
||||||
// k, v := :it.get() // DEFINE operator
|
// k, v := :it.get() // DEFINE operator
|
||||||
//
|
//
|
||||||
// ... body ...
|
// ... body ...
|
||||||
|
@ -93,7 +93,7 @@ func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
||||||
if itSymbol.Scope == ScopeGlobal {
|
if itSymbol.Scope == ScopeGlobal {
|
||||||
c.emit(OpSetGlobal, itSymbol.Index)
|
c.emit(OpSetGlobal, itSymbol.Index)
|
||||||
} else {
|
} else {
|
||||||
c.emit(OpSetLocal, itSymbol.Index)
|
c.emit(OpDefineLocal, itSymbol.Index)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pre-condition position
|
// pre-condition position
|
||||||
|
@ -126,7 +126,7 @@ func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
||||||
if keySymbol.Scope == ScopeGlobal {
|
if keySymbol.Scope == ScopeGlobal {
|
||||||
c.emit(OpSetGlobal, keySymbol.Index)
|
c.emit(OpSetGlobal, keySymbol.Index)
|
||||||
} else {
|
} else {
|
||||||
c.emit(OpSetLocal, keySymbol.Index)
|
c.emit(OpDefineLocal, keySymbol.Index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
||||||
if valueSymbol.Scope == ScopeGlobal {
|
if valueSymbol.Scope == ScopeGlobal {
|
||||||
c.emit(OpSetGlobal, valueSymbol.Index)
|
c.emit(OpSetGlobal, valueSymbol.Index)
|
||||||
} else {
|
} else {
|
||||||
c.emit(OpSetLocal, valueSymbol.Index)
|
c.emit(OpDefineLocal, valueSymbol.Index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -619,7 +619,7 @@ func TestCompiler_Compile(t *testing.T) {
|
||||||
intObject(55),
|
intObject(55),
|
||||||
compiledFunction(1, 0,
|
compiledFunction(1, 0,
|
||||||
compiler.MakeInstruction(compiler.OpConstant, 0),
|
compiler.MakeInstruction(compiler.OpConstant, 0),
|
||||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
compiler.MakeInstruction(compiler.OpDefineLocal, 0),
|
||||||
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
||||||
compiler.MakeInstruction(compiler.OpReturnValue, 1)))))
|
compiler.MakeInstruction(compiler.OpReturnValue, 1)))))
|
||||||
|
|
||||||
|
@ -633,7 +633,7 @@ func TestCompiler_Compile(t *testing.T) {
|
||||||
intObject(23),
|
intObject(23),
|
||||||
compiledFunction(1, 0,
|
compiledFunction(1, 0,
|
||||||
compiler.MakeInstruction(compiler.OpConstant, 0),
|
compiler.MakeInstruction(compiler.OpConstant, 0),
|
||||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
compiler.MakeInstruction(compiler.OpDefineLocal, 0),
|
||||||
compiler.MakeInstruction(compiler.OpConstant, 1),
|
compiler.MakeInstruction(compiler.OpConstant, 1),
|
||||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
||||||
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
||||||
|
@ -649,9 +649,9 @@ func TestCompiler_Compile(t *testing.T) {
|
||||||
intObject(77),
|
intObject(77),
|
||||||
compiledFunction(2, 0,
|
compiledFunction(2, 0,
|
||||||
compiler.MakeInstruction(compiler.OpConstant, 0),
|
compiler.MakeInstruction(compiler.OpConstant, 0),
|
||||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
compiler.MakeInstruction(compiler.OpDefineLocal, 0),
|
||||||
compiler.MakeInstruction(compiler.OpConstant, 1),
|
compiler.MakeInstruction(compiler.OpConstant, 1),
|
||||||
compiler.MakeInstruction(compiler.OpSetLocal, 1),
|
compiler.MakeInstruction(compiler.OpDefineLocal, 1),
|
||||||
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
||||||
compiler.MakeInstruction(compiler.OpGetLocal, 1),
|
compiler.MakeInstruction(compiler.OpGetLocal, 1),
|
||||||
compiler.MakeInstruction(compiler.OpAdd),
|
compiler.MakeInstruction(compiler.OpAdd),
|
||||||
|
@ -792,7 +792,7 @@ func() {
|
||||||
intObject(88),
|
intObject(88),
|
||||||
compiledFunction(1, 0,
|
compiledFunction(1, 0,
|
||||||
compiler.MakeInstruction(compiler.OpConstant, 3),
|
compiler.MakeInstruction(compiler.OpConstant, 3),
|
||||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
compiler.MakeInstruction(compiler.OpDefineLocal, 0),
|
||||||
compiler.MakeInstruction(compiler.OpGetGlobal, 0),
|
compiler.MakeInstruction(compiler.OpGetGlobal, 0),
|
||||||
compiler.MakeInstruction(compiler.OpGetFree, 0),
|
compiler.MakeInstruction(compiler.OpGetFree, 0),
|
||||||
compiler.MakeInstruction(compiler.OpAdd),
|
compiler.MakeInstruction(compiler.OpAdd),
|
||||||
|
@ -803,14 +803,14 @@ func() {
|
||||||
compiler.MakeInstruction(compiler.OpReturnValue, 1)),
|
compiler.MakeInstruction(compiler.OpReturnValue, 1)),
|
||||||
compiledFunction(1, 0,
|
compiledFunction(1, 0,
|
||||||
compiler.MakeInstruction(compiler.OpConstant, 2),
|
compiler.MakeInstruction(compiler.OpConstant, 2),
|
||||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
compiler.MakeInstruction(compiler.OpDefineLocal, 0),
|
||||||
compiler.MakeInstruction(compiler.OpGetFree, 0),
|
compiler.MakeInstruction(compiler.OpGetFree, 0),
|
||||||
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
||||||
compiler.MakeInstruction(compiler.OpClosure, 4, 2),
|
compiler.MakeInstruction(compiler.OpClosure, 4, 2),
|
||||||
compiler.MakeInstruction(compiler.OpReturnValue, 1)),
|
compiler.MakeInstruction(compiler.OpReturnValue, 1)),
|
||||||
compiledFunction(1, 0,
|
compiledFunction(1, 0,
|
||||||
compiler.MakeInstruction(compiler.OpConstant, 1),
|
compiler.MakeInstruction(compiler.OpConstant, 1),
|
||||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
compiler.MakeInstruction(compiler.OpDefineLocal, 0),
|
||||||
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
||||||
compiler.MakeInstruction(compiler.OpClosure, 5, 1),
|
compiler.MakeInstruction(compiler.OpClosure, 5, 1),
|
||||||
compiler.MakeInstruction(compiler.OpReturnValue, 1)))))
|
compiler.MakeInstruction(compiler.OpReturnValue, 1)))))
|
||||||
|
|
|
@ -45,6 +45,7 @@ var definitions = map[Opcode]*Definition{
|
||||||
OpReturnValue: {Name: "RETVAL", Operands: []int{1}},
|
OpReturnValue: {Name: "RETVAL", Operands: []int{1}},
|
||||||
OpGetLocal: {Name: "GETL", Operands: []int{1}},
|
OpGetLocal: {Name: "GETL", Operands: []int{1}},
|
||||||
OpSetLocal: {Name: "SETL", Operands: []int{1}},
|
OpSetLocal: {Name: "SETL", Operands: []int{1}},
|
||||||
|
OpDefineLocal: {Name: "DEFL", Operands: []int{1}},
|
||||||
OpSetSelLocal: {Name: "SETSL", Operands: []int{1, 1}},
|
OpSetSelLocal: {Name: "SETSL", Operands: []int{1, 1}},
|
||||||
OpGetBuiltin: {Name: "BUILTIN", Operands: []int{1}},
|
OpGetBuiltin: {Name: "BUILTIN", Operands: []int{1}},
|
||||||
OpClosure: {Name: "CLOSURE", Operands: []int{2, 1}},
|
OpClosure: {Name: "CLOSURE", Operands: []int{2, 1}},
|
||||||
|
|
|
@ -30,9 +30,6 @@ const (
|
||||||
OpOrJump
|
OpOrJump
|
||||||
OpJump
|
OpJump
|
||||||
OpNull
|
OpNull
|
||||||
OpGetGlobal
|
|
||||||
OpSetGlobal
|
|
||||||
OpSetSelGlobal
|
|
||||||
OpArray
|
OpArray
|
||||||
OpMap
|
OpMap
|
||||||
OpIndex
|
OpIndex
|
||||||
|
@ -40,14 +37,18 @@ const (
|
||||||
OpCall
|
OpCall
|
||||||
OpReturn
|
OpReturn
|
||||||
OpReturnValue
|
OpReturnValue
|
||||||
|
OpGetGlobal
|
||||||
|
OpSetGlobal
|
||||||
|
OpSetSelGlobal
|
||||||
OpGetLocal
|
OpGetLocal
|
||||||
OpSetLocal
|
OpSetLocal
|
||||||
|
OpDefineLocal
|
||||||
OpSetSelLocal
|
OpSetSelLocal
|
||||||
OpGetBuiltin
|
|
||||||
OpClosure
|
|
||||||
OpGetFree
|
OpGetFree
|
||||||
OpSetFree
|
OpSetFree
|
||||||
OpSetSelFree
|
OpSetSelFree
|
||||||
|
OpGetBuiltin
|
||||||
|
OpClosure
|
||||||
OpIteratorInit
|
OpIteratorInit
|
||||||
OpIteratorNext
|
OpIteratorNext
|
||||||
OpIteratorKey
|
OpIteratorKey
|
||||||
|
|
|
@ -11,34 +11,34 @@ type M map[string]interface{}
|
||||||
|
|
||||||
func TestCompiled_Get(t *testing.T) {
|
func TestCompiled_Get(t *testing.T) {
|
||||||
// simple script
|
// simple script
|
||||||
c := compile(t, `a = 5`, nil)
|
c := compile(t, `a := 5`, nil)
|
||||||
compiledRun(t, c)
|
compiledRun(t, c)
|
||||||
compiledGet(t, c, "a", int64(5))
|
compiledGet(t, c, "a", int64(5))
|
||||||
|
|
||||||
// user-defined variables
|
// user-defined variables
|
||||||
compileError(t, `a = b`, nil) // compile error because "b" is not defined
|
compileError(t, `a := b`, nil) // compile error because "b" is not defined
|
||||||
c = compile(t, `a = b`, M{"b": "foo"}) // now compile with b = "foo" defined
|
c = compile(t, `a := b`, M{"b": "foo"}) // now compile with b = "foo" defined
|
||||||
compiledGet(t, c, "a", nil) // a = undefined; because it's before Compiled.Run()
|
compiledGet(t, c, "a", nil) // a = undefined; because it's before Compiled.Run()
|
||||||
compiledRun(t, c) // Compiled.Run()
|
compiledRun(t, c) // Compiled.Run()
|
||||||
compiledGet(t, c, "a", "foo") // a = "foo"
|
compiledGet(t, c, "a", "foo") // a = "foo"
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompiled_GetAll(t *testing.T) {
|
func TestCompiled_GetAll(t *testing.T) {
|
||||||
c := compile(t, `a = 5`, nil)
|
c := compile(t, `a := 5`, nil)
|
||||||
compiledRun(t, c)
|
compiledRun(t, c)
|
||||||
compiledGetAll(t, c, M{"a": int64(5)})
|
compiledGetAll(t, c, M{"a": int64(5)})
|
||||||
|
|
||||||
c = compile(t, `a = b`, M{"b": "foo"})
|
c = compile(t, `a := b`, M{"b": "foo"})
|
||||||
compiledRun(t, c)
|
compiledRun(t, c)
|
||||||
compiledGetAll(t, c, M{"a": "foo", "b": "foo"})
|
compiledGetAll(t, c, M{"a": "foo", "b": "foo"})
|
||||||
|
|
||||||
c = compile(t, `a = b; b = 5`, M{"b": "foo"})
|
c = compile(t, `a := b; b = 5`, M{"b": "foo"})
|
||||||
compiledRun(t, c)
|
compiledRun(t, c)
|
||||||
compiledGetAll(t, c, M{"a": "foo", "b": int64(5)})
|
compiledGetAll(t, c, M{"a": "foo", "b": int64(5)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompiled_IsDefined(t *testing.T) {
|
func TestCompiled_IsDefined(t *testing.T) {
|
||||||
c := compile(t, `a = 5`, nil)
|
c := compile(t, `a := 5`, nil)
|
||||||
compiledIsDefined(t, c, "a", false) // a is not defined before Run()
|
compiledIsDefined(t, c, "a", false) // a is not defined before Run()
|
||||||
compiledRun(t, c)
|
compiledRun(t, c)
|
||||||
compiledIsDefined(t, c, "a", true)
|
compiledIsDefined(t, c, "a", true)
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestScript_Add(t *testing.T) {
|
func TestScript_Add(t *testing.T) {
|
||||||
s := script.New([]byte(`a = b`))
|
s := script.New([]byte(`a := b`))
|
||||||
assert.NoError(t, s.Add("b", 5)) // b = 5
|
assert.NoError(t, s.Add("b", 5)) // b = 5
|
||||||
assert.NoError(t, s.Add("b", "foo")) // b = "foo" (re-define before compilation)
|
assert.NoError(t, s.Add("b", "foo")) // b = "foo" (re-define before compilation)
|
||||||
c, err := s.Compile()
|
c, err := s.Compile()
|
||||||
|
@ -19,7 +19,7 @@ func TestScript_Add(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestScript_Remove(t *testing.T) {
|
func TestScript_Remove(t *testing.T) {
|
||||||
s := script.New([]byte(`a = b`))
|
s := script.New([]byte(`a := b`))
|
||||||
err := s.Add("b", 5)
|
err := s.Add("b", 5)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.True(t, s.Remove("b")) // b is removed
|
assert.True(t, s.Remove("b")) // b is removed
|
||||||
|
|
100
vm/vm.go
100
vm/vm.go
|
@ -81,6 +81,7 @@ func (v *VM) Run() error {
|
||||||
if err := v.push(&v.constants[cidx]); err != nil {
|
if err := v.push(&v.constants[cidx]); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case compiler.OpNull:
|
case compiler.OpNull:
|
||||||
if err := v.push(&undefinedObj); err != nil {
|
if err := v.push(&undefinedObj); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -530,14 +531,29 @@ func (v *VM) Run() error {
|
||||||
if err := v.push(&undefinedObj); err != nil {
|
if err := v.push(&undefinedObj); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case compiler.OpDefineLocal:
|
||||||
|
localIndex := compiler.ReadUint8(ins[ip+1:])
|
||||||
|
curFrame.ip += 1
|
||||||
|
|
||||||
|
sp := curFrame.basePointer + int(localIndex)
|
||||||
|
|
||||||
|
// local variables can be mutated by other actions
|
||||||
|
// so always store the copy of popped value
|
||||||
|
val := v.popValue()
|
||||||
|
v.stack[sp] = &val
|
||||||
|
|
||||||
case compiler.OpSetLocal:
|
case compiler.OpSetLocal:
|
||||||
localIndex := compiler.ReadUint8(ins[ip+1:])
|
localIndex := compiler.ReadUint8(ins[ip+1:])
|
||||||
curFrame.ip += 1
|
curFrame.ip += 1
|
||||||
|
|
||||||
sp := curFrame.basePointer + int(localIndex)
|
sp := curFrame.basePointer + int(localIndex)
|
||||||
val := v.pop()
|
|
||||||
|
|
||||||
v.stack[sp] = val
|
// update pointee of v.stack[sp] instead of replacing the pointer itself.
|
||||||
|
// this is needed because there can be free variables referencing the same local variables.
|
||||||
|
val := v.pop()
|
||||||
|
*v.stack[sp] = *val // also use a copy of popped value
|
||||||
|
|
||||||
case compiler.OpSetSelLocal:
|
case compiler.OpSetSelLocal:
|
||||||
localIndex := compiler.ReadUint8(ins[ip+1:])
|
localIndex := compiler.ReadUint8(ins[ip+1:])
|
||||||
numSelectors := int(compiler.ReadUint8(ins[ip+2:]))
|
numSelectors := int(compiler.ReadUint8(ins[ip+2:]))
|
||||||
|
@ -559,7 +575,7 @@ func (v *VM) Run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RHS value
|
// RHS value
|
||||||
val := v.pop()
|
val := v.pop() // no need to copy value here; selectorAssign uses copy of value
|
||||||
|
|
||||||
sp := curFrame.basePointer + int(localIndex)
|
sp := curFrame.basePointer + int(localIndex)
|
||||||
|
|
||||||
|
@ -632,6 +648,7 @@ func (v *VM) Run() error {
|
||||||
val := v.pop()
|
val := v.pop()
|
||||||
|
|
||||||
*v.frames[v.framesIndex-1].freeVars[freeIndex] = *val
|
*v.frames[v.framesIndex-1].freeVars[freeIndex] = *val
|
||||||
|
|
||||||
case compiler.OpIteratorInit:
|
case compiler.OpIteratorInit:
|
||||||
var iterator objects.Object
|
var iterator objects.Object
|
||||||
|
|
||||||
|
@ -650,6 +667,7 @@ func (v *VM) Run() error {
|
||||||
if err := v.push(&iterator); err != nil {
|
if err := v.push(&iterator); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case compiler.OpIteratorNext:
|
case compiler.OpIteratorNext:
|
||||||
iterator := v.pop()
|
iterator := v.pop()
|
||||||
b := (*iterator).(objects.Iterator).Next()
|
b := (*iterator).(objects.Iterator).Next()
|
||||||
|
@ -716,6 +734,13 @@ func (v *VM) pop() *objects.Object {
|
||||||
return o
|
return o
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *VM) popValue() objects.Object {
|
||||||
|
o := v.stack[v.sp-1]
|
||||||
|
v.sp--
|
||||||
|
|
||||||
|
return *o
|
||||||
|
}
|
||||||
|
|
||||||
func (v *VM) pushClosure(constIndex, numFree int) error {
|
func (v *VM) pushClosure(constIndex, numFree int) error {
|
||||||
c := v.constants[constIndex]
|
c := v.constants[constIndex]
|
||||||
|
|
||||||
|
@ -853,6 +878,61 @@ func (v *VM) callFunction(fn *objects.CompiledFunction, freeVars []*objects.Obje
|
||||||
fn.NumParameters, numArgs)
|
fn.NumParameters, numArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if this is a tail-call (recursive call right before return)
|
||||||
|
curFrame := &(v.frames[v.framesIndex-1])
|
||||||
|
if fn == curFrame.fn {
|
||||||
|
nextOp := compiler.Opcode(curFrame.fn.Instructions[curFrame.ip+1])
|
||||||
|
if nextOp == compiler.OpReturnValue ||
|
||||||
|
(nextOp == compiler.OpPop &&
|
||||||
|
compiler.OpReturn == compiler.Opcode(curFrame.fn.Instructions[curFrame.ip+2])) {
|
||||||
|
|
||||||
|
// stack before tail-call
|
||||||
|
//
|
||||||
|
// |--------|
|
||||||
|
// | | <- SP current
|
||||||
|
// |--------|
|
||||||
|
// | *ARG2 | for next function (tail-call)
|
||||||
|
// |--------|
|
||||||
|
// | *ARG1 | for next function (tail-call)
|
||||||
|
// |--------|
|
||||||
|
// | LOCAL3 | for current function
|
||||||
|
// |--------|
|
||||||
|
// | LOCAL2 | for current function
|
||||||
|
// |--------|
|
||||||
|
// | ARG2 | for current function
|
||||||
|
// |--------|
|
||||||
|
// | ARG1 | <- BP for current function
|
||||||
|
// |--------|
|
||||||
|
|
||||||
|
for i := 0; i < numArgs; i++ {
|
||||||
|
v.stack[curFrame.basePointer+i] = v.stack[v.sp-numArgs+i]
|
||||||
|
}
|
||||||
|
|
||||||
|
v.sp -= numArgs
|
||||||
|
curFrame.ip = -1
|
||||||
|
|
||||||
|
// stack after tail-call
|
||||||
|
//
|
||||||
|
// |--------|
|
||||||
|
// | |
|
||||||
|
// |--------|
|
||||||
|
// | *ARG2 |
|
||||||
|
// |--------|
|
||||||
|
// | *ARG1 | <- SP current
|
||||||
|
// |--------|
|
||||||
|
// | LOCAL3 | for current function
|
||||||
|
// |--------|
|
||||||
|
// | LOCAL2 | for current function
|
||||||
|
// |--------|
|
||||||
|
// | *ARG2 | (copied)
|
||||||
|
// |--------|
|
||||||
|
// | *ARG1 | <- BP (copied)
|
||||||
|
// |--------|
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
v.frames[v.framesIndex].fn = fn
|
v.frames[v.framesIndex].fn = fn
|
||||||
v.frames[v.framesIndex].freeVars = freeVars
|
v.frames[v.framesIndex].freeVars = freeVars
|
||||||
v.frames[v.framesIndex].ip = -1
|
v.frames[v.framesIndex].ip = -1
|
||||||
|
@ -861,6 +941,20 @@ func (v *VM) callFunction(fn *objects.CompiledFunction, freeVars []*objects.Obje
|
||||||
|
|
||||||
v.sp = v.sp - numArgs + fn.NumLocals
|
v.sp = v.sp - numArgs + fn.NumLocals
|
||||||
|
|
||||||
|
// stack after the function call
|
||||||
|
//
|
||||||
|
// |--------|
|
||||||
|
// | | <- SP after function call
|
||||||
|
// |--------|
|
||||||
|
// | LOCAL4 | (BP+3)
|
||||||
|
// |--------|
|
||||||
|
// | LOCAL3 | (BP+2) <- SP before function call
|
||||||
|
// |--------|
|
||||||
|
// | ARG2 | (BP+1)
|
||||||
|
// |--------|
|
||||||
|
// | ARG1 | (BP+0) <- BP
|
||||||
|
// |--------|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,14 @@ func TestArray(t *testing.T) {
|
||||||
expect(t, `out = [1, 2, 3][0]`, 1)
|
expect(t, `out = [1, 2, 3][0]`, 1)
|
||||||
expect(t, `out = [1, 2, 3][1]`, 2)
|
expect(t, `out = [1, 2, 3][1]`, 2)
|
||||||
expect(t, `out = [1, 2, 3][2]`, 3)
|
expect(t, `out = [1, 2, 3][2]`, 3)
|
||||||
expect(t, `i = 0; out = [1, 2, 3][i]`, 1)
|
expect(t, `i := 0; out = [1, 2, 3][i]`, 1)
|
||||||
expect(t, `out = [1, 2, 3][1 + 1]`, 3)
|
expect(t, `out = [1, 2, 3][1 + 1]`, 3)
|
||||||
expect(t, `arr = [1, 2, 3]; out = arr[2]`, 3)
|
expect(t, `arr := [1, 2, 3]; out = arr[2]`, 3)
|
||||||
expect(t, `arr = [1, 2, 3]; out = arr[0] + arr[1] + arr[2]`, 6)
|
expect(t, `arr := [1, 2, 3]; out = arr[0] + arr[1] + arr[2]`, 6)
|
||||||
expect(t, `arr = [1, 2, 3]; i = arr[0]; out = arr[i]`, 2)
|
expect(t, `arr := [1, 2, 3]; i := arr[0]; out = arr[i]`, 2)
|
||||||
|
|
||||||
expect(t, `out = [1, 2, 3][1+1]`, 3)
|
expect(t, `out = [1, 2, 3][1+1]`, 3)
|
||||||
expect(t, `a = 1; out = [1, 2, 3][a+1]`, 3)
|
expect(t, `a := 1; out = [1, 2, 3][a+1]`, 3)
|
||||||
|
|
||||||
expect(t, `out = [1, 2, 3][:]`, ARR{1, 2, 3})
|
expect(t, `out = [1, 2, 3][:]`, ARR{1, 2, 3})
|
||||||
expect(t, `out = [1, 2, 3][0:3]`, ARR{1, 2, 3})
|
expect(t, `out = [1, 2, 3][0:3]`, ARR{1, 2, 3})
|
||||||
|
@ -27,10 +27,11 @@ func TestArray(t *testing.T) {
|
||||||
expect(t, `out = [1, 2, 3][1:1]`, ARR{})
|
expect(t, `out = [1, 2, 3][1:1]`, ARR{})
|
||||||
|
|
||||||
expect(t, `out = [1, 2, 3][3-2:1+1]`, ARR{2})
|
expect(t, `out = [1, 2, 3][3-2:1+1]`, ARR{2})
|
||||||
expect(t, `a = 1; out = [1, 2, 3][a-1:a+1]`, ARR{1, 2})
|
expect(t, `a := 1; out = [1, 2, 3][a-1:a+1]`, ARR{1, 2})
|
||||||
|
|
||||||
// array copy-by-reference
|
// array copy-by-reference
|
||||||
expect(t, `a1 = [1, 2, 3]; a2 = a1; a1[0] = 5; out = a2`, ARR{5, 2, 3})
|
expect(t, `a1 := [1, 2, 3]; a2 := a1; a1[0] = 5; out = a2`, ARR{5, 2, 3})
|
||||||
|
expect(t, `func () { a1 := [1, 2, 3]; a2 := a1; a1[0] = 5; out = a2 }()`, ARR{5, 2, 3})
|
||||||
|
|
||||||
expectError(t, `[1, 2, 3][3]`)
|
expectError(t, `[1, 2, 3][3]`)
|
||||||
expectError(t, `[1, 2, 3][-1]`)
|
expectError(t, `[1, 2, 3][-1]`)
|
||||||
|
|
|
@ -8,18 +8,17 @@ func TestAssignment(t *testing.T) {
|
||||||
expect(t, `a := 1; a = 2; out = a`, 2)
|
expect(t, `a := 1; a = 2; out = a`, 2)
|
||||||
expect(t, `a := 1; a = 2; out = a`, 2)
|
expect(t, `a := 1; a = 2; out = a`, 2)
|
||||||
expect(t, `a := 1; a = a + 4; out = a`, 5)
|
expect(t, `a := 1; a = a + 4; out = a`, 5)
|
||||||
expect(t, `a := 1; f1 = func() { a = 2; return a }; out = f1()`, 2)
|
expect(t, `a := 1; f1 := func() { a = 2; return a }; out = f1()`, 2)
|
||||||
expect(t, `a := 1; f1 = func() { a := 3; a = 2; return a }; out = f1()`, 2)
|
expect(t, `a := 1; f1 := func() { a := 3; a = 2; return a }; out = f1()`, 2)
|
||||||
|
|
||||||
// define operator ":=" is optional
|
expect(t, `a := 1; out = a`, 1)
|
||||||
expect(t, `a = 1; out = a`, 1)
|
expect(t, `a := 1; a = 2; out = a`, 2)
|
||||||
expect(t, `a = 1; a = 2; out = a`, 2)
|
expect(t, `a := 1; func() { a = 2 }(); out = a`, 2)
|
||||||
expect(t, `a = 1; func() { a = 2 }(); out = a`, 2)
|
expect(t, `a := 1; func() { a := 2 }(); out = a`, 1) // "a := 2" defines a new local variable 'a'
|
||||||
expect(t, `a = 1; func() { a := 2 }(); out = a`, 1) // "a := 2" defines a new local variable 'a'
|
expect(t, `a := 1; func() { b := 2; out = b }()`, 2)
|
||||||
expect(t, `a = 1; func() { b = 2; out = b }()`, 2)
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
out = func() {
|
out = func() {
|
||||||
a = 2
|
a := 2
|
||||||
func() {
|
func() {
|
||||||
a = 3 // captured from outer scope
|
a = 3 // captured from outer scope
|
||||||
}()
|
}()
|
||||||
|
@ -29,7 +28,7 @@ out = func() {
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
func() {
|
func() {
|
||||||
a = 5
|
a := 5
|
||||||
out = func() {
|
out = func() {
|
||||||
a := 4
|
a := 4
|
||||||
return a
|
return a
|
||||||
|
@ -39,14 +38,14 @@ func() {
|
||||||
expectError(t, `a := 1; a := 2`) // redeclared in the same scope
|
expectError(t, `a := 1; a := 2`) // redeclared in the same scope
|
||||||
expectError(t, `func() { a := 1; a := 2 }()`) // redeclared in the same scope
|
expectError(t, `func() { a := 1; a := 2 }()`) // redeclared in the same scope
|
||||||
|
|
||||||
expect(t, `a = 1; a += 2; out = a`, 3)
|
expect(t, `a := 1; a += 2; out = a`, 3)
|
||||||
expect(t, `a = 1; a += 4 - 2;; out = a`, 3)
|
expect(t, `a := 1; a += 4 - 2;; out = a`, 3)
|
||||||
expect(t, `a = 3; a -= 1;; out = a`, 2)
|
expect(t, `a := 3; a -= 1;; out = a`, 2)
|
||||||
expect(t, `a = 3; a -= 5 - 4;; out = a`, 2)
|
expect(t, `a := 3; a -= 5 - 4;; out = a`, 2)
|
||||||
expect(t, `a = 2; a *= 4;; out = a`, 8)
|
expect(t, `a := 2; a *= 4;; out = a`, 8)
|
||||||
expect(t, `a = 2; a *= 1 + 3;; out = a`, 8)
|
expect(t, `a := 2; a *= 1 + 3;; out = a`, 8)
|
||||||
expect(t, `a = 10; a /= 2;; out = a`, 5)
|
expect(t, `a := 10; a /= 2;; out = a`, 5)
|
||||||
expect(t, `a = 10; a /= 5 - 3;; out = a`, 5)
|
expect(t, `a := 10; a /= 5 - 3;; out = a`, 5)
|
||||||
|
|
||||||
// +=, -=, *=, /= operator does not define new variable
|
// +=, -=, *=, /= operator does not define new variable
|
||||||
expectError(t, `a += 4`)
|
expectError(t, `a += 4`)
|
||||||
|
@ -55,9 +54,9 @@ func() {
|
||||||
expectError(t, `a /= 4`)
|
expectError(t, `a /= 4`)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f1 = func() {
|
f1 := func() {
|
||||||
f2 = func() {
|
f2 := func() {
|
||||||
a = 1
|
a := 1
|
||||||
a += 2 // it's a statement, not an expression
|
a += 2 // it's a statement, not an expression
|
||||||
return a
|
return a
|
||||||
};
|
};
|
||||||
|
@ -66,20 +65,20 @@ f1 = func() {
|
||||||
};
|
};
|
||||||
|
|
||||||
out = f1();`, 3)
|
out = f1();`, 3)
|
||||||
expect(t, `f1 = func() { f2 = func() { a = 1; a += 4 - 2; return a }; return f2(); }; out = f1()`, 3)
|
expect(t, `f1 := func() { f2 := func() { a := 1; a += 4 - 2; return a }; return f2(); }; out = f1()`, 3)
|
||||||
expect(t, `f1 = func() { f2 = func() { a = 3; a -= 1; return a }; return f2(); }; out = f1()`, 2)
|
expect(t, `f1 := func() { f2 := func() { a := 3; a -= 1; return a }; return f2(); }; out = f1()`, 2)
|
||||||
expect(t, `f1 = func() { f2 = func() { a = 3; a -= 5 - 4; return a }; return f2(); }; out = f1()`, 2)
|
expect(t, `f1 := func() { f2 := func() { a := 3; a -= 5 - 4; return a }; return f2(); }; out = f1()`, 2)
|
||||||
expect(t, `f1 = func() { f2 = func() { a = 2; a *= 4; return a }; return f2(); }; out = f1()`, 8)
|
expect(t, `f1 := func() { f2 := func() { a := 2; a *= 4; return a }; return f2(); }; out = f1()`, 8)
|
||||||
expect(t, `f1 = func() { f2 = func() { a = 2; a *= 1 + 3; return a }; return f2(); }; out = f1()`, 8)
|
expect(t, `f1 := func() { f2 := func() { a := 2; a *= 1 + 3; return a }; return f2(); }; out = f1()`, 8)
|
||||||
expect(t, `f1 = func() { f2 = func() { a = 10; a /= 2; return a }; return f2(); }; out = f1()`, 5)
|
expect(t, `f1 := func() { f2 := func() { a := 10; a /= 2; return a }; return f2(); }; out = f1()`, 5)
|
||||||
expect(t, `f1 = func() { f2 = func() { a = 10; a /= 5 - 3; return a }; return f2(); }; out = f1()`, 5)
|
expect(t, `f1 := func() { f2 := func() { a := 10; a /= 5 - 3; return a }; return f2(); }; out = f1()`, 5)
|
||||||
|
|
||||||
expect(t, `a = 1; f1 = func() { f2 = func() { a += 2; return a }; return f2(); }; out = f1()`, 3)
|
expect(t, `a := 1; f1 := func() { f2 := func() { a += 2; return a }; return f2(); }; out = f1()`, 3)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f1 = func(a) {
|
f1 := func(a) {
|
||||||
return func(b) {
|
return func(b) {
|
||||||
c = a
|
c := a
|
||||||
c += b * 2
|
c += b * 2
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
@ -90,7 +89,7 @@ out = f1();`, 3)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
out = func() {
|
out = func() {
|
||||||
a = 1
|
a := 1
|
||||||
func() {
|
func() {
|
||||||
a = 2
|
a = 2
|
||||||
func() {
|
func() {
|
||||||
|
@ -106,8 +105,8 @@ out = f1();`, 3)
|
||||||
|
|
||||||
// write on free variables
|
// write on free variables
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f1 = func() {
|
f1 := func() {
|
||||||
a = 5
|
a := 5
|
||||||
|
|
||||||
return func() {
|
return func() {
|
||||||
a += 3
|
a += 3
|
||||||
|
@ -118,14 +117,14 @@ out = f1();`, 3)
|
||||||
`, 8)
|
`, 8)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
it = func(seq, fn) {
|
it := func(seq, fn) {
|
||||||
fn(seq[0])
|
fn(seq[0])
|
||||||
fn(seq[1])
|
fn(seq[1])
|
||||||
fn(seq[2])
|
fn(seq[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
foo = func(a) {
|
foo := func(a) {
|
||||||
b = 0
|
b := 0
|
||||||
it([1, 2, 3], func(x) {
|
it([1, 2, 3], func(x) {
|
||||||
b = x + a
|
b = x + a
|
||||||
})
|
})
|
||||||
|
@ -136,14 +135,14 @@ out = f1();`, 3)
|
||||||
`, 5)
|
`, 5)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
it = func(seq, fn) {
|
it := func(seq, fn) {
|
||||||
fn(seq[0])
|
fn(seq[0])
|
||||||
fn(seq[1])
|
fn(seq[1])
|
||||||
fn(seq[2])
|
fn(seq[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
foo = func(a) {
|
foo := func(a) {
|
||||||
b = 0
|
b := 0
|
||||||
it([1, 2, 3], func(x) {
|
it([1, 2, 3], func(x) {
|
||||||
b += x + a
|
b += x + a
|
||||||
})
|
})
|
||||||
|
@ -155,7 +154,7 @@ out = f1();`, 3)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
out = func() {
|
out = func() {
|
||||||
a = 1
|
a := 1
|
||||||
func() {
|
func() {
|
||||||
a = 2
|
a = 2
|
||||||
}()
|
}()
|
||||||
|
@ -164,25 +163,25 @@ out = func() {
|
||||||
`, 2)
|
`, 2)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f = func() {
|
f := func() {
|
||||||
a = 1
|
a := 1
|
||||||
return {
|
return {
|
||||||
b: func() { a += 3 },
|
b: func() { a += 3 },
|
||||||
c: func() { a += 2 },
|
c: func() { a += 2 },
|
||||||
d: func() { return a }
|
d: func() { return a }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m = f()
|
m := f()
|
||||||
m.b()
|
m.b()
|
||||||
m.c()
|
m.c()
|
||||||
out = m.d()
|
out = m.d()
|
||||||
`, 6)
|
`, 6)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
each = func(s, x) { for i=0; i<len(s); i++ { x(s[i]) } }
|
each := func(s, x) { for i:=0; i<len(s); i++ { x(s[i]) } }
|
||||||
|
|
||||||
out = func() {
|
out = func() {
|
||||||
a = 100
|
a := 100
|
||||||
each([1, 2, 3], func(x) {
|
each([1, 2, 3], func(x) {
|
||||||
a += x
|
a += x
|
||||||
})
|
})
|
||||||
|
@ -194,11 +193,11 @@ out = func() {
|
||||||
`, 136)
|
`, 136)
|
||||||
|
|
||||||
// assigning different type value
|
// assigning different type value
|
||||||
expect(t, `a = 1; a = "foo"; out = a`, "foo") // global
|
expect(t, `a := 1; a = "foo"; out = a`, "foo") // global
|
||||||
expect(t, `func() { a = 1; a = "foo"; out = a }()`, "foo") // local
|
expect(t, `func() { a := 1; a = "foo"; out = a }()`, "foo") // local
|
||||||
expect(t, `
|
expect(t, `
|
||||||
out = func() {
|
out = func() {
|
||||||
a = 5
|
a := 5
|
||||||
return func() {
|
return func() {
|
||||||
a = "foo"
|
a = "foo"
|
||||||
return a
|
return a
|
||||||
|
@ -206,18 +205,18 @@ out = func() {
|
||||||
}()`, "foo") // free
|
}()`, "foo") // free
|
||||||
|
|
||||||
// variables declared in if/for blocks
|
// variables declared in if/for blocks
|
||||||
expect(t, `for a:=0; a<5; a++ {}; a = "foo"; out = a`, "foo")
|
expect(t, `for a:=0; a<5; a++ {}; a := "foo"; out = a`, "foo")
|
||||||
expect(t, `func() { for a:=0; a<5; a++ {}; a = "foo"; out = a }()`, "foo")
|
expect(t, `func() { for a:=0; a<5; a++ {}; a := "foo"; out = a }()`, "foo")
|
||||||
|
|
||||||
// selectors
|
// selectors
|
||||||
expect(t, `a=[1,2,3]; a[1] = 5; out = a[1]`, 5)
|
expect(t, `a:=[1,2,3]; a[1] = 5; out = a[1]`, 5)
|
||||||
expect(t, `a=[1,2,3]; a[1] += 5; out = a[1]`, 7)
|
expect(t, `a:=[1,2,3]; a[1] += 5; out = a[1]`, 7)
|
||||||
expect(t, `a={b:1,c:2}; a.b = 5; out = a.b`, 5)
|
expect(t, `a:={b:1,c:2}; a.b = 5; out = a.b`, 5)
|
||||||
expect(t, `a={b:1,c:2}; a.b += 5; out = a.b`, 6)
|
expect(t, `a:={b:1,c:2}; a.b += 5; out = a.b`, 6)
|
||||||
expect(t, `a={b:1,c:2}; a.b += a.c; out = a.b`, 3)
|
expect(t, `a:={b:1,c:2}; a.b += a.c; out = a.b`, 3)
|
||||||
expect(t, `a={b:1,c:2}; a.b += a.c; out = a.c`, 2)
|
expect(t, `a:={b:1,c:2}; a.b += a.c; out = a.c`, 2)
|
||||||
expect(t, `
|
expect(t, `
|
||||||
a = {
|
a := {
|
||||||
b: [1, 2, 3],
|
b: [1, 2, 3],
|
||||||
c: {
|
c: {
|
||||||
d: 8,
|
d: 8,
|
||||||
|
@ -230,7 +229,7 @@ out = a["c"]["f"][1]
|
||||||
`, 10)
|
`, 10)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
a = {
|
a := {
|
||||||
b: [1, 2, 3],
|
b: [1, 2, 3],
|
||||||
c: {
|
c: {
|
||||||
d: 8,
|
d: 8,
|
||||||
|
@ -243,7 +242,7 @@ out = a.c.h
|
||||||
`, "bar")
|
`, "bar")
|
||||||
|
|
||||||
expectError(t, `
|
expectError(t, `
|
||||||
a = {
|
a := {
|
||||||
b: [1, 2, 3],
|
b: [1, 2, 3],
|
||||||
c: {
|
c: {
|
||||||
d: 8,
|
d: 8,
|
||||||
|
|
|
@ -3,9 +3,9 @@ package vm_test
|
||||||
import "testing"
|
import "testing"
|
||||||
|
|
||||||
func TestCall(t *testing.T) {
|
func TestCall(t *testing.T) {
|
||||||
expect(t, `a = { b: func(x) { return x + 2 } }; out = a.b(5)`, 7)
|
expect(t, `a := { b: func(x) { return x + 2 } }; out = a.b(5)`, 7)
|
||||||
expect(t, `a = { b: { c: func(x) { return x + 2 } } }; out = a.b.c(5)`, 7)
|
expect(t, `a := { b: { c: func(x) { return x + 2 } } }; out = a.b.c(5)`, 7)
|
||||||
expect(t, `a = { b: { c: func(x) { return x + 2 } } }; out = a["b"].c(5)`, 7)
|
expect(t, `a := { b: { c: func(x) { return x + 2 } } }; out = a["b"].c(5)`, 7)
|
||||||
|
|
||||||
// "this" binding
|
// "this" binding
|
||||||
// expect(t, `
|
// expect(t, `
|
||||||
|
|
|
@ -24,21 +24,21 @@ func TestFor(t *testing.T) {
|
||||||
}()`, 5)
|
}()`, 5)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
for a=1; a<=10; a++ {
|
for a:=1; a<=10; a++ {
|
||||||
out += a
|
out += a
|
||||||
}`, 55)
|
}`, 55)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
for a=1; a<=3; a++ {
|
for a:=1; a<=3; a++ {
|
||||||
for b=3; b<=6; b++ {
|
for b:=3; b<=6; b++ {
|
||||||
out += b
|
out += b
|
||||||
}
|
}
|
||||||
}`, 54)
|
}`, 54)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
out = func() {
|
out = func() {
|
||||||
sum = 0
|
sum := 0
|
||||||
for a=1; a<=10; a++ {
|
for a:=1; a<=10; a++ {
|
||||||
sum += a
|
sum += a
|
||||||
}
|
}
|
||||||
return sum
|
return sum
|
||||||
|
@ -46,17 +46,17 @@ func TestFor(t *testing.T) {
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
out = func() {
|
out = func() {
|
||||||
sum = 0
|
sum := 0
|
||||||
for a=1; a<=4; a++ {
|
for a:=1; a<=4; a++ {
|
||||||
for b=3; b<=5; b++ {
|
for b:=3; b<=5; b++ {
|
||||||
sum += b
|
sum += b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sum
|
return sum
|
||||||
}()`, 48) // (3+4+5 * 4
|
}()`, 48) // (3+4+5) * 4
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
a = 1
|
a := 1
|
||||||
for ; a<=10; a++ {
|
for ; a<=10; a++ {
|
||||||
if a == 5 {
|
if a == 5 {
|
||||||
break
|
break
|
||||||
|
@ -65,7 +65,7 @@ func TestFor(t *testing.T) {
|
||||||
out = a`, 5)
|
out = a`, 5)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
for a=1; a<=10; a++ {
|
for a:=1; a<=10; a++ {
|
||||||
if a == 3 {
|
if a == 3 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -76,9 +76,9 @@ func TestFor(t *testing.T) {
|
||||||
}`, 12) // 1 + 2 + 4 + 5
|
}`, 12) // 1 + 2 + 4 + 5
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
for a=1; a<=10; {
|
for a:=1; a<=10; {
|
||||||
if a == 3 {
|
if a == 3 {
|
||||||
a++
|
a++
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
out += a
|
out += a
|
||||||
|
|
|
@ -6,20 +6,20 @@ import (
|
||||||
|
|
||||||
func TestFunction(t *testing.T) {
|
func TestFunction(t *testing.T) {
|
||||||
// function with no "return" statement returns "invalid" value.
|
// function with no "return" statement returns "invalid" value.
|
||||||
expect(t, `f1 = func() {}; out = f1();`, undefined())
|
expect(t, `f1 := func() {}; out = f1();`, undefined())
|
||||||
expect(t, `f1 = func() {}; f2 = func() { return f1(); }; f1(); out = f2();`, undefined())
|
expect(t, `f1 := func() {}; f2 := func() { return f1(); }; f1(); out = f2();`, undefined())
|
||||||
expect(t, `f = func(x) { x; }; out = f(5);`, undefined())
|
expect(t, `f := func(x) { x; }; out = f(5);`, undefined())
|
||||||
|
|
||||||
expect(t, `f = func(x) { return x; }; out = f(5);`, 5)
|
expect(t, `f := func(x) { return x; }; out = f(5);`, 5)
|
||||||
expect(t, `f = func(x) { return x * 2; }; out = f(5);`, 10)
|
expect(t, `f := func(x) { return x * 2; }; out = f(5);`, 10)
|
||||||
expect(t, `f = func(x, y) { return x + y; }; out = f(5, 5);`, 10)
|
expect(t, `f := func(x, y) { return x + y; }; out = f(5, 5);`, 10)
|
||||||
expect(t, `f = func(x, y) { return x + y; }; out = f(5 + 5, f(5, 5));`, 20)
|
expect(t, `f := func(x, y) { return x + y; }; out = f(5 + 5, f(5, 5));`, 20)
|
||||||
expect(t, `out = func(x) { return x; }(5)`, 5)
|
expect(t, `out = func(x) { return x; }(5)`, 5)
|
||||||
expect(t, `x = 10; f = func(x) { return x; }; f(5); out = x;`, 10)
|
expect(t, `x := 10; f := func(x) { return x; }; f(5); out = x;`, 10)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f2 = func(a) {
|
f2 := func(a) {
|
||||||
f1 = func(a) {
|
f1 := func(a) {
|
||||||
return a * 2;
|
return a * 2;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -31,93 +31,102 @@ out = f2(10);
|
||||||
|
|
||||||
// closures
|
// closures
|
||||||
expect(t, `
|
expect(t, `
|
||||||
newAdder = func(x) {
|
newAdder := func(x) {
|
||||||
return func(y) { return x + y };
|
return func(y) { return x + y };
|
||||||
};
|
};
|
||||||
|
|
||||||
add2 = newAdder(2);
|
add2 := newAdder(2);
|
||||||
out = add2(5);
|
out = add2(5);
|
||||||
`, 7)
|
`, 7)
|
||||||
|
|
||||||
// function as a argument
|
// function as a argument
|
||||||
expect(t, `
|
expect(t, `
|
||||||
add = func(a, b) { return a + b };
|
add := func(a, b) { return a + b };
|
||||||
sub = func(a, b) { return a - b };
|
sub := func(a, b) { return a - b };
|
||||||
applyFunc = func(a, b, f) { return f(a, b) };
|
applyFunc := func(a, b, f) { return f(a, b) };
|
||||||
|
|
||||||
out = applyFunc(applyFunc(2, 2, add), 3, sub);
|
out = applyFunc(applyFunc(2, 2, add), 3, sub);
|
||||||
`, 1)
|
`, 1)
|
||||||
|
|
||||||
expect(t, `f1 = func() { return 5 + 10; }; out = f1();`, 15)
|
expect(t, `f1 := func() { return 5 + 10; }; out = f1();`, 15)
|
||||||
expect(t, `f1 = func() { return 1 }; f2 = func() { return 2 }; out = f1() + f2()`, 3)
|
expect(t, `f1 := func() { return 1 }; f2 := func() { return 2 }; out = f1() + f2()`, 3)
|
||||||
expect(t, `f1 = func() { return 1 }; f2 = func() { return f1() + 2 }; f3 = func() { return f2() + 3 }; out = f3()`, 6)
|
expect(t, `f1 := func() { return 1 }; f2 := func() { return f1() + 2 }; f3 := func() { return f2() + 3 }; out = f3()`, 6)
|
||||||
expect(t, `f1 = func() { return 99; 100 }; out = f1();`, 99)
|
expect(t, `f1 := func() { return 99; 100 }; out = f1();`, 99)
|
||||||
expect(t, `f1 = func() { return 99; return 100 }; out = f1();`, 99)
|
expect(t, `f1 := func() { return 99; return 100 }; out = f1();`, 99)
|
||||||
expect(t, `f1 = func() { return 33; }; f2 = func() { return f1 }; out = f2()();`, 33)
|
expect(t, `f1 := func() { return 33; }; f2 := func() { return f1 }; out = f2()();`, 33)
|
||||||
expect(t, `one = func() { one = 1; return one }; out = one()`, 1)
|
expect(t, `one := func() { one = 1; return one }; out = one()`, 1)
|
||||||
expect(t, `three = func() { one = 1; two = 2; return one + two }; out = three()`, 3)
|
expect(t, `three := func() { one := 1; two := 2; return one + two }; out = three()`, 3)
|
||||||
expect(t, `three = func() { one = 1; two = 2; return one + two }; seven = func() { three = 3; four = 4; return three + four }; out = three() + seven()`, 10)
|
expect(t, `three := func() { one := 1; two := 2; return one + two }; seven := func() { three := 3; four := 4; return three + four }; out = three() + seven()`, 10)
|
||||||
expect(t, `foo1 = func() { foo = 50; return foo}; foo2 = func() { foo = 100; return foo }; out = foo1() + foo2()`, 150)
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
g = 50;
|
foo1 := func() {
|
||||||
minusOne = func() {
|
foo := 50
|
||||||
n = 1;
|
return foo
|
||||||
|
}
|
||||||
|
foo2 := func() {
|
||||||
|
foo := 100
|
||||||
|
return foo
|
||||||
|
}
|
||||||
|
out = foo1() + foo2()`, 150)
|
||||||
|
expect(t, `
|
||||||
|
g := 50;
|
||||||
|
minusOne := func() {
|
||||||
|
n := 1;
|
||||||
return g - n;
|
return g - n;
|
||||||
};
|
};
|
||||||
minusTwo = func() {
|
minusTwo := func() {
|
||||||
n = 2;
|
n := 2;
|
||||||
return g - n;
|
return g - n;
|
||||||
};
|
};
|
||||||
out = minusOne() + minusTwo()
|
out = minusOne() + minusTwo()
|
||||||
`, 97)
|
`, 97)
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f1 = func() {
|
f1 := func() {
|
||||||
f2 = func() { return 1; }
|
f2 := func() { return 1; }
|
||||||
return f2
|
return f2
|
||||||
};
|
};
|
||||||
out = f1()()
|
out = f1()()
|
||||||
`, 1)
|
`, 1)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f1 = func(a) { return a; };
|
f1 := func(a) { return a; };
|
||||||
out = f1(4)`, 4)
|
out = f1(4)`, 4)
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f1 = func(a, b) { return a + b; };
|
f1 := func(a, b) { return a + b; };
|
||||||
out = f1(1, 2)`, 3)
|
out = f1(1, 2)`, 3)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
sum = func(a, b) {
|
sum := func(a, b) {
|
||||||
c = a + b;
|
c := a + b;
|
||||||
return c;
|
return c;
|
||||||
};
|
};
|
||||||
out = sum(1, 2);`, 3)
|
out = sum(1, 2);`, 3)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
sum = func(a, b) {
|
sum := func(a, b) {
|
||||||
c = a + b;
|
c := a + b;
|
||||||
return c;
|
return c;
|
||||||
};
|
};
|
||||||
out = sum(1, 2) + sum(3, 4);`, 10)
|
out = sum(1, 2) + sum(3, 4);`, 10)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
sum = func(a, b) {
|
sum := func(a, b) {
|
||||||
c = a + b
|
c := a + b
|
||||||
return c
|
return c
|
||||||
};
|
};
|
||||||
outer = func() {
|
outer := func() {
|
||||||
return sum(1, 2) + sum(3, 4)
|
return sum(1, 2) + sum(3, 4)
|
||||||
};
|
};
|
||||||
out = outer();`, 10)
|
out = outer();`, 10)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
g = 10;
|
g := 10;
|
||||||
|
|
||||||
sum = func(a, b) {
|
sum := func(a, b) {
|
||||||
c = a + b;
|
c := a + b;
|
||||||
return c + g;
|
return c + g;
|
||||||
}
|
}
|
||||||
|
|
||||||
outer = func() {
|
outer := func() {
|
||||||
return sum(1, 2) + sum(3, 4) + g;
|
return sum(1, 2) + sum(3, 4) + g;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,65 +138,65 @@ out = outer() + g
|
||||||
expectError(t, `func(a, b) { return a + b; }(1)`)
|
expectError(t, `func(a, b) { return a + b; }(1)`)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f1 = func(a) {
|
f1 := func(a) {
|
||||||
return func() { return a; };
|
return func() { return a; };
|
||||||
};
|
};
|
||||||
f2 = f1(99);
|
f2 := f1(99);
|
||||||
out = f2()
|
out = f2()
|
||||||
`, 99)
|
`, 99)
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f1 = func(a, b) {
|
f1 := func(a, b) {
|
||||||
return func(c) { return a + b + c };
|
return func(c) { return a + b + c };
|
||||||
};
|
};
|
||||||
|
|
||||||
f2 = f1(1, 2);
|
f2 := f1(1, 2);
|
||||||
out = f2(8);
|
out = f2(8);
|
||||||
`, 11)
|
`, 11)
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f1 = func(a, b) {
|
f1 := func(a, b) {
|
||||||
c = a + b;
|
c := a + b;
|
||||||
return func(d) { return c + d };
|
return func(d) { return c + d };
|
||||||
};
|
};
|
||||||
f2 = f1(1, 2);
|
f2 := f1(1, 2);
|
||||||
out = f2(8);
|
out = f2(8);
|
||||||
`, 11)
|
`, 11)
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f1 = func(a, b) {
|
f1 := func(a, b) {
|
||||||
c = a + b;
|
c := a + b;
|
||||||
return func(d) {
|
return func(d) {
|
||||||
e = d + c;
|
e := d + c;
|
||||||
return func(f) { return e + f };
|
return func(f) { return e + f };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
f2 = f1(1, 2);
|
f2 := f1(1, 2);
|
||||||
f3 = f2(3);
|
f3 := f2(3);
|
||||||
out = f3(8);
|
out = f3(8);
|
||||||
`, 14)
|
`, 14)
|
||||||
expect(t, `
|
expect(t, `
|
||||||
a = 1;
|
a := 1;
|
||||||
f1 = func(b) {
|
f1 := func(b) {
|
||||||
return func(c) {
|
return func(c) {
|
||||||
return func(d) { return a + b + c + d }
|
return func(d) { return a + b + c + d }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
f2 = f1(2);
|
f2 := f1(2);
|
||||||
f3 = f2(3);
|
f3 := f2(3);
|
||||||
out = f3(8);
|
out = f3(8);
|
||||||
`, 14)
|
`, 14)
|
||||||
expect(t, `
|
expect(t, `
|
||||||
f1 = func(a, b) {
|
f1 := func(a, b) {
|
||||||
one = func() { return a; };
|
one := func() { return a; };
|
||||||
two = func() { return b; };
|
two := func() { return b; };
|
||||||
return func() { return one() + two(); }
|
return func() { return one() + two(); }
|
||||||
};
|
};
|
||||||
f2 = f1(9, 90);
|
f2 := f1(9, 90);
|
||||||
out = f2();
|
out = f2();
|
||||||
`, 99)
|
`, 99)
|
||||||
|
|
||||||
// fibonacci
|
// fibonacci
|
||||||
expect(t, `
|
expect(t, `
|
||||||
fib = func(x) {
|
fib := func(x) {
|
||||||
if(x == 0) {
|
if(x == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -26,11 +26,11 @@ func TestIf(t *testing.T) {
|
||||||
expect(t, `if (1 > 2) { out = 10 } else if (1 == 2) { if (1 == 2) { out = 21 } else if (2 == 3) { out = 22 } else { out = 23 } } else { out = 30 }`, 30)
|
expect(t, `if (1 > 2) { out = 10 } else if (1 == 2) { if (1 == 2) { out = 21 } else if (2 == 3) { out = 22 } else { out = 23 } } else { out = 30 }`, 30)
|
||||||
expect(t, `if (1 > 2) { out = 10 } else if (1 == 2) { out = 20 } else { if (1 == 2) { out = 31 } else if (2 == 3) { out = 32 } else { out = 33 } }`, 33)
|
expect(t, `if (1 > 2) { out = 10 } else if (1 == 2) { out = 20 } else { if (1 == 2) { out = 31 } else if (2 == 3) { out = 32 } else { out = 33 } }`, 33)
|
||||||
|
|
||||||
expect(t, `if a=0; a<1 { out = 10 }`, 10)
|
expect(t, `if a:=0; a<1 { out = 10 }`, 10)
|
||||||
expect(t, `a=0; if a++; a==1 { out = 10 }`, 10)
|
expect(t, `a:=0; if a++; a==1 { out = 10 }`, 10)
|
||||||
expect(t, `
|
expect(t, `
|
||||||
func() {
|
func() {
|
||||||
a = 1
|
a := 1
|
||||||
if a++; a > 1 {
|
if a++; a > 1 {
|
||||||
out = a
|
out = a
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ func() {
|
||||||
`, 2)
|
`, 2)
|
||||||
expect(t, `
|
expect(t, `
|
||||||
func() {
|
func() {
|
||||||
a = 1
|
a := 1
|
||||||
if a++; a == 1 {
|
if a++; a == 1 {
|
||||||
out = 10
|
out = 10
|
||||||
} else {
|
} else {
|
||||||
|
@ -48,7 +48,7 @@ func() {
|
||||||
`, 20)
|
`, 20)
|
||||||
expect(t, `
|
expect(t, `
|
||||||
func() {
|
func() {
|
||||||
a = 1
|
a := 1
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
if a++; a > 1 {
|
if a++; a > 1 {
|
||||||
|
|
|
@ -30,12 +30,12 @@ func TestLogical(t *testing.T) {
|
||||||
expect(t, `out = 0 || (0 && 2)`, 0)
|
expect(t, `out = 0 || (0 && 2)`, 0)
|
||||||
expect(t, `out = 0 || (2 && 0)`, 0)
|
expect(t, `out = 0 || (2 && 0)`, 0)
|
||||||
|
|
||||||
expect(t, `t=func() {out = 3; return true}; f=func() {out = 7; return false}; t() && f()`, 7)
|
expect(t, `t:=func() {out = 3; return true}; f:=func() {out = 7; return false}; t() && f()`, 7)
|
||||||
expect(t, `t=func() {out = 3; return true}; f=func() {out = 7; return false}; f() && t()`, 7)
|
expect(t, `t:=func() {out = 3; return true}; f:=func() {out = 7; return false}; f() && t()`, 7)
|
||||||
expect(t, `t=func() {out = 3; return true}; f=func() {out = 7; return false}; f() || t()`, 3)
|
expect(t, `t:=func() {out = 3; return true}; f:=func() {out = 7; return false}; f() || t()`, 3)
|
||||||
expect(t, `t=func() {out = 3; return true}; f=func() {out = 7; return false}; t() || f()`, 3)
|
expect(t, `t:=func() {out = 3; return true}; f:=func() {out = 7; return false}; t() || f()`, 3)
|
||||||
expect(t, `t=func() {out = 3; return true}; f=func() {out = 7; return false}; !t() && f()`, 3)
|
expect(t, `t:=func() {out = 3; return true}; f:=func() {out = 7; return false}; !t() && f()`, 3)
|
||||||
expect(t, `t=func() {out = 3; return true}; f=func() {out = 7; return false}; !f() && t()`, 3)
|
expect(t, `t:=func() {out = 3; return true}; f:=func() {out = 7; return false}; !f() && t()`, 3)
|
||||||
expect(t, `t=func() {out = 3; return true}; f=func() {out = 7; return false}; !f() || t()`, 7)
|
expect(t, `t:=func() {out = 3; return true}; f:=func() {out = 7; return false}; !f() || t()`, 7)
|
||||||
expect(t, `t=func() {out = 3; return true}; f=func() {out = 7; return false}; !t() || f()`, 7)
|
expect(t, `t:=func() {out = 3; return true}; f:=func() {out = 7; return false}; !t() || f()`, 7)
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,11 +18,11 @@ out = {
|
||||||
|
|
||||||
expect(t, `out = {foo: 5}["foo"]`, 5)
|
expect(t, `out = {foo: 5}["foo"]`, 5)
|
||||||
expect(t, `out = {foo: 5}["bar"]`, undefined())
|
expect(t, `out = {foo: 5}["bar"]`, undefined())
|
||||||
expect(t, `key = "foo"; out = {foo: 5}[key]`, 5)
|
expect(t, `key := "foo"; out = {foo: 5}[key]`, 5)
|
||||||
expect(t, `out = {}["foo"]`, undefined())
|
expect(t, `out = {}["foo"]`, undefined())
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
m = {
|
m := {
|
||||||
foo: func(x) {
|
foo: func(x) {
|
||||||
return x * 2
|
return x * 2
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,8 @@ out = m["foo"](2) + m["foo"](3)
|
||||||
`, 10)
|
`, 10)
|
||||||
|
|
||||||
// map assignment is copy-by-reference
|
// map assignment is copy-by-reference
|
||||||
expect(t, `m1 = {k1: 1, k2: "foo"}; m2 = m1; m1.k1 = 5; out = m2.k1`, 5)
|
expect(t, `m1 := {k1: 1, k2: "foo"}; m2 := m1; m1.k1 = 5; out = m2.k1`, 5)
|
||||||
expect(t, `m1 = {k1: 1, k2: "foo"}; m2 = m1; m2.k1 = 3; out = m1.k1`, 3)
|
expect(t, `m1 := {k1: 1, k2: "foo"}; m2 := m1; m2.k1 = 3; out = m1.k1`, 3)
|
||||||
|
expect(t, `func() { m1 := {k1: 1, k2: "foo"}; m2 := m1; m1.k1 = 5; out = m2.k1 }()`, 5)
|
||||||
|
expect(t, `func() { m1 := {k1: 1, k2: "foo"}; m2 := m1; m2.k1 = 3; out = m1.k1 }()`, 3)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,5 +20,5 @@ func TestReturn(t *testing.T) {
|
||||||
}
|
}
|
||||||
}()`, 10)
|
}()`, 10)
|
||||||
|
|
||||||
expect(t, `f1 = func() { return 2 * 5; }; out = f1()`, 10)
|
expect(t, `f1 := func() { return 2 * 5; }; out = f1()`, 10)
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSelector(t *testing.T) {
|
func TestSelector(t *testing.T) {
|
||||||
expect(t, `a = {k1: 5, k2: "foo"}; out = a.k1`, 5)
|
expect(t, `a := {k1: 5, k2: "foo"}; out = a.k1`, 5)
|
||||||
expect(t, `a = {k1: 5, k2: "foo"}; out = a.k2`, "foo")
|
expect(t, `a := {k1: 5, k2: "foo"}; out = a.k2`, "foo")
|
||||||
expect(t, `a = {k1: 5, k2: "foo"}; out = a.k3`, undefined())
|
expect(t, `a := {k1: 5, k2: "foo"}; out = a.k3`, undefined())
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
a = {
|
a := {
|
||||||
b: {
|
b: {
|
||||||
c: 4,
|
c: 4,
|
||||||
a: false
|
a: false
|
||||||
|
@ -20,7 +20,7 @@ a = {
|
||||||
out = a.b.c`, 4)
|
out = a.b.c`, 4)
|
||||||
|
|
||||||
expectError(t, `
|
expectError(t, `
|
||||||
a = {
|
a := {
|
||||||
b: {
|
b: {
|
||||||
c: 4,
|
c: 4,
|
||||||
a: false
|
a: false
|
||||||
|
@ -30,7 +30,7 @@ a = {
|
||||||
out = a.x.c`)
|
out = a.x.c`)
|
||||||
|
|
||||||
expectError(t, `
|
expectError(t, `
|
||||||
a = {
|
a := {
|
||||||
b: {
|
b: {
|
||||||
c: 4,
|
c: 4,
|
||||||
a: false
|
a: false
|
||||||
|
@ -39,26 +39,26 @@ a = {
|
||||||
}
|
}
|
||||||
out = a.x.y`)
|
out = a.x.y`)
|
||||||
|
|
||||||
expect(t, `a = {b: 1, c: "foo"}; a.b = 2; out = a.b`, 2)
|
expect(t, `a := {b: 1, c: "foo"}; a.b = 2; out = a.b`, 2)
|
||||||
expect(t, `a = {b: 1, c: "foo"}; a.c = 2; out = a.c`, 2) // type not checked on sub-field
|
expect(t, `a := {b: 1, c: "foo"}; a.c = 2; out = a.c`, 2) // type not checked on sub-field
|
||||||
expect(t, `a = {b: {c: 1}}; a.b.c = 2; out = a.b.c`, 2)
|
expect(t, `a := {b: {c: 1}}; a.b.c = 2; out = a.b.c`, 2)
|
||||||
expect(t, `a = {b: 1}; a.c = 2; out = a`, MAP{"b": 1, "c": 2})
|
expect(t, `a := {b: 1}; a.c = 2; out = a`, MAP{"b": 1, "c": 2})
|
||||||
expect(t, `a = {b: {c: 1}}; a.b.d = 2; out = a`, MAP{"b": MAP{"c": 1, "d": 2}})
|
expect(t, `a := {b: {c: 1}}; a.b.d = 2; out = a`, MAP{"b": MAP{"c": 1, "d": 2}})
|
||||||
|
|
||||||
expect(t, `func() { a = {b: 1, c: "foo"}; a.b = 2; out = a.b }()`, 2)
|
expect(t, `func() { a := {b: 1, c: "foo"}; a.b = 2; out = a.b }()`, 2)
|
||||||
expect(t, `func() { a = {b: 1, c: "foo"}; a.c = 2; out = a.c }()`, 2) // type not checked on sub-field
|
expect(t, `func() { a := {b: 1, c: "foo"}; a.c = 2; out = a.c }()`, 2) // type not checked on sub-field
|
||||||
expect(t, `func() { a = {b: {c: 1}}; a.b.c = 2; out = a.b.c }()`, 2)
|
expect(t, `func() { a := {b: {c: 1}}; a.b.c = 2; out = a.b.c }()`, 2)
|
||||||
expect(t, `func() { a = {b: 1}; a.c = 2; out = a }()`, MAP{"b": 1, "c": 2})
|
expect(t, `func() { a := {b: 1}; a.c = 2; out = a }()`, MAP{"b": 1, "c": 2})
|
||||||
expect(t, `func() { a = {b: {c: 1}}; a.b.d = 2; out = a }()`, MAP{"b": MAP{"c": 1, "d": 2}})
|
expect(t, `func() { a := {b: {c: 1}}; a.b.d = 2; out = a }()`, MAP{"b": MAP{"c": 1, "d": 2}})
|
||||||
|
|
||||||
expect(t, `func() { a = {b: 1, c: "foo"}; func() { a.b = 2 }(); out = a.b }()`, 2)
|
expect(t, `func() { a := {b: 1, c: "foo"}; func() { a.b = 2 }(); out = a.b }()`, 2)
|
||||||
expect(t, `func() { a = {b: 1, c: "foo"}; func() { a.c = 2 }(); out = a.c }()`, 2) // type not checked on sub-field
|
expect(t, `func() { a := {b: 1, c: "foo"}; func() { a.c = 2 }(); out = a.c }()`, 2) // type not checked on sub-field
|
||||||
expect(t, `func() { a = {b: {c: 1}}; func() { a.b.c = 2 }(); out = a.b.c }()`, 2)
|
expect(t, `func() { a := {b: {c: 1}}; func() { a.b.c = 2 }(); out = a.b.c }()`, 2)
|
||||||
expect(t, `func() { a = {b: 1}; func() { a.c = 2 }(); out = a }()`, MAP{"b": 1, "c": 2})
|
expect(t, `func() { a := {b: 1}; func() { a.c = 2 }(); out = a }()`, MAP{"b": 1, "c": 2})
|
||||||
expect(t, `func() { a = {b: {c: 1}}; func() { a.b.d = 2 }(); out = a }()`, MAP{"b": MAP{"c": 1, "d": 2}})
|
expect(t, `func() { a := {b: {c: 1}}; func() { a.b.d = 2 }(); out = a }()`, MAP{"b": MAP{"c": 1, "d": 2}})
|
||||||
|
|
||||||
expect(t, `
|
expect(t, `
|
||||||
a = {
|
a := {
|
||||||
b: [1, 2, 3],
|
b: [1, 2, 3],
|
||||||
c: {
|
c: {
|
||||||
d: 8,
|
d: 8,
|
||||||
|
@ -69,10 +69,20 @@ a = {
|
||||||
out = [a.b[2], a.c.d, a.c.e, a.c.f[1]]
|
out = [a.b[2], a.c.d, a.c.e, a.c.f[1]]
|
||||||
`, ARR{3, 8, "foo", 8})
|
`, ARR{3, 8, "foo", 8})
|
||||||
|
|
||||||
expectError(t, `a = {b: {c: 1}}; a.d.c = 2`)
|
expect(t, `
|
||||||
expectError(t, `a = [1, 2, 3]; a.b = 2`)
|
func() {
|
||||||
expectError(t, `a = "foo"; a.b = 2`)
|
a := [1, 2, 3]
|
||||||
expectError(t, `func() { a = {b: {c: 1}}; a.d.c = 2 }()`)
|
b := 9
|
||||||
expectError(t, `func() { a = [1, 2, 3]; a.b = 2 }()`)
|
a[1] = b
|
||||||
expectError(t, `func() { a = "foo"; a.b = 2 }()`)
|
b = 7 // make sure a[1] has a COPY of value of 'b'
|
||||||
|
out = a[1]
|
||||||
|
}()
|
||||||
|
`, 9)
|
||||||
|
|
||||||
|
expectError(t, `a := {b: {c: 1}}; a.d.c = 2`)
|
||||||
|
expectError(t, `a := [1, 2, 3]; a.b = 2`)
|
||||||
|
expectError(t, `a := "foo"; a.b = 2`)
|
||||||
|
expectError(t, `func() { a := {b: {c: 1}}; a.d.c = 2 }()`)
|
||||||
|
expectError(t, `func() { a := [1, 2, 3]; a.b = 2 }()`)
|
||||||
|
expectError(t, `func() { a := "foo"; a.b = 2 }()`)
|
||||||
}
|
}
|
||||||
|
|
77
vm/vm_tail_call_test.go
Normal file
77
vm/vm_tail_call_test.go
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
package vm_test
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestTailCall(t *testing.T) {
|
||||||
|
expect(t, `
|
||||||
|
fac := func(n, a) {
|
||||||
|
if n == 1 {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return fac(n-1, n*a)
|
||||||
|
}
|
||||||
|
out = fac(5, 1)`, 120)
|
||||||
|
|
||||||
|
expect(t, `
|
||||||
|
fac := func(n, a) {
|
||||||
|
if n == 1 {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
x := {foo: fac} // indirection for test
|
||||||
|
return x.foo(n-1, n*a)
|
||||||
|
}
|
||||||
|
out = fac(5, 1)`, 120)
|
||||||
|
|
||||||
|
expect(t, `
|
||||||
|
fib := func(x, s) {
|
||||||
|
if x == 0 {
|
||||||
|
return 0 + s
|
||||||
|
} else if x == 1 {
|
||||||
|
return 1 + s
|
||||||
|
}
|
||||||
|
return fib(x-1, fib(x-2, s))
|
||||||
|
}
|
||||||
|
out = fib(15, 0)`, 610)
|
||||||
|
|
||||||
|
// global variable and no return value
|
||||||
|
expect(t, `
|
||||||
|
out = 0
|
||||||
|
foo := func(a) {
|
||||||
|
if a == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
out += a
|
||||||
|
foo(a-1)
|
||||||
|
}
|
||||||
|
foo(10)`, 55)
|
||||||
|
|
||||||
|
// TODO: see comment inside the code
|
||||||
|
expect(t, `
|
||||||
|
f1 := func() {
|
||||||
|
f2 := undefined // TODO: this is really inconvenient
|
||||||
|
f2 = func(n, s) {
|
||||||
|
if n == 0 { return s }
|
||||||
|
return f2(n-1, n + s)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f2(5, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
out = f1()`, 15)
|
||||||
|
|
||||||
|
// tail call with free vars
|
||||||
|
// expect(t, `
|
||||||
|
//f1 := func() {
|
||||||
|
// a := 10
|
||||||
|
// f2 := undefined
|
||||||
|
// f2 = func(n, s) {
|
||||||
|
// a += s
|
||||||
|
// if n == 0 {
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// f2(n-1, n + s)
|
||||||
|
// }
|
||||||
|
// return f2
|
||||||
|
//}
|
||||||
|
//out = f1()(5, 0)`, 25)
|
||||||
|
}
|
Loading…
Reference in a new issue