Compare commits

...

2 Commits

Author SHA1 Message Date
cc29386cee Use parser2 in querier tests 2022-05-27 08:22:07 -04:00
95453bf197 Retain literals that are values 2022-05-27 08:11:31 -04:00
4 changed files with 258 additions and 45 deletions

View File

@@ -55,6 +55,8 @@ func (p *parser2) parseArgs() (*ParseTree, error) {
tracef("parseArgs() parsing %q as program command; cfg=%+#v", p.lit, p.cfg.Prog) tracef("parseArgs() parsing %q as program command; cfg=%+#v", p.lit, p.cfg.Prog)
prog := p.parseCommand(&p.cfg.Prog) prog := p.parseCommand(&p.cfg.Prog)
tracef("parseArgs() top level node is %T", prog)
nodes := []Node{prog} nodes := []Node{prog}
if v := p.parsePassthrough(); v != nil { if v := p.parsePassthrough(); v != nil {
tracef("parseArgs() appending passthrough argument %v", v) tracef("parseArgs() appending passthrough argument %v", v)
@@ -129,18 +131,14 @@ func (p *parser2) parseCommand(cCfg *CommandConfig) Node {
tracef("parseCommand(...) setting name=%s from repeating value name", name) tracef("parseCommand(...) setting name=%s from repeating value name", name)
} }
if node.Values == nil { values[name] = p.lit
node.Values = map[string]string{}
} }
node.Values[name] = p.lit
} else {
if p.tok == STDIN_FLAG { if p.tok == STDIN_FLAG {
nodes = append(nodes, &StdinFlag{}) nodes = append(nodes, &StdinFlag{})
} else { } else {
nodes = append(nodes, &Ident{Literal: p.lit}) nodes = append(nodes, &Ident{Literal: p.lit})
} }
}
identIndex++ identIndex++
case LONG_FLAG, SHORT_FLAG, COMPOUND_SHORT_FLAG: case LONG_FLAG, SHORT_FLAG, COMPOUND_SHORT_FLAG:
@@ -266,6 +264,12 @@ func (p *parser2) parseConfiguredFlag(node *Flag, flCfg FlagConfig) Node {
values[name] = p.lit values[name] = p.lit
if p.tok == STDIN_FLAG {
nodes = append(nodes, &StdinFlag{})
} else {
nodes = append(nodes, &Ident{Literal: p.lit})
}
identIndex++ identIndex++
default: default:
tracef("parseLongFlag(...) breaking on %s %q %v; setting buffered=true", p.tok, p.lit, p.pos) tracef("parseLongFlag(...) breaking on %s %q %v; setting buffered=true", p.tok, p.lit, p.pos)

View File

@@ -55,6 +55,7 @@ func TestParser2(t *testing.T) {
}, },
Nodes: []argh.Node{ Nodes: []argh.Node{
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
&argh.Ident{Literal: "mario"},
}, },
}, },
}, },
@@ -73,6 +74,9 @@ func TestParser2(t *testing.T) {
Values: map[string]string{ Values: map[string]string{
"name": "mario", "name": "mario",
}, },
Nodes: []argh.Node{
&argh.Ident{Literal: "mario"},
},
}, },
}, },
}, },
@@ -104,6 +108,7 @@ func TestParser2(t *testing.T) {
Values: map[string]string{"0": "excel"}, Values: map[string]string{"0": "excel"},
Nodes: []argh.Node{ Nodes: []argh.Node{
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
&argh.Ident{Literal: "excel"},
}, },
}, },
}, },
@@ -111,6 +116,9 @@ func TestParser2(t *testing.T) {
&argh.Command{ &argh.Command{
Name: "pizzas", Name: "pizzas",
Values: map[string]string{"0": "excel"}, Values: map[string]string{"0": "excel"},
Nodes: []argh.Node{
&argh.Ident{Literal: "excel"},
},
}, },
}, },
}, },
@@ -134,9 +142,13 @@ func TestParser2(t *testing.T) {
}, },
Nodes: []argh.Node{ Nodes: []argh.Node{
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
&argh.Ident{Literal: "excel"},
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
&argh.Ident{Literal: "wildly"},
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
&argh.Ident{Literal: "when"},
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
&argh.Ident{Literal: "feral"},
}, },
}, },
}, },
@@ -149,6 +161,12 @@ func TestParser2(t *testing.T) {
"word.2": "when", "word.2": "when",
"word.3": "feral", "word.3": "feral",
}, },
Nodes: []argh.Node{
&argh.Ident{Literal: "excel"},
&argh.Ident{Literal: "wildly"},
&argh.Ident{Literal: "when"},
&argh.Ident{Literal: "feral"},
},
}, },
}, },
}, },
@@ -210,6 +228,7 @@ func TestParser2(t *testing.T) {
Values: map[string]string{"0": "soon"}, Values: map[string]string{"0": "soon"},
Nodes: []argh.Node{ Nodes: []argh.Node{
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
&argh.Ident{Literal: "soon"},
}, },
}, },
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
@@ -220,8 +239,11 @@ func TestParser2(t *testing.T) {
Values: map[string]string{"0": "square", "1": "shaped", "2": "hot"}, Values: map[string]string{"0": "square", "1": "shaped", "2": "hot"},
Nodes: []argh.Node{ Nodes: []argh.Node{
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
&argh.Ident{Literal: "square"},
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
&argh.Ident{Literal: "shaped"},
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
&argh.Ident{Literal: "hot"},
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
}, },
}, },
@@ -234,9 +256,23 @@ func TestParser2(t *testing.T) {
Name: "pizzas", Name: "pizzas",
Nodes: []argh.Node{ Nodes: []argh.Node{
&argh.Flag{Name: "tasty"}, &argh.Flag{Name: "tasty"},
&argh.Flag{Name: "fresh", Values: map[string]string{"0": "soon"}}, &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: "super-hot-right-now"},
&argh.Flag{Name: "box", Values: map[string]string{"0": "square", "1": "shaped", "2": "hot"}}, &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"}, &argh.Flag{Name: "please"},
}, },
}, },
@@ -336,6 +372,7 @@ func TestParser2(t *testing.T) {
Values: map[string]string{"0": "1312"}, Values: map[string]string{"0": "1312"},
Nodes: []argh.Node{ Nodes: []argh.Node{
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
&argh.Ident{Literal: "1312"},
}, },
}, },
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
@@ -355,7 +392,13 @@ func TestParser2(t *testing.T) {
Nodes: []argh.Node{ Nodes: []argh.Node{
&argh.Flag{Name: "a"}, &argh.Flag{Name: "a"},
&argh.Flag{Name: "ca"}, &argh.Flag{Name: "ca"},
&argh.Flag{Name: "b", Values: map[string]string{"0": "1312"}}, &argh.Flag{
Name: "b",
Values: map[string]string{"0": "1312"},
Nodes: []argh.Node{
&argh.Ident{Literal: "1312"},
},
},
&argh.Flag{Name: "l"}, &argh.Flag{Name: "l"},
&argh.Flag{Name: "o"}, &argh.Flag{Name: "o"},
&argh.Flag{Name: "l"}, &argh.Flag{Name: "l"},
@@ -411,6 +454,131 @@ func TestParser2(t *testing.T) {
}, },
}, },
}, },
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", name: "command specific flags",
@@ -463,6 +631,7 @@ func TestParser2(t *testing.T) {
Values: map[string]string{"0": "hugs"}, Values: map[string]string{"0": "hugs"},
Nodes: []argh.Node{ Nodes: []argh.Node{
&argh.ArgDelimiter{}, &argh.ArgDelimiter{},
&argh.Ident{Literal: "hugs"},
}, },
}, },
}, },
@@ -474,6 +643,34 @@ func TestParser2(t *testing.T) {
}, },
}, },
}, },
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"},
},
},
},
},
},
},
},
},
},
}, },
{ {
skip: true, skip: true,
@@ -500,30 +697,42 @@ func TestParser2(t *testing.T) {
}, },
}, },
expPT: []argh.Node{ expPT: []argh.Node{
argh.Command{Name: "PIZZAs"}, &argh.Command{
argh.ArgDelimiter{}, Name: "PIZZAs",
argh.CompoundShortFlag{
Nodes: []argh.Node{ Nodes: []argh.Node{
argh.Flag{Name: "w"}, &argh.ArgDelimiter{},
argh.Flag{Name: "A"}, &argh.CompoundShortFlag{
argh.Flag{Name: "T", Values: map[string]string{"0": "golf"}}, Nodes: []argh.Node{
&argh.Flag{Name: "w"},
&argh.Flag{Name: "A"},
&argh.Flag{Name: "T", Values: map[string]string{"0": "golf"}},
},
},
&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"}},
}, },
}, },
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"}},
}, },
}, },
{ {
skip: true, skip: true,
name: "invalid bare assignment", name: "invalid bare assignment",
args: []string{"pizzas", "=", "--wat"}, args: []string{"pizzas", "=", "--wat"},
expErr: argh.ErrSyntax, expErr: argh.ErrSyntax,
expPT: []argh.Node{ expPT: []argh.Node{
argh.Command{Name: "pizzas"}, &argh.Command{
Name: "pizzas",
Nodes: []argh.Node{
&argh.ArgDelimiter{},
&argh.ArgDelimiter{},
&argh.Flag{Name: "wat"},
},
},
}, },
}, },
} { } {
@@ -535,7 +744,7 @@ func TestParser2(t *testing.T) {
} }
pt, err := argh.ParseArgs2(tc.args, tc.cfg) pt, err := argh.ParseArgs2(tc.args, tc.cfg)
if err != nil { if err != nil || tc.expErr != nil {
assert.ErrorIs(ct, err, tc.expErr) assert.ErrorIs(ct, err, tc.expErr)
return return
} }
@@ -554,8 +763,8 @@ func TestParser2(t *testing.T) {
} }
pt, err := argh.ParseArgs2(tc.args, tc.cfg) pt, err := argh.ParseArgs2(tc.args, tc.cfg)
if err != nil { if err != nil || tc.expErr != nil {
ct.Logf("err=%+#v", err) assert.ErrorIs(ct, err, tc.expErr)
return return
} }

View File

@@ -1,7 +1,7 @@
package argh package argh
type Querier interface { type Querier interface {
Program() (Command, bool) Program() (*Command, bool)
AST() []Node AST() []Node
} }
@@ -13,12 +13,19 @@ type defaultQuerier struct {
nodes []Node nodes []Node
} }
func (dq *defaultQuerier) Program() (Command, bool) { func (dq *defaultQuerier) Program() (*Command, bool) {
if len(dq.nodes) == 0 { if len(dq.nodes) == 0 {
return Command{}, false 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
} }
v, ok := dq.nodes[0].(Command)
return v, ok return v, ok
} }

View File

@@ -12,41 +12,34 @@ func TestQuerier_Program(t *testing.T) {
name string name string
args []string args []string
cfg *argh.ParserConfig cfg *argh.ParserConfig
exp argh.Command exp string
expOK bool expOK bool
}{ }{
{ {
name: "typical", name: "typical",
args: []string{"pizzas", "ahoy", "--treatsa", "fun"}, args: []string{"pizzas", "ahoy", "--treatsa", "fun"},
exp: argh.Command{Name: "pizzas"}, exp: "pizzas",
expOK: true, expOK: true,
}, },
{ {
name: "minimal", name: "minimal",
args: []string{"pizzas"}, args: []string{"pizzas"},
exp: argh.Command{Name: "pizzas"}, exp: "pizzas",
expOK: true, expOK: true,
}, },
{ {
name: "invalid", name: "invalid",
args: []string{}, args: []string{},
exp: argh.Command{},
expOK: false,
},
{
name: "invalid flag only",
args: []string{"--oh-no"},
exp: argh.Command{},
expOK: false, expOK: false,
}, },
} { } {
t.Run(tc.name, func(ct *testing.T) { t.Run(tc.name, func(ct *testing.T) {
pt, err := argh.ParseArgs(tc.args, tc.cfg) pt, err := argh.ParseArgs2(tc.args, tc.cfg)
require.Nil(ct, err) require.Nil(ct, err)
prog, ok := argh.NewQuerier(pt.Nodes).Program() prog, ok := argh.NewQuerier(pt.Nodes).Program()
require.Equal(ct, tc.exp, prog)
require.Equal(ct, tc.expOK, ok) require.Equal(ct, tc.expOK, ok)
require.Equal(ct, tc.exp, prog.Name)
}) })
} }
} }