1// Copyright 2020 CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package export
16
17import (
18 "cmp"
19 "fmt"
20 "maps"
21 "slices"
22
23 "cuelang.org/go/cue/ast"
24 "cuelang.org/go/cue/token"
25 "cuelang.org/go/internal/core/adt"
26)
27
28// Modes:
29// raw: as is
30// def: merge structs, print reset as is.
31//
32// Possible simplifications in def mode:
33// - merge contents of multiple _literal_ structs.
34// - this is not possible if some of the elements are bulk optional
35// (or is it?).
36// - still do not ever resolve references.
37// - to do this, fields must be pre-linked to their destinations.
38// - use astutil.Sanitize to resolve shadowing and imports.
39//
40//
41// Categories of printing:
42// - concrete
43// - optionals
44// - references
45// - constraints
46//
47// Mixed mode is also not supported in the old implementation (at least not
48// correctly). It requires references to resolve properly, backtracking to
49// a common root and prefixing that to the reference. This is now possible
50// with the Environment construct and could be done later.
51
52var empty *adt.Vertex
53
54func init() {
55 // TODO: Consider setting a non-nil BaseValue.
56 empty = &adt.Vertex{}
57 empty.ForceDone()
58}
59
60// innerExpr is like expr, but prohibits inlining in certain cases.
61func (e *exporter) innerExpr(env *adt.Environment, v adt.Elem) (result ast.Expr) {
62 e.inExpression++
63 r := e.expr(env, v)
64 e.inExpression--
65 return r
66}
67
68// expr converts an ADT expression to an AST expression.
69// The env is used for resolution and does not need to be given if v is known
70// to not contain any references.
71func (e *exporter) expr(env *adt.Environment, v adt.Elem) (result ast.Expr) {
72 switch x := v.(type) {
73 case nil:
74 return nil
75
76 case *adt.Vertex:
77 if x.IsData() {
78 // Treat as literal value.
79 return e.value(x)
80 } // Should this be the arcs label?
81
82 a := []conjunct{}
83 for c := range x.LeafConjuncts() {
84 if c, ok := c.Elem().(*adt.Comprehension); ok && !c.DidResolve() {
85 continue
86 }
87 a = append(a, conjunct{c, 0})
88 }
89
90 return e.mergeValues(adt.InvalidLabel, x, a, x.Conjuncts...)
91
92 case *adt.StructLit:
93 c := adt.MakeRootConjunct(env, x)
94 return e.mergeValues(adt.InvalidLabel, nil, []conjunct{{c: c, up: 0}}, c)
95
96 case adt.Value:
97 return e.value(x) // Use conjuncts.
98
99 default:
100 return e.adt(env, v)
101 }
102}
103
104// Piece out values:
105
106// For a struct, piece out conjuncts that are already values. Those can be
107// unified. All other conjuncts are added verbatim.
108
109func (x *exporter) mergeValues(label adt.Feature, src *adt.Vertex, a []conjunct, orig ...adt.Conjunct) (expr ast.Expr) {
110
111 e := conjuncts{
112 exporter: x,
113 values: &adt.Vertex{},
114 fields: map[adt.Feature]field{},
115 attrs: []*ast.Attribute{},
116 }
117
118 s, saved := e.pushFrame(src, orig)
119 defer e.popFrame(saved)
120
121 // Handle value aliases and lets
122 var valueAlias *ast.Alias
123 for _, c := range a {
124 if f, ok := c.c.Field().Source().(*ast.Field); ok {
125 if a, ok := f.Value.(*ast.Alias); ok {
126 if valueAlias == nil {
127 if e.valueAlias == nil {
128 e.valueAlias = map[*ast.Alias]*ast.Alias{}
129 }
130 name := a.Ident.Name
131 name = e.uniqueAlias(name)
132 valueAlias = &ast.Alias{Ident: ast.NewIdent(name)}
133 }
134 e.valueAlias[a] = valueAlias
135 }
136 }
137 x.markLets(c.c.Expr().Source(), s)
138 }
139
140 defer filterUnusedLets(s)
141
142 defer func() {
143 if valueAlias != nil {
144 valueAlias.Expr = expr
145 expr = valueAlias
146 }
147 }()
148
149 hasAlias := len(s.Elts) > 0
150
151 for _, c := range a {
152 e.top().upCount = c.up
153 x := c.c.Elem()
154 e.addExpr(c.c.Env, src, x, false)
155 }
156
157 if src != nil {
158 for _, a := range src.Arcs {
159 if x, ok := e.fields[a.Label]; ok {
160 x.arc = a
161 x.arcType = a.ArcType
162 e.fields[a.Label] = x
163 }
164 }
165 }
166
167 for _, a := range e.attrs {
168 s.Elts = append(s.Elts, a)
169 }
170
171 // Unify values only for one level.
172 if e.values.HasConjuncts() {
173 e.values.Finalize(e.ctx)
174 e.embed = append(e.embed, e.value(e.values, e.values.Conjuncts...))
175 }
176
177 // Collect and order set of fields.
178
179 fields := slices.Collect(maps.Keys(e.fields))
180
181 // Sort fields in case features lists are missing to ensure
182 // predictability. Also sort in reverse order, so that bugs
183 // are more likely exposed.
184 slices.SortFunc(fields, func(f1, f2 adt.Feature) int {
185 return -cmp.Compare(f1, f2)
186 })
187
188 // TODO: should this not use the new toposort? it still uses the pre-toposort field sorting.
189 m := sortArcs(extractFeatures(e.structs))
190 slices.SortStableFunc(fields, func(f1, f2 adt.Feature) int {
191 if m[f2] == 0 {
192 if m[f1] == 0 {
193 return +1
194 }
195 return -1
196 }
197 return -cmp.Compare(m[f1], m[f2])
198 })
199
200 if len(e.fields) == 0 && !e.hasEllipsis {
201 switch len(e.embed) + len(e.conjuncts) {
202 case 0:
203 if len(e.attrs) > 0 {
204 break
205 }
206 if len(e.structs) > 0 || e.isData {
207 return e.wrapCloseIfNecessary(s, src)
208 }
209 return ast.NewIdent("_")
210 case 1:
211 var x ast.Expr
212 if len(e.conjuncts) == 1 {
213 x = e.conjuncts[0]
214 } else {
215 x = e.embed[0]
216 }
217 if len(e.attrs) == 0 && !hasAlias {
218 return x
219 }
220 if st, ok := x.(*ast.StructLit); ok {
221 s.Elts = append(s.Elts, st.Elts...)
222 return e.wrapCloseIfNecessary(s, src)
223 }
224 }
225 }
226
227 for _, x := range e.embed {
228 s.Elts = append(s.Elts, &ast.EmbedDecl{Expr: x})
229 }
230
231 for _, f := range fields {
232 if f.IsLet() {
233 continue
234 }
235 field := e.getField(f)
236 c := field.conjuncts
237
238 label := e.stringLabel(f)
239
240 if f.IsDef() {
241 x.inDefinition++
242 }
243
244 a := []adt.Conjunct{}
245 for _, cc := range c {
246 a = append(a, cc.c)
247 }
248
249 d := &ast.Field{Label: label}
250
251 top := e.frame(0)
252 if fr, ok := top.fields[f]; ok && fr.alias != "" {
253 setFieldAlias(d, fr.alias)
254 fr.node = d
255 top.fields[f] = fr
256 }
257
258 d.Value = e.mergeValues(f, field.arc, c, a...)
259
260 e.linkField(field.arc, d)
261
262 if f.IsDef() {
263 x.inDefinition--
264 }
265
266 d.Constraint = field.arcType.Token()
267 if x.cfg.ShowDocs {
268 v := &adt.Vertex{Conjuncts: a}
269 docs := extractDocs(v)
270 ast.SetComments(d, docs)
271 }
272 if x.cfg.ShowAttributes {
273 for _, c := range a {
274 d.Attrs = extractFieldAttrs(d.Attrs, c.Field())
275 }
276 }
277 s.Elts = append(s.Elts, d)
278 }
279 if e.hasEllipsis {
280 s.Elts = append(s.Elts, &ast.Ellipsis{})
281 }
282
283 ws := e.wrapCloseIfNecessary(s, src)
284 switch {
285 case len(e.conjuncts) == 0:
286 return ws
287
288 case len(e.structs) > 0, len(s.Elts) > 0:
289 e.conjuncts = append(e.conjuncts, ws)
290 }
291
292 return ast.NewBinExpr(token.AND, e.conjuncts...)
293}
294
295func (e *conjuncts) wrapCloseIfNecessary(s *ast.StructLit, v *adt.Vertex) ast.Expr {
296 if !e.hasEllipsis && v != nil && v.ClosedNonRecursive {
297 return ast.NewCall(ast.NewIdent("close"), s)
298 }
299 return s
300}
301
302// Conjuncts if for collecting values of a single vertex.
303type conjuncts struct {
304 *exporter
305 // Values is used to collect non-struct values.
306 values *adt.Vertex
307 embed []ast.Expr
308 conjuncts []ast.Expr
309 structs []adt.StructInfo
310 fields map[adt.Feature]field
311 attrs []*ast.Attribute
312 hasEllipsis bool
313
314 // A value is a struct if it has a non-zero structs slice or if isData is
315 // set to true. Data vertices may not have conjuncts associated with them.
316 isData bool
317}
318
319func (c *conjuncts) getField(label adt.Feature) field {
320 f, ok := c.fields[label]
321 if !ok {
322 f.arcType = adt.ArcNotPresent
323 }
324 return f
325}
326
327func (c *conjuncts) addValueConjunct(src *adt.Vertex, env *adt.Environment, x adt.Elem) {
328 switch b, ok := x.(adt.BaseValue); {
329 case ok && src != nil && isTop(b) && !isTop(src.BaseValue):
330 // drop top
331 default:
332 c.values.AddConjunct(adt.MakeRootConjunct(env, x))
333 }
334}
335
336func (c *conjuncts) addConjunct(f adt.Feature, t adt.ArcType, env *adt.Environment, n adt.Node) {
337 x := c.getField(f)
338 if t < x.arcType {
339 x.arcType = t
340 }
341
342 v := adt.MakeRootConjunct(env, n)
343 x.conjuncts = append(x.conjuncts, conjunct{
344 c: v,
345 up: c.top().upCount,
346 })
347 // x.upCounts = append(x.upCounts, c.top().upCount)
348 c.fields[f] = x
349}
350
351type field struct {
352 arcType adt.ArcType
353 arc *adt.Vertex
354 conjuncts []conjunct
355}
356
357type conjunct struct {
358 c adt.Conjunct
359 up int32
360}
361
362func (e *conjuncts) addExpr(env *adt.Environment, src *adt.Vertex, x adt.Elem, isEmbed bool) {
363 switch x := x.(type) {
364 case *adt.StructLit:
365 e.top().upCount++
366
367 if e.cfg.ShowAttributes {
368 e.attrs = extractDeclAttrs(e.attrs, x.Src)
369 }
370
371 // Only add if it only has no bulk fields or ellipsis.
372 if isComplexStruct(x) {
373 _, saved := e.pushFrame(src, nil)
374 e.embed = append(e.embed, e.adt(env, x))
375 e.top().upCount-- // not necessary, but for proper form
376 e.popFrame(saved)
377 return
378 }
379 // Used for sorting.
380 e.structs = append(e.structs, adt.StructInfo{StructLit: x})
381
382 env = &adt.Environment{Up: env, Vertex: e.node()}
383
384 for _, d := range x.Decls {
385 var label adt.Feature
386 t := adt.ArcNotPresent
387 switch f := d.(type) {
388 case *adt.Field:
389 label = f.Label
390 t = f.ArcType
391 // TODO: mark optional here, if needed.
392 case *adt.LetField:
393 continue
394 case *adt.Ellipsis:
395 e.hasEllipsis = true
396 continue
397 case adt.Expr:
398 e.addExpr(env, nil, f, true)
399 continue
400
401 // TODO: also handle dynamic fields
402 default:
403 panic(fmt.Sprintf("Unexpected type %T", d))
404 }
405 e.addConjunct(label, t, env, d)
406 }
407 e.top().upCount--
408
409 case adt.Value: // other values.
410 switch v := x.(type) {
411 case nil:
412 default:
413 e.addValueConjunct(src, env, x)
414
415 case *adt.Vertex:
416 if b := v.Bottom(); b != nil {
417 if !b.IsIncomplete() || e.cfg.Final {
418 e.addExpr(env, v, b, false)
419 return
420 }
421 }
422
423 switch {
424 default:
425 for c := range v.LeafConjuncts() {
426 e.addExpr(c.Env, v, c.Elem(), false)
427 }
428
429 case v.IsData():
430 e.structs = append(e.structs, v.Structs...)
431 e.isData = true
432
433 if y, ok := v.BaseValue.(adt.Value); ok {
434 e.addValueConjunct(src, env, y)
435 }
436
437 for _, a := range v.Arcs {
438 a.Finalize(e.ctx) // TODO: should we do this?
439
440 if !a.IsDefined(e.ctx) {
441 continue
442 }
443
444 e.addConjunct(a.Label, a.ArcType, env, a)
445 }
446 }
447 }
448
449 case *adt.BinaryExpr:
450 switch {
451 case x.Op == adt.AndOp && !isEmbed:
452 e.addExpr(env, src, x.X, false)
453 e.addExpr(env, src, x.Y, false)
454 case isSelfContained(x):
455 e.addValueConjunct(src, env, x)
456 default:
457 if isEmbed {
458 e.embed = append(e.embed, e.expr(env, x))
459 } else {
460 e.conjuncts = append(e.conjuncts, e.expr(env, x))
461 }
462 }
463
464 default:
465 switch {
466 case isSelfContained(x):
467 e.addValueConjunct(src, env, x)
468 case isEmbed:
469 e.embed = append(e.embed, e.expr(env, x))
470 default:
471 if x := e.expr(env, x); x != dummyTop {
472 e.conjuncts = append(e.conjuncts, x)
473 }
474 }
475 }
476}
477
478func isTop(x adt.BaseValue) bool {
479 switch v := x.(type) {
480 case *adt.Top:
481 return true
482 case *adt.BasicType:
483 return v.K == adt.TopKind
484 default:
485 return false
486 }
487}
488
489func isComplexStruct(s *adt.StructLit) bool {
490 for _, d := range s.Decls {
491 switch x := d.(type) {
492 case *adt.Field:
493 // TODO: remove this and also handle field annotation in expr().
494 // This allows structs to be merged. Ditto below.
495 if x.Src != nil {
496 if _, ok := x.Src.Label.(*ast.Alias); ok {
497 return ok
498 }
499 }
500
501 case *adt.LetField:
502
503 case adt.Expr:
504
505 case *adt.Ellipsis:
506 if x.Value != nil {
507 return true
508 }
509
510 default:
511 return true
512 }
513 }
514 return false
515}
516
517func isSelfContained(expr adt.Elem) bool {
518 switch x := expr.(type) {
519 case *adt.BinaryExpr:
520 return isSelfContained(x.X) && isSelfContained(x.Y)
521 case *adt.UnaryExpr:
522 return isSelfContained(x.X)
523 case *adt.BoundExpr:
524 return isSelfContained(x.Expr)
525 case adt.Value:
526 return true
527 }
528 return false
529}