diff options
| author | Marc Vertes <mvertes@free.fr> | 2025-11-14 13:47:51 +0100 |
|---|---|---|
| committer | Marc Vertes <mvertes@free.fr> | 2025-11-14 13:47:51 +0100 |
| commit | 1a9c22d1219eb0239417db16c9fe96eea3ced2fe (patch) | |
| tree | a4fa82cfbbc924d281a94e307656a7af14caadcd | |
| parent | a4d7730581e8e65faa6a9285d107a041accf60b1 (diff) | |
fix: refactor VM instruction type
Use custom types for VM instructions. More idiomatic code for tracing.
| -rw-r--r-- | parser/README.md | 2 | ||||
| -rw-r--r-- | parser/compiler.go | 106 | ||||
| -rw-r--r-- | parser/interpreter.go | 4 | ||||
| -rw-r--r-- | vm/op_string.go | 57 | ||||
| -rw-r--r-- | vm/vm.go | 243 | ||||
| -rw-r--r-- | vm/vm_test.go | 264 |
6 files changed, 332 insertions, 344 deletions
diff --git a/parser/README.md b/parser/README.md index 6f0f734..ba2f15c 100644 --- a/parser/README.md +++ b/parser/README.md @@ -112,5 +112,5 @@ Other items: - [ ] build constraints (arch, sys, etc) - [ ] test command (running go test / benchmark / example files) - [ ] skipping / including test files -- [ ] test coverage +- [x] test coverage - [ ] fuzzy tests for scanner, vm, ... diff --git a/parser/compiler.go b/parser/compiler.go index 06bc87a..e821c90 100644 --- a/parser/compiler.go +++ b/parser/compiler.go @@ -45,7 +45,9 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { fixList := Tokens{} // list of tokens to fix after we gathered all necessary information stack := []*symbol{} // for symbolic evaluation, type checking, etc - emit := func(op ...int64) { c.Code = append(c.Code, op) } + emit := func(pos int, op vm.Op, arg ...int) { + c.Code = append(c.Code, vm.Instruction{Pos: vm.Pos(pos), Op: op, Arg: arg}) + } push := func(s *symbol) { stack = append(stack, s) } pop := func() *symbol { l := len(stack) - 1; s := stack[l]; stack = stack[:l]; return s } @@ -57,7 +59,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { return err } push(&symbol{kind: symConst, value: vm.ValueOf(n), typ: vm.TypeOf(0)}) - emit(int64(t.Pos), vm.Push, int64(n)) + emit(t.Pos, vm.Push, n) case lang.String: s := t.Block() @@ -69,49 +71,49 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { c.strings[s] = i } push(&symbol{kind: symConst, value: v}) - emit(int64(t.Pos), vm.Dup, int64(i)) + emit(t.Pos, vm.Dup, i) case lang.Add: push(&symbol{typ: arithmeticOpType(pop(), pop())}) - emit(int64(t.Pos), vm.Add) + emit(t.Pos, vm.Add) case lang.Mul: push(&symbol{typ: arithmeticOpType(pop(), pop())}) - emit(int64(t.Pos), vm.Mul) + emit(t.Pos, vm.Mul) case lang.Sub: push(&symbol{typ: arithmeticOpType(pop(), pop())}) - emit(int64(t.Pos), vm.Sub) + emit(t.Pos, vm.Sub) case lang.Minus: - emit(int64(t.Pos), vm.Push, 0) - emit(int64(t.Pos), vm.Sub) + emit(t.Pos, vm.Push, 0) + emit(t.Pos, vm.Sub) case lang.Not: - emit(int64(t.Pos), vm.Not) + emit(t.Pos, vm.Not) case lang.Plus: // Unary '+' is idempotent. Nothing to do. case lang.Addr: push(&symbol{typ: vm.PointerTo(pop().typ)}) - emit(int64(t.Pos), vm.Addr) + emit(t.Pos, vm.Addr) case lang.Deref: push(&symbol{typ: pop().typ.Elem()}) - emit(int64(t.Pos), vm.Deref) + emit(t.Pos, vm.Deref) case lang.Index: push(&symbol{typ: pop().typ.Elem()}) - emit(int64(t.Pos), vm.Index) + emit(t.Pos, vm.Index) case lang.Greater: push(&symbol{typ: booleanOpType(pop(), pop())}) - emit(int64(t.Pos), vm.Greater) + emit(t.Pos, vm.Greater) case lang.Less: push(&symbol{typ: booleanOpType(pop(), pop())}) - emit(int64(t.Pos), vm.Lower) + emit(t.Pos, vm.Lower) case lang.Call: s := pop() @@ -121,7 +123,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { for i := 0; i < typ.Rtype.NumOut(); i++ { push(&symbol{typ: typ.Out(i)}) } - emit(int64(t.Pos), vm.Call) + emit(t.Pos, vm.Call) break } push(s) @@ -133,13 +135,13 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { for i := 0; i < rtyp.NumOut(); i++ { push(&symbol{typ: &vm.Type{Rtype: rtyp.Out(i)}}) } - emit(int64(t.Pos), vm.CallX, int64(t.Beg)) + emit(t.Pos, vm.CallX, t.Beg) case lang.Composite: log.Println("COMPOSITE") case lang.Grow: - emit(int64(t.Pos), vm.Grow, int64(t.Beg)) + emit(t.Pos, vm.Grow, t.Beg) case lang.Define: // TODO: support assignment to local, composite objects. @@ -153,12 +155,12 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { v := vm.NewValue(typ) c.addSym(l, st.Str, v, symVar, typ, false) c.Data = append(c.Data, v) - emit(int64(st.Pos), vm.Assign, int64(l)) + emit(st.Pos, vm.Assign, l) case lang.Assign: st := tokens[i-1] if st.Tok == lang.Period || st.Tok == lang.Index { - emit(int64(t.Pos), vm.Vassign) + emit(t.Pos, vm.Vassign) break } s, ok := c.symbols[st.Str] @@ -176,25 +178,25 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { } if s.local { if !s.used { - emit(int64(st.Pos), vm.New, int64(s.index), int64(c.typeSym(s.typ).index)) + emit(st.Pos, vm.New, s.index, c.typeSym(s.typ).index) s.used = true } - emit(int64(st.Pos), vm.Fassign, int64(s.index)) + emit(st.Pos, vm.Fassign, s.index) break } if s.index == unsetAddr { s.index = len(c.Data) c.Data = append(c.Data, s.value) } - emit(int64(st.Pos), vm.Assign, int64(s.index)) + emit(st.Pos, vm.Assign, s.index) case lang.Equal: push(&symbol{typ: booleanOpType(pop(), pop())}) - emit(int64(t.Pos), vm.Equal) + emit(t.Pos, vm.Equal) case lang.EqualSet: push(&symbol{typ: booleanOpType(pop(), pop())}) - emit(int64(t.Pos), vm.EqualSet) + emit(t.Pos, vm.EqualSet) case lang.Ident: if i < len(tokens)-1 { @@ -212,13 +214,13 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { break } if s.local { - emit(int64(t.Pos), vm.Fdup, int64(s.index)) + emit(t.Pos, vm.Fdup, s.index) } else { if s.index == unsetAddr { s.index = len(c.Data) c.Data = append(c.Data, s.value) } - emit(int64(t.Pos), vm.Dup, int64(s.index)) + emit(t.Pos, vm.Dup, s.index) } case lang.Label: @@ -238,47 +240,47 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { } case lang.JumpFalse: - var i int64 + var i int if s, ok := c.symbols[t.Str]; !ok { // t.Beg contains the position in code which needs to be fixed. t.Beg = len(c.Code) fixList = append(fixList, t) } else { - i = s.value.Data.Int() - int64(len(c.Code)) + i = int(s.value.Data.Int()) - len(c.Code) } - emit(int64(t.Pos), vm.JumpFalse, i) + emit(t.Pos, vm.JumpFalse, i) case lang.JumpSetFalse: - var i int64 + var i int if s, ok := c.symbols[t.Str]; !ok { // t.Beg contains the position in code which needs to be fixed. t.Beg = len(c.Code) fixList = append(fixList, t) } else { - i = s.value.Data.Int() - int64(len(c.Code)) + i = int(s.value.Data.Int()) - len(c.Code) } - emit(int64(t.Pos), vm.JumpSetFalse, i) + emit(t.Pos, vm.JumpSetFalse, i) case lang.JumpSetTrue: - var i int64 + var i int if s, ok := c.symbols[t.Str]; !ok { // t.Beg contains the position in code which needs to be fixed. t.Beg = len(c.Code) fixList = append(fixList, t) } else { - i = s.value.Data.Int() - int64(len(c.Code)) + i = int(s.value.Data.Int()) - len(c.Code) } - emit(int64(t.Pos), vm.JumpSetTrue, i) + emit(t.Pos, vm.JumpSetTrue, i) case lang.Goto: - var i int64 + var i int if s, ok := c.symbols[t.Str]; !ok { t.Beg = len(c.Code) fixList = append(fixList, t) } else { - i = s.value.Data.Int() - int64(len(c.Code)) + i = int(s.value.Data.Int()) - len(c.Code) } - emit(int64(t.Pos), vm.Jump, i) + emit(t.Pos, vm.Jump, i) case lang.Period: s := pop() @@ -304,17 +306,17 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { sym = c.symbols[name] } push(sym) - emit(int64(t.Pos), vm.Dup, int64(l)) + emit(t.Pos, vm.Dup, l) default: if f, ok := s.typ.Rtype.FieldByName(t.Str[1:]); ok { - emit(append([]int64{int64(t.Pos), vm.Field}, slint64(f.Index)...)...) + emit(t.Pos, vm.Field, f.Index...) break } return fmt.Errorf("field or method not found: %s", t.Str[1:]) } case lang.Return: - emit(int64(t.Pos), vm.Return, int64(t.Beg), int64(t.End)) + emit(t.Pos, vm.Return, t.Beg, t.End) default: return fmt.Errorf("Codegen: unsupported token %v", t) @@ -327,7 +329,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) { if !ok { return fmt.Errorf("label not found: %q", t.Str) } - c.Code[t.Beg][2] = s.value.Data.Int() - int64(t.Beg) + c.Code[t.Beg].Arg[0] = int(s.value.Data.Int()) - t.Beg } return err @@ -357,17 +359,17 @@ func (c *Compiler) PrintCode() { fmt.Fprintln(os.Stderr, label+":") } extra := "" - switch l[1] { + switch l.Op { case vm.Jump, vm.JumpFalse, vm.JumpTrue, vm.JumpSetFalse, vm.JumpSetTrue, vm.Calli: - if d, ok := labels[i+(int)(l[2])]; ok { + if d, ok := labels[i+l.Arg[0]]; ok { extra = "// " + d[0] } case vm.Dup, vm.Assign, vm.Fdup, vm.Fassign: - if d, ok := data[int(l[2])]; ok { + if d, ok := data[l.Arg[0]]; ok { extra = "// " + d } } - fmt.Fprintf(os.Stderr, "%4d %-14v %v\n", i, vm.CodeString(l), extra) + fmt.Fprintf(os.Stderr, "%4d %-14v %v\n", i, l, extra) } for _, label := range labels[len(c.Code)] { @@ -392,7 +394,6 @@ func (e entry) String() string { e.value, ) } - return e.name } @@ -414,7 +415,6 @@ func (c *Compiler) symbolsByIndex() map[int]entry { } dict[sym.index] = entry{name, sym} } - return dict } @@ -452,7 +452,6 @@ func (c *Compiler) Dump() *Dump { Value: d.Data.Interface(), } } - return &Dump{Values: dv} } @@ -486,7 +485,6 @@ func (c *Compiler) ApplyDump(d *Dump) error { c.Data[dv.Index].Data.Set(reflect.ValueOf(dv.Value)) } - return nil } @@ -502,11 +500,3 @@ func (c *Compiler) typeSym(t *vm.Type) *symbol { } return tsym } - -func slint64(a []int) []int64 { - r := make([]int64, len(a)) - for i, v := range a { - r[i] = int64(v) - } - return r -} diff --git a/parser/interpreter.go b/parser/interpreter.go index e6ac95c..6f354d9 100644 --- a/parser/interpreter.go +++ b/parser/interpreter.go @@ -40,9 +40,9 @@ func (i *Interpreter) Eval(src string) (res reflect.Value, err error) { i.Push(i.Data[dataOffset:]...) i.PushCode(i.Code[codeOffset:]...) if s, ok := i.symbols["main"]; ok { - i.PushCode([]int64{0, vm.Calli, i.Data[s.index].Data.Int()}) + i.PushCode(vm.Instruction{Op: vm.Calli, Arg: []int{int(i.Data[s.index].Data.Int())}}) } - i.PushCode([]int64{0, vm.Exit}) + i.PushCode(vm.Instruction{Op: vm.Exit}) i.SetIP(max(codeOffset, i.Entry)) if debug { i.PrintData() diff --git a/vm/op_string.go b/vm/op_string.go new file mode 100644 index 0000000..ef3f3ee --- /dev/null +++ b/vm/op_string.go @@ -0,0 +1,57 @@ +// Code generated by "stringer -type=Op"; DO NOT EDIT. + +package vm + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[Nop-0] + _ = x[Add-1] + _ = x[Addr-2] + _ = x[Assign-3] + _ = x[Fassign-4] + _ = x[Vassign-5] + _ = x[Call-6] + _ = x[Calli-7] + _ = x[CallX-8] + _ = x[Deref-9] + _ = x[Dup-10] + _ = x[Fdup-11] + _ = x[Equal-12] + _ = x[EqualSet-13] + _ = x[Exit-14] + _ = x[Field-15] + _ = x[Greater-16] + _ = x[Grow-17] + _ = x[Index-18] + _ = x[Jump-19] + _ = x[JumpTrue-20] + _ = x[JumpFalse-21] + _ = x[JumpSetTrue-22] + _ = x[JumpSetFalse-23] + _ = x[Lower-24] + _ = x[Loweri-25] + _ = x[Mul-26] + _ = x[New-27] + _ = x[Not-28] + _ = x[Pop-29] + _ = x[Push-30] + _ = x[Return-31] + _ = x[Sub-32] + _ = x[Subi-33] +} + +const _Op_name = "NopAddAddrAssignFassignVassignCallCalliCallXDerefDupFdupEqualEqualSetExitFieldGreaterGrowIndexJumpJumpTrueJumpFalseJumpSetTrueJumpSetFalseLowerLoweriMulNewNotPopPushReturnSubSubi" + +var _Op_index = [...]uint8{0, 3, 6, 10, 16, 23, 30, 34, 39, 44, 49, 52, 56, 61, 69, 73, 78, 85, 89, 94, 98, 106, 115, 126, 138, 143, 149, 152, 155, 158, 161, 165, 171, 174, 178} + +func (i Op) String() string { + idx := int(i) - 0 + if i < 0 || idx >= len(_Op_index)-1 { + return "Op(" + strconv.FormatInt(int64(i), 10) + ")" + } + return _Op_name[_Op_index[idx]:_Op_index[idx+1]] +} @@ -5,126 +5,98 @@ import ( "fmt" // for tracing only "log" // for tracing only "reflect" // for optional CallX only - "strconv" // for tracing only "unsafe" // to allow setting unexported struct fields ) const debug = true +type ( + Op int // Op is a VM opcode (bytecode instruction). + Pos int // Pos is the source code position of instruction +) + +//go:generate stringer -type=Op + // Byte-code instruction set. const ( // Instruction effect on stack: values consumed -- values produced. - Nop = iota // -- - Add // n1 n2 -- sum ; sum = n1+n2 - Addr // 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 - EqualSet // n1 n2 -- n1 cond ; cond = n1 == n2 - Exit // -- ; - Field // s -- f ; f = s.FieldIndex($1, ...) - Greater // n1 n2 -- cond; cond = n1 > n2 - Grow // -- ; sp += $1 - Index // a i -- a[i] ; - Jump // -- ; ip += $1 - JumpTrue // cond -- ; if cond { ip += $1 } - JumpFalse // cond -- ; if cond { ip += $1 } - JumpSetTrue // - JumpSetFalse // - Lower // n1 n2 -- cond ; cond = n1 < n2 - Loweri // n1 -- cond ; cond = n1 < $1 - Mul // n1 n2 -- prod ; prod = n1*n2 - New // -- x; mem[fp+$1] = new mem[$2] - Not // c -- r ; r = !c - Pop // v -- - Push // -- v - Return // [r1 .. ri] -- ; exit frame: sp = fp, fp = pop - Sub // n1 n2 -- diff ; diff = n1 - n2 - Subi // n1 -- diff ; diff = n1 - $1 + Nop Op = iota // -- + Add // n1 n2 -- sum ; sum = n1+n2 + Addr // 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 + EqualSet // n1 n2 -- n1 cond ; cond = n1 == n2 + Exit // -- ; + Field // s -- f ; f = s.FieldIndex($1, ...) + Greater // n1 n2 -- cond; cond = n1 > n2 + Grow // -- ; sp += $1 + Index // a i -- a[i] ; + Jump // -- ; ip += $1 + JumpTrue // cond -- ; if cond { ip += $1 } + JumpFalse // cond -- ; if cond { ip += $1 } + JumpSetTrue // + JumpSetFalse // + Lower // n1 n2 -- cond ; cond = n1 < n2 + Loweri // n1 -- cond ; cond = n1 < $1 + Mul // n1 n2 -- prod ; prod = n1*n2 + New // -- x; mem[fp+$1] = new mem[$2] + Not // c -- r ; r = !c + Pop // v -- + Push // -- v + Return // [r1 .. ri] -- ; exit frame: sp = fp, fp = pop + Sub // n1 n2 -- diff ; diff = n1 - n2 + Subi // n1 -- diff ; diff = n1 - $1 ) -var strop = [...]string{ // for VM tracing. - Nop: "Nop", - Add: "Add", - Addr: "Addr", - Assign: "Assign", - Call: "Call", - Calli: "Calli", - CallX: "CallX", - Deref: "Deref", - Dup: "Dup", - Equal: "Equal", - EqualSet: "EqualSet", - Exit: "Exit", - Fassign: "Fassign", - Fdup: "Fdup", - Field: "Field", - Greater: "Greater", - Grow: "Grow", - Index: "Index", - Jump: "Jump", - JumpTrue: "JumpTrue", - JumpFalse: "JumpFalse", - JumpSetTrue: "JumpSetTrue", - JumpSetFalse: "JumpSetFalse", - Lower: "Lower", - Loweri: "Loweri", - Mul: "Mul", - New: "New", - Not: "Not", - Pop: "Pop", - Push: "Push", - Return: "Return", - Sub: "Sub", - Subi: "Subi", - Vassign: "Vassign", +// Instruction represents a virtual machine bytecode instruction. +type Instruction struct { + Pos // position in source + Op // opcode + Arg []int // arguments +} + +func (i Instruction) String() (s string) { + s = i.Op.String() + for _, a := range i.Arg { + s += fmt.Sprintf(" %v", a) + } + return s } // Code represents the virtual machine byte code. -type Code [][]int64 +type Code []Instruction // Machine represents a virtual machine. type Machine struct { code Code // code to execute mem []Value // memory, as a stack - ip, fp int // instruction and frame pointer + ip, fp int // instruction pointer and frame pointer ic uint64 // instruction counter, incremented at each instruction executed // flags uint // to set options such as restrict CallX, etc... } // Run runs a program. func (m *Machine) Run() (err error) { - code, mem, ip, fp, sp, ic := m.code, m.mem, m.ip, m.fp, 0, m.ic + mem, ip, fp, sp, ic := m.mem, m.ip, m.fp, 0, m.ic defer func() { m.mem, m.ip, m.fp, m.ic = mem, ip, fp, ic }() - trace := func() { - if !debug { - return - } - var op2, op3 string - c := code[ip] - if l := len(c); l > 2 { - op2 = strconv.Itoa(int(c[2])) - if l > 3 { - op3 = strconv.Itoa(int(c[3])) - } - } - log.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-12s %-4s %-4s] mem:%v\n", ip, sp, fp, strop[c[1]], op2, op3, Vstring(mem)) - } - for { sp = len(mem) // stack pointer - trace() + c := m.code[ip] + if debug { + log.Printf("ip:%-4d sp:%-4d fp:%-4d op:[%-14v] mem:%v\n", ip, sp, fp, c, Vstring(mem)) + } ic++ - switch op := code[ip]; op[1] { + switch c.Op { case Add: mem[sp-2] = ValueOf(int(mem[sp-2].Data.Int() + mem[sp-1].Data.Int())) mem = mem[:sp-1] @@ -134,10 +106,10 @@ func (m *Machine) Run() (err error) { case Addr: mem[sp-1].Data = mem[sp-1].Data.Addr() case Assign: - mem[op[2]].Data.Set(mem[sp-1].Data) + mem[c.Arg[0]].Data.Set(mem[sp-1].Data) mem = mem[:sp-1] case Fassign: - mem[fp+int(op[2])-1].Data.Set(mem[sp-1].Data) + mem[fp+c.Arg[0]-1].Data.Set(mem[sp-1].Data) mem = mem[:sp-1] case Call: nip := int(mem[sp-1].Data.Int()) @@ -148,25 +120,24 @@ func (m *Machine) Run() (err error) { case Calli: mem = append(mem, ValueOf(ip+1), ValueOf(fp)) fp = sp + 2 - ip = int(op[2]) + ip = c.Arg[0] continue case CallX: // Should be made optional. - l := int(op[2]) - in := make([]reflect.Value, l) + in := make([]reflect.Value, c.Arg[0]) for i := range in { in[i] = mem[sp-2-i].Data } f := mem[sp-1].Data - mem = mem[:sp-l-1] + mem = mem[:sp-c.Arg[0]-1] for _, v := range f.Call(in) { mem = append(mem, Value{Data: v}) } case Deref: mem[sp-1].Data = mem[sp-1].Data.Elem() case Dup: - mem = append(mem, mem[int(op[2])]) + mem = append(mem, mem[c.Arg[0]]) case New: - mem[int(op[2])+fp-1] = NewValue(mem[int(op[3])].Type) + mem[c.Arg[0]+fp-1] = NewValue(mem[c.Arg[1]].Type) case Equal: mem[sp-2] = ValueOf(mem[sp-2].Data.Equal(mem[sp-1].Data)) mem = mem[:sp-1] @@ -183,44 +154,44 @@ func (m *Machine) Run() (err error) { case Exit: return err case Fdup: - mem = append(mem, mem[int(op[2])+fp-1]) + mem = append(mem, mem[c.Arg[0]+fp-1]) case Field: - fv := mem[sp-1].Data.FieldByIndex(slint(op[2:])) + fv := mem[sp-1].Data.FieldByIndex(c.Arg) if !fv.CanSet() { // Normally private fields can not bet set via reflect. Override this limitation. fv = reflect.NewAt(fv.Type(), unsafe.Pointer(fv.UnsafeAddr())).Elem() } mem[sp-1].Data = fv case Jump: - ip += int(op[2]) + ip += c.Arg[0] continue case JumpTrue: cond := mem[sp-1].Data.Bool() mem = mem[:sp-1] if cond { - ip += int(op[2]) + ip += c.Arg[0] continue } case JumpFalse: cond := mem[sp-1].Data.Bool() mem = mem[:sp-1] if !cond { - ip += int(op[2]) + ip += c.Arg[0] continue } case JumpSetTrue: cond := mem[sp-1].Data.Bool() if cond { - ip += int(op[2]) - // Note that stack is not modified if cond is true + ip += c.Arg[0] + // Note that the stack is not modified if cond is true. continue } mem = mem[:sp-1] case JumpSetFalse: cond := mem[sp-1].Data.Bool() if !cond { - ip += int(op[2]) - // Note that stack is not modified if cond is false + ip += c.Arg[0] + // Note that the stack is not modified if cond is false. continue } mem = mem[:sp-1] @@ -231,28 +202,27 @@ func (m *Machine) Run() (err error) { mem[sp-2] = ValueOf(mem[sp-1].Data.Int() < mem[sp-2].Data.Int()) mem = mem[:sp-1] case Loweri: - mem[sp-1] = ValueOf(mem[sp-1].Data.Int() < op[2]) + mem[sp-1] = ValueOf(mem[sp-1].Data.Int() < int64(c.Arg[0])) case Not: mem[sp-1] = ValueOf(!mem[sp-1].Data.Bool()) case Pop: - mem = mem[:sp-int(op[2])] + mem = mem[:sp-c.Arg[0]] case Push: - // mem = append(mem, reflect.ValueOf(int(op[2]))) mem = append(mem, NewValue(TypeOf(0))) - mem[sp].Data.SetInt(op[2]) + mem[sp].Data.SetInt(int64(c.Arg[0])) case Grow: - mem = append(mem, make([]Value, op[2])...) + mem = append(mem, make([]Value, c.Arg[0])...) case Return: ip = int(mem[fp-2].Data.Int()) ofp := fp fp = int(mem[fp-1].Data.Int()) - mem = append(mem[:ofp-int(op[2])-int(op[3])-1], mem[sp-int(op[2]):]...) + mem = append(mem[:ofp-c.Arg[0]-c.Arg[1]-1], mem[sp-c.Arg[0]:]...) continue case Sub: mem[sp-2] = ValueOf(int(mem[sp-1].Data.Int() - mem[sp-2].Data.Int())) mem = mem[:sp-1] case Subi: - mem[sp-1] = ValueOf(int(mem[sp-1].Data.Int() - op[2])) + mem[sp-1] = ValueOf(int(mem[sp-1].Data.Int()) - c.Arg[0]) case Index: mem[sp-2].Data = mem[sp-1].Data.Index(int(mem[sp-2].Data.Int())) mem = mem[:sp-1] @@ -265,7 +235,7 @@ func (m *Machine) Run() (err error) { } // PushCode adds instructions to the machine code. -func (m *Machine) PushCode(code ...[]int64) (p int) { +func (m *Machine) PushCode(code ...Instruction) (p int) { p = len(m.code) m.code = append(m.code, code...) return p @@ -282,12 +252,12 @@ func (m *Machine) Push(v ...Value) (l int) { } // Pop removes and returns the value on the top of machine stack. -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) Pop() (v Value) { +// l := len(m.mem) - 1 +// v = m.mem[l] +// m.mem = m.mem[:l] +// return v +// } // Top returns (but not remove) the value on the top of machine stack. func (m *Machine) Top() (v Value) { @@ -299,40 +269,11 @@ func (m *Machine) Top() (v Value) { // PopExit removes the last machine code instruction if is Exit. func (m *Machine) PopExit() { - if l := len(m.code); l > 0 && m.code[l-1][1] == Exit { + if l := len(m.code); l > 0 && m.code[l-1].Op == Exit { m.code = m.code[:l-1] } } -// CodeString returns the string representation of a machine code instruction. -func CodeString(op []int64) string { - switch len(op) { - case 2: - return strop[op[1]] - case 3: - return fmt.Sprintf("%s %d", strop[op[1]], op[2]) - case 4: - return fmt.Sprintf("%s %d %d", strop[op[1]], op[2], op[3]) - } - return "" -} - -// Disassemble returns the code as a readable string. -func Disassemble(code [][]int64) (asm string) { - for _, op := range code { - asm += CodeString(op) + "\n" - } - return asm -} - -func slint(a []int64) []int { - r := make([]int, len(a)) - for i, v := range a { - r[i] = int(v) - } - return r -} - // Vstring returns the string repreentation of a list of values. func Vstring(lv []Value) string { s := "[" diff --git a/vm/vm_test.go b/vm/vm_test.go index 43333ad..f7cbfbf 100644 --- a/vm/vm_test.go +++ b/vm/vm_test.go @@ -50,189 +50,189 @@ func BenchmarkVM(b *testing.B) { } var tests = []struct { - sym []Value // initial memory values - code [][]int64 // bytecode to execute - start, end int // - mem string // expected memory content + sym []Value // initial memory values + code []Instruction // bytecode to execute + start, end int // + mem string // expected memory content }{{ // #00 -- A simple addition. - code: [][]int64{ - {0, Push, 1}, - {0, Push, 2}, - {0, Add}, - {0, Exit}, + code: []Instruction{ + {Op: Push, Arg: []int{1}}, + {Op: Push, Arg: []int{2}}, + {Op: Add}, + {Op: Exit}, }, start: 0, end: 1, mem: "[3]", }, { // #01 -- A simple subtraction. - code: [][]int64{ - {0, Push, 2}, - {0, Push, 3}, - {0, Sub}, - {0, Exit}, + code: []Instruction{ + {Op: Push, Arg: []int{2}}, + {Op: Push, Arg: []int{3}}, + {Op: Sub}, + {Op: Exit}, }, start: 0, end: 1, mem: "[1]", }, { // #02 -- A simple multiplication. - code: [][]int64{ - {0, Push, 3}, - {0, Push, 2}, - {0, Mul}, - {0, Exit}, + code: []Instruction{ + {Op: Push, Arg: []int{3}}, + {Op: Push, Arg: []int{2}}, + {Op: Mul}, + {Op: Exit}, }, start: 0, end: 1, mem: "[6]", }, { // #03 -- lower. - code: [][]int64{ - {0, Push, 3}, - {0, Push, 2}, - {0, Lower}, - {0, Exit}, + code: []Instruction{ + {Op: Push, Arg: []int{3}}, + {Op: Push, Arg: []int{2}}, + {Op: Lower}, + {Op: Exit}, }, start: 0, end: 1, mem: "[true]", }, { // #04 -- greater. - code: [][]int64{ - {0, Push, 2}, - {0, Push, 3}, - {0, Greater}, - {0, Exit}, + code: []Instruction{ + {Op: Push, Arg: []int{2}}, + {Op: Push, Arg: []int{3}}, + {Op: Greater}, + {Op: Exit}, }, start: 0, end: 1, mem: "[true]", }, { // #05 -- equal. - code: [][]int64{ - {0, Push, 2}, - {0, Push, 3}, - {0, Equal}, - {0, Exit}, + code: []Instruction{ + {Op: Push, Arg: []int{2}}, + {Op: Push, Arg: []int{3}}, + {Op: Equal}, + {Op: Exit}, }, start: 0, end: 1, mem: "[false]", }, { // #06 -- equalSet. - code: [][]int64{ - {0, Push, 2}, - {0, Push, 3}, - {0, EqualSet}, - {0, Exit}, + code: []Instruction{ + {Op: Push, Arg: []int{2}}, + {Op: Push, Arg: []int{3}}, + {Op: EqualSet}, + {Op: Exit}, }, start: 0, end: 2, mem: "[2 false]", }, { // #07 -- equalSet. - code: [][]int64{ - {0, Push, 3}, - {0, Push, 3}, - {0, EqualSet}, - {0, Exit}, + code: []Instruction{ + {Op: Push, Arg: []int{3}}, + {Op: Push, Arg: []int{3}}, + {Op: EqualSet}, + {Op: Exit}, }, start: 0, end: 1, mem: "[true]", }, { // #08 not. - code: [][]int64{ - {0, Push, 3}, - {0, Push, 3}, - {0, Equal}, - {0, Not}, - {0, Exit}, + code: []Instruction{ + {Op: Push, Arg: []int{3}}, + {Op: Push, Arg: []int{3}}, + {Op: Equal}, + {Op: Not}, + {Op: Exit}, }, start: 0, end: 1, mem: "[false]", }, { // #09 pop. - code: [][]int64{ - {0, Push, 3}, - {0, Push, 2}, - {0, Pop, 1}, - {0, Exit}, + code: []Instruction{ + {Op: Push, Arg: []int{3}}, + {Op: Push, Arg: []int{2}}, + {Op: Pop, Arg: []int{1}}, + {Op: Exit}, }, start: 0, end: 1, mem: "[3]", }, { // #10 -- Assign a variable. sym: []Value{{Type: TypeOf(0), Data: reflect.ValueOf(0)}}, - code: [][]int64{ - {0, Grow, 1}, - {0, New, 2, 0}, - {0, Push, 2}, - {0, Assign, 1}, - {0, Exit}, + code: []Instruction{ + {Op: Grow, Arg: []int{1}}, + {Op: New, Arg: []int{2, 0}}, + {Op: Push, Arg: []int{2}}, + {Op: Assign, Arg: []int{1}}, + {Op: Exit}, }, start: 1, end: 2, mem: "[2]", }, { // #11 -- Calling a function defined outside the VM. sym: []Value{ValueOf(fmt.Println), ValueOf("Hello")}, - code: [][]int64{ - {0, Dup, 0}, - {0, CallX, 1}, - {0, Exit}, + code: []Instruction{ + {Op: Dup, Arg: []int{0}}, + {Op: CallX, Arg: []int{1}}, + {Op: Exit}, }, start: 1, end: 3, mem: "[6 <nil>]", }, { // #12 -- Defining and calling a function in VM. - code: [][]int64{ - {0, Jump, 3}, // 0 - {0, Push, 3}, // 1 - {0, Return, 1, 1}, // 2 - {0, Push, 1}, // 3 - {0, Calli, 1}, // 4 - {0, Exit}, // 5 + code: []Instruction{ + {Op: Jump, Arg: []int{3}}, // 0 + {Op: Push, Arg: []int{3}}, // 1 + {Op: Return, Arg: []int{1, 1}}, // 2 + {Op: Push, Arg: []int{1}}, // 3 + {Op: Calli, Arg: []int{1}}, // 4 + {Op: Exit}, // 5 }, start: 0, end: 1, mem: "[3]", }, { // #13 -- Defining and calling a function in VM. - code: [][]int64{ - {0, Jump, 3}, // 0 - {0, Push, 3}, // 1 - {0, Return, 1, 1}, // 2 - {0, Push, 1}, // 3 - {0, Push, 1}, // 4 - {0, Call}, // 5 - {0, Exit}, // 6 + code: []Instruction{ + {Op: Jump, Arg: []int{3}}, // 0 + {Op: Push, Arg: []int{3}}, // 1 + {Op: Return, Arg: []int{1, 1}}, // 2 + {Op: Push, Arg: []int{1}}, // 3 + {Op: Push, Arg: []int{1}}, // 4 + {Op: Call}, // 5 + {Op: Exit}, // 6 }, start: 0, end: 1, mem: "[3]", }, { // #14 -- Defining and calling a function in VM. - code: [][]int64{ - {0, Jump, 5}, // 0 - {0, Push, 3}, // 1 - {0, Fassign, -2}, // 2 - {0, Fdup, -2}, // 3 - {0, Return, 1, 1}, // 4 - {0, Push, 1}, // 5 - {0, Push, 1}, // 6 - {0, Call}, // 7 - {0, Exit}, // 8 + code: []Instruction{ + {Op: Jump, Arg: []int{5}}, // 0 + {Op: Push, Arg: []int{3}}, // 1 + {Op: Fassign, Arg: []int{-2}}, // 2 + {Op: Fdup, Arg: []int{-2}}, // 3 + {Op: Return, Arg: []int{1, 1}}, // 4 + {Op: Push, Arg: []int{1}}, // 5 + {Op: Push, Arg: []int{1}}, // 6 + {Op: Call}, // 7 + {Op: Exit}, // 8 }, start: 0, end: 1, mem: "[3]", }, { // #15 -- Fibonacci numbers, hand written. Showcase recursivity. - code: [][]int64{ - {0, Jump, 19}, // 0 - {0, Push, 2}, // 2 [2] - {0, Fdup, -2}, // 1 [2 i] - {0, Lower}, // 3 [true/false] - {0, JumpTrue, 13}, // 4 [], goto 17 - {0, Push, 2}, // 5 [i 2] - {0, Fdup, -2}, // 6 [i] - {0, Sub}, // 7 [(i-2)] - {0, Push, 1}, // 8 - {0, Call}, // 9 [fib(i-2)] - {0, Push, 1}, // 10 [(i-2) i 1] - {0, Fdup, -2}, // 11 [fib(i-2) i] - {0, Sub}, // 12 [(i-2) (i-1)] - {0, Push, 1}, // 13 - {0, Call}, // 14 [fib(i-2) fib(i-1)] - {0, Add}, // 15 [fib(i-2)+fib(i-1)] - {0, Return, 1, 1}, // 16 return i - {0, Fdup, -2}, // 17 [i] - {0, Return, 1, 1}, // 18 return i - {0, Push, 6}, // 19 [1] - {0, Push, 1}, // 20 - {0, Call}, // 21 [fib(*1)] - {0, Exit}, // 22 + code: []Instruction{ + {Op: Jump, Arg: []int{19}}, // 0 + {Op: Push, Arg: []int{2}}, // 2 [2] + {Op: Fdup, Arg: []int{-2}}, // 1 [2 i] + {Op: Lower}, // 3 [true/false] + {Op: JumpTrue, Arg: []int{13}}, // 4 [], goto 17 + {Op: Push, Arg: []int{2}}, // 5 [i 2] + {Op: Fdup, Arg: []int{-2}}, // 6 [i] + {Op: Sub}, // 7 [(i-2)] + {Op: Push, Arg: []int{1}}, // 8 + {Op: Call}, // 9 [fib(i-2)] + {Op: Push, Arg: []int{1}}, // 10 [(i-2) i 1] + {Op: Fdup, Arg: []int{-2}}, // 11 [fib(i-2) i] + {Op: Sub}, // 12 [(i-2) (i-1)] + {Op: Push, Arg: []int{1}}, // 13 + {Op: Call}, // 14 [fib(i-2) fib(i-1)] + {Op: Add}, // 15 [fib(i-2)+fib(i-1)] + {Op: Return, Arg: []int{1, 1}}, // 16 return i + {Op: Fdup, Arg: []int{-2}}, // 17 [i] + {Op: Return, Arg: []int{1, 1}}, // 18 return i + {Op: Push, Arg: []int{6}}, // 19 [1] + {Op: Push, Arg: []int{1}}, // 20 + {Op: Call}, // 21 [fib(*1)] + {Op: Exit}, // 22 }, start: 0, end: 1, mem: "[8]", }, { // #16 -- Fibonacci with some immediate instructions. - code: [][]int64{ - {0, Jump, 14}, // 0 - {0, Fdup, -2}, // 1 [i] - {0, Loweri, 2}, // 2 [true/false] - {0, JumpTrue, 9}, // 3 [], goto 12 - {0, Fdup, -2}, // 4 [i] - {0, Subi, 2}, // 5 [(i-2)] - {0, Calli, 1}, // 6 [fib(i-2)] - {0, Fdup, -2}, // 7 [fib(i-2) i] - {0, Subi, 1}, // 8 [(i-2) (i-1)] - {0, Calli, 1}, // 9 [fib(i-2) fib(i-1)], call 1 - {0, Add}, // 10 [fib(i-2)+fib(i-1)] - {0, Return, 1, 1}, // 11 return i - {0, Fdup, -2}, // 12 [i] - {0, Return, 1, 1}, // 13 return i - {0, Push, 6}, // 14 [1] - {0, Calli, 1}, // 15 [fib(*1)], call 1 - {0, Exit}, // 16 + code: []Instruction{ + {Op: Jump, Arg: []int{14}}, // 0 + {Op: Fdup, Arg: []int{-2}}, // 1 [i] + {Op: Loweri, Arg: []int{2}}, // 2 [true/false] + {Op: JumpTrue, Arg: []int{9}}, // 3 [], goto 12 + {Op: Fdup, Arg: []int{-2}}, // 4 [i] + {Op: Subi, Arg: []int{2}}, // 5 [(i-2)] + {Op: Calli, Arg: []int{1}}, // 6 [fib(i-2)] + {Op: Fdup, Arg: []int{-2}}, // 7 [fib(i-2) i] + {Op: Subi, Arg: []int{1}}, // 8 [(i-2) (i-1)] + {Op: Calli, Arg: []int{1}}, // 9 [fib(i-2) fib(i-1)], call 1 + {Op: Add}, // 10 [fib(i-2)+fib(i-1)] + {Op: Return, Arg: []int{1, 1}}, // 11 return i + {Op: Fdup, Arg: []int{-2}}, // 12 [i] + {Op: Return, Arg: []int{1, 1}}, // 13 return i + {Op: Push, Arg: []int{6}}, // 14 [1] + {Op: Calli, Arg: []int{1}}, // 15 [fib(*1)], call 1 + {Op: Exit}, // 16 }, start: 0, end: 1, mem: "[8]", }} |
