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 }