internal/core/export: export let

This is by no means complete.

Change-Id: I4571f2529db55705dd36a38b5a0b0b8ea2f083a1
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6706
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/core/export/adt.go b/internal/core/export/adt.go
index 49bb789..27a94d5 100644
--- a/internal/core/export/adt.go
+++ b/internal/core/export/adt.go
@@ -118,14 +118,31 @@
 		return ident
 
 	case *adt.LetReference:
-		f := e.frame(x.UpCount)
+		// TODO:
+		// - rename if necessary
+		// - look in to reusing the mechanism of the old evaluator
+		//
+		// Either way, we need a better mechanism. References may go out of
+		// scope. In case of aliases this means they may need to be reproduced
+		// locally. Most of these issues can be avoided by either fully
+		// expanding a configuration (export) or not at all (def).
+		//
+		i := len(e.stack) - 1 - int(x.UpCount) - 1
+		if i < 0 {
+			i = 0
+		}
+		f := &(e.stack[i])
 		let := f.let[x.X]
 		if let == nil {
 			if f.let == nil {
 				f.let = map[adt.Expr]*ast.LetClause{}
 			}
-			let = &ast.LetClause{Expr: e.expr(x.X)}
+			let = &ast.LetClause{
+				Ident: e.ident(x.Label),
+				Expr:  e.expr(x.X),
+			}
 			f.let[x.X] = let
+			f.scope.Elts = append(f.scope.Elts, let)
 		}
 		ident := e.ident(x.Label)
 		ident.Node = let
diff --git a/internal/core/export/testdata/let.txtar b/internal/core/export/testdata/let.txtar
new file mode 100644
index 0000000..331647c
--- /dev/null
+++ b/internal/core/export/testdata/let.txtar
@@ -0,0 +1,21 @@
+-- in.cue --
+let X = 1 + 1
+#Foo: X
+-- out/definition --
+
+let X = 1 + 1
+#Foo: X
+-- out/doc --
+[]
+[#Foo]
+-- out/value --
+== Simplified
+{}
+== Raw
+{
+	#Foo: 2
+}
+== All
+{
+	#Foo: 2
+}