1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package internal
19
20
21
22 import (
23 "bufio"
24 "fmt"
25 "path/filepath"
26 "strings"
27
28 "github.com/cockroachdb/apd/v3"
29
30 "cuelang.org/go/cue/ast"
31 "cuelang.org/go/cue/ast/astutil"
32 "cuelang.org/go/cue/errors"
33 "cuelang.org/go/cue/token"
34 )
35
36
37
38
39 type Decimal = apd.Decimal
40
41
42
43
44 type Context struct {
45 apd.Context
46 }
47
48
49 func (c Context) WithPrecision(p uint32) Context {
50 c.Context = *c.Context.WithPrecision(p)
51 return c
52 }
53
54
55
56
57
58
59
60
61
62
63
64
65 func reduceKeepingFloats(d *apd.Decimal) {
66 oldExponent := d.Exponent
67 d.Reduce(d)
68
69
70 if oldExponent < 0 && d.Exponent >= 0 {
71 d.Exponent--
72
73 d.Coeff.Mul(&d.Coeff, apd.NewBigInt(10))
74 }
75 }
76
77 func (c Context) Quo(d, x, y *apd.Decimal) (apd.Condition, error) {
78 res, err := c.Context.Quo(d, x, y)
79 reduceKeepingFloats(d)
80 return res, err
81 }
82
83 func (c Context) Sqrt(d, x *apd.Decimal) (apd.Condition, error) {
84 res, err := c.Context.Sqrt(d, x)
85 reduceKeepingFloats(d)
86 return res, err
87 }
88
89
90
91 var ErrIncomplete = errors.New("incomplete value")
92
93
94 var MakeInstance func(value interface{}) (instance interface{})
95
96
97 var BaseContext = Context{*apd.BaseContext.WithPrecision(34)}
98
99
100
101 var APIVersionSupported = Version(MinorSupported, PatchSupported)
102
103 const (
104 MinorCurrent = 5
105 MinorSupported = 4
106 PatchSupported = 0
107 )
108
109 func Version(minor, patch int) int {
110 return -1000 + 100*minor + patch
111 }
112
113 type EvaluatorVersion int
114
115 const (
116 DefaultVersion EvaluatorVersion = iota
117
118
119
120 DevVersion
121 )
122
123
124
125
126
127 func ListEllipsis(n *ast.ListLit) (elts []ast.Expr, e *ast.Ellipsis) {
128 elts = n.Elts
129 if n := len(elts); n > 0 {
130 var ok bool
131 if e, ok = elts[n-1].(*ast.Ellipsis); ok {
132 elts = elts[:n-1]
133 }
134 }
135 return elts, e
136 }
137
138 type PkgInfo struct {
139 Package *ast.Package
140 Index int
141 Name string
142 }
143
144
145 func (p *PkgInfo) IsAnonymous() bool {
146 return p.Name == "" || p.Name == "_"
147 }
148
149 func GetPackageInfo(f *ast.File) PkgInfo {
150 for i, d := range f.Decls {
151 switch x := d.(type) {
152 case *ast.CommentGroup:
153 case *ast.Attribute:
154 case *ast.Package:
155 if x.Name == nil {
156 break
157 }
158 return PkgInfo{x, i, x.Name.Name}
159 }
160 }
161 return PkgInfo{}
162 }
163
164
165 func PackageInfo(f *ast.File) (p *ast.Package, name string, tok token.Pos) {
166 x := GetPackageInfo(f)
167 if p := x.Package; p != nil {
168 return p, x.Name, p.Name.Pos()
169 }
170 return nil, "", f.Pos()
171 }
172
173 func SetPackage(f *ast.File, name string, overwrite bool) {
174 p, str, _ := PackageInfo(f)
175 if p != nil {
176 if !overwrite || str == name {
177 return
178 }
179 ident := ast.NewIdent(name)
180 astutil.CopyMeta(ident, p.Name)
181 return
182 }
183
184 decls := make([]ast.Decl, len(f.Decls)+1)
185 k := 0
186 for _, d := range f.Decls {
187 if _, ok := d.(*ast.CommentGroup); ok {
188 decls[k] = d
189 k++
190 continue
191 }
192 break
193 }
194 decls[k] = &ast.Package{Name: ast.NewIdent(name)}
195 copy(decls[k+1:], f.Decls[k:])
196 f.Decls = decls
197 }
198
199
200
201
202 func NewComment(isDoc bool, s string) *ast.CommentGroup {
203 if s == "" {
204 return nil
205 }
206 cg := &ast.CommentGroup{Doc: isDoc}
207 if !isDoc {
208 cg.Line = true
209 cg.Position = 10
210 }
211 scanner := bufio.NewScanner(strings.NewReader(s))
212 for scanner.Scan() {
213 scanner := bufio.NewScanner(strings.NewReader(scanner.Text()))
214 scanner.Split(bufio.ScanWords)
215 const maxRunesPerLine = 66
216 count := 2
217 buf := strings.Builder{}
218 buf.WriteString("//")
219 for scanner.Scan() {
220 s := scanner.Text()
221 n := len([]rune(s)) + 1
222 if count+n > maxRunesPerLine && count > 3 {
223 cg.List = append(cg.List, &ast.Comment{Text: buf.String()})
224 count = 3
225 buf.Reset()
226 buf.WriteString("//")
227 }
228 buf.WriteString(" ")
229 buf.WriteString(s)
230 count += n
231 }
232 cg.List = append(cg.List, &ast.Comment{Text: buf.String()})
233 }
234 if last := len(cg.List) - 1; cg.List[last].Text == "//" {
235 cg.List = cg.List[:last]
236 }
237 return cg
238 }
239
240 func FileComment(f *ast.File) *ast.CommentGroup {
241 pkg, _, _ := PackageInfo(f)
242 var cgs []*ast.CommentGroup
243 if pkg != nil {
244 cgs = pkg.Comments()
245 } else if cgs = f.Comments(); len(cgs) > 0 {
246
247 } else {
248
249 for _, d := range f.Decls {
250 if cg, ok := d.(*ast.CommentGroup); ok {
251 return cg
252 }
253 if cgs = ast.Comments(d); cgs != nil {
254 break
255 }
256
257 if _, ok := d.(*ast.Attribute); !ok {
258 break
259 }
260 }
261 }
262 var cg *ast.CommentGroup
263 for _, c := range cgs {
264 if c.Position == 0 {
265 cg = c
266 }
267 }
268 return cg
269 }
270
271 func NewAttr(name, str string) *ast.Attribute {
272 buf := &strings.Builder{}
273 buf.WriteByte('@')
274 buf.WriteString(name)
275 buf.WriteByte('(')
276 buf.WriteString(str)
277 buf.WriteByte(')')
278 return &ast.Attribute{Text: buf.String()}
279 }
280
281
282
283
284 func ToExpr(n ast.Node) ast.Expr {
285 switch x := n.(type) {
286 case nil:
287 return nil
288
289 case ast.Expr:
290 return x
291
292 case *ast.File:
293 start := 0
294 outer:
295 for i, d := range x.Decls {
296 switch d.(type) {
297 case *ast.Package, *ast.ImportDecl:
298 start = i + 1
299 case *ast.CommentGroup, *ast.Attribute:
300 default:
301 break outer
302 }
303 }
304 decls := x.Decls[start:]
305 if len(decls) == 1 {
306 if e, ok := decls[0].(*ast.EmbedDecl); ok {
307 return e.Expr
308 }
309 }
310 return &ast.StructLit{Elts: decls}
311
312 default:
313 panic(fmt.Sprintf("Unsupported node type %T", x))
314 }
315 }
316
317
318
319
320 func ToFile(n ast.Node) *ast.File {
321 switch x := n.(type) {
322 case nil:
323 return nil
324 case *ast.StructLit:
325 return &ast.File{Decls: x.Elts}
326 case ast.Expr:
327 ast.SetRelPos(x, token.NoSpace)
328 return &ast.File{Decls: []ast.Decl{&ast.EmbedDecl{Expr: x}}}
329 case *ast.File:
330 return x
331 default:
332 panic(fmt.Sprintf("Unsupported node type %T", x))
333 }
334 }
335
336 func IsDef(s string) bool {
337 return strings.HasPrefix(s, "#") || strings.HasPrefix(s, "_#")
338 }
339
340 func IsHidden(s string) bool {
341 return strings.HasPrefix(s, "_")
342 }
343
344 func IsDefOrHidden(s string) bool {
345 return strings.HasPrefix(s, "#") || strings.HasPrefix(s, "_")
346 }
347
348 func IsDefinition(label ast.Label) bool {
349 switch x := label.(type) {
350 case *ast.Alias:
351 if ident, ok := x.Expr.(*ast.Ident); ok {
352 return IsDef(ident.Name)
353 }
354 case *ast.Ident:
355 return IsDef(x.Name)
356 }
357 return false
358 }
359
360 func IsRegularField(f *ast.Field) bool {
361 var ident *ast.Ident
362 switch x := f.Label.(type) {
363 case *ast.Alias:
364 ident, _ = x.Expr.(*ast.Ident)
365 case *ast.Ident:
366 ident = x
367 }
368 if ident == nil {
369 return true
370 }
371 if strings.HasPrefix(ident.Name, "#") || strings.HasPrefix(ident.Name, "_") {
372 return false
373 }
374 return true
375 }
376
377
378
379
380 func ConstraintToken(f *ast.Field) (t token.Token, ok bool) {
381 if f.Constraint != token.ILLEGAL {
382 return f.Constraint, true
383 }
384 if f.Optional != token.NoPos {
385 return token.OPTION, true
386 }
387 return f.Constraint, false
388 }
389
390
391
392 func SetConstraint(f *ast.Field, t token.Token) {
393 f.Constraint = t
394 if t == token.ILLEGAL {
395 f.Optional = token.NoPos
396 } else {
397 f.Optional = token.Blank.Pos()
398 }
399 }
400
401 func EmbedStruct(s *ast.StructLit) *ast.EmbedDecl {
402 e := &ast.EmbedDecl{Expr: s}
403 if len(s.Elts) == 1 {
404 d := s.Elts[0]
405 astutil.CopyPosition(e, d)
406 ast.SetRelPos(d, token.NoSpace)
407 astutil.CopyComments(e, d)
408 ast.SetComments(d, nil)
409 if f, ok := d.(*ast.Field); ok {
410 ast.SetRelPos(f.Label, token.NoSpace)
411 }
412 }
413 s.Lbrace = token.Newline.Pos()
414 s.Rbrace = token.NoSpace.Pos()
415 return e
416 }
417
418
419 func IsEllipsis(x ast.Decl) bool {
420
421 if _, ok := x.(*ast.Ellipsis); ok {
422 return true
423 }
424
425
426 f, ok := x.(*ast.Field)
427 if !ok {
428 return false
429 }
430 v, ok := f.Value.(*ast.Ident)
431 if !ok || v.Name != "_" {
432 return false
433 }
434 l, ok := f.Label.(*ast.ListLit)
435 if !ok || len(l.Elts) != 1 {
436 return false
437 }
438 i, ok := l.Elts[0].(*ast.Ident)
439 if !ok {
440 return false
441 }
442 return i.Name == "string" || i.Name == "_"
443 }
444
445
446 func GenPath(root string) string {
447 return filepath.Join(root, "cue.mod", "gen")
448 }
449
450 var ErrInexact = errors.New("inexact subsumption")
451
452 func DecorateError(info error, err errors.Error) errors.Error {
453 return &decorated{cueError: err, info: info}
454 }
455
456 type cueError = errors.Error
457
458 type decorated struct {
459 cueError
460
461 info error
462 }
463
464 func (e *decorated) Is(err error) bool {
465 return errors.Is(e.info, err) || errors.Is(e.cueError, err)
466 }
467
468
469
470
471
472
473
474
475 const MaxDepth = 20
476
View as plain text