...

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

View as plain text