bind expression (:=) instead of let
Some checks failed
Build / build (push) Successful in 10m26s
Test / build (push) Failing after 16m44s

This commit is contained in:
Chuck Smith
2024-03-21 17:43:03 -04:00
parent 66d5453ecc
commit 6282075e66
36 changed files with 425 additions and 583 deletions

View File

@@ -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)