---------------------------------------------------------------------- -- | -- Module : Macros -- Maintainer : AR -- Stability : (stable) -- Portability : (portable) -- -- > CVS $Date: 2005/11/11 16:38:00 $ -- > CVS $Author: bringert $ -- > CVS $Revision: 1.24 $ -- -- Macros for constructing and analysing source code terms. -- -- operations on terms and types not involving lookup in or reference to grammars -- -- AR 7\/12\/1999 - 9\/5\/2000 -- 4\/6\/2001 ----------------------------------------------------------------------------- module GF.Grammar.Macros where import GF.Data.Operations import GF.Data.Str import GF.Infra.Ident import GF.Grammar.Grammar import GF.Grammar.Predef import GF.Grammar.Printer import Control.Monad.Identity(Identity(..)) import qualified Data.Traversable as T(mapM) import qualified Data.Map as Map import Control.Monad (liftM, liftM2, liftM3) import Data.List (sortBy,nub) import Data.Monoid import GF.Text.Pretty(render,(<+>),hsep,fsep) import qualified Control.Monad.Fail as Fail -- ** Functions for constructing and analysing source code terms. typeForm :: Type -> (Context, Cat, [Term]) typeForm t = case t of Prod b x a t -> let (x', cat, args) = typeForm t in ((b,x,a):x', cat, args) App c a -> let (_, cat, args) = typeForm c in ([],cat,args ++ [a]) Q c -> ([],c,[]) QC c -> ([],c,[]) Sort c -> ([],(MN identW, c),[]) _ -> error (render ("no normal form of type" <+> ppTerm Unqualified 0 t)) typeFormCnc :: Type -> (Context, Type) typeFormCnc t = case t of Prod b x a t -> let (x', v) = typeFormCnc t in ((b,x,a):x',v) _ -> ([],t) valCat :: Type -> Cat valCat typ = let (_,cat,_) = typeForm typ in cat valType :: Type -> Type valType typ = let (_,cat,xx) = typeForm typ --- not optimal to do in this way in mkApp (Q cat) xx valTypeCnc :: Type -> Type valTypeCnc typ = snd (typeFormCnc typ) typeSkeleton :: Type -> ([(Int,Cat)],Cat) typeSkeleton typ = let (ctxt,cat,_) = typeForm typ in ([(length c, v) | (b,x,t) <- ctxt, let (c,v) = typeSkeleton t], cat) catSkeleton :: Type -> ([Cat],Cat) catSkeleton typ = let (args,val) = typeSkeleton typ in (map snd args, val) funsToAndFrom :: Type -> (Cat, [(Cat,[Int])]) funsToAndFrom t = let (cs,v) = catSkeleton t cis = zip cs [0..] in (v, [(c,[i | (c',i) <- cis, c' == c]) | c <- cs]) isRecursiveType :: Type -> Bool isRecursiveType t = let (cc,c) = catSkeleton t -- thus recursivity on Cat level in any (== c) cc isHigherOrderType :: Type -> Bool isHigherOrderType t = fromErr True $ do -- pessimistic choice co <- contextOfType t return $ not $ null [x | (_,x,Prod _ _ _ _) <- co] contextOfType :: Monad m => Type -> m Context contextOfType typ = case typ of Prod b x a t -> liftM ((b,x,a):) $ contextOfType t _ -> return [] termForm :: Monad m => Term -> m ([(BindType,Ident)], Term, [Term]) termForm t = case t of Abs b x t -> do (x', fun, args) <- termForm t return ((b,x):x', fun, args) App c a -> do (_,fun, args) <- termForm c return ([],fun,args ++ [a]) _ -> return ([],t,[]) termFormCnc :: Term -> ([(BindType,Ident)], Term) termFormCnc t = case t of Abs b x t -> ((b,x):xs, t') where (xs,t') = termFormCnc t _ -> ([],t) appForm :: Term -> (Term, [Term]) appForm t = case t of App c a -> (fun, args ++ [a]) where (fun, args) = appForm c Typed t _ -> appForm t _ -> (t,[]) mkProdSimple :: Context -> Term -> Term mkProdSimple c t = mkProd c t [] mkProd :: Context -> Term -> [Term] -> Term mkProd [] typ args = mkApp typ args mkProd ((b,x,a):dd) typ args = Prod b x a (mkProd dd typ args) mkTerm :: ([(BindType,Ident)], Term, [Term]) -> Term mkTerm (xx,t,aa) = mkAbs xx (mkApp t aa) mkApp :: Term -> [Term] -> Term mkApp = foldl App mkAbs :: [(BindType,Ident)] -> Term -> Term mkAbs xx t = foldr (uncurry Abs) t xx appCons :: Ident -> [Term] -> Term appCons = mkApp . Cn mkLet :: [LocalDef] -> Term -> Term mkLet defs t = foldr Let t defs mkLetUntyped :: Context -> Term -> Term mkLetUntyped defs = mkLet [(x,(Nothing,t)) | (_,x,t) <- defs] isVariable :: Term -> Bool isVariable (Vr _ ) = True isVariable _ = False --eqIdent :: Ident -> Ident -> Bool --eqIdent = (==) uType :: Type uType = Cn cUndefinedType -- *** Assignment assign :: Label -> Term -> Assign assign l t = (l,(Nothing,t)) assignT :: Label -> Type -> Term -> Assign assignT l a t = (l,(Just a,t)) unzipR :: [Assign] -> ([Label],[Term]) unzipR r = (ls, map snd ts) where (ls,ts) = unzip r mkAssign :: [(Label,Term)] -> [Assign] mkAssign lts = [assign l t | (l,t) <- lts] projectRec :: Label -> [Assign] -> Term projectRec l rs = case lookup l rs of Just (_,t) -> t Nothing -> error (render ("no value for label" <+> l)) zipAssign :: [Label] -> [Term] -> [Assign] zipAssign ls ts = [assign l t | (l,t) <- zip ls ts] mapAssignM :: Monad m => (Term -> m c) -> [Assign] -> m [(Label,(Maybe c,c))] mapAssignM f = mapM (\ (ls,tv) -> liftM ((,) ls) (g tv)) where g (t,v) = liftM2 (,) (maybe (return Nothing) (liftM Just . f) t) (f v) -- *** Records mkRecordN :: Int -> (Int -> Label) -> [Term] -> Term mkRecordN int lab typs = R [ assign (lab i) t | (i,t) <- zip [int..] typs] mkRecord :: (Int -> Label) -> [Term] -> Term mkRecord = mkRecordN 0 mkRecTypeN :: Int -> (Int -> Label) -> [Type] -> Type mkRecTypeN int lab typs = RecType [ (lab i, t) | (i,t) <- zip [int..] typs] mkRecType :: (Int -> Label) -> [Type] -> Type mkRecType = mkRecTypeN 0 record2subst :: Term -> Err Substitution record2subst t = case t of R fs -> return [(identC x, t) | (LIdent x,(_,t)) <- fs] _ -> Bad (render ("record expected, found" <+> ppTerm Unqualified 0 t)) -- *** Types typeType, typePType, typeStr, typeTok, typeStrs :: Type typeType = Sort cType typePType = Sort cPType typeStr = Sort cStr typeTok = Sort cTok typeStrs = Sort cStrs typeString, typeFloat, typeInt :: Type typeInts :: Int -> Type typePBool :: Type typeError :: Type typeString = cnPredef cString typeInt = cnPredef cInt typeFloat = cnPredef cFloat typeInts i = App (cnPredef cInts) (EInt i) typePBool = cnPredef cPBool typeError = cnPredef cErrorType isTypeInts :: Type -> Maybe Int isTypeInts (App c (EInt i)) | c == cnPredef cInts = Just i isTypeInts _ = Nothing -- *** Terms isPredefConstant :: Term -> Bool isPredefConstant t = case t of Q (mod,_) | mod == cPredef || mod == cPredefAbs -> True _ -> False checkPredefError :: Fail.MonadFail m => Term -> m Term checkPredefError t = case t of Error s -> fail ("Error: "++s) _ -> return t cnPredef :: Ident -> Term cnPredef f = Q (cPredef,f) mkSelects :: Term -> [Term] -> Term mkSelects t tt = foldl S t tt mkTable :: [Term] -> Term -> Term mkTable tt t = foldr Table t tt mkCTable :: [(BindType,Ident)] -> Term -> Term mkCTable ids v = foldr ccase v ids where ccase (_,x) t = T TRaw [(PV x,t)] mkHypo :: Term -> Hypo mkHypo typ = (Explicit,identW, typ) eqStrIdent :: Ident -> Ident -> Bool eqStrIdent = (==) tuple2record :: [Term] -> [Assign] tuple2record ts = [assign (tupleLabel i) t | (i,t) <- zip [1..] ts] tuple2recordType :: [Term] -> [Labelling] tuple2recordType ts = [(tupleLabel i, t) | (i,t) <- zip [1..] ts] tuple2recordPatt :: [Patt] -> [(Label,Patt)] tuple2recordPatt ts = [(tupleLabel i, t) | (i,t) <- zip [1..] ts] mkCases :: Ident -> Term -> Term mkCases x t = T TRaw [(PV x, t)] mkWildCases :: Term -> Term mkWildCases = mkCases identW mkFunType :: [Type] -> Type -> Type mkFunType tt t = mkProd [(Explicit,identW, ty) | ty <- tt] t [] -- nondep prod --plusRecType :: Type -> Type -> Err Type plusRecType t1 t2 = case (t1, t2) of (RecType r1, RecType r2) -> case filter (`elem` (map fst r1)) (map fst r2) of [] -> return (RecType (r1 ++ r2)) ls -> raise $ render ("clashing labels" <+> hsep ls) _ -> raise $ render ("cannot add record types" <+> ppTerm Unqualified 0 t1 <+> "and" <+> ppTerm Unqualified 0 t2) --plusRecord :: Term -> Term -> Err Term plusRecord t1 t2 = case (t1,t2) of (R r1, R r2 ) -> return (R ([(l,v) | -- overshadowing of old fields (l,v) <- r1, not (elem l (map fst r2)) ] ++ r2)) (_, FV rs) -> mapM (plusRecord t1) rs >>= return . FV (FV rs,_ ) -> mapM (`plusRecord` t2) rs >>= return . FV _ -> raise $ render ("cannot add records" <+> ppTerm Unqualified 0 t1 <+> "and" <+> ppTerm Unqualified 0 t2) -- | default linearization type defLinType :: Type defLinType = RecType [(theLinLabel, typeStr)] -- | refreshing variables mkFreshVar :: [Ident] -> Ident mkFreshVar olds = varX (maxVarIndex olds + 1) -- | trying to preserve a given symbol mkFreshVarX :: [Ident] -> Ident -> Ident mkFreshVarX olds x = if (elem x olds) then (varX (maxVarIndex olds + 1)) else x maxVarIndex :: [Ident] -> Int maxVarIndex = maximum . ((-1):) . map varIndex mkFreshVars :: Int -> [Ident] -> [Ident] mkFreshVars n olds = [varX (maxVarIndex olds + i) | i <- [1..n]] -- | quick hack for refining with var in editor freshAsTerm :: String -> Term freshAsTerm s = Vr (varX (readIntArg s)) -- | create a terminal for concrete syntax string2term :: String -> Term string2term = K int2term :: Int -> Term int2term = EInt float2term :: Double -> Term float2term = EFloat -- | create a terminal from identifier ident2terminal :: Ident -> Term ident2terminal = K . showIdent symbolOfIdent :: Ident -> String symbolOfIdent = showIdent symid :: Ident -> String symid = symbolOfIdent justIdentOf :: Term -> Maybe Ident justIdentOf (Vr x) = Just x justIdentOf (Cn x) = Just x justIdentOf _ = Nothing linTypeStr :: Type linTypeStr = mkRecType linLabel [typeStr] -- default lintype {s :: Str} linAsStr :: String -> Term linAsStr s = mkRecord linLabel [K s] -- default linearization {s = s} -- *** Term and pattern conversion term2patt :: Term -> Err Patt term2patt trm = case termForm trm of Ok ([], Vr x, []) | x == identW -> return PW | otherwise -> return (PV x) Ok ([], Con c, aa) -> do aa' <- mapM term2patt aa return (PC c aa') Ok ([], QC c, aa) -> do aa' <- mapM term2patt aa return (PP c aa') Ok ([], Q c, []) -> do return (PM c) Ok ([], R r, []) -> do let (ll,aa) = unzipR r aa' <- mapM term2patt aa return (PR (zip ll aa')) Ok ([],EInt i,[]) -> return $ PInt i Ok ([],EFloat i,[]) -> return $ PFloat i Ok ([],K s, []) -> return $ PString s --- encodings due to excessive use of term-patt convs. AR 7/1/2005 Ok ([], Cn id, [Vr a,b]) | id == cAs -> do b' <- term2patt b return (PAs a b') Ok ([], Cn id, [a]) | id == cNeg -> do a' <- term2patt a return (PNeg a') Ok ([], Cn id, [a]) | id == cRep -> do a' <- term2patt a return (PRep a') Ok ([], Cn id, []) | id == cRep -> do return PChar Ok ([], Cn id,[K s]) | id == cChars -> do return $ PChars s Ok ([], Cn id, [a,b]) | id == cSeq -> do a' <- term2patt a b' <- term2patt b return (PSeq a' b') Ok ([], Cn id, [a,b]) | id == cAlt -> do a' <- term2patt a b' <- term2patt b return (PAlt a' b') Ok ([], Cn c, []) -> do return (PMacro c) _ -> Bad $ render ("no pattern corresponds to term" <+> ppTerm Unqualified 0 trm) patt2term :: Patt -> Term patt2term pt = case pt of PV x -> Vr x PW -> Vr identW --- not parsable, should not occur PMacro c -> Cn c PM c -> Q c PC c pp -> mkApp (Con c) (map patt2term pp) PP c pp -> mkApp (QC c) (map patt2term pp) PR r -> R [assign l (patt2term p) | (l,p) <- r] PT _ p -> patt2term p PInt i -> EInt i PFloat i -> EFloat i PString s -> K s PAs x p -> appCons cAs [Vr x, patt2term p] --- an encoding PChar -> appCons cChar [] --- an encoding PChars s -> appCons cChars [K s] --- an encoding PSeq a b -> appCons cSeq [(patt2term a), (patt2term b)] --- an encoding PAlt a b -> appCons cAlt [(patt2term a), (patt2term b)] --- an encoding PRep a -> appCons cRep [(patt2term a)] --- an encoding PNeg a -> appCons cNeg [(patt2term a)] --- an encoding -- *** Almost compositional -- | to define compositional term functions composSafeOp :: (Term -> Term) -> Term -> Term composSafeOp op = runIdentity . composOp (return . op) -- | to define compositional term functions composOp :: Monad m => (Term -> m Term) -> Term -> m Term composOp co trm = case trm of App c a -> liftM2 App (co c) (co a) Abs b x t -> liftM (Abs b x) (co t) Prod b x a t -> liftM2 (Prod b x) (co a) (co t) S c a -> liftM2 S (co c) (co a) Table a c -> liftM2 Table (co a) (co c) R r -> liftM R (mapAssignM co r) RecType r -> liftM RecType (mapPairsM co r) P t i -> liftM2 P (co t) (return i) ExtR a c -> liftM2 ExtR (co a) (co c) T i cc -> liftM2 (flip T) (mapPairsM co cc) (changeTableType co i) V ty vs -> liftM2 V (co ty) (mapM co vs) Let (x,(mt,a)) b -> liftM3 let' (co a) (T.mapM co mt) (co b) where let' a' mt' b' = Let (x,(mt',a')) b' C s1 s2 -> liftM2 C (co s1) (co s2) Glue s1 s2 -> liftM2 Glue (co s1) (co s2) Alts t aa -> liftM2 Alts (co t) (mapM (pairM co) aa) FV ts -> liftM FV (mapM co ts) Strs tt -> liftM Strs (mapM co tt) EPattType ty -> liftM EPattType (co ty) ELincat c ty -> liftM (ELincat c) (co ty) ELin c ty -> liftM (ELin c) (co ty) ImplArg t -> liftM ImplArg (co t) _ -> return trm -- covers K, Vr, Cn, Sort, EPatt composSafePattOp op = runIdentity . composPattOp (return . op) composPattOp :: Monad m => (Patt -> m Patt) -> Patt -> m Patt composPattOp op patt = case patt of PC c ps -> liftM (PC c) (mapM op ps) PP qc ps -> liftM (PP qc) (mapM op ps) PR as -> liftM PR (mapPairsM op as) PT ty p -> liftM (PT ty) (op p) PAs x p -> liftM (PAs x) (op p) PImplArg p -> liftM PImplArg (op p) PNeg p -> liftM PNeg (op p) PAlt p1 p2 -> liftM2 PAlt (op p1) (op p2) PSeq p1 p2 -> liftM2 PSeq (op p1) (op p2) PMSeq (_,p1) (_,p2) -> liftM2 PSeq (op p1) (op p2) -- information loss PRep p -> liftM PRep (op p) _ -> return patt -- covers cases without subpatterns collectOp :: Monoid m => (Term -> m) -> Term -> m collectOp co trm = case trm of App c a -> co c <> co a Abs _ _ b -> co b Prod _ _ a b -> co a <> co b S c a -> co c <> co a Table a c -> co a <> co c ExtR a c -> co a <> co c R r -> mconcatMap (\ (_,(mt,a)) -> maybe mempty co mt <> co a) r RecType r -> mconcatMap (co . snd) r P t i -> co t T _ cc -> mconcatMap (co . snd) cc -- not from patterns --- nor from type annot V _ cc -> mconcatMap co cc --- nor from type annot Let (x,(mt,a)) b -> maybe mempty co mt <> co a <> co b C s1 s2 -> co s1 <> co s2 Glue s1 s2 -> co s1 <> co s2 Alts t aa -> let (x,y) = unzip aa in co t <> mconcatMap co (x <> y) FV ts -> mconcatMap co ts Strs tt -> mconcatMap co tt _ -> mempty -- covers K, Vr, Cn, Sort mconcatMap f = mconcat . map f collectPattOp :: (Patt -> [a]) -> Patt -> [a] collectPattOp op patt = case patt of PC c ps -> concatMap op ps PP qc ps -> concatMap op ps PR as -> concatMap (op.snd) as PT ty p -> op p PAs x p -> op p PImplArg p -> op p PNeg p -> op p PAlt p1 p2 -> op p1++op p2 PSeq p1 p2 -> op p1++op p2 PMSeq (_,p1) (_,p2) -> op p1++op p2 PRep p -> op p _ -> [] -- covers cases without subpatterns -- *** Misc redirectTerm :: ModuleName -> Term -> Term redirectTerm n t = case t of QC (_,f) -> QC (n,f) Q (_,f) -> Q (n,f) _ -> composSafeOp (redirectTerm n) t -- | to gather ultimate cases in a table; preserves pattern list allCaseValues :: Term -> [([Patt],Term)] allCaseValues trm = case trm of T _ cs -> [(p:ps, t) | (p,t0) <- cs, (ps,t) <- allCaseValues t0] _ -> [([],trm)] -- | to get a string from a term that represents a sequence of terminals strsFromTerm :: Term -> Err [Str] strsFromTerm t = case t of K s -> return [str s] Empty -> return [str []] C s t -> do s' <- strsFromTerm s t' <- strsFromTerm t return [plusStr x y | x <- s', y <- t'] Glue s t -> do s' <- strsFromTerm s t' <- strsFromTerm t return [glueStr x y | x <- s', y <- t'] Alts d vs -> do d0 <- strsFromTerm d v0 <- mapM (strsFromTerm . fst) vs c0 <- mapM (strsFromTerm . snd) vs --let vs' = zip v0 c0 return [strTok (str2strings def) vars | def <- d0, vars <- [[(str2strings v, map sstr c) | (v,c) <- zip vv c0] | vv <- sequence v0] ] FV ts -> mapM strsFromTerm ts >>= return . concat Strs ts -> mapM strsFromTerm ts >>= return . concat _ -> raise (render ("cannot get Str from term" <+> ppTerm Unqualified 0 t)) getTableType :: TInfo -> Err Type getTableType i = case i of TTyped ty -> return ty TComp ty -> return ty TWild ty -> return ty _ -> Bad "the table is untyped" changeTableType :: Monad m => (Type -> m Type) -> TInfo -> m TInfo changeTableType co i = case i of TTyped ty -> co ty >>= return . TTyped TComp ty -> co ty >>= return . TComp TWild ty -> co ty >>= return . TWild _ -> return i -- | to find the word items in a term wordsInTerm :: Term -> [String] wordsInTerm trm = filter (not . null) $ case trm of K s -> [s] S c _ -> wo c Alts t aa -> wo t ++ concatMap (wo . fst) aa _ -> collectOp wo trm where wo = wordsInTerm noExist :: Term noExist = FV [] defaultLinType :: Type defaultLinType = mkRecType linLabel [typeStr] -- | normalize records and record types; put s first sortRec :: [(Label,a)] -> [(Label,a)] sortRec = sortBy ordLabel where ordLabel (r1,_) (r2,_) = case (showIdent (label2ident r1), showIdent (label2ident r2)) of ("s",_) -> LT (_,"s") -> GT (s1,s2) -> compare s1 s2 -- *** Dependencies -- | dependency check, detecting circularities and returning topo-sorted list allDependencies :: (ModuleName -> Bool) -> Map.Map Ident Info -> [(Ident,[Ident])] allDependencies ism b = [(f, nub (concatMap opty (pts i))) | (f,i) <- Map.toList b] where opersIn t = case t of Q (n,c) | ism n -> [c] QC (n,c) | ism n -> [c] _ -> collectOp opersIn t opty (Just (L _ ty)) = opersIn ty opty _ = [] pts i = case i of ResOper pty pt -> [pty,pt] ResOverload _ tyts -> concat [[Just ty, Just tr] | (ty,tr) <- tyts] ResParam (Just (L loc ps)) _ -> [Just (L loc t) | (_,cont) <- ps, (_,_,t) <- cont] CncCat pty _ _ _ _ -> [pty] CncFun _ pt _ _ -> [pt] ---- (Maybe (Ident,(Context,Type)) AbsFun pty _ ptr _ -> [pty] --- ptr is def, which can be mutual AbsCat (Just (L loc co)) -> [Just (L loc ty) | (_,_,ty) <- co] _ -> [] topoSortJments :: ErrorMonad m => SourceModule -> m [(Ident,Info)] topoSortJments (m,mi) = do is <- either return (\cyc -> raise (render ("circular definitions:" <+> fsep (head cyc)))) (topoTest (allDependencies (==m) (jments mi))) return (reverse [(i,info) | i <- is, Just info <- [Map.lookup i (jments mi)]]) topoSortJments2 :: ErrorMonad m => SourceModule -> m [[(Ident,Info)]] topoSortJments2 (m,mi) = do iss <- either return (\cyc -> raise (render ("circular definitions:" <+> fsep (head cyc)))) (topoTest2 (allDependencies (==m) (jments mi))) return [[(i,info) | i<-is,Just info<-[Map.lookup i (jments mi)]] | is<-iss]