...

Source file src/cuelang.org/go/internal/core/export/export.go

Documentation: cuelang.org/go/internal/core/export

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

View as plain text