From d2f69dccb3643834a79da79be4ece189a7178c9e Mon Sep 17 00:00:00 2001 From: Mel Date: Sun, 29 May 2022 21:42:32 +0000 Subject: Types, Methods and basic Core Lib --- pkg/lang/vm/code/op.go | 1 - pkg/lang/vm/core.go | 93 +++++++++++++++++++++++++++++++++ pkg/lang/vm/errors.go | 6 +-- pkg/lang/vm/exec.go | 113 ++++++++++++++++++++++++++++++++++------- pkg/lang/vm/mem/cell.go | 3 ++ pkg/lang/vm/mem/mem.go | 30 +++++++++++ pkg/lang/vm/mem/mem_test.go | 9 ++++ pkg/lang/vm/setup.go | 43 ++++++++++++++++ pkg/lang/vm/text/op.go | 1 - pkg/lang/vm/utils.go | 2 + pkg/lang/vm/value/cells.go | 23 +++++++++ pkg/lang/vm/value/core_ptrs.go | 15 ++++++ pkg/lang/vm/value/env.go | 4 ++ pkg/lang/vm/value/type.go | 23 ++++++--- pkg/lang/vm/value/value.go | 64 ++++++++++++++--------- pkg/lang/vm/vm.go | 15 ++++-- pkg/lang/vm/vm_test.go | 23 +++++++++ 17 files changed, 411 insertions(+), 57 deletions(-) create mode 100644 pkg/lang/vm/core.go create mode 100644 pkg/lang/vm/setup.go create mode 100644 pkg/lang/vm/value/core_ptrs.go diff --git a/pkg/lang/vm/code/op.go b/pkg/lang/vm/code/op.go index a0214ac..6f9983a 100644 --- a/pkg/lang/vm/code/op.go +++ b/pkg/lang/vm/code/op.go @@ -22,7 +22,6 @@ const ( OpGetGlobal OpGetLocal OpGetMember - OpGetMethod OpGetEnv OpSetEnv diff --git a/pkg/lang/vm/core.go b/pkg/lang/vm/core.go new file mode 100644 index 0000000..e482211 --- /dev/null +++ b/pkg/lang/vm/core.go @@ -0,0 +1,93 @@ +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), + }, + } +} + +func (vm *VM) createCoreFunctionType() value.Type { + return value.Type{ + Kind: value.FunctionType, + } +} + +func (vm *VM) coreArrayLength(args []value.Value) (value.Value, error) { + a, err := vm.getThis() + if err != nil { + return value.Value{}, err + } + + switch a.Type() { + case value.ArrayType: + arr := a.Data().(value.ArrayData) + len, err := arr.Len(vm.memory) + if err != nil { + return value.Value{}, err + } + res := value.NewInt(int64(len)) + return res, nil + default: + return value.Value{}, ErrInvalidOperandType{ + Op: code.OpTempArrLen, + X: a.Type(), + } + } +} + +func makeCoreFn(f value.NativeFunc) value.FunctionData { + return value.NewNativeFunction(f).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 a717c09..5423070 100644 --- a/pkg/lang/vm/errors.go +++ b/pkg/lang/vm/errors.go @@ -66,7 +66,7 @@ func (e ErrCorruptedMemCell) Error() string { type ErrInvalidOperandType struct { Op code.Op - X value.Type + X value.TypeKind } func (e ErrInvalidOperandType) Error() string { @@ -75,8 +75,8 @@ func (e ErrInvalidOperandType) Error() string { type ErrInvalidOperandTypes struct { Op code.Op - X value.Type - Y value.Type + X value.TypeKind + Y value.TypeKind } func (e ErrInvalidOperandTypes) Error() string { diff --git a/pkg/lang/vm/exec.go b/pkg/lang/vm/exec.go index 10ac604..79016fa 100644 --- a/pkg/lang/vm/exec.go +++ b/pkg/lang/vm/exec.go @@ -58,6 +58,82 @@ func (vm *VM) execGetLocal(offset int) error { return nil } +func (vm *VM) execGetMember(name string) error { + parent, err := vm.stack.Pop() + if err != nil { + return err + } + + typeCell, err := vm.getMemCell(parent.TypePtr(), mem.CellKindType, false) + if err != nil { + return err + } + + t := typeCell.(value.TypeCell).Get() + + member, ok := t.GetMethod(name) + if !ok { + vm.stack.Push(value.NewNull()) + return nil + } + + if parent.Outlet().IsNull() { + outletPtr, err := vm.memory.Allocate(mem.CellKindOutlet) + if err != nil { + return err + } + parent = parent.WithOutlet(outletPtr) + } + + var envPtr mem.Ptr + if member.Env().IsNull() { + envPtr, err = vm.memory.Allocate(mem.CellKindEnv) + if err != nil { + return err + } + + newEnv := value.NewEnv() + newEnv.Add(0, parent.Outlet()) // stackIndex can be 0, because the parent will be dropped when the function returns. + vm.memory.Set(envPtr, value.EnvCell(newEnv)) + + member = member.WithEnv(envPtr) + } else { + // Clone the environment, with the parent at env 0. + oldEnvCell, err := vm.getMemCell(member.Env(), mem.CellKindEnv, false) + if err != nil { + return err + } + + oldEnv := oldEnvCell.(value.EnvCell).Get() + newEnv := value.NewEnv() + + newEnv.Add(0, parent.Outlet()) + + for i := 0; i < oldEnv.Len(); i++ { + differentOutlet := oldEnv.GetOutlet(i) + if err = vm.memory.Retain(differentOutlet); err != nil { + return err + } + + newEnv.Add(oldEnv.GetStackIndex(i), differentOutlet) + } + + envPtr, err = vm.memory.Allocate(mem.CellKindEnv) + if err != nil { + return err + } + vm.memory.Set(envPtr, value.EnvCell(newEnv)) + member = member.WithEnv(envPtr) + } + + val := value.NewFunction(0).WithData(member) + vm.stack.Push(val) + + parent.Drop(vm.memory) + + return nil +} + func (vm *VM) execGetEnv(envIndex int) error { envCell, err := vm.getMemCell(vm.stack.CurrentCallEnv(), mem.CellKindEnv, false) if err != nil { @@ -136,7 +212,7 @@ func (vm *VM) execAddToEnv(localIndex int) error { return err } - if f.Type().Kind != value.FunctionType { + if f.Type() != value.FunctionType { return ErrInvalidOperandType{ Op: code.OpAddToEnv, X: f.Type(), @@ -206,10 +282,10 @@ func (vm *VM) execAdd() error { var res value.Value - switch x.Type().Kind { + switch x.Type() { case value.IntType: xv := x.Data().(value.IntData).Get() - switch y.Type().Kind { + switch y.Type() { case value.IntType: yv := y.Data().(value.IntData).Get() res = value.NewInt(xv + yv) @@ -225,7 +301,7 @@ func (vm *VM) execAdd() error { } case value.FloatType: xv := x.Data().(value.FloatData).Get() - switch y.Type().Kind { + switch y.Type() { case value.IntType: yv := y.Data().(value.IntData).Get() res = value.NewFloat(xv + float64(yv)) @@ -240,7 +316,7 @@ func (vm *VM) execAdd() error { } } case value.StringType: - switch y.Type().Kind { + switch y.Type() { case value.StringType: panic("not implemented") // res = value.NewString(x.Data().(value.StringData).Get() + y.Data().(value.StringData).Get()) @@ -275,10 +351,10 @@ func (vm *VM) execSub() error { var res value.Value - switch x.Type().Kind { + switch x.Type() { case value.IntType: xv := x.Data().(value.IntData).Get() - switch y.Type().Kind { + switch y.Type() { case value.IntType: yv := y.Data().(value.IntData).Get() res = value.NewInt(xv - yv) @@ -294,7 +370,7 @@ func (vm *VM) execSub() error { } case value.FloatType: xv := x.Data().(value.FloatData).Get() - switch y.Type().Kind { + switch y.Type() { case value.IntType: yv := y.Data().(value.IntData).Get() res = value.NewFloat(xv - float64(yv)) @@ -330,10 +406,10 @@ func (vm *VM) execIndex() error { return err } - switch v.Type().Kind { + switch v.Type() { case value.ArrayType: arr := v.Data().(value.ArrayData) - switch i.Type().Kind { + switch i.Type() { case value.IntType: idx := i.Data().(value.IntData).Get() len, err := arr.Len(vm.memory) @@ -383,10 +459,10 @@ func (vm *VM) execLte() error { var res value.Value - switch x.Type().Kind { + switch x.Type() { case value.IntType: xv := x.Data().(value.IntData).Get() - switch y.Type().Kind { + switch y.Type() { case value.IntType: yv := y.Data().(value.IntData).Get() res = value.NewBool(xv <= yv) @@ -402,7 +478,7 @@ func (vm *VM) execLte() error { } case value.FloatType: xv := x.Data().(value.FloatData).Get() - switch y.Type().Kind { + switch y.Type() { case value.IntType: yv := y.Data().(value.IntData).Get() res = value.NewBool(xv <= float64(yv)) @@ -434,7 +510,7 @@ func (vm *VM) execCall() error { return err } - if f.Type().Kind != value.FunctionType { + if f.Type() != value.FunctionType { return ErrInvalidOperandType{ Op: code.OpCall, X: f.Type(), @@ -471,7 +547,7 @@ func (vm *VM) execJumpIf(pc int, cond bool) error { return err } - switch b.Type().Kind { + switch b.Type() { case value.BoolType: bl := b.Data().(value.BoolData) if bl.Get() == cond { @@ -511,12 +587,12 @@ func (vm *VM) execRet() error { } func (vm *VM) execTempArrLen() error { - a, err := vm.popAndDrop() + a, err := vm.stack.Pop() if err != nil { return err } - switch a.Type().Kind { + switch a.Type() { case value.ArrayType: arr := a.Data().(value.ArrayData) len, err := arr.Len(vm.memory) @@ -525,6 +601,7 @@ func (vm *VM) execTempArrLen() error { } res := value.NewInt(int64(len)) vm.stack.Push(res) + a.Drop(vm.memory) default: return ErrInvalidOperandTypes{ Op: code.OpTempArrLen, @@ -545,7 +622,7 @@ func (vm *VM) execTempArrPush() error { return err } - switch a.Type().Kind { + switch a.Type() { case value.ArrayType: arr := a.Data().(value.ArrayData) arr.Push(vm.memory, e) diff --git a/pkg/lang/vm/mem/cell.go b/pkg/lang/vm/mem/cell.go index 562d4da..5a0e9fb 100644 --- a/pkg/lang/vm/mem/cell.go +++ b/pkg/lang/vm/mem/cell.go @@ -6,6 +6,7 @@ const ( CellKindEmpty CellKind = iota CellKindString CellKindArray + CellKindType CellKindEnv CellKindOutlet @@ -21,6 +22,8 @@ func (c CellKind) String() string { return "string" case CellKindArray: return "array" + case CellKindType: + return "type" case CellKindEnv: return "env" case CellKindOutlet: diff --git a/pkg/lang/vm/mem/mem.go b/pkg/lang/vm/mem/mem.go index 0f15743..6c1d58d 100644 --- a/pkg/lang/vm/mem/mem.go +++ b/pkg/lang/vm/mem/mem.go @@ -2,6 +2,7 @@ package mem type Mem interface { Allocate(kind CellKind) (Ptr, error) + AllocateAt(ptr Ptr, kind CellKind) error Set(ptr Ptr, v CellData) error Get(ptr Ptr) (CellData, error) @@ -55,7 +56,36 @@ func (m *memImpl) Allocate(kind CellKind) (Ptr, error) { m.cells = append(m.cells, cell{kind: kind, refs: 1}) return Ptr(idx), nil } +} + +func (m *memImpl) AllocateAt(ptr Ptr, kind CellKind) error { + if kind == CellKindForbidden || kind == CellKindEmpty { + return ErrInvalidCellKind{kind} + } + + if ptr < Ptr(len(m.cells)) && m.cells[ptr].kind != CellKindEmpty { + return ErrInvalidMemAccess{ptr} + } + + if ptr > 10000 { + return ErrMemOverflow + } + if ptr >= Ptr(len(m.cells)) { + m.cells = append(m.cells, make([]cell, ptr-Ptr(len(m.cells))+1)...) + } + + m.cells[ptr] = cell{kind: kind, refs: 1} + + // Remove cell from free list, if it was there. + for i, f := range m.free { + if f == ptr { + m.free = append(m.free[:i], m.free[i+1:]...) + break + } + } + + return nil } func (m *memImpl) Set(ptr Ptr, v CellData) error { diff --git a/pkg/lang/vm/mem/mem_test.go b/pkg/lang/vm/mem/mem_test.go index a75753c..f916e49 100644 --- a/pkg/lang/vm/mem/mem_test.go +++ b/pkg/lang/vm/mem/mem_test.go @@ -21,6 +21,15 @@ func TestAllocation(t *testing.T) { assert.Equal(t, two, mem.Ptr(2)) } +func TestSpecificAllocation(t *testing.T) { + m := mem.New() + + assert.NoError(t, m.AllocateAt(mem.Ptr(123), mem.CellKindString)) + assert.Error(t, m.AllocateAt(mem.NullPtr, mem.CellKindString)) + assert.Error(t, m.AllocateAt(mem.Ptr(124), mem.CellKindEmpty)) + assert.Error(t, m.AllocateAt(mem.Ptr(123), mem.CellKindArray)) +} + func TestGetSet(t *testing.T) { m := mem.New() diff --git a/pkg/lang/vm/setup.go b/pkg/lang/vm/setup.go new file mode 100644 index 0000000..754fd3a --- /dev/null +++ b/pkg/lang/vm/setup.go @@ -0,0 +1,43 @@ +package vm + +import ( + "jinx/pkg/lang/vm/mem" + "jinx/pkg/lang/vm/value" +) + +func (vm *VM) setup() error { + if err := vm.setupCoreLib(); err != nil { + return err + } + + return nil +} + +func (vm *VM) setupCoreLib() error { + type allocationJob struct { + at mem.Ptr + t value.Type + } + + 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()}, + } + + for _, job := range toAllocate { + if err := vm.memory.AllocateAt(job.at, mem.CellKindType); err != nil { + return err + } + + if err := vm.memory.Set(job.at, value.TypeCell(job.t)); err != nil { + return err + } + } + + return nil +} diff --git a/pkg/lang/vm/text/op.go b/pkg/lang/vm/text/op.go index dc6ac1e..4eb54a1 100644 --- a/pkg/lang/vm/text/op.go +++ b/pkg/lang/vm/text/op.go @@ -20,7 +20,6 @@ var ( code.OpGetGlobal: "get_global", code.OpGetLocal: "get_local", code.OpGetMember: "get_member", - code.OpGetMethod: "get_method", code.OpGetEnv: "get_env", code.OpSetEnv: "set_env", code.OpAddToEnv: "add_to_env", diff --git a/pkg/lang/vm/utils.go b/pkg/lang/vm/utils.go index 11c195e..1a884d0 100644 --- a/pkg/lang/vm/utils.go +++ b/pkg/lang/vm/utils.go @@ -59,6 +59,8 @@ func (vm *VM) getMemCell(ptr mem.Ptr, kind mem.CellKind, allowNil bool) (mem.Cel _, ok = cell.(value.EnvCell) case mem.CellKindOutlet: _, ok = cell.(value.OutletCell) + case mem.CellKindType: + _, ok = cell.(value.TypeCell) } if !ok { diff --git a/pkg/lang/vm/value/cells.go b/pkg/lang/vm/value/cells.go index 116fa5a..146402d 100644 --- a/pkg/lang/vm/value/cells.go +++ b/pkg/lang/vm/value/cells.go @@ -31,6 +31,29 @@ func (s StringCell) Get() string { return string(s) } +type TypeCell Type + +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).WithData(f) + val.Drop(m) + } + + for _, v := range typ.Statics { + v.Drop(m) + } +} + +func (t TypeCell) MatchingCellKind() mem.CellKind { + return mem.CellKindType +} + +func (t TypeCell) Get() Type { + return Type(t) +} + type OutletCell Value func (o OutletCell) DropCell(m mem.Mem) { diff --git a/pkg/lang/vm/value/core_ptrs.go b/pkg/lang/vm/value/core_ptrs.go new file mode 100644 index 0000000..5462a8f --- /dev/null +++ b/pkg/lang/vm/value/core_ptrs.go @@ -0,0 +1,15 @@ +package value + +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 +) diff --git a/pkg/lang/vm/value/env.go b/pkg/lang/vm/value/env.go index b7bb3ab..34a396c 100644 --- a/pkg/lang/vm/value/env.go +++ b/pkg/lang/vm/value/env.go @@ -27,6 +27,10 @@ func (e *Env) GetStackIndex(envIndex int) int { return e.references[envIndex].stackIndex } +func (e *Env) Len() int { + return len(e.references) +} + type reference struct { stackIndex int outlet mem.Ptr diff --git a/pkg/lang/vm/value/type.go b/pkg/lang/vm/value/type.go index f294aaf..4bda3c1 100644 --- a/pkg/lang/vm/value/type.go +++ b/pkg/lang/vm/value/type.go @@ -14,12 +14,8 @@ const ( ObjectType ) -type Type struct { - Kind TypeKind -} - -func (t Type) String() string { - switch t.Kind { +func (t TypeKind) String() string { + switch t { case IntType: return "int" case FloatType: @@ -40,3 +36,18 @@ func (t Type) String() string { panic("invalid type kind") } + +type Type struct { + Kind TypeKind + Methods map[string]FunctionData + Statics map[string]Value +} + +func (t *Type) GetMethod(name string) (FunctionData, bool) { + if t.Methods == nil { + return FunctionData{}, false + } + + method, ok := t.Methods[name] + return method, ok +} diff --git a/pkg/lang/vm/value/value.go b/pkg/lang/vm/value/value.go index cce0f1b..eb63503 100644 --- a/pkg/lang/vm/value/value.go +++ b/pkg/lang/vm/value/value.go @@ -5,24 +5,20 @@ import ( ) type Value struct { - t Type + t mem.Ptr d Data outlet mem.Ptr } func NewInt(x int64) Value { - t := Type{Kind: IntType} - return Value{t: t, d: IntData(x)} + return Value{t: CORE_TYPE_INT, d: IntData(x)} } func NewFloat(x float64) Value { - t := Type{Kind: FloatType} - return Value{t: t, d: FloatData(x)} + return Value{t: CORE_TYPE_FLOAT, d: FloatData(x)} } func NewString(m mem.Mem, str string) (Value, error) { - t := Type{Kind: StringType} - ptr, err := m.Allocate(mem.CellKindString) if err != nil { return Value{}, err @@ -30,17 +26,14 @@ func NewString(m mem.Mem, str string) (Value, error) { m.Set(ptr, StringCell(str)) - return Value{t: t, d: StringData{data: ptr}}, nil + return Value{t: CORE_TYPE_STRING, d: StringData{data: ptr}}, nil } func NewBool(b bool) Value { - t := Type{Kind: BoolType} - return Value{t: t, d: BoolData(b)} + return Value{t: CORE_TYPE_BOOL, d: BoolData(b)} } func NewArray(m mem.Mem, arr []Value) (Value, error) { - t := Type{Kind: ArrayType} - ptr, err := m.Allocate(mem.CellKindArray) if err != nil { return Value{}, err @@ -48,27 +41,50 @@ func NewArray(m mem.Mem, arr []Value) (Value, error) { m.Set(ptr, ArrayCell(arr)) - return Value{t: t, d: ArrayData{data: ptr}}, nil + return Value{t: CORE_TYPE_ARRAY, d: ArrayData{data: ptr}}, nil } func NewNull() Value { - t := Type{Kind: NullType} - return Value{t: t} + return Value{t: CORE_TYPE_NULL} } func NewFunction(pc int) Value { - t := Type{Kind: FunctionType} - return Value{t: t, d: FunctionData{pc: pc}} + return Value{t: CORE_TYPE_FUNCTION, d: FunctionData{pc: pc}} +} + +func NewNativeFunction(f NativeFunc) Value { + return Value{t: CORE_TYPE_FUNCTION, d: FunctionData{native: f}} } func NewObject() Value { panic("not implemented") } -func (v Value) Type() Type { +func (v Value) TypePtr() mem.Ptr { return v.t } +func (v Value) Type() TypeKind { + switch v.t { + case CORE_TYPE_NULL: + return NullType + case CORE_TYPE_INT: + return IntType + case CORE_TYPE_FLOAT: + return FloatType + case CORE_TYPE_STRING: + return StringType + case CORE_TYPE_BOOL: + return BoolType + case CORE_TYPE_ARRAY: + return ArrayType + case CORE_TYPE_FUNCTION: + return FunctionType + default: + return ObjectType + } +} + func (v Value) Data() Data { return v.d } @@ -90,17 +106,17 @@ func (v Value) IsEmpty() bool { } func (v Value) Clone(m mem.Mem) Value { - if v.t.Kind == StringType { + if v.t == CORE_TYPE_STRING { str := v.d.(StringData) m.Retain(str.data) } - if v.t.Kind == ArrayType { + if v.t == CORE_TYPE_ARRAY { arr := v.d.(ArrayData) m.Retain(arr.data) } - if v.t.Kind == FunctionType { + if v.t == CORE_TYPE_FUNCTION { fn := v.d.(FunctionData) m.Retain(fn.env) } @@ -115,17 +131,17 @@ func (v Value) Drop(m mem.Mem) { return } - if v.t.Kind == StringType { + if v.t == CORE_TYPE_STRING { str := v.d.(StringData) m.Release(str.data) } - if v.t.Kind == ArrayType { + if v.t == CORE_TYPE_ARRAY { arr := v.d.(ArrayData) m.Release(arr.data) } - if v.t.Kind == FunctionType { + if v.t == CORE_TYPE_FUNCTION { f := v.d.(FunctionData) m.Release(f.env) } diff --git a/pkg/lang/vm/vm.go b/pkg/lang/vm/vm.go index 2ac689b..d4a1366 100644 --- a/pkg/lang/vm/vm.go +++ b/pkg/lang/vm/vm.go @@ -14,12 +14,18 @@ type VM struct { } func New(code *code.Code) *VM { - return &VM{ + vm := &VM{ code: code, pc: 0, stack: stack.New(), memory: mem.New(), } + + if err := vm.setup(); err != nil { + panic(err) + } + + return vm } func (vm *VM) GetResult() (string, error) { @@ -116,9 +122,10 @@ func (vm *VM) step(op code.Op) (stepDecision, error) { err = vm.execGetLocal(int(offset)) case code.OpGetMember: - panic("not implemented") - case code.OpGetMethod: - panic("not implemented") + name, advance := vm.code.GetString(vm.pc) + vm.pc += advance + + err = vm.execGetMember(name) case code.OpGetEnv: envIndex, advance := vm.code.GetUint(vm.pc) diff --git a/pkg/lang/vm/vm_test.go b/pkg/lang/vm/vm_test.go index fd4f961..53b7c3c 100644 --- a/pkg/lang/vm/vm_test.go +++ b/pkg/lang/vm/vm_test.go @@ -185,6 +185,29 @@ func TestEscapedEnv(t *testing.T) { test(t, src, "[1, 2, 3]") } +func TestMember(t *testing.T) { + src := ` + push_array + + push_int 1 + get_local 0 + temp_arr_push + + push_int 2 + get_local 0 + temp_arr_push + + push_int 3 + get_local 0 + temp_arr_push + + get_member "length" + call + ` + + test(t, src, "3") +} + func test(t *testing.T, src string, expected string) { bc := compile(t, src) vm := vm.New(&bc) -- cgit 1.4.1