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 "fmt"
19 "slices"
20 "strings"
21
22 "cuelang.org/go/cue/ast"
23 "cuelang.org/go/cue/ast/astutil"
24 "cuelang.org/go/cue/literal"
25 "cuelang.org/go/cue/token"
26 "cuelang.org/go/internal/core/adt"
27)
28
29func (e *exporter) bareValue(v adt.Value) ast.Expr {
30 switch x := v.(type) {
31 case *adt.Vertex:
32 return e.vertex(x)
33 case adt.Value:
34 a := &adt.Vertex{BaseValue: x}
35 return e.vertex(a)
36 default:
37 panic("unreachable")
38 }
39 // TODO: allow a Value context wrapper.
40}
41
42// TODO: if the original value was a single reference, we could replace the
43// value with a reference in graph mode.
44
45func (e *exporter) vertex(n *adt.Vertex) (result ast.Expr) {
46 var attrs []*ast.Attribute
47 if e.cfg.ShowAttributes {
48 attrs = ExtractDeclAttrs(n)
49 }
50
51 s, saved := e.pushFrame(n, n.Conjuncts)
52 e.top().upCount++
53 defer func() {
54 e.top().upCount--
55 e.popFrame(saved)
56 }()
57
58 for c := range n.LeafConjuncts() {
59 e.markLets(c.Expr().Source(), s)
60 }
61
62 switch x := n.BaseValue.(type) {
63 case nil:
64 // bare
65 case *adt.StructMarker:
66 result = e.structComposite(n, attrs)
67
68 case *adt.ListMarker:
69 if e.showArcs(n) || attrs != nil {
70 result = e.structComposite(n, attrs)
71 } else {
72 result = e.listComposite(n)
73 }
74
75 case *adt.Bottom:
76 switch {
77 case n.ArcType == adt.ArcOptional:
78 // Optional fields may always be the original value.
79
80 case e.cfg.ShowErrors && x.ChildError:
81 // TODO(perf): use precompiled arc statistics
82 if len(n.Arcs) > 0 && n.Arcs[0].Label.IsInt() && !e.showArcs(n) && attrs == nil {
83 result = e.listComposite(n)
84 } else {
85 result = e.structComposite(n, attrs)
86 }
87
88 case !x.IsIncomplete() || !n.HasConjuncts() || e.cfg.Final:
89 result = e.bottom(x)
90 }
91
92 case adt.Value:
93 if e.showArcs(n) || attrs != nil {
94 result = e.structComposite(n, attrs)
95 } else {
96 result = e.value(x, n.Conjuncts...)
97 }
98
99 default:
100 panic("unknown value")
101 }
102 if result == nil {
103 // fall back to expression mode
104 // Use stable sort to ensure that tie breaks (for instance if elements
105 // are not associated with a position) are deterministic.
106 a := slices.SortedStableFunc(n.LeafConjuncts(), cmpConjuncts)
107
108 exprs := make([]ast.Expr, 0, len(a))
109 for _, c := range a {
110 if x := e.expr(c.Env, c.Elem()); x != dummyTop {
111 exprs = append(exprs, x)
112 }
113 }
114
115 result = ast.NewBinExpr(token.AND, exprs...)
116 }
117
118 filterUnusedLets(s)
119 if result != s && len(s.Elts) > 0 {
120 // There are used let expressions within a non-struct.
121 // For now we just fall back to the original expressions.
122 result = e.adt(nil, n)
123 }
124
125 return result
126}
127
128func (e *exporter) value(n adt.Value, a ...adt.Conjunct) (result ast.Expr) {
129 if e.cfg.TakeDefaults {
130 n = adt.Default(n)
131 }
132 // Evaluate arc if needed?
133
134 // if e.concrete && !adt.IsConcrete(n.Value) {
135 // return e.errf("non-concrete value: %v", e.bareValue(n.Value))
136 // }
137
138 switch x := n.(type) {
139 case *adt.Bottom:
140 result = e.bottom(x)
141
142 case *adt.Null:
143 result = e.null(x)
144
145 case *adt.Bool:
146 result = e.bool(x)
147
148 case *adt.Num:
149 result = e.num(x, a)
150
151 case *adt.String:
152 result = e.string(x, a)
153
154 case *adt.Bytes:
155 result = e.bytes(x, a)
156
157 case *adt.BasicType:
158 result = e.basicType(x)
159
160 case *adt.Top:
161 result = ast.NewIdent("_")
162
163 case *adt.BoundValue:
164 result = e.boundValue(x)
165
166 case *adt.Builtin:
167 result = e.builtin(x)
168
169 case *adt.BuiltinValidator:
170 result = e.builtinValidator(x)
171
172 case *adt.Vertex:
173 result = e.vertex(x)
174
175 case *adt.Conjunction:
176 switch len(x.Values) {
177 case 0:
178 return ast.NewIdent("_")
179 case 1:
180 if e.cfg.Simplify {
181 return e.expr(nil, x.Values[0])
182 }
183 return e.bareValue(x.Values[0])
184 }
185
186 a := []adt.Value{}
187 b := boundSimplifier{e: e}
188 for _, v := range x.Values {
189 if !e.cfg.Simplify || !b.add(v) {
190 a = append(a, v)
191 }
192 }
193
194 result = b.expr(e.ctx)
195 if result == nil {
196 a = x.Values
197 }
198
199 slices.SortStableFunc(a, cmpLeafNodes)
200
201 for _, x := range a {
202 result = wrapBin(result, e.bareValue(x), adt.AndOp)
203 }
204
205 case *adt.Disjunction:
206 a := []ast.Expr{}
207
208 for i, v := range x.Values {
209 var expr ast.Expr
210 if e.cfg.Simplify {
211 expr = e.bareValue(v)
212 } else {
213 expr = e.expr(nil, v)
214 }
215 if i < x.NumDefaults {
216 expr = &ast.UnaryExpr{Op: token.MUL, X: expr}
217 }
218 a = append(a, expr)
219 }
220 result = ast.NewBinExpr(token.OR, a...)
221
222 case *adt.NodeLink:
223 return e.value(x.Node, a...)
224
225 default:
226 panic(fmt.Sprintf("unsupported type %T", x))
227 }
228
229 // TODO: Add comments from original.
230
231 return result
232}
233
234func (e *exporter) bottom(n *adt.Bottom) *ast.BottomLit {
235 err := &ast.BottomLit{}
236 if x := n.Err; x != nil {
237 msg := x.Error()
238 comment := &ast.Comment{Text: "// " + msg}
239 ast.AddComment(err, &ast.CommentGroup{
240 Line: true,
241 Position: 2,
242 List: []*ast.Comment{comment},
243 })
244 }
245 return err
246}
247
248func (e *exporter) null(n *adt.Null) *ast.BasicLit {
249 return &ast.BasicLit{Kind: token.NULL, Value: "null"}
250}
251
252func (e *exporter) bool(n *adt.Bool) (b *ast.BasicLit) {
253 return ast.NewBool(n.B)
254}
255
256func extractBasic(a []adt.Conjunct) *ast.BasicLit {
257 for c := range adt.ConjunctsSeq(a) {
258 if b, ok := c.Source().(*ast.BasicLit); ok {
259 return &ast.BasicLit{Kind: b.Kind, Value: b.Value}
260 }
261 }
262 return nil
263}
264
265func (e *exporter) num(n *adt.Num, orig []adt.Conjunct) *ast.BasicLit {
266 // TODO: take original formatting into account.
267 if b := extractBasic(orig); b != nil {
268 return b
269 }
270 kind := token.FLOAT
271 if n.K&adt.IntKind != 0 {
272 kind = token.INT
273 }
274 s := n.X.String()
275 if kind == token.FLOAT && !strings.ContainsAny(s, "eE.") {
276 s += "."
277 }
278 return &ast.BasicLit{Kind: kind, Value: s}
279}
280
281func (e *exporter) string(n *adt.String, orig []adt.Conjunct) *ast.BasicLit {
282 // TODO: take original formatting into account.
283 if b := extractBasic(orig); b != nil {
284 return b
285 }
286 s := literal.String.WithOptionalTabIndent(len(e.stack)).Quote(n.Str)
287 return &ast.BasicLit{
288 Kind: token.STRING,
289 Value: s,
290 }
291}
292
293func (e *exporter) bytes(n *adt.Bytes, orig []adt.Conjunct) *ast.BasicLit {
294 // TODO: take original formatting into account.
295 if b := extractBasic(orig); b != nil {
296 return b
297 }
298 s := literal.Bytes.WithOptionalTabIndent(len(e.stack)).Quote(string(n.B))
299 return &ast.BasicLit{
300 Kind: token.STRING,
301 Value: s,
302 }
303}
304
305func (e *exporter) basicType(n *adt.BasicType) ast.Expr {
306 // TODO: allow multi-bit types?
307 return ast.NewIdent(n.K.String())
308}
309
310func (e *exporter) boundValue(n *adt.BoundValue) ast.Expr {
311 return &ast.UnaryExpr{Op: n.Op.Token(), X: e.value(n.Value)}
312}
313
314func (e *exporter) builtin(x *adt.Builtin) ast.Expr {
315 if x.Package == 0 {
316 return ast.NewIdent(x.Name)
317 }
318 spec := ast.NewImport(nil, x.Package.StringValue(e.index))
319 info, _ := astutil.ParseImportSpec(spec)
320 ident := ast.NewIdent(info.Ident)
321 ident.Node = spec
322 return ast.NewSel(ident, x.Name)
323}
324
325func (e *exporter) builtinValidator(n *adt.BuiltinValidator) ast.Expr {
326 call := ast.NewCall(e.builtin(n.Builtin))
327 for _, a := range n.Args {
328 call.Args = append(call.Args, e.value(a))
329 }
330 return call
331}
332
333func (e *exporter) listComposite(v *adt.Vertex) ast.Expr {
334 l := &ast.ListLit{}
335 for _, a := range v.Arcs {
336 if !a.Label.IsInt() {
337 continue
338 }
339 elem := e.vertex(a)
340
341 if e.cfg.ShowDocs {
342 docs := ExtractDoc(a)
343 ast.SetComments(elem, docs)
344 }
345
346 l.Elts = append(l.Elts, elem)
347 }
348 m, ok := v.BaseValue.(*adt.ListMarker)
349 if !e.cfg.TakeDefaults && ok && m.IsOpen {
350 ellipsis := &ast.Ellipsis{}
351 typ := &adt.Vertex{
352 Parent: v,
353 Label: adt.AnyIndex,
354 }
355 v.MatchAndInsert(e.ctx, typ)
356 typ.Finalize(e.ctx)
357 if typ.Kind() != adt.TopKind {
358 ellipsis.Type = e.value(typ)
359 }
360
361 l.Elts = append(l.Elts, ellipsis)
362 }
363 return l
364}
365
366func (e exporter) showArcs(v *adt.Vertex) bool {
367 p := e.cfg
368 if !p.ShowHidden && !p.ShowDefinitions {
369 return false
370 }
371 for _, a := range v.Arcs {
372 switch {
373 case a.Label.IsDef() && p.ShowDefinitions:
374 return true
375 case a.Label.IsHidden() && p.ShowHidden:
376 return true
377 }
378 }
379 return false
380}
381
382func (e *exporter) structComposite(v *adt.Vertex, attrs []*ast.Attribute) ast.Expr {
383 s := e.top().scope
384
385 showRegular := false
386 switch x := v.BaseValue.(type) {
387 case *adt.StructMarker:
388 showRegular = true
389 case *adt.ListMarker:
390 // As lists may be long, put them at the end.
391 defer e.addEmbed(e.listComposite(v))
392 case *adt.Bottom:
393 if !e.cfg.ShowErrors || !x.ChildError {
394 // Should not be reachable, but just in case. The output will be
395 // correct.
396 e.addEmbed(e.value(x))
397 return s
398 }
399 // Always also show regular fields, even when list, as we are in
400 // debugging mode.
401 showRegular = true
402 // TODO(perf): do something better
403 for _, a := range v.Arcs {
404 if a.Label.IsInt() {
405 defer e.addEmbed(e.listComposite(v))
406 break
407 }
408 }
409
410 case adt.Value:
411 e.addEmbed(e.value(x))
412 }
413
414 for _, a := range attrs {
415 s.Elts = append(s.Elts, a)
416 }
417
418 p := e.cfg
419 for _, label := range VertexFeatures(e.ctx, v) {
420 show := false
421 switch label.Typ() {
422 case adt.StringLabel:
423 show = showRegular
424 case adt.IntLabel:
425 continue
426 case adt.DefinitionLabel:
427 show = p.ShowDefinitions
428 case adt.HiddenLabel, adt.HiddenDefinitionLabel:
429 show = p.ShowHidden && label.PkgID(e.ctx) == e.pkgID
430 }
431 if !show {
432 continue
433 }
434
435 f := &ast.Field{Label: e.stringLabel(label)}
436
437 e.addField(label, f, f.Value)
438
439 if label.IsDef() {
440 e.inDefinition++
441 }
442
443 arc := v.LookupRaw(label)
444 if arc == nil {
445 continue
446 }
447
448 if arc.ArcType == adt.ArcOptional && !p.ShowOptional {
449 continue
450 }
451 // TODO: report an error for required fields in Final mode?
452 // This package typically does not create errors that did not result
453 // from evaluation already.
454
455 f.Constraint = arc.ArcType.Token()
456
457 f.Value = e.vertex(arc.DerefValue())
458
459 if label.IsDef() {
460 e.inDefinition--
461 }
462
463 if p.ShowAttributes {
464 f.Attrs = ExtractFieldAttrs(arc)
465 }
466
467 if p.ShowDocs {
468 docs := ExtractDoc(arc)
469 ast.SetComments(f, docs)
470 }
471
472 s.Elts = append(s.Elts, f)
473 }
474
475 return s
476}