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  }