From 86f31acf6789be116dcc54ed85b069a37c0f7aa8 Mon Sep 17 00:00:00 2001 From: Mel Date: Thu, 11 Aug 2022 01:25:47 +0000 Subject: Actual modules and core --- pkg/lang/vm/core.go | 146 ------------------------------------- pkg/lang/vm/errors.go | 11 +-- pkg/lang/vm/exe.go | 62 ++++++++++++++++ pkg/lang/vm/exec.go | 53 ++++++++++---- pkg/lang/vm/executor/executor.go | 17 +++++ pkg/lang/vm/executor/nativefunc.go | 5 ++ pkg/lang/vm/setup.go | 58 +++++++++------ pkg/lang/vm/utils.go | 13 +++- pkg/lang/vm/value/core_ptrs.go | 98 ++++++++++++++++++++++--- pkg/lang/vm/value/data.go | 22 +++++- pkg/lang/vm/value/value.go | 18 ++--- pkg/lang/vm/vm.go | 43 +++++++---- pkg/lang/vm/vm_test.go | 93 +++++++++++++++-------- 13 files changed, 379 insertions(+), 260 deletions(-) delete mode 100644 pkg/lang/vm/core.go create mode 100644 pkg/lang/vm/exe.go create mode 100644 pkg/lang/vm/executor/executor.go create mode 100644 pkg/lang/vm/executor/nativefunc.go (limited to 'pkg/lang/vm') 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) -- cgit 1.4.1