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