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  }