{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE StrictData      #-}
module Language.Cimple.Program
  ( Program
  , fromList
  , toList
  , includeGraph
  ) where

import           Data.Map.Strict                   (Map)
import qualified Data.Map.Strict                   as Map
import           Data.Text                         (Text)
import           Language.Cimple.Ast               (Node)
import           Language.Cimple.Graph             (Graph)
import qualified Language.Cimple.Graph             as Graph
import           Language.Cimple.Lexer             (Lexeme (..))
import           Language.Cimple.SemCheck.Includes (collectIncludes,
                                                    normaliseIncludes)
import           Language.Cimple.TranslationUnit   (TranslationUnit)


data Program text = Program
  { Program text -> Map FilePath [Node (Lexeme text)]
progAsts     :: Map FilePath [Node (Lexeme text)]
  , Program text -> Graph () FilePath
progIncludes :: Graph () FilePath
  }


toList :: Program a -> [TranslationUnit a]
toList :: Program a -> [TranslationUnit a]
toList = Map FilePath [Node (Lexeme a)] -> [TranslationUnit a]
forall k a. Map k a -> [(k, a)]
Map.toList (Map FilePath [Node (Lexeme a)] -> [TranslationUnit a])
-> (Program a -> Map FilePath [Node (Lexeme a)])
-> Program a
-> [TranslationUnit a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Program a -> Map FilePath [Node (Lexeme a)]
forall text. Program text -> Map FilePath [Node (Lexeme text)]
progAsts


includeGraph :: Program a -> [(FilePath, FilePath)]
includeGraph :: Program a -> [(FilePath, FilePath)]
includeGraph = Graph () FilePath -> [(FilePath, FilePath)]
forall node key. Graph node key -> [(key, key)]
Graph.edges (Graph () FilePath -> [(FilePath, FilePath)])
-> (Program a -> Graph () FilePath)
-> Program a
-> [(FilePath, FilePath)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Program a -> Graph () FilePath
forall text. Program text -> Graph () FilePath
progIncludes


fromList :: [TranslationUnit Text] -> Either String (Program Text)
fromList :: [TranslationUnit Text] -> Either FilePath (Program Text)
fromList [TranslationUnit Text]
tus = do
    let tusWithIncludes :: [(TranslationUnit Text, [FilePath])]
tusWithIncludes = (TranslationUnit Text -> (TranslationUnit Text, [FilePath]))
-> [TranslationUnit Text] -> [(TranslationUnit Text, [FilePath])]
forall a b. (a -> b) -> [a] -> [b]
map TranslationUnit Text -> (TranslationUnit Text, [FilePath])
normaliseIncludes [TranslationUnit Text]
tus
    let progAsts :: Map FilePath [Node (Lexeme Text)]
progAsts = [TranslationUnit Text] -> Map FilePath [Node (Lexeme Text)]
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList ([TranslationUnit Text] -> Map FilePath [Node (Lexeme Text)])
-> ([(TranslationUnit Text, [FilePath])] -> [TranslationUnit Text])
-> [(TranslationUnit Text, [FilePath])]
-> Map FilePath [Node (Lexeme Text)]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((TranslationUnit Text, [FilePath]) -> TranslationUnit Text)
-> [(TranslationUnit Text, [FilePath])] -> [TranslationUnit Text]
forall a b. (a -> b) -> [a] -> [b]
map (TranslationUnit Text, [FilePath]) -> TranslationUnit Text
forall a b. (a, b) -> a
fst ([(TranslationUnit Text, [FilePath])]
 -> Map FilePath [Node (Lexeme Text)])
-> [(TranslationUnit Text, [FilePath])]
-> Map FilePath [Node (Lexeme Text)]
forall a b. (a -> b) -> a -> b
$ [(TranslationUnit Text, [FilePath])]
tusWithIncludes
    -- Check whether all includes can be resolved.
    [((), FilePath, [FilePath])]
includeEdges <- ((TranslationUnit Text, [FilePath])
 -> Either FilePath ((), FilePath, [FilePath]))
-> [(TranslationUnit Text, [FilePath])]
-> Either FilePath [((), FilePath, [FilePath])]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM ((TranslationUnit Text
 -> [FilePath] -> Either FilePath ((), FilePath, [FilePath]))
-> (TranslationUnit Text, [FilePath])
-> Either FilePath ((), FilePath, [FilePath])
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry ((TranslationUnit Text
  -> [FilePath] -> Either FilePath ((), FilePath, [FilePath]))
 -> (TranslationUnit Text, [FilePath])
 -> Either FilePath ((), FilePath, [FilePath]))
-> (TranslationUnit Text
    -> [FilePath] -> Either FilePath ((), FilePath, [FilePath]))
-> (TranslationUnit Text, [FilePath])
-> Either FilePath ((), FilePath, [FilePath])
forall a b. (a -> b) -> a -> b
$ [FilePath]
-> TranslationUnit Text
-> [FilePath]
-> Either FilePath ((), FilePath, [FilePath])
collectIncludes ([FilePath]
 -> TranslationUnit Text
 -> [FilePath]
 -> Either FilePath ((), FilePath, [FilePath]))
-> [FilePath]
-> TranslationUnit Text
-> [FilePath]
-> Either FilePath ((), FilePath, [FilePath])
forall a b. (a -> b) -> a -> b
$ (TranslationUnit Text -> FilePath)
-> [TranslationUnit Text] -> [FilePath]
forall a b. (a -> b) -> [a] -> [b]
map TranslationUnit Text -> FilePath
forall a b. (a, b) -> a
fst [TranslationUnit Text]
tus) [(TranslationUnit Text, [FilePath])]
tusWithIncludes
    let progIncludes :: Graph () FilePath
progIncludes = [((), FilePath, [FilePath])] -> Graph () FilePath
forall key node. Ord key => [(node, key, [key])] -> Graph node key
Graph.fromEdges [((), FilePath, [FilePath])]
includeEdges
    Program Text -> Either FilePath (Program Text)
forall (m :: * -> *) a. Monad m => a -> m a
return Program :: forall text.
Map FilePath [Node (Lexeme text)]
-> Graph () FilePath -> Program text
Program{Map FilePath [Node (Lexeme Text)]
Graph () FilePath
progIncludes :: Graph () FilePath
progAsts :: Map FilePath [Node (Lexeme Text)]
progIncludes :: Graph () FilePath
progAsts :: Map FilePath [Node (Lexeme Text)]
..}