summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Vertes <mvertes@free.fr>2023-11-21 16:05:13 +0100
committerMarc Vertes <mvertes@free.fr>2023-11-24 09:12:46 +0100
commitc548093d79edece3c1cbb7e4dc03d92fe45b1cc7 (patch)
tree9262c8ef34a5d393cfb1ebe99558d70120c5d55d
parentae58deb5da1fa2ae5e461783ce592a9b962da778 (diff)
vm: use only reflect.Values (WIP)
-rw-r--r--lang/token.go3
-rw-r--r--vm/vm.go92
-rw-r--r--vm/vm_test.go12
3 files changed, 64 insertions, 43 deletions
diff --git a/lang/token.go b/lang/token.go
index 4bf9bf2..40e6bfc 100644
--- a/lang/token.go
+++ b/lang/token.go
@@ -65,7 +65,7 @@ const (
Arrow // unary ->
Ellipsis // unary ...
Not // unary !
- Tilde // unary ~
+ Tilde // unary ~ (underlying type)
// Separators (punctuation)
Comma // ,
@@ -116,6 +116,7 @@ const (
EqualSet
)
+// TODO: define UnaryOp per language
var UnaryOp = map[TokenId]TokenId{
Add: Plus, // +
And: Address, // &
diff --git a/vm/vm.go b/vm/vm.go
index 21205de..52618de 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -14,12 +14,14 @@ const (
// instruction effect on stack: values consumed -- values produced
Nop = iota // --
Add // n1 n2 -- sum ; sum = n1+n2
+ Address // a -- &a ;
Assign // val -- ; mem[$1] = val
Fassign // val -- ; mem[$1] = val
Vassign // val dest -- ; dest.Set(val)
Call // f [a1 .. ai] -- [r1 .. rj] ; r1, ... = prog[f](a1, ...)
Calli // f [a1 .. ai] -- [r1 .. rj] ; r1, ... = prog[f](a1, ...)
CallX // f [a1 .. ai] -- [r1 .. rj] ; r1, ... = mem[f](a1, ...)
+ Deref // x -- *x ;
Dup // addr -- value ; value = mem[addr]
Fdup // addr -- value ; value = mem[addr]
Equal // n1 n2 -- cond ; cond = n1 == n2
@@ -48,10 +50,12 @@ const (
var strop = [...]string{ // for VM tracing.
Nop: "Nop",
Add: "Add",
+ Address: "Address",
Assign: "Assign",
Call: "Call",
Calli: "Calli",
CallX: "CallX",
+ Deref: "Deref",
Dup: "Dup",
Equal: "Equal",
EqualSet: "EqualSet",
@@ -83,10 +87,11 @@ type Code [][]int64
// Machine represents a virtual machine.
type Machine struct {
- code Code // code to execute
- mem []any // 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 []any // memory, as a stack
+ mem []reflect.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...
}
@@ -117,11 +122,13 @@ func (m *Machine) Run() (err error) {
ic++
switch op := code[ip]; op[1] {
case Add:
- mem[sp-2] = mem[sp-2].(int) + mem[sp-1].(int)
+ mem[sp-2] = reflect.ValueOf(int(mem[sp-2].Int() + mem[sp-1].Int()))
mem = mem[:sp-1]
case Mul:
- mem[sp-2] = mem[sp-2].(int) * mem[sp-1].(int)
+ mem[sp-2].SetInt(mem[sp-2].Int() * mem[sp-1].Int())
mem = mem[:sp-1]
+ case Address:
+ mem[sp-1] = mem[sp-1].Addr()
case Assign:
mem[op[2]] = mem[sp-1]
mem = mem[:sp-1]
@@ -129,13 +136,13 @@ func (m *Machine) Run() (err error) {
mem[fp+int(op[2])-1] = mem[sp-1]
mem = mem[:sp-1]
case Call:
- nip := mem[sp-1].(int)
- mem = append(mem[:sp-1], ip+1, fp)
+ nip := int(mem[sp-1].Int())
+ mem = append(mem[:sp-1], reflect.ValueOf(ip+1), reflect.ValueOf(fp))
ip = nip
fp = sp + 1
continue
case Calli:
- mem = append(mem, ip+1, fp)
+ mem = append(mem, reflect.ValueOf(ip+1), reflect.ValueOf(fp))
fp = sp + 2
ip += int(op[2])
continue
@@ -143,53 +150,55 @@ func (m *Machine) Run() (err error) {
l := int(op[2])
in := make([]reflect.Value, l)
for i := range in {
- in[i] = reflect.ValueOf(mem[sp-2-i])
+ in[i] = mem[sp-2-i]
}
- f := reflect.ValueOf(mem[sp-1])
+ f := mem[sp-1]
mem = mem[:sp-l-1]
for _, v := range f.Call(in) {
- mem = append(mem, v.Interface())
+ mem = append(mem, v)
}
+ case Deref:
+ mem[sp-1] = mem[sp-1].Elem()
case Dup:
mem = append(mem, mem[int(op[2])])
case Equal:
- mem[sp-2] = mem[sp-2].(int) == mem[sp-1].(int)
+ mem[sp-2] = reflect.ValueOf(mem[sp-2].Equal(mem[sp-1]))
mem = mem[:sp-1]
case EqualSet:
- if mem[sp-2].(int) == mem[sp-1].(int) {
+ if mem[sp-2].Equal(mem[sp-1]) {
// If equal then lhs and rhs are popped, replaced by test result, as in Equal.
- mem[sp-2] = true
+ mem[sp-2] = reflect.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] = false
+ mem[sp-1] = reflect.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].(reflect.Value).FieldByIndex(slint(op[2:]))
+ mem[sp-1] = mem[sp-1].FieldByIndex(slint(op[2:]))
case Jump:
ip += int(op[2])
continue
case JumpTrue:
- cond := mem[sp-1].(bool)
+ cond := mem[sp-1].Bool()
mem = mem[:sp-1]
if cond {
ip += int(op[2])
continue
}
case JumpFalse:
- cond := mem[sp-1].(bool)
+ cond := mem[sp-1].Bool()
mem = mem[:sp-1]
if !cond {
ip += int(op[2])
continue
}
case JumpSetTrue:
- cond := mem[sp-1].(bool)
+ cond := mem[sp-1].Bool()
if cond {
ip += int(op[2])
// Note that stack is not modified if cond is true
@@ -197,7 +206,7 @@ func (m *Machine) Run() (err error) {
}
mem = mem[:sp-1]
case JumpSetFalse:
- cond := mem[sp-1].(bool)
+ cond := mem[sp-1].Bool()
if !cond {
ip += int(op[2])
// Note that stack is not modified if cond is false
@@ -205,37 +214,37 @@ func (m *Machine) Run() (err error) {
}
mem = mem[:sp-1]
case Greater:
- mem[sp-2] = mem[sp-1].(int) > mem[sp-2].(int)
+ mem[sp-2] = reflect.ValueOf(mem[sp-1].Int() > mem[sp-2].Int())
mem = mem[:sp-1]
case Lower:
- mem[sp-2] = mem[sp-1].(int) < mem[sp-2].(int)
+ mem[sp-2] = reflect.ValueOf(mem[sp-1].Int() < mem[sp-2].Int())
mem = mem[:sp-1]
case Loweri:
- mem[sp-1] = mem[sp-1].(int) < int(op[2])
+ mem[sp-1] = reflect.ValueOf(mem[sp-1].Int() < op[2])
case Not:
- mem[sp-1] = !mem[sp-1].(bool)
+ mem[sp-1] = reflect.ValueOf(!mem[sp-1].Bool())
case Pop:
mem = mem[:sp-int(op[2])]
case Push:
- mem = append(mem, int(op[2]))
+ mem = append(mem, reflect.ValueOf(int(op[2])))
case Grow:
- mem = append(mem, make([]any, op[2])...)
+ mem = append(mem, make([]reflect.Value, op[2])...)
case Return:
- ip = mem[fp-2].(int)
+ ip = int(mem[fp-2].Int())
ofp := fp
- fp = mem[fp-1].(int)
+ fp = int(mem[fp-1].Int())
mem = append(mem[:ofp-int(op[2])-int(op[3])-1], mem[sp-int(op[2]):]...)
continue
case Sub:
- mem[sp-2] = mem[sp-1].(int) - mem[sp-2].(int)
+ mem[sp-2].SetInt(mem[sp-1].Int() - mem[sp-2].Int())
mem = mem[:sp-1]
case Subi:
- mem[sp-1] = mem[sp-1].(int) - int(op[2])
+ mem[sp-1].SetInt(mem[sp-1].Int() - op[2])
case Index:
- mem[sp-2] = mem[sp-1].(reflect.Value).Index(mem[sp-2].(int))
+ mem[sp-2] = mem[sp-1].Index(int(mem[sp-2].Int()))
mem = mem[:sp-1]
case Vassign:
- mem[sp-1].(reflect.Value).Set(reflect.ValueOf(mem[sp-2]))
+ mem[sp-1].Set(mem[sp-2])
mem = mem[:sp-2]
}
ip++
@@ -248,10 +257,19 @@ func (m *Machine) PushCode(code ...[]int64) (p int) {
return p
}
-func (m *Machine) SetIP(ip int) { m.ip = ip }
-func (m *Machine) Push(v ...any) (l int) { l = len(m.mem); m.mem = append(m.mem, v...); return l }
-func (m *Machine) Pop() (v any) { l := len(m.mem) - 1; v = m.mem[l]; m.mem = m.mem[:l]; return v }
-func (m *Machine) Top() (v any) {
+func (m *Machine) SetIP(ip int) { m.ip = ip }
+func (m *Machine) Push(v ...reflect.Value) (l int) {
+ l = len(m.mem)
+ m.mem = append(m.mem, v...)
+ return l
+}
+func (m *Machine) Pop() (v reflect.Value) {
+ l := len(m.mem) - 1
+ v = m.mem[l]
+ m.mem = m.mem[:l]
+ return v
+}
+func (m *Machine) Top() (v reflect.Value) {
if l := len(m.mem); l > 0 {
v = m.mem[l-1]
}
diff --git a/vm/vm_test.go b/vm/vm_test.go
index 08e8054..c9540d2 100644
--- a/vm/vm_test.go
+++ b/vm/vm_test.go
@@ -3,6 +3,7 @@ package vm
import (
"fmt"
"log"
+ "reflect"
"testing"
)
@@ -50,10 +51,11 @@ func BenchmarkVM(b *testing.B) {
}
var tests = []struct {
- sym []any // initial memory values
- code [][]int64 // bytecode to execute
- start, end int //
- mem string // expected memory content
+ //sym []any // initial memory values
+ sym []reflect.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},
@@ -63,7 +65,7 @@ var tests = []struct {
},
start: 0, end: 1, mem: "[3]",
}, { // #01 -- Calling a function defined outside the VM.
- sym: []any{fmt.Println, "Hello"},
+ sym: []reflect.Value{reflect.ValueOf(fmt.Println), reflect.ValueOf("Hello")},
code: [][]int64{
{0, Dup, 0},
{0, CallX, 1},