compile functions
Some checks failed
Build / build (push) Failing after 1m57s
Test / build (push) Successful in 2m15s

This commit is contained in:
Chuck Smith
2024-02-28 16:57:01 -05:00
parent 4185926e3e
commit e56fb40f83
5 changed files with 320 additions and 34 deletions

View File

@@ -421,6 +421,171 @@ func TestIndexExpressions(t *testing.T) {
code.Make(code.OpPop),
},
},
{
input: `fn() { 1; 2 }`,
expectedConstants: []interface{}{
1,
2,
[]code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpPop),
code.Make(code.OpConstant, 1),
code.Make(code.OpReturnValue),
},
},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 2),
code.Make(code.OpPop),
},
},
}
runCompilerTests(t, tests)
}
func TestFunctions(t *testing.T) {
tests := []compilerTestCase{
{
input: "fn() { return 5 + 10 }",
expectedConstants: []interface{}{
5,
10,
[]code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpConstant, 1),
code.Make(code.OpAdd),
code.Make(code.OpReturnValue),
},
},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 2),
code.Make(code.OpPop),
},
},
{
input: `fn() { 5 + 10 }`,
expectedConstants: []interface{}{
5,
10,
[]code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpConstant, 1),
code.Make(code.OpAdd),
code.Make(code.OpReturnValue),
},
},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 2),
code.Make(code.OpPop),
},
},
}
runCompilerTests(t, tests)
}
func TestFunctionsWithoutReturnValue(t *testing.T) {
tests := []compilerTestCase{
{
input: `fn() { }`,
expectedConstants: []interface{}{
[]code.Instructions{
code.Make(code.OpReturn),
},
},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpPop),
},
},
}
runCompilerTests(t, tests)
}
func TestCompilerScopes(t *testing.T) {
compiler := New()
if compiler.scopeIndex != 0 {
t.Errorf("scopeIndex wrong. got=%d, want=%d", compiler.scopeIndex, 0)
}
compiler.emit(code.OpMul)
compiler.enterScope()
if compiler.scopeIndex != 1 {
t.Errorf("scopeIndex wrong. got=%d, want=%d", compiler.scopeIndex, 1)
}
compiler.emit(code.OpSub)
if len(compiler.scopes[compiler.scopeIndex].instructions) != 1 {
t.Errorf("instructions length is wrong. got=%d", len(compiler.scopes[compiler.scopeIndex].instructions))
}
last := compiler.scopes[compiler.scopeIndex].lastInstruction
if last.Opcode != code.OpSub {
t.Errorf("lastInstruction.OpCode wrong. got=%d, want=%d", last.Opcode, code.OpSub)
}
compiler.leaveScope()
if compiler.scopeIndex != 0 {
t.Errorf("scopeIndex wrong. got=%d, want=%d", compiler.scopeIndex, 0)
}
compiler.emit(code.OpAdd)
if len(compiler.scopes[compiler.scopeIndex].instructions) != 2 {
t.Errorf("instructions length is wrong. got=%d", len(compiler.scopes[compiler.scopeIndex].instructions))
}
last = compiler.scopes[compiler.scopeIndex].lastInstruction
if last.Opcode != code.OpAdd {
t.Errorf("lastInstruction.OpCode wrong. got=%d, want=%d", last.Opcode, code.OpSub)
}
previous := compiler.scopes[compiler.scopeIndex].previousInstruction
if previous.Opcode != code.OpMul {
t.Errorf("previousInstruction.OpCode wrong. got=%d, want=%d", last.Opcode, code.OpMul)
}
}
func TestFunctionCalls(t *testing.T) {
tests := []compilerTestCase{
{
input: `fn() { 24 }();`,
expectedConstants: []interface{}{
24,
[]code.Instructions{
code.Make(code.OpConstant, 0), // The literal "24"
code.Make(code.OpReturnValue),
},
},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 1), // The compiled function
code.Make(code.OpCall),
code.Make(code.OpPop),
},
},
{
input: `
let noArg = fn() { 24 };
noArg();
`,
expectedConstants: []interface{}{
24,
[]code.Instructions{
code.Make(code.OpConstant, 0), // The literal "24"
code.Make(code.OpReturnValue),
},
},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 1), // The compiled function
code.Make(code.OpSetGlobal, 0),
code.Make(code.OpGetGlobal, 0),
code.Make(code.OpCall),
code.Make(code.OpPop),
},
},
}
runCompilerTests(t, tests)
@@ -502,6 +667,17 @@ func testConstants(t *testing.T, expected []interface{}, actual []object.Object)
if err != nil {
return fmt.Errorf("constant %d = testStringObject failed : %s", i, err)
}
case []code.Instructions:
fn, ok := actual[i].(*object.CompiledFunction)
if !ok {
return fmt.Errorf("constant %d - not a function: %T", i, actual[i])
}
err := testInstructions(constant, fn.Instructions)
if err != nil {
return fmt.Errorf("constant %d = testInstructions failed: %s", i, err)
}
}
}
return nil