internal: add ToFile and ToExpr

Also fixes cue/gen.go

Change-Id: I6497649e3863dbacd896d0246661ef2c9780f84e
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5560
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cmd/cue/cmd/eval.go b/cmd/cue/cmd/eval.go
index 081571c..361bff0 100644
--- a/cmd/cue/cmd/eval.go
+++ b/cmd/cue/cmd/eval.go
@@ -22,7 +22,7 @@
 	"cuelang.org/go/cue"
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/format"
-	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal"
 	"cuelang.org/go/internal/encoding"
 	"cuelang.org/go/internal/filetypes"
 )
@@ -165,16 +165,5 @@
 }
 
 func getSyntax(v cue.Value, opts []cue.Option) *ast.File {
-	n := v.Syntax(opts...)
-	switch x := n.(type) {
-	case *ast.File:
-		return x
-	case *ast.StructLit:
-		return &ast.File{Decls: x.Elts}
-	case ast.Expr:
-		ast.SetRelPos(x, token.NoSpace)
-		return &ast.File{Decls: []ast.Decl{&ast.EmbedDecl{Expr: x}}}
-	default:
-		panic("unreachable")
-	}
+	return internal.ToFile(v.Syntax(opts...))
 }
diff --git a/cue/gen.go b/cue/gen.go
index 6f3025d..cb1494a 100644
--- a/cue/gen.go
+++ b/cue/gen.go
@@ -41,6 +41,7 @@
 	"cuelang.org/go/cue/errors"
 	cueformat "cuelang.org/go/cue/format"
 	"cuelang.org/go/cue/load"
+	"cuelang.org/go/internal"
 )
 
 const prefix = "../pkg/"
@@ -203,7 +204,7 @@
 		return
 	}
 
-	n := instances[0].Value().Syntax(cue.Raw())
+	n := internal.ToExpr(instances[0].Value().Syntax(cue.Raw()))
 	b, err := cueformat.Node(n)
 	if err != nil {
 		log.Fatal(err)
diff --git a/internal/encoding/encoding.go b/internal/encoding/encoding.go
index 14d579b..6618a02 100644
--- a/internal/encoding/encoding.go
+++ b/internal/encoding/encoding.go
@@ -37,6 +37,7 @@
 	"cuelang.org/go/encoding/jsonschema"
 	"cuelang.org/go/encoding/openapi"
 	"cuelang.org/go/encoding/protobuf"
+	"cuelang.org/go/internal"
 	"cuelang.org/go/internal/filetypes"
 	"cuelang.org/go/internal/third_party/yaml"
 )
@@ -91,25 +92,11 @@
 }
 
 func toFile(x ast.Expr) *ast.File {
-	switch x := x.(type) {
-	case nil:
-		return nil
-	case *ast.StructLit:
-		return &ast.File{Decls: x.Elts}
-	default:
-		return &ast.File{Decls: []ast.Decl{&ast.EmbedDecl{Expr: x}}}
-	}
+	return internal.ToFile(x)
 }
 
 func valueToFile(v cue.Value) *ast.File {
-	switch x := v.Syntax().(type) {
-	case *ast.File:
-		return x
-	case ast.Expr:
-		return toFile(x)
-	default:
-		panic("unrreachable")
-	}
+	return internal.ToFile(v.Syntax())
 }
 
 func (i *Decoder) File() *ast.File {
diff --git a/internal/internal.go b/internal/internal.go
index f12f267..63ec888 100644
--- a/internal/internal.go
+++ b/internal/internal.go
@@ -146,6 +146,53 @@
 	return &ast.Attribute{Text: buf.String()}
 }
 
+// ToExpr converts a node to an expression. If it is a file, it will return
+// it as a struct. If is an expression, it will return it as is. Otherwise
+// it panics.
+func ToExpr(n ast.Node) ast.Expr {
+	switch x := n.(type) {
+	case nil:
+		return nil
+
+	case ast.Expr:
+		return x
+
+	case *ast.File:
+		start := 0
+	outer:
+		for i, d := range x.Decls {
+			switch d.(type) {
+			case *ast.Package, *ast.ImportDecl:
+				start = i + 1
+			case *ast.CommentGroup, *ast.Attribute:
+			default:
+				break outer
+			}
+		}
+		return &ast.StructLit{Elts: x.Decls[start:]}
+
+	default:
+		panic(fmt.Sprintf("Unsupported node type %T", x))
+	}
+}
+
+// ToFile converts an expression to a file.
+//
+// Adjusts the spacing of x when needed.
+func ToFile(n ast.Node) *ast.File {
+	switch x := n.(type) {
+	case nil:
+		return nil
+	case *ast.StructLit:
+		return &ast.File{Decls: x.Elts}
+	case ast.Expr:
+		ast.SetRelPos(x, token.NoSpace)
+		return &ast.File{Decls: []ast.Decl{&ast.EmbedDecl{Expr: x}}}
+	default:
+		panic(fmt.Sprintf("Unsupported node type %T", x))
+	}
+}
+
 // IsEllipsis reports whether the declaration can be represented as an ellipsis.
 func IsEllipsis(x ast.Decl) bool {
 	// ...
diff --git a/tools/trim/trim.go b/tools/trim/trim.go
index 45110f4..a9c3e03 100644
--- a/tools/trim/trim.go
+++ b/tools/trim/trim.go
@@ -275,13 +275,7 @@
 				// resolved. Attempt to complete the expression by
 				// evaluting it within the struct to which the template
 				// is applied.
-				var expr ast.Expr
-				switch x := v.Syntax().(type) {
-				case ast.Expr:
-					expr = x
-				case *ast.File:
-					expr = &ast.StructLit{Elts: x.Decls}
-				}
+				expr := internal.ToExpr(v.Syntax())
 
 				// TODO: this only resolves references contained in scope.
 				v = internal.EvalExpr(scope, expr).(cue.Value)