about summary refs log tree commit diff
path: root/pkg/lang/vm
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/lang/vm')
-rw-r--r--pkg/lang/vm/code/op.go1
-rw-r--r--pkg/lang/vm/exec.go74
-rw-r--r--pkg/lang/vm/mem/cell.go3
-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.go18
-rw-r--r--pkg/lang/vm/value/data.go11
-rw-r--r--pkg/lang/vm/value/value.go21
-rw-r--r--pkg/lang/vm/vm.go9
-rw-r--r--pkg/lang/vm/vm_test.go19
10 files changed, 151 insertions, 8 deletions
diff --git a/pkg/lang/vm/code/op.go b/pkg/lang/vm/code/op.go
index 272674a..1fa8afc 100644
--- a/pkg/lang/vm/code/op.go
+++ b/pkg/lang/vm/code/op.go
@@ -21,6 +21,7 @@ const (
 	OpGetGlobal
 	OpGetLocal
 	OpGetMember
+	OpSetMember
 
 	OpGetEnv
 	OpSetEnv
diff --git a/pkg/lang/vm/exec.go b/pkg/lang/vm/exec.go
index 41b7019..d4426e1 100644
--- a/pkg/lang/vm/exec.go
+++ b/pkg/lang/vm/exec.go
@@ -47,6 +47,16 @@ func (vm *VM) execPushFunction(pc int) {
 	vm.stack.Push(value.NewFunction(pc, 0))
 }
 
+func (vm *VM) execPushObject() error {
+	obj, err := value.NewObject(vm.memory, mem.NullPtr)
+	if err != nil {
+		return err
+	}
+
+	vm.stack.Push(obj)
+	return nil
+}
+
 func (vm *VM) execGetLocal(offset int) error {
 	local, err := vm.stack.Local(int(offset))
 	if err != nil {
@@ -65,6 +75,28 @@ func (vm *VM) execGetMember(name string) error {
 		return err
 	}
 
+	if parent.Type() == value.ObjectType {
+		obj := parent.Data().(value.ObjectData)
+		ptr := obj.Ptr()
+
+		cell, err := vm.getMemCell(ptr, mem.CellKindObject, false)
+		if err != nil {
+			return err
+		}
+
+		objCell := cell.(value.ObjectCell)
+		member, ok := objCell.Get()[name]
+		if ok {
+			member = member.Clone(vm.memory)
+		} else {
+			member = value.NewNull()
+		}
+
+		vm.stack.Push(member)
+		parent.Drop(vm.memory)
+		return nil
+	}
+
 	typeCell, err := vm.getMemCell(parent.TypePtr(), mem.CellKindType, false)
 	if err != nil {
 		return err
@@ -135,6 +167,46 @@ func (vm *VM) execGetMember(name string) error {
 	return nil
 }
 
+func (vm *VM) execSetMember(name string) error {
+	parent, err := vm.stack.Pop()
+	if err != nil {
+		return err
+	}
+
+	v, err := vm.stack.Pop()
+	if err != nil {
+		return err
+	}
+
+	if parent.Type() != value.ObjectType {
+		return ErrInvalidOperandType{
+			Op: code.OpSetMember,
+			X:  parent.Type(),
+		}
+	}
+
+	ptr := parent.Data().(value.ObjectData).Ptr()
+
+	cell, err := vm.getMemCell(ptr, mem.CellKindObject, false)
+	if err != nil {
+		return err
+	}
+
+	objCell := cell.(value.ObjectCell)
+	obj := objCell.Get()
+
+	obj[name] = v
+
+	if err := vm.memory.Set(ptr, value.ObjectCell{Members: obj}); err != nil {
+		return err
+	}
+
+	parent.Drop(vm.memory)
+	// v was moved, no need to drop it.
+
+	return nil
+}
+
 func (vm *VM) execGetEnv(envIndex int) error {
 	envCell, err := vm.getMemCell(vm.stack.CurrentCallEnv(), mem.CellKindEnv, false)
 	if err != nil {
@@ -522,7 +594,7 @@ func (vm *VM) execCall(argCount uint) error {
 
 	if argCount != fn.Args() {
 		return ErrNotEnoughArguments{
-			Got:   argCount,
+			Got:    argCount,
 			Needed: fn.Args(),
 		}
 	}
diff --git a/pkg/lang/vm/mem/cell.go b/pkg/lang/vm/mem/cell.go
index 5a0e9fb..73aa0c6 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
+	CellKindObject
 	CellKindType
 
 	CellKindEnv
@@ -22,6 +23,8 @@ func (c CellKind) String() string {
 		return "string"
 	case CellKindArray:
 		return "array"
+	case CellKindObject:
+		return "object"
 	case CellKindType:
 		return "type"
 	case CellKindEnv:
diff --git a/pkg/lang/vm/text/op.go b/pkg/lang/vm/text/op.go
index 2d6eb7e..37b89dc 100644
--- a/pkg/lang/vm/text/op.go
+++ b/pkg/lang/vm/text/op.go
@@ -19,6 +19,7 @@ var (
 		code.OpGetGlobal:    "get_global",
 		code.OpGetLocal:     "get_local",
 		code.OpGetMember:    "get_member",
+		code.OpSetMember:    "set_member",
 		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 1a884d0..a40a14f 100644
--- a/pkg/lang/vm/utils.go
+++ b/pkg/lang/vm/utils.go
@@ -61,6 +61,8 @@ func (vm *VM) getMemCell(ptr mem.Ptr, kind mem.CellKind, allowNil bool) (mem.Cel
 		_, ok = cell.(value.OutletCell)
 	case mem.CellKindType:
 		_, ok = cell.(value.TypeCell)
+	case mem.CellKindObject:
+		_, ok = cell.(value.ObjectCell)
 	}
 
 	if !ok {
diff --git a/pkg/lang/vm/value/cells.go b/pkg/lang/vm/value/cells.go
index a700940..5abb032 100644
--- a/pkg/lang/vm/value/cells.go
+++ b/pkg/lang/vm/value/cells.go
@@ -31,6 +31,24 @@ func (s StringCell) Get() string {
 	return string(s)
 }
 
+type ObjectCell struct {
+	Members map[string]Value
+}
+
+func (o ObjectCell) DropCell(m mem.Mem) {
+	for _, v := range o.Members {
+		v.Drop(m)
+	}
+}
+
+func (o ObjectCell) MatchingCellKind() mem.CellKind {
+	return mem.CellKindObject
+}
+
+func (o ObjectCell) Get() map[string]Value {
+	return o.Members
+}
+
 type TypeCell Type
 
 func (t TypeCell) DropCell(m mem.Mem) {
diff --git a/pkg/lang/vm/value/data.go b/pkg/lang/vm/value/data.go
index 13716fd..9a8f7b6 100644
--- a/pkg/lang/vm/value/data.go
+++ b/pkg/lang/vm/value/data.go
@@ -150,6 +150,15 @@ func (f FunctionData) String(_ mem.Mem) (string, error) {
 		return fmt.Sprintf("<fn(%d) %d>", f.args, f.pc), nil
 	}
 }
+
+type ObjectData struct {
+	obj mem.Ptr
+}
+
+func (o ObjectData) String(_ mem.Mem) (string, error) {
+	return fmt.Sprintf("<object %v>", o.obj), nil
 }
 
-type ObjectData struct{} // TODO
+func (o ObjectData) Ptr() mem.Ptr {
+	return o.obj
+}
diff --git a/pkg/lang/vm/value/value.go b/pkg/lang/vm/value/value.go
index 05d3f09..5e222a3 100644
--- a/pkg/lang/vm/value/value.go
+++ b/pkg/lang/vm/value/value.go
@@ -24,7 +24,9 @@ func NewString(m mem.Mem, str string) (Value, error) {
 		return Value{}, err
 	}
 
-	m.Set(ptr, StringCell(str))
+	if err = m.Set(ptr, StringCell(str)); err != nil {
+		return Value{}, err
+	}
 
 	return Value{t: CORE_TYPE_STRING, d: StringData{data: ptr}}, nil
 }
@@ -39,7 +41,9 @@ func NewArray(m mem.Mem, arr []Value) (Value, error) {
 		return Value{}, err
 	}
 
-	m.Set(ptr, ArrayCell(arr))
+	if err = m.Set(ptr, ArrayCell(arr)); err != nil {
+		return Value{}, err
+	}
 
 	return Value{t: CORE_TYPE_ARRAY, d: ArrayData{data: ptr}}, nil
 }
@@ -56,8 +60,17 @@ func NewNativeFunction(f NativeFunc, args uint) Value {
 	return Value{t: CORE_TYPE_FUNCTION, d: FunctionData{native: f, args: args}}
 }
 
-func NewObject() Value {
-	panic("not implemented")
+func NewObject(m mem.Mem, t mem.Ptr) (Value, error) {
+	ptr, err := m.Allocate(mem.CellKindObject)
+	if err != nil {
+		return Value{}, err
+	}
+
+	if err = m.Set(ptr, ObjectCell{make(map[string]Value)}); err != nil {
+		return Value{}, err
+	}
+
+	return Value{t: t, d: ObjectData{obj: ptr}}, nil
 }
 
 func (v Value) TypePtr() mem.Ptr {
diff --git a/pkg/lang/vm/vm.go b/pkg/lang/vm/vm.go
index 2377d89..8422299 100644
--- a/pkg/lang/vm/vm.go
+++ b/pkg/lang/vm/vm.go
@@ -89,7 +89,7 @@ func (vm *VM) step(op code.Op) (stepDecision, error) {
 		str, advance := vm.code.GetString(vm.pc)
 		vm.pc += advance
 
-		vm.execPushString(str)
+		err = vm.execPushString(str)
 	case code.OpPushNull:
 		vm.execPushNull()
 	case code.OpPushTrue:
@@ -104,7 +104,7 @@ func (vm *VM) step(op code.Op) (stepDecision, error) {
 
 		vm.execPushFunction(int(x))
 	case code.OpPushObject:
-		panic("not implemented")
+		err = vm.execPushObject()
 
 	case code.OpDrop:
 		_, err = vm.stack.Pop()
@@ -121,6 +121,11 @@ func (vm *VM) step(op code.Op) (stepDecision, error) {
 		vm.pc += advance
 
 		err = vm.execGetMember(name)
+	case code.OpSetMember:
+		name, advance := vm.code.GetString(vm.pc)
+		vm.pc += advance
+
+		err = vm.execSetMember(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 9f6efc0..287e7a8 100644
--- a/pkg/lang/vm/vm_test.go
+++ b/pkg/lang/vm/vm_test.go
@@ -210,6 +210,25 @@ func TestMember(t *testing.T) {
 	test(t, src, "3")
 }
 
+func TestObject(t *testing.T) {
+	src := `
+	push_object
+
+	push_string "Petronij"
+	get_local 0
+	set_member "name"
+
+	push_int 10
+	get_local 0
+	set_member "age"
+
+	get_local 0
+	get_member "name"
+	`
+
+	test(t, src, "\"Petronij\"")
+}
+
 func test(t *testing.T, src string, expected string) {
 	bc := compile(t, src)
 	vm := vm.New(&bc)