diff options
| author | Marc Vertes <mvertes@free.fr> | 2023-11-21 16:05:13 +0100 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2023-11-24 09:12:46 +0100 |
| commit | c548093d79edece3c1cbb7e4dc03d92fe45b1cc7 (patch) | |
| tree | 9262c8ef34a5d393cfb1ebe99558d70120c5d55d | |
| parent | ae58deb5da1fa2ae5e461783ce592a9b962da778 (diff) | |
vm: use only reflect.Values (WIP)
| -rw-r--r-- | lang/token.go | 3 | ||||
| -rw-r--r-- | vm/vm.go | 92 | ||||
| -rw-r--r-- | vm/vm_test.go | 12 |
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, // & @@ -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}, |
