module ABS.Compiler.Codegen.Mod
( tModul
) where
import ABS.Compiler.Firstpass.Base
import ABS.Compiler.Utils
import ABS.Compiler.Codegen.Dec (tDataInterfDecl,tRestDecl)
import ABS.Compiler.Codegen.Stm (tMethod)
import ABS.Compiler.CmdOpt
import qualified ABS.AST as ABS
import qualified Language.Haskell.Exts.Simple.Syntax as HS
import Language.Haskell.Exts.QQ (hs, dec)
import qualified Data.Map as M (Map, lookup, keys, empty, foldlWithKey, member, toAscList)
import Data.List (find, partition)
import Data.Maybe (fromJust, fromMaybe, mapMaybe)
import Data.Char (isUpper)
tModul :: (?absFileName::String)
=> ABS.Module
-> M.Map ModuleName SymbolTable
-> HS.Module
tModul (ABS.Module thisModuleQU exports imports decls maybeMain) allSymbolTables = HS.Module
(Just $ HS.ModuleHead
(HS.ModuleName thisModuleName)
Nothing
(Just $ HS.ExportSpecList $ (case maybeMain of
ABS.JustBlock _ -> ((HS.EVar $ HS.UnQual $ HS.Ident "main") :)
ABS.NoBlock -> id) $ concatMap tExport exports)
)
[ HS.LanguagePragma [ HS.Ident "NoImplicitPrelude"
, HS.Ident "ExistentialQuantification"
, HS.Ident "MultiParamTypeClasses"
, HS.Ident "ScopedTypeVariables"
, HS.Ident "FlexibleContexts"
, HS.Ident "PartialTypeSignatures"
, HS.Ident "NamedWildCards"
, HS.Ident "LambdaCase"
, HS.Ident "OverloadedStrings"
, HS.Ident "TemplateHaskell"
]
, HS.OptionsPragma (Just HS.GHC) "-w -Werror -fforce-recomp -fwarn-missing-methods -fno-ignore-asserts"
]
((if nostdlib cmdOpt
then id
else
(HS.ImportDecl { HS.importModule = HS.ModuleName "ABS.StdLib"
, HS.importQualified = False
, HS.importAs = Nothing
, HS.importSrc = False, HS.importPkg = Nothing, HS.importSpecs = Nothing, HS.importSafe = False
} :))
([ HS.ImportDecl { HS.importModule = HS.ModuleName "ABS.Runtime"
, HS.importQualified = False
, HS.importAs = Nothing
, HS.importSrc = False, HS.importPkg = Nothing, HS.importSpecs = Nothing, HS.importSafe = False
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Data.Function"
, HS.importQualified = False
, HS.importAs = Nothing
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Symbol "."]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Control.Applicative"
, HS.importQualified = False
, HS.importAs = Nothing
, HS.importSpecs =Just $ HS.ImportSpecList False [HS.IVar $ HS.Symbol "<*>", HS.IVar $ HS.Symbol "*>"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Control.Monad"
, HS.importQualified = False
, HS.importAs = Nothing
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Symbol "=<<"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Control.Applicative"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "pure"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Data.IORef"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs =Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "newIORef", HS.IVar $ HS.Ident "readIORef", HS.IVar $ HS.Ident "writeIORef", HS.IVar $ HS.Ident "atomicModifyIORef'"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Control.Monad.Trans.Class"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "lift"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Control.Monad"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs =Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "Monad", HS.IVar $ HS.Ident "when", HS.IVar $ HS.Ident "sequence", HS.IVar $ HS.Ident "join"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Prelude"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "IO", HS.IVar $ HS.Ident "Eq", HS.IThingAll $ HS.Ident "Ord", HS.IThingAll $ HS.Ident "Show", HS.IVar $ HS.Ident "undefined", HS.IVar $ HS.Ident "error", HS.IVar $ HS.Ident "negate", HS.IVar $ HS.Ident "fromIntegral", HS.IVar $ HS.Ident "mapM_", HS.IVar $ HS.Ident "id"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Unsafe.Coerce"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "unsafeCoerce"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Control.Concurrent"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "ThreadId"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Control.Concurrent.MVar"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "isEmptyMVar", HS.IVar $ HS.Ident "readMVar"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Control.Exception"
, HS.importQualified = False
, HS.importAs = Nothing
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "assert"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Control.Exception"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs =Just $ HS.ImportSpecList False [HS.IThingAll $ HS.Ident "Exception", HS.IVar $ HS.Ident "SomeException", HS.IVar $ HS.Ident "throwTo", HS.IVar $ HS.Ident "throw"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Data.Dynamic"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "toDyn", HS.IVar $ HS.Ident "fromDynamic"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Data.Map.Lazy"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "lookup", HS.IVar $ HS.Ident "insert"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Web.Scotty"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "get", HS.IVar $ HS.Ident "param", HS.IVar $ HS.Ident "json", HS.IVar $ HS.Ident "raise"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
, HS.ImportDecl { HS.importModule = HS.ModuleName "Data.Generics.Genifunctors"
, HS.importQualified = True
, HS.importAs = Just (HS.ModuleName "I'")
, HS.importSpecs = Just $ HS.ImportSpecList False [HS.IVar $ HS.Ident "genFmap"]
, HS.importSrc = False, HS.importSafe = False, HS.importPkg = Nothing
}
]
++ concatMap tImport imports
))
(let ?st = st
in let (dataDecls, restDecls) = partition (\case
ABS.DData _ _ -> True
ABS.DDataPoly _ _ _ -> True
ABS.DInterf _ _ -> True
ABS.DExtends _ _ _ -> True
_ -> False) $ map (\ (ABS.AnnDecl _ d) -> d) decls
in [dec|default (Int,Rat)|]
: concatMap tDataInterfDecl dataDecls
++ [HS.SpliceDecl [hs|return []|]]
++ foldl (\ acc -> \case
ABS.DDataPoly (ABS.U (_,tid)) _ _ -> HS.PatBind (HS.PVar $ HS.Ident $ "fmap'" ++ tid) (HS.UnGuardedRhs $
HS.SpliceExp $ HS.ParenSplice $ [hs|I'.genFmap|] `HS.App` HS.TypQuote (HS.UnQual $ HS.Ident tid)) Nothing : acc
_ -> acc) [] dataDecls
++ concatMap tRestDecl restDecls
++ tMain maybeMain)
where
thisModuleName = showQU thisModuleQU
st = fromJust $ M.lookup thisModuleName allSymbolTables
tExport :: ABS.Export -> [HS.ExportSpec]
tExport ABS.StarExport = [HS.EModuleContents $ HS.ModuleName thisModuleName]
tExport (ABS.StarFromExport qtyp) = [HS.EModuleContents $ HS.ModuleName $ showQU qtyp]
tExport (ABS.AnyExport es) = concatMap (\ iden ->
let symbolName = fromMaybe (error "symbol not in scope") $ find (\ (SN sname mimported) -> sname == showQA iden && case mimported of
Nothing -> True
Just (_, False) -> True
_ -> False) (M.keys st)
SV symbolValue _ = fromJust $ M.lookup symbolName st
in case symbolValue of
Interface _ _ -> [ EThingAll $ HS.UnQual $ HS.Ident $ showQA iden ++ "'"
, EThingAll $ HS.UnQual $ HS.Ident $ showQA iden]
Datacons dname _ _ _ -> [HS.EThingWith HS.NoWildcard (HS.UnQual $ HS.Ident dname)
[HS.ConName $ HS.Ident $ showQA iden]]
Exception -> [EThingAll $ HS.UnQual $ HS.Ident $ showQA iden,
HS.EVar $ HS.UnQual $ HS.Ident $ headToLower $ showQA iden ++ "'"
]
Class _ _ -> [EThingAll $ HS.UnQual $ HS.Ident $ showQA iden
,HS.EVar $ HS.UnQual $ HS.Ident $ "smart'" ++ showQA iden
,HS.EVar $ HS.UnQual $ HS.Ident $ "init'" ++ showQA iden
]
_ -> [HS.EVar $ HS.UnQual $ HS.Ident $ showQA iden] ) es
tExport (ABS.AnyFromExport es qtyp) = concatMap (\ iden ->
let symbolName@(SN _ (Just (mname',isQualified'))) = fromMaybe (error "symbol not in scope") $
find (\ (SN sname mimported) -> sname == showQA iden && case mimported of
Just (mname'',_) -> mname'' == showQU qtyp
_ -> False) (M.keys st)
SV symbolValue _ = fromJust $ M.lookup symbolName st
maybeQual = if isQualified'
then HS.Qual (HS.ModuleName mname')
else HS.UnQual
in case symbolValue of
Interface _ _ -> [EThingAll $ maybeQual $ HS.Ident $ showQA iden]
Datacons dname _ _ _ -> [HS.EThingWith HS.NoWildcard (maybeQual $ HS.Ident dname)
[HS.ConName $ HS.Ident $ showQA iden]]
Exception -> [EThingAll $ maybeQual $ HS.Ident $ showQA iden,
HS.EVar $ maybeQual $ HS.Ident $ headToLower $ showQA iden ++ "'"
]
Class _ _ -> [EThingAll $ maybeQual $ HS.Ident $ showQA iden,
HS.EVar $ maybeQual $ HS.Ident $ headToLower $ showQA iden ++ "'"
]
_ -> [HS.EVar $ maybeQual $ HS.Ident $ showQA iden]
) es
tImport :: (?absFileName::String) => ABS.Import -> [HS.ImportDecl]
tImport (ABS.StarFromImport _ityp qu) = [HS.ImportDecl (HS.ModuleName $ showQU qu)
False
False False Nothing Nothing
(Just $ HS.ImportSpecList True [HS.IVar $ HS.Ident "main"])]
tImport (ABS.AnyImport _ityp qas) = mapMaybe (\ qa ->
let (prefix, iden) = splitQA qa
in if prefix == "ABS.StdLib." && not (nostdlib cmdOpt)
then Nothing
else Just $ HS.ImportDecl (HS.ModuleName prefix)
True
False False Nothing Nothing
(Just $ HS.ImportSpecList False $ tImport' True prefix iden)
) qas
tImport (ABS.AnyFromImport _ityp qas qu) = if showQU qu == "ABS.StdLib" && not (nostdlib cmdOpt)
then []
else [HS.ImportDecl (HS.ModuleName $ showQU qu)
False
False False Nothing Nothing
(Just $ HS.ImportSpecList False $ concatMap (tImport' False (showQU qu) . showQA) qas)]
tImport' :: IsQualified -> String -> String -> [HS.ImportSpec]
tImport' isQualified moduleName iden =
let
symbolName = SN iden $ Just (moduleName, isQualified)
SV symbolValue _ = fromJust $ M.lookup symbolName st
in case symbolValue of
Interface _ _ -> [HS.IThingAll $ HS.Ident iden]
Datacons dname _ _ _ -> [HS.IThingWith (HS.Ident dname)
[HS.ConName $ HS.Ident iden]]
Exception -> [HS.IThingAll $ HS.Ident iden,
HS.IVar $ HS.Ident $ headToLower iden ++ "'"
]
Class _ _ -> [HS.IThingAll $ HS.Ident iden,
HS.IVar $ HS.Ident $ headToLower iden ++ "'"
]
Foreign -> [if isUpper $ head iden
then HS.IThingAll $ HS.Ident iden
else HS.IVar $ HS.Ident iden]
_ -> [HS.IVar $ HS.Ident iden]
tMain :: (?st::SymbolTable) => ABS.MaybeBlock -> [HS.Decl]
tMain ABS.NoBlock = []
tMain (ABS.JustBlock block) =
let callableMethods :: [(String,String,[String])]
callableMethods = M.foldlWithKey (\ acc k v -> case k of
SN iname Nothing -> case v of
SV (Interface dmethods _imethods) _ -> foldl (\ acc' (mname, mfparams) ->
case mfparams of
Nothing -> acc'
Just fparams -> (iname,mname,fparams):acc') [] dmethods ++ acc
_ -> acc
_ -> acc
) [] ?st
makeCallable :: (String,String,[String]) -> HS.Stmt
makeCallable (iname,mname,fparams) = HS.Qualifier
[hs|I'.get $(HS.Lit $ HS.String $ "/call/:httpName'/" ++ mname) (do
objs' <- I'.lift (I'.readIORef apiStore')
httpName' <- I'.param "httpName'"
case I'.lookup httpName' objs' of
Just obj' -> I'.json =<< $casts
Nothing -> I'.raise "no such object name")
|]
where ipat = iname ++ " obj''"
mcalled = foldl (\ acc str -> [hs|$acc <*> I'.param $(HS.Lit $ HS.String str)|])
[hs|I'.pure $(HS.Var $ HS.UnQual $ HS.Ident mname)|] fparams
subInterfaces = foldl (\ acc -> \case
(SN iname' _, SV (Interface _ extends) _) -> if SN iname Nothing `M.member` extends then iname':acc else acc
_ -> acc
) [] (M.toAscList ?st)
makeSubCast acc iname' = [hs|case I'.fromDynamic obj' of
Just (__ipat'__) -> do
mapplied' <- $mcalled
I'.lift (get =<< obj'' <!> mapplied')
Nothing -> $acc|]
where ipat' = iname' ++ " obj''"
casts = foldl makeSubCast [hs|case I'.fromDynamic obj' of
Just (__ipat__) -> do
mapplied' <- $mcalled
I'.lift (get =<< obj'' <!> mapplied')
Nothing -> I'.raise "wrong interface"|] subInterfaces
scottyAction = if null callableMethods
then [hs|I'.pure ()|]
else HS.Do $ map makeCallable callableMethods
in [[dec|main = main_is' (\ this -> $(tMethod block [] M.empty "" [] False ABS.TInfer)) ($scottyAction)|]]
pattern EThingAll x = HS.EThingWith (HS.EWildcard 1) x []