diff --git a/nvalue_string.go b/nvalue_string.go index f159a23..d9d1f06 100644 --- a/nvalue_string.go +++ b/nvalue_string.go @@ -8,18 +8,19 @@ 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] - _ = 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 { + i -= -2 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]] } diff --git a/parser.go b/parser.go index 87707b1..47c6fca 100644 --- a/parser.go +++ b/parser.go @@ -10,13 +10,13 @@ import ( ) const ( - ZeroValue NValue = iota - OneValue - OneOrMoreValue + OneOrMoreValue NValue = -2 + ZeroOrMoreValue NValue = -1 + ZeroValue NValue = 0 ) var ( - errSyntax = errors.New("syntax error") + ErrSyntax = errors.New("syntax error") DefaultParserConfig = &ParserConfig{ Commands: map[string]NValue{}, @@ -54,9 +54,13 @@ type ScanEntry struct { } type ParserConfig struct { - ProgValues NValue - Commands map[string]NValue - Flags map[string]NValue + ProgValues NValue + Commands map[string]NValue + Flags map[string]NValue + + OnUnknownFlag func(string) error + OnUnknownCommand func(string) error + ScannerConfig *ScannerConfig } @@ -97,7 +101,7 @@ func (p *Parser) Parse() (*Argh, error) { 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) + return false, errors.Wrapf(ErrSyntax, "illegal value %q at pos=%v", lit, pos) } if tok == EOL { @@ -127,6 +131,10 @@ func (p *Parser) nodify() (Node, error) { tracef("nodify tok=%s lit=%q pos=%v", tok, lit, pos) switch tok { + case ARG_DELIMITER: + return ArgDelimiter{}, nil + case ASSIGN: + return nil, errors.Wrapf(ErrSyntax, "bare assignment operator at pos=%v", pos) case IDENT: if len(p.nodes) == 0 { values, err := p.scanValues(lit, pos, p.cfg.ProgValues) @@ -147,8 +155,6 @@ func (p *Parser) nodify() (Node, error) { } return Ident{Literal: lit}, nil - case ARG_DELIMITER: - return ArgDelimiter{}, nil case COMPOUND_SHORT_FLAG: flagNodes := []Node{} @@ -222,7 +228,7 @@ func (p *Parser) scanValues(lit string, pos int, n NValue) ([]string, error) { for { lit, err := p.scanIdent() if err != nil { - if n == OneValue { + if n == NValue(1) { return nil, err } @@ -233,7 +239,7 @@ func (p *Parser) scanValues(lit string, pos int, n NValue) ([]string, error) { ret = append(ret, lit) - if n == OneValue && len(ret) == 1 { + if n == NValue(1) && len(ret) == 1 { break } } @@ -255,10 +261,16 @@ func (p *Parser) scanValues(lit string, pos int, n NValue) ([]string, error) { func (p *Parser) scanIdent() (string, error) { tok, lit, pos := p.scan() + tracef("scanIdent scanned tok=%s lit=%q pos=%v", tok, lit, pos) + unscanBuf := []ScanEntry{} 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() } @@ -267,13 +279,17 @@ func (p *Parser) scanIdent() (string, error) { 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 { 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) { diff --git a/parser_test.go b/parser_test.go index 6432105..db9bc03 100644 --- a/parser_test.go +++ b/parser_test.go @@ -35,7 +35,7 @@ func TestParser(t *testing.T) { name: "one positional arg", args: []string{"pizzas", "excel"}, cfg: &argh.ParserConfig{ - ProgValues: argh.OneValue, + ProgValues: 1, }, expPT: []argh.Node{ argh.Program{Name: "pizzas", Values: []string{"excel"}}, @@ -89,7 +89,7 @@ func TestParser(t *testing.T) { cfg: &argh.ParserConfig{ Commands: map[string]argh.NValue{}, Flags: map[string]argh.NValue{ - "fresh": argh.OneValue, + "fresh": 1, "box": argh.OneOrMoreValue, }, }, @@ -173,7 +173,7 @@ func TestParser(t *testing.T) { args: []string{"pizzas", "-a", "--ca", "-b", "1312", "-lol"}, cfg: &argh.ParserConfig{ Commands: map[string]argh.NValue{}, - Flags: map[string]argh.NValue{"b": argh.OneValue}, + Flags: map[string]argh.NValue{"b": 1}, }, expPT: []argh.Node{ argh.Program{Name: "pizzas"}, @@ -221,13 +221,13 @@ func TestParser(t *testing.T) { name: "total weirdo", args: []string{"PIZZAs", "^wAT@golf", "^^hecKing", "goose", "bonk", "^^FIERCENESS@-2"}, cfg: &argh.ParserConfig{ - Commands: map[string]argh.NValue{"goose": argh.OneValue}, + Commands: map[string]argh.NValue{"goose": 1}, Flags: map[string]argh.NValue{ - "w": argh.ZeroValue, - "A": argh.ZeroValue, - "T": argh.OneValue, - "hecking": argh.ZeroValue, - "FIERCENESS": argh.OneValue, + "w": 0, + "A": 0, + "T": 1, + "hecking": 0, + "FIERCENESS": 1, }, ScannerConfig: &argh.ScannerConfig{ AssignmentOperator: '@', @@ -253,6 +253,15 @@ func TestParser(t *testing.T) { 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 { continue diff --git a/scanner.go b/scanner.go index cc24842..27f01d0 100644 --- a/scanner.go +++ b/scanner.go @@ -167,6 +167,10 @@ func (s *Scanner) scanArg() (Token, string, int) { return STDIN_FLAG, str, pos } + if s.isAssignmentOperator(ch0) { + return ASSIGN, str, pos + } + return IDENT, str, pos }