aboutsummaryrefslogtreecommitdiffstats
path: root/commands/parser.go
diff options
context:
space:
mode:
Diffstat (limited to 'commands/parser.go')
-rw-r--r--commands/parser.go134
1 files changed, 134 insertions, 0 deletions
diff --git a/commands/parser.go b/commands/parser.go
new file mode 100644
index 00000000..e8146506
--- /dev/null
+++ b/commands/parser.go
@@ -0,0 +1,134 @@
+package commands
+
+import (
+ "strings"
+)
+
+type completionType int
+
+const (
+ NONE completionType = iota
+ COMMAND
+ OPERAND
+ SHORT_OPTION
+ OPTION_ARGUMENT
+)
+
+type parser struct {
+ tokens []string
+ optind int
+ spec string
+ space bool
+ kind completionType
+ flag string
+ arg string
+ err error
+}
+
+func newParser(cmd, spec string, spaceTerminated bool) (*parser, error) {
+ args, err := splitCmd(cmd)
+ if err != nil {
+ return nil, err
+ }
+
+ p := &parser{
+ tokens: args,
+ optind: 0,
+ spec: spec,
+ space: spaceTerminated,
+ kind: NONE,
+ flag: "",
+ arg: "",
+ err: nil,
+ }
+
+ state := command
+ for state != nil {
+ state = state(p)
+ }
+
+ return p, p.err
+}
+
+func (p *parser) empty() bool {
+ return len(p.tokens) == 0
+}
+
+func (p *parser) peek() string {
+ return p.tokens[0]
+}
+
+func (p *parser) advance() string {
+ if p.empty() {
+ return ""
+ }
+ tok := p.tokens[0]
+ p.tokens = p.tokens[1:]
+ p.optind++
+ return tok
+}
+
+func (p *parser) set(t completionType) {
+ p.kind = t
+}
+
+func (p *parser) hasArgument() bool {
+ n := len(p.flag)
+ if n > 0 {
+ s := string(p.flag[n-1]) + ":"
+ return strings.Contains(p.spec, s)
+ }
+ return false
+}
+
+type stateFn func(*parser) stateFn
+
+func command(p *parser) stateFn {
+ p.set(COMMAND)
+ p.advance()
+ return peek(p)
+}
+
+func peek(p *parser) stateFn {
+ if p.empty() {
+ if p.space {
+ return operand
+ }
+ return nil
+ }
+ if p.spec == "" {
+ return operand
+ }
+ s := p.peek()
+ switch {
+ case s == "--":
+ p.advance()
+ case strings.HasPrefix(s, "-"):
+ return short_option
+ }
+ return operand
+}
+
+func short_option(p *parser) stateFn {
+ p.set(SHORT_OPTION)
+ tok := p.advance()
+ p.flag = tok[1:]
+ if p.hasArgument() {
+ return option_argument
+ }
+ return peek(p)
+}
+
+func option_argument(p *parser) stateFn {
+ p.set(OPTION_ARGUMENT)
+ p.arg = p.advance()
+ if p.empty() && len(p.arg) == 0 {
+ return nil
+ }
+ return peek(p)
+}
+
+func operand(p *parser) stateFn {
+ p.set(OPERAND)
+ return nil
+}