From 30ed115ed0c14e075de6307d18d13c9804b88002 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 6 Jun 2022 18:50:25 -0400 Subject: [PATCH] Moving argh to separate repo --- argh/README.md | 4 - argh/argh.go | 34 -- argh/cmd/argh-example/main.go | 46 -- argh/go.mod | 12 - argh/go.sum | 12 - argh/node.go | 46 -- argh/nvalue_string.go | 26 - argh/parser.go | 346 ----------- argh/parser_config.go | 146 ----- argh/parser_error.go | 88 --- argh/parser_test.go | 1028 --------------------------------- argh/querier.go | 94 --- argh/querier_test.go | 60 -- argh/scanner.go | 159 ----- argh/scanner_config.go | 39 -- argh/token.go | 53 -- argh/token_string.go | 35 -- 17 files changed, 2228 deletions(-) delete mode 100644 argh/README.md delete mode 100644 argh/argh.go delete mode 100644 argh/cmd/argh-example/main.go delete mode 100644 argh/go.mod delete mode 100644 argh/go.sum delete mode 100644 argh/node.go delete mode 100644 argh/nvalue_string.go delete mode 100644 argh/parser.go delete mode 100644 argh/parser_config.go delete mode 100644 argh/parser_error.go delete mode 100644 argh/parser_test.go delete mode 100644 argh/querier.go delete mode 100644 argh/querier_test.go delete mode 100644 argh/scanner.go delete mode 100644 argh/scanner_config.go delete mode 100644 argh/token.go delete mode 100644 argh/token_string.go diff --git a/argh/README.md b/argh/README.md deleted file mode 100644 index 2c701db..0000000 --- a/argh/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# argh command line parser - -> NOTE: much of this is lifted from -> https://blog.gopheracademy.com/advent-2014/parsers-lexers/ diff --git a/argh/argh.go b/argh/argh.go deleted file mode 100644 index 47f3ca1..0000000 --- a/argh/argh.go +++ /dev/null @@ -1,34 +0,0 @@ -package argh - -import ( - "fmt" - "log" - "os" - "path/filepath" - "runtime" -) - -var ( - tracingEnabled = os.Getenv("ARGH_TRACING") == "enabled" - traceLogger *log.Logger -) - -func init() { - if !tracingEnabled { - return - } - - traceLogger = log.New(os.Stderr, "ARGH TRACING: ", 0) -} - -func tracef(format string, v ...any) { - if !tracingEnabled { - return - } - - if _, file, line, ok := runtime.Caller(1); ok { - format = fmt.Sprintf("%v:%v ", filepath.Base(file), line) + format - } - - traceLogger.Printf(format, v...) -} diff --git a/argh/cmd/argh-example/main.go b/argh/cmd/argh-example/main.go deleted file mode 100644 index 325c7ec..0000000 --- a/argh/cmd/argh-example/main.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "log" - "os" - - "git.meatballhat.com/x/box-o-sand/argh" - "github.com/davecgh/go-spew/spew" -) - -func main() { - asJSON := os.Getenv("ARGH_OUTPUT_JSON") == "enabled" - - log.SetFlags(0) - - pt, err := argh.ParseArgs(os.Args, argh.NewParserConfig( - &argh.CommandConfig{ - NValue: argh.OneOrMoreValue, - ValueNames: []string{"topping"}, - Flags: &argh.Flags{ - Automatic: true, - }, - }, - nil, - )) - if err != nil { - log.Fatal(err) - } - - ast := argh.NewQuerier(pt.Nodes).AST() - - if asJSON { - b, err := json.MarshalIndent(ast, "", " ") - if err != nil { - log.Fatal(err) - } - - fmt.Println(string(b)) - - return - } - - spew.Dump(ast) -} diff --git a/argh/go.mod b/argh/go.mod deleted file mode 100644 index 7091c9a..0000000 --- a/argh/go.mod +++ /dev/null @@ -1,12 +0,0 @@ -module git.meatballhat.com/x/box-o-sand/argh - -go 1.18 - -require github.com/pkg/errors v0.9.1 - -require ( - github.com/davecgh/go-spew v1.1.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.7.1 // indirect - gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect -) diff --git a/argh/go.sum b/argh/go.sum deleted file mode 100644 index 842edf5..0000000 --- a/argh/go.sum +++ /dev/null @@ -1,12 +0,0 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -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/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/argh/node.go b/argh/node.go deleted file mode 100644 index b4ce2e9..0000000 --- a/argh/node.go +++ /dev/null @@ -1,46 +0,0 @@ -package argh - -type Node interface{} - -type TypedNode struct { - Type string - Node Node -} - -type PassthroughArgs struct { - Nodes []Node -} - -type CompoundShortFlag struct { - Nodes []Node -} - -type Ident struct { - Literal string -} - -type BadArg struct { - Literal string - From Pos - To Pos -} - -type Command struct { - Name string - Values map[string]string - Nodes []Node -} - -type Flag struct { - Name string - Values map[string]string - Nodes []Node -} - -type StdinFlag struct{} - -type StopFlag struct{} - -type ArgDelimiter struct{} - -type Assign struct{} diff --git a/argh/nvalue_string.go b/argh/nvalue_string.go deleted file mode 100644 index d9d1f06..0000000 --- a/argh/nvalue_string.go +++ /dev/null @@ -1,26 +0,0 @@ -// Code generated by "stringer -type NValue"; DO NOT EDIT. - -package argh - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[OneOrMoreValue - -2] - _ = x[ZeroOrMoreValue - -1] - _ = x[ZeroValue-0] -} - -const _NValue_name = "OneOrMoreValueZeroOrMoreValueZeroValue" - -var _NValue_index = [...]uint8{0, 14, 29, 38} - -func (i NValue) String() string { - i -= -2 - if i < 0 || i >= NValue(len(_NValue_index)-1) { - return "NValue(" + strconv.FormatInt(int64(i+-2), 10) + ")" - } - return _NValue_name[_NValue_index[i]:_NValue_index[i+1]] -} diff --git a/argh/parser.go b/argh/parser.go deleted file mode 100644 index 72c15d9..0000000 --- a/argh/parser.go +++ /dev/null @@ -1,346 +0,0 @@ -package argh - -import ( - "fmt" - "io" - "strings" -) - -type parser struct { - s *Scanner - - cfg *ParserConfig - - errors ParserErrorList - - tok Token - lit string - pos Pos - - buffered bool -} - -type ParseTree struct { - Nodes []Node `json:"nodes"` -} - -func ParseArgs(args []string, pCfg *ParserConfig) (*ParseTree, error) { - p := &parser{} - p.init( - strings.NewReader(strings.Join(args, string(nul))), - pCfg, - ) - - tracef("ParseArgs(...) parser=%+#v", p) - - return p.parseArgs() -} - -func (p *parser) addError(msg string) { - p.errors.Add(Position{Column: int(p.pos)}, msg) -} - -func (p *parser) init(r io.Reader, pCfg *ParserConfig) { - p.errors = ParserErrorList{} - - if pCfg == nil { - pCfg = POSIXyParserConfig - } - - p.cfg = pCfg - - p.s = NewScanner(r, pCfg.ScannerConfig) - - p.next() -} - -func (p *parser) parseArgs() (*ParseTree, error) { - if p.errors.Len() != 0 { - tracef("parseArgs() bailing due to initial error") - return nil, p.errors.Err() - } - - tracef("parseArgs() parsing %q as program command; cfg=%+#v", p.lit, p.cfg.Prog) - prog := p.parseCommand(&p.cfg.Prog) - - tracef("parseArgs() top level node is %T", prog) - - nodes := []Node{prog} - if v := p.parsePassthrough(); v != nil { - tracef("parseArgs() appending passthrough argument %v", v) - nodes = append(nodes, v) - } - - tracef("parseArgs() returning ParseTree") - - return &ParseTree{Nodes: nodes}, p.errors.Err() -} - -func (p *parser) next() { - tracef("next() before scan: %v %q %v", p.tok, p.lit, p.pos) - - p.tok, p.lit, p.pos = p.s.Scan() - - tracef("next() after scan: %v %q %v", p.tok, p.lit, p.pos) -} - -func (p *parser) parseCommand(cCfg *CommandConfig) Node { - tracef("parseCommand(%+#v)", cCfg) - - node := &Command{ - Name: p.lit, - } - values := map[string]string{} - nodes := []Node{} - - identIndex := 0 - - for i := 0; p.tok != EOL; i++ { - if !p.buffered { - tracef("parseCommand(...) buffered=false; scanning next") - p.next() - } - - p.buffered = false - - tracef("parseCommand(...) for=%d values=%+#v", i, values) - tracef("parseCommand(...) for=%d nodes=%+#v", i, nodes) - tracef("parseCommand(...) for=%d tok=%s lit=%q pos=%v", i, p.tok, p.lit, p.pos) - - tracef("parseCommand(...) cCfg=%+#v", cCfg) - - if subCfg, ok := cCfg.GetCommandConfig(p.lit); ok { - subCommand := p.lit - - nodes = append(nodes, p.parseCommand(&subCfg)) - - tracef("parseCommand(...) breaking after sub-command=%v", subCommand) - break - } - - switch p.tok { - case ARG_DELIMITER: - tracef("parseCommand(...) handling %s", p.tok) - - nodes = append(nodes, &ArgDelimiter{}) - - continue - case IDENT, STDIN_FLAG: - tracef("parseCommand(...) handling %s", p.tok) - - if cCfg.NValue.Contains(identIndex) { - name := fmt.Sprintf("%d", identIndex) - - tracef("parseCommand(...) checking for name of identIndex=%d", identIndex) - - if len(cCfg.ValueNames) > identIndex { - name = cCfg.ValueNames[identIndex] - tracef("parseCommand(...) setting name=%s from config value names", name) - } else if len(cCfg.ValueNames) == 1 && (cCfg.NValue == OneOrMoreValue || cCfg.NValue == ZeroOrMoreValue) { - name = fmt.Sprintf("%s.%d", cCfg.ValueNames[0], identIndex) - tracef("parseCommand(...) setting name=%s from repeating value name", name) - } - - values[name] = p.lit - } - - if p.tok == STDIN_FLAG { - nodes = append(nodes, &StdinFlag{}) - } else { - nodes = append(nodes, &Ident{Literal: p.lit}) - } - - identIndex++ - case LONG_FLAG, SHORT_FLAG, COMPOUND_SHORT_FLAG: - tok := p.tok - - flagNode := p.parseFlag(cCfg.Flags) - - tracef("parseCommand(...) appending %s node=%+#v", tok, flagNode) - - nodes = append(nodes, flagNode) - case ASSIGN: - tracef("parseCommand(...) error on bare %s", p.tok) - - p.addError("invalid bare assignment") - - break - default: - tracef("parseCommand(...) breaking on %s", p.tok) - break - } - } - - if len(nodes) > 0 { - node.Nodes = nodes - } - - if len(values) > 0 { - node.Values = values - } - - tracef("parseCommand(...) returning node=%+#v", node) - return node -} - -func (p *parser) parseIdent() Node { - node := &Ident{Literal: p.lit} - return node -} - -func (p *parser) parseFlag(flags *Flags) Node { - switch p.tok { - case SHORT_FLAG: - tracef("parseFlag(...) parsing short flag with config=%+#v", flags) - return p.parseShortFlag(flags) - case LONG_FLAG: - tracef("parseFlag(...) parsing long flag with config=%+#v", flags) - return p.parseLongFlag(flags) - case COMPOUND_SHORT_FLAG: - tracef("parseFlag(...) parsing compound short flag with config=%+#v", flags) - return p.parseCompoundShortFlag(flags) - } - - panic(fmt.Sprintf("token %v cannot be parsed as flag", p.tok)) -} - -func (p *parser) parseShortFlag(flags *Flags) Node { - node := &Flag{Name: string(p.lit[1])} - - flCfg, ok := flags.Get(node.Name) - if !ok { - p.addError(fmt.Sprintf("unknown flag %q", node.Name)) - - return node - } - - return p.parseConfiguredFlag(node, flCfg) -} - -func (p *parser) parseLongFlag(flags *Flags) Node { - node := &Flag{Name: string(p.lit[2:])} - - flCfg, ok := flags.Get(node.Name) - if !ok { - p.addError(fmt.Sprintf("unknown flag %q", node.Name)) - - return node - } - - return p.parseConfiguredFlag(node, flCfg) -} - -func (p *parser) parseCompoundShortFlag(flags *Flags) Node { - flagNodes := []Node{} - - withoutFlagPrefix := p.lit[1:] - - for i, r := range withoutFlagPrefix { - node := &Flag{Name: string(r)} - - if i == len(withoutFlagPrefix)-1 { - flCfg, ok := flags.Get(node.Name) - if !ok { - p.addError(fmt.Sprintf("unknown flag %q", node.Name)) - - continue - } - - flagNodes = append(flagNodes, p.parseConfiguredFlag(node, flCfg)) - - continue - } - - flagNodes = append(flagNodes, node) - } - - return &CompoundShortFlag{Nodes: flagNodes} -} - -func (p *parser) parseConfiguredFlag(node *Flag, flCfg FlagConfig) Node { - values := map[string]string{} - nodes := []Node{} - - identIndex := 0 - - for i := 0; p.tok != EOL; i++ { - if !flCfg.NValue.Contains(identIndex) { - tracef("parseConfiguredFlag(...) identIndex=%d exceeds expected=%v; breaking", identIndex, flCfg.NValue) - break - } - - p.next() - - switch p.tok { - case ARG_DELIMITER: - nodes = append(nodes, &ArgDelimiter{}) - - continue - case ASSIGN: - nodes = append(nodes, &Assign{}) - - continue - case IDENT, STDIN_FLAG: - name := fmt.Sprintf("%d", identIndex) - - tracef("parseConfiguredFlag(...) checking for name of identIndex=%d", identIndex) - - if len(flCfg.ValueNames) > identIndex { - name = flCfg.ValueNames[identIndex] - tracef("parseConfiguredFlag(...) setting name=%s from config value names", name) - } else if len(flCfg.ValueNames) == 1 && (flCfg.NValue == OneOrMoreValue || flCfg.NValue == ZeroOrMoreValue) { - name = fmt.Sprintf("%s.%d", flCfg.ValueNames[0], identIndex) - tracef("parseConfiguredFlag(...) setting name=%s from repeating value name", name) - } else { - tracef("parseConfiguredFlag(...) setting name=%s", name) - } - - values[name] = p.lit - - if p.tok == STDIN_FLAG { - nodes = append(nodes, &StdinFlag{}) - } else { - nodes = append(nodes, &Ident{Literal: p.lit}) - } - - identIndex++ - default: - tracef("parseConfiguredFlag(...) breaking on %s %q %v; setting buffered=true", p.tok, p.lit, p.pos) - p.buffered = true - - if len(nodes) > 0 { - node.Nodes = nodes - } - - if len(values) > 0 { - node.Values = values - } - - return node - } - } - - if len(nodes) > 0 { - node.Nodes = nodes - } - - if len(values) > 0 { - node.Values = values - } - - return node -} - -func (p *parser) parsePassthrough() Node { - nodes := []Node{} - - for ; p.tok != EOL; p.next() { - nodes = append(nodes, &Ident{Literal: p.lit}) - } - - if len(nodes) == 0 { - return nil - } - - return &PassthroughArgs{Nodes: nodes} -} diff --git a/argh/parser_config.go b/argh/parser_config.go deleted file mode 100644 index 62ea57d..0000000 --- a/argh/parser_config.go +++ /dev/null @@ -1,146 +0,0 @@ -package argh - -const ( - OneOrMoreValue NValue = -2 - ZeroOrMoreValue NValue = -1 - ZeroValue NValue = 0 -) - -var ( - POSIXyParserConfig = NewParserConfig( - nil, - POSIXyScannerConfig, - ) -) - -type NValue int - -func (nv NValue) Contains(i int) bool { - tracef("NValue.Contains(%v)", i) - - if i < int(ZeroValue) { - return false - } - - if nv == OneOrMoreValue || nv == ZeroOrMoreValue { - return true - } - - return int(nv) > i -} - -type ParserConfig struct { - Prog CommandConfig - - ScannerConfig *ScannerConfig -} - -func NewParserConfig(prog *CommandConfig, sCfg *ScannerConfig) *ParserConfig { - if sCfg == nil { - sCfg = POSIXyScannerConfig - } - - if prog == nil { - prog = &CommandConfig{} - } - - prog.init() - - pCfg := &ParserConfig{ - Prog: *prog, - ScannerConfig: sCfg, - } - - return pCfg -} - -type CommandConfig struct { - NValue NValue - ValueNames []string - Flags *Flags - Commands *Commands -} - -func (cCfg *CommandConfig) init() { - if cCfg.ValueNames == nil { - cCfg.ValueNames = []string{} - } - - if cCfg.Flags == nil { - cCfg.Flags = &Flags{} - } - - if cCfg.Commands == nil { - cCfg.Commands = &Commands{} - } -} - -func (cCfg *CommandConfig) GetCommandConfig(name string) (CommandConfig, bool) { - tracef("CommandConfig.GetCommandConfig(%q)", name) - - if cCfg.Commands == nil { - cCfg.Commands = &Commands{Map: map[string]CommandConfig{}} - } - - return cCfg.Commands.Get(name) -} - -func (cCfg *CommandConfig) GetFlagConfig(name string) (FlagConfig, bool) { - tracef("CommandConfig.GetFlagConfig(%q)", name) - - if cCfg.Flags == nil { - cCfg.Flags = &Flags{Map: map[string]FlagConfig{}} - } - - return cCfg.Flags.Get(name) -} - -type FlagConfig struct { - NValue NValue - Persist bool - ValueNames []string -} - -type Flags struct { - Parent *Flags - Map map[string]FlagConfig - - Automatic bool -} - -func (fl *Flags) Get(name string) (FlagConfig, bool) { - tracef("Flags.Get(%q)", name) - - if fl.Map == nil { - fl.Map = map[string]FlagConfig{} - } - - flCfg, ok := fl.Map[name] - if !ok { - if fl.Automatic { - return FlagConfig{}, true - } - - if fl.Parent != nil { - flCfg, ok = fl.Parent.Get(name) - return flCfg, ok && flCfg.Persist - } - } - - return flCfg, ok -} - -type Commands struct { - Map map[string]CommandConfig -} - -func (cmd *Commands) Get(name string) (CommandConfig, bool) { - tracef("Commands.Get(%q)", name) - - if cmd.Map == nil { - cmd.Map = map[string]CommandConfig{} - } - - cmdCfg, ok := cmd.Map[name] - return cmdCfg, ok -} diff --git a/argh/parser_error.go b/argh/parser_error.go deleted file mode 100644 index 62ae3b7..0000000 --- a/argh/parser_error.go +++ /dev/null @@ -1,88 +0,0 @@ -package argh - -import ( - "fmt" - "io" - "sort" -) - -// ParserError is largely borrowed from go/scanner.Error -type ParserError struct { - Pos Position - Msg string -} - -func (e ParserError) Error() string { - if e.Pos.IsValid() { - return e.Pos.String() + ":" + e.Msg - } - - return e.Msg -} - -// ParserErrorList is largely borrowed from go/scanner.ErrorList -type ParserErrorList []*ParserError - -func (el *ParserErrorList) Add(pos Position, msg string) { - *el = append(*el, &ParserError{Pos: pos, Msg: msg}) -} - -func (el *ParserErrorList) Reset() { *el = (*el)[0:0] } - -func (el ParserErrorList) Len() int { return len(el) } - -func (el ParserErrorList) Swap(i, j int) { el[i], el[j] = el[j], el[i] } - -func (el ParserErrorList) Less(i, j int) bool { - e := &el[i].Pos - f := &el[j].Pos - - if e.Column != f.Column { - return e.Column < f.Column - } - - return el[i].Msg < el[j].Msg -} - -func (el ParserErrorList) Sort() { - sort.Sort(el) -} - -func (el ParserErrorList) Error() string { - switch len(el) { - case 0: - return "no errors" - case 1: - return el[0].Error() - } - return fmt.Sprintf("%s (and %d more errors)", el[0], len(el)-1) -} - -func (el ParserErrorList) Err() error { - if len(el) == 0 { - return nil - } - return el -} - -func (el ParserErrorList) Is(other error) bool { - if _, ok := other.(ParserErrorList); ok { - return el.Error() == other.Error() - } - - if v, ok := other.(*ParserErrorList); ok { - return el.Error() == (*v).Error() - } - - return false -} - -func PrintParserError(w io.Writer, err error) { - if list, ok := err.(ParserErrorList); ok { - for _, e := range list { - fmt.Fprintf(w, "%s\n", e) - } - } else if err != nil { - fmt.Fprintf(w, "%s\n", err) - } -} diff --git a/argh/parser_test.go b/argh/parser_test.go deleted file mode 100644 index bea803f..0000000 --- a/argh/parser_test.go +++ /dev/null @@ -1,1028 +0,0 @@ -package argh_test - -import ( - "testing" - - "git.meatballhat.com/x/box-o-sand/argh" - "github.com/davecgh/go-spew/spew" - "github.com/stretchr/testify/assert" -) - -func TestParser(t *testing.T) { - for _, tc := range []struct { - name string - args []string - cfg *argh.ParserConfig - expErr error - expPT []argh.Node - expAST []argh.Node - skip bool - }{ - { - name: "basic", - args: []string{ - "pies", "-eat", "--wat", "hello", "mario", - }, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "e": {}, - "a": {}, - "t": {}, - "wat": {}, - }, - }, - Commands: &argh.Commands{ - Map: map[string]argh.CommandConfig{ - "hello": argh.CommandConfig{ - NValue: 1, - ValueNames: []string{"name"}, - }, - }, - }, - }, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pies", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.CompoundShortFlag{ - Nodes: []argh.Node{ - &argh.Flag{Name: "e"}, - &argh.Flag{Name: "a"}, - &argh.Flag{Name: "t"}, - }, - }, - &argh.ArgDelimiter{}, - &argh.Flag{Name: "wat"}, - &argh.ArgDelimiter{}, - &argh.Command{ - Name: "hello", - Values: map[string]string{ - "name": "mario", - }, - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "mario"}, - }, - }, - }, - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pies", - Nodes: []argh.Node{ - &argh.Flag{Name: "e"}, - &argh.Flag{Name: "a"}, - &argh.Flag{Name: "t"}, - &argh.Flag{Name: "wat"}, - &argh.Command{ - Name: "hello", - Values: map[string]string{ - "name": "mario", - }, - Nodes: []argh.Node{ - &argh.Ident{Literal: "mario"}, - }, - }, - }, - }, - }, - }, - { - name: "persistent flags", - args: []string{ - "pies", "--wat", "hello", "mario", "-eat", - }, - cfg: &argh.ParserConfig{ - Prog: func() argh.CommandConfig { - cmdCfg := argh.CommandConfig{ - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "e": {Persist: true}, - "a": {Persist: true}, - "t": {Persist: true}, - "wat": {}, - }, - }, - } - - cmdCfg.Commands = &argh.Commands{ - Map: map[string]argh.CommandConfig{ - "hello": argh.CommandConfig{ - NValue: 1, - ValueNames: []string{"name"}, - Flags: &argh.Flags{ - Parent: cmdCfg.Flags, - Map: map[string]argh.FlagConfig{}, - }, - }, - }, - } - - return cmdCfg - }(), - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pies", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Flag{Name: "wat"}, - &argh.ArgDelimiter{}, - &argh.Command{ - Name: "hello", - Values: map[string]string{ - "name": "mario", - }, - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "mario"}, - &argh.ArgDelimiter{}, - &argh.CompoundShortFlag{ - Nodes: []argh.Node{ - &argh.Flag{Name: "e"}, - &argh.Flag{Name: "a"}, - &argh.Flag{Name: "t"}, - }, - }, - }, - }, - }, - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pies", - Nodes: []argh.Node{ - &argh.Flag{Name: "wat"}, - &argh.Command{ - Name: "hello", - Values: map[string]string{ - "name": "mario", - }, - Nodes: []argh.Node{ - &argh.Ident{Literal: "mario"}, - &argh.Flag{Name: "e"}, - &argh.Flag{Name: "a"}, - &argh.Flag{Name: "t"}, - }, - }, - }, - }, - }, - }, - { - name: "bare", - args: []string{"pizzas"}, - expPT: []argh.Node{ - &argh.Command{ - Name: "pizzas", - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pizzas", - }, - }, - }, - { - name: "one positional arg", - args: []string{"pizzas", "excel"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{NValue: 1}, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Values: map[string]string{"0": "excel"}, - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "excel"}, - }, - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Values: map[string]string{"0": "excel"}, - Nodes: []argh.Node{ - &argh.Ident{Literal: "excel"}, - }, - }, - }, - }, - { - name: "many positional args", - args: []string{"pizzas", "excel", "wildly", "when", "feral"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - NValue: argh.OneOrMoreValue, - ValueNames: []string{"word"}, - }, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Values: map[string]string{ - "word": "excel", - "word.1": "wildly", - "word.2": "when", - "word.3": "feral", - }, - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "excel"}, - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "wildly"}, - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "when"}, - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "feral"}, - }, - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Values: map[string]string{ - "word": "excel", - "word.1": "wildly", - "word.2": "when", - "word.3": "feral", - }, - Nodes: []argh.Node{ - &argh.Ident{Literal: "excel"}, - &argh.Ident{Literal: "wildly"}, - &argh.Ident{Literal: "when"}, - &argh.Ident{Literal: "feral"}, - }, - }, - }, - }, - { - name: "long value-less flags", - args: []string{"pizzas", "--tasty", "--fresh", "--super-hot-right-now"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "tasty": {}, - "fresh": {}, - "super-hot-right-now": {}, - }, - }, - }, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Flag{Name: "tasty"}, - &argh.ArgDelimiter{}, - &argh.Flag{Name: "fresh"}, - &argh.ArgDelimiter{}, - &argh.Flag{Name: "super-hot-right-now"}, - }, - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.Flag{Name: "tasty"}, - &argh.Flag{Name: "fresh"}, - &argh.Flag{Name: "super-hot-right-now"}, - }, - }, - }, - }, - { - name: "long flags mixed", - args: []string{ - "pizzas", - "--tasty", - "--fresh", "soon", - "--super-hot-right-now", - "--box", "square", "shaped", "hot", - "--please", - }, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Commands: &argh.Commands{Map: map[string]argh.CommandConfig{}}, - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "tasty": {}, - "fresh": argh.FlagConfig{NValue: 1}, - "super-hot-right-now": {}, - "box": argh.FlagConfig{NValue: argh.OneOrMoreValue}, - "please": {}, - }, - }, - }, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Flag{Name: "tasty"}, - &argh.ArgDelimiter{}, - &argh.Flag{ - Name: "fresh", - Values: map[string]string{"0": "soon"}, - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "soon"}, - }, - }, - &argh.ArgDelimiter{}, - &argh.Flag{Name: "super-hot-right-now"}, - &argh.ArgDelimiter{}, - &argh.Flag{ - Name: "box", - Values: map[string]string{"0": "square", "1": "shaped", "2": "hot"}, - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "square"}, - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "shaped"}, - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "hot"}, - &argh.ArgDelimiter{}, - }, - }, - &argh.Flag{Name: "please"}, - }, - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.Flag{Name: "tasty"}, - &argh.Flag{ - Name: "fresh", - Values: map[string]string{"0": "soon"}, - Nodes: []argh.Node{ - &argh.Ident{Literal: "soon"}, - }, - }, - &argh.Flag{Name: "super-hot-right-now"}, - &argh.Flag{ - Name: "box", - Values: map[string]string{"0": "square", "1": "shaped", "2": "hot"}, - Nodes: []argh.Node{ - &argh.Ident{Literal: "square"}, - &argh.Ident{Literal: "shaped"}, - &argh.Ident{Literal: "hot"}, - }, - }, - &argh.Flag{Name: "please"}, - }, - }, - }, - }, - { - name: "short value-less flags", - args: []string{"pizzas", "-t", "-f", "-s"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "t": {}, - "f": {}, - "s": {}, - }, - }, - }, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Flag{Name: "t"}, - &argh.ArgDelimiter{}, - &argh.Flag{Name: "f"}, - &argh.ArgDelimiter{}, - &argh.Flag{Name: "s"}, - }, - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.Flag{Name: "t"}, - &argh.Flag{Name: "f"}, - &argh.Flag{Name: "s"}, - }, - }, - }, - }, - { - name: "compound short flags", - args: []string{"pizzas", "-aca", "-blol"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "a": {}, - "b": {}, - "c": {}, - "l": {}, - "o": {}, - }, - }, - }, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.CompoundShortFlag{ - Nodes: []argh.Node{ - &argh.Flag{Name: "a"}, - &argh.Flag{Name: "c"}, - &argh.Flag{Name: "a"}, - }, - }, - &argh.ArgDelimiter{}, - &argh.CompoundShortFlag{ - Nodes: []argh.Node{ - &argh.Flag{Name: "b"}, - &argh.Flag{Name: "l"}, - &argh.Flag{Name: "o"}, - &argh.Flag{Name: "l"}, - }, - }, - }, - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.Flag{Name: "a"}, - &argh.Flag{Name: "c"}, - &argh.Flag{Name: "a"}, - &argh.Flag{Name: "b"}, - &argh.Flag{Name: "l"}, - &argh.Flag{Name: "o"}, - &argh.Flag{Name: "l"}, - }, - }, - }, - }, - { - name: "mixed long short value flags", - args: []string{"pizzas", "-a", "--ca", "-b", "1312", "-lol"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Commands: &argh.Commands{Map: map[string]argh.CommandConfig{}}, - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "a": {}, - "b": argh.FlagConfig{NValue: 1}, - "ca": {}, - "l": {}, - "o": {}, - }, - }, - }, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Flag{Name: "a"}, - &argh.ArgDelimiter{}, - &argh.Flag{Name: "ca"}, - &argh.ArgDelimiter{}, - &argh.Flag{ - Name: "b", - Values: map[string]string{"0": "1312"}, - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "1312"}, - }, - }, - &argh.ArgDelimiter{}, - &argh.CompoundShortFlag{ - Nodes: []argh.Node{ - &argh.Flag{Name: "l"}, - &argh.Flag{Name: "o"}, - &argh.Flag{Name: "l"}, - }, - }, - }, - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.Flag{Name: "a"}, - &argh.Flag{Name: "ca"}, - &argh.Flag{ - Name: "b", - Values: map[string]string{"0": "1312"}, - Nodes: []argh.Node{ - &argh.Ident{Literal: "1312"}, - }, - }, - &argh.Flag{Name: "l"}, - &argh.Flag{Name: "o"}, - &argh.Flag{Name: "l"}, - }, - }, - }, - }, - { - name: "nested commands with positional args", - args: []string{"pizzas", "fly", "freely", "sometimes", "and", "other", "times", "fry", "deeply", "--forever"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Commands: &argh.Commands{ - Map: map[string]argh.CommandConfig{ - "fly": argh.CommandConfig{ - Commands: &argh.Commands{ - Map: map[string]argh.CommandConfig{ - "fry": argh.CommandConfig{ - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "forever": {}, - }, - }, - }, - }, - }, - }, - }, - }, - Flags: &argh.Flags{Map: map[string]argh.FlagConfig{}}, - }, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Command{ - Name: "fly", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "freely"}, - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "sometimes"}, - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "and"}, - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "other"}, - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "times"}, - &argh.ArgDelimiter{}, - &argh.Command{ - Name: "fry", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "deeply"}, - &argh.ArgDelimiter{}, - &argh.Flag{Name: "forever"}, - }, - }, - }, - }, - }, - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.Command{ - Name: "fly", - Nodes: []argh.Node{ - &argh.Ident{Literal: "freely"}, - &argh.Ident{Literal: "sometimes"}, - &argh.Ident{Literal: "and"}, - &argh.Ident{Literal: "other"}, - &argh.Ident{Literal: "times"}, - &argh.Command{ - Name: "fry", - Nodes: []argh.Node{ - &argh.Ident{Literal: "deeply"}, - &argh.Flag{Name: "forever"}, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "compound flags with values", - args: []string{"pizzas", "-need", "sauce", "heat", "love", "-also", "over9000"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "a": {NValue: argh.ZeroOrMoreValue}, - "d": {NValue: argh.OneOrMoreValue}, - "e": {}, - "l": {}, - "n": {}, - "o": {NValue: 1, ValueNames: []string{"level"}}, - "s": {NValue: argh.ZeroOrMoreValue}, - }, - }, - }, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.CompoundShortFlag{ - Nodes: []argh.Node{ - &argh.Flag{Name: "n"}, - &argh.Flag{Name: "e"}, - &argh.Flag{Name: "e"}, - &argh.Flag{ - Name: "d", - Values: map[string]string{ - "0": "sauce", - "1": "heat", - "2": "love", - }, - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "sauce"}, - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "heat"}, - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "love"}, - &argh.ArgDelimiter{}, - }, - }, - }, - }, - &argh.CompoundShortFlag{ - Nodes: []argh.Node{ - &argh.Flag{Name: "a"}, - &argh.Flag{Name: "l"}, - &argh.Flag{Name: "s"}, - &argh.Flag{ - Name: "o", - Values: map[string]string{ - "level": "over9000", - }, - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "over9000"}, - }, - }, - }, - }, - }, - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.Flag{Name: "n"}, - &argh.Flag{Name: "e"}, - &argh.Flag{Name: "e"}, - &argh.Flag{ - Name: "d", - Values: map[string]string{ - "0": "sauce", - "1": "heat", - "2": "love", - }, - Nodes: []argh.Node{ - &argh.Ident{Literal: "sauce"}, - &argh.Ident{Literal: "heat"}, - &argh.Ident{Literal: "love"}, - }, - }, - &argh.Flag{Name: "a"}, - &argh.Flag{Name: "l"}, - &argh.Flag{Name: "s"}, - &argh.Flag{ - Name: "o", - Values: map[string]string{ - "level": "over9000", - }, - Nodes: []argh.Node{ - &argh.Ident{Literal: "over9000"}, - }, - }, - }, - }, - }, - }, - { - name: "command specific flags", - args: []string{"pizzas", "fly", "--freely", "fry", "--deeply", "-wAt", "hugs"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Commands: &argh.Commands{ - Map: map[string]argh.CommandConfig{ - "fly": argh.CommandConfig{ - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "freely": {}, - }, - }, - Commands: &argh.Commands{ - Map: map[string]argh.CommandConfig{ - "fry": argh.CommandConfig{ - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "deeply": {}, - "w": {}, - "A": {}, - "t": argh.FlagConfig{NValue: 1}, - }, - }, - }, - }, - }, - }, - }, - }, - Flags: &argh.Flags{Map: map[string]argh.FlagConfig{}}, - }, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Command{ - Name: "fly", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Flag{Name: "freely"}, - &argh.ArgDelimiter{}, - &argh.Command{ - Name: "fry", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Flag{Name: "deeply"}, - &argh.ArgDelimiter{}, - &argh.CompoundShortFlag{ - Nodes: []argh.Node{ - &argh.Flag{Name: "w"}, - &argh.Flag{Name: "A"}, - &argh.Flag{ - Name: "t", - Values: map[string]string{"0": "hugs"}, - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "hugs"}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - expAST: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.Command{ - Name: "fly", - Nodes: []argh.Node{ - &argh.Flag{Name: "freely"}, - &argh.Command{ - Name: "fry", - Nodes: []argh.Node{ - &argh.Flag{Name: "deeply"}, - &argh.Flag{Name: "w"}, - &argh.Flag{Name: "A"}, - &argh.Flag{ - Name: "t", - Values: map[string]string{"0": "hugs"}, - Nodes: []argh.Node{ - &argh.Ident{Literal: "hugs"}, - }, - }, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "total weirdo", - args: []string{"PIZZAs", "^wAT@golf", "^^hecKing", "goose", "bonk", "^^FIERCENESS@-2"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Commands: &argh.Commands{ - Map: map[string]argh.CommandConfig{ - "goose": argh.CommandConfig{ - NValue: 1, - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "FIERCENESS": argh.FlagConfig{NValue: 1}, - }, - }, - }, - }, - }, - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "w": argh.FlagConfig{}, - "A": argh.FlagConfig{}, - "T": argh.FlagConfig{NValue: 1}, - "hecKing": argh.FlagConfig{}, - }, - }, - }, - ScannerConfig: &argh.ScannerConfig{ - AssignmentOperator: '@', - FlagPrefix: '^', - MultiValueDelim: ',', - }, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "PIZZAs", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.CompoundShortFlag{ - Nodes: []argh.Node{ - &argh.Flag{Name: "w"}, - &argh.Flag{Name: "A"}, - &argh.Flag{ - Name: "T", - Values: map[string]string{"0": "golf"}, - Nodes: []argh.Node{ - &argh.Assign{}, - &argh.Ident{Literal: "golf"}, - }, - }, - }, - }, - &argh.ArgDelimiter{}, - &argh.Flag{Name: "hecKing"}, - &argh.ArgDelimiter{}, - &argh.Command{ - Name: "goose", - Values: map[string]string{"0": "bonk"}, - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Ident{Literal: "bonk"}, - &argh.ArgDelimiter{}, - &argh.Flag{ - Name: "FIERCENESS", - Values: map[string]string{"0": "-2"}, - Nodes: []argh.Node{ - &argh.Assign{}, - &argh.Ident{Literal: "-2"}, - }, - }, - }, - }, - }, - }, - }, - }, - { - name: "windows like", - args: []string{"hotdog", "/f", "/L", "/o:ppy", "hats"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "f": {}, - "L": {}, - "o": argh.FlagConfig{NValue: 1}, - }, - }, - Commands: &argh.Commands{ - Map: map[string]argh.CommandConfig{ - "hats": {}, - }, - }, - }, - ScannerConfig: &argh.ScannerConfig{ - AssignmentOperator: ':', - FlagPrefix: '/', - MultiValueDelim: ',', - }, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "hotdog", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.Flag{Name: "f"}, - &argh.ArgDelimiter{}, - &argh.Flag{Name: "L"}, - &argh.ArgDelimiter{}, - &argh.Flag{ - Name: "o", - Values: map[string]string{"0": "ppy"}, - Nodes: []argh.Node{ - &argh.Assign{}, - &argh.Ident{Literal: "ppy"}, - }, - }, - &argh.ArgDelimiter{}, - &argh.Command{Name: "hats"}, - }, - }, - }, - }, - { - name: "invalid bare assignment", - args: []string{"pizzas", "=", "--wat"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "wat": {}, - }, - }, - }, - }, - expErr: argh.ParserErrorList{ - &argh.ParserError{Pos: argh.Position{Column: 8}, Msg: "invalid bare assignment"}, - }, - expPT: []argh.Node{ - &argh.Command{ - Name: "pizzas", - Nodes: []argh.Node{ - &argh.ArgDelimiter{}, - &argh.ArgDelimiter{}, - &argh.Flag{Name: "wat"}, - }, - }, - }, - }, - } { - if tc.expPT != nil { - t.Run(tc.name+" parse tree", func(ct *testing.T) { - if tc.skip { - ct.SkipNow() - return - } - - pt, err := argh.ParseArgs(tc.args, tc.cfg) - if err != nil || tc.expErr != nil { - if !assert.ErrorIs(ct, err, tc.expErr) { - spew.Dump(err, tc.expErr) - spew.Dump(pt) - } - return - } - - if !assert.Equal(ct, tc.expPT, pt.Nodes) { - spew.Dump(pt) - } - }) - } - - if tc.expAST != nil { - t.Run(tc.name+" ast", func(ct *testing.T) { - if tc.skip { - ct.SkipNow() - return - } - - pt, err := argh.ParseArgs(tc.args, tc.cfg) - if err != nil || tc.expErr != nil { - if !assert.ErrorIs(ct, err, tc.expErr) { - spew.Dump(pt) - } - return - } - - ast := argh.NewQuerier(pt.Nodes).AST() - - if !assert.Equal(ct, tc.expAST, ast) { - spew.Dump(ast) - } - }) - } - } -} diff --git a/argh/querier.go b/argh/querier.go deleted file mode 100644 index 2d1ca54..0000000 --- a/argh/querier.go +++ /dev/null @@ -1,94 +0,0 @@ -package argh - -type Querier interface { - Program() (*Command, bool) - AST() []Node -} - -func NewQuerier(nodes []Node) Querier { - return &defaultQuerier{nodes: nodes} -} - -type defaultQuerier struct { - nodes []Node -} - -func (dq *defaultQuerier) Program() (*Command, bool) { - if len(dq.nodes) == 0 { - tracef("Program nodes are empty") - return nil, false - } - - tracef("Program node[0] is %T", dq.nodes[0]) - - v, ok := dq.nodes[0].(*Command) - if ok && v.Name == "" { - return v, false - } - - return v, ok -} - -func (dq *defaultQuerier) AST() []Node { - ret := []Node{} - - for i, node := range dq.nodes { - tracef("AST i=%d node type=%T", i, node) - - if _, ok := node.(*ArgDelimiter); ok { - continue - } - - if _, ok := node.(*StopFlag); ok { - continue - } - - if v, ok := node.(*CompoundShortFlag); ok { - if v.Nodes != nil { - ret = append(ret, NewQuerier(v.Nodes).AST()...) - } - - continue - } - - if v, ok := node.(*Command); ok { - astNodes := NewQuerier(v.Nodes).AST() - - if len(astNodes) == 0 { - astNodes = nil - } - - ret = append( - ret, - &Command{ - Name: v.Name, - Values: v.Values, - Nodes: astNodes, - }) - - continue - } - - if v, ok := node.(*Flag); ok { - astNodes := NewQuerier(v.Nodes).AST() - - if len(astNodes) == 0 { - astNodes = nil - } - - ret = append( - ret, - &Flag{ - Name: v.Name, - Values: v.Values, - Nodes: astNodes, - }) - - continue - } - - ret = append(ret, node) - } - - return ret -} diff --git a/argh/querier_test.go b/argh/querier_test.go deleted file mode 100644 index 962d869..0000000 --- a/argh/querier_test.go +++ /dev/null @@ -1,60 +0,0 @@ -package argh_test - -import ( - "testing" - - "git.meatballhat.com/x/box-o-sand/argh" - "github.com/stretchr/testify/require" -) - -func TestQuerier_Program(t *testing.T) { - for _, tc := range []struct { - name string - args []string - cfg *argh.ParserConfig - exp string - expOK bool - }{ - { - name: "typical", - args: []string{"pizzas", "ahoy", "--treatsa", "fun"}, - cfg: &argh.ParserConfig{ - Prog: argh.CommandConfig{ - Commands: &argh.Commands{ - Map: map[string]argh.CommandConfig{ - "ahoy": argh.CommandConfig{ - Flags: &argh.Flags{ - Map: map[string]argh.FlagConfig{ - "treatsa": argh.FlagConfig{NValue: 1}, - }, - }, - }, - }, - }, - }, - }, - exp: "pizzas", - expOK: true, - }, - { - name: "minimal", - args: []string{"pizzas"}, - exp: "pizzas", - expOK: true, - }, - { - name: "invalid", - args: []string{}, - expOK: false, - }, - } { - t.Run(tc.name, func(ct *testing.T) { - pt, err := argh.ParseArgs(tc.args, tc.cfg) - require.Nil(ct, err) - - prog, ok := argh.NewQuerier(pt.Nodes).Program() - require.Equal(ct, tc.expOK, ok) - require.Equal(ct, tc.exp, prog.Name) - }) - } -} diff --git a/argh/scanner.go b/argh/scanner.go deleted file mode 100644 index 3cc8d45..0000000 --- a/argh/scanner.go +++ /dev/null @@ -1,159 +0,0 @@ -package argh - -import ( - "bufio" - "bytes" - "errors" - "io" - "log" - "unicode" -) - -type Scanner struct { - r *bufio.Reader - i int - cfg *ScannerConfig -} - -func NewScanner(r io.Reader, cfg *ScannerConfig) *Scanner { - if cfg == nil { - cfg = POSIXyScannerConfig - } - - return &Scanner{ - r: bufio.NewReader(r), - cfg: cfg, - } -} - -func (s *Scanner) Scan() (Token, string, Pos) { - ch, pos := s.read() - - if s.cfg.IsBlankspace(ch) { - _ = s.unread() - return s.scanBlankspace() - } - - if s.cfg.IsAssignmentOperator(ch) { - return ASSIGN, string(ch), pos - } - - if s.cfg.IsMultiValueDelim(ch) { - return MULTI_VALUE_DELIMITER, string(ch), pos - } - - if ch == eol { - return EOL, "", pos - } - - if ch == nul { - return ARG_DELIMITER, string(ch), pos - } - - if unicode.IsGraphic(ch) { - _ = s.unread() - return s.scanArg() - } - - return ILLEGAL, string(ch), pos -} - -func (s *Scanner) read() (rune, Pos) { - ch, _, err := s.r.ReadRune() - s.i++ - - if errors.Is(err, io.EOF) { - return eol, Pos(s.i) - } else if err != nil { - log.Printf("unknown scanner error=%+v", err) - return eol, Pos(s.i) - } - - return ch, Pos(s.i) -} - -func (s *Scanner) unread() Pos { - _ = s.r.UnreadRune() - s.i-- - return Pos(s.i) -} - -func (s *Scanner) scanBlankspace() (Token, string, Pos) { - buf := &bytes.Buffer{} - ch, pos := s.read() - buf.WriteRune(ch) - - for { - ch, pos = s.read() - - if ch == eol { - break - } else if !s.cfg.IsBlankspace(ch) { - pos = s.unread() - break - } else { - _, _ = buf.WriteRune(ch) - } - } - - return BS, buf.String(), pos -} - -func (s *Scanner) scanArg() (Token, string, Pos) { - buf := &bytes.Buffer{} - ch, pos := s.read() - buf.WriteRune(ch) - - for { - ch, pos = s.read() - - if ch == eol || ch == nul || s.cfg.IsAssignmentOperator(ch) || s.cfg.IsMultiValueDelim(ch) { - pos = s.unread() - break - } - - _, _ = buf.WriteRune(ch) - } - - str := buf.String() - - if len(str) == 0 { - return EMPTY, str, pos - } - - ch0 := rune(str[0]) - - if len(str) == 1 { - if s.cfg.IsFlagPrefix(ch0) { - return STDIN_FLAG, str, pos - } - - if s.cfg.IsAssignmentOperator(ch0) { - return ASSIGN, str, pos - } - - return IDENT, str, pos - } - - ch1 := rune(str[1]) - - if len(str) == 2 { - if s.cfg.IsFlagPrefix(ch0) && s.cfg.IsFlagPrefix(ch1) { - return STOP_FLAG, str, pos - } - - if s.cfg.IsFlagPrefix(ch0) { - return SHORT_FLAG, str, pos - } - } - - if s.cfg.IsFlagPrefix(ch0) { - if s.cfg.IsFlagPrefix(ch1) { - return LONG_FLAG, str, pos - } - - return COMPOUND_SHORT_FLAG, str, pos - } - - return IDENT, str, pos -} diff --git a/argh/scanner_config.go b/argh/scanner_config.go deleted file mode 100644 index 9bc9be2..0000000 --- a/argh/scanner_config.go +++ /dev/null @@ -1,39 +0,0 @@ -package argh - -var ( - // POSIXyScannerConfig defines a scanner config that uses '-' - // as the flag prefix, which also means that "--" is the "long - // flag" prefix, a bare "--" is considered STOP_FLAG, and a - // bare "-" is considered STDIN_FLAG. - POSIXyScannerConfig = &ScannerConfig{ - AssignmentOperator: '=', - FlagPrefix: '-', - MultiValueDelim: ',', - } -) - -type ScannerConfig struct { - AssignmentOperator rune - FlagPrefix rune - MultiValueDelim rune -} - -func (cfg *ScannerConfig) IsFlagPrefix(ch rune) bool { - return ch == cfg.FlagPrefix -} - -func (cfg *ScannerConfig) IsMultiValueDelim(ch rune) bool { - return ch == cfg.MultiValueDelim -} - -func (cfg *ScannerConfig) IsAssignmentOperator(ch rune) bool { - return ch == cfg.AssignmentOperator -} - -func (cfg *ScannerConfig) IsBlankspace(ch rune) bool { - return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' -} - -func (cfg *ScannerConfig) IsUnderscore(ch rune) bool { - return ch == '_' -} diff --git a/argh/token.go b/argh/token.go deleted file mode 100644 index b26b8b7..0000000 --- a/argh/token.go +++ /dev/null @@ -1,53 +0,0 @@ -//go:generate stringer -type Token - -package argh - -import "fmt" - -const ( - ILLEGAL Token = iota - EOL - EMPTY // '' - BS // ' ' '\t' '\n' - IDENT // char group without flag prefix: 'some' 'words' - ARG_DELIMITER // rune(0) - ASSIGN // '=' - MULTI_VALUE_DELIMITER // ',' - LONG_FLAG // char group with double flag prefix: '--flag' - SHORT_FLAG // single char with single flag prefix: '-f' - COMPOUND_SHORT_FLAG // char group with single flag prefix: '-flag' - STDIN_FLAG // '-' - STOP_FLAG // '--' - - nul = rune(0) - eol = rune(-1) -) - -type Token int - -// Position is adapted from go/token.Position -type Position struct { - Column int -} - -func (p *Position) IsValid() bool { return p.Column > 0 } - -func (p Position) String() string { - s := "" - if p.IsValid() { - s = fmt.Sprintf("%d", p.Column) - } - if s == "" { - s = "-" - } - return s -} - -// Pos is borrowed from go/token.Pos -type Pos int - -const NoPos Pos = 0 - -func (p Pos) IsValid() bool { - return p != NoPos -} diff --git a/argh/token_string.go b/argh/token_string.go deleted file mode 100644 index ff6a07e..0000000 --- a/argh/token_string.go +++ /dev/null @@ -1,35 +0,0 @@ -// Code generated by "stringer -type Token"; DO NOT EDIT. - -package argh - -import "strconv" - -func _() { - // An "invalid array index" compiler error signifies that the constant values have changed. - // Re-run the stringer command to generate them again. - var x [1]struct{} - _ = x[ILLEGAL-0] - _ = x[EOL-1] - _ = x[EMPTY-2] - _ = x[BS-3] - _ = x[IDENT-4] - _ = x[ARG_DELIMITER-5] - _ = x[ASSIGN-6] - _ = x[MULTI_VALUE_DELIMITER-7] - _ = x[LONG_FLAG-8] - _ = x[SHORT_FLAG-9] - _ = x[COMPOUND_SHORT_FLAG-10] - _ = x[STDIN_FLAG-11] - _ = x[STOP_FLAG-12] -} - -const _Token_name = "ILLEGALEOLEMPTYBSIDENTARG_DELIMITERASSIGNMULTI_VALUE_DELIMITERLONG_FLAGSHORT_FLAGCOMPOUND_SHORT_FLAGSTDIN_FLAGSTOP_FLAG" - -var _Token_index = [...]uint8{0, 7, 10, 15, 17, 22, 35, 41, 62, 71, 81, 100, 110, 119} - -func (i Token) String() string { - if i < 0 || i >= Token(len(_Token_index)-1) { - return "Token(" + strconv.FormatInt(int64(i), 10) + ")" - } - return _Token_name[_Token_index[i]:_Token_index[i+1]] -}