---------------------------------------------------------------------- -- | -- Module : GF.Compile.Concrete.Compute -- Maintainer : AR -- Stability : (stable) -- Portability : (portable) -- -- > CVS $Date: 2005/11/01 15:39:12 $ -- > CVS $Author: aarne $ -- > CVS $Revision: 1.19 $ -- -- Computation of source terms. Used in compilation and in @cc@ command. ----------------------------------------------------------------------------- module GF.Compile.Compute.ConcreteLazy (computeConcrete, computeTerm,checkPredefError) where import GF.Data.Operations import GF.Grammar.Grammar import GF.Infra.Ident import GF.Infra.Option import GF.Data.Str import GF.Grammar.ShowTerm import GF.Grammar.Printer import GF.Grammar.Predef import GF.Grammar.Macros import GF.Grammar.Lookup import GF.Compile.Refresh import GF.Grammar.PatternMatch import GF.Grammar.Lockfield (isLockLabel,unlockRecord) ---- import GF.Compile.Compute.AppPredefined import Data.List (nub,intersperse) import Control.Monad (liftM2, liftM) import Control.Monad.Identity import Text.PrettyPrint ----import Debug.Trace --type Comp a = Err a -- makes computations (hyper)strict --errr = id type Comp a = Identity a -- inherit Haskell's laziness errr = err runtime_error return -- convert interpreter error to run-time error no_error = err fail return -- failure caused by interpreter/type checker bug (?) runtime_error = return . Error -- run-time error term -- | computation of concrete syntax terms into normal form -- used mainly for partial evaluation computeConcrete :: SourceGrammar -> Term -> Err Term computeConcrete g t = {- refreshTerm t >>= -} computeTerm g [] t computeTerm :: SourceGrammar -> Substitution -> Term -> Err Term computeTerm gr g = return . runIdentity . computeTermOpt gr g computeTermOpt :: SourceGrammar -> Substitution -> Term -> Comp Term computeTermOpt gr = comput True where -- full = True means full evaluation under Abs comput full g t = ---- errIn ("subterm" +++ prt t) $ --- for debugging --trace ("comput "++show (map fst g)++" "++take 65 (show t)) $ case t of Q (p,c) | p == cPredef -> return t -- qualified constant | otherwise -> look (p,c) Vr x -> do -- local variable t' <- maybe (fail (render (text "no value given to variable" <+> ppIdent x))) return $ lookup x g case t' of _ | t == t' -> return t _ -> comp g t' --- why compute again? AR 25/8/2011 -- Abs x@(IA _) b -> do Abs _ _ _ | full -> do -- \xs -> b let (xs,b1) = termFormCnc t b' <- comp ([(x,Vr x) | (_,x) <- xs] ++ g) b1 return $ mkAbs xs b' -- b' <- comp (ext x (Vr x) g) b -- return $ Abs x b' Abs _ _ _ -> return t -- hnf Let (x,(ty,a)) b -> do -- let x : ty = a in b a' <- comp g a comp (ext x a' g) b {- -- trying to prevent Let expansion with non-evaluated exps. AR 19/8/2011 Let (x,(ty,a)) b -> do a' <- comp g a let ea' = checkNoArgVars a' case ea' of Ok v -> comp (ext x v g) b _ -> return $ Let (x,(ty,a')) b -} Prod b x a t -> do -- (x : a) -> t ; b for hiding a' <- comp g a t' <- comp (ext x (Vr x) g) t return $ Prod b x a' t' -- beta-convert: simultaneous for as many arguments as possible App f a -> case appForm t of -- (f a) --> (h as) (h,as) | length as > 1 -> do h' <- hnf g h as' <- mapM (comp g) as case h' of Error{} -> return h' _ | not (null [() | FV _ <- as']) -> compApp g (mkApp h' as') c@(QC _) -> do return $ mkApp c as' Q (mod,f) | mod == cPredef -> case appPredefined (mkApp h' as') of Ok (t',b) -> if b then return t' else comp g t' Bad s -> runtime_error s Abs _ _ _ -> do let (xs,b) = termFormCnc h' let g' = (zip (map snd xs) as') ++ g let as2 = drop (length xs) as' let xs2 = drop (length as') xs b' <- comp g' (mkAbs xs2 b) if null as2 then return b' else comp g (mkApp b' as2) _ -> compApp g (mkApp h' as') _ -> compApp g t P t l | isLockLabel l -> return $ R [] -- t.lock_C ---- a workaround 18/2/2005: take this away and find the reason ---- why earlier compilation destroys the lock field P t l -> do -- t.l t' <- comp g t case t' of Error{} -> return t' FV rs -> mapM (\c -> comp g (P c l)) rs >>= returnC . variants -- (r| r').l R r -> project l r --{...}.l ExtR a (R b) -> -- (a ** {...}).l maybe (comp g (P a l)) (comp g) (try_project l b) --- { - --- this is incorrect, since b can contain the proper value ExtR (R a) b -> -- NOT POSSIBLE both a and b records! maybe (comp g (P b l)) (comp g) (try_project l a) --- - } --- S (T i cs) e -> prawitz g i (flip P l) cs e -- ((table i branches) ! e).l S (V i cs) e -> prawitzV g i (flip P l) cs e -- ((table i values) ! e).l _ -> returnC $ P t' l S t v -> do -- t ! v t' <- compTable g t v' <- comp g v t1 <- case t' of ---- V (RecType fs) _ -> uncurrySelect g fs t' v' ---- T (TComp (RecType fs)) _ -> uncurrySelect g fs t' v' _ -> return $ S t' v' compSelect g t1 -- normalize away empty tokens K "" -> return Empty -- [] -- glue if you can Glue x0 y0 -> do -- x0 + y0 x <- comp g x0 y <- comp g y0 case (x,y) of (Error{},_) -> return x (_,Error{}) -> return y (FV ks,_) -> do -- (k|k') + y kys <- mapM (comp g . flip Glue y) ks return $ variants kys (_,FV ks) -> do -- x + (k|k') xks <- mapM (comp g . Glue x) ks return $ variants xks (S (T i cs) e, s) -> prawitz g i (flip Glue s) cs e -- (table cs ! e) + s (s, S (T i cs) e) -> prawitz g i (Glue s) cs e -- s + (table cs ! e) (S (V i cs) e, s) -> prawitzV g i (flip Glue s) cs e -- same with values (s, S (V i cs) e) -> prawitzV g i (Glue s) cs e (_,Empty) -> return x -- x + [] (Empty,_) -> return y (K a, K b) -> return $ K (a ++ b) -- "foo" + "bar" (_, Alts d vs) -> do -- x + pre {...} ---- (K a, Alts (d,vs)) -> do let glx = Glue x comp g $ Alts (glx d) [(glx v,c) | (v,c) <- vs] (Alts _ _, ka) -> errr $ checks [do -- pre {...} + ka y' <- strsFromTerm ka ---- (Alts _, K a) -> checks [do x' <- strsFromTerm x -- this may fail when compiling opers return $ variants [ foldr1 C (map K (str2strings (glueStr v u))) | v <- x', u <- y'] ---- foldr1 C (map K (str2strings (glueStr v (str a)))) | v <- x'] ,return $ Glue x y ] (C u v,_) -> comp g $ C u (Glue v y) -- (u ++ v) + y _ -> do mapM_ checkNoArgVars [x,y] r <- composOp (comp g) t returnC r Alts d aa -> do -- pre {...} d' <- comp g d aa' <- mapM (compInAlts g) aa returnC (Alts d' aa') -- remove empty C a b -> do -- a ++ b a0 <- comp g a b0 <- comp g b let (a',b') = strForm (C a0 b0) case (a',b') of (Error{},_) -> return a' (_,Error{}) -> return b' (Alts _ _, K d) -> errr $ checks [do -- pre {...} ++ "d" as <- strsFromTerm a' -- this may fail when compiling opers return $ variants [ foldr1 C (map K (str2strings (plusStr v (str d)))) | v <- as] , return $ C a' b' ] (Alts _ _, C (K d) e) -> errr $ checks [do -- pre {...} ++ ("d" ++ e) as <- strsFromTerm a' -- this may fail when compiling opers return $ C (variants [ foldr1 C (map K (str2strings (plusStr v (str d)))) | v <- as]) e , return $ C a' b' ] (Empty,_) -> returnC b' -- [] ++ b' (_,Empty) -> returnC a' -- a' ++ [] _ -> returnC $ C a' b' -- reduce free variation as much as you can FV ts -> mapM (comp g) ts >>= returnC . variants -- variants {...} -- merge record extensions if you can ExtR r s -> do -- r ** s r' <- comp g r s' <- comp g s case (r',s') of (Error{},_) -> return r' (_,Error{}) -> return s' (R rs, R ss) -> errr $ plusRecord r' s' (RecType rs, RecType ss) -> errr $ plusRecType r' s' _ -> return $ ExtR r' s' ELin c r -> do -- lin c r r' <- comp g r unlockRecord c r' T _ _ -> compTable g t -- table { ... p => t ... } V _ _ -> compTable g t -- table [ ... v ... ] -- otherwise go ahead _ -> composOp (comp g) t >>= returnC where --{...}.l project l = maybe (fail_project l) (comp g) . try_project l try_project l = fmap snd . lookup l fail_project l = fail (render (text "no value for label" <+> ppLabel l)) compApp g (App f a) = do -- (f a) f' <- hnf g f a' <- comp g a case (f',a') of (Error{},_) -> return f' (Abs _ x b, FV as) -> -- (\x -> b) (variants {...}) liftM variants $ mapM (\c -> comp (ext x c g) b) as (_, FV as) -> liftM variants $ mapM (\c -> comp g (App f' c)) as (FV fs, _) -> liftM variants $ mapM (\c -> comp g (App c a')) fs (Abs _ x b,_) -> comp (ext x a' g) b -- (\x -> b) a -- normal beta conv. (QC _,_) -> returnC $ App f' a' -- (C a') -- constructor application (S (T i cs) e,_) -> prawitz g i (flip App a') cs e -- (table cs ! e) a' (S (V i cs) e,_) -> prawitzV g i (flip App a') cs e _ -> case appPredefined (App f' a') of Ok (t',b) -> if b then return t' else comp g t' Bad s -> runtime_error s hnf, comp :: Substitution -> Term -> Comp Term hnf = comput False comp = comput True look c = errr (lookupResDef gr c) {- -- This seems to loop in the greek example: look c = --trace ("look "++show c) $ optcomp =<< errr (lookupResDef gr c) where optcomp t = if t==Q c then --trace "looking up undefined oper" $ return t else comp [] t -- g or []? -} ext x a g = (x,a):g -- extend environment with new variable and its value returnC = return --- . computed variants ts = case nub ts of [t] -> t ts -> FV ts isCan v = case v of -- is canonical (and should be matched by a pattern) Con _ -> True QC _ -> True App f a -> isCan f && isCan a R rs -> all (isCan . snd . snd) rs _ -> False compPatternMacro p = case p of PM c -> case look c of Identity (EPatt p') -> compPatternMacro p' -- _ -> fail (render (text "pattern expected as value of" $$ nest 2 (ppPatt Unqualified 0 p))) PAs x p -> do p' <- compPatternMacro p return $ PAs x p' PAlt p q -> do p' <- compPatternMacro p q' <- compPatternMacro q return $ PAlt p' q' PSeq p q -> do p' <- compPatternMacro p q' <- compPatternMacro q return $ PSeq p' q' PRep p -> do p' <- compPatternMacro p return $ PRep p' PNeg p -> do p' <- compPatternMacro p return $ PNeg p' PR rs -> do rs' <- mapPairsM compPatternMacro rs return $ PR rs' _ -> return p compSelect g (S t' v') = case v' of -- t' ! v' FV vs -> mapM (\c -> comp g (S t' c)) vs >>= returnC . variants ---- S (T i cs) e -> prawitz g i (S t') cs e -- AR 8/7/2010 sometimes better ---- S (V i cs) e -> prawitzV g i (S t') cs e -- sometimes much worse _ -> case t' of Error{} -> return t' FV ccs -> mapM (\c -> comp g (S c v')) ccs >>= returnC . variants T _ [(PW,c)] -> comp g c -- (\\_ => c) ! v' T _ [(PT _ PW,c)] -> comp g c -- (\\(_ : typ) => c) ! v' T _ [(PV z,c)] -> comp (ext z v' g) c -- (\\z => c) ! v' T _ [(PT _ (PV z),c)] -> comp (ext z v' g) c -- course-of-values table: look up by index, no pattern matching needed V ptyp ts -> do -- (table [...ts...]) ! v' vs <- no_error $ allParamValues gr ptyp case lookupR v' (zip vs [0 .. length vs - 1]) of Just i -> comp g $ ts !! i _ -> return $ S t' v' -- if v' is not canonical T _ cc -> do -- (table {...cc...}) ! v' case matchPattern cc v' of Ok (c,g') -> comp (g' ++ g) c _ | isCan v' -> fail (render (text "missing case" <+> ppTerm Unqualified 0 v' <+> text "in" <+> ppTerm Unqualified 0 t)) _ -> return $ S t' v' -- if v' is not canonical S (T i cs) e -> prawitz g i (flip S v') cs e -- (table {...cs...} ! e) ! v' S (V i cs) e -> prawitzV g i (flip S v') cs e _ -> returnC $ S t' v' --- needed to match records with and without type information ---- todo: eliminate linear search in a list of records! lookupR v vs = case v of R rs -> lookup ([(x,y) | (x,(_,y)) <- rs]) [([(x,y) | (x,(_,y)) <- rs],v) | (R rs,v) <- vs] _ -> lookup v vs -- case-expand tables: branches for every value of argument type -- if already expanded, don't expand again compTable g t = case t of T i@(TComp ty) cs -> do -- if there are no variables, don't even go inside cs' <- if (null g) then return cs else mapPairsM (comp g) cs ---- return $ V ty (map snd cs') return $ T i cs' V ty cs -> do ty' <- comp g ty -- if there are no variables, don't even go inside cs' <- if (null g) then return cs else mapM (comp g) cs return $ V ty' cs' T i cs -> do pty0 <- errr $ getTableType i ptyp <- comp g pty0 case allParamValues gr ptyp of Ok vs0 -> do let vs = vs0 ---- [Val v ptyp i | (v,i) <- zip vs0 [0..]] ps0 <- mapM (compPatternMacro . fst) cs cs' <- mapM (compBranchOpt g) (zip ps0 (map snd cs)) sts <- no_error $ mapM (matchPattern cs') vs ts <- mapM (\ (c,g') -> comp (g' ++ g) c) sts ps <- no_error $ mapM term2patt vs let ps' = ps --- PT ptyp (head ps) : tail ps ---- return $ V ptyp ts -- to save space, just course of values return $ T (TComp ptyp) (zip ps' ts) _ -> do ps0 <- mapM (compPatternMacro . fst) cs cs' <- mapM (compBranch g) (zip ps0 (map snd cs)) ----- cs' <- return (zip ps0 (map snd cs)) --- probably right AR 22/8/2011 but can leave uninstantiated variables :-( ---- cs' <- mapM (compBranch g) cs return $ T i cs' -- happens with variable types _ -> comp g t compBranch g (p,v) = do -- compute a branch in a table let g' = contP p ++ g -- add the pattern's variables to environment v' <- comp g' v return (p,v') compBranchOpt g c@(p,v) = case contP p of [] -> return c _ -> {-err (const (return c)) return $-} compBranch g c -- collect the context of variables of a pattern contP p = case p of PV x -> [(x,Vr x)] PC _ ps -> concatMap contP ps PP _ ps -> concatMap contP ps PT _ p -> contP p PR rs -> concatMap (contP . snd) rs PAs x p -> (x,Vr x) : contP p PSeq p q -> concatMap contP [p,q] PAlt p q -> concatMap contP [p,q] PRep p -> contP p PNeg p -> contP p _ -> [] prawitz g i f cs e = do cs' <- mapM (compBranch g) [(p, f v) | (p,v) <- cs] return $ S (T i cs') e prawitzV g i f cs e = do cs' <- mapM (comp g) [(f v) | v <- cs] return $ S (V i cs') e compInAlts g (v,c) = do v' <- comp g v c' <- comp g c c2 <- case c' of EPatt p -> liftM Strs $ getPatts p _ -> return c' return (v',c2) where getPatts p = case p of PAlt a b -> liftM2 (++) (getPatts a) (getPatts b) PString s -> return [K s] PSeq a b -> do as <- getPatts a bs <- getPatts b return [K (s ++ t) | K s <- as, K t <- bs] _ -> fail (render (text "not valid pattern in pre expression" <+> ppPatt Unqualified 0 p)) strForm s = case s of C (C a b) c -> let (a1,a2) = strForm a in (a1, ccStr a2 (ccStr b c)) C a b -> (a,b) _ -> (s,Empty) ccStr a b = case (a,b) of (Empty,_) -> b (_,Empty) -> a _ -> C a b {- ---- uncurrySelect g fs t v = do ts <- mapM (allParamValues gr . snd) fs vs <- mapM (comp g) [P v r | r <- map fst fs] return $ reorderSelect t fs ts vs reorderSelect t fs pss vs = case (t,fs,pss,vs) of (V _ ts, f:fs1, ps:pss1, v:vs1) -> S (V (snd f) [reorderSelect (V (RecType fs1) t) fs1 pss1 vs1 | t <- segments (length ts `div` length ps) ts]) v (T (TComp _) cs, f:fs1, ps:pss1, v:vs1) -> S (T (TComp (snd f)) [(p,reorderSelect (T (TComp (RecType fs1)) c) fs1 pss1 vs1) | (ep,c) <- zip ps (segments (length cs `div` length ps) cs), let Ok p = term2patt ep]) v _ -> t segments i xs = let (x0,xs1) = splitAt i xs in x0 : takeWhile (not . null) (segments i xs1) -} -- | argument variables cannot be glued checkNoArgVars :: Term -> Comp Term checkNoArgVars t = case t of Vr (IA _ _) -> fail $ glueErrorMsg $ ppTerm Unqualified 0 t Vr (IAV _ _ _) -> fail $ glueErrorMsg $ ppTerm Unqualified 0 t _ -> composOp checkNoArgVars t glueErrorMsg s = render (text "Cannot glue (+) term with run-time variable" <+> s <> char '.' $$ text "Use Prelude.bind instead.") getArgType t = case t of V ty _ -> return ty T (TComp ty) _ -> return ty _ -> fail (render (text "cannot get argument type of table" $$ nest 2 (ppTerm Unqualified 0 t))) checkPredefError :: SourceGrammar -> Term -> Err Term checkPredefError sgr t = case t of Error s -> fail ("Error: "++s) _ -> return t {- -- Old checkPredefError sgr t = case t of App (Q (mod,f)) s | mod == cPredef && f == cError -> fail $ showTerm sgr TermPrintOne Unqualified s _ -> composOp (checkPredefError sgr) t predef_error s = App (Q (cPredef,cError)) (K s) -}