lang: Add modern type unification implementation
This adds a modern type unification algorithm, which drastically improves performance, particularly for bigger programs. This required a change to the AST to add TypeCheck methods (for Stmt) and Infer/Check methods (for Expr). This also changed how the functions express their invariants, and as a result this was changed as well. This greatly improves the way we express these invariants, and as a result it makes adding new polymorphic functions significantly easier. This also makes error output for the user a lot better in pretty much all scenarios. The one downside of this patch is that a good chunk of it is merged in this giant single commit since it was hard to do it step-wise. That's not the end of the world. This couldn't be done without the guidance of Sam who helped me in explaining, debugging, and writing all the sneaky algorithmic parts and much more. Thanks again Sam! Co-authored-by: Samuel Gélineau <gelisam@gmail.com>
This commit is contained in:
@@ -39,7 +39,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/purpleidea/mgmt/lang/ast"
|
||||
"github.com/purpleidea/mgmt/lang/funcs"
|
||||
"github.com/purpleidea/mgmt/lang/funcs/operators"
|
||||
"github.com/purpleidea/mgmt/lang/interfaces"
|
||||
"github.com/purpleidea/mgmt/lang/types"
|
||||
langUtil "github.com/purpleidea/mgmt/lang/util"
|
||||
@@ -595,7 +595,7 @@ func TestLexParse0(t *testing.T) {
|
||||
&ast.StmtResField{
|
||||
Field: "int64ptr",
|
||||
Value: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
@@ -636,13 +636,13 @@ func TestLexParse0(t *testing.T) {
|
||||
&ast.StmtResField{
|
||||
Field: "float32",
|
||||
Value: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
},
|
||||
&ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
@@ -688,7 +688,7 @@ func TestLexParse0(t *testing.T) {
|
||||
&ast.StmtResField{
|
||||
Field: "int64ptr",
|
||||
Value: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
@@ -697,7 +697,7 @@ func TestLexParse0(t *testing.T) {
|
||||
V: 4,
|
||||
},
|
||||
&ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "*",
|
||||
@@ -740,13 +740,13 @@ func TestLexParse0(t *testing.T) {
|
||||
&ast.StmtResField{
|
||||
Field: "int64ptr",
|
||||
Value: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
},
|
||||
&ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "*",
|
||||
@@ -792,7 +792,7 @@ func TestLexParse0(t *testing.T) {
|
||||
&ast.StmtResField{
|
||||
Field: "int64ptr",
|
||||
Value: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "*",
|
||||
@@ -801,7 +801,7 @@ func TestLexParse0(t *testing.T) {
|
||||
V: 3,
|
||||
},
|
||||
&ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
@@ -844,13 +844,13 @@ func TestLexParse0(t *testing.T) {
|
||||
&ast.StmtResField{
|
||||
Field: "boolptr",
|
||||
Value: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: ">",
|
||||
},
|
||||
&ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
@@ -896,7 +896,7 @@ func TestLexParse0(t *testing.T) {
|
||||
&ast.StmtResField{
|
||||
Field: "boolptr",
|
||||
Value: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: ">",
|
||||
@@ -905,7 +905,7 @@ func TestLexParse0(t *testing.T) {
|
||||
V: 3,
|
||||
},
|
||||
&ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
@@ -948,13 +948,13 @@ func TestLexParse0(t *testing.T) {
|
||||
&ast.StmtResField{
|
||||
Field: "boolptr",
|
||||
Value: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: ">",
|
||||
},
|
||||
&ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "not",
|
||||
@@ -997,13 +997,13 @@ func TestLexParse0(t *testing.T) {
|
||||
&ast.StmtResField{
|
||||
Field: "boolptr",
|
||||
Value: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "and",
|
||||
},
|
||||
&ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "<",
|
||||
@@ -1766,7 +1766,7 @@ func TestLexParse0(t *testing.T) {
|
||||
Args: []*interfaces.Arg{},
|
||||
Return: types.TypeInt,
|
||||
Body: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
@@ -1781,7 +1781,8 @@ func TestLexParse0(t *testing.T) {
|
||||
},
|
||||
}
|
||||
// sometimes, the type can get set by the parser when it's known
|
||||
if err := fn.SetType(types.NewType("func() int")); err != nil {
|
||||
typ := types.NewType("func() int")
|
||||
if err := fn.SetType(typ); err != nil {
|
||||
t.Fatal("could not build type")
|
||||
}
|
||||
exp := &ast.StmtProg{
|
||||
@@ -1789,6 +1790,7 @@ func TestLexParse0(t *testing.T) {
|
||||
&ast.StmtFunc{
|
||||
Name: "f2",
|
||||
Func: fn,
|
||||
Type: typ,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1817,7 +1819,7 @@ func TestLexParse0(t *testing.T) {
|
||||
},
|
||||
Return: types.TypeInt,
|
||||
Body: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
@@ -1864,7 +1866,7 @@ func TestLexParse0(t *testing.T) {
|
||||
},
|
||||
Return: types.TypeStr,
|
||||
Body: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
@@ -1878,7 +1880,8 @@ func TestLexParse0(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := fn.SetType(types.NewType("func(x str) str")); err != nil {
|
||||
typ := types.NewType("func(x str) str")
|
||||
if err := fn.SetType(typ); err != nil {
|
||||
t.Fatal("could not build type")
|
||||
}
|
||||
exp := &ast.StmtProg{
|
||||
@@ -1886,6 +1889,7 @@ func TestLexParse0(t *testing.T) {
|
||||
&ast.StmtFunc{
|
||||
Name: "f4",
|
||||
Func: fn,
|
||||
Type: typ,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1938,7 +1942,7 @@ func TestLexParse0(t *testing.T) {
|
||||
},
|
||||
Return: types.TypeStr,
|
||||
Body: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
@@ -1985,7 +1989,7 @@ func TestLexParse0(t *testing.T) {
|
||||
},
|
||||
Return: types.TypeStr,
|
||||
Body: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "+",
|
||||
@@ -2095,7 +2099,7 @@ func TestLexParse0(t *testing.T) {
|
||||
},
|
||||
//Return: types.TypeInt,
|
||||
Body: &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{
|
||||
V: "*",
|
||||
|
||||
@@ -36,6 +36,7 @@ import (
|
||||
|
||||
"github.com/purpleidea/mgmt/lang/ast"
|
||||
"github.com/purpleidea/mgmt/lang/funcs"
|
||||
"github.com/purpleidea/mgmt/lang/funcs/operators"
|
||||
"github.com/purpleidea/mgmt/lang/interfaces"
|
||||
"github.com/purpleidea/mgmt/lang/types"
|
||||
"github.com/purpleidea/mgmt/util"
|
||||
@@ -251,13 +252,15 @@ stmt:
|
||||
m[a.Name] = a.Type
|
||||
ord = append(ord, a.Name)
|
||||
}
|
||||
var typ *types.Type
|
||||
if isFullyTyped {
|
||||
typ := &types.Type{
|
||||
typ = &types.Type{
|
||||
Kind: types.KindFunc,
|
||||
Map: m,
|
||||
Ord: ord,
|
||||
Out: $6.typ,
|
||||
}
|
||||
// XXX: We might still need to do this for now...
|
||||
if err := fn.SetType(typ); err != nil {
|
||||
// this will ultimately cause a parser error to occur...
|
||||
yylex.Error(fmt.Sprintf("%s: %+v", ErrParseSetType, err))
|
||||
@@ -266,6 +269,7 @@ stmt:
|
||||
$$.stmt = &ast.StmtFunc{
|
||||
Name: $2.str,
|
||||
Func: fn,
|
||||
Type: typ, // sam says add the type here instead...
|
||||
}
|
||||
}
|
||||
// `class name { <prog> }`
|
||||
@@ -566,7 +570,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $2.str, // for PLUS this is a `+` character
|
||||
@@ -580,7 +584,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $2.str,
|
||||
@@ -594,7 +598,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $2.str,
|
||||
@@ -608,7 +612,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $2.str,
|
||||
@@ -622,7 +626,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $2.str,
|
||||
@@ -636,7 +640,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $2.str,
|
||||
@@ -650,7 +654,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $2.str,
|
||||
@@ -664,7 +668,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $2.str,
|
||||
@@ -678,7 +682,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $2.str,
|
||||
@@ -692,7 +696,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $2.str,
|
||||
@@ -706,7 +710,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $2.str,
|
||||
@@ -720,7 +724,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $2.str,
|
||||
@@ -734,7 +738,7 @@ call:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
$$.expr = &ast.ExprCall{
|
||||
Name: funcs.OperatorFuncName,
|
||||
Name: operators.OperatorFuncName,
|
||||
Args: []interfaces.Expr{
|
||||
&ast.ExprStr{ // operator first
|
||||
V: $1.str,
|
||||
@@ -947,6 +951,7 @@ bind:
|
||||
{
|
||||
posLast(yylex, yyDollar) // our pos
|
||||
var expr interfaces.Expr = $4.expr
|
||||
// XXX: We still need to do this for now it seems...
|
||||
if err := expr.SetType($2.typ); err != nil {
|
||||
// this will ultimately cause a parser error to occur...
|
||||
yylex.Error(fmt.Sprintf("%s: %+v", ErrParseSetType, err))
|
||||
@@ -954,6 +959,7 @@ bind:
|
||||
$$.stmt = &ast.StmtBind{
|
||||
Ident: $1.str,
|
||||
Value: expr,
|
||||
Type: $2.typ, // sam says add the type here instead...
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
Reference in New Issue
Block a user