diff --git a/go/asm/asm.go b/go/asm/asm.go new file mode 100644 index 0000000..1069411 --- /dev/null +++ b/go/asm/asm.go @@ -0,0 +1,118 @@ +package asm + +import ( + "fmt" + "io" + "strings" + + "github.com/alecthomas/participle/v2" + "github.com/alecthomas/participle/v2/lexer" +) + +type Asm struct { + Instructions []*Instruction `@@*` +} + +type Display struct { + Sym string `@Sym Whitespace` + Val string `@Quote @Sym @Quote Whitespace` +} + +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 Single struct { + One string `@Sym Whitespace` +} + +func(s Single) String() string { + return fmt.Sprintf("Single: %v", s.One) +} + +type Double struct { + One string `@Sym Whitespace` + Two string `@Sym Whitespace` +} + +func(d Double) String() string { + return fmt.Sprintf("Double: %v %v", d.One, d.Two) +} + +type Sized struct { + Sym string `@Sym Whitespace` + Size uint32 `@Size Whitespace` + X uint32 `(@Size Whitespace)?` +} + +func(s Sized) String() string { + return fmt.Sprintf("Sized: %v %v", s.Sym, s.Size) +} + +type Arg struct { + ArgNone string "Whitespace?" + ArgDisplay *Display `@@?` + ArgSized *Sized `@@?` + ArgSingle *Single `@@?` + ArgDouble *Double `@@?` +} + +func (a Arg) String() string { + if a.ArgDisplay != nil { + return fmt.Sprintf("%s", a.ArgDisplay) + } + if a.ArgSized != nil { + return fmt.Sprintf("%s", a.ArgSized) + } + if a.ArgSingle != nil { + return fmt.Sprintf("%s", a.ArgSingle) + } + if a.ArgDouble != nil { + return fmt.Sprintf("%s", a.ArgDouble) + } + return "" +} + +type Instruction struct { + OpCode string `@Ident` + OpArg Arg `@@` + Comment string `Comment?` +} + +func (i Instruction) String() string { + return fmt.Sprintf("%s %s", i.OpCode, i.OpArg) +} + +var ( + asmLexer = lexer.MustSimple([]lexer.SimpleRule{ + {"Comment", `(?:#)[^\n]*\n?`}, + {"Ident", `^[A-Z]+`}, + {"Sym", `[a-zA-Z]+`}, + {"Size", `[0-9]+`}, + {"Whitespace", `[ \t\n\r]+`}, + {"Quote", `["']`}, + }) + asmParser = participle.MustBuild[Asm]( + participle.Lexer(asmLexer), + participle.Elide("Comment", "Whitespace"), + ) +) + +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 { + fmt.Printf("%v %v\n", i, v) + } + return 0, err +} diff --git a/go/asm/asm_test.go b/go/asm/asm_test.go new file mode 100644 index 0000000..83ed58f --- /dev/null +++ b/go/asm/asm_test.go @@ -0,0 +1,26 @@ +package asm + +import ( + "log" + "testing" + + "git.defalsify.org/festive/vm" +) + + +func TestParserInit(t *testing.T) { + var b []byte + b = vm.NewLine(b, vm.HALT, nil, nil, nil) + b = vm.NewLine(b, vm.CATCH, []string{"xyzzy"}, []byte{0x02, 0x9a}, []uint8{1}) + b = vm.NewLine(b, vm.LOAD, []string{"foo"}, []byte{42}, nil) + 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) + } + if n != 0 { + t.Fatalf("expected 0 byte write count, got %v", n) + } +} diff --git a/go/vm/interpreter.go b/go/asm/menu.go similarity index 77% rename from go/vm/interpreter.go rename to go/asm/menu.go index 90fbb43..1f415ef 100644 --- a/go/vm/interpreter.go +++ b/go/asm/menu.go @@ -1,7 +1,9 @@ -package vm +package asm import ( "fmt" + + "git.defalsify.org/festive/vm" ) type BatchCode uint16 @@ -61,19 +63,21 @@ func (mp *MenuProcessor) ToLines() []byte { postLines := []byte{} for _, v := range mp.items { - preLines = NewLine(preLines, MOUT, []string{v.choice, v.display}, nil, nil) + preLines = vm.NewLine(preLines, vm.MOUT, []string{v.choice, v.display}, nil, nil) switch v.code { case MENU_UP: - postLines = NewLine(postLines, INCMP, []string{v.choice, "_"}, nil, nil) + postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, "_"}, nil, nil) case MENU_NEXT: _ = postLines case MENU_PREVIOUS: _ = postLines default: - postLines = NewLine(postLines, INCMP, []string{v.choice, v.target}, nil, nil) + postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, v.target}, nil, nil) } } - preLines = NewLine(preLines, HALT, nil, nil, nil) + preLines = vm.NewLine(preLines, vm.HALT, nil, nil, nil) return append(preLines, postLines...) } + + diff --git a/go/vm/interpreter_test.go b/go/asm/menu_test.go similarity index 91% rename from go/vm/interpreter_test.go rename to go/asm/menu_test.go index 33af62c..426339d 100644 --- a/go/vm/interpreter_test.go +++ b/go/asm/menu_test.go @@ -1,7 +1,9 @@ -package vm +package asm import ( "testing" + + "git.defalsify.org/festive/vm" ) @@ -28,7 +30,7 @@ func TestMenuInterpreter(t *testing.T) { t.Errorf("expected error on invalid menu item 'BOGUS'") } b := m.ToLines() - r, err := ToString(b) + r, err := vm.ToString(b) if err != nil { t.Fatal(err) } diff --git a/go/go.mod b/go/go.mod index 77af5ba..e70f0c4 100644 --- a/go/go.mod +++ b/go/go.mod @@ -2,4 +2,7 @@ module git.defalsify.org/festive go 1.20 -require github.com/peteole/testdata-loader v0.3.0 +require ( + github.com/alecthomas/participle/v2 v2.0.0 + github.com/peteole/testdata-loader v0.3.0 +) diff --git a/go/vm/debug.go b/go/vm/debug.go index 7c25369..6b912a8 100644 --- a/go/vm/debug.go +++ b/go/vm/debug.go @@ -52,7 +52,8 @@ func ParseAll(b []byte, w io.Writer) (int, error) { vv = 1 } if w != nil { - rs = fmt.Sprintf("%s %s %v %v # invertmatch=%v\n", s, r, n, vv, m) + //rs = fmt.Sprintf("%s %s %v %v # invertmatch=%v\n", s, r, n, vv, m) + rs = fmt.Sprintf("%s %s %v %v\n", s, r, n, vv) } } } @@ -65,7 +66,8 @@ func ParseAll(b []byte, w io.Writer) (int, error) { if m { vv = 1 } - rs = fmt.Sprintf("%s %v %v # invertmatch=%v\n", s, n, vv, m) + //rs = fmt.Sprintf("%s %v %v # invertmatch=%v\n", s, n, vv, m) + rs = fmt.Sprintf("%s %v %v\n", s, n, vv) } } case LOAD: @@ -153,7 +155,7 @@ func ParseAll(b []byte, w io.Writer) (int, error) { return rn, err } rn += n - log.Printf("wrote %v bytes from instruction %v", n, s) + log.Printf("wrote %v bytes for instruction %v", n, s) } //rs += "\n"