1- module LambdaBuffers.Frontend (runFrontend , FrontendError (.. ), FrontendResult (.. ), Symbol , Scope , parseModule ) where
1+ module LambdaBuffers.Frontend (runFrontend , FrontendError (.. ), FrontendResult (.. ), parseModule ) where
22
3- import Control.Monad (foldM , void , when )
4- import Control.Monad.Except (ExceptT , runExceptT )
3+ import Control.Monad (foldM , when )
4+ import Control.Monad.Except (runExceptT )
55import Control.Monad.State.Strict (MonadIO (liftIO ), MonadTrans (lift ), StateT (runStateT ), gets , modify )
6- import Control.Monad.Trans.Except (throwE )
76import Control.Monad.Trans.Reader (ReaderT (runReaderT ), asks , local )
8- import Data.Foldable (for_ )
97import Data.List (isSuffixOf )
10- import Data.Map (Map )
118import Data.Map qualified as Map
12- import Data.Maybe (fromMaybe )
13- import Data.Set (Set )
14- import Data.Set qualified as Set
159import Data.Text (Text , unpack )
1610import Data.Text.IO qualified as Text
17- import Data.Traversable (for )
11+ import LambdaBuffers.Frontend.CheckReferences (checkReferences )
12+ import LambdaBuffers.Frontend.Errors (FrontendError )
13+ import LambdaBuffers.Frontend.Errors qualified as Errors
14+ import LambdaBuffers.Frontend.Monad (FrontRead (FrontRead , current , importPaths , visited ), FrontState (FrontState , importedModules ), FrontendResult (FrontendResult ), FrontendT , throwE' )
1815import LambdaBuffers.Frontend.PPrint ()
1916import LambdaBuffers.Frontend.Parsec qualified as Parsec
20- import LambdaBuffers.Frontend.Syntax ( Constructor ( Constructor ), Field ( Field ), Import ( Import , importInfo , importModuleName ), Module ( moduleImports , moduleName , moduleTyDefs ), ModuleAlias ( ModuleAlias ), ModuleName ( ModuleName ), ModuleNamePart ( ModuleNamePart ), Product ( Product ), Record ( Record ), SourceInfo , Sum ( Sum ), Ty ( TyApp , TyRef' , TyVar ), TyBody ( Opaque , ProductBody , RecordBody , SumBody ), TyDef ( TyDef , tyBody , tyDefInfo , tyName ), TyName ( TyName ), TyRef ( TyRef ), defSourceInfo )
21- import Prettyprinter ( Doc , LayoutOptions ( layoutPageWidth ), PageWidth ( Unbounded ), Pretty ( pretty ), defaultLayoutOptions , layoutPretty , (<+>) )
22- import Prettyprinter.Render.String ( renderShowS )
17+ import LambdaBuffers.Frontend.Scope ( addToClassScope , addToTyScope , collectImportedScope , collectLocalScope )
18+ import LambdaBuffers.Frontend.Syntax ( Import ( importModuleName ), Module ( moduleImports , moduleName ), ModuleName ( ModuleName ), ModuleNamePart ( ModuleNamePart ), SourceInfo , defSourceInfo )
19+ import LambdaBuffers.Frontend.Utils ( ClassScope , TyScope , strip )
2320import System.Directory (findFiles )
2421import System.FilePath (joinPath , (<.>) )
25- import Text.Parsec (ParseError )
26-
27- data FrontRead = FrontRead
28- { current :: ModuleName SourceInfo
29- , visited :: [ModuleName () ]
30- , importPaths :: [FilePath ]
31- }
32- deriving stock (Eq , Show )
33-
34- newtype FrontState = FrontState
35- { importedModules :: Map (ModuleName () ) (Module SourceInfo , Scope )
36- }
37- deriving stock (Eq , Show )
38-
39- type Symbol = TyRef ()
40- type Scope = Map Symbol (ModuleName SourceInfo )
41-
42- data FrontendError
43- = ModuleNotFound (ModuleName SourceInfo ) (Import SourceInfo ) [FilePath ]
44- | MultipleModulesFound (ModuleName SourceInfo ) (Import SourceInfo ) [FilePath ]
45- | ImportCycleFound (ModuleName SourceInfo ) (Import SourceInfo ) [ModuleName () ]
46- | ModuleParseError FilePath ParseError
47- | ImportedNotFound (ModuleName SourceInfo ) (ModuleName SourceInfo ) (TyName SourceInfo ) (Set (TyName SourceInfo ))
48- | InvalidModuleFilepath (ModuleName SourceInfo ) FilePath FilePath
49- | SymbolAlreadyImported (ModuleName SourceInfo ) (Import SourceInfo ) Symbol (ModuleName SourceInfo )
50- | TyRefNotFound (ModuleName SourceInfo ) (TyRef SourceInfo ) Scope
51- | DuplicateTyDef (ModuleName SourceInfo ) (TyDef SourceInfo )
52- | TyDefNameConflict (ModuleName SourceInfo ) (TyDef SourceInfo ) (ModuleName SourceInfo )
53- deriving stock (Eq )
54-
55- newtype FrontendResult = FrontendResult
56- { processedModules :: Map (ModuleName () ) (Module SourceInfo , Scope )
57- }
58- deriving stock (Eq , Show )
59-
60- showOneLine :: Doc a -> String
61- showOneLine d = (renderShowS . layoutPretty (defaultLayoutOptions {layoutPageWidth = Unbounded }) $ d) " "
62-
63- instance Show FrontendError where
64- show (ModuleNotFound _cm imp impPaths) = showOneLine $ pretty (importInfo imp) <+> " Module" <+> pretty (importModuleName imp) <+> " not found in available import paths" <+> pretty impPaths
65- show (MultipleModulesFound _cm imp conflictingPaths) = showOneLine $ pretty (importInfo imp) <+> " Module" <+> pretty (importModuleName imp) <+> " found in multiple files" <+> pretty conflictingPaths
66- show (ImportCycleFound _cm imp visited) = showOneLine $ pretty (importInfo imp) <+> " Tried to load module" <+> pretty (importModuleName imp) <+> " which constitutes a cycle" <+> pretty visited
67- show (ModuleParseError _fp err) = showOneLine $ pretty err
68- show (ImportedNotFound _cm mn tn@ (TyName _ info) available) = showOneLine $ pretty info <+> " Type" <+> pretty tn <+> " not found in module" <+> pretty mn <> " , did you mean one of" <+> pretty (Set. toList available)
69- show (InvalidModuleFilepath mn@ (ModuleName _ info) gotModFp wantedFpSuffix) = showOneLine $ pretty info <+> " File name" <+> pretty gotModFp <+> " doesn't match module name" <+> pretty mn <+> " expected" <+> pretty wantedFpSuffix
70- show (SymbolAlreadyImported _cm imp sym alreadyInModuleName) = showOneLine $ pretty (importInfo imp) <+> " Symbol" <+> pretty sym <+> " already imported from module" <+> pretty alreadyInModuleName
71- show (TyRefNotFound _cm tyR@ (TyRef _ _ info) scope) = showOneLine $ pretty info <+> " Type " <> pretty tyR <+> " not found in the module's scope" <+> (pretty . Map. keys $ scope)
72- show (DuplicateTyDef _cm tyDef) = showOneLine $ pretty (tyDefInfo tyDef) <+> " Duplicate type definition with the name" <+> pretty (tyName tyDef)
73- show (TyDefNameConflict _cm tyDef imn) = showOneLine $ pretty (tyDefInfo tyDef) <+> " Type name" <+> pretty (tyName tyDef) <+> " conflicts with an imported type name from module" <+> pretty imn
74-
75- type FrontendT m a = MonadIO m = > ReaderT FrontRead (StateT FrontState (ExceptT FrontendError m )) a
7622
7723-- | Run a Frontend compilation action on a "lbf" file, return the entire compilation closure or a frontend error.
7824runFrontend :: MonadIO m => [FilePath ] -> FilePath -> m (Either FrontendError FrontendResult )
@@ -82,29 +28,23 @@ runFrontend importPaths modFp = do
8228 ioM = runExceptT exM
8329 fmap (FrontendResult . importedModules . snd ) <$> ioM
8430
85- throwE' :: FrontendError -> FrontendT m a
86- throwE' = lift . lift . throwE
87-
8831moduleNameToFilepath :: ModuleName info -> FilePath
8932moduleNameToFilepath (ModuleName parts _) = joinPath [unpack p | ModuleNamePart p _ <- parts] <.> " lbf"
9033
9134checkCycle :: Import SourceInfo -> FrontendT m ()
9235checkCycle imp = do
9336 ms <- asks visited
9437 cm <- asks current
95- when ((strip . importModuleName $ imp) `elem` ms) $ throwE' $ ImportCycleFound cm imp ms
38+ when ((strip . importModuleName $ imp) `elem` ms) $ throwE' $ Errors. ImportCycleFound cm imp ms
9639
97- -- | Parse a LambdaBuffers modules with a specified filename (for reporting) and content.
40+ -- | `parseModule fp txt` parses a LambdaBuffers module with a specified filename `fp` (for reporting) and content in `txt` .
9841parseModule :: FilePath -> Text -> FrontendT m (Module SourceInfo )
9942parseModule modFp modContent = do
10043 modOrErr <- liftIO $ Parsec. runParser Parsec. parseModule modFp modContent
10144 case modOrErr of
102- Left err -> throwE' $ ModuleParseError modFp err
45+ Left err -> throwE' $ Errors. ModuleParseError modFp err
10346 Right m -> return m
10447
105- strip :: Functor f => f a -> f ()
106- strip = void
107-
10848importModule :: Import SourceInfo -> FrontendT m (Module SourceInfo )
10949importModule imp = do
11050 let modName = importModuleName imp
@@ -116,16 +56,16 @@ importModule imp = do
11656 case found of
11757 [] -> do
11858 cm <- asks current
119- throwE' $ ModuleNotFound cm imp ips
59+ throwE' $ Errors. ModuleNotFound cm imp ips
12060 [modFp] -> do
12161 modContent <- liftIO $ Text. readFile modFp
12262 modOrErr <- liftIO $ Parsec. runParser Parsec. parseModule modFp modContent
12363 case modOrErr of
124- Left err -> throwE' $ ModuleParseError modFp err
64+ Left err -> throwE' $ Errors. ModuleParseError modFp err
12565 Right m -> return m
12666 modFps -> do
12767 cm <- asks current
128- throwE' $ MultipleModulesFound cm imp modFps
68+ throwE' $ Errors. MultipleModulesFound cm imp modFps
12969 Just (m, _) -> return m
13070
13171processFile :: FilePath -> FrontendT m (Module SourceInfo )
@@ -147,101 +87,34 @@ processModule m = local
14787 importedScope <- processImports m
14888 localScope <- collectLocalScope m importedScope
14989 let modScope = localScope <> importedScope
150- checkReferences modScope m
90+ checkReferences m modScope
15191 _ <- lift $ modify (FrontState . Map. insert (strip . moduleName $ m) (m, modScope) . importedModules)
15292 return m
15393
154- checkReferences :: Scope -> Module SourceInfo -> FrontendT m ()
155- checkReferences scope m = for_ (moduleTyDefs m) (checkBody . tyBody)
156- where
157- checkBody (SumBody (Sum cs _)) = for_ cs checkConstructor
158- checkBody (ProductBody (Product fields _)) = for_ fields checkTy
159- checkBody (RecordBody (Record fields _)) = for_ fields (\ (Field _ ty _) -> checkTy ty)
160- checkBody Opaque = return ()
161-
162- checkConstructor (Constructor _ (Product tys _) _) = for tys checkTy
163-
164- checkTy (TyApp tyF tyAs _) = checkTy tyF >> for_ tyAs checkTy
165- checkTy (TyVar _) = return ()
166- checkTy (TyRef' tyR _) =
167- if Map. member (strip tyR) scope
168- then return ()
169- else do
170- cm <- asks current
171- throwE' $ TyRefNotFound cm tyR scope
172-
17394checkModuleName :: FilePath -> ModuleName SourceInfo -> FrontendT m ()
17495checkModuleName fp mn =
17596 let suffix = moduleNameToFilepath mn
17697 in if suffix `isSuffixOf` fp
17798 then return ()
178- else throwE' $ InvalidModuleFilepath mn fp suffix
179-
180- processImports :: Module SourceInfo -> FrontendT m Scope
181- processImports m =
182- foldM
183- ( \ totalScope imp -> do
184- scope <- processImport imp
185- foldM
186- ( \ totalScope' sym -> case Map. lookup sym totalScope' of
187- Nothing -> return $ Map. insert sym (importModuleName imp) totalScope'
188- Just mn -> do
189- cm <- asks current
190- throwE' $ SymbolAlreadyImported cm imp sym mn
191- )
192- totalScope
193- scope
194- )
195- mempty
196- (moduleImports m)
197-
198- processImport :: Import SourceInfo -> FrontendT m (Set Symbol )
99+ else throwE' $ Errors. InvalidModuleFilepath mn fp suffix
100+
101+ processImports :: Module SourceInfo -> FrontendT m (TyScope , ClassScope )
102+ processImports m = do
103+ (totalTyScope, totalClassScope) <-
104+ foldM
105+ ( \ (totalTyScope', totalClassScope') imp ->
106+ do
107+ (tyScope, classScope) <- processImport imp
108+ (,)
109+ <$> addToTyScope totalTyScope' (imp, tyScope)
110+ <*> addToClassScope totalClassScope' (imp, classScope)
111+ )
112+ mempty
113+ (moduleImports m)
114+ return (totalTyScope, totalClassScope)
115+
116+ processImport :: Import SourceInfo -> FrontendT m (TyScope , ClassScope )
199117processImport imp = do
200118 checkCycle imp
201119 im <- importModule imp >>= processModule
202120 collectImportedScope imp im
203-
204- collectImportedScope :: Import SourceInfo -> Module SourceInfo -> FrontendT m (Set Symbol )
205- collectImportedScope (Import isQual modName mayImports mayAlias _) m =
206- let availableTyNs = [tyN | (TyDef tyN _ _ _) <- moduleTyDefs m]
207- availableTyNs' = Set. fromList availableTyNs
208- importedTyNs = fromMaybe availableTyNs mayImports
209- availableSTyNs = Set. fromList $ strip <$> availableTyNs
210- in foldM
211- ( \ total tyN ->
212- let styN = strip tyN
213- in if Set. member styN availableSTyNs
214- then return . Set. union total . Set. fromList $
215- case mayAlias of
216- Nothing ->
217- if isQual
218- then [TyRef (Just $ ModuleAlias (strip modName) () ) styN () ]
219- else [TyRef (Just $ ModuleAlias (strip modName) () ) styN () , TyRef Nothing styN () ]
220- Just al ->
221- if isQual
222- then [TyRef (Just . strip $ al) styN () ]
223- else [TyRef (Just . strip $ al) styN () , TyRef Nothing styN () ]
224- else do
225- cm <- asks current
226- throwE' $ ImportedNotFound cm modName tyN availableTyNs'
227- )
228- Set. empty
229- importedTyNs
230-
231- collectLocalScope :: Module SourceInfo -> Scope -> FrontendT m Scope
232- collectLocalScope m importedScope =
233- foldM
234- ( \ totalScope tyDef@ (TyDef tn _ _ _) -> do
235- let tyR = TyRef Nothing (strip tn) ()
236- case Map. lookup tyR totalScope of
237- Nothing -> case Map. lookup tyR importedScope of
238- Nothing -> return $ Map. insert tyR (moduleName m) totalScope
239- Just im -> do
240- cm <- asks current
241- throwE' $ TyDefNameConflict cm tyDef im
242- Just _ -> do
243- cm <- asks current
244- throwE' $ DuplicateTyDef cm tyDef
245- )
246- mempty
247- (moduleTyDefs m)
0 commit comments