summaryrefslogtreecommitdiff
path: root/vm
diff options
context:
space:
mode:
authorMarc Vertes <mvertes@free.fr>2024-03-11 08:46:20 +0100
committerGitHub <noreply@github.com>2024-03-11 08:46:20 +0100
commit73eb1891da7171f2bea0d3eb36e3458f267e6f19 (patch)
treefeacda8284413bcf2e7f5c712882cfe41ebe1594 /vm
parente02428ebd435dd2366231918f2388ab4fccf50c8 (diff)
parent8c4fa9d85cd274439dbd7d0a5c699fe1cea557dc (diff)
Merge pull request #2 from mvertes/vm-type
feat: add type representation in vm package
Diffstat (limited to 'vm')
-rw-r--r--vm/type.go74
-rw-r--r--vm/vm.go88
-rw-r--r--vm/vm_test.go12
3 files changed, 123 insertions, 51 deletions
diff --git a/vm/type.go b/vm/type.go
new file mode 100644
index 0000000..d29de48
--- /dev/null
+++ b/vm/type.go
@@ -0,0 +1,74 @@
+package vm
+
+import "reflect"
+
+// Runtime type and value representations (based on reflect).
+
+// Type is the representation of a runtime type.
+type Type struct {
+ Name string
+ Rtype reflect.Type
+}
+
+func (t *Type) Elem() *Type {
+ return &Type{Rtype: t.Rtype.Elem()}
+}
+
+func (t *Type) Out(i int) *Type {
+ return &Type{Rtype: t.Rtype.Out(i)}
+}
+
+// Value is the representation of a runtime value.
+type Value struct {
+ Type *Type
+ Data reflect.Value
+}
+
+// NewValue returns an addressable zero value for the specified type.
+func NewValue(typ *Type) Value {
+ return Value{Type: typ, Data: reflect.New(typ.Rtype).Elem()}
+}
+
+// TypeOf returns the runtime type of v.
+func TypeOf(v any) *Type {
+ t := reflect.TypeOf(v)
+ return &Type{Name: t.Name(), Rtype: t}
+}
+
+// ValueOf returns the runtime value of v.
+func ValueOf(v any) Value {
+ return Value{Data: reflect.ValueOf(v)}
+}
+
+func PointerTo(t *Type) *Type {
+ return &Type{Rtype: reflect.PointerTo(t.Rtype)}
+}
+
+func ArrayOf(size int, t *Type) *Type {
+ return &Type{Rtype: reflect.ArrayOf(size, t.Rtype)}
+}
+
+func SliceOf(t *Type) *Type {
+ return &Type{Rtype: reflect.SliceOf(t.Rtype)}
+}
+
+func FuncOf(arg, ret []*Type, variadic bool) *Type {
+ a := make([]reflect.Type, len(arg))
+ for i, e := range arg {
+ a[i] = e.Rtype
+ }
+ r := make([]reflect.Type, len(ret))
+ for i, e := range ret {
+ r[i] = e.Rtype
+ }
+ return &Type{Rtype: reflect.FuncOf(a, r, variadic)}
+}
+
+func StructOf(fields []*Type) *Type {
+ rf := make([]reflect.StructField, len(fields))
+ for i, f := range fields {
+ rf[i].Name = "X" + f.Name
+ rf[i].Type = f.Rtype
+ }
+ return &Type{Rtype: reflect.StructOf(rf)}
+}
diff --git a/vm/vm.go b/vm/vm.go
index f282367..a8c1f28 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -89,10 +89,10 @@ type Code [][]int64
// Machine represents a virtual machine.
type Machine struct {
- code Code // code to execute
- mem []reflect.Value // memory, as a stack
- ip, fp int // instruction and frame pointer
- ic uint64 // instruction counter, incremented at each instruction executed
+ code Code // code to execute
+ mem []Value // memory, as a stack
+ ip, fp int // instruction and frame pointer
+ ic uint64 // instruction counter, incremented at each instruction executed
// flags uint // to set options such as restrict CallX, etc...
}
@@ -123,27 +123,27 @@ func (m *Machine) Run() (err error) {
ic++
switch op := code[ip]; op[1] {
case Add:
- mem[sp-2] = reflect.ValueOf(int(mem[sp-2].Int() + mem[sp-1].Int()))
+ mem[sp-2] = ValueOf(int(mem[sp-2].Data.Int() + mem[sp-1].Data.Int()))
mem = mem[:sp-1]
case Mul:
- mem[sp-2] = reflect.ValueOf(int(mem[sp-2].Int() * mem[sp-1].Int()))
+ mem[sp-2] = ValueOf(int(mem[sp-2].Data.Int() * mem[sp-1].Data.Int()))
mem = mem[:sp-1]
case Addr:
- mem[sp-1] = mem[sp-1].Addr()
+ mem[sp-1].Data = mem[sp-1].Data.Addr()
case Assign:
- mem[op[2]].Set(mem[sp-1])
+ mem[op[2]].Data.Set(mem[sp-1].Data)
mem = mem[:sp-1]
case Fassign:
- mem[fp+int(op[2])-1].Set(mem[sp-1])
+ mem[fp+int(op[2])-1].Data.Set(mem[sp-1].Data)
mem = mem[:sp-1]
case Call:
- nip := int(mem[sp-1].Int())
- mem = append(mem[:sp-1], reflect.ValueOf(ip+1), reflect.ValueOf(fp))
+ nip := int(mem[sp-1].Data.Int())
+ mem = append(mem[:sp-1], ValueOf(ip+1), ValueOf(fp))
ip = nip
fp = sp + 1
continue
case Calli:
- mem = append(mem, reflect.ValueOf(ip+1), reflect.ValueOf(fp))
+ mem = append(mem, ValueOf(ip+1), ValueOf(fp))
fp = sp + 2
ip += int(op[2])
continue
@@ -151,57 +151,57 @@ func (m *Machine) Run() (err error) {
l := int(op[2])
in := make([]reflect.Value, l)
for i := range in {
- in[i] = mem[sp-2-i]
+ in[i] = mem[sp-2-i].Data
}
- f := mem[sp-1]
+ f := mem[sp-1].Data
mem = mem[:sp-l-1]
for _, v := range f.Call(in) {
- mem = append(mem, v)
+ mem = append(mem, Value{Data: v})
}
case Deref:
- mem[sp-1] = mem[sp-1].Elem()
+ mem[sp-1].Data = mem[sp-1].Data.Elem()
case Dup:
mem = append(mem, mem[int(op[2])])
case New:
- mem[int(op[2])+fp-1] = reflect.New(mem[int(op[3])].Type()).Elem()
+ mem[int(op[2])+fp-1] = NewValue(mem[int(op[3])].Type)
case Equal:
- mem[sp-2] = reflect.ValueOf(mem[sp-2].Equal(mem[sp-1]))
+ mem[sp-2] = ValueOf(mem[sp-2].Data.Equal(mem[sp-1].Data))
mem = mem[:sp-1]
case EqualSet:
- if mem[sp-2].Equal(mem[sp-1]) {
+ if mem[sp-2].Data.Equal(mem[sp-1].Data) {
// If equal then lhs and rhs are popped, replaced by test result, as in Equal.
- mem[sp-2] = reflect.ValueOf(true)
+ mem[sp-2] = ValueOf(true)
mem = mem[:sp-1]
} else {
// If not equal then the lhs is let on stack for further processing.
// This is used to simplify bytecode in case clauses of switch statments.
- mem[sp-1] = reflect.ValueOf(false)
+ mem[sp-1] = ValueOf(false)
}
case Exit:
return err
case Fdup:
mem = append(mem, mem[int(op[2])+fp-1])
case Field:
- mem[sp-1] = mem[sp-1].FieldByIndex(slint(op[2:]))
+ mem[sp-1].Data = mem[sp-1].Data.FieldByIndex(slint(op[2:]))
case Jump:
ip += int(op[2])
continue
case JumpTrue:
- cond := mem[sp-1].Bool()
+ cond := mem[sp-1].Data.Bool()
mem = mem[:sp-1]
if cond {
ip += int(op[2])
continue
}
case JumpFalse:
- cond := mem[sp-1].Bool()
+ cond := mem[sp-1].Data.Bool()
mem = mem[:sp-1]
if !cond {
ip += int(op[2])
continue
}
case JumpSetTrue:
- cond := mem[sp-1].Bool()
+ cond := mem[sp-1].Data.Bool()
if cond {
ip += int(op[2])
// Note that stack is not modified if cond is true
@@ -209,7 +209,7 @@ func (m *Machine) Run() (err error) {
}
mem = mem[:sp-1]
case JumpSetFalse:
- cond := mem[sp-1].Bool()
+ cond := mem[sp-1].Data.Bool()
if !cond {
ip += int(op[2])
// Note that stack is not modified if cond is false
@@ -217,39 +217,39 @@ func (m *Machine) Run() (err error) {
}
mem = mem[:sp-1]
case Greater:
- mem[sp-2] = reflect.ValueOf(mem[sp-1].Int() > mem[sp-2].Int())
+ mem[sp-2] = ValueOf(mem[sp-1].Data.Int() > mem[sp-2].Data.Int())
mem = mem[:sp-1]
case Lower:
- mem[sp-2] = reflect.ValueOf(mem[sp-1].Int() < mem[sp-2].Int())
+ mem[sp-2] = ValueOf(mem[sp-1].Data.Int() < mem[sp-2].Data.Int())
mem = mem[:sp-1]
case Loweri:
- mem[sp-1] = reflect.ValueOf(mem[sp-1].Int() < op[2])
+ mem[sp-1] = ValueOf(mem[sp-1].Data.Int() < op[2])
case Not:
- mem[sp-1] = reflect.ValueOf(!mem[sp-1].Bool())
+ mem[sp-1] = ValueOf(!mem[sp-1].Data.Bool())
case Pop:
mem = mem[:sp-int(op[2])]
case Push:
//mem = append(mem, reflect.ValueOf(int(op[2])))
- mem = append(mem, reflect.New(reflect.TypeOf(0)).Elem())
- mem[sp].SetInt(op[2])
+ mem = append(mem, NewValue(TypeOf(0)))
+ mem[sp].Data.SetInt(op[2])
case Grow:
- mem = append(mem, make([]reflect.Value, op[2])...)
+ mem = append(mem, make([]Value, op[2])...)
case Return:
- ip = int(mem[fp-2].Int())
+ ip = int(mem[fp-2].Data.Int())
ofp := fp
- fp = int(mem[fp-1].Int())
+ fp = int(mem[fp-1].Data.Int())
mem = append(mem[:ofp-int(op[2])-int(op[3])-1], mem[sp-int(op[2]):]...)
continue
case Sub:
- mem[sp-2] = reflect.ValueOf(int(mem[sp-1].Int() - mem[sp-2].Int()))
+ mem[sp-2] = ValueOf(int(mem[sp-1].Data.Int() - mem[sp-2].Data.Int()))
mem = mem[:sp-1]
case Subi:
- mem[sp-1] = reflect.ValueOf(int(mem[sp-1].Int() - op[2]))
+ mem[sp-1] = ValueOf(int(mem[sp-1].Data.Int() - op[2]))
case Index:
- mem[sp-2] = mem[sp-1].Index(int(mem[sp-2].Int()))
+ mem[sp-2].Data = mem[sp-1].Data.Index(int(mem[sp-2].Data.Int()))
mem = mem[:sp-1]
case Vassign:
- mem[sp-1].Set(mem[sp-2])
+ mem[sp-1].Data.Set(mem[sp-2].Data)
mem = mem[:sp-2]
}
ip++
@@ -263,18 +263,18 @@ func (m *Machine) PushCode(code ...[]int64) (p int) {
}
func (m *Machine) SetIP(ip int) { m.ip = ip }
-func (m *Machine) Push(v ...reflect.Value) (l int) {
+func (m *Machine) Push(v ...Value) (l int) {
l = len(m.mem)
m.mem = append(m.mem, v...)
return l
}
-func (m *Machine) Pop() (v reflect.Value) {
+func (m *Machine) Pop() (v Value) {
l := len(m.mem) - 1
v = m.mem[l]
m.mem = m.mem[:l]
return v
}
-func (m *Machine) Top() (v reflect.Value) {
+func (m *Machine) Top() (v Value) {
if l := len(m.mem); l > 0 {
v = m.mem[l-1]
}
@@ -315,13 +315,13 @@ func slint(a []int64) []int {
return r
}
-func Vstring(lv []reflect.Value) string {
+func Vstring(lv []Value) string {
s := "["
for _, v := range lv {
if s != "[" {
s += " "
}
- s += fmt.Sprintf("%v", v)
+ s += fmt.Sprintf("%v", v.Data)
}
return s + "]"
}
diff --git a/vm/vm_test.go b/vm/vm_test.go
index 07c063e..fb71176 100644
--- a/vm/vm_test.go
+++ b/vm/vm_test.go
@@ -3,7 +3,6 @@ package vm
import (
"fmt"
"log"
- "reflect"
"testing"
)
@@ -50,11 +49,10 @@ func BenchmarkVM(b *testing.B) {
}
var tests = []struct {
- //sym []any // initial memory values
- sym []reflect.Value // initial memory values
- code [][]int64 // bytecode to execute
- start, end int //
- mem string // expected memory content
+ sym []Value // initial memory values
+ code [][]int64 // bytecode to execute
+ start, end int //
+ mem string // expected memory content
}{{ // #00 -- A simple addition.
code: [][]int64{
{0, Push, 1},
@@ -64,7 +62,7 @@ var tests = []struct {
},
start: 0, end: 1, mem: "[3]",
}, { // #01 -- Calling a function defined outside the VM.
- sym: []reflect.Value{reflect.ValueOf(fmt.Println), reflect.ValueOf("Hello")},
+ sym: []Value{ValueOf(fmt.Println), ValueOf("Hello")},
code: [][]int64{
{0, Dup, 0},
{0, CallX, 1},