1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package internal
16
17 import (
18 "fmt"
19 "strconv"
20 "strings"
21
22 "cuelang.org/go/cue/errors"
23 "cuelang.org/go/cue/literal"
24 "cuelang.org/go/cue/scanner"
25 "cuelang.org/go/cue/token"
26 )
27
28
29 type AttrKind uint8
30
31 const (
32
33
34 FieldAttr AttrKind = 1 << iota
35
36
37
38
39
40 DeclAttr
41
42
43
44
45
46 )
47
48
49 type Attr struct {
50 Name string
51 Body string
52 Kind AttrKind
53 Fields []KeyValue
54 Err errors.Error
55 }
56
57
58 func NewNonExisting(key string) Attr {
59 const msgNotExist = "attribute %q does not exist"
60 return Attr{Err: errors.Newf(token.NoPos, msgNotExist, key)}
61 }
62
63 type KeyValue struct {
64 key string
65 value string
66 text string
67 }
68
69 func (kv *KeyValue) Text() string {
70 return kv.text
71 }
72
73 func (kv *KeyValue) Key() string {
74 return kv.key
75 }
76
77 func (kv *KeyValue) Value() string {
78 return kv.value
79 }
80
81 func (a *Attr) hasPos(p int) error {
82 if a.Err != nil {
83 return a.Err
84 }
85 if p >= len(a.Fields) {
86 return fmt.Errorf("field does not exist")
87 }
88 return nil
89 }
90
91
92
93 func (a *Attr) String(pos int) (string, error) {
94 if err := a.hasPos(pos); err != nil {
95 return "", err
96 }
97 f := a.Fields[pos]
98 if f.key != "" {
99
100 return f.Text(), nil
101 }
102 return a.Fields[pos].Value(), nil
103 }
104
105
106
107
108 func (a *Attr) Int(pos int) (int64, error) {
109 if err := a.hasPos(pos); err != nil {
110 return 0, err
111 }
112
113
114 return strconv.ParseInt(a.Fields[pos].Text(), 10, 64)
115 }
116
117
118
119
120 func (a *Attr) Flag(pos int, key string) (bool, error) {
121 if err := a.hasPos(pos - 1); err != nil {
122 return false, err
123 }
124 for _, kv := range a.Fields[pos:] {
125 if kv.Key() == "" && kv.Value() == key {
126 return true, nil
127 }
128 }
129 return false, nil
130 }
131
132
133
134
135 func (a *Attr) Lookup(pos int, key string) (val string, found bool, err error) {
136 if err := a.hasPos(pos - 1); err != nil {
137 return "", false, err
138 }
139 for _, kv := range a.Fields[pos:] {
140 if kv.Key() == key {
141 return kv.Value(), true, nil
142 }
143 }
144 return "", false, nil
145 }
146
147 func ParseAttrBody(pos token.Pos, s string) (a Attr) {
148
149
150
151 tmpFile := token.NewFile("", -1, len(s))
152 if len(s) > 0 {
153 tmpFile.AddLine(len(s) - 1)
154 }
155 a.Body = s
156 var scan scanner.Scanner
157 scan.Init(tmpFile, []byte(s), nil, scanner.DontInsertCommas)
158 for {
159 start := scan.Offset()
160 tok, err := scanAttributeTokens(&scan, pos, 1<<token.COMMA|1<<token.BIND|1<<token.EOF)
161 if err != nil {
162
163
164 a.Err = err
165 return a
166 }
167 switch tok {
168 case token.EOF:
169
170 a.appendField("", s[start:], s[start:])
171 return a
172 case token.COMMA:
173 val := s[start : scan.Offset()-1]
174 a.appendField("", val, val)
175 continue
176 }
177 valStart := scan.Offset()
178 key := s[start : valStart-1]
179 tok, err = scanAttributeTokens(&scan, pos, 1<<token.COMMA|1<<token.EOF)
180 if err != nil {
181
182
183 a.Err = err
184 return a
185 }
186 valEnd := len(s)
187 if tok != token.EOF {
188 valEnd = scan.Offset() - 1
189 }
190 value := s[valStart:valEnd]
191 text := s[start:valEnd]
192 a.appendField(key, value, text)
193 if tok == token.EOF {
194 return a
195 }
196 }
197 }
198
199 func (a *Attr) appendField(k, v, text string) {
200 a.Fields = append(a.Fields, KeyValue{
201 key: strings.TrimSpace(k),
202 value: maybeUnquote(strings.TrimSpace(v)),
203 text: text,
204 })
205 }
206
207 func maybeUnquote(s string) string {
208 if !possiblyQuoted(s) {
209 return s
210 }
211 s1, err := literal.Unquote(s)
212 if err != nil {
213 return s
214 }
215 return s1
216 }
217
218 func possiblyQuoted(s string) bool {
219 if len(s) < 2 {
220 return false
221 }
222 if s[0] == '#' && s[len(s)-1] == '#' {
223 return true
224 }
225 if s[0] == '"' && s[len(s)-1] == '"' {
226 return true
227 }
228 if s[0] == '\'' && s[len(s)-1] == '\'' {
229 return true
230 }
231 return false
232 }
233
234
235
236 func scanAttributeTokens(s *scanner.Scanner, startPos token.Pos, close uint64) (token.Token, errors.Error) {
237 for {
238 pos, tok, _ := s.Scan()
239 if s.ErrorCount > 0 {
240
241
242 return 0, errors.Newf(startPos.Add(pos.Offset()), "error scanning attribute text")
243 }
244 if tok < 64 && (close&(1<<tok)) != 0 {
245 return tok, nil
246 }
247 var err error
248 switch tok {
249 case token.EOF:
250 err = fmt.Errorf("attribute missing '%s'", tokenMaskStr(close))
251 case token.LPAREN:
252 _, err = scanAttributeTokens(s, startPos, 1<<token.RPAREN)
253 case token.LBRACE:
254 _, err = scanAttributeTokens(s, startPos, 1<<token.RBRACE)
255 case token.LBRACK:
256 _, err = scanAttributeTokens(s, startPos, 1<<token.RBRACK)
257 case token.RPAREN, token.RBRACK, token.RBRACE:
258 err = fmt.Errorf("unexpected '%s'", tok)
259 }
260 if err != nil {
261 return 0, errors.Newf(startPos.Add(pos.Offset()), "%v", err)
262 }
263 }
264 }
265
266 func tokenMaskStr(m uint64) string {
267 var buf strings.Builder
268 for t := token.Token(0); t < 64; t++ {
269 if (m & (1 << t)) != 0 {
270 if buf.Len() > 0 {
271 buf.WriteByte('|')
272 }
273 buf.WriteString(t.String())
274 }
275 }
276 return buf.String()
277 }
278
View as plain text