go-vise

Constrained Size Output Virtual Machine
Info | Log | Files | Refs | README | LICENSE

commit 7bb479f4cbf2b85686abcf3af4d12f007cfb8546
parent d3fb782a8cb9cd9774b6a442c3ff4af0a21201b0
Author: lash <dev@holbrook.no>
Date:   Tue,  4 Apr 2023 20:32:40 +0100

Add parser for sized arg

Diffstat:
Mgo/asm/asm.go | 105++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Mgo/asm/asm_test.go | 22++++++++++++++++++++++
2 files changed, 115 insertions(+), 12 deletions(-)

diff --git a/go/asm/asm.go b/go/asm/asm.go @@ -1,8 +1,11 @@ package asm import ( + "bytes" + "encoding/binary" "fmt" "io" + "math" "strings" "github.com/alecthomas/participle/v2" @@ -25,15 +28,15 @@ func(d Display) String() string { return fmt.Sprintf("Display: %v %v", d.Sym, d.Val) } -type Sig struct { - Sym string `@Sym Whitespace` - Size uint32 `@Size Whitespace` - Val uint32 `@Size Whitespace` -} - -func(s Sig) String() string { - return fmt.Sprintf("Sig: %v %v %v", s.Sym, s.Size, s.Val) -} +//type Sig struct { +// Sym string `@Sym Whitespace` +// Size uint32 `@Size Whitespace` +// Val uint32 `@Size Whitespace` +//} +// +//func(s Sig) String() string { +// return fmt.Sprintf("Sig: %v %v %v", s.Sym, s.Size, s.Val) +//} type Single struct { One string `@Sym Whitespace` @@ -111,12 +114,90 @@ var ( ) ) +func numSize(n uint32) int { + v := math.Log2(float64(n)) + return int(((v - 1) / 8) + 1) +} + +func writeOpcode(op vm.Opcode, w *bytes.Buffer) (int, error) { + bn := [2]byte{} + binary.BigEndian.PutUint16(bn[:], uint16(op)) + n, err := w.Write(bn[:]) + return n, err +} + +func writeSym(s string, w *bytes.Buffer) (int, error) { + sz := len(s) + if sz > 255 { + return 0, fmt.Errorf("string size %v too big", sz) + } + w.Write([]byte{byte(sz)}) + return w.WriteString(s) +} + +func writeSize(n uint32, w *bytes.Buffer) (int, error) { + bn := [4]byte{} + sz := numSize(n) + if sz > 4 { + return 0, fmt.Errorf("number size %v too big", sz) + } + w.Write([]byte{byte(sz)}) + binary.BigEndian.PutUint32(bn[:], n) + c := 4-sz + return w.Write(bn[c:]) +} + + +func parseSized(op vm.Opcode, arg Arg, w io.Writer) (int, error) { + var rn int + + v := arg.ArgSized + if v == nil { + return 0, nil + } + + b := bytes.NewBuffer(nil) + + n, err := writeOpcode(op, b) + rn += n + if err != nil { + return rn, err + } + + n, err = writeSym(v.Sym, b) + rn += n + if err != nil { + return rn, err + } + + n, err = writeSize(v.Size, b) + rn += n + if err != nil { + return rn, err + } + if w != nil { + rn, err = w.Write(b.Bytes()) + } else { + rn = 0 + } + return rn, err +} + func Parse(s string, w io.Writer) (int, error) { rd := strings.NewReader(s) ast, err := asmParser.Parse("file", rd) - for i, v := range ast.Instructions { + var rn int + + for _, v := range ast.Instructions { op := vm.OpcodeIndex[v.OpCode] - fmt.Printf("%v (%v) %v\n", i, op, v) + n, err := parseSized(op, v.OpArg, w) + if err != nil { + return n, err + } + if n > 0 { + rn += n + continue + } } - return 0, err + return rn, err } diff --git a/go/asm/asm_test.go b/go/asm/asm_test.go @@ -1,6 +1,7 @@ package asm import ( + "bytes" "log" "testing" @@ -16,6 +17,7 @@ func TestParserInit(t *testing.T) { b = vm.NewLine(b, vm.MOUT, []string{"bar", "barbarbaz"}, nil, nil) s, err := vm.ToString(b) log.Printf("parsing:\n%s\n", s) + n, err := Parse(s, nil) if err != nil { t.Fatal(err) @@ -24,3 +26,23 @@ func TestParserInit(t *testing.T) { t.Fatalf("expected 0 byte write count, got %v", n) } } + +func TestParserSized(t *testing.T) { + var b []byte + b = vm.NewLine(b, vm.LOAD, []string{"foo"}, []byte{42}, nil) + s, err := vm.ToString(b) + log.Printf("parsing:\n%s\n", s) + + r := bytes.NewBuffer(nil) + n, err := Parse(s, r) + if err != nil { + t.Fatal(err) + } + if n != 8 { + t.Fatalf("expected 0 byte write count, got %v", n) + } + rb := r.Bytes() + if !bytes.Equal(rb, []byte{0x00, vm.LOAD, 0x03, 0x66, 0x6f, 0x6f, 0x01, 0x2a}) { + t.Fatalf("expected 0x00%x012a, got %v", vm.LOAD, rb) + } +}