Handle bare assignments as syntax error + NValue rework

urfave-cli-integration
Dan Buch 2 years ago
parent a34fb7a9cd
commit 29de5e3867

@ -8,18 +8,19 @@ func _() {
// An "invalid array index" compiler error signifies that the constant values have changed. // An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again. // Re-run the stringer command to generate them again.
var x [1]struct{} var x [1]struct{}
_ = x[OneOrMoreValue - -2]
_ = x[ZeroOrMoreValue - -1]
_ = x[ZeroValue-0] _ = x[ZeroValue-0]
_ = x[OneValue-1]
_ = x[OneOrMoreValue-2]
} }
const _NValue_name = "ZeroValueOneValueOneOrMoreValue" const _NValue_name = "OneOrMoreValueZeroOrMoreValueZeroValue"
var _NValue_index = [...]uint8{0, 9, 17, 31} var _NValue_index = [...]uint8{0, 14, 29, 38}
func (i NValue) String() string { func (i NValue) String() string {
i -= -2
if i < 0 || i >= NValue(len(_NValue_index)-1) { if i < 0 || i >= NValue(len(_NValue_index)-1) {
return "NValue(" + strconv.FormatInt(int64(i), 10) + ")" return "NValue(" + strconv.FormatInt(int64(i+-2), 10) + ")"
} }
return _NValue_name[_NValue_index[i]:_NValue_index[i+1]] return _NValue_name[_NValue_index[i]:_NValue_index[i+1]]
} }

@ -10,13 +10,13 @@ import (
) )
const ( const (
ZeroValue NValue = iota OneOrMoreValue NValue = -2
OneValue ZeroOrMoreValue NValue = -1
OneOrMoreValue ZeroValue NValue = 0
) )
var ( var (
errSyntax = errors.New("syntax error") ErrSyntax = errors.New("syntax error")
DefaultParserConfig = &ParserConfig{ DefaultParserConfig = &ParserConfig{
Commands: map[string]NValue{}, Commands: map[string]NValue{},
@ -54,9 +54,13 @@ type ScanEntry struct {
} }
type ParserConfig struct { type ParserConfig struct {
ProgValues NValue ProgValues NValue
Commands map[string]NValue Commands map[string]NValue
Flags map[string]NValue Flags map[string]NValue
OnUnknownFlag func(string) error
OnUnknownCommand func(string) error
ScannerConfig *ScannerConfig ScannerConfig *ScannerConfig
} }
@ -97,7 +101,7 @@ func (p *Parser) Parse() (*Argh, error) {
func (p *Parser) parseArg() (bool, error) { func (p *Parser) parseArg() (bool, error) {
tok, lit, pos := p.scan() tok, lit, pos := p.scan()
if tok == ILLEGAL { if tok == ILLEGAL {
return false, errors.Wrapf(errSyntax, "illegal value %q at pos=%v", lit, pos) return false, errors.Wrapf(ErrSyntax, "illegal value %q at pos=%v", lit, pos)
} }
if tok == EOL { if tok == EOL {
@ -127,6 +131,10 @@ func (p *Parser) nodify() (Node, error) {
tracef("nodify tok=%s lit=%q pos=%v", tok, lit, pos) tracef("nodify tok=%s lit=%q pos=%v", tok, lit, pos)
switch tok { switch tok {
case ARG_DELIMITER:
return ArgDelimiter{}, nil
case ASSIGN:
return nil, errors.Wrapf(ErrSyntax, "bare assignment operator at pos=%v", pos)
case IDENT: case IDENT:
if len(p.nodes) == 0 { if len(p.nodes) == 0 {
values, err := p.scanValues(lit, pos, p.cfg.ProgValues) values, err := p.scanValues(lit, pos, p.cfg.ProgValues)
@ -147,8 +155,6 @@ func (p *Parser) nodify() (Node, error) {
} }
return Ident{Literal: lit}, nil return Ident{Literal: lit}, nil
case ARG_DELIMITER:
return ArgDelimiter{}, nil
case COMPOUND_SHORT_FLAG: case COMPOUND_SHORT_FLAG:
flagNodes := []Node{} flagNodes := []Node{}
@ -222,7 +228,7 @@ func (p *Parser) scanValues(lit string, pos int, n NValue) ([]string, error) {
for { for {
lit, err := p.scanIdent() lit, err := p.scanIdent()
if err != nil { if err != nil {
if n == OneValue { if n == NValue(1) {
return nil, err return nil, err
} }
@ -233,7 +239,7 @@ func (p *Parser) scanValues(lit string, pos int, n NValue) ([]string, error) {
ret = append(ret, lit) ret = append(ret, lit)
if n == OneValue && len(ret) == 1 { if n == NValue(1) && len(ret) == 1 {
break break
} }
} }
@ -255,10 +261,16 @@ func (p *Parser) scanValues(lit string, pos int, n NValue) ([]string, error) {
func (p *Parser) scanIdent() (string, error) { func (p *Parser) scanIdent() (string, error) {
tok, lit, pos := p.scan() tok, lit, pos := p.scan()
tracef("scanIdent scanned tok=%s lit=%q pos=%v", tok, lit, pos)
unscanBuf := []ScanEntry{} unscanBuf := []ScanEntry{}
if tok == ASSIGN || tok == ARG_DELIMITER { if tok == ASSIGN || tok == ARG_DELIMITER {
unscanBuf = append([]ScanEntry{{tok: tok, lit: lit, pos: pos}}, unscanBuf...) entry := ScanEntry{tok: tok, lit: lit, pos: pos}
tracef("scanIdent tok=%s; scanning next and pushing to unscan buffer entry=%+#v", tok, entry)
unscanBuf = append([]ScanEntry{entry}, unscanBuf...)
tok, lit, pos = p.scan() tok, lit, pos = p.scan()
} }
@ -267,13 +279,17 @@ func (p *Parser) scanIdent() (string, error) {
return lit, nil return lit, nil
} }
unscanBuf = append([]ScanEntry{{tok: tok, lit: lit, pos: pos}}, unscanBuf...) entry := ScanEntry{tok: tok, lit: lit, pos: pos}
tracef("scanIdent tok=%s; unscanning entry=%+#v", tok, entry)
unscanBuf = append([]ScanEntry{entry}, unscanBuf...)
for _, entry := range unscanBuf { for _, entry := range unscanBuf {
p.unscan(entry.tok, entry.lit, entry.pos) p.unscan(entry.tok, entry.lit, entry.pos)
} }
return "", errors.Wrapf(errSyntax, "expected ident at pos=%v but got %s (%q)", pos, tok, lit) return "", errors.Wrapf(ErrSyntax, "expected ident at pos=%v but got %s (%q)", pos, tok, lit)
} }
func (p *Parser) scan() (Token, string, int) { func (p *Parser) scan() (Token, string, int) {

@ -35,7 +35,7 @@ func TestParser(t *testing.T) {
name: "one positional arg", name: "one positional arg",
args: []string{"pizzas", "excel"}, args: []string{"pizzas", "excel"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
ProgValues: argh.OneValue, ProgValues: 1,
}, },
expPT: []argh.Node{ expPT: []argh.Node{
argh.Program{Name: "pizzas", Values: []string{"excel"}}, argh.Program{Name: "pizzas", Values: []string{"excel"}},
@ -89,7 +89,7 @@ func TestParser(t *testing.T) {
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Commands: map[string]argh.NValue{}, Commands: map[string]argh.NValue{},
Flags: map[string]argh.NValue{ Flags: map[string]argh.NValue{
"fresh": argh.OneValue, "fresh": 1,
"box": argh.OneOrMoreValue, "box": argh.OneOrMoreValue,
}, },
}, },
@ -173,7 +173,7 @@ func TestParser(t *testing.T) {
args: []string{"pizzas", "-a", "--ca", "-b", "1312", "-lol"}, args: []string{"pizzas", "-a", "--ca", "-b", "1312", "-lol"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Commands: map[string]argh.NValue{}, Commands: map[string]argh.NValue{},
Flags: map[string]argh.NValue{"b": argh.OneValue}, Flags: map[string]argh.NValue{"b": 1},
}, },
expPT: []argh.Node{ expPT: []argh.Node{
argh.Program{Name: "pizzas"}, argh.Program{Name: "pizzas"},
@ -221,13 +221,13 @@ func TestParser(t *testing.T) {
name: "total weirdo", name: "total weirdo",
args: []string{"PIZZAs", "^wAT@golf", "^^hecKing", "goose", "bonk", "^^FIERCENESS@-2"}, args: []string{"PIZZAs", "^wAT@golf", "^^hecKing", "goose", "bonk", "^^FIERCENESS@-2"},
cfg: &argh.ParserConfig{ cfg: &argh.ParserConfig{
Commands: map[string]argh.NValue{"goose": argh.OneValue}, Commands: map[string]argh.NValue{"goose": 1},
Flags: map[string]argh.NValue{ Flags: map[string]argh.NValue{
"w": argh.ZeroValue, "w": 0,
"A": argh.ZeroValue, "A": 0,
"T": argh.OneValue, "T": 1,
"hecking": argh.ZeroValue, "hecking": 0,
"FIERCENESS": argh.OneValue, "FIERCENESS": 1,
}, },
ScannerConfig: &argh.ScannerConfig{ ScannerConfig: &argh.ScannerConfig{
AssignmentOperator: '@', AssignmentOperator: '@',
@ -253,6 +253,15 @@ func TestParser(t *testing.T) {
argh.Flag{Name: "FIERCENESS", Values: []string{"-2"}}, argh.Flag{Name: "FIERCENESS", Values: []string{"-2"}},
}, },
}, },
{
name: "invalid bare assignment",
args: []string{"pizzas", "=", "--wat"},
expErr: argh.ErrSyntax,
expPT: []argh.Node{
argh.Program{Name: "pizzas"},
},
},
{},
} { } {
if tc.skip { if tc.skip {
continue continue

@ -167,6 +167,10 @@ func (s *Scanner) scanArg() (Token, string, int) {
return STDIN_FLAG, str, pos return STDIN_FLAG, str, pos
} }
if s.isAssignmentOperator(ch0) {
return ASSIGN, str, pos
}
return IDENT, str, pos return IDENT, str, pos
} }

Loading…
Cancel
Save