cuelang.org/go@v0.10.1/internal/core/export/export.go (about) 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 15 package export 16 17 import ( 18 "fmt" 19 "math/rand" 20 21 "cuelang.org/go/cue/ast" 22 "cuelang.org/go/cue/ast/astutil" 23 "cuelang.org/go/cue/errors" 24 "cuelang.org/go/internal" 25 "cuelang.org/go/internal/core/adt" 26 "cuelang.org/go/internal/core/eval" 27 "cuelang.org/go/internal/core/walk" 28 ) 29 30 const debug = false 31 32 type Profile struct { 33 Simplify bool 34 35 // Final reports incomplete errors as errors. 36 Final bool 37 38 // TakeDefaults is used in Value mode to drop non-default values. 39 TakeDefaults bool 40 41 ShowOptional bool 42 ShowDefinitions bool 43 44 // ShowHidden forces the inclusion of hidden fields when these would 45 // otherwise be omitted. Only hidden fields from the current package are 46 // included. 47 ShowHidden bool 48 ShowDocs bool 49 ShowAttributes bool 50 51 // ShowErrors treats errors as values and will not percolate errors up. 52 // 53 // TODO: convert this option to an error level instead, showing only 54 // errors below a certain severity. 55 ShowErrors bool 56 57 // Use unevaluated conjuncts for these error types 58 // IgnoreRecursive 59 60 // SelfContained exports a schema such that it does not rely on any imports. 61 SelfContained bool 62 63 // Fragment disables printing a value as self contained. To successfully 64 // parse a fragment, the compiler needs to be given a scope with the value 65 // from which the fragment was extracted. 66 Fragment bool 67 68 // AddPackage causes a package clause to be added. 69 AddPackage bool 70 71 // InlineImports expands references to non-builtin packages. 72 InlineImports bool 73 } 74 75 var Simplified = &Profile{ 76 Simplify: true, 77 ShowDocs: true, 78 } 79 80 var Final = &Profile{ 81 Simplify: true, 82 TakeDefaults: true, 83 Final: true, 84 } 85 86 var Raw = &Profile{ 87 ShowOptional: true, 88 ShowDefinitions: true, 89 ShowHidden: true, 90 ShowDocs: true, 91 AddPackage: true, 92 } 93 94 var All = &Profile{ 95 Simplify: true, 96 ShowOptional: true, 97 ShowDefinitions: true, 98 ShowHidden: true, 99 ShowDocs: true, 100 ShowAttributes: true, 101 AddPackage: true, 102 } 103 104 // Concrete 105 106 // Def exports v as a definition. 107 // It resolves references that point outside any of the vertices in v. 108 func Def(r adt.Runtime, pkgID string, v *adt.Vertex) (*ast.File, errors.Error) { 109 return All.Def(r, pkgID, v) 110 } 111 112 // Def exports v as a definition. 113 // It resolves references that point outside any of the vertices in v. 114 func (p *Profile) Def(r adt.Runtime, pkgID string, v *adt.Vertex) (f *ast.File, err errors.Error) { 115 e := newExporter(p, r, pkgID, v) 116 e.initPivot(v) 117 118 isDef := v.IsRecursivelyClosed() 119 if isDef { 120 e.inDefinition++ 121 } 122 123 expr := e.expr(nil, v) 124 125 switch isDef { 126 case true: 127 e.inDefinition-- 128 129 // This eliminates the need to wrap in _#def in the most common cases, 130 // while ensuring only one level of _#def wrapping is ever used. 131 if st, ok := expr.(*ast.StructLit); ok { 132 for _, elem := range st.Elts { 133 if d, ok := elem.(*ast.EmbedDecl); ok { 134 if isDefinitionReference(d.Expr) { 135 return e.finalize(v, expr) 136 } 137 } 138 } 139 } 140 141 // TODO: embed an empty definition instead once we verify that this 142 // preserves semantics. 143 if v.Kind() == adt.StructKind && !p.Fragment { 144 expr = ast.NewStruct( 145 ast.Embed(ast.NewIdent("_#def")), 146 ast.NewIdent("_#def"), expr, 147 ) 148 } 149 } 150 151 return e.finalize(v, expr) 152 } 153 154 func isDefinitionReference(x ast.Expr) bool { 155 switch x := x.(type) { 156 case *ast.Ident: 157 if internal.IsDef(x.Name) { 158 return true 159 } 160 case *ast.SelectorExpr: 161 if internal.IsDefinition(x.Sel) { 162 return true 163 } 164 return isDefinitionReference(x.X) 165 case *ast.IndexExpr: 166 return isDefinitionReference(x.X) 167 } 168 return false 169 } 170 171 // Expr exports the given unevaluated expression (schema mode). 172 // It does not resolve references that point outside the given expression. 173 func Expr(r adt.Runtime, pkgID string, n adt.Expr) (ast.Expr, errors.Error) { 174 return Simplified.Expr(r, pkgID, n) 175 } 176 177 // Expr exports the given unevaluated expression (schema mode). 178 // It does not resolve references that point outside the given expression. 179 func (p *Profile) Expr(r adt.Runtime, pkgID string, n adt.Expr) (ast.Expr, errors.Error) { 180 e := newExporter(p, r, pkgID, nil) 181 182 return e.expr(nil, n), nil 183 } 184 185 func (e *exporter) toFile(v *adt.Vertex, x ast.Expr) *ast.File { 186 f := &ast.File{} 187 188 if e.cfg.AddPackage { 189 pkgName := "" 190 pkg := &ast.Package{} 191 v.VisitLeafConjuncts(func(c adt.Conjunct) bool { 192 f, _ := c.Source().(*ast.File) 193 if f == nil { 194 return true 195 } 196 197 if name := f.PackageName(); name != "" { 198 pkgName = name 199 } 200 201 if e.cfg.ShowDocs { 202 if doc := internal.FileComment(f); doc != nil { 203 ast.AddComment(pkg, doc) 204 } 205 } 206 return true 207 }) 208 209 if pkgName != "" { 210 pkg.Name = ast.NewIdent(pkgName) 211 f.Decls = append(f.Decls, pkg) 212 } 213 } 214 215 switch st := x.(type) { 216 case nil: 217 panic("null input") 218 219 case *ast.StructLit: 220 f.Decls = append(f.Decls, st.Elts...) 221 222 default: 223 f.Decls = append(f.Decls, &ast.EmbedDecl{Expr: x}) 224 } 225 226 return f 227 } 228 229 // Vertex exports evaluated values (data mode). 230 // It resolves incomplete references that point outside the current context. 231 func Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (*ast.File, errors.Error) { 232 return Simplified.Vertex(r, pkgID, n) 233 } 234 235 // Vertex exports evaluated values (data mode). 236 // It resolves incomplete references that point outside the current context. 237 func (p *Profile) Vertex(r adt.Runtime, pkgID string, n *adt.Vertex) (f *ast.File, err errors.Error) { 238 e := newExporter(p, r, pkgID, n) 239 e.initPivot(n) 240 241 v := e.value(n, n.Conjuncts...) 242 return e.finalize(n, v) 243 } 244 245 // Value exports evaluated values (data mode). 246 // It does not resolve references that point outside the given Value. 247 func Value(r adt.Runtime, pkgID string, n adt.Value) (ast.Expr, errors.Error) { 248 return Simplified.Value(r, pkgID, n) 249 } 250 251 // Value exports evaluated values (data mode). 252 // 253 // It does not resolve references that point outside the given Value. 254 // 255 // TODO: Should take context. 256 func (p *Profile) Value(r adt.Runtime, pkgID string, n adt.Value) (ast.Expr, errors.Error) { 257 e := newExporter(p, r, pkgID, n) 258 v := e.value(n) 259 return v, e.errs 260 } 261 262 type exporter struct { 263 cfg *Profile // Make value todo 264 errs errors.Error 265 266 ctx *adt.OpContext 267 268 index adt.StringIndexer 269 rand *rand.Rand 270 271 // For resolving references. 272 stack []frame 273 274 inDefinition int // for close() wrapping. 275 inExpression int // for inlining decisions. 276 277 // hidden label handling 278 pkgID string 279 // pkgHash is used when mangling hidden identifiers of packages that are 280 // inlined. 281 pkgHash map[string]string 282 283 // If a used feature maps to an expression, it means it is assigned to a 284 // unique let expression. 285 usedFeature map[adt.Feature]adt.Expr 286 labelAlias map[adt.Expr]adt.Feature 287 valueAlias map[*ast.Alias]*ast.Alias 288 // fieldAlias is used to track original alias names of regular fields. 289 fieldAlias map[*ast.Field]fieldAndScope 290 letAlias map[*ast.LetClause]*ast.LetClause 291 references map[*adt.Vertex]*referenceInfo 292 293 pivotter *pivotter 294 } 295 296 type fieldAndScope struct { 297 field *ast.Field 298 scope ast.Node // StructLit or File 299 } 300 301 // referenceInfo is used to track which Field.Value fields should be linked 302 // to Ident.Node fields. The Node field is used by astutil.Resolve to mark 303 // the value in the AST to which the respective identifier points. 304 // astutil.Sanitize, in turn, uses this information to determine whether 305 // a reference is shadowed and apply fixes accordingly. 306 type referenceInfo struct { 307 field *ast.Field 308 references []*ast.Ident 309 } 310 311 // linkField reports the Field that represents certain Vertex in the generated 312 // output. The Node fields for any references (*ast.Ident) that were already 313 // recorded as pointed to this vertex are updated accordingly. 314 func (e *exporter) linkField(v *adt.Vertex, f *ast.Field) { 315 if v == nil { 316 return 317 } 318 refs := e.references[v] 319 if refs == nil { 320 // TODO(perf): do a first sweep to only mark referenced arcs or keep 321 // track of that information elsewhere. 322 e.references[v] = &referenceInfo{field: f} 323 return 324 } 325 for _, r := range refs.references { 326 r.Node = f.Value 327 } 328 refs.references = refs.references[:0] 329 } 330 331 // linkIdentifier reports the Vertex to which indent points. Once the ast.Field 332 // for a corresponding Vertex is known, it is linked to ident. 333 func (e *exporter) linkIdentifier(v *adt.Vertex, ident *ast.Ident) { 334 refs := e.references[v] 335 if refs == nil { 336 refs = &referenceInfo{} 337 e.references[v] = refs 338 } 339 if refs.field == nil { 340 refs.references = append(refs.references, ident) 341 return 342 } 343 ident.Node = refs.field.Value 344 } 345 346 // newExporter creates and initializes an exporter. 347 func newExporter(p *Profile, r adt.Runtime, pkgID string, v adt.Value) *exporter { 348 n, _ := v.(*adt.Vertex) 349 e := &exporter{ 350 cfg: p, 351 ctx: eval.NewContext(r, n), 352 index: r, 353 pkgID: pkgID, 354 355 references: map[*adt.Vertex]*referenceInfo{}, 356 } 357 358 e.markUsedFeatures(v) 359 360 return e 361 } 362 363 // initPivot initializes the pivotter to allow aligning a configuration around 364 // a new root, if needed. 365 func (e *exporter) initPivot(n *adt.Vertex) { 366 switch { 367 case e.cfg.SelfContained, e.cfg.InlineImports: 368 // Explicitly enabled. 369 case n.Parent == nil, e.cfg.Fragment: 370 return 371 } 372 e.initPivotter(n) 373 } 374 375 // finalize finalizes the result of an export. It is only needed for use cases 376 // that require conversion to a File, Sanitization, and self containment. 377 func (e *exporter) finalize(n *adt.Vertex, v ast.Expr) (f *ast.File, err errors.Error) { 378 f = e.toFile(n, v) 379 380 e.completePivot(f) 381 382 if err := astutil.Sanitize(f); err != nil { 383 err := errors.Promote(err, "export") 384 return f, errors.Append(e.errs, err) 385 } 386 387 return f, nil 388 } 389 390 func (e *exporter) markUsedFeatures(x adt.Expr) { 391 e.usedFeature = make(map[adt.Feature]adt.Expr) 392 393 w := &walk.Visitor{} 394 w.Before = func(n adt.Node) bool { 395 switch x := n.(type) { 396 case *adt.Vertex: 397 if !x.IsData() { 398 x.VisitLeafConjuncts(func(c adt.Conjunct) bool { 399 w.Elem(c.Elem()) 400 return true 401 }) 402 } 403 404 case *adt.DynamicReference: 405 if e.labelAlias == nil { 406 e.labelAlias = make(map[adt.Expr]adt.Feature) 407 } 408 // TODO: add preferred label. 409 e.labelAlias[x.Label] = adt.InvalidLabel 410 411 case *adt.LabelReference: 412 } 413 return true 414 } 415 416 w.Feature = func(f adt.Feature, src adt.Node) { 417 _, ok := e.usedFeature[f] 418 419 switch x := src.(type) { 420 case *adt.LetReference: 421 if !ok { 422 e.usedFeature[f] = x.X 423 } 424 425 default: 426 e.usedFeature[f] = nil 427 } 428 } 429 430 w.Elem(x) 431 } 432 433 func (e *exporter) getFieldAlias(f *ast.Field, name string) string { 434 a, ok := f.Label.(*ast.Alias) 435 if !ok { 436 a = &ast.Alias{ 437 Ident: ast.NewIdent(e.uniqueAlias(name)), 438 Expr: f.Label.(ast.Expr), 439 } 440 f.Label = a 441 } 442 return a.Ident.Name 443 } 444 445 func setFieldAlias(f *ast.Field, name string) { 446 if _, ok := f.Label.(*ast.Alias); !ok { 447 x := f.Label.(ast.Expr) 448 f.Label = &ast.Alias{ 449 Ident: ast.NewIdent(name), 450 Expr: x, 451 } 452 ast.SetComments(f.Label, ast.Comments(x)) 453 ast.SetComments(x, nil) 454 // TODO: move position information. 455 } 456 } 457 458 func (e *exporter) markLets(n ast.Node, scope *ast.StructLit) { 459 if n == nil { 460 return 461 } 462 ast.Walk(n, func(n ast.Node) bool { 463 switch v := n.(type) { 464 case *ast.StructLit: 465 e.markLetDecls(v.Elts, scope) 466 case *ast.File: 467 e.markLetDecls(v.Decls, scope) 468 // TODO: return true here and false for everything else? 469 470 case *ast.Field, 471 *ast.LetClause, 472 *ast.IfClause, 473 *ast.ForClause, 474 *ast.Comprehension: 475 return false 476 } 477 return true 478 }, nil) 479 } 480 481 func (e *exporter) markLetDecls(decls []ast.Decl, scope *ast.StructLit) { 482 for _, d := range decls { 483 switch x := d.(type) { 484 case *ast.Field: 485 e.prepareAliasedField(x, scope) 486 case *ast.LetClause: 487 e.markLetAlias(x) 488 } 489 } 490 } 491 492 // prepareAliasField creates an aliased ast.Field. It is done so before 493 // recursively processing any of the fields so that a processed field that 494 // occurs earlier in a struct can already refer to it. 495 // 496 // It is assumed that the same alias names can be used. We rely on Sanitize 497 // to do any renaming of aliases in case of shadowing. 498 func (e *exporter) prepareAliasedField(f *ast.Field, scope ast.Node) { 499 if _, ok := e.fieldAlias[f]; ok { 500 return 501 } 502 503 alias, ok := f.Label.(*ast.Alias) 504 if !ok { 505 return // not aliased 506 } 507 field := &ast.Field{ 508 Label: &ast.Alias{ 509 Ident: ast.NewIdent(alias.Ident.Name), 510 Expr: alias.Expr, 511 }, 512 } 513 514 if e.fieldAlias == nil { 515 e.fieldAlias = make(map[*ast.Field]fieldAndScope) 516 } 517 518 e.fieldAlias[f] = fieldAndScope{field: field, scope: scope} 519 } 520 521 func (e *exporter) getFixedField(f *adt.Field) *ast.Field { 522 if f.Src != nil { 523 if entry, ok := e.fieldAlias[f.Src]; ok { 524 return entry.field 525 } 526 } 527 return &ast.Field{ 528 Label: e.stringLabel(f.Label), 529 } 530 } 531 532 // markLetAlias inserts an uninitialized let clause into the current scope. 533 // It gets initialized upon first usage. 534 func (e *exporter) markLetAlias(x *ast.LetClause) { 535 // The created let clause is initialized upon first usage, and removed 536 // later if never referenced. 537 let := &ast.LetClause{} 538 539 if e.letAlias == nil { 540 e.letAlias = make(map[*ast.LetClause]*ast.LetClause) 541 } 542 e.letAlias[x] = let 543 544 scope := e.top().scope 545 scope.Elts = append(scope.Elts, let) 546 } 547 548 // In value mode, lets are only used if there wasn't an error. 549 func filterUnusedLets(s *ast.StructLit) { 550 k := 0 551 for i, d := range s.Elts { 552 if let, ok := d.(*ast.LetClause); ok && let.Expr == nil { 553 continue 554 } 555 s.Elts[k] = s.Elts[i] 556 k++ 557 } 558 s.Elts = s.Elts[:k] 559 } 560 561 // resolveLet actually parses the let expression. 562 // If there was no recorded let expression, it expands the expression in place. 563 func (e *exporter) resolveLet(env *adt.Environment, x *adt.LetReference) ast.Expr { 564 letClause, _ := x.Src.Node.(*ast.LetClause) 565 let := e.letAlias[letClause] 566 567 switch { 568 case let == nil: 569 ref, _ := e.ctx.Lookup(env, x) 570 if ref == nil { 571 // This can happen if x.X does not resolve to a valid value. At this 572 // point we will not get a valid configuration. 573 574 // TODO: get rid of the use of x.X. 575 // str := x.Label.IdentString(e.ctx) 576 // ident := ast.NewIdent(str) 577 // return ident 578 579 return e.expr(env, x.X) 580 } 581 c, _ := ref.SingleConjunct() 582 return e.expr(c.EnvExpr()) 583 584 case let.Expr == nil: 585 label := e.uniqueLetIdent(x.Label, x.X) 586 587 let.Ident = e.ident(label) 588 let.Expr = e.expr(env, x.X) 589 } 590 591 ident := ast.NewIdent(let.Ident.Name) 592 ident.Node = let 593 // TODO: set scope? 594 return ident 595 } 596 597 func (e *exporter) uniqueLetIdent(f adt.Feature, x adt.Expr) adt.Feature { 598 if e.usedFeature[f] == x { 599 return f 600 } 601 602 f, _ = e.uniqueFeature(f.IdentString(e.ctx)) 603 e.usedFeature[f] = x 604 return f 605 } 606 607 func (e *exporter) uniqueAlias(name string) string { 608 f := adt.MakeIdentLabel(e.ctx, name, "") 609 610 if _, ok := e.usedFeature[f]; !ok { 611 e.usedFeature[f] = nil 612 return name 613 } 614 615 _, name = e.uniqueFeature(f.IdentString(e.ctx)) 616 return name 617 } 618 619 // A featureSet implements a set of Features. It only supports testing 620 // whether a given string is available as a Feature. 621 type featureSet interface { 622 // intn returns a pseudo-random integer in [0..n). 623 intn(n int) int 624 625 // makeFeature converts s to f if it is available. 626 makeFeature(s string) (f adt.Feature, ok bool) 627 } 628 629 func (e *exporter) intn(n int) int { 630 return e.rand.Intn(n) 631 } 632 633 func (e *exporter) makeFeature(s string) (f adt.Feature, ok bool) { 634 f = adt.MakeIdentLabel(e.ctx, s, "") 635 _, exists := e.usedFeature[f] 636 if !exists { 637 e.usedFeature[f] = nil 638 } 639 return f, !exists 640 } 641 642 // uniqueFeature returns a name for an identifier that uniquely identifies 643 // the given expression. If the preferred name is already taken, a new globally 644 // unique name of the form base_N ... base_NNNNNNNNNNNNNN is generated. 645 // 646 // It prefers short extensions over large ones, while ensuring the likelihood of 647 // fast termination is high. There are at least two digits to make it visually 648 // clearer this concerns a generated number. 649 func (e *exporter) uniqueFeature(base string) (f adt.Feature, name string) { 650 if e.rand == nil { 651 e.rand = rand.New(rand.NewSource(808)) 652 } 653 return findUnique(e, base) 654 } 655 656 func findUnique(set featureSet, base string) (f adt.Feature, name string) { 657 if f, ok := set.makeFeature(base); ok { 658 return f, base 659 } 660 661 // Try the first few numbers in sequence. 662 for i := 1; i < 5; i++ { 663 name := fmt.Sprintf("%s_%01X", base, i) 664 if f, ok := set.makeFeature(name); ok { 665 return f, name 666 } 667 } 668 669 const mask = 0xff_ffff_ffff_ffff // max bits; stay clear of int64 overflow 670 const shift = 4 // rate of growth 671 digits := 1 672 for n := int64(0x10); ; n = int64(mask&((n<<shift)-1)) + 1 { 673 num := set.intn(int(n)-1) + 1 674 name := fmt.Sprintf("%[1]s_%0[2]*[3]X", base, digits, num) 675 if f, ok := set.makeFeature(name); ok { 676 return f, name 677 } 678 digits++ 679 } 680 } 681 682 type frame struct { 683 node *adt.Vertex 684 685 scope *ast.StructLit 686 687 docSources []adt.Conjunct 688 689 // For resolving pattern constraints fields labels 690 field *ast.Field 691 labelExpr ast.Expr 692 693 dynamicFields []*entry 694 695 // for off-by-one handling 696 upCount int32 697 698 // labeled fields 699 fields map[adt.Feature]entry 700 701 // field to new field 702 mapped map[adt.Node]ast.Node 703 } 704 705 type entry struct { 706 alias string 707 field *ast.Field 708 node ast.Node // How to reference. See astutil.Resolve 709 references []*ast.Ident 710 } 711 712 func (e *exporter) addField(label adt.Feature, f *ast.Field, n ast.Node) { 713 frame := e.top() 714 entry := frame.fields[label] 715 entry.field = f 716 entry.node = n 717 frame.fields[label] = entry 718 } 719 720 func (e *exporter) addEmbed(x ast.Expr) { 721 frame := e.top() 722 frame.scope.Elts = append(frame.scope.Elts, x) 723 } 724 725 func (e *exporter) pushFrame(src *adt.Vertex, conjuncts []adt.Conjunct) (s *ast.StructLit, saved []frame) { 726 saved = e.stack 727 s = &ast.StructLit{} 728 e.stack = append(e.stack, frame{ 729 node: src, 730 scope: s, 731 mapped: map[adt.Node]ast.Node{}, 732 fields: map[adt.Feature]entry{}, 733 docSources: conjuncts, 734 }) 735 return s, saved 736 } 737 738 func (e *exporter) popFrame(saved []frame) { 739 top := e.stack[len(e.stack)-1] 740 741 for _, f := range top.fields { 742 node := f.node 743 if f.alias != "" && f.field != nil { 744 setFieldAlias(f.field, f.alias) 745 node = f.field 746 } 747 if node != nil { 748 for _, r := range f.references { 749 r.Node = node 750 } 751 } 752 } 753 754 e.stack = saved 755 } 756 757 func (e *exporter) top() *frame { 758 return &(e.stack[len(e.stack)-1]) 759 } 760 761 func (e *exporter) node() *adt.Vertex { 762 if len(e.stack) == 0 { 763 return empty 764 } 765 n := e.stack[len(e.stack)-1].node 766 if n == nil { 767 return empty 768 } 769 return n 770 } 771 772 func (e *exporter) frame(upCount int32) *frame { 773 for i := len(e.stack) - 1; i >= 0; i-- { 774 f := &(e.stack[i]) 775 if upCount <= (f.upCount - 1) { 776 return f 777 } 778 upCount -= f.upCount 779 } 780 if debug { 781 // This may be valid when exporting incomplete references. These are 782 // not yet handled though, so find a way to catch them when debugging 783 // printing of values that are supposed to be complete. 784 panic("unreachable reference") 785 } 786 787 return &frame{} 788 } 789 790 func (e *exporter) setDocs(x adt.Node) { 791 f := e.stack[len(e.stack)-1] 792 f.docSources = []adt.Conjunct{adt.MakeRootConjunct(nil, x)} 793 e.stack[len(e.stack)-1] = f 794 }