forked from GitHub/gf-core
339 lines
11 KiB
Haskell
339 lines
11 KiB
Haskell
----------------------------------------------------------------------
|
|
-- |
|
|
-- Module : Rename
|
|
-- Maintainer : AR
|
|
-- Stability : (stable)
|
|
-- Portability : (portable)
|
|
--
|
|
-- > CVS $Date: 2005/05/30 18:39:44 $
|
|
-- > CVS $Author: aarne $
|
|
-- > CVS $Revision: 1.19 $
|
|
--
|
|
-- AR 14\/5\/2003
|
|
-- The top-level function 'renameGrammar' does several things:
|
|
--
|
|
-- - extends each module symbol table by indirections to extended module
|
|
--
|
|
-- - changes unqualified and as-qualified imports to absolutely qualified
|
|
--
|
|
-- - goes through the definitions and resolves names
|
|
--
|
|
-- Dependency analysis between modules has been performed before this pass.
|
|
-- Hence we can proceed by @fold@ing "from left to right".
|
|
-----------------------------------------------------------------------------
|
|
|
|
module GF.Compile.Rename (renameGrammar,
|
|
renameSourceTerm,
|
|
renameModule
|
|
) where
|
|
|
|
import GF.Grammar.Grammar
|
|
import GF.Grammar.Values
|
|
import GF.Grammar.Predef
|
|
import GF.Infra.Modules
|
|
import GF.Infra.Ident
|
|
import GF.Grammar.Macros
|
|
import GF.Grammar.PrGrammar
|
|
import GF.Grammar.AppPredefined
|
|
import GF.Grammar.Lookup
|
|
import GF.Compile.Extend
|
|
import GF.Data.Operations
|
|
|
|
import Control.Monad
|
|
import Data.List (nub)
|
|
import Debug.Trace (trace)
|
|
|
|
renameGrammar :: SourceGrammar -> Err SourceGrammar
|
|
renameGrammar g = liftM (MGrammar . reverse) $ foldM renameModule [] (modules g)
|
|
|
|
-- | this gives top-level access to renaming term input in the cc command
|
|
renameSourceTerm :: SourceGrammar -> Ident -> Term -> Err Term
|
|
renameSourceTerm g m t = do
|
|
mo <- lookupErr m (modules g)
|
|
status <- buildStatus g m mo
|
|
renameTerm status [] t
|
|
|
|
renameModule :: [SourceModule] -> SourceModule -> Err [SourceModule]
|
|
renameModule ms (name,mod) = errIn ("renaming module" +++ prt name) $ case mod of
|
|
ModMod mo -> do
|
|
let js1 = jments mo
|
|
status <- buildStatus (MGrammar ms) name mod
|
|
js2 <- mapsErrTree (renameInfo mo status) js1
|
|
let mod2 = ModMod $ mo {opens = map forceQualif (opens mo), jments = js2}
|
|
return $ (name,mod2) : ms
|
|
|
|
type Status = (StatusTree, [(OpenSpec Ident, StatusTree)])
|
|
|
|
type StatusTree = BinTree Ident StatusInfo
|
|
|
|
type StatusInfo = Ident -> Term
|
|
|
|
renameIdentTerm :: Status -> Term -> Err Term
|
|
renameIdentTerm env@(act,imps) t =
|
|
errIn ("atomic term" +++ prt t +++ "given" +++ unwords (map (prt . fst) qualifs)) $
|
|
case t of
|
|
Vr c -> ident predefAbs c
|
|
Cn c -> ident (\_ s -> Bad s) c
|
|
Q m' c | m' == cPredef {- && isInPredefined c -} -> return t
|
|
Q m' c -> do
|
|
m <- lookupErr m' qualifs
|
|
f <- lookupTree prt c m
|
|
return $ f c
|
|
QC m' c | m' == cPredef {- && isInPredefined c -} -> return t
|
|
QC m' c -> do
|
|
m <- lookupErr m' qualifs
|
|
f <- lookupTree prt c m
|
|
return $ f c
|
|
_ -> return t
|
|
where
|
|
opens = [st | (OSimple _ _,st) <- imps]
|
|
qualifs = [(m, st) | (OQualif _ m _, st) <- imps] ++
|
|
[(m, st) | (OSimple _ m, st) <- imps] -- qualif is always possible
|
|
|
|
-- this facility is mainly for BWC with GF1: you need not import PredefAbs
|
|
predefAbs c s
|
|
| isPredefCat c = return $ Q cPredefAbs c
|
|
| otherwise = Bad s
|
|
|
|
ident alt c = case lookupTree prt c act of
|
|
Ok f -> return $ f c
|
|
_ -> case lookupTreeManyAll prt opens c of
|
|
[f] -> return $ f c
|
|
[] -> alt c ("constant not found:" +++ prt c)
|
|
fs -> case nub [f c | f <- fs] of
|
|
[tr] -> return tr
|
|
ts@(t:_) -> trace ("WARNING: conflict" +++ unwords (map prt ts)) (return t)
|
|
-- a warning will be generated in CheckGrammar, and the head returned
|
|
-- in next V:
|
|
-- Bad $ "conflicting imports:" +++ unwords (map prt ts)
|
|
|
|
|
|
--- | would it make sense to optimize this by inlining?
|
|
renameIdentPatt :: Status -> Patt -> Err Patt
|
|
renameIdentPatt env p = do
|
|
let t = patt2term p
|
|
t' <- renameIdentTerm env t
|
|
term2patt t'
|
|
|
|
info2status :: Maybe Ident -> (Ident,Info) -> (Ident,StatusInfo)
|
|
info2status mq (c,i) = (c, case i of
|
|
AbsFun _ (Yes EData) -> maybe Con QC mq
|
|
ResValue _ -> maybe Con QC mq
|
|
ResParam _ -> maybe Con QC mq
|
|
AnyInd True m -> maybe Con (const (QC m)) mq
|
|
AnyInd False m -> maybe Cn (const (Q m)) mq
|
|
_ -> maybe Cn Q mq
|
|
)
|
|
|
|
tree2status :: OpenSpec Ident -> BinTree Ident Info -> BinTree Ident StatusInfo
|
|
tree2status o = case o of
|
|
OSimple _ i -> mapTree (info2status (Just i))
|
|
OQualif _ i j -> mapTree (info2status (Just j))
|
|
|
|
buildStatus :: SourceGrammar -> Ident -> SourceModInfo -> Err Status
|
|
buildStatus gr c mo = let mo' = self2status c mo in case mo of
|
|
ModMod m -> do
|
|
let gr1 = MGrammar $ (c,mo) : modules gr
|
|
ops = [OSimple OQNormal e | e <- allExtends gr1 c] ++ allOpens m
|
|
mods <- mapM (lookupModule gr1 . openedModule) ops
|
|
let sts = map modInfo2status $ zip ops mods
|
|
return $ if isModCnc m
|
|
then (emptyBinTree, reverse sts) -- the module itself does not define any names
|
|
else (mo',reverse sts) -- so the empty ident is not needed
|
|
|
|
modInfo2status :: (OpenSpec Ident,SourceModInfo) -> (OpenSpec Ident, StatusTree)
|
|
modInfo2status (o,i) = (o,case i of
|
|
ModMod m -> tree2status o (jments m)
|
|
)
|
|
|
|
self2status :: Ident -> SourceModInfo -> StatusTree
|
|
self2status c i = mapTree (info2status (Just c)) js where -- qualify internal
|
|
js = case i of
|
|
ModMod m
|
|
| isModTrans m -> sorted2tree $ filter noTrans $ tree2list $ jments m
|
|
| otherwise -> jments m
|
|
noTrans (_,d) = case d of -- to enable other than transfer js in transfer module
|
|
AbsTrans _ -> False
|
|
_ -> True
|
|
|
|
forceQualif o = case o of
|
|
OSimple q i -> OQualif q i i
|
|
OQualif q _ i -> OQualif q i i
|
|
|
|
renameInfo :: Module Ident Info -> Status -> (Ident,Info) -> Err (Ident,Info)
|
|
renameInfo mo status (i,info) = errIn
|
|
("renaming definition of" +++ prt i +++ showPosition mo i) $
|
|
liftM ((,) i) $ case info of
|
|
AbsCat pco pfs -> liftM2 AbsCat (renPerh (renameContext status) pco)
|
|
(renPerh (mapM rent) pfs)
|
|
AbsFun pty ptr -> liftM2 AbsFun (ren pty) (ren ptr)
|
|
AbsTrans f -> liftM AbsTrans (rent f)
|
|
|
|
ResOper pty ptr -> liftM2 ResOper (ren pty) (ren ptr)
|
|
ResOverload os tysts ->
|
|
liftM (ResOverload os) (mapM (pairM rent) tysts)
|
|
|
|
ResParam (Yes (pp,m)) -> do
|
|
pp' <- mapM (renameParam status) pp
|
|
return $ ResParam $ Yes (pp',m)
|
|
ResValue (Yes (t,m)) -> do
|
|
t' <- rent t
|
|
return $ ResValue $ Yes (t',m)
|
|
CncCat pty ptr ppr -> liftM3 CncCat (ren pty) (ren ptr) (ren ppr)
|
|
CncFun mt ptr ppr -> liftM2 (CncFun mt) (ren ptr) (ren ppr)
|
|
_ -> return info
|
|
where
|
|
ren = renPerh rent
|
|
rent = renameTerm status []
|
|
|
|
renPerh ren pt = case pt of
|
|
Yes t -> liftM Yes $ ren t
|
|
_ -> return pt
|
|
|
|
renameTerm :: Status -> [Ident] -> Term -> Err Term
|
|
renameTerm env vars = ren vars where
|
|
ren vs trm = case trm of
|
|
Abs x b -> liftM (Abs x) (ren (x:vs) b)
|
|
Prod x a b -> liftM2 (Prod x) (ren vs a) (ren (x:vs) b)
|
|
Typed a b -> liftM2 Typed (ren vs a) (ren vs b)
|
|
Vr x
|
|
| elem x vs -> return trm
|
|
| otherwise -> renid trm
|
|
Cn _ -> renid trm
|
|
Con _ -> renid trm
|
|
Q _ _ -> renid trm
|
|
QC _ _ -> renid trm
|
|
Eqs eqs -> liftM Eqs $ mapM (renameEquation env vars) eqs
|
|
T i cs -> do
|
|
i' <- case i of
|
|
TTyped ty -> liftM TTyped $ ren vs ty -- the only annotation in source
|
|
_ -> return i
|
|
liftM (T i') $ mapM (renCase vs) cs
|
|
|
|
Let (x,(m,a)) b -> do
|
|
m' <- case m of
|
|
Just ty -> liftM Just $ ren vs ty
|
|
_ -> return m
|
|
a' <- ren vs a
|
|
b' <- ren (x:vs) b
|
|
return $ Let (x,(m',a')) b'
|
|
|
|
P t@(Vr r) l -- for constant t we know it is projection
|
|
| elem r vs -> return trm -- var proj first
|
|
| otherwise -> case renid (Q r (label2ident l)) of -- qualif second
|
|
Ok t -> return t
|
|
_ -> case liftM (flip P l) $ renid t of
|
|
Ok t -> return t -- const proj last
|
|
_ -> prtBad "unknown qualified constant" trm
|
|
|
|
EPatt p -> do
|
|
(p',_) <- renpatt p
|
|
return $ EPatt p'
|
|
|
|
_ -> composOp (ren vs) trm
|
|
|
|
renid = renameIdentTerm env
|
|
renCase vs (p,t) = do
|
|
(p',vs') <- renpatt p
|
|
t' <- ren (vs' ++ vs) t
|
|
return (p',t')
|
|
renpatt = renamePattern env
|
|
|
|
-- | vars not needed in env, since patterns always overshadow old vars
|
|
renamePattern :: Status -> Patt -> Err (Patt,[Ident])
|
|
renamePattern env patt = case patt of
|
|
|
|
PMacro c -> do
|
|
c' <- renid $ Vr c
|
|
case c' of
|
|
Q p d -> renp $ PM p d
|
|
_ -> prtBad "unresolved pattern" patt
|
|
|
|
PC c ps -> do
|
|
c' <- renameIdentTerm env $ Cn c
|
|
case c' of
|
|
QC p d -> renp $ PP p d ps
|
|
-- Q p d -> renp $ PP p d ps --- why this? AR 15/3/2008
|
|
_ -> prtBad "unresolved pattern" c' ---- (PC c ps', concat vs)
|
|
|
|
PP p c ps -> do
|
|
|
|
(p', c') <- case renameIdentTerm env (QC p c) of
|
|
Ok (QC p' c') -> return (p',c')
|
|
_ -> return (p,c) --- temporarily, for bw compat
|
|
psvss <- mapM renp ps
|
|
let (ps',vs) = unzip psvss
|
|
return (PP p' c' ps', concat vs)
|
|
|
|
PM p c -> do
|
|
(p', c') <- case renameIdentTerm env (Q p c) of
|
|
Ok (Q p' c') -> return (p',c')
|
|
_ -> prtBad "not a pattern macro" patt
|
|
return (PM p' c', [])
|
|
|
|
PV x -> case renid (Vr x) of
|
|
Ok (QC m c) -> return (PP m c [],[])
|
|
_ -> return (patt, [x])
|
|
|
|
PR r -> do
|
|
let (ls,ps) = unzip r
|
|
psvss <- mapM renp ps
|
|
let (ps',vs') = unzip psvss
|
|
return (PR (zip ls ps'), concat vs')
|
|
|
|
PAlt p q -> do
|
|
(p',vs) <- renp p
|
|
(q',ws) <- renp q
|
|
return (PAlt p' q', vs ++ ws)
|
|
|
|
PSeq p q -> do
|
|
(p',vs) <- renp p
|
|
(q',ws) <- renp q
|
|
return (PSeq p' q', vs ++ ws)
|
|
|
|
PRep p -> do
|
|
(p',vs) <- renp p
|
|
return (PRep p', vs)
|
|
|
|
PNeg p -> do
|
|
(p',vs) <- renp p
|
|
return (PNeg p', vs)
|
|
|
|
PAs x p -> do
|
|
(p',vs) <- renp p
|
|
return (PAs x p', x:vs)
|
|
|
|
_ -> return (patt,[])
|
|
|
|
where
|
|
renp = renamePattern env
|
|
renid = renameIdentTerm env
|
|
|
|
renameParam :: Status -> (Ident, Context) -> Err (Ident, Context)
|
|
renameParam env (c,co) = do
|
|
co' <- renameContext env co
|
|
return (c,co')
|
|
|
|
renameContext :: Status -> Context -> Err Context
|
|
renameContext b = renc [] where
|
|
renc vs cont = case cont of
|
|
(x,t) : xts
|
|
| isWildIdent x -> do
|
|
t' <- ren vs t
|
|
xts' <- renc vs xts
|
|
return $ (x,t') : xts'
|
|
| otherwise -> do
|
|
t' <- ren vs t
|
|
let vs' = x:vs
|
|
xts' <- renc vs' xts
|
|
return $ (x,t') : xts'
|
|
_ -> return cont
|
|
ren = renameTerm b
|
|
|
|
-- | vars not needed in env, since patterns always overshadow old vars
|
|
renameEquation :: Status -> [Ident] -> Equation -> Err Equation
|
|
renameEquation b vs (ps,t) = do
|
|
(ps',vs') <- liftM unzip $ mapM (renamePattern b) ps
|
|
t' <- renameTerm b (concat vs' ++ vs) t
|
|
return (ps',t')
|