cmd/cue/cmd: use format for eval

- also export ast nodes with proper labels
- evaluate when calling Syntax
  (Source is already for raw conversion)

 Fixes cuelang/go#18

Change-Id: Id2d9affe5c356fe5a57fe54615e99750ff9d0a11
diff --git a/cmd/cue/cmd/eval.go b/cmd/cue/cmd/eval.go
index 1e58f14..81351ba 100644
--- a/cmd/cue/cmd/eval.go
+++ b/cmd/cue/cmd/eval.go
@@ -16,11 +16,7 @@
 
 import (
 	"fmt"
-	"io"
-	"strings"
-	"text/tabwriter"
 
-	"cuelang.org/go/cue"
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/format"
 	"cuelang.org/go/cue/parser"
@@ -61,19 +57,22 @@
 			exprs = append(exprs, expr)
 		}
 
-		tw := tabwriter.NewWriter(cmd.OutOrStdout(), 0, 0, 1, ' ', 0)
-		defer tw.Flush()
+		w := cmd.OutOrStdout()
+
 		for _, inst := range instances {
 			// TODO: use ImportPath or some other sanitized path.
-			fmt.Fprintf(cmd.OutOrStdout(), "// %s\n", inst.Dir)
-			p := evalPrinter{w: tw}
+			fmt.Fprintf(w, "// %s\n", inst.Dir)
+			opts := []format.Option{
+				format.UseSpaces(4),
+				format.TabIndent(false),
+			}
 			if exprs == nil {
-				p.print(inst.Value())
-				fmt.Fprintln(tw)
+				format.Node(w, inst.Value().Syntax(), opts...)
+				fmt.Fprintln(w)
 			}
 			for _, e := range exprs {
-				p.print(inst.Eval(e))
-				fmt.Fprintln(tw)
+				format.Node(w, inst.Eval(e).Syntax(), opts...)
+				fmt.Fprintln(w)
 			}
 		}
 		return nil
@@ -90,102 +89,3 @@
 var (
 	expressions *[]string
 )
-
-type evalPrinter struct {
-	w        io.Writer
-	fset     *token.FileSet
-	indent   int
-	newline  bool
-	formfeed bool
-}
-
-type ws byte
-
-const (
-	unindent    = -1
-	indent      = 1
-	newline  ws = '\n'
-	vtab     ws = '\v'
-	space    ws = ' '
-
-	// maxDiffLen is the maximum different in length for object keys for which
-	// to still align keys and values.
-	maxDiffLen = 5
-)
-
-func (p *evalPrinter) print(args ...interface{}) {
-	for _, a := range args {
-		if d, ok := a.(int); ok {
-			p.indent += d
-			continue
-		}
-		if p.newline {
-			nl := '\n'
-			if p.formfeed {
-				nl = '\f'
-			}
-			p.w.Write([]byte{byte(nl)})
-			fmt.Fprint(p.w, strings.Repeat("    ", int(p.indent)))
-			p.newline = false
-		}
-		switch v := a.(type) {
-		case ws:
-			switch v {
-			case newline:
-				p.newline = true
-			default:
-				p.w.Write([]byte{byte(v)})
-			}
-		case string:
-			fmt.Fprint(p.w, v)
-		case cue.Value:
-			switch v.Kind() {
-			case cue.StructKind:
-				iter, err := v.AllFields()
-				must(err)
-				lastLen := 0
-				p.print("{", indent, newline)
-				for iter.Next() {
-					value := iter.Value()
-					key := iter.Label()
-					newLen := len([]rune(key)) // TODO: measure cluster length.
-					if lastLen > 0 && abs(lastLen, newLen) > maxDiffLen {
-						p.formfeed = true
-					} else {
-						k := value.Kind()
-						p.formfeed = k == cue.StructKind || k == cue.ListKind
-					}
-					p.print(key, ":", vtab, value, newline)
-					p.formfeed = false
-					lastLen = newLen
-				}
-				p.print(unindent, "}")
-			case cue.ListKind:
-				list, err := v.List()
-				must(err)
-				p.print("[", indent, newline)
-				for list.Next() {
-					p.print(list.Value(), newline)
-				}
-				p.print(unindent, "]")
-			default:
-				format.Node(p.w, v.Syntax())
-			}
-		}
-	}
-}
-
-func abs(a, b int) int {
-	a -= b
-	if a < 0 {
-		return -a
-	}
-	return a
-}
-
-func max(a, b int) int {
-	if a > b {
-		return a
-	}
-	return b
-}
diff --git a/cue/export.go b/cue/export.go
index 3366d9b..4b18225 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -18,27 +18,50 @@
 	"fmt"
 	"strconv"
 	"strings"
+	"unicode"
 	"unicode/utf8"
 
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/token"
 )
 
-func export(ctx *context, v value) ast.Expr {
-	e := exporter{ctx}
+type exportMode int
+
+const (
+	exportRaw exportMode = iota
+	exportEval
+)
+
+func export(ctx *context, v value, m exportMode) ast.Expr {
+	e := exporter{ctx, m}
 	return e.expr(v)
 }
 
 type exporter struct {
-	ctx *context
+	ctx  *context
+	mode exportMode
 }
 
-func (p *exporter) label(f label) *ast.Ident {
+func (p *exporter) label(f label) ast.Label {
 	orig := p.ctx.labelStr(f)
 	str := strconv.Quote(orig)
-	if len(orig)+2 == len(str) {
-		str = str[1 : len(str)-1]
+	if len(orig)+2 < len(str) {
+		return &ast.BasicLit{Value: str}
 	}
+	for i, r := range orig {
+		if unicode.IsLetter(r) || r == '_' {
+			continue
+		}
+		if i > 0 && unicode.IsDigit(r) {
+			continue
+		}
+		return &ast.BasicLit{Value: str}
+	}
+	return &ast.Ident{Name: orig}
+}
+
+func (p *exporter) identifier(f label) *ast.Ident {
+	str := p.ctx.labelStr(f)
 	return &ast.Ident{Name: str}
 }
 
@@ -50,12 +73,12 @@
 	switch x := v.(type) {
 	case *feed:
 		feed := &ast.ForClause{
-			Value:  p.label(x.fn.params.arcs[1].feature),
+			Value:  p.identifier(x.fn.params.arcs[1].feature),
 			Source: p.expr(x.source),
 		}
 		key := x.fn.params.arcs[0]
 		if p.ctx.labelStr(key.feature) != "_" {
-			feed.Key = p.label(key.feature)
+			feed.Key = p.identifier(key.feature)
 		}
 		return feed, x.fn.value.(yielder)
 
@@ -66,6 +89,10 @@
 }
 
 func (p *exporter) expr(v value) ast.Expr {
+	if p.mode == exportEval {
+		v = v.evalPartial(p.ctx)
+	}
+
 	// TODO: also add position information.
 	switch x := v.(type) {
 	case *builtin:
@@ -75,9 +102,9 @@
 	case *selectorExpr:
 		n := p.expr(x.x)
 		if n == nil {
-			return p.label(x.feature)
+			return p.identifier(x.feature)
 		}
-		return &ast.SelectorExpr{X: n, Sel: p.label(x.feature)}
+		return &ast.SelectorExpr{X: n, Sel: p.identifier(x.feature)}
 	case *indexExpr:
 		return &ast.IndexExpr{X: p.expr(x.x), Index: p.expr(x.index)}
 	case *sliceExpr:
@@ -130,6 +157,9 @@
 
 	case *structLit:
 		obj := &ast.StructLit{}
+		if p.mode == exportEval {
+			x = x.expandFields(p.ctx)
+		}
 		if x.emit != nil {
 			obj.Elts = append(obj.Elts, &ast.EmitDecl{Expr: p.expr(x.emit)})
 		}
@@ -144,8 +174,14 @@
 			next := c.clauses
 			for {
 				if yield, ok := next.(*yield); ok {
+					l := p.expr(yield.key)
+					label, ok := l.(ast.Label)
+					if !ok {
+						// TODO: add an invalid field instead?
+						continue
+					}
 					f := &ast.Field{
-						Label: p.expr(yield.key).(ast.Label),
+						Label: label,
 						Value: p.expr(yield.value),
 					}
 					var decl ast.Decl = f
diff --git a/cue/export_test.go b/cue/export_test.go
index 2673305..7578f92 100644
--- a/cue/export_test.go
+++ b/cue/export_test.go
@@ -138,12 +138,12 @@
 			}`),
 	}, {
 		raw: true,
-		in:  `{ a: [1, 2], b: { "\(k)": v for k, v in a if a > 1 } }`,
+		in:  `{ a: [1, 2], b: { "\(k)": v for k, v in a if v > 1 } }`,
 		out: unindent(`
 			{
 				a: [1, 2]
 				b: {
-					"\(k)": v for k, v in a if a > 1
+					"\(k)": v for k, v in a if v > 1
 				}
 			}`),
 	}, {
@@ -186,7 +186,7 @@
 			v := newValueRoot(ctx, n)
 
 			buf := &bytes.Buffer{}
-			err := format.Node(buf, export(ctx, v.eval(ctx)))
+			err := format.Node(buf, export(ctx, v.eval(ctx), exportRaw))
 			if err != nil {
 				log.Fatal(err)
 			}
diff --git a/cue/types.go b/cue/types.go
index 7cecd8c..d12f805 100644
--- a/cue/types.go
+++ b/cue/types.go
@@ -592,7 +592,7 @@
 		return nil
 	}
 	ctx := v.ctx()
-	return export(ctx, v.eval(ctx))
+	return export(ctx, v.eval(ctx), exportEval)
 }
 
 // Decode initializes x with Value v. If x is a struct, it will validate the
diff --git a/cue/value.go b/cue/value.go
index 897af40..5c34b96 100644
--- a/cue/value.go
+++ b/cue/value.go
@@ -705,8 +705,10 @@
 	// TODO(perf): improve big O
 	arcs := make([]arc, 0, len(x.arcs)+len(newArcs))
 	arcs = append(arcs, x.arcs...)
+	orig := x
 	x = &structLit{x.baseValue, emit, template, nil, arcs, nil}
 	x.expanded = x
+	orig.expanded = x
 
 outer:
 	for _, na := range newArcs {