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/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
+}