summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Vertes <mvertes@free.fr>2025-11-14 13:47:51 +0100
committerMarc Vertes <mvertes@free.fr>2025-11-14 13:47:51 +0100
commit1a9c22d1219eb0239417db16c9fe96eea3ced2fe (patch)
treea4fa82cfbbc924d281a94e307656a7af14caadcd
parenta4d7730581e8e65faa6a9285d107a041accf60b1 (diff)
fix: refactor VM instruction type
Use custom types for VM instructions. More idiomatic code for tracing.
-rw-r--r--parser/README.md2
-rw-r--r--parser/compiler.go106
-rw-r--r--parser/interpreter.go4
-rw-r--r--vm/op_string.go57
-rw-r--r--vm/vm.go243
-rw-r--r--vm/vm_test.go264
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]]
+}
diff --git a/vm/vm.go b/vm/vm.go
index 3cb9358..0aedaef 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -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]",
}}