bind expression (:=) instead of let
This commit is contained in:
17
vm/vm.go
17
vm/vm.go
@@ -181,6 +181,11 @@ func (vm *VM) Run() error {
|
||||
vm.globals[globalIndex] = ref
|
||||
}
|
||||
|
||||
err := vm.push(Null)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case code.OpAssignGlobal:
|
||||
globalIndex := code.ReadUint16(ins[ip+1:])
|
||||
vm.currentFrame().ip += 2
|
||||
@@ -284,7 +289,17 @@ func (vm *VM) Run() error {
|
||||
|
||||
frame := vm.currentFrame()
|
||||
|
||||
vm.stack[frame.basePointer+int(localIndex)] = vm.pop()
|
||||
ref := vm.pop()
|
||||
if immutable, ok := ref.(object.Immutable); ok {
|
||||
vm.stack[frame.basePointer+int(localIndex)] = immutable.Clone()
|
||||
} else {
|
||||
vm.stack[frame.basePointer+int(localIndex)] = ref
|
||||
}
|
||||
|
||||
err := vm.push(Null)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
case code.OpGetLocal:
|
||||
localIndex := code.ReadUint8(ins[ip+1:])
|
||||
|
||||
214
vm/vm_test.go
214
vm/vm_test.go
@@ -261,12 +261,12 @@ func TestConditionals(t *testing.T) {
|
||||
{"if (1 > 2) { 10 }", Null},
|
||||
{"if (false) { 10 }", Null},
|
||||
{"if ((if (false) { 10 })) { 10 } else { 20 }", 20},
|
||||
{"if (true) { let a = 5; }", Null},
|
||||
{"if (true) { 10; let a = 5; }", Null},
|
||||
{"if (false) { 10 } else { let b = 5; }", Null},
|
||||
{"if (false) { 10 } else { 10; let b = 5; }", Null},
|
||||
{"if (true) { let a = 5; } else { 10 }", Null},
|
||||
{"let x = 0; if (true) { x = 1; }; if (false) { x = 2; }; x", 1},
|
||||
{"if (true) { a := 5; }", Null},
|
||||
{"if (true) { 10; a := 5; }", Null},
|
||||
{"if (false) { 10 } else { b := 5; }", Null},
|
||||
{"if (false) { 10 } else { 10; b := 5; }", Null},
|
||||
{"if (true) { a := 5; } else { 10 }", Null},
|
||||
{"x := 0; if (true) { x = 1; }; if (false) { x = 2; }; x", 1},
|
||||
{"if (1 < 2) { 10 } else if (1 == 2) { 20 }", 10},
|
||||
{"if (1 > 2) { 10 } else if (1 == 2) { 20 } else { 30 }", 30},
|
||||
}
|
||||
@@ -274,11 +274,11 @@ func TestConditionals(t *testing.T) {
|
||||
runVmTests(t, tests)
|
||||
}
|
||||
|
||||
func TestGlobalLetStatements(t *testing.T) {
|
||||
func TestGlobalBindExpressions(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{"let one = 1; one", 1},
|
||||
{"let one = 1; let two = 2; one + two", 3},
|
||||
{"let one = 1; let two = one + one; one + two", 3},
|
||||
{"one := 1; one", 1},
|
||||
{"one := 1; two := 2; one + two", 3},
|
||||
{"one := 1; two := one + one; one + two", 3},
|
||||
}
|
||||
|
||||
runVmTests(t, tests)
|
||||
@@ -364,24 +364,24 @@ func TestCallingFunctionsWithoutArguments(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let fivePlusTen = fn() { return 5 + 10; };
|
||||
fivePlusTen := fn() { return 5 + 10; };
|
||||
fivePlusTen();
|
||||
`,
|
||||
expected: 15,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let one = fn() { return 1; };
|
||||
let two = fn() { return 2; };
|
||||
one := fn() { return 1; };
|
||||
two := fn() { return 2; };
|
||||
one() + two()
|
||||
`,
|
||||
expected: 3,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let a = fn() { return 1 };
|
||||
let b = fn() { return a() + 1 };
|
||||
let c = fn() { return b() + 1 };
|
||||
a := fn() { return 1 };
|
||||
b := fn() { return a() + 1 };
|
||||
c:= fn() { return b() + 1 };
|
||||
c();
|
||||
`,
|
||||
expected: 3,
|
||||
@@ -395,14 +395,14 @@ func TestFunctionsWithReturnStatements(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let earlyExit = fn() { return 99; 100; };
|
||||
earlyExit := fn() { return 99; 100; };
|
||||
earlyExit();
|
||||
`,
|
||||
expected: 99,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let earlyExit = fn() { return 99; return 100; };
|
||||
earlyExit := fn() { return 99; return 100; };
|
||||
earlyExit();
|
||||
`,
|
||||
expected: 99,
|
||||
@@ -416,15 +416,15 @@ func TestFunctionsWithoutReturnValue(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let noReturn = fn() { };
|
||||
noReturn := fn() { };
|
||||
noReturn();
|
||||
`,
|
||||
expected: Null,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let noReturn = fn() { };
|
||||
let noReturnTwo = fn() { noReturn(); };
|
||||
noReturn := fn() { };
|
||||
noReturnTwo := fn() { noReturn(); };
|
||||
noReturn();
|
||||
noReturnTwo();
|
||||
`,
|
||||
@@ -439,16 +439,16 @@ func TestFirstClassFunctions(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let returnsOne = fn() { return 1; };
|
||||
let returnsOneReturner = fn() { return returnsOne; };
|
||||
returnsOne := fn() { return 1; };
|
||||
returnsOneReturner := fn() { return returnsOne; };
|
||||
returnsOneReturner()();
|
||||
`,
|
||||
expected: 1,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let returnsOneReturner = fn() {
|
||||
let returnsOne = fn() { return 1; };
|
||||
returnsOneReturner := fn() {
|
||||
returnsOne := fn() { return 1; };
|
||||
return returnsOne;
|
||||
};
|
||||
returnsOneReturner()();
|
||||
@@ -464,43 +464,43 @@ func TestCallingFunctionsWithBindings(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
//{
|
||||
// input: `
|
||||
// let one = fn() { let one = 1; return one };
|
||||
// one := fn() { one := 1; return one };
|
||||
// one();
|
||||
// `,
|
||||
// expected: 1,
|
||||
//},
|
||||
{
|
||||
input: `
|
||||
let oneAndTwo = fn() { let one = 1; let two = 2; return one + two; };
|
||||
oneAndTwo := fn() { one := 1; two := 2; return one + two; };
|
||||
oneAndTwo();
|
||||
`,
|
||||
expected: 3,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let oneAndTwo = fn() { let one = 1; let two = 2; return one + two; };
|
||||
let threeAndFour = fn() { let three = 3; let four = 4; return three + four; };
|
||||
oneAndTwo := fn() { one := 1; two := 2; return one + two; };
|
||||
threeAndFour := fn() { three := 3; four := 4; return three + four; };
|
||||
oneAndTwo() + threeAndFour();
|
||||
`,
|
||||
expected: 10,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let firstFoobar = fn() { let foobar = 50; return foobar; };
|
||||
let secondFoobar = fn() { let foobar = 100; return foobar; };
|
||||
firstFoobar := fn() { foobar := 50; return foobar; };
|
||||
secondFoobar := fn() { foobar := 100; return foobar; };
|
||||
firstFoobar() + secondFoobar();
|
||||
`,
|
||||
expected: 150,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let globalSeed = 50;
|
||||
let minusOne = fn() {
|
||||
let num = 1;
|
||||
globalSeed := 50;
|
||||
minusOne := fn() {
|
||||
num := 1;
|
||||
return globalSeed - num;
|
||||
}
|
||||
let minusTwo = fn() {
|
||||
let num = 2;
|
||||
minusTwo := fn() {
|
||||
num := 2;
|
||||
return globalSeed - num;
|
||||
}
|
||||
minusOne() + minusTwo();
|
||||
@@ -516,22 +516,22 @@ func TestCallingFunctionsWithArgumentsAndBindings(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let identity = fn(a) { return a; };
|
||||
identity := fn(a) { return a; };
|
||||
identity(4);
|
||||
`,
|
||||
expected: 4,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let sum = fn(a, b) { return a + b; };
|
||||
sum := fn(a, b) { return a + b; };
|
||||
sum(1, 2);
|
||||
`,
|
||||
expected: 3,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let sum = fn(a, b) {
|
||||
let c = a + b;
|
||||
sum := fn(a, b) {
|
||||
c := a + b;
|
||||
return c;
|
||||
};
|
||||
sum(1, 2);
|
||||
@@ -540,8 +540,8 @@ func TestCallingFunctionsWithArgumentsAndBindings(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let sum = fn(a, b) {
|
||||
let c = a + b;
|
||||
sum := fn(a, b) {
|
||||
c := a + b;
|
||||
return c;
|
||||
};
|
||||
sum(1, 2) + sum(3, 4);`,
|
||||
@@ -549,11 +549,11 @@ func TestCallingFunctionsWithArgumentsAndBindings(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let sum = fn(a, b) {
|
||||
let c = a + b;
|
||||
sum := fn(a, b) {
|
||||
c := a + b;
|
||||
return c;
|
||||
};
|
||||
let outer = fn() {
|
||||
outer := fn() {
|
||||
return sum(1, 2) + sum(3, 4);
|
||||
};
|
||||
outer();
|
||||
@@ -562,14 +562,14 @@ func TestCallingFunctionsWithArgumentsAndBindings(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let globalNum = 10;
|
||||
globalNum := 10;
|
||||
|
||||
let sum = fn(a, b) {
|
||||
let c = a + b;
|
||||
sum := fn(a, b) {
|
||||
c := a + b;
|
||||
return c + globalNum;
|
||||
};
|
||||
|
||||
let outer = fn() {
|
||||
outer := fn() {
|
||||
return sum(1, 2) + sum(3, 4) + globalNum;
|
||||
};
|
||||
|
||||
@@ -697,72 +697,72 @@ func TestClosures(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let newClosure = fn(a) {
|
||||
newClosure := fn(a) {
|
||||
return fn() { return a; };
|
||||
};
|
||||
let closure = newClosure(99);
|
||||
closure := newClosure(99);
|
||||
closure();
|
||||
`,
|
||||
expected: 99,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let newAdder = fn(a, b) {
|
||||
newAdder := fn(a, b) {
|
||||
return fn(c) { return a + b + c };
|
||||
};
|
||||
let adder = newAdder(1, 2);
|
||||
adder := newAdder(1, 2);
|
||||
adder(8);
|
||||
`,
|
||||
expected: 11,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let newAdder = fn(a, b) {
|
||||
let c = a + b;
|
||||
newAdder := fn(a, b) {
|
||||
c := a + b;
|
||||
return fn(d) { return c + d };
|
||||
};
|
||||
let adder = newAdder(1, 2);
|
||||
adder := newAdder(1, 2);
|
||||
adder(8);
|
||||
`,
|
||||
expected: 11,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let newAdderOuter = fn(a, b) {
|
||||
let c = a + b;
|
||||
newAdderOuter := fn(a, b) {
|
||||
c := a + b;
|
||||
return fn(d) {
|
||||
let e = d + c;
|
||||
e := d + c;
|
||||
return fn(f) { return e + f; };
|
||||
};
|
||||
};
|
||||
let newAdderInner = newAdderOuter(1, 2)
|
||||
let adder = newAdderInner(3);
|
||||
newAdderInner := newAdderOuter(1, 2)
|
||||
adder := newAdderInner(3);
|
||||
adder(8);
|
||||
`,
|
||||
expected: 14,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let a = 1;
|
||||
let newAdderOuter = fn(b) {
|
||||
a := 1;
|
||||
newAdderOuter := fn(b) {
|
||||
return fn(c) {
|
||||
return fn(d) { return a + b + c + d };
|
||||
};
|
||||
};
|
||||
let newAdderInner = newAdderOuter(2)
|
||||
let adder = newAdderInner(3);
|
||||
newAdderInner := newAdderOuter(2)
|
||||
adder := newAdderInner(3);
|
||||
adder(8);
|
||||
`,
|
||||
expected: 14,
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let newClosure = fn(a, b) {
|
||||
let one = fn() { return a; };
|
||||
let two = fn() { return b; };
|
||||
newClosure := fn(a, b) {
|
||||
one := fn() { return a; };
|
||||
two := fn() { return b; };
|
||||
return fn() { return one() + two(); };
|
||||
};
|
||||
let closure = newClosure(9, 90);
|
||||
closure := newClosure(9, 90);
|
||||
closure();
|
||||
`,
|
||||
expected: 99,
|
||||
@@ -776,7 +776,7 @@ func TestRecursiveFunctions(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let countDown = fn(x) {
|
||||
countDown := fn(x) {
|
||||
if (x == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -789,14 +789,14 @@ func TestRecursiveFunctions(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let countDown = fn(x) {
|
||||
countDown := fn(x) {
|
||||
if (x == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
return countDown(x - 1);
|
||||
}
|
||||
};
|
||||
let wrapper = fn() {
|
||||
wrapper := fn() {
|
||||
return countDown(1);
|
||||
};
|
||||
wrapper();
|
||||
@@ -805,8 +805,8 @@ func TestRecursiveFunctions(t *testing.T) {
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let wrapper = fn() {
|
||||
let countDown = fn(x) {
|
||||
wrapper := fn() {
|
||||
countDown := fn(x) {
|
||||
if (x == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -828,7 +828,7 @@ func TestRecursiveFibonacci(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let fibonacci = fn(x) {
|
||||
fibonacci := fn(x) {
|
||||
if (x == 0) {
|
||||
return 0;
|
||||
} else {
|
||||
@@ -851,14 +851,14 @@ func TestRecursiveFibonacci(t *testing.T) {
|
||||
func TestIterations(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{"while (false) { }", nil},
|
||||
{"let n = 0; while (n < 10) { let n = n + 1 }; n", 10},
|
||||
{"let n = 10; while (n > 0) { let n = n - 1 }; n", 0},
|
||||
{"let n = 0; while (n < 10) { let n = n + 1 }", nil},
|
||||
{"let n = 10; while (n > 0) { let n = n - 1 }", nil},
|
||||
{"let n = 0; while (n < 10) { n = n + 1 }; n", 10},
|
||||
{"let n = 10; while (n > 0) { n = n - 1 }; n", 0},
|
||||
{"let n = 0; while (n < 10) { n = n + 1 }", nil},
|
||||
{"let n = 10; while (n > 0) { n = n - 1 }", nil},
|
||||
{"n := 0; while (n < 10) { n := n + 1 }; n", 10},
|
||||
{"n := 10; while (n > 0) { n := n - 1 }; n", 0},
|
||||
{"n := 0; while (n < 10) { n := n + 1 }", nil},
|
||||
{"n := 10; while (n > 0) { n := n - 1 }", nil},
|
||||
{"n := 0; while (n < 10) { n = n + 1 }; n", 10},
|
||||
{"n := 10; while (n > 0) { n = n - 1 }; n", 0},
|
||||
{"n := 0; while (n < 10) { n = n + 1 }", nil},
|
||||
{"n := 10; while (n > 0) { n = n - 1 }", nil},
|
||||
}
|
||||
|
||||
runVmTests(t, tests)
|
||||
@@ -866,7 +866,7 @@ func TestIterations(t *testing.T) {
|
||||
|
||||
func TestIndexAssignmentStatements(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{"let xs = [1, 2, 3]; xs[1] = 4; xs[1];", 4},
|
||||
{"xs := [1, 2, 3]; xs[1] = 4; xs[1];", 4},
|
||||
}
|
||||
|
||||
runVmTests(t, tests)
|
||||
@@ -874,20 +874,20 @@ func TestIndexAssignmentStatements(t *testing.T) {
|
||||
|
||||
func TestAssignmentExpressions(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{"let a = 0; a = 5;", nil},
|
||||
{"let a = 0; a = 5; a;", 5},
|
||||
{"let a = 0; a = 5 * 5;", nil},
|
||||
{"let a = 0; a = 5 * 5; a;", 25},
|
||||
{"let a = 0; a = 5; let b = 0; b = a;", nil},
|
||||
{"let a = 0; a = 5; let b = 0; b = a; b;", 5},
|
||||
{"let a = 0; a = 5; let b = 0; b = a; let c = 0; c = a + b + 5;", nil},
|
||||
{"let a = 0; a = 5; let b = 0; b = a; let c = 0; c = a + b + 5; c;", 15},
|
||||
{"let a = 5; let b = a; a = 0;", nil},
|
||||
{"let a = 5; let b = a; a = 0; b;", 5},
|
||||
{"let one = 0; one = 1", nil},
|
||||
{"let one = 0; one = 1; one", 1},
|
||||
{"let one = 0; one = 1; let two = 0; two = 2; one + two", 3},
|
||||
{"let one = 0; one = 1; let two = 0; two = one + one; one + two", 3},
|
||||
{"a := 0; a = 5;", nil},
|
||||
{"a := 0; a = 5; a;", 5},
|
||||
{"a := 0; a = 5 * 5;", nil},
|
||||
{"a := 0; a = 5 * 5; a;", 25},
|
||||
{"a := 0; a = 5; b := 0; b = a;", nil},
|
||||
{"a := 0; a = 5; b := 0; b = a; b;", 5},
|
||||
{"a := 0; a = 5; b := 0; b = a; c := 0; c = a + b + 5;", nil},
|
||||
{"a := 0; a = 5; b := 0; b = a; c := 0; c = a + b + 5; c;", 15},
|
||||
{"a := 5; b := a; a = 0;", nil},
|
||||
{"a := 5; b := a; a = 0; b;", 5},
|
||||
{"one := 0; one = 1", nil},
|
||||
{"one := 0; one = 1; one", 1},
|
||||
{"one := 0; one = 1; two := 0; two = 2; one + two", 3},
|
||||
{"one := 0; one = 1; two := 0; two = one + one; one + two", 3},
|
||||
}
|
||||
|
||||
runVmTests(t, tests)
|
||||
@@ -897,7 +897,7 @@ func TestTailCalls(t *testing.T) {
|
||||
tests := []vmTestCase{
|
||||
{
|
||||
input: `
|
||||
let fact = fn(n, a) {
|
||||
fact := fn(n, a) {
|
||||
if (n == 0) {
|
||||
return a
|
||||
}
|
||||
@@ -912,7 +912,7 @@ func TestTailCalls(t *testing.T) {
|
||||
// without tail recursion optimization this will cause a stack overflow
|
||||
{
|
||||
input: `
|
||||
let iter = fn(n, max) {
|
||||
iter := fn(n, max) {
|
||||
if (n == max) {
|
||||
return n
|
||||
}
|
||||
@@ -971,14 +971,14 @@ func TestIntegration(t *testing.T) {
|
||||
func BenchmarkFibonacci(b *testing.B) {
|
||||
tests := map[string]string{
|
||||
"iterative": `
|
||||
let fib = fn(n) {
|
||||
fib := fn(n) {
|
||||
if (n < 3) {
|
||||
return 1
|
||||
}
|
||||
let a = 1
|
||||
let b = 1
|
||||
let c = 0
|
||||
let i = 0
|
||||
a := 1
|
||||
b := 1
|
||||
c := 0
|
||||
i := 0
|
||||
while (i < n - 2) {
|
||||
c = a + b
|
||||
b = a
|
||||
@@ -991,7 +991,7 @@ func BenchmarkFibonacci(b *testing.B) {
|
||||
fib(35)
|
||||
`,
|
||||
"recursive": `
|
||||
let fib = fn(x) {
|
||||
fib := fn(x) {
|
||||
if (x == 0) {
|
||||
return 0
|
||||
}
|
||||
@@ -1004,7 +1004,7 @@ func BenchmarkFibonacci(b *testing.B) {
|
||||
fib(35)
|
||||
`,
|
||||
"tail-recursive": `
|
||||
let fib = fn(n, a, b) {
|
||||
fib := fn(n, a, b) {
|
||||
if (n == 0) {
|
||||
return a
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user