internal/core/adt: initial commit

Defines Abstract Data Types. These are internal
types used by the CUE evaluator.

Change-Id: Ibb109bf3b5d63525199cab05ec609595bee0a5bf
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6500
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
diff --git a/internal/core/adt/adt.go b/internal/core/adt/adt.go
new file mode 100644
index 0000000..2a91ff9
--- /dev/null
+++ b/internal/core/adt/adt.go
@@ -0,0 +1,289 @@
+// 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 adt
+
+import "cuelang.org/go/cue/ast"
+
+// A Node is any abstract data type representing an value or expression.
+type Node interface {
+	Source() ast.Node
+	node() // enforce internal.
+}
+
+// A Decl represents all valid StructLit elements.
+type Decl interface {
+	Node
+	declNode()
+}
+
+// An Elem represents all value ListLit elements.
+//
+// All Elem values can be used as a Decl.
+type Elem interface {
+	Decl
+	elemNode()
+}
+
+// An Expr corresponds to an ast.Expr.
+//
+// All Expr values can be used as an Elem or Decl.
+type Expr interface {
+	Elem
+	expr()
+}
+
+// A Value represents a node in the evaluated data graph.
+//
+// All Values values can also be used as a Expr.
+type Value interface {
+	Expr
+	Concreteness() Concreteness
+	Kind() Kind
+}
+
+// An Evaluator provides a method to convert to a value.
+type Evaluator interface {
+	Node
+	// TODO: Eval(c Context, env *Environment) Value
+}
+
+// A Resolver represents a reference somewhere else within a tree that resolves
+// a value.
+type Resolver interface {
+	Node
+	// TODO: Resolve(c Context, env *Environment) Arc
+}
+
+// A Yielder represents 0 or more labeled values of structs or lists.
+type Yielder interface {
+	Node
+	yielderNode()
+	// TODO: Yield()
+}
+
+// A Validator validates a Value. All Validators are Values.
+type Validator interface {
+	// TODO: Validate(c Context, v Value) *Bottom
+	Value
+}
+
+// Value
+
+func (x *Vertex) Concreteness() Concreteness {
+	// Depends on concreteness of value.
+	if x.Value == nil {
+		return Concrete // Should be indetermined.
+	}
+	return x.Value.Concreteness()
+}
+
+func (x *ListMarker) Concreteness() Concreteness   { return Concrete }
+func (x *StructMarker) Concreteness() Concreteness { return Concrete }
+
+func (*Conjunction) Concreteness() Concreteness      { return Constraint }
+func (*Disjunction) Concreteness() Concreteness      { return Constraint }
+func (*BoundValue) Concreteness() Concreteness       { return Constraint }
+func (*BuiltinValidator) Concreteness() Concreteness { return Constraint }
+
+// Value and Expr
+
+func (*Bottom) Concreteness() Concreteness    { return BottomLevel }
+func (*Null) Concreteness() Concreteness      { return Concrete }
+func (*Bool) Concreteness() Concreteness      { return Concrete }
+func (*Num) Concreteness() Concreteness       { return Concrete }
+func (*String) Concreteness() Concreteness    { return Concrete }
+func (*Bytes) Concreteness() Concreteness     { return Concrete }
+func (*Top) Concreteness() Concreteness       { return Any }
+func (*BasicType) Concreteness() Concreteness { return Type }
+
+// Expr
+
+func (*StructLit) expr()       {}
+func (*ListLit) expr()         {}
+func (*DisjunctionExpr) expr() {}
+
+// Expr and Value
+
+func (*Bottom) expr()           {}
+func (*Null) expr()             {}
+func (*Bool) expr()             {}
+func (*Num) expr()              {}
+func (*String) expr()           {}
+func (*Bytes) expr()            {}
+func (*Top) expr()              {}
+func (*BasicType) expr()        {}
+func (*Vertex) expr()           {}
+func (*ListMarker) expr()       {}
+func (*StructMarker) expr()     {}
+func (*Conjunction) expr()      {}
+func (*Disjunction) expr()      {}
+func (*BoundValue) expr()       {}
+func (*BuiltinValidator) expr() {}
+
+// Expr and Resolver
+
+func (*FieldReference) expr()   {}
+func (*LabelReference) expr()   {}
+func (*DynamicReference) expr() {}
+func (*ImportReference) expr()  {}
+func (*LetReference) expr()     {}
+
+// Expr and Evaluator
+
+func (*BoundExpr) expr()     {}
+func (*SelectorExpr) expr()  {}
+func (*IndexExpr) expr()     {}
+func (*SliceExpr) expr()     {}
+func (*Interpolation) expr() {}
+func (*UnaryExpr) expr()     {}
+func (*BinaryExpr) expr()    {}
+func (*CallExpr) expr()      {}
+
+// Decl and Expr (so allow attaching original source in Conjunct)
+
+func (*Field) declNode()                {}
+func (x *Field) expr() Expr             { return x.Value }
+func (*OptionalField) declNode()        {}
+func (x *OptionalField) expr() Expr     { return x.Value }
+func (*BulkOptionalField) declNode()    {}
+func (x *BulkOptionalField) expr() Expr { return x.Value }
+func (*DynamicField) declNode()         {}
+func (x *DynamicField) expr() Expr      { return x.Value }
+
+// Decl and Yielder
+
+func (*LetClause) declNode()    {}
+func (*LetClause) yielderNode() {}
+
+// Decl and Elem
+
+func (*StructLit) declNode()        {}
+func (*StructLit) elemNode()        {}
+func (*ListLit) declNode()          {}
+func (*ListLit) elemNode()          {}
+func (*Ellipsis) elemNode()         {}
+func (*Ellipsis) declNode()         {}
+func (*Bottom) declNode()           {}
+func (*Bottom) elemNode()           {}
+func (*Null) declNode()             {}
+func (*Null) elemNode()             {}
+func (*Bool) declNode()             {}
+func (*Bool) elemNode()             {}
+func (*Num) declNode()              {}
+func (*Num) elemNode()              {}
+func (*String) declNode()           {}
+func (*String) elemNode()           {}
+func (*Bytes) declNode()            {}
+func (*Bytes) elemNode()            {}
+func (*Top) declNode()              {}
+func (*Top) elemNode()              {}
+func (*BasicType) declNode()        {}
+func (*BasicType) elemNode()        {}
+func (*BoundExpr) declNode()        {}
+func (*BoundExpr) elemNode()        {}
+func (*Vertex) declNode()           {}
+func (*Vertex) elemNode()           {}
+func (*ListMarker) declNode()       {}
+func (*ListMarker) elemNode()       {}
+func (*StructMarker) declNode()     {}
+func (*StructMarker) elemNode()     {}
+func (*Conjunction) declNode()      {}
+func (*Conjunction) elemNode()      {}
+func (*Disjunction) declNode()      {}
+func (*Disjunction) elemNode()      {}
+func (*BoundValue) declNode()       {}
+func (*BoundValue) elemNode()       {}
+func (*BuiltinValidator) declNode() {}
+func (*BuiltinValidator) elemNode() {}
+func (*FieldReference) declNode()   {}
+func (*FieldReference) elemNode()   {}
+func (*LabelReference) declNode()   {}
+func (*LabelReference) elemNode()   {}
+func (*DynamicReference) declNode() {}
+func (*DynamicReference) elemNode() {}
+func (*ImportReference) declNode()  {}
+func (*ImportReference) elemNode()  {}
+func (*LetReference) declNode()     {}
+func (*LetReference) elemNode()     {}
+func (*SelectorExpr) declNode()     {}
+func (*SelectorExpr) elemNode()     {}
+func (*IndexExpr) declNode()        {}
+func (*IndexExpr) elemNode()        {}
+func (*SliceExpr) declNode()        {}
+func (*SliceExpr) elemNode()        {}
+func (*Interpolation) declNode()    {}
+func (*Interpolation) elemNode()    {}
+func (*UnaryExpr) declNode()        {}
+func (*UnaryExpr) elemNode()        {}
+func (*BinaryExpr) declNode()       {}
+func (*BinaryExpr) elemNode()       {}
+func (*CallExpr) declNode()         {}
+func (*CallExpr) elemNode()         {}
+func (*DisjunctionExpr) declNode()  {}
+func (*DisjunctionExpr) elemNode()  {}
+
+// Decl, Elem, and Yielder
+
+func (*ForClause) declNode()    {}
+func (*ForClause) elemNode()    {}
+func (*ForClause) yielderNode() {}
+func (*IfClause) declNode()     {}
+func (*IfClause) elemNode()     {}
+func (*IfClause) yielderNode()  {}
+
+// Yielder
+
+func (*ValueClause) yielderNode() {}
+
+// Node
+
+func (*Vertex) node()            {}
+func (*Conjunction) node()       {}
+func (*Disjunction) node()       {}
+func (*BoundValue) node()        {}
+func (*BuiltinValidator) node()  {}
+func (*Bottom) node()            {}
+func (*Null) node()              {}
+func (*Bool) node()              {}
+func (*Num) node()               {}
+func (*String) node()            {}
+func (*Bytes) node()             {}
+func (*Top) node()               {}
+func (*BasicType) node()         {}
+func (*StructLit) node()         {}
+func (*ListLit) node()           {}
+func (*BoundExpr) node()         {}
+func (*FieldReference) node()    {}
+func (*LabelReference) node()    {}
+func (*DynamicReference) node()  {}
+func (*ImportReference) node()   {}
+func (*LetReference) node()      {}
+func (*SelectorExpr) node()      {}
+func (*IndexExpr) node()         {}
+func (*SliceExpr) node()         {}
+func (*Interpolation) node()     {}
+func (*UnaryExpr) node()         {}
+func (*BinaryExpr) node()        {}
+func (*CallExpr) node()          {}
+func (*DisjunctionExpr) node()   {}
+func (*Field) node()             {}
+func (*OptionalField) node()     {}
+func (*BulkOptionalField) node() {}
+func (*DynamicField) node()      {}
+func (*Ellipsis) node()          {}
+func (*ForClause) node()         {}
+func (*IfClause) node()          {}
+func (*LetClause) node()         {}
+func (*ValueClause) node()       {}
diff --git a/internal/core/adt/composite.go b/internal/core/adt/composite.go
new file mode 100644
index 0000000..bd5f99b
--- /dev/null
+++ b/internal/core/adt/composite.go
@@ -0,0 +1,163 @@
+// 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 adt
+
+import (
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/token"
+)
+
+// An Environment links the parent scopes for identifier lookup to a composite
+// node. Each conjunct that make up node in the tree can be associated with
+// a different environment (although some conjuncts may share an Environment).
+type Environment struct {
+	Up     *Environment
+	Vertex *Vertex
+}
+
+// A Vertex is a node in the value tree. It may be a leaf or internal node.
+// It may have arcs to represent elements of a fully evaluated struct or list.
+//
+// For structs, it only contains definitions and concrete fields.
+// optional fields are dropped.
+//
+// It maintains source information such as a list of conjuncts that contributed
+// to the value.
+type Vertex struct {
+	Parent *Vertex // Do we need this?
+
+	// Label is the feature leading to this vertex.
+	Label Feature
+
+	// Value is the value associated with this vertex. For lists and structs
+	// this is a sentinel value indicating its kind.
+	Value Value
+
+	// The parent of nodes can be followed to determine the path within the
+	// configuration of this node.
+	// Value  Value
+	Arcs []*Vertex // arcs are sorted in display order.
+
+	// Conjuncts lists the structs that ultimately formed this Composite value.
+	// This includes all selected disjuncts. This information is used to compute
+	// the topological sort of arcs.
+	Conjuncts []Conjunct
+
+	// Structs is a slice of struct literals that contributed to this value.
+	Structs []*StructLit
+}
+
+func (v *Vertex) Kind() Kind {
+	// This is possible when evaluating comprehensions. It is potentially
+	// not known at this time what the type is.
+	if v.Value == nil {
+		return TopKind
+	}
+	return v.Value.Kind()
+}
+
+func (v *Vertex) IsList() bool {
+	_, ok := v.Value.(*ListMarker)
+	return ok
+}
+
+// lookup returns the Arc with label f if it exists or nil otherwise.
+func (v *Vertex) lookup(f Feature) *Vertex {
+	for _, a := range v.Arcs {
+		if a.Label == f {
+			return a
+		}
+	}
+	return nil
+}
+
+// GetArc returns a Vertex for the outgoing arc with label f. It creates and
+// ads one if it doesn't yet exist.
+func (v *Vertex) GetArc(f Feature) (arc *Vertex, isNew bool) {
+	arc = v.lookup(f)
+	if arc == nil {
+		arc = &Vertex{Parent: v, Label: f}
+		v.Arcs = append(v.Arcs, arc)
+		isNew = true
+	}
+	return arc, isNew
+}
+
+func (v *Vertex) Source() ast.Node { return nil }
+
+// AddConjunct adds the given Conjuncts to v if it doesn't already exist.
+func (v *Vertex) AddConjunct(c Conjunct) *Bottom {
+	for _, x := range v.Conjuncts {
+		if x == c {
+			return nil
+		}
+	}
+	if v.Value != nil {
+		// This is likely a bug in the evaluator and should not happen.
+		return &Bottom{Err: errors.Newf(token.NoPos, "cannot add conjunct")}
+	}
+	v.Conjuncts = append(v.Conjuncts, c)
+	return nil
+}
+
+// Path computes the sequence of Features leading from the root to of the
+// instance to this Vertex.
+func (v *Vertex) Path() []Feature {
+	return appendPath(nil, v)
+}
+
+func appendPath(a []Feature, v *Vertex) []Feature {
+	if v.Parent == nil {
+		return a
+	}
+	a = appendPath(a, v.Parent)
+	return append(a, v.Label)
+}
+
+// An Conjunct is an Environment-Expr pair. The Environment is the starting
+// point for reference lookup for any reference contained in X.
+type Conjunct struct {
+	Env *Environment
+	x   Node
+}
+
+// TODO(perf): replace with composite literal if this helps performance.
+
+// MakeConjunct creates a conjunct from the given environment and node.
+// It panics if x cannot be used as an expression.
+func MakeConjunct(env *Environment, x Node) Conjunct {
+	switch x.(type) {
+	case Expr, interface{ expr() Expr }:
+	default:
+		panic("invalid Node type")
+	}
+	return Conjunct{env, x}
+}
+
+func (c *Conjunct) Source() ast.Node {
+	return c.x.Source()
+}
+
+func (c *Conjunct) Expr() Expr {
+	switch x := c.x.(type) {
+	case Expr:
+		return x
+	case interface{ expr() Expr }:
+		return x.expr()
+	default:
+		panic("unreachable")
+	}
+}
diff --git a/internal/core/adt/doc.go b/internal/core/adt/doc.go
new file mode 100644
index 0000000..ba251b5
--- /dev/null
+++ b/internal/core/adt/doc.go
@@ -0,0 +1,78 @@
+// 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 adt represents partially and fully evaluated CUE types.
+//
+// This package introduces several categories of types that indicate some set of
+// values that may be used in a certain situation. Concrete types may belong to
+// multiple categories.
+//
+//
+// Abstract Types
+//
+// The following types describe the a place where a value may be used:
+//
+//    Decl       a value than can be used as a StructLit element.
+//    Elem       a value than can be used as a ListLit element.
+//    Expr       represents an Expr in the CUE grammar.
+//    Value      a fully evaluated value that has no references (except for
+//               children in composite values).
+//    Node       any of the above values.
+//
+// The following types categorize nodes by function:
+//
+//    Resolver   a reference to position in the result tree.
+//    Evaluator  evaluates to 1 value.
+//    Yielder    evaluates to 0 or more values.
+//    Validator  validates another value.
+//
+//
+// Reference resolution algorithm
+//
+// A Resolver is resolved within the context of an Environment. In CUE, a
+// reference is evaluated by substituting it with a copy of the value to which
+// it refers. If the copied value itself contains references we can distinguish
+// two different cases. References that refer to values within the copied
+// reference (not regarding selectors) will henceforth point to the copied node.
+// References that point to outside the referened value will keep refering to
+// their original value.
+//
+//      a: b: {
+//        c: int
+//        d: c
+//        e: f
+//      }
+//      f: 4
+//      g: a.b { // d.c points to inside the referred value, e.f, not.
+//        c: 3
+//      }
+//
+// The implementation doesn't actually copy referred values, but rather resolves
+// references with the aid of an Environment. During compile time, each
+// references is associated with the label and a number indicating in which
+// parent scope (offset from the current) this label needs to be looked up. An
+// Environment keeps track of the point at which a value was referenced,
+// providing enough information to look up the labeled value. This Environment
+// is the identical for all references within a fields conjunct. Often, an
+// Environment can even be shared among conjuncts.
+//
+//
+// Values
+//
+// Values are fully evaluated expressions. As this means that all references
+// will have been eliminated, Values are fully defined without the need for an
+// Environment. Additionally, Values represent a fully evaluated form, stripped
+// of any comprehensions, optional fields or embeddings.
+//
+package adt
diff --git a/internal/core/adt/expr.go b/internal/core/adt/expr.go
new file mode 100644
index 0000000..c36ea9c
--- /dev/null
+++ b/internal/core/adt/expr.go
@@ -0,0 +1,546 @@
+// 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 adt
+
+import (
+	"regexp"
+
+	"github.com/cockroachdb/apd/v2"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/token"
+)
+
+// A StructLit represents an unevaluated struct literal or file body.
+type StructLit struct {
+	Src   ast.Node // ast.File or ast.StructLit
+	Decls []Decl
+
+	// administrative fields like hasreferences.
+	// hasReferences bool
+}
+
+func (x *StructLit) Source() ast.Node { return x.Src }
+
+// FIELDS
+//
+// Fields can also be used as expressions whereby the value field is the
+// expression this allows retaining more context.
+
+// Field represents a field with a fixed label. It can be a regular field,
+// definition or hidden field.
+//
+//   foo: bar
+//   #foo: bar
+//   _foo: bar
+//
+// Legacy:
+//
+//   Foo :: bar
+//
+type Field struct {
+	Src *ast.Field
+
+	Label Feature
+	Value Expr
+}
+
+func (x *Field) Source() ast.Node { return x.Src }
+
+// An OptionalField represents an optional regular field.
+//
+//   foo?: expr
+//
+type OptionalField struct {
+	Src   *ast.Field
+	Label Feature
+	Value Expr
+}
+
+func (x *OptionalField) Source() ast.Node { return x.Src }
+
+// A BulkOptionalField represents a set of optional field.
+//
+//   [expr]: expr
+//
+type BulkOptionalField struct {
+	Src    *ast.Field // Elipsis or Field
+	Filter Expr
+	Value  Expr
+	Label  Feature // for reference and formatting
+}
+
+func (x *BulkOptionalField) Source() ast.Node { return x.Src }
+
+// A Ellipsis represents a set of optional fields of a given type.
+//
+//   ...T
+//
+type Ellipsis struct {
+	Src   *ast.Ellipsis
+	Value Expr
+}
+
+func (x *Ellipsis) Source() ast.Node { return x.Src }
+
+// A DynamicField represents a regular field for which the key is computed.
+//
+//    "\(expr)": expr
+//    (expr): expr
+//
+type DynamicField struct {
+	Src   *ast.Field
+	Key   Expr
+	Value Expr
+}
+
+func (x *DynamicField) IsOptional() bool {
+	return x.Src.Optional != token.NoPos
+}
+
+func (x *DynamicField) Source() ast.Node { return x.Src }
+
+// Expressions
+
+// A ListLit represents an unevaluated list literal.
+//
+//    [a, for x in src { ... }, b, ...T]
+//
+type ListLit struct {
+	Src *ast.ListLit
+
+	// scalars, comprehensions, ...T
+	Elems []Elem
+}
+
+func (x *ListLit) Source() ast.Node { return x.Src }
+
+// -- Literals
+
+// Bottom represents an error or bottom symbol.
+type Bottom struct {
+	Src ast.Node
+	Err errors.Error
+}
+
+func (x *Bottom) Source() ast.Node { return x.Src }
+func (x *Bottom) Kind() Kind       { return BottomKind }
+
+// Null represents null. It can be used as a Value and Expr.
+type Null struct {
+	Src ast.Node
+}
+
+func (x *Null) Source() ast.Node { return x.Src }
+func (x *Null) Kind() Kind       { return NullKind }
+
+// Bool is a boolean value. It can be used as a Value and Expr.
+type Bool struct {
+	Src ast.Node
+	B   bool
+}
+
+func (x *Bool) Source() ast.Node { return x.Src }
+func (x *Bool) Kind() Kind       { return BoolKind }
+
+// Num is a numeric value. It can be used as a Value and Expr.
+type Num struct {
+	Src ast.Node
+	K   Kind        // needed?
+	X   apd.Decimal // Is integer if the apd.Decimal is an integer.
+}
+
+func (x *Num) Source() ast.Node { return x.Src }
+func (x *Num) Kind() Kind       { return x.K }
+
+// String is a string value. It can be used as a Value and Expr.
+type String struct {
+	Src ast.Node
+	Str string
+	RE  *regexp.Regexp // only set if needed
+}
+
+func (x *String) Source() ast.Node { return x.Src }
+func (x *String) Kind() Kind       { return StringKind }
+
+// Bytes is a bytes value. It can be used as a Value and Expr.
+type Bytes struct {
+	Src ast.Node
+	B   []byte
+	RE  *regexp.Regexp // only set if needed
+}
+
+func (x *Bytes) Source() ast.Node { return x.Src }
+func (x *Bytes) Kind() Kind       { return BytesKind }
+
+// -- composites: the evaluated fields of a composite are recorded in the arc
+// vertices.
+
+type ListMarker struct {
+	Src ast.Node
+}
+
+func (x *ListMarker) Source() ast.Node { return x.Src }
+func (x *ListMarker) Kind() Kind       { return ListKind }
+func (x *ListMarker) node()            {}
+
+type StructMarker struct {
+	Closed bool
+}
+
+func (x *StructMarker) Source() ast.Node { return nil }
+func (x *StructMarker) Kind() Kind       { return StructKind }
+func (x *StructMarker) node()            {}
+
+// -- top types
+
+// Top represents all possible values. It can be used as a Value and Expr.
+type Top struct{ Src *ast.Ident }
+
+func (x *Top) Source() ast.Node { return x.Src }
+func (x *Top) Kind() Kind       { return TopKind }
+
+// BasicType represents all values of a certain Kind. It can be used as a Value
+// and Expr.
+//
+//   string
+//   int
+//   num
+//   bool
+//
+type BasicType struct {
+	Src *ast.Ident
+	K   Kind
+}
+
+func (x *BasicType) Source() ast.Node { return x.Src }
+func (x *BasicType) Kind() Kind       { return x.K }
+
+// TODO: should we use UnaryExpr for Bound now we have BoundValue?
+
+// BoundExpr represents an unresolved unary comparator.
+//
+//    <a
+//    =~MyPattern
+//
+type BoundExpr struct {
+	Src  *ast.UnaryExpr
+	Op   Op
+	Expr Expr
+}
+
+func (x *BoundExpr) Source() ast.Node { return x.Src }
+
+// A BoundValue is a fully evaluated unary comparator that can be used to
+// validate other values.
+//
+//    <5
+//    =~"Name$"
+//
+type BoundValue struct {
+	Src   ast.Node
+	Op    Op
+	Value Value
+	K     Kind
+}
+
+func (x *BoundValue) Source() ast.Node { return x.Src }
+func (x *BoundValue) Kind() Kind       { return x.K }
+
+// -- References
+
+// A FieldReference represents a lexical reference to a field.
+//
+//    a
+//
+type FieldReference struct {
+	Src     *ast.Ident
+	UpCount int32
+	Label   Feature
+}
+
+func (x *FieldReference) Source() ast.Node { return x.Src }
+
+// A LabelReference refers to the string or integer value of a label.
+//
+//    [X=Pattern]: b: X
+//
+type LabelReference struct {
+	Src     *ast.Ident
+	UpCount int32
+}
+
+// TODO: should this implement resolver at all?
+
+func (x *LabelReference) Source() ast.Node { return x.Src }
+
+// A DynamicReference is like a LabelReference, but with a computed label.
+//
+//    X=(x): X
+//    X="\(x)": X
+//
+type DynamicReference struct {
+	Src     *ast.Ident
+	UpCount int32
+	Label   Expr
+
+	// TODO: only use aliases and store the actual expression only in the scope.
+	// The feature is unique for every instance. This will also allow dynamic
+	// fields to be ordered among normal fields.
+	//
+	// This could also be used to assign labels to embedded values, if they
+	// don't match a label.
+	Alias Feature
+}
+
+func (x *DynamicReference) Source() ast.Node { return x.Src }
+
+// An ImportReference refers to an imported package.
+//
+//    import "strings"
+//
+//    strings.ToLower("Upper")
+//
+type ImportReference struct {
+	Src        *ast.Ident
+	ImportPath Feature
+	Label      Feature // for informative purposes
+}
+
+func (x *ImportReference) Source() ast.Node { return x.Src }
+
+// A LetReference evaluates a let expression in its original environment.
+//
+//   let X = x
+//
+type LetReference struct {
+	Src     *ast.Ident
+	UpCount int32
+	Label   Feature // for informative purposes
+	X       Expr
+}
+
+func (x *LetReference) Source() ast.Node { return x.Src }
+
+// A SelectorExpr looks up a fixed field in an expression.
+//
+//     X.Sel
+//
+type SelectorExpr struct {
+	Src *ast.SelectorExpr
+	X   Expr
+	Sel Feature
+}
+
+func (x *SelectorExpr) Source() ast.Node { return x.Src }
+
+// IndexExpr is like a selector, but selects an index.
+//
+//    X[Index]
+//
+type IndexExpr struct {
+	Src   *ast.IndexExpr
+	X     Expr
+	Index Expr
+}
+
+func (x *IndexExpr) Source() ast.Node { return x.Src }
+
+// A SliceExpr represents a slice operation. (Not currently in spec.)
+//
+//    X[Lo:Hi:Stride]
+//
+type SliceExpr struct {
+	Src    *ast.SliceExpr
+	X      Expr
+	Lo     Expr
+	Hi     Expr
+	Stride Expr
+}
+
+func (x *SliceExpr) Source() ast.Node { return x.Src }
+
+// An Interpolation is a string interpolation.
+//
+//    "a \(b) c"
+//
+type Interpolation struct {
+	Src   *ast.Interpolation
+	K     Kind   // string or bytes
+	Parts []Expr // odd: strings, even sources
+}
+
+func (x *Interpolation) Source() ast.Node { return x.Src }
+
+// UnaryExpr is a unary expression.
+//
+//    Op X
+//    -X !X +X
+//
+type UnaryExpr struct {
+	Src *ast.UnaryExpr
+	Op  Op
+	X   Expr
+}
+
+func (x *UnaryExpr) Source() ast.Node { return x.Src }
+
+// BinaryExpr is a binary expression.
+//
+//    X + Y
+//    X & Y
+//
+type BinaryExpr struct {
+	Src *ast.BinaryExpr
+	Op  Op
+	X   Expr
+	Y   Expr
+}
+
+func (x *BinaryExpr) Source() ast.Node { return x.Src }
+
+// -- builtins
+
+// A CallExpr represents a call to a builtin.
+//
+//    len(x)
+//    strings.ToLower(x)
+//
+type CallExpr struct {
+	Src  *ast.CallExpr
+	Fun  Expr
+	Args []Expr
+}
+
+func (x *CallExpr) Source() ast.Node { return x.Src }
+
+// A BuiltinValidator is a Value that results from evaluation a partial call
+// to a builtin (using CallExpr).
+//
+//    strings.MinRunes(4)
+//
+type BuiltinValidator struct {
+	Src  *ast.CallExpr
+	Fun  Expr    // TODO: should probably be builtin.
+	Args []Value // any but the first value
+	// call *builtin // function must return a bool
+}
+
+func (x *BuiltinValidator) Source() ast.Node { return x.Src }
+func (x *BuiltinValidator) Kind() Kind       { return TopKind }
+
+// A Disjunction represents a disjunction, where each disjunct may or may not
+// be marked as a default.
+type DisjunctionExpr struct {
+	Src    *ast.BinaryExpr
+	Values []Disjunct
+
+	HasDefaults bool
+}
+
+// A Disjunct is used in Disjunction.
+type Disjunct struct {
+	Val     Expr
+	Default bool
+}
+
+func (x *DisjunctionExpr) Source() ast.Node {
+	if x.Src == nil {
+		return nil
+	}
+	return x.Src
+}
+
+// A Conjunction is a conjunction of values that cannot be represented as a
+// single value. It is the result of unification.
+type Conjunction struct {
+	Values []Value
+}
+
+func (x *Conjunction) Source() ast.Node { return nil }
+func (x *Conjunction) Kind() Kind       { return TopKind }
+
+// A disjunction is a disjunction of values. It is the result of expanding
+// a DisjunctionExpr if the expression cannot be represented as a single value.
+type Disjunction struct {
+	Src ast.Expr
+
+	// Values are the non-error disjuncts of this expression. The first
+	// NumDefault values are default values.
+	Values []*Vertex
+
+	Errors *Bottom // []bottom
+
+	// NumDefaults indicates the number of default values.
+	NumDefaults int
+}
+
+func (x *Disjunction) Source() ast.Node { return x.Src }
+func (x *Disjunction) Kind() Kind {
+	k := BottomKind
+	for _, v := range x.Values {
+		k |= v.Kind()
+	}
+	return k
+}
+
+// A ForClause represents a for clause of a comprehension. It can be used
+// as a struct or list element.
+//
+//    for k, v in src {}
+//
+type ForClause struct {
+	Syntax *ast.ForClause
+	Key    Feature
+	Value  Feature
+	Src    Expr
+	Dst    Yielder
+}
+
+func (x *ForClause) Source() ast.Node { return x.Syntax }
+
+// An IfClause represents an if clause of a comprehension. It can be used
+// as a struct or list element.
+//
+//    if cond {}
+//
+type IfClause struct {
+	Src       *ast.IfClause
+	Condition Expr
+	Dst       Yielder
+}
+
+func (x *IfClause) Source() ast.Node { return x.Src }
+
+// An LetClause represents a let clause in a comprehension.
+//
+//    for k, v in src {}
+//
+type LetClause struct {
+	Src   *ast.LetClause
+	Label Feature
+	Expr  Expr
+	Dst   Yielder
+}
+
+func (x *LetClause) Source() ast.Node { return x.Src }
+
+// A ValueClause represents the value part of a comprehension.
+type ValueClause struct {
+	*StructLit
+}
+
+func (x *ValueClause) Source() ast.Node { return x.Src }
diff --git a/internal/core/adt/feature.go b/internal/core/adt/feature.go
new file mode 100644
index 0000000..684a74e
--- /dev/null
+++ b/internal/core/adt/feature.go
@@ -0,0 +1,137 @@
+// 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 adt
+
+import (
+	"strconv"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/token"
+	"cuelang.org/go/internal"
+)
+
+// A Feature is an encoded form of a label which comprises a compact
+// representation of an integer or string label as well as a label type.
+type Feature uint32
+
+// InvalidLabel is an encoding of an erroneous label.
+const InvalidLabel Feature = 0x7 // 0xb111
+
+// MaxIndex indicates the maximum number of unique strings that are used for
+// labeles within this CUE implementation.
+const MaxIndex int64 = 1<<28 - 1
+
+// A StringIndexer coverts strings to and from an index that is unique for a
+// given string.
+type StringIndexer interface {
+	// ToIndex returns a unique positive index for s (0 < index < 2^28-1).
+	//
+	// For each pair of strings s and t it must return the same index if and
+	// only if s == t.
+	StringToIndex(s string) (index int64)
+
+	// ToString returns a string s for index such that ToIndex(s) == index.
+	IndexToString(index int64) string
+}
+
+// SelectorString reports the shortest string representation of f when used as a
+// selector.
+func (f Feature) SelectorString(index StringIndexer) string {
+	if f == 0 {
+		return "_"
+	}
+	x := f.Index()
+	switch f.Typ() {
+	case IntLabel:
+		return strconv.Itoa(int(x))
+	case StringLabel:
+		s := index.IndexToString(int64(x))
+		if ast.IsValidIdent(s) && !internal.IsDefOrHidden(s) {
+			return s
+		}
+		return strconv.Quote(s)
+	default:
+		return index.IndexToString(int64(x))
+	}
+}
+
+// MakeLabel creates a label. It reports an error if the index is out of range.
+func MakeLabel(p token.Pos, index int64, f FeatureType) (Feature, errors.Error) {
+	if 0 > index || index > MaxIndex {
+		return InvalidLabel,
+			errors.Newf(p, "int label out of range (%d not >=0 and <= %d)",
+				index, MaxIndex)
+	}
+	return Feature(index)<<indexShift | Feature(f), nil
+}
+
+// A FeatureType indicates the type of label.
+type FeatureType int8
+
+const (
+	StringLabel           FeatureType = 0 // 0b000
+	IntLabel              FeatureType = 1 // 0b001
+	DefinitionLabel       FeatureType = 3 // 0b011
+	HiddenLabel           FeatureType = 6 // 0b110
+	HiddenDefinitionLabel FeatureType = 7 // 0b111
+
+	// letLabel              FeatureType = 0b010
+
+	fTypeMask Feature = 7 // 0b111
+
+	indexShift = 3
+)
+
+// IsValid reports whether f is a valid label.
+func (f Feature) IsValid() bool { return f != InvalidLabel }
+
+// Typ reports the type of label.
+func (f Feature) Typ() FeatureType { return FeatureType(f & fTypeMask) }
+
+// IsRegular reports whether a label represents a data field.
+func (f Feature) IsRegular() bool { return f.Typ() <= IntLabel }
+
+// IsString reports whether a label represents a regular field.
+func (f Feature) IsString() bool { return f.Typ() == StringLabel }
+
+// IsDef reports whether the label is a definition (an identifier starting with
+// # or #_.
+func (f Feature) IsDef() bool {
+	if f == InvalidLabel {
+		// TODO(perf): do more mask trickery to avoid this branch.
+		return false
+	}
+	return f.Typ()&DefinitionLabel == DefinitionLabel
+}
+
+// IsInt reports whether this is an integer index.
+func (f Feature) IsInt() bool { return f.Typ() == IntLabel }
+
+// IsHidden reports whether this label is hidden (an identifier starting with
+// _ or #_).
+func (f Feature) IsHidden() bool {
+	if f == InvalidLabel {
+		// TODO(perf): do more mask trickery to avoid this branch.
+		return false
+	}
+	return f.Typ()&HiddenLabel == HiddenLabel
+}
+
+// Index reports the abstract index associated with f.
+func (f Feature) Index() int { return int(f >> indexShift) }
+
+// TODO: should let declarations be implemented as fields?
+// func (f Feature) isLet() bool  { return f.typ() == letLabel }
diff --git a/internal/core/adt/kind.go b/internal/core/adt/kind.go
new file mode 100644
index 0000000..cd82b4e
--- /dev/null
+++ b/internal/core/adt/kind.go
@@ -0,0 +1,174 @@
+// 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 adt
+
+import (
+	"fmt"
+	"math/bits"
+	"strings"
+)
+
+// Concreteness is a measure of the level of concreteness of a value, where
+// lower values mean more concrete.
+type Concreteness int
+
+const (
+	BottomLevel Concreteness = iota
+
+	// Concrete indicates a concrete scalar value, list or struct.
+	Concrete
+
+	// Constraint indicates a non-concrete scalar value that is more specific,
+	// than a top-level type.
+	Constraint
+
+	// PrimitiveType indicates a top-level specific type, for instance, string,
+	// bytes, number, or bool.
+	Type
+
+	// Any indicates any value, or top.
+	Any
+)
+
+// IsConcrete returns whether a value is concrete.
+func IsConcrete(v Value) bool {
+	if x, ok := v.(*Vertex); ok {
+		v = x.Value
+	}
+	if v == nil {
+		return false
+	}
+	return v.Concreteness() <= Concrete
+}
+
+// Kind reports the Value kind.
+type Kind uint16
+
+const (
+	NullKind Kind = (1 << iota)
+	BoolKind
+	IntKind
+	FloatKind
+	StringKind
+	BytesKind
+	ListKind
+	StructKind
+
+	allKinds
+
+	_numberKind
+
+	NumberKind = IntKind | FloatKind
+
+	BottomKind Kind = 0
+
+	NumKind          = IntKind | FloatKind
+	TopKind     Kind = (allKinds - 1) // all kinds, but not references
+	ScalarKinds      = NullKind | BoolKind |
+		IntKind | FloatKind | StringKind | BytesKind
+)
+
+// IsAnyOf reports whether k is any of the given kinds.
+//
+// For instances, k.IsAnyOf(String|Bytes) reports whether k overlaps with
+// the String or Bytes kind.
+func (k Kind) IsAnyOf(of Kind) bool {
+	return k&of != BottomKind
+}
+
+// CanString reports whether the given type can convert to a string.
+func (k Kind) CanString() bool {
+	return k&StringKind|ScalarKinds != BottomKind
+}
+
+// String returns the representation of the Kind as
+// a CUE expression. For example:
+//
+//	(IntKind|ListKind).String()
+//
+// will return:
+//
+//	(int|[...])
+func (k Kind) String() string {
+	return toString(k, kindStrs)
+}
+
+// TypeString is like String, but returns a string representation of a valid
+// CUE type.
+func (k Kind) TypeString() string {
+	return toString(k, typeStrs)
+}
+
+func toString(k Kind, m map[Kind]string) string {
+	if k == BottomKind {
+		return "_|_"
+	}
+	if k == TopKind {
+		return "_"
+	}
+	if (k & NumberKind) == NumberKind {
+		k = (k &^ NumberKind) | _numberKind
+	}
+	var buf strings.Builder
+	multiple := bits.OnesCount(uint(k)) > 1
+	if multiple {
+		buf.WriteByte('(')
+	}
+	for count := 0; ; count++ {
+		n := bits.TrailingZeros(uint(k))
+		if n == bits.UintSize {
+			break
+		}
+		bit := Kind(1 << uint(n))
+		k &^= bit
+		s, ok := m[bit]
+		if !ok {
+			s = fmt.Sprintf("bad(%d)", n)
+		}
+		if count > 0 {
+			buf.WriteByte('|')
+		}
+		buf.WriteString(s)
+	}
+	if multiple {
+		buf.WriteByte(')')
+	}
+	return buf.String()
+}
+
+var kindStrs = map[Kind]string{
+	NullKind:    "null",
+	BoolKind:    "bool",
+	IntKind:     "int",
+	FloatKind:   "float",
+	StringKind:  "string",
+	BytesKind:   "bytes",
+	StructKind:  "struct",
+	ListKind:    "list",
+	_numberKind: "number",
+}
+
+// used to generate a parseable CUE type.
+var typeStrs = map[Kind]string{
+	NullKind:    "null",
+	BoolKind:    "bool",
+	IntKind:     "int",
+	FloatKind:   "float",
+	StringKind:  "string",
+	BytesKind:   "bytes",
+	StructKind:  "{...}",
+	ListKind:    "[...]",
+	_numberKind: "number",
+}
diff --git a/internal/core/adt/kind_test.go b/internal/core/adt/kind_test.go
new file mode 100644
index 0000000..749a6dd
--- /dev/null
+++ b/internal/core/adt/kind_test.go
@@ -0,0 +1,68 @@
+// 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 adt
+
+import "testing"
+
+func TestKindString(t *testing.T) {
+	testCases := []struct {
+		input Kind
+		want  string
+	}{{
+		input: BottomKind,
+		want:  "_|_",
+	}, {
+		input: IntKind | ListKind,
+		want:  `(int|[...])`,
+	}, {
+		input: NullKind,
+		want:  "null",
+	}, {
+		input: IntKind,
+		want:  "int",
+	}, {
+		input: FloatKind,
+		want:  "float",
+	}, {
+		input: StringKind,
+		want:  "string",
+	}, {
+		input: BytesKind,
+		want:  "bytes",
+	}, {
+		input: StructKind,
+		want:  "{...}",
+	}, {
+		input: ListKind,
+		want:  "[...]",
+	}, {
+		input: NumberKind,
+		want:  "number",
+	}, {
+		input: BoolKind | NumberKind | ListKind,
+		want:  "(bool|[...]|number)",
+	}, {
+		input: 1 << 15,
+		want:  "bad(15)",
+	}}
+	for _, tc := range testCases {
+		t.Run(tc.want, func(t *testing.T) {
+			got := tc.input.TypeString()
+			if got != tc.want {
+				t.Errorf("\n got %v;\nwant %v", got, tc.want)
+			}
+		})
+	}
+}
diff --git a/internal/core/adt/op.go b/internal/core/adt/op.go
new file mode 100644
index 0000000..6383290
--- /dev/null
+++ b/internal/core/adt/op.go
@@ -0,0 +1,141 @@
+// 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 adt
+
+import "cuelang.org/go/cue/token"
+
+// Op indicates the operation at the top of an expression tree of the expression
+// use to evaluate a value.
+type Op int
+
+func (o Op) String() string {
+	return opToString[o]
+}
+
+// Values of Op.
+const (
+	NoOp Op = iota
+
+	AndOp
+	OrOp
+
+	SelectorOp
+	IndexOp
+	SliceOp
+	CallOp
+
+	BoolAndOp
+	BoolOrOp
+
+	EqualOp
+	NotOp
+	NotEqualOp
+	LessThanOp
+	LessEqualOp
+	GreaterThanOp
+	GreaterEqualOp
+
+	MatchOp
+	NotMatchOp
+
+	AddOp
+	SubtractOp
+	MultiplyOp
+	FloatQuotientOp
+	IntQuotientOp
+	IntRemainderOp
+	IntDivideOp
+	IntModuloOp
+
+	InterpolationOp
+)
+
+var opToString = map[Op]string{
+	AndOp:           "&",
+	OrOp:            "|",
+	BoolAndOp:       "&&",
+	BoolOrOp:        "||",
+	EqualOp:         "==",
+	NotOp:           "!",
+	NotEqualOp:      "!=",
+	LessThanOp:      "<",
+	LessEqualOp:     "<=",
+	GreaterThanOp:   ">",
+	GreaterEqualOp:  ">=",
+	MatchOp:         "=~",
+	NotMatchOp:      "!~",
+	AddOp:           "+",
+	SubtractOp:      "-",
+	MultiplyOp:      "*",
+	FloatQuotientOp: "/",
+	IntQuotientOp:   "quo",
+	IntRemainderOp:  "rem",
+	IntDivideOp:     "div",
+	IntModuloOp:     "mod",
+
+	SelectorOp: ".",
+	IndexOp:    "[]",
+	SliceOp:    "[:]",
+	CallOp:     "()",
+
+	InterpolationOp: `\()`,
+}
+
+// OpFromToken converts a token.Token to an Op.
+func OpFromToken(t token.Token) Op {
+	return tokenMap[t]
+}
+
+// Token returns the token.Token corresponding to the Op.
+func (op Op) Token() token.Token {
+	return opMap[op]
+}
+
+var tokenMap = map[token.Token]Op{
+	token.OR:  OrOp,  // |
+	token.AND: AndOp, // &
+
+	token.ADD: AddOp,           // +
+	token.SUB: SubtractOp,      // -
+	token.MUL: MultiplyOp,      // *
+	token.QUO: FloatQuotientOp, // /
+
+	token.IDIV: IntDivideOp,    // div
+	token.IMOD: IntModuloOp,    // mod
+	token.IQUO: IntQuotientOp,  // quo
+	token.IREM: IntRemainderOp, // rem
+
+	token.LAND: BoolAndOp, // &&
+	token.LOR:  BoolOrOp,  // ||
+
+	token.EQL: EqualOp,       // ==
+	token.LSS: LessThanOp,    // <
+	token.GTR: GreaterThanOp, // >
+	token.NOT: NotOp,         // !
+
+	token.NEQ:  NotEqualOp,     // !=
+	token.LEQ:  LessEqualOp,    // <=
+	token.GEQ:  GreaterEqualOp, // >=
+	token.MAT:  MatchOp,        // =~
+	token.NMAT: NotMatchOp,     // !~
+}
+
+var opMap = map[Op]token.Token{}
+
+func init() {
+	for t, o := range tokenMap {
+		opMap[o] = t
+	}
+}