From efcc4291a31102885b41af8562861aa3dc2eff88 Mon Sep 17 00:00:00 2001 From: James Shubin Date: Tue, 11 May 2021 03:22:27 -0400 Subject: [PATCH] lang: funcs: core: Add Unify method for template function This is an implementation of the Unify approach for the template function. --- lang/funcs/core/template_func.go | 134 +++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) diff --git a/lang/funcs/core/template_func.go b/lang/funcs/core/template_func.go index 4e26fa12..550fac7e 100644 --- a/lang/funcs/core/template_func.go +++ b/lang/funcs/core/template_func.go @@ -82,6 +82,140 @@ func (obj *TemplateFunc) ArgGen(index int) (string, error) { return seq[index], nil } +// Unify returns the list of invariants that this func produces. +func (obj *TemplateFunc) Unify(expr interfaces.Expr) ([]interfaces.Invariant, error) { + var invariants []interfaces.Invariant + var invar interfaces.Invariant + + // func(format string, arg variant) string + + formatName, err := obj.ArgGen(0) + if err != nil { + return nil, err + } + + dummyFormat := &interfaces.ExprAny{} // corresponds to the format type + dummyOut := &interfaces.ExprAny{} // corresponds to the out string + + // format arg type of string + invar = &interfaces.EqualsInvariant{ + Expr: dummyFormat, + Type: types.TypeStr, + } + invariants = append(invariants, invar) + + // return type of string + invar = &interfaces.EqualsInvariant{ + Expr: dummyOut, + Type: types.TypeStr, + } + 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! + // cfavInvar.Args are the args that ExprCall uses! + if len(cfavInvar.Args) == 0 { + return nil, fmt.Errorf("unable to build function with no args") + } + if l := len(cfavInvar.Args); l > 2 { + return nil, fmt.Errorf("unable to build function with %d args", l) + } + // we can either have one arg or two + + var invariants []interfaces.Invariant + var invar interfaces.Invariant + + // first arg must be a string + invar = &interfaces.EqualsInvariant{ + Expr: cfavInvar.Args[0], + Type: types.TypeStr, + } + invariants = append(invariants, invar) + + // TODO: if the template is known statically, we could + // parse it to check for variable safety if we wanted! + //value, err := cfavInvar.Args[0].Value() // is it known? + //if err != nil { + //} + + // full function + mapped := make(map[string]interfaces.Expr) + ordered := []string{formatName} + mapped[formatName] = dummyFormat + + if len(cfavInvar.Args) == 2 { // two args is more complex + argName, err := obj.ArgGen(1) // 1st arg after 0 + if err != nil { + return nil, err + } + if argName == argNameTemplate { + return nil, fmt.Errorf("could not build function with %d args", 1) + } + + dummyArg := &interfaces.ExprAny{} + + // speculate about the type? (maybe redundant) + if typ, err := cfavInvar.Args[1].Type(); err == nil { + invar := &interfaces.EqualsInvariant{ + Expr: dummyArg, + Type: typ, + } + invariants = append(invariants, invar) + } + + // expression must match type of the input arg + invar := &interfaces.EqualityInvariant{ + Expr1: dummyArg, + Expr2: cfavInvar.Args[1], + } + invariants = append(invariants, invar) + + mapped[argName] = dummyArg + ordered = append(ordered, argName) + } + + invar = &interfaces.EqualityWrapFuncInvariant{ + Expr1: expr, // maps directly to us! + Expr2Map: mapped, + Expr2Ord: ordered, + Expr2Out: dummyOut, + } + invariants = append(invariants, invar) + + // 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 possible type signatures for this template. In this // case, since the second argument can be an infinite number of values, it // instead returns either the final precise type (if it can be gleamed from the