lang: funcs: Add Unify method for maplookup function
This also adds a few tests.
This commit is contained in:
@@ -61,6 +61,304 @@ func (obj *MapLookupPolyFunc) ArgGen(index int) (string, error) {
|
|||||||
return seq[index], nil
|
return seq[index], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unify returns the list of invariants that this func produces.
|
||||||
|
func (obj *MapLookupPolyFunc) Unify(expr interfaces.Expr) ([]interfaces.Invariant, error) {
|
||||||
|
var invariants []interfaces.Invariant
|
||||||
|
var invar interfaces.Invariant
|
||||||
|
|
||||||
|
// func(map T1, key T2, default T3) T3
|
||||||
|
// (map: T2 => T3)
|
||||||
|
|
||||||
|
mapName, err := obj.ArgGen(0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
keyName, err := obj.ArgGen(1)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultName, err := obj.ArgGen(2)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dummyMap := &interfaces.ExprAny{} // corresponds to the map type
|
||||||
|
dummyKey := &interfaces.ExprAny{} // corresponds to the key type
|
||||||
|
dummyDefault := &interfaces.ExprAny{} // corresponds to the default type
|
||||||
|
dummyOut := &interfaces.ExprAny{} // corresponds to the out string
|
||||||
|
|
||||||
|
// default type and out are the same
|
||||||
|
invar = &interfaces.EqualityInvariant{
|
||||||
|
Expr1: dummyDefault,
|
||||||
|
Expr2: dummyOut,
|
||||||
|
}
|
||||||
|
invariants = append(invariants, invar)
|
||||||
|
|
||||||
|
// relationship between T1, T2 and T3
|
||||||
|
invar = &interfaces.EqualityWrapMapInvariant{
|
||||||
|
Expr1: dummyMap,
|
||||||
|
Expr2Key: dummyKey,
|
||||||
|
Expr2Val: dummyDefault,
|
||||||
|
}
|
||||||
|
invariants = append(invariants, invar)
|
||||||
|
|
||||||
|
// full function
|
||||||
|
mapped := make(map[string]interfaces.Expr)
|
||||||
|
ordered := []string{mapName, keyName, defaultName}
|
||||||
|
mapped[mapName] = dummyMap
|
||||||
|
mapped[keyName] = dummyKey
|
||||||
|
mapped[defaultName] = dummyDefault
|
||||||
|
|
||||||
|
invar = &interfaces.EqualityWrapFuncInvariant{
|
||||||
|
Expr1: expr, // maps directly to us!
|
||||||
|
Expr2Map: mapped,
|
||||||
|
Expr2Ord: ordered,
|
||||||
|
Expr2Out: dummyOut,
|
||||||
|
}
|
||||||
|
invariants = append(invariants, invar)
|
||||||
|
|
||||||
|
// generator function
|
||||||
|
fn := func(fnInvariants []interfaces.Invariant, solved map[interfaces.Expr]*types.Type) ([]interfaces.Invariant, error) {
|
||||||
|
for _, invariant := range fnInvariants {
|
||||||
|
// search for this special type of invariant
|
||||||
|
cfavInvar, ok := invariant.(*interfaces.CallFuncArgsValueInvariant)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// did we find the mapping from us to ExprCall ?
|
||||||
|
if cfavInvar.Func != expr {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// cfavInvar.Expr is the ExprCall! (the return pointer)
|
||||||
|
// cfavInvar.Args are the args that ExprCall uses!
|
||||||
|
if l := len(cfavInvar.Args); l != 3 {
|
||||||
|
return nil, fmt.Errorf("unable to build function with %d args", l)
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the relationship to the returned value
|
||||||
|
invar = &interfaces.EqualityInvariant{
|
||||||
|
Expr1: cfavInvar.Expr,
|
||||||
|
Expr2: dummyOut,
|
||||||
|
}
|
||||||
|
invariants = append(invariants, invar)
|
||||||
|
|
||||||
|
// add the relationships to the called args
|
||||||
|
invar = &interfaces.EqualityInvariant{
|
||||||
|
Expr1: cfavInvar.Args[0],
|
||||||
|
Expr2: dummyMap,
|
||||||
|
}
|
||||||
|
invariants = append(invariants, invar)
|
||||||
|
|
||||||
|
invar = &interfaces.EqualityInvariant{
|
||||||
|
Expr1: cfavInvar.Args[1],
|
||||||
|
Expr2: dummyKey,
|
||||||
|
}
|
||||||
|
invariants = append(invariants, invar)
|
||||||
|
|
||||||
|
invar = &interfaces.EqualityInvariant{
|
||||||
|
Expr1: cfavInvar.Args[2],
|
||||||
|
Expr2: dummyDefault,
|
||||||
|
}
|
||||||
|
invariants = append(invariants, invar)
|
||||||
|
|
||||||
|
var invariants []interfaces.Invariant
|
||||||
|
var invar interfaces.Invariant
|
||||||
|
|
||||||
|
// If we figure out all of these three types, we'll
|
||||||
|
// know the full type...
|
||||||
|
var t1 *types.Type // map type
|
||||||
|
var t2 *types.Type // map key type
|
||||||
|
var t3 *types.Type // map val type
|
||||||
|
|
||||||
|
// validateArg0 checks: map T1
|
||||||
|
validateArg0 := func(typ *types.Type) error {
|
||||||
|
if typ == nil { // unknown so far
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// we happen to have a map!
|
||||||
|
if k := typ.Kind; k != types.KindMap {
|
||||||
|
return fmt.Errorf("unable to build function with 0th arg of kind: %s", k)
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ.Key == nil || typ.Val == nil {
|
||||||
|
// programming error
|
||||||
|
return fmt.Errorf("map is missing type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := typ.Cmp(t1); t1 != nil && err != nil {
|
||||||
|
return errwrap.Wrapf(err, "input type was inconsistent")
|
||||||
|
}
|
||||||
|
if err := typ.Key.Cmp(t2); t2 != nil && err != nil {
|
||||||
|
return errwrap.Wrapf(err, "input key type was inconsistent")
|
||||||
|
}
|
||||||
|
if err := typ.Val.Cmp(t3); t3 != nil && err != nil {
|
||||||
|
return errwrap.Wrapf(err, "input val type was inconsistent")
|
||||||
|
}
|
||||||
|
|
||||||
|
// learn!
|
||||||
|
t1 = typ
|
||||||
|
t2 = typ.Key
|
||||||
|
t3 = typ.Val
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateArg1 checks: map key T2
|
||||||
|
validateArg1 := func(typ *types.Type) error {
|
||||||
|
if typ == nil { // unknown so far
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := typ.Cmp(t2); t2 != nil && err != nil {
|
||||||
|
return errwrap.Wrapf(err, "input key type was inconsistent")
|
||||||
|
}
|
||||||
|
if t1 != nil {
|
||||||
|
if err := typ.Cmp(t1.Key); err != nil {
|
||||||
|
return errwrap.Wrapf(err, "input key type was inconsistent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t3 != nil {
|
||||||
|
t := &types.Type{ // build t1
|
||||||
|
Kind: types.KindMap,
|
||||||
|
Key: typ, // t2
|
||||||
|
Val: t3,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.Cmp(t1); t1 != nil && err != nil {
|
||||||
|
return errwrap.Wrapf(err, "input type was inconsistent")
|
||||||
|
}
|
||||||
|
t1 = t // learn!
|
||||||
|
}
|
||||||
|
|
||||||
|
// learn!
|
||||||
|
t2 = typ
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateArg2 checks: map val T3
|
||||||
|
validateArg2 := func(typ *types.Type) error {
|
||||||
|
if typ == nil { // unknown so far
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := typ.Cmp(t3); t3 != nil && err != nil {
|
||||||
|
return errwrap.Wrapf(err, "input val type was inconsistent")
|
||||||
|
}
|
||||||
|
if t1 != nil {
|
||||||
|
if err := typ.Cmp(t1.Val); err != nil {
|
||||||
|
return errwrap.Wrapf(err, "input val type was inconsistent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t2 != nil {
|
||||||
|
t := &types.Type{ // build t1
|
||||||
|
Kind: types.KindMap,
|
||||||
|
Key: t2,
|
||||||
|
Val: typ, // t3
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := t.Cmp(t1); t1 != nil && err != nil {
|
||||||
|
return errwrap.Wrapf(err, "input type was inconsistent")
|
||||||
|
}
|
||||||
|
t1 = t // learn!
|
||||||
|
}
|
||||||
|
|
||||||
|
// learn!
|
||||||
|
t3 = typ
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ, err := cfavInvar.Args[0].Type(); err == nil { // is it known?
|
||||||
|
// this sets t1 and t2 and t3 on success if it learned
|
||||||
|
if err := validateArg0(typ); err != nil {
|
||||||
|
return nil, errwrap.Wrapf(err, "first map arg type is inconsistent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if typ, exists := solved[cfavInvar.Args[0]]; exists { // alternate way to lookup type
|
||||||
|
// this sets t1 and t2 and t3 on success if it learned
|
||||||
|
if err := validateArg0(typ); err != nil {
|
||||||
|
return nil, errwrap.Wrapf(err, "first map arg type is inconsistent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ, err := cfavInvar.Args[1].Type(); err == nil { // is it known?
|
||||||
|
// this sets t2 (and sometimes t1) on success if it learned
|
||||||
|
if err := validateArg1(typ); err != nil {
|
||||||
|
return nil, errwrap.Wrapf(err, "second key arg type is inconsistent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if typ, exists := solved[cfavInvar.Args[1]]; exists { // alternate way to lookup type
|
||||||
|
// this sets t2 (and sometimes t1) on success if it learned
|
||||||
|
if err := validateArg1(typ); err != nil {
|
||||||
|
return nil, errwrap.Wrapf(err, "second key arg type is inconsistent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if typ, err := cfavInvar.Args[2].Type(); err == nil { // is it known?
|
||||||
|
// this sets t3 (and sometimes t1) on success if it learned
|
||||||
|
if err := validateArg2(typ); err != nil {
|
||||||
|
return nil, errwrap.Wrapf(err, "third default arg type is inconsistent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if typ, exists := solved[cfavInvar.Args[2]]; exists { // alternate way to lookup type
|
||||||
|
// this sets t3 (and sometimes t1) on success if it learned
|
||||||
|
if err := validateArg2(typ); err != nil {
|
||||||
|
return nil, errwrap.Wrapf(err, "third default arg type is inconsistent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: if the types aren't know statically?
|
||||||
|
|
||||||
|
if t1 != nil {
|
||||||
|
invar := &interfaces.EqualsInvariant{
|
||||||
|
Expr: dummyMap,
|
||||||
|
Type: t1,
|
||||||
|
}
|
||||||
|
invariants = append(invariants, invar)
|
||||||
|
}
|
||||||
|
if t2 != nil {
|
||||||
|
invar := &interfaces.EqualsInvariant{
|
||||||
|
Expr: dummyKey,
|
||||||
|
Type: t2,
|
||||||
|
}
|
||||||
|
invariants = append(invariants, invar)
|
||||||
|
}
|
||||||
|
if t3 != nil {
|
||||||
|
invar := &interfaces.EqualsInvariant{
|
||||||
|
Expr: dummyDefault,
|
||||||
|
Type: t3,
|
||||||
|
}
|
||||||
|
invariants = append(invariants, invar)
|
||||||
|
}
|
||||||
|
|
||||||
|
// XXX: if t{1..3} are missing, we could also return a
|
||||||
|
// new generator for later if we learn new information,
|
||||||
|
// but we'd have to be careful to not do the infinitely
|
||||||
|
|
||||||
|
// TODO: do we return this relationship with ExprCall?
|
||||||
|
invar = &interfaces.EqualityWrapCallInvariant{
|
||||||
|
// TODO: should Expr1 and Expr2 be reversed???
|
||||||
|
Expr1: cfavInvar.Expr,
|
||||||
|
//Expr2Func: cfavInvar.Func, // same as below
|
||||||
|
Expr2Func: expr,
|
||||||
|
}
|
||||||
|
invariants = append(invariants, invar)
|
||||||
|
|
||||||
|
// TODO: are there any other invariants we should build?
|
||||||
|
return invariants, nil // generator return
|
||||||
|
}
|
||||||
|
// We couldn't tell the solver anything it didn't already know!
|
||||||
|
return nil, fmt.Errorf("couldn't generate new invariants")
|
||||||
|
}
|
||||||
|
invar = &interfaces.GeneratorInvariant{
|
||||||
|
Func: fn,
|
||||||
|
}
|
||||||
|
invariants = append(invariants, invar)
|
||||||
|
|
||||||
|
return invariants, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Polymorphisms returns the list of possible function signatures available for
|
// Polymorphisms returns the list of possible function signatures available for
|
||||||
// this static polymorphic function. It relies on type and value hints to limit
|
// this static polymorphic function. It relies on type and value hints to limit
|
||||||
// the number of returned possibilities.
|
// the number of returned possibilities.
|
||||||
|
|||||||
4
lang/interpret_test/TestAstFunc2/maplookup0.output
Normal file
4
lang/interpret_test/TestAstFunc2/maplookup0.output
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
Vertex: test[hello1]
|
||||||
|
Vertex: test[hello3]
|
||||||
|
Vertex: test[world2]
|
||||||
|
Vertex: test[world4]
|
||||||
11
lang/interpret_test/TestAstFunc2/maplookup0/main.mcl
Normal file
11
lang/interpret_test/TestAstFunc2/maplookup0/main.mcl
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
$map1 map{int: str} = {42 => "hello1",}
|
||||||
|
test maplookup($map1, 42, "not found") {}
|
||||||
|
|
||||||
|
$map2 map{int: str} = {42 => "hello2",}
|
||||||
|
test maplookup($map2, 13, "world2") {}
|
||||||
|
|
||||||
|
$map3 = {42 => "hello3",}
|
||||||
|
test maplookup($map3, 42, "not found") {}
|
||||||
|
|
||||||
|
$map4 = {42 => "hello4",}
|
||||||
|
test maplookup($map4, 13, "world4") {}
|
||||||
Reference in New Issue
Block a user