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() {
|
||||
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) {
|
||||
|
@ -42,7 +78,8 @@ fib := func(x) {
|
|||
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.Printf("Go: %s\n", nativeTime)
|
||||
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) {
|
||||
var astFile *ast.File
|
||||
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)
|
||||
} else if op == token.Assign {
|
||||
if !exists {
|
||||
symbol = c.symbolTable.Define(ident)
|
||||
}
|
||||
} else {
|
||||
if !exists {
|
||||
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 {
|
||||
c.emit(OpSetSelLocal, symbol.Index, numSel)
|
||||
} else {
|
||||
c.emit(OpSetLocal, symbol.Index)
|
||||
if op == token.Define {
|
||||
c.emit(OpDefineLocal, symbol.Index)
|
||||
} else {
|
||||
c.emit(OpSetLocal, symbol.Index)
|
||||
}
|
||||
}
|
||||
case ScopeFree:
|
||||
if numSel > 0 {
|
||||
|
|
|
@ -74,7 +74,7 @@ func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
|||
|
||||
// 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
|
||||
//
|
||||
// ... body ...
|
||||
|
@ -93,7 +93,7 @@ func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
|||
if itSymbol.Scope == ScopeGlobal {
|
||||
c.emit(OpSetGlobal, itSymbol.Index)
|
||||
} else {
|
||||
c.emit(OpSetLocal, itSymbol.Index)
|
||||
c.emit(OpDefineLocal, itSymbol.Index)
|
||||
}
|
||||
|
||||
// pre-condition position
|
||||
|
@ -126,7 +126,7 @@ func (c *Compiler) compileForInStmt(stmt *ast.ForInStmt) error {
|
|||
if keySymbol.Scope == ScopeGlobal {
|
||||
c.emit(OpSetGlobal, keySymbol.Index)
|
||||
} 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 {
|
||||
c.emit(OpSetGlobal, valueSymbol.Index)
|
||||
} else {
|
||||
c.emit(OpSetLocal, valueSymbol.Index)
|
||||
c.emit(OpDefineLocal, valueSymbol.Index)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -619,7 +619,7 @@ func TestCompiler_Compile(t *testing.T) {
|
|||
intObject(55),
|
||||
compiledFunction(1, 0,
|
||||
compiler.MakeInstruction(compiler.OpConstant, 0),
|
||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpDefineLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpReturnValue, 1)))))
|
||||
|
||||
|
@ -633,7 +633,7 @@ func TestCompiler_Compile(t *testing.T) {
|
|||
intObject(23),
|
||||
compiledFunction(1, 0,
|
||||
compiler.MakeInstruction(compiler.OpConstant, 0),
|
||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpDefineLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpConstant, 1),
|
||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
||||
|
@ -649,9 +649,9 @@ func TestCompiler_Compile(t *testing.T) {
|
|||
intObject(77),
|
||||
compiledFunction(2, 0,
|
||||
compiler.MakeInstruction(compiler.OpConstant, 0),
|
||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpDefineLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpConstant, 1),
|
||||
compiler.MakeInstruction(compiler.OpSetLocal, 1),
|
||||
compiler.MakeInstruction(compiler.OpDefineLocal, 1),
|
||||
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpGetLocal, 1),
|
||||
compiler.MakeInstruction(compiler.OpAdd),
|
||||
|
@ -792,7 +792,7 @@ func() {
|
|||
intObject(88),
|
||||
compiledFunction(1, 0,
|
||||
compiler.MakeInstruction(compiler.OpConstant, 3),
|
||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpDefineLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpGetGlobal, 0),
|
||||
compiler.MakeInstruction(compiler.OpGetFree, 0),
|
||||
compiler.MakeInstruction(compiler.OpAdd),
|
||||
|
@ -803,14 +803,14 @@ func() {
|
|||
compiler.MakeInstruction(compiler.OpReturnValue, 1)),
|
||||
compiledFunction(1, 0,
|
||||
compiler.MakeInstruction(compiler.OpConstant, 2),
|
||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpDefineLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpGetFree, 0),
|
||||
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpClosure, 4, 2),
|
||||
compiler.MakeInstruction(compiler.OpReturnValue, 1)),
|
||||
compiledFunction(1, 0,
|
||||
compiler.MakeInstruction(compiler.OpConstant, 1),
|
||||
compiler.MakeInstruction(compiler.OpSetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpDefineLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpGetLocal, 0),
|
||||
compiler.MakeInstruction(compiler.OpClosure, 5, 1),
|
||||
compiler.MakeInstruction(compiler.OpReturnValue, 1)))))
|
||||
|
|
|
@ -45,6 +45,7 @@ var definitions = map[Opcode]*Definition{
|
|||
OpReturnValue: {Name: "RETVAL", Operands: []int{1}},
|
||||
OpGetLocal: {Name: "GETL", Operands: []int{1}},
|
||||
OpSetLocal: {Name: "SETL", Operands: []int{1}},
|
||||
OpDefineLocal: {Name: "DEFL", Operands: []int{1}},
|
||||
OpSetSelLocal: {Name: "SETSL", Operands: []int{1, 1}},
|
||||
OpGetBuiltin: {Name: "BUILTIN", Operands: []int{1}},
|
||||
OpClosure: {Name: "CLOSURE", Operands: []int{2, 1}},
|
||||
|
|
|
@ -30,9 +30,6 @@ const (
|
|||
OpOrJump
|
||||
OpJump
|
||||
OpNull
|
||||
OpGetGlobal
|
||||
OpSetGlobal
|
||||
OpSetSelGlobal
|
||||
OpArray
|
||||
OpMap
|
||||
OpIndex
|
||||
|
@ -40,14 +37,18 @@ const (
|
|||
OpCall
|
||||
OpReturn
|
||||
OpReturnValue
|
||||
OpGetGlobal
|
||||
OpSetGlobal
|
||||
OpSetSelGlobal
|
||||
OpGetLocal
|
||||
OpSetLocal
|
||||
OpDefineLocal
|
||||
OpSetSelLocal
|
||||
OpGetBuiltin
|
||||
OpClosure
|
||||
OpGetFree
|
||||
OpSetFree
|
||||
OpSetSelFree
|
||||
OpGetBuiltin
|
||||
OpClosure
|
||||
OpIteratorInit
|
||||
OpIteratorNext
|
||||
OpIteratorKey
|
||||
|
|
|
@ -11,34 +11,34 @@ type M map[string]interface{}
|
|||
|
||||
func TestCompiled_Get(t *testing.T) {
|
||||
// simple script
|
||||
c := compile(t, `a = 5`, nil)
|
||||
c := compile(t, `a := 5`, nil)
|
||||
compiledRun(t, c)
|
||||
compiledGet(t, c, "a", int64(5))
|
||||
|
||||
// user-defined variables
|
||||
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
|
||||
compiledGet(t, c, "a", nil) // a = undefined; because it's before Compiled.Run()
|
||||
compiledRun(t, c) // Compiled.Run()
|
||||
compiledGet(t, c, "a", "foo") // a = "foo"
|
||||
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
|
||||
compiledGet(t, c, "a", nil) // a = undefined; because it's before Compiled.Run()
|
||||
compiledRun(t, c) // Compiled.Run()
|
||||
compiledGet(t, c, "a", "foo") // a = "foo"
|
||||
}
|
||||
|
||||
func TestCompiled_GetAll(t *testing.T) {
|
||||
c := compile(t, `a = 5`, nil)
|
||||
c := compile(t, `a := 5`, nil)
|
||||
compiledRun(t, c)
|
||||
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)
|
||||
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)
|
||||
compiledGetAll(t, c, M{"a": "foo", "b": int64(5)})
|
||||
}
|
||||
|
||||
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()
|
||||
compiledRun(t, c)
|
||||
compiledIsDefined(t, c, "a", true)
|
||||
|
|
|
@ -8,7 +8,7 @@ import (
|
|||
)
|
||||
|
||||
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", "foo")) // b = "foo" (re-define before compilation)
|
||||
c, err := s.Compile()
|
||||
|
@ -19,7 +19,7 @@ func TestScript_Add(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)
|
||||
assert.NoError(t, err)
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
|
||||
case compiler.OpNull:
|
||||
if err := v.push(&undefinedObj); err != nil {
|
||||
return err
|
||||
|
@ -530,14 +531,29 @@ func (v *VM) Run() error {
|
|||
if err := v.push(&undefinedObj); err != nil {
|
||||
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:
|
||||
localIndex := compiler.ReadUint8(ins[ip+1:])
|
||||
curFrame.ip += 1
|
||||
|
||||
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:
|
||||
localIndex := compiler.ReadUint8(ins[ip+1:])
|
||||
numSelectors := int(compiler.ReadUint8(ins[ip+2:]))
|
||||
|
@ -559,7 +575,7 @@ func (v *VM) Run() error {
|
|||
}
|
||||
|
||||
// RHS value
|
||||
val := v.pop()
|
||||
val := v.pop() // no need to copy value here; selectorAssign uses copy of value
|
||||
|
||||
sp := curFrame.basePointer + int(localIndex)
|
||||
|
||||
|
@ -632,6 +648,7 @@ func (v *VM) Run() error {
|
|||
val := v.pop()
|
||||
|
||||
*v.frames[v.framesIndex-1].freeVars[freeIndex] = *val
|
||||
|
||||
case compiler.OpIteratorInit:
|
||||
var iterator objects.Object
|
||||
|
||||
|
@ -650,6 +667,7 @@ func (v *VM) Run() error {
|
|||
if err := v.push(&iterator); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case compiler.OpIteratorNext:
|
||||
iterator := v.pop()
|
||||
b := (*iterator).(objects.Iterator).Next()
|
||||
|
@ -716,6 +734,13 @@ func (v *VM) pop() *objects.Object {
|
|||
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 {
|
||||
c := v.constants[constIndex]
|
||||
|
||||
|
@ -853,6 +878,61 @@ func (v *VM) callFunction(fn *objects.CompiledFunction, freeVars []*objects.Obje
|
|||
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].freeVars = freeVars
|
||||
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
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
|
|
|
@ -10,14 +10,14 @@ func TestArray(t *testing.T) {
|
|||
expect(t, `out = [1, 2, 3][0]`, 1)
|
||||
expect(t, `out = [1, 2, 3][1]`, 2)
|
||||
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, `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]; i = arr[0]; out = arr[i]`, 2)
|
||||
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]; i := arr[0]; out = arr[i]`, 2)
|
||||
|
||||
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][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][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
|
||||
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][-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 = 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 := 3; 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)
|
||||
|
||||
// define operator ":=" is optional
|
||||
expect(t, `a = 1; out = a`, 1)
|
||||
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`, 1) // "a := 2" defines a new local variable 'a'
|
||||
expect(t, `a = 1; func() { b = 2; out = b }()`, 2)
|
||||
expect(t, `a := 1; out = a`, 1)
|
||||
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`, 1) // "a := 2" defines a new local variable 'a'
|
||||
expect(t, `a := 1; func() { b := 2; out = b }()`, 2)
|
||||
expect(t, `
|
||||
out = func() {
|
||||
a = 2
|
||||
a := 2
|
||||
func() {
|
||||
a = 3 // captured from outer scope
|
||||
}()
|
||||
|
@ -29,7 +28,7 @@ out = func() {
|
|||
|
||||
expect(t, `
|
||||
func() {
|
||||
a = 5
|
||||
a := 5
|
||||
out = func() {
|
||||
a := 4
|
||||
return a
|
||||
|
@ -39,14 +38,14 @@ func() {
|
|||
expectError(t, `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 += 4 - 2;; out = a`, 3)
|
||||
expect(t, `a = 3; a -= 1;; 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 *= 1 + 3;; out = a`, 8)
|
||||
expect(t, `a = 10; a /= 2;; out = a`, 5)
|
||||
expect(t, `a = 10; a /= 5 - 3;; out = a`, 5)
|
||||
expect(t, `a := 1; a += 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 -= 5 - 4;; out = a`, 2)
|
||||
expect(t, `a := 2; a *= 4;; 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 /= 5 - 3;; out = a`, 5)
|
||||
|
||||
// +=, -=, *=, /= operator does not define new variable
|
||||
expectError(t, `a += 4`)
|
||||
|
@ -55,9 +54,9 @@ func() {
|
|||
expectError(t, `a /= 4`)
|
||||
|
||||
expect(t, `
|
||||
f1 = func() {
|
||||
f2 = func() {
|
||||
a = 1
|
||||
f1 := func() {
|
||||
f2 := func() {
|
||||
a := 1
|
||||
a += 2 // it's a statement, not an expression
|
||||
return a
|
||||
};
|
||||
|
@ -66,20 +65,20 @@ f1 = func() {
|
|||
};
|
||||
|
||||
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 -= 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 *= 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 /= 5 - 3; return a }; return f2(); }; out = f1()`, 5)
|
||||
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 -= 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 *= 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 /= 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, `
|
||||
f1 = func(a) {
|
||||
f1 := func(a) {
|
||||
return func(b) {
|
||||
c = a
|
||||
c := a
|
||||
c += b * 2
|
||||
return c
|
||||
}
|
||||
|
@ -90,7 +89,7 @@ out = f1();`, 3)
|
|||
|
||||
expect(t, `
|
||||
out = func() {
|
||||
a = 1
|
||||
a := 1
|
||||
func() {
|
||||
a = 2
|
||||
func() {
|
||||
|
@ -106,8 +105,8 @@ out = f1();`, 3)
|
|||
|
||||
// write on free variables
|
||||
expect(t, `
|
||||
f1 = func() {
|
||||
a = 5
|
||||
f1 := func() {
|
||||
a := 5
|
||||
|
||||
return func() {
|
||||
a += 3
|
||||
|
@ -118,14 +117,14 @@ out = f1();`, 3)
|
|||
`, 8)
|
||||
|
||||
expect(t, `
|
||||
it = func(seq, fn) {
|
||||
it := func(seq, fn) {
|
||||
fn(seq[0])
|
||||
fn(seq[1])
|
||||
fn(seq[2])
|
||||
}
|
||||
|
||||
foo = func(a) {
|
||||
b = 0
|
||||
foo := func(a) {
|
||||
b := 0
|
||||
it([1, 2, 3], func(x) {
|
||||
b = x + a
|
||||
})
|
||||
|
@ -136,14 +135,14 @@ out = f1();`, 3)
|
|||
`, 5)
|
||||
|
||||
expect(t, `
|
||||
it = func(seq, fn) {
|
||||
it := func(seq, fn) {
|
||||
fn(seq[0])
|
||||
fn(seq[1])
|
||||
fn(seq[2])
|
||||
}
|
||||
|
||||
foo = func(a) {
|
||||
b = 0
|
||||
foo := func(a) {
|
||||
b := 0
|
||||
it([1, 2, 3], func(x) {
|
||||
b += x + a
|
||||
})
|
||||
|
@ -155,7 +154,7 @@ out = f1();`, 3)
|
|||
|
||||
expect(t, `
|
||||
out = func() {
|
||||
a = 1
|
||||
a := 1
|
||||
func() {
|
||||
a = 2
|
||||
}()
|
||||
|
@ -164,25 +163,25 @@ out = func() {
|
|||
`, 2)
|
||||
|
||||
expect(t, `
|
||||
f = func() {
|
||||
a = 1
|
||||
f := func() {
|
||||
a := 1
|
||||
return {
|
||||
b: func() { a += 3 },
|
||||
c: func() { a += 2 },
|
||||
d: func() { return a }
|
||||
}
|
||||
}
|
||||
m = f()
|
||||
m := f()
|
||||
m.b()
|
||||
m.c()
|
||||
out = m.d()
|
||||
`, 6)
|
||||
|
||||
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() {
|
||||
a = 100
|
||||
a := 100
|
||||
each([1, 2, 3], func(x) {
|
||||
a += x
|
||||
})
|
||||
|
@ -194,11 +193,11 @@ out = func() {
|
|||
`, 136)
|
||||
|
||||
// assigning different type value
|
||||
expect(t, `a = 1; a = "foo"; out = a`, "foo") // global
|
||||
expect(t, `func() { a = 1; a = "foo"; out = a }()`, "foo") // local
|
||||
expect(t, `a := 1; a = "foo"; out = a`, "foo") // global
|
||||
expect(t, `func() { a := 1; a = "foo"; out = a }()`, "foo") // local
|
||||
expect(t, `
|
||||
out = func() {
|
||||
a = 5
|
||||
a := 5
|
||||
return func() {
|
||||
a = "foo"
|
||||
return a
|
||||
|
@ -206,18 +205,18 @@ out = func() {
|
|||
}()`, "foo") // free
|
||||
|
||||
// variables declared in if/for blocks
|
||||
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, `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
|
||||
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={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 += a.c; out = a.b`, 3)
|
||||
expect(t, `a={b:1,c:2}; a.b += a.c; out = a.c`, 2)
|
||||
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:={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 += a.c; out = a.b`, 3)
|
||||
expect(t, `a:={b:1,c:2}; a.b += a.c; out = a.c`, 2)
|
||||
expect(t, `
|
||||
a = {
|
||||
a := {
|
||||
b: [1, 2, 3],
|
||||
c: {
|
||||
d: 8,
|
||||
|
@ -230,7 +229,7 @@ out = a["c"]["f"][1]
|
|||
`, 10)
|
||||
|
||||
expect(t, `
|
||||
a = {
|
||||
a := {
|
||||
b: [1, 2, 3],
|
||||
c: {
|
||||
d: 8,
|
||||
|
@ -243,7 +242,7 @@ out = a.c.h
|
|||
`, "bar")
|
||||
|
||||
expectError(t, `
|
||||
a = {
|
||||
a := {
|
||||
b: [1, 2, 3],
|
||||
c: {
|
||||
d: 8,
|
||||
|
|
|
@ -3,9 +3,9 @@ package vm_test
|
|||
import "testing"
|
||||
|
||||
func TestCall(t *testing.T) {
|
||||
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: 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)
|
||||
|
||||
// "this" binding
|
||||
// expect(t, `
|
||||
|
|
|
@ -24,21 +24,21 @@ func TestFor(t *testing.T) {
|
|||
}()`, 5)
|
||||
|
||||
expect(t, `
|
||||
for a=1; a<=10; a++ {
|
||||
for a:=1; a<=10; a++ {
|
||||
out += a
|
||||
}`, 55)
|
||||
|
||||
expect(t, `
|
||||
for a=1; a<=3; a++ {
|
||||
for b=3; b<=6; b++ {
|
||||
for a:=1; a<=3; a++ {
|
||||
for b:=3; b<=6; b++ {
|
||||
out += b
|
||||
}
|
||||
}`, 54)
|
||||
|
||||
expect(t, `
|
||||
out = func() {
|
||||
sum = 0
|
||||
for a=1; a<=10; a++ {
|
||||
sum := 0
|
||||
for a:=1; a<=10; a++ {
|
||||
sum += a
|
||||
}
|
||||
return sum
|
||||
|
@ -46,17 +46,17 @@ func TestFor(t *testing.T) {
|
|||
|
||||
expect(t, `
|
||||
out = func() {
|
||||
sum = 0
|
||||
for a=1; a<=4; a++ {
|
||||
for b=3; b<=5; b++ {
|
||||
sum := 0
|
||||
for a:=1; a<=4; a++ {
|
||||
for b:=3; b<=5; b++ {
|
||||
sum += b
|
||||
}
|
||||
}
|
||||
return sum
|
||||
}()`, 48) // (3+4+5 * 4
|
||||
}()`, 48) // (3+4+5) * 4
|
||||
|
||||
expect(t, `
|
||||
a = 1
|
||||
a := 1
|
||||
for ; a<=10; a++ {
|
||||
if a == 5 {
|
||||
break
|
||||
|
@ -65,7 +65,7 @@ func TestFor(t *testing.T) {
|
|||
out = a`, 5)
|
||||
|
||||
expect(t, `
|
||||
for a=1; a<=10; a++ {
|
||||
for a:=1; a<=10; a++ {
|
||||
if a == 3 {
|
||||
continue
|
||||
}
|
||||
|
@ -76,9 +76,9 @@ func TestFor(t *testing.T) {
|
|||
}`, 12) // 1 + 2 + 4 + 5
|
||||
|
||||
expect(t, `
|
||||
for a=1; a<=10; {
|
||||
for a:=1; a<=10; {
|
||||
if a == 3 {
|
||||
a++
|
||||
a++
|
||||
continue
|
||||
}
|
||||
out += a
|
||||
|
|
|
@ -6,20 +6,20 @@ import (
|
|||
|
||||
func TestFunction(t *testing.T) {
|
||||
// function with no "return" statement returns "invalid" value.
|
||||
expect(t, `f1 = func() {}; out = f1();`, 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, `f1 := func() {}; out = f1();`, 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) { return x; }; out = f(5);`, 5)
|
||||
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, f(5, 5));`, 20)
|
||||
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, 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, `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, `
|
||||
f2 = func(a) {
|
||||
f1 = func(a) {
|
||||
f2 := func(a) {
|
||||
f1 := func(a) {
|
||||
return a * 2;
|
||||
};
|
||||
|
||||
|
@ -31,93 +31,102 @@ out = f2(10);
|
|||
|
||||
// closures
|
||||
expect(t, `
|
||||
newAdder = func(x) {
|
||||
newAdder := func(x) {
|
||||
return func(y) { return x + y };
|
||||
};
|
||||
|
||||
add2 = newAdder(2);
|
||||
add2 := newAdder(2);
|
||||
out = add2(5);
|
||||
`, 7)
|
||||
|
||||
// function as a argument
|
||||
expect(t, `
|
||||
add = func(a, b) { return a + b };
|
||||
sub = func(a, b) { return a - b };
|
||||
applyFunc = func(a, b, f) { return f(a, b) };
|
||||
add := func(a, b) { return a + b };
|
||||
sub := func(a, b) { return a - b };
|
||||
applyFunc := func(a, b, f) { return f(a, b) };
|
||||
|
||||
out = applyFunc(applyFunc(2, 2, add), 3, sub);
|
||||
`, 1)
|
||||
|
||||
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 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; return 100 }; out = f1();`, 99)
|
||||
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, `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, `foo1 = func() { foo = 50; return foo}; foo2 = func() { foo = 100; return foo }; out = foo1() + foo2()`, 150)
|
||||
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 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; return 100 }; out = f1();`, 99)
|
||||
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, `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, `
|
||||
g = 50;
|
||||
minusOne = func() {
|
||||
n = 1;
|
||||
foo1 := func() {
|
||||
foo := 50
|
||||
return foo
|
||||
}
|
||||
foo2 := func() {
|
||||
foo := 100
|
||||
return foo
|
||||
}
|
||||
out = foo1() + foo2()`, 150)
|
||||
expect(t, `
|
||||
g := 50;
|
||||
minusOne := func() {
|
||||
n := 1;
|
||||
return g - n;
|
||||
};
|
||||
minusTwo = func() {
|
||||
n = 2;
|
||||
minusTwo := func() {
|
||||
n := 2;
|
||||
return g - n;
|
||||
};
|
||||
out = minusOne() + minusTwo()
|
||||
`, 97)
|
||||
expect(t, `
|
||||
f1 = func() {
|
||||
f2 = func() { return 1; }
|
||||
f1 := func() {
|
||||
f2 := func() { return 1; }
|
||||
return f2
|
||||
};
|
||||
out = f1()()
|
||||
`, 1)
|
||||
|
||||
expect(t, `
|
||||
f1 = func(a) { return a; };
|
||||
f1 := func(a) { return a; };
|
||||
out = f1(4)`, 4)
|
||||
expect(t, `
|
||||
f1 = func(a, b) { return a + b; };
|
||||
f1 := func(a, b) { return a + b; };
|
||||
out = f1(1, 2)`, 3)
|
||||
|
||||
expect(t, `
|
||||
sum = func(a, b) {
|
||||
c = a + b;
|
||||
sum := func(a, b) {
|
||||
c := a + b;
|
||||
return c;
|
||||
};
|
||||
out = sum(1, 2);`, 3)
|
||||
|
||||
expect(t, `
|
||||
sum = func(a, b) {
|
||||
c = a + b;
|
||||
sum := func(a, b) {
|
||||
c := a + b;
|
||||
return c;
|
||||
};
|
||||
out = sum(1, 2) + sum(3, 4);`, 10)
|
||||
|
||||
expect(t, `
|
||||
sum = func(a, b) {
|
||||
c = a + b
|
||||
sum := func(a, b) {
|
||||
c := a + b
|
||||
return c
|
||||
};
|
||||
outer = func() {
|
||||
outer := func() {
|
||||
return sum(1, 2) + sum(3, 4)
|
||||
};
|
||||
out = outer();`, 10)
|
||||
|
||||
expect(t, `
|
||||
g = 10;
|
||||
g := 10;
|
||||
|
||||
sum = func(a, b) {
|
||||
c = a + b;
|
||||
sum := func(a, b) {
|
||||
c := a + b;
|
||||
return c + g;
|
||||
}
|
||||
|
||||
outer = func() {
|
||||
outer := func() {
|
||||
return sum(1, 2) + sum(3, 4) + g;
|
||||
}
|
||||
|
||||
|
@ -129,65 +138,65 @@ out = outer() + g
|
|||
expectError(t, `func(a, b) { return a + b; }(1)`)
|
||||
|
||||
expect(t, `
|
||||
f1 = func(a) {
|
||||
f1 := func(a) {
|
||||
return func() { return a; };
|
||||
};
|
||||
f2 = f1(99);
|
||||
f2 := f1(99);
|
||||
out = f2()
|
||||
`, 99)
|
||||
|
||||
expect(t, `
|
||||
f1 = func(a, b) {
|
||||
f1 := func(a, b) {
|
||||
return func(c) { return a + b + c };
|
||||
};
|
||||
|
||||
f2 = f1(1, 2);
|
||||
f2 := f1(1, 2);
|
||||
out = f2(8);
|
||||
`, 11)
|
||||
expect(t, `
|
||||
f1 = func(a, b) {
|
||||
c = a + b;
|
||||
f1 := func(a, b) {
|
||||
c := a + b;
|
||||
return func(d) { return c + d };
|
||||
};
|
||||
f2 = f1(1, 2);
|
||||
f2 := f1(1, 2);
|
||||
out = f2(8);
|
||||
`, 11)
|
||||
expect(t, `
|
||||
f1 = func(a, b) {
|
||||
c = a + b;
|
||||
f1 := func(a, b) {
|
||||
c := a + b;
|
||||
return func(d) {
|
||||
e = d + c;
|
||||
e := d + c;
|
||||
return func(f) { return e + f };
|
||||
}
|
||||
};
|
||||
f2 = f1(1, 2);
|
||||
f3 = f2(3);
|
||||
f2 := f1(1, 2);
|
||||
f3 := f2(3);
|
||||
out = f3(8);
|
||||
`, 14)
|
||||
expect(t, `
|
||||
a = 1;
|
||||
f1 = func(b) {
|
||||
a := 1;
|
||||
f1 := func(b) {
|
||||
return func(c) {
|
||||
return func(d) { return a + b + c + d }
|
||||
};
|
||||
};
|
||||
f2 = f1(2);
|
||||
f3 = f2(3);
|
||||
f2 := f1(2);
|
||||
f3 := f2(3);
|
||||
out = f3(8);
|
||||
`, 14)
|
||||
expect(t, `
|
||||
f1 = func(a, b) {
|
||||
one = func() { return a; };
|
||||
two = func() { return b; };
|
||||
f1 := func(a, b) {
|
||||
one := func() { return a; };
|
||||
two := func() { return b; };
|
||||
return func() { return one() + two(); }
|
||||
};
|
||||
f2 = f1(9, 90);
|
||||
f2 := f1(9, 90);
|
||||
out = f2();
|
||||
`, 99)
|
||||
|
||||
// fibonacci
|
||||
expect(t, `
|
||||
fib = func(x) {
|
||||
fib := func(x) {
|
||||
if(x == 0) {
|
||||
return 0;
|
||||
} 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) { 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, `a=0; if a++; 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, `
|
||||
func() {
|
||||
a = 1
|
||||
a := 1
|
||||
if a++; a > 1 {
|
||||
out = a
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ func() {
|
|||
`, 2)
|
||||
expect(t, `
|
||||
func() {
|
||||
a = 1
|
||||
a := 1
|
||||
if a++; a == 1 {
|
||||
out = 10
|
||||
} else {
|
||||
|
@ -48,7 +48,7 @@ func() {
|
|||
`, 20)
|
||||
expect(t, `
|
||||
func() {
|
||||
a = 1
|
||||
a := 1
|
||||
|
||||
func() {
|
||||
if a++; a > 1 {
|
||||
|
|
|
@ -30,12 +30,12 @@ func TestLogical(t *testing.T) {
|
|||
expect(t, `out = 0 || (0 && 2)`, 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}; 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}; 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()`, 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)
|
||||
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}; 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()`, 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}["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, `
|
||||
m = {
|
||||
m := {
|
||||
foo: func(x) {
|
||||
return x * 2
|
||||
}
|
||||
|
@ -31,6 +31,8 @@ out = m["foo"](2) + m["foo"](3)
|
|||
`, 10)
|
||||
|
||||
// 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; m2.k1 = 3; out = m1.k1`, 3)
|
||||
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, `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)
|
||||
|
||||
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) {
|
||||
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.k3`, undefined())
|
||||
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.k3`, undefined())
|
||||
|
||||
expect(t, `
|
||||
a = {
|
||||
a := {
|
||||
b: {
|
||||
c: 4,
|
||||
a: false
|
||||
|
@ -20,7 +20,7 @@ a = {
|
|||
out = a.b.c`, 4)
|
||||
|
||||
expectError(t, `
|
||||
a = {
|
||||
a := {
|
||||
b: {
|
||||
c: 4,
|
||||
a: false
|
||||
|
@ -30,7 +30,7 @@ a = {
|
|||
out = a.x.c`)
|
||||
|
||||
expectError(t, `
|
||||
a = {
|
||||
a := {
|
||||
b: {
|
||||
c: 4,
|
||||
a: false
|
||||
|
@ -39,26 +39,26 @@ a = {
|
|||
}
|
||||
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.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: 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: 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: {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: {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.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: 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: 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: {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: {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.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: 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: 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: {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: {c: 1}}; func() { a.b.d = 2 }(); out = a }()`, MAP{"b": MAP{"c": 1, "d": 2}})
|
||||
|
||||
expect(t, `
|
||||
a = {
|
||||
a := {
|
||||
b: [1, 2, 3],
|
||||
c: {
|
||||
d: 8,
|
||||
|
@ -69,10 +69,20 @@ a = {
|
|||
out = [a.b[2], a.c.d, a.c.e, a.c.f[1]]
|
||||
`, ARR{3, 8, "foo", 8})
|
||||
|
||||
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 }()`)
|
||||
expect(t, `
|
||||
func() {
|
||||
a := [1, 2, 3]
|
||||
b := 9
|
||||
a[1] = b
|
||||
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