1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package export
16
17 import (
18 "fmt"
19 "strconv"
20 "strings"
21
22 "cuelang.org/go/cue/ast"
23 "cuelang.org/go/cue/token"
24 "cuelang.org/go/internal/core/adt"
25 "cuelang.org/go/internal/core/dep"
26 )
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 func (e *exporter) initPivotter(v *adt.Vertex) {
44 s := &pivotter{}
45 e.pivotter = s
46 s.x = e
47
48 s.depsMap = map[*adt.Vertex]*depData{}
49 s.refMap = map[adt.Resolver]*refData{}
50
51 s.linkDependencies(v)
52 }
53
54 func (e *exporter) completePivot(f *ast.File) {
55 s := e.pivotter
56 if s == nil || f == nil {
57 return
58 }
59 for _, d := range s.deps {
60 if !d.isExternalRoot() {
61 continue
62 }
63 s.addExternal(d)
64 }
65 f.Decls = append(f.Decls, s.decls...)
66 }
67
68
69
70
71
72
73
74
75 type pivotter struct {
76 x *exporter
77
78 deps []*depData
79 depsMap map[*adt.Vertex]*depData
80
81 refs []*refData
82 refMap map[adt.Resolver]*refData
83
84 decls []ast.Decl
85 }
86
87 type depData struct {
88 parent *depData
89
90 dstNode *adt.Vertex
91 dstImport *adt.ImportReference
92
93 ident adt.Feature
94 path []adt.Feature
95 useCount int
96 included bool
97 needTopLevel bool
98 }
99
100
101
102
103 func (d *depData) isExternalRoot() bool {
104 return d.ident != 0
105 }
106
107 func (d *depData) usageCount() int {
108 return getParent(d).useCount
109 }
110
111 type refData struct {
112 dst *depData
113 }
114
115 func (v *depData) node() *adt.Vertex {
116 return v.dstNode
117 }
118
119 func (p *pivotter) linkDependencies(v *adt.Vertex) {
120 p.markDeps(v, nil)
121
122
123 p.markIncluded(v)
124
125
126 for _, d := range p.depsMap {
127 p.markParentsPass1(d)
128 }
129
130
131 for _, d := range p.depsMap {
132 if d.parent != nil {
133 d.parent = getParent(d)
134 d.parent.useCount++
135 }
136 }
137
138
139 for _, d := range p.deps {
140 if d.parent == nil {
141 p.makeParentPath(d)
142 }
143 }
144 }
145
146 func getParent(d *depData) *depData {
147 for ; d.parent != nil; d = d.parent {
148 }
149 return d
150 }
151
152 func (p *pivotter) markDeps(v *adt.Vertex, pkg *adt.ImportReference) {
153
154
155 cfg := &dep.Config{
156 Descend: true,
157 Pkg: pkg,
158 }
159 dep.Visit(cfg, p.x.ctx, v, func(d dep.Dependency) error {
160 node := d.Node
161
162 switch {
163 case p.refMap[d.Reference] != nil:
164
165 return nil
166
167 case d.Import() != nil:
168
169 if !p.x.cfg.InlineImports {
170 return nil
171 }
172
173
174
175
176
177
178
179
180
181
182 path := d.Import().ImportPath.StringValue(p.x.ctx)
183 if !strings.ContainsRune(path, '.') {
184 return nil
185 }
186
187 case node.IsUnprocessed():
188
189 return nil
190 }
191
192 data, ok := p.depsMap[node]
193 if !ok {
194 data = &depData{
195 dstNode: node,
196 dstImport: d.Import(),
197 }
198 p.depsMap[node] = data
199 p.deps = append(p.deps, data)
200 }
201 data.useCount++
202
203 ref := &refData{dst: data}
204 p.refs = append(p.refs, ref)
205 p.refMap[d.Reference] = ref
206
207 if !ok {
208 d.Recurse()
209 }
210
211 return nil
212 })
213 }
214
215
216
217 func (p *pivotter) markIncluded(v *adt.Vertex) {
218 d, ok := p.depsMap[v]
219 if !ok {
220 d = &depData{dstNode: v}
221 p.depsMap[v] = d
222 }
223 d.included = true
224
225 for _, a := range v.Arcs {
226 p.markIncluded(a)
227 }
228 }
229
230
231
232
233 func (p *pivotter) markParentsPass1(d *depData) {
234 for n := d.node().Parent; n != nil; n = n.Parent {
235 if v, ok := p.depsMap[n]; ok {
236 d.parent = v
237 }
238 }
239 }
240
241 func (p *pivotter) makeParentPath(d *depData) {
242 if d.parent != nil {
243 panic("not a parent")
244 }
245
246 if d.included || d.isExternalRoot() {
247 return
248 }
249
250 var f adt.Feature
251
252 if path := d.dstNode.Path(); len(path) > 0 {
253 f = path[len(path)-1]
254 } else if imp := d.dstImport; imp != nil {
255 f = imp.Label
256 } else {
257
258
259 return
260 }
261
262 var str string
263 if f.IsInt() {
264 str = fmt.Sprintf("Index%d", f.Index())
265 } else {
266 str = f.IdentString(p.x.ctx)
267 str = strings.TrimLeft(str, "_#")
268 str = strings.ToUpper(str)
269 }
270 uf, _ := p.x.uniqueFeature(str)
271
272 d.path = []adt.Feature{uf}
273 d.ident = uf
274
275
276 if d.dstNode.IsRecursivelyClosed() {
277 d.path = append(d.path, adt.MakeIdentLabel(p.x.ctx, "#x", ""))
278 }
279 }
280
281
282 func (p *pivotter) makeAlternativeReference(ref *refData, r adt.Resolver) ast.Expr {
283 d := ref.dst
284
285
286
287 var path []adt.Feature
288 if d.parent == nil {
289
290 path = d.path
291 } else {
292 pathLen, pkgRef := relPathLength(r)
293 path = d.node().Path()
294 count := d.stepsToParent()
295
296 switch {
297 case ref.dst.included:
298
299 if count > pathLen {
300
301 return nil
302 }
303
304 case pkgRef:
305
306 default:
307
308 if count >= pathLen {
309 return nil
310 }
311 }
312
313 path = path[len(path)-count:]
314 path = append(d.parent.path, path...)
315 }
316
317 if len(path) == 0 {
318 path = append(path, p.x.ctx.StringLabel("ROOT"))
319 }
320
321 var x ast.Expr = p.x.ident(path[0])
322
323 for _, f := range path[1:] {
324 if f.IsInt() {
325 x = &ast.IndexExpr{
326 X: x,
327 Index: ast.NewLit(token.INT, strconv.Itoa(f.Index())),
328 }
329 } else {
330 x = &ast.SelectorExpr{
331 X: x,
332 Sel: p.x.stringLabel(f),
333 }
334 }
335 }
336
337 return x
338 }
339
340 func (d *depData) stepsToParent() int {
341 parent := d.parent.node()
342 count := 0
343 for p := d.node(); p != parent; p = p.Parent {
344 if p == nil {
345 break
346 }
347 count++
348 }
349 return count
350 }
351
352 func relPathLength(r adt.Resolver) (length int, newRoot bool) {
353 for {
354 var expr adt.Expr
355 switch x := r.(type) {
356 case *adt.FieldReference,
357 *adt.DynamicReference,
358 *adt.LetReference,
359 *adt.ValueReference:
360 length++
361 case *adt.ImportReference:
362
363
364 return length, true
365 case *adt.SelectorExpr:
366 length++
367 expr = x.X
368 case *adt.IndexExpr:
369 length++
370 expr = x.X
371 }
372
373 switch x := expr.(type) {
374 case nil:
375 return length, false
376 case adt.Resolver:
377 r = x
378 default:
379 panic("unreachable")
380 }
381 }
382 }
383
384
385
386
387 func (p *pivotter) refExpr(r adt.Resolver) ast.Expr {
388 ref, ok := p.refMap[r]
389 if !ok {
390 return nil
391 }
392
393 dst := ref.dst
394 n := dst.node()
395
396
397
398
399 switch {
400 case dst.included:
401
402
403
404
405 case p.x.inDefinition == 0 && n.IsRecursivelyClosed():
406
407 case dst.usageCount() == 0:
408
409 case n.IsErr():
410
411 case !n.IsConcrete() && p.x.inExpression > 0:
412
413 case dst.usageCount() == 1 && p.x.inExpression == 0:
414
415 fallthrough
416 case n.IsConcrete() && len(n.Arcs) == 0:
417
418 return p.x.expr(nil, n)
419 }
420
421 if r := p.makeAlternativeReference(ref, r); r != nil {
422 dst.needTopLevel = true
423 return r
424 }
425
426 return nil
427 }
428
429
430 func (p *pivotter) addExternal(d *depData) {
431 if !d.needTopLevel {
432 return
433 }
434
435 expr := p.x.expr(nil, d.node())
436
437 if len(d.path) > 1 {
438 expr = ast.NewStruct(&ast.Field{
439 Label: p.x.stringLabel(d.path[1]),
440 Value: expr,
441 })
442 }
443 let := &ast.LetClause{
444 Ident: p.x.ident(d.path[0]),
445 Expr: expr,
446 }
447
448 ast.SetRelPos(let, token.NewSection)
449
450 path := p.x.ctx.PathToString(d.node().Path())
451 var msg string
452 if d.dstImport == nil {
453 msg = fmt.Sprintf("//cue:path: %s", path)
454 } else {
455 pkg := d.dstImport.ImportPath.SelectorString(p.x.ctx)
456 if path == "" {
457 msg = fmt.Sprintf("//cue:path: %s", pkg)
458 } else {
459 msg = fmt.Sprintf("//cue:path: %s.%s", pkg, path)
460 }
461 }
462 cg := &ast.CommentGroup{
463 Doc: true,
464 List: []*ast.Comment{{Text: msg}},
465 }
466 ast.SetRelPos(cg, token.NewSection)
467 ast.AddComment(let, cg)
468
469 p.decls = append(p.decls, let)
470 }
471
View as plain text