about summary refs log tree commit diff
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.go98
-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.go93
13 files changed, 379 insertions, 260 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
-
-	CORE_TYPE_TYPE_REF
-)
+// 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
+
+	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
-	`
-
-	librarySrc1 := `
-	push_int 41
-	add_global "x"
-	halt
-	`
+	t.Skip("We can't pass in random modules anymore, they have to go through the resolver")
+
+	// 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)
+}
 
-	librarySrc2 := `
-	push_function @add_one
-	set_arg_count 1
-	add_global "add_one"
+func TestCoreSay(t *testing.T) {
+	src := `
+	get_global ":core:say"
+	push_string "Meow!!"
+	call 1
+	push_int 0
 	halt
-
-	@add_one:
-	get_local 0
-	push_int 1
-	add
-	ret
 	`
 
-	main := compile(t, mainSrc)
-	library1 := compile(t, librarySrc1)
-	library2 := compile(t, librarySrc2)
+	previous := os.Stdout
+	r, w, _ := os.Pipe()
+	defer w.Close()
+	os.Stdout = w
+	test(t, src, "0")
+	os.Stdout = previous
 
-	vm := vm.New(&main, []*code.Code{&library1, &library2})
-	err := vm.Run()
-	require.NoError(t, err)
-
-	res, err := vm.GetResult()
+	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)