aboutsummaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-07-27 11:01:29 +0000
committerMel <einebeere@gmail.com>2022-07-27 11:01:29 +0000
commit22a69393f58abcf3bcf9e7039f994dae78422213 (patch)
tree6f88f586bd57d298d54e345a6c4b8d7144a5d2ec /pkg
parent45b6f073fe398e820e9e4a82900bc282ee32af9b (diff)
downloadjinx-22a69393f58abcf3bcf9e7039f994dae78422213.tar.zst
jinx-22a69393f58abcf3bcf9e7039f994dae78422213.zip
Implement VM modules and globals
Diffstat (limited to 'pkg')
-rw-r--r--pkg/lang/vm/code/op.go2
-rw-r--r--pkg/lang/vm/code/pos.go13
-rw-r--r--pkg/lang/vm/errors.go30
-rw-r--r--pkg/lang/vm/exec.go82
-rw-r--r--pkg/lang/vm/mem/cell.go4
-rw-r--r--pkg/lang/vm/stack/errors.go1
-rw-r--r--pkg/lang/vm/stack/stack.go59
-rw-r--r--pkg/lang/vm/text/decompiler.go2
-rw-r--r--pkg/lang/vm/text/op.go2
-rw-r--r--pkg/lang/vm/utils.go27
-rw-r--r--pkg/lang/vm/value/cells.go21
-rw-r--r--pkg/lang/vm/value/data.go13
-rw-r--r--pkg/lang/vm/value/value.go5
-rw-r--r--pkg/lang/vm/vm.go163
-rw-r--r--pkg/lang/vm/vm_test.go43
15 files changed, 374 insertions, 93 deletions
diff --git a/pkg/lang/vm/code/op.go b/pkg/lang/vm/code/op.go
index 1ce08f7..b4c172a 100644
--- a/pkg/lang/vm/code/op.go
+++ b/pkg/lang/vm/code/op.go
@@ -19,7 +19,9 @@ const (
OpDrop
+ OpAddGlobal
OpGetGlobal
+ OpSetGlobal
OpGetLocal
OpSetLocal
OpGetMember
diff --git a/pkg/lang/vm/code/pos.go b/pkg/lang/vm/code/pos.go
new file mode 100644
index 0000000..90deb33
--- /dev/null
+++ b/pkg/lang/vm/code/pos.go
@@ -0,0 +1,13 @@
+package code
+
+type Pos struct {
+ Module int
+ PC int
+}
+
+func NewPos(module int, pc int) Pos {
+ return Pos{
+ Module: module,
+ PC: pc,
+ }
+} \ No newline at end of file
diff --git a/pkg/lang/vm/errors.go b/pkg/lang/vm/errors.go
index bfc8a34..0dd73e7 100644
--- a/pkg/lang/vm/errors.go
+++ b/pkg/lang/vm/errors.go
@@ -9,16 +9,16 @@ import (
)
type Error struct {
- Pc int
+ Pos code.Pos
Line int
Err error
}
func (e Error) Error() string {
if e.Line == -1 {
- return fmt.Sprintf("vm error at pc %d, unknown line: %v", e.Pc, e.Err)
+ return fmt.Sprintf("vm error in module '%d' at pc %d, unknown line: %v", e.Pos.Module, e.Pos.PC, e.Err)
}
- return fmt.Sprintf("vm error at pc %d, line %d: %v", e.Pc, e.Line, e.Err)
+ return fmt.Sprintf("vm error in module '%d' at pc %d, line %d: %v", e.Pos.Module, e.Pos.PC, e.Line, e.Err)
}
// Fatal errors
@@ -111,3 +111,27 @@ type ErrWrongNumberOfArguments struct {
func (e ErrWrongNumberOfArguments) Error() string {
return fmt.Sprintf("wrong number of arguments: needed %d, got %d", e.Needed, e.Got)
}
+
+type ErrCantAddGlobalFromMain struct {
+ GlobalName string
+}
+
+func (e ErrCantAddGlobalFromMain) Error() string {
+ return fmt.Sprintf("can't export '%s' from main module", e.GlobalName)
+}
+
+type ErrGlobalAlreadyExists struct {
+ GlobalName string
+}
+
+func (e ErrGlobalAlreadyExists) Error() string {
+ return fmt.Sprintf("global '%s' already exists", e.GlobalName)
+}
+
+type ErrNoSuchGlobal struct {
+ GlobalName string
+}
+
+func (e ErrNoSuchGlobal) Error() string {
+ return fmt.Sprintf("no such global '%s'", e.GlobalName)
+}
diff --git a/pkg/lang/vm/exec.go b/pkg/lang/vm/exec.go
index c5b0539..5a8eb05 100644
--- a/pkg/lang/vm/exec.go
+++ b/pkg/lang/vm/exec.go
@@ -44,7 +44,7 @@ func (vm *VM) execPushArray() error {
func (vm *VM) execPushFunction(pc int) {
// TODO: Make push ops into functions, where the argCount can be passed.
- vm.stack.Push(value.NewFunction(pc, 0))
+ vm.stack.Push(value.NewFunction(code.NewPos(vm.module(), pc), 0))
}
func (vm *VM) execPushObject() error {
@@ -57,6 +57,72 @@ func (vm *VM) execPushObject() error {
return nil
}
+func (vm *VM) execAddGlobal(name string) error {
+ if !vm.canAddGlobals {
+ return ErrCantAddGlobalFromMain{GlobalName: name}
+ }
+
+ if _, ok := vm.globals[name]; ok {
+ return ErrGlobalAlreadyExists{GlobalName: name}
+ }
+
+ v, err := vm.stack.Pop()
+ if err != nil {
+ return err
+ }
+
+ globalPtr, err := vm.memory.Allocate(mem.CellKindGlobal)
+ if err != nil {
+ return err
+ }
+
+ globalCell := value.GlobalCell(v)
+ if err := vm.memory.Set(globalPtr, globalCell); err != nil {
+ return err
+ }
+
+ vm.globals[name] = globalPtr
+
+ return nil
+}
+
+func (vm *VM) execGetGlobal(name string) error {
+ ptr, ok := vm.globals[name]
+ if !ok {
+ return ErrNoSuchGlobal{GlobalName: name}
+ }
+
+ cell, err := vm.getMemCell(ptr, mem.CellKindGlobal, false)
+ if err != nil {
+ return err
+ }
+
+ v := cell.(value.GlobalCell).Get()
+ v = v.Clone(vm.memory)
+
+ vm.stack.Push(v)
+ return nil
+}
+
+func (vm *VM) execSetGlobal(name string) error {
+ ptr, ok := vm.globals[name]
+ if !ok {
+ return ErrNoSuchGlobal{GlobalName: name}
+ }
+
+ new, err := vm.stack.Pop()
+ if err != nil {
+ return err
+ }
+
+ globalCell := value.GlobalCell(new)
+ if err := vm.memory.Set(ptr, globalCell); err != nil {
+ return err
+ }
+
+ return nil
+}
+
func (vm *VM) execGetLocal(offset int) error {
local, err := vm.stack.Local(int(offset))
if err != nil {
@@ -165,7 +231,7 @@ func (vm *VM) execGetMember(name string) error {
return err
}
- method := value.NewFunction(0, 0).WithData(methodData.WithEnv(envPtr))
+ method := value.NewFunction(code.Pos{}, 0).WithData(methodData.WithEnv(envPtr))
// method = method.Clone(vm.memory) will only be necessary when we support methods with environments.
vm.stack.Push(method)
@@ -219,7 +285,7 @@ func (vm *VM) execGetMember(name string) error {
member = member.WithEnv(envPtr)
- val := value.NewFunction(0, 0).WithData(member)
+ val := value.NewFunction(code.Pos{}, 0).WithData(member)
vm.stack.Push(val)
parent.Drop(vm.memory)
@@ -781,7 +847,7 @@ func (vm *VM) execCall(argCount uint) error {
}
}
- if err = vm.stack.PushCall(fn.Pc(), vm.pc, fn.Env()); err != nil {
+ if err = vm.stack.PushCall(fn.Pos(), vm.pos, fn.Env()); err != nil {
return err
}
@@ -806,7 +872,7 @@ func (vm *VM) execCall(argCount uint) error {
vm.stack.Push(arg)
}
- vm.pc = fn.Pc()
+ vm.setPos(fn.Pos())
return nil
}
@@ -821,7 +887,7 @@ func (vm *VM) execJumpIf(pc int, cond bool) error {
case value.BoolType:
bl := b.Data().(value.BoolData)
if bl.Get() == cond {
- vm.pc = pc
+ vm.setPC(pc)
}
default:
var op code.Op
@@ -846,13 +912,13 @@ func (vm *VM) execRet() error {
return err
}
- pc, err := vm.popCallAndDrop()
+ pos, err := vm.popCallAndDrop()
if err != nil {
return err
}
vm.stack.Push(returned)
- vm.pc = pc
+ vm.setPos(pos)
return nil
}
diff --git a/pkg/lang/vm/mem/cell.go b/pkg/lang/vm/mem/cell.go
index 73aa0c6..85d6c34 100644
--- a/pkg/lang/vm/mem/cell.go
+++ b/pkg/lang/vm/mem/cell.go
@@ -12,6 +12,8 @@ const (
CellKindEnv
CellKindOutlet
+ CellKindGlobal
+
CellKindForbidden
)
@@ -31,6 +33,8 @@ func (c CellKind) String() string {
return "env"
case CellKindOutlet:
return "outlet"
+ case CellKindGlobal:
+ return "global"
case CellKindForbidden:
return "forbidden"
default:
diff --git a/pkg/lang/vm/stack/errors.go b/pkg/lang/vm/stack/errors.go
index 55ef2ea..55145d5 100644
--- a/pkg/lang/vm/stack/errors.go
+++ b/pkg/lang/vm/stack/errors.go
@@ -11,6 +11,7 @@ var (
ErrReachedMaxCallDepth = errors.New("reached max call depth (max depth: 1000)")
ErrReachedRootCallFrame = errors.New("reached root call frame")
+ ErrNotAtRootCallFrame = errors.New("not at root call frame")
ErrCallBaseCantBeNegative = errors.New("call base cannot be negative")
)
diff --git a/pkg/lang/vm/stack/stack.go b/pkg/lang/vm/stack/stack.go
index 34ca896..74d4ab8 100644
--- a/pkg/lang/vm/stack/stack.go
+++ b/pkg/lang/vm/stack/stack.go
@@ -1,6 +1,7 @@
package stack
import (
+ "jinx/pkg/lang/vm/code"
"jinx/pkg/lang/vm/mem"
"jinx/pkg/lang/vm/value"
)
@@ -17,8 +18,9 @@ type Stack interface {
Len() int
IsEmpty() bool
- PushCall(newPc, returnPc int, env mem.Ptr) error
- PopCall() (int, error)
+ PutRootCall(basePos code.Pos) error
+ PushCall(newPos, returnPos code.Pos, env mem.Ptr) error
+ PopCall() (code.Pos, error)
CurrentCallEnv() mem.Ptr
CallDepth() int
@@ -35,12 +37,6 @@ func New() Stack {
data := make([]value.Value, 0, 64)
calls := make([]callFrame, 0, 8)
- calls = append(calls, callFrame{
- pc: 0,
- returnPc: 0,
- base: 0,
- })
-
return &stackImpl{
data: data,
calls: calls,
@@ -106,30 +102,53 @@ func (stack *stackImpl) IsEmpty() bool {
return len(stack.data) == 0
}
-func (stack *stackImpl) PushCall(newPc, returnPc int, env mem.Ptr) error {
+func (stack *stackImpl) PutRootCall(basePos code.Pos) error {
+ frame := callFrame{
+ pos: basePos,
+ returnPos: basePos,
+ base: 0,
+ env: mem.NullPtr,
+ }
+
+ if stack.CallDepth() == 0 {
+ stack.calls = append(stack.calls, frame)
+
+ return nil
+ }
+
+ if stack.CallDepth() != 1 {
+ return ErrNotAtRootCallFrame
+ }
+
+ stack.calls[0] = frame
+
+ return nil
+}
+
+func (stack *stackImpl) PushCall(newPos, returnPos code.Pos, env mem.Ptr) error {
if stack.CallDepth() == 1000 {
return ErrReachedMaxCallDepth
}
stack.calls = append(stack.calls, callFrame{
- pc: newPc,
- returnPc: returnPc,
- base: stack.Len(),
- env: env,
+ pos: newPos,
+ returnPos: returnPos,
+ base: stack.Len(),
+ env: env,
})
return nil
}
-func (stack *stackImpl) PopCall() (int, error) {
+func (stack *stackImpl) PopCall() (code.Pos, error) {
if stack.CallDepth() == 0 {
- return 0, ErrReachedRootCallFrame
+ return code.Pos{}, ErrReachedRootCallFrame
}
call := stack.topCall()
stack.calls = stack.calls[:stack.CallDepth()-1]
- return call.returnPc, nil
+ return call.returnPos, nil
}
func (stack *stackImpl) CurrentCallEnv() mem.Ptr {
@@ -160,8 +179,8 @@ func (stack *stackImpl) topCall() *callFrame {
}
type callFrame struct {
- pc int // Beginning of the called function.
- returnPc int // Where to return to after the called function returns.
- base int // Base of the local variables on the data stack.
- env mem.Ptr // Environment of the called function.
+ pos code.Pos // Beginning of the called function.
+ returnPos code.Pos // Where to return to after the called function returns.
+ base int // Base of the local variables on the data stack.
+ env mem.Ptr // Environment of the called function.
}
diff --git a/pkg/lang/vm/text/decompiler.go b/pkg/lang/vm/text/decompiler.go
index 2b4e704..8a85d49 100644
--- a/pkg/lang/vm/text/decompiler.go
+++ b/pkg/lang/vm/text/decompiler.go
@@ -93,7 +93,9 @@ func (d *Decompiler) decompileInstruction(bc code.Raw) (string, code.Raw) {
// Operations that take a string.
case code.OpPushString,
code.OpPushType,
+ code.OpAddGlobal,
code.OpGetGlobal,
+ code.OpSetGlobal,
code.OpSetMember,
code.OpGetMember:
s, rest := d.decompileString(bc[1:])
diff --git a/pkg/lang/vm/text/op.go b/pkg/lang/vm/text/op.go
index ce53f50..ff3c568 100644
--- a/pkg/lang/vm/text/op.go
+++ b/pkg/lang/vm/text/op.go
@@ -17,7 +17,9 @@ var (
code.OpPushObject: "push_object",
code.OpPushType: "push_type",
code.OpDrop: "drop",
+ code.OpAddGlobal: "add_global",
code.OpGetGlobal: "get_global",
+ code.OpSetGlobal: "set_global",
code.OpGetLocal: "get_local",
code.OpSetLocal: "set_local",
code.OpGetMember: "get_member",
diff --git a/pkg/lang/vm/utils.go b/pkg/lang/vm/utils.go
index 00311ef..2b7fa6f 100644
--- a/pkg/lang/vm/utils.go
+++ b/pkg/lang/vm/utils.go
@@ -1,6 +1,7 @@
package vm
import (
+ "jinx/pkg/lang/vm/code"
"jinx/pkg/lang/vm/mem"
"jinx/pkg/lang/vm/value"
)
@@ -14,14 +15,14 @@ func (vm *VM) popAndDrop() (value.Value, error) {
return v, nil
}
-func (vm *VM) popCallAndDrop() (int, error) {
+func (vm *VM) popCallAndDrop() (code.Pos, error) {
envPtr := vm.stack.CurrentCallEnv()
vm.memory.Release(envPtr)
for !vm.stack.ReachedBaseOfCall() {
_, err := vm.popAndDrop()
if err != nil {
- return 0, err
+ return code.Pos{}, err
}
}
@@ -63,6 +64,8 @@ func (vm *VM) getMemCell(ptr mem.Ptr, kind mem.CellKind, allowNil bool) (mem.Cel
_, ok = cell.(value.TypeCell)
case mem.CellKindObject:
_, ok = cell.(value.ObjectCell)
+ case mem.CellKindGlobal:
+ _, ok = cell.(value.GlobalCell)
}
if !ok {
@@ -71,3 +74,23 @@ func (vm *VM) getMemCell(ptr mem.Ptr, kind mem.CellKind, allowNil bool) (mem.Cel
return cell, nil
}
+
+func (vm *VM) module() int {
+ return vm.pos.Module
+}
+
+func (vm *VM) pc() int {
+ return vm.pos.PC
+}
+
+func (vm *VM) setPC(pc int) {
+ vm.pos.PC = pc
+}
+
+func (vm *VM) advancePC(by int) {
+ vm.pos.PC += by
+}
+
+func (vm *VM) setPos(pos code.Pos) {
+ vm.pos = pos
+}
diff --git a/pkg/lang/vm/value/cells.go b/pkg/lang/vm/value/cells.go
index 5abb032..19ecfde 100644
--- a/pkg/lang/vm/value/cells.go
+++ b/pkg/lang/vm/value/cells.go
@@ -1,6 +1,9 @@
package value
-import "jinx/pkg/lang/vm/mem"
+import (
+ "jinx/pkg/lang/vm/code"
+ "jinx/pkg/lang/vm/mem"
+)
type ArrayCell []Value
@@ -55,7 +58,7 @@ func (t TypeCell) DropCell(m mem.Mem) {
typ := t.Get()
for _, f := range typ.Methods {
// Wrap data in a Value to drop it.
- val := NewFunction(0, 0).WithData(f)
+ val := NewFunction(code.Pos{}, 0).WithData(f)
val.Drop(m)
}
@@ -101,3 +104,17 @@ func (e EnvCell) MatchingCellKind() mem.CellKind {
func (e EnvCell) Get() Env {
return Env(e)
}
+
+type GlobalCell Value
+
+func (g GlobalCell) DropCell(m mem.Mem) {
+ panic("global cell cannot be dropped")
+}
+
+func (g GlobalCell) MatchingCellKind() mem.CellKind {
+ return mem.CellKindGlobal
+}
+
+func (g GlobalCell) Get() Value {
+ return Value(g)
+}
diff --git a/pkg/lang/vm/value/data.go b/pkg/lang/vm/value/data.go
index 18e1b3d..6c3d762 100644
--- a/pkg/lang/vm/value/data.go
+++ b/pkg/lang/vm/value/data.go
@@ -2,6 +2,7 @@ package value
import (
"fmt"
+ "jinx/pkg/lang/vm/code"
"jinx/pkg/lang/vm/mem"
"strconv"
"strings"
@@ -127,7 +128,7 @@ func (n NullData) String(_ mem.Mem) (string, error) {
}
type FunctionData struct {
- pc int
+ pos code.Pos
args uint
env mem.Ptr
native NativeFunc
@@ -135,8 +136,8 @@ type FunctionData struct {
type NativeFunc func([]Value) (Value, error)
-func (f FunctionData) Pc() int {
- return f.pc
+func (f FunctionData) Pos() code.Pos {
+ return f.pos
}
func (f FunctionData) Args() uint {
@@ -152,18 +153,18 @@ func (f FunctionData) Native() NativeFunc {
}
func (f FunctionData) WithEnv(env mem.Ptr) FunctionData {
- return FunctionData{pc: f.pc, args: f.args, env: env, native: f.native}
+ return FunctionData{pos: f.pos, args: f.args, env: env, native: f.native}
}
func (f FunctionData) WithArgs(args uint) FunctionData {
- return FunctionData{pc: f.pc, args: args, env: f.env, native: f.native}
+ return FunctionData{pos: f.pos, args: args, env: f.env, native: f.native}
}
func (f FunctionData) String(_ mem.Mem) (string, error) {
if f.native != nil {
return fmt.Sprintf("<fn(%d) native>", f.args), nil
} else {
- return fmt.Sprintf("<fn(%d) %d>", f.args, f.pc), nil
+ return fmt.Sprintf("<fn(%d) %d:%d>", f.args, f.pos.Module, f.pos.PC), nil
}
}
diff --git a/pkg/lang/vm/value/value.go b/pkg/lang/vm/value/value.go
index 5dd5012..b051f04 100644
--- a/pkg/lang/vm/value/value.go
+++ b/pkg/lang/vm/value/value.go
@@ -1,6 +1,7 @@
package value
import (
+ "jinx/pkg/lang/vm/code"
"jinx/pkg/lang/vm/mem"
)
@@ -52,8 +53,8 @@ func NewNull() Value {
return Value{t: CORE_TYPE_NULL, d: NullData{}}
}
-func NewFunction(pc int, args uint) Value {
- return Value{t: CORE_TYPE_FUNCTION, d: FunctionData{pc: pc, args: args}}
+func NewFunction(pos code.Pos, args uint) Value {
+ return Value{t: CORE_TYPE_FUNCTION, d: FunctionData{pos: pos, args: args}}
}
func NewNativeFunction(f NativeFunc, args uint) Value {
diff --git a/pkg/lang/vm/vm.go b/pkg/lang/vm/vm.go
index 8b47915..4a4aa68 100644
--- a/pkg/lang/vm/vm.go
+++ b/pkg/lang/vm/vm.go
@@ -7,18 +7,28 @@ import (
)
type VM struct {
- code *code.Code
- pc int
+ pos code.Pos
+
+ modules []*code.Code
+
stack stack.Stack
memory mem.Mem
+
+ canAddGlobals bool
+ globals map[string]mem.Ptr
}
-func New(code *code.Code) *VM {
+func New(main *code.Code, deps []*code.Code) *VM {
vm := &VM{
- code: code,
- pc: 0,
+ pos: code.NewPos(0, 0),
+
+ modules: append([]*code.Code{main}, deps...),
+
stack: stack.New(),
memory: mem.New(),
+
+ canAddGlobals: false,
+ globals: make(map[string]mem.Ptr),
}
if err := vm.setup(); err != nil {
@@ -46,14 +56,56 @@ func (vm *VM) GetResult() (string, error) {
}
func (vm *VM) Run() error {
- for vm.pc < vm.code.Len() {
- op, advance := vm.code.GetOp(vm.pc)
- vm.pc += advance
+ vm.canAddGlobals = true
+ for i := 1; i < len(vm.modules); i++ {
+ if err := vm.executeModule(i); err != nil {
+ return err
+ }
+
+ // Drop all calls from the stack after the module has finished
+ for vm.stack.CallDepth() > 1 {
+ if _, err := vm.popCallAndDrop(); err != nil {
+ return err
+ }
+ }
+
+ // Drop the root stack values
+ for !vm.stack.IsEmpty() {
+ if _, err := vm.popAndDrop(); err != nil {
+ return err
+ }
+ }
+ }
+
+ vm.canAddGlobals = false
+ if err := vm.executeModule(0); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (vm *VM) executeModule(moduleID int) error {
+ vm.pos = code.NewPos(moduleID, 0)
+
+ if err := vm.stack.PutRootCall(vm.pos); err != nil {
+ return err
+ }
+
+ for {
+ module := vm.modules[vm.module()]
- if decision, err := vm.step(op); err != nil {
+ if vm.pc() >= module.Len() {
+ return nil
+ }
+
+ op, advance := module.GetOp(vm.pc())
+ vm.advancePC(advance)
+
+ if decision, err := vm.step(module, op); err != nil {
return Error{
- Pc: vm.pc,
- Line: vm.code.Debug().PCToLine(vm.pc),
+ Pos: vm.pos,
+ Line: module.Debug().PCToLine(vm.pc()),
Err: err,
}
} else if decision == stepDecisionHalt {
@@ -71,7 +123,7 @@ const (
stepDecisionHalt
)
-func (vm *VM) step(op code.Op) (stepDecision, error) {
+func (vm *VM) step(module *code.Code, op code.Op) (stepDecision, error) {
var err error
switch op {
case code.OpNop:
@@ -80,18 +132,18 @@ func (vm *VM) step(op code.Op) (stepDecision, error) {
return stepDecisionHalt, nil
case code.OpPushInt:
- x, advance := vm.code.GetInt(vm.pc)
- vm.pc += advance
+ x, advance := module.GetInt(vm.pc())
+ vm.advancePC(advance)
vm.execPushInt(x)
case code.OpPushFloat:
- x, advance := vm.code.GetFloat(vm.pc)
- vm.pc += advance
+ x, advance := module.GetFloat(vm.pc())
+ vm.advancePC(advance)
vm.execPushFloat(x)
case code.OpPushString:
- str, advance := vm.code.GetString(vm.pc)
- vm.pc += advance
+ str, advance := module.GetString(vm.pc())
+ vm.advancePC(advance)
err = vm.execPushString(str)
case code.OpPushNull:
@@ -103,60 +155,73 @@ func (vm *VM) step(op code.Op) (stepDecision, error) {
case code.OpPushArray:
vm.execPushArray()
case code.OpPushFunction:
- x, advance := vm.code.GetUint(vm.pc)
- vm.pc += advance
+ x, advance := module.GetUint(vm.pc())
+ vm.advancePC(advance)
vm.execPushFunction(int(x))
case code.OpPushObject:
err = vm.execPushObject()
case code.OpPushType:
- name, advance := vm.code.GetString(vm.pc)
- vm.pc += advance
+ name, advance := module.GetString(vm.pc())
+ vm.advancePC(advance)
err = vm.execPushType(name)
case code.OpDrop:
- dropAmount, advance := vm.code.GetUint(vm.pc)
- vm.pc += advance
+ dropAmount, advance := module.GetUint(vm.pc())
+ vm.advancePC(advance)
err = vm.execDrop(uint(dropAmount))
+ case code.OpAddGlobal:
+ globalName, advance := module.GetString(vm.pc())
+ vm.advancePC(advance)
+
+ err = vm.execAddGlobal(globalName)
case code.OpGetGlobal:
- panic("not implemented")
+ globalName, advance := module.GetString(vm.pc())
+ vm.advancePC(advance)
+
+ err = vm.execGetGlobal(globalName)
+ case code.OpSetGlobal:
+ globalName, advance := module.GetString(vm.pc())
+ vm.advancePC(advance)
+
+ err = vm.execSetGlobal(globalName)
case code.OpGetLocal:
- offset, advance := vm.code.GetInt(vm.pc)
- vm.pc += advance
+ offset, advance := module.GetInt(vm.pc())
+ vm.advancePC(advance)
err = vm.execGetLocal(int(offset))
case code.OpSetLocal:
- offset, advance := vm.code.GetInt(vm.pc)
- vm.pc += advance
+ offset, advance := module.GetInt(vm.pc())
+ vm.advancePC(advance)
err = vm.execSetLocal(int(offset))
case code.OpGetMember:
- name, advance := vm.code.GetString(vm.pc)
- vm.pc += advance
+ name, advance := module.GetString(vm.pc())
+ vm.advancePC(advance)
err = vm.execGetMember(name)
case code.OpSetMember:
- name, advance := vm.code.GetString(vm.pc)
- vm.pc += advance
+ name, advance := module.GetString(vm.pc())
+ vm.advancePC(advance)
err = vm.execSetMember(name)
case code.OpGetEnv:
- envIndex, advance := vm.code.GetUint(vm.pc)
- vm.pc += advance
+ envIndex, advance := module.GetUint(vm.pc())
+ vm.advancePC(advance)
err = vm.execGetEnv(int(envIndex))
case code.OpSetEnv:
- envIndex, advance := vm.code.GetUint(vm.pc)
- vm.pc += advance
+ envIndex, advance := module.GetUint(vm.pc())
+ vm.advancePC(advance)
err = vm.execSetEnv(int(envIndex))
case code.OpAddToEnv:
- local, advance := vm.code.GetUint(vm.pc)
- vm.pc += advance
+ local, advance := module.GetUint(vm.pc())
+ vm.advancePC(advance)
err = vm.execAddToEnv(int(local))
@@ -164,8 +229,8 @@ func (vm *VM) step(op code.Op) (stepDecision, error) {
err = vm.execAnchorType()
case code.OpSetArgCount:
- argCount, advance := vm.code.GetUint(vm.pc)
- vm.pc += advance
+ argCount, advance := module.GetUint(vm.pc())
+ vm.advancePC(advance)
err = vm.execSetArgCount(uint(argCount))
@@ -193,21 +258,21 @@ func (vm *VM) step(op code.Op) (stepDecision, error) {
case code.OpIndex:
err = vm.execIndex()
case code.OpCall:
- argCount, advance := vm.code.GetUint(vm.pc)
- vm.pc += advance
+ argCount, advance := module.GetUint(vm.pc())
+ vm.advancePC(advance)
err = vm.execCall(uint(argCount))
case code.OpJmp:
- pc, _ := vm.code.GetUint(vm.pc)
- vm.pc = int(pc)
+ pc, _ := module.GetUint(vm.pc())
+ vm.setPC(int(pc))
case code.OpJt:
- pc, advance := vm.code.GetUint(vm.pc)
- vm.pc += advance
+ pc, advance := module.GetUint(vm.pc())
+ vm.advancePC(advance)
err = vm.execJumpIf(int(pc), true)
case code.OpJf:
- pc, advance := vm.code.GetUint(vm.pc)
- vm.pc += advance
+ pc, advance := module.GetUint(vm.pc())
+ vm.advancePC(advance)
err = vm.execJumpIf(int(pc), false)
case code.OpRet:
err = vm.execRet()
diff --git a/pkg/lang/vm/vm_test.go b/pkg/lang/vm/vm_test.go
index ae0b8e6..b25a446 100644
--- a/pkg/lang/vm/vm_test.go
+++ b/pkg/lang/vm/vm_test.go
@@ -440,9 +440,50 @@ func TestPrimes(t *testing.T) {
test(t, src, "[2, 3, 5, 7]")
}
+func TestModules(t *testing.T) {
+ mainSrc := `
+ get_global "add_one"
+ get_global "x"
+ call 1
+ halt
+ `
+
+ librarySrc1 := `
+ push_int 41
+ add_global "x"
+ halt
+ `
+
+ librarySrc2 := `
+ push_function @add_one
+ set_arg_count 1
+ add_global "add_one"
+ halt
+
+ @add_one:
+ get_local 0
+ push_int 1
+ add
+ ret
+ `
+
+ main := compile(t, mainSrc)
+ library1 := compile(t, librarySrc1)
+ library2 := compile(t, librarySrc2)
+
+ vm := vm.New(&main, []*code.Code{&library1, &library2})
+ err := vm.Run()
+ require.NoError(t, err)
+
+ res, err := vm.GetResult()
+ require.NoError(t, err)
+
+ require.Equal(t, "42", res)
+}
+
func test(t *testing.T, src string, expected string) {
bc := compile(t, src)
- vm := vm.New(&bc)
+ vm := vm.New(&bc, nil)
err := vm.Run()
require.NoError(t, err)