cue: hoist functionality to cue/internal/runtime

This will allow this code to be shared between new and old
implementation.

This makes cue use the new internal representation for labels.

Change-Id: I574f1401f9c55c0e151445d1f89312d06be49818
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6508
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/core/runtime/errors.go b/internal/core/runtime/errors.go
new file mode 100644
index 0000000..e98cdda
--- /dev/null
+++ b/internal/core/runtime/errors.go
@@ -0,0 +1,52 @@
+// 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 runtime
+
+import (
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/errors"
+	"cuelang.org/go/cue/token"
+)
+
+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
+}
diff --git a/internal/core/runtime/index.go b/internal/core/runtime/index.go
index 8af0f41..11c342c 100644
--- a/internal/core/runtime/index.go
+++ b/internal/core/runtime/index.go
@@ -19,81 +19,158 @@
 	"sync"
 
 	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/internal"
 	"cuelang.org/go/internal/core/adt"
 )
 
-// index maps conversions from label names to internal codes.
+// Index maps conversions from label names to internal codes.
 //
-// All instances belonging to the same package should share this index.
-type index struct {
-	labelMap map[string]int
+// All instances belonging to the same package should share this Index.
+//
+// INDEX IS A TRANSITIONAL TYPE TO BRIDGE THE OLD AND NEW
+// IMPLEMENTATIONS. USE RUNTIME.
+type Index struct {
+	labelMap map[string]int64
 	labels   []string
 
-	offset int
-	parent *index
+	// Change this to Instance at some point.
+	// From *structLit/*Vertex -> Instance
+	imports       map[interface{}]interface{}
+	importsByPath map[string]interface{}
+	// imports map[string]*adt.Vertex
 
-	mutex     sync.Mutex
+	offset int64
+	parent *Index
+
+	// mutex     sync.Mutex
 	typeCache sync.Map // map[reflect.Type]evaluated
 }
 
-// sharedIndex is used for indexing builtins and any other labels common to
+// SharedIndex is used for indexing builtins and any other labels common to
 // all instances.
-var sharedIndex = newSharedIndex()
+var SharedIndex = newSharedIndex()
 
-func newSharedIndex() *index {
-	i := &index{
-		labelMap: map[string]int{"": 0},
-		labels:   []string{"_"},
+var SharedIndexNew = newSharedIndex()
+
+func newSharedIndex() *Index {
+	i := &Index{
+		labelMap:      map[string]int64{"": 0},
+		labels:        []string{""},
+		imports:       map[interface{}]interface{}{},
+		importsByPath: map[string]interface{}{},
 	}
 	return i
 }
 
-// newIndex creates a new index.
-func newIndex(parent *index) *index {
-	i := &index{
-		labelMap: map[string]int{},
-		offset:   len(parent.labels) + parent.offset,
-		parent:   parent,
+// NewIndex creates a new index.
+func NewIndex(parent *Index) *Index {
+	i := &Index{
+		labelMap:      map[string]int64{},
+		imports:       map[interface{}]interface{}{},
+		importsByPath: map[string]interface{}{},
+		offset:        int64(len(parent.labels)) + parent.offset,
+		parent:        parent,
 	}
 	return i
 }
 
-func (x *index) IndexToString(i int64) string {
-	for ; int(i) < x.offset; x = x.parent {
+func (x *Index) IndexToString(i int64) string {
+	for ; i < x.offset; x = x.parent {
 	}
-	return x.labels[int(i)-x.offset]
+	return x.labels[i-x.offset]
 }
 
-func (x *index) StringToIndex(s string) int64 {
+func (x *Index) StringToIndex(s string) int64 {
 	for p := x; p != nil; p = p.parent {
 		if f, ok := p.labelMap[s]; ok {
 			return int64(f)
 		}
 	}
-	index := len(x.labelMap) + x.offset
+	index := int64(len(x.labelMap)) + x.offset
 	x.labelMap[s] = index
 	x.labels = append(x.labels, s)
 	return int64(index)
 }
 
-func (x *index) StoreType(t reflect.Type, src ast.Expr, expr adt.Expr) {
-	if expr == nil {
-		x.typeCache.Store(t, src)
-	} else {
-		x.typeCache.Store(t, expr)
+func (x *Index) HasLabel(s string) (ok bool) {
+	for c := x; c != nil; c = c.parent {
+		_, ok = c.labelMap[s]
+		if ok {
+			break
+		}
+	}
+	return ok
+}
+
+func (x *Index) StoreType(t reflect.Type, v interface{}) {
+	x.typeCache.Store(t, v)
+}
+
+func (x *Index) LoadType(t reflect.Type) (v interface{}, ok bool) {
+	v, ok = x.typeCache.Load(t)
+	return v, ok
+}
+
+func (x *Index) StrLabel(str string) adt.Feature {
+	return x.Label(str, false)
+}
+
+func (x *Index) NodeLabel(n ast.Node) (f adt.Feature, ok bool) {
+	switch label := n.(type) {
+	case *ast.BasicLit:
+		name, _, err := ast.LabelName(label)
+		return x.Label(name, false), err == nil
+	case *ast.Ident:
+		name, err := ast.ParseIdent(label)
+		return x.Label(name, true), err == nil
+	}
+	return 0, false
+}
+
+func (x *Index) Label(s string, isIdent bool) adt.Feature {
+	index := x.StringToIndex(s)
+	typ := adt.StringLabel
+	if isIdent {
+		switch {
+		case internal.IsDef(s) && internal.IsHidden(s):
+			typ = adt.HiddenDefinitionLabel
+		case internal.IsDef(s):
+			typ = adt.DefinitionLabel
+		case internal.IsHidden(s):
+			typ = adt.HiddenLabel
+		}
+	}
+	f, _ := adt.MakeLabel(nil, index, typ)
+	return f
+}
+
+func (idx *Index) LabelStr(l adt.Feature) string {
+	index := int64(l.Index())
+	return idx.IndexToString(index)
+}
+
+func (x *Index) AddInst(path string, key, p interface{}) {
+	if key == nil {
+		panic("key must not be nil")
+	}
+	x.imports[key] = p
+	if path != "" {
+		x.importsByPath[path] = key
 	}
 }
 
-func (x *index) LoadType(t reflect.Type) (src ast.Expr, expr adt.Expr, ok bool) {
-	v, ok := x.typeCache.Load(t)
-	if ok {
-		switch x := v.(type) {
-		case ast.Expr:
-			return x, nil, true
-		case adt.Expr:
-			src, _ = x.Source().(ast.Expr)
-			return src, x, true
-		}
+func (x *Index) GetImportFromNode(key interface{}) interface{} {
+	imp := x.imports[key]
+	if imp == nil && x.parent != nil {
+		return x.parent.GetImportFromNode(key)
 	}
-	return nil, nil, false
+	return imp
+}
+
+func (x *Index) GetImportFromPath(id string) interface{} {
+	key := x.importsByPath[id]
+	if key == nil && x.parent != nil {
+		return x.parent.GetImportFromPath(id)
+	}
+	return key
 }
diff --git a/internal/core/runtime/resolve.go b/internal/core/runtime/resolve.go
new file mode 100644
index 0000000..80ba789
--- /dev/null
+++ b/internal/core/runtime/resolve.go
@@ -0,0 +1,165 @@
+// 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 runtime
+
+import (
+	"path"
+	"strconv"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/build"
+	"cuelang.org/go/cue/errors"
+)
+
+func lineStr(idx *Index, n ast.Node) string {
+	return n.Pos().String()
+}
+
+func ResolveFiles(
+	idx *Index,
+	p *build.Instance,
+	isBuiltin func(s string) bool,
+) errors.Error {
+	// Link top-level declarations. As top-level entries get unified, an entry
+	// may be linked to any top-level entry of any of the files.
+	allFields := map[string]ast.Node{}
+	for _, file := range p.Files {
+		for _, d := range file.Decls {
+			if f, ok := d.(*ast.Field); ok && f.Value != nil {
+				if ident, ok := f.Label.(*ast.Ident); ok {
+					allFields[ident.Name] = f.Value
+				}
+			}
+		}
+	}
+	for _, f := range p.Files {
+		if err := ResolveFile(idx, f, p, allFields, isBuiltin); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func ResolveFile(
+	idx *Index,
+	f *ast.File,
+	p *build.Instance,
+	allFields map[string]ast.Node,
+	isBuiltin func(s string) bool,
+) errors.Error {
+	unresolved := map[string][]*ast.Ident{}
+	for _, u := range f.Unresolved {
+		unresolved[u.Name] = append(unresolved[u.Name], u)
+	}
+	fields := map[string]ast.Node{}
+	for _, d := range f.Decls {
+		if f, ok := d.(*ast.Field); ok && f.Value != nil {
+			if ident, ok := f.Label.(*ast.Ident); ok {
+				fields[ident.Name] = d
+			}
+		}
+	}
+	var errs errors.Error
+
+	specs := []*ast.ImportSpec{}
+
+	for _, spec := range f.Imports {
+		id, err := strconv.Unquote(spec.Path.Value)
+		if err != nil {
+			continue // quietly ignore the error
+		}
+		name := path.Base(id)
+		if imp := p.LookupImport(id); imp != nil {
+			name = imp.PkgName
+		} else if !isBuiltin(id) {
+			errs = errors.Append(errs,
+				nodeErrorf(spec, "package %q not found", id))
+			continue
+		}
+		if spec.Name != nil {
+			name = spec.Name.Name
+		}
+		if n, ok := fields[name]; ok {
+			errs = errors.Append(errs, nodeErrorf(spec,
+				"%s redeclared as imported package name\n"+
+					"\tprevious declaration at %v", name, lineStr(idx, n)))
+			continue
+		}
+		fields[name] = spec
+		used := false
+		for _, u := range unresolved[name] {
+			used = true
+			u.Node = spec
+		}
+		if !used {
+			specs = append(specs, spec)
+		}
+	}
+
+	// Verify each import is used.
+	if len(specs) > 0 {
+		// Find references to imports. This assumes that identifiers in labels
+		// are not resolved or that such errors are caught elsewhere.
+		ast.Walk(f, nil, func(n ast.Node) {
+			if x, ok := n.(*ast.Ident); ok {
+				// As we also visit labels, most nodes will be nil.
+				if x.Node == nil {
+					return
+				}
+				for i, s := range specs {
+					if s == x.Node {
+						specs[i] = nil
+						return
+					}
+				}
+			}
+		})
+
+		// Add errors for unused imports.
+		for _, spec := range specs {
+			if spec == nil {
+				continue
+			}
+			if spec.Name == nil {
+				errs = errors.Append(errs, nodeErrorf(spec,
+					"imported and not used: %s", spec.Path.Value))
+			} else {
+				errs = errors.Append(errs, nodeErrorf(spec,
+					"imported and not used: %s as %s", spec.Path.Value, spec.Name))
+			}
+		}
+	}
+
+	k := 0
+	for _, u := range f.Unresolved {
+		if u.Node != nil {
+			continue
+		}
+		if n, ok := allFields[u.Name]; ok {
+			u.Node = n
+			u.Scope = f
+			continue
+		}
+		f.Unresolved[k] = u
+		k++
+	}
+	f.Unresolved = f.Unresolved[:k]
+	// TODO: also need to resolve types.
+	// if len(f.Unresolved) > 0 {
+	// 	n := f.Unresolved[0]
+	// 	return ctx.mkErr(newBase(n), "unresolved reference %s", n.Name)
+	// }
+	return errs
+}
diff --git a/internal/core/runtime/resolve_test.go b/internal/core/runtime/resolve_test.go
new file mode 100644
index 0000000..42ec1dd
--- /dev/null
+++ b/internal/core/runtime/resolve_test.go
@@ -0,0 +1,63 @@
+// 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 runtime
+
+import (
+	"testing"
+
+	"cuelang.org/go/cue/ast"
+	"cuelang.org/go/cue/build"
+)
+
+// TestPartiallyResolved tests that the resolve will detect the usage of
+// imports that are referenced by previously resolved nodes.
+func TestPartiallyResolved(t *testing.T) {
+	const importPath = "acme.com/foo"
+	spec1 := &ast.ImportSpec{
+		Path: ast.NewString(importPath),
+	}
+	spec2 := &ast.ImportSpec{
+		Name: ast.NewIdent("bar"),
+		Path: ast.NewString(importPath),
+	}
+
+	isBuiltin := func(s string) bool { return false }
+
+	f := &ast.File{
+		Decls: []ast.Decl{
+			&ast.ImportDecl{Specs: []*ast.ImportSpec{spec1, spec2}},
+			&ast.Field{
+				Label: ast.NewIdent("X"),
+				Value: &ast.Ident{Name: "foo", Node: spec1},
+			},
+			&ast.Alias{
+				Ident: ast.NewIdent("Y"),
+				Expr:  &ast.Ident{Name: "bar", Node: spec2},
+			},
+		},
+		Imports: []*ast.ImportSpec{spec1, spec2},
+	}
+
+	err := ResolveFile(nil, f, &build.Instance{
+		Imports: []*build.Instance{{
+			ImportPath: importPath,
+			PkgName:    "foo",
+		}},
+	}, map[string]ast.Node{}, isBuiltin)
+
+	if err != nil {
+		t.Errorf("exected no error, found %v", err)
+	}
+}
diff --git a/internal/core/runtime/runtime.go b/internal/core/runtime/runtime.go
index c2f7d83..61ae9a1 100644
--- a/internal/core/runtime/runtime.go
+++ b/internal/core/runtime/runtime.go
@@ -15,22 +15,59 @@
 package runtime
 
 import (
+	"reflect"
+
+	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/errors"
 	"cuelang.org/go/internal/core/adt"
 )
 
 // A Runtime maintains data structures for indexing and resuse for evaluation.
 type Runtime struct {
-	*index
+	index *Index
 }
 
 // New creates a new Runtime.
 func New() *Runtime {
 	return &Runtime{
-		index: newIndex(sharedIndex),
+		index: NewIndex(SharedIndexNew),
 	}
 }
 
+func (x *Runtime) IndexToString(i int64) string {
+	return x.index.IndexToString(i)
+}
+
+func (x *Runtime) StringToIndex(s string) int64 {
+	return x.index.StringToIndex(s)
+}
+
 func (x *Runtime) LoadImport(importPath string) (*adt.Vertex, errors.Error) {
-	return nil, nil
+	v := x.index.GetImportFromPath(importPath)
+	if v == nil {
+		return nil, nil
+	}
+	return v.(*adt.Vertex), nil
+}
+
+func (x *Runtime) StoreType(t reflect.Type, src ast.Expr, expr adt.Expr) {
+	if expr == nil {
+		x.index.StoreType(t, src)
+	} else {
+		x.index.StoreType(t, expr)
+	}
+}
+
+func (x *Runtime) LoadType(t reflect.Type) (src ast.Expr, expr adt.Expr, ok bool) {
+	v, ok := x.index.LoadType(t)
+	if ok {
+		switch x := v.(type) {
+		case ast.Expr:
+			return x, nil, true
+		case adt.Expr:
+			src, _ = x.Source().(ast.Expr)
+			return src, x, true
+		}
+	}
+	return nil, nil, false
 }