Everything is a closure
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
11
vm/frame.go
11
vm/frame.go
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
39
vm/vm.go
39
vm/vm.go
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user