bind expression (:=) instead of let
This commit is contained in:
@@ -11,6 +11,8 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
type Instructions string
|
||||
|
||||
type compilerTestCase struct {
|
||||
input string
|
||||
expectedConstants []interface{}
|
||||
@@ -210,146 +212,61 @@ func TestBooleanExpressions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestConditionals(t *testing.T) {
|
||||
tests := []compilerTestCase{
|
||||
tests := []compilerTestCase2{
|
||||
{
|
||||
input: `
|
||||
if (true) { 10 }; 3333;
|
||||
`,
|
||||
expectedConstants: []interface{}{10, 3333},
|
||||
expectedInstructions: []code.Instructions{
|
||||
// 0000
|
||||
code.Make(code.OpTrue),
|
||||
// 0001
|
||||
code.Make(code.OpJumpNotTruthy, 10),
|
||||
// 0004
|
||||
code.Make(code.OpConstant, 0),
|
||||
// 0007
|
||||
code.Make(code.OpJump, 11),
|
||||
// 0010
|
||||
code.Make(code.OpNull),
|
||||
// 0011
|
||||
code.Make(code.OpPop),
|
||||
// 0012
|
||||
code.Make(code.OpConstant, 1),
|
||||
// 0015
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
constants: []interface{}{10, 3333},
|
||||
instructions: "0000 LoadTrue\n0001 JumpIfFalse 10\n0004 LoadConstant 0\n0007 Jump 11\n0010 LoadNull\n0011 Pop\n0012 LoadConstant 1\n0015 Pop\n",
|
||||
}, {
|
||||
input: `
|
||||
if (true) { 10 } else { 20 }; 3333;
|
||||
`,
|
||||
expectedConstants: []interface{}{10, 20, 3333},
|
||||
expectedInstructions: []code.Instructions{
|
||||
// 0000
|
||||
code.Make(code.OpTrue),
|
||||
// 0001
|
||||
code.Make(code.OpJumpNotTruthy, 10),
|
||||
// 0004
|
||||
code.Make(code.OpConstant, 0),
|
||||
// 0008
|
||||
code.Make(code.OpJump, 13),
|
||||
// 0011
|
||||
code.Make(code.OpConstant, 1),
|
||||
// 0013
|
||||
code.Make(code.OpPop),
|
||||
// 0014
|
||||
code.Make(code.OpConstant, 2),
|
||||
// 0018
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
constants: []interface{}{10, 20, 3333},
|
||||
instructions: "0000 LoadTrue\n0001 JumpIfFalse 10\n0004 LoadConstant 0\n0007 Jump 13\n0010 LoadConstant 1\n0013 Pop\n0014 LoadConstant 2\n0017 Pop\n",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let x = 0; if (true) { x = 1; }; if (false) { x = 2; }
|
||||
`,
|
||||
expectedConstants: []interface{}{0, 1, 2},
|
||||
expectedInstructions: []code.Instructions{
|
||||
// 0000
|
||||
code.Make(code.OpConstant, 0),
|
||||
// 0003
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
// 0006
|
||||
code.Make(code.OpTrue),
|
||||
// 0007
|
||||
code.Make(code.OpJumpNotTruthy, 19),
|
||||
// 0010
|
||||
code.Make(code.OpConstant, 1),
|
||||
// 0013
|
||||
code.Make(code.OpAssignGlobal, 0),
|
||||
// 0018
|
||||
code.Make(code.OpJump, 20),
|
||||
// 0019
|
||||
code.Make(code.OpNull),
|
||||
// 0020
|
||||
code.Make(code.OpPop),
|
||||
// 0021
|
||||
code.Make(code.OpFalse),
|
||||
// 0022
|
||||
code.Make(code.OpJumpNotTruthy, 34),
|
||||
// 0024
|
||||
code.Make(code.OpConstant, 2),
|
||||
// 0028
|
||||
code.Make(code.OpAssignGlobal, 0),
|
||||
// 0032
|
||||
code.Make(code.OpJump, 35),
|
||||
// 0035
|
||||
code.Make(code.OpNull),
|
||||
// 0036
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
constants: []interface{}{0, 1, 2},
|
||||
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadTrue\n0008 JumpIfFalse 20\n0011 LoadConstant 1\n0014 AssignGlobal 0\n0017 Jump 21\n0020 LoadNull\n0021 Pop\n0022 LoadFalse\n0023 JumpIfFalse 35\n0026 LoadConstant 2\n0029 AssignGlobal 0\n0032 Jump 36\n0035 LoadNull\n0036 Pop\n",
|
||||
},
|
||||
}
|
||||
|
||||
runCompilerTests(t, tests)
|
||||
runCompilerTests2(t, tests)
|
||||
}
|
||||
|
||||
func TestGlobalLetStatements(t *testing.T) {
|
||||
tests := []compilerTestCase{
|
||||
func TestGlobalBindExpressions(t *testing.T) {
|
||||
tests := []compilerTestCase2{
|
||||
{
|
||||
input: `
|
||||
let one = 1;
|
||||
let two = 2;
|
||||
one := 1;
|
||||
two := 2;
|
||||
`,
|
||||
expectedConstants: []interface{}{1, 2},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpConstant, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpConstant, 1),
|
||||
code.Make(code.OpSetGlobal, 1),
|
||||
},
|
||||
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadConstant 1\n0010 BindGlobal 1\n0013 Pop\n",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let one = 1;
|
||||
one := 1;
|
||||
one;
|
||||
`,
|
||||
expectedConstants: []interface{}{1},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpConstant, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpGetGlobal, 0),
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
constants: []interface{}{1},
|
||||
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadGlobal 0\n0010 Pop\n",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let one = 1;
|
||||
let two = one;
|
||||
one := 1;
|
||||
two := one;
|
||||
two;
|
||||
`,
|
||||
expectedConstants: []interface{}{1},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpConstant, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpGetGlobal, 0),
|
||||
code.Make(code.OpSetGlobal, 1),
|
||||
code.Make(code.OpGetGlobal, 1),
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
constants: []interface{}{1},
|
||||
instructions: "0000 LoadConstant 0\n0003 BindGlobal 0\n0006 Pop\n0007 LoadGlobal 0\n0010 BindGlobal 1\n0013 Pop\n0014 LoadGlobal 1\n0017 Pop\n",
|
||||
},
|
||||
}
|
||||
|
||||
runCompilerTests(t, tests)
|
||||
runCompilerTests2(t, tests)
|
||||
}
|
||||
|
||||
func TestStringExpressions(t *testing.T) {
|
||||
@@ -735,95 +652,53 @@ func TestCompilerScopes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestFunctionCalls(t *testing.T) {
|
||||
tests := []compilerTestCase{
|
||||
tests := []compilerTestCase2{
|
||||
{
|
||||
input: `fn() { return 24 }();`,
|
||||
expectedConstants: []interface{}{
|
||||
constants: []interface{}{
|
||||
24,
|
||||
[]code.Instructions{
|
||||
code.Make(code.OpConstant, 0), // The literal "24"
|
||||
code.Make(code.OpReturn),
|
||||
},
|
||||
},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpClosure, 1, 0), // The compiled function
|
||||
code.Make(code.OpCall, 0),
|
||||
code.Make(code.OpPop),
|
||||
Instructions("0000 LoadConstant 0\n0003 Return\n"),
|
||||
},
|
||||
instructions: "0000 MakeClosure 1 0\n0004 Call 0\n0006 Pop\n",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let noArg = fn() { return 24 };
|
||||
noArg := fn() { return 24 };
|
||||
noArg();
|
||||
`,
|
||||
expectedConstants: []interface{}{
|
||||
constants: []interface{}{
|
||||
24,
|
||||
[]code.Instructions{
|
||||
code.Make(code.OpConstant, 0), // The literal "24"
|
||||
code.Make(code.OpReturn),
|
||||
},
|
||||
},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpClosure, 1, 0), // The compiled function
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpGetGlobal, 0),
|
||||
code.Make(code.OpCall, 0),
|
||||
code.Make(code.OpPop),
|
||||
Instructions("0000 SetSelf 0\n0002 LoadConstant 0\n0005 Return\n"),
|
||||
},
|
||||
instructions: "0000 LoadGlobal 0\n0003 MakeClosure 1 1\n0007 BindGlobal 0\n0010 Pop\n0011 LoadGlobal 0\n0014 Call 0\n0016 Pop\n",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let oneArg = fn(a) { return a };
|
||||
oneArg := fn(a) { return a };
|
||||
oneArg(24);
|
||||
`,
|
||||
expectedConstants: []interface{}{
|
||||
[]code.Instructions{
|
||||
code.Make(code.OpGetLocal, 0),
|
||||
code.Make(code.OpReturn),
|
||||
},
|
||||
constants: []interface{}{
|
||||
Instructions("0000 SetSelf 0\n0002 LoadLocal 0\n0004 Return\n"),
|
||||
24,
|
||||
},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpClosure, 0, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpGetGlobal, 0),
|
||||
code.Make(code.OpConstant, 1),
|
||||
code.Make(code.OpCall, 1),
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
instructions: "0000 LoadGlobal 0\n0003 MakeClosure 0 1\n0007 BindGlobal 0\n0010 Pop\n0011 LoadGlobal 0\n0014 LoadConstant 1\n0017 Call 1\n0019 Pop\n",
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let manyArg = fn(a, b, c) { a; b; return c };
|
||||
manyArg := fn(a, b, c) { a; b; return c };
|
||||
manyArg(24, 25, 26);
|
||||
`,
|
||||
expectedConstants: []interface{}{
|
||||
[]code.Instructions{
|
||||
code.Make(code.OpGetLocal, 0),
|
||||
code.Make(code.OpPop),
|
||||
code.Make(code.OpGetLocal, 1),
|
||||
code.Make(code.OpPop),
|
||||
code.Make(code.OpGetLocal, 2),
|
||||
code.Make(code.OpReturn),
|
||||
},
|
||||
constants: []interface{}{
|
||||
Instructions("0000 SetSelf 0\n0002 LoadLocal 0\n0004 Pop\n0005 LoadLocal 1\n0007 Pop\n0008 LoadLocal 2\n0010 Return\n"),
|
||||
24,
|
||||
25,
|
||||
26,
|
||||
},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpClosure, 0, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpGetGlobal, 0),
|
||||
code.Make(code.OpConstant, 1),
|
||||
code.Make(code.OpConstant, 2),
|
||||
code.Make(code.OpConstant, 3),
|
||||
code.Make(code.OpCall, 3),
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
instructions: "0000 LoadGlobal 0\n0003 MakeClosure 0 1\n0007 BindGlobal 0\n0010 Pop\n0011 LoadGlobal 0\n0014 LoadConstant 1\n0017 LoadConstant 2\n0020 LoadConstant 3\n0023 Call 3\n0025 Pop\n",
|
||||
},
|
||||
}
|
||||
|
||||
runCompilerTests(t, tests)
|
||||
runCompilerTests2(t, tests)
|
||||
}
|
||||
|
||||
func TestAssignmentExpressions(t *testing.T) {
|
||||
@@ -1240,10 +1115,11 @@ func testConstants2(t *testing.T, expected []interface{}, actual []object.Object
|
||||
for i, constant := range expected {
|
||||
switch constant := constant.(type) {
|
||||
|
||||
case []code.Instructions:
|
||||
fn, ok := actual[i].(*object.CompiledFunction)
|
||||
assert.True(ok)
|
||||
assert.Equal(constant, fn.Instructions.String())
|
||||
case Instructions:
|
||||
assert.Equal(
|
||||
string(constant),
|
||||
actual[i].(*object.CompiledFunction).Instructions.String(),
|
||||
)
|
||||
|
||||
case string:
|
||||
assert.Equal(constant, actual[i].(*object.String).Value)
|
||||
|
||||
Reference in New Issue
Block a user