-- | This module contains a compiler pass that computes the strongly connected
--   components of either the function or type dependency graph and transforms
--   the strongly connected components using the given child passes.
--
--   = Example
--
--    If @pass1@, @pass2@ and @pass3@ are 'DependencyAwarePass'es for function
--    declarations, then @dependencyAnalysisPass [pass1, pass2, pass3]@ builds
--    a compiler pass that computes the strongly connected components of the
--    given modules function dependency graph. Each strongly connected component
--    is first transformed with @pass1@. The result is further processed by
--    @pass2@ and its result is passed to @pass3@. After all three passes have
--    been applied to the first strongly connected component, the subsequent
--    components are processed.
--
--   = Specification
--
--   == Preconditions
--
--   The preconditions of all child passes that are not already guaranteed by
--   preceding child passes should hold.
--
--   == Translation
--
--   The transformations of the child passes are applied.
--   Due to the computation of strongly connected components, the order of
--   declarations is changed. They are rearranged into reverse topological
--   order.
--
--   == Postconditions
--
--   The postconditions of all child passes that are not broken by subsequent
--   child passes hold.
--
--   == Error cases
--
--   The messages reported by the child passes are reported.
module FreeC.Pass.DependencyAnalysisPass
  ( DependencyAwarePass
  , dependencyAnalysisPass
  ) where

import           FreeC.IR.DependencyGraph
import qualified FreeC.IR.Syntax          as IR
import           FreeC.Monad.Converter
import           FreeC.Pass

-- | Type of a compiler 'Pass' that does not operate on entire modules but
--   on individual strongly connected components of the dependency graph
--   for declarations of type @decl@.
type DependencyAwarePass decl = Pass (DependencyComponent decl)
  (DependencyComponent decl)

-- | Type class for declaration AST nodes whose dependencies can be analyzed.
class DependencyAnalysisPass decl where
  -- | Constructs the dependency graph for the given nodes.
  groupDecls :: [decl] -> [DependencyComponent decl]

  -- | Gets the declarations of the node type from the given module.
  getDecls :: IR.Module -> [decl]

  -- | Replaces the declarations of the node type in the given module.
  setDecls :: [decl] -> IR.Module -> IR.Module

-- | The dependencies of type declarations can be analyzed.
instance DependencyAnalysisPass IR.TypeDecl where
  groupDecls = typeDependencyComponents

  getDecls   = IR.modTypeDecls

  setDecls   = IR.modWithTypeDecls

-- | The dependencies of function declarations can be analyzed.
instance DependencyAnalysisPass IR.FuncDecl where
  groupDecls = valueDependencyComponents

  getDecls   = IR.modFuncDecls

  setDecls   = IR.modWithFuncDecls

-- | Applies the given child passes to the strongly connected components
--   of the dependency graph for declarations of type @decl@ of the given
--   module.
dependencyAnalysisPass :: DependencyAnalysisPass decl
                       => DependencyAwarePass decl
                       -> IR.Module
                       -> Converter IR.Module
dependencyAnalysisPass = subPipelinePass getComponents setComponents
 where
  getComponents     = groupDecls . getDecls

  setComponents ast = flip setDecls ast . concatMap unwrapComponent