Files
gf-core/src/compiler/api/GF/Compile/GeneratePMCFG.hs
T
2026-06-05 10:25:11 +02:00

539 lines
22 KiB
Haskell

{-# LANGUAGE RankNTypes #-}
module GF.Compile.GeneratePMCFG
(generatePMCFG, pmcfgForm, type2fields
) where
import GF.Infra.Ident
import GF.Infra.Option
import GF.Infra.CheckM
import GF.Data.Operations
import GF.Grammar.Grammar
import GF.Grammar.Lookup
import GF.Grammar.Macros
import GF.Grammar.Predef
import GF.Grammar.Printer hiding (ppValue)
import GF.Text.Pretty hiding (empty)
import GF.Compile.Compute hiding ( getMeta, setMeta, globals, variants )
import qualified GF.Text.Pretty as PP
import qualified Data.Map as Map
import qualified Data.Set as Set
import Control.Applicative
import Control.Monad (foldM,zipWithM,liftM,liftM2,forM,MonadPlus(..))
import Control.Monad.Fix
import Data.Maybe
import Data.List(mapAccumL,sortBy,sortOn,intersperse)
import Data.Containers.ListUtils(nubOrd)
import Prelude hiding ((<>))
generatePMCFG :: Options -> FilePath -> SourceGrammar -> SourceModule -> Check SourceModule
generatePMCFG opts cwd gr cmo@(cm,cmi)
| mstatus cmi == MSComplete && isModCnc cmi =
do let gr' = prependModule gr cmo
g = Gl gr' (stdPredef g) False
js <- Map.traverseWithKey (addPMCFG cwd g cmi) (jments cmi)
return (cm,cmi{jments = js})
| otherwise = return cmo
addPMCFG cwd g cmi id (CncCat mty@(Just (L loc ty)) mdef mref mprn Nothing) = do
defs <- case mdef of
Nothing -> checkInModule cwd cmi loc ("Happened in the rule generation for the lindef of" <+> id) $ do
t <- mkLinDefault sgr ty
pmcfgForm g t [(Explicit,identW,Sort cStr)] ty
Just (L loc t) -> checkInModule cwd cmi loc ("Happened in the PMCFG generation for the lindef of" <+> id) $ do
pmcfgForm g t [(Explicit,identW,Sort cStr)] ty
refs <- case mref of
Nothing -> checkInModule cwd cmi loc ("Happened in the rule generation for the linref of" <+> id) $ do
t <- mkLinReference sgr ty
pmcfgForm g t [(Explicit,identW,ty)] (Sort cStr)
Just (L loc t) -> checkInModule cwd cmi loc ("Happened in the PMCFG generation for the linref of" <+> id) $ do
pmcfgForm g t [(Explicit,identW,ty)] (Sort cStr)
mprn <- case mprn of
Nothing -> return Nothing
Just (L loc prn) -> checkInModule cwd cmi loc ("Happened in the computation of the print name for" <+> id) $ do
prn <- normalForm g prn
return (Just (L loc prn))
return (CncCat mty mdef mref mprn (Just (defs,refs)))
where
Gl sgr _ _ = g
addPMCFG cwd g cmi id (CncFun (Just lty@(cats,cat,ctxt,ty)) mlin@(Just (L loc term)) mprn Nothing) = do
rules <- checkInModule cwd cmi loc ("Happened in the rule generation for" <+> id) $
pmcfgForm g term ctxt ty
mprn <- case mprn of
Nothing -> return Nothing
Just (L loc prn) -> checkInModule cwd cmi loc ("Happened in the computation of the print name for" <+> id) $ do
prn <- normalForm g prn
return (Just (L loc prn))
return (CncFun (Just lty) mlin mprn (Just rules))
where
Gl sgr _ _ = g
addPMCFG cwd g cmi id info = return info
pmcfgForm g t ctxt ty = do
let (ms,s',t',arg_params) = apply 0 Map.empty unit ctxt t []
let v = eval g [] s' t' []
(ms,_,_,fn) <- breakDown g ms unit 0 [] v ty (return []) empty
res <- fmap nubOrd $ runGenM g ms [] $ do
(r,rs,v,res_params) <- fn
(subst,arg_params) <- mapAccumM params2int Map.empty arg_params
(subst,res_params) <- params2int subst res_params
(subst,lin_idx) <- params2int' subst r rs
(subst,seq) <- flatten subst v
qs <- quantifiers (Map.toList subst)
return (Rule qs res_params arg_params lin_idx seq)
length res `seq` return res
where
Gl sgr _ _ = g
quantifiers vars = GenM (\(Gl sgr _ _) k svs ms ->
k [boundsOf sgr ms variable | (variable,v) <- sortOn snd vars]
svs ms)
where
boundsOf sgr ms i =
case Map.lookup i ms of
Just (Narrowing _ pty) -> case countParamValues sgr pty of
Ok c -> c
Bad msg -> error msg
_ -> error (show (ppLVar i <+> "is not a free variable"))
apply d ms s [] t args = (ms,s,t,reverse args)
apply d ms s ((_,_,ty):ctxt) t args =
let (ms',s',_,t2,params) = type2metaTerm sgr d ms s 0 [] ty []
in apply (d+1) ms' s' ctxt (App t t2) (params:args)
type2fields :: SourceGrammar -> Type -> [String]
type2fields gr = type2fields PP.empty
where
type2fields d (Sort s) | s == cStr = [show d]
type2fields d (RecType lbls) =
concatMap (\(lbl,_,ty) -> type2fields (d <+> pp lbl) ty) lbls
type2fields d (Table p q) =
let Ok ts = allParamValues gr p
in concatMap (\t -> type2fields (d <+> ppTerm Unqualified 5 t) q) ts
type2fields d _ = []
mkLinDefault :: SourceGrammar -> Type -> Check Term
mkLinDefault gr typ = liftM (Abs Explicit varStr) $ mkDefField typ
where
mkDefField ty =
case ty of
Table p t -> do t' <- mkDefField t
let T _ cs = mkWildCases t'
return $ T (TWild p) cs
Sort s | s == cStr -> return (Vr varStr)
QC p -> case allParamValues gr ty of
Ok [] -> checkError ("no parameter values given to type" <+> ppQIdent Qualified p)
Ok (v:_) -> return v
Bad msg -> fail msg
RecType r -> do
let (ls,_,ts) = unzip3 r
ts <- mapM mkDefField ts
return $ R (zipWith assign ls ts)
_ | Just _ <- isTypeInts ty -> return $ EInt 0 -- exists in all as first val
_ -> checkError ("a field in a linearization type cannot be" <+> ty)
mkLinReference :: SourceGrammar -> Type -> Check Term
mkLinReference gr typ = do
mb_term <- mkRefField typ (Vr varStr)
return (Abs Explicit varStr (fromMaybe Empty mb_term))
where
mkRefField ty trm =
case ty of
Table pty ty -> do ps <- allParamValues gr pty
case ps of
[] -> fail (render ("no parameter values given to type" <+> pty))
(p:ps) -> mkRefField ty (S trm p)
Sort s | s == cStr -> return (Just trm)
QC p -> return Nothing
RecType rs -> traverse rs trm
_ | Just _ <- isTypeInts ty -> return Nothing
_ -> fail (render ("a field in a linearization type cannot be" <+> typ))
traverse [] trm = return Nothing
traverse ((l,_,ty):rs) trm = do res <- mkRefField ty (P trm l)
case res of
Just trm -> return (Just trm)
Nothing -> traverse rs trm
type2metaTerm :: SourceGrammar -> Int -> MetaVars -> Choice -> LIndex -> [(LIndex,(Ident,Type))] -> Type -> [(Value,Type)] -> (MetaVars,Choice,Int,Term,[(Value,Type)])
type2metaTerm gr d ms s r rs (Sort srt) params | srt == cStr = (ms,s,r+1,TSymCat d r rs,params)
type2metaTerm gr d ms s r rs (RecType lbls) params =
let ((ms',s',r',params'),ass) =
mapAccumL (\(ms,s,r,params) (lbl,_,ty) -> case lbl of
LVar j -> ((ms,s,r,params),(lbl,(Just ty,TSymVar d j)))
lbl -> let (ms',s',r',t,params') = type2metaTerm gr d ms s r rs ty params
in ((ms',s',r',params'),(lbl,(Just ty,t))))
(ms,s,r,params) lbls
in (ms',s',r',R ass,params')
type2metaTerm gr d ms s r rs (Table p q) params
| count == 1 = let (ms',s',r',t,params') = type2metaTerm gr d ms s r rs q params
in (ms',s',r+(r'-r),T (TTyped p) [(PV identW,t)],params')
| otherwise = let pv = varX (length rs+1)
(ms',s',r',t,params') = type2metaTerm gr d ms s r ((delta,(pv,p)):rs) q params
delta = r'-r
in (ms',s',r+delta*count,T (TTyped p) [(PV pv,t)],params')
where
count = case countParamValues gr p of
Ok c -> c
Bad msg -> error msg
type2metaTerm gr d ms c r rs ty@(QC q) params =
let i = Map.size ms + 1
(c1,c2) = split c
in (Map.insert i (Narrowing c1 ty) ms,c2,r,Meta i,(VMeta i [],ty):params)
type2metaTerm gr d ms c r rs ty params
| Just n <- isTypeInts ty =
let i = Map.size ms + 1
(c1,c2) = split c
in (Map.insert i (Narrowing c1 ty) ms,c2,r,Meta i,(VMeta i [],ty):params)
breakDown g ms s r rs v (Sort sort) fn0 fn
| sort == cStr =
let fn' = do params <- fn0
v <- force v
return (r,rs,v,params)
<|>
do fn
in return (ms,r+1,fn0,fn')
breakDown g ms s r rs v (RecType lbls) fn0 fn = traverse ms r rs lbls fn0 fn
where
traverse ms r rs [] fn0 fn = return (ms,r,fn0,fn)
traverse ms r rs ((lbl,_,ty):lbls) fn0 fn = do (ms,r,fn0,fn) <- breakDown g ms s r rs (project v) ty fn0 fn
traverse ms r rs lbls fn0 fn
where
project (VR as) = case lookup lbl as of
Nothing -> error (render ("Missing value for label" <+> pp lbl $$
"in" <+> ppValue Unqualified 0 (VR as)))
Just v -> v
project (VFV c fvs) = VFV c (fmap project fvs)
project (VMeta i vs) = VSusp i (\v -> project (apply g v vs)) []
project (VSusp i k vs)= VSusp i (\v -> project (apply g (k v) vs)) []
project (VError msg) = VError msg
project v = VP v lbl []
breakDown g ms c r rs v (Table p q) fn0 fn = do
let i = Map.size ms + 1
v2 = VMeta i []
v0 = VS v v2 []
(c1,c2) = split c
Gl gr _ _ = g
cnt <- countParamValues gr p
(ms,r',fn0,fn) <- mfix $ \(~(_,r',_,_)) ->
breakDown g (Map.insert i (Narrowing c1 p) ms) c2 r ((r'-r,(v2,p)):rs) (select v0 v v2) q fn0 fn
return (ms,r+(r'-r)*cnt,fn0,fn)
where
select v0 (VT _ env s cs) v2 = patternMatch g s v0 (map (\(p,t) -> (env,[p],[v2],t)) cs)
select v0 (VV vty tvs) v2 = vtableSelect g v0 vty tvs v2 []
select v0 (VFV i fvs) v2 = VFV i (fmap (\v1 -> select v0 v1 v2) fvs)
select v0 (VMeta i vs) v2 = VSusp i (\v -> select v0 (apply g v vs) v2) []
select v0 (VSusp i k vs) v2 = VSusp i (\v -> select v0 (apply g (k v) vs) v2) []
select v0 (VError msg) v2 = VError msg
select v0 v1 v2 = v0
breakDown g ms s r rs v ty@(QC q) fn0 fn =
let fn0' = do params <- fn0
v <- force v
return ((v,ty):params)
fn' = do (r,rs,v',res_params) <- fn
v <- force v
return (r,rs,v',(v,ty):res_params)
in return (ms,r,fn0',fn')
breakDown g ms s r rs v ty@(App (Q q) _) fn0 fn =
let fn0' = do params <- fn0
v <- force v
return ((v,ty):params)
fn' = do (r,rs,v',res_params) <- fn
v <- force v
return (r,rs,v',(v,ty):res_params)
in return (ms,r,fn0',fn')
force (VStr s) = return (VStr s)
force (VInt n) = return (VInt n)
force (VFlt d) = return (VFlt d)
force (VSymCat d r rs) = do
rs <- mapM force_ rs
return (VSymCat d r rs)
where
force_ (factor, (v, ty)) = do
v <- force v
return (factor, (v, ty))
force (VApp q vs) = do
vs <- mapM force vs
return (VApp q vs)
force (VAlts def alts) = do
def <- force def
alts <- mapM force_ alts
return (VAlts def alts)
where
force_ (x,y) = do
x <- force x
y <- force y
return (x,y)
force VEmpty = return VEmpty
force (VC v1 v2) = do
v1 <- force v1
v2 <- force v2
return (VC v1 v2)
force (VMeta i vs) = do
vs <- mapM force vs
return (VMeta i vs)
force (VSusp i k vs) = do
vs <- mapM force vs
st <- getMeta i
v <- case st of
Narrowing c ty -> do v <- chooseMetaValue c ty
setMeta i (Bound undefined v)
return v
Bound _ v -> return v
g <- globals
force (apply g (k v) vs)
force (VStrs vs) = do
vs <- mapM force vs
return (VStrs vs)
force (VR as) = do
as <- mapM (\(l,v) -> fmap ((,) l) (force v)) as
return (VR as)
force v@(VPatt _ _ _) = return v
force (VFV c vs) = do
v <- variants c (unvariants vs)
force v
force (VError msg) = compileError msg
force v = compileError ("Cannot evaluate" <+> ppValue Unqualified 0 v)
flatten subst (VStr s) = return (subst,[SymKS s])
flatten subst (VSymCat d r rs) = do
(subst,lin_index) <- params2int' subst r rs
return (subst,[SymCat d lin_index])
flatten subst (VApp (m,id) [])
| m == cPredef && id == cBIND = return (subst,[SymBIND])
| m == cPredef && id == cSOFT_BIND = return (subst,[SymSOFT_BIND])
| m == cPredef && id == cSOFT_SPACE = return (subst,[SymSOFT_SPACE])
| m == cPredef && id == cNonExist = return (subst,[SymNE])
| m == cPredef && id == cCAPIT = return (subst,[SymCAPIT])
| m == cPredef && id == cALL_CAPIT = return (subst,[SymALL_CAPIT])
flatten subst v0@(VAlts def alts) = do
(subst,def) <- flatten subst def
(subst,alts) <- mapAccumM (\subst (alt,ps) -> do
(subst,alt) <- flatten subst alt
ps <- to_strs ps
return (subst,(alt,ps)))
subst
alts
return (subst,[SymKP def alts])
where
to_strs (VStrs vs) = mapM to_str vs
to_strs (VPatt _ _ p) = from_patt p
to_strs v = fail
to_str (VStr s) = return s
to_str _ = fail
from_patt (PAlt p1 p2) = liftM2 (++) (from_patt p1) (from_patt p2)
from_patt (PSeq _ _ p1 _ _ p2) = liftM2 (liftM2 (++)) (from_patt p1) (from_patt p2)
from_patt (PString s) = return [s]
from_patt (PChars cs) = return (map (:[]) cs)
from_patt _ = fail
fail = compileError ("Complex patterns are not supported in:" $$ nest 2 (ppValue Unqualified 0 v0))
flatten subst VEmpty = return (subst,[])
flatten subst (VC v1 v2) = do
(subst,s1) <- flatten subst v1
(subst,s2) <- flatten subst v2
return (subst,s1++s2)
flatten subst (VSusp i k vs) = do
st <- getMeta i
v <- case st of
Narrowing c ty -> do v <- chooseMetaValue c ty
setMeta i (Bound undefined v)
return v
Bound _ v -> return v
g <- globals
flatten subst (apply g (k v) vs)
flatten subst (VFV c vs) = do
v <- variants c (unvariants vs)
flatten subst v
flatten subst (VError msg) = compileError msg
flatten subst v = compileError ("Cannot evaluate" <+> ppValue Unqualified 0 v <+> "to a string")
params2int subst rs = do
(subst,r,rs,_) <- compute subst rs
return (subst,LParam r (order rs))
where
compute subst [] = return (subst,0,[],1)
compute subst ((v,ty):params) = do
(subst, r, rs, cnt ) <- param2int subst v ty
(subst, r',rs',cnt') <- compute subst params
return (subst, r*cnt'+r',combine cnt' rs rs',cnt*cnt')
params2int' subst r0 rs = do
(subst,r,rs) <- compute subst rs
return (subst,LParam (r0+r) (order rs))
where
compute subst [] = return (subst,0,[])
compute subst ((cnt',(v,ty)):params) = do
(subst, r, rs, cnt) <- param2int subst v ty
(subst, r',rs') <- compute subst params
return (subst,r*cnt'+r',combine cnt' rs rs')
param2int subst (VR as) (RecType lbls) = compute subst lbls
where
compute subst [] = return (subst,0,[],1)
compute subst ((lbl,_,ty):lbls) = do
case lookup lbl as of
Just v -> do (subst, r, rs ,cnt ) <- param2int subst v ty
(subst, r',rs',cnt') <- compute subst lbls
return (subst,r*cnt'+r',combine' cnt rs cnt' rs',cnt*cnt')
Nothing -> compileError ("Missing value for label" <+> pp lbl $$
"among" <+> hsep (punctuate (pp ',') (map fst as)))
param2int subst (VApp q vs) ty = do
( r , ctxt,cnt ) <- getIdxCnt q
(subst,r',rs', cnt') <- compute subst ctxt vs
return (subst,r+r',rs',cnt)
where
compute subst [] [] = return (subst,0,[],1)
compute subst ((_,_,ty):ctxt) (v:vs) = do
(subst, r, rs ,cnt ) <- param2int subst v ty
(subst, r',rs',cnt') <- compute subst ctxt vs
return (subst,r*cnt'+r',combine' cnt rs cnt' rs',cnt*cnt')
param2int subst (VInt n) ty
| Just max <- isTypeInts ty= return (subst,fromIntegral n,[],fromIntegral max+1)
param2int subst (VMeta i _) ty = do
st <- getMeta i
case st of
Narrowing c ty -> do count <- getCnt ty
case Map.lookup i subst of
Just v -> return (subst,0,[(1,v)],count)
Nothing -> let v = Map.size subst
subst' = Map.insert i v subst
in return (subst',0,[(1,v)],count)
Bound _ v -> param2int subst v ty
param2int subst (VSusp i k vs) ty = do
st <- getMeta i
v <- case st of
Narrowing c ty -> do v <- chooseMetaValue c ty
setMeta i (Bound undefined v)
return v
Bound _ v -> return v
g <- globals
param2int subst (apply g (k v) vs) ty
param2int subst (VFV c vs) ty = do
v <- variants c (unvariants vs)
param2int subst v ty
param2int subst v ty = compileError ("the parameter:" <+> ppValue Unqualified 0 v $$
"cannot be evaluated at compile time.")
combine' 1 rs 1 rs' = []
combine' 1 rs cnt' rs' = rs'
combine' cnt rs 1 rs' = rs
combine' cnt rs cnt' rs' = combine cnt' rs rs'
combine cnt' [] rs' = rs'
combine cnt' rs [] = [(r*cnt',pv) | (r,pv) <- rs]
combine cnt' ((r,pv):rs) ((r',pv'):rs') =
case compare pv pv' of
LT -> (r*cnt', pv ) : combine cnt' rs ((r',pv'):rs')
EQ -> (r*cnt'+r',pv ) : combine cnt' rs ((r',pv'):rs')
GT -> ( r',pv') : combine cnt' ((r,pv):rs) rs'
type ChoiceMap = Map.Map Choice Int
type MetaVars = Map.Map Int MetaState
newtype GenM a = GenM {unGen :: forall r . Globals -> (a -> ChoiceMap -> MetaVars -> r -> Check r) -> ChoiceMap -> MetaVars -> r -> Check r}
instance Functor GenM where
fmap f (GenM m) = GenM (\g k -> m g (k . f))
instance Applicative GenM where
pure x = GenM (\g k -> k x)
(GenM f) <*> (GenM h) = GenM (\g k -> f g (\fn -> h g (\x -> k (fn x))))
instance Alternative GenM where
empty = GenM (\g k svs ms r -> pure r)
(GenM f) <|> (GenM h) = GenM (\g k svs ms r -> f g k svs ms r >>= h g k svs ms)
instance Monad GenM where
(GenM f) >>= h = GenM (\g k -> f g (\x -> case h x of {GenM h -> h g k}))
instance MonadFail GenM where
fail msg = GenM (\_ _ _ _ _ -> fail msg)
runGenM g ms r (GenM f) = f g (\x svs ms xs -> pure (x:xs)) Map.empty ms r
compileError d = GenM (\_ _ _ _ _ -> checkError d)
globals = GenM $ \g k -> k g
variants :: Choice -> [a] -> GenM a
variants c xs = GenM (\g k svs ms r ->
case Map.lookup c svs of
Just j -> k (xs !! j) svs ms r
Nothing -> foldM (\r (j,x) -> k x (Map.insert c j svs) ms r) r (zip [0..] xs))
newMeta c ty = GenM $ \_ k svs ms ->
let i = Map.size ms + 1
in k i svs (Map.insert i (Narrowing c ty) ms)
getMeta i = GenM $ \_ k svs ms r ->
case Map.lookup i ms of
Just v -> k v svs ms r
Nothing -> checkError (pp "Meta variable" <+> ppMeta i <+> "is not defined")
setMeta i st = GenM $ \_ k svs ms ->
k () svs (Map.insert i st ms)
getCnt ty = GenM $ \(Gl gr _ _) k svs ms r ->
case countParamValues gr ty of
Ok c -> k c svs ms r
Bad msg -> checkError (pp msg)
getIdxCnt q = GenM $ \(Gl gr _ _) k svs ms r ->
case lookupOrigInfo gr q of
Ok (_,ResValue (L _ ty) idx) ->
let (ctxt,QC p) = typeFormCnc ty
in case lookupOrigInfo gr p of
Ok (_,ResParam _ (Just (_,cnt))) -> k (idx,ctxt,cnt) svs ms r
Bad msg -> checkError (pp msg)
Bad msg -> checkError (pp msg)
chooseMetaValue :: Choice -> Type -> GenM Value
chooseMetaValue s ptyp = GenM $ \g@(Gl gr _ _) k svs ms r ->
case ptyp of
_ | Just n <- isTypeInts ptyp -> foldM (\r i -> k (VInt i) svs ms r) r [0..n]
QC c -> do (mod,info) <- lookupOrigInfo gr c
case info of
ResParam (Just ps) _ -> mkValue mod k svs ms r 0 (unLoc ps)
_ -> checkError (ppQIdent Qualified c <+> "has no parameter values defined")
Q c -> lookupResDef gr c >>= \ty -> unGen (chooseMetaValue s ty) g k svs ms r
RecType lbls -> unGen (mapAccumM mkField s lbls >>= \(_,lbls) -> return (VR lbls)) g k svs ms r
_ -> checkError ("cannot find parameter values for" <+> ptyp)
where
mkValue mod k svs ms r idx [] = return r
mkValue mod k svs ms r idx ((id,ctxt):ps) = do
let (ms',args) = mkVars ms s ctxt
r <- k (VApp (mod,id) args) (Map.insert s idx svs) ms' r
mkValue mod k svs ms r (idx+1) ps
mkVars ms c [] = (ms,[])
mkVars ms c ((_,_,ty):ctxt) =
let i = Map.size ms + 1
(c1,c2) = split c
(ms',args) = mkVars (Map.insert i (Narrowing c1 ty) ms) c2 ctxt
in (ms',VMeta i []:args)
mkField c (l,_,ty) = do
let (c1,c2) = split c
v <- chooseMetaValue c1 ty
return (c2,(l,v))
order :: Ord a => [(a,b)] -> [(a,b)]
order = sortBy (\(r1,_) (r2,_) -> compare r2 r1)
mapAccumM f a [] = return (a,[])
mapAccumM f a (x:xs) = do (a, y) <- f a x
(a,ys) <- mapAccumM f a xs
return (a,y:ys)