aboutsummaryrefslogtreecommitdiff
path: root/pkg/lang/vm
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-08-11 01:25:47 +0000
committerMel <einebeere@gmail.com>2022-08-11 01:25:47 +0000
commit86f31acf6789be116dcc54ed85b069a37c0f7aa8 (patch)
treebc7afd6a8c340825996d29c6cfd392ae42b4fbd5 /pkg/lang/vm
parentc46b2bc7ce6df1f2c6c9494ef08015ec29992da5 (diff)
downloadjinx-86f31acf6789be116dcc54ed85b069a37c0f7aa8.tar.zst
jinx-86f31acf6789be116dcc54ed85b069a37c0f7aa8.zip
Actual modules and core
Diffstat (limited to 'pkg/lang/vm')
-rw-r--r--pkg/lang/vm/core.go146
-rw-r--r--pkg/lang/vm/errors.go11
-rw-r--r--pkg/lang/vm/exe.go62
-rw-r--r--pkg/lang/vm/exec.go53
-rw-r--r--pkg/lang/vm/executor/executor.go17
-rw-r--r--pkg/lang/vm/executor/nativefunc.go5
-rw-r--r--pkg/lang/vm/setup.go58
-rw-r--r--pkg/lang/vm/utils.go13
-rw-r--r--pkg/lang/vm/value/core_ptrs.go96
-rw-r--r--pkg/lang/vm/value/data.go22
-rw-r--r--pkg/lang/vm/value/value.go18
-rw-r--r--pkg/lang/vm/vm.go43
-rw-r--r--pkg/lang/vm/vm_test.go91
13 files changed, 377 insertions, 258 deletions
diff --git a/pkg/lang/vm/core.go b/pkg/lang/vm/core.go
deleted file mode 100644
index 477cee8..0000000
--- a/pkg/lang/vm/core.go
+++ /dev/null
@@ -1,146 +0,0 @@
-package vm
-
-import (
- "jinx/pkg/lang/vm/code"
- "jinx/pkg/lang/vm/value"
-)
-
-func (vm *VM) createCoreNullType() value.Type {
- return value.Type{
- Kind: value.NullType,
- }
-}
-
-func (vm *VM) createCoreIntType() value.Type {
- return value.Type{
- Kind: value.IntType,
- }
-}
-
-func (vm *VM) createCoreFloatType() value.Type {
- return value.Type{
- Kind: value.FloatType,
- }
-}
-
-func (vm *VM) createCoreStringType() value.Type {
- return value.Type{
- Kind: value.StringType,
- }
-}
-
-func (vm *VM) createCoreBoolType() value.Type {
- return value.Type{
- Kind: value.BoolType,
- }
-}
-
-func (vm *VM) createCoreArrayType() value.Type {
- return value.Type{
- Kind: value.ArrayType,
- Methods: map[string]value.FunctionData{
- "length": makeCoreFn(vm.coreArrayLength, 0),
- "push": makeCoreFn(vm.coreArrayPush, 1),
- },
- }
-}
-
-func (vm *VM) createCoreFunctionType() value.Type {
- return value.Type{
- Kind: value.FunctionType,
- }
-}
-
-func (vm *VM) createCoreTypeRefType() value.Type {
- return value.Type{
- Kind: value.TypeRefType,
- Methods: map[string]value.FunctionData{
- "$add_method": makeCoreFn(vm.coreTypeRefIntrAddMethod, 2),
- },
- }
-}
-
-func (vm *VM) coreArrayLength(args []value.Value) (value.Value, error) {
- a, err := ensureTypeThis[value.ArrayData](vm, value.ArrayType)
- if err != nil {
- return value.Value{}, err
- }
-
- len, err := a.Len(vm.memory)
- if err != nil {
- return value.Value{}, err
- }
- res := value.NewInt(int64(len))
- return res, nil
-}
-
-func (vm *VM) coreArrayPush(args []value.Value) (value.Value, error) {
- a, err := ensureTypeThis[value.ArrayData](vm, value.ArrayType)
- if err != nil {
- return value.Value{}, err
- }
-
- return value.Value{}, a.Push(vm.memory, args[0])
-}
-
-func (vm *VM) coreTypeRefIntrAddMethod(args []value.Value) (value.Value, error) {
- ref, err := ensureTypeThis[value.TypeRefData](vm, value.TypeRefType)
- if err != nil {
- return value.Value{}, err
- }
-
- fn, err := ensureType[value.FunctionData](args[0], value.FunctionType)
- if err != nil {
- return value.Value{}, err
- }
-
- nameData, err := ensureType[value.StringData](args[1], value.StringType)
- if err != nil {
- return value.Value{}, err
- }
- name, err := nameData.RawString(vm.memory)
- if err != nil {
- return value.Value{}, err
- }
-
- return value.Value{}, ref.AddMethod(vm.memory, name, fn)
-}
-
-func ensureTypeThis[D value.Data](vm *VM, t value.TypeKind) (D, error) {
- this, err := vm.getThis()
- if err != nil {
- return *new(D), err
- }
-
- return ensureType[D](this, t)
-}
-
-func ensureType[D value.Data](val value.Value, t value.TypeKind) (D, error) {
- if val.Type() != t {
- return *new(D), ErrInvalidOperandType{
- Op: code.OpNop, // TODO: Add error not dependent on op.
- X: val.Type(),
- }
- }
-
- return val.Data().(D), nil
-}
-
-func makeCoreFn(f value.NativeFunc, argCount uint) value.FunctionData {
- return value.NewNativeFunction(f, argCount).Data().(value.FunctionData)
-}
-
-func (vm *VM) getThis() (value.Value, error) {
- err := vm.execGetEnv(0)
- if err != nil {
- return value.Value{}, err
- }
-
- // Take it back!
- this, err := vm.popAndDrop()
- if err != nil {
- return value.Value{}, err
- }
-
- return this, nil
-}
diff --git a/pkg/lang/vm/errors.go b/pkg/lang/vm/errors.go
index 0dd73e7..0c1b916 100644
--- a/pkg/lang/vm/errors.go
+++ b/pkg/lang/vm/errors.go
@@ -9,16 +9,17 @@ import (
)
type Error struct {
- Pos code.Pos
- Line int
- Err error
+ Pos code.Pos
+ Module string
+ Line int
+ Err error
}
func (e Error) Error() string {
if e.Line == -1 {
- 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 in module '%s' at pc %d, unknown line: %v", e.Module, e.Pos.PC, 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)
+ return fmt.Sprintf("vm error in module '%s' at pc %d, line %d: %v", e.Module, e.Pos.PC, e.Line, e.Err)
}
// Fatal errors
diff --git a/pkg/lang/vm/exe.go b/pkg/lang/vm/exe.go
new file mode 100644
index 0000000..7d6fc61
--- /dev/null
+++ b/pkg/lang/vm/exe.go
@@ -0,0 +1,62 @@
+package vm
+
+import (
+ "jinx/pkg/lang/vm/mem"
+ "jinx/pkg/lang/vm/stack"
+ "jinx/pkg/lang/vm/value"
+)
+
+func (vm *VM) Mem() mem.Mem {
+ return vm.memory
+}
+
+func (vm *VM) Stack() stack.Stack {
+ return vm.stack
+}
+
+func (vm *VM) GetThis() (value.Value, error) {
+ err := vm.execGetEnv(0)
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ // Take it back!
+ this, err := vm.popAndDrop()
+ if err != nil {
+ return value.Value{}, err
+ }
+
+ return this, nil
+}
+
+func (vm *VM) AddGlobal(name string, v value.Value) error {
+ 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) GetGlobal(name string) (value.Value, bool, error) {
+ ptr, ok := vm.globals[name]
+ if !ok {
+ return value.Value{}, false, nil
+ }
+
+ cell, err := vm.getMemCell(ptr, mem.CellKindGlobal, false)
+ if err != nil {
+ return value.Value{}, false, err
+ }
+
+ v := cell.(value.GlobalCell).Get()
+
+ return v, true, nil
+}
diff --git a/pkg/lang/vm/exec.go b/pkg/lang/vm/exec.go
index eeb8710..ade6cc0 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(code.NewPos(vm.module(), pc), 0))
+ vm.stack.Push(value.NewFunction(code.NewPos(vm.moduleID(), pc), 0))
}
func (vm *VM) execPushObject() error {
@@ -71,35 +71,56 @@ func (vm *VM) execAddGlobal(name string) error {
return err
}
- globalPtr, err := vm.memory.Allocate(mem.CellKindGlobal)
+ module := vm.module()
+ qualifiedName, err := module.GetGlobal(name)
if err != nil {
return err
}
- globalCell := value.GlobalCell(v)
- if err := vm.memory.Set(globalPtr, globalCell); err != nil {
- return err
+ // Fill the core ptrs if we got a core global.
+ if module.IsCore() {
+ if v.Type() == value.TypeRefType {
+ ptr := v.Data().(value.TypeRefData).TypeRef()
+
+ switch qualifiedName {
+ case ":core:Type":
+ vm.corePtrs.SetTypeRef(ptr)
+ case ":core:Int":
+ vm.corePtrs.SetInt(ptr)
+ case ":core:Float":
+ vm.corePtrs.SetFloat(ptr)
+ case ":core:Bool":
+ vm.corePtrs.SetBool(ptr)
+ case ":core:Null":
+ vm.corePtrs.SetNull(ptr)
+ case ":core:Array":
+ vm.corePtrs.SetArray(ptr)
+ case ":core:Function":
+ vm.corePtrs.SetFunction(ptr)
+ case ":core:String":
+ vm.corePtrs.SetString(ptr)
+ }
+ }
}
- vm.globals[name] = globalPtr
+ if err := vm.AddGlobal(qualifiedName, v); err != nil {
+ return err
+ }
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)
+ v, ok, err := vm.GetGlobal(name)
if err != nil {
return err
}
- v := cell.(value.GlobalCell).Get()
- v = v.Clone(vm.memory)
+ if !ok {
+ return ErrNoSuchGlobal{GlobalName: name}
+ }
+ v = v.Clone(vm.memory)
vm.stack.Push(v)
return nil
}
@@ -234,7 +255,7 @@ func (vm *VM) execGetMember(name string) error {
}
}
- typeCell, err := vm.getMemCell(parent.TypePtr(), mem.CellKindType, false)
+ typeCell, err := vm.getMemCell(parent.TypePtr(&vm.corePtrs), mem.CellKindType, false)
if err != nil {
return err
}
@@ -889,6 +910,8 @@ func (vm *VM) execCall(argCount uint) error {
if !val.IsEmpty() {
vm.stack.Push(val)
+ } else {
+ vm.stack.Push(value.NewNull())
}
return nil
diff --git a/pkg/lang/vm/executor/executor.go b/pkg/lang/vm/executor/executor.go
new file mode 100644
index 0000000..0b7ccef
--- /dev/null
+++ b/pkg/lang/vm/executor/executor.go
@@ -0,0 +1,17 @@
+package executor
+
+import (
+ "jinx/pkg/lang/vm/mem"
+ "jinx/pkg/lang/vm/stack"
+ "jinx/pkg/lang/vm/value"
+)
+
+type Exectutor interface {
+ Mem() mem.Mem
+ Stack() stack.Stack
+
+ GetThis() (value.Value, error)
+
+ AddGlobal(name string, v value.Value) error
+ GetGlobal(name string) (value.Value, bool, error)
+}
diff --git a/pkg/lang/vm/executor/nativefunc.go b/pkg/lang/vm/executor/nativefunc.go
new file mode 100644
index 0000000..2188bfb
--- /dev/null
+++ b/pkg/lang/vm/executor/nativefunc.go
@@ -0,0 +1,5 @@
+package executor
+
+import "jinx/pkg/lang/vm/value"
+
+type NativeFunc func(executor Exectutor, args []value.Value) (value.Value, error)
diff --git a/pkg/lang/vm/setup.go b/pkg/lang/vm/setup.go
index db44b8a..201538d 100644
--- a/pkg/lang/vm/setup.go
+++ b/pkg/lang/vm/setup.go
@@ -1,44 +1,54 @@
package vm
import (
- "jinx/pkg/lang/vm/mem"
+ "fmt"
+ "jinx/pkg/lang/modules"
+ "jinx/pkg/lang/modules/core"
+ "jinx/pkg/lang/modules/natives"
"jinx/pkg/lang/vm/value"
)
func (vm *VM) setup() error {
- if err := vm.setupCoreLib(); err != nil {
- return err
- }
+ vm.modules = make([]modules.Module, 0, len(vm.main.Deps()))
- return nil
-}
+ // Add all natives to the VM as globals.
+ for _, native := range natives.Natives {
+ decollidedName := fmt.Sprintf("%s#native", native.Name)
-func (vm *VM) setupCoreLib() error {
- type allocationJob struct {
- at mem.Ptr
- t value.Type
- }
+ if _, ok := vm.globals[decollidedName]; ok {
+ return fmt.Errorf("native %s already exists", decollidedName)
+ }
- toAllocate := []allocationJob{
- {at: value.CORE_TYPE_NULL, t: vm.createCoreNullType()},
- {at: value.CORE_TYPE_INT, t: vm.createCoreIntType()},
- {at: value.CORE_TYPE_FLOAT, t: vm.createCoreFloatType()},
- {at: value.CORE_TYPE_STRING, t: vm.createCoreStringType()},
- {at: value.CORE_TYPE_BOOL, t: vm.createCoreBoolType()},
- {at: value.CORE_TYPE_ARRAY, t: vm.createCoreArrayType()},
- {at: value.CORE_TYPE_FUNCTION, t: vm.createCoreFunctionType()},
- {at: value.CORE_TYPE_TYPE_REF, t: vm.createCoreTypeRefType()},
- }
+ nativeFunc := native.Fn // Capture the native function, because Go is fun.
+ wrappedFunc := func(args []value.Value) (value.Value, error) {
+ return nativeFunc(vm, args)
+ }
- for _, job := range toAllocate {
- if err := vm.memory.AllocateAt(job.at, mem.CellKindType); err != nil {
+ nativeFunction := value.NewNativeFunction(wrappedFunc, uint(native.ArgCount))
+ if err := vm.AddGlobal(decollidedName, nativeFunction); err != nil {
return err
}
+ }
- if err := vm.memory.Set(job.at, value.TypeCell(job.t)); err != nil {
+ for _, depRef := range vm.main.Deps() {
+ dep, err := vm.resolveModule(depRef)
+ if err != nil {
return err
}
+
+ vm.modules = append(vm.modules, dep)
}
return nil
}
+
+// TODO: Make an actual module resolver.
+func (vm *VM) resolveModule(ref modules.ModuleRef) (modules.Module, error) {
+ // TODO: Support other modules than core.
+ switch ref.Name() {
+ case "core":
+ return core.Module, nil
+ default:
+ return modules.Module{}, fmt.Errorf("unknown module: %s", ref.Name())
+ }
+}
diff --git a/pkg/lang/vm/utils.go b/pkg/lang/vm/utils.go
index 2b7fa6f..975d31b 100644
--- a/pkg/lang/vm/utils.go
+++ b/pkg/lang/vm/utils.go
@@ -1,6 +1,7 @@
package vm
import (
+ "jinx/pkg/lang/modules"
"jinx/pkg/lang/vm/code"
"jinx/pkg/lang/vm/mem"
"jinx/pkg/lang/vm/value"
@@ -35,7 +36,7 @@ func (vm *VM) getMemCell(ptr mem.Ptr, kind mem.CellKind, allowNil bool) (mem.Cel
}
if !vm.memory.Is(ptr, kind) {
- return nil, ErrUnexpectedMemCell{Ptr: ptr, Expected: mem.CellKindEnv, Got: vm.memory.Kind(ptr)}
+ return nil, ErrUnexpectedMemCell{Ptr: ptr, Expected: kind, Got: vm.memory.Kind(ptr)}
}
cell, err := vm.memory.Get(ptr)
@@ -75,10 +76,18 @@ func (vm *VM) getMemCell(ptr mem.Ptr, kind mem.CellKind, allowNil bool) (mem.Cel
return cell, nil
}
-func (vm *VM) module() int {
+func (vm *VM) moduleID() int {
return vm.pos.Module
}
+func (vm *VM) module() modules.Module {
+ if vm.moduleID() == -1 {
+ return vm.main
+ } else {
+ return vm.modules[vm.moduleID()]
+ }
+}
+
func (vm *VM) pc() int {
return vm.pos.PC
}
diff --git a/pkg/lang/vm/value/core_ptrs.go b/pkg/lang/vm/value/core_ptrs.go
index a1f0365..adb9a6b 100644
--- a/pkg/lang/vm/value/core_ptrs.go
+++ b/pkg/lang/vm/value/core_ptrs.go
@@ -4,14 +4,90 @@ import (
"jinx/pkg/lang/vm/mem"
)
-const (
- CORE_TYPE_NULL = mem.Ptr(iota + 1)
- CORE_TYPE_INT
- CORE_TYPE_FLOAT
- CORE_TYPE_STRING
- CORE_TYPE_BOOL
- CORE_TYPE_ARRAY
- CORE_TYPE_FUNCTION
+// All CorePtrs are unitialized until the core module is loaded.
+type CorePtrs struct {
+ CoreTypeNull mem.Ptr
+ CoreTypeInt mem.Ptr
+ CoreTypeFloat mem.Ptr
+ CoreTypeString mem.Ptr
+ CoreTypeBool mem.Ptr
+ CoreTypeArray mem.Ptr
+ CoreTypeFunction mem.Ptr
- CORE_TYPE_TYPE_REF
-)
+ CoreTypeTypeRef mem.Ptr
+}
+
+func (c *CorePtrs) SetNull(p mem.Ptr) {
+ if c.CoreTypeNull != mem.NullPtr {
+ panic("core ptr null already set")
+ }
+
+ c.CoreTypeNull = p
+}
+
+func (c *CorePtrs) SetInt(p mem.Ptr) {
+ if c.CoreTypeInt != mem.NullPtr {
+ panic("core ptr int already set")
+ }
+
+ c.CoreTypeInt = p
+}
+
+func (c *CorePtrs) SetFloat(p mem.Ptr) {
+ if c.CoreTypeFloat != mem.NullPtr {
+ panic("core ptr float already set")
+ }
+
+ c.CoreTypeFloat = p
+}
+
+func (c *CorePtrs) SetString(p mem.Ptr) {
+ if c.CoreTypeString != mem.NullPtr {
+ panic("core ptr string already set")
+ }
+
+ c.CoreTypeString = p
+}
+
+func (c *CorePtrs) SetBool(p mem.Ptr) {
+ if c.CoreTypeBool != mem.NullPtr {
+ panic("core ptr bool already set")
+ }
+
+ c.CoreTypeBool = p
+}
+
+func (c *CorePtrs) SetArray(p mem.Ptr) {
+ if c.CoreTypeArray != mem.NullPtr {
+ panic("core ptr array already set")
+ }
+
+ c.CoreTypeArray = p
+}
+
+func (c *CorePtrs) SetFunction(p mem.Ptr) {
+ if c.CoreTypeFunction != mem.NullPtr {
+ panic("core ptr function already set")
+ }
+
+ c.CoreTypeFunction = p
+}
+
+func (c *CorePtrs) SetTypeRef(p mem.Ptr) {
+ if c.CoreTypeTypeRef != mem.NullPtr {
+ panic("core ptr type ref already set")
+ }
+
+ c.CoreTypeTypeRef = p
+}
+
+func (c *CorePtrs) Complete() bool {
+ return c.CoreTypeNull != mem.NullPtr &&
+ c.CoreTypeInt != mem.NullPtr &&
+ c.CoreTypeFloat != mem.NullPtr &&
+ c.CoreTypeString != mem.NullPtr &&
+ c.CoreTypeBool != mem.NullPtr &&
+ c.CoreTypeArray != mem.NullPtr &&
+ c.CoreTypeFunction != mem.NullPtr &&
+ c.CoreTypeTypeRef != mem.NullPtr
+}
diff --git a/pkg/lang/vm/value/data.go b/pkg/lang/vm/value/data.go
index 39193d5..1cd258b 100644
--- a/pkg/lang/vm/value/data.go
+++ b/pkg/lang/vm/value/data.go
@@ -116,11 +116,31 @@ func (a ArrayData) Push(m mem.Mem, v Value) error {
arr := data.(ArrayCell).Get()
arr = append(arr, v)
- m.Set(a.data, ArrayCell(arr))
+ if err := m.Set(a.data, ArrayCell(arr)); err != nil {
+ return err
+ }
return nil
}
+func (a ArrayData) Pop(m mem.Mem) (Value, error) {
+ data, err := m.Get(a.data)
+ if err != nil {
+ return Value{}, err
+ }
+
+ arr := data.(ArrayCell).Get()
+
+ popped := arr[len(arr)-1]
+ arr = arr[:len(arr)-1]
+
+ if err := m.Set(a.data, ArrayCell(arr)); err != nil {
+ return Value{}, err
+ }
+
+ return popped, nil
+}
+
type NullData struct{}
func (n NullData) String(_ mem.Mem) (string, error) {
diff --git a/pkg/lang/vm/value/value.go b/pkg/lang/vm/value/value.go
index 5ef3f07..cce2f5e 100644
--- a/pkg/lang/vm/value/value.go
+++ b/pkg/lang/vm/value/value.go
@@ -93,24 +93,24 @@ func (v Value) Type() TypeKind {
return v.t
}
-func (v Value) TypePtr() mem.Ptr {
+func (v Value) TypePtr(corePtrs *CorePtrs) mem.Ptr {
switch v.t {
case IntType:
- return CORE_TYPE_INT
+ return corePtrs.CoreTypeInt
case FloatType:
- return CORE_TYPE_FLOAT
+ return corePtrs.CoreTypeFloat
case StringType:
- return CORE_TYPE_STRING
+ return corePtrs.CoreTypeFloat
case BoolType:
- return CORE_TYPE_BOOL
+ return corePtrs.CoreTypeBool
case ArrayType:
- return CORE_TYPE_ARRAY
+ return corePtrs.CoreTypeArray
case NullType:
- return CORE_TYPE_NULL
+ return corePtrs.CoreTypeNull
case FunctionType:
- return CORE_TYPE_FUNCTION
+ return corePtrs.CoreTypeFunction
case TypeRefType:
- return CORE_TYPE_TYPE_REF
+ return corePtrs.CoreTypeTypeRef
case ObjectType:
return v.d.(ObjectData).t
default:
diff --git a/pkg/lang/vm/vm.go b/pkg/lang/vm/vm.go
index 4a4aa68..4b40633 100644
--- a/pkg/lang/vm/vm.go
+++ b/pkg/lang/vm/vm.go
@@ -1,34 +1,42 @@
package vm
import (
+ "jinx/pkg/lang/modules"
"jinx/pkg/lang/vm/code"
"jinx/pkg/lang/vm/mem"
"jinx/pkg/lang/vm/stack"
+ "jinx/pkg/lang/vm/value"
)
type VM struct {
pos code.Pos
- modules []*code.Code
+ main modules.Module
+ modules []modules.Module
stack stack.Stack
memory mem.Mem
canAddGlobals bool
globals map[string]mem.Ptr
+
+ corePtrs value.CorePtrs
}
-func New(main *code.Code, deps []*code.Code) *VM {
+func New(main modules.Module) *VM {
vm := &VM{
- pos: code.NewPos(0, 0),
+ pos: code.NewPos(-1, 0),
- modules: append([]*code.Code{main}, deps...),
+ main: main,
+ modules: []modules.Module{},
stack: stack.New(),
memory: mem.New(),
canAddGlobals: false,
globals: make(map[string]mem.Ptr),
+
+ corePtrs: value.CorePtrs{},
}
if err := vm.setup(); err != nil {
@@ -57,7 +65,7 @@ func (vm *VM) GetResult() (string, error) {
func (vm *VM) Run() error {
vm.canAddGlobals = true
- for i := 1; i < len(vm.modules); i++ {
+ for i := 0; i < len(vm.modules); i++ {
if err := vm.executeModule(i); err != nil {
return err
}
@@ -77,8 +85,13 @@ func (vm *VM) Run() error {
}
}
+ // All the core ptrs should be initialized once core is loaded
+ if !vm.corePtrs.Complete() {
+ panic("core ptrs not initialized after loading core (perhaps core is missing?)")
+ }
+
vm.canAddGlobals = false
- if err := vm.executeModule(0); err != nil {
+ if err := vm.executeModule(-1); err != nil {
return err
}
@@ -93,27 +106,27 @@ func (vm *VM) executeModule(moduleID int) error {
}
for {
- module := vm.modules[vm.module()]
+ module := vm.module()
+ code := module.Code()
- if vm.pc() >= module.Len() {
+ if vm.pc() >= code.Len() {
return nil
}
- op, advance := module.GetOp(vm.pc())
+ op, advance := code.GetOp(vm.pc())
vm.advancePC(advance)
- if decision, err := vm.step(module, op); err != nil {
+ if decision, err := vm.step(code, op); err != nil {
return Error{
- Pos: vm.pos,
- Line: module.Debug().PCToLine(vm.pc()),
- Err: err,
+ Pos: vm.pos,
+ Module: module.Name(),
+ Line: code.Debug().PCToLine(vm.pc()),
+ Err: err,
}
} else if decision == stepDecisionHalt {
return nil
}
}
-
- return nil
}
type stepDecision int
diff --git a/pkg/lang/vm/vm_test.go b/pkg/lang/vm/vm_test.go
index b25a446..17edb2e 100644
--- a/pkg/lang/vm/vm_test.go
+++ b/pkg/lang/vm/vm_test.go
@@ -1,9 +1,11 @@
package vm_test
import (
+ "jinx/pkg/lang/modules"
"jinx/pkg/lang/vm"
"jinx/pkg/lang/vm/code"
"jinx/pkg/lang/vm/text"
+ "os"
"strings"
"testing"
@@ -294,6 +296,7 @@ func TestTypeConstruct(t *testing.T) {
set_arg_count 2
call 2
+ drop 1
get_local 0
get_member "$add_method"
@@ -302,6 +305,7 @@ func TestTypeConstruct(t *testing.T) {
push_function @Cat:meow
call 2
+ drop 1
# Create a new instance of Cat
get_local 0
@@ -441,49 +445,74 @@ func TestPrimes(t *testing.T) {
}
func TestModules(t *testing.T) {
- mainSrc := `
- get_global "add_one"
- get_global "x"
- call 1
- halt
- `
+ t.Skip("We can't pass in random modules anymore, they have to go through the resolver")
- librarySrc1 := `
- push_int 41
- add_global "x"
- halt
- `
+ // mainSrc := `
+ // get_global "add_one"
+ // get_global "x"
+ // call 1
+ // halt
+ // `
- librarySrc2 := `
- push_function @add_one
- set_arg_count 1
- add_global "add_one"
- halt
+ // librarySrc1 := `
+ // push_int 41
+ // add_global "x"
+ // halt
+ // `
- @add_one:
- get_local 0
- push_int 1
- add
- ret
- `
+ // librarySrc2 := `
+ // push_function @add_one
+ // set_arg_count 1
+ // add_global "add_one"
+ // halt
- main := compile(t, mainSrc)
- library1 := compile(t, librarySrc1)
- library2 := compile(t, librarySrc2)
+ // @add_one:
+ // get_local 0
+ // push_int 1
+ // add
+ // ret
+ // `
- vm := vm.New(&main, []*code.Code{&library1, &library2})
- err := vm.Run()
- require.NoError(t, err)
+ // main := compile(t, mainSrc)
+ // library1 := compile(t, librarySrc1)
+ // library2 := compile(t, librarySrc2)
- res, err := vm.GetResult()
+ // 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 TestCoreSay(t *testing.T) {
+ src := `
+ get_global ":core:say"
+ push_string "Meow!!"
+ call 1
+ push_int 0
+ halt
+ `
+
+ previous := os.Stdout
+ r, w, _ := os.Pipe()
+ defer w.Close()
+ os.Stdout = w
+ test(t, src, "0")
+ os.Stdout = previous
+
+ result := make([]byte, 8)
+ _, err := r.Read(result)
require.NoError(t, err)
- require.Equal(t, "42", res)
+ require.Equal(t, "\"Meow!!\"", string(result))
}
func test(t *testing.T, src string, expected string) {
bc := compile(t, src)
- vm := vm.New(&bc, nil)
+ vm := vm.New(modules.NewUnknownModule(&bc))
err := vm.Run()
require.NoError(t, err)