Files
gf-core/src/compiler/GF/Compile/Compute/Concrete.hs
2021-10-05 15:37:42 +02:00

422 lines
18 KiB
Haskell

{-# LANGUAGE RankNTypes, CPP #-}
-- | Functions for computing the values of terms in the concrete syntax, in
-- | preparation for PMCFG generation.
module GF.Compile.Compute.Concrete
(normalForm,
Value(..), Env, value2term, eval
) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import GF.Grammar hiding (Env, VGen, VApp, VRecType)
import GF.Grammar.Lookup(lookupResDef,allParamValues)
import GF.Grammar.Predef
import GF.Grammar.Lockfield(isLockLabel,lockRecType) --unlockRecord,lockLabel
import GF.Grammar.Printer
import GF.Data.Str(Str,glueStr,str2strings,str,sstr,plusStr,strTok)
import GF.Data.Operations(Err(..),err,errIn,maybeErr,mapPairsM)
import GF.Data.Utilities(mapFst,mapSnd)
import GF.Infra.Option
import Data.STRef
import Data.Maybe(fromMaybe)
import Data.List
import Data.Char
import Control.Monad
import Control.Monad.ST
import Control.Applicative
import qualified Control.Monad.Fail as Fail
import qualified Data.Map as Map
import GF.Text.Pretty
-- * Main entry points
normalForm :: Grammar -> L Ident -> Term -> Term
normalForm gr loc t =
case runEvalM gr (eval [] t [] >>= value2term 0) of
Left msg -> error (render (ppL loc msg))
Right [t] -> t
Right ts -> FV ts
data ThunkState s
= Unevaluated (Env s) Term
| Evaluated (Value s)
| Unbound {-# UNPACK #-} !MetaId
type Thunk s = STRef s (ThunkState s)
type Env s = [(Ident,Thunk s)]
data Value s
= VApp QIdent [Thunk s]
| VMeta (Thunk s) (Env s) [Thunk s]
| VSusp (Thunk s) (Env s) [Thunk s] (Thunk s -> EvalM s (Value s))
| VGen {-# UNPACK #-} !Int [Thunk s]
| VClosure (Env s) Term
| VProd BindType Ident (Value s) (Value s)
| VRecType [(Label, Value s)]
| VR [(Label, Thunk s)]
| VP (Value s) Label [Thunk s]
| VTable (Value s) (Value s)
| VT TInfo [Case]
| VV Type [Thunk s]
| VS (Value s) (Thunk s) [Thunk s]
| VSort Ident
| VInt Integer
| VFlt Double
| VStr String
| VC [Value s]
eval env (Vr x) vs = case lookup x env of
Just tnk -> force tnk vs
Nothing -> evalError ("Variable" <+> pp x <+> "is not in scope")
eval env (Sort s) [] = return (VSort s)
eval env (EInt n) [] = return (VInt n)
eval env (EFloat d) [] = return (VFlt d)
eval env (K t) [] = return (VStr t)
eval env Empty [] = return (VC [])
eval env (App t1 t2) vs = do tnk <- newThunk env t2
eval env t1 (tnk : vs)
eval env (Abs b x t) [] = return (VClosure env (Abs b x t))
eval env (Abs b x t) (v:vs) = eval ((x,v):env) t vs
eval env (Meta i) vs = do tnk <- newMeta i
return (VMeta tnk env vs)
eval env (ImplArg t) [] = eval env t []
eval env (Prod b x t1 t2)[] = do v1 <- eval env t1 []
return (VProd b x v1 (VClosure env (Abs b x t2)))
eval env (Typed t ty) vs = eval env t vs
eval env (RecType lbls) [] = do lbls <- mapM (\(lbl,ty) -> fmap ((,) lbl) (eval env ty [])) lbls
return (VRecType lbls)
eval env (R as) [] = do as <- mapM (\(lbl,(_,t)) -> fmap ((,) lbl) (newThunk env t)) as
return (VR as)
eval env (P t lbl) vs = do v <- eval env t []
case v of
VR as -> case lookup lbl as of
Nothing -> evalError ("Missing value for label" <+> pp lbl $$
"in record" <+> pp t)
Just tnk -> force tnk vs
v -> return (VP v lbl vs)
eval env (Table t1 t2) [] = do v1 <- eval env t1 []
v2 <- eval env t2 []
return (VTable v1 v2)
eval env (T i cs) [] = return (VT i cs)
eval env (V ty ts) [] = do tnks <- mapM (newThunk env) ts
return (VV ty tnks)
eval env t@(S t1 t2) vs = do v1 <- eval env t1 []
tnk2 <- newThunk env t2
let v0 = VS v1 tnk2 vs
case v1 of
VT _ cs -> patternMatch v0 (map (\(p,t) -> (env,[p],tnk2:vs,t)) cs)
v1 -> return v0
eval env (Let (x,(_,t1)) t2) vs = do tnk <- newThunk env t1
eval ((x,tnk):env) t2 vs
eval env (Q q@(m,id)) vs
| m == cPredef = do vs' <- mapM (flip force []) vs
mb_res <- evalPredef id vs'
case mb_res of
Just res -> return res
Nothing -> return (VApp q vs)
| otherwise = do t <- lookupGlobal q
eval env t vs
eval env (QC q) vs = return (VApp q vs)
eval env (C t1 t2) [] = do v1 <- eval env t1 []
v2 <- eval env t2 []
case (v1,v2) of
(VC vs1,VC vs2) -> return (VC (vs1++vs2))
(VC vs1,v2 ) -> return (VC (vs1++[v2]))
(v1, VC vs2) -> return (VC ([v1]++vs2))
(v1, v2 ) -> return (VC [v1,v2])
eval env t@(Glue t1 t2) [] = do v1 <- eval env t1 []
v2 <- eval env t2 []
case liftM2 (++) (value2string v1) (value2string v2) of
Just s -> return (string2value s)
Nothing -> evalError ("Cannot reduce term" <+> pp t)
eval env (FV ts) vs = msum [eval env t vs | t <- ts]
eval env (Error msg) vs = fail msg
eval env t vs = evalError ("Cannot reduce term" <+> pp t)
apply v [] = return v
apply (VApp f vs0) vs = return (VApp f (vs0++vs))
apply (VMeta m env vs0) vs = return (VMeta m env (vs0++vs))
apply (VGen i vs0) vs = return (VGen i (vs0++vs))
apply (VClosure env (Abs b x t)) (v:vs) = eval ((x,v):env) t vs
evalPredef id [v]
| id == cLength = return (fmap VInt (liftM genericLength (value2string v)))
evalPredef id [v1,v2]
| id == cTake = return (fmap string2value (liftM2 genericTake (value2int v1) (value2string v2)))
evalPredef id [v1,v2]
| id == cDrop = return (fmap string2value (liftM2 genericDrop (value2int v1) (value2string v2)))
evalPredef id [v1,v2]
| id == cTk = return (fmap string2value (liftM2 genericTk (value2int v1) (value2string v2)))
where
genericTk n = reverse . genericTake n . reverse
evalPredef id [v1,v2]
| id == cDp = return (fmap string2value (liftM2 genericDp (value2int v1) (value2string v2)))
where
genericDp n = reverse . genericDrop n . reverse
evalPredef id [v]
| id == cIsUpper= return (fmap toPBool (liftM (all isUpper) (value2string v)))
evalPredef id [v]
| id == cToUpper= return (fmap string2value (liftM (map toUpper) (value2string v)))
evalPredef id [v]
| id == cToLower= return (fmap string2value (liftM (map toLower) (value2string v)))
evalPredef id [v1,v2]
| id == cEqStr = return (fmap toPBool (liftM2 (==) (value2string v1) (value2string v2)))
evalPredef id [v1,v2]
| id == cOccur = return (fmap toPBool (liftM2 occur (value2string v1) (value2string v2)))
evalPredef id [v1,v2]
| id == cOccurs = return (fmap toPBool (liftM2 occurs (value2string v1) (value2string v2)))
evalPredef id [v1,v2]
| id == cEqInt = return (fmap toPBool (liftM2 (==) (value2int v1) (value2int v2)))
evalPredef id [v1,v2]
| id == cLessInt= return (fmap toPBool (liftM2 (<) (value2int v1) (value2int v2)))
evalPredef id [v1,v2]
| id == cPlus = return (fmap VInt (liftM2 (+) (value2int v1) (value2int v2)))
evalPredef id [v]
| id == cError = case value2string v of
Just msg -> fail msg
Nothing -> return Nothing
evalPredef id vs = return Nothing
toPBool True = VApp (cPredef,cPTrue) []
toPBool False = VApp (cPredef,cPFalse) []
occur s1 [] = False
occur s1 s2@(_:tail) = check s1 s2
where
check xs [] = False
check [] ys = True
check (x:xs) (y:ys)
| x == y = check xs ys
check _ _ = occur s1 tail
occurs cs s2 = any (\c -> elem c s2) cs
patternMatch v0 [] = fail "No matching pattern found"
patternMatch v0 ((env0,ps,args0,t):eqs) = match env0 ps eqs args0
where
match env [] eqs args = eval env t args
match env (PT ty p :ps) eqs args = match env (p:ps) eqs args
match env (PAlt p1 p2:ps) eqs args = match env (p1:ps) ((env,p2:ps,args,t):eqs) args
match env (PM q :ps) eqs args = do t <- lookupGlobal q
case t of
EPatt _ _ p -> match env (p:ps) eqs args
_ -> evalError $ hang "Expected pattern macro:" 4
(pp t)
match env (PV v :ps) eqs (arg:args) = match ((v,arg):env) ps eqs args
match env (PAs v p :ps) eqs (arg:args) = match ((v,arg):env) (p:ps) eqs (arg:args)
match env (PW :ps) eqs (arg:args) = match env ps eqs args
match env (PTilde _ :ps) eqs (arg:args) = match env ps eqs args
match env (p :ps) eqs (arg:args) = do
v <- force arg []
case (p,v) of
(p, VMeta i envi vs ) -> return (VSusp i envi vs (\tnk -> match env (p:ps) eqs (tnk:args)))
(p, VGen i vs ) -> return v0
(p, VSusp i envi vs k) -> return (VSusp i envi vs (\tnk -> match env (p:ps) eqs (tnk:args)))
(PP q qs, VApp r tnks)
| q == r -> match env (qs++ps) eqs (tnks++args)
(PR pas, VR as) -> matchRec env pas as ps eqs args
(PString s1, VStr s2)
| s1 == s2 -> match env ps eqs args
(PString s1, VC [])
| null s1 -> match env ps eqs args
(PSeq min1 max1 p1 min2 max2 p2,v)
-> case value2string v of
Just s -> do let n = length s
lo = min1 `max` (n-fromMaybe n max2)
hi = (n-min2) `min` fromMaybe n max1
(ds,cs) = splitAt lo s
eqs <- matchStr env (p1:p2:ps) eqs (hi-lo) (reverse ds) cs args
patternMatch v0 eqs
Nothing -> return v0
(PRep minp maxp p, v)
-> case value2string v of
Just s -> do let n = length s `div` (max minp 1)
eqs <- matchRep env n minp maxp p minp maxp p ps ((env,PString []:ps,(arg:args),t) : eqs) (arg:args)
patternMatch v0 eqs
Nothing -> return v0
(PChar, VStr [_]) -> match env ps eqs args
(PChars cs, VStr [c])
| elem c cs -> match env ps eqs args
(PInt n, VInt m)
| n == m -> match env ps eqs args
(PFloat n, VFlt m)
| n == m -> match env ps eqs args
_ -> patternMatch v0 eqs
matchRec env [] as ps eqs args = match env ps eqs args
matchRec env ((lbl,p):pas) as ps eqs args =
case lookup lbl as of
Just tnk -> matchRec env pas as (p:ps) eqs (tnk:args)
Nothing -> evalError ("Missing value for label" <+> pp lbl)
matchStr env ps eqs i ds [] args = do
arg1 <- newEvaluatedThunk (string2value (reverse ds))
arg2 <- newEvaluatedThunk (string2value [])
return ((env,ps,arg1:arg2:args,t) : eqs)
matchStr env ps eqs 0 ds cs args = do
arg1 <- newEvaluatedThunk (string2value (reverse ds))
arg2 <- newEvaluatedThunk (string2value cs)
return ((env,ps,arg1:arg2:args,t) : eqs)
matchStr env ps eqs i ds (c:cs) args = do
arg1 <- newEvaluatedThunk (string2value (reverse ds))
arg2 <- newEvaluatedThunk (string2value (c:cs))
eqs <- matchStr env ps eqs (i-1 :: Int) (c:ds) cs args
return ((env,ps,arg1:arg2:args,t) : eqs)
matchRep env 0 minp maxp p minq maxq q ps eqs args = do
return eqs
matchRep env n minp maxp p minq maxq q ps eqs args = do
matchRep env (n-1) minp maxp p (minp+minq) (liftM2 (+) maxp maxq) (PSeq minp maxp p minq maxq q) ps ((env,q:ps,args,t) : eqs) args
value2term i (VApp q tnks) =
foldM (\e1 tnk -> fmap (App e1) (force tnk [] >>= value2term i)) (QC q) tnks
value2term i (VMeta m env tnks) = do
res <- zonk m tnks
case res of
Right i -> foldM (\e1 tnk -> fmap (App e1) (force tnk [] >>= value2term i)) (Meta i) tnks
Left v -> value2term i v
value2term i (VGen j tnks) =
foldM (\e1 tnk -> fmap (App e1) (force tnk [] >>= value2term i)) (Vr (identS ('v':show j))) tnks
value2term i (VClosure env (Abs b x t)) = do
tnk <- newGen i
v <- eval ((x,tnk):env) t []
t <- value2term (i+1) v
return (Abs b (identS ('v':show i)) t)
value2term i (VProd b x v1 v2) = do
t1 <- value2term i v1
t2 <- value2term i v2
return (Prod b x t1 t2)
value2term i (VRecType lbls) = do
lbls <- mapM (\(lbl,v) -> fmap ((,) lbl) (value2term i v)) lbls
return (RecType lbls)
value2term i (VR as) = do
as <- mapM (\(lbl,tnk) -> fmap (\t -> (lbl,(Nothing,t))) (force tnk [] >>= value2term i)) as
return (R as)
value2term i (VP v lbl tnks) = do
t <- value2term i v
foldM (\e1 tnk -> fmap (App e1) (force tnk [] >>= value2term i)) (P t lbl) tnks
value2term i (VTable v1 v2) = do
t1 <- value2term i v1
t2 <- value2term i v2
return (Table t1 t2)
value2term i (VT ti cs) = return (T ti cs)
value2term i (VV ty tnks) = do ts <- mapM (\tnk -> force tnk [] >>= value2term i) tnks
return (V ty ts)
value2term i (VS v1 tnk2 tnks) = do t1 <- value2term i v1
t2 <- force tnk2 [] >>= value2term i
foldM (\e1 tnk -> fmap (App e1) (force tnk [] >>= value2term i)) (S t1 t2) tnks
value2term i (VSort s) = return (Sort s)
value2term i (VStr tok) = return (K tok)
value2term i (VInt n) = return (EInt n)
value2term i (VFlt n) = return (EFloat n)
value2term i (VC vs) = do
ts <- mapM (value2term i) vs
case ts of
[] -> return Empty
(t:ts) -> return (foldl C t ts)
value2string (VStr s) = Just s
value2string (VC vs) = fmap unwords (mapM value2string vs)
value2string _ = Nothing
string2value s =
case words s of
[] -> VC []
[w] -> VStr w
ws -> VC (map VStr ws)
value2int (VInt n) = Just n
value2int _ = Nothing
-----------------------------------------------------------------------
-- * Evaluation monad
type MetaThunks s = Map.Map MetaId (Thunk s)
type Cont s r = MetaThunks s -> r -> ST s (Either Doc r)
newtype EvalM s a = EvalM (forall r . Grammar -> (a -> Cont s r) -> Cont s r)
instance Functor (EvalM s) where
fmap f (EvalM g) = EvalM (\gr k -> g gr (k . f))
instance Applicative (EvalM s) where
pure x = EvalM (\gr k -> k x)
(EvalM f) <*> (EvalM x) = EvalM (\gr k -> f gr (\f -> x gr (\x -> k (f x))))
instance Monad (EvalM s) where
(EvalM f) >>= g = EvalM (\gr k -> f gr (\x -> case g x of
EvalM g -> g gr k))
#if !(MIN_VERSION_base(4,13,0))
-- Monad(fail) will be removed in GHC 8.8+
fail = Fail.fail
#endif
instance Fail.MonadFail (EvalM s) where
fail msg = EvalM (\gr k _ r -> return (Left (pp msg)))
instance Alternative (EvalM s) where
empty = EvalM (\gr k _ r -> return (Right r))
(EvalM f) <|> (EvalM g) = EvalM $ \gr k mt r -> do
res <- f gr k mt r
case res of
Left msg -> return (Left msg)
Right r -> g gr k mt r
instance MonadPlus (EvalM s) where
runEvalM :: Grammar -> (forall s . EvalM s a) -> Either Doc [a]
runEvalM gr f =
case runST (case f of
EvalM f -> f gr (\x mt xs -> return (Right (x:xs))) Map.empty []) of
Left msg -> Left msg
Right xs -> Right (reverse xs)
evalError :: Doc -> EvalM s a
evalError msg = EvalM (\gr k _ r -> return (Left msg))
lookupGlobal :: QIdent -> EvalM s Term
lookupGlobal q = EvalM $ \gr k mt r -> do
case lookupResDef gr q of
Ok t -> k t mt r
Bad msg -> return (Left (pp msg))
newThunk env t = EvalM $ \gr k mt r -> do
tnk <- newSTRef (Unevaluated env t)
k tnk mt r
newEvaluatedThunk v = EvalM $ \gr k mt r -> do
tnk <- newSTRef (Evaluated v)
k tnk mt r
newMeta i = EvalM $ \gr k mt r ->
if i == 0
then do tnk <- newSTRef (Unbound i)
k tnk mt r
else case Map.lookup i mt of
Just tnk -> k tnk mt r
Nothing -> do tnk <- newSTRef (Unbound i)
k tnk (Map.insert i tnk mt) r
newGen i = EvalM $ \gr k mt r -> do
tnk <- newSTRef (Evaluated (VGen i []))
k tnk mt r
force tnk vs = EvalM $ \gr k mt r -> do
s <- readSTRef tnk
case s of
Unevaluated env t -> case eval env t vs of
EvalM f -> f gr (\v mt r -> do writeSTRef tnk (Evaluated v)
r <- k v mt r
writeSTRef tnk s
return r) mt r
Evaluated v -> case apply v vs of
EvalM f -> f gr k mt r
zonk tnk vs = EvalM $ \gr k mt r -> do
s <- readSTRef tnk
case s of
Evaluated v -> case apply v vs of
EvalM f -> f gr (k . Left) mt r
Unbound i -> k (Right i) mt r