Factor parsing out into a ParseContext type.
This is groundwork for supporting context-sensitive help and completion. Fixes #7.
This commit is contained in:
@@ -65,8 +65,11 @@ func (k *Kong) Parse(args []string) (command string, err error) {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
k.reset(k.Model)
|
k.reset(k.Model)
|
||||||
cmd, err := k.applyNode(Scan(args...), k.Model, nil)
|
ctx := &ParseContext{
|
||||||
return strings.Join(cmd, " "), err
|
Scan: Scan(args...),
|
||||||
|
}
|
||||||
|
err = ctx.applyNode(k.Model)
|
||||||
|
return strings.Join(ctx.Command, " "), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recursively reset values to defaults (as specified in the grammar) or the zero value.
|
// Recursively reset values to defaults (as specified in the grammar) or the zero value.
|
||||||
@@ -96,11 +99,17 @@ func (k *Kong) reset(node *Node) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *Kong) applyNode(scan *Scanner, node *Node, flags []*Flag) (command []string, err error) { // nolint: gocyclo
|
type ParseContext struct {
|
||||||
positional := 0
|
Scan *Scanner
|
||||||
flags = append(flags, node.Flags...) // Track all parent flags.
|
Command []string
|
||||||
|
Flags []*Flag
|
||||||
|
}
|
||||||
|
|
||||||
for token := scan.Pop(); token.Type != EOLToken; token = scan.Pop() {
|
func (p *ParseContext) applyNode(node *Node) (err error) { // nolint: gocyclo
|
||||||
|
positional := 0
|
||||||
|
p.Flags = append(p.Flags, node.Flags...)
|
||||||
|
|
||||||
|
for token := p.Scan.Pop(); token.Type != EOLToken; token = p.Scan.Pop() {
|
||||||
switch token.Type {
|
switch token.Type {
|
||||||
case UntypedToken:
|
case UntypedToken:
|
||||||
switch {
|
switch {
|
||||||
@@ -108,7 +117,7 @@ func (k *Kong) applyNode(scan *Scanner, node *Node, flags []*Flag) (command []st
|
|||||||
case token.Value == "--":
|
case token.Value == "--":
|
||||||
args := []string{}
|
args := []string{}
|
||||||
for {
|
for {
|
||||||
token = scan.Pop()
|
token = p.Scan.Pop()
|
||||||
if token.Type == EOLToken {
|
if token.Type == EOLToken {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -116,7 +125,7 @@ func (k *Kong) applyNode(scan *Scanner, node *Node, flags []*Flag) (command []st
|
|||||||
}
|
}
|
||||||
// Note: tokens must be pushed in reverse order.
|
// Note: tokens must be pushed in reverse order.
|
||||||
for i := range args {
|
for i := range args {
|
||||||
scan.PushTyped(args[len(args)-1-i], PositionalArgumentToken)
|
p.Scan.PushTyped(args[len(args)-1-i], PositionalArgumentToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Long flag.
|
// Long flag.
|
||||||
@@ -124,52 +133,52 @@ func (k *Kong) applyNode(scan *Scanner, node *Node, flags []*Flag) (command []st
|
|||||||
// Parse it and push the tokens.
|
// Parse it and push the tokens.
|
||||||
parts := strings.SplitN(token.Value[2:], "=", 2)
|
parts := strings.SplitN(token.Value[2:], "=", 2)
|
||||||
if len(parts) > 1 {
|
if len(parts) > 1 {
|
||||||
scan.PushTyped(parts[1], FlagValueToken)
|
p.Scan.PushTyped(parts[1], FlagValueToken)
|
||||||
}
|
}
|
||||||
scan.PushTyped(parts[0], FlagToken)
|
p.Scan.PushTyped(parts[0], FlagToken)
|
||||||
|
|
||||||
// Short flag.
|
// Short flag.
|
||||||
case strings.HasPrefix(token.Value, "-"):
|
case strings.HasPrefix(token.Value, "-"):
|
||||||
// Note: tokens must be pushed in reverse order.
|
// Note: tokens must be pushed in reverse order.
|
||||||
scan.PushTyped(token.Value[2:], ShortFlagTailToken)
|
p.Scan.PushTyped(token.Value[2:], ShortFlagTailToken)
|
||||||
scan.PushTyped(token.Value[1:2], ShortFlagToken)
|
p.Scan.PushTyped(token.Value[1:2], ShortFlagToken)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
scan.PushTyped(token.Value, PositionalArgumentToken)
|
p.Scan.PushTyped(token.Value, PositionalArgumentToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
case ShortFlagTailToken:
|
case ShortFlagTailToken:
|
||||||
// Note: tokens must be pushed in reverse order.
|
// Note: tokens must be pushed in reverse order.
|
||||||
scan.PushTyped(token.Value[1:], ShortFlagTailToken)
|
p.Scan.PushTyped(token.Value[1:], ShortFlagTailToken)
|
||||||
scan.PushTyped(token.Value[0:1], ShortFlagToken)
|
p.Scan.PushTyped(token.Value[0:1], ShortFlagToken)
|
||||||
|
|
||||||
case FlagToken:
|
case FlagToken:
|
||||||
if err := matchFlags(flags, token, scan, func(f *Flag) bool {
|
if err := matchFlags(p.Flags, token, p.Scan, func(f *Flag) bool {
|
||||||
return f.Name == token.Value
|
return f.Name == token.Value
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case ShortFlagToken:
|
case ShortFlagToken:
|
||||||
if err := matchFlags(flags, token, scan, func(f *Flag) bool {
|
if err := matchFlags(p.Flags, token, p.Scan, func(f *Flag) bool {
|
||||||
return string(f.Name) == token.Value
|
return string(f.Name) == token.Value
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
case FlagValueToken:
|
case FlagValueToken:
|
||||||
return nil, fmt.Errorf("unexpected flag argument %q", token.Value)
|
return fmt.Errorf("unexpected flag argument %q", token.Value)
|
||||||
|
|
||||||
case PositionalArgumentToken:
|
case PositionalArgumentToken:
|
||||||
scan.PushToken(token)
|
p.Scan.PushToken(token)
|
||||||
// Ensure we've consumed all positional arguments.
|
// Ensure we've consumed all positional arguments.
|
||||||
if positional < len(node.Positional) {
|
if positional < len(node.Positional) {
|
||||||
arg := node.Positional[positional]
|
arg := node.Positional[positional]
|
||||||
err := arg.Decode(scan)
|
err := arg.Decode(p.Scan)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
command = append(command, "<"+arg.Name+">")
|
p.Command = append(p.Command, "<"+arg.Name+">")
|
||||||
positional++
|
positional++
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -179,47 +188,39 @@ func (k *Kong) applyNode(scan *Scanner, node *Node, flags []*Flag) (command []st
|
|||||||
switch {
|
switch {
|
||||||
case branch.Command != nil:
|
case branch.Command != nil:
|
||||||
if branch.Command.Name == token.Value {
|
if branch.Command.Name == token.Value {
|
||||||
scan.Pop()
|
p.Scan.Pop()
|
||||||
command = append(command, branch.Command.Name)
|
p.Command = append(p.Command, branch.Command.Name)
|
||||||
cmd, err := k.applyNode(scan, branch.Command, flags)
|
return p.applyNode(branch.Command)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return append(command, cmd...), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
case branch.Argument != nil:
|
case branch.Argument != nil:
|
||||||
arg := branch.Argument.Argument
|
arg := branch.Argument.Argument
|
||||||
if err := arg.Decode(scan); err == nil {
|
if err := arg.Decode(p.Scan); err == nil {
|
||||||
command = append(command, "<"+arg.Name+">")
|
p.Command = append(p.Command, "<"+arg.Name+">")
|
||||||
cmd, err := k.applyNode(scan, &branch.Argument.Node, flags)
|
return p.applyNode(&branch.Argument.Node)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return append(command, cmd...), nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unexpected positional argument %s", token)
|
return fmt.Errorf("unexpected positional argument %s", token)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unexpected token %s", token)
|
return fmt.Errorf("unexpected token %s", token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkMissingPositionals(positional, node.Positional); err != nil {
|
if err := checkMissingPositionals(positional, node.Positional); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkMissingChildren(node.Children); err != nil {
|
if err := checkMissingChildren(node.Children); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkMissingFlags(node.Children, flags); err != nil {
|
if err := checkMissingFlags(node.Children, p.Flags); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkMissingFlags(children []*Branch, flags []*Flag) error {
|
func checkMissingFlags(children []*Branch, flags []*Flag) error {
|
||||||
|
|||||||
Reference in New Issue
Block a user