cue: add Runtime type

Used for easier programmatic creation of Instances.

Change-Id: Ic1e6ea43529399f2e3a1c2bdb637362d0b953229
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2124
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cmd/cue/cmd/common.go b/cmd/cue/cmd/common.go
index e852780..9cd6b4b 100644
--- a/cmd/cue/cmd/common.go
+++ b/cmd/cue/cmd/common.go
@@ -36,6 +36,8 @@
 	}
 }
 
+var runtime = &cue.Runtime{}
+
 var cwd = "////"
 
 // printHeader is a hacky and unprincipled way to sanatize the package path.
diff --git a/cmd/cue/cmd/import.go b/cmd/cue/cmd/import.go
index 4023d1e..72235ae 100644
--- a/cmd/cue/cmd/import.go
+++ b/cmd/cue/cmd/import.go
@@ -428,7 +428,7 @@
 
 		switch {
 		case *node != "":
-			inst, err := cue.FromExpr(expr)
+			inst, err := runtime.FromExpr(expr)
 			if err != nil {
 				return err
 			}
diff --git a/cue/build.go b/cue/build.go
index 2e7402b..59845be 100644
--- a/cue/build.go
+++ b/cue/build.go
@@ -19,10 +19,63 @@
 	"strconv"
 
 	"cuelang.org/go/cue/ast"
-	build "cuelang.org/go/cue/build"
+	"cuelang.org/go/cue/build"
 	"cuelang.org/go/cue/token"
 )
 
+// A Runtime is used for creating CUE interpretations.
+//
+// Any operation that involves two Values or Instances should originate from
+// the same Runtime.
+type Runtime struct {
+	Context *build.Context
+	idx     *index
+	ctxt    *build.Context
+}
+
+func dummyLoad(string) *build.Instance { return nil }
+
+func (r *Runtime) index() *index {
+	if r.idx == nil {
+		r.idx = newIndex()
+	}
+	return r.idx
+}
+
+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)
+	if inst.Err != nil {
+		return nil, inst.Err
+	}
+	return inst, nil
+}
+
+// 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.
+//
+func (r *Runtime) Parse(name string, source interface{}) (*Instance, error) {
+	ctx := r.Context
+	if ctx == nil {
+		ctx = build.NewContext()
+	}
+	p := ctx.NewInstance(name, dummyLoad)
+	if err := p.AddFile(name, source); err != nil {
+		return nil, err
+	}
+	return r.complete(p)
+}
+
+// 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.
 //
@@ -33,7 +86,8 @@
 	if len(instances) == 0 {
 		panic("cue: list of instances must not be empty")
 	}
-	index := newIndex()
+	var r Runtime
+	index := r.index()
 
 	loaded := []*Instance{}
 
@@ -48,8 +102,8 @@
 
 // FromExpr creates an instance from an expression.
 // Any references must be resolved beforehand.
-func FromExpr(expr ast.Expr) (*Instance, error) {
-	i := newIndex().NewInstance(nil)
+func (r *Runtime) FromExpr(expr ast.Expr) (*Instance, error) {
+	i := r.index().NewInstance(nil)
 	err := i.insertFile(&ast.File{
 		Decls: []ast.Decl{&ast.EmitDecl{Expr: expr}},
 	})
diff --git a/cue/build_test.go b/cue/build_test.go
index f06adad..e02f80d 100644
--- a/cue/build_test.go
+++ b/cue/build_test.go
@@ -41,7 +41,8 @@
 	}}
 	for _, tc := range testCases {
 		t.Run("", func(t *testing.T) {
-			inst, err := FromExpr(tc.expr)
+			r := &Runtime{}
+			inst, err := r.FromExpr(tc.expr)
 			if err != nil {
 				t.Fatal(err)
 			}
diff --git a/cue/examples_test.go b/cue/examples_test.go
new file mode 100644
index 0000000..f9922e9
--- /dev/null
+++ b/cue/examples_test.go
@@ -0,0 +1,45 @@
+// 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_test
+
+import (
+	"fmt"
+
+	"cuelang.org/go/cue"
+)
+
+func ExampleRuntime_Parse() {
+	const config = `
+	msg:   "Hello \(place)!"
+	place: "world"
+	`
+
+	var r cue.Runtime
+
+	instance, err := r.Parse("test", config)
+	if err != nil {
+		// handle error
+	}
+
+	str, err := instance.Lookup("msg").String()
+	if err != nil {
+		// handle error
+	}
+
+	fmt.Println(str)
+
+	// Output:
+	// Hello world!
+}
diff --git a/pkg/tool/file/file_test.go b/pkg/tool/file/file_test.go
index 097a5cb..474432f 100644
--- a/pkg/tool/file/file_test.go
+++ b/pkg/tool/file/file_test.go
@@ -33,7 +33,8 @@
 	if err != nil {
 		t.Fatal(err)
 	}
-	i, err := cue.FromExpr(x)
+	var r cue.Runtime
+	i, err := r.FromExpr(x)
 	if err != nil {
 		t.Fatal(err)
 	}