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