{-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE DerivingVia #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} module Rename.Rename where import Ahc.Data.Lens import Control.Monad.IO.Class import Control.Monad.Reader import Control.Concurrent import Control.Exception import qualified Data.HashMap.Strict as HashMap import Data.HashMap.Strict (HashMap) import Data.ByteString (ByteString) import Data.Text (Text) import Debug.Trace import Development.Shake ( Action, askOracle ) import Development.Shake.Classes import Errors import qualified Frontend.Syntax as Fe import Frontend.Parser.Posn import Frontend.Syntax.Var import Frontend.Syntax import GHC.Generics import Main.Queries import Rename.Types import Text.Show.Pretty import Control.Applicative newtype Rename a = RenameM { runRename :: RenameCtx -> Action a } deriving (Functor, Applicative, Monad, MonadIO, MonadReader RenameCtx) via ReaderT RenameCtx Action liftAct :: Action a -> Rename a liftAct a = RenameM $ \x -> a data RenameCtx = RenameCtx { rcOurModId :: ModuleId , rcOurFilePath :: String , rcScope :: Scope , rcThisModule :: MVar Scope , rcSpanStart :: Posn , rcSpanEnd :: Posn } _rcScope :: Lens' RenameCtx Scope _rcScope = lens rcScope (\s x -> s { rcScope = x }) renamerSourcePos :: Posn -> Posn -> RenameCtx -> RenameCtx renamerSourcePos start end x = x { rcSpanStart = start, rcSpanEnd = end } renameModule :: FeModule ParsedVar -> Rename RenamedMod renameModule Fe.Module{moduleExports, moduleImports, moduleItems, Fe.moduleName} = do (imports, scopes) <- fmap unzip $ traverse importModule moduleImports our_mod_id <- asks rcOurModId local (extendBy (mconcat scopes)) $ do items <- renameModuleBody moduleItems this_mod <- liftIO . readMVar =<< asks rcThisModule liftIO $ print this_mod export <- traverse (renameModVarList this_mod) moduleExports our_mod_name <- makeVariable Rename.Types.Module moduleName let scope = restrictToModVarList export this_mod rnm_mod = Fe.Module { Fe.moduleName = our_mod_name , moduleExports = export , moduleImports = imports , moduleItems = items } pure (RnmMod rnm_mod scope) restrictToModVarList :: Maybe [NamespacedItem FqVar] -> Scope -> Scope restrictToModVarList Nothing x = x restrictToModVarList (Just xs) sc = foldr del sc xs where del :: NamespacedItem FqVar -> Scope -> Scope del (IEVar n) = over scopeNames (HashMap.delete (fqVarName n)) del (IECon n) = over scopeNames (HashMap.delete (fqVarName n)) . over scopeTypes (HashMap.delete (fqVarName n)) del (IEModule n) = over scopeNamespaces (HashMap.delete (fqVarName n)) renameModVarList :: Scope -> [NamespacedItem ParsedVar] -> Rename [NamespacedItem FqVar] renameModVarList sc (x:xs) = case x of IEVar pv -> case HashMap.lookup (varId pv) (sc ^. scopeNames) of Just [t] -> (IEVar t:) <$> renameModVarList sc xs _ -> notInScope pv IECon pv -> case HashMap.lookup (varId pv) (sc ^. scopeNames) <|> HashMap.lookup (varId pv) (sc ^. scopeTypes) of Just [t] -> (IECon t:) <$> renameModVarList sc xs _ -> notInScope pv IEModule pv -> case HashMap.lookup (varId pv) (sc ^. scopeNamespaces) of Just (t, _) -> (IEModule t:) <$> renameModVarList sc xs _ -> notInScope pv renameModVarList sc [] = pure [] renameModuleBody :: [ModuleItem ParsedVar] -> Rename [ModuleItem FqVar] renameModuleBody = wrap where wrap [] = pure [] wrap xs = do var <- asks rcThisModule go xs go [] = pure [] go (ModDecl (TySig vars tipe begin end) begin' end':items) = local (renamerSourcePos begin end) $ do tipe <- renameTypeToplevel tipe vars <- traverse (makeVariable_unique Variable scopeNames) vars r <- go items insertVariables scopeNames vars $ pure (ModDecl (TySig (map snd vars) tipe begin end) begin' end':r) go (x:items) = error $ "show item: " ++ ppShow x renameTypeToplevel :: FeType ParsedVar -> Rename (FeType FqVar) renameTypeToplevel (Tytup xs) = Tytup <$> traverse renameTypeToplevel xs renameTypeToplevel (SPType ty s e) = SPType <$> local (renamerSourcePos s e) (renameTypeToplevel ty) <*> pure s <*> pure e -- rename units to units renameTypeToplevel (Tycon (BuiltinId _ (BuiltinTuple 0) _ _)) = pure (Tytup []) renameTypeToplevel (Tycon x) = do var <- useVariable scopeTypes x pure (Tycon var) renameTypeToplevel x = error $ "idk how to rename: " ++ show x extendBy :: Scope -> RenameCtx -> RenameCtx extendBy s rc = rc{rcScope = s <> rcScope rc} importModule :: ModuleImport ParsedVar -> Rename (ModuleImport FqVar, Scope) importModule Import{importMod, importList, importQualified, importAlias, importBegin, importEnd} = do fp <- asks rcOurFilePath our_mod_id <- asks rcOurModId res_fp <- liftAct $ askOracle $ AhcModuleFilepath { ahcfpImportingModule = fp , ahcfpModName = importMod } renamed_import <- liftAct $ askOracle $ AhcRenamedModule res_fp let orig = rnmModSignature renamed_import other_mod = Fe.moduleName $ rnmModModule renamed_import il <- traverse (renameModVarList orig) importList orig <- pure $ restrictToModVarList il orig mod <- pure $ if importQualified then mempty { _scopeNamespaces = HashMap.singleton (varId importMod) (other_mod, orig) } else orig mod <- pure $ case importAlias of Just x -> mod & scopeNamespaces %~ HashMap.insert (varId x) (other_mod, orig) Nothing -> mod our_mod_name <- makeVariable Rename.Types.Module importMod alias <- traverse (makeVariable Rename.Types.Module) importAlias let imp = Import our_mod_name il importQualified alias importBegin importEnd pure (imp, mod) makeVariable :: MonadReader RenameCtx m => Namespace -> ParsedVar -> m FqVar makeVariable nsc x = do context <- ask pure $ FqVar { fqVarName = varId x , fqVarModule = rcOurModId context , fqVarNamespace = nsc , fqBegin = rcSpanStart context , fqEnd = rcSpanEnd context } makeVariable_unique :: Namespace -> Lens' Scope (HashMap Text [FqVar]) -> ParsedVar -> Rename (Text, FqVar) makeVariable_unique this_ns ns x = do var <- asks rcThisModule scope <- liftIO . readMVar $ var let the_hm = scope ^. ns case HashMap.lookup (varId x) the_hm of Just (orig:_) -> redeclaration orig x _ -> do new_var <- makeVariable this_ns x liftIO $ modifyMVar_ var $ \s -> pure $ over ns (insertVariable (varId x) new_var) s pure (varId x, new_var) insertVariable :: Text -> FqVar -> HashMap Text [FqVar] -> HashMap Text [FqVar] insertVariable c v = HashMap.alter go c where go (Just xs) = Just (v:xs) go Nothing = Just [v] insertVariables :: Lens' Scope (HashMap Text [FqVar]) -> [(Text, FqVar)] -> Rename a -> Rename a insertVariables lens vars = local (over (_rcScope . lens) go) where go x = foldr (\(x, y) -> insertVariable x y) x vars useVariable :: Lens' Scope (HashMap Text [FqVar]) -> ParsedVar -> Rename FqVar useVariable final_ns var = do scope <- asks rcScope go scope var where go scope (QualVar id prfx begin end) = case HashMap.lookup prfx (scope ^. scopeNamespaces) of Nothing -> notInScope var Just (_, sc) -> local (\s -> s { rcScope = sc }) $ useVariable final_ns (UnqualVar id begin end) go scope (UnqualVar id begin end) = case HashMap.lookup id (scope ^. final_ns) of Just [var] -> pure var Just vars -> ambiguousVariables vars Nothing -> notInScope var go _ x = error $ "idk how to rename yet: " ++ show x ambiguousVariables :: [FqVar] -> Rename a ambiguousVariables vs = throwRenamer $ \e -> e { errorMessage = show vs } notInScope :: HasPosn x => x -> Rename a notInScope x = local (renamerSourcePos (startPosn x) (endPosn x)) $ throwRenamer $ \e -> e { errorMessage = "variable not in scope" } redeclaration :: FqVar -> ParsedVar -> Rename a redeclaration first v = do liftIO $ pPrint first throwRenamer $ \e -> e { errorMessage = "variables can only have one declaration." , errorInlineDesc = Just "first declaration here" , errorPointers = pointTo v v "redeclaration here" , errorBegin = startPosn first , errorEnd = endPosn first } throwRenamer :: (MonadIO m, MonadReader RenameCtx m) => (AhcError -> AhcError) -> m b throwRenamer cont = do context <- ask let fname = moduleFp (rcOurModId context) errorBegin = rcSpanStart context errorEnd = rcSpanEnd context liftIO . throwIO $ cont (emptyError { errorFilename = fname, errorBegin, errorEnd })