module support
Some checks failed
Build / build (push) Successful in 10m22s
Test / build (push) Failing after 15m54s

This commit is contained in:
Chuck Smith
2024-03-26 16:49:38 -04:00
parent 6d234099d1
commit 110152a139
21 changed files with 541 additions and 100 deletions

151
vm/vm.go
View File

@@ -2,12 +2,17 @@ package vm
import (
"fmt"
"io/ioutil"
"log"
"monkey/builtins"
"monkey/code"
"monkey/compiler"
"monkey/lexer"
"monkey/object"
"monkey/parser"
"monkey/utils"
"strings"
"unicode"
)
const StackSize = 2048
@@ -18,16 +23,88 @@ var Null = &object.Null{}
var True = &object.Boolean{Value: true}
var False = &object.Boolean{Value: false}
// ExecModule compiles the named module and returns a *object.Module object
func ExecModule(name string, state *VMState) (object.Object, error) {
filename := utils.FindModule(name)
if filename == "" {
return nil, fmt.Errorf("ImportError: no module named '%s'", name)
}
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("IOError: error reading module '%s': %s", name, err)
}
l := lexer.New(string(b))
p := parser.New(l)
module := p.ParseProgram()
if len(p.Errors()) != 0 {
return nil, fmt.Errorf("ParseError: %s", p.Errors())
}
c := compiler.NewWithState(state.Symbols, state.Constants)
err = c.Compile(module)
if err != nil {
return nil, fmt.Errorf("CompileError: %s", err)
}
code := c.Bytecode()
state.Constants = code.Constants
machine := NewWithState(code, state)
err = machine.Run()
if err != nil {
return nil, fmt.Errorf("RuntimeError: error loading module '%s'", err)
}
return state.ExportedHash(), nil
}
type VMState struct {
Constants []object.Object
Globals []object.Object
Symbols *compiler.SymbolTable
}
func NewVMState() *VMState {
symbolTable := compiler.NewSymbolTable()
for i, builtin := range builtins.BuiltinsIndex {
symbolTable.DefineBuiltin(i, builtin.Name)
}
return &VMState{
Constants: []object.Object{},
Globals: make([]object.Object, GlobalsSize),
Symbols: symbolTable,
}
}
// exported binding in the vm state. That is every binding that starts with a
// capital letter. This is used by the module import system to wrap up the
// compiled and evaulated module into an object.
func (s *VMState) ExportedHash() *object.Hash {
pairs := make(map[object.HashKey]object.HashPair)
for name, symbol := range s.Symbols.Store {
if unicode.IsUpper(rune(name[0])) {
if symbol.Scope == compiler.GlobalScope {
obj := s.Globals[symbol.Index]
s := &object.String{Value: name}
pairs[s.HashKey()] = object.HashPair{Key: s, Value: obj}
}
}
}
return &object.Hash{Pairs: pairs}
}
type VM struct {
Debug bool
constants []object.Object
state *VMState
stack []object.Object
sp int // Always points to the next value. Top of stack is stack[sp-1]
globals []object.Object
frames []*Frame
framesIndex int
}
@@ -40,23 +117,37 @@ func New(bytecode *compiler.Bytecode) *VM {
frames := make([]*Frame, MaxFrames)
frames[0] = mainFrame
state := NewVMState()
state.Constants = bytecode.Constants
return &VM{
constants: bytecode.Constants,
state: state,
stack: make([]object.Object, StackSize),
sp: 0,
globals: make([]object.Object, GlobalsSize),
frames: frames,
framesIndex: 1,
}
}
func NewWithGlobalState(bytecode *compiler.Bytecode, s []object.Object) *VM {
vm := New(bytecode)
vm.globals = s
return vm
func NewWithState(bytecode *compiler.Bytecode, state *VMState) *VM {
mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions}
mainClosure := &object.Closure{Fn: mainFn}
mainFrame := NewFrame(mainClosure, 0)
frames := make([]*Frame, MaxFrames)
frames[0] = mainFrame
return &VM{
state: state,
frames: frames,
framesIndex: 1,
stack: make([]object.Object, StackSize),
sp: 0,
}
}
func (vm *VM) currentFrame() *Frame {
@@ -73,6 +164,24 @@ func (vm *VM) popFrame() *Frame {
return vm.frames[vm.framesIndex]
}
func (vm *VM) loadModule(name object.Object) error {
s, ok := name.(*object.String)
if !ok {
return fmt.Errorf(
"TypeError: import() expected argument #1 to be `str` got `%s`",
name.Type(),
)
}
attrs, err := ExecModule(s.Value, vm.state)
if err != nil {
return err
}
module := &object.Module{Name: s.Value, Attrs: attrs}
return vm.push(module)
}
func (vm *VM) LastPoppedStackElem() object.Object {
return vm.stack[vm.sp]
}
@@ -108,7 +217,7 @@ func (vm *VM) Run() error {
constIndex := code.ReadUint16(ins[ip+1:])
vm.currentFrame().ip += 2
err := vm.push(vm.constants[constIndex])
err := vm.push(vm.state.Constants[constIndex])
if err != nil {
return err
}
@@ -185,9 +294,9 @@ func (vm *VM) Run() error {
ref := vm.pop()
if immutable, ok := ref.(object.Immutable); ok {
vm.globals[globalIndex] = immutable.Clone()
vm.state.Globals[globalIndex] = immutable.Clone()
} else {
vm.globals[globalIndex] = ref
vm.state.Globals[globalIndex] = ref
}
err := vm.push(Null)
@@ -198,7 +307,7 @@ func (vm *VM) Run() error {
case code.OpAssignGlobal:
globalIndex := code.ReadUint16(ins[ip+1:])
vm.currentFrame().ip += 2
vm.globals[globalIndex] = vm.pop()
vm.state.Globals[globalIndex] = vm.pop()
err := vm.push(Null)
if err != nil {
@@ -221,7 +330,7 @@ func (vm *VM) Run() error {
globalIndex := code.ReadUint16(ins[ip+1:])
vm.currentFrame().ip += 2
err := vm.push(vm.globals[globalIndex])
err := vm.push(vm.state.Globals[globalIndex])
if err != nil {
return err
}
@@ -359,6 +468,14 @@ func (vm *VM) Run() error {
return err
}
case code.OpLoadModule:
name := vm.pop()
err := vm.loadModule(name)
if err != nil {
return err
}
}
if vm.Debug {
@@ -396,6 +513,8 @@ func (vm *VM) executeGetItem(left, index object.Object) error {
return vm.executeArrayGetItem(left, index)
case left.Type() == object.HASH_OBJ:
return vm.executeHashGetItem(left, index)
case left.Type() == object.MODULE_OBJ:
return vm.executeHashGetItem(left.(*object.Module).Attrs, index)
default:
return fmt.Errorf(
"index operator not supported: left=%s index=%s",
@@ -766,7 +885,7 @@ func (vm *VM) callBuiltin(builtin *object.Builtin, numArgs int) error {
}
func (vm *VM) pushClosure(constIndex, numFree int) error {
constant := vm.constants[constIndex]
constant := vm.state.Constants[constIndex]
function, ok := constant.(*object.CompiledFunction)
if !ok {
return fmt.Errorf("not a function %+v", constant)