{-# LANGUAGE BangPatterns #-} {-# LANGUAGE OverloadedStrings #-} module G2.Liquid.Inference.Sygus.RefSynth ( -- refSynth -- , grammar -- , intRuleList -- , boolRuleList -- , intSort -- , boolSort -- , termToLHExpr -- , runCVC4 -- , runCVC4Stream ) where -- import G2.Language.Expr -- import qualified G2.Language.ExprEnv as E -- import G2.Language.Naming -- import G2.Language.Support -- import G2.Language.Syntax as G2 -- import G2.Language.TypeClasses -- import G2.Language.Typing -- import G2.Liquid.Conversion -- import G2.Liquid.Helpers -- import G2.Liquid.Interface -- import G2.Liquid.Types -- import G2.Liquid.Inference.Config -- import G2.Liquid.Inference.FuncConstraint -- import G2.Liquid.Inference.G2Calls -- import G2.Liquid.Inference.GeneratedSpecs -- import G2.Liquid.Inference.PolyRef -- import G2.Liquid.Inference.Sygus.SimplifySygus -- import G2.Liquid.Inference.Sygus.UnsatCoreElim -- import Sygus.LexSygus -- import Sygus.ParseSygus -- import Sygus.Print -- import Sygus.Syntax as Sy -- import Language.Haskell.Liquid.Types as LH -- import Language.Haskell.Liquid.Types.RefType -- import Language.Fixpoint.Types.Constraints -- import Language.Fixpoint.Types.Refinements as LH -- import qualified Language.Fixpoint.Types as LH -- import qualified Language.Fixpoint.Types as LHF -- import Control.Exception -- import Control.Monad.IO.Class -- import qualified Control.Monad.State as S -- import Data.Coerce -- import Data.List -- import Data.Hashable -- import qualified Data.HashMap.Lazy as HM -- import qualified Data.HashSet as HS -- import qualified Data.Map as M -- import Data.Maybe -- import Data.Monoid -- import Data.Ratio -- import qualified Data.Text as T -- import Data.Tuple -- import Data.Tuple.Extra -- import System.Directory -- import System.IO -- import System.IO.Temp -- import qualified System.Process as P -- import TyCon -- import qualified Var as V -- import Debug.Trace -- refSynth :: (InfConfigM m, MonadIO m) => [GhcInfo] -> LiquidReadyState -> MeasureExs -- -> FuncConstraints -> Name -> m (Maybe ([PolyBound LH.Expr], [Qualifier])) -- refSynth ghci lrs meas_ex fc n@(Name n' _ _ _) = undefined -- do {- let eenv = expr_env . state $ lr_state lrs tc = type_classes . state $ lr_state lrs fc_of_n = lookupFC n fc fspec = case genSpec ghci n of Just spec' -> spec' _ -> error $ "synthesize: No spec found for " ++ show n e = case E.occLookup (nameOcc n) (nameModule n) eenv of Just e' -> e' Nothing -> error $ "synthesize: No expr found" meas = lrsMeasures ghci lrs liftIO . print $ "Synthesize spec for " ++ show n let tcemb = foldr (<>) mempty $ map (gsTcEmbeds . spec) ghci refSynth' fspec e tc meas meas_ex fc_of_n (measureSymbols ghci) tcemb refSynth' :: (InfConfigM m, MonadIO m) => SpecType -> G2.Expr -> TypeClasses -> Measures -> MeasureExs -> [FuncConstraint] -> MeasureSymbols -> LH.TCEmb TyCon -> m (Maybe ([PolyBound LH.Expr], [Qualifier])) refSynth' spc e tc meas meas_ex fc meas_sym tycons = do infconfig <- infConfigM liftIO $ do putStrLn "refSynth" let (call, f_num, arg_pb, ret_pb) = sygusCall e tc meas meas_ex fc s_call = nub . simplifyNegatedAnds . simplifyImpliesExistentials . elimNegatedExistential . simplifyImpliesLHS . splitAnds . elimRedundantAnds $ call (es_dt, s_call2) = elimSimpleDTs s_call no_unsat_call = unsatCoreElim s_call2 let sygus = printSygus no_unsat_call putStrLn . T.unpack $ sygus res <- runCVC4 infconfig (T.unpack sygus) -- res <- runCVC4StreamSolutions infconfig f_num (T.unpack sygus) case res of Left _ -> do putStrLn "Timeout" return Nothing -- error "refSynth: Bad call to CVC4" Right smt_st -> do let smt_st' = restoreSimpleDTs es_dt smt_st putStrLn . T.unpack $ printSygus smt_st' let lh_st = refToLHExpr spc arg_pb ret_pb smt_st' meas_sym lh_quals = refToQualifiers spc arg_pb ret_pb smt_st' meas_sym tycons print lh_st print lh_quals return $ Just (lh_st, lh_quals) ------------------------------- -- Constructing Sygus Formula ------------------------------- sygusCall :: G2.Expr -> TypeClasses -> Measures -> MeasureExs -> [FuncConstraint] -> ([Cmd], Int, [RefNamePolyBound], RefNamePolyBound) sygusCall e tc meas meas_ex fcs@(_:_) = let -- Figure out what measures we need to/can consider (arg_ty_c, ret_ty_c, ex_ty_c) = generateRelTypes tc e func_ty_c = arg_ty_c ++ [ret_ty_c] all_ty_c = func_ty_c ++ ex_ty_c rel_arg_ty_c = filter relTy arg_ty_c rel_fcs = map (relArgs tc arg_ty_c) fcs sorts = typesToSort meas meas_ex all_ty_c declare_dts = sortsToDeclareDTs sorts (grams, cons, arg_pb, ret_pb) = generateGrammarsAndConstraints sorts meas_ex rel_arg_ty_c ret_ty_c rel_fcs call = [ SmtCmd (SetLogic "ALL")] ++ declare_dts ++ [ safeModDecl , clampIntDecl clampUpper , clampDoubleDecl clampUpper ] ++ grams ++ cons ++ [ CheckSynth ] in (call, length grams, arg_pb, ret_pb) sygusCall _ _ _ _ _ = error "sygusCall: empty list" applicableMeasures :: Measures -> Type -> [Name] applicableMeasures meas t = E.keys $ E.filter (applicableMeasure t) meas applicableMeasure :: Type -> G2.Expr -> Bool applicableMeasure t e = let te = filter notLH . argumentTypes . PresType . inTyForAlls $ typeOf e in case te of [te'] -> PresType t .:: te' _ -> False where notLH ty | TyCon (Name n _ _ _) _ <- tyAppCenter ty = n /= "lh" | otherwise = False generateGrammarsAndConstraints :: TypesToSorts -> MeasureExs -> [Type] -> Type -> [FuncConstraint] -> ([Cmd], [Cmd], [RefNamePolyBound], RefNamePolyBound) generateGrammarsAndConstraints sorts meas_ex arg_tys ret_ty fcs@(fc:_) = let ret_names = refinementNames "ret" (typeOf . returns $ constraint fc) ret_gram_cmds = generateGrammars extGrammar sorts ret_names meas_ex arg_tys ret_ty arg_names_grams = generateParamRefGrammars fc sorts meas_ex arg_tys (arg_names, arg_grams_cmds) = unzip arg_names_grams cons = generateConstraints sorts meas_ex arg_names ret_names arg_tys ret_ty fcs in (concat arg_grams_cmds ++ ret_gram_cmds, cons, arg_names, ret_names) refinementNames :: String -> G2.Type -> RefNamePolyBound refinementNames prefix t = let poly_bd = extractTypePolyBound t in mapPB (\i -> prefix ++ "_refinement_" ++ show i) $ uniqueIds poly_bd generateParamRefGrammars :: FuncConstraint -> TypesToSorts -> MeasureExs -> [Type] -> [(RefNamePolyBound, [Cmd])] generateParamRefGrammars fc sorts meas_ex arg_tys = map (\(i, as@((_, a):_)) -> let arg_tys = map fst $ init as ret_ty = fst $ last as arg_ref_names = refinementNames ("args_" ++ show i) ret_ty -- a arg_gram_cmds = generateGrammars regGrammar sorts arg_ref_names meas_ex arg_tys ret_ty in (arg_ref_names, arg_gram_cmds)) (zip [0..] . map (zip arg_tys) . filter (not . null) . inits . arguments $ constraint fc) generateGrammars :: GrammarGen -> TypesToSorts -> RefNamePolyBound -> MeasureExs -> [Type] -> Type -> [Cmd] generateGrammars g sorts ref_names meas_ex arg_tys ret_ty = let rt_bound = extractTypePolyBound ret_ty ns_rt = zipPB ref_names rt_bound in map (uncurry (generateSynthFun g sorts arg_tys)) . filter (relTy . snd) $ extractValues ns_rt generateSynthFun :: GrammarGen -> TypesToSorts -> [Type] -- ^ Argument types -> String -- ^ Name of function to synthesize -> Type -- ^ Return type -> Cmd generateSynthFun g sorts arg_tys n rt = let param_vars = generateParams sorts arg_tys ret_sort_var = SortedVar "r" (typeToSort sorts rt) sort_vars = param_vars ++ [ret_sort_var] gram = g param_vars ret_sort_var sorts in SynthFun n sort_vars boolSort (Just gram) generateParams :: TypesToSorts -> [Type] -> [SortedVar] generateParams sorts arg_tys = let varN = map (\i -> "x" ++ show i) ([0..] :: [Integer]) in map (uncurry SortedVar) . zip varN . map (typeToSort sorts) . filter (not . isLHDict) $ arg_tys where isLHDict e | (TyCon (Name n _ _ _) _):_ <- unTyApp e = n == "lh" | otherwise = False ------------------------------- -- define-fun ------------------------------- -- We define a function safe-mod, which forces the denominator of mod to be positive. safeModSymb :: Symbol safeModSymb = "safe-mod" safeModDecl :: Cmd safeModDecl = SmtCmd . DefineFun safeModSymb [SortedVar "x" intSort, SortedVar "y" intSort] intSort $ TermCall (ISymb "mod") [ TermIdent (ISymb "x") , TermCall (ISymb "+") [TermLit (LitNum 1), TermCall (ISymb "abs") [TermIdent (ISymb "y")]] ] -- We define a function clamp, which forces (Constant sort) to fall only in a fixed range clampIntSymb :: Symbol clampIntSymb = clampSymb "int" clampIntDecl :: Integer -> Cmd clampIntDecl = clampDecl clampIntSymb intSort clampDoubleSymb :: Symbol clampDoubleSymb = clampSymb "double" clampDoubleDecl :: Integer -> Cmd clampDoubleDecl = clampDecl clampDoubleSymb doubleSort clampSymb :: Symbol -> Symbol clampSymb = (++) "clamp-" clampDecl :: Symbol -> Sort -> Integer -> Cmd clampDecl fn srt mx = SmtCmd . DefineFun fn [SortedVar "x" srt] srt $ TermCall (ISymb "ite") [ TermCall (ISymb "<") [TermLit $ LitNum mx, TermIdent (ISymb "x")] , TermLit $ LitNum mx , TermCall (ISymb "ite") [ TermCall (ISymb "<") [TermIdent (ISymb "x"), TermLit $ LitNum 0] , TermLit $ LitNum 0 , TermIdent (ISymb "x") ] ] clampUpper :: Num a => a clampUpper = 5 ------------------------------- -- Grammar ------------------------------- type GrammarGen = [SortedVar] -> SortedVar -> TypesToSorts -> GrammarDef regGrammar :: GrammarGen regGrammar = grammar intRuleList doubleRuleList extGrammar :: GrammarGen extGrammar = grammar extIntRuleList extDoubleRuleList grammar :: [GTerm] -- Int Rules -> [GTerm] -- Double Rules -> GrammarGen grammar intRules doubleRules arg_sort_vars ret_sorted_var@(SortedVar _ (IdentSort (ISymb ret_srt_symb))) sorts = let sorted_vars = arg_sort_vars ++ [ret_sorted_var] rel_to_ret = case ret_srt_symb of "Bool" -> arg_sort_vars _ -> maybe [ret_sorted_var] meas_names $ lookupSortInfoBySort ret_srt_symb sorts sorts' = filterToSorts (map (\(SortedVar _ s) -> sortSymb s) sorted_vars) sorts gramNames = zip (map (\i -> "G" ++ show i) ([0..] :: [Integer])) (allSortNames sorts') grams = map (\(g, s_symb) -> (g, IdentSort . ISymb $ s_symb)) gramNames sortsToGN = HM.fromList $ map swap gramNames irl = GroupedRuleList "I" intSort (intRules ++ addSelectors sortsToGN intSort sorts') clamp_int = GroupedRuleList "IClamp" intSort [GBfTerm $ BfIdentifierBfs (ISymb clampIntSymb) [BfIdentifier (ISymb "IConst")]] const_int = GroupedRuleList "IConst" intSort [GConstant intSort] (bool_int, decl_int, grl_int) = adjustTypeUsage rel_to_ret intSort boolIntArgRuleList [SortedVar "I" intSort, SortedVar "IClamp" intSort, SortedVar "IConst" intSort] [ ("I", intRules, addSelectors sortsToGN intSort sorts') , ("IClamp", [GBfTerm $ BfIdentifierBfs (ISymb clampIntSymb) [BfIdentifier (ISymb "IConst")]], []) , ("IConst", [GConstant intSort], []) ] (bool_double, decl_double, grl_double) = adjustTypeUsage rel_to_ret doubleSort boolDoubleArgRuleList [SortedVar "D" doubleSort, SortedVar "DClamp" doubleSort, SortedVar "DConst" doubleSort] [ ("D", doubleRules, addSelectors sortsToGN doubleSort sorts') , ("DClamp", [GBfTerm $ BfIdentifierBfs (ISymb clampDoubleSymb) [BfIdentifier (ISymb "DConst")]], []) , ("DConst", [GConstant doubleSort], []) ] bool_rl = boolTrueRuleList ++ if not (null bool_int) || not (null bool_double) then boolOpRuleList else [] brl = GroupedRuleList "B" boolSort (bool_rl ++ bool_int ++ bool_double ++ addSelectors sortsToGN boolSort sorts') grm = GrammarDef ([ SortedVar "B" boolSort ] ++ decl_int ++ decl_double ++ map (uncurry SortedVar) grams) ([ brl ] ++ grl_int ++ grl_double ++ map (uncurry dtGroupRuleList) grams) in forceVarInGrammar ret_sorted_var arg_sort_vars grm where sortSymb (IdentSort (ISymb s)) = s sortSymb _ = error "grammar: sortSymb" -- | Including doubles/ints in the grammar increases CVC4s runtime, and is not -- needed if there are no variables of the given type, or selectors that return -- the given type. This function checks if any variables/selectors exists, -- and either returns the needed grammar elements if they do, or returns -- empty lists if they do not. adjustTypeUsage :: [SortedVar] -- ^ The function parameters -> Sort -- ^ The type under consideration -> [GTerm] -- ^ A set of terminals relating the given type to Bool -> [SortedVar] -- ^ Declarations for the terminals for the given type -> [(Symbol, [GTerm], [GTerm])] -- ^ The symbols, set(s) of default -- terminals, and sets of selector terminals -- for the given type -> ( [GTerm] -- ^ The set of terminals to add to add to Bool , [SortedVar] -- ^ Declarations for the terminals for the -- given type to add to the grammar , [GroupedRuleList] -- ^ The GRL(s) to add to the grammar ) adjustTypeUsage params srt bool_trms decls type_trms = let param_typs = filter (\(SortedVar _ s) -> s == srt) params sels = concatMap (\(_, _, sel) -> sel) type_trms type_grl = map (\(s, def, sel) -> GroupedRuleList s srt (def ++ sel)) type_trms in if param_typs /= [] -- || sels /= [] then (bool_trms, decls, type_grl) else ([], [], []) -- For some reason, CVC4 seems better at handling * when both sides are intBf, -- instead of one side being intConstBf extIntRuleList :: [GTerm] extIntRuleList = intRuleList ++ [ GBfTerm $ BfIdentifierBfs (ISymb "*") [intBf, intBf] , GBfTerm $ BfIdentifierBfs (ISymb safeModSymb) [intBf, intConstBf] ] intRuleList :: [GTerm] intRuleList = [ GVariable intSort , GBfTerm $ BfIdentifier (ISymb "IClamp") -- , GConstant intSort , GBfTerm $ BfIdentifierBfs (ISymb "+") [intBf, intBf] , GBfTerm $ BfIdentifierBfs (ISymb "-") [intBf, intBf] ] -- ++ [GBfTerm . BfLiteral . LitNum $ x | x <- [0..0]] extDoubleRuleList :: [GTerm] extDoubleRuleList = doubleRuleList ++ [GBfTerm $ BfIdentifierBfs (ISymb "*") [doubleConstBf, doubleBf]] doubleRuleList :: [GTerm] doubleRuleList = [ GVariable doubleSort , GBfTerm $ BfIdentifier (ISymb "DClamp") -- GConstant doubleSort , GBfTerm $ BfIdentifierBfs (ISymb "+") [doubleBf, doubleBf] , GBfTerm $ BfIdentifierBfs (ISymb "-") [doubleBf, doubleBf] ] -- ++ [GBfTerm . BfLiteral . LitNum $ x | x <- [0..0]] boolRuleList :: [GTerm] boolRuleList = boolDefRuleList ++ boolIntArgRuleList ++ boolDoubleArgRuleList boolDefRuleList :: [GTerm] boolDefRuleList = boolTrueRuleList ++ boolOpRuleList boolTrueRuleList :: [GTerm] boolTrueRuleList = [ -- (GConstant boolSort) is significantly slower than just enumerating the bools -- , GConstant boolSort GBfTerm $ BfLiteral (LitBool True) ] boolOpRuleList :: [GTerm] boolOpRuleList = [ GVariable boolSort , GBfTerm $ BfIdentifierBfs (ISymb "=>") [boolBf, boolBf] , GBfTerm $ BfIdentifierBfs (ISymb "and") [boolBf, boolBf] -- , GBfTerm $ BfIdentifierBfs (ISymb "or") [boolBf, boolBf] , GBfTerm $ BfIdentifierBfs (ISymb "not") [boolBf] ] boolIntArgRuleList :: [GTerm] boolIntArgRuleList = [ GBfTerm $ BfIdentifierBfs (ISymb "=") [intBf, intBf] , GBfTerm $ BfIdentifierBfs (ISymb "<") [intBf, intBf] , GBfTerm $ BfIdentifierBfs (ISymb "<=") [intBf, intBf] ] boolDoubleArgRuleList :: [GTerm] boolDoubleArgRuleList = [ GBfTerm $ BfIdentifierBfs (ISymb "=") [doubleBf, doubleBf] , GBfTerm $ BfIdentifierBfs (ISymb "<") [doubleBf, doubleBf] , GBfTerm $ BfIdentifierBfs (ISymb "<=") [doubleBf, doubleBf] ] elimHigherOrderArgs :: FuncConstraint -> FuncConstraint elimHigherOrderArgs fc = let cons = constraint fc as = arguments cons as' = filter (not . isTyFun . typeOf) as in fc { constraint = cons { arguments = as' }} dtGroupRuleList :: Symbol -> Sort -> GroupedRuleList dtGroupRuleList symb srt = GroupedRuleList symb srt [GVariable srt] intBf :: BfTerm intBf = BfIdentifier (ISymb "I") intConstBf :: BfTerm intConstBf = BfIdentifier (ISymb "IConst") doubleBf :: BfTerm doubleBf = BfIdentifier (ISymb "D") doubleConstBf :: BfTerm doubleConstBf = BfIdentifier (ISymb "DConst") boolBf :: BfTerm boolBf = BfIdentifier (ISymb "B") intSort :: Sort intSort = IdentSort (ISymb "Int") doubleSort :: Sort doubleSort = IdentSort (ISymb "Real") boolSort :: Sort boolSort = IdentSort (ISymb "Bool") charSort :: Sort charSort = IdentSort (ISymb "String") relArgs :: TypeClasses -> [Type] -> FuncConstraint -> FuncConstraint relArgs tc ts fc = let cons = constraint fc as = filter (relArg tc) $ arguments cons ts_as = zip ts as as' = map snd $ filter (relTy . fst) ts_as in fc { constraint = cons { arguments = as' }} relArg :: TypeClasses -> G2.Expr -> Bool relArg tc e = (not . isTypeClass tc . typeOf $ e) && (not . isType $ e) where isType (Type _) = True isType _ = False type ArgTys = [Type] type RetType = Type type PolyTypes = [Type] generateRelTypes :: TypeClasses -> G2.Expr -> (ArgTys, RetType, PolyTypes) generateRelTypes tc e = let ty_e = PresType $ inTyForAlls (typeOf e) arg_ty_c = filter (not . isTYPE) . filter (not . isTypeClass tc) $ argumentTypes ty_e ret_ty_c = returnType ty_e ex_ty_c = concatMap unTyApp $ ret_ty_c:arg_ty_c in (arg_ty_c, ret_ty_c, ex_ty_c) -- | Is the given type usable by SyGuS? relTy :: Type -> Bool relTy (TyVar _) = False relTy (TyFun _ _) = False relTy _ = True ------------------------------- -- Constraints ------------------------------- -- | Constraints expresessed as "anded" terms data TermConstraint = TC { pos_term :: Bool, tc_violated :: Violated, param_ret_connector :: Symbol, param_terms :: [Term], ret_terms :: [Term] } deriving (Show, Read) modifyParamTC :: ([Term] -> [Term]) -> TermConstraint -> TermConstraint modifyParamTC f tc = tc { param_terms = f (param_terms tc) } modifyRetTC :: ([Term] -> [Term]) -> TermConstraint -> TermConstraint modifyRetTC f tc = tc { ret_terms = f (ret_terms tc) } -- | Convert constraints. Measures cause us to lose information about the data, so after -- conversion we can have a constraint both postively and negatively. We know that the postive -- constraint corresponds to an actual execution, so we keep that one, adnd drop the negative constraint. generateConstraints :: TypesToSorts -> MeasureExs -> [RefNamePolyBound] -> RefNamePolyBound -> [Type] -> Type -> [FuncConstraint] -> [Cmd] generateConstraints sorts meas_ex arg_poly_names ret_poly_names arg_tys ret_ty fcs = let cons = map (termConstraints sorts meas_ex arg_poly_names ret_poly_names arg_tys ret_ty) fcs cons' = cons -- filterPosAndNegConstraints cons cons'' = map termConstraintToConstraint cons' exists_args = concatMap (\(pn, ars) -> existentialConstraints sorts meas_ex pn (init ars) (last ars)) $ zip arg_poly_names (filter (not . null) $ inits arg_tys) exists_ret = existentialConstraints sorts meas_ex ret_poly_names arg_tys ret_ty in {- exists_args ++ exists_ret ++ -} cons'' -- | Prevents any refinements from being set to "False" (or equivalent, i.e. 0 < 0) existentialConstraints :: TypesToSorts -> MeasureExs -> RefNamePolyBound -> [Type] -> Type -> [Cmd] existentialConstraints sorts meas_ex poly_names arg_tys ret_ty = let rt_bound = extractTypePolyBound ret_ty ns_rt = zipPB rt_bound poly_names in mapMaybe (fmap Constraint . uncurry (existentialTerms sorts meas_ex arg_tys)) $ extractValues ns_rt existentialTerms :: TypesToSorts -> MeasureExs -> [Type] -> Type -> String -> Maybe Term existentialTerms _ _ _ (TyVar _) _ = Nothing existentialTerms sorts meas_ex arg_tys ret_ty fn = let ar_vs = map (\(i, t) -> ("arg" ++ show i, typeToSort sorts t)) $ zip [0..] (arg_tys) srt_v = ("e_ret", typeToSort sorts ret_ty) in Just . TermExists (map (uncurry SortedVar) $ srt_v:ar_vs) $ TermCall (ISymb fn) (map (TermIdent . ISymb . fst) ar_vs ++ [TermIdent (ISymb "e_ret")]) termConstraints :: TypesToSorts -> MeasureExs -> [RefNamePolyBound] -> RefNamePolyBound -> [Type] -> Type -> FuncConstraint -> TermConstraint termConstraints sorts meas_ex arg_poly_names ret_poly_names arg_tys ret_ty (FC { polarity = p , violated = v , bool_rel = br , constraint = fc }) = TC { pos_term = p == Pos , tc_violated = v , param_ret_connector = if br == BRAnd then "and" else "=>" , param_terms = funcParamTerms sorts meas_ex arg_poly_names arg_tys (arguments fc) , ret_terms = funcCallRetTerm sorts meas_ex ret_poly_names arg_tys ret_ty (arguments fc) (returns fc) } -- When polymorphic arguments are instantiated with values, we use those as -- arguments for the polymorphic refinement functions. However, even when they -- do not have values, we still want to enforce that (for some value) the polymorphic -- refinement function is true. Thus, given arguments a_1, ..., a_n, we need to add -- an expression of the form: -- exists x . r(a_1, ..., a_n, x) data ValOrExistential v = Val v | Existential funcParamTerms :: TypesToSorts -> MeasureExs -> [RefNamePolyBound] -> [Type] -> [G2.Expr] -> [Term] funcParamTerms sorts meas_ex poly_names arg_tys ars = let inits_arg_tys = filter (not . null) $ inits arg_tys init_ars = filter (not . null) $ inits ars in concatMap (\(pn, at, as) -> funcCallTerm sorts meas_ex pn (init at) (last at) (init as) (last as) ) $ zip3 poly_names inits_arg_tys init_ars funcCallRetTerm :: TypesToSorts -> MeasureExs -> RefNamePolyBound -> [Type] -> Type -> [G2.Expr] -> G2.Expr -> [Term] funcCallRetTerm _ _ _ _ _ _ (Prim Error _) = [TermLit $ LitBool True] funcCallRetTerm sorts meas_ex poly_names arg_tys ret_ty ars r = funcCallTerm sorts meas_ex poly_names arg_tys ret_ty ars r funcCallTerm :: TypesToSorts -> MeasureExs -> RefNamePolyBound -> [Type] -> Type -> [G2.Expr] -> G2.Expr -> [Term] funcCallTerm sorts meas_ex poly_names arg_tys ret_ty ars r = let r_bound = extractExprPolyBoundWithRoot r rt_bound = extractTypePolyBound ret_ty ns_r_bound = zip3PB r_bound rt_bound poly_names ns_r_bound' = concatMap expand1 (extractValues ns_r_bound) in mapMaybe (\(r, rt, n) -> funcCallTerm' sorts meas_ex arg_tys ars r rt n) $ ns_r_bound' -- r where expand1 :: ([a], b, c) -> [(ValOrExistential a, b, c)] expand1 ([], b, c) = [(Existential, b, c)] expand1 (as, b, c) = map (\a -> (Val a, b, c)) as funcCallTerm' :: TypesToSorts -> MeasureExs -> [Type] -> [G2.Expr] -> ValOrExistential G2.Expr -> Type -> String -> Maybe Term funcCallTerm' sorts meas_ex arg_tys ars r ret_ty fn | Val r' <- r , relTy ret_ty = let trm_ret = exprToTerm sorts meas_ex ret_ty r' in Just $ TermCall (ISymb fn) (trm_ars ++ [trm_ret]) | Existential <- r , relTy ret_ty = let srt_v = SortedVar "e_ret" $ typeToSort sorts ret_ty in Just . TermExists [srt_v] $ TermCall (ISymb fn) (trm_ars ++ [TermIdent (ISymb "e_ret")]) | otherwise = Nothing where trm_ars = map (uncurry (exprToTerm sorts meas_ex)) (zip arg_tys ars) exprToTerm :: TypesToSorts -> MeasureExs -> Type -> G2.Expr -> Term exprToTerm _ _ (TyCon (Name "Bool" _ _ _) _) (Data (DataCon (Name n _ _ _) _)) | "True" <- n = TermLit $ LitBool True | "False" <- n =TermLit $ LitBool False exprToTerm _ _ (TyCon (Name n _ _ _) _) (App _ (Lit l)) | n == "Int" || n == "Float" || n == "Double" || n == "Char" = litToTerm l exprToTerm _ _ _ (Lit l) = litToTerm l exprToTerm sorts meas_ex t e = exprToDTTerm sorts meas_ex t e exprToTerm _ _ _ e = error $ "exprToTerm: Unhandled Expr " ++ show e litToTerm :: G2.Lit -> Term litToTerm (LitInt i) = TermLit (LitNum i) litToTerm (LitDouble d) = TermCall (ISymb "/") [ TermLit . LitNum $ numerator d , TermLit . LitNum $ denominator d] litToTerm (LitChar c) = TermLit (LitStr [c]) litToTerm _ = error "litToTerm: Unhandled Lit" exprToDTTerm :: TypesToSorts -> MeasureExs -> Type -> G2.Expr -> Term exprToDTTerm sorts meas_ex t e = case lookupSort t sorts of Just si | not . null $ meas_names si -> TermCall (ISymb (dt_name si)) $ map (measVal sorts meas_ex e) (meas_names si) | otherwise -> TermIdent (ISymb (dt_name si)) Nothing -> error $ "exprToDTTerm: No sort found" ++ "\nsorts = " ++ show sorts ++ "\nt = " ++ show t ++ "\ne = " ++ show e filterPosAndNegConstraints :: [TermConstraint] -> [TermConstraint] filterPosAndNegConstraints ts = let tre = concatMap ret_terms $ filter isForcedRet ts in filter (\t -> (not . null $ ret_terms t) || param_ret_connector t == "=>" || tc_violated t == Pre) $ map (\t -> if pos_term t || param_ret_connector t == "=>" then t else modifyRetTC (filter (not . flip elem tre)) t) ts where -- Does ths post-condition HAVE to hold? isForcedRet t = pos_term t && param_ret_connector t == "and" termConstraintToConstraint :: TermConstraint -> Cmd termConstraintToConstraint (TC p v pr_con param_ts ret_ts) = let param_tc = case param_ts of [] -> TermLit (LitBool True) _ -> TermCall (ISymb "and") param_ts ret_tc = case ret_ts of [] -> TermLit (LitBool True) _ -> TermCall (ISymb "and") ret_ts ret_tc' = case p of True -> ret_tc False -> TermCall (ISymb "not") [ret_tc] tc = TermCall (ISymb pr_con) [param_tc, ret_tc'] in Constraint tc typeToSort :: TypesToSorts -> Type -> Sort typeToSort _ (TyCon (Name n _ _ _) _) | n == "Int" = intSort | n == "Double" = doubleSort | n == "Bool" = boolSort | n == "Char" = charSort typeToSort sm t | Just si <- lookupSort t sm = IdentSort (ISymb $ sort_name si) typeToSort sm t = error $ "Unknown Type\n" ++ show t ++ "\nsm = " ++ show sm ------------------------------- -- Measures ------------------------------- measVal :: TypesToSorts -> MeasureExs -> G2.Expr -> SortedVar -> Term measVal sorts meas_ex e (SortedVar mn _) = let meas_n = strToName mn in case HM.lookup e meas_ex of Just meas_out | Just (_, v) <- find (\(n', _) -> nameOcc meas_n == nameOcc n') meas_out -> exprToTerm sorts meas_ex (typeOf v) v Nothing -> error $ "measVal: Expr not found\nmeas_ex = " ++ show meas_ex ++ "\ne = " ++ show e newtype TypesToSorts = TypesToSorts { types_to_sorts :: [(Type, SortInfo)] } deriving (Show, Read) data SortInfo = SortInfo { sort_name :: Symbol , dt_name :: Symbol , meas_names :: [SortedVar]} deriving (Show, Read) typesToSort :: Measures -> MeasureExs -> [Type] -> TypesToSorts typesToSort meas meas_ex ty_c = let rel_ty_c = filter relTy ty_c rel_ty_c' = nubBy (\t1 t2 -> t1 .::. t2) rel_ty_c dt_ts = filter (not . isPrimTy) rel_ty_c' ns = concatMap (map fst) . HM.elems $ meas_ex applic_meas = map (applicableMeasures meas) dt_ts applic_meas' = map (filter (\m -> m `elem` ns)) applic_meas meas_ids = map (map (\n -> Id n (returnType (case E.lookup n meas of Just e -> e Nothing -> error "sygusCall: No type found")))) applic_meas' meas_ids' = filterNonPrimMeasure meas_ids ts_applic_meas = zip dt_ts meas_ids' in typesToSort' ts_applic_meas isPrimTy :: Type -> Bool isPrimTy (TyCon (Name "Int" _ _ _) _) = True isPrimTy (TyCon (Name "Double" _ _ _) _) = True isPrimTy (TyCon (Name "Bool" _ _ _) _) = True isPrimTy _ = False typesToSort' :: [(Type, [Id])] -> TypesToSorts typesToSort' ts = let ts_s = map (\(i, (t, ns)) -> typesToSort'' i t ns) $ zip [0..] ts in TypesToSorts ts_s typesToSort'' :: Int -> Type -> [Id] -> (Type, SortInfo) typesToSort'' i t ns = let srt = "Sort_" ++ show i dt = "DT_" ++ show i sel_svs = map (\is@(Id (Name n m _ _) _) -> SortedVar (nameToStr (Name n m i Nothing)) (typeToSort (TypesToSorts []) (typeOf is)) ) ns in (t, SortInfo { sort_name = srt, dt_name = dt, meas_names = sel_svs }) lookupSort :: Type -> TypesToSorts -> Maybe SortInfo lookupSort t (TypesToSorts sorts) = let sis = filter (\(t', _) -> PresType t .:: t') sorts min_sis = filter (\(t', _) -> all (\(t'', _) -> PresType t' .:: t'') sis) sis in case min_sis of [(_, si)] -> Just si [] -> Nothing _ -> error $ "t = " ++ show t ++ "\nmin_sis = " ++ show min_sis -- = fmap (snd) . find (\(t', _) -> PresType t .:: t') . types_to_sorts lookupSortInfoBySort :: Symbol -> TypesToSorts -> Maybe SortInfo lookupSortInfoBySort symb (TypesToSorts ts) = find (\s -> symb == sort_name s) $ map snd ts sortsToDeclareDTs :: TypesToSorts -> [Cmd] sortsToDeclareDTs = map (sortToDeclareDT) . map snd . types_to_sorts sortToDeclareDT :: SortInfo -> Cmd sortToDeclareDT (SortInfo {sort_name = srt, dt_name = dtn, meas_names = sels}) = SmtCmd . DeclareDatatype srt $ DTDec [DTConsDec dtn sels] filterNonPrimMeasure :: [[Id]] -> [[Id]] filterNonPrimMeasure = map (filter isPrimMeasure) isPrimMeasure :: Id -> Bool isPrimMeasure = isPrimTy . typeOf allSorts :: TypesToSorts -> [Sort] allSorts = map (IdentSort . ISymb) . allSortNames allSortNames :: TypesToSorts -> [Symbol] allSortNames = map (sort_name . snd) . types_to_sorts addSelectors :: HM.HashMap Symbol String -> Sort -> TypesToSorts -> [GTerm] addSelectors grams s = concatMap (\si -> case HM.lookup (sort_name si) grams of Just gn -> mapMaybe (addSelector gn s) (meas_names si) Nothing -> error "addSelectors: Grammar name not found") . map snd . types_to_sorts addSelector :: Symbol -> Sort -> SortedVar -> Maybe GTerm addSelector gn s (SortedVar ident vs) | s == vs = Just . GBfTerm $ BfIdentifierBfs (ISymb ident) [BfIdentifier (ISymb gn)] | otherwise = Nothing filterToSorts :: [Symbol] -> TypesToSorts -> TypesToSorts filterToSorts xs (TypesToSorts sorts) = TypesToSorts $ filter (\(_, s) -> any (sort_name s ==) xs) sorts ------------------------------- -- Enforcing return value use ------------------------------- -- Adjusts a grammar to force using a given GTerm forceVarInGrammar :: SortedVar -- ^ The variable to force -> [SortedVar] -- ^ All other variables -> GrammarDef -> GrammarDef forceVarInGrammar var params (GrammarDef sv grls) = let prod_srt = mapMaybe (\grl@(GroupedRuleList grl_symb srt' _) -> if any (flip canProduceVar grl) (var:params) then Just grl_symb else Nothing ) grls reach = gramSymbReachableFrom prod_srt grls sv_reach = concatMap (grammarDefSortedVars reach) sv (sv_final, grl_final) = elimNonTermGRL var (forceVarInGRLList var reach grls) sv_reach in GrammarDef sv_final grl_final forceVarInGRLList :: SortedVar -> [Symbol] -> [GroupedRuleList] -> [GroupedRuleList] forceVarInGRLList var reach grls = let fv_map = HM.fromList $ map (\n -> (toBf n, toBf $ forcedVarSymb n)) reach in concatMap (forceVarInGRL var reach fv_map) grls where toBf = BfIdentifier . ISymb forceVarInGRL :: SortedVar -> [Symbol] -> HM.HashMap BfTerm BfTerm -> GroupedRuleList -> [GroupedRuleList] forceVarInGRL (SortedVar sv_symb sv_srt) reach fv_map grl@(GroupedRuleList grl_symb grl_srt gtrms) | grl_symb `elem` reach = let bf_var = BfIdentifier (ISymb sv_symb) fv_gtrms' = if sv_srt == grl_srt then GBfTerm bf_var:(filter (not . isClamp) $ elimVariable fv_gtrms) else filter (not . isClamp) $ elimVariable fv_gtrms in [GroupedRuleList fv_symb grl_srt fv_gtrms', grl] | otherwise = [grl] where fv_symb = forcedVarSymb grl_symb fv_gtrms = substOnceGTerms fv_map gtrms forcedVarSymb :: Symbol -> Symbol forcedVarSymb = ("fv_" ++) elimVariable :: [GTerm] -> [GTerm] elimVariable = filter (\t -> case t of GVariable _ -> False _ -> True) isClamp :: GTerm -> Bool isClamp (GBfTerm (BfIdentifier (ISymb "IClamp"))) = True isClamp (GBfTerm (BfIdentifier (ISymb "DClamp"))) = True isClamp _ = False ----------------------------------------------------- elimNonTermGRL :: SortedVar -> [GroupedRuleList] -> [SortedVar] -> ([SortedVar], [GroupedRuleList]) elimNonTermGRL (SortedVar sv_n _) grls sv = let has_term = hasTermFix (HS.singleton sv_n) grls sv' = filter (\(SortedVar n _) -> n `elem` has_term) sv grls' = map (elimRules has_term) $ filter (\(GroupedRuleList n _ _) -> n `elem` has_term) grls in trace "-----" (sv', grls') hasTermFix :: HS.HashSet Symbol -> [GroupedRuleList] -> [Symbol] hasTermFix ht grl = let ht' = HS.fromList . map (\(GroupedRuleList n _ _) -> n) $ filter (hasTermGRL ht) grl ht_all = HS.union ht ht' in if ht == ht_all then HS.toList ht_all else hasTermFix ht_all grl hasTermGRL :: HS.HashSet Symbol -> GroupedRuleList -> Bool hasTermGRL ht (GroupedRuleList n _ r) = n `HS.member` ht || any (hasTermGTerm ht) r hasTermGTerm :: HS.HashSet Symbol -> GTerm -> Bool hasTermGTerm _ (GConstant _) = True hasTermGTerm _ (GVariable _) = True hasTermGTerm ht (GBfTerm bft) = hasTermBfTerm ht bft hasTermBfTerm :: HS.HashSet Symbol -> BfTerm -> Bool hasTermBfTerm ht (BfIdentifier (ISymb i)) = i `HS.member` ht hasTermBfTerm _ (BfLiteral _) = True hasTermBfTerm ht (BfIdentifierBfs _ bfs) = all (hasTermBfTerm ht) bfs ----------------------------------------------------- -- elimEmptyGRL :: [GroupedRuleList] -> [GroupedRuleList] -- elimEmptyGRL grl = -- let -- emp_grl = map (\(GroupedRuleList n _ _) -> n) -- $ filter (\(GroupedRuleList _ _ r) -> null r) grl -- in -- filter (\(GroupedRuleList _ _ r) -> not $ null r) $ map (elimRules emp_grl) grl elimRules :: [Symbol] -> GroupedRuleList -> GroupedRuleList elimRules grls (GroupedRuleList symb srt r) = GroupedRuleList symb srt $ filter (elimRules' grls) r elimRules' :: [Symbol] -> GTerm -> Bool elimRules' _ (GConstant _) = True elimRules' _ (GVariable _) = True elimRules' grls (GBfTerm bft) = elimRulesBfT grls bft elimRulesBfT :: [Symbol] -> BfTerm -> Bool elimRulesBfT grls (BfIdentifier (ISymb i)) = i `elem` grls elimRulesBfT _ (BfLiteral _) = True elimRulesBfT grls (BfIdentifierBfs _ bfs) = all (elimRulesBfT grls) bfs ----------------------------------------------------- grammarDefSortedVars :: [Symbol] -> SortedVar -> [SortedVar] grammarDefSortedVars symbs sv@(SortedVar n srt) | n `elem` symbs = [SortedVar (forcedVarSymb n) srt, sv] | otherwise = [sv] -- Can a GroupedRuleList produce a given variable? canProduceVar :: SortedVar -> GroupedRuleList -> Bool canProduceVar var@(SortedVar symb sv_srt) (GroupedRuleList _ grl_srt gtrms) | sv_srt == grl_srt = any (canProduceVarGTerm var) gtrms | otherwise = False canProduceVarGTerm :: SortedVar -> GTerm -> Bool canProduceVarGTerm (SortedVar _ sv_srt) (GVariable gv_srt) = sv_srt == gv_srt canProduceVarGTerm (SortedVar s _) (GBfTerm (BfIdentifier (ISymb is))) = s == is canProduceVarGTerm s (GBfTerm (BfIdentifierBfs _ bfs)) = any (canProduceVarGTerm s) $ map GBfTerm bfs canProduceVarGTerm _ (GBfTerm (BfLiteral _)) = False canProduceVarGTerm _ (GConstant _) = False canProduceVarGTerm _ t = error $ "Unhandled term" ++ show t -- Reachability checks gramSymbReachableFrom :: [Symbol] -> [GroupedRuleList] -> [Symbol] gramSymbReachableFrom = gramSymbReachableFrom' HS.empty gramSymbReachableFrom' :: HS.HashSet Symbol -> [Symbol] -> [GroupedRuleList] -> [Symbol] gramSymbReachableFrom' searched [] _ = HS.toList searched gramSymbReachableFrom' searched (x:xs) grls | x `HS.member` searched = gramSymbReachableFrom' searched xs grls | otherwise = let contains_x = map (\(GroupedRuleList s _ _) -> s) $ filter (containsSymbol x) grls in gramSymbReachableFrom' (HS.insert x searched) (contains_x ++ xs) grls containsSymbol :: Symbol -> GroupedRuleList -> Bool containsSymbol symb (GroupedRuleList _ _ gtrms) = any (containsSymbolGTerm symb) gtrms containsSymbolGTerm :: Symbol -> GTerm -> Bool containsSymbolGTerm symb (GBfTerm bf) = containsSymbolBfTerm symb bf containsSymbolGTerm _ _ = False containsSymbolBfTerm :: Symbol -> BfTerm -> Bool containsSymbolBfTerm symb (BfIdentifier ident) = containsSymbolIdent symb ident containsSymbolBfTerm symb (BfIdentifierBfs ident bfs) = containsSymbolIdent symb ident || any (containsSymbolBfTerm symb) bfs containsSymbolBfTerm _ (BfLiteral _) = False containsSymbolIdent :: Symbol -> Identifier -> Bool containsSymbolIdent symb (ISymb symb') = symb == symb' containsSymbolIdent symb _ = False -- Substitution functions substOnceGRL :: HM.HashMap BfTerm BfTerm -> GroupedRuleList -> GroupedRuleList substOnceGRL m (GroupedRuleList symb srt gtrms) = GroupedRuleList symb srt $ substOnceGTerms m gtrms substOnceGTerms :: HM.HashMap BfTerm BfTerm -> [GTerm] -> [GTerm] substOnceGTerms m = concatMap (substOnceGTerm m) substOnceGTerm :: HM.HashMap BfTerm BfTerm -> GTerm -> [GTerm] substOnceGTerm m (GBfTerm bf) = map GBfTerm $ substOnceBfTerm m bf substOnceGTerm _ gt = [gt] substOnceBfTerm :: HM.HashMap BfTerm BfTerm -> BfTerm -> [BfTerm] substOnceBfTerm m (BfIdentifierBfs c bfs) = elimRedundant . map (BfIdentifierBfs c) $ substsOnces m bfs substOnceBfTerm _ bf = [bf] substOnceTerm :: HM.HashMap Term Term -> Term -> [Term] substOnceTerm m (TermCall c ts) = map (TermCall c) $ substsOnces m ts substOnceTerm _ t = [t] elimRedundant :: [BfTerm] -> [BfTerm] elimRedundant (b@(BfIdentifierBfs (ISymb s) [b1, b2]):xs) = let xs' = if isCommutative s then delete (BfIdentifierBfs (ISymb s) [b2, b1]) xs else xs in b:elimRedundant xs' elimRedundant (x:xs) = x:elimRedundant xs elimRedundant [] = [] isCommutative :: Symbol -> Bool isCommutative "and" = True isCommutative "=" = True isCommutative "+" = True isCommutative _ = False -- | Given: -- * A mapping of list element to be replaced, to new elements -- * A list, xs -- returns a list of new lists. Each new list is xs, but with exactly one -- occurence of an old element replaced by the corresponding new element. substsOnces :: (Eq a, Hashable a) => HM.HashMap a a -> [a] -> [[a]] substsOnces m = substsOnces' m [] substsOnces' :: (Eq a, Hashable a) => HM.HashMap a a -> [a] -> [a] -> [[a]] substsOnces' m rv [] = [] substsOnces' m rv (x:xs) | Just new <- HM.lookup x m = (reverse rv ++ new:xs):rst | otherwise = rst where rst = substsOnces' m (x:rv) xs ------------------------------- -- Converting to refinement ------------------------------- stripUnsat :: String -> String stripUnsat ('u':'n':'s':'a':'t':xs) = xs stripUnsat xs = xs refToQualifiers :: SpecType -> [RefNamePolyBound] -> RefNamePolyBound -> [Cmd] -> MeasureSymbols -> LH.TCEmb TyCon -> [Qualifier] refToQualifiers st arg_pb ret_pb cmds meas_sym tycons = let arg_termsPB = map (defineFunsPB cmds) arg_pb ret_termsPB = defineFunsPB cmds ret_pb lh_e = refToLHExpr' st arg_termsPB ret_termsPB meas_sym in map (uncurry (refToQualifier tycons)) (concatMap extractValues lh_e) refToQualifier :: LH.TCEmb TyCon -> [(LH.Symbol, SpecType)] -> LH.Expr -> Qualifier refToQualifier tycons params e = Q { qName = "G2" , qParams = map (mkParam tycons) (last params:init params) , qBody = e , qPos = LH.dummyPos "G2" } mkParam :: LH.TCEmb TyCon -> (LH.Symbol, SpecType) -> QualParam mkParam tycons (symb, st) = QP { qpSym = symb, qpPat = PatNone, qpSort = funcHead $ rTypeSort tycons st } where funcHead (LH.FFunc h _) = h funcHead s = s refToLHExpr :: SpecType -> [RefNamePolyBound] -> RefNamePolyBound -> [Cmd] -> MeasureSymbols -> [PolyBound LH.Expr] refToLHExpr st arg_pb ret_pb cmds meas_sym = let arg_termsPB = map (defineFunsPB cmds) arg_pb ret_termsPB = defineFunsPB cmds ret_pb lh_e = refToLHExpr' st arg_termsPB ret_termsPB meas_sym in map (mapPB snd) lh_e defineFunsPB :: [Cmd] -> RefNamePolyBound -> PolyBound ([SortedVar], Term) defineFunsPB cmds = mapPB (defineFunsPB' cmds) defineFunsPB' :: [Cmd] -> String -> ([SortedVar], Term) defineFunsPB' cmds fn | Just (SmtCmd (DefineFun _ ars _ trm)) <- find (\(SmtCmd (DefineFun n _ _ _)) -> n == fn) cmds = (ars, trm) | otherwise = ([], TermLit (LitBool True)) -- | Shift all terms up as much as possible. This avoids expressions being nested more deeply- -- and thus (in G2) checked more frequently- than needed. shiftPB :: PolyBound ([SortedVar], Term) -> PolyBound ([SortedVar], Term) shiftPB pb = let pb' = shiftPB' pb in if pb == pb' then pb else shiftPB pb' shiftPB' :: PolyBound ([SortedVar], Term) -> PolyBound ([SortedVar], Term) shiftPB' (PolyBound svt@(sv, t) svts) = let (shift, leave) = partition (\(PolyBound (sv', t') _) -> let t_syms = termSymbols t' in case sv' of [] -> False _ -> let SortedVar s _ = (last sv') in s `notElem` t_syms) svts sv_new = nub $ sv ++ concatMap (\(PolyBound (sv', _) _) -> sv') shift t_new = case shift of [] -> t _ -> TermCall (ISymb "and") $ t:map (\(PolyBound (_, t') _) -> t') shift shift_new = map (\(PolyBound _ pb) -> PolyBound ([], TermLit (LitBool True)) pb) shift in PolyBound (sv_new, t_new) (shift_new ++ leave) refToLHExpr' :: SpecType -> [PolyBound ([SortedVar], Term)] -- ^ Arguments -> PolyBound ([SortedVar], Term) -- ^ Returns -> MeasureSymbols -> [PolyBound ([(LH.Symbol, SpecType)], LH.Expr)] refToLHExpr' st sygus_args sygus_ret meas_sym = let pieces = specTypePieces st -- This is a bit of a dirty hack. The relArgs function drops typeclasses, -- so that we don't have to deal with them in the SyGuS solver. But we still -- gather the bindings for the typeclasses with specTypeSymbols. -- Fortunately, the typeclasses are always the first arguments in the list, -- so we can simply take the correct number of arguments from the end of the list. -- Then, we fill up the front of the returned PolyBound list with true rel_pieces_len = length sygus_args + 1 tc_num = length pieces - rel_pieces_len tc_pb_exprs = map (\st -> PolyBound ([(specTypeSymbol st, st)], PTrue) []) $ take tc_num pieces last_pieces = reverse . take rel_pieces_len $ reverse pieces sygus_all = sygus_args ++ [sygus_ret] sygus_all_inits = filter (not . null) $ inits sygus_all pieces_inits = filter (not . null) $ inits last_pieces pb_expr = map (uncurry (refToLHExpr'' meas_sym)) $ zip sygus_all_inits pieces_inits in tc_pb_exprs ++ pb_expr refToLHExpr'' :: MeasureSymbols -> [PolyBound ([SortedVar], Term)] -> [SpecType] -> PolyBound ([(LH.Symbol, SpecType)], LH.Expr) refToLHExpr'' meas_sym sygus_in st = let sygus_args = map headValue $ init sygus_in sygus_ret = last sygus_in st_args = map (\st -> (specTypeSymbol st, st)) $ init st st_ret = last st st_ret_pb = specTypeRAppPiecesInFunc st_ret in mapPB (\(st, (sv, t)) -> refToLHExpr''' meas_sym (st_args ++ [(specTypeSymbol st, st)]) sv t) $ zipPB st_ret_pb sygus_ret refToLHExpr''' :: MeasureSymbols -> [(LH.Symbol, SpecType)] -> [SortedVar] -> Term -> ([(LH.Symbol, SpecType)], LH.Expr) refToLHExpr''' meas_sym symbs_st ars trm = let ars' = map (\(SortedVar sym _) -> sym) ars symbs = map fst symbs_st symbsArgs = M.fromList $ zip ars' symbs in (symbs_st, termToLHExpr meas_sym symbsArgs trm) termToLHExpr :: MeasureSymbols -> M.Map Sy.Symbol LH.Symbol -> Term -> LH.Expr termToLHExpr _ m_args (TermIdent (ISymb v)) = case M.lookup v m_args of Just v' -> EVar v' Nothing -> error "termToLHExpr: Variable not found" termToLHExpr _ _ (TermLit l) = litToLHConstant l termToLHExpr meas_sym@(MeasureSymbols meas_sym') m_args (TermCall (ISymb v) ts) -- Measures | Just meas <- find (\meas' -> Just (symbolName meas') == fmap zeroName (maybe_StrToName v)) meas_sym' = foldl' EApp (EVar meas) $ map (termToLHExpr meas_sym m_args) ts -- Clamped numbers | clampIntSymb == v , [TermLit l] <- ts = clampedInt l | clampDoubleSymb == v , [TermCall (ISymb "/") [t1, t2]] <- ts = clampedDouble t1 t2 | clampDoubleSymb == v , [t] <- ts = clampedDouble t (TermLit $ LitNum 1) -- EBin | "+" <- v , [t1, t2] <- ts = EBin LH.Plus (termToLHExpr meas_sym m_args t1) (termToLHExpr meas_sym m_args t2) | "-" <- v , [t1] <- ts = ENeg (termToLHExpr meas_sym m_args t1) | "-" <- v , [t1, t2] <- ts = EBin LH.Minus (termToLHExpr meas_sym m_args t1) (termToLHExpr meas_sym m_args t2) | "*" <- v , [t1, t2] <- ts = EBin LH.Times (termToLHExpr meas_sym m_args t1) (termToLHExpr meas_sym m_args t2) | "/" <- v , [t1, t2] <- ts , Just n1 <- getInteger t1 , Just n2 <- getInteger t2 = ECon . LHF.R $ fromRational (n1 % n2) | "mod" <- v , [t1, t2] <- ts = EBin LH.Mod (termToLHExpr meas_sym m_args t1) (termToLHExpr meas_sym m_args t2) -- Special handling for safe-mod. We enforce via the grammar that the denominator is an Integer | "safe-mod" <- v , [t1, t2] <- ts , TermLit (LitNum n) <- t2 = EBin LH.Mod (termToLHExpr meas_sym m_args t1) (ECon (I ((abs n) + 1))) -- More EBin... | "and" <- v = PAnd $ map (termToLHExpr meas_sym m_args) ts | "or" <- v = POr $ map (termToLHExpr meas_sym m_args) ts | "not" <- v, [t1] <- ts = PNot (termToLHExpr meas_sym m_args t1) | "=>" <- v , [t1, t2] <- ts = PImp (termToLHExpr meas_sym m_args t1) (termToLHExpr meas_sym m_args t2) -- PAtom | "=" <- v , [t1, t2] <- ts = PAtom LH.Eq (termToLHExpr meas_sym m_args t1) (termToLHExpr meas_sym m_args t2) | ">" <- v , [t1, t2] <- ts = PAtom LH.Gt (termToLHExpr meas_sym m_args t1) (termToLHExpr meas_sym m_args t2) | ">=" <- v , [t1, t2] <- ts = PAtom LH.Ge (termToLHExpr meas_sym m_args t1) (termToLHExpr meas_sym m_args t2) | "<" <- v , [t1, t2] <- ts = PAtom LH.Lt (termToLHExpr meas_sym m_args t1) (termToLHExpr meas_sym m_args t2) | "<=" <- v , [t1, t2] <- ts = PAtom LH.Le (termToLHExpr meas_sym m_args t1) (termToLHExpr meas_sym m_args t2) -- More PAtom... termToLHExpr meas_sym@(MeasureSymbols meas_sym') m_args (TermCall (ISymb v) ts) = error $ "v = " ++ show v ++ "\nts = " ++ show ts ++ "\nmaybe_StrToName v = " ++ show (maybe_StrToName v) ++ "\nmeas_syms' = " ++ show (map symbolName meas_sym') termToLHExpr (_) _ t = error $ "termToLHExpr meas_sym m_args: unhandled " ++ show t getInteger :: Term -> Maybe Integer getInteger (TermLit (LitNum n)) = Just n getInteger (TermCall (ISymb "-") [TermLit (LitNum n)]) = Just (- n) getInteger _ = Nothing zeroName :: Name -> Name zeroName (Name n m _ l) = Name n m 0 l litToLHConstant :: Sy.Lit -> LH.Expr litToLHConstant (LitNum n) = ECon (I n) litToLHConstant (LitBool b) = if b then PTrue else PFalse litToLHConstant l = error $ "litToLHConstant: Unhandled literal " ++ show l clampedInt :: Sy.Lit -> LH.Expr clampedInt (LitNum n) | n < 0 = ECon (LHF.I 0) | n > clampUpper = ECon (LHF.I clampUpper) | otherwise = ECon (LHF.I n) clampedInt _ = error $ "clampedInt: Unhandled literals" clampedDouble :: Term -> Term -> LH.Expr clampedDouble t1 t2 | n < 0 = ECon (LHF.R 0) | n > clampUpper = ECon (LHF.R clampUpper) | otherwise = ECon (LHF.R n) where d1 = termToInteger t1 d2 = termToInteger t2 n = fromInteger d1 / fromInteger d2 clampedDouble _ _ = error $ "clampedDouble: Unhandled literals" termToInteger :: Term -> Integer termToInteger (TermLit (LitNum d)) = d termToInteger (TermCall (ISymb "-") [t]) = - (termToInteger t) ------------------- specTypeNestedSymbol :: SpecType -> LH.Symbol specTypeNestedSymbol (RFun { rt_in = i }) = specTypeNestedSymbol i specTypeNestedSymbol st = specTypeSymbol st specTypeSymbol :: SpecType -> LH.Symbol specTypeSymbol (RFun { rt_bind = b }) = b specTypeSymbol rapp@(RApp { rt_reft = ref }) = reftSymbol $ ur_reft ref specTypeSymbol (RVar { rt_reft = ref }) = reftSymbol $ ur_reft ref specTypeSymbol _ = error $ "specTypeSymbol: SpecType not handled" specTypePieces :: SpecType -> [SpecType] specTypePieces st = specTypePieces' [] st specTypePieces' :: [SpecType] -> SpecType -> [SpecType] specTypePieces' sts rfun@(RFun { rt_in = i, rt_out = out }) = case i of RVar {} -> specTypePieces' sts out RFun {} -> specTypePieces' sts out _ -> specTypePieces' (rfun:sts) out specTypePieces' sts rapp@(RApp {}) = reverse (rapp:sts) specTypePieces' sts rvar@(RVar {}) = reverse (rvar:sts) specTypePieces' sts (RAllT { rt_ty = out }) = specTypePieces' sts out specTypeRAppPiecesInFunc :: SpecType -> PolyBound SpecType specTypeRAppPiecesInFunc (RFun {rt_in = i}) = specTypeRAppPiecesInFunc i specTypeRAppPiecesInFunc st = specTypeRAppPieces st specTypeRAppPieces :: SpecType -> PolyBound SpecType specTypeRAppPieces rapp@(RApp { rt_reft = ref, rt_args = ars }) = PolyBound rapp $ map specTypeRAppPieces ars specTypeRAppPieces rvar@(RVar {}) = PolyBound rvar [] specTypeRAppPieces r = error $ "specTypeRAppPieces: Unexpected SpecType" ++ "\n" ++ show r ------------------- reftSymbol :: Reft -> LH.Symbol reftSymbol = fst . unpackReft unpackReft :: Reft -> (LH.Symbol, LH.Expr) unpackReft = coerce -- | Collects all the symbols from a term termSymbols :: Term -> [Symbol] termSymbols (TermIdent i) = identifierSymbols i termSymbols (TermLit _) = [] termSymbols (TermCall i ts) = identifierSymbols i ++ concatMap termSymbols ts termSymbols (TermExists sv t) = map svSymbol sv ++ termSymbols t termSymbols (TermForAll sv t) = map svSymbol sv ++ termSymbols t termSymbols (TermLet vb t) = concatMap vbSymbols vb ++ termSymbols t identifierSymbols :: Identifier -> [Symbol] identifierSymbols (ISymb s) = [s] identifierSymbol (Indexed s inds) = s:mapMaybe indexSymbol inds indexSymbol :: Index -> Maybe Symbol indexSymbol (IndSymb s) = Just s indexSymbol _ = Nothing svSymbol :: SortedVar -> Symbol svSymbol (SortedVar s _) = s vbSymbols :: VarBinding -> [Symbol] vbSymbols (VarBinding s t) = s:termSymbols t ------------------------------- -- Calling SyGuS ------------------------------- runCVC4 :: InferenceConfig -> String -> IO (Either SomeException [Cmd]) runCVC4 infconfig sygus = try ( withSystemTempFile ("cvc4_input.sy") (\fp h -> do hPutStr h sygus -- We call hFlush to prevent hPutStr from buffering hFlush h toCommandOSX <- findExecutable "gtimeout" let toCommand = case toCommandOSX of Just c -> c -- Mac Nothing -> "timeout" -- Linux sol <- P.readProcess toCommand ([show (timeout_sygus infconfig), "cvc4", fp, "--lang=sygus2"]) "" let sol' = case stripPrefix "unsat" sol of { Just s -> s; Nothing -> error "runCVC4: non-unsat result" } return . parse . lexSygus $ sol') ) runCVC4StreamSolutions :: InferenceConfig -> Int -> String -> IO (Either SomeException [Cmd]) runCVC4StreamSolutions infconfig grouped sygus = try ( withSystemTempFile ("cvc4_input.sy") (\fp h -> do hPutStr h sygus -- We call hFlush to prevent hPutStr from buffering hFlush h timeout <- timeOutCommand -- --no-sygus-fair-max searches for functions that minimize the sum of the sizes of all functions (inp, outp, errp, _) <- P.runInteractiveCommand $ timeout ++ " " ++ show (timeout_sygus infconfig) ++ " cvc4 " ++ fp ++ " --lang=sygus2 --sygus-stream --no-sygus-fair-max" lnes <- checkIfSolution grouped outp hClose inp hClose outp hClose errp return lnes ) ) checkIfSolution :: Int -> Handle -> IO [Cmd] checkIfSolution grouped h = do sol <- getSolution grouped h let sol' = concatMap (parse . lexSygus) $ sol if all (\c -> rInCmd c || noVarInCmd c) sol' then return sol' else checkIfSolution grouped h getSolution :: Int -> Handle -> IO [String] getSolution 0 _ = return [] getSolution !n h = do lne <- hGetLine h lnes <- getSolution (n - 1) h return $ lne:lnes rInCmd :: Cmd -> Bool rInCmd (SmtCmd (DefineFun _ _ _ t)) = rInTerm t rInCmd _ = False rInTerm :: Term -> Bool rInTerm (TermIdent (ISymb n)) = n == "r" rInTerm (TermIdent _) = False rInTerm (TermLit _) = False rInTerm (TermCall _ ts) = any rInTerm ts rInTerm (TermExists _ t) = rInTerm t rInTerm (TermForAll _ t) = rInTerm t rInTerm (TermLet vs t) = any (\(VarBinding _ t') -> rInTerm t') vs || rInTerm t noVarInCmd :: Cmd -> Bool noVarInCmd (SmtCmd (DefineFun _ _ _ t)) = noVarInTerm t noVarInCmd _ = False noVarInTerm :: Term -> Bool noVarInTerm (TermIdent _) = False noVarInTerm (TermLit _) = True noVarInTerm (TermCall _ ts) = all noVarInTerm ts noVarInTerm (TermExists _ t) = noVarInTerm t noVarInTerm (TermForAll _ t) = noVarInTerm t noVarInTerm (TermLet vs t) = all (\(VarBinding _ t') -> noVarInTerm t') vs && noVarInTerm t runCVC4Stream :: Int -> String -> IO (Either SomeException String) runCVC4Stream max_size sygus = try ( withSystemTempFile ("cvc4_input.sy") (\fp h -> do hPutStr h sygus -- We call hFlush to prevent hPutStr from buffering hFlush h (inp, outp, errp, _) <- P.runInteractiveCommand $ "cvc4 " ++ fp ++ " --lang=sygus2 --sygus-stream --sygus-abort-size=" ++ show max_size lnes <- readLines outp [] hClose inp hClose outp hClose errp return lnes ) ) readLines :: Handle -> [String] -> IO String readLines h lnes = do b <- hIsEOF h if b then return . concat . reverse $ lnes else do lne <- hGetLine h if "(error" `isInfixOf` lne then readLines h lnes else readLines h (lne:lnes) timeOutCommand :: IO String timeOutCommand = do cmdMacOS <- findExecutable "gtimeout" case cmdMacOS of Just c -> return c Nothing -> return "timeout" -}