1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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
}
|