internal/legacy/cue: copy relevant cue files for new implementation

Change-Id: I9c2549e0c37fdb4802265ce9c40753b4cc681f29
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6518
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/legacy/cue/alias.go b/internal/legacy/cue/alias.go
new file mode 100644
index 0000000..ee438fc
--- /dev/null
+++ b/internal/legacy/cue/alias.go
@@ -0,0 +1,69 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import "cuelang.org/go/internal/core/adt"
+
+type (
+	bottom    = adt.Bottom
+	source    = adt.Node
+	errCode   = adt.ErrorCode
+	kind      = adt.Kind
+	nullLit   = adt.Null
+	boolLit   = adt.Bool
+	numLit    = adt.Num
+	stringLit = adt.String
+	bytesLit  = adt.Bytes
+	context   = adt.OpContext
+	structLit = adt.Vertex
+
+	arc       = *adt.Vertex
+	value     = adt.Expr
+	evaluated = adt.Value
+	label     = adt.Feature
+	Op        = adt.Op
+
+	listLit       = adt.ListLit
+	top           = adt.Top
+	basicType     = adt.BasicType
+	boundExpr     = adt.BoundExpr
+	boundValue    = adt.BoundValue
+	selectorExpr  = adt.SelectorExpr
+	indexExpr     = adt.IndexExpr
+	sliceExpr     = adt.SliceExpr
+	interpolation = adt.Interpolation
+	unaryExpr     = adt.UnaryExpr
+	binaryExpr    = adt.BinaryExpr
+	callExpr      = adt.CallExpr
+)
+
+const (
+	topKind    = adt.TopKind
+	nullKind   = adt.NullKind
+	boolKind   = adt.BoolKind
+	numKind    = adt.NumKind
+	intKind    = adt.IntKind
+	floatKind  = adt.FloatKind
+	stringKind = adt.StringKind
+	bytesKind  = adt.BytesKind
+	listKind   = adt.ListKind
+	structKind = adt.StructKind
+	bottomKind = adt.BottomKind
+
+	NoOp = adt.NoOp
+
+	codeIncomplete = adt.IncompleteError
+	codeNotExist   = adt.IncompleteError
+)
diff --git a/internal/legacy/cue/build.go b/internal/legacy/cue/build.go
new file mode 100644
index 0000000..9f86daf
--- /dev/null
+++ b/internal/legacy/cue/build.go
@@ -0,0 +1,248 @@
+// Copyright 2018 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+	"sync"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/ast/astutil"
+	"cuelang.org/go/cue/build"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal"
+	"cuelang.org/go/internal/core/runtime"
+)
+
+// A Runtime is used for creating CUE interpretations.
+//
+// Any operation that involves two Values or Instances should originate from
+// the same Runtime.
+//
+// The zero value of a Runtime is ready to use.
+type Runtime struct {
+	ctx *build.Context // TODO: remove
+	idx *index
+}
+
+func init() {
+	internal.GetRuntime = func(instance interface{}) interface{} {
+		switch x := instance.(type) {
+		case Value:
+			return &Runtime{idx: x.idx}
+
+		case *Instance:
+			return &Runtime{idx: x.index}
+
+		default:
+			panic("argument must be Value or *Instance")
+		}
+	}
+
+	internal.CheckAndForkRuntime = func(runtime, value interface{}) interface{} {
+		r := runtime.(*Runtime)
+		idx := value.(Value).ctx().index
+		if idx != r.idx {
+			panic("value not from same runtime")
+		}
+		return &Runtime{idx: newIndex(idx)}
+	}
+}
+
+func dummyLoad(token.Pos, string) *build.Instance { return nil }
+
+func (r *Runtime) index() *index {
+	if r.idx == nil {
+		r.idx = newIndex(sharedIndex)
+	}
+	return r.idx
+}
+
+func (r *Runtime) buildContext() *build.Context {
+	ctx := r.ctx
+	if r.ctx == nil {
+		ctx = build.NewContext()
+	}
+	return ctx
+}
+
+func (r *Runtime) complete(p *build.Instance) (*Instance, error) {
+	idx := r.index()
+	if err := p.Complete(); err != nil {
+		return nil, err
+	}
+	inst := idx.loadInstance(p)
+	inst.ImportPath = p.ImportPath
+	if inst.Err != nil {
+		return nil, inst.Err
+	}
+	return inst, nil
+}
+
+// Compile compiles the given source into an Instance. The source code may be
+// provided as a string, byte slice, io.Reader. The name is used as the file
+// name in position information. The source may import builtin packages. Use
+// Build to allow importing non-builtin packages.
+func (r *Runtime) Compile(filename string, source interface{}) (*Instance, error) {
+	ctx := r.buildContext()
+	p := ctx.NewInstance(filename, dummyLoad)
+	if err := p.AddFile(filename, source); err != nil {
+		return nil, p.Err
+	}
+	return r.complete(p)
+}
+
+// CompileFile compiles the given source file into an Instance. The source may
+// import builtin packages. Use Build to allow importing non-builtin packages.
+func (r *Runtime) CompileFile(file *ast.File) (*Instance, error) {
+	ctx := r.buildContext()
+	p := ctx.NewInstance(file.Filename, dummyLoad)
+	err := p.AddSyntax(file)
+	if err != nil {
+		return nil, err
+	}
+	_, p.PkgName, _ = internal.PackageInfo(file)
+	return r.complete(p)
+}
+
+// CompileExpr compiles the given source expression into an Instance. The source
+// may import builtin packages. Use Build to allow importing non-builtin
+// packages.
+func (r *Runtime) CompileExpr(expr ast.Expr) (*Instance, error) {
+	f, err := astutil.ToFile(expr)
+	if err != nil {
+		return nil, err
+	}
+	return r.CompileFile(f)
+}
+
+// Parse parses a CUE source value into a CUE Instance. The source code may
+// be provided as a string, byte slice, or io.Reader. The name is used as the
+// file name in position information. The source may import builtin packages.
+//
+// Deprecated: use Compile
+func (r *Runtime) Parse(name string, source interface{}) (*Instance, error) {
+	return r.Compile(name, source)
+}
+
+// Build creates an Instance from the given build.Instance. A returned Instance
+// may be incomplete, in which case its Err field is set.
+func (r *Runtime) Build(instance *build.Instance) (*Instance, error) {
+	return r.complete(instance)
+}
+
+// Build creates one Instance for each build.Instance. A returned Instance
+// may be incomplete, in which case its Err field is set.
+//
+// Example:
+//	inst := cue.Build(load.Instances(args))
+//
+func Build(instances []*build.Instance) []*Instance {
+	if len(instances) == 0 {
+		panic("cue: list of instances must not be empty")
+	}
+	var r Runtime
+	a, _ := r.build(instances)
+	return a
+}
+
+func (r *Runtime) build(instances []*build.Instance) ([]*Instance, error) {
+	index := r.index()
+
+	loaded := []*Instance{}
+
+	var errs errors.Error
+
+	for _, p := range instances {
+		_ = p.Complete()
+		errs = errors.Append(errs, p.Err)
+
+		i := index.loadInstance(p)
+		errs = errors.Append(errs, i.Err)
+		loaded = append(loaded, i)
+	}
+
+	// TODO: insert imports
+	return loaded, errs
+}
+
+// FromExpr creates an instance from an expression.
+// Any references must be resolved beforehand.
+//
+// Deprecated: use CompileExpr
+func (r *Runtime) FromExpr(expr ast.Expr) (*Instance, error) {
+	return r.CompileFile(&ast.File{
+		Decls: []ast.Decl{&ast.EmbedDecl{Expr: expr}},
+	})
+}
+
+// index maps conversions from label names to internal codes.
+//
+// All instances belonging to the same package should share this index.
+type index struct {
+	*runtime.Index
+
+	loaded map[*build.Instance]*Instance
+	mutex  sync.Mutex
+}
+
+// sharedIndex is used for indexing builtins and any other labels common to
+// all instances.
+var sharedIndex = &index{
+	Index:  runtime.SharedIndex,
+	loaded: map[*build.Instance]*Instance{},
+}
+
+// newIndex creates a new index.
+func newIndex(parent *index) *index {
+	return &index{
+		Index:  runtime.NewIndex(parent.Index),
+		loaded: map[*build.Instance]*Instance{},
+	}
+}
+
+func isBuiltin(s string) bool {
+	_, ok := builtins[s]
+	return ok
+}
+
+func (idx *index) loadInstance(p *build.Instance) *Instance {
+	if inst := idx.loaded[p]; inst != nil {
+		if !inst.complete {
+			// cycles should be detected by the builder and it should not be
+			// possible to construct a build.Instance that has them.
+			panic("cue: cycle")
+		}
+		return inst
+	}
+	errs := runtime.ResolveFiles(idx.Index, p, isBuiltin)
+	files := p.Files
+	inst := newInstance(idx, p)
+	idx.loaded[p] = inst
+
+	if inst.Err == nil {
+		// inst.instance.index.state = s
+		// inst.instance.inst = p
+		inst.Err = errs
+		for _, f := range files {
+			err := inst.insertFile(f)
+			inst.Err = errors.Append(inst.Err, err)
+		}
+	}
+	inst.ImportPath = p.ImportPath
+
+	inst.complete = true
+	return inst
+}
diff --git a/internal/legacy/cue/build_test.go b/internal/legacy/cue/build_test.go
new file mode 100644
index 0000000..7817ec7
--- /dev/null
+++ b/internal/legacy/cue/build_test.go
@@ -0,0 +1,259 @@
+// Copyright 2018 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/build"
+	"cuelang.org/go/cue/token"
+)
+
+func TestFromExpr(t *testing.T) {
+	testCases := []struct {
+		expr ast.Expr
+		out  string
+	}{{
+		expr: ast.NewString("Hello"),
+		out:  `"Hello"`,
+	}, {
+		expr: ast.NewList(
+			ast.NewString("Hello"),
+			ast.NewString("World"),
+		),
+		out: `["Hello","World"]`,
+	}}
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			r := &Runtime{}
+			inst, err := r.CompileExpr(tc.expr)
+			if err != nil {
+				t.Fatal(err)
+			}
+			ctx := inst.newContext()
+			if got := debugStr(ctx, inst.eval(ctx)); got != tc.out {
+				t.Errorf("\n got: %v; want %v", got, tc.out)
+			}
+		})
+	}
+}
+
+func TestBuild(t *testing.T) {
+	files := func(s ...string) []string { return s }
+	insts := func(i ...*bimport) []*bimport { return i }
+	pkg1 := &bimport{
+		"pkg1",
+		files(`
+		package pkg1
+
+		Object: "World"
+		`),
+	}
+	pkg2 := &bimport{
+		"example.com/foo/pkg2:pkg",
+		files(`
+		package pkg
+
+		Number: 12
+		`),
+	}
+	pkg3 := &bimport{
+		"example.com/foo/v1:pkg3",
+		files(`
+		package pkg3
+
+		List: [1,2,3]
+		`),
+	}
+
+	testCases := []struct {
+		instances []*bimport
+		emit      string
+	}{{
+		insts(&bimport{"", files(`test: "ok"`)}),
+		`{test: "ok"}`,
+	}, {
+		insts(&bimport{"",
+			files(
+				`package test
+
+				import "math"
+
+				"Pi: \(math.Pi)!"`)}),
+		`"Pi: 3.14159265358979323846264338327950288419716939937510582097494459!"`,
+	}, {
+		insts(&bimport{"",
+			files(
+				`package test
+
+				import math2 "math"
+
+				"Pi: \(math2.Pi)!"`)}),
+		`"Pi: 3.14159265358979323846264338327950288419716939937510582097494459!"`,
+	}, {
+		insts(pkg1, &bimport{"",
+			files(
+				`package test
+
+				import "pkg1"
+
+				"Hello \(pkg1.Object)!"`),
+		}),
+		`"Hello World!"`,
+	}, {
+		insts(pkg1, &bimport{"",
+			files(
+				`package test
+
+				import "pkg1"
+
+				"Hello \(pkg1.Object)!"`),
+		}),
+		`"Hello World!"`,
+	}, {
+		insts(pkg1, &bimport{"",
+			files(
+				`package test
+
+				import pkg2 "pkg1"
+				#pkg1: pkg2.Object
+
+				"Hello \(#pkg1)!"`),
+		}),
+		`"Hello World!"`,
+	}, {
+		insts(pkg1, pkg2, &bimport{"",
+			files(
+				`package test
+
+				import bar "pkg1"
+				import baz "example.com/foo/pkg2:pkg"
+
+				pkg1: Object: 3
+				"Hello \(pkg1.Object)!"`),
+		}),
+		`imported and not used: "pkg1" as bar (and 1 more errors)`,
+	}, {
+		insts(pkg2, &bimport{"",
+			files(
+				`package test
+
+				import "example.com/foo/pkg2:pkg"
+
+				"Hello \(pkg2.Number)!"`),
+		}),
+		`imported and not used: "example.com/foo/pkg2:pkg" (and 1 more errors)`,
+		// `file0.cue:5:14: unresolved reference pkg2`,
+	}, {
+		insts(pkg2, &bimport{"",
+			files(
+				`package test
+
+				import "example.com/foo/pkg2:pkg"
+
+				"Hello \(pkg.Number)!"`),
+		}),
+		`"Hello 12!"`,
+	}, {
+		insts(pkg3, &bimport{"",
+			files(
+				`package test
+
+				import "example.com/foo/v1:pkg3"
+
+				"Hello \(pkg3.List[1])!"`),
+		}),
+		`"Hello 2!"`,
+	}, {
+		insts(pkg3, &bimport{"",
+			files(
+				`package test
+
+				import "example.com/foo/v1:pkg3"
+
+				pkg3: 3
+
+				"Hello \(pkg3.List[1])!"`),
+		}),
+		`pkg3 redeclared as imported package name
+	previous declaration at file0.cue:5:5`,
+	}}
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			insts := Build(makeInstances(tc.instances))
+			var got string
+			if err := insts[0].Err; err != nil {
+				got = err.Error()
+			} else {
+				got = strings.TrimSpace(fmt.Sprintf("%s\n", insts[0].Value()))
+			}
+			if got != tc.emit {
+				t.Errorf("\n got: %s\nwant: %s", got, tc.emit)
+			}
+		})
+	}
+}
+
+type builder struct {
+	ctxt    *build.Context
+	imports map[string]*bimport
+}
+
+func (b *builder) load(pos token.Pos, path string) *build.Instance {
+	bi := b.imports[path]
+	if bi == nil {
+		return nil
+	}
+	return b.build(bi)
+}
+
+type bimport struct {
+	path  string // "" means top-level
+	files []string
+}
+
+func makeInstances(insts []*bimport) (instances []*build.Instance) {
+	b := builder{
+		ctxt:    build.NewContext(),
+		imports: map[string]*bimport{},
+	}
+	for _, bi := range insts {
+		if bi.path != "" {
+			b.imports[bi.path] = bi
+		}
+	}
+	for _, bi := range insts {
+		if bi.path == "" {
+			instances = append(instances, b.build(bi))
+		}
+	}
+	return
+}
+
+func (b *builder) build(bi *bimport) *build.Instance {
+	path := bi.path
+	if path == "" {
+		path = "dir"
+	}
+	p := b.ctxt.NewInstance(path, b.load)
+	for i, f := range bi.files {
+		_ = p.AddFile(fmt.Sprintf("file%d.cue", i), f)
+	}
+	_ = p.Complete()
+	return p
+}
diff --git a/internal/legacy/cue/builtin.go b/internal/legacy/cue/builtin.go
new file mode 100644
index 0000000..cfd98a6
--- /dev/null
+++ b/internal/legacy/cue/builtin.go
@@ -0,0 +1,656 @@
+// Copyright 2018 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//go:generate go run gen.go
+//go:generate go run golang.org/x/tools/cmd/goimports -w -local cuelang.org/go builtins.go
+//go:generate gofmt -s -w builtins.go
+
+package cue
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"math/big"
+	"path"
+	"sort"
+	"strings"
+
+	"github.com/cockroachdb/apd/v2"
+
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal"
+)
+
+// A builtin is a builtin function or constant.
+//
+// A function may return and a constant may be any of the following types:
+//
+//   error (translates to bottom)
+//   nil   (translates to null)
+//   bool
+//   int*
+//   uint*
+//   float64
+//   string
+//   *big.Float
+//   *big.Int
+//
+//   For any of the above, including interface{} and these types recursively:
+//   []T
+//   map[string]T
+//
+type builtin struct {
+	baseValue
+	Name   string
+	pkg    label
+	Params []kind
+	Result kind
+	Func   func(c *callCtxt)
+	// Const  interface{}
+	Const string
+}
+
+type builtinPkg struct {
+	native []*builtin
+	cue    string
+}
+
+func mustCompileBuiltins(ctx *context, p *builtinPkg, pkgName string) *structLit {
+	obj := &structLit{}
+	pkgLabel := ctx.Label(pkgName, false)
+	for _, b := range p.native {
+		b.pkg = pkgLabel
+
+		f := ctx.Label(b.Name, false) // never starts with _
+		// n := &node{baseValue: newBase(imp.Path)}
+		var v evaluated = b
+		if b.Const != "" {
+			v = mustParseConstBuiltin(ctx, b.Name, b.Const)
+		}
+		obj.Arcs = append(obj.Arcs, arc{Label: f, v: v})
+	}
+	sort.Sort(obj)
+
+	// Parse builtin CUE
+	if p.cue != "" {
+		expr, err := parser.ParseExpr(pkgName, p.cue)
+		if err != nil {
+			panic(fmt.Errorf("could not parse %v: %v", p.cue, err))
+		}
+		v := newVisitor(ctx.index, nil, nil, nil, false)
+		value := v.walk(expr)
+		pkg := value.evalPartial(ctx).(*structLit)
+		for _, a := range pkg.Arcs {
+			// Discard option status and attributes at top level.
+			// TODO: filter on capitalized fields?
+			obj.insertValue(ctx, a.Label, false, false, a.v, nil, a.docs)
+		}
+	}
+
+	return obj
+}
+
+// newConstBuiltin parses and creates any CUE expression that does not have
+// fields.
+func mustParseConstBuiltin(ctx *context, name, val string) evaluated {
+	expr, err := parser.ParseExpr("<builtin:"+name+">", val)
+	if err != nil {
+		panic(err)
+	}
+	v := newVisitor(ctx.index, nil, nil, nil, false)
+	value := v.walk(expr)
+	return value.evalPartial(ctx)
+}
+
+var _ caller = &builtin{}
+
+var lenBuiltin = &builtin{
+	Name:   "len",
+	Params: []kind{stringKind | bytesKind | listKind | structKind},
+	Result: intKind,
+	Func: func(c *callCtxt) {
+		v := c.value(0)
+		switch k := v.IncompleteKind(); k {
+		case StructKind:
+			s, err := v.structValData(c.ctx)
+			if err != nil {
+				c.ret = err
+				break
+			}
+			c.ret = s.Len()
+		case ListKind:
+			i := 0
+			iter, err := v.List()
+			if err != nil {
+				c.ret = err
+				break
+			}
+			for ; iter.Next(); i++ {
+			}
+			c.ret = i
+		case BytesKind:
+			b, err := v.Bytes()
+			if err != nil {
+				c.ret = err
+				break
+			}
+			c.ret = len(b)
+		case StringKind:
+			s, err := v.String()
+			if err != nil {
+				c.ret = err
+				break
+			}
+			c.ret = len(s)
+		default:
+			c.ret = errors.Newf(token.NoPos,
+				"invalid argument type %v", k)
+		}
+	},
+}
+
+var closeBuiltin = &builtin{
+	Name:   "close",
+	Params: []kind{structKind},
+	Result: structKind,
+	Func: func(c *callCtxt) {
+		s, ok := c.args[0].(*structLit)
+		if !ok {
+			c.ret = errors.Newf(c.args[0].Pos(), "struct argument must be concrete")
+			return
+		}
+		c.ret = s.close()
+	},
+}
+
+var andBuiltin = &builtin{
+	Name:   "and",
+	Params: []kind{listKind},
+	Result: intKind,
+	Func: func(c *callCtxt) {
+		iter := c.iter(0)
+		if !iter.Next() {
+			c.ret = &top{baseValue{c.src}}
+			return
+		}
+		u := iter.Value().v.v
+		for iter.Next() {
+			u = mkBin(c.ctx, c.src.Pos(), opUnify, u, iter.Value().v.v)
+		}
+		c.ret = u
+	},
+}
+
+var orBuiltin = &builtin{
+	Name:   "or",
+	Params: []kind{listKind},
+	Result: intKind,
+	Func: func(c *callCtxt) {
+		iter := c.iter(0)
+		d := []dValue{}
+		for iter.Next() {
+			d = append(d, dValue{iter.Value().v.v, false})
+		}
+		c.ret = &disjunction{baseValue{c.src}, d, nil, false}
+		if len(d) == 0 {
+			// TODO(manifest): This should not be unconditionally incomplete,
+			// but it requires results from comprehensions and all to have
+			// some special status. Maybe this can be solved by having results
+			// of list comprehensions be open if they result from iterating over
+			// an open list or struct. This would actually be exactly what
+			// that means. The error here could then only add an incomplete
+			// status if the source is open.
+			c.ret = c.ctx.mkErr(c.src, codeIncomplete, "empty list in call to or")
+		}
+	},
+}
+
+func (x *builtin) representedKind() kind {
+	if x.isValidator() {
+		return x.Params[0]
+	}
+	return x.Kind()
+}
+
+func (x *builtin) Kind() kind {
+	return lambdaKind
+}
+
+func (x *builtin) evalPartial(ctx *context) evaluated {
+	return x
+}
+
+func (x *builtin) subsumesImpl(s *subsumer, v value) bool {
+	if y, ok := v.(*builtin); ok {
+		return x == y
+	}
+	return false
+}
+
+func (x *builtin) name(ctx *context) string {
+	if x.pkg == 0 {
+		return x.Name
+	}
+	return fmt.Sprintf("%s.%s", ctx.LabelStr(x.pkg), x.Name)
+}
+
+func (x *builtin) isValidator() bool {
+	return len(x.Params) == 1 && x.Result == boolKind
+}
+
+func convertBuiltin(v evaluated) evaluated {
+	x, ok := v.(*builtin)
+	if ok && x.isValidator() {
+		return &customValidator{v.base(), x, []evaluated{}}
+	}
+	return v
+}
+
+func (x *builtin) call(ctx *context, src source, args ...evaluated) (ret value) {
+	if x.Func == nil {
+		return ctx.mkErr(x, "builtin %s is not a function", x.name(ctx))
+	}
+	if len(x.Params)-1 == len(args) && x.Result == boolKind {
+		// We have a custom builtin
+		return &customValidator{src.base(), x, args}
+	}
+	switch {
+	case len(x.Params) < len(args):
+		return ctx.mkErr(src, x, "too many arguments in call to %s (have %d, want %d)",
+			x.name(ctx), len(args), len(x.Params))
+	case len(x.Params) > len(args):
+		return ctx.mkErr(src, x, "not enough arguments in call to %s (have %d, want %d)",
+			x.name(ctx), len(args), len(x.Params))
+	}
+	for i, a := range args {
+		if x.Params[i] != bottomKind {
+			if unifyType(x.Params[i], a.Kind()) == bottomKind {
+				const msg = "cannot use %s (type %s) as %s in argument %d to %s"
+				return ctx.mkErr(src, x, msg, ctx.str(a), a.Kind(), x.Params[i], i+1, x.name(ctx))
+			}
+		}
+	}
+	call := callCtxt{src: src, ctx: ctx, builtin: x, args: args}
+	defer func() {
+		var errVal interface{} = call.err
+		if err := recover(); err != nil {
+			errVal = err
+		}
+		ret = processErr(&call, errVal, ret)
+	}()
+	x.Func(&call)
+	switch v := call.ret.(type) {
+	case value:
+		return v
+	case *valueError:
+		return v.err
+	}
+	return convertVal(ctx, x, true, call.ret)
+}
+
+func processErr(call *callCtxt, errVal interface{}, ret value) value {
+	ctx := call.ctx
+	x := call.builtin
+	src := call.src
+	const msg = "error in call to %s: %v"
+	switch err := errVal.(type) {
+	case nil:
+	case *callError:
+		ret = err.b
+	case *json.MarshalerError:
+		if err, ok := err.Err.(*marshalError); ok && err.b != nil {
+			ret = err.b
+		}
+	case *marshalError:
+		ret = err.b
+		ret = ctx.mkErr(src, x, ret, msg, x.name(ctx), err)
+	case *valueError:
+		ret = err.err
+		ret = ctx.mkErr(src, x, ret, msg, x.name(ctx), err)
+	default:
+		if call.err == internal.ErrIncomplete {
+			ret = ctx.mkErr(src, codeIncomplete, "incomplete value")
+		} else {
+			// TODO: store the underlying error explicitly
+			ret = ctx.mkErr(src, x, msg, x.name(ctx), err)
+		}
+	}
+	return ret
+}
+
+// callCtxt is passed to builtin implementations.
+type callCtxt struct {
+	src     source
+	ctx     *context
+	builtin *builtin
+	args    []evaluated
+	err     error
+	ret     interface{}
+}
+
+func (c *callCtxt) name() string {
+	return c.builtin.name(c.ctx)
+}
+
+var builtins = map[string]*Instance{}
+
+func initBuiltins(pkgs map[string]*builtinPkg) {
+	ctx := sharedIndex.newContext()
+	keys := []string{}
+	for k := range pkgs {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+	for _, k := range keys {
+		b := pkgs[k]
+		e := mustCompileBuiltins(ctx, b, k)
+
+		i := sharedIndex.addInst(&Instance{
+			ImportPath: k,
+			PkgName:    path.Base(k),
+			rootStruct: e,
+			rootValue:  e,
+		})
+
+		builtins[k] = i
+		builtins["-/"+path.Base(k)] = i
+	}
+}
+
+func getBuiltinShorthandPkg(ctx *context, shorthand string) *structLit {
+	return getBuiltinPkg(ctx, "-/"+shorthand)
+}
+
+func getBuiltinPkg(ctx *context, path string) *structLit {
+	p, ok := builtins[path]
+	if !ok {
+		return nil
+	}
+	return p.rootStruct
+}
+
+func init() {
+	internal.UnifyBuiltin = func(val interface{}, kind string) interface{} {
+		v := val.(Value)
+		ctx := v.ctx()
+
+		p := strings.Split(kind, ".")
+		pkg, name := p[0], p[1]
+		s := getBuiltinPkg(ctx, pkg)
+		if s == nil {
+			return v
+		}
+		a := s.Lookup(ctx, ctx.Label(name, false))
+		if a.v == nil {
+			return v
+		}
+
+		return v.Unify(newValueRoot(ctx, a.v.evalPartial(ctx)))
+	}
+}
+
+// do returns whether the call should be done.
+func (c *callCtxt) do() bool {
+	return c.err == nil
+}
+
+type callError struct {
+	b *bottom
+}
+
+func (e *callError) Error() string {
+	return fmt.Sprint(e.b)
+}
+
+func (c *callCtxt) errf(src source, underlying error, format string, args ...interface{}) {
+	a := make([]interface{}, 0, 2+len(args))
+	if err, ok := underlying.(*valueError); ok {
+		a = append(a, err.err)
+	}
+	a = append(a, format)
+	a = append(a, args...)
+	err := c.ctx.mkErr(src, a...)
+	c.err = &callError{err}
+}
+
+func (c *callCtxt) value(i int) Value {
+	v := newValueRoot(c.ctx, c.args[i])
+	v, _ = v.Default()
+	if !v.IsConcrete() {
+		c.errf(c.src, v.toErr(c.ctx.mkErr(c.src, codeIncomplete,
+			"non-concrete value")), "incomplete")
+	}
+	return v
+}
+
+func (c *callCtxt) structVal(i int) *Struct {
+	v := newValueRoot(c.ctx, c.args[i])
+	s, err := v.Struct()
+	if err != nil {
+		c.invalidArgType(c.args[i], i, "struct", err)
+		return nil
+	}
+	return s
+}
+
+func (c *callCtxt) invalidArgType(arg value, i int, typ string, err error) {
+	if err != nil {
+		c.errf(c.src, err, "cannot use %s (type %s) as %s in argument %d to %s: %v",
+			c.ctx.str(arg), arg.Kind(), typ, i, c.name(), err)
+	} else {
+		c.errf(c.src, nil, "cannot use %s (type %s) as %s in argument %d to %s",
+			c.ctx.str(arg), arg.Kind(), typ, i, c.name())
+	}
+}
+
+func (c *callCtxt) int(i int) int     { return int(c.intValue(i, 64, "int64")) }
+func (c *callCtxt) int8(i int) int8   { return int8(c.intValue(i, 8, "int8")) }
+func (c *callCtxt) int16(i int) int16 { return int16(c.intValue(i, 16, "int16")) }
+func (c *callCtxt) int32(i int) int32 { return int32(c.intValue(i, 32, "int32")) }
+func (c *callCtxt) rune(i int) rune   { return rune(c.intValue(i, 32, "rune")) }
+func (c *callCtxt) int64(i int) int64 { return int64(c.intValue(i, 64, "int64")) }
+
+func (c *callCtxt) intValue(i, bits int, typ string) int64 {
+	arg := c.args[i]
+	x := newValueRoot(c.ctx, arg)
+	n, err := x.Int(nil)
+	if err != nil {
+		c.invalidArgType(arg, i, typ, err)
+		return 0
+	}
+	if n.BitLen() > bits {
+		c.errf(c.src, err, "int %s overflows %s in argument %d in call to %s",
+			n, typ, i, c.name())
+	}
+	res, _ := x.Int64()
+	return res
+}
+
+func (c *callCtxt) uint(i int) uint     { return uint(c.uintValue(i, 64, "uint64")) }
+func (c *callCtxt) uint8(i int) uint8   { return uint8(c.uintValue(i, 8, "uint8")) }
+func (c *callCtxt) byte(i int) uint8    { return byte(c.uintValue(i, 8, "byte")) }
+func (c *callCtxt) uint16(i int) uint16 { return uint16(c.uintValue(i, 16, "uint16")) }
+func (c *callCtxt) uint32(i int) uint32 { return uint32(c.uintValue(i, 32, "uint32")) }
+func (c *callCtxt) uint64(i int) uint64 { return uint64(c.uintValue(i, 64, "uint64")) }
+
+func (c *callCtxt) uintValue(i, bits int, typ string) uint64 {
+	x := newValueRoot(c.ctx, c.args[i])
+	n, err := x.Int(nil)
+	if err != nil || n.Sign() < 0 {
+		c.invalidArgType(c.args[i], i, typ, err)
+		return 0
+	}
+	if n.BitLen() > bits {
+		c.errf(c.src, err, "int %s overflows %s in argument %d in call to %s",
+			n, typ, i, c.name())
+	}
+	res, _ := x.Uint64()
+	return res
+}
+
+func (c *callCtxt) decimal(i int) *apd.Decimal {
+	x := newValueRoot(c.ctx, c.args[i])
+	if _, err := x.MantExp(nil); err != nil {
+		c.invalidArgType(c.args[i], i, "Decimal", err)
+		return nil
+	}
+	return &c.args[i].(*numLit).X
+}
+
+func (c *callCtxt) float64(i int) float64 {
+	x := newValueRoot(c.ctx, c.args[i])
+	res, err := x.Float64()
+	if err != nil {
+		c.invalidArgType(c.args[i], i, "float64", err)
+		return 0
+	}
+	return res
+}
+
+func (c *callCtxt) bigInt(i int) *big.Int {
+	x := newValueRoot(c.ctx, c.args[i])
+	n, err := x.Int(nil)
+	if err != nil {
+		c.invalidArgType(c.args[i], i, "int", err)
+		return nil
+	}
+	return n
+}
+
+func (c *callCtxt) bigFloat(i int) *big.Float {
+	x := newValueRoot(c.ctx, c.args[i])
+	var mant big.Int
+	exp, err := x.MantExp(&mant)
+	if err != nil {
+		c.invalidArgType(c.args[i], i, "float", err)
+		return nil
+	}
+	f := &big.Float{}
+	f.SetInt(&mant)
+	if exp != 0 {
+		var g big.Float
+		e := big.NewInt(int64(exp))
+		f.Mul(f, g.SetInt(e.Exp(ten, e, nil)))
+	}
+	return f
+}
+
+func (c *callCtxt) string(i int) string {
+	x := newValueRoot(c.ctx, c.args[i])
+	v, err := x.String()
+	if err != nil {
+		c.invalidArgType(c.args[i], i, "string", err)
+		return ""
+	}
+	return v
+}
+
+func (c *callCtxt) bytes(i int) []byte {
+	x := newValueRoot(c.ctx, c.args[i])
+	v, err := x.Bytes()
+	if err != nil {
+		c.invalidArgType(c.args[i], i, "bytes", err)
+		return nil
+	}
+	return v
+}
+
+func (c *callCtxt) reader(i int) io.Reader {
+	x := newValueRoot(c.ctx, c.args[i])
+	// TODO: optimize for string and bytes cases
+	r, err := x.Reader()
+	if err != nil {
+		c.invalidArgType(c.args[i], i, "bytes|string", err)
+		return nil
+	}
+	return r
+}
+
+func (c *callCtxt) bool(i int) bool {
+	x := newValueRoot(c.ctx, c.args[i])
+	b, err := x.Bool()
+	if err != nil {
+		c.invalidArgType(c.args[i], i, "bool", err)
+		return false
+	}
+	return b
+}
+
+func (c *callCtxt) list(i int) (a []Value) {
+	arg := c.args[i]
+	x := newValueRoot(c.ctx, arg)
+	v, err := x.List()
+	if err != nil {
+		c.invalidArgType(c.args[i], i, "list", err)
+		return a
+	}
+	for v.Next() {
+		a = append(a, v.Value())
+	}
+	return a
+}
+
+func (c *callCtxt) iter(i int) (a Iterator) {
+	arg := c.args[i]
+	x := newValueRoot(c.ctx, arg)
+	v, err := x.List()
+	if err != nil {
+		c.invalidArgType(c.args[i], i, "list", err)
+		return Iterator{ctx: c.ctx}
+	}
+	return v
+}
+
+func (c *callCtxt) decimalList(i int) (a []*apd.Decimal) {
+	arg := c.args[i]
+	x := newValueRoot(c.ctx, arg)
+	v, err := x.List()
+	if err != nil {
+		c.invalidArgType(c.args[i], i, "list", err)
+		return nil
+	}
+	for j := 0; v.Next(); j++ {
+		num, err := v.Value().getNum(numKind)
+		if err != nil {
+			c.errf(c.src, err, "invalid list element %d in argument %d to %s: %v",
+				j, i, c.name(), err)
+			break
+		}
+		a = append(a, &num.X)
+	}
+	return a
+}
+
+func (c *callCtxt) strList(i int) (a []string) {
+	arg := c.args[i]
+	x := newValueRoot(c.ctx, arg)
+	v, err := x.List()
+	if err != nil {
+		c.invalidArgType(c.args[i], i, "list", err)
+		return nil
+	}
+	for j := 0; v.Next(); j++ {
+		str, err := v.Value().String()
+		if err != nil {
+			c.errf(c.src, err, "invalid list element %d in argument %d to %s: %v",
+				j, i, c.name(), err)
+			break
+		}
+		a = append(a, str)
+	}
+	return a
+}
diff --git a/internal/legacy/cue/builtin_test.go b/internal/legacy/cue/builtin_test.go
new file mode 100644
index 0000000..1901d34
--- /dev/null
+++ b/internal/legacy/cue/builtin_test.go
@@ -0,0 +1,678 @@
+// Copyright 2018 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+	"fmt"
+	"math/big"
+	"strconv"
+	"strings"
+	"testing"
+)
+
+func TestBuiltins(t *testing.T) {
+	test := func(pkg, expr string) []*bimport {
+		return []*bimport{{"",
+			[]string{fmt.Sprintf("import %q\n(%s)", pkg, expr)},
+		}}
+	}
+	testExpr := func(expr string) []*bimport {
+		return []*bimport{{"",
+			[]string{fmt.Sprintf("(%s)", expr)},
+		}}
+	}
+	hexToDec := func(s string) string {
+		var x big.Int
+		x.SetString(s, 16)
+		return x.String()
+	}
+	testCases := []struct {
+		instances []*bimport
+		emit      string
+	}{{
+		test("math", "math.Pi"),
+		`3.14159265358979323846264338327950288419716939937510582097494459`,
+	}, {
+		test("math", "math.Floor(math.Pi)"),
+		`3`,
+	}, {
+		test("math", "math.Pi(3)"),
+		`_|_(cannot call non-function Pi (type float))`,
+	}, {
+		test("math", "math.Floor(3, 5)"),
+		`_|_(too many arguments in call to math.Floor (have 2, want 1))`,
+	}, {
+		test("math", `math.Floor("foo")`),
+		`_|_(cannot use "foo" (type string) as number in argument 1 to math.Floor)`,
+	}, {
+		test("crypto/sha256", `sha256.Sum256("hash me")`),
+		`'\xeb \x1a\xf5\xaa\xf0\xd6\x06)\xd3Ò¦\x1eFl\xfc\x0f\xed\xb5\x17\xad\xd81\xec\xacR5\xe1Ú©c\xd6'`,
+	}, {
+		test("crypto/md5", `len(md5.Sum("hash me"))`),
+		`16`,
+	}, {
+		test("crypto/sha1", `len(sha1.Sum("hash me"))`),
+		`20`,
+	}, {
+		test("crypto/sha256", `len(sha256.Sum256("hash me"))`),
+		`32`,
+	}, {
+		test("crypto/sha256", `len(sha256.Sum224("hash me"))`),
+		`28`,
+	}, {
+		test("crypto/sha512", `len(sha512.Sum512("hash me"))`),
+		`64`,
+	}, {
+		test("crypto/sha512", `len(sha512.Sum384("hash me"))`),
+		`48`,
+	}, {
+		test("crypto/sha512", `len(sha512.Sum512_224("hash me"))`),
+		`28`,
+	}, {
+		test("crypto/sha512", `len(sha512.Sum512_256("hash me"))`),
+		`32`,
+	}, {
+		test("encoding/base64", `base64.Encode(null, "foo")`),
+		`"Zm9v"`,
+	}, {
+		test("encoding/base64", `base64.Decode(null, base64.Encode(null, "foo"))`),
+		`'foo'`,
+	}, {
+		test("encoding/base64", `base64.Decode(null, "foo")`),
+		`_|_(error in call to encoding/base64.Decode: illegal base64 data at input byte 0)`,
+	}, {
+		test("encoding/base64", `base64.Decode({}, "foo")`),
+		`_|_(error in call to encoding/base64.Decode: base64: unsupported encoding: cannot use value {} (type struct) as null)`,
+	}, {
+		test("encoding/hex", `hex.Encode("foo")`),
+		`"666f6f"`,
+	}, {
+		test("encoding/hex", `hex.Decode(hex.Encode("foo"))`),
+		`'foo'`,
+	}, {
+		test("encoding/hex", `hex.Decode("foo")`),
+		`_|_(error in call to encoding/hex.Decode: encoding/hex: invalid byte: U+006F 'o')`,
+	}, {
+		test("encoding/hex", `hex.Dump('foo')`),
+		`"00000000  66 6f 6f                                          |foo|\n"`,
+	}, {
+		test("encoding/json", `json.Validate("{\"a\":10}", {b:string})`),
+		`true`,
+	}, {
+		test("encoding/json", `json.Validate("{\"a\":10}", {a:<3})`),
+		`_|_(error in call to encoding/json.Validate: a: invalid value 10 (out of bound <3))`,
+	}, {
+		test("encoding/yaml", `yaml.Validate("a: 2\n---\na: 4", {a:<3})`),
+		`_|_(error in call to encoding/yaml.Validate: a: invalid value 4 (out of bound <3))`,
+	}, {
+		test("encoding/yaml", `yaml.Validate("a: 2\n---\na: 4", {a:<5})`),
+		`true`,
+	}, {
+		test("encoding/yaml", `yaml.Validate("a: 2\n", {a:<5, b:int})`),
+		`_|_(error in call to encoding/yaml.Validate: b: incomplete value (int))`,
+	}, {
+		test("encoding/yaml", `yaml.ValidatePartial("a: 2\n---\na: 4", {a:<3})`),
+		`_|_(error in call to encoding/yaml.ValidatePartial: a: invalid value 4 (out of bound <3))`,
+	}, {
+		test("encoding/yaml", `yaml.ValidatePartial("a: 2\n---\na: 4", {a:<5})`),
+		`true`,
+	}, {
+		test("encoding/yaml", `yaml.ValidatePartial("a: 2\n", {a:<5, b:int})`),
+		`true`,
+	}, {
+		test("strconv", `strconv.FormatUint(64, 16)`),
+		`"40"`,
+	}, {
+		// Find a better alternative, as this call should go.
+		test("strconv", `strconv.FormatFloat(3.02, 300, 4, 64)`),
+		`_|_(int 300 overflows byte in argument 1 in call to strconv.FormatFloat)`,
+	}, {
+		// Find a better alternative, as this call should go.
+		test("strconv", `strconv.FormatFloat(3.02, -1, 4, 64)`),
+		`_|_(cannot use -1 (type int) as byte in argument 1 to strconv.FormatFloat)`,
+	}, {
+		// Find a better alternative, as this call should go.
+		test("strconv", `strconv.FormatFloat(3.02, 1.0, 4, 64)`),
+		`_|_(cannot use 1.0 (type float) as int in argument 2 to strconv.FormatFloat)`,
+	}, {
+		test("list", `list.Avg([1, 2, 3, 4])`),
+		`2.5`,
+	}, {
+		test("list", `list.Avg([])`),
+		`_|_(error in call to list.Avg: empty list)`,
+	}, {
+		test("list", `list.Avg("foo")`),
+		`_|_(cannot use "foo" (type string) as list in argument 1 to list.Avg)`,
+	}, {
+		test("list", `list.Drop([1, 2, 3, 4], 0)`),
+		`[1,2,3,4]`,
+	}, {
+		test("list", `list.Drop([1, 2, 3, 4], 2)`),
+		`[3,4]`,
+	}, {
+		test("list", `list.Drop([1, 2, 3, 4], 10)`),
+		`[]`,
+	}, {
+		test("list", `list.Drop([1, 2, 3, 4], -1)`),
+		`_|_(error in call to list.Drop: negative index)`,
+	}, {
+		test("list", `list.FlattenN([1, [[2, 3], []], [4]], -1)`),
+		`[1,2,3,4]`,
+	}, {
+		test("list", `list.FlattenN([1, [[2, 3], []], [4]], 0)`),
+		`[1,[[2,3],[]],[4]]`,
+	}, {
+		test("list", `list.FlattenN([1, [[2, 3], []], [4]], 1)`),
+		`[1,[2,3],[],4]`,
+	}, {
+		test("list", `list.FlattenN([1, [[2, 3], []], [4]], 2)`),
+		`[1,2,3,4]`,
+	}, {
+		test("list", `list.FlattenN([[1, 2] | *[]], -1)`),
+		`[]`,
+	}, {
+		test("list", `list.FlattenN("foo", 1)`),
+		`_|_(error in call to list.FlattenN: cannot use value "foo" (type string) as list)`,
+	}, {
+		test("list", `list.FlattenN([], "foo")`),
+		`_|_(cannot use "foo" (type string) as int in argument 2 to list.FlattenN)`,
+	}, {
+		test("list", `list.Max([1, 2, 3, 4])`),
+		`4`,
+	}, {
+		test("list", `list.Max([])`),
+		`_|_(error in call to list.Max: empty list)`,
+	}, {
+		test("list", `list.Max("foo")`),
+		`_|_(cannot use "foo" (type string) as list in argument 1 to list.Max)`,
+	}, {
+		test("list", `list.Min([1, 2, 3, 4])`),
+		`1`,
+	}, {
+		test("list", `list.Min([])`),
+		`_|_(error in call to list.Min: empty list)`,
+	}, {
+		test("list", `list.Min("foo")`),
+		`_|_(cannot use "foo" (type string) as list in argument 1 to list.Min)`,
+	}, {
+		test("list", `list.Product([1, 2, 3, 4])`),
+		`24`,
+	}, {
+		test("list", `list.Product([])`),
+		`1`,
+	}, {
+		test("list", `list.Product("foo")`),
+		`_|_(cannot use "foo" (type string) as list in argument 1 to list.Product)`,
+	}, {
+		test("list", `list.Range(0, 5, 0)`),
+		`_|_(error in call to list.Range: step must be non zero)`,
+	}, {
+		test("list", `list.Range(5, 0, 1)`),
+		`_|_(error in call to list.Range: end must be greater than start when step is positive)`,
+	}, {
+		test("list", `list.Range(0, 5, -1)`),
+		`_|_(error in call to list.Range: end must be less than start when step is negative)`,
+	}, {
+		test("list", `list.Range(0, 5, 1)`),
+		`[0,1,2,3,4]`,
+	}, {
+		test("list", `list.Range(0, 1, 1)`),
+		`[0]`,
+	}, {
+		test("list", `list.Range(0, 5, 2)`),
+		`[0,2,4]`,
+	}, {
+		test("list", `list.Range(5, 0, -1)`),
+		`[5,4,3,2,1]`,
+	}, {
+		test("list", `list.Range(0, 5, 0.5)`),
+		`[0,0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5]`,
+	}, {
+		test("list", `list.Slice([1, 2, 3, 4], 1, 3)`),
+		`[2,3]`,
+	}, {
+		test("list", `list.Slice([1, 2, 3, 4], -1, 3)`),
+		`_|_(error in call to list.Slice: negative index)`,
+	}, {
+		test("list", `list.Slice([1, 2, 3, 4], 3, 1)`),
+		`_|_(error in call to list.Slice: invalid index: 3 > 1)`,
+	}, {
+		test("list", `list.Slice([1, 2, 3, 4], 5, 5)`),
+		`_|_(error in call to list.Slice: slice bounds out of range)`,
+	}, {
+		test("list", `list.Slice([1, 2, 3, 4], 1, 5)`),
+		`_|_(error in call to list.Slice: slice bounds out of range)`,
+	}, {
+		test("list", `list.Sort([], list.Ascending)`),
+		`[]`,
+	}, {
+		test("list", `list.Sort([2, 3, 1, 4], {x:_, y:_, less: x<y})`),
+		`[1,2,3,4]`,
+	}, {
+		test("list", `list.SortStable([{a:2,v:1}, {a:1,v:2}, {a:1,v:3}], {
+			x:_,
+			y:_,
+			less: (x.a < y.a)
+		})`),
+		`[{a: 1, v: 2},{a: 1, v: 3},{a: 2, v: 1}]`,
+	}, {
+		test("list", `list.Sort([{a:1}, {b:2}], list.Ascending)`),
+		`_|_(error in call to list.Sort: less: conflicting values close(T, close(T)) and {b: 2} (mismatched types number|string and struct))`,
+	}, {
+		test("list", `list.SortStrings(["b", "a"])`),
+		`["a","b"]`,
+	}, {
+		test("list", `list.SortStrings([1, 2])`),
+		`_|_(invalid list element 0 in argument 0 to list.SortStrings: 0: cannot use value 1 (type int) as string)`,
+	}, {
+		test("list", `list.Sum([1, 2, 3, 4])`),
+		`10`,
+	}, {
+		test("list", `list.Sum([])`),
+		`0`,
+	}, {
+		test("list", `list.Sum("foo")`),
+		`_|_(cannot use "foo" (type string) as list in argument 1 to list.Sum)`,
+	}, {
+		test("list", `list.Take([1, 2, 3, 4], 0)`),
+		`[]`,
+	}, {
+		test("list", `list.Take([1, 2, 3, 4], 2)`),
+		`[1,2]`,
+	}, {
+		test("list", `list.Take([1, 2, 3, 4], 10)`),
+		`[1,2,3,4]`,
+	}, {
+		test("list", `list.Take([1, 2, 3, 4], -1)`),
+		`_|_(error in call to list.Take: negative index)`,
+	}, {
+		test("list", `list.MinItems([1, 2, 3, 4], 2)`),
+		`true`,
+	}, {
+		test("list", `list.MinItems([1, 2, 3, 4], 5)`),
+		`false`,
+	}, {
+		test("list", `list.MaxItems([1, 2, 3, 4], 5)`),
+		`true`,
+	}, {
+		test("list", `list.MaxItems([1, 2, 3, 4], 2)`),
+		`false`,
+	}, {
+		// Panics
+		test("math", `math.Jacobi(1000, 2000)`),
+		`_|_(error in call to math.Jacobi: big: invalid 2nd argument to Int.Jacobi: need odd integer but got 2000)`,
+	}, {
+		test("math", `math.Jacobi(1000, 201)`),
+		`1`,
+	}, {
+		test("math", `math.Asin(2.0e400)`),
+		`_|_(cannot use 2.0e+400 (type float) as float64 in argument 0 to math.Asin: value was rounded up)`,
+	}, {
+		test("math", `math.MultipleOf(4, 2)`), `true`,
+	}, {
+		test("math", `math.MultipleOf(5, 2)`), `false`,
+	}, {
+		test("math", `math.MultipleOf(5, 0)`),
+		`_|_(error in call to math.MultipleOf: division by zero)`,
+	}, {
+		test("math", `math.MultipleOf(100, 1.00001)`), `false`,
+	}, {
+		test("math", `math.MultipleOf(1, 1)`), `true`,
+	}, {
+		test("math", `math.MultipleOf(5, 2.5)`), `true`,
+	}, {
+		test("math", `math.MultipleOf(100e100, 10)`), `true`,
+	}, {
+		test("encoding/csv", `csv.Decode("1,2,3\n4,5,6")`),
+		`[["1","2","3"],["4","5","6"]]`,
+	}, {
+		test("regexp", `regexp.Find(#"f\w\w"#, "afoot")`),
+		`"foo"`,
+	}, {
+		test("regexp", `regexp.Find(#"f\w\w"#, "bar")`),
+		`_|_(error in call to regexp.Find: no match)`,
+	}, {
+		test("regexp", `regexp.FindAll(#"f\w\w"#, "afoot afloat from", 2)`),
+		`["foo","flo"]`,
+	}, {
+		test("regexp", `regexp.FindAll(#"f\w\w"#, "afoot afloat from", 2)`),
+		`["foo","flo"]`,
+	}, {
+		test("regexp", `regexp.FindAll(#"f\w\w"#, "bla bla", -1)`),
+		`_|_(error in call to regexp.FindAll: no match)`,
+	}, {
+		test("regexp", `regexp.FindSubmatch(#"f(\w)(\w)"#, "afloat afoot from")`),
+		`["flo","l","o"]`,
+	}, {
+		test("regexp", `regexp.FindAllSubmatch(#"f(\w)(\w)"#, "afloat afoot from", -1)`),
+		`[["flo","l","o"],["foo","o","o"],["fro","r","o"]]`,
+	}, {
+		test("regexp", `regexp.FindAllSubmatch(#"f(\w)(\w)"#, "aglom", -1)`),
+		`_|_(error in call to regexp.FindAllSubmatch: no match)`,
+	}, {
+		test("regexp", `regexp.FindNamedSubmatch(#"f(?P<A>\w)(?P<B>\w)"#, "afloat afoot from")`),
+		`{A: "l", B: "o"}`,
+	}, {
+		test("regexp", `regexp.FindAllNamedSubmatch(#"f(?P<A>\w)(?P<B>\w)"#, "afloat afoot from", -1)`),
+		`[{A: "l", B: "o"},{A: "o", B: "o"},{A: "r", B: "o"}]`,
+	}, {
+		test("regexp", `regexp.FindAllNamedSubmatch(#"f(?P<A>optional)?"#, "fbla", -1)`),
+		`[{A: ""}]`,
+	}, {
+		test("regexp", `regexp.FindAllNamedSubmatch(#"f(?P<A>\w)(?P<B>\w)"#, "aglom", -1)`),
+		`_|_(error in call to regexp.FindAllNamedSubmatch: no match)`,
+	}, {
+		test("regexp", `regexp.Valid & "valid"`),
+		`"valid"`,
+	}, {
+		test("regexp", `regexp.Valid & "invalid)"`),
+		"_|_(error in call to regexp.Valid: error parsing regexp: unexpected ): `invalid)`)",
+	}, {
+		test("strconv", `strconv.FormatBool(true)`),
+		`"true"`,
+	}, {
+		test("strings", `strings.Join(["Hello", "World!"], " ")`),
+		`"Hello World!"`,
+	}, {
+		test("strings", `strings.Join([1, 2], " ")`),
+		`_|_(invalid list element 0 in argument 0 to strings.Join: 0: cannot use value 1 (type int) as string)`,
+	}, {
+		test("strings", `strings.ByteAt("a", 0)`),
+		strconv.Itoa('a'),
+	}, {
+		test("strings", `strings.ByteSlice("Hello", 2, 5)`),
+		`'llo'`,
+	}, {
+		test("strings", `strings.SliceRunes("✓ Hello", 0, 3)`),
+		`"✓ H"`,
+	}, {
+		test("strings", `strings.Runes("Café")`),
+		strings.Replace(fmt.Sprint([]rune{'C', 'a', 'f', 'é'}), " ", ",", -1),
+	}, {
+		test("math/bits", `bits.Or(0x8, 0x1)`),
+		`9`,
+	}, {
+		testExpr(`len({})`),
+		`0`,
+	}, {
+		testExpr(`len({a: 1, b: 2, {[foo=_]: int}, _c: 3})`),
+		`2`,
+	}, {
+		testExpr(`len([1, 2, 3])`),
+		`3`,
+	}, {
+		testExpr(`len("foo")`),
+		`3`,
+	}, {
+		testExpr(`len('f\x20\x20')`),
+		`3`,
+	}, {
+		testExpr(`and([string, "foo"])`),
+		`"foo"`,
+	}, {
+		testExpr(`and([string, =~"fo"]) & "foo"`),
+		`"foo"`,
+	}, {
+		testExpr(`and([])`),
+		`{}`, // _ & top scope
+	}, {
+		testExpr(`or([1, 2, 3]) & 2`),
+		`2`,
+	}, {
+		testExpr(`or([])`),
+		`_|_(empty list in call to or)`,
+	}, {
+		test("encoding/csv", `csv.Encode([[1,2,3],[4,5],[7,8,9]])`),
+		`"1,2,3\n4,5\n7,8,9\n"`,
+	}, {
+		test("encoding/csv", `csv.Encode([["a", "b"], ["c"]])`),
+		`"a,b\nc\n"`,
+	}, {
+		test("encoding/json", `json.Valid("1")`),
+		`true`,
+	}, {
+		test("encoding/json", `json.Compact("[1, 2]")`),
+		`"[1,2]"`,
+	}, {
+		test("encoding/json", `json.Indent(#"{"a": 1, "b": 2}"#, "", "  ")`),
+		`"{\n  \"a\": 1,\n  \"b\": 2\n}"`,
+	}, {
+		test("encoding/json", `json.Unmarshal("1")`),
+		`1`,
+	}, {
+		test("encoding/json", `json.MarshalStream([{a: 1}, {b: 2}])`),
+		`"{\"a\":1}\n{\"b\":2}\n"`,
+	}, {
+		test("encoding/json", `{
+			x: int
+			y: json.Marshal({a: x})
+		}`),
+		`{x: int, y: Marshal ({a: x})}`,
+	}, {
+		test("encoding/yaml", `yaml.MarshalStream([{a: 1}, {b: 2}])`),
+		`"a: 1\n---\nb: 2\n"`,
+	}, {
+		test("net", `net.FQDN & "foo.bar."`),
+		`"foo.bar."`,
+	}, {
+		test("net", `net.FQDN("foo.bararararararararararararararararararararararararararararararararara")`),
+		`false`,
+	}, {
+		test("net", `net.SplitHostPort("[::%lo0]:80")`),
+		`["::%lo0","80"]`,
+	}, {
+		test("net", `net.JoinHostPort("example.com", "80")`),
+		`"example.com:80"`,
+	}, {
+		test("net", `net.JoinHostPort("2001:db8::1", 80)`),
+		`"[2001:db8::1]:80"`,
+	}, {
+		test("net", `net.JoinHostPort([192,30,4,2], 80)`),
+		`"192.30.4.2:80"`,
+	}, {
+		test("net", `net.JoinHostPort([192,30,4], 80)`),
+		`_|_(error in call to net.JoinHostPort: invalid host [192,30,4])`,
+	}, {
+		test("net", `net.IP("23.23.23.23")`),
+		`true`,
+	}, {
+		test("net", `net.IPv4 & "23.23.23.2333"`),
+		`_|_(invalid value "23.23.23.2333" (does not satisfy net.IPv4()))`,
+	}, {
+		test("net", `net.IP("23.23.23.23")`),
+		`true`,
+	}, {
+		test("net", `net.IP("2001:db8::1")`),
+		`true`,
+	}, {
+		test("net", `net.IPv4("2001:db8::1")`),
+		`false`,
+	}, {
+		test("net", `net.IPv4() & "ff02::1:3"`),
+		`_|_(invalid value "ff02::1:3" (does not satisfy net.IPv4()))`,
+	}, {
+		test("net", `net.LoopbackIP([127, 0, 0, 1])`),
+		`true`,
+	}, {
+		test("net", `net.LoopbackIP("127.0.0.1")`),
+		`true`,
+	}, {
+		test("net", `net.ToIP4("127.0.0.1")`),
+		`[127,0,0,1]`,
+	}, {
+		test("net", `net.ToIP16("127.0.0.1")`),
+		`[0,0,0,0,0,0,0,0,0,0,255,255,127,0,0,1]`,
+	}, {
+		test("strings", `strings.ToCamel("AlphaBeta")`),
+		`"alphaBeta"`,
+	}, {
+		test("strings", `strings.ToTitle("alpha")`),
+		`"Alpha"`,
+	}, {
+		test("strings", `strings.MaxRunes(3) & "foo"`),
+		`"foo"`,
+	}, {
+		test("strings", `strings.MaxRunes(3) & "quux"`),
+		`_|_(invalid value "quux" (does not satisfy strings.MaxRunes(3)))`,
+	}, {
+		test("strings", `strings.MinRunes(1) & "e"`),
+		`"e"`,
+	}, {
+		test("strings", `strings.MaxRunes(0) & "e"`),
+		`_|_(invalid value "e" (does not satisfy strings.MaxRunes(0)))`,
+	}, {
+		test("strings", `strings.MaxRunes(0) & ""`),
+		`""`,
+	}, {
+		test("strings", `strings.MinRunes(3) & "hello"`),
+		`"hello"`,
+	}, {
+		test("strings", `strings.MaxRunes(10) & "hello"`),
+		`"hello"`,
+	}, {
+		test("strings", `strings.MaxRunes(3) & "hello"`),
+		`_|_(invalid value "hello" (does not satisfy strings.MaxRunes(3)))`,
+	}, {
+		test("strings", `strings.MinRunes(10) & "hello"`),
+		`_|_(invalid value "hello" (does not satisfy strings.MinRunes(10)))`,
+	}, {
+		test("struct", `struct.MinFields(0) & ""`),
+		`_|_(conflicting values MinFields (0) and "" (mismatched types struct and string))`,
+	}, {
+		test("struct", `struct.MinFields(0) & {a: 1}`),
+		`{a: 1}`,
+	}, {
+		test("struct", `struct.MinFields(2) & {a: 1}`),
+		`_|_(invalid value {a: 1} (does not satisfy struct.MinFields(2)))`,
+	}, {
+		test("struct", `struct.MaxFields(0) & {a: 1}`),
+		`_|_(invalid value {a: 1} (does not satisfy struct.MaxFields(0)))`,
+	}, {
+		test("struct", `struct.MaxFields(2) & {a: 1}`),
+		`{a: 1}`,
+	}, {
+		test("math", `math.Pow(8, 4)`), `4096`,
+	}, {
+		test("math", `math.Pow10(4)`), `10000`,
+	}, {
+		test("math", `math.Signbit(-4)`), `true`,
+	}, {
+		test("math", `math.Round(2.5)`), `3`,
+	}, {
+		test("math", `math.Round(-2.5)`), `-3`,
+	}, {
+		test("math", `math.RoundToEven(2.5)`), `2`,
+	}, {
+		test("math", `math.RoundToEven(-2.5)`), `-2`,
+	}, {
+		test("math", `math.Abs(2.5)`), `2.5`,
+	}, {
+		test("math", `math.Abs(-2.2)`), `2.2`,
+	}, {
+		test("math", `math.Cbrt(2)`), `1.25992104989487316476721`,
+	}, {
+		test("math", `math.Copysign(5, -2.2)`), `-5`,
+	}, {
+		test("math", `math.Exp(3)`), `20.0855369231876677409285`,
+	}, {
+		test("math", `math.Exp2(3.5)`), `11.3137084989847603904135`,
+	}, {
+		test("math", `math.Log(4)`), `1.38629436111989061883446`,
+	}, {
+		test("math", `math.Log10(4)`), `0.602059991327962390427478`,
+	}, {
+		test("math", `math.Log2(5)`),
+		`2.32192809488736234787032`,
+	}, {
+		test("math", `math.Dim(3, 2.5)`), `0.5`,
+	}, {
+		test("math", `math.Dim(5, 7.2)`), `0`,
+	}, {
+		test("math", `math.Ceil(2.5)`), `3`,
+	}, {
+		test("math", `math.Ceil(-2.2)`), `-2`,
+	}, {
+		test("math", `math.Floor(2.9)`), `2`,
+	}, {
+		test("math", `math.Floor(-2.2)`), `-3`,
+	}, {
+		test("math", `math.Trunc(2.5)`), `2`,
+	}, {
+		test("math", `math.Trunc(-2.9)`), `-2`,
+	}, {
+		test("math/bits", `bits.Lsh(0x8, 4)`), `128`,
+	}, {
+		test("math/bits", `bits.Rsh(0x100, 4)`), `16`,
+	}, {
+		test("math/bits", `bits.At(0x100, 8)`), `1`,
+	}, {
+		test("math/bits", `bits.At(0x100, 9)`), `0`,
+	}, {
+		test("math/bits", `bits.Set(0x100, 7, 1)`), `384`,
+	}, {
+		test("math/bits", `bits.Set(0x100, 8, 0)`), `0`,
+	}, {
+		test("math/bits", `bits.And(0x10000000000000F0E, 0xF0F7)`), `6`,
+	}, {
+		test("math/bits", `bits.Or(0x100000000000000F0, 0x0F)`),
+		hexToDec("100000000000000FF"),
+	}, {
+		test("math/bits", `bits.Xor(0x10000000000000F0F, 0xFF0)`),
+		hexToDec("100000000000000FF"),
+	}, {
+		test("math/bits", `bits.Xor(0xFF0, 0x10000000000000F0F)`),
+		hexToDec("100000000000000FF"),
+	}, {
+		test("math/bits", `bits.Clear(0xF, 0x100000000000008)`), `7`,
+	}, {
+		test("math/bits", `bits.Clear(0x1000000000000000008, 0xF)`),
+		hexToDec("1000000000000000000"),
+	}, {
+		test("text/tabwriter", `tabwriter.Write("""
+			a\tb\tc
+			aaa\tbb\tvv
+			""")`),
+		`"a   b  c\naaa bb vv"`,
+	}, {
+		test("text/tabwriter", `tabwriter.Write([
+				"a\tb\tc",
+				"aaa\tbb\tvv"])`),
+		`"a   b  c\naaa bb vv\n"`,
+	}, {
+		test("text/template", `template.Execute("{{.}}-{{.}}", "foo")`),
+		`"foo-foo"`,
+	}, {
+		test("time", `time.Time & "1937-01-01T12:00:27.87+00:20"`),
+		`"1937-01-01T12:00:27.87+00:20"`,
+	}, {
+		test("time", `time.Time & "no time"`),
+		`_|_(error in call to time.Time: invalid time "no time")`,
+	}, {
+		test("time", `time.Unix(1500000000, 123456)`),
+		`"2017-07-14T02:40:00.000123456Z"`,
+	}}
+	for i, tc := range testCases {
+		t.Run(fmt.Sprint(i), func(t *testing.T) {
+			insts := Build(makeInstances(tc.instances))
+			if err := insts[0].Err; err != nil {
+				t.Fatal(err)
+			}
+			got := strings.TrimSpace(fmt.Sprintf("%s\n", insts[0].Value()))
+			if got != tc.emit {
+				t.Errorf("\n got: %s\nwant: %s", got, tc.emit)
+			}
+		})
+	}
+}
diff --git a/internal/legacy/cue/builtins.go b/internal/legacy/cue/builtins.go
new file mode 100644
index 0000000..290ab79
--- /dev/null
+++ b/internal/legacy/cue/builtins.go
@@ -0,0 +1,3782 @@
+// Code generated by go generate. DO NOT EDIT.
+
+package cue
+
+import (
+	"bytes"
+	"crypto/md5"
+	"crypto/sha1"
+	"crypto/sha256"
+	"crypto/sha512"
+	"encoding/base64"
+	"encoding/csv"
+	"encoding/hex"
+	"encoding/json"
+	"fmt"
+	"html"
+	"io"
+	"math"
+	"math/big"
+	"math/bits"
+	"net"
+	"path"
+	"regexp"
+	"sort"
+	"strconv"
+	"strings"
+	"text/tabwriter"
+	"text/template"
+	"time"
+	"unicode"
+	"unicode/utf8"
+
+	"github.com/cockroachdb/apd/v2"
+	"golang.org/x/net/idna"
+
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/literal"
+	"cuelang.org/go/cue/parser"
+	"cuelang.org/go/internal"
+	cueyaml "cuelang.org/go/internal/encoding/yaml"
+	"cuelang.org/go/internal/third_party/yaml"
+)
+
+func init() {
+	initBuiltins(builtinPackages)
+}
+
+var _ io.Reader
+
+var roundTruncContext = apd.Context{Rounding: apd.RoundDown}
+
+var roundUpContext = apd.Context{Rounding: apd.RoundHalfUp}
+
+var roundEvenContext = apd.Context{Rounding: apd.RoundHalfEven}
+
+var mulContext = apd.BaseContext.WithPrecision(1)
+
+var apdContext = apd.BaseContext.WithPrecision(24)
+
+var zero = apd.New(0, 0)
+
+var two = apd.New(2, 0)
+
+var idnaProfile = idna.New(
+	idna.ValidateLabels(true),
+	idna.VerifyDNSLength(true),
+	idna.StrictDomainName(true),
+)
+
+func netGetIP(ip Value) (goip net.IP) {
+	switch ip.Kind() {
+	case StringKind:
+		s, err := ip.String()
+		if err != nil {
+			return nil
+		}
+		goip := net.ParseIP(s)
+		if goip == nil {
+			return nil
+		}
+		return goip
+
+	case BytesKind:
+		b, err := ip.Bytes()
+		if err != nil {
+			return nil
+		}
+		goip := net.ParseIP(string(b))
+		if goip == nil {
+			return nil
+		}
+		return goip
+
+	case ListKind:
+		iter, err := ip.List()
+		if err != nil {
+			return nil
+		}
+		for iter.Next() {
+			v, err := iter.Value().Int64()
+			if err != nil {
+				return nil
+			}
+			if v < 0 || 255 < v {
+				return nil
+			}
+			goip = append(goip, byte(v))
+		}
+		return goip
+
+	default:
+
+		return nil
+	}
+}
+
+func netToList(ip net.IP) []uint {
+	a := make([]uint, len(ip))
+	for i, p := range ip {
+		a[i] = uint(p)
+	}
+	return a
+}
+
+var split = path.Split
+
+var pathClean = path.Clean
+
+var pathExt = path.Ext
+
+var pathBase = path.Base
+
+var pathIsAbs = path.IsAbs
+
+var pathDir = path.Dir
+
+var errNoMatch = errors.New("no match")
+
+var errNoNamedGroup = errors.New("no named groups")
+
+func timeFormat(value, layout string) (bool, error) {
+	_, err := time.Parse(layout, value)
+	if err != nil {
+
+		return false, fmt.Errorf("invalid time %q", value)
+	}
+	return true, nil
+}
+
+var builtinPackages = map[string]*builtinPkg{
+	"": {
+		native: []*builtin{},
+	},
+	"crypto/md5": {
+		native: []*builtin{{
+			Name:  "Size",
+			Const: "16",
+		}, {
+			Name:  "BlockSize",
+			Const: "64",
+		}, {
+			Name:   "Sum",
+			Params: []kind{bytesKind | stringKind},
+			Result: bytesKind | stringKind,
+			Func: func(c *callCtxt) {
+				data := c.bytes(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						a := md5.Sum(data)
+						return a[:]
+					}()
+				}
+			},
+		}},
+	},
+	"crypto/sha1": {
+		native: []*builtin{{
+			Name:  "Size",
+			Const: "20",
+		}, {
+			Name:  "BlockSize",
+			Const: "64",
+		}, {
+			Name:   "Sum",
+			Params: []kind{bytesKind | stringKind},
+			Result: bytesKind | stringKind,
+			Func: func(c *callCtxt) {
+				data := c.bytes(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						a := sha1.Sum(data)
+						return a[:]
+					}()
+				}
+			},
+		}},
+	},
+	"crypto/sha256": {
+		native: []*builtin{{
+			Name:  "Size",
+			Const: "32",
+		}, {
+			Name:  "Size224",
+			Const: "28",
+		}, {
+			Name:  "BlockSize",
+			Const: "64",
+		}, {
+			Name:   "Sum256",
+			Params: []kind{bytesKind | stringKind},
+			Result: bytesKind | stringKind,
+			Func: func(c *callCtxt) {
+				data := c.bytes(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						a := sha256.Sum256(data)
+						return a[:]
+					}()
+				}
+			},
+		}, {
+			Name:   "Sum224",
+			Params: []kind{bytesKind | stringKind},
+			Result: bytesKind | stringKind,
+			Func: func(c *callCtxt) {
+				data := c.bytes(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						a := sha256.Sum224(data)
+						return a[:]
+					}()
+				}
+			},
+		}},
+	},
+	"crypto/sha512": {
+		native: []*builtin{{
+			Name:  "Size",
+			Const: "64",
+		}, {
+			Name:  "Size224",
+			Const: "28",
+		}, {
+			Name:  "Size256",
+			Const: "32",
+		}, {
+			Name:  "Size384",
+			Const: "48",
+		}, {
+			Name:  "BlockSize",
+			Const: "128",
+		}, {
+			Name:   "Sum512",
+			Params: []kind{bytesKind | stringKind},
+			Result: bytesKind | stringKind,
+			Func: func(c *callCtxt) {
+				data := c.bytes(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						a := sha512.Sum512(data)
+						return a[:]
+					}()
+				}
+			},
+		}, {
+			Name:   "Sum384",
+			Params: []kind{bytesKind | stringKind},
+			Result: bytesKind | stringKind,
+			Func: func(c *callCtxt) {
+				data := c.bytes(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						a := sha512.Sum384(data)
+						return a[:]
+					}()
+				}
+			},
+		}, {
+			Name:   "Sum512_224",
+			Params: []kind{bytesKind | stringKind},
+			Result: bytesKind | stringKind,
+			Func: func(c *callCtxt) {
+				data := c.bytes(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						a := sha512.Sum512_224(data)
+						return a[:]
+					}()
+				}
+			},
+		}, {
+			Name:   "Sum512_256",
+			Params: []kind{bytesKind | stringKind},
+			Result: bytesKind | stringKind,
+			Func: func(c *callCtxt) {
+				data := c.bytes(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						a := sha512.Sum512_256(data)
+						return a[:]
+					}()
+				}
+			},
+		}},
+	},
+	"encoding/base64": {
+		native: []*builtin{{
+			Name:   "EncodedLen",
+			Params: []kind{topKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				encoding, n := c.value(0), c.int(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if err := encoding.Null(); err != nil {
+							return 0, fmt.Errorf("base64: unsupported encoding: %v", err)
+						}
+						return base64.StdEncoding.EncodedLen(n), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "DecodedLen",
+			Params: []kind{topKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				encoding, x := c.value(0), c.int(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if err := encoding.Null(); err != nil {
+							return 0, fmt.Errorf("base64: unsupported encoding: %v", err)
+						}
+						return base64.StdEncoding.DecodedLen(x), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Encode",
+			Params: []kind{topKind, bytesKind | stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				encoding, src := c.value(0), c.bytes(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if err := encoding.Null(); err != nil {
+							return "", fmt.Errorf("base64: unsupported encoding: %v", err)
+						}
+						return base64.StdEncoding.EncodeToString(src), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Decode",
+			Params: []kind{topKind, stringKind},
+			Result: bytesKind | stringKind,
+			Func: func(c *callCtxt) {
+				encoding, s := c.value(0), c.string(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if err := encoding.Null(); err != nil {
+							return nil, fmt.Errorf("base64: unsupported encoding: %v", err)
+						}
+						return base64.StdEncoding.DecodeString(s)
+					}()
+				}
+			},
+		}},
+	},
+	"encoding/csv": {
+		native: []*builtin{{
+			Name:   "Encode",
+			Params: []kind{topKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				x := c.value(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						buf := &bytes.Buffer{}
+						w := csv.NewWriter(buf)
+						iter, err := x.List()
+						if err != nil {
+							return "", err
+						}
+						for iter.Next() {
+							row, err := iter.Value().List()
+							if err != nil {
+								return "", err
+							}
+							a := []string{}
+							for row.Next() {
+								col := row.Value()
+								if str, err := col.String(); err == nil {
+									a = append(a, str)
+								} else {
+									b, err := col.MarshalJSON()
+									if err != nil {
+										return "", err
+									}
+									a = append(a, string(b))
+								}
+							}
+							_ = w.Write(a)
+						}
+						w.Flush()
+						return buf.String(), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Decode",
+			Params: []kind{bytesKind | stringKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				r := c.reader(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return csv.NewReader(r).ReadAll()
+					}()
+				}
+			},
+		}},
+	},
+	"encoding/hex": {
+		native: []*builtin{{
+			Name:   "EncodedLen",
+			Params: []kind{intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				n := c.int(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return hex.EncodedLen(n)
+					}()
+				}
+			},
+		}, {
+			Name:   "DecodedLen",
+			Params: []kind{intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x := c.int(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return hex.DecodedLen(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Decode",
+			Params: []kind{stringKind},
+			Result: bytesKind | stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return hex.DecodeString(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "Dump",
+			Params: []kind{bytesKind | stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				data := c.bytes(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return hex.Dump(data)
+					}()
+				}
+			},
+		}, {
+			Name:   "Encode",
+			Params: []kind{bytesKind | stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				src := c.bytes(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return hex.EncodeToString(src)
+					}()
+				}
+			},
+		}},
+	},
+	"encoding/json": {
+		native: []*builtin{{
+			Name:   "Valid",
+			Params: []kind{bytesKind | stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				data := c.bytes(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return json.Valid(data)
+					}()
+				}
+			},
+		}, {
+			Name:   "Compact",
+			Params: []kind{bytesKind | stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				src := c.bytes(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						dst := bytes.Buffer{}
+						if err := json.Compact(&dst, src); err != nil {
+							return "", err
+						}
+						return dst.String(), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Indent",
+			Params: []kind{bytesKind | stringKind, stringKind, stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				src, prefix, indent := c.bytes(0), c.string(1), c.string(2)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						dst := bytes.Buffer{}
+						if err := json.Indent(&dst, src, prefix, indent); err != nil {
+							return "", err
+						}
+						return dst.String(), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "HTMLEscape",
+			Params: []kind{bytesKind | stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				src := c.bytes(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						dst := &bytes.Buffer{}
+						json.HTMLEscape(dst, src)
+						return dst.String()
+					}()
+				}
+			},
+		}, {
+			Name:   "Marshal",
+			Params: []kind{topKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				v := c.value(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						b, err := json.Marshal(v)
+						return string(b), err
+					}()
+				}
+			},
+		}, {
+			Name:   "MarshalStream",
+			Params: []kind{topKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				v := c.value(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+
+						iter, err := v.List()
+						if err != nil {
+							return "", err
+						}
+						buf := &bytes.Buffer{}
+						for iter.Next() {
+							b, err := json.Marshal(iter.Value())
+							if err != nil {
+								return "", err
+							}
+							buf.Write(b)
+							buf.WriteByte('\n')
+						}
+						return buf.String(), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Unmarshal",
+			Params: []kind{bytesKind | stringKind},
+			Result: topKind,
+			Func: func(c *callCtxt) {
+				b := c.bytes(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if !json.Valid(b) {
+							return nil, fmt.Errorf("json: invalid JSON")
+						}
+						expr, err := parser.ParseExpr("json", b)
+						if err != nil {
+
+							return nil, fmt.Errorf("json: could not parse JSON: %v", err)
+						}
+						return expr, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Validate",
+			Params: []kind{bytesKind | stringKind, topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				b, v := c.bytes(0), c.value(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if !json.Valid(b) {
+							return false, fmt.Errorf("json: invalid JSON")
+						}
+						r := internal.GetRuntime(v).(*Runtime)
+						inst, err := r.Compile("json.Validate", b)
+						if err != nil {
+							return false, err
+						}
+
+						v = v.Unify(inst.Value())
+						if v.Err() != nil {
+							return false, v.Err()
+						}
+						return true, nil
+					}()
+				}
+			},
+		}},
+	},
+	"encoding/yaml": {
+		native: []*builtin{{
+			Name:   "Marshal",
+			Params: []kind{topKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				v := c.value(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if err := v.Validate(Concrete(true)); err != nil {
+							if err := v.Validate(); err != nil {
+								return "", err
+							}
+							return "", internal.ErrIncomplete
+						}
+						n := v.Syntax(Final(), Concrete(true))
+						b, err := cueyaml.Encode(n)
+						return string(b), err
+					}()
+				}
+			},
+		}, {
+			Name:   "MarshalStream",
+			Params: []kind{topKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				v := c.value(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+
+						iter, err := v.List()
+						if err != nil {
+							return "", err
+						}
+						buf := &bytes.Buffer{}
+						for i := 0; iter.Next(); i++ {
+							if i > 0 {
+								buf.WriteString("---\n")
+							}
+							v := iter.Value()
+							if err := v.Validate(Concrete(true)); err != nil {
+								if err := v.Validate(); err != nil {
+									return "", err
+								}
+								return "", internal.ErrIncomplete
+							}
+							n := v.Syntax(Final(), Concrete(true))
+							b, err := cueyaml.Encode(n)
+							if err != nil {
+								return "", err
+							}
+							buf.Write(b)
+						}
+						return buf.String(), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Unmarshal",
+			Params: []kind{bytesKind | stringKind},
+			Result: topKind,
+			Func: func(c *callCtxt) {
+				data := c.bytes(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return yaml.Unmarshal("", data)
+					}()
+				}
+			},
+		}, {
+			Name:   "Validate",
+			Params: []kind{bytesKind | stringKind, topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				b, v := c.bytes(0), c.value(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						d, err := yaml.NewDecoder("yaml.Validate", b)
+						if err != nil {
+							return false, err
+						}
+						r := internal.GetRuntime(v).(*Runtime)
+						for {
+							expr, err := d.Decode()
+							if err != nil {
+								if err == io.EOF {
+									return true, nil
+								}
+								return false, err
+							}
+
+							inst, err := r.CompileExpr(expr)
+							if err != nil {
+								return false, err
+							}
+
+							x := v.Unify(inst.Value())
+							if err := x.Err(); err != nil {
+								return false, err
+							}
+							if err := x.Validate(Concrete(true)); err != nil {
+								return false, err
+							}
+
+						}
+					}()
+				}
+			},
+		}, {
+			Name:   "ValidatePartial",
+			Params: []kind{bytesKind | stringKind, topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				b, v := c.bytes(0), c.value(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						d, err := yaml.NewDecoder("yaml.ValidatePartial", b)
+						if err != nil {
+							return false, err
+						}
+						r := internal.GetRuntime(v).(*Runtime)
+						for {
+							expr, err := d.Decode()
+							if err != nil {
+								if err == io.EOF {
+									return true, nil
+								}
+								return false, err
+							}
+
+							inst, err := r.CompileExpr(expr)
+							if err != nil {
+								return false, err
+							}
+
+							if x := v.Unify(inst.Value()); x.Err() != nil {
+								return false, x.Err()
+							}
+						}
+					}()
+				}
+			},
+		}},
+	},
+	"html": {
+		native: []*builtin{{
+			Name:   "Escape",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return html.EscapeString(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "Unescape",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return html.UnescapeString(s)
+					}()
+				}
+			},
+		}},
+	},
+	"list": {
+		native: []*builtin{{
+			Name:   "Drop",
+			Params: []kind{listKind, intKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				x, n := c.list(0), c.int(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if n < 0 {
+							return nil, fmt.Errorf("negative index")
+						}
+
+						if n > len(x) {
+							return []Value{}, nil
+						}
+
+						return x[n:], nil
+					}()
+				}
+			},
+		}, {
+			Name:   "FlattenN",
+			Params: []kind{topKind, intKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				xs, depth := c.value(0), c.int(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var flattenN func(Value, int) ([]Value, error)
+						flattenN = func(xs Value, depth int) ([]Value, error) {
+							var res []Value
+							iter, err := xs.List()
+							if err != nil {
+								return nil, err
+							}
+							for iter.Next() {
+								val, _ := iter.Value().Default()
+								if val.Kind() == ListKind && depth != 0 {
+									d := depth - 1
+									values, err := flattenN(val, d)
+									if err != nil {
+										return nil, err
+									}
+									res = append(res, values...)
+								} else {
+									res = append(res, val)
+								}
+							}
+							return res, nil
+						}
+						return flattenN(xs, depth)
+					}()
+				}
+			},
+		}, {
+			Name:   "Take",
+			Params: []kind{listKind, intKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				x, n := c.list(0), c.int(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if n < 0 {
+							return nil, fmt.Errorf("negative index")
+						}
+
+						if n > len(x) {
+							return x, nil
+						}
+
+						return x[:n], nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Slice",
+			Params: []kind{listKind, intKind, intKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				x, i, j := c.list(0), c.int(1), c.int(2)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if i < 0 {
+							return nil, fmt.Errorf("negative index")
+						}
+
+						if i > j {
+							return nil, fmt.Errorf("invalid index: %v > %v", i, j)
+						}
+
+						if i > len(x) {
+							return nil, fmt.Errorf("slice bounds out of range")
+						}
+
+						if j > len(x) {
+							return nil, fmt.Errorf("slice bounds out of range")
+						}
+
+						return x[i:j], nil
+					}()
+				}
+			},
+		}, {
+			Name:   "MinItems",
+			Params: []kind{listKind, intKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				a, n := c.list(0), c.int(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return len(a) >= n
+					}()
+				}
+			},
+		}, {
+			Name:   "MaxItems",
+			Params: []kind{listKind, intKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				a, n := c.list(0), c.int(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return len(a) <= n
+					}()
+				}
+			},
+		}, {
+			Name:   "UniqueItems",
+			Params: []kind{listKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				a := c.list(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						b := []string{}
+						for _, v := range a {
+							b = append(b, fmt.Sprint(v))
+						}
+						sort.Strings(b)
+						for i := 1; i < len(b); i++ {
+							if b[i-1] == b[i] {
+								return false
+							}
+						}
+						return true
+					}()
+				}
+			},
+		}, {
+			Name:   "Contains",
+			Params: []kind{listKind, topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				a, v := c.list(0), c.value(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						for _, w := range a {
+							if v.Equals(w) {
+								return true
+							}
+						}
+						return false
+					}()
+				}
+			},
+		}, {
+			Name:   "Avg",
+			Params: []kind{listKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				xs := c.decimalList(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if 0 == len(xs) {
+							return nil, fmt.Errorf("empty list")
+						}
+
+						s := apd.New(0, 0)
+						for _, x := range xs {
+							_, err := internal.BaseContext.Add(s, x, s)
+							if err != nil {
+								return nil, err
+							}
+						}
+
+						var d apd.Decimal
+						l := apd.New(int64(len(xs)), 0)
+						_, err := internal.BaseContext.Quo(&d, s, l)
+						if err != nil {
+							return nil, err
+						}
+						return &d, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Max",
+			Params: []kind{listKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				xs := c.decimalList(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if 0 == len(xs) {
+							return nil, fmt.Errorf("empty list")
+						}
+
+						max := xs[0]
+						for _, x := range xs[1:] {
+							if -1 == max.Cmp(x) {
+								max = x
+							}
+						}
+						return max, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Min",
+			Params: []kind{listKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				xs := c.decimalList(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if 0 == len(xs) {
+							return nil, fmt.Errorf("empty list")
+						}
+
+						min := xs[0]
+						for _, x := range xs[1:] {
+							if +1 == min.Cmp(x) {
+								min = x
+							}
+						}
+						return min, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Product",
+			Params: []kind{listKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				xs := c.decimalList(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						d := apd.New(1, 0)
+						for _, x := range xs {
+							_, err := internal.BaseContext.Mul(d, x, d)
+							if err != nil {
+								return nil, err
+							}
+						}
+						return d, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Range",
+			Params: []kind{numKind, numKind, numKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				start, limit, step := c.decimal(0), c.decimal(1), c.decimal(2)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if step.IsZero() {
+							return nil, fmt.Errorf("step must be non zero")
+						}
+
+						if !step.Negative && +1 == start.Cmp(limit) {
+							return nil, fmt.Errorf("end must be greater than start when step is positive")
+						}
+
+						if step.Negative && -1 == start.Cmp(limit) {
+							return nil, fmt.Errorf("end must be less than start when step is negative")
+						}
+
+						var vals []*internal.Decimal
+						num := start
+						for {
+							if !step.Negative && -1 != num.Cmp(limit) {
+								break
+							}
+
+							if step.Negative && +1 != num.Cmp(limit) {
+								break
+							}
+
+							vals = append(vals, num)
+							d := apd.New(0, 0)
+							_, err := internal.BaseContext.Add(d, step, num)
+							if err != nil {
+								return nil, err
+							}
+							num = d
+						}
+						return vals, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Sum",
+			Params: []kind{listKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				xs := c.decimalList(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						d := apd.New(0, 0)
+						for _, x := range xs {
+							_, err := internal.BaseContext.Add(d, x, d)
+							if err != nil {
+								return nil, err
+							}
+						}
+						return d, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Sort",
+			Params: []kind{listKind, topKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				list, cmp := c.list(0), c.value(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						s := valueSorter{list, cmp, nil}
+
+						sort.Sort(&s)
+						return s.ret()
+					}()
+				}
+			},
+		}, {
+			Name:   "SortStable",
+			Params: []kind{listKind, topKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				list, cmp := c.list(0), c.value(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						s := valueSorter{list, cmp, nil}
+						sort.Stable(&s)
+						return s.ret()
+					}()
+				}
+			},
+		}, {
+			Name:   "SortStrings",
+			Params: []kind{listKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				a := c.strList(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						sort.Strings(a)
+						return a
+					}()
+				}
+			},
+		}, {
+			Name:   "IsSorted",
+			Params: []kind{listKind, topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				list, cmp := c.list(0), c.value(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						s := valueSorter{list, cmp, nil}
+						return sort.IsSorted(&s)
+					}()
+				}
+			},
+		}, {
+			Name:   "IsSortedStrings",
+			Params: []kind{listKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				a := c.strList(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return sort.StringsAreSorted(a)
+					}()
+				}
+			},
+		}},
+		cue: `{
+	Comparer: {
+		T:    _
+		less: bool
+		x:    T
+		y:    T
+	}
+	Ascending: {
+		T:    number | string
+		less: true && x < y
+		x:    T
+		y:    T
+		Comparer
+	}
+	Descending: {
+		T:    number | string
+		less: x > y
+		x:    T
+		y:    T
+		Comparer
+	}
+}`,
+	},
+	"math": {
+		native: []*builtin{{
+			Name:  "MaxExp",
+			Const: "2147483647",
+		}, {
+			Name:  "MinExp",
+			Const: "-2147483648",
+		}, {
+			Name:  "MaxPrec",
+			Const: "4294967295",
+		}, {
+			Name:  "ToNearestEven",
+			Const: "0",
+		}, {
+			Name:  "ToNearestAway",
+			Const: "1",
+		}, {
+			Name:  "ToZero",
+			Const: "2",
+		}, {
+			Name:  "AwayFromZero",
+			Const: "3",
+		}, {
+			Name:  "ToNegativeInf",
+			Const: "4",
+		}, {
+			Name:  "ToPositiveInf",
+			Const: "5",
+		}, {
+			Name:  "Below",
+			Const: "-1",
+		}, {
+			Name:  "Exact",
+			Const: "0",
+		}, {
+			Name:  "Above",
+			Const: "1",
+		}, {
+			Name:   "Jacobi",
+			Params: []kind{intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x, y := c.bigInt(0), c.bigInt(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return big.Jacobi(x, y)
+					}()
+				}
+			},
+		}, {
+			Name:  "MaxBase",
+			Const: "62",
+		}, {
+			Name:   "Floor",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := apdContext.Floor(&d, x)
+						return &d, err
+					}()
+				}
+			},
+		}, {
+			Name:   "Ceil",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := apdContext.Ceil(&d, x)
+						return &d, err
+					}()
+				}
+			},
+		}, {
+			Name:   "Trunc",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := roundTruncContext.RoundToIntegralExact(&d, x)
+						return &d, err
+					}()
+				}
+			},
+		}, {
+			Name:   "Round",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := roundUpContext.RoundToIntegralExact(&d, x)
+						return &d, err
+					}()
+				}
+			},
+		}, {
+			Name:   "RoundToEven",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := roundEvenContext.RoundToIntegralExact(&d, x)
+						return &d, err
+					}()
+				}
+			},
+		}, {
+			Name:   "MultipleOf",
+			Params: []kind{numKind, numKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				x, y := c.decimal(0), c.decimal(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d apd.Decimal
+						cond, err := mulContext.Quo(&d, x, y)
+						return !cond.Inexact(), err
+					}()
+				}
+			},
+		}, {
+			Name:   "Abs",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := apdContext.Abs(&d, x)
+						return &d, err
+					}()
+				}
+			},
+		}, {
+			Name:   "Acosh",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Acosh(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Asin",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Asin(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Acos",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Acos(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Asinh",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Asinh(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Atan",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Atan(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Atan2",
+			Params: []kind{numKind, numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				y, x := c.float64(0), c.float64(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Atan2(y, x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Atanh",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Atanh(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Cbrt",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := apdContext.Cbrt(&d, x)
+						return &d, err
+					}()
+				}
+			},
+		}, {
+			Name:  "E",
+			Const: "2.71828182845904523536028747135266249775724709369995957496696763",
+		}, {
+			Name:  "Pi",
+			Const: "3.14159265358979323846264338327950288419716939937510582097494459",
+		}, {
+			Name:  "Phi",
+			Const: "1.61803398874989484820458683436563811772030917980576286213544861",
+		}, {
+			Name:  "Sqrt2",
+			Const: "1.41421356237309504880168872420969807856967187537694807317667974",
+		}, {
+			Name:  "SqrtE",
+			Const: "1.64872127070012814684865078781416357165377610071014801157507931",
+		}, {
+			Name:  "SqrtPi",
+			Const: "1.77245385090551602729816748334114518279754945612238712821380779",
+		}, {
+			Name:  "SqrtPhi",
+			Const: "1.27201964951406896425242246173749149171560804184009624861664038",
+		}, {
+			Name:  "Ln2",
+			Const: "0.693147180559945309417232121458176568075500134360255254120680009",
+		}, {
+			Name:  "Log2E",
+			Const: "1.442695040888963407359924681001892137426645954152985934135449408",
+		}, {
+			Name:  "Ln10",
+			Const: "2.3025850929940456840179914546843642076011014886287729760333278",
+		}, {
+			Name:  "Log10E",
+			Const: "0.43429448190325182765112891891660508229439700580366656611445378",
+		}, {
+			Name:   "Copysign",
+			Params: []kind{numKind, numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x, y := c.decimal(0), c.decimal(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						var d internal.Decimal
+						d.Set(x)
+						d.Negative = y.Negative
+						return &d
+					}()
+				}
+			},
+		}, {
+			Name:   "Dim",
+			Params: []kind{numKind, numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x, y := c.decimal(0), c.decimal(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := apdContext.Sub(&d, x, y)
+						if err != nil {
+							return nil, err
+						}
+						if d.Negative {
+							return zero, nil
+						}
+						return &d, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Erf",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Erf(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Erfc",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Erfc(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Erfinv",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Erfinv(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Erfcinv",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Erfcinv(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Exp",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := apdContext.Exp(&d, x)
+						return &d, err
+					}()
+				}
+			},
+		}, {
+			Name:   "Exp2",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := apdContext.Pow(&d, two, x)
+						return &d, err
+					}()
+				}
+			},
+		}, {
+			Name:   "Expm1",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Expm1(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Gamma",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Gamma(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Hypot",
+			Params: []kind{numKind, numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				p, q := c.float64(0), c.float64(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Hypot(p, q)
+					}()
+				}
+			},
+		}, {
+			Name:   "J0",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.J0(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Y0",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Y0(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "J1",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.J1(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Y1",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Y1(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Jn",
+			Params: []kind{intKind, numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				n, x := c.int(0), c.float64(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Jn(n, x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Yn",
+			Params: []kind{intKind, numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				n, x := c.int(0), c.float64(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Yn(n, x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Ldexp",
+			Params: []kind{numKind, intKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				frac, exp := c.float64(0), c.int(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Ldexp(frac, exp)
+					}()
+				}
+			},
+		}, {
+			Name:   "Log",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := apdContext.Ln(&d, x)
+						return &d, err
+					}()
+				}
+			},
+		}, {
+			Name:   "Log10",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := apdContext.Log10(&d, x)
+						return &d, err
+					}()
+				}
+			},
+		}, {
+			Name:   "Log2",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d, ln2 internal.Decimal
+						_, _ = apdContext.Ln(&ln2, two)
+						_, err := apdContext.Ln(&d, x)
+						if err != nil {
+							return &d, err
+						}
+						_, err = apdContext.Quo(&d, &d, &ln2)
+						return &d, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Log1p",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Log1p(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Logb",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Logb(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Ilogb",
+			Params: []kind{numKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Ilogb(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Mod",
+			Params: []kind{numKind, numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x, y := c.float64(0), c.float64(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Mod(x, y)
+					}()
+				}
+			},
+		}, {
+			Name:   "Pow",
+			Params: []kind{numKind, numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x, y := c.decimal(0), c.decimal(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var d internal.Decimal
+						_, err := apdContext.Pow(&d, x, y)
+						return &d, err
+					}()
+				}
+			},
+		}, {
+			Name:   "Pow10",
+			Params: []kind{intKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				n := c.int32(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return apd.New(1, n)
+					}()
+				}
+			},
+		}, {
+			Name:   "Remainder",
+			Params: []kind{numKind, numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x, y := c.float64(0), c.float64(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Remainder(x, y)
+					}()
+				}
+			},
+		}, {
+			Name:   "Signbit",
+			Params: []kind{numKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				x := c.decimal(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return x.Negative
+					}()
+				}
+			},
+		}, {
+			Name:   "Cos",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Cos(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Sin",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Sin(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Sinh",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Sinh(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Cosh",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Cosh(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Sqrt",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Sqrt(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Tan",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Tan(x)
+					}()
+				}
+			},
+		}, {
+			Name:   "Tanh",
+			Params: []kind{numKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				x := c.float64(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return math.Tanh(x)
+					}()
+				}
+			},
+		}},
+	},
+	"math/bits": {
+		native: []*builtin{{
+			Name:   "Lsh",
+			Params: []kind{intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x, n := c.bigInt(0), c.uint(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						var z big.Int
+						z.Lsh(x, n)
+						return &z
+					}()
+				}
+			},
+		}, {
+			Name:   "Rsh",
+			Params: []kind{intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x, n := c.bigInt(0), c.uint(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						var z big.Int
+						z.Rsh(x, n)
+						return &z
+					}()
+				}
+			},
+		}, {
+			Name:   "At",
+			Params: []kind{intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x, i := c.bigInt(0), c.uint(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if i > math.MaxInt32 {
+							return 0, fmt.Errorf("bit index too large")
+						}
+						return x.Bit(int(i)), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Set",
+			Params: []kind{intKind, intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x, i, bit := c.bigInt(0), c.int(1), c.uint(2)
+				if c.do() {
+					c.ret = func() interface{} {
+						var z big.Int
+						z.SetBit(x, i, bit)
+						return &z
+					}()
+				}
+			},
+		}, {
+			Name:   "And",
+			Params: []kind{intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				a, b := c.bigInt(0), c.bigInt(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						var z big.Int
+						z.And(a, b)
+						return &z
+					}()
+				}
+			},
+		}, {
+			Name:   "Or",
+			Params: []kind{intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				a, b := c.bigInt(0), c.bigInt(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						var z big.Int
+						z.Or(a, b)
+						return &z
+					}()
+				}
+			},
+		}, {
+			Name:   "Xor",
+			Params: []kind{intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				a, b := c.bigInt(0), c.bigInt(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						var z big.Int
+						z.Xor(a, b)
+						return &z
+					}()
+				}
+			},
+		}, {
+			Name:   "Clear",
+			Params: []kind{intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				a, b := c.bigInt(0), c.bigInt(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						var z big.Int
+						z.AndNot(a, b)
+						return &z
+					}()
+				}
+			},
+		}, {
+			Name:   "OnesCount",
+			Params: []kind{intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x := c.bigInt(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						var count int
+						for _, w := range x.Bits() {
+							count += bits.OnesCount64(uint64(w))
+						}
+						return count
+					}()
+				}
+			},
+		}, {
+			Name:   "Len",
+			Params: []kind{intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				x := c.bigInt(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return x.BitLen()
+					}()
+				}
+			},
+		}},
+	},
+	"net": {
+		native: []*builtin{{
+			Name:   "SplitHostPort",
+			Params: []kind{stringKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						host, port, err := net.SplitHostPort(s)
+						if err != nil {
+							return nil, err
+						}
+						return []string{host, port}, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "JoinHostPort",
+			Params: []kind{topKind, topKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				host, port := c.value(0), c.value(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						var err error
+						hostStr := ""
+						switch host.Kind() {
+						case ListKind:
+							ipdata := netGetIP(host)
+							if len(ipdata) != 4 && len(ipdata) != 16 {
+								err = fmt.Errorf("invalid host %q", host)
+							}
+							hostStr = ipdata.String()
+						case BytesKind:
+							var b []byte
+							b, err = host.Bytes()
+							hostStr = string(b)
+						default:
+							hostStr, err = host.String()
+						}
+						if err != nil {
+							return "", err
+						}
+
+						portStr := ""
+						switch port.Kind() {
+						case StringKind:
+							portStr, err = port.String()
+						case BytesKind:
+							var b []byte
+							b, err = port.Bytes()
+							portStr = string(b)
+						default:
+							var i int64
+							i, err = port.Int64()
+							portStr = strconv.Itoa(int(i))
+						}
+						if err != nil {
+							return "", err
+						}
+
+						return net.JoinHostPort(hostStr, portStr), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "FQDN",
+			Params: []kind{stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						for i := 0; i < len(s); i++ {
+							if s[i] >= utf8.RuneSelf {
+								return false
+							}
+						}
+						_, err := idnaProfile.ToASCII(s)
+						return err == nil
+					}()
+				}
+			},
+		}, {
+			Name:  "IPv4len",
+			Const: "4",
+		}, {
+			Name:  "IPv6len",
+			Const: "16",
+		}, {
+			Name:   "ParseIP",
+			Params: []kind{stringKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						goip := net.ParseIP(s)
+						if goip == nil {
+							return nil, fmt.Errorf("invalid IP address %q", s)
+						}
+						return netToList(goip), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "IPv4",
+			Params: []kind{topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				ip := c.value(0)
+				if c.do() {
+					c.ret = func() interface{} {
+
+						return netGetIP(ip).To4() != nil
+					}()
+				}
+			},
+		}, {
+			Name:   "IP",
+			Params: []kind{topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				ip := c.value(0)
+				if c.do() {
+					c.ret = func() interface{} {
+
+						return netGetIP(ip) != nil
+					}()
+				}
+			},
+		}, {
+			Name:   "LoopbackIP",
+			Params: []kind{topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				ip := c.value(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return netGetIP(ip).IsLoopback()
+					}()
+				}
+			},
+		}, {
+			Name:   "MulticastIP",
+			Params: []kind{topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				ip := c.value(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return netGetIP(ip).IsMulticast()
+					}()
+				}
+			},
+		}, {
+			Name:   "InterfaceLocalMulticastIP",
+			Params: []kind{topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				ip := c.value(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return netGetIP(ip).IsInterfaceLocalMulticast()
+					}()
+				}
+			},
+		}, {
+			Name:   "LinkLocalMulticastIP",
+			Params: []kind{topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				ip := c.value(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return netGetIP(ip).IsLinkLocalMulticast()
+					}()
+				}
+			},
+		}, {
+			Name:   "LinkLocalUnicastIP",
+			Params: []kind{topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				ip := c.value(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return netGetIP(ip).IsLinkLocalUnicast()
+					}()
+				}
+			},
+		}, {
+			Name:   "GlobalUnicastIP",
+			Params: []kind{topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				ip := c.value(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return netGetIP(ip).IsGlobalUnicast()
+					}()
+				}
+			},
+		}, {
+			Name:   "UnspecifiedIP",
+			Params: []kind{topKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				ip := c.value(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return netGetIP(ip).IsUnspecified()
+					}()
+				}
+			},
+		}, {
+			Name:   "ToIP4",
+			Params: []kind{topKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				ip := c.value(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						ipdata := netGetIP(ip)
+						if ipdata == nil {
+							return nil, fmt.Errorf("invalid IP %q", ip)
+						}
+						ipv4 := ipdata.To4()
+						if ipv4 == nil {
+							return nil, fmt.Errorf("cannot convert %q to IPv4", ipdata)
+						}
+						return netToList(ipv4), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "ToIP16",
+			Params: []kind{topKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				ip := c.value(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						ipdata := netGetIP(ip)
+						if ipdata == nil {
+							return nil, fmt.Errorf("invalid IP %q", ip)
+						}
+						return netToList(ipdata), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "IPString",
+			Params: []kind{topKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				ip := c.value(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						ipdata := netGetIP(ip)
+						if ipdata == nil {
+							return "", fmt.Errorf("invalid IP %q", ip)
+						}
+						return ipdata.String(), nil
+					}()
+				}
+			},
+		}},
+	},
+	"path": {
+		native: []*builtin{{
+			Name:   "Split",
+			Params: []kind{stringKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				path := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						file, dir := split(path)
+						return []string{file, dir}
+					}()
+				}
+			},
+		}, {
+			Name:   "Match",
+			Params: []kind{stringKind, stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				pattern, name := c.string(0), c.string(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return path.Match(pattern, name)
+					}()
+				}
+			},
+		}, {
+			Name:   "Clean",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				path := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return pathClean(path)
+					}()
+				}
+			},
+		}, {
+			Name:   "Ext",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				path := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return pathExt(path)
+					}()
+				}
+			},
+		}, {
+			Name:   "Base",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				path := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return pathBase(path)
+					}()
+				}
+			},
+		}, {
+			Name:   "IsAbs",
+			Params: []kind{stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				path := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return pathIsAbs(path)
+					}()
+				}
+			},
+		}, {
+			Name:   "Dir",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				path := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return pathDir(path)
+					}()
+				}
+			},
+		}},
+	},
+	"regexp": {
+		native: []*builtin{{
+			Name:   "Valid",
+			Params: []kind{stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				pattern := c.string(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						_, err := regexp.Compile(pattern)
+						return err == nil, err
+					}()
+				}
+			},
+		}, {
+			Name:   "Find",
+			Params: []kind{stringKind, stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				pattern, s := c.string(0), c.string(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						re, err := regexp.Compile(pattern)
+						if err != nil {
+							return "", err
+						}
+						m := re.FindStringIndex(s)
+						if m == nil {
+							return "", errNoMatch
+						}
+						return s[m[0]:m[1]], nil
+					}()
+				}
+			},
+		}, {
+			Name:   "FindAll",
+			Params: []kind{stringKind, stringKind, intKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				pattern, s, n := c.string(0), c.string(1), c.int(2)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						re, err := regexp.Compile(pattern)
+						if err != nil {
+							return nil, err
+						}
+						m := re.FindAllString(s, n)
+						if m == nil {
+							return nil, errNoMatch
+						}
+						return m, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "FindSubmatch",
+			Params: []kind{stringKind, stringKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				pattern, s := c.string(0), c.string(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						re, err := regexp.Compile(pattern)
+						if err != nil {
+							return nil, err
+						}
+						m := re.FindStringSubmatch(s)
+						if m == nil {
+							return nil, errNoMatch
+						}
+						return m, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "FindAllSubmatch",
+			Params: []kind{stringKind, stringKind, intKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				pattern, s, n := c.string(0), c.string(1), c.int(2)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						re, err := regexp.Compile(pattern)
+						if err != nil {
+							return nil, err
+						}
+						m := re.FindAllStringSubmatch(s, n)
+						if m == nil {
+							return nil, errNoMatch
+						}
+						return m, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "FindNamedSubmatch",
+			Params: []kind{stringKind, stringKind},
+			Result: structKind,
+			Func: func(c *callCtxt) {
+				pattern, s := c.string(0), c.string(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						re, err := regexp.Compile(pattern)
+						if err != nil {
+							return nil, err
+						}
+						names := re.SubexpNames()
+						if len(names) == 0 {
+							return nil, errNoNamedGroup
+						}
+						m := re.FindStringSubmatch(s)
+						if m == nil {
+							return nil, errNoMatch
+						}
+						r := make(map[string]string, len(names)-1)
+						for k, name := range names {
+							if name != "" {
+								r[name] = m[k]
+							}
+						}
+						return r, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "FindAllNamedSubmatch",
+			Params: []kind{stringKind, stringKind, intKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				pattern, s, n := c.string(0), c.string(1), c.int(2)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						re, err := regexp.Compile(pattern)
+						if err != nil {
+							return nil, err
+						}
+						names := re.SubexpNames()
+						if len(names) == 0 {
+							return nil, errNoNamedGroup
+						}
+						m := re.FindAllStringSubmatch(s, n)
+						if m == nil {
+							return nil, errNoMatch
+						}
+						result := make([]map[string]string, len(m))
+						for i, m := range m {
+							r := make(map[string]string, len(names)-1)
+							for k, name := range names {
+								if name != "" {
+									r[name] = m[k]
+								}
+							}
+							result[i] = r
+						}
+						return result, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Match",
+			Params: []kind{stringKind, stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				pattern, s := c.string(0), c.string(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return regexp.MatchString(pattern, s)
+					}()
+				}
+			},
+		}, {
+			Name:   "QuoteMeta",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return regexp.QuoteMeta(s)
+					}()
+				}
+			},
+		}},
+	},
+	"strconv": {
+		native: []*builtin{{
+			Name:   "Unquote",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return literal.Unquote(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "ParseBool",
+			Params: []kind{stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				str := c.string(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return strconv.ParseBool(str)
+					}()
+				}
+			},
+		}, {
+			Name:   "FormatBool",
+			Params: []kind{boolKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				b := c.bool(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strconv.FormatBool(b)
+					}()
+				}
+			},
+		}, {
+			Name:   "ParseFloat",
+			Params: []kind{stringKind, intKind},
+			Result: numKind,
+			Func: func(c *callCtxt) {
+				s, bitSize := c.string(0), c.int(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return strconv.ParseFloat(s, bitSize)
+					}()
+				}
+			},
+		}, {
+			Name:  "IntSize",
+			Const: "64",
+		}, {
+			Name:   "ParseUint",
+			Params: []kind{stringKind, intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				s, base, bitSize := c.string(0), c.int(1), c.int(2)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return strconv.ParseUint(s, base, bitSize)
+					}()
+				}
+			},
+		}, {
+			Name:   "ParseInt",
+			Params: []kind{stringKind, intKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				s, base, bitSize := c.string(0), c.int(1), c.int(2)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return strconv.ParseInt(s, base, bitSize)
+					}()
+				}
+			},
+		}, {
+			Name:   "Atoi",
+			Params: []kind{stringKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return strconv.Atoi(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "FormatFloat",
+			Params: []kind{numKind, intKind, intKind, intKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				f, fmt, prec, bitSize := c.float64(0), c.byte(1), c.int(2), c.int(3)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strconv.FormatFloat(f, fmt, prec, bitSize)
+					}()
+				}
+			},
+		}, {
+			Name:   "FormatUint",
+			Params: []kind{intKind, intKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				i, base := c.uint64(0), c.int(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strconv.FormatUint(i, base)
+					}()
+				}
+			},
+		}, {
+			Name:   "FormatInt",
+			Params: []kind{intKind, intKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				i, base := c.int64(0), c.int(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strconv.FormatInt(i, base)
+					}()
+				}
+			},
+		}, {
+			Name:   "Quote",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strconv.Quote(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "QuoteToASCII",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strconv.QuoteToASCII(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "QuoteToGraphic",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strconv.QuoteToGraphic(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "QuoteRune",
+			Params: []kind{intKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				r := c.rune(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strconv.QuoteRune(r)
+					}()
+				}
+			},
+		}, {
+			Name:   "QuoteRuneToASCII",
+			Params: []kind{intKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				r := c.rune(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strconv.QuoteRuneToASCII(r)
+					}()
+				}
+			},
+		}, {
+			Name:   "QuoteRuneToGraphic",
+			Params: []kind{intKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				r := c.rune(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strconv.QuoteRuneToGraphic(r)
+					}()
+				}
+			},
+		}, {
+			Name:   "IsPrint",
+			Params: []kind{intKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				r := c.rune(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strconv.IsPrint(r)
+					}()
+				}
+			},
+		}, {
+			Name:   "IsGraphic",
+			Params: []kind{intKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				r := c.rune(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strconv.IsGraphic(r)
+					}()
+				}
+			},
+		}},
+	},
+	"strings": {
+		native: []*builtin{{
+			Name:   "ByteAt",
+			Params: []kind{bytesKind | stringKind, intKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				b, i := c.bytes(0), c.int(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if i < 0 || i >= len(b) {
+							return 0, fmt.Errorf("index out of range")
+						}
+						return b[i], nil
+					}()
+				}
+			},
+		}, {
+			Name:   "ByteSlice",
+			Params: []kind{bytesKind | stringKind, intKind, intKind},
+			Result: bytesKind | stringKind,
+			Func: func(c *callCtxt) {
+				b, start, end := c.bytes(0), c.int(1), c.int(2)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if start < 0 || start > end || end > len(b) {
+							return nil, fmt.Errorf("index out of range")
+						}
+						return b[start:end], nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Runes",
+			Params: []kind{stringKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return []rune(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "MinRunes",
+			Params: []kind{stringKind, intKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				s, min := c.string(0), c.int(1)
+				if c.do() {
+					c.ret = func() interface{} {
+
+						return len([]rune(s)) >= min
+					}()
+				}
+			},
+		}, {
+			Name:   "MaxRunes",
+			Params: []kind{stringKind, intKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				s, max := c.string(0), c.int(1)
+				if c.do() {
+					c.ret = func() interface{} {
+
+						return len([]rune(s)) <= max
+					}()
+				}
+			},
+		}, {
+			Name:   "ToTitle",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+
+						prev := ' '
+						return strings.Map(
+							func(r rune) rune {
+								if unicode.IsSpace(prev) {
+									prev = r
+									return unicode.ToTitle(r)
+								}
+								prev = r
+								return r
+							},
+							s)
+					}()
+				}
+			},
+		}, {
+			Name:   "ToCamel",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+
+						prev := ' '
+						return strings.Map(
+							func(r rune) rune {
+								if unicode.IsSpace(prev) {
+									prev = r
+									return unicode.ToLower(r)
+								}
+								prev = r
+								return r
+							},
+							s)
+					}()
+				}
+			},
+		}, {
+			Name:   "SliceRunes",
+			Params: []kind{stringKind, intKind, intKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s, start, end := c.string(0), c.int(1), c.int(2)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						runes := []rune(s)
+						if start < 0 || start > end || end > len(runes) {
+							return "", fmt.Errorf("index out of range")
+						}
+						return string(runes[start:end]), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Compare",
+			Params: []kind{stringKind, stringKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				a, b := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.Compare(a, b)
+					}()
+				}
+			},
+		}, {
+			Name:   "Count",
+			Params: []kind{stringKind, stringKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				s, substr := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.Count(s, substr)
+					}()
+				}
+			},
+		}, {
+			Name:   "Contains",
+			Params: []kind{stringKind, stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				s, substr := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.Contains(s, substr)
+					}()
+				}
+			},
+		}, {
+			Name:   "ContainsAny",
+			Params: []kind{stringKind, stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				s, chars := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.ContainsAny(s, chars)
+					}()
+				}
+			},
+		}, {
+			Name:   "LastIndex",
+			Params: []kind{stringKind, stringKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				s, substr := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.LastIndex(s, substr)
+					}()
+				}
+			},
+		}, {
+			Name:   "IndexAny",
+			Params: []kind{stringKind, stringKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				s, chars := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.IndexAny(s, chars)
+					}()
+				}
+			},
+		}, {
+			Name:   "LastIndexAny",
+			Params: []kind{stringKind, stringKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				s, chars := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.LastIndexAny(s, chars)
+					}()
+				}
+			},
+		}, {
+			Name:   "SplitN",
+			Params: []kind{stringKind, stringKind, intKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				s, sep, n := c.string(0), c.string(1), c.int(2)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.SplitN(s, sep, n)
+					}()
+				}
+			},
+		}, {
+			Name:   "SplitAfterN",
+			Params: []kind{stringKind, stringKind, intKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				s, sep, n := c.string(0), c.string(1), c.int(2)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.SplitAfterN(s, sep, n)
+					}()
+				}
+			},
+		}, {
+			Name:   "Split",
+			Params: []kind{stringKind, stringKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				s, sep := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.Split(s, sep)
+					}()
+				}
+			},
+		}, {
+			Name:   "SplitAfter",
+			Params: []kind{stringKind, stringKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				s, sep := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.SplitAfter(s, sep)
+					}()
+				}
+			},
+		}, {
+			Name:   "Fields",
+			Params: []kind{stringKind},
+			Result: listKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.Fields(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "Join",
+			Params: []kind{listKind, stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				elems, sep := c.strList(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.Join(elems, sep)
+					}()
+				}
+			},
+		}, {
+			Name:   "HasPrefix",
+			Params: []kind{stringKind, stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				s, prefix := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.HasPrefix(s, prefix)
+					}()
+				}
+			},
+		}, {
+			Name:   "HasSuffix",
+			Params: []kind{stringKind, stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				s, suffix := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.HasSuffix(s, suffix)
+					}()
+				}
+			},
+		}, {
+			Name:   "Repeat",
+			Params: []kind{stringKind, intKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s, count := c.string(0), c.int(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.Repeat(s, count)
+					}()
+				}
+			},
+		}, {
+			Name:   "ToUpper",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.ToUpper(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "ToLower",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.ToLower(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "Trim",
+			Params: []kind{stringKind, stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s, cutset := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.Trim(s, cutset)
+					}()
+				}
+			},
+		}, {
+			Name:   "TrimLeft",
+			Params: []kind{stringKind, stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s, cutset := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.TrimLeft(s, cutset)
+					}()
+				}
+			},
+		}, {
+			Name:   "TrimRight",
+			Params: []kind{stringKind, stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s, cutset := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.TrimRight(s, cutset)
+					}()
+				}
+			},
+		}, {
+			Name:   "TrimSpace",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.TrimSpace(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "TrimPrefix",
+			Params: []kind{stringKind, stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s, prefix := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.TrimPrefix(s, prefix)
+					}()
+				}
+			},
+		}, {
+			Name:   "TrimSuffix",
+			Params: []kind{stringKind, stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s, suffix := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.TrimSuffix(s, suffix)
+					}()
+				}
+			},
+		}, {
+			Name:   "Replace",
+			Params: []kind{stringKind, stringKind, stringKind, intKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s, old, new, n := c.string(0), c.string(1), c.string(2), c.int(3)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.Replace(s, old, new, n)
+					}()
+				}
+			},
+		}, {
+			Name:   "Index",
+			Params: []kind{stringKind, stringKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				s, substr := c.string(0), c.string(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						return strings.Index(s, substr)
+					}()
+				}
+			},
+		}},
+	},
+	"struct": {
+		native: []*builtin{{
+			Name:   "MinFields",
+			Params: []kind{structKind, intKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				object, n := c.structVal(0), c.int(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						iter := object.Fields(Hidden(false), Optional(false))
+						count := 0
+						for iter.Next() {
+							count++
+						}
+						return count >= n, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "MaxFields",
+			Params: []kind{structKind, intKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				object, n := c.structVal(0), c.int(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						iter := object.Fields(Hidden(false), Optional(false))
+						count := 0
+						for iter.Next() {
+							count++
+						}
+						return count <= n, nil
+					}()
+				}
+			},
+		}},
+	},
+	"text/tabwriter": {
+		native: []*builtin{{
+			Name:   "Write",
+			Params: []kind{topKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				data := c.value(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						buf := &bytes.Buffer{}
+						tw := tabwriter.NewWriter(buf, 0, 4, 1, ' ', 0)
+
+						write := func(v Value) error {
+							b, err := v.Bytes()
+							if err != nil {
+								return err
+							}
+							_, err = tw.Write(b)
+							if err != nil {
+								return err
+							}
+							return nil
+						}
+
+						switch data.Kind() {
+						case BytesKind, StringKind:
+							if err := write(data); err != nil {
+								return "", err
+							}
+						case ListKind:
+							for i, _ := data.List(); i.Next(); {
+								if err := write(i.Value()); err != nil {
+									return "", err
+								}
+								_, _ = tw.Write([]byte{'\n'})
+							}
+						default:
+							return "", fmt.Errorf("tabwriter.Write: unsupported type %v", data.Kind())
+						}
+
+						err := tw.Flush()
+						return buf.String(), err
+					}()
+				}
+			},
+		}},
+	},
+	"text/template": {
+		native: []*builtin{{
+			Name:   "Execute",
+			Params: []kind{stringKind, topKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				templ, data := c.string(0), c.value(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						t, err := template.New("").Parse(templ)
+						if err != nil {
+							return "", err
+						}
+						var x interface{}
+						if err := data.Decode(&x); err != nil {
+							return "", err
+						}
+						buf := &bytes.Buffer{}
+						if err := t.Execute(buf, x); err != nil {
+							return "", err
+						}
+						return buf.String(), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "HTMLEscape",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return template.HTMLEscapeString(s)
+					}()
+				}
+			},
+		}, {
+			Name:   "JSEscape",
+			Params: []kind{stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret = func() interface{} {
+						return template.JSEscapeString(s)
+					}()
+				}
+			},
+		}},
+	},
+	"time": {
+		native: []*builtin{{
+			Name:  "Nanosecond",
+			Const: "1",
+		}, {
+			Name:  "Microsecond",
+			Const: "1000",
+		}, {
+			Name:  "Millisecond",
+			Const: "1000000",
+		}, {
+			Name:  "Second",
+			Const: "1000000000",
+		}, {
+			Name:  "Minute",
+			Const: "60000000000",
+		}, {
+			Name:  "Hour",
+			Const: "3600000000000",
+		}, {
+			Name:   "Duration",
+			Params: []kind{stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						if _, err := time.ParseDuration(s); err != nil {
+							return false, err
+						}
+						return true, nil
+					}()
+				}
+			},
+		}, {
+			Name:   "ParseDuration",
+			Params: []kind{stringKind},
+			Result: intKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						d, err := time.ParseDuration(s)
+						if err != nil {
+							return 0, err
+						}
+						return int64(d), nil
+					}()
+				}
+			},
+		}, {
+			Name:  "ANSIC",
+			Const: "\"Mon Jan _2 15:04:05 2006\"",
+		}, {
+			Name:  "UnixDate",
+			Const: "\"Mon Jan _2 15:04:05 MST 2006\"",
+		}, {
+			Name:  "RubyDate",
+			Const: "\"Mon Jan 02 15:04:05 -0700 2006\"",
+		}, {
+			Name:  "RFC822",
+			Const: "\"02 Jan 06 15:04 MST\"",
+		}, {
+			Name:  "RFC822Z",
+			Const: "\"02 Jan 06 15:04 -0700\"",
+		}, {
+			Name:  "RFC850",
+			Const: "\"Monday, 02-Jan-06 15:04:05 MST\"",
+		}, {
+			Name:  "RFC1123",
+			Const: "\"Mon, 02 Jan 2006 15:04:05 MST\"",
+		}, {
+			Name:  "RFC1123Z",
+			Const: "\"Mon, 02 Jan 2006 15:04:05 -0700\"",
+		}, {
+			Name:  "RFC3339",
+			Const: "\"2006-01-02T15:04:05Z07:00\"",
+		}, {
+			Name:  "RFC3339Nano",
+			Const: "\"2006-01-02T15:04:05.999999999Z07:00\"",
+		}, {
+			Name:  "RFC3339Date",
+			Const: "\"2006-01-02\"",
+		}, {
+			Name:  "Kitchen",
+			Const: "\"3:04PM\"",
+		}, {
+			Name:  "Kitchen24",
+			Const: "\"15:04\"",
+		}, {
+			Name:  "January",
+			Const: "1",
+		}, {
+			Name:  "February",
+			Const: "2",
+		}, {
+			Name:  "March",
+			Const: "3",
+		}, {
+			Name:  "April",
+			Const: "4",
+		}, {
+			Name:  "May",
+			Const: "5",
+		}, {
+			Name:  "June",
+			Const: "6",
+		}, {
+			Name:  "July",
+			Const: "7",
+		}, {
+			Name:  "August",
+			Const: "8",
+		}, {
+			Name:  "September",
+			Const: "9",
+		}, {
+			Name:  "October",
+			Const: "10",
+		}, {
+			Name:  "November",
+			Const: "11",
+		}, {
+			Name:  "December",
+			Const: "12",
+		}, {
+			Name:  "Sunday",
+			Const: "0",
+		}, {
+			Name:  "Monday",
+			Const: "1",
+		}, {
+			Name:  "Tuesday",
+			Const: "2",
+		}, {
+			Name:  "Wednesday",
+			Const: "3",
+		}, {
+			Name:  "Thursday",
+			Const: "4",
+		}, {
+			Name:  "Friday",
+			Const: "5",
+		}, {
+			Name:  "Saturday",
+			Const: "6",
+		}, {
+			Name:   "Time",
+			Params: []kind{stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				s := c.string(0)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return timeFormat(s, time.RFC3339Nano)
+					}()
+				}
+			},
+		}, {
+			Name:   "Format",
+			Params: []kind{stringKind, stringKind},
+			Result: boolKind,
+			Func: func(c *callCtxt) {
+				value, layout := c.string(0), c.string(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						return timeFormat(value, layout)
+					}()
+				}
+			},
+		}, {
+			Name:   "Parse",
+			Params: []kind{stringKind, stringKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				layout, value := c.string(0), c.string(1)
+				if c.do() {
+					c.ret, c.err = func() (interface{}, error) {
+						t, err := time.Parse(layout, value)
+						if err != nil {
+							return "", err
+						}
+						return t.UTC().Format(time.RFC3339Nano), nil
+					}()
+				}
+			},
+		}, {
+			Name:   "Unix",
+			Params: []kind{intKind, intKind},
+			Result: stringKind,
+			Func: func(c *callCtxt) {
+				sec, nsec := c.int64(0), c.int64(1)
+				if c.do() {
+					c.ret = func() interface{} {
+						t := time.Unix(sec, nsec)
+						return t.UTC().Format(time.RFC3339Nano)
+					}()
+				}
+			},
+		}},
+	},
+	"tool": {
+		native: []*builtin{},
+		cue: `{
+	Command: {
+		$usage?: string
+		$short?: string
+		$long?:  string
+		Tasks
+	}
+	Tasks: Task | {
+		[name=string]: Tasks
+	}
+	Task: {
+		$type:   "tool.Task"
+		$id:     =~"\\."
+		$after?: Task | [...Task]
+	}
+	Name: =~"^\\PL([-](\\PL|\\PN))*$"
+}`,
+	},
+	"tool/cli": {
+		native: []*builtin{},
+		cue: `{
+	Print: {
+		$id:  *"tool/cli.Print" | "print"
+		text: string
+	}
+}`,
+	},
+	"tool/exec": {
+		native: []*builtin{},
+		cue: `{
+	Run: {
+		$id: *"tool/exec.Run" | "exec"
+		cmd: string | [string, ...string]
+		env: {
+			[string]: string | [...=~"="]
+		}
+		stdout:  *null | string | bytes
+		stderr:  *null | string | bytes
+		stdin:   *null | string | bytes
+		success: bool
+	}
+}`,
+	},
+	"tool/file": {
+		native: []*builtin{},
+		cue: `{
+	Read: {
+		$id:      "tool/file.Read"
+		filename: !=""
+		contents: *bytes | string
+	}
+	Create: {
+		$id:         "tool/file.Create"
+		filename:    !=""
+		contents:    bytes | string
+		permissions: int | *420
+	}
+	Append: {
+		$id:         "tool/file.Append"
+		filename:    !=""
+		contents:    bytes | string
+		permissions: int | *420
+	}
+	Glob: {
+		$id:  "tool/file.Glob"
+		glob: !=""
+		files: [...string]
+	}
+}`,
+	},
+	"tool/http": {
+		native: []*builtin{},
+		cue: `{
+	Get: Do & {
+		method: "GET"
+	}
+	Do: {
+		$id:    *"tool/http.Do" | "http"
+		method: string
+		response: {
+			body: *bytes | string
+			header: {
+				[string]: string | [...string]
+			}
+			trailer: {
+				[string]: string | [...string]
+			}
+			status:     string
+			statusCode: int
+		}
+		url: string
+		request: {
+			body: *bytes | string
+			header: {
+				[string]: string | [...string]
+			}
+			trailer: {
+				[string]: string | [...string]
+			}
+		}
+	}
+	Post: Do & {
+		method: "POST"
+	}
+	Put: Do & {
+		method: "PUT"
+	}
+	Delete: Do & {
+		method: "DELETE"
+	}
+}`,
+	},
+	"tool/os": {
+		native: []*builtin{},
+		cue: `{
+	Name:  !="" & !~"^[$]"
+	Value: bool | number | *string | null
+	Setenv: {
+		$id: "tool/os.Setenv"
+		{[Name]: Value}
+	}
+	Getenv: {
+		$id: "tool/os.Getenv"
+		{[Name]: Value}
+	}
+	Environ: {
+		$id: "tool/os.Environ"
+		{[Name]: Value}
+	}
+	Clearenv: {
+		$id: "tool/os.Clearenv"
+	}
+}`,
+	},
+}
diff --git a/internal/legacy/cue/builtinutil.go b/internal/legacy/cue/builtinutil.go
new file mode 100644
index 0000000..df22d71
--- /dev/null
+++ b/internal/legacy/cue/builtinutil.go
@@ -0,0 +1,57 @@
+// Copyright 2019 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+// TODO: this code could be generated, but currently isn't.
+
+type valueSorter struct {
+	a   []Value
+	cmp Value
+	err error
+}
+
+func (s *valueSorter) ret() ([]Value, error) {
+	if s.err != nil {
+		return nil, s.err
+	}
+	// The input slice is already a copy and that we can modify it safely.
+	return s.a, nil
+}
+
+func (s *valueSorter) Len() int      { return len(s.a) }
+func (s *valueSorter) Swap(i, j int) { s.a[i], s.a[j] = s.a[j], s.a[i] }
+func (s *valueSorter) Less(i, j int) bool {
+	x := fill(s.cmp, s.a[i], "x")
+	x = fill(x, s.a[j], "y")
+	isLess, err := x.Lookup("less").Bool()
+	if err != nil && s.err == nil {
+		s.err = err
+		return true
+	}
+	return isLess
+}
+
+// fill creates a new value with the old value unified with the given value.
+// TODO: consider making this a method on Value.
+func fill(v Value, x interface{}, path ...string) Value {
+	ctx := v.ctx()
+	root := v.v.val()
+	for i := len(path) - 1; i >= 0; i-- {
+		x = map[string]interface{}{path[i]: x}
+	}
+	value := convertVal(ctx, root, false, x)
+	eval := binOp(ctx, baseValue{}, opUnify, root, value)
+	return newValueRoot(ctx, eval)
+}
diff --git a/internal/legacy/cue/context.go b/internal/legacy/cue/context.go
new file mode 100644
index 0000000..a63e3e9
--- /dev/null
+++ b/internal/legacy/cue/context.go
@@ -0,0 +1,121 @@
+// Copyright 2018 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+	"github.com/cockroachdb/apd/v2"
+)
+
+// context manages evaluation state.
+type context struct {
+	*apd.Context
+
+	*index
+
+	forwardMap []scope // pairs
+	oldSize    []int
+
+	// constraints are to be evaluated at the end values to be evaluated later.
+	constraints []*binaryExpr
+	evalStack   []bottom
+
+	inDefinition int
+	inSum        int
+	cycleErr     bool
+
+	// for debug strings
+	nodeRefs map[scope]string
+
+	// tracing
+	trace bool
+	level int
+
+	// TODO: replace with proper structural cycle detection/ occurs check.
+	// See Issue #29.
+	maxDepth int
+}
+
+func (c *context) incEvalDepth() {
+	if len(c.evalStack) > 0 {
+		c.evalStack[len(c.evalStack)-1].exprDepth++
+	}
+}
+
+func (c *context) decEvalDepth() {
+	if len(c.evalStack) > 0 {
+		c.evalStack[len(c.evalStack)-1].exprDepth--
+	}
+}
+
+var baseContext apd.Context
+
+func init() {
+	baseContext = apd.BaseContext
+	baseContext.Precision = 24
+}
+
+// newContext returns a new evaluation context.
+func (idx *index) newContext() *context {
+	c := &context{
+		Context: &baseContext,
+		index:   idx,
+	}
+	return c
+}
+
+// delayConstraint schedules constraint to be evaluated and returns ret. If
+// delaying constraints is currently not allowed, it returns an error instead.
+func (c *context) delayConstraint(ret evaluated, constraint *binaryExpr) evaluated {
+	c.cycleErr = true
+	c.constraints = append(c.constraints, constraint)
+	return ret
+}
+
+func (c *context) processDelayedConstraints() evaluated {
+	cons := c.constraints
+	c.constraints = c.constraints[:0]
+	for _, dc := range cons {
+		v := binOp(c, dc, dc.Op, dc.X.evalPartial(c), dc.Y.evalPartial(c))
+		if isBottom(v) {
+			return v
+		}
+	}
+	return nil
+}
+
+func (c *context) deref(f scope) scope {
+outer:
+	for {
+		for i := 0; i < len(c.forwardMap); i += 2 {
+			if c.forwardMap[i] == f {
+				f = c.forwardMap[i+1]
+				continue outer
+			}
+		}
+		return f
+	}
+}
+
+func (c *context) pushForwards(pairs ...scope) *context {
+	c.oldSize = append(c.oldSize, len(c.forwardMap))
+	c.forwardMap = append(c.forwardMap, pairs...)
+	return c
+}
+
+func (c *context) popForwards() {
+	last := len(c.oldSize) - 1
+	c.forwardMap = c.forwardMap[:c.oldSize[last]]
+	c.oldSize = c.oldSize[:last]
+}
diff --git a/internal/legacy/cue/cue.go b/internal/legacy/cue/cue.go
new file mode 100644
index 0000000..2e0a031
--- /dev/null
+++ b/internal/legacy/cue/cue.go
@@ -0,0 +1,17 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package cue is a transition package for supporting the cue.Value API.
+// It aims to be plugin compatible with the old API.
+package cue
diff --git a/internal/legacy/cue/errors.go b/internal/legacy/cue/errors.go
new file mode 100644
index 0000000..467254f
--- /dev/null
+++ b/internal/legacy/cue/errors.go
@@ -0,0 +1,311 @@
+// Copyright 2018 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+	"fmt"
+	"reflect"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal/core/adt"
+)
+
+var _ errors.Error = &nodeError{}
+
+// A nodeError is an error associated with processing an AST node.
+type nodeError struct {
+	path []string // optional
+	n    ast.Node
+
+	errors.Message
+}
+
+func (n *nodeError) Error() string {
+	return errors.String(n)
+}
+
+func nodeErrorf(n ast.Node, format string, args ...interface{}) *nodeError {
+	return &nodeError{
+		n:       n,
+		Message: errors.NewMessage(format, args),
+	}
+}
+
+func (e *nodeError) Position() token.Pos {
+	return e.n.Pos()
+}
+
+func (e *nodeError) InputPositions() []token.Pos { return nil }
+
+func (e *nodeError) Path() []string {
+	return e.path
+}
+
+func (v Value) appendErr(err errors.Error, b *bottom) errors.Error {
+	switch {
+	case len(b.sub) > 0:
+		for _, b := range b.sub {
+			err = v.appendErr(err, b)
+		}
+		fallthrough
+	case b.err != nil:
+		err = errors.Append(err, b.err)
+	default:
+		err = errors.Append(err, &valueError{
+			v:   v,
+			err: b,
+		})
+	}
+	return err
+}
+
+func (v Value) toErr(b *bottom) (err errors.Error) {
+	return v.appendErr(nil, b)
+}
+
+var _ errors.Error = &valueError{}
+
+// A valueError is returned as a result of evaluating a value.
+type valueError struct {
+	v   Value
+	err *bottom
+}
+
+func (e *valueError) Error() string {
+	return errors.String(e)
+}
+
+func (e *valueError) Position() token.Pos {
+	return e.err.Pos()
+}
+
+func (e *valueError) InputPositions() []token.Pos {
+	return e.err.Positions(e.v.ctx())
+}
+
+func (e *valueError) Msg() (string, []interface{}) {
+	return e.err.Msg()
+}
+
+func (e *valueError) Path() (a []string) {
+	if e.v.v == nil {
+		return nil
+	}
+	a, _ = e.v.v.appendPath(a, e.v.idx)
+	return a
+}
+
+type errCode = adt.ErrorCode
+
+const (
+	codeNone       errCode = 0
+	codeFatal              = adt.EvalError
+	codeNotExist           = adt.NotExistError
+	codeTypeError          = adt.EvalError
+	codeIncomplete         = adt.IncompleteError
+	codeUser               = adt.UserError
+	codeCycle              = adt.CycleError
+)
+
+func isIncomplete(v value) bool {
+	if err, ok := v.(*bottom); ok {
+		return err.Code == codeIncomplete || err.Code == codeCycle
+	}
+	return false
+}
+
+func isLiteralBottom(v value) bool {
+	if err, ok := v.(*bottom); ok {
+		return err.Code == codeUser
+	}
+	return false
+}
+
+var errNotExists = &bottom{Code: codeNotExist, format: "undefined value"}
+
+func exists(v value) bool {
+	if err, ok := v.(*bottom); ok {
+		return err.Code != codeNotExist
+	}
+	return true
+}
+
+// bottom is the bottom of the value lattice. It is subsumed by all values.
+type bottom struct {
+	baseValue
+
+	index     *index
+	Code      errCode
+	exprDepth int
+	pos       source
+	format    string
+	args      []interface{}
+
+	err     errors.Error // pass-through from higher-level API
+	sub     []*bottom    // sub errors
+	value   value
+	wrapped *bottom
+}
+
+func (x *bottom) Kind() kind { return bottomKind }
+
+func (x *bottom) Positions(ctx *context) []token.Pos {
+	var a []token.Pos
+	if x.index != nil { // TODO: remove check?
+		a = appendPositions(ctx, nil, x.pos)
+	}
+	if w := x.wrapped; w != nil {
+		a = append(a, w.Positions(ctx)...)
+	}
+	for _, sub := range x.sub {
+		a = append(a, sub.Positions(ctx)...)
+	}
+	return a
+}
+
+func appendPositions(ctx *context, pos []token.Pos, src source) []token.Pos {
+	if len(pos) > 15 {
+		return pos
+	}
+	if src != nil {
+		if p := src.Pos(); p != token.NoPos {
+			pos = append(pos, src.Pos())
+		}
+		if c := src.computed(); c != nil {
+			pos = appendPositions(ctx, pos, c.x)
+			pos = appendPositions(ctx, pos, c.y)
+		}
+		switch x := src.(type) {
+		case evaluated:
+		case value:
+			pos = appendPositions(ctx, pos, x.evalPartial(ctx))
+		}
+	}
+	return pos
+}
+
+func (x *bottom) Msg() (format string, args []interface{}) {
+	ctx := x.index.newContext()
+	// We need to copy to avoid races.
+	args = make([]interface{}, len(x.args))
+	copy(args, x.args)
+	preEvalArgs(ctx, args)
+	return x.format, x.args
+}
+
+func (x *bottom) msg() string {
+	return fmt.Sprint(x)
+}
+
+func (x *bottom) Format(s fmt.State, verb rune) {
+	msg, args := x.Msg()
+	fmt.Fprintf(s, msg, args...)
+}
+
+func cycleError(v evaluated) *bottom {
+	if err, ok := v.(*bottom); ok && err.Code == codeCycle {
+		return err
+	}
+	return nil
+}
+
+func (c *context) mkIncompatible(src source, op op, a, b evaluated) evaluated {
+	if err := firstBottom(a, b); err != nil {
+		return err
+	}
+	e := mkBin(c, src.Pos(), op, a, b)
+	return c.mkErr(e, "invalid operation %s %s %s (mismatched types %s and %s)",
+		c.str(a), op, c.str(b), a.Kind(), b.Kind())
+}
+
+func (idx *index) mkErr(src source, args ...interface{}) *bottom {
+	e := &bottom{index: idx, pos: src}
+	if src != nil {
+		e.baseValue = src.base()
+	}
+	if v, ok := src.(value); ok {
+		e.value = v
+	}
+outer:
+	for i, a := range args {
+		switch x := a.(type) {
+		case errCode:
+			e.Code = x
+		case *bottom:
+			e.wrapped = x
+		case []*bottom:
+			e.sub = x
+		case errors.Error:
+			e.err = x
+		case value:
+		case string:
+			e.format = x
+			e.args = args[i+1:]
+			// Do not expand message so that errors can be localized.
+			for i, a := range e.args {
+				e.args[i] = fixArg(idx, a)
+			}
+			break outer
+		}
+	}
+	if e.Code == codeNone && e.wrapped != nil {
+		e.Code = e.wrapped.Code
+	}
+	return e
+}
+
+func fixArg(idx *index, x interface{}) interface{} {
+	switch x.(type) {
+	case uint, int, string:
+		return x
+	case value:
+		return x
+	}
+	t := reflect.TypeOf(x)
+	// Store all non-ptr types as is, as they cannot change.
+	if k := t.Kind(); k == reflect.String || k <= reflect.Complex128 {
+		return x
+	}
+	return fmt.Sprint(x)
+}
+
+// preEvalArgs is used to expand value arguments just before printing.
+func preEvalArgs(ctx *context, args []interface{}) {
+	for i, a := range args {
+		switch v := a.(type) {
+		case *bottom:
+			args[i] = v.msg()
+		case value:
+			// TODO: convert to Go values so that localization frameworks
+			// can format values accordingly.
+			args[i] = ctx.str(v)
+		}
+	}
+}
+
+func isBottom(n value) bool {
+	return n.Kind() == bottomKind
+}
+
+func firstBottom(v ...value) *bottom {
+	for _, b := range v {
+		if isBottom(b) {
+			return b.(*bottom)
+		}
+	}
+	return nil
+}
diff --git a/internal/legacy/cue/instance.go b/internal/legacy/cue/instance.go
new file mode 100644
index 0000000..a779041
--- /dev/null
+++ b/internal/legacy/cue/instance.go
@@ -0,0 +1,365 @@
+// Copyright 2018 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/build"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal"
+	"cuelang.org/go/internal/core/runtime"
+)
+
+// An Instance defines a single configuration based on a collection of
+// underlying CUE files.
+type Instance struct {
+	*index
+
+	rootStruct *structLit // the struct to insert root values into
+	rootValue  value      // the value to evaluate: may add comprehensions
+
+	// scope is used as an additional top-level scope between the package scope
+	// and the predeclared identifiers.
+	scope *structLit
+
+	ImportPath  string
+	Dir         string
+	PkgName     string
+	DisplayName string
+
+	Incomplete bool         // true if Pkg and all its dependencies are free of errors
+	Err        errors.Error // non-nil if the package had errors
+
+	inst *build.Instance
+
+	complete bool // for cycle detection
+}
+
+func (x *index) addInst(p *Instance) *Instance {
+	x.Index.AddInst(p.ImportPath, p.rootStruct, p)
+	p.index = x
+	return p
+}
+
+func (x *index) getImportFromNode(v value) *Instance {
+	p := x.Index.GetImportFromNode(v)
+	if p == nil {
+		return nil
+	}
+	return p.(*Instance)
+}
+
+func (x *index) getImportFromPath(id string) *Instance {
+	node := x.Index.GetImportFromPath(id)
+	if node == nil {
+		return nil
+	}
+	return x.Index.GetImportFromNode(node).(*Instance)
+}
+
+func init() {
+	internal.MakeInstance = func(value interface{}) interface{} {
+		v := value.(Value)
+		x := v.eval(v.ctx())
+		st, ok := x.(*structLit)
+		if !ok {
+			st = &structLit{baseValue: x.base(), emit: x}
+		}
+		return v.ctx().index.addInst(&Instance{
+			rootStruct: st,
+			rootValue:  v.v.v,
+		})
+	}
+}
+
+// newInstance creates a new instance. Use Insert to populate the instance.
+func newInstance(x *index, p *build.Instance) *Instance {
+	// TODO: associate root source with structLit.
+	st := &structLit{baseValue: baseValue{nil}}
+	i := &Instance{
+		rootStruct: st,
+		rootValue:  st,
+		inst:       p,
+	}
+	if p != nil {
+		i.ImportPath = p.ImportPath
+		i.Dir = p.Dir
+		i.PkgName = p.PkgName
+		i.DisplayName = p.ImportPath
+		if p.Err != nil {
+			i.setListOrError(p.Err)
+		}
+	}
+	return x.addInst(i)
+}
+
+func (inst *Instance) setListOrError(err errors.Error) {
+	inst.Incomplete = true
+	inst.Err = errors.Append(inst.Err, err)
+}
+
+func (inst *Instance) setError(err errors.Error) {
+	inst.Incomplete = true
+	inst.Err = errors.Append(inst.Err, err)
+}
+
+func (inst *Instance) eval(ctx *context) evaluated {
+	// TODO: remove manifest here?
+	v := ctx.manifest(inst.rootValue)
+	if s, ok := v.(*structLit); ok && s.emit != nil {
+		e := s.emit.evalPartial(ctx)
+		src := binSrc(token.NoPos, opUnify, v, e)
+	outer:
+		switch e.(type) {
+		case *structLit, *top:
+			v = binOp(ctx, src, opUnifyUnchecked, v, e)
+			if s, ok := v.(*structLit); ok {
+				s.emit = nil
+			}
+
+		default:
+			for _, a := range s.Arcs {
+				if !a.definition {
+					v = binOp(ctx, src, opUnify, v, e)
+					break outer
+				}
+			}
+			return e
+		}
+	}
+	return v
+}
+
+func init() {
+	internal.EvalExpr = func(value, expr interface{}) interface{} {
+		v := value.(Value)
+		e := expr.(ast.Expr)
+		ctx := v.idx.newContext()
+		return newValueRoot(ctx, evalExpr(ctx, v.eval(ctx), e))
+	}
+}
+
+func evalExpr(ctx *context, x value, expr ast.Expr) evaluated {
+	if isBottom(x) {
+		return ctx.mkErr(x, "error evaluating instance: %v", x)
+	}
+	obj, ok := x.(*structLit)
+	if !ok {
+		return ctx.mkErr(x, "instance is not a struct, found %s", x.Kind())
+	}
+	v := newVisitor(ctx.index, nil, nil, obj, true)
+	return v.walk(expr).evalPartial(ctx)
+}
+
+// Doc returns the package comments for this instance.
+func (inst *Instance) Doc() []*ast.CommentGroup {
+	var docs []*ast.CommentGroup
+	if inst.inst == nil {
+		return nil
+	}
+	for _, f := range inst.inst.Files {
+		if c := internal.FileComment(f); c != nil {
+			docs = append(docs, c)
+		}
+	}
+	return docs
+}
+
+// Value returns the root value of the configuration. If the configuration
+// defines in emit value, it will be that value. Otherwise it will be all
+// top-level values.
+func (inst *Instance) Value() Value {
+	ctx := inst.newContext()
+	return newValueRoot(ctx, inst.eval(ctx))
+}
+
+// Eval evaluates an expression within an existing instance.
+//
+// Expressions may refer to builtin packages if they can be uniquely identified.
+func (inst *Instance) Eval(expr ast.Expr) Value {
+	ctx := inst.newContext()
+	result := evalExpr(ctx, inst.eval(ctx), expr)
+	return newValueRoot(ctx, result)
+}
+
+// Merge unifies the given instances into a single one.
+//
+// Errors regarding conflicts are included in the result, but not reported, so
+// that these will only surface during manifestation. This allows
+// non-conflicting parts to be used.
+func Merge(inst ...*Instance) *Instance {
+	switch len(inst) {
+	case 0:
+		return nil
+	case 1:
+		return inst[0]
+	}
+
+	values := []value{}
+	for _, i := range inst {
+		if i.Err != nil {
+			return i
+		}
+		values = append(values, i.rootValue)
+	}
+	merged := &mergedValues{values: values}
+
+	ctx := inst[0].newContext()
+
+	st, ok := ctx.manifest(merged).(*structLit)
+	if !ok {
+		return nil
+	}
+
+	p := ctx.index.addInst(&Instance{
+		rootStruct: st,
+		rootValue:  merged,
+		complete:   true,
+	})
+	return p
+}
+
+// Build creates a new instance from the build instances, allowing unbound
+// identifier to bind to the top-level field in inst. The top-level fields in
+// inst take precedence over predeclared identifier and builtin functions.
+func (inst *Instance) Build(p *build.Instance) *Instance {
+	p.Complete()
+
+	idx := inst.index
+
+	i := newInstance(idx, p)
+	if i.Err != nil {
+		return i
+	}
+
+	ctx := inst.newContext()
+	val := newValueRoot(ctx, inst.rootValue)
+	v, err := val.structValFull(ctx)
+	if err != nil {
+		i.setError(val.toErr(err))
+		return i
+	}
+	i.scope = v.obj
+
+	if err := runtime.ResolveFiles(idx.Index, p, isBuiltin); err != nil {
+		i.setError(err)
+		return i
+	}
+	for _, f := range p.Files {
+		if err := i.insertFile(f); err != nil {
+			i.setListOrError(err)
+		}
+	}
+	i.complete = true
+
+	return i
+}
+
+func (inst *Instance) value() Value {
+	return newValueRoot(inst.newContext(), inst.rootValue)
+}
+
+// Lookup reports the value at a path starting from the top level struct. The
+// Exists method of the returned value will report false if the path did not
+// exist. The Err method reports if any error occurred during evaluation. The
+// empty path returns the top-level configuration struct. Use LookupDef for definitions or LookupField for
+// any kind of field.
+func (inst *Instance) Lookup(path ...string) Value {
+	return inst.value().Lookup(path...)
+}
+
+// LookupDef reports the definition with the given name within struct v. The
+// Exists method of the returned value will report false if the definition did
+// not exist. The Err method reports if any error occurred during evaluation.
+func (inst *Instance) LookupDef(path string) Value {
+	return inst.value().LookupDef(path)
+}
+
+// LookupField reports a Field at a path starting from v, or an error if the
+// path is not. The empty path returns v itself.
+//
+// It cannot look up hidden or unexported fields.
+//
+// Deprecated: this API does not work with new-style definitions. Use
+// FieldByName defined on inst.Value().
+func (inst *Instance) LookupField(path ...string) (f FieldInfo, err error) {
+	v := inst.value()
+	for _, k := range path {
+		s, err := v.Struct()
+		if err != nil {
+			return f, err
+		}
+
+		f, err = s.FieldByName(k, true)
+		if err != nil {
+			return f, err
+		}
+		if f.IsHidden {
+			return f, errNotFound
+		}
+		v = f.Value
+	}
+	return f, err
+}
+
+// Fill creates a new instance with the values of the old instance unified with
+// the given value. It is not possible to update the emit value.
+//
+// Values may be any Go value that can be converted to CUE, an ast.Expr or
+// a Value. In the latter case, it will panic if the Value is not from the same
+// Runtime.
+func (inst *Instance) Fill(x interface{}, path ...string) (*Instance, error) {
+	ctx := inst.newContext()
+	root := ctx.manifest(inst.rootValue)
+	for i := len(path) - 1; i >= 0; i-- {
+		x = map[string]interface{}{path[i]: x}
+	}
+	value := convertVal(ctx, root, true, x)
+	eval := binOp(ctx, baseValue{}, opUnify, root, value)
+	// TODO: validate recursively?
+	err := inst.Err
+	var st *structLit
+	var stVal evaluated
+	switch x := eval.(type) {
+	case *structLit:
+		st = x
+		stVal = x
+	default:
+		// This should not happen.
+		b := ctx.mkErr(eval, "error filling struct")
+		err = inst.Value().toErr(b)
+		st = &structLit{emit: b}
+		stVal = b
+	case *bottom:
+		err = inst.Value().toErr(x)
+		st = &structLit{emit: x}
+		stVal = x
+	}
+	inst = inst.index.addInst(&Instance{
+		rootStruct: st,
+		rootValue:  stVal,
+		inst:       nil,
+
+		// Omit ImportPath to indicate this is not an importable package.
+		Dir:        inst.Dir,
+		PkgName:    inst.PkgName,
+		Incomplete: inst.Incomplete,
+		Err:        err,
+
+		complete: err != nil,
+	})
+	return inst, err
+}
diff --git a/internal/legacy/cue/marshal.go b/internal/legacy/cue/marshal.go
new file mode 100644
index 0000000..fd3cbe1
--- /dev/null
+++ b/internal/legacy/cue/marshal.go
@@ -0,0 +1,215 @@
+// Copyright 2019 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+	"bytes"
+	"compress/gzip"
+	"encoding/gob"
+	"path/filepath"
+	"strings"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/ast/astutil"
+	"cuelang.org/go/cue/build"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/format"
+	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal"
+)
+
+// root.
+type instanceData struct {
+	Root  bool
+	Path  string
+	Files []fileData
+}
+
+type fileData struct {
+	Name string
+	Data []byte
+}
+
+const version = 1
+
+type unmarshaller struct {
+	ctxt    *build.Context
+	imports map[string]*instanceData
+}
+
+func (b *unmarshaller) load(pos token.Pos, path string) *build.Instance {
+	bi := b.imports[path]
+	if bi == nil {
+		return nil
+	}
+	return b.build(bi)
+}
+
+func (b *unmarshaller) build(bi *instanceData) *build.Instance {
+	p := b.ctxt.NewInstance(bi.Path, b.load)
+	p.ImportPath = bi.Path
+	for _, f := range bi.Files {
+		_ = p.AddFile(f.Name, f.Data)
+	}
+	p.Complete()
+	return p
+}
+
+func compileInstances(r *Runtime, data []*instanceData) (instances []*Instance, err error) {
+	b := unmarshaller{
+		ctxt:    r.buildContext(),
+		imports: map[string]*instanceData{},
+	}
+	for _, i := range data {
+		if i.Path == "" {
+			if !i.Root {
+				return nil, errors.Newf(token.NoPos,
+					"data contains non-root package without import path")
+			}
+			continue
+		}
+		b.imports[i.Path] = i
+	}
+
+	builds := []*build.Instance{}
+	for _, i := range data {
+		if !i.Root {
+			continue
+		}
+		builds = append(builds, b.build(i))
+	}
+
+	return r.build(builds)
+}
+
+// Unmarshal creates an Instance from bytes generated by the MarshalBinary
+// method of an instance.
+func (r *Runtime) Unmarshal(b []byte) ([]*Instance, error) {
+	if len(b) == 0 {
+		return nil, errors.Newf(token.NoPos, "unmarshal failed: empty buffer")
+	}
+
+	switch b[0] {
+	case version:
+	default:
+		return nil, errors.Newf(token.NoPos,
+			"unmarshal failed: unsupported version %d, regenerate data", b[0])
+	}
+
+	reader, err := gzip.NewReader(bytes.NewReader(b[1:]))
+	if err != nil {
+		return nil, errors.Newf(token.NoPos, "unmarshal failed: %v", err)
+	}
+
+	data := []*instanceData{}
+	err = gob.NewDecoder(reader).Decode(&data)
+	if err != nil {
+		return nil, errors.Newf(token.NoPos, "unmarshal failed: %v", err)
+	}
+
+	return compileInstances(r, data)
+}
+
+// Marshal creates bytes from a group of instances. Imported instances will
+// be included in the emission.
+//
+// The stored instances are functionally the same, but preserving of file
+// information is only done on a best-effort basis.
+func (r *Runtime) Marshal(instances ...*Instance) (b []byte, err error) {
+	ctx := r.index().newContext()
+
+	staged := []instanceData{}
+	done := map[string]int{}
+
+	var errs errors.Error
+
+	var stageInstance func(i *Instance) (pos int)
+	stageInstance = func(i *Instance) (pos int) {
+		if p, ok := done[i.ImportPath]; ok {
+			return p
+		}
+		// TODO: support exporting instance
+		file, _ := exportFile(ctx, nil, i.rootValue, options{raw: true})
+		imports := []string{}
+		for _, i := range internal.Imports(file) {
+			for _, spec := range i.(*ast.ImportDecl).Specs {
+				info, _ := astutil.ParseImportSpec(spec)
+				imports = append(imports, info.ID)
+			}
+		}
+
+		if i.PkgName != "" {
+			pkg := &ast.Package{Name: ast.NewIdent(i.PkgName)}
+			file.Decls = append([]ast.Decl{pkg}, file.Decls...)
+		}
+
+		b, err := format.Node(file)
+		errs = errors.Append(errs, errors.Promote(err, "marshal"))
+
+		filename := "unmarshal"
+		if i.inst != nil && len(i.inst.Files) == 1 {
+			filename = i.inst.Files[0].Filename
+
+			dir := i.Dir
+			if i.inst != nil && i.inst.Root != "" {
+				dir = i.inst.Root
+			}
+			if dir != "" {
+				filename = filepath.FromSlash(filename)
+				filename, _ = filepath.Rel(dir, filename)
+				filename = filepath.ToSlash(filename)
+			}
+		}
+		// TODO: this should probably be changed upstream, but as the path
+		// is for reference purposes only, this is safe.
+		importPath := filepath.ToSlash(i.ImportPath)
+
+		staged = append(staged, instanceData{
+			Path:  importPath,
+			Files: []fileData{{filename, b}},
+		})
+
+		p := len(staged) - 1
+
+		for _, imp := range imports {
+			i := ctx.getImportFromPath(imp)
+			if i == nil || !strings.Contains(imp, ".") {
+				continue // a builtin package.
+			}
+			stageInstance(i)
+		}
+
+		return p
+	}
+
+	for _, i := range instances {
+		staged[stageInstance(i)].Root = true
+	}
+
+	buf := &bytes.Buffer{}
+	buf.WriteByte(version)
+
+	zw := gzip.NewWriter(buf)
+	if err := gob.NewEncoder(zw).Encode(staged); err != nil {
+		return nil, err
+	}
+
+	if err := zw.Close(); err != nil {
+		return nil, err
+	}
+
+	return buf.Bytes(), nil
+
+}
diff --git a/internal/legacy/cue/marshal_test.go b/internal/legacy/cue/marshal_test.go
new file mode 100644
index 0000000..b380b1a
--- /dev/null
+++ b/internal/legacy/cue/marshal_test.go
@@ -0,0 +1,202 @@
+// Copyright 2019 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+	"fmt"
+	"strings"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+func TestMarshalling(t *testing.T) {
+	testCases := []struct {
+		filename string
+		input    string
+		pkg      string
+	}{{
+		filename: "foo.cue",
+		pkg:      "foo",
+		input: `package foo
+
+		A: int
+		B: string
+		`,
+	}, {
+		filename: "bar.cue",
+		pkg:      "bar",
+		input: `package bar
+
+		"Hello world!"
+		`,
+	}, {
+		filename: "qux.cue",
+		input: `
+			"Hello world!"
+		`,
+	}, {
+		filename: "baz.cue",
+		pkg:      "baz",
+		input: `package baz
+
+		import "strings"
+
+		a: strings.TrimSpace("  Hello world!  ")
+		`}}
+	for _, tc := range testCases {
+		t.Run(tc.filename, func(t *testing.T) {
+			r := &Runtime{}
+			inst, err := r.Compile(tc.filename, tc.input)
+			if err != nil {
+				t.Fatal(err)
+			}
+			inst.ImportPath = "test/pkg"
+			want := fmt.Sprint(inst.Value())
+
+			b, err := r.Marshal(inst)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			r2 := &Runtime{}
+			instances, err := r2.Unmarshal(b)
+			if err != nil {
+				t.Fatal(err)
+			}
+			inst = instances[0]
+
+			if inst.ImportPath != "test/pkg" {
+				t.Error("import path was not restored")
+			}
+			got := fmt.Sprint(inst.Value())
+
+			if got != want {
+				t.Errorf("\ngot:  %q;\nwant: %q", got, want)
+			}
+		})
+	}
+}
+
+func TestMarshalMultiPackage(t *testing.T) {
+	files := func(s ...string) (a []fileData) {
+		for i, s := range s {
+			a = append(a, fileData{fmt.Sprintf("file%d.cue", i), []byte(s)})
+		}
+		return a
+	}
+	insts := func(i ...*instanceData) []*instanceData { return i }
+	pkg1 := &instanceData{
+		true,
+		"pkg1",
+		files(`
+		package pkg1
+
+		Object: "World"
+		`),
+	}
+	pkg2 := &instanceData{
+		true,
+		"example.com/foo/pkg2",
+		files(`
+		package pkg
+
+		Number: 12
+		`),
+	}
+
+	testCases := []struct {
+		instances []*instanceData
+		emit      string
+	}{{
+		insts(&instanceData{true, "", files(`test: "ok"`)}),
+		`{test: "ok"}`,
+	}, {
+		insts(&instanceData{true, "",
+			files(
+				`package test
+
+		import math2 "math"
+
+		"Pi: \(math2.Pi)!"`)}),
+		`"Pi: 3.14159265358979323846264338327950288419716939937510582097494459!"`,
+	}, {
+		insts(pkg1, &instanceData{true, "",
+			files(
+				`package test
+
+			import "pkg1"
+
+			"Hello \(pkg1.Object)!"`),
+		}),
+		`"Hello World!"`,
+	}, {
+		insts(pkg1, &instanceData{true, "",
+			files(
+				`package test
+
+		import pkg2 "pkg1"
+		pkg1: pkg2.Object
+
+		"Hello \(pkg1)!"`),
+		}),
+		`"Hello World!"`,
+	}, {
+		insts(pkg2, &instanceData{true, "",
+			files(
+				`package test
+
+		import "example.com/foo/pkg2"
+
+		"Hello \(pkg.Number)!"`),
+		}),
+		`"Hello 12!"`,
+	}}
+
+	strValue := func(a []*Instance) (ret []string) {
+		for _, i := range a {
+			ret = append(ret, strings.TrimSpace((fmt.Sprint(i.Value()))))
+		}
+		return ret
+	}
+
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			r := &Runtime{}
+
+			insts, err := compileInstances(r, tc.instances)
+			if err != nil {
+				t.Fatal(err)
+			}
+			want := strValue(insts)
+
+			b, err := r.Marshal(insts...)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			r2 := &Runtime{}
+			insts, err = r2.Unmarshal(b)
+			if err != nil {
+				t.Fatal(err)
+			}
+			got := strValue(insts)
+
+			if !cmp.Equal(got, want) {
+				t.Error(cmp.Diff(got, want))
+			}
+		})
+	}
+}
diff --git a/internal/legacy/cue/op.go b/internal/legacy/cue/op.go
new file mode 100644
index 0000000..b313289
--- /dev/null
+++ b/internal/legacy/cue/op.go
@@ -0,0 +1,219 @@
+// Copyright 2018 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal/core/adt"
+)
+
+// Op indicates the operation at the top of an expression tree of the expression
+// use to evaluate a value.
+type Op = adt.Op
+
+// Values of Op.
+const (
+	NoOp = adt.NoOp
+
+	AndOp = adt.AndOp
+	OrOp  = adt.OrOp
+
+	SelectorOp = adt.SelectorOp
+	IndexOp    = adt.IndexOp
+	SliceOp    = adt.SliceOp
+	CallOp     = adt.CallOp
+
+	BooleanAndOp = adt.BoolAndOp
+	BooleanOrOp  = adt.BoolOrOp
+
+	EqualOp            = adt.EqualOp
+	NotOp              = adt.NotOp
+	NotEqualOp         = adt.NotEqualOp
+	LessThanOp         = adt.LessThanOp
+	LessThanEqualOp    = adt.LessEqualOp
+	GreaterThanOp      = adt.GreaterThanOp
+	GreaterThanEqualOp = adt.GreaterEqualOp
+
+	RegexMatchOp    = adt.MatchOp
+	NotRegexMatchOp = adt.NotMatchOp
+
+	AddOp           = adt.AddOp
+	SubtractOp      = adt.SubtractOp
+	MultiplyOp      = adt.MultiplyOp
+	FloatQuotientOp = adt.FloatQuotientOp
+	IntQuotientOp   = adt.IntQuotientOp
+	IntRemainderOp  = adt.IntRemainderOp
+	IntDivideOp     = adt.IntDivideOp
+	IntModuloOp     = adt.IntModuloOp
+
+	InterpolationOp = adt.InterpolationOp
+)
+
+var opToOp = map[op]Op{
+	opUnify: AndOp,
+	// TODO(eval): opUnifyUnchecked is not the same as opUnify and should have its own
+	// category, if needed. More likely opUnifyUnchecked, should be
+	// represented as a separate embedding method.
+	opUnifyUnchecked: AndOp,
+	opDisjunction:    OrOp,
+	opLand:           BooleanAndOp,
+	opLor:            BooleanOrOp,
+	opEql:            EqualOp,
+	opNot:            NotOp,
+	opNeq:            NotEqualOp,
+	opLss:            LessThanOp,
+	opLeq:            LessThanEqualOp,
+	opGtr:            GreaterThanOp,
+	opGeq:            GreaterThanEqualOp,
+	opMat:            RegexMatchOp,
+	opNMat:           NotRegexMatchOp,
+	opAdd:            AddOp,
+	opSub:            SubtractOp,
+	opMul:            MultiplyOp,
+	opQuo:            FloatQuotientOp,
+	opIQuo:           IntQuotientOp,
+	opIRem:           IntRemainderOp,
+	opIDiv:           IntDivideOp,
+	opIMod:           IntModuloOp,
+}
+
+func opIn(op op, anyOf ...op) bool {
+	for _, o := range anyOf {
+		if o == op {
+			return true
+		}
+	}
+	return false
+}
+
+// isCmp reports whether an op is a comparator.
+func (op op) isCmp() bool {
+	return opEql <= op && op <= opGeq
+}
+
+func (op op) unifyType() (unchecked, ok bool) {
+	if op == opUnifyUnchecked {
+		return true, true
+	}
+	return false, op == opUnify
+}
+
+type op uint16
+
+const (
+	opUnknown op = iota
+
+	opUnify
+	opUnifyUnchecked
+	opDisjunction
+
+	opLand
+	opLor
+	opNot
+
+	opEql
+	opNeq
+	opMat
+	opNMat
+
+	opLss
+	opGtr
+	opLeq
+	opGeq
+
+	opAdd
+	opSub
+	opMul
+	opQuo
+	opRem
+
+	opIDiv
+	opIMod
+	opIQuo
+	opIRem
+)
+
+var opStrings = []string{
+	opUnknown: "??",
+
+	opUnify: "&",
+	// opUnifyUnchecked is internal only. Syntactically this is
+	// represented as embedding.
+	opUnifyUnchecked: "&!",
+	opDisjunction:    "|",
+
+	opLand: "&&",
+	opLor:  "||",
+	opNot:  "!",
+
+	opEql:  "==",
+	opNeq:  "!=",
+	opMat:  "=~",
+	opNMat: "!~",
+
+	opLss: "<",
+	opGtr: ">",
+	opLeq: "<=",
+	opGeq: ">=",
+
+	opAdd: "+",
+	opSub: "-",
+	opMul: "*",
+	opQuo: "/",
+
+	opIDiv: "div",
+	opIMod: "mod",
+	opIQuo: "quo",
+	opIRem: "rem",
+}
+
+func (op op) String() string { return opStrings[op] }
+
+var tokenMap = map[token.Token]op{
+	token.OR:  opDisjunction, // |
+	token.AND: opUnify,       // &
+
+	token.ADD: opAdd, // +
+	token.SUB: opSub, // -
+	token.MUL: opMul, // *
+	token.QUO: opQuo, // /
+
+	token.IDIV: opIDiv, // div
+	token.IMOD: opIMod, // mod
+	token.IQUO: opIQuo, // quo
+	token.IREM: opIRem, // rem
+
+	token.LAND: opLand, // &&
+	token.LOR:  opLor,  // ||
+
+	token.EQL: opEql, // ==
+	token.LSS: opLss, // <
+	token.GTR: opGtr, // >
+	token.NOT: opNot, // !
+
+	token.NEQ:  opNeq,  // !=
+	token.LEQ:  opLeq,  // <=
+	token.GEQ:  opGeq,  // >=
+	token.MAT:  opMat,  // =~
+	token.NMAT: opNMat, // !~
+}
+
+var opMap = map[op]token.Token{}
+
+func init() {
+	for t, o := range tokenMap {
+		opMap[o] = t
+	}
+}
diff --git a/internal/legacy/cue/resolve_test.go b/internal/legacy/cue/resolve_test.go
new file mode 100644
index 0000000..e7480da
--- /dev/null
+++ b/internal/legacy/cue/resolve_test.go
@@ -0,0 +1,3052 @@
+// Copyright 2018 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+	"flag"
+	"strings"
+	"testing"
+)
+
+var traceOn = flag.Bool("debug", false, "enable tracing")
+
+func compileFileWithErrors(t *testing.T, body string) (*context, *structLit, error) {
+	t.Helper()
+	ctx, inst, err := compileInstance(t, body)
+	return ctx, inst.rootValue.evalPartial(ctx).(*structLit), err
+}
+
+func compileFile(t *testing.T, body string) (*context, *structLit) {
+	t.Helper()
+	ctx, inst, errs := compileInstance(t, body)
+	if errs != nil {
+		t.Fatal(errs)
+	}
+	return ctx, inst.rootValue.evalPartial(ctx).(*structLit)
+}
+
+func compileInstance(t *testing.T, body string) (*context, *Instance, error) {
+	var r Runtime
+	inst, err := r.Compile("test", body)
+
+	if err != nil {
+		x := newInstance(newIndex(sharedIndex), nil)
+		ctx := x.newContext()
+		return ctx, x, err
+	}
+
+	return r.index().newContext(), inst, nil
+}
+
+func rewriteHelper(t *testing.T, cases []testCase, r rewriteMode) {
+	for _, tc := range cases {
+		t.Run(tc.desc, func(t *testing.T) {
+			ctx, obj := compileFile(t, tc.in)
+			ctx.trace = *traceOn
+			root := testResolve(ctx, obj, r)
+
+			got := debugStr(ctx, root)
+
+			// Copy the result
+			if got != tc.out {
+				fn := t.Errorf
+				if tc.skip {
+					fn = t.Skipf
+				}
+				fn("output differs:\ngot  %s\nwant %s", got, tc.out)
+			}
+		})
+	}
+}
+
+type testCase struct {
+	desc string
+	in   string
+	out  string
+	skip bool
+}
+
+func TestBasicRewrite(t *testing.T) {
+	testCases := []testCase{{
+		desc: "errors",
+		in: `
+			a: _|_ & _|_
+			b: null & _|_
+			c: b.a == _|_
+			d: _|_ != b.a
+			e: _|_ == _|_
+			`,
+		out: `<0>{a: _|_(from source), b: _|_(from source), c: true, d: false, e: true}`,
+	}, {
+		desc: "regexp",
+		in: `
+			c1: "a" =~ "a"
+			c2: "foo" =~ "[a-z]{3}"
+			c3: "foo" =~ "[a-z]{4}"
+			c4: "foo" !~ "[a-z]{4}"
+
+			b1: =~ "a"
+			b1: "a"
+			b2: =~ "[a-z]{3}"
+			b2: "foo"
+			b3: =~ "[a-z]{4}"
+			b3: "foo"
+			b4: !~ "[a-z]{4}"
+			b4: "foo"
+
+			s1: != "b" & =~"c"      // =~"c"
+			s2: != "b" & =~"[a-z]"  // != "b" & =~"[a-z]"
+
+			e1: "foo" =~ 1
+			e2: "foo" !~ true
+			e3: != "a" & <5
+		`,
+		out: `<0>{c1: true, ` +
+			`c2: true, ` +
+			`c3: false, ` +
+			`c4: true, ` +
+
+			`b1: "a", ` +
+			`b2: "foo", ` +
+			`b3: _|_((=~"[a-z]{4}" & "foo"):invalid value "foo" (does not match =~"[a-z]{4}")), ` +
+			`b4: "foo", ` +
+
+			`s1: =~"c", ` +
+			`s2: (!="b" & =~"[a-z]"), ` +
+
+			`e1: _|_(("foo" =~ 1):invalid operation "foo" =~ 1 (mismatched types string and int)), ` +
+			`e2: _|_(("foo" !~ true):invalid operation "foo" !~ true (mismatched types string and bool)), ` +
+			`e3: _|_((!="a" & <5):conflicting values !="a" and <5 (mismatched types string and number))}`,
+	}, {
+		desc: "arithmetic",
+		in: `
+			i1: 1 & int
+			i2: 2 & int
+
+			sum: -1 + +2        // 1
+			div1: 2.0 / 3 * 6   // 4
+			div2: 2 / 3 * 6     // 4
+			div3: 1.00 / 1.00
+			divZero: 1.0 / 0
+			div00: 0 / 0
+			b: 1 != 4
+			add: div1 + 1.0
+
+			idiv00: 0 div 0
+			imod00: 0 mod 0
+			iquo00: 0 quo 0
+			irem00: 0 rem 0
+
+			v1: 1.0T/2.0
+			v2: 2.0 == 2
+			v3: 2.0/3.0
+			v5: i1 div i2
+
+			e0: 2 + "a"
+			// these are now all alloweed
+			// e1: 2.0 / i1
+			// e2: i1 / 2.0
+			// e3: 3.0 % i2
+			// e4: i1 % 2.0
+			e5: 1.0 div 2
+			e6: 2 rem 2.0
+			e7: 2 quo 2.0
+			e8: 1.0 mod 1
+			`,
+		out: `<0>{i1: 1, i2: 2, ` +
+			`sum: 1, ` +
+			`div1: 4.00000000000000000000000, ` +
+			`div2: 4.00000000000000000000000, ` +
+			`div3: 1., ` +
+			`divZero: _|_((1.0 / 0):division by zero), ` +
+			`div00: _|_((0 / 0):division undefined), ` +
+			`b: true, ` +
+			`add: 5.00000000000000000000000, ` +
+			`idiv00: _|_((0 div 0):division by zero), ` +
+			`imod00: _|_((0 mod 0):division by zero), ` +
+			`iquo00: _|_((0 quo 0):division by zero), ` +
+			`irem00: _|_((0 rem 0):division by zero), ` +
+			`v1: 5.0000000000e+11, ` +
+			`v2: true, ` +
+			`v3: 0.666666666666666666666667, ` +
+			`v5: 0, ` +
+
+			`e0: _|_((2 + "a"):invalid operation 2 + "a" (mismatched types int and string)), ` +
+			// `e1: _|_((2.0 / 1):unsupported op /(float, int)), ` +
+			// `e2: _|_((1 / 2.0):unsupported op /(int, float)), ` +
+			// `e3: _|_((3.0 % 2):unsupported op %(float, int)), ` +
+			// `e4: _|_((1 % 2.0):unsupported op %(int, float)), ` +
+			`e5: _|_((1.0 div 2):invalid operation 1.0 div 2 (mismatched types float and int)), ` +
+			`e6: _|_((2 rem 2.0):invalid operation 2 rem 2.0 (mismatched types int and float)), ` +
+			`e7: _|_((2 quo 2.0):invalid operation 2 quo 2.0 (mismatched types int and float)), ` +
+			`e8: _|_((1.0 mod 1):invalid operation 1.0 mod 1 (mismatched types float and int))}`,
+	}, {
+		desc: "integer-specific arithmetic",
+		in: `
+			q1: 5 quo 2    // 2
+			q2: 5 quo -2   // -2
+			q3: -5 quo 2   // -2
+			q4: -5 quo -2  // 2
+			qe1: 2.0 quo 1
+			qe2: 2 quo 1.0
+
+			r1: 5 rem 2    // 1
+			r2: 5 rem -2   // 1
+			r3: -5 rem 2   // -1
+			r4: -5 rem -2  // -1
+			re1: 2.0 rem 1
+			re2: 2 rem 1.0
+
+			d1: 5 div 2    // 2
+			d2: 5 div -2   // -2
+			d3: -5 div 2   // -3
+			d4: -5 div -2  // 3
+			de1: 2.0 div 1
+			de2: 2 div 1.0
+
+			m1: 5 mod 2    // 1
+			m2: 5 mod -2   // 1
+			m3: -5 mod 2   // 1
+			m4: -5 mod -2  // 1
+			me1: 2.0 mod 1
+			me2: 2 mod 1.0
+			`,
+		out: `<0>{q1: 2, q2: -2, q3: -2, q4: 2, ` +
+			`qe1: _|_((2.0 quo 1):invalid operation 2.0 quo 1 (mismatched types float and int)), ` +
+			`qe2: _|_((2 quo 1.0):invalid operation 2 quo 1.0 (mismatched types int and float)), ` +
+			`r1: 1, r2: 1, r3: -1, r4: -1, ` +
+			`re1: _|_((2.0 rem 1):invalid operation 2.0 rem 1 (mismatched types float and int)), ` +
+			`re2: _|_((2 rem 1.0):invalid operation 2 rem 1.0 (mismatched types int and float)), ` +
+			`d1: 2, d2: -2, d3: -3, d4: 3, ` +
+			`de1: _|_((2.0 div 1):invalid operation 2.0 div 1 (mismatched types float and int)), ` +
+			`de2: _|_((2 div 1.0):invalid operation 2 div 1.0 (mismatched types int and float)), ` +
+			`m1: 1, m2: 1, m3: 1, m4: 1, ` +
+			`me1: _|_((2.0 mod 1):invalid operation 2.0 mod 1 (mismatched types float and int)), ` +
+			`me2: _|_((2 mod 1.0):invalid operation 2 mod 1.0 (mismatched types int and float))}`,
+	}, {
+		desc: "booleans",
+		in: `
+			t: true
+			t: !false
+			f: false
+			f: !t
+			e: true
+			e: !true
+			`,
+		out: "<0>{t: true, f: false, e: _|_(true:conflicting values true and false)}",
+	}, {
+		desc: "boolean arithmetic",
+		in: `
+			a: true && true
+			b: true || false
+			c: false == true
+			d: false != true
+			e: true & true
+			f: true & false
+			`,
+		out: "<0>{a: true, b: true, c: false, d: true, e: true, f: _|_(true:conflicting values true and false)}",
+	}, {
+		desc: "basic type",
+		in: `
+			a: 1 & int
+			b: number & 1
+			c: 1.0
+			c: float
+			d: int & float // _|_
+			e: "4" & string
+			f: true
+			f: bool
+			`,
+		out: `<0>{a: 1, b: 1, c: 1.0, d: _|_((int & float):conflicting values int and float (mismatched types int and float)), e: "4", f: true}`, // TODO: eliminate redundancy
+	}, {
+		desc: "strings and bytes",
+		in: `
+			s0: "foo" + "bar"
+			s1: 3 * "abc"
+			s2: "abc" * 2
+
+			b0: 'foo' + 'bar'
+			b1: 3 * 'abc'
+			b2: 'abc' * 2
+
+			// TODO: consider the semantics of this and perhaps allow this.
+			e0: "a" + ''
+			e1: 'b' + "c"
+		`,
+		out: `<0>{` +
+			`s0: "foobar", ` +
+			`s1: "abcabcabc", ` +
+			`s2: "abcabc", ` +
+			`b0: 'foobar', ` +
+			`b1: 'abcabcabc', ` +
+			`b2: 'abcabc', ` +
+
+			`e0: _|_(("a" + ''):invalid operation "a" + '' (mismatched types string and bytes)), ` +
+			`e1: _|_(('b' + "c"):invalid operation 'b' + "c" (mismatched types bytes and string))` +
+			`}`,
+	}, {
+		desc: "escaping",
+
+		in: `
+			a: "foo\nbar",
+			b: a,
+
+			// TODO: mimic http://exploringjs.com/es6/ch_template-literals.html#sec_introduction-template-literals
+		`,
+		out: `<0>{a: "foo\nbar", b: "foo\nbar"}`,
+		// out: `<0>{a: "foo\nbar", b: <0>.a}`,
+	}, {
+		desc: "reference",
+		in: `
+			a: b
+			b: 2
+			d: {
+				d: 3
+				e: d
+			}
+			e: {
+				e: {
+					v: 1
+				}
+				f: {
+					v: e.v
+				}
+			}
+			`,
+		out: "<0>{a: 2, b: 2, d: <1>{d: 3, e: 3}, e: <2>{e: <3>{v: 1}, f: <4>{v: 1}}}",
+	}, {
+		desc: "lists",
+		in: `
+			list: [1,2,3]
+			index: [1,2,3][1]
+			unify: [1,2,3] & [_,2,3]
+			e: [] & 4
+			e2: [3]["d"]
+			e3: [3][-1]
+			e4: [1, 2, ...>=4 & <=5] & [1, 2, 4, 8]
+			e5: [1, 2, 4, 8] & [1, 2, ...>=4 & <=5]
+			`,
+		out: `<0>{list: [1,2,3], index: 2, unify: [1,2,3], e: _|_(([] & 4):conflicting values [] and 4 (mismatched types list and int)), e2: _|_("d":invalid list index "d" (type string)), e3: _|_(-1:invalid list index -1 (index must be non-negative)), e4: [1,2,4,_|_((<=5 & 8):invalid value 8 (out of bound <=5))], e5: [1,2,4,_|_((<=5 & 8):invalid value 8 (out of bound <=5))]}`,
+	}, {
+		desc: "list arithmetic",
+		in: `
+			list: [1,2,3]
+			mul0: list*0
+			mul1: list*1
+			mul2: 2*list
+			list1: [1]
+		    mul1_0: list1*0
+			mul1_1: 1*list1
+			mul1_2: list1*2
+			e: list*-1
+			`,
+		out: `<0>{list: [1,2,3], ` +
+			`mul0: [], ` +
+			`mul1: [1,2,3], ` +
+			`mul2: [1,2,3,1,2,3], ` +
+			`list1: [1], ` +
+			`mul1_0: [], ` +
+			`mul1_1: [1], ` +
+			`mul1_2: [1,1], ` +
+			`e: _|_((<1>.list * -1):negative number -1 multiplies list)}`,
+	}, {
+		desc: "selecting",
+		in: `
+			obj: {a: 1, b: 2}
+			index: {a: 1, b: 2}["b"]
+			mulidx: {a: 1, b: {a:1, b: 3}}["b"]["b"]
+			e: {a: 1}[4]
+			f: {a: 1}.b
+			g: {a: 1}["b"]
+			h: [3].b
+			`,
+		out: `<0>{obj: <1>{a: 1, b: 2}, index: 2, mulidx: 3, e: _|_(4:invalid struct index 4 (type int)), f: <2>{a: 1}.b, g: <3>{a: 1}["b"], h: _|_([3]:invalid operation: [3].b (type list does not support selection))}`,
+	}, {
+		desc: "obj unify",
+		in: `
+			o1: {a: 1 } & { b: 2}      // {a:1,b:2}
+			o2: {a: 1, b:2 } & { b: 2} // {a:1,b:2}
+			o3: {a: 1 } & { a:1, b: 2} // {a:1,b:2}
+			o4: {a: 1 } & { b: 2}      // {a:1,b:2}
+			o4: {a: 1, b:2 } & { b: 2}
+			o4: {a: 1 } & { a:1, b: 2}
+			e: 1                       // 1 & {a:3}
+			e: {a:3}
+			`,
+		out: "<0>{o1: <1>{a: 1, b: 2}, o2: <2>{a: 1, b: 2}, o3: <3>{a: 1, b: 2}, o4: <4>{a: 1, b: 2}, e: _|_((1 & <5>{a: 3}):conflicting values 1 and {a: 3} (mismatched types int and struct))}",
+	}, {
+		desc: "disjunctions",
+		in: `
+			o1: 1 | 2 | 3
+			o2: (1 | 2 | 3) & 1
+			o3: 2 & (1 | *2 | 3)
+			o4: (1 | *2 | 3) & (1 | 2 | *3)
+			o5: (1 | *2 | 3) & (3 | *2 | 1)
+			o6: (1 | 2 | 3) & (3 | 1 | 2)
+			o7: (1 | 2 | 3) & (2 | 3)
+			o8: (1 | 2 | 3) & (3 | 2)
+			o9: (2 | 3) & (1 | 2 | 3)
+			o10: (3 | 2) & (1 | *2 | 3)
+
+			m1: (*1 | (*2 | 3)) & (>=2 & <=3)
+			m2: (*1 | (*2 | 3)) & (2 | 3)
+			m3: (*1 | *(*2 | 3)) & (2 | 3)
+			m4: (2 | 3) & (*2 | 3)
+			m5: (*2 | 3) & (2 | 3)
+
+			// (*2 | 3) & (2 | 3)
+			// (2 | 3) & (*2 | 3)
+			// 2&(*2 | 3) | 3&(*2 | 3)
+			// (*1 | (*2 | 3)) & (2 | 3)
+			// *1& (2 | 3) | (*2 | 3)&(2 | 3)
+			// *2&(2 | 3) | 3&(2 | 3)
+
+			// (2 | 3)&(*1 | (*2 | 3))
+			// 2&(*1 | (*2 | 3)) | 3&(*1 | (*2 | 3))
+			// *1&2 | (*2 | 3)&2 | *1&3 | (*2 | 3)&3
+			// (*2 | 3)&2 | (*2 | 3)&3
+			// *2 | 3
+
+
+			// All errors are treated the same as per the unification model.
+			i1: [1, 2][3] | "c"
+			`,
+		out: `<0>{o1: (1 | 2 | 3), o2: 1, o3: 2, o4: (1 | 2 | 3 | *_|_), o5: (1 | *2 | 3), o6: (1 | 2 | 3), o7: (2 | 3), o8: (2 | 3), o9: (2 | 3), o10: (3 | *2), m1: (*2 | 3), m2: (*2 | 3), m3: (*2 | 3), m4: (*2 | 3), m5: (*2 | 3), i1: "c"}`,
+	}, {
+		desc: "types",
+		in: `
+			i: int
+			j: int & 3
+			s: string
+			t: "s" & string
+			e: int & string
+			e2: 1 & string
+			b: !int
+			p: +true
+			m: -false
+		`,
+		out: `<0>{i: int, j: 3, s: string, t: "s", e: _|_((int & string):conflicting values int and string (mismatched types int and string)), e2: _|_((1 & string):conflicting values 1 and string (mismatched types int and string)), b: _|_(!int:invalid operation !int (! int)), p: _|_(+true:invalid operation +true (+ bool)), m: _|_(-false:invalid operation -false (- bool))}`,
+	}, {
+		desc: "comparison",
+		in: `
+			lss: 1 < 2
+			leq: 1 <= 1.0
+			leq: 2.0 <= 3
+			eql: 1 == 1.0
+			neq: 1.0 == 1
+			gtr: !(2 > 3)
+			geq: 2.0 >= 2
+			seq: "a" + "b" == "ab"
+			err: 2 == "s"
+		`,
+		out: `<0>{lss: true, leq: true, eql: true, neq: true, gtr: true, geq: true, seq: true, err: _|_((2 == "s"):invalid operation 2 == "s" (mismatched types int and string))}`,
+	}, {
+		desc: "null",
+		in: `
+			eql: null == null
+			neq: null != null
+			unf: null & null
+
+			// errors
+			eq1: null == 1
+			eq2: 1 == null
+			ne1: "s" != null
+			call: null()
+		`,
+		out: `<0>{eql: true, neq: false, unf: null, eq1: false, eq2: false, ne1: true, call: _|_(null:cannot call non-function null (type null))}`,
+	}, {
+		desc: "self-reference cycles",
+		in: `
+			a: b - 100
+			b: a + 100
+
+			c: [c[1], c[0]]
+		`,
+		out: `<0>{a: (<1>.b - 100), ` +
+			`b: (<1>.a + 100), ` +
+			`c: [<1>.c[1],<1>.c[0]]}`,
+	}, {
+		desc: "resolved self-reference cycles",
+		in: `
+			a: b - 100
+			b: a + 100
+			b: 200
+
+			c: [c[1], a]
+
+			s1: s2 & {a: 1}
+			s2: s3 & {b: 2}
+			s3: s1 & {c: 3}
+		`,
+		out: `<0>{a: 100, b: 200, c: [100,100], s1: <1>{a: 1, b: 2, c: 3}, s2: <2>{a: 1, b: 2, c: 3}, s3: <3>{a: 1, b: 2, c: 3}}`,
+	}, {
+		desc: "resolved self-reference cycles: Issue 19",
+		in: `
+			// CUE knows how to resolve the following:
+			x: y + 100
+			y: x - 100
+			x: 200
+
+			z1: z2 + 1
+			z2: z3 + 2
+			z3: z1 - 3
+			z3: 8
+
+			// TODO: extensive tests with disjunctions.
+		`,
+		out: `<0>{x: 200, y: 100, z1: 11, z2: 10, z3: 8}`,
+	}, {
+		desc: "delayed constraint failure",
+		in: `
+			a: b - 100
+			b: a + 110
+			b: 200
+
+			x: 100
+			x: x + 1
+		`,
+		out: `<0>{` +
+			`x: _|_((100 & 101):conflicting values 100 and 101), ` +
+			`a: _|_((210 & 200):conflicting values 210 and 200), ` +
+			`b: _|_((210 & 200):conflicting values 210 and 200)}`,
+		// TODO: find a way to mark error in data.
+	}}
+	rewriteHelper(t, testCases, evalPartial)
+}
+
+func TestChooseDefault(t *testing.T) {
+	testCases := []testCase{{
+		desc: "pick first",
+		in: `
+		a: *5 | "a" | true
+		b: c: *{
+			a: 2
+		} | {
+			a : 3
+		}
+		`,
+		out: "<0>{a: 5, b: <1>{c: <2>{a: 2}}}",
+	}, {
+		// In this test, default results to bottom, meaning that the non-default
+		// value remains.
+		desc: "simple disambiguation conflict",
+		in: `
+			a: *"a" | "b"
+			b: *"b" | "a"
+			c: a & b
+			`,
+		out: `<0>{a: "a", b: "b", c: ("a" | "b")}`,
+	}, {
+		desc: "associativity of defaults",
+		in: `
+			a: *"a" | ("b" | "c")
+			b: (*"a" | "b") | "c"
+			c: *"a" | (*"b" | "c")
+			x: a & b
+			y: b & c
+			`,
+		out: `<0>{x: "a", y: (*"a" | *"b"), a: "a", b: "a", c: (*"a" | *"b")}`,
+	}}
+	rewriteHelper(t, testCases, evalFull)
+}
+
+func TestResolve(t *testing.T) {
+	testCases := []testCase{{
+		desc: "convert _ to top",
+		in:   `a: { [_]: _ }`,
+		out:  `<0>{a: <1>{...}}`,
+	}, {
+		in: `
+			a: b.c.d
+			b: c: { d: 3 }
+			c: { c: d.d, }
+			d: { d: 2 }
+			`,
+		out: "<0>{a: 3, b: <1>{c: <2>{d: 3}}, c: <3>{c: 2}, d: <4>{d: 2}}",
+	}, {
+		in:  "`foo-bar`: 3\n x: `foo-bar`,",
+		out: `<0>{x: 3, "foo-bar": 3}`,
+	}, {
+		desc: "resolution of quoted identifiers",
+		in: `
+		package foo
+
+` + "`foo-bar`" + `: 2
+"baz":     ` + "`foo-bar`" + `
+
+a: {
+	qux:        3
+	` + "`qux-quux`" + `: qux
+	"qaz":      ` + "`qux-quux`" + `
+}`,
+		out: `<0>{"foo-bar": 2, baz: 2, a: <1>{qux: 3, "qux-quux": 3, qaz: 3}}`,
+	}, {
+		in: `
+			a: _
+			b: a
+			a: { d: 1, d: _ }
+			b: _
+			`,
+		out: `<0>{a: <1>{d: 1}, b: <2>{d: 1}}`,
+	}, {
+		desc: "JSON",
+		in: `
+			a="a": 3
+			b: a
+			o: { "a\nb": 2 } // TODO: use $ for root?
+			c: o["a\nb"]
+		`,
+		out: `<0>{a: 3, b: 3, o: <1>{"a\nb": 2}, c: 2}`,
+	}, {
+		desc: "arithmetic",
+		in: `
+				v1: 1.0T/2.0
+				v2: 2.0 == 2
+				n1: 1
+				v5: 2.0 / n1
+				v6: 1.0 / 1.0
+				e2: int & 4.0/2.0
+				`,
+		out: `<0>{v1: 5.0000000000e+11, v2: true, n1: 1, v5: 2.0, v6: 1., e2: _|_((int & (4.0 / 2.0)):conflicting values int and (4.0 / 2.0) (mismatched types int and float))}`,
+	}, {
+		desc: "inequality",
+		in: `
+			a: 1 != 2
+			b: 1 != null
+			c: true == null
+			d: null != {}
+			e: null == []
+			f: 0 == 0.0    // types are unified first TODO: make this consistent
+		`,
+		out: `<0>{a: true, b: true, c: false, d: true, e: false, f: true}`,
+	}, {
+		desc: "attributes",
+		in: `
+			a: { foo: 1 @foo() @baz(1) }
+			b: { foo: 1 @bar() @foo() }
+			c: a & b
+
+			e: a & { foo: 1 @foo(other) }
+		`,
+		out: `<0>{a: <1>{foo: 1 @baz(1) @foo()}, ` +
+			`b: <2>{foo: 1 @bar() @foo()}, ` +
+			`c: <3>{foo: 1 @bar() @baz(1) @foo()}, ` +
+			`e: _|_((<4>.a & <5>{foo: 1 @foo(other)}):conflicting attributes for key "foo")}`,
+	}, {
+		desc: "optional field unification",
+		in: `
+			a: { foo?: string }
+			b: { foo: "foo" }
+			c: a & b
+			d: a & { "foo"?: "bar" }
+
+			g1: 1
+			"g\(1)"?: 1
+			"g\(2)"?: 2
+		`,
+		out: `<0>{a: <1>{foo?: string}, ` +
+			`b: <2>{foo: "foo"}, ` +
+			`c: <3>{foo: "foo"}, ` +
+			`d: <4>{foo?: "bar"}, ` +
+			`g1: 1, ` +
+			`g2?: 2}`,
+	}, {
+		desc: "optional field resolves to incomplete",
+		in: `
+		r: {
+			a?: 3
+			b: a
+			c: r["a"]
+		}
+	`,
+		out: `<0>{r: <1>{a?: 3, b: <2>.a, c: <3>.r["a"]}}`,
+		// TODO(#152): should be
+		// out: `<0>{r: <1>{a?: 3, b: <2>.a, c: <2>["a"]}}`,
+	}, {
+		desc: "bounds",
+		in: `
+			i1: >1 & 5
+			i2: (>=0 & <=10) & 5
+			i3: !=null & []
+			i4: !=2 & !=4
+
+
+			s1: >=0 & <=10 & !=1        // no simplification
+			s2: >=0 & <=10 & !=11       // >=0 & <=10
+			s3: >5 & !=5                // >5
+			s4: <10 & !=10              // <10
+			s5: !=2 & !=2
+
+			// TODO: could change inequality
+			s6: !=2 & >=2
+			s7: >=2 & !=2
+
+			s8: !=5 & >5
+
+			s10: >=0 & <=10 & <12 & >1   // >1  & <=10
+			s11: >0 & >=0 & <=12 & <12   // >0  & <12
+
+			s20: >=10 & <=10             // 10
+
+			s22:  >5 & <=6               // no simplification
+			s22a: >5 & (<=6 & int)       // 6
+			s22b: (int & >5) & <=6       // 6
+			s22c: >=5 & (<6 & int)       // 5
+			s22d: (int & >=5) & <6       // 5
+			s22e: (>=5 & <6) & int       // 5
+			s22f: int & (>=5 & <6)       // 5
+
+			s23: >0 & <2                 // no simplification
+			s23a: (>0 & <2) & int        // int & 1
+			s23b: int & (>0 & <2)        // int & 1
+			s23c: (int & >0) & <2        // int & 1
+			s23d: >0 & (int & <2)        // int & 1
+			s23e: >0.0 & <2.0            // no simplification
+
+			s30: >0 & int
+
+			e1: null & !=null
+			e2: !=null & null
+			e3: >1 & 1
+			e4: <0 & 0
+			e5: >1 & <0
+			e6: >11 & <11
+			e7: >=11 & <11
+			e8: >11 & <=11
+			e9: >"a" & <1
+		`,
+		out: `<0>{i1: 5, i2: 5, i3: [], i4: (!=2 & !=4), ` +
+
+			`s1: (>=0 & <=10 & !=1), ` +
+			`s2: (>=0 & <=10), ` +
+			`s3: >5, ` +
+			`s4: <10, ` +
+			`s5: !=2, ` +
+
+			`s6: (!=2 & >=2), ` +
+			`s7: (>=2 & !=2), ` +
+
+			`s8: >5, ` +
+
+			`s10: (<=10 & >1), ` +
+			`s11: (>0 & <12), ` +
+
+			`s20: 10, ` +
+
+			`s22: (>5 & <=6), ` +
+			`s22a: 6, ` +
+			`s22b: 6, ` +
+			`s22c: 5, ` +
+			`s22d: 5, ` +
+			`s22e: 5, ` +
+			`s22f: 5, ` +
+
+			`s23: (>0 & <2), ` +
+			`s23a: 1, ` +
+			`s23b: 1, ` +
+			`s23c: 1, ` +
+			`s23d: 1, ` +
+			`s23e: (>0.0 & <2.0), ` +
+
+			`s30: int & >0, ` +
+
+			`e1: _|_((!=null & null):invalid value null (excluded by !=null)), ` +
+			`e2: _|_((!=null & null):invalid value null (excluded by !=null)), ` +
+			`e3: _|_((>1 & 1):invalid value 1 (out of bound >1)), ` +
+			`e4: _|_((<0 & 0):invalid value 0 (out of bound <0)), ` +
+			`e5: _|_(conflicting bounds >1 and <0), ` +
+			`e6: _|_(conflicting bounds >11 and <11), ` +
+			`e7: _|_(conflicting bounds >=11 and <11), ` +
+			`e8: _|_(conflicting bounds >11 and <=11), ` +
+			`e9: _|_((>"a" & <1):conflicting values >"a" and <1 (mismatched types string and number))}`,
+	}, {
+		desc: "bound conversions",
+		in: `
+		r0: int & >0.1 &  <=1.9
+		r1: int & >0.1 & <1.9
+		r2: int & >=0.1 & <1.9
+		r3: int & >=-1.9 & <=-0.1
+		r4: int & >-1.9 & <=-0.1
+
+		r5: >=1.1 & <=1.1
+		r6: r5 & 1.1
+
+		c1: (1.2 & >1.3) & <2
+		c2: 1.2 & (>1.3 & <2)
+
+		c3: 1.2 & (>=1 & <2)
+		c4: 1.2 & (>=1 & <2 & int)
+		`,
+		out: `<0>{` +
+			`r0: 1, ` +
+			`r1: 1, ` +
+			`r2: 1, ` +
+			`r3: -1, ` +
+			`r4: -1, ` +
+			`r5: 1.1, ` +
+			`r6: 1.1, ` +
+			`c1: _|_((>1.3 & 1.2):invalid value 1.2 (out of bound >1.3)), ` +
+			`c2: _|_((>1.3 & 1.2):invalid value 1.2 (out of bound >1.3)), ` +
+			`c3: 1.2, ` +
+			`c4: _|_((1.2 & ((>=1 & <2) & int)):conflicting values 1.2 and ((>=1 & <2) & int) (mismatched types float and int))}`,
+	}, {
+		desc: "custom validators",
+		in: `
+		import "strings"
+
+		a: strings.ContainsAny("ab")
+		a: "after"
+
+		b: strings.ContainsAny("c")
+		b: "dog"
+
+		c: strings.ContainsAny("d") & strings.ContainsAny("g")
+		c: "dog"
+		`,
+		out: `<0>{` +
+			`a: "after", ` +
+			`b: _|_(strings.ContainsAny ("c"):invalid value "dog" (does not satisfy strings.ContainsAny("c"))), ` +
+			`c: "dog"` +
+			`}`,
+	}, {
+		desc: "null coalescing",
+		in: `
+			a: null
+			b: a.x | "b"
+			c: a["x"] | "c"
+			`,
+		out: `<0>{a: null, b: "b", c: "c"}`,
+	}, {
+		desc: "reference across tuples and back",
+		// Tests that it is okay to partially evaluate structs.
+		in: `
+			a: { c: b.e, d: b.f }
+			b: { e: 3, f: a.c }
+			`,
+		out: "<0>{a: <1>{c: 3, d: 3}, b: <2>{e: 3, f: 3}}",
+	}, {
+		desc: "index",
+		in: `
+			a: [2][0]
+			b: {foo:"bar"}["foo"]
+			c: (*l|{"3":3})["3"]
+			d: (*[]|[1])[0]
+			l: []
+			e1: [2][""]
+			e2: 2[2]
+			e3: [][true]
+			e4: [1,2,3][3]
+			e5: [1,2,3][-1]
+			e6: (*[]|{})[1]
+			def: {
+				a: 1
+				#b: 3
+			}
+			e7: def["b"]
+		`,
+		out: `<0>{a: 2, b: "bar", c: _|_("3":invalid list index "3" (type string)), l: [], d: _|_([]:index 0 out of bounds), e1: _|_("":invalid list index "" (type string)), e2: _|_(2:invalid operation: 2[2] (type int does not support indexing)), e3: _|_(true:invalid list index true (type bool)), e4: _|_([1,2,3]:index 3 out of bounds), e5: _|_(-1:invalid list index -1 (index must be non-negative)), e6: _|_([]:index 1 out of bounds), def: <1>{a: 1, #b: 3}, e7: <2>.def["b"]}`,
+		// }, {
+		// NOTE: string indexing no longer supported.
+		// Keeping it around until this is no longer an experiment.
+		// 	desc: "string index",
+		// 	in: `
+		// 		a0: "abc"[0]
+		// 		a1: "abc"[1]
+		// 		a2: "abc"[2]
+		// 		a3: "abc"[3]
+		// 		a4: "abc"[-1]
+
+		// 		b: "zoëven"[2]
+		// 	`,
+		// 	out: `<0>{a0: "a", a1: "b", a2: "c", a3: _|_("abc":index 3 out of bounds), a4: _|_(-1:invalid string index -1 (index must be non-negative)), b: "ë"}`,
+	}, {
+		desc: "disjunctions of lists",
+		in: `
+			l: [ int, int ] | [ string, string ]
+
+			l1: [ "a", "b" ]
+			l2: l & [ "c", "d" ]
+			`,
+		out: `<0>{l: ([int,int] | [string,string]), l1: ["a","b"], l2: ["c","d"]}`,
+	}, {
+		desc: "slice",
+		in: `
+			a: [2][0:0]
+			b: [0][1:1]
+			e1: [][1:1]
+			e2: [0][-1:0]
+			e3: [0][1:0]
+			e4: [0][1:2]
+			e5: 4[1:2]
+			e6: [2]["":]
+			e7: [2][:"9"]
+
+		`,
+		out: `<0>{a: [], b: [], e1: _|_(1:slice bounds out of range), e2: _|_([0]:negative slice index), e3: _|_([0]:invalid slice index: 1 > 0), e4: _|_(2:slice bounds out of range), e5: _|_(4:cannot slice 4 (type int)), e6: _|_("":invalid slice index "" (type string)), e7: _|_("9":invalid slice index "9" (type string))}`,
+		// }, {
+		// NOTE: string indexing no longer supported.
+		// Keeping it around until this is no longer an experiment.
+		// 	desc: "string slice",
+		// 	in: `
+		// 		a0: ""[0:0]
+		// 		a1: ""[:]
+		// 		a2: ""[0:]
+		// 		a3: ""[:0]
+		// 		b0: "abc"[0:0]
+		// 		b1: "abc"[0:1]
+		// 		b2: "abc"[0:2]
+		// 		b3: "abc"[0:3]
+		// 		b4: "abc"[3:3]
+		// 		b5: "abc"[1:]
+		// 		b6: "abc"[:2]
+
+		// 		// TODO: supported extended graphemes, instead of just runes.
+		// 		u: "Spaß"[3:4]
+		// 	`,
+		// 	out: `<0>{a0: "", a1: "", a2: "", a3: "", b0: "", b1: "a", b2: "ab", b3: "abc", b4: "", b5: "bc", b6: "ab", u: "ß"}`,
+	}, {
+		desc: "list types",
+		in: `
+			l0: 3*[int]
+			l0: [1, 2, 3]
+			l2: [...{ a: int }]
+			l2: [{a: 1}, {a: 2, b: 3}]
+
+			// TODO: work out a decent way to specify length ranges of lists.
+			// l3: <=10*[int]
+			// l3: [1, 2, 3, ...]
+
+			s1: (6*[int])[2:3]
+			s2: [0,2,3][1:2]
+
+			i1: (6*[int])[2]
+			i2: [0,2,3][2]
+
+			t0: [...{a: 8}]
+			t0: [{}]
+			t1: [...]
+			t1: [...int]
+
+			e0: 2*[{}]
+			e0: [{}]
+			e1: [...int]
+			e1: [...float]
+			`,
+		out: `<0>{` +
+			`l0: [1,2,3], ` +
+			`l2: [<1>{a: 1},<2>{a: 2, b: 3}], ` +
+			`s1: [int], ` +
+			`s2: [2], ` +
+			`i1: int, ` +
+			`i2: 3, ` +
+			`t0: [<3>{a: 8}], ` +
+			`t1: [, ...int], ` +
+			`e0: _|_(([<4>{},<4>{}] & [<5>{}]):conflicting list lengths: conflicting values 2 and 1), ` +
+			`e1: [, ..._|_((int & float):conflicting values int and float (mismatched types int and float))]` +
+			`}`,
+	}, {
+		// TODO: consider removing list arithmetic altogether. It is no longer
+		// needed to indicate the allowed capacity of a list and that didn't
+		// work anyway.
+		desc: "list arithmetic",
+		in: `
+			l0: 3*[1, 2, 3]
+			l1: 0*[1, 2, 3]
+			l2: 10*[]
+			l3: <=2*[]
+			l4: <=2*[int]
+			l5: <=2*(int*[int])
+			l6: 3*[...int]
+			l7: 3*[1, ...int]
+			l8: 3*[1, 2, ...int]
+
+			s0: [] + []
+			s1: [1] + []
+			s2: [] + [2]
+			s3: [1] + [2]
+			s4: [1,2] + []
+			s5: [] + [1,2]
+			s6: [1] + [1,2]
+			s7: [1,2] + [1]
+			s8: [1,2] + [1,2]
+			s9: [] + [...]
+			s10: [1] + [...]
+			s11: [] + [2, ...]
+			s12: [1] + [2, ...]
+			s13: [1,2] + [...]
+			s14: [] + [1,2, ...]
+			s15: [1] + [1,2, ...]
+			s16: [1,2] + [1, ...]
+			s17: [1,2] + [1,2, ...]
+
+			s18: [...] + []
+			s19: [1, ...] + []
+			s20: [...] + [2]
+			s21: [1, ...] + [2]
+			s22: [1,2, ...] + []
+			s23: [...] + [1,2]
+			s24: [1, ...] + [1,2]
+			s25: [1,2, ...] + [1]
+			s26: [1,2, ...] + [1,2]
+			s27: [...] + [...]
+			s28: [1, ...] + [...]
+			s29: [...] + [2, ...]
+			s30: [1, ...] + [2, ...]
+			s31: [1,2, ...] + [...]
+			s32: [...] + [1,2, ...]
+			s33: [1, ...] + [1,2, ...]
+			s34: [1,2, ...] + [1, ...]
+			s35: [1,2, ...] + [1,2, ...]
+			`,
+		out: `<0>{l0: [1,2,3,1,2,3,1,2,3], ` +
+			`l1: [], ` +
+			`l2: [], ` +
+			`l3: (<=2 * []), ` +
+			`l4: (<=2 * [int]), ` +
+			`l5: (<=2 * (int * [int])), ` +
+			`l6: [], ` +
+			`l7: [1,1,1], ` +
+			`l8: [1,2,1,2,1,2], ` +
+
+			`s0: [], ` +
+			`s1: [1], ` +
+			`s2: [2], ` +
+			`s3: [1,2], ` +
+			`s4: [1,2], ` +
+			`s5: [1,2], ` +
+			`s6: [1,1,2], ` +
+			`s7: [1,2,1], ` +
+			`s8: [1,2,1,2], ` +
+			`s9: [], ` +
+			`s10: [1], ` +
+			`s11: [2], ` +
+			`s12: [1,2], ` +
+			`s13: [1,2], ` +
+			`s14: [1,2], ` +
+			`s15: [1,1,2], ` +
+			`s16: [1,2,1], ` +
+			`s17: [1,2,1,2], ` +
+
+			`s18: [], ` +
+			`s19: [1], ` +
+			`s20: [2], ` +
+			`s21: [1,2], ` +
+			`s22: [1,2], ` +
+			`s23: [1,2], ` +
+			`s24: [1,1,2], ` +
+			`s25: [1,2,1], ` +
+			`s26: [1,2,1,2], ` +
+			`s27: [], ` +
+			`s28: [1], ` +
+			`s29: [2], ` +
+			`s30: [1,2], ` +
+			`s31: [1,2], ` +
+			`s32: [1,2], ` +
+			`s33: [1,1,2], ` +
+			`s34: [1,2,1], ` +
+			`s35: [1,2,1,2]` +
+
+			`}`,
+	}, {
+		desc: "list equality",
+		in: `
+		eq0: [] == []
+		eq1: [...] == []
+		eq2: [] == [...]
+		eq3: [...] == [...]
+
+		eq4: [1] == [1]
+		eq5: [1, ...] == [1]
+		eq6: [1] == [1, ...]
+		eq7: [1, ...] == [1, ...]
+
+		eq8: [1, 2] == [1, 2]
+		eq9: [1, 2, ...] == [1, 2]
+		eq10: [1, 2] == [1, 2, ...]
+		eq11: [1, 2, ...] == [1, 2, ...]
+
+		ne0: [] != []
+		ne1: [...] != []
+		ne2: [] != [...]
+		ne3: [...] != [...]
+
+		ne4: [1] != [1]
+		ne5: [1, ...] != [1]
+		ne6: [1] != [1, ...]
+		ne7: [1, ...] != [1, ...]
+
+		ne8: [1, 2] != [1, 2]
+		ne9: [1, 2, ...] != [1, 2]
+		ne10: [1, 2] != [1, 2, ...]
+		ne11: [1, 2, ...] != [1, 2, ...]
+
+		feq0: [] == [1]
+		feq1: [...] == [1]
+		feq2: [] == [1, ...]
+		feq3: [...] == [1, ...]
+
+		feq4: [1] == []
+		feq5: [1, ...] == []
+		feq6: [1] == [...]
+		feq7: [1, ...] == [...]
+
+		feq8: [1, 2] == [1]
+		feq9: [1, ...] == [1, 2]
+		feq10: [1, 2] == [1, ...]
+		feq11: [1, ...] == [1, 2, ...]
+
+		fne0: [] != [1]
+		fne1: [...] != [1]
+		fne2: [] != [1, ...]
+		fne3: [1, ...] != [1, ...]
+
+		fne4: [1] != []
+		fne5: [1, ...] != []
+		fne6: [1] != [...]
+		fne7: [1, ...] != [...]
+
+		fne8: [1, 2] != [1]
+		fne9: [1, ...] != [1, 2]
+		fne10: [1, 2] != [1, ...]
+		fne11: [1, ...] != [1, 2, ...]
+		`,
+		out: `<0>{` +
+			`eq0: true, eq1: true, eq2: true, eq3: true, eq4: true, eq5: true, eq6: true, eq7: true, eq8: true, eq9: true, eq10: true, eq11: true, ` +
+			`ne0: true, ne1: true, ne2: true, ne3: true, ne4: false, ne5: false, ne6: false, ne7: false, ne8: false, ne9: false, ne10: false, ne11: false, ` +
+			`feq0: false, feq1: false, feq2: false, feq3: false, feq4: false, feq5: false, feq6: false, feq7: false, feq8: false, feq9: false, feq10: false, feq11: false, ` +
+			`fne0: false, fne1: false, fne2: false, fne3: false, fne4: false, fne5: false, fne6: false, fne7: false, fne8: false, fne9: false, fne10: false, fne11: false}`,
+	}, {
+		desc: "list unification",
+		in: `
+		a: { l: ["foo", v], v: l[1] }
+		b: a & { l: [_, "bar"] }
+		`,
+		out: `<0>{` +
+			`a: <1>{l: ["foo",<2>.v], ` +
+			`v: <2>.l[1]}, ` +
+			`b: <3>{l: ["foo","bar"], v: "bar"}}`,
+	}, {
+		desc: "correct error messages",
+		// Tests that it is okay to partially evaluate structs.
+		in: `
+			a: "a" & 1
+			`,
+		out: `<0>{a: _|_(("a" & 1):conflicting values "a" and 1 (mismatched types string and int))}`,
+	}, {
+		desc: "structs",
+		in: `
+			a: t & { c: 5 }             // {c:5,d:15}
+			b: ti & { c: 7 }            // {c:7,d:21}
+			t: { c: number, d: c * 3 }  // {c:number,d:number*3}
+			ti: t & { c: int }
+			`,
+		out: `<0>{a: <1>{c: 5, d: 15}, t: <2>{c: number, d: (<3>.c * 3)}, b: <4>{c: 7, d: 21}, ti: <5>{c: int, d: (<6>.c * 3)}}`,
+	}, {
+		desc: "definitions",
+		in: `
+			#Foo: {
+				field: int
+				recursive: {
+					field: string
+				}
+			}
+
+			// Allowed
+			#Foo1: { field: int }
+			#Foo1: { field2: string }
+
+			foo: #Foo
+			foo: { feild: 2 }
+
+			foo1: #Foo
+			foo1: {
+				field: 2
+				recursive: {
+					feild: 2 // Not caught as per spec. TODO: change?
+				}
+			}
+
+			#Bar: {
+				field: int
+				{[A=_]:   int}
+			}
+			bar: #Bar
+			bar: { feild: 2 }
+
+			#Mixed: string
+			Mixed: string
+
+			mixedRec: { #Mixed: string }
+			mixedRec: { Mixed: string }
+			`,
+		out: `<0>{` +
+			`#Foo: <1>C{field: int, recursive: <2>C{field: string}}, ` +
+			`#Foo1: <3>C{field: int, field2: string}, ` +
+			`foo: _|_(2:field "feild" not allowed in closed struct), ` +
+			`foo1: <4>C{field: 2, recursive: _|_(2:field "feild" not allowed in closed struct)}, ` +
+			`#Bar: <5>{[]: <6>(A: string)->int, field: int}, ` +
+			`bar: <7>{[]: <8>(A: string)->int, field: int, feild: 2}, ` +
+			`#Mixed: string, ` +
+			`Mixed: string, ` +
+			`mixedRec: <9>{#Mixed: string, Mixed: string}}`,
+	}, {
+		desc: "combined definitions",
+		in: `
+			// Allow combining of structs within a definition
+			#D1: {
+				env: a: "A"
+				env: b: "B"
+				#def: {a: "A"}
+				#def: {b: "B"}
+			}
+
+			d1: #D1 & { env: c: "C" }
+
+			#D2: {
+				a: int
+			}
+			#D2: {
+				b: int
+			}
+
+			#D3: {
+				env: a: "A"
+			}
+			#D3: {
+				env: b: "B"
+			}
+
+			#D4: {
+				env: #DC
+				env: b: int
+			}
+
+			#DC: { a: int }
+					`,
+		out: `<0>{` +
+			`#D1: <1>C{env: <2>C{a: "A", b: "B"}, #def: <3>C{a: "A", b: "B"}}, ` +
+			`d1: <4>C{env: _|_("C":field "c" not allowed in closed struct), #def: <5>C{a: "A", b: "B"}}, ` +
+			`#D2: <6>C{a: int, b: int}, ` +
+			`#D3: <7>C{env: <8>C{a: "A", b: "B"}}, ` +
+			`#D4: <9>C{env: _|_(int:field "b" not allowed in closed struct)}, ` +
+			`#DC: <10>C{a: int}` +
+			`}`,
+	}, {
+		desc: "new-style definitions",
+		in: `
+		#Foo: {
+			a: 1
+			b: int
+		}
+		"#Foo": #Foo & {b: 1}
+
+		bulk: {[string]: string} & {
+			#def: 4 // Different namespace, so bulk option does not apply.
+			_hid: 3
+			a: "foo"
+		}
+		`,
+		out: `<0>{` +
+			`"#Foo": <1>C{a: 1, b: 1}, ` +
+			`#Foo: <2>C{a: 1, b: int}, ` +
+			`bulk: <3>{[]: <4>(_: string)->string, a: "foo", #def: 4, _hid: 3}` +
+			`}`,
+	}, {
+		desc: "recursive closing starting at non-definition",
+		in: `
+			z: a: {
+				#B: {
+					c: d: 1
+					c: f: 1
+				}
+			}
+			A: z & { a: { #B: { c: e: 2 } } }
+			`,
+		out: `<0>{z: <1>{a: <2>{#B: <3>C{c: <4>C{d: 1, f: 1}}}}, A: <5>{a: <6>{#B: <7>C{c: _|_(2:field "e" not allowed in closed struct)}}}}`,
+	}, {
+		desc: "non-closed definition carries over closedness to enclosed template",
+		in: `
+		#S: {
+			[string]: { a: int }
+		}
+		a: #S & {
+			v: { b: int }
+		}
+		#Q: {
+			[string]: { a: int } | { b: int }
+		}
+		b: #Q & {
+			w: { c: int }
+		}
+		#R: {
+			[string]: [{ a: int }, { b: int }]
+		}
+		c: #R & {
+			w: [{ d: int }, ...]
+		}
+		`,
+		out: `<0>{` +
+			`#S: <1>{[]: <2>(_: string)-><3>C{a: int}, }, ` +
+			`a: <4>{[]: <5>(_: string)-><6>C{a: int}, v: _|_(int:field "b" not allowed in closed struct)}, ` +
+			`b: <7>{[]: <8>(_: string)->(<9>C{a: int} | <10>C{b: int}), w: _|_(int:empty disjunction: field "c" not allowed in closed struct)}, ` +
+			`#Q: <11>{[]: <12>(_: string)->(<13>C{a: int} | <14>C{b: int}), }, ` +
+			`c: <15>{[]: <16>(_: string)->[<17>C{a: int},<18>C{b: int}], w: [_|_(int:field "d" not allowed in closed struct),<19>C{b: int}]}, ` +
+			`#R: <20>{[]: <21>(_: string)->[<22>C{a: int},<23>C{b: int}], }}`,
+	}, {
+		desc: "definitions with disjunctions",
+		in: `
+			#Foo: {
+				field: int
+
+				{ a: 1 } |
+				{ b: 2 }
+			}
+
+			foo: #Foo
+			foo: { a: 1 }
+
+			bar: #Foo
+			bar: { c: 2 }
+
+			baz: #Foo
+			baz: { b: 2 }
+			`,
+		out: `<0>{` +
+			`#Foo: (<1>C{field: int, a: 1} | <2>C{field: int, b: 2}), ` +
+			`foo: <3>C{field: int, a: 1}, ` +
+			`bar: _|_(2:empty disjunction: field "c" not allowed in closed struct), ` +
+			`baz: <4>C{field: int, b: 2}}`,
+	}, {
+		desc: "definitions with disjunctions recurisive",
+		in: `
+			#Foo: {
+				x: {
+					field: int
+
+					{ a: 1 } |
+					{ b: 2 }
+				}
+				x: c: 3
+			}
+					`,
+		out: `<0>{` +
+			`#Foo: <1>C{x: (<2>C{field: int, a: 1, c: 3} | <3>C{field: int, b: 2, c: 3})}` +
+			`}`,
+	}, {
+		desc: "definitions with embedding",
+		in: `
+		#E: {
+			a: { b: int }
+		}
+
+		#S: {
+			#E
+			a: { c: int }
+			b: 3
+		}
+
+		// adding a field to a nested struct that is closed.
+		#e1: #S & { a: d: 4 }
+		// literal struct not closed until after unification.
+		#v1: #S & { a: c: 4 }
+		`,
+		out: `<0>{` +
+			`#E: <1>C{a: <2>C{b: int}}, ` +
+			`#S: <3>C{a: <4>C{b: int, c: int}, b: 3}, ` +
+			`#e1: <5>C{a: _|_(4:field "d" not allowed in closed struct), b: 3}, ` +
+			`#v1: <6>C{a: <7>C{b: int, c: 4}, b: 3}}`,
+	}, {
+		desc: "top-level definition with struct and disjunction",
+		in: `
+		#def: {
+			Type: string
+			Text: string
+			Size: int
+		}
+
+		#def: {
+			Type: "B"
+			Size: 0
+		} | {
+			Type: "A"
+			Size: 1
+		}`,
+		out: `<0>{` +
+			`#def: (<1>C{Size: (0 & int), Type: ("B" & string), Text: string} | ` +
+			`<2>C{Size: (1 & int), Type: ("A" & string), Text: string})` +
+			`}`,
+	}, {
+		desc: "closing structs",
+		in: `
+		op: {x: int}             // {x: int}
+		ot: {x: int, ...}        // {x: int, ...}
+		cp: close({x: int})      // closed({x: int})
+		ct: close({x: int, ...}) // {x: int, ...}
+
+		opot: op & ot  // {x: int, ...}
+		otop: ot & op  // {x: int, ...}
+		opcp: op & cp  // closed({x: int})
+		cpop: cp & op  // closed({x: int})
+		opct: op & ct  // {x: int, ...}
+		ctop: ct & op  // {x: int, ...}
+		otcp: ot & cp  // closed({x: int})
+		cpot: cp & ot  // closed({x: int})
+		otct: ot & ct  // {x: int, ...}
+		ctot: ct & ot  // {x: int, ...}
+		cpct: cp & ct  // closed({x: int})
+		ctcp: ct & cp  // closed({x: int})
+		ctct: ct & ct  // {x: int, ...}
+		`,
+		out: `<0>{` +
+			`op: <1>{x: int}, ` +
+			`ot: <2>{x: int, ...}, ` +
+			`cp: <3>C{x: int}, ` +
+			`ct: <4>{x: int, ...}, ` +
+			`opot: <5>{x: int, ...}, ` +
+			`otop: <6>{x: int, ...}, ` +
+			`opcp: <7>C{x: int}, ` +
+			`cpop: <8>C{x: int}, ` +
+			`opct: <9>{x: int, ...}, ` +
+			`ctop: <10>{x: int, ...}, ` +
+			`otcp: <11>C{x: int}, ` +
+			`cpot: <12>C{x: int}, ` +
+			`otct: <13>{x: int, ...}, ` +
+			`ctot: <14>{x: int, ...}, ` +
+			`cpct: <15>C{x: int}, ` +
+			`ctcp: <16>C{x: int}, ` +
+			`ctct: <17>{x: int, ...}}`,
+	}, {
+		desc: "excluded embedding from closing",
+		in: `
+		#S: {
+			a: { c: int }
+			{
+				c: { d: int }
+			}
+			B = { open: int }
+			b: B
+		}
+		V: #S & {
+			c: e: int
+			b: extra: int
+		}
+		`,
+		out: `<0>{` +
+			`#S: <1>C{` +
+			`a: <2>C{c: int}, ` +
+			`c: <3>{d: int}, ` +
+			`b: <4>{open: int}}, ` +
+			`V: <5>C{` +
+			`a: <6>C{c: int}, ` +
+			`c: <7>{d: int, e: int}, ` +
+			`b: <8>{open: int, extra: int}}}`,
+	}, {
+		desc: "closing with failed optional",
+		in: `
+		#k1: {a: int, b?: int} & #A // closed({a: int})
+		#k2: #A & {a: int, b?: int} // closed({a: int})
+
+		o1: {a?: 3} & {a?: 4} // {a?: _|_}
+
+		// Optional fields with error values can be elimintated when closing
+		#o2: {a?: 3} & {a?: 4} // close({})
+
+		#d1: {a?: 2, b: 4} | {a?: 3, c: 5}
+		v1: #d1 & {a?: 3, b: 4}  // close({b: 4})
+
+		#A: {a: int}
+		`,
+		out: `<0>{` +
+			`#k1: <1>C{a: int}, ` +
+			`#A: <2>C{a: int}, ` +
+			`#k2: <3>C{a: int}, ` +
+			`o1: <4>{a?: _|_((3 & 4):conflicting values 3 and 4)}, ` +
+			`#o2: <5>C{a?: _|_((3 & 4):conflicting values 3 and 4)}, ` +
+			`#d1: (<6>C{a?: 2, b: 4} | <7>C{a?: 3, c: 5}), ` +
+			`v1: <8>C{a?: _|_((2 & 3):conflicting values 2 and 3), b: 4}` +
+			`}`,
+	}, {
+		desc: "closing with comprehensions",
+		in: `
+		#A: {f1: int, f2: int}
+
+		for k, v in {f3 : int} {
+			a: #A & { "\(k)": v }
+		}
+
+		#B: {
+			for k, v in {f1: int} {
+				"\(k)": v
+			}
+		}
+
+		#C: {
+			f1: _
+			for k, v in {f1: int} {
+				"\(k)": v
+			}
+		}
+
+		#D: {
+			for k, v in {f1: int} {
+				"\(k)": v
+			}
+			...
+		}
+
+		#E: #A & {
+			for k, v in { f3: int } {
+				"\(k)": v
+			}
+		}
+		`,
+		out: `<0>{` +
+			`#A: <1>C{f1: int, f2: int}, ` +
+			`a: _|_(<2>.v:field "f3" not allowed in closed struct), ` +
+			`#B: <3>C{f1: int}, ` +
+			`#C: <4>C{f1: int}, ` +
+			`#D: <5>{f1: int, ...}, ` +
+			`#E: _|_(<6>.v:field "f3" not allowed in closed struct)` +
+			`}`,
+	}, {
+		desc: "incomplete comprehensions",
+		in: `
+		A: {
+			for v in src {
+				"\(v)": v
+			}
+			src: _
+			if true {
+				baz: "baz"
+			}
+		}
+		B: A & {
+			src: ["foo", "bar"]
+		}
+		`,
+		out: `<0>{` +
+			`A: <1>{src: _, baz: "baz" <2>for _, v in <3>.src yield <4>{""+<2>.v+"": <2>.v}}, ` +
+			`B: <5>{src: ["foo","bar"], baz: "baz", foo: "foo", bar: "bar"}}`,
+	}, {
+		desc: "reference to root",
+		in: `
+			a: { b: int }
+			c: a & {
+				b: 100
+				d: a.b + 3 // do not resolve as c != a.
+			}
+			x: {
+				b: int
+				c: b + 5
+			}
+			y: x & {
+				b: 100
+				// c should resolve to 105
+			}
+			v: {
+				b: int
+				c: v.b + 5 // reference starting from copied node.
+			}
+			w: v & { b: 100 }
+			wp: v & { b: 100 }
+			`,
+		out: `<0>{x: <1>{b: int, c: (<2>.b + 5)}, y: <3>{b: 100, c: 105}, a: <4>{b: int}, c: <5>{b: 100, d: (<6>.a.b + 3)}, v: <7>{b: int, c: (<6>.v.b + 5)}, w: <8>{b: 100, c: (<6>.v.b + 5)}, wp: <9>{b: 100, c: (<6>.v.b + 5)}}`,
+		// TODO(#152): should be
+		// out: `<0>{a: <1>{b: int}, c: <2>{b: 100, d: (<3>.a.b + 3)}, x: <4>{b: int, c: (<5>.b + 5)}, y: <6>{b: 100, c: 105}, v: <7>{b: int, c: (<8>.b + 5)}, w: <9>{b: 100, c: 105}, wp: <10>{b: 100, c: 105}}`,
+	}, {
+		desc: "references from template to concrete",
+		in: `
+			res: [t]
+			t: [X=string]: {
+				a: c + b.str
+				b: str: string
+				c: "X"
+			}
+			t: x: { b: str: "DDDD" }
+			`,
+		out: `<0>{res: [<1>{[]: <2>(X: string)-><3>{a: (<3>.c + <3>.b.str), c: "X", b: <4>{str: string}}, x: <5>{a: "XDDDD", c: "X", b: <6>{str: "DDDD"}}}], t: <7>{[]: <2>(X: string)-><3>{a: (<3>.c + <3>.b.str), c: "X", b: <4>{str: string}}, x: <8>{a: "XDDDD", c: "X", b: <9>{str: "DDDD"}}}}`,
+	}, {
+		desc: "interpolation",
+		in: `
+			a: "\(4)"
+			b: "one \(a) two \(  a + c  )"
+			c: "one"
+			d: "\(r)"
+			u: "\(_)"
+			r: _
+			e: "\([])"`,
+		out: `<0>{a: "4", b: "one 4 two 4one", c: "one", d: ""+<1>.r+"", r: _, u: ""+_+"", e: _|_([]:expression in interpolation must evaluate to a number kind or string (found list))}`,
+	}, {
+		desc: "multiline interpolation",
+		in: `
+			a1: """
+			before
+			\(4)
+			after
+			"""
+			a2: """
+			before
+			\(4)
+
+			"""
+			a3: """
+
+			\(4)
+			after
+			"""
+			a4: """
+
+			\(4)
+
+			"""
+			m1: """
+			before
+			\(
+				4)
+			after
+			"""
+			m2: """
+			before
+			\(
+	4)
+
+			"""
+			m3: """
+
+			\(
+
+				4)
+			after
+			"""
+			m4: """
+
+			\(
+	4)
+
+			"""
+			`,
+		out: `<0>{` +
+			`a1: "before\n4\nafter", a2: "before\n4\n", a3: "\n4\nafter", a4: "\n4\n", ` +
+			`m1: "before\n4\nafter", m2: "before\n4\n", m3: "\n4\nafter", m4: "\n4\n"` +
+			`}`,
+	}, {
+		desc: "diamond-shaped constraints",
+		in: `
+		S: {
+			A: {
+				a: 1,
+			},
+			B: A & {
+				b: 2,
+			}
+		},
+		T: S & { // S == { A: { a:1 }, B: { a:1, b:2 } }
+			A: {
+				c: 3,
+			},
+			B: { // S.B & A
+				d: 4, // Combines constraints S.A, S.B, T.A, and T.B
+			}
+		}`,
+		out: "<0>{T: <1>{A: <2>{a: 1, c: 3}, B: <3>{a: 1, b: 2, c: 3, d: 4}}, S: <4>{A: <5>{a: 1}, B: <6>{a: 1, b: 2}}}",
+	}, {
+		desc: "field templates",
+		in: `
+			a: {
+				{[name=_]: int}
+				k: 1
+			}
+			b: {
+				{[X=_]: { x: 0, y: *1 | int }}
+				v: {}
+				w: { x: 0 }
+			}
+			b: { [y=_]: {} }
+			c: {
+				{[Name=_]: { name: Name, y: 1 }}
+				foo: {}
+				bar: _
+			}
+			`,
+		out: `<0>{a: <1>{[]: <2>(name: string)->int, k: 1}, b: <3>{[]: <4>(X: string)->(<5>{x: 0, y: (*1 | int)} & <6>{}), v: <7>{x: 0, y: (*1 | int)}, w: <8>{x: 0, y: (*1 | int)}}, c: <9>{[]: <10>(Name: string)-><11>{y: 1, name: <10>.Name}, foo: <12>{y: 1, name: "foo"}, bar: <13>{y: 1, name: "bar"}}}`,
+	}, {
+		desc: "range unification",
+		in: `
+			// with concrete values
+			a1: >=1 & <=5 & 3
+			a2: >=1 & <=5 & 1
+			a3: >=1 & <=5 & 5
+			a4: >=1 & <=5 & 6
+			a5: >=1 & <=5 & 0
+
+			a6: 3 & >=1 & <=5
+			a7: 1 & >=1 & <=5
+			a8: 5 & >=1 & <=5
+			a9: 6 & >=1 & <=5
+			a10: 0 & >=1 & <=5
+
+			// with ranges
+			b1: >=1 & <=5 & >=1 & <=5
+			b2: >=1 & <=5 & >=1 & <=1
+			b3: >=1 & <=5 & >=5 & <=5
+			b4: >=1 & <=5 & >=2 & <=3
+			b5: >=1 & <=5 & >=3 & <=9
+			b6: >=1 & <=5 & >=5 & <=9
+			b7: >=1 & <=5 & >=6 & <=9
+
+			b8: >=1 & <=5 & >=1 & <=5
+			b9: >=1 & <=1 & >=1 & <=5
+			b10: >=5 & <=5 & >=1 & <=5
+			b11: >=2 & <=3 & >=1 & <=5
+			b12: >=3 & <=9 & >=1 & <=5
+			b13: >=5 & <=9 & >=1 & <=5
+			b14: >=6 & <=9 & >=1 & <=5
+
+			// ranges with more general types
+			c1: int & >=1 & <=5
+			c2: >=1 & <=5 & int
+			c3: string & >=1 & <=5
+			c4: >=1 & <=5 & string
+
+			// other types
+			s1: >="d" & <="z" & "e"
+			s2: >="d" & <="z" & "ee"
+
+			n1: number & >=1 & <=2
+			n2: int & >=1.1 & <=1.3
+			n3: >=1.0 & <=3.0 & 2
+			n4: >=0.0 & <=0.1 & 0.09999
+			n5: >=1 & <=5 & 2.5
+			`,
+		out: `<0>{` +
+			`a1: 3, ` +
+			`a2: 1, ` +
+			`a3: 5, ` +
+			`a4: _|_((<=5 & 6):invalid value 6 (out of bound <=5)), ` +
+			`a5: _|_((>=1 & 0):invalid value 0 (out of bound >=1)), ` +
+			`a6: 3, ` +
+			`a7: 1, ` +
+			`a8: 5, ` +
+
+			`a9: _|_((<=5 & 6):invalid value 6 (out of bound <=5)), ` +
+			`a10: _|_((>=1 & 0):invalid value 0 (out of bound >=1)), ` +
+
+			`b1: (>=1 & <=5), ` +
+			`b2: 1, ` +
+			`b3: 5, ` +
+			`b4: (>=2 & <=3), ` +
+			`b5: (>=3 & <=5), ` +
+			`b6: 5, ` +
+			`b7: _|_(conflicting bounds >=6 and <=5), ` +
+			`b8: (>=1 & <=5), ` +
+			`b9: 1, ` +
+			`b10: 5, ` +
+			`b11: (>=2 & <=3), ` +
+			`b12: (>=3 & <=5), ` +
+			`b13: 5, ` +
+			`b14: _|_(conflicting bounds >=6 and <=5), ` +
+			`c1: (int & >=1 & <=5), ` +
+			`c2: (<=5 & int & >=1), ` +
+			`c3: _|_((string & >=1):conflicting values string and >=1 (mismatched types string and number)), ` +
+			`c4: _|_(((>=1 & <=5) & string):conflicting values (>=1 & <=5) and string (mismatched types number and string)), ` +
+			`s1: "e", ` +
+			`s2: "ee", ` +
+			`n1: (>=1 & <=2), ` +
+			`n2: _|_(conflicting bounds int & >=1.1 and <=1.3), ` +
+			`n3: 2, ` +
+			`n4: 0.09999, ` +
+			`n5: 2.5}`,
+	}, {
+		desc: "predefined ranges",
+		in: `
+			k1: int8
+			k1: 44
+
+			k2: int64
+			k2: -8_000_000_000
+
+			e1: int16
+			e1: 100_000
+		`,
+		out: `<0>{k1: 44, k2: -8000000000, ` +
+			`e1: _|_((int & <=32767 & 100000):invalid value 100000 (out of bound int & <=32767))}`,
+	}, {
+		desc: "struct comprehensions",
+		in: `
+			obj: foo: a: "bar"
+			obj: [Name=string]: {
+				a: *"dummy" | string
+				if true {
+					sub: as: a
+				}
+			}
+
+			for k, v in { #def: 1, opt?: 2, _hid: 3, reg: 4 } {
+				"\(k)": v
+			}
+		`,
+		out: `<0>{obj: <1>{[]: <2>(Name: string)-><3>{a: (*"dummy" | string) if true yield <4>{sub: <5>{as: <3>.a}}}, foo: <6>{a: "bar", sub: <7>{as: "bar"}}}, reg: 4}`,
+	}, {
+		desc: "builtins",
+		in: `
+		a1: {
+			a: and([b, c])
+			b: =~"oo"
+			c: =~"fo"
+		}
+		a2: a1 & { a: "foo" }
+		a3: a1 & { a: "bar" }
+
+		o1: {
+			a: or([b, c])
+			b: string
+			c: "bar"
+		}
+		o2: o1 & { a: "foo" }
+		o3: o1 & { a: "foo", b: "baz" }
+		`,
+		out: `<0>{` +
+			`a1: <1>{a: (=~"oo" & =~"fo"), b: =~"oo", c: =~"fo"}, ` +
+			`a2: <2>{a: "foo", b: =~"oo", c: =~"fo"}, ` +
+			`a3: <3>{a: _|_((=~"oo" & "bar"):invalid value "bar" (does not match =~"oo")), b: =~"oo", c: =~"fo"}, ` +
+			`o1: <4>{a: string, b: string, c: "bar"}, ` +
+			`o2: <5>{a: "foo", b: string, c: "bar"}, ` +
+			`o3: <6>{a: _|_(("baz" & "foo"):empty disjunction: conflicting values "baz" and "foo";("bar" & "foo"):empty disjunction: conflicting values "bar" and "foo"), b: "baz", c: "bar"}}`,
+	}, {
+		desc: "self-reference cycles conflicts with strings",
+		in: `
+			a: {
+				x: y+"?"
+				y: x+"!"
+			}
+			a: x: "hey"
+		`,
+		out: `<0>{a: <1>{x: _|_(("hey!?" & "hey"):conflicting values "hey!?" and "hey"), y: "hey!"}}`,
+	}, {
+		desc: "resolved self-reference cycles with disjunctions",
+		in: `
+			a: b&{x:1} | {y:1}  // {x:1,y:3,z:2} | {y:1}
+			b: {x:2} | c&{z:2}  // {x:2} | {x:1,y:3,z:2}
+			c: a&{y:3} | {z:3}  // {x:1,y:3,z:2} | {z:3}
+		`,
+		out: `<0>{a: (<1>{x: 1, y: 3, z: 2} | <2>{y: 1}), b: (<3>{x: 2} | <4>{x: 1, y: 3, z: 2}), c: (<5>{x: 1, y: 3, z: 2} | <6>{z: 3})}`,
+	}, {
+		// We take a very conservative stance on delaying arithmetic
+		// expressions within disjunctions. It should remain resolvable, though,
+		// once the user specifies one.
+		desc: "resolved self-reference cycles with disjunction",
+		in: `
+			// The second disjunct in xa1 is not resolvable and can be
+			// eliminated:
+			//   xa4 & 9
+			//   (xa2 + 2) & 9
+			//   ((xa3 + 2) + 2) & 9
+			//   (((6 & xa1-2) + 2) + 2) & 9
+			//   ((6 + 2) + 2) & 9 // 6 == xa1-2
+			//   10 & 9 => _|_
+			// The remaining values resolve.
+			xa1: (xa2 & 8) | (xa4 & 9)
+			xa2: xa3 + 2
+			xa3: 6 & xa1-2
+			xa4: xa2 + 2
+
+			// The second disjunct in xb4 can be eliminated as both disjuncts
+			// of xb3 result in an incompatible sum when substituted.
+			xb1: (xb2 & 8) | (xb4 & 9)
+			xb2: xb3 + 2
+			xb3: (6 & (xb1-2)) | (xb4 & 9)
+			xb4: xb2 + 2
+
+			// Another variant with more disjunctions. xc1 remains with two
+			// possibilities. Technically, only the first value is valid.
+			// However, to fully determine that, all options of the remaining
+			// disjunction will have to be evaluated algebraically, which is
+			// not done.
+			xc1: xc2 & 8 | xc4 & 9 | xc5 & 9
+			xc2: xc3 + 2
+			xc3: 6 & xc1-2
+			xc4: xc2 + 1
+			xc5: xc2 + 2
+
+			// The above is resolved by setting xd1 explicitly.
+			xd1: xd2 & 8 | xd4 & 9 | xd5 & 9
+			xd2: xd3 + 2
+			xd3: 6 & xd1-2
+			xd4: xd2 + 1
+			xd5: xd2 + 2
+			xd1: 8
+
+			// The above is resolved by setting xd1 explicitly to the wrong
+			// value, resulting in an error.
+			xe1: xe2 & 8 | xe4 & 9 | xe5 & 9
+			xe2: xe3 + 2
+			xe3: 6 & xe1-2
+			xe4: xe2 + 1
+			xe5: xe2 + 2
+			xe1: 9
+
+			// Only one solution.
+			xf1: xf2 & 8 | xf4 & 9
+			xf2: xf3 + 2
+			xf3: 6 & xf1-2 | xf4 & 9
+			xf4: xf2 + 2
+
+			z1: z2 + 1 | z3 + 5
+			z2: z3 + 2
+			z3: z1 - 3
+			z3: 8
+		`,
+		out: `<0>{` +
+			`xa1: 8, ` +
+			`xa2: 8, ` +
+			`xa4: 10, ` +
+			`xa3: 6, ` +
+
+			`xb1: 8, ` +
+			`xb2: 8, ` +
+			`xb4: 10, ` +
+			`xb3: 6, ` +
+
+			`xc1: ((<1>.xc2 & 8) | (<1>.xc4 & 9) | (<1>.xc5 & 9)), ` +
+			`xc2: (<1>.xc3 + 2), ` +
+			`xc4: (<1>.xc2 + 1), ` +
+			`xc5: (<1>.xc2 + 2), ` +
+			`xc3: (6 & (<1>.xc1 - 2)), ` +
+
+			`xd1: 8, ` +
+			`xd2: 8, ` +
+			`xd4: 9, ` +
+			`xd5: 10, ` +
+			`xd3: 6, ` +
+
+			`xe1: _|_((6 & 7):conflicting values 6 and 7), ` +
+			`xe2: _|_((6 & 7):conflicting values 6 and 7), ` +
+			`xe4: _|_((6 & 7):conflicting values 6 and 7), ` +
+			`xe5: _|_((6 & 7):conflicting values 6 and 7), ` +
+			`xe3: _|_((6 & 7):conflicting values 6 and 7), ` +
+
+			`xf1: 8, ` +
+			`xf2: 8, ` +
+			`xf4: 10, ` +
+			`xf3: 6, ` +
+
+			`z1: ((<1>.z2 + 1) | (<1>.z3 + 5)), ` +
+			`z2: (<1>.z3 + 2), ` +
+			`z3: ((<1>.z1 - 3) & 8)}`,
+	}, {
+		// Defaults should not alter the result of the above disjunctions.
+		// The results may differ, but errors and resolution should be roughly
+		// the same.
+		desc: "resolved self-reference cycles with disjunction with defaults",
+		in: `
+			// The disjunction in xa could be resolved, but as disjunctions
+			// are not resolved for expression, it remains unresolved.
+			xa1: (xa2 & 8) | *(xa4 & 9)
+			xa2: xa3 + 2
+			xa3: 6 & xa1-2
+			xa4: xa2 + 2
+
+			// As xb3 is a disjunction, xb2 cannot be resolved and evaluating
+			// the cycle completely is broken. However, it is not an error
+			// as the user might still resolve the disjunction.
+			xb1: *(xb2 & 8) | (xb4 & 9)
+			xb2: xb3 + 2
+			xb3: *(6 & (xb1-2)) | (xb4 & 9)
+			xb4: xb2 + 2
+
+			// Another variant with more disjunctions. xc1 remains with two
+			// possibilities. Technically, only the first value is valid.
+			// However, to fully determine that, all options of the remaining
+			// disjunction will have to be evaluated algebraically, which is
+			// not done.
+			xc1: *(xc2 & 8) | (xc4 & 9) | (xc5 & 9)
+			xc2: xc3 + 2
+			xc3: 6 & xc1-2
+			xc4: xc2 + 1
+			xc5: xc2 + 2
+
+			// The above is resolved by setting xd1 explicitly.
+			xd1: *(xd2 & 8) | xd4 & 9 | xd5 & 9
+			xd2: xd3 + 2
+			xd3: 6 & xd1-2
+			xd4: xd2 + 1
+			xd5: xd2 + 2
+
+			// The above is resolved by setting xd1 explicitly to the wrong
+			// value, resulting in an error.
+			xe1: *(xe2 & 8) | xe4 & 9 | xe5 & 9
+			xe2: xe3 + 2
+			xe3: 6 & xe1-2
+			xe4: xe2 + 1
+			xe5: xe2 + 2
+			xe1: 9
+
+			z1: *(z2 + 1) | z3 + 5
+			z2: z3 + 2
+			z3: z1 - 3
+			z3: 8
+		`,
+		out: `<0>{` +
+			`xa1: 8, ` +
+			`xa2: 8, ` +
+			`xa4: 10, ` +
+			`xa3: 6, ` +
+
+			`xb1: 8, ` +
+			`xb2: 8, ` +
+			`xb4: 10, ` +
+			`xb3: 6, ` +
+
+			`xc1: (*8 | 9), ` + // not resolved because we use evalPartial
+			`xc2: 8, ` +
+			`xc4: 9, ` +
+			`xc5: 10, ` +
+			`xc3: 6, ` +
+
+			`xd1: (*8 | 9), ` + // TODO: eliminate 9?
+			`xd2: 8, ` +
+			`xd4: 9, ` +
+			`xd5: 10, ` +
+			`xd3: 6, ` +
+
+			`xe1: _|_((6 & 7):conflicting values 6 and 7), ` +
+			`xe2: _|_((6 & 7):conflicting values 6 and 7), ` +
+			`xe4: _|_((6 & 7):conflicting values 6 and 7), ` +
+			`xe5: _|_((6 & 7):conflicting values 6 and 7), ` +
+			`xe3: _|_((6 & 7):conflicting values 6 and 7), ` +
+
+			`z1: (*11 | 13), ` + // 13 is eliminated with evalFull
+			`z2: 10, ` +
+			`z3: 8}`,
+	}}
+	rewriteHelper(t, testCases, evalPartial)
+}
+
+func TestFullEval(t *testing.T) {
+	testCases := []testCase{{
+		desc: "detect conflicting value",
+		in: `
+				a: 8000.9
+				a: 7080 | int`,
+		out: `<0>{a: _|_((8000.9 & (int | int)):conflicting values 8000.9 and int (mismatched types float and int))}`, // TODO: fix repetition
+	}, {
+		desc: "conflicts in optional fields are okay ",
+		in: `
+			d: {a: 1, b?: 3} | {a: 2}
+
+			// the following conjunction should not eliminate any disjuncts
+			c: d & {b?:4}
+		`,
+		out: `<0>{d: (<1>{a: 1, b?: 3} | <2>{a: 2}), c: (<3>{a: 1, b?: (3 & 4)} | <4>{a: 2, b?: 4})}`,
+	}, {
+		desc: "resolve all disjunctions",
+		in: `
+			service: [Name=string]: {
+				name: string | *Name
+				port: int | *7080
+			}
+			service: foo: _
+			service: bar: { port: 8000 }
+			service: baz: { name: "foobar" }
+			`,
+		out: `<0>{service: <1>{[]: <2>(Name: string)-><3>{name: (string | *<2>.Name), port: (int | *7080)}, foo: <4>{name: "foo", port: 7080}, bar: <5>{name: "bar", port: 8000}, baz: <6>{name: "foobar", port: 7080}}}`,
+	}, {
+		desc: "field templates",
+		in: `
+			a: {
+				[name=_]: int
+				k: 1
+			}
+			b: {
+				[X=_]: { x: 0, y: *1 | int }
+				v: {}
+				w: { y: 0 }
+			}
+			b: { [y=_]: {} } // TODO: allow different name
+			c: {
+				[Name=_]: { name: Name, y: 1 }
+				foo: {}
+				bar: _
+			}
+			`,
+		out: `<0>{a: <1>{[]: <2>(name: string)->int, k: 1}, b: <3>{[]: <4>(X: string)->(<5>{x: 0, y: (*1 | int)} & <6>{}), v: <7>{x: 0, y: 1}, w: <8>{x: 0, y: 0}}, c: <9>{[]: <10>(Name: string)-><11>{y: 1, name: <10>.Name}, foo: <12>{y: 1, name: "foo"}, bar: <13>{y: 1, name: "bar"}}}`,
+	}, {
+		desc: "field comprehension",
+		in: `
+			a: {
+				for k, v in b
+				if k < "d"
+				if v > b.a {
+					"\(k)": v
+				}
+			}
+			b: {
+				a: 1
+				b: 2
+				c: 3
+				d: 4
+			}
+			c: {
+				for k, v in b
+				if k < "d"
+				if v > b.a {
+					"\(k)": v
+				}
+			}
+			`,
+		out: `<0>{a: <1>{b: 2, c: 3}, b: <2>{a: 1, b: 2, c: 3, d: 4}, c: <3>{b: 2, c: 3}}`,
+	}, {
+		desc: "conditional field",
+		in: `
+			if b {
+				a: "foo"
+			}
+			b: true
+			c: {
+				a: 3
+				if a > 1 {
+					a: 3
+				}
+			}
+			d: {
+				a: int
+				if a > 1 {
+					a: 3
+				}
+			}
+		`,
+		// NOTE: the node numbers are not correct here, but this is an artifact
+		// of the testing code.
+		out: `<0>{b: true, a: "foo", c: <1>{a: 3}, d: <2>{a: int if (<3>.a > 1) yield <4>{a: 3}}}`,
+	}, {
+		desc: "referencing field in field comprehension",
+		in: `
+		a: { b: c: 4 }
+		a: {
+			b: d: 5
+			for k, v in b {
+				"\(k)": v
+			}
+		}
+		`,
+		out: `<0>{a: <1>{b: <2>{c: 4, d: 5}, c: 4, d: 5}}`,
+	}, {
+		desc: "different labels for templates",
+		in: `
+		a: [X=string]: { name: X }
+		a: [Name=string]: { name: Name }
+		a: foo: {}
+		`,
+		out: `<0>{a: <1>{[]: <2>(X: string)->(<3>{name: <2>.X} & <4>{name: <2>.X}), foo: <5>{name: "foo"}}}`,
+	}, {
+		// TODO: rename EE and FF to E and F to check correct ordering.
+
+		desc: "nested templates in one field",
+		in: `
+			a: [A=string]: b: [B=string]: {
+				name: A
+				kind: B
+			}
+			a: "A": b: "B": _
+			a: "C": b: "D": _
+			a: "EE": b: "FF": { c: "bar" }
+		`,
+		out: `<0>{a: <1>{[]: <2>(A: string)-><3>{b: <4>{[]: <5>(B: string)-><6>{name: <2>.A, kind: <5>.B}, }}, ` +
+			`A: <7>{b: <8>{[]: <9>(B: string)-><10>{name: <11>.A, kind: <9>.B}, ` +
+			`B: <12>{name: "A", kind: "B"}}}, ` +
+			`C: <13>{b: <14>{[]: <15>(B: string)-><16>{name: <17>.A, kind: <15>.B}, ` +
+			`D: <18>{name: "C", kind: "D"}}}, ` +
+			`EE: <19>{b: <20>{[]: <21>(B: string)-><22>{name: <23>.A, kind: <21>.B}, ` +
+			`FF: <24>{name: "EE", kind: "FF", c: "bar"}}}}}`,
+	}, {
+		desc: "template unification within one struct",
+		in: `
+			a: {
+				[A=string]: { name: A }
+				// TODO: allow duplicate alias here
+				[X=string]: { kind: X }
+			}
+			a: "A": _
+			a: "C": _
+			a: "E": { c: "bar" }
+		`,
+		out: `<0>{a: <1>{[]: <2>(A: string)->(<3>{name: <2>.A} & <4>{kind: <2>.A}), ` +
+			`E: <5>{name: "E", kind: "E", c: "bar"}, ` +
+			`A: <6>{name: "A", kind: "A"}, ` +
+			`C: <7>{name: "C", kind: "C"}}}`,
+	}, {
+		desc: "field comprehensions with multiple keys",
+		in: `
+			for x in [
+				{a: "A", b: "B" },
+				{a: "C", b: "D" },
+				{a: "E", b: "F" },
+			] {
+				a: "\(x.a)": b: "\(x.b)": x
+			}
+
+			for x in [
+				{a: "A", b: "B" },
+				{a: "C", b: "D" },
+				{a: "E", b: "F" },
+			] {
+				"\(x.a)": "\(x.b)": x
+			}
+			`,
+		out: `<0>{E: <1>{F: <2>{a: "E", b: "F"}}, ` +
+			`a: <3>{` +
+			`E: <4>{b: <5>{F: <6>{a: "E", b: "F"}}}, ` +
+			`A: <7>{b: <8>{B: <9>{a: "A", b: "B"}}}, ` +
+			`C: <10>{b: <11>{D: <12>{a: "C", b: "D"}}}}, ` +
+			`A: <13>{B: <14>{a: "A", b: "B"}}, ` +
+			`C: <15>{D: <16>{a: "C", b: "D"}}}`,
+		// TODO: this order would be desirable.
+		// out: `<0>{a: <1>{` +
+		// 	`A: <2>{b: <3>{B: <4>{a: "A", b: "B"}}}, ` +
+		// 	`C: <5>{b: <6>{D: <7>{a: "C", b: "D"}}}, ` +
+		// 	`E: <8>{b: <9>{F: <10>{a: "E", b: "F"}}}}, ` +
+		// 	`A: <11>{B: <12>{a: "A", b: "B"}}, ` +
+		// 	`C: <13>{D: <14>{a: "C", b: "D"}}, ` +
+		// 	`E: <15>{F: <16>{a: "E", b: "F"}}}`,
+	}, {
+		desc: "field comprehensions with templates",
+		in: `
+			num: 1
+			a: {
+				if num < 5 {
+					[A=string]: [B=string]: {
+						name: A
+						kind: B
+					}
+				}
+			}
+			a: b: c: d: "bar"
+			`,
+		out: `<0>{num: 1, a: <1>{[]: <2>(A: string)-><3>{[]: <4>(B: string)-><5>{name: <2>.A, kind: <4>.B}, }, ` +
+			`b: <6>{[]: <7>(B: string)-><8>{name: <9>.A, kind: <7>.B}, ` +
+			`c: <10>{name: "b", kind: "c", ` +
+			`d: "bar"}}}}`,
+	}, {
+		desc: "disjunctions of lists",
+		in: `
+			l: *[ int, int ] | [ string, string ]
+
+			l1: [ "a", "b" ]
+			l2: l & [ "c", "d" ]
+			`,
+		out: `<0>{l: [int,int], l1: ["a","b"], l2: ["c","d"]}`,
+	}, {
+		desc: "normalization",
+		in: `
+			a: string | string
+			b: *1 | *int
+			c: *1.0 | *float
+		`,
+		out: `<0>{a: string, b: int, c: float}`,
+	}, {
+		desc: "default disambiguation and elimination",
+		in: `
+		a: *1 | int
+		b: *3 | int
+		c: a & b
+		d: b & a
+
+		e: *1 | *1
+		`,
+		out: `<0>{a: 1, b: 3, c: int, d: int, e: 1}`,
+	}, {
+		desc: "list comprehension",
+		in: `
+			a: [ for k, v in b if k < "d" if v > b.a { k }]
+			b: {
+				a: 1
+				b: 2
+				c: 3
+				d: 4
+			}
+			c: [ for _, x in b for _, y in b  if x < y { x } ]
+			d: [ for x, _ in a { x } ]
+			`,
+		out: `<0>{a: ["b","c"], b: <1>{a: 1, b: 2, c: 3, d: 4}, c: [1,1,1,2,2,3], d: [0,1]}`,
+	}, {
+		desc: "struct comprehension with template",
+		in: `
+			result: [ for _, v in service { v } ]
+
+			service: [Name=string]: {
+				name: *Name | string
+				type: "service"
+				port: *7080 | int
+			}
+			service: foo: {}
+			service: bar: { port: 8000 }
+			service: baz: { name: "foobar" }
+			`,
+		out: `<0>{result: [` +
+			`<1>{name: "foo", type: "service", port: 7080},` +
+			`<2>{name: "bar", type: "service", port: 8000},` +
+			`<3>{name: "foobar", type: "service", port: 7080}], ` +
+
+			`service: <4>{` +
+			`[]: <5>(Name: string)-><6>{name: (*<5>.Name | string), type: "service", port: (*7080 | int)}, ` +
+			`foo: <7>{name: "foo", type: "service", port: 7080}, ` +
+			`bar: <8>{name: "bar", type: "service", port: 8000}, ` +
+			`baz: <9>{name: "foobar", type: "service", port: 7080}}}`,
+	}, {
+		desc: "resolutions in struct comprehension keys",
+		in: `
+			a: { for _, b in ["c"] { "\(b + ".")": "a" } }
+			`,
+		out: `<0>{a: <1>{"c.": "a"}}`,
+	}, {
+		desc: "recursive evaluation within list",
+		in: `
+			l: [a]
+			a: b & { c: "t" }
+			b: {
+				d: c
+				c: string
+			}
+			l1: [a1]
+			a1: b1 & { c: "t" }
+			b1: {
+				d: "s" + c
+				c:  string
+			}
+		`,
+		out: `<0>{` +
+			`l: [<1>{c: "t", d: "t"}], ` +
+			`a: <2>{c: "t", d: "t"}, ` +
+			`b: <3>{c: string, d: string}, ` +
+			`l1: [<4>{c: "t", d: "st"}], ` +
+			`a1: <5>{c: "t", d: "st"}, ` +
+			`b1: <6>{c: string, d: ("s" + <7>.c)}}`,
+	}, {
+		desc: "ips",
+		in: `
+		IP: 4*[ uint8 ]
+
+		Private:
+			*[ 192, 168, uint8, uint8 ] |
+			[ 10, uint8, uint8, uint8] |
+			[ 172, >=16 & <=32, uint8, uint8 ]
+
+		Inst: Private & [ _, 10, ... ]
+
+		MyIP: Inst & [_, _, 10, 10 ]
+		`,
+		out: `<0>{` +
+			`IP: [(int & >=0 & int & <=255),(int & >=0 & int & <=255),(int & >=0 & int & <=255),(int & >=0 & int & <=255)], ` +
+			`Private: [192,168,(int & >=0 & int & <=255),(int & >=0 & int & <=255)], ` +
+			`Inst: [10,10,(int & >=0 & int & <=255),(int & >=0 & int & <=255)], ` +
+			`MyIP: [10,10,10,10]` +
+			`}`,
+	}, {
+		desc: "complex interaction of groundness",
+		in: `
+			res: [ for x in a for y in x { y & { d: "b" } }]
+			res: [ a.b.c & { d: "b" } ]
+
+			a: b: [C=string]: { d: string, s: "a" + d }
+			a: b: c: d: string
+		`,
+		// TODO(perf): unification should catch shared node.
+		out: `<0>{res: [<1>{d: "b", s: "ab"}], ` +
+			`a: <2>{b: <3>{[]: <4>(C: string)-><5>{d: string, s: ("a" + <5>.d)}, c: <6>{d: string, s: ("a" + <7>.d)}}}}`,
+	}, {
+		desc: "complex groundness 2",
+		in: `
+			r1: f1 & { y: "c" }
+
+			f1: { y: string, res: a.b.c & { d: y } }
+
+			a: b: c: { d: string, s: "a" + d }
+			a: b: [C=string]: { d: string, s: "a" + d }
+			a: b: c: d: string
+		`,
+		out: `<0>{r1: <1>{y: "c", res: <2>{d: "c", s: "ac"}}, f1: <3>{y: string, res: <4>{d: string, s: (("a" + <5>.d) & ("a" + <5>.d))}}, a: <6>{b: <7>{[]: <8>(C: string)-><9>{d: string, s: ("a" + <9>.d)}, c: <10>{d: string, s: (("a" + <11>.d) & ("a" + <11>.d))}}}}`,
+	}, {
+		desc: "references from template to concrete",
+		in: `
+				res: [t]
+				t: [X=string]: {
+					a: c + b.str
+					b: str: string
+					c: "X"
+				}
+				t: x: { b: str: "DDDD" }
+				`,
+		out: `<0>{res: [<1>{[]: <2>(X: string)-><3>{a: (<3>.c + <3>.b.str), c: "X", b: <4>{str: string}}, x: <5>{a: "XDDDD", c: "X", b: <6>{str: "DDDD"}}}], ` +
+			`t: <7>{[]: <2>(X: string)-><3>{a: (<3>.c + <3>.b.str), c: "X", b: <4>{str: string}}, x: <8>{a: "XDDDD", c: "X", b: <9>{str: "DDDD"}}}}`,
+	}, {
+		// TODO: A nice property for CUE to have would be that evaluation time
+		// is proportional to the number of output nodes (note that this is
+		// not the same as saying that the running time is O(n)).
+		// We should probably disallow shenanigans like the one below. But until
+		// this is allowed, it should at least be correct. At least we are not
+		// making reentrant coding easy.
+		desc: "reentrance",
+		in: `
+		// This indirection is needed to avoid binding references to fib
+		// within fib to the instantiated version.
+		fibRec: {nn: int, out: (fib & {n: nn}).out}
+		fib: {
+			n: int
+
+			if n >= 2 {
+				out: (fibRec & {nn: n - 2}).out + (fibRec & {nn: n - 1}).out
+			}
+			if n < 2 {
+				out: n
+			}
+		}
+		fib2: (fib & {n: 2}).out
+		fib7: (fib & {n: 7}).out
+		fib12: (fib & {n: 12}).out
+		`,
+		out: `<0>{` +
+			`fibRec: <1>{` +
+			`nn: int, ` +
+			`out: (<2>.fib & <3>{n: <4>.nn}).out}, ` +
+			// NOTE: the node numbers are not correct here, but this is an artifact
+			// of the testing code.
+			`fib: <5>{n: int if (<6>.n >= 2) yield <7>{out: ((<2>.fibRec & <8>{nn: (<6>.n - 2)}).out + (<2>.fibRec & <9>{nn: (<6>.n - 1)}).out)},  if (<6>.n < 2) yield <10>{out: <6>.n}}, ` +
+			`fib2: 1, ` +
+			`fib7: 13, ` +
+			`fib12: 144}`,
+	}, {
+		desc: "Issue #23",
+		in: `
+			x: {a:1}|{a:2}
+			y: x & {a:3}
+		`,
+		out: `<0>{x: (<1>{a: 1} | <2>{a: 2}), y: _|_((1 & 3):empty disjunction: conflicting values 1 and 3;(2 & 3):empty disjunction: conflicting values 2 and 3)}`,
+	}, {
+		desc: "cannot resolve references that would be ambiguous",
+		in: `
+		a1: *0 | 1
+		a1: a3 - a2
+		a2: *0 | 1
+		a2: a3 - a1
+		a3: 1
+
+		b1: (*0 | 1) & b2
+		b2: (0 | *1) & b1
+
+		c1: (*{a:1} | {b:1}) & c2
+		c2: (*{a:2} | {b:2}) & c1
+		`,
+		out: `<0>{` +
+			`a1: ((*0 | 1) & (<1>.a3 - <1>.a2)), ` +
+			`a3: 1, ` +
+			`a2: ((*0 | 1) & (<1>.a3 - <1>.a1)), ` +
+			`b1: (0 | 1), ` +
+			`b2: (0 | 1), ` +
+			`c1: (<2>{a: 1, b: 2} | <3>{a: 2, b: 1}), ` +
+			`c2: (<4>{a: 2, b: 1} | <5>{a: 1, b: 2})}`,
+	}, {
+		desc: "dont convert incomplete errors to non-incomplete",
+		in: `
+		import "strings"
+
+		n1: {min: <max, max: >min}
+		n2: -num
+		n3: +num
+		n4: num + num
+		n5: num - num
+		n6: num * num
+		n7: num / num
+
+		b1: !is
+
+		s1: "\(str)"
+		s2: strings.ContainsAny("dd")
+		s3: strings.ContainsAny(str, "dd")
+
+		str: string
+		num: <4
+		is:  bool
+		`,
+		out: `<0>{` +
+			`n1: <1>{min: <<2>.max, max: ><2>.min}, ` +
+			`n2: -<3>.num, num: <4, ` +
+			`n3: +<3>.num, ` +
+			`n4: (<3>.num + <3>.num), ` +
+			`n5: (<3>.num - <3>.num), ` +
+			`n6: (<3>.num * <3>.num), ` +
+			`n7: (<3>.num / <3>.num), ` +
+			`b1: !<3>.is, ` +
+			`is: bool, ` +
+			`s1: ""+<3>.str+"", ` +
+			`str: string, ` +
+			`s2: strings.ContainsAny ("dd"), ` +
+			`s3: <4>.ContainsAny (<3>.str,"dd")}`,
+	}, {
+		desc: "len of incomplete types",
+		in: `
+		args: *[] | [...string]
+		v1: len(args)
+		v2: len([])
+		v3: len({})
+		v4: len({a: 3})
+		v5: len({a: 3} | {a: 4})
+		v6: len('sf' | 'dd')
+		v7: len([2] | *[1, 2])
+		v8: len([2] | [1, 2])
+		v9: len("😂")
+		v10: len("")
+		`,
+		out: `<0>{` +
+			`args: [], ` +
+			`v1: 0, ` +
+			`v2: 0, ` +
+			`v3: 0, ` +
+			`v4: 1, ` +
+			`v5: len ((<1>{a: 3} | <2>{a: 4})), ` +
+			`v6: len (('sf' | 'dd')), ` +
+			`v7: 2, ` +
+			`v8: len (([2] | [1,2])), ` +
+			`v9: 4, ` +
+			`v10: 0}`,
+	}, {
+		desc: "slice rewrite bug",
+		in: `
+		fn: {
+			arg: [...int] & [1]
+			out: arg[1:]
+		}
+		fn1: fn & {arg: [1]}
+		`,
+		out: `<0>{fn: <1>{arg: [1], out: []}, fn1: <2>{arg: [1], out: []}}`,
+	}, {
+		desc: "Issue #94",
+		in: `
+		foo: {
+			opt?: 1
+			"txt": 2
+			#def: 3
+			regular: 4
+			_hidden: 5
+		}
+		comp: { for k, v in foo { "\(k)": v } }
+		select: {
+			opt: foo.opt
+			"txt": foo.txt
+			#def: foo.#def
+			regular: foo.regular
+			_hidden: foo._hidden
+		}
+		index: {
+			opt: foo["opt"]
+			"txt": foo["txt"]
+			#def: foo["#def"]
+			regular: foo["regular"]
+			_hidden: foo["_hidden"]
+		}
+		`,
+		out: `<0>{` +
+			`foo: <1>{opt?: 1, txt: 2, #def: 3, regular: 4, _hidden: 5}, ` +
+			`comp: <2>{txt: 2, regular: 4}, ` +
+			`select: <3>{opt: <4>.foo.opt, txt: 2, #def: 3, regular: 4, _hidden: 5}, ` +
+			`index: <5>{opt: <4>.foo["opt"], txt: 2, #def: <4>.foo["#def"], regular: 4, _hidden: <4>.foo["_hidden"]}}`,
+	}, {
+		desc: "retain references with interleaved embedding",
+		in: `
+		a: d: {
+			#base
+			#info: {...}
+			Y: #info.X
+		}
+
+		#base: {
+			#info: {...}
+		}
+
+		a: [Name=string]: { #info: {
+			X: "foo"
+		}}
+		`,
+		out: `<0>{a: <1>{[]: <2>(Name: string)-><3>{#info: <4>C{X: "foo"}}, d: <5>C{#info: <6>C{X: "foo"}, Y: "foo"}}, #base: <7>C{#info: <8>{...}}}`,
+	}, {
+		desc: "comparison against bottom",
+		in: `
+		a: _|_ == _|_
+		b: err == 1&2 // not a literal error, so not allowed
+		c: err == _|_ // allowed
+		d: err != _|_ // allowed
+		e: err != 1&3
+		// z: err == err // TODO: should infer to be true?
+		f: ({a: 1} & {a: 2}) == _|_
+		g: ({a: 1} & {b: 2}) == _|_
+		h: _|_ == ({a: 1} & {a: 2})
+		i: _|_ == ({a: 1} & {b: 2})
+
+		err: 1 & 2
+		`,
+		out: `<0>{a: true, b: _|_((1 & 2):conflicting values 1 and 2), err: _|_((1 & 2):conflicting values 1 and 2), c: true, d: false, e: _|_((1 & 2):conflicting values 1 and 2), f: true, g: false, h: true, i: false}`,
+	}, {
+		desc: "or builtin should not fail on non-concrete empty list",
+		in: `
+		#Workflow: {
+			jobs: {
+				[jobID=string]: {
+				}
+			}
+			#JobID: or([ for k, _ in jobs { k } ])
+		}
+
+		foo: #Workflow & {
+			jobs: foo: {
+			}
+		}
+		`,
+		out: `<0>{#Workflow: <1>C{jobs: <2>{[]: <3>(jobID: string)-><4>C{}, }, #JobID: or ([ <5>for k, _ in <6>.jobs yield <5>.k ])}, foo: <7>C{jobs: <8>{[]: <9>(jobID: string)-><10>C{}, foo: <11>C{}}, #JobID: "foo"}}`,
+	}, {
+		desc: "Issue #153",
+		in: `
+		Foo: {
+			listOfCloseds: [...#Closed]
+		}
+
+		#Closed: {
+			a: int | *0
+		}
+
+		Junk: {
+			b: 2
+		}
+
+		Foo & {
+			listOfCloseds: [{
+				for k, v in Junk {
+					"\(k)": v
+				}
+			 }]
+		}
+		`,
+		out: `<0>{<1>{listOfCloseds: [_|_(<2>.v:field "b" not allowed in closed struct)]}, Foo: <3>{listOfCloseds: []}, #Closed: <4>C{a: 0}, Junk: <5>{b: 2}}`,
+	}, {
+		desc: "label and field aliases",
+		in: `
+		p: [ID=string]: { name: ID }
+		A="foo=bar": "str"
+		a: A
+		B=bb: 4
+		b1: B
+		b1: bb
+		C="\(a)": 5
+		c: C
+		`,
+		out: `<0>{` +
+			`p: <1>{[]: <2>(ID: string)-><3>{name: <2>.ID}, }, ` +
+			`"foo=bar": "str", ` +
+			`a: "str", ` +
+			`bb: 4, ` +
+			`b1: 4, ` +
+			`c: 5, ` +
+			`str: 5}`,
+	}, {
+		desc: "optionals with label filters",
+		in: `
+		#JobID: =~"^[a-zA-Z]*$"
+		#Job: {
+			name: string
+			cmd:  string
+		}
+		#Jobs: {
+			[#JobID]: #Job
+			[=~"Test$"]: name: =~"^test" // Must work without ...
+		}
+
+		jobs: foo: name: "allGood"
+		jobs: foo: name: "allGood"
+
+		jobs1: #Jobs
+		jobs1: foo1: {} // faulty
+
+		jobs2: #Jobs
+		jobs2: fooTest: name: "badName" // faulty
+
+		jobs3: #Jobs
+		jobs3: [string]: #Job
+		jobs3: fooTest1: name: "badName" // faulty
+		`,
+		out: `<0>{` +
+			`#JobID: =~"^[a-zA-Z]*$", ` +
+			`#Job: <1>C{name: string, cmd: string}, ` +
+			`#Jobs: <2>C{[=~"^[a-zA-Z]*$"]: <3>(_: string)-><4>.#Job, [=~"Test$"]: <5>(_: string)-><6>C{name: =~"^test"}, }, ` +
+			`jobs: <7>{foo: <8>{name: "allGood"}}, ` +
+			`jobs1: _|_(<9>{}:field "foo1" not allowed in closed struct), ` +
+			`jobs2: <10>C{[=~"^[a-zA-Z]*$"]: <11>(_: string)-><4>.#Job, [=~"Test$"]: <12>(_: string)-><13>C{name: =~"^test"}, fooTest: _|_(string:field "cmd" not allowed in closed struct)}, ` +
+			`jobs3: _|_(<14>{name: "badName"}:field "fooTest1" not allowed in closed struct)}`,
+	}, {
+		desc: "optionals in open structs",
+		in: `
+		A: {
+			[=~"^[a-s]*$"]: int
+		}
+		B: {
+			[=~"^[m-z]*$"]: int
+		}
+		#C: {A & B}
+		c: #C & { aaa: 3 }
+		`,
+		out: `<0>{A: <1>{[=~"^[a-s]*$"]: <2>(_: string)->int, }, B: <3>{[=~"^[m-z]*$"]: <4>(_: string)->int, }, #C: <5>C{[=~"^[a-s]*$"]: <6>(_: string)->int, [=~"^[m-z]*$"]: <7>(_: string)->int, }, c: <8>C{[=~"^[a-s]*$"]: <9>(_: string)->int, [=~"^[m-z]*$"]: <10>(_: string)->int, aaa: 3}}`,
+	}, {
+		desc: "conjunction of optional sets",
+		in: `
+		#A: {
+			[=~"^[a-s]*$"]: int
+		}
+		#B: {
+			[=~"^[m-z]*$"]: int
+		}
+
+		#C: #A & #B
+		c: #C & { aaa: 3 }
+
+		#D: {#A & #B}
+		d: #D & { aaa: 3 }
+		`,
+		out: `<0>{` +
+			`#A: <1>C{[=~"^[a-s]*$"]: <2>(_: string)->int, }, ` +
+			`#B: <3>C{[=~"^[m-z]*$"]: <4>(_: string)->int, }, ` +
+			`#C: <5>C{(C{[=~"^[a-s]*$"]: <6>(_: string)->int} & C{[=~"^[m-z]*$"]: <7>(_: string)->int}), }, ` +
+			`c: _|_(3:field "aaa" not allowed in closed struct), ` +
+			`#D: <8>C{(C{[=~"^[a-s]*$"]: <9>(_: string)->int} & C{[=~"^[m-z]*$"]: <10>(_: string)->int}), }, ` +
+			`d: _|_(3:field "aaa" not allowed in closed struct)` +
+			`}`,
+	}, {
+		desc: "continue recursive closing for optionals",
+		in: `
+		#S: {
+			[string]: { a: int }
+		}
+		a: #S & {
+			v: { b: int }
+		}
+		`,
+		out: `<0>{#S: <1>{[]: <2>(_: string)-><3>C{a: int}, }, a: <4>{[]: <5>(_: string)-><6>C{a: int}, v: _|_(int:field "b" not allowed in closed struct)}}`,
+	}, {
+		desc: "augment closed optionals",
+		in: `
+		#A: {
+			[=~"^[a-s]*$"]: int
+		}
+		#B: {
+			[=~"^[m-z]*?"]: int
+		}
+		#C: {
+			#A & #B
+			[=~"^Q*$"]: int
+		}
+		c: #C & { QQ: 3 }
+		#D: {
+			#A
+			#B
+		}
+		d: #D & { aaa: 4 }
+		`,
+		out: `<0>{` +
+			`#A: <1>C{[=~"^[a-s]*$"]: <2>(_: string)->int, }, ` +
+			`#B: <3>C{[=~"^[m-z]*?"]: <4>(_: string)->int, }, ` +
+			`#C: <5>C{C{[=~"^Q*$"]: <6>(_: string)->int}, C{(C{[=~"^[a-s]*$"]: <7>(_: string)->int} & C{[=~"^[m-z]*?"]: <8>(_: string)->int})}, }, ` +
+			`c: <9>C{C{[=~"^Q*$"]: <10>(_: string)->int}, C{(C{[=~"^[a-s]*$"]: <11>(_: string)->int} & C{[=~"^[m-z]*?"]: <12>(_: string)->int})}, QQ: 3}, ` +
+			`#D: <13>C{[=~"^[a-s]*$"]: <14>(_: string)->int, [=~"^[m-z]*?"]: <15>(_: string)->int, }, ` +
+			`d: <16>C{[=~"^[a-s]*$"]: <17>(_: string)->int, [=~"^[m-z]*?"]: <18>(_: string)->int, aaa: 4}}`,
+	}, {
+		in: `
+		#Task: {
+			{
+				op:          "pull"
+				tag:         *"latest" | string
+				refToTag:    tag
+				tagExpr: tag + "dd"
+				tagInString: "\(tag)"
+			} | {
+				op: "scratch"
+			}
+		}
+
+		foo: #Task & {"op": "pull"}
+		`,
+		out: `<0>{#Task: (<1>C{op: "pull", tag: (*"latest" | string), refToTag: <1>.tag, tagExpr: (<1>.tag + "dd"), tagInString: ""+<1>.tag+""} | <2>C{op: "scratch"}), foo: <3>C{op: "pull", tag: "latest", refToTag: "latest", tagExpr: "latestdd", tagInString: "latest"}}`,
+	}, {
+		in: `
+		t: {
+			#ok: *true | bool
+			if #ok {
+				x: int
+			}
+		}
+		s: t & {
+			#ok: false
+		}`,
+		out: `<0>{t: <1>{x: int, #ok: true}, s: <2>{#ok: false}}`,
+	}, {
+		desc: "cross-dependent comprehension",
+		// TODO(eval): fix: c should ultimately be allowed the struct. Current
+		// semantics require, however, that generated fields are not available
+		// for evaluation. This, however, does not have to hold, for closedness
+		// checks and allowing this would be more intuitive.
+		// Until that time, ensure that the behavior is at commutative.
+		in: `
+		#a: {
+			if b {
+				c: 4
+			}
+			b: bool
+		}
+		x: (#a & { b: true}) & {c: 4 }
+		y: x
+		`,
+		// c should not be allowed, as it would break commutativiy.
+		// See comments above.
+		out: `<0>{x: _|_(4:field "c" not allowed in closed struct), y: _|_(4:field "c" not allowed in closed struct), #a: <1>C{b: bool if <2>.b yield <3>C{c: 4}}}`,
+	}, {
+		desc: "optional expanded before lookup",
+		in: `
+		test: [ID=_]: {
+			name: ID
+		}
+
+		test: A: {
+			field1: "1"
+			field2: "2"
+		}
+
+		B: test.A & {}
+		`,
+		out: `<0>{test: <1>{[]: <2>(ID: string)-><3>{name: <2>.ID}, A: <4>{name: "A", field1: "1", field2: "2"}}, B: <5>{name: "A", field1: "1", field2: "2"}}`,
+	}, {
+		desc: "Issue #178",
+		in: `
+		import "encoding/csv"
+		import "encoding/hex"
+
+		foo: csv.Decode(data)
+		data: bytes
+
+		len: int
+		bar: hex.EncodedLen(len)
+		`,
+		out: `<0>{foo: <1>.Decode (<2>.data), data: bytes, len: int, bar: <3>.EncodedLen (<2>.len)}`,
+	}, {
+		// This resulted in an issue in an older version. Prevent regression.
+		desc: "comprehension and skipped field",
+		in: `
+		for key, value in {x: v: 1} {
+			"\(key)": {
+				v: *{for pod, _ in value.v {}} | {"\(value.v)": 2}
+				_p: 3
+			}
+		}
+		`,
+		out: `<0>{x: <1>{v: <2>{"1": 2}, _p: 3}}`,
+	}, {
+		desc: "non-structural direct cycles",
+		in: `
+		c1: {bar: baz: 2} & c1.bar
+		c2: {bar: 1} & c2.bar
+		`,
+		out: `<0>{` +
+			`c1: <1>{bar: <2>{baz: 2}, baz: 2}, ` +
+			`c2: _|_(conflicting values {bar: 1} and 1 (mismatched types struct and int))}`,
+	}, {
+		desc: "dont bind to string labels",
+		in: `
+			x: 1
+			y: {
+				"x": 2
+				z: x
+			}
+			`,
+		out: `<0>{x: 1, y: <1>{x: 2, z: 1}}`,
+	}, {
+		desc: "dont pass incomplete values to builtins",
+		in: `
+		import "encoding/json"
+
+		input: string
+		foo: json.Marshal(input)
+		`,
+		out: `<0>{input: string, foo: <1>.Marshal (<2>.input)}`,
+	}, {
+		desc: "alias reuse in nested scope",
+		in: `
+		#Foo: {
+			let X = or([ for k, _ in {} { k } ])
+			connection: [X]: X
+		}
+		#A: {
+			foo: "key"
+			let X = foo
+			a: foo: [X]: X
+		}
+		#B: {
+			foo: string
+			let X = foo
+			a: foo: [X]: X
+		}
+		b: #B & { foo: "key" }
+		`,
+		out: `<0>{` +
+			`#Foo: <1>C{connection: <2>C{[or ([ <3>for k, _ in <4>{} yield <3>.k ])]: <5>(_: string)->or ([ <3>for k, _ in <4>{} yield <3>.k ]), }}, ` +
+			`#A: <6>C{foo: "key", a: <7>C{foo: <8>C{["key"]: <9>(_: string)-><10>.foo, }}}, ` +
+			`#B: <11>C{foo: string, a: <12>C{foo: <13>C{[string]: <14>(_: string)-><15>.foo, }}}, ` +
+			`b: <16>C{foo: "key", a: <17>C{foo: <18>C{["key"]: <19>(_: string)-><20>.foo, }}}` +
+			`}`,
+	}, {
+		desc: "json Marshaling detects incomplete",
+		in: `
+		import "encoding/json"
+		a: json.Marshal({ a: string} )
+
+		foo: { a: 3, b: foo.c }
+		b: json.Marshal(foo)
+		`,
+		out: `<0>{` +
+			`a: <1>.Marshal (<2>{a: string}), ` +
+			`foo: <3>{a: 3, b: <4>.foo.c}, ` +
+			`b: <1>.Marshal (<4>.foo)}`,
+	}, {
+		desc: "detectIncompleteYAML",
+		in: `
+		package foobar
+
+		import yaml "encoding/yaml"
+
+		#Spec: {
+			_vars: {something: string}
+			data: {
+				#foo: {
+					use: _vars.something
+				}
+				baz:    yaml.Marshal(_vars.something)
+				foobar: yaml.Marshal(#foo)
+			}
+		}
+		Val: #Spec & {
+			_vars: something: "var-string"
+		}
+		`,
+		out: `<0>{#Spec: <1>C{_vars: <2>C{something: string}, data: <3>C{#foo: <4>C{use: string}, baz: <5>.Marshal (<6>._vars.something), foobar: <5>.Marshal (<7>.#foo)}}, Val: <8>C{_vars: <9>C{something: "var-string"}, data: <10>C{#foo: <11>C{use: "var-string"}, baz: "var-string\n", foobar: "use: var-string\n"}}}`,
+	}, {
+		desc: "detectIncompleteJSON",
+		in: `
+			package foobar
+	
+			import "encoding/json"
+		
+			#Spec: {
+				_vars: {something: string}
+				data: {
+					#foo: {
+						use: _vars.something
+					}
+					baz:    json.Marshal(_vars.something)
+					foobar: json.Marshal(#foo)
+				}
+			}
+			Val: #Spec & {
+				_vars: something: "var-string"
+			}
+			`,
+		out: `<0>{#Spec: <1>C{_vars: <2>C{something: string}, data: <3>C{#foo: <4>C{use: string}, baz: <5>.Marshal (<6>._vars.something), foobar: <5>.Marshal (<7>.#foo)}}, Val: <8>C{_vars: <9>C{something: "var-string"}, data: <10>C{#foo: <11>C{use: "var-string"}, baz: "\"var-string\"", foobar: "{\"use\":\"var-string\"}"}}}`,
+	}, {
+		desc: "issue312",
+		in: `
+		for x in [1] {
+			*close({}) | { [_]: null }
+		}
+		`,
+		out: `<0>{ <1>for _, x in [1] yield <2>{}, (*close (<3>{}) | <4>{[]: <5>(_: string)->null, })}`,
+	}, {
+		// TODO(eval): note that this behavior is incompatible with allowing
+		// non-struct as emit values. If we ever want to do this, we need to
+		// do it soon.
+		desc: "issue312",
+		in: `
+		y: *1 | {a: 2}
+		for x in [1] { y }
+		`,
+		out: `<0>{y: 1, a: 2}`,
+	}, {
+		desc: "issue318",
+		in: `
+		#T: {
+			arg: x: string
+			out1: "\(arg.x) \(arg.y)"
+			out2: "\(arg.y)"
+			vx: arg.x
+			vy: arg.y
+		}
+		`,
+		out: `<0>{` +
+			`#T: <1>C{` +
+			`arg: <2>C{x: string}, ` +
+			`out1: _|_(<3>.arg.y:undefined field "y"), ` +
+			`out2: _|_(<3>.arg.y:undefined field "y"), ` +
+			`vx: string, ` +
+			`vy: _|_(<3>.arg.y:undefined field "y")}}`,
+	}, {
+		desc: "issue314",
+		in: `
+		import (
+			"text/template"
+			"encoding/yaml"
+			"encoding/json"
+		)
+
+		x: {
+			s: "myname"
+			#T
+		}
+
+		#T: {
+			s: string
+			out: template.Execute("{{.s}}", {
+				"s": s
+			})
+		}
+
+		#V: {
+			s: string
+			out: json.Marshal({"s": s})
+		}
+
+		#U: {
+			s: string
+			out: yaml.Marshal({"s": s})
+		}`,
+		out: `<0>{` +
+			`x: <1>C{s: "myname", out: "myname"}, ` +
+			`#T: <2>C{s: string, out: <3>.Execute ("{{.s}}",<4>C{s: <5>.s})}, ` +
+			`#V: <6>C{s: string, out: <7>.Marshal (<8>C{s: <9>.s})}, ` +
+			`#U: <10>C{s: string, out: <11>.Marshal (<12>C{s: <13>.s})}}`,
+	}}
+	rewriteHelper(t, testCases, evalFull)
+}
+
+func TestX(t *testing.T) {
+	// Don't remove. For debugging.
+	in := `
+	`
+
+	if strings.TrimSpace(in) == "" {
+		t.Skip()
+	}
+	rewriteHelper(t, []testCase{{in: in}}, evalFull)
+}
diff --git a/internal/legacy/cue/types.go b/internal/legacy/cue/types.go
new file mode 100644
index 0000000..a5deb07
--- /dev/null
+++ b/internal/legacy/cue/types.go
@@ -0,0 +1,2247 @@
+// Copyright 2018 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"math"
+	"math/big"
+	"strconv"
+	"strings"
+
+	"github.com/cockroachdb/apd/v2"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal"
+	"cuelang.org/go/internal/core/adt"
+)
+
+// Kind determines the underlying type of a Value.
+type Kind = adt.Kind
+
+const BottomKind Kind = 0
+
+const (
+	// NullKind indicates a null value.
+	NullKind Kind = adt.NullKind
+
+	// BoolKind indicates a boolean value.
+	BoolKind = adt.BoolKind
+
+	// IntKind represents an integral number.
+	IntKind = adt.IntKind
+
+	// FloatKind represents a decimal float point number that cannot be
+	// converted to an integer. The underlying number may still be integral,
+	// but resulting from an operation that enforces the float type.
+	FloatKind = adt.FloatKind
+
+	// StringKind indicates any kind of string.
+	StringKind = adt.StringKind
+
+	// BytesKind is a blob of data.
+	BytesKind = adt.BytesKind
+
+	// StructKind is a kev-value map.
+	StructKind = adt.StructKind
+
+	// ListKind indicates a list of values.
+	ListKind = adt.ListKind
+
+	// _numberKind is used as a implementation detail inside
+	// Kind.String to indicate NumberKind.
+
+	// NumberKind represents any kind of number.
+	NumberKind = IntKind | FloatKind
+
+	TopKind = Kind(adt.TopKind)
+)
+
+// An structValue represents a JSON object.
+//
+// TODO: remove
+type structValue struct {
+	ctx  *context
+	path *valueData
+	obj  *structLit
+	Arcs arcs
+}
+
+// Len reports the number of fields in this struct.
+func (o *structValue) Len() int {
+	if o.obj == nil {
+		return 0
+	}
+	return len(o.Arcs)
+}
+
+// At reports the key and value of the ith field, i < o.Len().
+func (o *structValue) At(i int) (key string, v Value) {
+	a := o.Arcs[i]
+	v = newChildValue(o, i)
+	return o.ctx.LabelStr(a.Label), v
+}
+
+// Lookup reports the field for the given key. The returned Value is invalid
+// if it does not exist.
+func (o *structValue) Lookup(key string) Value {
+	f := o.ctx.StrLabel(key)
+	i := 0
+	len := o.Len()
+	for ; i < len; i++ {
+		if o.Arcs[i].Label == f {
+			break
+		}
+	}
+	if i == len {
+		// TODO: better message.
+		ctx := o.ctx
+		x := ctx.mkErr(o.obj, codeNotExist, "value %q not found", key)
+		v := x.evalPartial(ctx)
+		return Value{ctx.index, &valueData{o.path.parent, 0, arc{Label: o.path.Label, Value: v, v: x}}}
+	}
+	return newChildValue(o, i)
+}
+
+// MarshalJSON returns a valid JSON encoding or reports an error if any of the
+// fields is invalid.
+func (o *structValue) marshalJSON() (b []byte, err errors.Error) {
+	b = append(b, '{')
+	n := o.Len()
+	for i := 0; i < n; i++ {
+		k, v := o.At(i)
+		s, err := json.Marshal(k)
+		if err != nil {
+			return nil, unwrapJSONError(err)
+		}
+		b = append(b, s...)
+		b = append(b, ':')
+		bb, err := json.Marshal(v)
+		if err != nil {
+			return nil, unwrapJSONError(err)
+		}
+		b = append(b, bb...)
+		if i < n-1 {
+			b = append(b, ',')
+		}
+	}
+	b = append(b, '}')
+	return b, nil
+}
+
+var _ errors.Error = &marshalError{}
+
+type marshalError struct {
+	err errors.Error
+	b   *bottom
+}
+
+func toMarshalErr(v Value, b *bottom) error {
+	return &marshalError{v.toErr(b), b}
+}
+
+func marshalErrf(v Value, src source, code errCode, msg string, args ...interface{}) error {
+	arguments := append([]interface{}{code, msg}, args...)
+	b := v.idx.mkErr(src, arguments...)
+	return toMarshalErr(v, b)
+}
+
+func (e *marshalError) Error() string {
+	return fmt.Sprintf("cue: marshal error: %v", e.err)
+}
+
+func (e *marshalError) Path() []string               { return e.err.Path() }
+func (e *marshalError) Msg() (string, []interface{}) { return e.err.Msg() }
+func (e *marshalError) Position() token.Pos          { return e.err.Position() }
+func (e *marshalError) InputPositions() []token.Pos {
+	return e.err.InputPositions()
+}
+
+func unwrapJSONError(err error) errors.Error {
+	switch x := err.(type) {
+	case *json.MarshalerError:
+		return unwrapJSONError(x.Err)
+	case *marshalError:
+		return x
+	case errors.Error:
+		return &marshalError{x, nil}
+	default:
+		return &marshalError{errors.Wrapf(err, token.NoPos, "json error"), nil}
+	}
+}
+
+// An Iterator iterates over values.
+//
+type Iterator struct {
+	val  Value
+	ctx  *context
+	iter iterAtter
+	len  int
+	p    int
+	cur  Value
+	f    label
+}
+
+// Next advances the iterator to the next value and reports whether there was
+// any. It must be called before the first call to Value or Key.
+func (i *Iterator) Next() bool {
+	if i.p >= i.len {
+		i.cur = Value{}
+		return false
+	}
+	arc := i.iter.iterAt(i.ctx, i.p)
+	i.cur = i.val.makeChild(i.ctx, uint32(i.p), arc)
+	i.f = arc.Label
+	i.p++
+	return true
+}
+
+// Value returns the current value in the list. It will panic if Next advanced
+// past the last entry.
+func (i *Iterator) Value() Value {
+	return i.cur
+}
+
+// Label reports the label of the value if i iterates over struct fields and
+// "" otherwise.
+func (i *Iterator) Label() string {
+	if i.f == 0 {
+		return ""
+	}
+	return i.ctx.LabelStr(i.f)
+}
+
+// IsHidden reports if a field is hidden from the data model.
+func (i *Iterator) IsHidden() bool {
+	return i.f.IsHidden()
+}
+
+// IsOptional reports if a field is optional.
+func (i *Iterator) IsOptional() bool {
+	return i.cur.v.arc.optional
+}
+
+// IsDefinition reports if a field is a definition.
+func (i *Iterator) IsDefinition() bool {
+	return i.cur.v.arc.definition
+}
+
+// marshalJSON iterates over the list and generates JSON output. HasNext
+// will return false after this operation.
+func marshalList(l *Iterator) (b []byte, err errors.Error) {
+	b = append(b, '[')
+	if l.Next() {
+		for i := 0; ; i++ {
+			x, err := json.Marshal(l.Value())
+			if err != nil {
+				return nil, unwrapJSONError(err)
+			}
+			b = append(b, x...)
+			if !l.Next() {
+				break
+			}
+			b = append(b, ',')
+		}
+	}
+	b = append(b, ']')
+	return b, nil
+}
+
+func (v Value) getNum(k kind) (*numLit, errors.Error) {
+	v, _ = v.Default()
+	if err := v.checkKind(v.ctx(), k); err != nil {
+		return nil, v.toErr(err)
+	}
+	n, _ := v.v.Value.(*numLit)
+	return n, nil
+}
+
+// MantExp breaks x into its mantissa and exponent components and returns the
+// exponent. If a non-nil mant argument is provided its value is set to the
+// mantissa of x. The components satisfy x == mant × 10**exp. It returns an
+// error if v is not a number.
+//
+// The components are not normalized. For instance, 2.00 is represented mant ==
+// 200 and exp == -2. Calling MantExp with a nil argument is an efficient way to
+// get the exponent of the receiver.
+func (v Value) MantExp(mant *big.Int) (exp int, err error) {
+	n, err := v.getNum(numKind)
+	if err != nil {
+		return 0, err
+	}
+	if n.X.Form != 0 {
+		return 0, ErrInfinite
+	}
+	if mant != nil {
+		mant.Set(&n.X.Coeff)
+		if n.X.Negative {
+			mant.Neg(mant)
+		}
+	}
+	return int(n.X.Exponent), nil
+}
+
+// AppendInt appends the string representation of x in the given base to buf and
+// returns the extended buffer, or an error if the underlying number was not
+// an integer.
+func (v Value) AppendInt(buf []byte, base int) ([]byte, error) {
+	i, err := v.Int(nil)
+	if err != nil {
+		return nil, err
+	}
+	return i.Append(buf, base), nil
+}
+
+// AppendFloat appends to buf the string form of the floating-point number x.
+// It returns an error if v is not a number.
+func (v Value) AppendFloat(buf []byte, fmt byte, prec int) ([]byte, error) {
+	n, err := v.getNum(numKind)
+	if err != nil {
+		return nil, err
+	}
+	ctx := apd.BaseContext
+	nd := int(apd.NumDigits(&n.X.Coeff)) + int(n.X.Exponent)
+	if n.X.Form == apd.Infinite {
+		if n.X.Negative {
+			buf = append(buf, '-')
+		}
+		return append(buf, string('∞')...), nil
+	}
+	if fmt == 'f' && nd > 0 {
+		ctx.Precision = uint32(nd + prec)
+	} else {
+		ctx.Precision = uint32(prec)
+	}
+	var d apd.Decimal
+	ctx.Round(&d, &n.X)
+	return d.Append(buf, fmt), nil
+}
+
+var (
+	// ErrBelow indicates that a value was rounded down in a conversion.
+	ErrBelow = errors.New("value was rounded down")
+
+	// ErrAbove indicates that a value was rounded up in a conversion.
+	ErrAbove = errors.New("value was rounded up")
+
+	// ErrInfinite indicates that a value is infinite.
+	ErrInfinite = errors.New("infinite")
+)
+
+// Int converts the underlying integral number to an big.Int. It reports an
+// error if the underlying value is not an integer type. If a non-nil *Int
+// argument z is provided, Int stores the result in z instead of allocating a
+// new Int.
+func (v Value) Int(z *big.Int) (*big.Int, error) {
+	n, err := v.getNum(intKind)
+	if err != nil {
+		return nil, err
+	}
+	if z == nil {
+		z = &big.Int{}
+	}
+	if n.X.Exponent != 0 {
+		panic("cue: exponent should always be nil for integer types")
+	}
+	z.Set(&n.X.Coeff)
+	if n.X.Negative {
+		z.Neg(z)
+	}
+	return z, nil
+}
+
+// Int64 converts the underlying integral number to int64. It reports an
+// error if the underlying value is not an integer type or cannot be represented
+// as an int64. The result is (math.MinInt64, ErrAbove) for x < math.MinInt64,
+// and (math.MaxInt64, ErrBelow) for x > math.MaxInt64.
+func (v Value) Int64() (int64, error) {
+	n, err := v.getNum(intKind)
+	if err != nil {
+		return 0, err
+	}
+	if !n.X.Coeff.IsInt64() {
+		if n.X.Negative {
+			return math.MinInt64, ErrAbove
+		}
+		return math.MaxInt64, ErrBelow
+	}
+	i := n.X.Coeff.Int64()
+	if n.X.Negative {
+		i = -i
+	}
+	return i, nil
+}
+
+// Uint64 converts the underlying integral number to uint64. It reports an
+// error if the underlying value is not an integer type or cannot be represented
+// as a uint64. The result is (0, ErrAbove) for x < 0, and
+// (math.MaxUint64, ErrBelow) for x > math.MaxUint64.
+func (v Value) Uint64() (uint64, error) {
+	n, err := v.getNum(intKind)
+	if err != nil {
+		return 0, err
+	}
+	if n.X.Negative {
+		return 0, ErrAbove
+	}
+	if !n.X.Coeff.IsUint64() {
+		return math.MaxUint64, ErrBelow
+	}
+	i := n.X.Coeff.Uint64()
+	return i, nil
+}
+
+// trimZeros trims 0's for better JSON respresentations.
+func trimZeros(s string) string {
+	n1 := len(s)
+	s2 := strings.TrimRight(s, "0")
+	n2 := len(s2)
+	if p := strings.IndexByte(s2, '.'); p != -1 {
+		if p == n2-1 {
+			return s[:len(s2)+1]
+		}
+		return s2
+	}
+	if n1-n2 <= 4 {
+		return s
+	}
+	return fmt.Sprint(s2, "e+", n1-n2)
+}
+
+var (
+	smallestPosFloat64 *apd.Decimal
+	smallestNegFloat64 *apd.Decimal
+	maxPosFloat64      *apd.Decimal
+	maxNegFloat64      *apd.Decimal
+)
+
+func init() {
+	const (
+		// math.SmallestNonzeroFloat64: 1 / 2**(1023 - 1 + 52)
+		smallest = "4.940656458412465441765687928682213723651e-324"
+		// math.MaxFloat64: 2**1023 * (2**53 - 1) / 2**52
+		max = "1.797693134862315708145274237317043567981e+308"
+	)
+	ctx := apd.BaseContext
+	ctx.Precision = 40
+
+	var err error
+	smallestPosFloat64, _, err = ctx.NewFromString(smallest)
+	if err != nil {
+		panic(err)
+	}
+	smallestNegFloat64, _, err = ctx.NewFromString("-" + smallest)
+	if err != nil {
+		panic(err)
+	}
+	maxPosFloat64, _, err = ctx.NewFromString(max)
+	if err != nil {
+		panic(err)
+	}
+	maxNegFloat64, _, err = ctx.NewFromString("-" + max)
+	if err != nil {
+		panic(err)
+	}
+}
+
+// Float64 returns the float64 value nearest to x. It reports an error if v is
+// not a number. If x is too small to be represented by a float64 (|x| <
+// math.SmallestNonzeroFloat64), the result is (0, ErrBelow) or (-0, ErrAbove),
+// respectively, depending on the sign of x. If x is too large to be represented
+// by a float64 (|x| > math.MaxFloat64), the result is (+Inf, ErrAbove) or
+// (-Inf, ErrBelow), depending on the sign of x.
+func (v Value) Float64() (float64, error) {
+	n, err := v.getNum(numKind)
+	if err != nil {
+		return 0, err
+	}
+	if n.X.Negative {
+		if n.X.Cmp(smallestNegFloat64) == 1 {
+			return -0, ErrAbove
+		}
+		if n.X.Cmp(maxNegFloat64) == -1 {
+			return math.Inf(-1), ErrBelow
+		}
+	} else {
+		if n.X.Cmp(smallestPosFloat64) == -1 {
+			return 0, ErrBelow
+		}
+		if n.X.Cmp(maxPosFloat64) == 1 {
+			return math.Inf(1), ErrAbove
+		}
+	}
+	f, _ := n.X.Float64()
+	return f, nil
+}
+
+type valueData struct {
+	parent *valueData
+	index  uint32
+	arc
+}
+
+// path returns the path of the value.
+func (v *valueData) appendPath(a []string, idx *index) ([]string, kind) {
+	var k kind
+	if v.parent != nil {
+		a, k = v.parent.appendPath(a, idx)
+	}
+	switch k {
+	case listKind:
+		a = append(a, strconv.FormatInt(int64(v.index), 10))
+	case structKind:
+		label := idx.LabelStr(v.arc.Label)
+		if f := v.arc.Label; !f.IsDef() && !f.IsHidden() {
+			if !ast.IsValidIdent(label) {
+				label = strconv.Quote(label)
+			}
+		}
+		a = append(a, label)
+	}
+	return a, v.arc.Value.Kind()
+}
+
+// Value holds any value, which may be a Boolean, Error, List, Null, Number,
+// Struct, or String.
+type Value struct {
+	idx *index
+	v   *valueData
+}
+
+func newErrValue(v Value, b *bottom) Value {
+	ctx := v.ctx()
+	p := v.v
+	if p == nil {
+		return newValueRoot(ctx, b)
+	}
+	return Value{
+		ctx.index,
+		&valueData{p.parent, p.index, arc{
+			Label: p.arc.Label,
+			Value: b,
+			v:     b,
+		}},
+	}
+}
+
+func newValueRoot(ctx *context, x value) Value {
+	v := x.evalPartial(ctx)
+	return Value{ctx.index, &valueData{nil, 0, arc{Value: v, v: x}}}
+}
+
+func newChildValue(obj *structValue, i int) Value {
+	a := obj.Arcs[i]
+	for j, b := range obj.obj.Arcs {
+		if b.Label == a.Label {
+			a = obj.obj.iterAt(obj.ctx, j)
+			// TODO: adding more technical debt here. The evaluator should be
+			// rewritten.
+			x := obj.obj
+			ctx := obj.ctx
+			if x.optionals != nil {
+				name := ctx.LabelStr(x.Arcs[i].Label)
+				arg := &stringLit{x.baseValue, name, nil}
+
+				val, _ := x.optionals.constraint(ctx, arg)
+				if val != nil {
+					a.v = mkBin(ctx, x.Pos(), opUnify, a.v, val)
+				}
+			}
+			break
+		}
+	}
+
+	return Value{obj.ctx.index, &valueData{obj.path, uint32(i), a}}
+}
+
+// Dereference reports the value v refers to if v is a reference or v itself
+// otherwise.
+func Dereference(v Value) Value {
+	if v.v == nil {
+		return v
+	}
+
+	ctx := v.ctx()
+	a, n := appendPath(ctx, make([]label, 0, 3), v.v.v)
+
+	if n == nil {
+		return v
+
+	}
+
+	p := locateNode(v.v, n)
+
+	if p == nil {
+
+		imp := ctx.getImportFromNode(n.node)
+		if imp == nil {
+			// TODO(eval): embedded structs are currently represented at the
+			// same level as the enclosing struct. This means that the parent
+			// of an embedded struct skips the struct in which it is embedded.
+			// Treat embedded structs as "anonymous" fields.
+			// See TestPathCorrection.
+			return v
+		}
+		p = &valueData{arc: arc{v: imp.rootValue, Value: imp.rootStruct}}
+	}
+
+	cached := p.Value
+	if cached == nil {
+		cached = p.v.evalPartial(ctx)
+	}
+	s := cached.(*structLit)
+	for _, f := range a {
+		a := s.Lookup(ctx, f)
+		if a.v == nil {
+			return Value{}
+		}
+		p = &valueData{parent: p, arc: a} // index
+		s, _ = a.Value.(*structLit)
+	}
+
+	v = Value{v.idx, p}
+	return v
+}
+
+func appendPath(ctx *context, a []label, v value) (path []label, n *nodeRef) {
+	switch x := v.(type) {
+	case *selectorExpr:
+		a, n = appendPath(ctx, a, x.X)
+		if n == nil {
+			return nil, nil
+		}
+
+		a = append(a, x.Sel)
+
+	case *indexExpr:
+		e := x.Index.evalPartial(ctx)
+		s, ok := e.(*stringLit)
+		if !ok {
+			return nil, nil
+		}
+
+		a, n = appendPath(ctx, a, x.X)
+		if n == nil {
+			return nil, nil
+		}
+
+		a = append(a, ctx.Label(s.Str, false))
+
+	case *nodeRef:
+		n = x
+	}
+	return a, n
+}
+
+func remakeValue(base Value, v value) Value {
+	p := base.v
+	if n, ok := v.(*nodeRef); ok {
+		if q := locateNode(p, n); q != nil {
+			p = q
+		}
+	}
+	path := *p
+	path.v = v
+	path.Value = v.evalPartial(base.ctx())
+	return Value{base.idx, &path}
+}
+
+func locateNode(p *valueData, n *nodeRef) *valueData {
+	// the parent must exist.
+	for ; p != nil && p.Value != n.node.(value); p = p.parent {
+	}
+	return p
+}
+
+func (v Value) ctx() *context {
+	return v.idx.newContext()
+}
+
+func (v Value) makeChild(ctx *context, i uint32, a arc) Value {
+	return Value{v.idx, &valueData{v.v, i, a}}
+}
+
+func (v Value) makeElem(x value) Value {
+	v, e := v.evalFull(x)
+	return Value{v.idx, &valueData{v.v, 0, arc{
+		optional: true,
+		v:        x,
+		Value:    e,
+	}}}
+}
+
+func (v Value) eval(ctx *context) evaluated {
+	if v.v == nil || v.v.Value == nil {
+		panic("undefined value")
+	}
+	return ctx.manifest(v.v.Value)
+}
+
+func (v Value) evalFull(u value) (Value, evaluated) {
+	ctx := v.ctx()
+	x := u.evalPartial(ctx)
+	if st, ok := x.(*structLit); ok {
+		var err *bottom
+		x, err = st.expandFields(ctx)
+		if err != nil {
+			x = err
+		}
+		if x != st {
+			p := *v.v
+			p.Value = x
+			v.v = &p
+		}
+	}
+	return v, x
+}
+
+// Eval resolves the references of a value and returns the result.
+// This method is not necessary to obtain concrete values.
+func (v Value) Eval() Value {
+	if v.v == nil {
+		return v
+	}
+	return remakeValue(v.evalFull(v.v.v))
+}
+
+// Default reports the default value and whether it existed. It returns the
+// normal value if there is no default.
+func (v Value) Default() (Value, bool) {
+	if v.v == nil {
+		return v, false
+	}
+	v, u := v.evalFull(v.v.v)
+	x := v.ctx().manifest(u)
+	if x != u {
+		return remakeValue(v, x), true
+	}
+	return v, false
+}
+
+// Label reports he label used to obtain this value from the enclosing struct.
+//
+// TODO: get rid of this somehow. Probably by including a FieldInfo struct
+// or the like.
+func (v Value) Label() (string, bool) {
+	if v.v.Label == 0 {
+		return "", false
+	}
+	return v.idx.LabelStr(v.v.Label), true
+}
+
+// Kind returns the kind of value. It returns BottomKind for atomic values that
+// are not concrete. For instance, it will return BottomKind for the bounds
+// >=0.
+func (v Value) Kind() Kind {
+	if v.v == nil {
+		return BottomKind
+	}
+	c := v.v.Value
+	if c == nil {
+		c = v.v.v.evalPartial(v.ctx())
+	}
+	k := c.Kind()
+	if k.isGround() {
+		switch {
+		case k.isAnyOf(nullKind):
+			return NullKind
+		case k.isAnyOf(boolKind):
+			return BoolKind
+		case k&numKind == (intKind):
+			return IntKind
+		case k&numKind == (floatKind):
+			return FloatKind
+		case k.isAnyOf(numKind):
+			return NumberKind
+		case k.isAnyOf(bytesKind):
+			return BytesKind
+		case k.isAnyOf(stringKind):
+			return StringKind
+		case k.isAnyOf(structKind):
+			return StructKind
+		case k.isAnyOf(listKind):
+			return ListKind
+		}
+	}
+	return BottomKind
+}
+
+// IncompleteKind returns a mask of all kinds that this value may be.
+func (v Value) IncompleteKind() Kind {
+	if v.v == nil {
+		return BottomKind
+	}
+	var k kind
+	x := v.v.v.evalPartial(v.ctx())
+	switch x := convertBuiltin(x).(type) {
+	case *builtin:
+		k = x.representedKind()
+	case *customValidator:
+		k = x.Builtin.Params[0]
+	default:
+		k = x.Kind()
+	}
+	vk := BottomKind // Everything is a bottom kind.
+	for i := kind(1); i < nonGround; i <<= 1 {
+		if k&i != 0 {
+			switch i {
+			case nullKind:
+				vk |= NullKind
+			case boolKind:
+				vk |= BoolKind
+			case intKind:
+				vk |= IntKind
+			case floatKind:
+				vk |= FloatKind
+			case stringKind:
+				vk |= StringKind
+			case bytesKind:
+				vk |= BytesKind
+			case structKind:
+				vk |= StructKind
+			case listKind:
+				vk |= ListKind
+			}
+		}
+	}
+	return vk
+}
+
+// MarshalJSON marshalls this value into valid JSON.
+func (v Value) MarshalJSON() (b []byte, err error) {
+	b, err = v.marshalJSON()
+	if err != nil {
+		return nil, unwrapJSONError(err)
+	}
+	return b, nil
+}
+
+func (v Value) marshalJSON() (b []byte, err error) {
+	v, _ = v.Default()
+	if v.v == nil {
+		return json.Marshal(nil)
+	}
+	ctx := v.idx.newContext()
+	x := v.eval(ctx)
+	// TODO: implement marshalles in value.
+	switch k := x.Kind(); k {
+	case nullKind:
+		return json.Marshal(nil)
+	case boolKind:
+		return json.Marshal(x.(*boolLit).B)
+	case intKind, floatKind, numKind:
+		return x.(*numLit).X.MarshalText()
+	case stringKind:
+		return json.Marshal(x.(*stringLit).Str)
+	case bytesKind:
+		return json.Marshal(x.(*bytesLit).B)
+	case listKind:
+		i, _ := v.List()
+		return marshalList(&i)
+	case structKind:
+		obj, err := v.structValData(ctx)
+		st := obj.obj
+		if len(st.comprehensions) > 0 {
+			// This should always evaluate to incomplete. However, fall back
+			// to a bad error message, rather than crashing, in case it doesn't.
+			if err, ok := st.comprehensions[0].comp.evalPartial(ctx).(*bottom); ok {
+				return nil, toMarshalErr(v, err)
+			}
+		}
+
+		if err != nil {
+			return nil, toMarshalErr(v, err)
+		}
+		return obj.marshalJSON()
+	case bottomKind:
+		return nil, toMarshalErr(v, x.(*bottom))
+	default:
+		if k.hasReferences() {
+			return nil, marshalErrf(v, x, codeIncomplete, "value %q contains unresolved references", ctx.str(x))
+		}
+		if !k.isGround() {
+			return nil, marshalErrf(v, x, codeIncomplete, "cannot convert incomplete value %q to JSON", ctx.str(x))
+		}
+		return nil, marshalErrf(v, x, 0, "cannot convert value %q of type %T to JSON", ctx.str(x), x)
+	}
+}
+
+// Syntax converts the possibly partially evaluated value into syntax. This
+// can use used to print the value with package format.
+func (v Value) Syntax(opts ...Option) ast.Node {
+	// TODO: the default should ideally be simplified representation that
+	// exactly represents the value. The latter can currently only be
+	// ensured with Raw().
+	if v.v == nil || v.v.Value == nil {
+		return nil
+	}
+	ctx := v.ctx()
+	o := getOptions(opts)
+	var inst *Instance
+	if !o.final && !o.concrete {
+		inst = v.instance()
+	}
+	if o.raw {
+		n, _ := export(ctx, inst, v.v.v, o)
+		return n
+	}
+	n, _ := export(ctx, inst, v.v.Value, o)
+	return n
+}
+
+// Decode initializes x with Value v. If x is a struct, it will validate the
+// constraints specified in the field tags.
+func (v Value) Decode(x interface{}) error {
+	// TODO: optimize
+	b, err := v.MarshalJSON()
+	if err != nil {
+		return err
+	}
+	return json.Unmarshal(b, x)
+}
+
+// // EncodeJSON generates JSON for the given value.
+// func (v Value) EncodeJSON(w io.Writer, v Value) error {
+// 	return nil
+// }
+
+// Doc returns all documentation comments associated with the field from which
+// the current value originates.
+func (v Value) Doc() []*ast.CommentGroup {
+	if v.v == nil {
+		return nil
+	}
+	return v.v.docs.appendDocs(nil)
+}
+
+// Split returns a list of values from which v originated such that
+// the unification of all these values equals v and for all returned values.
+// It will also split unchecked unifications (embeddings), so unifying the
+// split values may fail if actually unified.
+// Source returns a non-nil value.
+//
+// Deprecated: use Expr.
+func (v Value) Split() []Value {
+	if v.v == nil {
+		return nil
+	}
+	ctx := v.ctx()
+	a := []Value{}
+	for _, x := range separate(v.v.v) {
+		path := *v.v
+		path.Value = x.evalPartial(ctx)
+		path.v = x
+		a = append(a, Value{v.idx, &path})
+	}
+	return a
+}
+
+func separate(v value) (a []value) {
+	c := v.computed()
+	if c == nil || (c.op != opUnify && c.op != opUnifyUnchecked) {
+		return []value{v}
+	}
+	if c.x != nil {
+		a = append(a, separate(c.x)...)
+	}
+	if c.y != nil {
+		a = append(a, separate(c.y)...)
+	}
+	return a
+}
+
+// Source returns the original node for this value. The return value may not
+// be a syntax.Expr. For instance, a struct kind may be represented by a
+// struct literal, a field comprehension, or a file. It returns nil for
+// computed nodes. Use Split to get all source values that apply to a field.
+func (v Value) Source() ast.Node {
+	if v.v == nil {
+		return nil
+	}
+	return v.v.v.Source()
+}
+
+// Err returns the error represented by v or nil v is not an error.
+func (v Value) Err() error {
+	if err := v.checkKind(v.ctx(), bottomKind); err != nil {
+		return v.toErr(err)
+	}
+	return nil
+}
+
+// Pos returns position information.
+func (v Value) Pos() token.Pos {
+	if v.v == nil || v.Source() == nil {
+		return token.NoPos
+	}
+	pos := v.Source().Pos()
+	return pos
+}
+
+// TODO: IsFinal: this value can never be changed.
+
+// IsClosed reports whether a list of struct is closed. It reports false when
+// when the value is not a list or struct.
+func (v Value) IsClosed() bool {
+	switch v.Kind() {
+	case StructKind:
+		if st, ok := v.v.val().(*structLit); ok {
+			return st.closeStatus.shouldClose()
+		}
+	case ListKind:
+		if l, ok := v.v.val().(*list); ok {
+			if n, ok := l.len.(*numLit); ok {
+				return n.intValue(v.ctx()) == len(l.elem.Arcs)
+			}
+		}
+	}
+	return false
+}
+
+// IsConcrete reports whether the current value is a concrete scalar value
+// (not relying on default values), a terminal error, a list, or a struct.
+// It does not verify that values of lists or structs are concrete themselves.
+// To check whether there is a concrete default, use v.Default().IsConcrete().
+func (v Value) IsConcrete() bool {
+	if v.v == nil {
+		return false // any is neither concrete, not a list or struct.
+	}
+	x := v.v.v.evalPartial(v.ctx())
+
+	// Errors marked as incomplete are treated as not complete.
+	if isIncomplete(x) {
+		return false
+	}
+	// Other errors are considered ground.
+	return x.Kind().isConcrete()
+}
+
+// Deprecated: IsIncomplete
+//
+// It indicates that the value cannot be fully evaluated due to
+// insufficient information.
+func (v Value) IsIncomplete() bool {
+	// TODO: remove
+	x := v.eval(v.ctx())
+	if !x.Kind().isConcrete() {
+		return true
+	}
+	return isIncomplete(x)
+}
+
+// Exists reports whether this value existed in the configuration.
+func (v Value) Exists() bool {
+	if v.v == nil {
+		return false
+	}
+	return exists(v.eval(v.ctx()))
+}
+
+func (v Value) checkKind(ctx *context, want kind) *bottom {
+	if v.v == nil {
+		return errNotExists
+	}
+	// TODO: use checkKind
+	x := v.eval(ctx)
+	if b, ok := x.(*bottom); ok {
+		return b
+	}
+	got := x.Kind()
+	if want != bottomKind {
+		if got&want&concreteKind == bottomKind {
+			return ctx.mkErr(x, "cannot use value %v (type %s) as %s",
+				v.ctx().str(x), got, want)
+		}
+		if !got.isGround() {
+			return ctx.mkErr(x, codeIncomplete,
+				"non-concrete value %v", got)
+		}
+	}
+	return nil
+}
+
+func makeInt(v Value, x int64) Value {
+	return remakeValue(v, newInt(v.v.v.base(), base10).setInt64(x))
+}
+
+// Len returns the number of items of the underlying value.
+// For lists it reports the capacity of the list. For structs it indicates the
+// number of fields, for bytes the number of bytes.
+func (v Value) Len() Value {
+	if v.v != nil {
+		switch x := v.v.v.evalPartial(v.ctx()).(type) {
+		case *list:
+			return remakeValue(v, x.len.evalPartial(v.ctx()))
+		case *bytesLit:
+			return makeInt(v, int64(x.len()))
+		case *stringLit:
+			return makeInt(v, int64(x.len()))
+		}
+	}
+	const msg = "len not supported for type %v"
+	return remakeValue(v, v.ctx().mkErr(v.v.v, msg, v.Kind()))
+}
+
+// Elem returns the value of undefined element types of lists and structs.
+func (v Value) Elem() (Value, bool) {
+	if v.v == nil {
+		return Value{}, false
+	}
+	ctx := v.ctx()
+	switch x := v.v.Value.(type) {
+	case *structLit:
+		t, _ := x.optionals.constraint(ctx, nil)
+		if t == nil {
+			break
+		}
+		return v.makeElem(t), true
+	case *list:
+		return v.makeElem(x.typ), true
+	}
+	return Value{}, false
+}
+
+// BulkOptionals returns all bulk optional fields as key-value pairs.
+// See also Elem and Template.
+func (v Value) BulkOptionals() [][2]Value {
+	x, ok := v.v.Value.(*structLit)
+	if !ok {
+		return nil
+	}
+	return v.appendBulk(nil, x.optionals)
+}
+
+func (v Value) appendBulk(a [][2]Value, x *optionals) [][2]Value {
+	if x == nil {
+		return a
+	}
+	a = v.appendBulk(a, x.left)
+	a = v.appendBulk(a, x.right)
+	for _, set := range x.fields {
+		if set.key != nil {
+			ctx := v.ctx()
+			fn, ok := ctx.manifest(set.value).(*lambdaExpr)
+			if !ok {
+				// create error
+				continue
+			}
+			x := fn.call(ctx, set.value, &basicType{K: stringKind})
+
+			a = append(a, [2]Value{v.makeElem(set.key), v.makeElem(x)})
+		}
+	}
+	return a
+}
+
+// List creates an iterator over the values of a list or reports an error if
+// v is not a list.
+func (v Value) List() (Iterator, error) {
+	v, _ = v.Default()
+	ctx := v.ctx()
+	if err := v.checkKind(ctx, listKind); err != nil {
+		return Iterator{ctx: ctx}, v.toErr(err)
+	}
+	l := v.eval(ctx).(*list)
+	return Iterator{ctx: ctx, val: v, iter: l, len: len(l.elem.Arcs)}, nil
+}
+
+// Null reports an error if v is not null.
+func (v Value) Null() error {
+	v, _ = v.Default()
+	if err := v.checkKind(v.ctx(), nullKind); err != nil {
+		return v.toErr(err)
+	}
+	return nil
+}
+
+// // IsNull reports whether v is null.
+// func (v Value) IsNull() bool {
+// 	return v.Null() == nil
+// }
+
+// Bool returns the bool value of v or false and an error if v is not a boolean.
+func (v Value) Bool() (bool, error) {
+	v, _ = v.Default()
+	ctx := v.ctx()
+	if err := v.checkKind(ctx, boolKind); err != nil {
+		return false, v.toErr(err)
+	}
+	return v.eval(ctx).(*boolLit).B, nil
+}
+
+// String returns the string value if v is a string or an error otherwise.
+func (v Value) String() (string, error) {
+	v, _ = v.Default()
+	ctx := v.ctx()
+	if err := v.checkKind(ctx, stringKind); err != nil {
+		return "", v.toErr(err)
+	}
+	return v.eval(ctx).(*stringLit).Str, nil
+}
+
+// Bytes returns a byte slice if v represents a list of bytes or an error
+// otherwise.
+func (v Value) Bytes() ([]byte, error) {
+	v, _ = v.Default()
+	ctx := v.ctx()
+	switch x := v.eval(ctx).(type) {
+	case *bytesLit:
+		return append([]byte(nil), x.B...), nil
+	case *stringLit:
+		return []byte(x.Str), nil
+	}
+	return nil, v.toErr(v.checkKind(ctx, bytesKind|stringKind))
+}
+
+// Reader returns a new Reader if v is a string or bytes type and an error
+// otherwise.
+func (v Value) Reader() (io.Reader, error) {
+	v, _ = v.Default()
+	ctx := v.ctx()
+	switch x := v.eval(ctx).(type) {
+	case *bytesLit:
+		return bytes.NewReader(x.B), nil
+	case *stringLit:
+		return strings.NewReader(x.Str), nil
+	}
+	return nil, v.toErr(v.checkKind(ctx, stringKind|bytesKind))
+}
+
+// TODO: distinguish between optional, hidden, etc. Probably the best approach
+// is to mark options in context and have a single function for creating
+// a structVal.
+
+// structVal returns an structVal or an error if v is not a struct.
+func (v Value) structValData(ctx *context) (structValue, *bottom) {
+	return v.structValOpts(ctx, options{
+		omitHidden:      true,
+		omitDefinitions: true,
+		omitOptional:    true,
+	})
+}
+
+func (v Value) structValFull(ctx *context) (structValue, *bottom) {
+	return v.structValOpts(ctx, options{})
+}
+
+// structVal returns an structVal or an error if v is not a struct.
+func (v Value) structValOpts(ctx *context, o options) (structValue, *bottom) {
+	v, _ = v.Default() // TODO: remove?
+
+	obj, path, err := v.getStruct()
+	if err != nil {
+		return structValue{}, err
+	}
+
+	// check if any fields can be omitted
+	needFilter := false
+	if o.omitHidden || o.omitOptional || o.omitDefinitions {
+		f := label(0)
+		for _, a := range obj.Arcs {
+			f |= a.Label
+			if a.optional && o.omitOptional {
+				needFilter = true
+				break
+			}
+			if a.definition && (o.omitDefinitions || o.concrete) {
+				needFilter = true
+				break
+			}
+		}
+		needFilter = needFilter || f.IsHidden()
+	}
+
+	if needFilter {
+		arcs := make([]arc, len(obj.Arcs))
+		k := 0
+		for _, a := range obj.Arcs {
+			if a.definition && (o.omitDefinitions || o.concrete) {
+				continue
+			}
+			if a.Label.IsHidden() && o.omitHidden {
+				continue
+			}
+			if o.omitOptional && a.optional {
+				continue
+			}
+			arcs[k] = a
+			k++
+		}
+		arcs = arcs[:k]
+		return structValue{ctx, path, obj, arcs}, nil
+	}
+	return structValue{ctx, path, obj, obj.Arcs}, nil
+}
+
+// Struct returns the underlying struct of a value or an error if the value
+// is not a struct.
+func (v Value) Struct() (*Struct, error) {
+	obj, path, err := v.getStruct()
+	if err != nil {
+		return nil, v.toErr(err)
+	}
+	return &Struct{Value{v.idx, path}, obj}, nil
+}
+
+func (v Value) getStruct() (*structLit, *valueData, *bottom) {
+	ctx := v.ctx()
+	if err := v.checkKind(ctx, structKind); err != nil {
+		return nil, nil, err
+	}
+	orig := v.eval(ctx).(*structLit)
+
+	// TODO: This is expansion appropriate?
+	obj, err := orig.expandFields(ctx)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	path := v.v
+	if obj != orig {
+		p := *path
+		p.arc.Value = obj
+		path = &p
+	}
+
+	return obj, path, nil
+}
+
+// Struct represents a CUE struct value.
+type Struct struct {
+	v Value
+	s *structLit
+}
+
+// FieldInfo contains information about a struct field.
+type FieldInfo struct {
+	Name  string
+	Pos   int
+	Value Value
+
+	IsDefinition bool
+	IsOptional   bool
+	IsHidden     bool
+}
+
+func (s *Struct) Len() int {
+	return len(s.s.Arcs)
+}
+
+// field reports information about the ith field, i < o.Len().
+func (s *Struct) Field(i int) FieldInfo {
+	ctx := s.v.ctx()
+	a := s.s.Arcs[i]
+	a.Value = s.s.at(ctx, i)
+
+	// TODO: adding more technical debt here. The evaluator should be
+	// rewritten.
+	x := s.s
+	if x.optionals != nil {
+		name := ctx.LabelStr(x.Arcs[i].Label)
+		arg := &stringLit{x.baseValue, name, nil}
+
+		val, _ := x.optionals.constraint(ctx, arg)
+		if val != nil {
+			a.v = mkBin(ctx, x.Pos(), opUnify, a.v, val)
+		}
+	}
+
+	v := Value{ctx.index, &valueData{s.v.v, uint32(i), a}}
+	str := ctx.LabelStr(a.Label)
+	return FieldInfo{str, i, v, a.definition, a.optional, a.Label.IsHidden()}
+}
+
+// FieldByName looks up a field for the given name. If isIdent is true, it will
+// look up a definition or hidden field (starting with `_` or `_#`). Otherwise
+// it interprets name as an arbitrary string for a regular field.
+func (s *Struct) FieldByName(name string, isIdent bool) (FieldInfo, error) {
+	f := s.v.ctx().Label(name, isIdent)
+	for i, a := range s.s.Arcs {
+		if a.Label == f {
+			return s.Field(i), nil
+		}
+	}
+	return FieldInfo{}, errNotFound
+}
+
+// Fields creates an iterator over the Struct's fields.
+func (s *Struct) Fields(opts ...Option) *Iterator {
+	iter, _ := s.v.Fields(opts...)
+	return iter
+}
+
+// Fields creates an iterator over v's fields if v is a struct or an error
+// otherwise.
+func (v Value) Fields(opts ...Option) (*Iterator, error) {
+	o := options{omitDefinitions: true, omitHidden: true, omitOptional: true}
+	o.updateOptions(opts)
+	ctx := v.ctx()
+	obj, err := v.structValOpts(ctx, o)
+	if err != nil {
+		return &Iterator{ctx: ctx}, v.toErr(err)
+	}
+	n := &structLit{
+		obj.obj.baseValue,   // baseValue
+		obj.obj.emit,        // emit
+		obj.obj.optionals,   // template
+		obj.obj.closeStatus, // closeStatus
+		nil,                 // comprehensions
+		obj.Arcs,            // arcs
+		nil,                 // attributes
+	}
+	return &Iterator{ctx: ctx, val: v, iter: n, len: len(n.Arcs)}, nil
+}
+
+// Lookup reports the value at a path starting from v. The empty path returns v
+// itself. Use LookupDef for definitions or LookupField for any kind of field.
+//
+// The Exists() method can be used to verify if the returned value existed.
+// Lookup cannot be used to look up hidden or optional fields or definitions.
+func (v Value) Lookup(path ...string) Value {
+	ctx := v.ctx()
+	for _, k := range path {
+		// TODO(eval) TODO(error): always search in full data and change error
+		// message if a field is found but is of the incorrect type.
+		obj, err := v.structValData(ctx)
+		if err != nil {
+			// TODO: return a Value at the same location and a new error?
+			return newErrValue(v, err)
+		}
+		v = obj.Lookup(k)
+	}
+	return v
+}
+
+// LookupDef reports the definition with the given name within struct v. The
+// Exists method of the returned value will report false if the definition did
+// not exist. The Err method reports if any error occurred during evaluation.
+func (v Value) LookupDef(name string) Value {
+	ctx := v.ctx()
+	o, err := v.structValFull(ctx)
+	if err != nil {
+		return newErrValue(v, err)
+	}
+
+	f := v.ctx().Label(name, true)
+	for i, a := range o.Arcs {
+		if a.Label == f {
+			if f.IsHidden() || !a.definition || a.optional {
+				break
+			}
+			return newChildValue(&o, i)
+		}
+	}
+	if !strings.HasPrefix(name, "#") {
+		alt := v.LookupDef("#" + name)
+		// Use the original error message if this resulted in an error as well.
+		if alt.Err() == nil {
+			return alt
+		}
+	}
+	return newErrValue(v, ctx.mkErr(v.v.v,
+		"definition %q not found", name))
+}
+
+var errNotFound = errors.Newf(token.NoPos, "field not found")
+
+// FieldByName looks up a field for the given name. If isIdent is true, it will
+// look up a definition or hidden field (starting with `_` or `_#`). Otherwise
+// it interprets name as an arbitrary string for a regular field.
+func (v Value) FieldByName(name string, isIdent bool) (f FieldInfo, err error) {
+	s, err := v.Struct()
+	if err != nil {
+		return f, err
+	}
+	return s.FieldByName(name, isIdent)
+}
+
+// LookupField reports information about a field of v.
+//
+// Deprecated: this API does not work with new-style definitions. Use FieldByName.
+func (v Value) LookupField(name string) (FieldInfo, error) {
+	s, err := v.Struct()
+	if err != nil {
+		// TODO: return a Value at the same location and a new error?
+		return FieldInfo{}, err
+	}
+	f, err := s.FieldByName(name, true)
+	if err != nil {
+		return f, err
+	}
+	if f.IsHidden {
+		return f, errNotFound
+	}
+	return f, err
+}
+
+// TODO: expose this API?
+//
+// // EvalExpr evaluates an expression within the scope of v, which must be
+// // a struct.
+// //
+// // Expressions may refer to builtin packages if they can be uniquely identified.
+// func (v Value) EvalExpr(expr ast.Expr) Value {
+// 	ctx := v.ctx()
+// 	result := evalExpr(ctx, v.eval(ctx), expr)
+// 	return newValueRoot(ctx, result)
+// }
+
+// Fill creates a new value by unifying v with the value of x at the given path.
+//
+// Values may be any Go value that can be converted to CUE, an ast.Expr or
+// a Value. In the latter case, it will panic if the Value is not from the same
+// Runtime.
+//
+// Any reference in v referring to the value at the given path will resolve
+// to x in the newly created value. The resulting value is not validated.
+func (v Value) Fill(x interface{}, path ...string) Value {
+	if v.v == nil {
+		return v
+	}
+	ctx := v.ctx()
+	root := v.v.val()
+	for i := len(path) - 1; i >= 0; i-- {
+		x = map[string]interface{}{path[i]: x}
+	}
+	value := convertVal(ctx, root, true, x)
+	a := v.v.arc
+	a.v = mkBin(ctx, v.Pos(), opUnify, root, value)
+	a.Value = a.v.evalPartial(ctx)
+	// TODO: validate recursively?
+	return Value{v.idx, &valueData{v.v.parent, v.v.index, a}}
+}
+
+// Template returns a function that represents the template definition for a
+// struct in a configuration file. It returns nil if v is not a struct kind or
+// if there is no template associated with the struct.
+//
+// The returned function returns the value that would be unified with field
+// given its name.
+func (v Value) Template() func(label string) Value {
+	// TODO: rename to optional.
+	if v.v == nil {
+		return nil
+	}
+
+	ctx := v.ctx()
+	x, ok := v.v.Value.(*structLit)
+	if !ok || x.optionals.isEmpty() {
+		return nil
+	}
+
+	return func(label string) Value {
+		arg := &stringLit{x.baseValue, label, nil}
+
+		if val, _ := x.optionals.constraint(ctx, arg); val != nil {
+			return remakeValue(v, val)
+		}
+		return Value{}
+	}
+}
+
+// Subsume reports nil when w is an instance of v or an error otherwise.
+//
+// Without options, the entire value is considered for assumption, which means
+// Subsume tests whether  v is a backwards compatible (newer) API version of w.
+// Use the Final() to indicate that the subsumed value is data, and that
+//
+// Use the Final option to check subsumption if a w is known to be final,
+// and should assumed to be closed.
+//
+// Options are currently ignored and the function will panic if any are passed.
+//
+// Value v and w must be obtained from the same build.
+// TODO: remove this requirement.
+func (v Value) Subsume(w Value, opts ...Option) error {
+	var mode subsumeMode
+	o := getOptions(opts)
+	if o.final {
+		mode |= subFinal | subChoose
+	}
+	if o.ignoreClosedness {
+		mode |= subSchema
+	}
+	return subsumes(v, w, mode)
+}
+
+// Deprecated: use Subsume.
+//
+// Subsumes reports whether w is an instance of v.
+//
+// Without options, Subsumes checks whether v is a backwards compatbile schema
+// of w.
+//
+// By default, Subsumes tests whether two values are compatib
+// Value v and w must be obtained from the same build.
+// TODO: remove this requirement.
+func (v Value) Subsumes(w Value) bool {
+	return subsumes(v, w, subChoose) == nil
+}
+
+// Unify reports the greatest lower bound of v and w.
+//
+// Value v and w must be obtained from the same build.
+// TODO: remove this requirement.
+func (v Value) Unify(w Value) Value {
+	ctx := v.ctx()
+	if v.v == nil {
+		return w
+	}
+	if w.v == nil {
+		return v
+	}
+	if v.Err() != nil {
+		// TODO: perhaps keep both errors.
+		return v
+	}
+	if w.Err() != nil {
+		return w
+	}
+	a := v.v.v
+	b := w.v.v
+	src := binSrc(token.NoPos, opUnify, a, b)
+	val := mkBin(ctx, src.Pos(), opUnify, a, b)
+	u := remakeValue(v, val)
+	if err := u.Validate(); err != nil {
+		u = newValueRoot(ctx, ctx.mkErr(src, err))
+	}
+	return u
+}
+
+// Equals reports whether two values are equal, ignoring optional fields.
+// The result is undefined for incomplete values.
+func (v Value) Equals(other Value) bool {
+	if v.v == nil || other.v == nil {
+		return false
+	}
+	x := v.v.val()
+	y := other.v.val()
+	return equals(v.ctx(), x, y)
+}
+
+// Format prints a debug version of a value.
+func (v Value) Format(state fmt.State, verb rune) {
+	ctx := v.ctx()
+	if v.v == nil {
+		fmt.Fprint(state, "<nil>")
+		return
+	}
+	switch {
+	case state.Flag('#'):
+		_, _ = io.WriteString(state, ctx.str(v.v.v))
+	case state.Flag('+'):
+		_, _ = io.WriteString(state, debugStr(ctx, v.v.v))
+	default:
+		_, _ = io.WriteString(state, ctx.str(v.v.Value))
+	}
+}
+
+func (v Value) instance() *Instance {
+	if v.v == nil {
+		return nil
+	}
+	return v.ctx().getImportFromNode(v.v.v)
+}
+
+// Reference returns the instance and path referred to by this value such that
+// inst.Lookup(path) resolves to the same value, or no path if this value is not
+// a reference. If a reference contains index selection (foo[bar]), it will
+// only return a reference if the index resolves to a concrete value.
+func (v Value) Reference() (inst *Instance, path []string) {
+	// TODO: don't include references to hidden fields.
+	if v.v == nil {
+		return nil, nil
+	}
+	ctx := v.ctx()
+	var x value
+	var feature string
+	switch sel := v.v.v.(type) {
+	case *selectorExpr:
+		x = sel.X
+		feature = ctx.LabelStr(sel.Sel)
+
+	case *indexExpr:
+		e := sel.Index.evalPartial(ctx)
+		s, ok := e.(*stringLit)
+		if !ok {
+			return nil, nil
+		}
+		x = sel.X
+		feature = s.Str
+
+	default:
+		return nil, nil
+	}
+	imp, a := mkPath(ctx, v.v, x, feature, 0)
+	return imp, a
+}
+
+func mkPath(c *context, up *valueData, x value, feature string, d int) (imp *Instance, a []string) {
+	switch x := x.(type) {
+	case *selectorExpr:
+		imp, a = mkPath(c, up, x.X, c.LabelStr(x.Sel), d+1)
+		if imp == nil {
+			return nil, nil
+		}
+
+	case *indexExpr:
+		e := x.Index.evalPartial(c)
+		s, ok := e.(*stringLit)
+		if !ok {
+			return nil, nil
+		}
+		imp, a = mkPath(c, up, x.X, s.Str, d+1)
+		if imp == nil {
+			return nil, nil
+		}
+
+	case *nodeRef:
+		// the parent must exist.
+		var v value
+		if p := locateNode(up, x); p != nil {
+			v, a = mkFromRoot(c, p, d+2)
+		} else {
+			// Either this references another parent, or it is an embedding.
+			imp = c.getImportFromNode(x.node)
+			if imp != nil {
+				break
+			}
+			// This must be an embedding, go one up.
+			v, a = mkFromRoot(c, up.parent, d+2)
+		}
+		if v == nil {
+			v = x.node
+		}
+		imp = c.getImportFromNode(v)
+	default:
+		return nil, nil
+	}
+	return imp, append(a, feature)
+}
+
+func mkFromRoot(c *context, up *valueData, d int) (root value, a []string) {
+	if up == nil {
+		return nil, make([]string, 0, d)
+	}
+	root, a = mkFromRoot(c, up.parent, d+1)
+	if up.parent != nil {
+		a = append(a, c.LabelStr(up.Label))
+	} else {
+		root = up.v
+	}
+	return root, a
+}
+
+// References reports all references used to evaluate this value. It does not
+// report references for sub fields if v is a struct.
+//
+// Deprecated: can be implemented in terms of Reference and Expr.
+func (v Value) References() [][]string {
+	// TODO: the pathFinder algorithm is quite broken. Using Reference and Expr
+	// will cast a much more accurate net on referenced values.
+	ctx := v.ctx()
+	pf := pathFinder{up: v.v}
+	raw := v.v.v
+	if raw == nil {
+		return nil
+	}
+	rewrite(ctx, raw, pf.find)
+	return pf.paths
+}
+
+type pathFinder struct {
+	paths [][]string
+	stack []label
+	up    *valueData
+}
+
+func (p *pathFinder) find(ctx *context, v value) (value, bool) {
+	switch x := v.(type) {
+	case *selectorExpr:
+		i := len(p.stack)
+		p.stack = append(p.stack, x.Sel)
+		rewrite(ctx, x.X, p.find)
+		p.stack = p.stack[:i]
+		return v, false
+
+	case *nodeRef:
+		i := len(p.stack)
+		up := p.up
+		for ; up != nil && up.Value != x.node.(value); up = up.parent {
+		}
+		for ; up != nil && up.Label > 0; up = up.parent {
+			p.stack = append(p.stack, up.Label)
+		}
+		path := make([]string, len(p.stack))
+		for i, v := range p.stack {
+			path[len(path)-1-i] = ctx.LabelStr(v)
+		}
+		p.paths = append(p.paths, path)
+		p.stack = p.stack[:i]
+		return v, false
+
+	case *structLit:
+		// If the stack is empty, we do not descend, as we are not evaluating
+		// sub fields.
+		if len(p.stack) == 0 {
+			return v, false
+		}
+
+		stack := p.stack
+		p.stack = nil
+		for _, a := range x.Arcs {
+			rewrite(ctx, a.v, p.find)
+		}
+		p.stack = stack
+		return v, false
+	}
+	return v, true
+}
+
+type options struct {
+	concrete          bool // enforce that values are concrete
+	raw               bool // show original values
+	hasHidden         bool
+	omitHidden        bool
+	omitDefinitions   bool
+	omitOptional      bool
+	omitAttrs         bool
+	resolveReferences bool
+	final             bool
+	ignoreClosedness  bool // used for comparing APIs
+	docs              bool
+	disallowCycles    bool // implied by concrete
+}
+
+// An Option defines modes of evaluation.
+type Option option
+
+type option func(p *options)
+
+// Final indicates a value is final. It implicitly closes all structs and lists
+// in a value and selects defaults.
+func Final() Option {
+	return func(o *options) {
+		o.final = true
+		o.omitDefinitions = true
+		o.omitOptional = true
+		o.omitHidden = true
+	}
+}
+
+// Schema specifies the input is a Schema. Used by Subsume.
+func Schema() Option {
+	return func(o *options) {
+		o.ignoreClosedness = true
+	}
+}
+
+// Concrete ensures that all values are concrete.
+//
+// For Validate this means it returns an error if this is not the case.
+// In other cases a non-concrete value will be replaced with an error.
+func Concrete(concrete bool) Option {
+	return func(p *options) {
+		if concrete {
+			p.concrete = true
+			p.final = true
+			if !p.hasHidden {
+				p.omitHidden = true
+				p.omitDefinitions = true
+			}
+		}
+	}
+}
+
+// DisallowCycles forces validation in the precense of cycles, even if
+// non-concrete values are allowed. This is implied by Concrete(true).
+func DisallowCycles(disallow bool) Option {
+	return func(p *options) { p.disallowCycles = disallow }
+}
+
+// ResolveReferences forces the evaluation of references when outputting.
+// This implies the input cannot have cycles.
+func ResolveReferences(resolve bool) Option {
+	return func(p *options) { p.resolveReferences = resolve }
+}
+
+// Raw tells Syntax to generate the value as is without any simplifications.
+func Raw() Option {
+	return func(p *options) { p.raw = true }
+}
+
+// All indicates that all fields and values should be included in processing
+// even if they can be elided or omitted.
+func All() Option {
+	return func(p *options) {
+		p.omitAttrs = false
+		p.omitHidden = false
+		p.omitDefinitions = false
+		p.omitOptional = false
+	}
+}
+
+// Docs indicates whether docs should be included.
+func Docs(include bool) Option {
+	return func(p *options) { p.docs = true }
+}
+
+// Definitions indicates whether definitions should be included.
+//
+// Definitions may still be included for certain functions if they are referred
+// to by other other values.
+func Definitions(include bool) Option {
+	return func(p *options) {
+		p.hasHidden = true
+		p.omitDefinitions = !include
+	}
+}
+
+// Hidden indicates that definitions and hidden fields should be included.
+//
+// Deprecated: Hidden fields are deprecated.
+func Hidden(include bool) Option {
+	return func(p *options) {
+		p.hasHidden = true
+		p.omitHidden = !include
+		p.omitDefinitions = !include
+	}
+}
+
+// Optional indicates that optional fields should be included.
+func Optional(include bool) Option {
+	return func(p *options) { p.omitOptional = !include }
+}
+
+// Attributes indicates that attributes should be included.
+func Attributes(include bool) Option {
+	return func(p *options) { p.omitAttrs = !include }
+}
+
+func getOptions(opts []Option) (o options) {
+	o.updateOptions(opts)
+	return
+}
+
+func (o *options) updateOptions(opts []Option) {
+	for _, fn := range opts {
+		fn(o)
+	}
+}
+
+// Validate reports any errors, recursively. The returned error may represent
+// more than one error, retrievable with errors.Errors, if more than one
+// exists.
+func (v Value) Validate(opts ...Option) error {
+	x := validator{}
+	o := options{}
+	o.updateOptions(opts)
+	// Logically, errors are always permitted in logical fields, so we
+	// force-disable them.
+	// TODO: consider whether we should honor the option to allow checking
+	// optional fields.
+	o.omitOptional = true
+	x.walk(v, o)
+	return errors.Sanitize(x.errs)
+}
+
+type validator struct {
+	errs  errors.Error
+	depth int
+}
+
+func (x *validator) before(v Value, o options) bool {
+	if err := v.checkKind(v.ctx(), bottomKind); err != nil {
+		if !o.concrete && isIncomplete(err) {
+			if o.disallowCycles && err.Code == codeCycle {
+				x.errs = errors.Append(x.errs, v.toErr(err))
+			}
+			return false
+		}
+		x.errs = errors.Append(x.errs, v.toErr(err))
+		if len(errors.Errors(x.errs)) > 50 {
+			return false // mostly to avoid some hypothetical cycle issue
+		}
+	}
+	if o.concrete {
+		ctx := v.ctx()
+		if err := isGroundRecursive(ctx, v.eval(ctx)); err != nil {
+			x.errs = errors.Append(x.errs, v.toErr(err))
+		}
+	}
+	return true
+}
+
+func (x *validator) walk(v Value, opts options) {
+	// TODO(#42): we can get rid of the arbitrary evaluation depth once CUE has
+	// proper structural cycle detection. See Issue #42. Currently errors
+	// occurring at a depth > internal.MaxDepth will not be detected.
+	if x.depth > internal.MaxDepth {
+		return
+	}
+	ctx := v.ctx()
+	switch v.Kind() {
+	case StructKind:
+		if !x.before(v, opts) {
+			return
+		}
+		x.depth++
+		obj, err := v.structValOpts(ctx, opts)
+		if err != nil {
+			if !isIncomplete(err) && opts.concrete {
+				x.errs = errors.Append(x.errs, v.toErr(err))
+			}
+		}
+		for i := 0; i < obj.Len(); i++ {
+			_, v := obj.At(i)
+			opts := opts
+			if obj.Arcs[i].definition {
+				opts.concrete = false
+			}
+			x.walk(v, opts)
+		}
+		x.depth--
+
+	case ListKind:
+		if !x.before(v, opts) {
+			return
+		}
+		x.depth++
+		list, _ := v.List()
+		for list.Next() {
+			x.walk(list.Value(), opts)
+		}
+		x.depth--
+
+	default:
+		x.before(v, opts)
+	}
+}
+
+func isGroundRecursive(ctx *context, v value) *bottom {
+	switch x := v.(type) {
+	case *bottom:
+		if isIncomplete(x) {
+			return x
+		}
+	case *list:
+		for i := 0; i < len(x.elem.Arcs); i++ {
+			v := ctx.manifest(x.at(ctx, i))
+			if err := isGroundRecursive(ctx, v); err != nil {
+				return err
+			}
+		}
+	default:
+		if !x.Kind().isGround() {
+			return ctx.mkErr(v, "incomplete value (%v)", ctx.str(v))
+		}
+	}
+	return nil
+}
+
+// Walk descends into all values of v, calling f. If f returns false, Walk
+// will not descent further. It only visits values that are part of the data
+// model, so this excludes optional fields, hidden fields, and definitions.
+func (v Value) Walk(before func(Value) bool, after func(Value)) {
+	ctx := v.ctx()
+	switch v.Kind() {
+	case StructKind:
+		if before != nil && !before(v) {
+			return
+		}
+		obj, _ := v.structValData(ctx)
+		for i := 0; i < obj.Len(); i++ {
+			_, v := obj.At(i)
+			v.Walk(before, after)
+		}
+	case ListKind:
+		if before != nil && !before(v) {
+			return
+		}
+		list, _ := v.List()
+		for list.Next() {
+			list.Value().Walk(before, after)
+		}
+	default:
+		if before != nil {
+			before(v)
+		}
+	}
+	if after != nil {
+		after(v)
+	}
+}
+
+// Attribute returns the attribute data for the given key.
+// The returned attribute will return an error for any of its methods if there
+// is no attribute for the requested key.
+func (v Value) Attribute(key string) Attribute {
+	// look up the attributes
+	if v.v == nil || v.v.attrs == nil {
+		return Attribute{internal.NewNonExisting(key)}
+	}
+	for _, a := range v.v.attrs.attr {
+		if a.key() != key {
+			continue
+		}
+		return Attribute{internal.ParseAttrBody(token.NoPos, a.body())}
+	}
+	return Attribute{internal.NewNonExisting(key)}
+}
+
+// An Attribute contains meta data about a field.
+type Attribute struct {
+	attr internal.Attr
+}
+
+// Err returns the error associated with this Attribute or nil if this
+// attribute is valid.
+func (a *Attribute) Err() error {
+	return a.attr.Err
+}
+
+// String reports the possibly empty string value at the given position or
+// an error the attribute is invalid or if the position does not exist.
+func (a *Attribute) String(pos int) (string, error) {
+	return a.attr.String(pos)
+}
+
+// Int reports the integer at the given position or an error if the attribute is
+// invalid, the position does not exist, or the value at the given position is
+// not an integer.
+func (a *Attribute) Int(pos int) (int64, error) {
+	return a.attr.Int(pos)
+}
+
+// Flag reports whether an entry with the given name exists at position pos or
+// onwards or an error if the attribute is invalid or if the first pos-1 entries
+// are not defined.
+func (a *Attribute) Flag(pos int, key string) (bool, error) {
+	return a.attr.Flag(pos, key)
+}
+
+// Lookup searches for an entry of the form key=value from position pos onwards
+// and reports the value if found. It reports an error if the attribute is
+// invalid or if the first pos-1 entries are not defined.
+func (a *Attribute) Lookup(pos int, key string) (val string, found bool, err error) {
+	return a.attr.Lookup(pos, key)
+}
+
+// Expr reports the operation of the underlying expression and the values it
+// operates on.
+//
+// For unary expressions, it returns the single value of the expression.
+//
+// For binary expressions it returns first the left and right value, in that
+// order. For associative operations however, (for instance '&' and '|'), it may
+// return more than two values, where the operation is to be applied in
+// sequence.
+//
+// For selector and index expressions it returns the subject and then the index.
+// For selectors, the index is the string value of the identifier.
+//
+// For interpolations it returns a sequence of values to be concatenated, some
+// of which will be literal strings and some unevaluated expressions.
+//
+// A builtin call expression returns the value of the builtin followed by the
+// args of the call.
+func (v Value) Expr() (Op, []Value) {
+	// TODO: return v if this is complete? Yes for now
+	if v.v == nil {
+		return NoOp, nil
+	}
+	// TODO: replace appends with []Value{}. For not leave.
+	a := []Value{}
+	op := NoOp
+	switch x := v.v.v.(type) {
+	case *binaryExpr:
+		a = append(a, remakeValue(v, x.X))
+		a = append(a, remakeValue(v, x.Y))
+		op = opToOp[x.Op]
+	case *unaryExpr:
+		a = append(a, remakeValue(v, x.X))
+		op = opToOp[x.Op]
+	case *bound:
+		a = append(a, remakeValue(v, x.Expr))
+		op = opToOp[x.Op]
+	case *unification:
+		// pre-expanded unification
+		for _, conjunct := range x.Values {
+			a = append(a, remakeValue(v, conjunct))
+		}
+		op = AndOp
+	case *disjunction:
+		// Filter defaults that are subsumed by another value.
+		count := 0
+	outer:
+		for _, disjunct := range x.Values {
+			if disjunct.Default {
+				for _, n := range x.Values {
+					s := subsumer{ctx: v.ctx()}
+					if !n.Default && s.subsumes(n.Val, disjunct.Val) {
+						continue outer
+					}
+				}
+			}
+			count++
+			a = append(a, remakeValue(v, disjunct.Val))
+		}
+		if count > 1 {
+			op = OrOp
+		}
+	case *interpolation:
+		for _, p := range x.Parts {
+			a = append(a, remakeValue(v, p))
+		}
+		op = InterpolationOp
+	case *selectorExpr:
+		a = append(a, remakeValue(v, x.X))
+		a = append(a, remakeValue(v, &stringLit{
+			x.baseValue,
+			v.ctx().LabelStr(x.Sel),
+			nil,
+		}))
+		op = SelectorOp
+	case *indexExpr:
+		a = append(a, remakeValue(v, x.X))
+		a = append(a, remakeValue(v, x.Index))
+		op = IndexOp
+	case *sliceExpr:
+		a = append(a, remakeValue(v, x.X))
+		a = append(a, remakeValue(v, x.Lo))
+		a = append(a, remakeValue(v, x.Hi))
+		op = SliceOp
+	case *callExpr:
+		a = append(a, remakeValue(v, x.Fun))
+		for _, arg := range x.Args {
+			a = append(a, remakeValue(v, arg))
+		}
+		op = CallOp
+	case *customValidator:
+		a = append(a, remakeValue(v, x.Builtin))
+		for _, arg := range x.Args {
+			a = append(a, remakeValue(v, arg))
+		}
+		op = CallOp
+	default:
+		a = append(a, v)
+	}
+	return op, a
+}
diff --git a/internal/legacy/cue/types_test.go b/internal/legacy/cue/types_test.go
new file mode 100644
index 0000000..cea8a14
--- /dev/null
+++ b/internal/legacy/cue/types_test.go
@@ -0,0 +1,2783 @@
+// Copyright 2018 The CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cue
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"math"
+	"math/big"
+	"reflect"
+	"strconv"
+	"strings"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/internal"
+)
+
+func getInstance(t *testing.T, body ...string) *Instance {
+	t.Helper()
+
+	insts := Build(makeInstances([]*bimport{{files: body}}))
+	if insts[0].Err != nil {
+		t.Fatalf("unexpected parse error: %v", insts[0].Err)
+	}
+	return insts[0]
+}
+
+func TestValueType(t *testing.T) {
+	testCases := []struct {
+		value          string
+		kind           Kind
+		incompleteKind Kind
+		json           string
+		valid          bool
+		concrete       bool
+		closed         bool
+		// pos            token.Pos
+	}{{ // Not a concrete value.
+		value:          `v: _`,
+		kind:           BottomKind,
+		incompleteKind: TopKind,
+	}, {
+		value:          `v: _|_`,
+		kind:           BottomKind,
+		incompleteKind: BottomKind,
+		concrete:       true,
+	}, {
+		value:          `v: 1&2`,
+		kind:           BottomKind,
+		incompleteKind: BottomKind,
+		concrete:       true,
+	}, {
+		value:          `v: b, b: 1&2`,
+		kind:           BottomKind,
+		incompleteKind: BottomKind,
+		concrete:       true,
+	}, {
+		value:          `v: (b[a]), b: 1, a: 1`,
+		kind:           BottomKind,
+		incompleteKind: BottomKind,
+		concrete:       true,
+	}, { // TODO: should be error{
+		value: `v: (b)
+			b: bool`,
+		kind:           BottomKind,
+		incompleteKind: BoolKind,
+	}, {
+		value:          `v: ([][b]), b: "d"`,
+		kind:           BottomKind,
+		incompleteKind: BottomKind,
+		concrete:       true,
+	}, {
+		value:          `v: null`,
+		kind:           NullKind,
+		incompleteKind: NullKind,
+		concrete:       true,
+	}, {
+		value:          `v: true`,
+		kind:           BoolKind,
+		incompleteKind: BoolKind,
+		concrete:       true,
+	}, {
+		value:          `v: false`,
+		kind:           BoolKind,
+		incompleteKind: BoolKind,
+		concrete:       true,
+	}, {
+		value:          `v: bool`,
+		kind:           BottomKind,
+		incompleteKind: BoolKind,
+	}, {
+		value:          `v: 2`,
+		kind:           IntKind,
+		incompleteKind: IntKind,
+		concrete:       true,
+	}, {
+		value:          `v: 2.0`,
+		kind:           FloatKind,
+		incompleteKind: FloatKind,
+		concrete:       true,
+	}, {
+		value:          `v: 2.0Mi`,
+		kind:           IntKind,
+		incompleteKind: IntKind,
+		concrete:       true,
+	}, {
+		value:          `v: 14_000`,
+		kind:           IntKind,
+		incompleteKind: IntKind,
+		concrete:       true,
+	}, {
+		value:          `v: >=0 & <5`,
+		kind:           BottomKind,
+		incompleteKind: NumberKind,
+	}, {
+		value:          `v: float`,
+		kind:           BottomKind,
+		incompleteKind: FloatKind,
+	}, {
+		value:          `v: "str"`,
+		kind:           StringKind,
+		incompleteKind: StringKind,
+		concrete:       true,
+	}, {
+		value:          "v: '''\n'''",
+		kind:           BytesKind,
+		incompleteKind: BytesKind,
+		concrete:       true,
+	}, {
+		value:          "v: string",
+		kind:           BottomKind,
+		incompleteKind: StringKind,
+	}, {
+		value:          `v: {}`,
+		kind:           StructKind,
+		incompleteKind: StructKind,
+		concrete:       true,
+	}, {
+		value:          `v: close({})`,
+		kind:           StructKind,
+		incompleteKind: StructKind,
+		concrete:       true,
+		closed:         true,
+	}, {
+		value:          `v: []`,
+		kind:           ListKind,
+		incompleteKind: ListKind,
+		concrete:       true,
+		closed:         true,
+	}, {
+		value:          `v: [...int]`,
+		kind:           BottomKind,
+		incompleteKind: ListKind,
+		concrete:       false,
+	}, {
+		value:    `v: {a: int, b: [1][a]}.b`,
+		kind:     BottomKind,
+		concrete: false,
+	}, {
+		value: `import "time"
+		v: {a: time.Time}.a`,
+		kind:           BottomKind,
+		incompleteKind: StringKind,
+		concrete:       false,
+	}, {
+		value: `import "time"
+			v: {a: time.Time & string}.a`,
+		kind:           BottomKind,
+		incompleteKind: StringKind,
+		concrete:       false,
+	}, {
+		value: `import "strings"
+			v: {a: strings.ContainsAny("D")}.a`,
+		kind:           BottomKind,
+		incompleteKind: StringKind,
+		concrete:       false,
+	}, {
+		value: `import "struct"
+		v: {a: struct.MaxFields(2) & {}}.a`,
+		kind:           StructKind, // Can determine a valid struct already.
+		incompleteKind: StructKind,
+		concrete:       true,
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			inst := getInstance(t, tc.value)
+			v := inst.Lookup("v")
+			if got := v.Kind(); got != tc.kind {
+				t.Errorf("Kind: got %x; want %x", int(got), int(tc.kind))
+			}
+			want := tc.incompleteKind | BottomKind
+			if got := v.IncompleteKind(); got != want {
+				t.Errorf("IncompleteKind: got %x; want %x", int(got), int(want))
+			}
+			if got := v.IsConcrete(); got != tc.concrete {
+				t.Errorf("IsConcrete: got %v; want %v", got, tc.concrete)
+			}
+			if got := v.IsClosed(); got != tc.closed {
+				t.Errorf("IsClosed: got %v; want %v", got, tc.closed)
+			}
+		})
+	}
+}
+
+func TestInt(t *testing.T) {
+	testCases := []struct {
+		value  string
+		int    int64
+		uint   uint64
+		base   int
+		err    string
+		errU   string
+		notInt bool
+	}{{
+		value: "1",
+		int:   1,
+		uint:  1,
+	}, {
+		value: "-1",
+		int:   -1,
+		uint:  0,
+		errU:  ErrAbove.Error(),
+	}, {
+		value: "-111222333444555666777888999000",
+		int:   math.MinInt64,
+		uint:  0,
+		err:   ErrAbove.Error(),
+		errU:  ErrAbove.Error(),
+	}, {
+		value: "111222333444555666777888999000",
+		int:   math.MaxInt64,
+		uint:  math.MaxUint64,
+		err:   ErrBelow.Error(),
+		errU:  ErrBelow.Error(),
+	}, {
+		value:  "1.0",
+		err:    "cannot use value 1.0 (type float) as int",
+		errU:   "cannot use value 1.0 (type float) as int",
+		notInt: true,
+	}, {
+		value:  "int",
+		err:    "non-concrete value int",
+		errU:   "non-concrete value int",
+		notInt: true,
+	}, {
+		value:  "_|_",
+		err:    "from source",
+		errU:   "from source",
+		notInt: true,
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			n := getInstance(t, tc.value).Value()
+			base := 10
+			if tc.base > 0 {
+				base = tc.base
+			}
+			b, err := n.AppendInt(nil, base)
+			if checkFailed(t, err, tc.err, "append") {
+				want := tc.value
+				if got := string(b); got != want {
+					t.Errorf("append: got %v; want %v", got, want)
+				}
+			}
+
+			vi, err := n.Int64()
+			checkErr(t, err, tc.err, "Int64")
+			if vi != tc.int {
+				t.Errorf("Int64: got %v; want %v", vi, tc.int)
+			}
+
+			vu, err := n.Uint64()
+			checkErr(t, err, tc.errU, "Uint64")
+			if vu != uint64(tc.uint) {
+				t.Errorf("Uint64: got %v; want %v", vu, tc.uint)
+			}
+		})
+	}
+}
+
+func TestFloat(t *testing.T) {
+	testCases := []struct {
+		value   string
+		float   string
+		float64 float64
+		mant    string
+		exp     int
+		fmt     byte
+		prec    int
+		kind    Kind
+		err     string
+	}{{
+		value:   "1",
+		float:   "1",
+		mant:    "1",
+		exp:     0,
+		float64: 1,
+		fmt:     'g',
+		kind:    IntKind,
+	}, {
+		value:   "-1",
+		float:   "-1",
+		mant:    "-1",
+		exp:     0,
+		float64: -1,
+		fmt:     'g',
+		kind:    IntKind,
+	}, {
+		value:   "1.0",
+		float:   "1.0",
+		mant:    "10",
+		exp:     -1,
+		float64: 1.0,
+		fmt:     'g',
+		kind:    FloatKind,
+	}, {
+		value:   "2.6",
+		float:   "2.6",
+		mant:    "26",
+		exp:     -1,
+		float64: 2.6,
+		fmt:     'g',
+		kind:    FloatKind,
+	}, {
+		value:   "20.600",
+		float:   "20.60",
+		mant:    "20600",
+		exp:     -3,
+		float64: 20.60,
+		prec:    2,
+		fmt:     'f',
+		kind:    FloatKind,
+	}, {
+		value:   "1/0",
+		float:   "",
+		float64: 0,
+		prec:    2,
+		fmt:     'f',
+		err:     "division by zero",
+		kind:    BottomKind,
+	}, {
+		value:   "1.797693134862315708145274237317043567982e+308",
+		float:   "1.8e+308",
+		mant:    "1797693134862315708145274237317043567982",
+		exp:     269,
+		float64: math.Inf(1),
+		prec:    2,
+		fmt:     'g',
+		err:     ErrAbove.Error(),
+		kind:    FloatKind,
+	}, {
+		value:   "-1.797693134862315708145274237317043567982e+308",
+		float:   "-1.8e+308",
+		mant:    "-1797693134862315708145274237317043567982",
+		exp:     269,
+		float64: math.Inf(-1),
+		prec:    2,
+		fmt:     'g',
+		kind:    FloatKind,
+		err:     ErrBelow.Error(),
+	}, {
+		value:   "4.940656458412465441765687928682213723650e-324",
+		float:   "4.941e-324",
+		mant:    "4940656458412465441765687928682213723650",
+		exp:     -363,
+		float64: 0,
+		prec:    4,
+		fmt:     'g',
+		kind:    FloatKind,
+		err:     ErrBelow.Error(),
+	}, {
+		value:   "-4.940656458412465441765687928682213723650e-324",
+		float:   "-4.940656458412465441765687928682213723650e-324",
+		mant:    "-4940656458412465441765687928682213723650",
+		exp:     -363,
+		float64: 0,
+		prec:    -1,
+		fmt:     'g',
+		kind:    FloatKind,
+		err:     ErrAbove.Error(),
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			n := getInstance(t, tc.value).Value()
+			if n.Kind() != tc.kind {
+				t.Fatal("Not a number")
+			}
+
+			var mant big.Int
+			exp, err := n.MantExp(&mant)
+			mstr := ""
+			if err == nil {
+				mstr = mant.String()
+			}
+			if exp != tc.exp || mstr != tc.mant {
+				t.Errorf("mantExp: got %s %d; want %s %d", mstr, exp, tc.mant, tc.exp)
+			}
+
+			b, _ := n.AppendFloat(nil, tc.fmt, tc.prec)
+			want := tc.float
+			if got := string(b); got != want {
+				t.Errorf("append: got %v; want %v", got, want)
+			}
+
+			f, err := n.Float64()
+			checkErr(t, err, tc.err, "Float64")
+			if f != tc.float64 {
+				t.Errorf("Float64: got %v; want %v", f, tc.float64)
+			}
+		})
+	}
+}
+
+func TestString(t *testing.T) {
+	testCases := []struct {
+		value string
+		str   string
+		err   string
+	}{{
+		value: `""`,
+		str:   ``,
+	}, {
+		value: `"Hello world!"`,
+		str:   `Hello world!`,
+	}, {
+		value: `"Hello \(#world)!"
+		#world: "world"`,
+		str: `Hello world!`,
+	}, {
+		value: `string`,
+		err:   "non-concrete value string",
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			str, err := getInstance(t, tc.value).Value().String()
+			checkFatal(t, err, tc.err, "init")
+			if str != tc.str {
+				t.Errorf("String: got %q; want %q", str, tc.str)
+			}
+
+			b, err := getInstance(t, tc.value).Value().Bytes()
+			checkFatal(t, err, tc.err, "init")
+			if got := string(b); got != tc.str {
+				t.Errorf("Bytes: got %q; want %q", got, tc.str)
+			}
+
+			r, err := getInstance(t, tc.value).Value().Reader()
+			checkFatal(t, err, tc.err, "init")
+			b, _ = ioutil.ReadAll(r)
+			if got := string(b); got != tc.str {
+				t.Errorf("Reader: got %q; want %q", got, tc.str)
+			}
+		})
+	}
+}
+
+func TestError(t *testing.T) {
+	testCases := []struct {
+		value string
+		err   string
+	}{{
+		value: `_|_`,
+		err:   "from source",
+	}, {
+		value: `"Hello world!"`,
+	}, {
+		value: `string`,
+		err:   "",
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			err := getInstance(t, tc.value).Value().Err()
+			checkErr(t, err, tc.err, "init")
+		})
+	}
+}
+
+func TestNull(t *testing.T) {
+	testCases := []struct {
+		value string
+		err   string
+	}{{
+		value: `v: _|_`,
+		err:   "from source",
+	}, {
+		value: `v: "str"`,
+		err:   "cannot use value \"str\" (type string) as null",
+	}, {
+		value: `v: null`,
+	}, {
+		value: `v: _`,
+		err:   "non-concrete value _",
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			err := getInstance(t, tc.value).Lookup("v").Null()
+			checkErr(t, err, tc.err, "init")
+		})
+	}
+}
+
+func TestBool(t *testing.T) {
+	testCases := []struct {
+		value string
+		bool  bool
+		err   string
+	}{{
+		value: `_|_`,
+		err:   "from source",
+	}, {
+		value: `"str"`,
+		err:   "cannot use value \"str\" (type string) as bool",
+	}, {
+		value: `true`,
+		bool:  true,
+	}, {
+		value: `false`,
+	}, {
+		value: `bool`,
+		err:   "non-concrete value bool",
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			got, err := getInstance(t, tc.value).Value().Bool()
+			if checkErr(t, err, tc.err, "init") {
+				if got != tc.bool {
+					t.Errorf("got %v; want %v", got, tc.bool)
+				}
+			}
+		})
+	}
+}
+
+func TestList(t *testing.T) {
+	testCases := []struct {
+		value string
+		res   string
+		err   string
+	}{{
+		value: `_|_`,
+		err:   "from source",
+	}, {
+		value: `"str"`,
+		err:   "cannot use value \"str\" (type string) as list",
+	}, {
+		value: `[]`,
+		res:   "[]",
+	}, {
+		value: `[1,2,3]`,
+		res:   "[1,2,3,]",
+	}, {
+		value: `>=5*[1,2,3, ...int]`,
+		err:   "incomplete",
+	}, {
+		value: `[for x in #y if x > 1 { x }]
+		#y: [1,2,3]`,
+		res: "[2,3,]",
+	}, {
+		value: `[int]`,
+		err:   "cannot convert incomplete value",
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			l, err := getInstance(t, tc.value).Value().List()
+			checkFatal(t, err, tc.err, "init")
+
+			buf := []byte{'['}
+			for l.Next() {
+				b, err := l.Value().MarshalJSON()
+				checkFatal(t, err, tc.err, "list.Value")
+				buf = append(buf, b...)
+				buf = append(buf, ',')
+			}
+			buf = append(buf, ']')
+			if got := string(buf); got != tc.res {
+				t.Errorf("got %v; want %v", got, tc.res)
+			}
+		})
+	}
+}
+
+func TestFields(t *testing.T) {
+	testCases := []struct {
+		value string
+		res   string
+		err   string
+	}{{
+		value: `_|_`,
+		err:   "from source",
+	}, {
+		value: `"str"`,
+		err:   "cannot use value \"str\" (type string) as struct",
+	}, {
+		value: `{}`,
+		res:   "{}",
+	}, {
+		value: `{a:1,b:2,c:3}`,
+		res:   "{a:1,b:2,c:3,}",
+	}, {
+		value: `{a:1,"_b":2,c:3,_d:4}`,
+		res:   "{a:1,_b:2,c:3,}",
+	}, {
+		value: `{_a:"a"}`,
+		res:   "{}",
+	}, {
+		value: `{ for k, v in #y if v > 1 {"\(k)": v} }
+		#y: {a:1,b:2,c:3}`,
+		res: "{b:2,c:3,}",
+	}, {
+		value: `{ #def: 1, _hidden: 2, opt?: 3, reg: 4 }`,
+		res:   "{reg:4,}",
+	}, {
+		value: `{a:1,b:2,c:int}`,
+		err:   "cannot convert incomplete value",
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			obj := getInstance(t, tc.value).Value()
+
+			iter, err := obj.Fields()
+			checkFatal(t, err, tc.err, "init")
+
+			buf := []byte{'{'}
+			for iter.Next() {
+				buf = append(buf, iter.Label()...)
+				buf = append(buf, ':')
+				b, err := iter.Value().MarshalJSON()
+				checkFatal(t, err, tc.err, "Obj.At")
+				buf = append(buf, b...)
+				buf = append(buf, ',')
+			}
+			buf = append(buf, '}')
+			if got := string(buf); got != tc.res {
+				t.Errorf("got %v; want %v", got, tc.res)
+			}
+
+			iter, _ = obj.Fields()
+			for iter.Next() {
+				want, err := iter.Value().MarshalJSON()
+				checkFatal(t, err, tc.err, "Obj.At2")
+
+				got, err := obj.Lookup(iter.Label()).MarshalJSON()
+				checkFatal(t, err, tc.err, "Obj.At2")
+
+				if !bytes.Equal(got, want) {
+					t.Errorf("Lookup: got %q; want %q", got, want)
+				}
+			}
+			v := obj.Lookup("non-existing")
+			checkErr(t, v.Err(), "not found", "non-existing")
+		})
+	}
+}
+
+func TestAllFields(t *testing.T) {
+	testCases := []struct {
+		value string
+		res   string
+		err   string
+	}{{
+		value: `{a:1,"_b":2,c:3,_d:4}`,
+		res:   "{a:1,_b:2,c:3,_d:4,}",
+	}, {
+		value: `{_a:"a"}`,
+		res:   `{_a:"a",}`,
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			obj := getInstance(t, tc.value).Value()
+
+			var iter *Iterator // Verify that the returned iterator is a pointer.
+			iter, err := obj.Fields(All())
+			checkFatal(t, err, tc.err, "init")
+
+			buf := []byte{'{'}
+			for iter.Next() {
+				buf = append(buf, iter.Label()...)
+				buf = append(buf, ':')
+				b, err := iter.Value().MarshalJSON()
+				checkFatal(t, err, tc.err, "Obj.At")
+				buf = append(buf, b...)
+				buf = append(buf, ',')
+			}
+			buf = append(buf, '}')
+			if got := string(buf); got != tc.res {
+				t.Errorf("got %v; want %v", got, tc.res)
+			}
+		})
+	}
+}
+
+func TestLookup(t *testing.T) {
+	var runtime = new(Runtime)
+	inst, err := runtime.Compile("x.cue", `
+#V: {
+	x: int
+}
+#X: {
+	[string]: int64
+} & #V
+v: #X
+`)
+	if err != nil {
+		t.Fatalf("compile: %v", err)
+	}
+	// expr, err := parser.ParseExpr("lookup.cue", `v`, parser.DeclarationErrors, parser.AllErrors)
+	// if err != nil {
+	// 	log.Fatalf("parseExpr: %v", err)
+	// }
+	// v := inst.Eval(expr)
+	testCases := []struct {
+		ref  []string
+		raw  string
+		eval string
+	}{{
+		ref:  []string{"v", "x"},
+		raw:  "(int & <=9223372036854775807 & int & >=-9223372036854775808)",
+		eval: "int64",
+	}}
+	for _, tc := range testCases {
+		v := inst.Lookup(tc.ref...)
+
+		if got := fmt.Sprint(v); got != tc.raw {
+			t.Errorf("got %v; want %v", got, tc.raw)
+		}
+
+		got := fmt.Sprint(internal.DebugStr(v.Eval().Syntax()))
+		if got != tc.eval {
+			t.Errorf("got %v; want %v", got, tc.eval)
+		}
+
+		v = inst.Lookup()
+		for _, ref := range tc.ref {
+			s, err := v.Struct()
+			if err != nil {
+				t.Fatal(err)
+			}
+			fi, err := s.FieldByName(ref, false)
+			if err != nil {
+				t.Fatal(err)
+			}
+			v = fi.Value
+		}
+
+		if got := fmt.Sprint(v); got != tc.raw {
+			t.Errorf("got %v; want %v", got, tc.raw)
+		}
+
+		got = fmt.Sprint(internal.DebugStr(v.Eval().Syntax()))
+		if got != tc.eval {
+			t.Errorf("got %v; want %v", got, tc.eval)
+		}
+	}
+}
+
+func compileT(t *testing.T, r *Runtime, s string) *Instance {
+	t.Helper()
+	inst, err := r.Compile("", s)
+	if err != nil {
+		t.Fatal(err)
+	}
+	return inst
+}
+
+func goValue(v Value) interface{} {
+	var x interface{}
+	err := v.Decode(&x)
+	if err != nil {
+		return err
+	}
+	return x
+}
+
+func TestFill(t *testing.T) {
+	r := &Runtime{}
+
+	inst, err := r.CompileExpr(ast.NewStruct("bar", ast.NewString("baz")))
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	testCases := []struct {
+		in   string
+		x    interface{}
+		path string // comma-separated path
+		out  string
+	}{{
+		in: `
+		foo: int
+		bar: foo
+		`,
+		x:    3,
+		path: "foo",
+		out: `
+		foo: 3
+		bar: 3
+		`,
+	}, {
+		in: `
+		string
+		`,
+		x:    "foo",
+		path: "",
+		out: `
+		"foo"
+		`,
+	}, {
+		in: `
+		foo: _
+		`,
+		x:    inst.Value(),
+		path: "foo",
+		out: `
+		{foo: {bar: "baz"}}
+		`,
+	}}
+
+	for _, tc := range testCases {
+		var path []string
+		if tc.path != "" {
+			path = strings.Split(tc.path, ",")
+		}
+
+		v := compileT(t, r, tc.in).Value().Fill(tc.x, path...)
+		w := compileT(t, r, tc.out).Value()
+
+		if !reflect.DeepEqual(goValue(v), goValue(w)) {
+			t.Errorf("\ngot:  %s\nwant: %s", v, w)
+		}
+	}
+}
+
+func TestFill2(t *testing.T) {
+	r := &Runtime{}
+
+	root, err := r.Compile("test", `
+	#Provider: {
+		ID: string
+		notConcrete: bool
+	}
+	`)
+
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	spec := root.LookupDef("#Provider")
+	providerInstance := spec.Fill("12345", "ID")
+	root, err = root.Fill(providerInstance, "providers", "myprovider")
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	got := fmt.Sprint(root.Value())
+
+	if got != `{#Provider: C{ID: string, notConcrete: bool}, providers: {myprovider: C{ID: (string & "12345"), notConcrete: bool}}}` {
+		t.Error(got)
+	}
+}
+
+func TestValue_LookupDef(t *testing.T) {
+	r := &Runtime{}
+
+	testCases := []struct {
+		in     string
+		def    string // comma-separated path
+		exists bool
+		out    string
+	}{{
+		in:  `#foo: 3`,
+		def: "#foo",
+		out: `3`,
+	}, {
+		in:  `_foo: 3`,
+		def: "_foo",
+		out: `_|_(definition "_foo" not found)`,
+	}, {
+		in:  `_#foo: 3`,
+		def: "_#foo",
+		out: `_|_(definition "_#foo" not found)`,
+	}}
+
+	for _, tc := range testCases {
+		t.Run(tc.def, func(t *testing.T) {
+			v := compileT(t, r, tc.in).Value()
+			v = v.LookupDef(tc.def)
+			got := fmt.Sprint(v)
+
+			if got != tc.out {
+				t.Errorf("\ngot:  %s\nwant: %s", got, tc.out)
+			}
+		})
+	}
+}
+
+func TestDefaults(t *testing.T) {
+	testCases := []struct {
+		value string
+		def   string
+		val   string
+		ok    bool
+	}{{
+		value: `number | *1`,
+		def:   "1",
+		val:   "number",
+		ok:    true,
+	}, {
+		value: `1 | 2 | *3`,
+		def:   "3",
+		val:   "1|2|3",
+		ok:    true,
+	}, {
+		value: `*{a:1,b:2}|{a:1}|{b:2}`,
+		def:   "{a: 1, b: 2}",
+		val:   "{a: 1}|{b: 2}",
+		ok:    true,
+	}, {
+		value: `{a:1}&{b:2}`,
+		def:   `{a: 1, b: 2}`,
+		val:   ``,
+		ok:    false,
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			v := getInstance(t, "a: "+tc.value).Lookup("a")
+
+			d, ok := v.Default()
+			if ok != tc.ok {
+				t.Errorf("hasDefault: got %v; want %v", ok, tc.ok)
+			}
+
+			if got := fmt.Sprint(d); got != tc.def {
+				t.Errorf("default: got %v; want %v", got, tc.def)
+			}
+
+			op, val := v.Expr()
+			if op != OrOp {
+				return
+			}
+			vars := []string{}
+			for _, v := range val {
+				vars = append(vars, fmt.Sprint(v))
+			}
+			if got := strings.Join(vars, "|"); got != tc.val {
+				t.Errorf("value: got %v; want %v", got, tc.val)
+			}
+		})
+	}
+}
+
+func TestLen(t *testing.T) {
+	testCases := []struct {
+		input  string
+		length string
+	}{{
+		input:  "[1, 3]",
+		length: "2",
+	}, {
+		input:  "[1, 3, ...]",
+		length: "int & >=2",
+	}, {
+		input:  `"foo"`,
+		length: "3",
+	}, {
+		input:  `'foo'`,
+		length: "3",
+		// TODO: Currently not supported.
+		// }, {
+		// 	input:  "{a:1, b:3, a:1, c?: 3, _hidden: 4}",
+		// 	length: "2",
+	}, {
+		input:  "3",
+		length: "_|_(len not supported for type int)",
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.input, func(t *testing.T) {
+			v := getInstance(t, "a: "+tc.input).Lookup("a")
+
+			length := v.Len()
+			if got := fmt.Sprint(length); got != tc.length {
+				t.Errorf("length: got %v; want %v", got, tc.length)
+			}
+		})
+	}
+}
+
+func TestTemplate(t *testing.T) {
+	testCases := []struct {
+		value string
+		path  []string
+		want  string
+	}{{
+		value: `
+		a: [Name=string]: Name
+		`,
+		path: []string{"a", ""},
+		want: `"label"`,
+	}, {
+		value: `
+		[Name=string]: { a: Name }
+		`,
+		path: []string{"", "a"},
+		want: `"label"`,
+	}, {
+		value: `
+		[Name=string]: { a: Name }
+		`,
+		path: []string{""},
+		want: `{"a":"label"}`,
+	}, {
+		value: `
+		a: [Foo=string]: [Bar=string]: { b: Foo+Bar }
+		`,
+		path: []string{"a", "", ""},
+		want: `{"b":"labellabel"}`,
+	}, {
+		value: `
+		a: [Foo=string]: b: [Bar=string]: { c: Foo+Bar }
+		a: foo: b: [Bar=string]: { d: Bar }
+		`,
+		path: []string{"a", "foo", "b", ""},
+		want: `{"c":"foolabel","d":"label"}`,
+	}}
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			v := getInstance(t, tc.value).Value()
+			for _, p := range tc.path {
+				if p == "" {
+					v = v.Template()("label")
+				} else {
+					v = v.Lookup(p)
+				}
+			}
+			b, err := v.MarshalJSON()
+			if err != nil {
+				t.Fatal(err)
+			}
+			if got := string(b); got != tc.want {
+				t.Errorf("\n got: %q\nwant: %q", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestSubsumes(t *testing.T) {
+	a := []string{"a"}
+	b := []string{"b"}
+	testCases := []struct {
+		value string
+		pathA []string
+		pathB []string
+		want  bool
+	}{{
+		value: `4`,
+		want:  true,
+	}, {
+		value: `a: string, b: "foo"`,
+		pathA: a,
+		pathB: b,
+		want:  true,
+	}, {
+		value: `a: string, b: "foo"`,
+		pathA: b,
+		pathB: a,
+		want:  false,
+	}, {
+		value: `a: {a: string, b: 4}, b: {a: "foo", b: 4}`,
+		pathA: a,
+		pathB: b,
+		want:  true,
+	}, {
+		value: `a: [string,  4], b: ["foo", 4]`,
+		pathA: a,
+		pathB: b,
+		want:  true,
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			v := getInstance(t, tc.value)
+			a := v.Lookup(tc.pathA...)
+			b := v.Lookup(tc.pathB...)
+			got := a.Subsumes(b)
+			if got != tc.want {
+				t.Errorf("got %v (%v); want %v (%v)", got, a, tc.want, b)
+			}
+		})
+	}
+}
+
+func TestUnify(t *testing.T) {
+	a := []string{"a"}
+	b := []string{"b"}
+	testCases := []struct {
+		value string
+		pathA []string
+		pathB []string
+		want  string
+	}{{
+		value: `4`,
+		want:  `4`,
+	}, {
+		value: `a: string, b: "foo"`,
+		pathA: a,
+		pathB: b,
+		want:  `"foo"`,
+	}, {
+		value: `a: string, b: "foo"`,
+		pathA: b,
+		pathB: a,
+		want:  `"foo"`,
+	}, {
+		value: `a: {a: string, b: 4}, b: {a: "foo", b: 4}`,
+		pathA: a,
+		pathB: b,
+		want:  `{"a":"foo","b":4}`,
+	}, {
+		value: `a: [string,  4], b: ["foo", 4]`,
+		pathA: a,
+		pathB: b,
+		want:  `["foo",4]`,
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			v := getInstance(t, tc.value).Value()
+			x := v.Lookup(tc.pathA...)
+			y := v.Lookup(tc.pathB...)
+			b, err := x.Unify(y).MarshalJSON()
+			if err != nil {
+				t.Fatal(err)
+			}
+			if got := string(b); got != tc.want {
+				t.Errorf("got %v; want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestEquals(t *testing.T) {
+	testCases := []struct {
+		a, b string
+		want bool
+	}{{
+		`4`, `4`, true,
+	}, {
+		`"str"`, `2`, false,
+	}, {
+		`2`, `3`, false,
+	}, {
+		`[1]`, `[3]`, false,
+	}, {
+		`[{a: 1,...}]`, `[{a: 1,...}]`, true,
+	}, {
+		`[]`, `[]`, true,
+	}, {
+		`{
+			a: b,
+			b: a,
+		}`,
+		`{
+			a: b,
+			b: a,
+		}`,
+		true,
+	}, {
+		`{
+			a: "foo",
+			b: "bar",
+		}`,
+		`{
+			a: "foo",
+		}`,
+		false,
+	}}
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			var r Runtime
+			a, err := r.Compile("a", tc.a)
+			if err != nil {
+				t.Fatal(err)
+			}
+			b, err := r.Compile("b", tc.b)
+			if err != nil {
+				t.Fatal(err)
+			}
+			got := a.Value().Equals(b.Value())
+			if got != tc.want {
+				t.Errorf("got %v; want %v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestDecode(t *testing.T) {
+	type fields struct {
+		A int `json:"A"`
+		B int `json:"B"`
+		C int `json:"C"`
+	}
+	intList := func(ints ...int) *[]int {
+		ints = append([]int{}, ints...)
+		return &ints
+	}
+	testCases := []struct {
+		value string
+		dst   interface{}
+		want  interface{}
+		err   string
+	}{{
+		value: `_|_`,
+		err:   "from source",
+	}, {
+		value: `"str"`,
+		dst:   new(string),
+		want:  "str",
+	}, {
+		value: `"str"`,
+		dst:   new(int),
+		err:   "cannot unmarshal string into Go value of type int",
+	}, {
+		value: `{}`,
+		dst:   &fields{},
+		want:  fields{},
+	}, {
+		value: `{a:1,b:2,c:3}`,
+		dst:   &fields{},
+		want:  fields{A: 1, B: 2, C: 3},
+	}, {
+		value: `{for k, v in y if v > 1 {"\(k)": v} }
+		y: {a:1,b:2,c:3}`,
+		dst:  &fields{},
+		want: fields{B: 2, C: 3},
+	}, {
+		value: `{a:1,b:2,c:int}`,
+		dst:   new(fields),
+		err:   "cannot convert incomplete value",
+	}, {
+		value: `[]`,
+		dst:   intList(),
+		want:  *intList(),
+	}, {
+		value: `[1,2,3]`,
+		dst:   intList(),
+		want:  *intList(1, 2, 3),
+	}, {
+		value: `[for x in #y if x > 1 { x }]
+				#y: [1,2,3]`,
+		dst:  intList(),
+		want: *intList(2, 3),
+	}, {
+		value: `[int]`,
+		err:   "cannot convert incomplete value",
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.value, func(t *testing.T) {
+			err := getInstance(t, tc.value).Value().Decode(tc.dst)
+			checkFatal(t, err, tc.err, "init")
+
+			got := reflect.ValueOf(tc.dst).Elem().Interface()
+			if !cmp.Equal(got, tc.want) {
+				t.Error(cmp.Diff(got, tc.want))
+				t.Errorf("\n%#v\n%#v", got, tc.want)
+			}
+		})
+	}
+}
+
+func TestValidate(t *testing.T) {
+	testCases := []struct {
+		desc string
+		in   string
+		err  bool
+		opts []Option
+	}{{
+		desc: "issue #51",
+		in: `
+		a: [string]: foo
+		a: b: {}
+		`,
+		err: true,
+	}, {
+		desc: "concrete",
+		in: `
+		a: 1
+		b: { c: 2, d: 3 }
+		c: d: e: f: 5
+		g?: int
+		`,
+		opts: []Option{Concrete(true)},
+	}, {
+		desc: "definition error",
+		in: `
+			#b: 1 & 2
+			`,
+		opts: []Option{},
+		err:  true,
+	}, {
+		desc: "definition error okay if optional",
+		in: `
+			#b?: 1 & 2
+			`,
+		opts: []Option{},
+	}, {
+		desc: "definition with optional",
+		in: `
+			#b: {
+				a: int
+				b?: >=0
+			}
+		`,
+		opts: []Option{Concrete(true)},
+	}, {
+		desc: "disjunction",
+		in:   `a: 1 | 2`,
+	}, {
+		desc: "disjunction concrete",
+		in:   `a: 1 | 2`,
+		opts: []Option{Concrete(true)},
+		err:  true,
+	}, {
+		desc: "incomplete concrete",
+		in:   `a: string`,
+	}, {
+		desc: "incomplete",
+		in:   `a: string`,
+		opts: []Option{Concrete(true)},
+		err:  true,
+	}, {
+		desc: "list",
+		in:   `a: [{b: string}, 3]`,
+	}, {
+		desc: "list concrete",
+		in:   `a: [{b: string}, 3]`,
+		opts: []Option{Concrete(true)},
+		err:  true,
+	}, {
+		desc: "allow cycles",
+		in: `
+			a: b - 100
+			b: a + 100
+			c: [c[1], c[0]]
+			`,
+	}, {
+		desc: "disallow cycles",
+		in: `
+			a: b - 100
+			b: a + 100
+			c: [c[1], c[0]]
+			`,
+		opts: []Option{DisallowCycles(true)},
+		err:  true,
+	}, {
+		desc: "builtins are okay",
+		in: `
+		import "time"
+
+		a: { b: time.Duration } | { c: time.Duration }
+		`,
+	}, {
+		desc: "comprehension error",
+		in: `
+			a: { if b == "foo" { field: 2 } }
+			`,
+		err: true,
+	}, {
+		desc: "ignore optional in schema",
+		in: `
+		#Schema1: {
+			a?: int
+		}
+		instance1: #Schema1
+		`,
+		opts: []Option{Concrete(true)},
+	}, {
+		desc: "issue324",
+		in: `
+		import "encoding/yaml"
+
+		x: string
+		a: b: c: *["\(x)"] | _
+		d: yaml.Marshal(a.b)
+		`,
+	}, {
+		desc: "allow non-concrete values for definitions",
+		in: `
+		variables: #variables
+
+		{[!~"^[.]"]: #job}
+
+		#variables: [string]: int | string
+
+		#job: ({a: int} | {b: int}) & {
+			"variables"?: #variables
+		}
+		`,
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			r := Runtime{}
+			inst, err := r.Parse("validate", tc.in)
+			if err == nil {
+				err = inst.Value().Validate(tc.opts...)
+			}
+			if gotErr := err != nil; gotErr != tc.err {
+				t.Errorf("got %v; want %v", err, tc.err)
+			}
+		})
+	}
+}
+
+func TestPath(t *testing.T) {
+	config := `
+	a: b: c: 5
+	b: {
+		b1: 3
+		b2: 4
+		"b 3": 5
+		"4b": 6
+		l: [
+			{a: 2},
+			{c: 2},
+		]
+	}
+	`
+	mkpath := func(p ...string) []string { return p }
+	testCases := [][]string{
+		mkpath("a", "b", "c"),
+		mkpath("b", "l", "1", "c"),
+		mkpath("b", `"b 3"`),
+		mkpath("b", `"4b"`),
+	}
+	for _, tc := range testCases {
+		r := Runtime{}
+		inst, err := r.Parse("config", config)
+		if err != nil {
+			t.Fatal(err)
+		}
+		t.Run(strings.Join(tc, "."), func(t *testing.T) {
+			v := inst.Lookup(tc[0])
+			for _, e := range tc[1:] {
+				if '0' <= e[0] && e[0] <= '9' {
+					i, err := strconv.Atoi(e)
+					if err != nil {
+						t.Fatal(err)
+					}
+					iter, err := v.List()
+					if err != nil {
+						t.Fatal(err)
+					}
+					for c := 0; iter.Next(); c++ {
+						if c == i {
+							v = iter.Value()
+							break
+						}
+					}
+				} else if e[0] == '"' {
+					v = v.Lookup(e[1 : len(e)-1])
+				} else {
+					v = v.Lookup(e)
+				}
+			}
+			got, _ := v.v.appendPath(nil, v.idx)
+			if !reflect.DeepEqual(got, tc) {
+				t.Errorf("got %v; want %v", got, tc)
+			}
+		})
+	}
+}
+
+func TestValueLookup(t *testing.T) {
+	config := `
+		a: {
+			a: 0
+			b: 1
+			c: 2
+		}
+		b: {
+			d: a.a
+			e: int
+		}
+	`
+
+	strList := func(s ...string) []string { return s }
+
+	testCases := []struct {
+		config    string
+		path      []string
+		str       string
+		notExists bool
+	}{{
+		config: "_|_",
+		path:   strList(""),
+		str:    "from source",
+	}, {
+		config: "_|_",
+		path:   strList("a"),
+		str:    "from source",
+	}, {
+		config: config,
+		path:   strList(),
+		str:    "{a: {a: 0, b: 1, c: 2}, b: {d: a.a, e: int}",
+	}, {
+		config: config,
+		path:   strList("a", "a"),
+		str:    "0",
+	}, {
+		config: config,
+		path:   strList("a"),
+		str:    "{a: 0, b: 1, c: 2}",
+	}, {
+		config: config,
+		path:   strList("b", "d"),
+		str:    "0",
+	}, {
+		config:    config,
+		path:      strList("c", "non-existing"),
+		str:       "not found",
+		notExists: true,
+	}, {
+		config: config,
+		path:   strList("b", "d", "lookup in non-struct"),
+		str:    "cannot use value 0 (type int) as struct",
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.str, func(t *testing.T) {
+			v := getInstance(t, tc.config).Value().Lookup(tc.path...)
+			if got := !v.Exists(); got != tc.notExists {
+				t.Errorf("exists: got %v; want %v", got, tc.notExists)
+			}
+
+			got := fmt.Sprint(v)
+			if tc.str == "" {
+				t.Fatalf("str empty, got %q", got)
+			}
+			if !strings.Contains(got, tc.str) {
+				t.Errorf("\n got %v\nwant %v", got, tc.str)
+			}
+		})
+	}
+}
+
+func cmpError(a, b error) bool {
+	if a == nil {
+		return b == nil
+	}
+	if b == nil {
+		return a == nil
+	}
+	return a.Error() == b.Error()
+}
+
+func TestAttributeErr(t *testing.T) {
+	const config = `
+	a: {
+		a: 0 @foo(a,b,c=1)
+		b: 1 @bar(a,b,c,d=1) @foo(a,,d=1)
+	}
+	`
+	testCases := []struct {
+		path string
+		attr string
+		err  error
+	}{{
+		path: "a",
+		attr: "foo",
+		err:  nil,
+	}, {
+		path: "a",
+		attr: "bar",
+		err:  errors.New(`attribute "bar" does not exist`),
+	}, {
+		path: "xx",
+		attr: "bar",
+		err:  errors.New(`attribute "bar" does not exist`),
+	}, {
+		path: "e",
+		attr: "bar",
+		err:  errors.New(`attribute "bar" does not exist`),
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.path+"-"+tc.attr, func(t *testing.T) {
+			v := getInstance(t, config).Value().Lookup("a", tc.path)
+			a := v.Attribute(tc.attr)
+			err := a.Err()
+			if !cmpError(err, tc.err) {
+				t.Errorf("got %v; want %v", err, tc.err)
+			}
+		})
+	}
+}
+
+func TestAttributeString(t *testing.T) {
+	const config = `
+	a: {
+		a: 0 @foo(a,b,c=1)
+		b: 1 @bar(a,b,c,d=1) @foo(a,,d=1)
+	}
+	`
+	testCases := []struct {
+		path string
+		attr string
+		pos  int
+		str  string
+		err  error
+	}{{
+		path: "a",
+		attr: "foo",
+		pos:  0,
+		str:  "a",
+	}, {
+		path: "a",
+		attr: "foo",
+		pos:  2,
+		str:  "c=1",
+	}, {
+		path: "b",
+		attr: "bar",
+		pos:  3,
+		str:  "d=1",
+	}, {
+		path: "e",
+		attr: "bar",
+		err:  errors.New(`attribute "bar" does not exist`),
+	}, {
+		path: "b",
+		attr: "foo",
+		pos:  4,
+		err:  errors.New("field does not exist"),
+	}}
+	for _, tc := range testCases {
+		t.Run(fmt.Sprintf("%s.%s:%d", tc.path, tc.attr, tc.pos), func(t *testing.T) {
+			v := getInstance(t, config).Value().Lookup("a", tc.path)
+			a := v.Attribute(tc.attr)
+			got, err := a.String(tc.pos)
+			if !cmpError(err, tc.err) {
+				t.Errorf("err: got %v; want %v", err, tc.err)
+			}
+			if got != tc.str {
+				t.Errorf("str: got %v; want %v", got, tc.str)
+			}
+		})
+	}
+}
+
+func TestAttributeInt(t *testing.T) {
+	const config = `
+	a: {
+		a: 0 @foo(1,3,c=1)
+		b: 1 @bar(a,-4,c,d=1) @foo(a,,d=1)
+	}
+	`
+	testCases := []struct {
+		path string
+		attr string
+		pos  int
+		val  int64
+		err  error
+	}{{
+		path: "a",
+		attr: "foo",
+		pos:  0,
+		val:  1,
+	}, {
+		path: "b",
+		attr: "bar",
+		pos:  1,
+		val:  -4,
+	}, {
+		path: "e",
+		attr: "bar",
+		err:  errors.New(`attribute "bar" does not exist`),
+	}, {
+		path: "b",
+		attr: "foo",
+		pos:  4,
+		err:  errors.New("field does not exist"),
+	}, {
+		path: "a",
+		attr: "foo",
+		pos:  2,
+		err:  errors.New(`strconv.ParseInt: parsing "c=1": invalid syntax`),
+	}}
+	for _, tc := range testCases {
+		t.Run(fmt.Sprintf("%s.%s:%d", tc.path, tc.attr, tc.pos), func(t *testing.T) {
+			v := getInstance(t, config).Value().Lookup("a", tc.path)
+			a := v.Attribute(tc.attr)
+			got, err := a.Int(tc.pos)
+			if !cmpError(err, tc.err) {
+				t.Errorf("err: got %v; want %v", err, tc.err)
+			}
+			if got != tc.val {
+				t.Errorf("val: got %v; want %v", got, tc.val)
+			}
+		})
+	}
+}
+
+func TestAttributeFlag(t *testing.T) {
+	const config = `
+	a: {
+		a: 0 @foo(a,b,c=1)
+		b: 1 @bar(a,b,c,d=1) @foo(a,,d=1)
+	}
+	`
+	testCases := []struct {
+		path string
+		attr string
+		pos  int
+		flag string
+		val  bool
+		err  error
+	}{{
+		path: "a",
+		attr: "foo",
+		pos:  0,
+		flag: "a",
+		val:  true,
+	}, {
+		path: "b",
+		attr: "bar",
+		pos:  1,
+		flag: "a",
+		val:  false,
+	}, {
+		path: "b",
+		attr: "bar",
+		pos:  0,
+		flag: "c",
+		val:  true,
+	}, {
+		path: "e",
+		attr: "bar",
+		err:  errors.New(`attribute "bar" does not exist`),
+	}, {
+		path: "b",
+		attr: "foo",
+		pos:  4,
+		err:  errors.New("field does not exist"),
+	}}
+	for _, tc := range testCases {
+		t.Run(fmt.Sprintf("%s.%s:%d", tc.path, tc.attr, tc.pos), func(t *testing.T) {
+			v := getInstance(t, config).Value().Lookup("a", tc.path)
+			a := v.Attribute(tc.attr)
+			got, err := a.Flag(tc.pos, tc.flag)
+			if !cmpError(err, tc.err) {
+				t.Errorf("err: got %v; want %v", err, tc.err)
+			}
+			if got != tc.val {
+				t.Errorf("val: got %v; want %v", got, tc.val)
+			}
+		})
+	}
+}
+
+func TestAttributeLookup(t *testing.T) {
+	const config = `
+	a: {
+		a: 0 @foo(a,b,c=1)
+		b: 1 @bar(a,b,e=-5,d=1) @foo(a,,d=1)
+	}
+	`
+	testCases := []struct {
+		path string
+		attr string
+		pos  int
+		key  string
+		val  string
+		err  error
+	}{{
+		path: "a",
+		attr: "foo",
+		pos:  0,
+		key:  "c",
+		val:  "1",
+	}, {
+		path: "b",
+		attr: "bar",
+		pos:  1,
+		key:  "a",
+		val:  "",
+	}, {
+		path: "b",
+		attr: "bar",
+		pos:  0,
+		key:  "e",
+		val:  "-5",
+	}, {
+		path: "b",
+		attr: "bar",
+		pos:  0,
+		key:  "d",
+		val:  "1",
+	}, {
+		path: "b",
+		attr: "foo",
+		pos:  2,
+		key:  "d",
+		val:  "1",
+	}, {
+		path: "b",
+		attr: "foo",
+		pos:  2,
+		key:  "f",
+		val:  "",
+	}, {
+		path: "e",
+		attr: "bar",
+		err:  errors.New(`attribute "bar" does not exist`),
+	}, {
+		path: "b",
+		attr: "foo",
+		pos:  4,
+		err:  errors.New("field does not exist"),
+	}}
+	for _, tc := range testCases {
+		t.Run(fmt.Sprintf("%s.%s:%d", tc.path, tc.attr, tc.pos), func(t *testing.T) {
+			v := getInstance(t, config).Value().Lookup("a", tc.path)
+			a := v.Attribute(tc.attr)
+			got, _, err := a.Lookup(tc.pos, tc.key)
+			if !cmpError(err, tc.err) {
+				t.Errorf("err: got %v; want %v", err, tc.err)
+			}
+			if got != tc.val {
+				t.Errorf("val: got %v; want %v", got, tc.val)
+			}
+		})
+	}
+}
+
+func TestValueDoc(t *testing.T) {
+	const config = `
+	// foobar defines at least foo.
+	package foobar
+
+	// A Foo fooses stuff.
+	Foo: {
+		// field1 is an int.
+		field1: int
+
+		field2: int
+
+		// duplicate field comment
+		dup3: int
+	}
+
+	// foos are instances of Foo.
+	foos: [string]: Foo
+
+	// My first little foo.
+	foos: MyFoo: {
+		// local field comment.
+		field1: 0
+
+		// Dangling comment.
+
+		// other field comment.
+		field2: 1
+
+		// duplicate field comment
+		dup3: int
+	}
+
+	bar: {
+		// comment from bar on field 1
+		field1: int
+		// comment from bar on field 2
+		field2: int // don't include this
+	}
+
+	baz: bar & {
+		// comment from baz on field 1
+		field1: int
+		field2: int
+	}
+	`
+	config2 := `
+	// Another Foo.
+	Foo: {}
+	`
+	var r Runtime
+	getInst := func(name, body string) *Instance {
+		inst, err := r.Compile("dir/file1.cue", body)
+		if err != nil {
+			t.Fatal(err)
+		}
+		return inst
+	}
+
+	inst := getInst("config", config)
+
+	v1 := inst.Value()
+	v2 := getInst("config2", config2).Value()
+	both := v1.Unify(v2)
+
+	testCases := []struct {
+		val  Value
+		path string
+		doc  string
+	}{{
+		val:  v1,
+		path: "foos",
+		doc:  "foos are instances of Foo.\n",
+	}, {
+		val:  v1,
+		path: "foos MyFoo",
+		doc:  "My first little foo.\n",
+	}, {
+		val:  v1,
+		path: "foos MyFoo field1",
+		doc: `field1 is an int.
+
+local field comment.
+`,
+	}, {
+		val:  v1,
+		path: "foos MyFoo field2",
+		doc:  "other field comment.\n",
+	}, {
+		val:  v1,
+		path: "foos MyFoo dup3",
+		doc: `duplicate field comment
+
+duplicate field comment
+`,
+	}, {
+		val:  v1,
+		path: "bar field1",
+		doc:  "comment from bar on field 1\n",
+	}, {
+		val:  v1,
+		path: "baz field1",
+		doc: `comment from baz on field 1
+
+comment from bar on field 1
+`,
+	}, {
+		val:  v1,
+		path: "baz field2",
+		doc:  "comment from bar on field 2\n",
+	}, {
+		val:  v2,
+		path: "Foo",
+		doc: `Another Foo.
+`,
+	}, {
+		val:  both,
+		path: "Foo",
+		doc: `Another Foo.
+
+A Foo fooses stuff.
+`,
+	}}
+	for _, tc := range testCases {
+		t.Run("field:"+tc.path, func(t *testing.T) {
+			v := tc.val.Lookup(strings.Split(tc.path, " ")...)
+			doc := docStr(v.Doc())
+			if doc != tc.doc {
+				t.Errorf("doc: got:\n%vwant:\n%v", doc, tc.doc)
+			}
+		})
+	}
+	want := "foobar defines at least foo.\n"
+	if got := docStr(inst.Doc()); got != want {
+		t.Errorf("pkg: got:\n%vwant:\n%v", got, want)
+	}
+}
+
+func docStr(docs []*ast.CommentGroup) string {
+	doc := ""
+	for _, d := range docs {
+		if doc != "" {
+			doc += "\n"
+		}
+		doc += d.Text()
+	}
+	return doc
+}
+
+func TestMarshalJSON(t *testing.T) {
+	testCases := []struct {
+		value string
+		json  string
+		err   string
+	}{{
+		value: `""`,
+		json:  `""`,
+	}, {
+		value: `null`,
+		json:  `null`,
+	}, {
+		value: `_|_`,
+		err:   "from source",
+	}, {
+		value: `(a.b)
+		a: {}`,
+		err: "undefined field",
+	}, {
+		value: `true`,
+		json:  `true`,
+	}, {
+		value: `false`,
+		json:  `false`,
+	}, {
+		value: `bool`,
+		err:   "cannot convert incomplete value",
+	}, {
+		value: `"str"`,
+		json:  `"str"`,
+	}, {
+		value: `12_000`,
+		json:  `12000`,
+	}, {
+		value: `12.000`,
+		json:  `12.000`,
+	}, {
+		value: `12M`,
+		json:  `12000000`,
+	}, {
+		value: `3.0e100`,
+		json:  `3.0E+100`,
+	}, {
+		value: `0/0`,
+		err:   "division undefined",
+	}, {
+		value: `[]`,
+		json:  `[]`,
+	}, {
+		value: `[1, 2, 3]`,
+		json:  `[1,2,3]`,
+	}, {
+		value: `[int]`,
+		err:   `0: cannot convert incomplete value`,
+	}, {
+		value: `(>=3 * [1, 2])`,
+		err:   "incomplete error", // TODO: improve error
+	}, {
+		value: `{}`,
+		json:  `{}`,
+	}, {
+		value: `{a: 2, b: 3, c: ["A", "B"]}`,
+		json:  `{"a":2,"b":3,"c":["A","B"]}`,
+	}, {
+		value: `{a: 2, b: 3, c: [string, "B"]}`,
+		err:   `c.0: cannot convert incomplete value`,
+	}, {
+		value: `{a: [{b: [0, {c: string}] }] }`,
+		err:   `a.0.b.1.c: cannot convert incomplete value`,
+	}, {
+		value: `{foo?: 1, bar?: 2, baz: 3}`,
+		json:  `{"baz":3}`,
+	}, {
+		// Has an unresolved cycle, but should not matter as all fields involved
+		// are optional
+		value: `{foo?: bar, bar?: foo, baz: 3}`,
+		json:  `{"baz":3}`,
+	}, {
+		// Issue #107
+		value: `a: 1.0/1`,
+		json:  `{"a":1.0}`,
+	}, {
+		// Issue #108
+		value: `
+		a: int
+		a: >0
+		a: <2
+
+		b: int
+		b: >=0.9
+		b: <1.1
+
+		c: int
+		c: >1
+		c: <=2
+
+		d: int
+		d: >=1
+		d: <=1.5
+
+		e: int
+		e: >=1
+		e: <=1.32
+
+		f: >=1.1 & <=1.1
+		`,
+		json: `{"a":1,"b":1,"c":2,"d":1,"e":1,"f":1.1}`,
+	}, {
+		value: `
+		#Task: {
+			{
+				op:          "pull"
+				tag:         *"latest" | string
+				tagInString: tag + "dd"
+			} | {
+				op: "scratch"
+			}
+		}
+
+		foo: #Task & {"op": "pull"}
+		`,
+		json: `{"foo":{"op":"pull","tag":"latest","tagInString":"latestdd"}}`,
+	}, {
+		// Issue #326
+		value: `x: "\(string)": "v"`,
+		err:   `x: incomplete value 'string' in interpolation`,
+	}, {
+		// Issue #326
+		value: `x: "\(bool)": "v"`,
+		err:   `x: expression in interpolation must evaluate to a number kind or string (found bool)`,
+	}, {
+		// Issue #326
+		value: `
+		x: {
+			for k, v in y {
+				"\(k)": v
+			}
+		}
+		y: {}
+		`,
+		json: `{"x":{},"y":{}}`,
+	}, {
+		// Issue #326
+		value: `
+		x: {
+			for k, v in y {
+				"\(k)": v
+			}
+		}
+		y: _
+		`,
+		err: `x: incomplete feed source`,
+	}}
+	for i, tc := range testCases {
+		t.Run(fmt.Sprintf("%d/%v", i, tc.value), func(t *testing.T) {
+			inst := getInstance(t, tc.value)
+			b, err := inst.Value().MarshalJSON()
+			checkFatal(t, err, tc.err, "init")
+
+			if got := string(b); got != tc.json {
+				t.Errorf("\n got %v;\nwant %v", got, tc.json)
+			}
+		})
+	}
+}
+
+func TestWalk(t *testing.T) {
+	testCases := []struct {
+		value string
+		out   string
+	}{{
+		value: `""`,
+		out:   `""`,
+	}, {
+		value: `null`,
+		out:   `null`,
+	}, {
+		value: `_|_`,
+		out:   "_|_(from source)",
+	}, {
+		value: `(a.b)
+		a: {}`,
+		out: `_|_(undefined field "b")`,
+	}, {
+		value: `true`,
+		out:   `true`,
+	}, {
+		value: `false`,
+		out:   `false`,
+	}, {
+		value: `bool`,
+		out:   "bool",
+	}, {
+		value: `"str"`,
+		out:   `"str"`,
+	}, {
+		value: `12_000`,
+		out:   `12000`,
+	}, {
+		value: `12.000`,
+		out:   `12.000`,
+	}, {
+		value: `12M`,
+		out:   `12000000`,
+	}, {
+		value: `3.0e100`,
+		out:   `3.0e+100`,
+	}, {
+		value: `[]`,
+		out:   `[]`,
+	}, {
+		value: `[1, 2, 3]`,
+		out:   `[1,2,3]`,
+	}, {
+		value: `[int]`,
+		out:   `[int]`,
+	}, {
+		value: `3 * [1, 2]`,
+		out:   `[1,2,1,2,1,2]`,
+	}, {
+		value: `{}`,
+		out:   `{}`,
+	}, {
+		value: `{a: 2, b: 3, c: ["A", "B"]}`,
+		out:   `{a:2,b:3,c:["A","B"]}`,
+	}}
+	for i, tc := range testCases {
+		t.Run(fmt.Sprintf("%d/%v", i, tc.value), func(t *testing.T) {
+			inst := getInstance(t, tc.value)
+			buf := []byte{}
+			stripComma := func() {
+				if n := len(buf) - 1; buf[n] == ',' {
+					buf = buf[:n]
+				}
+			}
+			inst.Value().Walk(func(v Value) bool {
+				if k, ok := v.Label(); ok {
+					buf = append(buf, k+":"...)
+				}
+				switch v.Kind() {
+				case StructKind:
+					buf = append(buf, '{')
+				case ListKind:
+					buf = append(buf, '[')
+				default:
+					buf = append(buf, fmt.Sprint(v, ",")...)
+				}
+				return true
+			}, func(v Value) {
+				switch v.Kind() {
+				case StructKind:
+					stripComma()
+					buf = append(buf, "},"...)
+				case ListKind:
+					stripComma()
+					buf = append(buf, "],"...)
+				}
+			})
+			stripComma()
+			if got := string(buf); got != tc.out {
+				t.Errorf("\n got %v;\nwant %v", got, tc.out)
+			}
+		})
+	}
+}
+
+func TestTrimZeros(t *testing.T) {
+	testCases := []struct {
+		in  string
+		out string
+	}{
+		{"", ""},
+		{"2", "2"},
+		{"2.0", "2.0"},
+		{"2.000000000000", "2.0"},
+		{"2000000000000", "2e+12"},
+		{"2000000", "2e+6"},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.in, func(t *testing.T) {
+			if got := trimZeros(tc.in); got != tc.out {
+				t.Errorf("got %q; want %q", got, tc.out)
+			}
+		})
+	}
+}
+
+func TestReference(t *testing.T) {
+	testCases := []struct {
+		input string
+		want  string
+	}{{
+		input: "v: w: x: _|_",
+		want:  "",
+	}, {
+		input: "v: w: x: 2",
+		want:  "",
+	}, {
+		input: "v: w: x: a, a: 1",
+		want:  "a",
+	}, {
+		input: "v: w: x: a.b.c, a: b: c: 1",
+		want:  "a b c",
+	}, {
+		input: "v: w: x: w.a.b.c, v: w: a: b: c: 1",
+		want:  "v w a b c",
+	}, {
+		input: `v: w: x: w.a.b.c, v: w: a: b: c: 1, #D: 3, opt?: 3, "v\(#D)": 3, X: {a: 3}, X`,
+		want:  "v w a b c",
+	}, {
+		input: `v: w: x: w.a[bb]["c"], v: w: a: b: c: 1, bb: "b"`,
+		want:  "v w a b c",
+	}, {
+		input: `v: {
+			for t in src {
+				w: "t\(t)": 1
+				w: "\(t)": w["t\(t)"]
+			}
+		},
+		src: ["x", "y"]`,
+		want: "v w tx",
+	}, {
+		input: `
+		v: w: x: a
+		a: 1
+		for i in [] {
+		}
+		`,
+		want: "a",
+	}, {
+		input: `
+		v: w: close({x: a})
+		a: 1
+		`,
+		want: "a",
+	}}
+	for _, tc := range testCases {
+		t.Run("", func(t *testing.T) {
+			var r Runtime
+			inst, _ := r.Compile("in", tc.input) // getInstance(t, tc.input)
+			v := inst.Lookup("v", "w", "x")
+			inst, a := v.Reference()
+			if got := strings.Join(a, " "); got != tc.want {
+				t.Errorf("\n got %v;\nwant %v", got, tc.want)
+			}
+
+			if tc.want != "" {
+				v := inst.Lookup(a...)
+				if x, _ := v.Int64(); x != 1 {
+					t.Errorf("path resolved to %s; want 1", v)
+				}
+			}
+		})
+	}
+}
+
+func TestPathCorrection(t *testing.T) {
+	testCases := []struct {
+		input  string
+		lookup func(i *Instance) Value
+		want   string
+		skip   bool
+	}{{
+		input: `
+		a: b: {
+			c: d: b
+		}
+		`,
+		lookup: func(i *Instance) Value {
+			_, a := i.Lookup("a", "b", "c", "d").Expr()
+			return a[0].Lookup("b", "c", "d")
+		},
+		want: "a.b",
+	}, {
+		input: `
+		a: {
+			c: 3
+			{x: c}
+		}
+		`,
+		lookup: func(i *Instance) Value {
+			_, a := i.Lookup("a").Expr()
+			return a[1].Lookup("x")
+		},
+		want: "a.c",
+	}, {
+		input: `
+		a: b: [...T]
+		a: b: [...T]
+		T: 1
+		`,
+		lookup: func(i *Instance) Value {
+			v, _ := i.Lookup("a", "b").Elem()
+			_, a := v.Expr()
+			return a[0]
+		},
+		want: "T",
+	}, {
+		input: `
+			#S: {
+				b?: [...#T]
+				b?: [...#T]
+			}
+			#T: int
+			`,
+		lookup: func(i *Instance) Value {
+			v := i.LookupDef("#S")
+			f, _ := v.LookupField("b")
+			v, _ = f.Value.Elem()
+			_, a := v.Expr()
+			return a[0]
+		},
+		want: "#T",
+	}, {
+		input: `
+			#a: {
+				#T: {b: 3}
+				close({}) | close({c: #T}) | close({d: string})
+			}
+			`,
+		lookup: func(i *Instance) Value {
+			f, _ := i.LookupField("#a")
+			_, a := f.Value.Expr() // &
+			_, a = a[1].Expr()     // |
+			return a[1].Lookup("c")
+		},
+		want: "#a.#T",
+	}, {
+		input: `
+		package foo
+
+		#Struct: {
+			#T: int
+
+			{b?: #T}
+		}`,
+		want: "#Struct.#T",
+		lookup: func(inst *Instance) Value {
+			// Locate Struct
+			i, _ := inst.Value().Fields(Definitions(true))
+			if !i.Next() {
+				t.Fatal("no fields")
+			}
+			// Locate b
+			i, _ = i.Value().Fields(Definitions(true), Optional(true))
+			if !(i.Next() && i.Next()) {
+				t.Fatal("no fields")
+			}
+			v := i.Value()
+			return v
+		},
+	}, {
+		input: `
+		package foo
+
+		#A: #B: #T
+
+		#T: {
+			a: #S.#U
+			#S: #U: {}
+		}
+		`,
+		want: "#T.#S.#U",
+		lookup: func(inst *Instance) Value {
+			f, _ := inst.Value().LookupField("#A")
+			f, _ = f.Value.LookupField("#B")
+			v := f.Value
+			v = Dereference(v)
+			v = v.Lookup("a")
+			return v
+		},
+	}, {
+		input: `
+		package foo
+
+		#A: #B: #T
+
+		#T: {
+			a: [...#S]
+			#S: {}
+		}
+		`,
+		want: "#T.#S",
+		lookup: func(inst *Instance) Value {
+			f, _ := inst.Value().LookupField("#A")
+			f, _ = f.Value.LookupField("#B")
+			v := f.Value
+			v = Dereference(v)
+			v, _ = v.Lookup("a").Elem()
+			return v
+		},
+	}, {
+		input: `
+		#A: {
+			b: #T
+		}
+
+		#T: {
+			a: #S
+			#S: {}
+		}
+		`,
+		want: "#T.#S",
+		lookup: func(inst *Instance) Value {
+			f, _ := inst.Value().LookupField("#A")
+			v := f.Value.Lookup("b")
+			v = Dereference(v)
+			v = v.Lookup("a")
+			return v
+		},
+	}, {
+		// TODO(eval): embedded structs are currently represented at the same
+		// level as the enclosing struct. This means that the parent of an
+		// embedded struct skips the struct in which it is embedded. Treat
+		// embedded structs as "anonymous" fields.
+		// This could perhaps be made fixed with dereferencing as well.
+		skip: true,
+		input: `
+		#Tracing: {
+			#T: { address?: string }
+			#S: { ip?: string }
+
+			close({}) | close({
+				t: #T
+			}) | close({
+				s: S
+			})
+		}
+		#X: {}
+		#X // Disconnect top-level struct from the one visible by close.
+		`,
+		want: "",
+		lookup: func(inst *Instance) Value {
+			f, _ := inst.Value().LookupField("#Tracing")
+			v := f.Value.Eval()
+			_, args := v.Expr()
+			v = args[1].Lookup("t")
+			v = Dereference(v)
+			return v
+		},
+	}}
+	for _, tc := range testCases {
+		if tc.skip {
+			continue
+		}
+		t.Run("", func(t *testing.T) {
+			var r Runtime
+			inst, err := r.Compile("in", tc.input)
+			if err != nil {
+				t.Fatal(err)
+			}
+			v := tc.lookup(inst)
+			gotInst, ref := v.Reference()
+			if gotInst != inst {
+				t.Error("reference not in original instance")
+			}
+			gotPath := strings.Join(ref, ".")
+			if gotPath != tc.want {
+				t.Errorf("got path %s; want %s", gotPath, tc.want)
+			}
+		})
+	}
+}
+
+func TestReferences(t *testing.T) {
+	config1 := `
+	a: {
+		b: 3
+	}
+	c: {
+		d: a.b
+		e: c.d
+		f: a
+	}
+	`
+	config2 := `
+	a: { c: 3 }
+	b: { c: int, d: 4 }
+	r: (a & b).c
+	c: {args: s1 + s2}.args
+	s1: string
+	s2: string
+	d: ({arg: b}).arg.c
+	e: f.arg.c
+	f: {arg: b}
+	`
+	testCases := []struct {
+		config string
+		in     string
+		out    string
+	}{
+		{config1, "c.d", "a.b"},
+		{config1, "c.e", "c.d"},
+		{config1, "c.f", "a"},
+
+		{config2, "r", "a.c b.c"},
+		{config2, "c", "s1 s2"},
+		// {config2, "d", "b.c"}, // TODO: make this work as well.
+		{config2, "e", "f.arg.c"}, // TODO: should also report b.c.
+	}
+	for _, tc := range testCases {
+		t.Run(tc.in, func(t *testing.T) {
+			ctx, st := compileFile(t, tc.config)
+			v := newValueRoot(ctx, st)
+			for _, k := range strings.Split(tc.in, ".") {
+				obj, err := v.structValFull(ctx)
+				if err != nil {
+					t.Fatal(err)
+				}
+				v = obj.Lookup(k)
+			}
+			got := []string{}
+			for _, r := range v.References() {
+				got = append(got, strings.Join(r, "."))
+			}
+			want := strings.Split(tc.out, " ")
+			if !reflect.DeepEqual(got, want) {
+				t.Errorf("got %v; want %v", got, want)
+			}
+		})
+	}
+}
+
+func checkErr(t *testing.T, err error, str, name string) bool {
+	t.Helper()
+	if err == nil {
+		if str != "" {
+			t.Errorf(`err:%s: got ""; want %q`, name, str)
+		}
+		return true
+	}
+	return checkFailed(t, err, str, name)
+}
+
+func checkFatal(t *testing.T, err error, str, name string) {
+	t.Helper()
+	if !checkFailed(t, err, str, name) {
+		t.SkipNow()
+	}
+}
+
+func checkFailed(t *testing.T, err error, str, name string) bool {
+	t.Helper()
+	if err != nil {
+		got := err.Error()
+		if str == "" {
+			t.Fatalf(`err:%s: got %q; want ""`, name, got)
+		}
+		if !strings.Contains(got, str) {
+			t.Errorf(`err:%s: got %q; want %q`, name, got, str)
+		}
+		return false
+	}
+	return true
+}
+
+func TestExpr(t *testing.T) {
+	testCases := []struct {
+		input string
+		want  string
+	}{{
+		input: "v: 3",
+		want:  "3",
+	}, {
+		input: "v: 3 + 4",
+		want:  "+(3 4)",
+	}, {
+		input: "v: !a, a: 3",
+		want:  `!(.(<0> "a"))`,
+	}, {
+		input: "v: 1 | 2 | 3 | *4",
+		want:  "|(1 2 3 4)",
+	}, {
+		input: "v: 2 & 5",
+		want:  "&(2 5)",
+	}, {
+		input: "v: 2 | 5",
+		want:  "|(2 5)",
+	}, {
+		input: "v: 2 && 5",
+		want:  "&&(2 5)",
+	}, {
+		input: "v: 2 || 5",
+		want:  "||(2 5)",
+	}, {
+		input: "v: 2 == 5",
+		want:  "==(2 5)",
+	}, {
+		input: "v: !b, b: true",
+		want:  `!(.(<0> "b"))`,
+	}, {
+		input: "v: 2 != 5",
+		want:  "!=(2 5)",
+	}, {
+		input: "v: <5",
+		want:  "<(5)",
+	}, {
+		input: "v: 2 <= 5",
+		want:  "<=(2 5)",
+	}, {
+		input: "v: 2 > 5",
+		want:  ">(2 5)",
+	}, {
+		input: "v: 2 >= 5",
+		want:  ">=(2 5)",
+	}, {
+		input: "v: 2 =~ 5",
+		want:  "=~(2 5)",
+	}, {
+		input: "v: 2 !~ 5",
+		want:  "!~(2 5)",
+	}, {
+		input: "v: 2 + 5",
+		want:  "+(2 5)",
+	}, {
+		input: "v: 2 - 5",
+		want:  "-(2 5)",
+	}, {
+		input: "v: 2 * 5",
+		want:  "*(2 5)",
+	}, {
+		input: "v: 2 / 5",
+		want:  "/(2 5)",
+	}, {
+		input: "v: 2 quo 5",
+		want:  "quo(2 5)",
+	}, {
+		input: "v: 2 rem 5",
+		want:  "rem(2 5)",
+	}, {
+		input: "v: 2 div 5",
+		want:  "div(2 5)",
+	}, {
+		input: "v: 2 mod 5",
+		want:  "mod(2 5)",
+	}, {
+		input: "v: a.b, a: b: 4",
+		want:  `.(.(<0> "a") "b")`,
+	}, {
+		input: `v: a["b"], a: b: 3 `,
+		want:  `[](.(<0> "a") "b")`,
+	}, {
+		input: "v: a[2:5], a: [1, 2, 3, 4, 5]",
+		want:  `[:](.(<0> "a") 2 5)`,
+	}, {
+		input: "v: len([])",
+		want:  "()(len [])",
+	}, {
+		input: "v: a.b, a: { b: string }",
+		want:  `.(.(<0> "a") "b")`,
+	}, {
+		input: `v: "Hello, \(x)! Welcome to \(place)", place: string, x: string`,
+		want:  `\()("Hello, " .(<0> "x") "! Welcome to " .(<0> "place") "")`,
+	}, {
+		input: `v: { a, b: 1 }, a: 2`,
+		want:  `&(<0>{b: 1} .(<0> "a"))`,
+	}, {
+		input: `v: { {c: a}, b: a }, a: int`,
+		want:  `&(<0>{b: <1>.a} <0>{c: <1>.a})`,
+	}, {
+		input: `v: [...number] | *[1, 2, 3]`,
+		want:  `([, ...number] | *[1,2,3])`,
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.input, func(t *testing.T) {
+			v := getInstance(t, tc.input).Lookup("v")
+			got := exprStr(v)
+			if got != tc.want {
+				t.Errorf("\n got %v;\nwant %v", got, tc.want)
+			}
+		})
+	}
+}
+func exprStr(v Value) string {
+	op, operands := v.Expr()
+	if op == NoOp {
+		return debugStr(v.ctx(), v.v.v)
+	}
+	s := op.String()
+	s += "("
+	for i, v := range operands {
+		if i > 0 {
+			s += " "
+		}
+		s += exprStr(v)
+	}
+	s += ")"
+	return s
+}
diff --git a/tmp.cue b/tmp.cue
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tmp.cue