Abbie's Haskell compiler
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

252 lines
8.8 KiB

{-# 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 })