From 8caeaca559af6d119a62af0fd2d9f0a7cf7d5b4a Mon Sep 17 00:00:00 2001 From: Chuck Smith Date: Tue, 20 Feb 2024 16:24:59 -0500 Subject: [PATCH] VM globals --- compiler/compiler.go | 7 +++++++ repl/repl.go | 12 ++++++++++-- vm/vm.go | 27 +++++++++++++++++++++++++++ vm/vm_test.go | 10 ++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index c3de4af..dcbd62b 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -32,6 +32,13 @@ func New() *Compiler { } } +func NewWithState(s *SymbolTable, constants []object.Object) *Compiler { + compiler := New() + compiler.symbolTable = s + compiler.constants = constants + return compiler +} + func (c *Compiler) Compile(node ast.Node) error { switch node := node.(type) { case *ast.Program: diff --git a/repl/repl.go b/repl/repl.go index 2c821ff..e1db9f7 100644 --- a/repl/repl.go +++ b/repl/repl.go @@ -6,6 +6,7 @@ import ( "io" "monkey/compiler" "monkey/lexer" + "monkey/object" "monkey/parser" "monkey/vm" ) @@ -15,6 +16,10 @@ const PROMPT = ">> " func Start(in io.Reader, out io.Writer) { scanner := bufio.NewScanner(in) + constants := []object.Object{} + globals := make([]object.Object, vm.GlobalsSize) + symbolTable := compiler.NewSymbolTable() + for { fmt.Fprintf(out, PROMPT) scanned := scanner.Scan() @@ -32,14 +37,17 @@ func Start(in io.Reader, out io.Writer) { continue } - comp := compiler.New() + comp := compiler.NewWithState(symbolTable, constants) err := comp.Compile(program) if err != nil { fmt.Fprintf(out, "Woops! Compilation failed:\n %s\n", err) continue } - machine := vm.New(comp.Bytecode()) + code := comp.Bytecode() + constants = code.Constants + + machine := vm.NewWithGlobalState(comp.Bytecode(), globals) err = machine.Run() if err != nil { fmt.Fprintf(out, "Woops! Executing bytecode failed:\n %s\n", err) diff --git a/vm/vm.go b/vm/vm.go index fd6e449..01803bb 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -8,6 +8,7 @@ import ( ) const StackSize = 2048 +const GlobalsSize = 65536 var Null = &object.Null{} var True = &object.Boolean{Value: true} @@ -19,6 +20,8 @@ type VM struct { stack []object.Object sp int // Always points to the next value. Top of stack is stack[sp-1] + + globals []object.Object } func New(bytecode *compiler.Bytecode) *VM { @@ -28,9 +31,17 @@ func New(bytecode *compiler.Bytecode) *VM { stack: make([]object.Object, StackSize), sp: 0, + + globals: make([]object.Object, GlobalsSize), } } +func NewWithGlobalState(bytecode *compiler.Bytecode, s []object.Object) *VM { + vm := New(bytecode) + vm.globals = s + return vm +} + func (vm *VM) LastPoppedStackElem() object.Object { return vm.stack[vm.sp] } @@ -106,6 +117,22 @@ func (vm *VM) Run() error { if err != nil { return err } + + case code.OpSetGlobal: + globalIndex := code.ReadUint16(vm.instructions[ip+1:]) + ip += 2 + + vm.globals[globalIndex] = vm.pop() + + case code.OpGetGlobal: + globalIndex := code.ReadUint16(vm.instructions[ip+1:]) + ip += 2 + + err := vm.push(vm.globals[globalIndex]) + if err != nil { + return err + } + } } diff --git a/vm/vm_test.go b/vm/vm_test.go index 3821b80..757ab55 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -165,3 +165,13 @@ func TestConditionals(t *testing.T) { runVmTests(t, tests) } + +func TestGlobalLetStatements(t *testing.T) { + tests := []vmTestCase{ + {"let one = 1; one", 1}, + {"let one = 1; let two = 2; one + two", 3}, + {"let one = 1; let two = one + one; one + two", 3}, + } + + runVmTests(t, tests) +}