Source file
src/go/ast/directive.go
1
2
3
4
5 package ast
6
7 import (
8 "fmt"
9 "go/token"
10 "strconv"
11 "strings"
12 "unicode"
13 "unicode/utf8"
14 )
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 type Directive struct {
33 Tool string
34 Name string
35 Args string
36
37
38 Slash token.Pos
39
40
41
42 ArgsPos token.Pos
43 }
44
45
46
47
48
49
50
51
52
53
54
55
56 func ParseDirective(pos token.Pos, c string) (Directive, bool) {
57
58
59 if !(len(c) >= 3 && c[0] == '/' && c[1] == '/' && isalnum(c[2])) {
60 return Directive{}, false
61 }
62
63 buf := directiveScanner{c, pos}
64 buf.skip(len("//"))
65
66
67
68
69
70 colon := strings.Index(buf.str, ":")
71 if colon <= 0 || colon+1 >= len(buf.str) {
72 return Directive{}, false
73 }
74 for i := 0; i <= colon+1; i++ {
75 if i == colon {
76 continue
77 }
78 if !isalnum(buf.str[i]) {
79 return Directive{}, false
80 }
81 }
82 tool := buf.take(colon)
83 buf.skip(len(":"))
84
85
86 name := buf.takeNonSpace()
87 buf.skipSpace()
88 argsPos := buf.pos
89 args := strings.TrimRightFunc(buf.str, unicode.IsSpace)
90
91 return Directive{tool, name, args, pos, argsPos}, true
92 }
93
94 func isalnum(b byte) bool {
95 return 'a' <= b && b <= 'z' || '0' <= b && b <= '9'
96 }
97
98 func (d *Directive) Pos() token.Pos { return d.Slash }
99 func (d *Directive) End() token.Pos { return token.Pos(int(d.ArgsPos) + len(d.Args)) }
100
101
102 type DirectiveArg struct {
103
104
105 Arg string
106
107 Pos token.Pos
108 }
109
110
111
112
113
114
115
116 func (d *Directive) ParseArgs() ([]DirectiveArg, error) {
117 args := directiveScanner{d.Args, d.ArgsPos}
118
119 list := []DirectiveArg{}
120 for args.skipSpace(); args.str != ""; args.skipSpace() {
121 var arg string
122 argPos := args.pos
123
124 switch args.str[0] {
125 default:
126 arg = args.takeNonSpace()
127
128 case '`', '"':
129 q, err := strconv.QuotedPrefix(args.str)
130 if err != nil {
131 return nil, fmt.Errorf("invalid quoted string in //%s:%s: %s", d.Tool, d.Name, args.str)
132 }
133
134 arg, _ = strconv.Unquote(args.take(len(q)))
135
136
137 if args.str != "" {
138 r, _ := utf8.DecodeRuneInString(args.str)
139 if !unicode.IsSpace(r) {
140 return nil, fmt.Errorf("invalid quoted string in //%s:%s: %s", d.Tool, d.Name, args.str)
141 }
142 }
143 }
144
145 list = append(list, DirectiveArg{arg, argPos})
146 }
147 return list, nil
148 }
149
150
151
152 type directiveScanner struct {
153 str string
154 pos token.Pos
155 }
156
157 func (s *directiveScanner) skip(n int) {
158 s.pos += token.Pos(n)
159 s.str = s.str[n:]
160 }
161
162 func (s *directiveScanner) take(n int) string {
163 res := s.str[:n]
164 s.skip(n)
165 return res
166 }
167
168 func (s *directiveScanner) takeNonSpace() string {
169 i := strings.IndexFunc(s.str, unicode.IsSpace)
170 if i == -1 {
171 i = len(s.str)
172 }
173 return s.take(i)
174 }
175
176 func (s *directiveScanner) skipSpace() {
177 trim := strings.TrimLeftFunc(s.str, unicode.IsSpace)
178 s.skip(len(s.str) - len(trim))
179 }
180
View as plain text