From b8584768887b78fd031e3321bbc6635ae0387d8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?O=C4=9Fuzhan=20Durgun?= Date: Sun, 28 Aug 2022 20:56:20 +0300 Subject: [PATCH] enhancement: Implement tis100 assembly parser with participle (#1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Oğuzhan Durgun --- .github/workflows/pr.yaml | 5 +-- .golangci.yaml | 9 ++++- Makefile | 4 +-- README.md | 62 +++++++++++++++++++++++++++++++++ cmd/main.go | 13 +++++-- cmd/parse/parse.go | 47 +++++++++++++++++++++++++ cmd/root/root.go | 7 ++++ go.mod | 12 +++++++ go.sum | 24 +++++++++++++ parser/ast.go | 62 +++++++++++++++++++++++++++++++++ parser/ast_types.go | 24 +++++++++++++ parser/parse.go | 43 +++++++++++++++++++++++ parser/parse_test.go | 52 +++++++++++++++++++++++++++ parser/testdata/syntax/add.yaml | 19 ++++++++++ parser/testdata/syntax/jez.yaml | 14 ++++++++ parser/testdata/syntax/jgz.yaml | 14 ++++++++ parser/testdata/syntax/jlz.yaml | 14 ++++++++ parser/testdata/syntax/jmp.yaml | 14 ++++++++ parser/testdata/syntax/jnz.yaml | 14 ++++++++ parser/testdata/syntax/jro.yaml | 23 ++++++++++++ parser/testdata/syntax/mov.yaml | 21 +++++++++++ parser/testdata/syntax/neg.yaml | 9 +++++ parser/testdata/syntax/nop.yaml | 9 +++++ parser/testdata/syntax/sav.yaml | 9 +++++ parser/testdata/syntax/sub.yaml | 19 ++++++++++ parser/testdata/syntax/swp.yaml | 9 +++++ testutil/find.go | 35 +++++++++++++++++++ testutil/path.go | 25 +++++++++++++ 28 files changed, 603 insertions(+), 9 deletions(-) create mode 100644 README.md create mode 100644 cmd/parse/parse.go create mode 100644 cmd/root/root.go create mode 100644 go.sum create mode 100644 parser/ast.go create mode 100644 parser/ast_types.go create mode 100644 parser/parse.go create mode 100644 parser/parse_test.go create mode 100644 parser/testdata/syntax/add.yaml create mode 100644 parser/testdata/syntax/jez.yaml create mode 100644 parser/testdata/syntax/jgz.yaml create mode 100644 parser/testdata/syntax/jlz.yaml create mode 100644 parser/testdata/syntax/jmp.yaml create mode 100644 parser/testdata/syntax/jnz.yaml create mode 100644 parser/testdata/syntax/jro.yaml create mode 100644 parser/testdata/syntax/mov.yaml create mode 100644 parser/testdata/syntax/neg.yaml create mode 100644 parser/testdata/syntax/nop.yaml create mode 100644 parser/testdata/syntax/sav.yaml create mode 100644 parser/testdata/syntax/sub.yaml create mode 100644 parser/testdata/syntax/swp.yaml create mode 100644 testutil/find.go create mode 100644 testutil/path.go diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 7c87d34..982e4a7 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -60,9 +60,6 @@ jobs: ~/.cache/diofis/bin key: ${{ runner.os }}-go-${{ hashFiles('**/go.mod') }} - - name: Generate - run: make generate - - name: Check repo status run: |- REPO_STATUS="$(git status --porcelain)" @@ -72,7 +69,7 @@ jobs: exit 1 fi - name: Test - run: make test-all + run: make test golangci: needs: changes diff --git a/.golangci.yaml b/.golangci.yaml index ca535da..bc03dd6 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -3,12 +3,18 @@ run: go: "1.19" timeout: 300s build-tags: - - tests + - test linters-settings: exhaustive: default-signifies-exhaustive: true + gci: + sections: + - standard + - default + - prefix(github.com/oguzhand95/tis100) + gofumpt: extra-rules: true @@ -42,6 +48,7 @@ linters: - exportloopref - forbidigo - forcetypeassert + - gci - goconst - gocritic - godot diff --git a/Makefile b/Makefile index 284443a..3133a90 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ deps: .PHONY: test test: $(GOTESTSUM) - @ $(GOTESTSUM) -- -tags=tests $(COVERPROFILE) -cover ./... + @ $(GOTESTSUM) -- -tags=test $(COVERPROFILE) -cover ./... .PHONY: lint lint: $(GOLANGCI_LINT) @@ -25,7 +25,7 @@ compile: @ go build -o bin/tis100 cmd/main.go .PHONY: build -build: lint test package +build: clean-tools lint test package .PHONY: clean-tools clean-tools: diff --git a/README.md b/README.md new file mode 100644 index 0000000..156f1bd --- /dev/null +++ b/README.md @@ -0,0 +1,62 @@ +# TIS100 + +A tool and package to parse TIS100 assembly programs. + +## CLI Usage + +### Command `tis100 parse ` + +Parses the TIS100 assembly program in the given ``, and prints the AST result into standard output. + +``` +MOV 10, ACC +MOV ACC, DOWN +``` + +```bash +-> tis100 parse ~/program.asm +``` + +The result for the above execution looks like this; + +```yaml +instructions: + - mov: + source: + literal: 10 + destination: + register: ACC + - mov: + source: + register: ACC + destination: + register: DOWN +``` + +## Package Usage + +```bash +go get github.com/oguzhand95/tis100 +``` + +```go +import "github.com/oguzhand95/tis100/parser" + +func main() { + b, err := os.ReadFile(c.Path) + if err != nil { + return fmt.Errorf("failed to open the file in path %s: %w", c.Path, err) + } + + ast, err := parser.Parse(bytes.NewReader(b), filepath.Base(c.Path)) + if err != nil { + return fmt.Errorf("failed to parse the program: %w", err) + } + + // Use ast struct to some stuff +} +``` + +# Thanks + +- Thanks [Zachtronics](https://www.zachtronics.com/) for developing this awesome [puzzle game](https://store.steampowered.com/app/370360/TIS100/)! \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index 776cbb1..b76dd4c 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,9 +1,18 @@ package main import ( - "log" + "github.com/alecthomas/kong" + + "github.com/oguzhand95/tis100/cmd/root" ) func main() { - log.Println("hello") + cli := &root.Cli{} + ctx := kong.Parse(cli, + kong.Name("tis100"), + kong.Description("A command line interface to do TIS100 related jobs"), + kong.UsageOnError(), + ) + + ctx.FatalIfErrorf(ctx.Run()) } diff --git a/cmd/parse/parse.go b/cmd/parse/parse.go new file mode 100644 index 0000000..d608b02 --- /dev/null +++ b/cmd/parse/parse.go @@ -0,0 +1,47 @@ +package parse + +import ( + "bytes" + "fmt" + "os" + "path/filepath" + + "github.com/alecthomas/kong" + "gopkg.in/yaml.v3" + + "github.com/oguzhand95/tis100/parser" +) + +const help = `Parse TIS100 assembly program and output the AST tree` + +type Cmd struct { + Path string `arg:"" name:"path" help:"path to tis100 assembly program"` +} + +func (c *Cmd) Run(k *kong.Kong) error { + b, err := os.ReadFile(c.Path) + if err != nil { + return fmt.Errorf("failed to open the file in path %s: %w", c.Path, err) + } + + ast, err := parser.Parse(bytes.NewReader(b), filepath.Base(c.Path)) + if err != nil { + return fmt.Errorf("failed to parse the program: %w", err) + } + + y, err := yaml.Marshal(ast) + if err != nil { + return fmt.Errorf("failed to marshal AST of the parsed TIS100 program: %w", err) + } + + _, err = fmt.Fprint(k.Stdout, string(y)) + if err != nil { + return fmt.Errorf("failed to print the AST to the stdout: %w", err) + } + + return nil +} + +func (c *Cmd) Help() string { + return help +} diff --git a/cmd/root/root.go b/cmd/root/root.go new file mode 100644 index 0000000..0edc9a1 --- /dev/null +++ b/cmd/root/root.go @@ -0,0 +1,7 @@ +package root + +import "github.com/oguzhand95/tis100/cmd/parse" + +type Cli struct { + Parse parse.Cmd `cmd:"" name:"parse"` +} diff --git a/go.mod b/go.mod index 698a062..1f6a0fa 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,15 @@ module github.com/oguzhand95/tis100 go 1.19 + +require ( + github.com/alecthomas/kong v0.6.1 + github.com/alecthomas/participle/v2 v2.0.0-beta.5 + github.com/stretchr/testify v1.8.0 + gopkg.in/yaml.v3 v3.0.1 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..5128ede --- /dev/null +++ b/go.sum @@ -0,0 +1,24 @@ +github.com/alecthomas/assert/v2 v2.0.3 h1:WKqJODfOiQG0nEJKFKzDIG3E29CN2/4zR9XGJzKIkbg= +github.com/alecthomas/kong v0.6.1 h1:1kNhcFepkR+HmasQpbiKDLylIL8yh5B5y1zPp5bJimA= +github.com/alecthomas/kong v0.6.1/go.mod h1:JfHWDzLmbh/puW6I3V7uWenoh56YNVONW+w8eKeUr9I= +github.com/alecthomas/participle/v2 v2.0.0-beta.5 h1:y6dsSYVb1G5eK6mgmy+BgI3Mw35a3WghArZ/Hbebrjo= +github.com/alecthomas/participle/v2 v2.0.0-beta.5/go.mod h1:RC764t6n4L8D8ITAJv0qdokritYSNR3wV5cVwmIEaMM= +github.com/alecthomas/repr v0.0.0-20210801044451-80ca428c5142/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= +github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/parser/ast.go b/parser/ast.go new file mode 100644 index 0000000..3f0e557 --- /dev/null +++ b/parser/ast.go @@ -0,0 +1,62 @@ +package parser + +type Program struct { + Instructions []*Instruction `yaml:"instructions" parser:"@@*"` +} + +type Instruction struct { + SimpleOp *SimpleOp `yaml:"simpleOp,omitempty" parser:"( @('NOP'|'SWP'|'SAV'|'NEG')"` + Add *Add `yaml:"add,omitempty" parser:"| 'ADD' @@"` + Mov *Mov `yaml:"mov,omitempty" parser:"| 'MOV' @@"` + Jmp *Jmp `yaml:"jmp,omitempty" parser:"| 'JMP' @@"` + Jez *Jez `yaml:"jez,omitempty" parser:"| 'JEZ' @@"` + Jnz *Jnz `yaml:"jnz,omitempty" parser:"| 'JNZ' @@"` + Jgz *Jgz `yaml:"jgz,omitempty" parser:"| 'JGZ' @@"` + Jlz *Jlz `yaml:"jlz,omitempty" parser:"| 'JLZ' @@"` + Jro *Jro `yaml:"jro,omitempty" parser:"| 'JRO' @@"` + Sub *Sub `yaml:"sub,omitempty" parser:"| 'SUB' @@"` + Label string `yaml:"label,omitempty" parser:"| @(Ident) Colon)"` +} + +type Mov struct { + Source *Param `yaml:"source" parser:"@@ ParamSep"` + Destination *Param `yaml:"destination" parser:"@@"` +} + +type Add struct { + Source *Param `yaml:"source" parser:"@@"` +} + +type Sub struct { + Source *Param `yaml:"source" parser:"@@"` +} + +type Jmp struct { + Label string `yaml:"label" parser:"@Ident"` +} + +type Jez struct { + Label string `yaml:"label" parser:"@Ident"` +} + +type Jnz struct { + Label string `yaml:"label" parser:"@Ident"` +} + +type Jgz struct { + Label string `yaml:"label" parser:"@Ident"` +} + +type Jlz struct { + Label string `yaml:"label" parser:"@Ident"` +} + +type Jro struct { + Source *Param `yaml:"source" parser:"@@"` +} + +type Param struct { + Register *Register `yaml:"register,omitempty" parser:"( @('ACC'|'BAK'|'NIL'|'LEFT'|'UP'|'RIGHT'|'DOWN'|'ANY'|'LAST') |"` + Literal *int `yaml:"literal,omitempty" parser:"@Literal |"` + Label *string `yaml:"label,omitempty" parser:"@Ident )"` +} diff --git a/parser/ast_types.go b/parser/ast_types.go new file mode 100644 index 0000000..e7ebbc2 --- /dev/null +++ b/parser/ast_types.go @@ -0,0 +1,24 @@ +package parser + +type SimpleOp string + +const ( + OpNop SimpleOp = "NOP" + OpSwp SimpleOp = "SWP" + OpSav SimpleOp = "SAV" + OpNeg SimpleOp = "NEG" +) + +type Register string + +const ( + RegisterAcc Register = "ACC" + RegisterBak Register = "BAK" + RegisterNil Register = "NIL" + RegisterLeft Register = "LEFT" + RegisterUp Register = "UP" + RegisterRight Register = "RIGHT" + RegisterDown Register = "DOWN" + RegisterAny Register = "ANY" + RegisterLast Register = "LAST" +) diff --git a/parser/parse.go b/parser/parse.go new file mode 100644 index 0000000..6df8b01 --- /dev/null +++ b/parser/parse.go @@ -0,0 +1,43 @@ +package parser + +import ( + "fmt" + "io" + + "github.com/alecthomas/participle/v2" + "github.com/alecthomas/participle/v2/lexer" +) + +const lookahead = 2 + +var l = lexer.MustSimple([]lexer.SimpleRule{ + {Name: "Ident", Pattern: `[A-Z]+`}, + {Name: "Colon", Pattern: `:`}, + {Name: "Literal", Pattern: `([-]?[0-9]+)`}, + {Name: "ParamSep", Pattern: `,([ ]+)?`}, + {Name: "Whitespace", Pattern: `[ \t]+`}, + {Name: "EOL", Pattern: `[\n\r]+`}, +}) + +func Parse(r io.Reader, name string) (*Program, error) { + p, err := participle.Build[Program]( + participle.Lexer(l), + participle.UseLookahead(lookahead), + participle.Elide("EOL", "Whitespace"), + ) + if err != nil { + return nil, fmt.Errorf("failed to create a parser: %w", err) + } + + b, err := io.ReadAll(r) + if err != nil { + return nil, fmt.Errorf("failed to read all bytes from the given reader: %w", err) + } + + ast, err := p.ParseBytes(name, b) + if err != nil { + return nil, fmt.Errorf("failed to parse from bytes: %w", err) + } + + return ast, nil +} diff --git a/parser/parse_test.go b/parser/parse_test.go new file mode 100644 index 0000000..2e1aae7 --- /dev/null +++ b/parser/parse_test.go @@ -0,0 +1,52 @@ +package parser_test + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/stretchr/testify/require" + "gopkg.in/yaml.v3" + + "github.com/oguzhand95/tis100/parser" + "github.com/oguzhand95/tis100/testutil" +) + +type testCase struct { + Input string `json:"input" yaml:"input"` + Expected string `json:"expected" yaml:"expected"` +} + +func TestParse(t *testing.T) { + paths, err := testutil.Walk(testutil.PathTo("syntax"), "*.yaml") + require.NoError(t, err) + + for _, path := range paths { + tc := readTestCaseFromFile(t, path) + t.Run(filepath.Base(path), func(t *testing.T) { + p, err := parser.Parse(strings.NewReader(tc.Input), "") + require.NoError(t, err) + + y, err := yaml.Marshal(p) + require.NoError(t, err) + + t.Log(string(y)) + + require.YAMLEq(t, tc.Expected, string(y)) + }) + } +} + +func readTestCaseFromFile(t *testing.T, path string) *testCase { + t.Helper() + + b, err := os.ReadFile(path) + require.NoError(t, err) + + var testCase *testCase + err = yaml.Unmarshal(b, &testCase) + require.NoError(t, err) + + return testCase +} diff --git a/parser/testdata/syntax/add.yaml b/parser/testdata/syntax/add.yaml new file mode 100644 index 0000000..f99c01f --- /dev/null +++ b/parser/testdata/syntax/add.yaml @@ -0,0 +1,19 @@ +input: | + ADD -10 + ADD 10 + ADD ACC + ADD UP +expected: | + instructions: + - add: + source: + literal: -10 + - add: + source: + literal: 10 + - add: + source: + register: ACC + - add: + source: + register: UP diff --git a/parser/testdata/syntax/jez.yaml b/parser/testdata/syntax/jez.yaml new file mode 100644 index 0000000..fc81fed --- /dev/null +++ b/parser/testdata/syntax/jez.yaml @@ -0,0 +1,14 @@ +input: | + START: + JEZ START + JEZ START + JEZ START +expected: | + instructions: + - label: START + - jez: + label: START + - jez: + label: START + - jez: + label: START \ No newline at end of file diff --git a/parser/testdata/syntax/jgz.yaml b/parser/testdata/syntax/jgz.yaml new file mode 100644 index 0000000..433eb0a --- /dev/null +++ b/parser/testdata/syntax/jgz.yaml @@ -0,0 +1,14 @@ +input: | + START: + JGZ START + JGZ START + JGZ START +expected: | + instructions: + - label: START + - jgz: + label: START + - jgz: + label: START + - jgz: + label: START diff --git a/parser/testdata/syntax/jlz.yaml b/parser/testdata/syntax/jlz.yaml new file mode 100644 index 0000000..27f1a1c --- /dev/null +++ b/parser/testdata/syntax/jlz.yaml @@ -0,0 +1,14 @@ +input: | + START: + JLZ START + JLZ START + JLZ START +expected: | + instructions: + - label: START + - jlz: + label: START + - jlz: + label: START + - jlz: + label: START diff --git a/parser/testdata/syntax/jmp.yaml b/parser/testdata/syntax/jmp.yaml new file mode 100644 index 0000000..f7983e7 --- /dev/null +++ b/parser/testdata/syntax/jmp.yaml @@ -0,0 +1,14 @@ +input: | + START: + JMP START + JMP START + JMP START +expected: | + instructions: + - label: START + - jmp: + label: START + - jmp: + label: START + - jmp: + label: START diff --git a/parser/testdata/syntax/jnz.yaml b/parser/testdata/syntax/jnz.yaml new file mode 100644 index 0000000..cc98325 --- /dev/null +++ b/parser/testdata/syntax/jnz.yaml @@ -0,0 +1,14 @@ +input: | + START: + JNZ START + JNZ START + JNZ START +expected: | + instructions: + - label: START + - jnz: + label: START + - jnz: + label: START + - jnz: + label: START diff --git a/parser/testdata/syntax/jro.yaml b/parser/testdata/syntax/jro.yaml new file mode 100644 index 0000000..b709241 --- /dev/null +++ b/parser/testdata/syntax/jro.yaml @@ -0,0 +1,23 @@ +input: | + JRO -1 + JRO 0 + JRO 1 + JRO ACC + JRO UP +expected: | + instructions: + - jro: + source: + literal: -1 + - jro: + source: + literal: 0 + - jro: + source: + literal: 1 + - jro: + source: + register: ACC + - jro: + source: + register: UP \ No newline at end of file diff --git a/parser/testdata/syntax/mov.yaml b/parser/testdata/syntax/mov.yaml new file mode 100644 index 0000000..123a956 --- /dev/null +++ b/parser/testdata/syntax/mov.yaml @@ -0,0 +1,21 @@ +input: | + MOV 10, ACC + MOV ACC, DOWN + MOV UP, DOWN +expected: | + instructions: + - mov: + source: + literal: 10 + destination: + register: ACC + - mov: + source: + register: ACC + destination: + register: DOWN + - mov: + source: + register: UP + destination: + register: DOWN diff --git a/parser/testdata/syntax/neg.yaml b/parser/testdata/syntax/neg.yaml new file mode 100644 index 0000000..f2790d6 --- /dev/null +++ b/parser/testdata/syntax/neg.yaml @@ -0,0 +1,9 @@ +input: | + NEG + NEG + NEG +expected: | + instructions: + - simpleOp: NEG + - simpleOp: NEG + - simpleOp: NEG diff --git a/parser/testdata/syntax/nop.yaml b/parser/testdata/syntax/nop.yaml new file mode 100644 index 0000000..d7cb3e6 --- /dev/null +++ b/parser/testdata/syntax/nop.yaml @@ -0,0 +1,9 @@ +input: | + NOP + NOP + NOP +expected: | + instructions: + - simpleOp: NOP + - simpleOp: NOP + - simpleOp: NOP diff --git a/parser/testdata/syntax/sav.yaml b/parser/testdata/syntax/sav.yaml new file mode 100644 index 0000000..3e90809 --- /dev/null +++ b/parser/testdata/syntax/sav.yaml @@ -0,0 +1,9 @@ +input: | + SAV + SAV + SAV +expected: | + instructions: + - simpleOp: SAV + - simpleOp: SAV + - simpleOp: SAV diff --git a/parser/testdata/syntax/sub.yaml b/parser/testdata/syntax/sub.yaml new file mode 100644 index 0000000..0454cbe --- /dev/null +++ b/parser/testdata/syntax/sub.yaml @@ -0,0 +1,19 @@ +input: | + SUB -10 + SUB 10 + SUB ACC + SUB UP +expected: | + instructions: + - sub: + source: + literal: -10 + - sub: + source: + literal: 10 + - sub: + source: + register: ACC + - sub: + source: + register: UP diff --git a/parser/testdata/syntax/swp.yaml b/parser/testdata/syntax/swp.yaml new file mode 100644 index 0000000..0a5df39 --- /dev/null +++ b/parser/testdata/syntax/swp.yaml @@ -0,0 +1,9 @@ +input: | + SWP + SWP + SWP +expected: | + instructions: + - simpleOp: SWP + - simpleOp: SWP + - simpleOp: SWP diff --git a/testutil/find.go b/testutil/find.go new file mode 100644 index 0000000..ab83cbe --- /dev/null +++ b/testutil/find.go @@ -0,0 +1,35 @@ +//go:build test +// +build test + +package testutil + +import ( + "fmt" + "os" + "path/filepath" +) + +// Walk walks the given path and returns the list of file paths seeking the files matching the given pattern. +// https://stackoverflow.com/a/55300382 +func Walk(path, pattern string) ([]string, error) { + var matches []string + err := filepath.Walk(path, func(p string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + if matched, err := filepath.Match(pattern, filepath.Base(p)); err != nil { + return err + } else if matched { + matches = append(matches, p) + } + return nil + }) + if err != nil { + return nil, fmt.Errorf("failed to find files with the given pattern: %w", err) + } + + return matches, nil +} diff --git a/testutil/path.go b/testutil/path.go new file mode 100644 index 0000000..9ef8219 --- /dev/null +++ b/testutil/path.go @@ -0,0 +1,25 @@ +//go:build test +// +build test + +package testutil + +import ( + "errors" + "path/filepath" + "runtime" +) + +const testDataPath = "testdata" + +func GlobalPathTo(paths ...string) string { + _, currFile, _, ok := runtime.Caller(0) + if !ok { + panic(errors.New("failed to detect testdata directory")) + } + + return filepath.Join(filepath.Dir(currFile), testDataPath, filepath.Join(paths...)) +} + +func PathTo(paths ...string) string { + return filepath.Join(testDataPath, filepath.Join(paths...)) +}