Everything is a closure
Some checks failed
Build / build (push) Failing after 1m56s
Test / build (push) Failing after 2m33s

This commit is contained in:
Chuck Smith
2024-03-13 17:08:17 -04:00
parent e373e9f68a
commit 78b560e457
7 changed files with 132 additions and 81 deletions

View File

@@ -38,6 +38,7 @@ const (
OpGetLocal OpGetLocal
OpSetLocal OpSetLocal
OpGetBuiltin OpGetBuiltin
OpClosure
) )
type Definition struct { type Definition struct {
@@ -73,6 +74,7 @@ var definitions = map[Opcode]*Definition{
OpGetLocal: {"OpGetLocal", []int{1}}, OpGetLocal: {"OpGetLocal", []int{1}},
OpSetLocal: {"OpSetLocal", []int{1}}, OpSetLocal: {"OpSetLocal", []int{1}},
OpGetBuiltin: {"OpGetBuiltin", []int{1}}, OpGetBuiltin: {"OpGetBuiltin", []int{1}},
OpClosure: {"OpClosure", []int{2, 1}},
} }
func Lookup(op byte) (*Definition, error) { func Lookup(op byte) (*Definition, error) {
@@ -146,6 +148,9 @@ func (ins Instructions) fmtInstruction(def *Definition, operands []int) string {
return def.Name return def.Name
case 1: case 1:
return fmt.Sprintf("%s %d", def.Name, operands[0]) return fmt.Sprintf("%s %d", def.Name, operands[0])
case 2:
return fmt.Sprintf("%s %d %d", def.Name, operands[0], operands[1])
} }
return fmt.Sprintf("ERROR: unhandled operandCount for %s\n", def.Name) return fmt.Sprintf("ERROR: unhandled operandCount for %s\n", def.Name)

View File

@@ -11,6 +11,7 @@ func TestMake(t *testing.T) {
{OpConstant, []int{65534}, []byte{byte(OpConstant), 255, 254}}, {OpConstant, []int{65534}, []byte{byte(OpConstant), 255, 254}},
{OpAdd, []int{}, []byte{byte(OpAdd)}}, {OpAdd, []int{}, []byte{byte(OpAdd)}},
{OpGetLocal, []int{255}, []byte{byte(OpGetLocal), 255}}, {OpGetLocal, []int{255}, []byte{byte(OpGetLocal), 255}},
{OpClosure, []int{65534, 255}, []byte{byte(OpClosure), 255, 254, 255}},
} }
for _, tt := range test { for _, tt := range test {
@@ -34,12 +35,14 @@ func TestInstructions(t *testing.T) {
Make(OpGetLocal, 1), Make(OpGetLocal, 1),
Make(OpConstant, 2), Make(OpConstant, 2),
Make(OpConstant, 65535), Make(OpConstant, 65535),
Make(OpClosure, 65535, 255),
} }
expected := `0000 OpAdd expected := `0000 OpAdd
0001 OpGetLocal 1 0001 OpGetLocal 1
0003 OpConstant 2 0003 OpConstant 2
0006 OpConstant 65535 0006 OpConstant 65535
0009 OpClosure 65535 255
` `
concatted := Instructions{} concatted := Instructions{}
@@ -60,6 +63,7 @@ func TestReadOperands(t *testing.T) {
}{ }{
{OpConstant, []int{65535}, 2}, {OpConstant, []int{65535}, 2},
{OpGetLocal, []int{255}, 1}, {OpGetLocal, []int{255}, 1},
{OpClosure, []int{65535, 255}, 3},
} }
for _, tt := range tests { for _, tt := range tests {

View File

@@ -292,7 +292,9 @@ func (c *Compiler) Compile(node ast.Node) error {
NumLocals: numLocals, NumLocals: numLocals,
NumParameters: len(node.Parameters), NumParameters: len(node.Parameters),
} }
c.emit(code.OpConstant, c.addConstant(compiledFn))
fnIndex := c.addConstant(compiledFn)
c.emit(code.OpClosure, fnIndex, 0)
case *ast.ReturnStatement: case *ast.ReturnStatement:
err := c.Compile(node.ReturnValue) err := c.Compile(node.ReturnValue)

View File

@@ -421,23 +421,6 @@ func TestIndexExpressions(t *testing.T) {
code.Make(code.OpPop), 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) runCompilerTests(t, tests)
@@ -458,7 +441,7 @@ func TestFunctions(t *testing.T) {
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 2), code.Make(code.OpClosure, 2, 0),
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
}, },
@@ -475,7 +458,24 @@ func TestFunctions(t *testing.T) {
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 2), code.Make(code.OpClosure, 2, 0),
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.OpClosure, 2, 0),
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
}, },
@@ -494,7 +494,7 @@ func TestFunctionsWithoutReturnValue(t *testing.T) {
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0), code.Make(code.OpClosure, 0, 0),
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
}, },
@@ -561,7 +561,7 @@ func TestFunctionCalls(t *testing.T) {
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 1), // The compiled function code.Make(code.OpClosure, 1, 0), // The compiled function
code.Make(code.OpCall, 0), code.Make(code.OpCall, 0),
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
@@ -579,7 +579,7 @@ func TestFunctionCalls(t *testing.T) {
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 1), // The compiled function code.Make(code.OpClosure, 1, 0), // The compiled function
code.Make(code.OpSetGlobal, 0), code.Make(code.OpSetGlobal, 0),
code.Make(code.OpGetGlobal, 0), code.Make(code.OpGetGlobal, 0),
code.Make(code.OpCall, 0), code.Make(code.OpCall, 0),
@@ -599,7 +599,7 @@ func TestFunctionCalls(t *testing.T) {
24, 24,
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0), code.Make(code.OpClosure, 0, 0),
code.Make(code.OpSetGlobal, 0), code.Make(code.OpSetGlobal, 0),
code.Make(code.OpGetGlobal, 0), code.Make(code.OpGetGlobal, 0),
code.Make(code.OpConstant, 1), code.Make(code.OpConstant, 1),
@@ -626,7 +626,7 @@ func TestFunctionCalls(t *testing.T) {
26, 26,
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0), code.Make(code.OpClosure, 0, 0),
code.Make(code.OpSetGlobal, 0), code.Make(code.OpSetGlobal, 0),
code.Make(code.OpGetGlobal, 0), code.Make(code.OpGetGlobal, 0),
code.Make(code.OpConstant, 1), code.Make(code.OpConstant, 1),
@@ -658,7 +658,7 @@ func TestLetStatementScopes(t *testing.T) {
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0), code.Make(code.OpConstant, 0),
code.Make(code.OpSetGlobal, 0), code.Make(code.OpSetGlobal, 0),
code.Make(code.OpConstant, 1), code.Make(code.OpClosure, 1, 0),
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
}, },
@@ -679,7 +679,7 @@ func TestLetStatementScopes(t *testing.T) {
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 1), code.Make(code.OpClosure, 1, 0),
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
}, },
@@ -706,7 +706,47 @@ func TestLetStatementScopes(t *testing.T) {
}, },
}, },
expectedInstructions: []code.Instructions{ expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 2), code.Make(code.OpClosure, 2, 0),
code.Make(code.OpPop),
},
},
}
runCompilerTests(t, tests)
}
func TestBuiltins(t *testing.T) {
tests := []compilerTestCase{
{
input: `
len([]);
push([], 1);
`,
expectedConstants: []interface{}{1},
expectedInstructions: []code.Instructions{
code.Make(code.OpGetBuiltin, 0),
code.Make(code.OpArray, 0),
code.Make(code.OpCall, 1),
code.Make(code.OpPop),
code.Make(code.OpGetBuiltin, 5),
code.Make(code.OpArray, 0),
code.Make(code.OpConstant, 0),
code.Make(code.OpCall, 2),
code.Make(code.OpPop),
},
},
{
input: `fn() { len([]) }`,
expectedConstants: []interface{}{
[]code.Instructions{
code.Make(code.OpGetBuiltin, 0),
code.Make(code.OpArray, 0),
code.Make(code.OpCall, 1),
code.Make(code.OpReturnValue),
},
},
expectedInstructions: []code.Instructions{
code.Make(code.OpClosure, 0, 0),
code.Make(code.OpPop), code.Make(code.OpPop),
}, },
}, },
@@ -832,43 +872,3 @@ func testStringObject(expected string, actual object.Object) error {
return nil return nil
} }
func TestBuiltins(t *testing.T) {
tests := []compilerTestCase{
{
input: `
len([]);
push([], 1);
`,
expectedConstants: []interface{}{1},
expectedInstructions: []code.Instructions{
code.Make(code.OpGetBuiltin, 0),
code.Make(code.OpArray, 0),
code.Make(code.OpCall, 1),
code.Make(code.OpPop),
code.Make(code.OpGetBuiltin, 5),
code.Make(code.OpArray, 0),
code.Make(code.OpConstant, 0),
code.Make(code.OpCall, 2),
code.Make(code.OpPop),
},
},
{
input: `fn() { len([]) }`,
expectedConstants: []interface{}{
[]code.Instructions{
code.Make(code.OpGetBuiltin, 0),
code.Make(code.OpArray, 0),
code.Make(code.OpCall, 1),
code.Make(code.OpReturnValue),
},
},
expectedInstructions: []code.Instructions{
code.Make(code.OpConstant, 0),
code.Make(code.OpPop),
},
},
}
runCompilerTests(t, tests)
}

View File

@@ -22,7 +22,8 @@ const (
BUILTIN_OBJ = "BUILTIN" BUILTIN_OBJ = "BUILTIN"
ARRAY_OBJ = "ARRAY" ARRAY_OBJ = "ARRAY"
HASH_OBJ = "HASH" HASH_OBJ = "HASH"
COMPILED_FUNCTION_OBJ = "COMPILED_FUNCTION_OBJ " COMPILED_FUNCTION_OBJ = "COMPILED_FUNCTION"
CLOSURE_OBJ = "CLOSURE"
) )
type Object interface { type Object interface {
@@ -235,3 +236,16 @@ func (cf *CompiledFunction) Type() ObjectType {
func (cf *CompiledFunction) Inspect() string { func (cf *CompiledFunction) Inspect() string {
return fmt.Sprintf("CompiledFunction[%p]", cf) return fmt.Sprintf("CompiledFunction[%p]", cf)
} }
type Closure struct {
Fn *CompiledFunction
Free []Object
}
func (c *Closure) Type() ObjectType {
return CLOSURE_OBJ
}
func (c *Closure) Inspect() string {
return fmt.Sprintf("Closure[%p]", c)
}

View File

@@ -6,15 +6,18 @@ import (
) )
type Frame struct { type Frame struct {
fn *object.CompiledFunction cl *object.Closure
ip int ip int
basePointer int basePointer int
} }
func NewFrame(fn *object.CompiledFunction, basePointer int) *Frame { func NewFrame(cl *object.Closure, basePointer int) *Frame {
return &Frame{fn: fn, ip: -1, basePointer: basePointer} return &Frame{
cl: cl,
ip: -1,
basePointer: basePointer}
} }
func (f *Frame) Instructions() code.Instructions { func (f *Frame) Instructions() code.Instructions {
return f.fn.Instructions return f.cl.Fn.Instructions
} }

View File

@@ -29,7 +29,8 @@ type VM struct {
func New(bytecode *compiler.Bytecode) *VM { func New(bytecode *compiler.Bytecode) *VM {
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions} mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
mainFrame := NewFrame(mainFn, 0) mainClosure := &object.Closure{Fn: mainFn}
mainFrame := NewFrame(mainClosure, 0)
frames := make([]*Frame, MaxFrames) frames := make([]*Frame, MaxFrames)
frames[0] = mainFrame frames[0] = mainFrame
@@ -260,6 +261,17 @@ func (vm *VM) Run() error {
if err != nil { if err != nil {
return err return err
} }
case code.OpClosure:
constIndex := code.ReadUint16(ins[ip+1:])
_ = code.ReadUint8(ins[ip+3:])
vm.currentFrame().ip += 3
err := vm.pushClosure(int(constIndex))
if err != nil {
return err
}
} }
} }
@@ -479,8 +491,8 @@ func (vm *VM) executeMinusOperator() error {
func (vm *VM) executeCall(numArgs int) error { func (vm *VM) executeCall(numArgs int) error {
callee := vm.stack[vm.sp-1-numArgs] callee := vm.stack[vm.sp-1-numArgs]
switch callee := callee.(type) { switch callee := callee.(type) {
case *object.CompiledFunction: case *object.Closure:
return vm.callFunction(callee, numArgs) return vm.callClosure(callee, numArgs)
case *object.Builtin: case *object.Builtin:
return vm.callBuiltin(callee, numArgs) return vm.callBuiltin(callee, numArgs)
default: default:
@@ -488,14 +500,14 @@ func (vm *VM) executeCall(numArgs int) error {
} }
} }
func (vm *VM) callFunction(fn *object.CompiledFunction, numArgs int) error { func (vm *VM) callClosure(cl *object.Closure, numArgs int) error {
if numArgs != fn.NumParameters { if numArgs != cl.Fn.NumParameters {
return fmt.Errorf("wrong number of arguments: want=%d, got=%d", fn.NumParameters, numArgs) return fmt.Errorf("wrong number of arguments: want=%d, got=%d", cl.Fn.NumParameters, numArgs)
} }
frame := NewFrame(fn, vm.sp-numArgs) frame := NewFrame(cl, vm.sp-numArgs)
vm.pushFrame(frame) vm.pushFrame(frame)
vm.sp = frame.basePointer + fn.NumLocals vm.sp = frame.basePointer + cl.Fn.NumLocals
return nil return nil
} }
@@ -521,6 +533,17 @@ func (vm *VM) callBuiltin(builtin *object.Builtin, numArgs int) error {
return nil return nil
} }
func (vm *VM) pushClosure(constIndex int) error {
constant := vm.constants[constIndex]
function, ok := constant.(*object.CompiledFunction)
if !ok {
return fmt.Errorf("not a function %+v", constant)
}
closure := &object.Closure{Fn: function}
return vm.push(closure)
}
func nativeBoolToBooleanObject(input bool) *object.Boolean { func nativeBoolToBooleanObject(input bool) *object.Boolean {
if input { if input {
return True return True