diff --git a/README.md b/README.md index 2c701db..04317b5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,26 @@ # argh command line parser -> NOTE: much of this is lifted from -> https://blog.gopheracademy.com/advent-2014/parsers-lexers/ +## background + +The Go standard library [flag](https://pkg.go.dev/flag) way of doing things has long been +a source of frustration while implementing and maintaining the +[urfave/cli](https://github.com/urfave/cli) library. [Many alternate parsers +exist](https://github.com/avelino/awesome-go#standard-cli), including: + +- [pflag](https://github.com/spf13/pflag) +- [argparse](https://github.com/akamensky/argparse) + +In addition to these other implementations, I also got some help via [this +oldie](https://blog.gopheracademy.com/advent-2014/parsers-lexers/) and the Go standard +library [parser](https://pkg.go.dev/go/parser). + +## goals + +- get a better understanding of the whole problem space +- support both POSIX-y and Windows-y styles +- build a printable/JSON-able parse tree +- support rich error reporting + + diff --git a/cmd/argh-example/main.go b/cmd/argh-example/main.go index 0b243e2..8c30a90 100644 --- a/cmd/argh-example/main.go +++ b/cmd/argh-example/main.go @@ -18,7 +18,7 @@ func main() { pCfg := argh.NewParserConfig() pCfg.Prog = &argh.CommandConfig{ NValue: argh.OneOrMoreValue, - ValueNames: []string{"topping"}, + ValueNames: []string{"val"}, Flags: &argh.Flags{ Automatic: true, }, diff --git a/scanner_test.go b/scanner_test.go new file mode 100644 index 0000000..72dccb9 --- /dev/null +++ b/scanner_test.go @@ -0,0 +1,83 @@ +package argh + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/require" +) + +func BenchmarkScannerPOSIXyScannerScan(b *testing.B) { + for i := 0; i < b.N; i++ { + scanner := NewScanner(strings.NewReader(strings.Join([]string{ + "walrus", + "-what", + "--ball=awesome", + "--elapsed", + "carrot cake", + }, string(nul))), nil) + for { + tok, _, _ := scanner.Scan() + if tok == EOL { + break + } + } + } +} + +func TestScannerPOSIXyScanner(t *testing.T) { + for _, tc := range []struct { + name string + argv []string + expectedTokens []Token + expectedLiterals []string + expectedPositions []Pos + }{ + { + name: "simple", + argv: []string{"walrus", "-cake", "--corn-dog", "awkward"}, + expectedTokens: []Token{ + IDENT, + ARG_DELIMITER, + COMPOUND_SHORT_FLAG, + ARG_DELIMITER, + LONG_FLAG, + ARG_DELIMITER, + IDENT, + EOL, + }, + expectedLiterals: []string{ + "walrus", string(nul), "-cake", string(nul), "--corn-dog", string(nul), "awkward", "", + }, + expectedPositions: []Pos{ + 6, 7, 12, 13, 23, 24, 31, 32, + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + r := require.New(t) + + scanner := NewScanner(strings.NewReader(strings.Join(tc.argv, string(nul))), nil) + + actualTokens := []Token{} + actualLiterals := []string{} + actualPositions := []Pos{} + + for { + tok, lit, pos := scanner.Scan() + + actualTokens = append(actualTokens, tok) + actualLiterals = append(actualLiterals, lit) + actualPositions = append(actualPositions, pos) + + if tok == EOL { + break + } + } + + r.Equal(tc.expectedTokens, actualTokens) + r.Equal(tc.expectedLiterals, actualLiterals) + r.Equal(tc.expectedPositions, actualPositions) + }) + } +}