summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarc Vertes <mvertes@free.fr>2023-11-23 17:56:35 +0100
committerMarc Vertes <mvertes@free.fr>2023-11-24 09:12:46 +0100
commit6a32a7bc5f6320902cd5c2910a1353a0f7039237 (patch)
tree0fcce51e4d4f54d48d57a5dda8a896a35264f68b
parentc548093d79edece3c1cbb7e4dc03d92fe45b1cc7 (diff)
parser: fix allocation of local variables
A 'New' instruction is added in VM to manage initialisation of typed variables in the stack. The instantiated type symbols are now added to global data. Accessing and setting values by address is now working.
-rw-r--r--lang/token.go9
-rw-r--r--parser/compiler.go106
-rw-r--r--parser/decl.go20
-rw-r--r--parser/interpreter_test.go7
-rw-r--r--parser/symbol.go26
-rw-r--r--parser/type.go7
-rw-r--r--vm/vm.go40
-rw-r--r--vm/vm_test.go5
8 files changed, 140 insertions, 80 deletions
diff --git a/lang/token.go b/lang/token.go
index 40e6bfc..5205efc 100644
--- a/lang/token.go
+++ b/lang/token.go
@@ -59,7 +59,7 @@ const (
// Unary operations
Plus // unary +
Minus // unary -
- Address // unary &
+ Addr // unary &
Deref // unary *
BitComp // unary ^
Arrow // unary ->
@@ -107,19 +107,20 @@ const (
// Internal virtual machine tokens (no corresponding keyword)
Call
CallX
+ EqualSet
Grow
Index
- Label
JumpFalse
JumpSetFalse
JumpSetTrue
- EqualSet
+ Label
+ New
)
// TODO: define UnaryOp per language
var UnaryOp = map[TokenId]TokenId{
Add: Plus, // +
- And: Address, // &
+ And: Addr, // &
Not: Not, // !
Mul: Deref, // *
Sub: Minus, // -
diff --git a/parser/compiler.go b/parser/compiler.go
index 36aa9d8..bb88026 100644
--- a/parser/compiler.go
+++ b/parser/compiler.go
@@ -13,9 +13,9 @@ import (
type Compiler struct {
*Parser
- vm.Code // produced code, to fill VM with
- Data []any // produced data, will be at the bottom of VM stack
- Entry int // offset in Code to start execution from (skip function defintions)
+ vm.Code // produced code, to fill VM with
+ Data []reflect.Value // produced data, will be at the bottom of VM stack
+ Entry int // offset in Code to start execution from (skip function defintions)
strings map[string]int // locations of strings in Data
}
@@ -28,7 +28,7 @@ func NewCompiler(scanner *scanner.Scanner) *Compiler {
}
}
-func (c *Compiler) AddSym(name string, value any) int {
+func (c *Compiler) AddSym(name string, value reflect.Value) int {
p := len(c.Data)
c.Data = append(c.Data, value)
c.Parser.AddSym(p, name, value)
@@ -51,7 +51,7 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
if err != nil {
return err
}
- push(&symbol{kind: symConst, value: n})
+ push(&symbol{kind: symConst, value: reflect.ValueOf(n), Type: reflect.TypeOf(0)})
emit(int64(t.Pos), vm.Push, int64(n))
case lang.String:
@@ -59,10 +59,10 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
i, ok := c.strings[s]
if !ok {
i = len(c.Data)
- c.Data = append(c.Data, s)
+ c.Data = append(c.Data, reflect.ValueOf(s))
c.strings[s] = i
}
- push(&symbol{kind: symConst, value: s})
+ push(&symbol{kind: symConst, value: reflect.ValueOf(s)})
emit(int64(t.Pos), vm.Dup, int64(i))
case lang.Add:
@@ -87,25 +87,40 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
case lang.Plus:
// Nothing to do.
- case lang.Address:
- emit(int64(t.Pos), vm.Address)
+ case lang.Addr:
+ push(&symbol{Type: reflect.PointerTo(pop().Type)})
+ emit(int64(t.Pos), vm.Addr)
case lang.Deref:
+ push(&symbol{Type: pop().Type.Elem()})
emit(int64(t.Pos), vm.Deref)
case lang.Index:
+ push(&symbol{Type: pop().Type.Elem()})
emit(int64(t.Pos), vm.Index)
case lang.Greater:
+ push(&symbol{Type: booleanOpType(pop(), pop())})
emit(int64(t.Pos), vm.Greater)
case lang.Less:
+ push(&symbol{Type: booleanOpType(pop(), pop())})
emit(int64(t.Pos), vm.Lower)
case lang.Call:
+ typ := pop().Type
+ // TODO: pop input types (careful with variadic function)
+ for i := 0; i < typ.NumOut(); i++ {
+ push(&symbol{Type: typ.Out(i)})
+ }
emit(int64(t.Pos), vm.Call)
case lang.CallX:
+ typ := pop().Type
+ // TODO: pop input types (careful with variadic function)
+ for i := 0; i < typ.NumOut(); i++ {
+ push(&symbol{Type: typ.Out(i)})
+ }
emit(int64(t.Pos), vm.CallX, int64(t.Beg))
case lang.Grow:
@@ -115,9 +130,10 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
// TODO: support assignment to local, composite objects
st := tokens[i-1]
l := len(c.Data)
- c.Data = append(c.Data, nil)
- // TODO: symbol should be added at parse, not here.
- c.addSym(l, st.Str, nil, symVar, nil, false)
+ typ := pop().Type
+ v := reflect.New(typ).Elem()
+ c.addSym(l, st.Str, v, symVar, typ, false)
+ c.Data = append(c.Data, v)
emit(int64(st.Pos), vm.Assign, int64(l))
case lang.Assign:
@@ -130,7 +146,16 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
if !ok {
return fmt.Errorf("symbol not found: %s", st.Str)
}
+ typ := pop().Type
+ if s.Type == nil {
+ s.Type = typ
+ s.value = reflect.New(typ).Elem()
+ }
if s.local {
+ if !s.used {
+ emit(int64(st.Pos), vm.New, int64(s.index), int64(c.typeSym(s.Type).index))
+ s.used = true
+ }
emit(int64(st.Pos), vm.Fassign, int64(s.index))
break
}
@@ -141,9 +166,11 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
emit(int64(st.Pos), vm.Assign, int64(s.index))
case lang.Equal:
+ push(&symbol{Type: booleanOpType(pop(), pop())})
emit(int64(t.Pos), vm.Equal)
case lang.EqualSet:
+ push(&symbol{Type: booleanOpType(pop(), pop())})
emit(int64(t.Pos), vm.EqualSet)
case lang.Ident:
@@ -172,64 +199,64 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
lc := len(c.Code)
s, ok := c.symbols[t.Str]
if ok {
- s.value = lc
+ s.value = reflect.ValueOf(lc)
if s.kind == symFunc {
// label is a function entry point, register its code address in data.
s.index = len(c.Data)
- c.Data = append(c.Data, lc)
+ c.Data = append(c.Data, s.value)
} else {
- c.Data[s.index] = lc
+ c.Data[s.index] = s.value
}
} else {
- c.symbols[t.Str] = &symbol{kind: symLabel, value: lc}
+ c.symbols[t.Str] = &symbol{kind: symLabel, value: reflect.ValueOf(lc)}
}
case lang.JumpFalse:
label := t.Str[10:]
- i := 0
+ var i int64
if s, ok := c.symbols[label]; !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.(int) - len(c.Code)
+ i = s.value.Int() - int64(len(c.Code))
}
- emit(int64(t.Pos), vm.JumpFalse, int64(i))
+ emit(int64(t.Pos), vm.JumpFalse, i)
case lang.JumpSetFalse:
label := t.Str[13:]
- i := 0
+ var i int64
if s, ok := c.symbols[label]; !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.(int) - len(c.Code)
+ i = s.value.Int() - int64(len(c.Code))
}
- emit(int64(t.Pos), vm.JumpSetFalse, int64(i))
+ emit(int64(t.Pos), vm.JumpSetFalse, i)
case lang.JumpSetTrue:
label := t.Str[12:]
- i := 0
+ var i int64
if s, ok := c.symbols[label]; !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.(int) - len(c.Code)
+ i = s.value.Int() - int64(len(c.Code))
}
emit(int64(t.Pos), vm.JumpSetTrue, int64(i))
case lang.Goto:
label := t.Str[5:]
- i := 0
+ var i int64
if s, ok := c.symbols[label]; !ok {
t.Beg = len(c.Code)
fixList = append(fixList, t)
} else {
- i = s.value.(int) - len(c.Code)
+ i = s.value.Int() - int64(len(c.Code))
}
- emit(int64(t.Pos), vm.Jump, int64(i))
+ emit(int64(t.Pos), vm.Jump, i)
case lang.Period:
if f, ok := pop().Type.FieldByName("X" + t.Str[1:]); ok {
@@ -264,16 +291,14 @@ func (c *Compiler) Codegen(tokens Tokens) (err error) {
if !ok {
return fmt.Errorf("label not found: %q", label)
}
- c.Code[t.Beg][2] = int64(s.value.(int) - t.Beg)
+ c.Code[t.Beg][2] = s.value.Int() - int64(t.Beg)
}
return err
}
-func arithmeticOpType(s1, s2 *symbol) reflect.Type {
- // TODO: make it complete
- return symtype(s1)
-}
+func arithmeticOpType(s1, s2 *symbol) reflect.Type { return symtype(s1) }
+func booleanOpType(s1, s2 *symbol) reflect.Type { return reflect.TypeOf(true) }
func (c *Compiler) PrintCode() {
labels := map[int][]string{} // labels indexed by code location
@@ -281,7 +306,7 @@ func (c *Compiler) PrintCode() {
for name, sym := range c.symbols {
if sym.kind == symLabel || sym.kind == symFunc {
- i := sym.value.(int)
+ i := int(sym.value.Int())
labels[i] = append(labels[i], name)
}
if sym.used {
@@ -329,7 +354,7 @@ func (c *Compiler) PrintData() {
}
fmt.Println("# Data:")
for i, d := range c.Data {
- fmt.Printf("%4d %T %v %v\n", i, d, d, dict[i])
+ fmt.Printf("%4d %T %v %v\n", i, d.Interface(), d, dict[i])
}
}
@@ -341,6 +366,19 @@ func (c *Compiler) NumIn(i int) (int, bool) {
return -1, false
}
+func (c *Compiler) typeSym(t reflect.Type) *symbol {
+ tsym, ok := c.symbols[t.String()]
+ if !ok {
+ tsym = &symbol{index: unsetAddr, kind: symType, Type: t}
+ c.symbols[t.String()] = tsym
+ }
+ if tsym.index == unsetAddr {
+ tsym.index = len(c.Data)
+ c.Data = append(c.Data, reflect.New(t).Elem())
+ }
+ return tsym
+}
+
func slint64(a []int) []int64 {
r := make([]int64, len(a))
for i, v := range a {
diff --git a/parser/decl.go b/parser/decl.go
index 782b57a..54a2989 100644
--- a/parser/decl.go
+++ b/parser/decl.go
@@ -4,12 +4,15 @@ import (
"errors"
"go/constant"
"go/token"
+ "reflect"
"strings"
"github.com/gnolang/parscan/lang"
"github.com/gnolang/parscan/scanner"
)
+var nilValue = reflect.ValueOf(nil)
+
func (p *Parser) ParseConst(in Tokens) (out Tokens, err error) {
if len(in) < 2 {
return out, errors.New("missing expression")
@@ -51,8 +54,8 @@ func (p *Parser) parseConstLine(in Tokens) (out Tokens, err error) {
if errors.Is(err, MissingTypeErr) {
for _, lt := range decl.Split(lang.Comma) {
vars = append(vars, lt[0].Str)
- // TODO: compute type from rhs
- p.addSym(unsetAddr, strings.TrimPrefix(p.scope+"/"+lt[0].Str, "/"), nil, symConst, nil, false)
+ name := strings.TrimPrefix(p.scope+"/"+lt[0].Str, "/")
+ p.addSym(unsetAddr, name, nilValue, symConst, nil, false)
}
} else {
return out, err
@@ -75,7 +78,7 @@ func (p *Parser) parseConstLine(in Tokens) (out Tokens, err error) {
kind: symConst,
index: unsetAddr,
cval: cval,
- value: constValue(cval),
+ value: reflect.ValueOf(constValue(cval)),
local: p.funcScope != "",
used: true,
}
@@ -222,7 +225,7 @@ func (p *Parser) parseTypeLine(in Tokens) (out Tokens, err error) {
if err != nil {
return out, err
}
- p.addSym(unsetAddr, in[0].Str, nil, symType, typ, p.funcScope != "")
+ p.addSym(unsetAddr, in[0].Str, reflect.New(typ).Elem(), symType, typ, p.funcScope != "")
return out, err
}
@@ -257,8 +260,13 @@ func (p *Parser) parseVarLine(in Tokens) (out Tokens, err error) {
if errors.Is(err, MissingTypeErr) {
for _, lt := range decl.Split(lang.Comma) {
vars = append(vars, lt[0].Str)
- // TODO: compute type from rhs
- p.addSym(unsetAddr, strings.TrimPrefix(p.scope+"/"+lt[0].Str, "/"), nil, symVar, nil, false)
+ name := strings.TrimPrefix(p.scope+"/"+lt[0].Str, "/")
+ if p.funcScope == "" {
+ p.addSym(unsetAddr, name, nilValue, symVar, nil, false)
+ continue
+ }
+ p.addSym(p.framelen[p.funcScope], name, nilValue, symVar, nil, false)
+ p.framelen[p.funcScope]++
}
} else {
return out, err
diff --git a/parser/interpreter_test.go b/parser/interpreter_test.go
index 9555475..9110e3c 100644
--- a/parser/interpreter_test.go
+++ b/parser/interpreter_test.go
@@ -52,7 +52,7 @@ func run(t *testing.T, tests []etest) {
func TestExpr(t *testing.T) {
run(t, []etest{
- {src: "", res: "<nil>"},
+ {src: "", res: "<invalid reflect.Value>"},
{src: "1+2", res: "3"},
{src: "1+", err: "block not terminated"},
{src: "a := 1 + 2; b := 0; a + 1", res: "4"},
@@ -92,7 +92,7 @@ func TestFunc(t *testing.T) {
{src: "func f() int {return 2}; a := f(); a", res: "2"},
{src: "func f() int {return 2}; f()", res: "2"},
{src: "func f(a int) int {return a+2}; f(3)", res: "5"},
- {src: "func f(a int) int {if a < 4 {a = 5}; return a }; f(3)", res: "5"},
+ {src: "func f(a int) int {if a < 4 {a = 5}; return a}; f(3)", res: "5"},
{src: "func f(a int) int {return a+2}; 7 - f(3)", res: "2"},
{src: "func f(a int) int {return a+2}; f(5) - f(3)", res: "2"},
{src: "func f(a int) int {return a+2}; f(3) - 2", res: "3"},
@@ -195,7 +195,8 @@ func TestArray(t *testing.T) {
func TestPointer(t *testing.T) {
run(t, []etest{
{src: "var a *int; a", res: "<nil>"},
- //{src: "var a int = 2; var b *int = &a; b", res: "2"},
+ {src: "var a int; var b *int = &a; *b", res: "0"},
+ {src: "var a int = 2; var b *int = &a; *b", res: "2"},
})
}
diff --git a/parser/symbol.go b/parser/symbol.go
index 7edcef3..6c5ab9b 100644
--- a/parser/symbol.go
+++ b/parser/symbol.go
@@ -22,12 +22,12 @@ const unsetAddr = -65535
type symbol struct {
kind symKind
- index int // address of symbol in frame
- value any
- cval constant.Value
- Type reflect.Type
- local bool // if true address is relative to local frame, otherwise global
- used bool
+ index int // address of symbol in frame
+ value reflect.Value //
+ cval constant.Value //
+ Type reflect.Type //
+ local bool // if true address is relative to local frame, otherwise global
+ used bool //
}
func symtype(s *symbol) reflect.Type {
@@ -37,11 +37,13 @@ func symtype(s *symbol) reflect.Type {
return reflect.TypeOf(s.value)
}
-func (p *Parser) AddSym(i int, name string, v any) { p.addSym(i, name, v, symValue, nil, false) }
+func (p *Parser) AddSym(i int, name string, v reflect.Value) {
+ p.addSym(i, name, v, symValue, nil, false)
+}
-func (p *Parser) addSym(i int, name string, v any, k symKind, t reflect.Type, local bool) {
+func (p *Parser) addSym(i int, name string, v reflect.Value, k symKind, t reflect.Type, local bool) {
name = strings.TrimPrefix(name, "/")
- p.symbols[name] = &symbol{kind: k, index: i, local: local, value: v, Type: t, used: true}
+ p.symbols[name] = &symbol{kind: k, index: i, local: local, value: v, Type: t}
}
// getSym searches for an existing symbol starting from the deepest scope.
@@ -72,9 +74,9 @@ func initUniverse() map[string]*symbol {
"nil": {index: unsetAddr},
"iota": {kind: symConst, index: unsetAddr},
- "true": {index: unsetAddr, value: true, Type: reflect.TypeOf(true)},
- "false": {index: unsetAddr, value: false, Type: reflect.TypeOf(false)},
+ "true": {index: unsetAddr, value: reflect.ValueOf(true), Type: reflect.TypeOf(true)},
+ "false": {index: unsetAddr, value: reflect.ValueOf(false), Type: reflect.TypeOf(false)},
- "println": {index: unsetAddr, value: func(v ...any) { fmt.Println(v...) }},
+ "println": {index: unsetAddr, value: reflect.ValueOf(func(v ...any) { fmt.Println(v...) })},
}
}
diff --git a/parser/type.go b/parser/type.go
index 31af1e3..ff16e4f 100644
--- a/parser/type.go
+++ b/parser/type.go
@@ -170,12 +170,7 @@ func (p *Parser) parseParamTypes(in Tokens, flag typeFlag) (types []reflect.Type
}
func (p *Parser) addSymVar(index int, name string, typ reflect.Type, flag typeFlag, local bool) {
- var zv any = reflect.New(typ).Elem()
- switch typ.Kind() {
- case reflect.Struct, reflect.Array, reflect.Slice, reflect.Pointer:
- default:
- zv = zv.(reflect.Value).Interface()
- }
+ zv := reflect.New(typ).Elem()
switch flag {
case parseTypeIn:
p.addSym(-index-2, name, zv, symVar, typ, true)
diff --git a/vm/vm.go b/vm/vm.go
index 52618de..f282367 100644
--- a/vm/vm.go
+++ b/vm/vm.go
@@ -14,7 +14,7 @@ const (
// instruction effect on stack: values consumed -- values produced
Nop = iota // --
Add // n1 n2 -- sum ; sum = n1+n2
- Address // a -- &a ;
+ Addr // a -- &a ;
Assign // val -- ; mem[$1] = val
Fassign // val -- ; mem[$1] = val
Vassign // val dest -- ; dest.Set(val)
@@ -39,6 +39,7 @@ const (
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
@@ -50,7 +51,7 @@ const (
var strop = [...]string{ // for VM tracing.
Nop: "Nop",
Add: "Add",
- Address: "Address",
+ Addr: "Addr",
Assign: "Assign",
Call: "Call",
Calli: "Calli",
@@ -74,6 +75,7 @@ var strop = [...]string{ // for VM tracing.
Lower: "Lower",
Loweri: "Loweri",
Mul: "Mul",
+ New: "New",
Not: "Not",
Pop: "Pop",
Push: "Push",
@@ -87,8 +89,7 @@ type Code [][]int64
// Machine represents a virtual machine.
type Machine struct {
- code Code // code to execute
- // mem []any // memory, as a stack
+ 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
@@ -113,7 +114,7 @@ func (m *Machine) Run() (err error) {
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, mem)
+ 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 {
@@ -125,15 +126,15 @@ func (m *Machine) Run() (err error) {
mem[sp-2] = reflect.ValueOf(int(mem[sp-2].Int() + mem[sp-1].Int()))
mem = mem[:sp-1]
case Mul:
- mem[sp-2].SetInt(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 Address:
+ case Addr:
mem[sp-1] = mem[sp-1].Addr()
case Assign:
- mem[op[2]] = mem[sp-1]
+ mem[op[2]].Set(mem[sp-1])
mem = mem[:sp-1]
case Fassign:
- mem[fp+int(op[2])-1] = mem[sp-1]
+ mem[fp+int(op[2])-1].Set(mem[sp-1])
mem = mem[:sp-1]
case Call:
nip := int(mem[sp-1].Int())
@@ -161,6 +162,8 @@ func (m *Machine) Run() (err error) {
mem[sp-1] = mem[sp-1].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()
case Equal:
mem[sp-2] = reflect.ValueOf(mem[sp-2].Equal(mem[sp-1]))
mem = mem[:sp-1]
@@ -226,7 +229,9 @@ func (m *Machine) Run() (err error) {
case Pop:
mem = mem[:sp-int(op[2])]
case Push:
- mem = append(mem, reflect.ValueOf(int(op[2])))
+ //mem = append(mem, reflect.ValueOf(int(op[2])))
+ mem = append(mem, reflect.New(reflect.TypeOf(0)).Elem())
+ mem[sp].SetInt(op[2])
case Grow:
mem = append(mem, make([]reflect.Value, op[2])...)
case Return:
@@ -236,10 +241,10 @@ func (m *Machine) Run() (err error) {
mem = append(mem[:ofp-int(op[2])-int(op[3])-1], mem[sp-int(op[2]):]...)
continue
case Sub:
- mem[sp-2].SetInt(mem[sp-1].Int() - mem[sp-2].Int())
+ mem[sp-2] = reflect.ValueOf(int(mem[sp-1].Int() - mem[sp-2].Int()))
mem = mem[:sp-1]
case Subi:
- mem[sp-1].SetInt(mem[sp-1].Int() - op[2])
+ mem[sp-1] = reflect.ValueOf(int(mem[sp-1].Int() - op[2]))
case Index:
mem[sp-2] = mem[sp-1].Index(int(mem[sp-2].Int()))
mem = mem[:sp-1]
@@ -309,3 +314,14 @@ func slint(a []int64) []int {
}
return r
}
+
+func Vstring(lv []reflect.Value) string {
+ s := "["
+ for _, v := range lv {
+ if s != "[" {
+ s += " "
+ }
+ s += fmt.Sprintf("%v", v)
+ }
+ return s + "]"
+}
diff --git a/vm/vm_test.go b/vm/vm_test.go
index c9540d2..07c063e 100644
--- a/vm/vm_test.go
+++ b/vm/vm_test.go
@@ -23,9 +23,8 @@ func TestVM(t *testing.T) {
if err := m.Run(); err != nil {
t.Errorf("run error: %v", err)
}
- t.Log(m.mem)
- r := fmt.Sprintf("%v", m.mem[test.start:test.end])
- if r != test.mem {
+ t.Log(Vstring(m.mem))
+ if r := Vstring(m.mem[test.start:test.end]); r != test.mem {
t.Errorf("got %v, want %v", r, test.mem)
}
})