Pārlūkot izejas kodu

Functions Local Bindings -- in vm

runningwater 3 gadi atpakaļ
vecāks
revīzija
3ca2e3ce0b
5 mainītis faili ar 81 papildinājumiem un 11 dzēšanām
  1. 5 1
      compiler/compiler.go
  2. 1 0
      object/object.go
  3. 9 4
      vm/frame.go
  4. 23 6
      vm/vm.go
  5. 43 0
      vm/vm_test.go

+ 5 - 1
compiler/compiler.go

@@ -262,9 +262,13 @@ func (c *Compiler) Compile(node ast.Node) error {
 		if !c.lastInstructionIs(code.OpReturnValue) {
 			c.emit(code.OpReturn)
 		}
+		numLocals := c.symbolTable.numDefinitions
 		instructions := c.leaveScope()
 
-		compiledFn := &object.CompileFunction{Instructions: instructions}
+		compiledFn := &object.CompileFunction{
+			Instructions: instructions,
+			NumLocals:    numLocals,
+		}
 		c.emit(code.OpConstant, c.addConstant(compiledFn))
 	case *ast.CallExpression:
 		err := c.Compile(node.Function)

+ 1 - 0
object/object.go

@@ -184,6 +184,7 @@ type Hashtable interface {
 
 type CompileFunction struct {
 	Instructions code.Instructions
+	NumLocals    int // number of local bindings this functions
 }
 
 func (cf *CompileFunction) Type() ObjType { return CompiledFunctionObj }

+ 9 - 4
vm/frame.go

@@ -11,12 +11,17 @@ import (
 )
 
 type Frame struct {
-	fn *object.CompileFunction // points to the compiled function
-	ip int                     // the instruction pointer in this frame
+	fn          *object.CompileFunction // points to the compiled function
+	ip          int                     // the instruction pointer in this frame
+	basePointer int
 }
 
-func NewFrame(fn *object.CompileFunction) *Frame {
-	return &Frame{fn: fn, ip: -1}
+func NewFrame(fn *object.CompileFunction, basePointer int) *Frame {
+	return &Frame{
+		fn:          fn,
+		ip:          -1,
+		basePointer: basePointer,
+	}
 }
 func (f *Frame) Instructions() code.Instructions {
 	return f.fn.Instructions

+ 23 - 6
vm/vm.go

@@ -35,7 +35,7 @@ type VM struct {
 
 func New(byteCode *compiler.ByteCode) *VM {
 	mainFn := &object.CompileFunction{Instructions: byteCode.Instructions}
-	mainFrame := NewFrame(mainFn)
+	mainFrame := NewFrame(mainFn, 0)
 
 	frames := make([]*Frame, MaxFrames)
 	frames[0] = mainFrame
@@ -189,26 +189,43 @@ func (vm *VM) Run() error {
 			if !ok {
 				return fmt.Errorf("calling non-function")
 			}
-			frame := NewFrame(fn)
+			frame := NewFrame(fn, vm.sp)
 			vm.pushFrame(frame)
+			vm.sp = frame.basePointer + fn.NumLocals
 		case code.OpReturnValue:
 			returnValue := vm.pop()
 
-			vm.popFrame()
-			vm.pop()
+			frame := vm.popFrame()
+			vm.sp = frame.basePointer - 1
 
 			err := vm.push(returnValue)
 			if err != nil {
 				return err
 			}
 		case code.OpReturn:
-			vm.popFrame()
-			vm.pop()
+			frame := vm.popFrame()
+			vm.sp = frame.basePointer - 1
 
 			err := vm.push(Null)
 			if err != nil {
 				return err
 			}
+		case code.OpSetLocal:
+			localIndex := code.ReadUint8(ins[ip+1:])
+			vm.currentFrame().ip += 1
+
+			frame := vm.currentFrame()
+			vm.stack[frame.basePointer+int(localIndex)] = vm.pop()
+		case code.OpGetLocal:
+			localIndex := code.ReadUint8(ins[ip+1:])
+			vm.currentFrame().ip += 1
+
+			frame := vm.currentFrame()
+
+			err := vm.push(vm.stack[frame.basePointer+int(localIndex)])
+			if err != nil {
+				return err
+			}
 		}
 	}
 

+ 43 - 0
vm/vm_test.go

@@ -189,6 +189,49 @@ func TestFirstClassFunctions(t *testing.T) {
 	runVmTests(t, tests)
 }
 
+func TestCallingFunctionsWithBindings(t *testing.T) {
+	tests := []vmTestCase{
+		{
+			input:    `let one = fn () {let one = 1; one;}; one();`,
+			expected: 1,
+		},
+		{
+			input: `
+			let oneAndTwo = fn() { let one = 1; let two = 2; one + two;};
+			oneAndTwo();
+			`,
+			expected: 3,
+		},
+		{
+			input: `
+			let oneAndTwo = fn() {let one = 1; let two = 2; one + two;};
+			let threeAndFour = fn () {let three = 3; let four = 4; three + four; };
+			oneAndTwo() + threeAndFour();
+			`,
+			expected: 10,
+		},
+		{
+			input: `
+			let firstFoobar = fn() { let foobar = 50; foobar; };
+        	let secondFoobar = fn() { let foobar = 100; foobar; };
+        	firstFoobar() + secondFoobar();
+			`,
+			expected: 150,
+		},
+		{
+			input: `
+			let globalSeed = 50;
+			let minusOne = fn () { let num = 1; globalSeed - num; };
+			let minusTwo = fn () { let num = 2; globalSeed - num; };
+			minusOne() + minusTwo();
+			`,
+			expected: 97,
+		},
+	}
+
+	runVmTests(t, tests)
+}
+
 func runVmTests(t *testing.T, tests []vmTestCase) {
 	t.Helper()