about summary refs log tree commit diff
path: root/pkg/lang/vm
diff options
context:
space:
mode:
authorMel <einebeere@gmail.com>2022-05-29 21:42:32 +0000
committerGitHub <noreply@github.com>2022-05-29 21:42:32 +0000
commitd2f69dccb3643834a79da79be4ece189a7178c9e (patch)
tree7e32365a25f37bee199dde36dfdfef12916de46f /pkg/lang/vm
parent11bcf772bf8d9aa353eb4c04bfb85378ba392b1e (diff)
downloadjinx-d2f69dccb3643834a79da79be4ece189a7178c9e.tar.zst
jinx-d2f69dccb3643834a79da79be4ece189a7178c9e.zip
Types, Methods and basic Core Lib
Diffstat (limited to 'pkg/lang/vm')
-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)