commit 664eab98d97deacd1d299994275bcca74be7ca3e
parent d95d27f8fe5fe87954278c604b0451a9553d7896
Author: lash <dev@holbrook.no>
Date: Tue, 4 Apr 2023 10:32:39 +0100
Add asm parser
Diffstat:
8 files changed, 284 insertions(+), 129 deletions(-)
diff --git a/go/asm/asm.go 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
@@ -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/asm/menu.go b/go/asm/menu.go
@@ -0,0 +1,83 @@
+package asm
+
+import (
+ "fmt"
+
+ "git.defalsify.org/festive/vm"
+)
+
+type BatchCode uint16
+
+const (
+ _MENU_OFFSET = 256
+ MENU_DOWN = _MENU_OFFSET
+ MENU_UP = _MENU_OFFSET + 1
+ MENU_NEXT = _MENU_OFFSET + 2
+ MENU_PREVIOUS = _MENU_OFFSET + 3
+ //MENU_BROWSE = _MENU_OFFSET + 4
+)
+
+var (
+ batchCode = map[string]BatchCode{
+ "DOWN": MENU_DOWN,
+ "UP": MENU_UP,
+ "NEXT": MENU_NEXT,
+ "PREVIOUS": MENU_PREVIOUS,
+ //"BROWSE": MENU_BROWSE,
+ }
+)
+
+type menuItem struct {
+ code BatchCode
+ choice string
+ display string
+ target string
+}
+
+type MenuProcessor struct {
+ items []menuItem
+ size uint32
+}
+
+func NewMenuProcessor() MenuProcessor {
+ return MenuProcessor{}
+}
+
+func(mp *MenuProcessor) Add(bop string, choice string, display string, target string) error {
+ bopCode := batchCode[bop]
+ if bopCode == 0 {
+ return fmt.Errorf("unknown menu instruction: %v", bop)
+ }
+ m := menuItem{
+ code: bopCode,
+ choice: choice,
+ display: display,
+ target: target,
+ }
+ mp.items = append(mp.items, m)
+ return nil
+}
+
+func (mp *MenuProcessor) ToLines() []byte {
+ preLines := []byte{}
+ postLines := []byte{}
+
+ for _, v := range mp.items {
+ preLines = vm.NewLine(preLines, vm.MOUT, []string{v.choice, v.display}, nil, nil)
+ switch v.code {
+ case MENU_UP:
+ postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, "_"}, nil, nil)
+ case MENU_NEXT:
+ _ = postLines
+ case MENU_PREVIOUS:
+ _ = postLines
+ default:
+ postLines = vm.NewLine(postLines, vm.INCMP, []string{v.choice, v.target}, nil, nil)
+ }
+ }
+
+ preLines = vm.NewLine(preLines, vm.HALT, nil, nil, nil)
+ return append(preLines, postLines...)
+}
+
+
diff --git a/go/asm/menu_test.go b/go/asm/menu_test.go
@@ -0,0 +1,48 @@
+package asm
+
+import (
+ "testing"
+
+ "git.defalsify.org/festive/vm"
+)
+
+
+func TestMenuInterpreter(t *testing.T) {
+ m := NewMenuProcessor()
+ err := m.Add("DOWN", "0", "inky", "foo")
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = m.Add("NEXT", "1", "pinky", "bar")
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = m.Add("PREVIOUS", "2", "blinky clyde", "baz")
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = m.Add("UP", "99", "tinky-winky", "xyzzy")
+ if err != nil {
+ t.Fatal(err)
+ }
+ err = m.Add("BOGUS", "42", "lala poo", "plugh")
+ if err == nil {
+ t.Errorf("expected error on invalid menu item 'BOGUS'")
+ }
+ b := m.ToLines()
+ r, err := vm.ToString(b)
+ if err != nil {
+ t.Fatal(err)
+ }
+ expect := `MOUT 0 "inky"
+MOUT 1 "pinky"
+MOUT 2 "blinky clyde"
+MOUT 99 "tinky-winky"
+HALT
+INCMP 0 foo
+INCMP 99 _
+`
+ if r != expect {
+ t.Errorf("expected:\n\t%v\ngot:\n\t%v\n", expect, r)
+ }
+}
diff --git 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
@@ -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"
diff --git a/go/vm/interpreter.go b/go/vm/interpreter.go
@@ -1,79 +0,0 @@
-package vm
-
-import (
- "fmt"
-)
-
-type BatchCode uint16
-
-const (
- _MENU_OFFSET = 256
- MENU_DOWN = _MENU_OFFSET
- MENU_UP = _MENU_OFFSET + 1
- MENU_NEXT = _MENU_OFFSET + 2
- MENU_PREVIOUS = _MENU_OFFSET + 3
- //MENU_BROWSE = _MENU_OFFSET + 4
-)
-
-var (
- batchCode = map[string]BatchCode{
- "DOWN": MENU_DOWN,
- "UP": MENU_UP,
- "NEXT": MENU_NEXT,
- "PREVIOUS": MENU_PREVIOUS,
- //"BROWSE": MENU_BROWSE,
- }
-)
-
-type menuItem struct {
- code BatchCode
- choice string
- display string
- target string
-}
-
-type MenuProcessor struct {
- items []menuItem
- size uint32
-}
-
-func NewMenuProcessor() MenuProcessor {
- return MenuProcessor{}
-}
-
-func(mp *MenuProcessor) Add(bop string, choice string, display string, target string) error {
- bopCode := batchCode[bop]
- if bopCode == 0 {
- return fmt.Errorf("unknown menu instruction: %v", bop)
- }
- m := menuItem{
- code: bopCode,
- choice: choice,
- display: display,
- target: target,
- }
- mp.items = append(mp.items, m)
- return nil
-}
-
-func (mp *MenuProcessor) ToLines() []byte {
- preLines := []byte{}
- postLines := []byte{}
-
- for _, v := range mp.items {
- preLines = NewLine(preLines, MOUT, []string{v.choice, v.display}, nil, nil)
- switch v.code {
- case MENU_UP:
- postLines = NewLine(postLines, 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)
- }
- }
-
- preLines = NewLine(preLines, HALT, nil, nil, nil)
- return append(preLines, postLines...)
-}
diff --git a/go/vm/interpreter_test.go b/go/vm/interpreter_test.go
@@ -1,46 +0,0 @@
-package vm
-
-import (
- "testing"
-)
-
-
-func TestMenuInterpreter(t *testing.T) {
- m := NewMenuProcessor()
- err := m.Add("DOWN", "0", "inky", "foo")
- if err != nil {
- t.Fatal(err)
- }
- err = m.Add("NEXT", "1", "pinky", "bar")
- if err != nil {
- t.Fatal(err)
- }
- err = m.Add("PREVIOUS", "2", "blinky clyde", "baz")
- if err != nil {
- t.Fatal(err)
- }
- err = m.Add("UP", "99", "tinky-winky", "xyzzy")
- if err != nil {
- t.Fatal(err)
- }
- err = m.Add("BOGUS", "42", "lala poo", "plugh")
- if err == nil {
- t.Errorf("expected error on invalid menu item 'BOGUS'")
- }
- b := m.ToLines()
- r, err := ToString(b)
- if err != nil {
- t.Fatal(err)
- }
- expect := `MOUT 0 "inky"
-MOUT 1 "pinky"
-MOUT 2 "blinky clyde"
-MOUT 99 "tinky-winky"
-HALT
-INCMP 0 foo
-INCMP 99 _
-`
- if r != expect {
- t.Errorf("expected:\n\t%v\ngot:\n\t%v\n", expect, r)
- }
-}