lang: Plumb through a context into unification
If we have a long type unification, we might want to cancel it early. This also helps us visualize where we want context to be seen.
This commit is contained in:
@@ -276,7 +276,7 @@ func (obj *GAPI) Cli(info *gapi.Info) (*gapi.Deploy, error) {
|
|||||||
Debug: debug,
|
Debug: debug,
|
||||||
Logf: unificationLogf,
|
Logf: unificationLogf,
|
||||||
}
|
}
|
||||||
unifyErr := unifier.Unify()
|
unifyErr := unifier.Unify(context.TODO())
|
||||||
delta := time.Since(startTime)
|
delta := time.Since(startTime)
|
||||||
formatted := delta.String()
|
formatted := delta.String()
|
||||||
if delta.Milliseconds() > 1000 { // 1 second
|
if delta.Milliseconds() > 1000 { // 1 second
|
||||||
|
|||||||
@@ -464,7 +464,7 @@ func TestAstFunc1(t *testing.T) {
|
|||||||
Debug: testing.Verbose(),
|
Debug: testing.Verbose(),
|
||||||
Logf: xlogf,
|
Logf: xlogf,
|
||||||
}
|
}
|
||||||
err = unifier.Unify()
|
err = unifier.Unify(context.TODO())
|
||||||
if (!fail || !failUnify) && err != nil {
|
if (!fail || !failUnify) && err != nil {
|
||||||
t.Errorf("test #%d: FAIL", index)
|
t.Errorf("test #%d: FAIL", index)
|
||||||
t.Errorf("test #%d: could not unify types: %+v", index, err)
|
t.Errorf("test #%d: could not unify types: %+v", index, err)
|
||||||
@@ -1034,7 +1034,7 @@ func TestAstFunc2(t *testing.T) {
|
|||||||
Debug: testing.Verbose(),
|
Debug: testing.Verbose(),
|
||||||
Logf: xlogf,
|
Logf: xlogf,
|
||||||
}
|
}
|
||||||
err = unifier.Unify()
|
err = unifier.Unify(context.TODO())
|
||||||
if (!fail || !failUnify) && err != nil {
|
if (!fail || !failUnify) && err != nil {
|
||||||
t.Errorf("test #%d: FAIL", index)
|
t.Errorf("test #%d: FAIL", index)
|
||||||
t.Errorf("test #%d: could not unify types: %+v", index, err)
|
t.Errorf("test #%d: could not unify types: %+v", index, err)
|
||||||
@@ -1836,7 +1836,7 @@ func TestAstFunc3(t *testing.T) {
|
|||||||
Debug: testing.Verbose(),
|
Debug: testing.Verbose(),
|
||||||
Logf: xlogf,
|
Logf: xlogf,
|
||||||
}
|
}
|
||||||
err = unifier.Unify()
|
err = unifier.Unify(context.TODO())
|
||||||
if (!fail || !failUnify) && err != nil {
|
if (!fail || !failUnify) && err != nil {
|
||||||
t.Errorf("test #%d: FAIL", index)
|
t.Errorf("test #%d: FAIL", index)
|
||||||
t.Errorf("test #%d: could not unify types: %+v", index, err)
|
t.Errorf("test #%d: could not unify types: %+v", index, err)
|
||||||
|
|||||||
@@ -232,7 +232,7 @@ func (obj *Lang) Init() error {
|
|||||||
Debug: obj.Debug,
|
Debug: obj.Debug,
|
||||||
Logf: logf,
|
Logf: logf,
|
||||||
}
|
}
|
||||||
unifyErr := unifier.Unify()
|
unifyErr := unifier.Unify(context.TODO())
|
||||||
obj.Logf("type unification took: %s", time.Since(timing))
|
obj.Logf("type unification took: %s", time.Since(timing))
|
||||||
if unifyErr != nil {
|
if unifyErr != nil {
|
||||||
return errwrap.Wrapf(unifyErr, "could not unify types")
|
return errwrap.Wrapf(unifyErr, "could not unify types")
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
package unification // TODO: can we put this solver in a sub-package?
|
package unification // TODO: can we put this solver in a sub-package?
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
@@ -64,9 +65,9 @@ const (
|
|||||||
// SimpleInvariantSolver with the log parameter of your choice specified. The
|
// SimpleInvariantSolver with the log parameter of your choice specified. The
|
||||||
// result satisfies the correct signature for the solver parameter of the
|
// result satisfies the correct signature for the solver parameter of the
|
||||||
// Unification function.
|
// Unification function.
|
||||||
func SimpleInvariantSolverLogger(logf func(format string, v ...interface{})) func([]interfaces.Invariant, []interfaces.Expr) (*InvariantSolution, error) {
|
func SimpleInvariantSolverLogger(logf func(format string, v ...interface{})) func(context.Context, []interfaces.Invariant, []interfaces.Expr) (*InvariantSolution, error) {
|
||||||
return func(invariants []interfaces.Invariant, expected []interfaces.Expr) (*InvariantSolution, error) {
|
return func(ctx context.Context, invariants []interfaces.Invariant, expected []interfaces.Expr) (*InvariantSolution, error) {
|
||||||
return SimpleInvariantSolver(invariants, expected, logf)
|
return SimpleInvariantSolver(ctx, invariants, expected, logf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +196,7 @@ func DebugSolverState(solved map[interfaces.Expr]*types.Type, equalities []inter
|
|||||||
|
|
||||||
// SimpleInvariantSolver is an iterative invariant solver for AST expressions.
|
// SimpleInvariantSolver is an iterative invariant solver for AST expressions.
|
||||||
// It is intended to be very simple, even if it's computationally inefficient.
|
// It is intended to be very simple, even if it's computationally inefficient.
|
||||||
func SimpleInvariantSolver(invariants []interfaces.Invariant, expected []interfaces.Expr, logf func(format string, v ...interface{})) (*InvariantSolution, error) {
|
func SimpleInvariantSolver(ctx context.Context, invariants []interfaces.Invariant, expected []interfaces.Expr, logf func(format string, v ...interface{})) (*InvariantSolution, error) {
|
||||||
debug := false // XXX: add to interface
|
debug := false // XXX: add to interface
|
||||||
process := func(invariants []interfaces.Invariant) ([]interfaces.Invariant, []*interfaces.ExclusiveInvariant, error) {
|
process := func(invariants []interfaces.Invariant) ([]interfaces.Invariant, []*interfaces.ExclusiveInvariant, error) {
|
||||||
equalities := []interfaces.Invariant{}
|
equalities := []interfaces.Invariant{}
|
||||||
@@ -395,6 +396,12 @@ func SimpleInvariantSolver(invariants []interfaces.Invariant, expected []interfa
|
|||||||
// run until we're solved, stop consuming equalities, or type clash
|
// run until we're solved, stop consuming equalities, or type clash
|
||||||
Loop:
|
Loop:
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
// pass
|
||||||
|
}
|
||||||
// Once we're done solving everything else except the generators
|
// Once we're done solving everything else except the generators
|
||||||
// then we can exit, but we want to make sure the generators had
|
// then we can exit, but we want to make sure the generators had
|
||||||
// a chance to "speak up" and make sure they were part of Unify.
|
// a chance to "speak up" and make sure they were part of Unify.
|
||||||
@@ -1314,6 +1321,12 @@ Loop:
|
|||||||
|
|
||||||
// let's try each combination, one at a time...
|
// let's try each combination, one at a time...
|
||||||
for i, ex := range exclusivesProduct(exclusives) { // [][]interfaces.Invariant
|
for i, ex := range exclusivesProduct(exclusives) { // [][]interfaces.Invariant
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
default:
|
||||||
|
// pass
|
||||||
|
}
|
||||||
logf("%s: exclusive(%d):\n%+v", Name, i, ex)
|
logf("%s: exclusive(%d):\n%+v", Name, i, ex)
|
||||||
// we could waste a lot of cpu, and start from
|
// we could waste a lot of cpu, and start from
|
||||||
// the beginning, but instead we could use the
|
// the beginning, but instead we could use the
|
||||||
@@ -1324,7 +1337,7 @@ Loop:
|
|||||||
recursiveInvariants = append(recursiveInvariants, ex...)
|
recursiveInvariants = append(recursiveInvariants, ex...)
|
||||||
// FIXME: implement RecursionDepthLimit
|
// FIXME: implement RecursionDepthLimit
|
||||||
logf("%s: recursing...", Name)
|
logf("%s: recursing...", Name)
|
||||||
solution, err := SimpleInvariantSolver(recursiveInvariants, expected, logf)
|
solution, err := SimpleInvariantSolver(ctx, recursiveInvariants, expected, logf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logf("%s: recursive solution failed: %+v", Name, err)
|
logf("%s: recursive solution failed: %+v", Name, err)
|
||||||
continue // no solution found here...
|
continue // no solution found here...
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
package unification
|
package unification
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -265,7 +266,7 @@ func TestSimpleSolver1(t *testing.T) {
|
|||||||
|
|
||||||
solver := SimpleInvariantSolverLogger(logf) // generates a solver with built-in logging
|
solver := SimpleInvariantSolverLogger(logf) // generates a solver with built-in logging
|
||||||
|
|
||||||
solution, err := solver(invariants, expected)
|
solution, err := solver(context.TODO(), invariants, expected)
|
||||||
t.Logf("test #%d: solver completed with: %+v", index, err)
|
t.Logf("test #%d: solver completed with: %+v", index, err)
|
||||||
|
|
||||||
if !fail && err != nil {
|
if !fail && err != nil {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
package unification
|
package unification
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -45,7 +46,8 @@ type Unifier struct {
|
|||||||
AST interfaces.Stmt
|
AST interfaces.Stmt
|
||||||
|
|
||||||
// Solver is the solver algorithm implementation to use.
|
// Solver is the solver algorithm implementation to use.
|
||||||
Solver func([]interfaces.Invariant, []interfaces.Expr) (*InvariantSolution, error)
|
// XXX: Solver should be a solver interface, not a function signature.
|
||||||
|
Solver func(context.Context, []interfaces.Invariant, []interfaces.Expr) (*InvariantSolution, error)
|
||||||
|
|
||||||
Debug bool
|
Debug bool
|
||||||
Logf func(format string, v ...interface{})
|
Logf func(format string, v ...interface{})
|
||||||
@@ -63,7 +65,7 @@ type Unifier struct {
|
|||||||
// type. This function and logic was invented after the author could not find
|
// type. This function and logic was invented after the author could not find
|
||||||
// any proper literature or examples describing a well-known implementation of
|
// any proper literature or examples describing a well-known implementation of
|
||||||
// this process. Improvements and polite recommendations are welcome.
|
// this process. Improvements and polite recommendations are welcome.
|
||||||
func (obj *Unifier) Unify() error {
|
func (obj *Unifier) Unify(ctx context.Context) error {
|
||||||
if obj.AST == nil {
|
if obj.AST == nil {
|
||||||
return fmt.Errorf("the AST is nil")
|
return fmt.Errorf("the AST is nil")
|
||||||
}
|
}
|
||||||
@@ -96,7 +98,7 @@ func (obj *Unifier) Unify() error {
|
|||||||
exprMap := ExprListToExprMap(exprs) // makes searching faster
|
exprMap := ExprListToExprMap(exprs) // makes searching faster
|
||||||
exprList := ExprMapToExprList(exprMap) // makes it unique (no duplicates)
|
exprList := ExprMapToExprList(exprMap) // makes it unique (no duplicates)
|
||||||
|
|
||||||
solved, err := obj.Solver(invariants, exprList)
|
solved, err := obj.Solver(ctx, invariants, exprList)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
package lang // XXX: move this to the unification package
|
package lang // XXX: move this to the unification package
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -856,7 +857,7 @@ func TestUnification1(t *testing.T) {
|
|||||||
Debug: testing.Verbose(),
|
Debug: testing.Verbose(),
|
||||||
Logf: logf,
|
Logf: logf,
|
||||||
}
|
}
|
||||||
err = unifier.Unify()
|
err = unifier.Unify(context.TODO())
|
||||||
|
|
||||||
// TODO: print out the AST's so that we can see the types
|
// TODO: print out the AST's so that we can see the types
|
||||||
t.Logf("\n\ntest #%d: AST (after): %+v\n", index, xast)
|
t.Logf("\n\ntest #%d: AST (after): %+v\n", index, xast)
|
||||||
|
|||||||
Reference in New Issue
Block a user