aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pkg/lang/vm/code/op.go1
-rw-r--r--pkg/lang/vm/core.go93
-rw-r--r--pkg/lang/vm/errors.go6
-rw-r--r--pkg/lang/vm/exec.go113
-rw-r--r--pkg/lang/vm/mem/cell.go3
-rw-r--r--pkg/lang/vm/mem/mem.go30
-rw-r--r--pkg/lang/vm/mem/mem_test.go9
-rw-r--r--pkg/lang/vm/setup.go43
-rw-r--r--pkg/lang/vm/text/op.go1
-rw-r--r--pkg/lang/vm/utils.go2
-rw-r--r--pkg/lang/vm/value/cells.go23
-rw-r--r--pkg/lang/vm/value/core_ptrs.go15
-rw-r--r--pkg/lang/vm/value/env.go4
-rw-r--r--pkg/lang/vm/value/type.go23
-rw-r--r--pkg/lang/vm/value/value.go64
-rw-r--r--pkg/lang/vm/vm.go15
-rw-r--r--pkg/lang/vm/vm_test.go23
17 files changed, 411 insertions, 57 deletions
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)