@ -1,2 +1,4 @@ | |||||
import Distribution.Simple | import Distribution.Simple | ||||
main = defaultMain | main = defaultMain |
@ -0,0 +1,37 @@ | |||||
{-# LANGUAGE RankNTypes #-} | |||||
module Ahc.Data.Lens where | |||||
import Control.Applicative | |||||
import Data.Functor.Identity | |||||
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t | |||||
type Lens' s a = Lens s s a a | |||||
get :: Lens s t a b -> s -> a | |||||
get l = getConst . l Const | |||||
over :: Lens s t a b -> (a -> b) -> s -> t | |||||
over l m = runIdentity . l (Identity . m) | |||||
set :: Lens s t a b -> b -> s -> t | |||||
set l b a = over l (const b) a | |||||
(.~) :: Lens s t a b -> b -> s -> t | |||||
(.~) = set | |||||
infixr 4 .~ | |||||
(%~) :: Lens s t a b -> (a -> b) -> s -> t | |||||
(%~) = over | |||||
infixr 4 %~ | |||||
(^.) :: s -> Lens s t a b -> a | |||||
x ^. l = get l x | |||||
(&) :: t1 -> (t1 -> t2) -> t2 | |||||
x & f = f x | |||||
infixr 0 & | |||||
lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b | |||||
lens sa sbt afb s = sbt s <$> afb (sa s) |
@ -0,0 +1,44 @@ | |||||
{-# LANGUAGE MultiParamTypeClasses #-} | |||||
{-# LANGUAGE FunctionalDependencies #-} | |||||
{-# LANGUAGE FlexibleInstances #-} | |||||
module Ahc.Data.Lens.Tuple where | |||||
import Ahc.Data.Lens | |||||
import Data.Functor.Identity | |||||
class Field1 s t a b | s -> a, t -> b, s b -> t, t a -> s where | |||||
_1 :: Lens s t a b | |||||
instance Field1 (Identity a) (Identity a) a a where | |||||
_1 = lens runIdentity (\_ x -> Identity x) | |||||
instance Field1 (a, b) (a', b) a a' where | |||||
_1 = lens (\(~(x, _)) -> x) (\(~(_, y)) x -> (x, y)) | |||||
instance Field1 (a, b, c) (a', b, c) a a' where | |||||
_1 = lens (\(~(x, _, _)) -> x) (\(~(_, y, z)) x -> (x, y, z)) | |||||
instance Field1 (a, b, c, d) (a', b, c, d) a a' where | |||||
_1 = lens (\(~(x, _, _, _)) -> x) (\(~(_, y, z, a)) x -> (x, y, z, a)) | |||||
class Field2 s t a b | s -> a, t -> b, s b -> t, t a -> s where | |||||
_2 :: Lens s t a b | |||||
instance Field2 (a, b) (a, b') b b' where | |||||
_2 = lens (\(~(_, y)) -> y) (\(~(x, _)) y -> (x, y)) | |||||
instance Field2 (a, b, c) (a, b', c) b b' where | |||||
_2 = lens (\(~(_, x, _)) -> x) (\(~(x, _, z)) y -> (x, y, z)) | |||||
instance Field2 (a, b, c, d) (a, b', c, d) b b' where | |||||
_2 = lens (\(~(_, x, _, _)) -> x) (\(~(x, _, z, a)) y -> (x, y, z, a)) | |||||
class Field3 s t a b | s -> a, t -> b, s b -> t, t a -> s where | |||||
_3 :: Lens s t a b | |||||
instance Field3 (a, b, c) (a, b, c') c c' where | |||||
_3 = lens (\(~(_, _, x)) -> x) (\(~(x, y, _)) z -> (x, y, z)) | |||||
instance Field3 (a, b, c, d) (a, b, c', d) c c' where | |||||
_3 = lens (\(~(_, _, x, _)) -> x) (\(~(x, y, _, a)) z -> (x, y, z, a)) |
@ -0,0 +1,78 @@ | |||||
module Frontend.Lexer.Unicode | |||||
( UnicodeClass(..) | |||||
, classify | |||||
, fudgeCharacterClass | |||||
) where | |||||
import Data.Word (Word8) | |||||
import Data.Char | |||||
-- | A less specfic version of 'GeneralCategory', grouping characters | |||||
-- into a couple of key categories. | |||||
data UnicodeClass | |||||
-- These are used to designate the beginning of symbols | |||||
= Upper | Lower | Symbol | |||||
-- Generic and digit can be used in identifiers, but not at the start of one | |||||
| Generic | Digit | |||||
| Whitespace | |||||
-- These are guaranteed parse order. The only difference is that "graphic" is printable, while | |||||
-- other may not be. | |||||
| OtherGraphic | Other | |||||
deriving (Eq, Show) | |||||
-- | Determine the class for a given character. | |||||
classify :: Char -> UnicodeClass | |||||
classify c = case generalCategory c of | |||||
-- See classification descriptions in | |||||
-- <http://www.unicode.org/reports/tr44/tr44-14.html#GC_Values_Table> | |||||
-- Cased letters | |||||
UppercaseLetter -> Upper | |||||
LowercaseLetter -> Lower | |||||
TitlecaseLetter -> Upper | |||||
ModifierLetter -> Generic | |||||
OtherLetter -> Lower | |||||
NonSpacingMark -> Generic | |||||
SpacingCombiningMark -> OtherGraphic | |||||
EnclosingMark -> OtherGraphic | |||||
DecimalNumber -> Digit | |||||
LetterNumber -> Generic | |||||
OtherNumber -> Digit | |||||
ConnectorPunctuation -> Symbol | |||||
DashPunctuation -> Symbol | |||||
OpenPunctuation -> OtherGraphic | |||||
ClosePunctuation -> OtherGraphic | |||||
InitialQuote -> OtherGraphic | |||||
FinalQuote -> OtherGraphic | |||||
OtherPunctuation -> Symbol | |||||
MathSymbol -> Symbol | |||||
CurrencySymbol -> Symbol | |||||
ModifierSymbol -> Symbol | |||||
-- So this _could_ be Lower or something, just so we can allow for emoji variables. | |||||
-- Hrmrm, maybe not. | |||||
OtherSymbol -> Symbol | |||||
Space -> Whitespace | |||||
-- This is all the wacky things in C* and Z* groups | |||||
_ -> Other | |||||
-- | Convert a character class into a fake byte which will be used by | |||||
-- "Parser.Lexer" | |||||
fudgeCharacterClass :: UnicodeClass -> Word8 | |||||
fudgeCharacterClass Upper = 0xf0 | |||||
fudgeCharacterClass Lower = 0xf1 | |||||
fudgeCharacterClass Symbol = 0xf2 | |||||
fudgeCharacterClass Generic = 0xf3 | |||||
fudgeCharacterClass Digit = 0xf4 | |||||
fudgeCharacterClass Whitespace = 0xf5 | |||||
fudgeCharacterClass OtherGraphic = 0xf6 | |||||
fudgeCharacterClass Other = 0xf7 |
@ -0,0 +1,56 @@ | |||||
{-# LANGUAGE DeriveGeneric #-} | |||||
{-# LANGUAGE DeriveAnyClass #-} | |||||
module Frontend.Syntax.Var where | |||||
import qualified Data.Text as T | |||||
import Data.Text (Text) | |||||
import Development.Shake.Classes | |||||
import Frontend.Parser.Posn | |||||
import GHC.Generics (Generic) | |||||
data ParsedVar | |||||
= UnqualVar | |||||
{ varId :: Text | |||||
, varBegin :: Posn | |||||
, varEnd :: Posn | |||||
} | |||||
| QualVar | |||||
{ varId :: Text | |||||
, varPrefix :: Text | |||||
, varBegin :: Posn | |||||
, varEnd :: Posn | |||||
} | |||||
| ModId | |||||
{ varId :: Text | |||||
, varBegin :: Posn | |||||
, varEnd :: Posn | |||||
} | |||||
| BuiltinId | |||||
{ varId :: Text | |||||
, varBuiltin :: BuiltinIdClass | |||||
, varBegin :: Posn | |||||
, varEnd :: Posn | |||||
} | |||||
deriving (Eq, Show, Generic, Binary, Hashable, NFData) | |||||
data BuiltinIdClass | |||||
= BuiltinTuple !Int | |||||
| BuiltinNil | |||||
| BuiltinArrow | |||||
deriving (Eq, Show, Generic, Binary, Hashable, NFData) | |||||
toModId :: ParsedVar -> ParsedVar | |||||
toModId x@ModId{} = x | |||||
toModId (UnqualVar x y z) = ModId x y z | |||||
toModId (QualVar id pref b e) = ModId (pref <> T.singleton '.' <> id) b e | |||||
toModId BuiltinId{} = | |||||
error "Built-in variable can not be a module identifier!" | |||||
splitModuleIdentifier :: Text -> [Text] | |||||
splitModuleIdentifier t | |||||
| T.null fst = [] | |||||
| otherwise = fst:splitModuleIdentifier (T.drop 1 snd) | |||||
where (fst, snd) = T.span (/= '.') t |
@ -0,0 +1,42 @@ | |||||
{-# LANGUAGE TypeFamilies #-} | |||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-} | |||||
{-# LANGUAGE DeriveGeneric #-} | |||||
{-# LANGUAGE DeriveAnyClass #-} | |||||
{-# LANGUAGE DerivingStrategies #-} | |||||
module Main.Queries where | |||||
import Data.ByteString (ByteString) | |||||
import Development.Shake.Classes | |||||
import Development.Shake | |||||
import Frontend.Syntax.Var (ParsedVar) | |||||
import Frontend.Syntax (FeModule) | |||||
import GHC.Generics (Generic) | |||||
import Rename.Types (FqVar, RenamedMod) | |||||
newtype AhcParsedModule = AhcParsedModule { ahcpmFilePath :: String } | |||||
deriving newtype (Eq, Show, Binary, Hashable, NFData) | |||||
type instance RuleResult AhcParsedModule = FeModule ParsedVar | |||||
newtype AhcRenamedModule = AhcRenamedModule { ahcrnFilePath :: String } | |||||
deriving newtype (Eq, Show, Binary, Hashable, NFData) | |||||
type instance RuleResult AhcRenamedModule = RenamedMod | |||||
data AhcModuleFilepath = | |||||
AhcModuleFilepath | |||||
{ ahcfpImportingModule :: String | |||||
, ahcfpModName :: ParsedVar | |||||
} | |||||
deriving (Eq, Show, Generic, Binary, Hashable, NFData) | |||||
type instance RuleResult AhcModuleFilepath = String | |||||
newtype AhcModuleHash = AhcModuleHash { ahcmhFilePath :: String } | |||||
deriving newtype (Eq, Show, Binary, Hashable, NFData) | |||||
type instance RuleResult AhcModuleHash = ByteString |
@ -0,0 +1,121 @@ | |||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-} | |||||
{-# LANGUAGE TypeFamilies #-} | |||||
module Main.Rules where | |||||
import Control.DeepSeq (deepseq) | |||||
import Control.Concurrent | |||||
import Control.Exception | |||||
import Control.Monad | |||||
import Crypto.Hash.SHA1 as Sha1 | |||||
import qualified Data.ByteString.Lazy as Lbs | |||||
import qualified Data.Text.Lazy.IO as Lt | |||||
import Data.ByteString (ByteString) | |||||
import qualified Data.Text as T | |||||
import Data.Foldable (for_) | |||||
import Development.Shake.FilePath | |||||
import Development.Shake.Classes | |||||
import Development.Shake | |||||
import Errors | |||||
import Frontend.Lexer.Wrapper (runAlex) | |||||
import Frontend.Autogen.Parser | |||||
import Frontend.Parser.Posn | |||||
import Frontend.Syntax.Var | |||||
import Frontend.Syntax | |||||
import GHC.Generics (Generic) | |||||
import Main.Queries | |||||
import Rename.Types (FqVar, ModuleId(..), RenamedMod) | |||||
import qualified Rename.Types as Rv | |||||
import Rename.Rename | |||||
import System.IO | |||||
import Text.Show.Pretty | |||||
findModuleOracle :: Rules () | |||||
findModuleOracle = void $ addOracleCache find where | |||||
find :: AhcModuleFilepath -> Action String | |||||
find (AhcModuleFilepath our_fp (ModId id (Posn sl sc) pe)) = go "" id where | |||||
go fp xs | T.null xs || not (T.singleton '.' `T.isInfixOf` xs) = do | |||||
fp <- pure (fp </> T.unpack xs <.> "hs") | |||||
t <- doesFileExist fp | |||||
unless t . liftIO . throwIO $ | |||||
emptyError { errorMessage = "file does not exist:" | |||||
, errorFilename = our_fp | |||||
, errorBegin = Posn sl (sc + length fp) | |||||
, errorEnd = pe | |||||
} | |||||
pure fp | |||||
go fp xs = do | |||||
(x, xs) <- pure $ T.span (/= '.') xs | |||||
ex <- doesDirectoryExist (T.unpack x) | |||||
unless ex . liftIO . throwIO $ | |||||
emptyError { errorMessage = "directory in module path does not exist:" | |||||
, errorFilename = our_fp | |||||
, errorBegin = Posn sl (sc + length fp) | |||||
, errorEnd = Posn sl (sc + length fp + T.length x) | |||||
} | |||||
go (fp </> T.unpack x) xs | |||||
find _ = undefined | |||||
parserOracle :: Rules (String -> Action (FeModule ParsedVar)) | |||||
parserOracle = fmap (. AhcParsedModule) $ addOracleCache (parse . ahcpmFilePath) where | |||||
parse :: String -> Action (FeModule ParsedVar) | |||||
parse fpath = do | |||||
need [fpath] | |||||
fileContents <- liftIO $ Lt.readFile fpath | |||||
let mod = runAlex fpath fileContents parseMod | |||||
() <- liftIO . evaluate $ rnf mod | |||||
case mod of | |||||
Left e -> liftIO $ throwIO e | |||||
Right x -> pure x | |||||
renamerOracle :: Rules (String -> Action RenamedMod) | |||||
renamerOracle = fmap (. AhcRenamedModule) $ addOracleCache (rename . ahcrnFilePath) where | |||||
rename :: String -> Action RenamedMod | |||||
rename fpath = do | |||||
mod <- askOracle (AhcParsedModule fpath) :: Action (FeModule ParsedVar) | |||||
hash <- askOracle (AhcModuleHash fpath) | |||||
mvar <- liftIO $ newMVar mempty | |||||
let | |||||
modid = ModuleId { Rv.moduleName = varId $ Frontend.Syntax.moduleName mod | |||||
, moduleHash = hash | |||||
, moduleFp = fpath | |||||
} | |||||
rnctx = RenameCtx { rcOurModId = modid | |||||
, rcOurFilePath = fpath | |||||
, rcScope = mempty | |||||
, rcThisModule = mvar | |||||
, rcSpanStart = Posn 1 1 | |||||
, rcSpanEnd = Posn 1 1 | |||||
} | |||||
runRename (renameModule mod) rnctx | |||||
hashOracle :: Rules (String -> Action ByteString) | |||||
hashOracle = fmap (. AhcModuleHash) . addOracleCache $ \(AhcModuleHash fp) -> do | |||||
need [fp] | |||||
fileContents <- liftIO $ Lbs.readFile fp | |||||
pure (Sha1.hashlazy fileContents) | |||||
compilerRules :: [String] -> Rules () | |||||
compilerRules wanted_mods = do | |||||
findModuleOracle | |||||
parser <- parserOracle | |||||
hash <- hashOracle | |||||
rename <- renamerOracle | |||||
action $ do | |||||
for_ wanted_mods $ \path -> do | |||||
ast <- rename path | |||||
liftIO $ pPrint ast |
@ -0,0 +1,252 @@ | |||||
{-# 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 }) |
@ -0,0 +1,81 @@ | |||||
{-# LANGUAGE DeriveAnyClass #-} | |||||
{-# LANGUAGE DeriveGeneric #-} | |||||
{-# LANGUAGE StandaloneDeriving #-} | |||||
module Rename.Types where | |||||
import Ahc.Data.Lens (lens, Lens, Lens') | |||||
import qualified Data.HashMap.Strict as HashMap | |||||
import Data.HashMap.Strict (HashMap) | |||||
import Data.ByteString (ByteString) | |||||
import Data.Text (Text) | |||||
import Development.Shake.Classes | |||||
import Frontend.Parser.Posn | |||||
import Frontend.Syntax | |||||
import GHC.Generics | |||||
data ModuleId | |||||
= ModuleId { moduleName :: Text | |||||
, moduleFp :: String | |||||
, moduleHash :: ByteString | |||||
} | |||||
deriving (Eq, Show, Generic, NFData, Binary, Hashable) | |||||
data Namespace | |||||
= DataCon | |||||
| TypeCon | |||||
| Variable | |||||
| Module | |||||
deriving (Eq, Show, Generic, NFData, Binary, Hashable) | |||||
data FqVar | |||||
= FqVar { fqVarName :: Text | |||||
, fqVarModule :: ModuleId | |||||
, fqVarNamespace :: Namespace | |||||
, fqBegin :: Posn | |||||
, fqEnd :: Posn | |||||
} | |||||
deriving (Eq, Show, Generic, NFData, Binary, Hashable) | |||||
instance HasPosn FqVar where | |||||
startPosn = fqBegin | |||||
endPosn = fqEnd | |||||
span s e var = var { fqBegin = startPosn s, fqEnd = endPosn e } | |||||
data RenamedMod | |||||
= RnmMod { rnmModModule :: FeModule FqVar | |||||
, rnmModSignature :: Scope } | |||||
deriving (Eq, Show, Generic, NFData, Binary, Hashable) | |||||
data Scope | |||||
= Scope | |||||
{ _scopeNames :: HashMap Text [FqVar] | |||||
, _scopeTypes :: HashMap Text [FqVar] | |||||
, _scopeNamespaces :: HashMap Text (FqVar, Scope) } | |||||
deriving (Eq, Show, Generic, NFData, Binary, Hashable) | |||||
scopeNames :: Lens' Scope (HashMap Text [FqVar]) | |||||
scopeNames = lens _scopeNames (\s x -> s { _scopeNames = x}) | |||||
scopeTypes :: Lens' Scope (HashMap Text [FqVar]) | |||||
scopeTypes = lens _scopeTypes (\s x -> s { _scopeTypes = x}) | |||||
scopeNamespaces :: Lens' Scope (HashMap Text (FqVar, Scope)) | |||||
scopeNamespaces = lens _scopeNamespaces (\s x -> s { _scopeNamespaces = x}) | |||||
instance Semigroup Scope where | |||||
Scope nam typ ns <> Scope nam' typ' ns' | |||||
= Scope (nam `merge` nam') (typ `merge` typ') (ns `merge'` ns') | |||||
where | |||||
merge = HashMap.unionWith (<>) | |||||
merge' = HashMap.unionWith (\(x, y) (_, z) -> (x, y <> z)) | |||||
instance Monoid Scope where | |||||
mempty = Scope mempty mempty mempty | |||||
instance (Eq k, Hashable k, Binary k, Binary v) => Binary (HashMap k v) where | |||||
get = HashMap.fromList <$> get | |||||
put = put . HashMap.toList |