-- | -- Copyright : © 2009 CNRS - École Polytechnique - INRIA -- License : GPL -- -- A code generator based on the haskell-src-exts package by Niklas Broberg. module Dedukti.CodeGen.Exts (module Dedukti.CodeGen, Code) where import Dedukti.CodeGen import Dedukti.Core import Dedukti.Module import Dedukti.Pretty import qualified Dedukti.Rule as Rule import qualified Language.Haskell.Exts.Syntax as Hs import qualified Language.Haskell.Exts.Build as Hs import Language.Haskell.Exts.Pretty import Language.Haskell.Exts.QQ import qualified Data.ByteString.Lazy.Char8 as B import Data.Char (toUpper) import Data.List (foldl') import qualified Data.Stream as Stream import Prelude hiding ((*)) (*) :: Hs.SrcLoc -- dummy source location. (*) = Hs.SrcLoc "" 0 0 type Em a = a (Id Record) (A Record) type instance Id Record = Qid type instance A Record = Unannot -- External view of record. type Code = Record -- Create a record for each declaration in the source. data Record = Rec { rec_name :: Qid , rec_rules :: Int -- ^ Number of rules associated with qid. , rec_code :: [Hs.Decl] } instance CodeGen Record where data Bundle Record = Bundle [Hs.Decl] emit rs@(RS x ty rules) = Rec x (length rules) (function rs : def_ty : def_box : zipWith defs_rule [0..] rules) where (tyname, boxname) = (varName (x .$ "ty"), varName (x .$ "box")) def_ty = [$dec| ((tyname)) = $(code ty) |] def_box = [$dec| ((boxname)) = bbox $(term ty) $(Hs.var tyname) $(var x) |] -- Checking rules involves much of the same work as checking all -- declarations at top-level, so let's just call the code -- generation functions recursively. defs_rule n (env :@ lhs :--> rhs) = let f (x ::: ty) rs = (emit (RS x ty []) :: Record) : rs ruleCheck = let rule_box = varName (qid "rule" .$ "box") in Rec (qid "rule") 0 [[$dec| ((rule_box)) = checkRule $(term lhs) $(term rhs) |]] Bundle decls = coalesce $ foldr f [ruleCheck] (env_bindings env) rule = varName (x .$ "rule" .$ B.pack (show n)) body = Hs.letE decls [$hs| main |] in [$dec| ((rule)) = $body |] coalesce records = Bundle $ concatMap rec_code records ++ [main] where main = [$dec| main = $checks |] checks = Hs.Do (concatMap rules records ++ map declaration records) declaration r = let desc = Hs.strE $ show $ pretty $ unqualify $ rec_name r in Hs.qualStmt [$hs| checkDeclaration $desc $(var (rec_name r .$ "box")) |] rules (Rec _ 0 _) = [] rules (Rec x nr _) = let startmsg = Hs.strE $ "Starting rule " ++ show (pretty (unqualify x)) ++ "." finishmsg = Hs.strE $ "Finished rule " ++ show (pretty (unqualify x)) ++ "." in [Hs.qualStmt [$hs| putStrLn $startmsg |]] ++ map (\n -> Hs.qualStmt $ var (x .$ "rule" .$ B.pack (show n))) [0..nr-1] ++ [Hs.qualStmt [$hs| putStrLn $finishmsg |]] serialize mod deps (Bundle decls) = B.pack $ prettyPrintWithMode defaultMode {layout = PPInLine} $ Hs.Module (*) (modname mod) [] Nothing Nothing imports decls where imports = runtime : map (\m -> Hs.ImportDecl (*) (modname m) True False Nothing Nothing Nothing) deps runtime = Hs.ImportDecl (*) (Hs.ModuleName "Dedukti.Runtime") False False Nothing Nothing Nothing modname m = Hs.ModuleName $ B.unpack $ B.intercalate "." $ map capitalize $ toList m -- | A similar encoding of names as the z-encoding of GHC. Non-letter -- characters are escaped with an x. xencode :: Qid -> String xencode qid = B.unpack $ joinQ (qid_qualifier qid) `B.append` -- Prepend all idents with an x to avoid clash with runtime functions. B.cons 'x' (enc $ fromAtom $ qid_stem qid) `B.append` joinS (qid_suffix qid) where joinQ Root = "" joinQ (h :. x) = joinQ h `B.append` capitalize (fromAtom x) `B.append` "." joinS Root = "" joinS (h :. x) = joinS h `B.append` "_" `B.append` fromAtom x enc = B.concatMap f where f 'x' = "xx" f '\'' = "xq" f '_' = "xu" f x | x >= '0', x <= '9' = 'x' `B.cons` B.singleton x | otherwise = B.singleton x function :: Em RuleSet -> Hs.Decl function (RS x _ []) = Hs.nameBind (*) (varName x) (constant x) function (RS x _ rs) = Hs.sfun (*) (varName x) [] (Hs.UnGuardedRhs rhs) (Hs.binds [f]) where n = Rule.arity (head rs) occs = Stream.take n (Stream.map Hs.var variables) pats = Stream.take n variables rhs = foldr (\x y -> [$hs| Lam (\((x)) -> $y) |]) (Hs.metaFunction "__" occs) pats f | n > 0 = Hs.FunBind (map clause rs ++ [defaultClause x n]) | otherwise = Hs.FunBind (map clause rs) clause :: Em TyRule -> Hs.Match clause rule = let (lrule@(env :@ _ :--> rhs), constraints) = Rule.linearize qids rule in if null constraints then Hs.Match (*) (Hs.name "__") (map (pattern env) (Rule.patterns lrule)) Nothing (Hs.UnGuardedRhs (code rhs)) Hs.noBinds else Hs.Match (*) (Hs.name "__") (map (pattern env) (Rule.patterns lrule)) Nothing (Hs.GuardedRhss [Hs.GuardedRhs (*) (guards constraints) (code rhs)]) Hs.noBinds where guards = map (\(x, x') -> Hs.qualStmt $ [$hs| convertible 0 $(var x) $(var x') |]) qids = Stream.unfold (\i -> ((qid $ B.pack $ show i) .$ "fresh", i + 1)) 0 defaultClause :: Id Record -> Int -> Hs.Match defaultClause x n = Hs.Match (*) (Hs.name "__") (Stream.take n (Stream.map Hs.pvar variables)) Nothing (Hs.UnGuardedRhs (foldl' (\e x -> [$hs| App $e $x |]) (constant x) (Stream.take n (Stream.map Hs.var variables)))) Hs.noBinds constant c = [$hs| Con $(Hs.strE $ show $ pretty c) |] pattern :: Em Env -> Em Expr -> Hs.Pat pattern env (Var x _) | x `isin` env = Hs.pvar (varName x) pattern env expr = case unapply expr of Var x _ : xs -> primAppsP x (map (pattern env) xs) -- | Build a pattern matching constant. primConP c = Hs.PParen (Hs.pApp (Hs.name "Con") [Hs.strP (show (pretty c))]) primAppP t1 t2 = Hs.PParen (Hs.pApp (Hs.name "App") [t1, t2]) primAppsP c = foldl' primAppP (primConP c) -- | Turn an expression into object code with types erased. code :: Em Expr -> Hs.Exp code (Var x _) = var x code (Lam (x ::: ty) t _) | n <- varName x = [$hs| Lam (\((n)) -> $(code t)) |] code (Lam (Hole ty) t _) = [$hs| Lam (\_ -> $(code t)) |] code (Pi (x ::: ty) t _) | n <- varName x = [$hs| Pi $(code ty) (\((n)) -> $(code t)) |] code (Pi (Hole ty) t _) = [$hs| Pi $(code ty) (\_ -> $(code t)) |] code (App t1 t2 _) = [$hs| ap $(code t1) $(code t2) |] code Type = [$hs| Type |] -- | Turn a term into its Haskell representation, including all types. term :: Em Expr -> Hs.Exp term (Var x _) = var (x .$ "box") term (Lam b t _) = typedAbstraction [$hs| TLam |] b (term t) term (Pi b t _) = typedAbstraction [$hs| TPi |] b (term t) term (App t1 t2 _) = [$hs| TApp $(term t1) (UBox $(term t2) $(code t2)) |] term Type = [$hs| TType |] typedAbstraction c b t = case b of x ::: ty -> [$hs| $c $(dom ty) (\((box)) -> $ran) |] where box = varName (x .$ "box") ran = let n = varName x in [$hs| let ((n)) = obj $(Hs.var box) in $t |] Hole ty -> [$hs| $c $(dom ty) (\_ -> $t) |] where dom ty = if isVariable ty then term ty else [$hs| sbox $(term ty) Type $(code ty) |] varName :: Id Record -> Hs.Name varName = Hs.name . xencode . unqualify -- | Smart variable constructor. var :: Id Record -> Hs.Exp var = Hs.var . Hs.name . xencode -- | Produce a set of variables y1, ..., yn variables = Stream.unfold (\i -> (Hs.name $ ('y':) $ show i, i + 1)) 0 -- | Capitalize a word. capitalize :: B.ByteString -> B.ByteString capitalize s = case B.uncons s of Nothing -> B.empty Just (x, xs) -> toUpper x `B.cons` xs