internal/core/eval: add Stats counters

Change-Id: I54ee7f30666a44285259f222fdc5a53ac95d1648
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6642
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/core/eval/eval.go b/internal/core/eval/eval.go
index 409eda3..596a62d 100644
--- a/internal/core/eval/eval.go
+++ b/internal/core/eval/eval.go
@@ -26,6 +26,8 @@
 
 import (
 	"fmt"
+	"html/template"
+	"strings"
 
 	"cuelang.org/go/cue/ast"
 	"cuelang.org/go/cue/errors"
@@ -34,6 +36,15 @@
 	"cuelang.org/go/internal/core/debug"
 )
 
+// TODO TODO TODO TODO TODO TODO  TODO TODO TODO  TODO TODO TODO  TODO TODO TODO
+//
+// - Reuse work from previous cycles. For instance, if we can guarantee that a
+//   value is always correct for partial results, we can just process the arcs
+//   going from Partial to Finalized, without having to reevaluate the value.
+//
+// - Test closedness far more thoroughly.
+//
+
 func Evaluate(r adt.Runtime, v *adt.Vertex) {
 	format := func(n adt.Node) string {
 		return debug.NodeString(r, n, printConfig)
@@ -51,6 +62,25 @@
 	return &Evaluator{r: r, index: r}
 }
 
+type Stats struct {
+	DisjunctCount int
+	UnifyCount    int
+}
+
+var stats = template.Must(template.New("stats").Parse(`{{"" -}}
+Unifications: {{.UnifyCount}}
+Disjuncts:    {{.DisjunctCount}}`))
+
+func (s *Stats) String() string {
+	buf := strings.Builder{}
+	_ = stats.Execute(&buf, s)
+	return buf.String()
+}
+
+func (e *Evaluator) Stats() *Stats {
+	return &e.stats
+}
+
 // TODO: Note: NewContext takes essentially a cue.Value. By making this
 // type more central, we can perhaps avoid context creation.
 
@@ -83,6 +113,8 @@
 	r       adt.Runtime
 	index   adt.StringIndexer
 	closeID uint32
+
+	stats Stats
 }
 
 func (e *Evaluator) nextID() uint32 {
@@ -255,7 +287,9 @@
 	}
 	saved := *v
 
+	e.stats.UnifyCount++
 	for i := 0; ; i++ {
+		e.stats.DisjunctCount++
 
 		// Clear any remaining error.
 		if err := c.Err(); err != nil {
diff --git a/internal/core/eval/eval_test.go b/internal/core/eval/eval_test.go
index 0d465f7..604def6 100644
--- a/internal/core/eval/eval_test.go
+++ b/internal/core/eval/eval_test.go
@@ -19,7 +19,6 @@
 	"fmt"
 	"testing"
 
-	"cuelang.org/go/internal/core/adt"
 	"cuelang.org/go/internal/core/debug"
 	"cuelang.org/go/internal/core/eval"
 	"cuelang.org/go/internal/core/validate"
@@ -57,9 +56,12 @@
 			t.Fatal(err)
 		}
 
-		ctx := eval.NewContext(r, v)
+		e := eval.New(r)
+		ctx := e.NewContext(v)
 		v.Finalize(ctx)
 
+		t.Log(e.Stats())
+
 		if b := validate.Validate(r, v, &validate.Config{
 			AllErrors: true,
 		}); b != nil {
@@ -89,9 +91,6 @@
 
 	"cycle/025_cannot_resolve_references_that_would_be_ambiguous": "cycle",
 	"compile/scope": "cycle",
-
-	"resolve/034_closing_structs": "close()",
-	"fulleval/053_issue312":       "close()",
 }
 
 // TestX is for debugging. Do not delete.
@@ -121,12 +120,11 @@
 	}
 	t.Error(debug.NodeString(r, v, nil))
 
-	ctx := eval.NewContext(r, v)
-
-	ctx.Unify(ctx, v, adt.Finalized)
-	// if err != nil {
-	// 	t.Fatal(err)
-	// }
+	e := eval.New(r)
+	ctx := e.NewContext(v)
+	v.Finalize(ctx)
 
 	t.Error(debug.NodeString(r, v, nil))
+
+	t.Log(e.Stats())
 }