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/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 {
 	// ...