aboutsummaryrefslogtreecommitdiff
path: root/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'pkg')
-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)