1- {-# LANGUAGE DerivingVia, GeneralizedNewtypeDeriving, OverloadedStrings, DeriveFunctor, DeriveTraversable #-}
1+ {-# LANGUAGE DerivingVia, GeneralizedNewtypeDeriving, OverloadedStrings, DeriveFunctor, DeriveTraversable, NamedFieldPuns #-}
22-- | Compilation monad.
33module Agda2Lambox.Compile.Monad
44 ( CompileM
5+ , CompileEnv (.. )
56 , requireDef
7+ , runCompile
68 , compileLoop
79 , genericError
810 , genericDocError
911 , internalError
1012 , CompiledItem (.. )
13+ , ask
14+ , asks
1115 ) where
1216
1317import Control.Monad ( unless )
18+ import Control.Monad.Reader
1419import Control.Monad.State
1520import Control.Monad.IO.Class ( MonadIO )
21+ import Data.Bifunctor ( bimap )
1622import Data.Set ( Set )
1723import Data.Set qualified as Set
24+ import Data.Set qualified as Set
25+ import Data.Map qualified as Map
26+ import Data.Maybe ( catMaybes )
1827
1928import Agda.Syntax.Abstract (QName )
2029import Agda.Compiler.Backend (getConstInfo , PureTCM , HasConstInfo , HasBuiltins , canonicalName )
2130import Agda.TypeChecking.Monad (MonadDebug , MonadTrace , MonadAddContext )
2231import Agda.TypeChecking.Monad.Debug (MonadDebug , reportSDoc )
2332import Agda.TypeChecking.Monad.Base hiding (initState )
2433import Agda.Utils.List ( mcons )
34+ import Agda.Utils.Monad ( unlessM )
2535import Agda.TypeChecking.Pretty
2636import Control.Monad.Error.Class (MonadError )
2737
28- import Agda2Lambox.Compile.Utils (CompiledItem (.. ), topoSort )
38+ import Data.Foldable (traverse_ )
39+
40+ -- | Compilation Environment
41+ data CompileEnv = CompileEnv
42+ { noBlocks :: Bool
43+ -- ^ When set to 'True', constructors take no arguments,
44+ -- and regular function application is used instead.
45+ }
2946
3047-- | Backend compilation state.
3148data CompileState = CompileState
@@ -37,20 +54,21 @@ data CompileState = CompileState
3754 -- ^ (Locally) required definitions.
3855 }
3956
40- -- | Initial compile state, with a set of names required for compilation .
41- initState :: [ QName ] -> CompileState
42- initState qs = CompileState
43- { seenDefs = Set. fromList qs
44- , compileStack = qs
57+ -- Initial compile state.
58+ initState :: CompileState
59+ initState = CompileState
60+ { seenDefs = Set. empty
61+ , compileStack = []
4562 , requiredDefs = Set. empty
4663 }
4764
4865-- | Backend compilation monad.
49- newtype CompileM a = Compile (StateT CompileState TCM a )
66+ newtype CompileM a = Compile (ReaderT CompileEnv ( StateT CompileState TCM ) a )
5067 deriving newtype (Functor , Applicative , Monad )
5168 deriving newtype (MonadIO , MonadFail , MonadDebug , ReadTCState , MonadTrace )
5269 deriving newtype (MonadError TCErr , MonadTCEnv , MonadTCState , HasOptions , MonadTCM )
5370 deriving newtype (MonadAddContext , MonadReduce , HasConstInfo , HasBuiltins , PureTCM )
71+ deriving newtype (MonadReader CompileEnv )
5472
5573-- | Require a definition to be compiled.
5674requireDef :: QName -> CompileM ()
@@ -89,19 +107,71 @@ trackDeps (Compile c) = Compile do
89107 deps <- gets requiredDefs
90108 pure (x, Set. toList deps)
91109
110+ -- | Run a compile action in 'TCM'.
111+ runCompile :: CompileEnv -> CompileM a -> TCM a
112+ runCompile env (Compile c) = evalStateT (runReaderT c env) initState
113+
92114-- | Run the processing function as long as there are names to be compiled.
93115-- Returns a list of compiled items, (topologically) sorted by dependency order.
94116-- This means that whenever @A@ depends on @B@, @A@ will appear before @B@ in the list.
95117compileLoop
96118 :: forall a . (Definition -> CompileM (Maybe a ))
97- -- ^ The compilation function
119+ -- ^ The compilation function
98120 -> [QName ] -- ^ Initial names to compile
99- -> TCM [CompiledItem a ]
100- compileLoop step names = topoSort <$> evalStateT unloop (initState names)
121+ -> CompileM [CompiledItem a ]
122+ compileLoop step names = do
123+ traverse_ requireDef names
124+ topoSort <$> loop
101125 where
102126 loop :: CompileM [CompiledItem (Maybe a )]
103127 loop@ (Compile unloop) = nextUnit >>= \ case
104128 Nothing -> pure []
105129 Just q -> do
106130 (mr, deps) <- trackDeps . step =<< (liftTCM $ getConstInfo q)
107131 (CompiledItem q deps mr: ) <$> loop
132+
133+
134+ -- * Compilation items and topological sort
135+
136+ -- | Named compilation item, with a set of dependencies.
137+ data CompiledItem a = CompiledItem
138+ { itemName :: QName
139+ , itemDeps :: [QName ]
140+ , itemValue :: a
141+ } deriving (Functor , Foldable , Traversable )
142+
143+ -- | Stateful monad for the topological sort.
144+ -- State contains the list of items that have been permanently inserted,
145+ -- along with their names.
146+ type TopoM a = State (Set QName , [CompiledItem a ])
147+
148+ -- | Topological sort of compiled items, based on dependencies.
149+ -- Skipped items are required for dependency analysis, as they
150+ -- can transively force ordering
151+ -- (e.g constructors are skipped but force compilation of their datatype).
152+ -- In the end, we get a list of items that are effectively compiled.
153+ topoSort :: forall a . [CompiledItem (Maybe a )] -> [CompiledItem a ]
154+ topoSort defs = snd $ execState (traverse (visit Set. empty) defs) (Set. empty, [] )
155+ where
156+ items = Map. fromList $ map (\ x -> (itemName x, x)) defs
157+
158+ -- | Whether an item has been permanently inserted already
159+ isMarked :: QName -> TopoM a Bool
160+ isMarked q = Set. member q <$> gets fst
161+
162+ push :: CompiledItem (Maybe a ) -> TopoM a ()
163+ push item@ CompiledItem {itemName, itemValue}
164+ | Just value <- itemValue
165+ = modify $ bimap (Set. insert itemName) (item {itemValue = value}: )
166+ | otherwise = pure ()
167+
168+ visit :: Set QName -> CompiledItem (Maybe a ) -> TopoM a ()
169+ visit temp item@ CompiledItem {.. } = do
170+ -- NOTE(flupe): Visiting an item that has already been temporarily marked
171+ -- means something went wrong and we have a cycle in the graph.
172+ -- We could throw an error, but this should never happen.
173+ -- Here, we continue and pick an arbitrary order.
174+ unlessM ((Set. member itemName temp || ) <$> isMarked itemName) do
175+ let deps = catMaybes $ (`Map.lookup` items) <$> itemDeps
176+ traverse (visit (Set. insert itemName temp)) deps
177+ push item
0 commit comments