From 1452d544bbcf71529b8aaab7247f7591023a2c17 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Sun, 29 May 2022 19:10:16 -0400 Subject: [PATCH] Dropping previous parser attempt --- argh/cmd/argh/main.go | 2 +- argh/parser.go | 444 +++++++++++------------ argh/parser2.go | 328 ----------------- argh/parser2_test.go | 807 ------------------------------------------ argh/parser_test.go | 781 +++++++++++++++++++++++++++++++--------- 5 files changed, 842 insertions(+), 1520 deletions(-) delete mode 100644 argh/parser2.go delete mode 100644 argh/parser2_test.go diff --git a/argh/cmd/argh/main.go b/argh/cmd/argh/main.go index bd9b32c..1313da4 100644 --- a/argh/cmd/argh/main.go +++ b/argh/cmd/argh/main.go @@ -12,7 +12,7 @@ import ( func main() { log.SetFlags(0) - ast, err := argh.ParseArgs(os.Args, nil) + ast, err := argh.ParseArgs2(os.Args, nil) if err != nil { log.Fatal(err) } diff --git a/argh/parser.go b/argh/parser.go index f9ec92a..d994efe 100644 --- a/argh/parser.go +++ b/argh/parser.go @@ -1,332 +1,332 @@ -//go:generate stringer -type NValue - package argh import ( "fmt" "io" "strings" - - "github.com/pkg/errors" -) - -var ( - ErrSyntax = errors.New("syntax error") ) -func ParseArgs(args []string, pCfg *ParserConfig) (*ParseTree, error) { - reEncoded := strings.Join(args, string(nul)) - - return NewParser( - strings.NewReader(reEncoded), - pCfg, - ).Parse() -} - -type Parser struct { +type parser struct { s *Scanner - buf []scanEntry - cfg *ParserConfig - nodes []Node - node Node + errors ScannerErrorList + + tok Token + lit string + pos Pos + + buffered bool } type ParseTree struct { Nodes []Node `json:"nodes"` } -type scanEntry struct { - tok Token - lit string - pos Pos +func ParseArgs2(args []string, pCfg *ParserConfig) (*ParseTree, error) { + p := &parser{} + p.init( + strings.NewReader(strings.Join(args, string(nul))), + pCfg, + ) + + tracef("ParseArgs2(...) parser=%+#v", p) + + return p.parseArgs() } -func NewParser(r io.Reader, pCfg *ParserConfig) *Parser { +func (p *parser) init(r io.Reader, pCfg *ParserConfig) { + p.errors = ScannerErrorList{} + if pCfg == nil { pCfg = POSIXyParserConfig } - parser := &Parser{ - buf: []scanEntry{}, - s: NewScanner(r, pCfg.ScannerConfig), - cfg: pCfg, - } + p.cfg = pCfg - tracef("NewParser parser=%+#v", parser) - tracef("NewParser pCfg=%+#v", pCfg) + p.s = NewScanner(r, pCfg.ScannerConfig) - return parser + p.next() } -func (p *Parser) Parse() (*ParseTree, error) { - p.nodes = []Node{} - - for { - br, err := p.parseArg() - if err != nil { - return nil, err - } - - if br { - break - } +func (p *parser) parseArgs() (*ParseTree, error) { + if p.errors.Len() != 0 { + tracef("parseArgs() bailing due to initial error") + return nil, p.errors.Err() } - return &ParseTree{Nodes: p.nodes}, nil -} + tracef("parseArgs() parsing %q as program command; cfg=%+#v", p.lit, p.cfg.Prog) + prog := p.parseCommand(&p.cfg.Prog) -func (p *Parser) parseArg() (bool, error) { - tok, lit, pos := p.scan() - if tok == ILLEGAL { - return false, errors.Wrapf(ErrSyntax, "illegal value %q at pos=%v", lit, pos) - } + tracef("parseArgs() top level node is %T", prog) - if tok == EOL { - return true, nil + nodes := []Node{prog} + if v := p.parsePassthrough(); v != nil { + tracef("parseArgs() appending passthrough argument %v", v) + nodes = append(nodes, v) } - p.unscan(tok, lit, pos) + tracef("parseArgs() returning ParseTree") - node, err := p.scanNode() + return &ParseTree{Nodes: nodes}, p.errors.Err() +} - tracef("parseArg node=%+#v err=%+#v", node, err) +func (p *parser) next() { + tracef("next() before scan: %v %q %v", p.tok, p.lit, p.pos) - if err != nil { - return false, errors.Wrapf(err, "value %q at pos=%v", lit, pos) - } + p.tok, p.lit, p.pos = p.s.Scan() - if node != nil { - p.nodes = append(p.nodes, node) - } - - return false, nil + tracef("next() after scan: %v %q %v", p.tok, p.lit, p.pos) } -func (p *Parser) scanNode() (Node, error) { - tok, lit, pos := p.scan() - - tracef("scanNode tok=%s lit=%q pos=%v", tok, lit, pos) +func (p *parser) parseCommand(cCfg *CommandConfig) Node { + tracef("parseCommand(%+#v)", cCfg) - switch tok { - case ARG_DELIMITER: - return ArgDelimiter{}, nil - case ASSIGN: - return nil, errors.Wrapf(ErrSyntax, "bare assignment operator at pos=%v", pos) - case IDENT: - p.unscan(tok, lit, pos) - return p.scanCommandOrIdent() - case COMPOUND_SHORT_FLAG: - p.unscan(tok, lit, pos) - return p.scanCompoundShortFlag() - case SHORT_FLAG, LONG_FLAG: - p.unscan(tok, lit, pos) - return p.scanFlag() - default: + node := &Command{ + Name: p.lit, } + values := map[string]string{} + nodes := []Node{} - return Ident{Literal: lit}, nil -} - -func (p *Parser) scanCommandOrIdent() (Node, error) { - tok, lit, pos := p.scan() + identIndex := 0 - if len(p.nodes) == 0 { - p.unscan(tok, lit, pos) - values, err := p.scanValues(p.cfg.Prog.NValue, p.cfg.Prog.ValueNames) - if err != nil { - return nil, err + for i := 0; p.tok != EOL; i++ { + if !p.buffered { + tracef("parseCommand(...) buffered=false; scanning next") + p.next() } - return Command{Name: lit, Values: values}, nil - } + p.buffered = false - if cfg, ok := p.cfg.Prog.Commands[lit]; ok { - p.unscan(tok, lit, pos) - values, err := p.scanValues(cfg.NValue, cfg.ValueNames) - if err != nil { - return nil, err - } + 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) - return Command{Name: lit, Values: values}, nil - } + if subCfg, ok := cCfg.Commands[p.lit]; ok { + subCommand := p.lit - return Ident{Literal: lit}, nil -} + nodes = append(nodes, p.parseCommand(&subCfg)) -func (p *Parser) scanFlag() (Node, error) { - tok, lit, pos := p.scan() + tracef("parseCommand(...) breaking after sub-command=%v", subCommand) + break + } - flagName := string(lit[1:]) - if tok == LONG_FLAG { - flagName = string(lit[2:]) - } + switch p.tok { + case ARG_DELIMITER: + tracef("parseCommand(...) handling %s", p.tok) - if cfg, ok := p.cfg.Prog.Flags[flagName]; ok { - p.unscan(tok, flagName, pos) + nodes = append(nodes, &ArgDelimiter{}) - values, err := p.scanValues(cfg.NValue, cfg.ValueNames) - if err != nil { - return nil, err - } + continue + case IDENT, STDIN_FLAG: + tracef("parseCommand(...) handling %s", p.tok) - return Flag{Name: flagName, Values: values}, nil - } + if cCfg.NValue.Contains(identIndex) { + name := fmt.Sprintf("%d", identIndex) - return Flag{Name: flagName}, nil -} + tracef("parseCommand(...) checking for name of identIndex=%d", identIndex) -func (p *Parser) scanCompoundShortFlag() (Node, error) { - tok, lit, pos := p.scan() + 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) + } - flagNodes := []Node{} + values[name] = p.lit + } - withoutFlagPrefix := lit[1:] + if p.tok == STDIN_FLAG { + nodes = append(nodes, &StdinFlag{}) + } else { + nodes = append(nodes, &Ident{Literal: p.lit}) + } - for i, r := range withoutFlagPrefix { - if i == len(withoutFlagPrefix)-1 { - flagName := string(r) + identIndex++ + case LONG_FLAG, SHORT_FLAG, COMPOUND_SHORT_FLAG: + tok := p.tok - if cfg, ok := p.cfg.Prog.Flags[flagName]; ok { - p.unscan(tok, flagName, pos) + flagNode := p.parseFlag(cCfg.Flags) - values, err := p.scanValues(cfg.NValue, cfg.ValueNames) - if err != nil { - return nil, err - } + tracef("parseCommand(...) appending %s node=%+#v", tok, flagNode) - flagNodes = append(flagNodes, Flag{Name: flagName, Values: values}) + nodes = append(nodes, flagNode) + case ASSIGN: + tracef("parseCommand(...) error on bare %s", p.tok) - continue - } + p.errors.Add(Position{Column: int(p.pos)}, "invalid bare assignment") + + break + default: + tracef("parseCommand(...) breaking on %s", p.tok) + break } + } - flagNodes = append( - flagNodes, - Flag{ - Name: string(r), - }, - ) + if len(nodes) > 0 { + node.Nodes = nodes } - return CompoundShortFlag{Nodes: flagNodes}, nil + if len(values) > 0 { + node.Values = values + } + + tracef("parseCommand(...) returning node=%+#v", node) + return node } -func (p *Parser) scanValuesAndFlags() (map[string]string, []Node, error) { - return nil, nil, nil +func (p *parser) parseIdent() Node { + node := &Ident{Literal: p.lit} + return node } -func (p *Parser) scanValues(n NValue, valueNames []string) (map[string]string, error) { - _, lit, pos := p.scan() +func (p *parser) parseFlag(flCfgMap map[string]FlagConfig) Node { + switch p.tok { + case SHORT_FLAG: + tracef("parseFlag(...) parsing short flag with config=%+#v", flCfgMap) + return p.parseShortFlag(flCfgMap) + case LONG_FLAG: + tracef("parseFlag(...) parsing long flag with config=%+#v", flCfgMap) + return p.parseLongFlag(flCfgMap) + case COMPOUND_SHORT_FLAG: + tracef("parseFlag(...) parsing compound short flag with config=%+#v", flCfgMap) + return p.parseCompoundShortFlag(flCfgMap) + } - tracef("scanValues lit=%q pos=%v n=%v valueNames=%+v", lit, pos, n, valueNames) + panic(fmt.Sprintf("token %v cannot be parsed as flag", p.tok)) +} - values, err := func() (map[string]string, error) { - if n == ZeroValue { - return map[string]string{}, nil - } +func (p *parser) parseShortFlag(flCfgMap map[string]FlagConfig) Node { + node := &Flag{Name: string(p.lit[1])} - ret := map[string]string{} - i := 0 + flCfg, ok := flCfgMap[node.Name] + if !ok { + return node + } - for { - lit, err := p.scanIdent() - if err != nil { - if n == NValue(1) { - return nil, err - } + return p.parseConfiguredFlag(node, flCfg) +} - if n == OneOrMoreValue { - break - } - } +func (p *parser) parseLongFlag(flCfgMap map[string]FlagConfig) Node { + node := &Flag{Name: string(p.lit[2:])} - name := fmt.Sprintf("%d", i) - if len(valueNames)-1 >= i { - name = valueNames[i] - } else if len(valueNames) > 0 && strings.HasSuffix(valueNames[len(valueNames)-1], "+") { - name = strings.TrimSuffix(valueNames[len(valueNames)-1], "+") - } + flCfg, ok := flCfgMap[node.Name] + if !ok { + return node + } - ret[name] = lit + return p.parseConfiguredFlag(node, flCfg) +} - if n == NValue(1) && len(ret) == 1 { - break - } +func (p *parser) parseCompoundShortFlag(flCfgMap map[string]FlagConfig) Node { + flagNodes := []Node{} - i++ - } + withoutFlagPrefix := p.lit[1:] - return ret, nil - }() + for i, r := range withoutFlagPrefix { + node := &Flag{Name: string(r)} - if err != nil { - return nil, err - } + if i == len(withoutFlagPrefix)-1 { + flCfg, ok := flCfgMap[node.Name] + if ok { + flagNodes = append(flagNodes, p.parseConfiguredFlag(node, flCfg)) - if len(values) == 0 { - return nil, nil + continue + } + } + + flagNodes = append(flagNodes, node) } - return values, nil + return &CompoundShortFlag{Nodes: flagNodes} } -func (p *Parser) scanIdent() (string, error) { - tok, lit, pos := p.scan() +func (p *parser) parseConfiguredFlag(node *Flag, flCfg FlagConfig) Node { + values := map[string]string{} + nodes := []Node{} - tracef("scanIdent scanned tok=%s lit=%q pos=%v", tok, lit, pos) + identIndex := 0 - unscanBuf := []scanEntry{} + for i := 0; p.tok != EOL; i++ { + if !flCfg.NValue.Contains(identIndex) { + tracef("parseConfiguredFlag(...) identIndex=%d exceeds expected=%v; breaking", identIndex, flCfg.NValue) + break + } - if tok == ASSIGN || tok == ARG_DELIMITER { - entry := scanEntry{tok: tok, lit: lit, pos: Pos(pos)} + p.next() - tracef("scanIdent tok=%s; scanning next and pushing to unscan buffer entry=%+#v", tok, entry) + switch p.tok { + case ARG_DELIMITER: + nodes = append(nodes, &ArgDelimiter{}) - unscanBuf = append([]scanEntry{entry}, unscanBuf...) + continue + case ASSIGN: + nodes = append(nodes, &Assign{}) - tok, lit, pos = p.scan() - } + continue + case IDENT, STDIN_FLAG: + name := fmt.Sprintf("%d", identIndex) - if tok == IDENT { - return lit, nil - } + tracef("parseConfiguredFlag(...) checking for name of identIndex=%d", identIndex) - entry := scanEntry{tok: tok, lit: lit, pos: Pos(pos)} + 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) + } - tracef("scanIdent tok=%s; unscanning entry=%+#v", tok, entry) + values[name] = p.lit - unscanBuf = append([]scanEntry{entry}, unscanBuf...) + if p.tok == STDIN_FLAG { + nodes = append(nodes, &StdinFlag{}) + } else { + nodes = append(nodes, &Ident{Literal: p.lit}) + } - for _, entry := range unscanBuf { - p.unscan(entry.tok, entry.lit, entry.pos) - } + identIndex++ + default: + tracef("parseConfiguredFlag(...) breaking on %s %q %v; setting buffered=true", p.tok, p.lit, p.pos) + p.buffered = true - return "", errors.Wrapf(ErrSyntax, "expected ident at pos=%v but got %s (%q)", pos, tok, lit) -} + if len(nodes) > 0 { + node.Nodes = nodes + } -func (p *Parser) scan() (Token, string, Pos) { - if len(p.buf) != 0 { - entry, buf := p.buf[len(p.buf)-1], p.buf[:len(p.buf)-1] - p.buf = buf + if len(values) > 0 { + node.Values = values + } - tracef("scan returning buffer entry=%s %+#v", entry.tok, entry) - return entry.tok, entry.lit, entry.pos + return node + } } - tok, lit, pos := p.s.Scan() + if len(nodes) > 0 { + node.Nodes = nodes + } - tracef("scan returning next=%s %+#v", tok, scanEntry{tok: tok, lit: lit, pos: pos}) + if len(values) > 0 { + node.Values = values + } - return tok, lit, pos + return node } -func (p *Parser) unscan(tok Token, lit string, pos Pos) { - entry := scanEntry{tok: tok, lit: lit, pos: pos} +func (p *parser) parsePassthrough() Node { + nodes := []Node{} - tracef("unscan entry=%s %+#v", tok, entry) + for ; p.tok != EOL; p.next() { + nodes = append(nodes, &Ident{Literal: p.lit}) + } + + if len(nodes) == 0 { + return nil + } - p.buf = append(p.buf, entry) + return &PassthroughArgs{Nodes: nodes} } diff --git a/argh/parser2.go b/argh/parser2.go deleted file mode 100644 index 66a0142..0000000 --- a/argh/parser2.go +++ /dev/null @@ -1,328 +0,0 @@ -package argh - -import ( - "fmt" - "io" - "strings" -) - -type parser2 struct { - s *Scanner - - cfg *ParserConfig - - errors ScannerErrorList - - tok Token - lit string - pos Pos - - buffered bool -} - -func ParseArgs2(args []string, pCfg *ParserConfig) (*ParseTree, error) { - parser := &parser2{} - parser.init( - strings.NewReader(strings.Join(args, string(nul))), - pCfg, - ) - - tracef("ParseArgs2(...) parser=%+#v", parser) - - return parser.parseArgs() -} - -func (p *parser2) init(r io.Reader, pCfg *ParserConfig) { - p.errors = ScannerErrorList{} - - if pCfg == nil { - pCfg = POSIXyParserConfig - } - - p.cfg = pCfg - - p.s = NewScanner(r, pCfg.ScannerConfig) - - p.next() -} - -func (p *parser2) 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 *parser2) 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 *parser2) 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) - - if subCfg, ok := cCfg.Commands[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.errors.Add(Position{Column: int(p.pos)}, "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 *parser2) parseIdent() Node { - node := &Ident{Literal: p.lit} - return node -} - -func (p *parser2) parseFlag(flCfgMap map[string]FlagConfig) Node { - switch p.tok { - case SHORT_FLAG: - tracef("parseFlag(...) parsing short flag with config=%+#v", flCfgMap) - return p.parseShortFlag(flCfgMap) - case LONG_FLAG: - tracef("parseFlag(...) parsing long flag with config=%+#v", flCfgMap) - return p.parseLongFlag(flCfgMap) - case COMPOUND_SHORT_FLAG: - tracef("parseFlag(...) parsing compound short flag with config=%+#v", flCfgMap) - return p.parseCompoundShortFlag(flCfgMap) - } - - panic(fmt.Sprintf("token %v cannot be parsed as flag", p.tok)) -} - -func (p *parser2) parseShortFlag(flCfgMap map[string]FlagConfig) Node { - node := &Flag{Name: string(p.lit[1])} - - flCfg, ok := flCfgMap[node.Name] - if !ok { - return node - } - - return p.parseConfiguredFlag(node, flCfg) -} - -func (p *parser2) parseLongFlag(flCfgMap map[string]FlagConfig) Node { - node := &Flag{Name: string(p.lit[2:])} - - flCfg, ok := flCfgMap[node.Name] - if !ok { - return node - } - - return p.parseConfiguredFlag(node, flCfg) -} - -func (p *parser2) parseCompoundShortFlag(flCfgMap map[string]FlagConfig) Node { - flagNodes := []Node{} - - withoutFlagPrefix := p.lit[1:] - - for i, r := range withoutFlagPrefix { - node := &Flag{Name: string(r)} - - if i == len(withoutFlagPrefix)-1 { - flCfg, ok := flCfgMap[node.Name] - if ok { - flagNodes = append(flagNodes, p.parseConfiguredFlag(node, flCfg)) - - continue - } - } - - flagNodes = append(flagNodes, node) - } - - return &CompoundShortFlag{Nodes: flagNodes} -} - -func (p *parser2) 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 *parser2) 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/parser2_test.go b/argh/parser2_test.go deleted file mode 100644 index 5265e09..0000000 --- a/argh/parser2_test.go +++ /dev/null @@ -1,807 +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 TestParser2(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{ - Commands: 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: "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"}, - 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: map[string]argh.CommandConfig{}, - Flags: map[string]argh.FlagConfig{ - "fresh": argh.FlagConfig{NValue: 1}, - "box": argh.FlagConfig{NValue: argh.OneOrMoreValue}, - }, - }, - }, - 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"}, - 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"}, - 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: map[string]argh.CommandConfig{}, - Flags: map[string]argh.FlagConfig{ - "b": argh.FlagConfig{NValue: 1}, - }, - }, - }, - 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: map[string]argh.CommandConfig{ - "fly": argh.CommandConfig{ - Commands: map[string]argh.CommandConfig{ - "fry": argh.CommandConfig{}, - }, - }, - }, - Flags: 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: 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: map[string]argh.CommandConfig{ - "fly": argh.CommandConfig{ - Flags: map[string]argh.FlagConfig{ - "freely": {}, - }, - Commands: map[string]argh.CommandConfig{ - "fry": argh.CommandConfig{ - Flags: map[string]argh.FlagConfig{ - "deeply": {}, - "w": {}, - "A": {}, - "t": argh.FlagConfig{NValue: 1}, - }, - }, - }, - }, - }, - Flags: 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: map[string]argh.CommandConfig{ - "goose": argh.CommandConfig{ - NValue: 1, - Flags: map[string]argh.FlagConfig{ - "FIERCENESS": argh.FlagConfig{NValue: 1}, - }, - }, - }, - Flags: 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: "invalid bare assignment", - args: []string{"pizzas", "=", "--wat"}, - expErr: argh.ScannerErrorList{ - &argh.ScannerError{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.ParseArgs2(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.ParseArgs2(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/parser_test.go b/argh/parser_test.go index 5f89369..5265e09 100644 --- a/argh/parser_test.go +++ b/argh/parser_test.go @@ -4,27 +4,96 @@ 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) { +func TestParser2(t *testing.T) { for _, tc := range []struct { name string args []string cfg *argh.ParserConfig + expErr error expPT []argh.Node expAST []argh.Node - expErr error skip bool }{ + { + name: "basic", + args: []string{ + "pies", "-eat", "--wat", "hello", "mario", + }, + cfg: &argh.ParserConfig{ + Prog: argh.CommandConfig{ + Commands: 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: "bare", args: []string{"pizzas"}, expPT: []argh.Node{ - argh.Command{Name: "pizzas"}, + &argh.Command{ + Name: "pizzas", + }, }, expAST: []argh.Node{ - argh.Command{Name: "pizzas"}, + &argh.Command{ + Name: "pizzas", + }, }, }, { @@ -34,42 +103,98 @@ func TestParser(t *testing.T) { Prog: argh.CommandConfig{NValue: 1}, }, expPT: []argh.Node{ - argh.Command{Name: "pizzas", Values: map[string]string{"0": "excel"}}, + &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"}}, + &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}, + Prog: argh.CommandConfig{ + NValue: argh.OneOrMoreValue, + ValueNames: []string{"word"}, + }, }, expPT: []argh.Node{ - argh.Command{Name: "pizzas", Values: map[string]string{"0": "excel", "1": "wildly", "2": "when", "3": "feral"}}, + &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{"0": "excel", "1": "wildly", "2": "when", "3": "feral"}}, + &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"}, expPT: []argh.Node{ - argh.Command{Name: "pizzas"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "tasty"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "fresh"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "super-hot-right-now"}, + &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"}, - argh.Flag{Name: "tasty"}, - argh.Flag{Name: "fresh"}, - argh.Flag{Name: "super-hot-right-now"}, + &argh.Command{ + Name: "pizzas", + Nodes: []argh.Node{ + &argh.Flag{Name: "tasty"}, + &argh.Flag{Name: "fresh"}, + &argh.Flag{Name: "super-hot-right-now"}, + }, + }, }, }, { @@ -92,79 +217,135 @@ func TestParser(t *testing.T) { }, }, expPT: []argh.Node{ - argh.Command{Name: "pizzas"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "tasty"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "fresh", Values: map[string]string{"0": "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"}}, - argh.ArgDelimiter{}, - argh.Flag{Name: "please"}, + &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"}, - argh.Flag{Name: "tasty"}, - argh.Flag{Name: "fresh", Values: map[string]string{"0": "soon"}}, - argh.Flag{Name: "super-hot-right-now"}, - argh.Flag{Name: "box", Values: map[string]string{"0": "square", "1": "shaped", "2": "hot"}}, - argh.Flag{Name: "please"}, + &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"}, expPT: []argh.Node{ - argh.Command{Name: "pizzas"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "t"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "f"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "s"}, + &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"}, - argh.Flag{Name: "t"}, - argh.Flag{Name: "f"}, - argh.Flag{Name: "s"}, + &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"}, expPT: []argh.Node{ - argh.Command{Name: "pizzas"}, - argh.ArgDelimiter{}, - argh.CompoundShortFlag{ + &argh.Command{ + Name: "pizzas", 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: "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"}, + }, + }, }, }, - argh.ArgDelimiter{}, - argh.CompoundShortFlag{ + }, + expAST: []argh.Node{ + &argh.Command{ + Name: "pizzas", Nodes: []argh.Node{ - argh.Flag{Name: "b"}, - argh.Flag{Name: "l"}, - argh.Flag{Name: "o"}, - argh.Flag{Name: "l"}, + &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"}, }, }, }, - expAST: []argh.Node{ - argh.Command{Name: "pizzas"}, - 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", @@ -178,55 +359,230 @@ func TestParser(t *testing.T) { }, }, expPT: []argh.Node{ - argh.Command{Name: "pizzas"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "a"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "ca"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "b", Values: map[string]string{"0": "1312"}}, - argh.ArgDelimiter{}, - argh.CompoundShortFlag{ + &argh.Command{ + Name: "pizzas", Nodes: []argh.Node{ - argh.Flag{Name: "l"}, - argh.Flag{Name: "o"}, - argh.Flag{Name: "l"}, + &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"}, - argh.Flag{Name: "a"}, - argh.Flag{Name: "ca"}, - argh.Flag{Name: "b", Values: map[string]string{"0": "1312"}}, - argh.Flag{Name: "l"}, - argh.Flag{Name: "o"}, - argh.Flag{Name: "l"}, + &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: "commands", - args: []string{"pizzas", "fly", "fry"}, + 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: map[string]argh.CommandConfig{ - "fly": argh.CommandConfig{}, - "fry": argh.CommandConfig{}, + "fly": argh.CommandConfig{ + Commands: map[string]argh.CommandConfig{ + "fry": argh.CommandConfig{}, + }, + }, }, Flags: map[string]argh.FlagConfig{}, }, }, expPT: []argh.Node{ - argh.Command{Name: "pizzas"}, - argh.ArgDelimiter{}, - argh.Command{Name: "fly"}, - argh.ArgDelimiter{}, - argh.Command{Name: "fry"}, + &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: 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"}, + args: []string{"pizzas", "fly", "--freely", "fry", "--deeply", "-wAt", "hugs"}, cfg: &argh.ParserConfig{ Prog: argh.CommandConfig{ Commands: map[string]argh.CommandConfig{ @@ -234,13 +590,15 @@ func TestParser(t *testing.T) { Flags: map[string]argh.FlagConfig{ "freely": {}, }, - }, - "fry": argh.CommandConfig{ - Flags: map[string]argh.FlagConfig{ - "deeply": {}, - "w": {}, - "A": {}, - "t": {}, + Commands: map[string]argh.CommandConfig{ + "fry": argh.CommandConfig{ + Flags: map[string]argh.FlagConfig{ + "deeply": {}, + "w": {}, + "A": {}, + "t": argh.FlagConfig{NValue: 1}, + }, + }, }, }, }, @@ -248,21 +606,68 @@ func TestParser(t *testing.T) { }, }, expPT: []argh.Node{ - argh.Command{Name: "pizzas"}, - argh.ArgDelimiter{}, - argh.Command{Name: "fly"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "freely"}, - argh.ArgDelimiter{}, - argh.Command{Name: "fry"}, - argh.ArgDelimiter{}, - argh.Flag{Name: "deeply"}, - argh.ArgDelimiter{}, - argh.CompoundShortFlag{ + &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.Flag{Name: "w"}, - argh.Flag{Name: "A"}, - argh.Flag{Name: "t"}, + &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"}, + }, + }, + }, + }, + }, + }, }, }, }, @@ -273,14 +678,18 @@ func TestParser(t *testing.T) { cfg: &argh.ParserConfig{ Prog: argh.CommandConfig{ Commands: map[string]argh.CommandConfig{ - "goose": argh.CommandConfig{NValue: 1}, + "goose": argh.CommandConfig{ + NValue: 1, + Flags: map[string]argh.FlagConfig{ + "FIERCENESS": argh.FlagConfig{NValue: 1}, + }, + }, }, Flags: map[string]argh.FlagConfig{ - "w": argh.FlagConfig{}, - "A": argh.FlagConfig{}, - "T": argh.FlagConfig{NValue: 1}, - "hecking": argh.FlagConfig{}, - "FIERCENESS": argh.FlagConfig{NValue: 1}, + "w": argh.FlagConfig{}, + "A": argh.FlagConfig{}, + "T": argh.FlagConfig{NValue: 1}, + "hecking": argh.FlagConfig{}, }, }, ScannerConfig: &argh.ScannerConfig{ @@ -290,61 +699,109 @@ func TestParser(t *testing.T) { }, }, expPT: []argh.Node{ - argh.Command{Name: "PIZZAs"}, - argh.ArgDelimiter{}, - argh.CompoundShortFlag{ + &argh.Command{ + Name: "PIZZAs", Nodes: []argh.Node{ - argh.Flag{Name: "w"}, - argh.Flag{Name: "A"}, - argh.Flag{Name: "T", Values: map[string]string{"0": "golf"}}, + &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"}, + }, + }, + }, + }, }, }, - argh.ArgDelimiter{}, - argh.Flag{Name: "hecKing"}, - argh.ArgDelimiter{}, - argh.Command{Name: "goose", Values: map[string]string{"0": "bonk"}}, - argh.ArgDelimiter{}, - argh.Flag{Name: "FIERCENESS", Values: map[string]string{"0": "-2"}}, }, }, { - name: "invalid bare assignment", - args: []string{"pizzas", "=", "--wat"}, - expErr: argh.ErrSyntax, + name: "invalid bare assignment", + args: []string{"pizzas", "=", "--wat"}, + expErr: argh.ScannerErrorList{ + &argh.ScannerError{Pos: argh.Position{Column: 8}, Msg: "invalid bare assignment"}, + }, expPT: []argh.Node{ - argh.Command{Name: "pizzas"}, + &argh.Command{ + Name: "pizzas", + Nodes: []argh.Node{ + &argh.ArgDelimiter{}, + &argh.ArgDelimiter{}, + &argh.Flag{Name: "wat"}, + }, + }, }, }, - {}, } { - if tc.skip { - continue - } - if tc.expPT != nil { t.Run(tc.name+" parse tree", func(ct *testing.T) { - actual, err := argh.ParseArgs(tc.args, tc.cfg) - if err != nil { - assert.ErrorIs(ct, err, tc.expErr) + if tc.skip { + ct.SkipNow() + return + } + + pt, err := argh.ParseArgs2(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 } - assert.Equal(ct, tc.expPT, actual.Nodes) + if !assert.Equal(ct, tc.expPT, pt.Nodes) { + spew.Dump(pt) + } }) } - /* - if tc.expAST != nil { - t.Run(tc.name+" ast", func(ct *testing.T) { - actual, err := argh.ParseArgs(tc.args, tc.cfg) - if err != nil { - assert.ErrorIs(ct, err, tc.expErr) - return + if tc.expAST != nil { + t.Run(tc.name+" ast", func(ct *testing.T) { + if tc.skip { + ct.SkipNow() + return + } + + pt, err := argh.ParseArgs2(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() - assert.Equal(ct, tc.expAST, argh.NewQuerier(actual.Nodes).AST()) - }) - } - */ + if !assert.Equal(ct, tc.expAST, ast) { + spew.Dump(ast) + } + }) + } } }