internal/core/export: better handling of generated CUE

Generated CUE structs can organized somewhat different from
the ones computed during evaluation. Most notably, conjuncts
can be a Vertex. Handle this.

Fixes #499

Change-Id: I752e2d09ab7f49476d331028a75c1a9c8f9d480a
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/7123
Reviewed-by: Paul Jolly <paul@myitcv.org.uk>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/internal/core/export/export_test.go b/internal/core/export/export_test.go
index 1b415d2..69a44db 100644
--- a/internal/core/export/export_test.go
+++ b/internal/core/export/export_test.go
@@ -22,7 +22,10 @@
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/cue/format"
+	"cuelang.org/go/internal"
+	"cuelang.org/go/internal/core/adt"
 	"cuelang.org/go/internal/core/compile"
+	"cuelang.org/go/internal/core/convert"
 	"cuelang.org/go/internal/core/eval"
 	"cuelang.org/go/internal/core/export"
 	"cuelang.org/go/internal/core/runtime"
@@ -69,6 +72,75 @@
 	return b
 }
 
+// TestGenerated tests conversions of generated Go structs, which may be
+// different from parsed or evaluated CUE, such as having Vertex values.
+func TestGenerated(t *testing.T) {
+	testCases := []struct {
+		in    interface{}
+		value string
+		typ   string
+	}{{
+		in: &C{
+			Terminals: []*A{
+				{Name: "Name", Description: "Desc"},
+			},
+		},
+		value: `{Terminals: [{Description: "Desc", Name: "Name"}]}`,
+		typ:   `*null|{Terminals?: *null|[...*null|{Name: string, Description: string}]}`,
+	}, {
+		in: []*A{
+			{Name: "Name", Description: "Desc"},
+		},
+		value: `[{Name: "Name", Description: "Desc"}]`,
+		typ:   `*null|[...*null|{Name: string, Description: string}]`,
+	}}
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			r := runtime.New()
+			e := eval.New(r)
+			ctx := adt.NewContext(r, e, &adt.Vertex{})
+
+			v := convert.GoValueToValue(ctx, tc.in, false)
+
+			expr, err := export.Expr(ctx, v)
+			if err != nil {
+				t.Fatal(err)
+			}
+			got := internal.DebugStr(expr)
+			if got != tc.value {
+				t.Errorf("value: got:  %s\nwant: %s", got, tc.value)
+			}
+
+			x, err := convert.GoTypeToExpr(ctx, tc.in)
+			if err != nil {
+				t.Fatal(err)
+			}
+			expr, err = export.Expr(ctx, x)
+			if err != nil {
+				t.Fatal(err)
+			}
+			got = internal.DebugStr(expr)
+			if got != tc.typ {
+				t.Errorf("type: got:  %s\nwant: %s", got, tc.typ)
+			}
+
+		})
+	}
+}
+
+type A struct {
+	Name        string
+	Description string
+}
+
+type B struct {
+	Image string
+}
+
+type C struct {
+	Terminals []*A
+}
+
 // For debugging purposes. Do not delete.
 func TestX(t *testing.T) {
 	t.Skip()
diff --git a/internal/core/export/expr.go b/internal/core/export/expr.go
index 7f130f5..d58eff8 100644
--- a/internal/core/export/expr.go
+++ b/internal/core/export/expr.go
@@ -263,9 +263,20 @@
 
 	case adt.Value: // other values.
 		if v, ok := x.(*adt.Vertex); ok {
-			// if !v.IsList() {
-			// 	panic("what to do?") // TO
-			// }
+			if v.IsList() {
+				a := []ast.Expr{}
+				for _, x := range v.Elems() {
+					a = append(a, e.expr(x))
+				}
+				if !v.IsClosed(e.ctx) {
+					v := &adt.Vertex{}
+					v.MatchAndInsert(e.ctx, v)
+					a = append(a, &ast.Ellipsis{Type: e.expr(v)})
+				}
+				e.exprs = append(e.exprs, ast.NewList(a...))
+				return
+			}
+
 			e.structs = append(e.structs, v.Structs...)
 
 			// generated, only consider arcs.