compile functions
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user