module support
This commit is contained in:
151
vm/vm.go
151
vm/vm.go
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user