cuelang.org/go@v0.13.0/internal/core/export/adt.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  	"bytes"
    19  	"cmp"
    20  	"fmt"
    21  	"slices"
    22  	"strings"
    23  
    24  	"cuelang.org/go/cue/ast"
    25  	"cuelang.org/go/cue/ast/astutil"
    26  	"cuelang.org/go/cue/literal"
    27  	"cuelang.org/go/cue/token"
    28  	"cuelang.org/go/internal"
    29  	"cuelang.org/go/internal/core/adt"
    30  )
    31  
    32  func (e *exporter) ident(x adt.Feature) *ast.Ident {
    33  	s := x.IdentString(e.ctx)
    34  	if !ast.IsValidIdent(s) {
    35  		panic(s + " is not a valid identifier")
    36  	}
    37  	return ast.NewIdent(s)
    38  }
    39  
    40  func (e *exporter) adt(env *adt.Environment, expr adt.Elem) ast.Expr {
    41  	switch x := expr.(type) {
    42  	case adt.Value:
    43  		return e.expr(env, x)
    44  
    45  	case *adt.ListLit:
    46  		env := &adt.Environment{Up: env, Vertex: e.node()}
    47  		a := []ast.Expr{}
    48  		for _, x := range x.Elems {
    49  			a = append(a, e.elem(env, x))
    50  		}
    51  		return ast.NewList(a...)
    52  
    53  	case *adt.StructLit:
    54  		// TODO: should we use pushFrame here?
    55  		// _, saved := e.pushFrame([]adt.Conjunct{adt.MakeConjunct(nil, x)})
    56  		// defer e.popFrame(saved)
    57  		// s := e.frame(0).scope
    58  
    59  		s := &ast.StructLit{}
    60  		// TODO: ensure e.node() is set in more cases. Right now it is not
    61  		// always set in mergeValues, even in cases where it could be. Better
    62  		// to be conservative for now, though.
    63  		env := &adt.Environment{Up: env, Vertex: e.node()}
    64  
    65  		for _, d := range x.Decls {
    66  			var a *ast.Alias
    67  			if orig, ok := d.Source().(*ast.Field); ok {
    68  				if alias, ok := orig.Value.(*ast.Alias); ok {
    69  					if e.valueAlias == nil {
    70  						e.valueAlias = map[*ast.Alias]*ast.Alias{}
    71  					}
    72  					a = &ast.Alias{Ident: ast.NewIdent(alias.Ident.Name)}
    73  					e.valueAlias[alias] = a
    74  				}
    75  			}
    76  			decl := e.decl(env, d)
    77  
    78  			// decl may be nil if it represents a let. Lets are added later, and
    79  			// only when they are still used.
    80  			if decl == nil {
    81  				continue
    82  			}
    83  
    84  			if e.cfg.ShowDocs {
    85  				ast.SetComments(decl, filterDocs(ast.Comments(d.Source())))
    86  			}
    87  			// TODO: use e.copyMeta for positions, but only when the original
    88  			// source is available.
    89  
    90  			if a != nil {
    91  				if f, ok := decl.(*ast.Field); ok {
    92  					a.Expr = f.Value
    93  					f.Value = a
    94  				}
    95  			}
    96  
    97  			s.Elts = append(s.Elts, decl)
    98  		}
    99  
   100  		return s
   101  
   102  	// TODO: why does LabelReference not implement resolve?
   103  	case *adt.LabelReference:
   104  		// get potential label from Source. Otherwise use X.
   105  		v, ok := e.ctx.Evaluate(env, x)
   106  		f := e.frame(x.UpCount)
   107  		if ok && (adt.IsConcrete(v) || f.field == nil) {
   108  			return e.value(v)
   109  		}
   110  		if f.field == nil {
   111  			// This can happen when the LabelReference is evaluated outside of
   112  			// normal evaluation, that is, if a pattern constraint or
   113  			// additional constraint is evaluated by itself.
   114  			return ast.NewIdent("string")
   115  		}
   116  		list, ok := f.field.Label.(*ast.ListLit)
   117  		if !ok || len(list.Elts) != 1 {
   118  			panic("label reference to non-pattern constraint field or invalid list")
   119  		}
   120  		name := ""
   121  		if a, ok := list.Elts[0].(*ast.Alias); ok {
   122  			name = a.Ident.Name
   123  		} else {
   124  			if x.Src != nil {
   125  				name = x.Src.Name
   126  			}
   127  			name = e.uniqueAlias(name)
   128  			list.Elts[0] = &ast.Alias{
   129  				Ident: ast.NewIdent(name),
   130  				Expr:  list.Elts[0],
   131  			}
   132  		}
   133  		ident := ast.NewIdent(name)
   134  		ident.Scope = f.field
   135  		ident.Node = f.labelExpr
   136  		return ident
   137  
   138  	case adt.Resolver:
   139  		return e.resolve(env, x)
   140  
   141  	case *adt.SliceExpr:
   142  		var lo, hi ast.Expr
   143  		if x.Lo != nil {
   144  			lo = e.innerExpr(env, x.Lo)
   145  		}
   146  		if x.Hi != nil {
   147  			hi = e.innerExpr(env, x.Hi)
   148  		}
   149  		// TODO: Stride not yet? implemented.
   150  		// if x.Stride != nil {
   151  		// 	stride = e.innerExpr(env, x.Stride)
   152  		// }
   153  		return &ast.SliceExpr{X: e.innerExpr(env, x.X), Low: lo, High: hi}
   154  
   155  	case *adt.Interpolation:
   156  		var (
   157  			tripple    = `"""`
   158  			openQuote  = `"`
   159  			closeQuote = `"`
   160  			f          = literal.String
   161  		)
   162  		if x.K&adt.BytesKind != 0 {
   163  			tripple = `'''`
   164  			openQuote = `'`
   165  			closeQuote = `'`
   166  			f = literal.Bytes
   167  		}
   168  		toString := func(v adt.Expr) string {
   169  			str := ""
   170  			switch x := v.(type) {
   171  			case *adt.String:
   172  				str = x.Str
   173  			case *adt.Bytes:
   174  				str = string(x.B)
   175  			}
   176  			return str
   177  		}
   178  		t := &ast.Interpolation{}
   179  		f = f.WithGraphicOnly()
   180  		indent := ""
   181  		// TODO: mark formatting in interpolation itself.
   182  		for i := 0; i < len(x.Parts); i += 2 {
   183  			if strings.IndexByte(toString(x.Parts[i]), '\n') >= 0 {
   184  				f = f.WithTabIndent(len(e.stack))
   185  				indent = strings.Repeat("\t", len(e.stack))
   186  				openQuote = tripple + "\n" + indent
   187  				closeQuote = tripple
   188  				break
   189  			}
   190  		}
   191  		prefix := openQuote
   192  		suffix := `\(`
   193  		for i, elem := range x.Parts {
   194  			if i%2 == 1 {
   195  				t.Elts = append(t.Elts, e.innerExpr(env, elem))
   196  			} else {
   197  				// b := strings.Builder{}
   198  				buf := []byte(prefix)
   199  				str := toString(elem)
   200  				buf = f.AppendEscaped(buf, str)
   201  				if i == len(x.Parts)-1 {
   202  					if len(closeQuote) > 1 {
   203  						buf = append(buf, '\n')
   204  						buf = append(buf, indent...)
   205  					}
   206  					buf = append(buf, closeQuote...)
   207  				} else {
   208  					if bytes.HasSuffix(buf, []byte("\n")) {
   209  						buf = append(buf, indent...)
   210  					}
   211  					buf = append(buf, suffix...)
   212  				}
   213  				t.Elts = append(t.Elts, &ast.BasicLit{
   214  					Kind:  token.STRING,
   215  					Value: string(buf),
   216  				})
   217  			}
   218  			prefix = ")"
   219  		}
   220  		return t
   221  
   222  	case *adt.BoundExpr:
   223  		return &ast.UnaryExpr{
   224  			Op: x.Op.Token(),
   225  			X:  e.innerExpr(env, x.Expr),
   226  		}
   227  
   228  	case *adt.UnaryExpr:
   229  		return &ast.UnaryExpr{
   230  			Op: x.Op.Token(),
   231  			X:  e.innerExpr(env, x.X),
   232  		}
   233  
   234  	case *adt.BinaryExpr:
   235  		if x.Op == adt.AndOp || x.Op == adt.OrOp {
   236  			return e.sortBinaryTree(env, x)
   237  		}
   238  		return &ast.BinaryExpr{
   239  			Op: x.Op.Token(),
   240  			X:  e.innerExpr(env, x.X),
   241  			Y:  e.innerExpr(env, x.Y),
   242  		}
   243  
   244  	case *adt.CallExpr:
   245  		a := []ast.Expr{}
   246  		for _, arg := range x.Args {
   247  			v := e.innerExpr(env, arg)
   248  			if v == nil {
   249  				e.innerExpr(env, arg)
   250  				panic("")
   251  			}
   252  			a = append(a, v)
   253  		}
   254  		fun := e.innerExpr(env, x.Fun)
   255  		return &ast.CallExpr{Fun: fun, Args: a}
   256  
   257  	case *adt.DisjunctionExpr:
   258  		a := []ast.Expr{}
   259  		for _, d := range x.Values {
   260  			v := e.expr(env, d.Val)
   261  			if d.Default {
   262  				v = &ast.UnaryExpr{Op: token.MUL, X: v}
   263  			}
   264  			a = append(a, v)
   265  		}
   266  		return ast.NewBinExpr(token.OR, a...)
   267  
   268  	case *adt.ConjunctGroup:
   269  		a := []ast.Expr{}
   270  		for _, c := range *x {
   271  			v := e.expr(c.EnvExpr())
   272  			a = append(a, v)
   273  		}
   274  		return ast.NewBinExpr(token.AND, a...)
   275  
   276  	case *adt.Comprehension:
   277  		if !x.DidResolve() {
   278  			return dummyTop
   279  		}
   280  		for _, c := range x.Clauses {
   281  			switch c.(type) {
   282  			case *adt.ForClause:
   283  				env = &adt.Environment{Up: env, Vertex: empty}
   284  			case *adt.IfClause:
   285  			case *adt.LetClause:
   286  				env = &adt.Environment{Up: env, Vertex: empty}
   287  			case *adt.ValueClause:
   288  				// Can occur in nested comprehenions.
   289  				env = &adt.Environment{Up: env, Vertex: empty}
   290  			default:
   291  				panic("unreachable")
   292  			}
   293  		}
   294  
   295  		// If this is an "unwrapped" comprehension, we need to also
   296  		// account for the curly braces of the original comprehension.
   297  		if x.Nest() > 0 {
   298  			env = &adt.Environment{Up: env, Vertex: empty}
   299  		}
   300  
   301  		// TODO: consider using adt.EnvExpr.
   302  		return e.adt(env, adt.ToExpr(x.Value))
   303  
   304  	default:
   305  		panic(fmt.Sprintf("unknown field %T", x))
   306  	}
   307  }
   308  
   309  // sortBinaryTree converte x to a binary tree and sorts it's elements
   310  // using sortLeafAdt.
   311  func (e *exporter) sortBinaryTree(env *adt.Environment, x *adt.BinaryExpr) (b ast.Expr) {
   312  	var exprs []adt.Node
   313  
   314  	var flatten func(expr adt.Expr)
   315  	flatten = func(expr adt.Expr) {
   316  		if y, ok := expr.(*adt.BinaryExpr); ok && x.Op == y.Op {
   317  			flatten(y.X)
   318  			flatten(y.Y)
   319  		} else {
   320  			exprs = append(exprs, expr)
   321  		}
   322  	}
   323  	flatten(x)
   324  
   325  	// Sort the expressions
   326  	slices.SortStableFunc(exprs, cmpLeafNodes)
   327  
   328  	nodes := make([]ast.Expr, 0, len(exprs))
   329  	for _, x := range exprs {
   330  		switch y := x.(type) {
   331  		case *adt.Top:
   332  		case *adt.BasicType:
   333  			if y.K != adt.TopKind {
   334  				nodes = append(nodes, e.expr(env, y))
   335  			}
   336  		default:
   337  			nodes = append(nodes, e.innerExpr(env, y.(adt.Expr)))
   338  		}
   339  	}
   340  
   341  	if len(nodes) == 0 {
   342  		return e.adt(env, &adt.Top{})
   343  	}
   344  
   345  	return ast.NewBinExpr(x.Op.Token(), nodes...)
   346  }
   347  
   348  // cmpConjuncts compares two Conjunct based on their element using cmpLeafNodes.
   349  func cmpConjuncts(a, b adt.Conjunct) int {
   350  	return cmpLeafNodes(a.Expr(), b.Expr())
   351  }
   352  
   353  // cmpLeafNodes compares two adt.Expr values. The values may not be a binary
   354  // expressions. It returns true if a is less than b.
   355  func cmpLeafNodes[T adt.Node](a, b T) int {
   356  	if c := cmp.Compare(typeOrder(a), typeOrder(b)); c != 0 {
   357  		return c
   358  	}
   359  
   360  	srcA := a.Source()
   361  	srcB := b.Source()
   362  
   363  	if srcA == nil || srcB == nil {
   364  		// TODO: some tie breaker
   365  		return 0
   366  	}
   367  
   368  	return srcA.Pos().Compare(srcB.Pos())
   369  }
   370  
   371  func typeOrder(x adt.Node) int {
   372  	switch x.(type) {
   373  	case *adt.Top:
   374  		return 0
   375  	case *adt.BasicType:
   376  		return 1
   377  	case *adt.FieldReference:
   378  		return 2 // sometimes basic types are represented as field references.
   379  	case *adt.Bool, *adt.Null, *adt.Num, *adt.String, *adt.Bytes:
   380  		return 10
   381  	case *adt.BoundValue:
   382  		return 20
   383  	case *adt.StructLit, *adt.ListLit:
   384  		return 500
   385  	case adt.Expr:
   386  		return 25
   387  	default:
   388  		return 100
   389  	}
   390  }
   391  
   392  var dummyTop = &ast.Ident{Name: "_"}
   393  
   394  func (e *exporter) resolve(env *adt.Environment, r adt.Resolver) ast.Expr {
   395  	if c := e.pivotter; c != nil {
   396  		if alt := c.refExpr(r); alt != nil {
   397  			return alt
   398  		}
   399  	}
   400  
   401  	switch x := r.(type) {
   402  	case *adt.FieldReference:
   403  		// Special case when the original CUE already had an alias.
   404  		if x.Src != nil {
   405  			if f, ok := x.Src.Node.(*ast.Field); ok {
   406  				if entry, ok := e.fieldAlias[f]; ok {
   407  					ident := ast.NewIdent(aliasFromLabel(f))
   408  					ident.Node = entry.field
   409  					ident.Scope = entry.scope
   410  					return ident
   411  				}
   412  			}
   413  		}
   414  
   415  		ident, _ := e.newIdentForField(x.Src, x.Label, x.UpCount)
   416  
   417  		// Use low-level lookup to bypass structural cycle detection. This is
   418  		// fine as we do not recurse on the result and it is necessary to detect
   419  		// shadowing even when a configuration has a structural cycle.
   420  		for i := 0; i < int(x.UpCount); i++ {
   421  			env = env.Up
   422  		}
   423  
   424  		// Exclude comprehensions and other temporary/ inlined Vertices, which
   425  		// cannot be properly resolved, throwing off the sanitize. Also,
   426  		// comprehensions originate from a single source and do not need to be
   427  		// handled.
   428  		if env != nil { // for generated stuff
   429  			// TODO: note that env.Vertex should never be nil; investigate and replace the nil check below.
   430  			if v := env.Vertex; v != nil && !v.IsDynamic {
   431  				if v = v.Lookup(x.Label); v != nil {
   432  					e.linkIdentifier(v, ident)
   433  				}
   434  			}
   435  		}
   436  
   437  		return ident
   438  
   439  	case *adt.ValueReference:
   440  		name := x.Label.IdentString(e.ctx)
   441  		if a, ok := x.Src.Node.(*ast.Alias); ok { // Should always pass
   442  			if b, ok := e.valueAlias[a]; ok {
   443  				name = b.Ident.Name
   444  			}
   445  		}
   446  		ident := ast.NewIdent(name)
   447  		return ident
   448  
   449  	case *adt.DynamicReference:
   450  		// TODO(unshadow): ensure we correctly unshadow newly visible fields.
   451  		//   before uncommenting this.
   452  		// if v := x.EvaluateLabel(e.ctx, env); v != 0 {
   453  		// 	str := v.StringValue(e.ctx)
   454  		// 	if ast.IsValidIdent(str) {
   455  		// 		label := e.ctx.StringLabel(str)
   456  		// 		ident, ok := e.newIdentForField(x.Src, label, x.UpCount)
   457  		// 		if ok {
   458  		// 			return ident
   459  		// 		}
   460  		// 	}
   461  		// }
   462  
   463  		name := "X"
   464  		if x.Src != nil {
   465  			name = x.Src.Name
   466  		}
   467  		var f *ast.Field
   468  		for i := len(e.stack) - 1; i >= 0; i-- {
   469  			for _, entry := range e.stack[i].dynamicFields {
   470  				if entry.alias == name {
   471  					f = entry.field
   472  				}
   473  			}
   474  		}
   475  
   476  		if f != nil {
   477  			name = e.getFieldAlias(f, name)
   478  		}
   479  
   480  		ident := ast.NewIdent(name)
   481  		ident.Scope = f
   482  		ident.Node = f
   483  		return ident
   484  
   485  	case *adt.ImportReference:
   486  		importPath := x.ImportPath.StringValue(e.index)
   487  		spec := ast.NewImport(nil, importPath)
   488  
   489  		info, _ := astutil.ParseImportSpec(spec)
   490  		name := info.PkgName
   491  		if x.Label != 0 {
   492  			name = x.Label.StringValue(e.index)
   493  			if name != info.PkgName {
   494  				spec.Name = ast.NewIdent(name)
   495  			}
   496  		}
   497  		ident := ast.NewIdent(name)
   498  		ident.Node = spec
   499  		return ident
   500  
   501  	case *adt.LetReference:
   502  		return e.resolveLet(env, x)
   503  
   504  	case *adt.SelectorExpr:
   505  		return &ast.SelectorExpr{
   506  			X:   e.innerExpr(env, x.X),
   507  			Sel: e.stringLabel(x.Sel),
   508  		}
   509  
   510  	case *adt.IndexExpr:
   511  		return &ast.IndexExpr{
   512  			X:     e.innerExpr(env, x.X),
   513  			Index: e.innerExpr(env, x.Index),
   514  		}
   515  	}
   516  	panic("unreachable")
   517  }
   518  
   519  func (e *exporter) newIdentForField(
   520  	orig *ast.Ident,
   521  	label adt.Feature,
   522  	upCount int32) (ident *ast.Ident, ok bool) {
   523  	f := e.frame(upCount)
   524  	entry := f.fields[label]
   525  
   526  	name := e.identString(label)
   527  	switch {
   528  	case entry.alias != "":
   529  		name = entry.alias
   530  
   531  	case !ast.IsValidIdent(name):
   532  		name = "X"
   533  		if orig != nil {
   534  			name = orig.Name
   535  		}
   536  		name = e.uniqueAlias(name)
   537  		entry.alias = name
   538  	}
   539  
   540  	ident = ast.NewIdent(name)
   541  	entry.references = append(entry.references, ident)
   542  
   543  	if f.fields != nil {
   544  		f.fields[label] = entry
   545  		ok = true
   546  	}
   547  
   548  	return ident, ok
   549  }
   550  
   551  func (e *exporter) decl(env *adt.Environment, d adt.Decl) ast.Decl {
   552  	switch x := d.(type) {
   553  	case adt.Elem:
   554  		return e.elem(env, x)
   555  
   556  	case *adt.Field:
   557  		e.setDocs(x)
   558  		f := e.getFixedField(x)
   559  
   560  		internal.SetConstraint(f, x.ArcType.Token())
   561  		e.setField(x.Label, f)
   562  
   563  		f.Attrs = extractFieldAttrs(nil, x)
   564  
   565  		st, ok := x.Value.(*adt.StructLit)
   566  		if !ok {
   567  			f.Value = e.expr(env, x.Value)
   568  			return f
   569  
   570  		}
   571  
   572  		top := e.frame(0)
   573  		var src *adt.Vertex
   574  		if top.node != nil {
   575  			src = top.node.Lookup(x.Label)
   576  		}
   577  
   578  		// Instead of calling e.expr directly, we inline the case for
   579  		// *adt.StructLit, so that we can pass src.
   580  		c := adt.MakeRootConjunct(env, st)
   581  		f.Value = e.mergeValues(adt.InvalidLabel, src, []conjunct{{c: c, up: 0}}, c)
   582  
   583  		if top.node != nil {
   584  			if v := top.node.Lookup(x.Label); v != nil {
   585  				e.linkField(v, f)
   586  			}
   587  		}
   588  
   589  		return f
   590  
   591  	case *adt.LetField:
   592  		// Handled elsewhere
   593  		return nil
   594  
   595  	case *adt.BulkOptionalField:
   596  		e.setDocs(x)
   597  		// set bulk in frame.
   598  		frame := e.frame(0)
   599  
   600  		expr := e.innerExpr(env, x.Filter)
   601  		frame.labelExpr = expr // see astutil.Resolve.
   602  
   603  		if x.Label != 0 {
   604  			expr = &ast.Alias{Ident: e.ident(x.Label), Expr: expr}
   605  		}
   606  		f := &ast.Field{
   607  			Label: ast.NewList(expr),
   608  		}
   609  
   610  		frame.field = f
   611  
   612  		if alias := aliasFromLabel(x.Src); alias != "" {
   613  			frame.dynamicFields = append(frame.dynamicFields, &entry{
   614  				alias: alias,
   615  				field: f,
   616  			})
   617  		}
   618  
   619  		f.Value = e.expr(env, x.Value)
   620  		f.Attrs = extractFieldAttrs(nil, x)
   621  
   622  		return f
   623  
   624  	case *adt.DynamicField:
   625  		e.setDocs(x)
   626  		srcKey := x.Key
   627  
   628  		f := &ast.Field{}
   629  		internal.SetConstraint(f, x.ArcType.Token())
   630  
   631  		v, _ := e.ctx.Evaluate(env, x.Key)
   632  
   633  		switch s, ok := v.(*adt.String); {
   634  		// TODO(unshadow): allow once unshadowing algorithm is fixed.
   635  		// case ok && ast.IsValidIdent(s.Str):
   636  		// 	label := e.ctx.StringLabel(s.Str)
   637  		// 	f.Label = ast.NewIdent(s.Str)
   638  		// 	e.setField(label, f)
   639  
   640  		case ok:
   641  			srcKey = s
   642  
   643  			fallthrough
   644  
   645  		default:
   646  			key := e.innerExpr(env, srcKey)
   647  			switch key.(type) {
   648  			case *ast.Interpolation, *ast.BasicLit:
   649  			default:
   650  				key = &ast.ParenExpr{X: key}
   651  			}
   652  			f.Label = key.(ast.Label)
   653  		}
   654  
   655  		alias := aliasFromLabel(x.Src)
   656  
   657  		frame := e.frame(0)
   658  		frame.dynamicFields = append(frame.dynamicFields, &entry{
   659  			alias: alias,
   660  			field: f,
   661  		})
   662  
   663  		f.Value = e.expr(env, x.Value)
   664  		f.Attrs = extractFieldAttrs(nil, x)
   665  
   666  		return f
   667  
   668  	default:
   669  		panic(fmt.Sprintf("unknown field %T", x))
   670  	}
   671  }
   672  
   673  func (e *exporter) copyMeta(dst, src ast.Node) {
   674  	if e.cfg.ShowDocs {
   675  		ast.SetComments(dst, filterDocs(ast.Comments(src)))
   676  	}
   677  	astutil.CopyPosition(dst, src)
   678  }
   679  
   680  func filterDocs(a []*ast.CommentGroup) (out []*ast.CommentGroup) {
   681  	out = append(out, a...)
   682  	k := 0
   683  	for _, c := range a {
   684  		if !c.Doc {
   685  			continue
   686  		}
   687  		out[k] = c
   688  		k++
   689  	}
   690  	out = out[:k]
   691  	return out
   692  }
   693  
   694  func (e *exporter) setField(label adt.Feature, f *ast.Field) {
   695  	frame := e.frame(0)
   696  	entry := frame.fields[label]
   697  	entry.field = f
   698  	entry.node = f.Value
   699  	// This can happen when evaluation is "pivoted".
   700  	if frame.fields != nil {
   701  		frame.fields[label] = entry
   702  	}
   703  }
   704  
   705  func aliasFromLabel(src *ast.Field) string {
   706  	if src != nil {
   707  		if a, ok := src.Label.(*ast.Alias); ok {
   708  			return a.Ident.Name
   709  		}
   710  	}
   711  	return ""
   712  }
   713  
   714  func (e *exporter) elem(env *adt.Environment, d adt.Elem) ast.Expr {
   715  
   716  	switch x := d.(type) {
   717  	case adt.Expr:
   718  		return e.expr(env, x)
   719  
   720  	case *adt.Ellipsis:
   721  		t := &ast.Ellipsis{}
   722  		if x.Value != nil {
   723  			t.Type = e.expr(env, x.Value)
   724  		}
   725  		return t
   726  
   727  	case *adt.Comprehension:
   728  		return e.comprehension(env, x)
   729  
   730  	default:
   731  		panic(fmt.Sprintf("unknown field %T", x))
   732  	}
   733  }
   734  
   735  func (e *exporter) comprehension(env *adt.Environment, comp *adt.Comprehension) *ast.Comprehension {
   736  	c := &ast.Comprehension{}
   737  
   738  	for _, y := range comp.Clauses {
   739  		switch x := y.(type) {
   740  		case *adt.ForClause:
   741  			env = &adt.Environment{Up: env, Vertex: empty}
   742  			value := e.ident(x.Value)
   743  			src := e.innerExpr(env, x.Src)
   744  			clause := &ast.ForClause{Value: value, Source: src}
   745  			e.copyMeta(clause, x.Syntax)
   746  			c.Clauses = append(c.Clauses, clause)
   747  
   748  			_, saved := e.pushFrame(empty, nil)
   749  			defer e.popFrame(saved)
   750  
   751  			if x.Key != adt.InvalidLabel ||
   752  				(x.Syntax != nil && x.Syntax.Key != nil) {
   753  				key := e.ident(x.Key)
   754  				clause.Key = key
   755  				e.addField(x.Key, nil, clause)
   756  			}
   757  			e.addField(x.Value, nil, clause)
   758  
   759  		case *adt.IfClause:
   760  			cond := e.innerExpr(env, x.Condition)
   761  			clause := &ast.IfClause{Condition: cond}
   762  			e.copyMeta(clause, x.Src)
   763  			c.Clauses = append(c.Clauses, clause)
   764  
   765  		case *adt.LetClause:
   766  			env = &adt.Environment{Up: env, Vertex: empty}
   767  			expr := e.innerExpr(env, x.Expr)
   768  			clause := &ast.LetClause{
   769  				Ident: e.ident(x.Label),
   770  				Expr:  expr,
   771  			}
   772  			e.copyMeta(clause, x.Src)
   773  			c.Clauses = append(c.Clauses, clause)
   774  
   775  			_, saved := e.pushFrame(empty, nil)
   776  			defer e.popFrame(saved)
   777  
   778  			e.addField(x.Label, nil, clause)
   779  
   780  		case *adt.ValueClause:
   781  			// Can occur in nested comprehenions.
   782  			env = &adt.Environment{Up: env, Vertex: empty}
   783  
   784  		default:
   785  			panic(fmt.Sprintf("unknown field %T", x))
   786  		}
   787  	}
   788  	e.copyMeta(c, comp.Syntax)
   789  
   790  	// If this is an "unwrapped" comprehension, we need to also
   791  	// account for the curly braces of the original comprehension.
   792  	if comp.Nest() > 0 {
   793  		env = &adt.Environment{Up: env, Vertex: empty}
   794  	}
   795  
   796  	// TODO: consider using adt.EnvExpr.
   797  	v := e.expr(env, adt.ToExpr(comp.Value))
   798  	if _, ok := v.(*ast.StructLit); !ok {
   799  		v = ast.NewStruct(ast.Embed(v))
   800  	}
   801  	c.Value = v
   802  	return c
   803  }