cimple-0.0.4: src/Language/Cimple/IO.hs
{-# LANGUAGE StrictData #-}
module Language.Cimple.IO
( parseFile
, parseFiles
, parseProgram
, parseText
) where
import Control.Monad ((>=>))
import Control.Monad.State.Lazy (State, evalState, get, put)
import qualified Data.ByteString as BS
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import Language.Cimple.AST (Node (..))
import Language.Cimple.Lexer (Lexeme, runAlex)
import qualified Language.Cimple.Parser as Parser
import Language.Cimple.Program (Program)
import qualified Language.Cimple.Program as Program
import Language.Cimple.TranslationUnit (TranslationUnit)
import qualified Language.Cimple.TreeParser as TreeParser
type CacheState a = State (Map String Text) a
cacheText :: String -> CacheState Text
cacheText s = do
m <- get
case Map.lookup s m of
Nothing -> do
let text = Text.pack s
put $ Map.insert s text m
return text
Just text ->
return text
process :: [Node a (Lexeme String)] -> [Node a (Lexeme Text)]
process stringAst =
evalState (mapM (mapM (mapM cacheText)) stringAst) Map.empty
parseText :: Text -> Either String [Node () (Lexeme Text)]
parseText contents =
process <$> res
where
res :: Either String [Node () (Lexeme String)]
res =
runAlex (Text.unpack contents) Parser.parseTranslationUnit
parseTextStrict :: Text -> Either String [Node () (Lexeme Text)]
parseTextStrict = parseText >=> TreeParser.toEither . TreeParser.parseTranslationUnit
parseFile :: FilePath -> IO (Either String (TranslationUnit Text))
parseFile source =
addSource . parseTextStrict . Text.decodeUtf8 <$> BS.readFile source
where
-- Add source filename to the error message, if any.
addSource (Left err) = Left $ source <> ":" <> err
-- If there's no error message, record the source filename in the returned
-- TranslationUnit.
addSource (Right ok) = Right (source, ok)
parseFiles' :: [FilePath] -> IO (Either String [TranslationUnit Text])
parseFiles' sources = sequenceA <$> traverse parseFile sources
parseFiles :: [FilePath] -> IO (Either String [TranslationUnit Text])
parseFiles sources = fmap Program.toList . (>>= Program.fromList) <$> parseFiles' sources
parseProgram :: [FilePath] -> IO (Either String (Program Text))
parseProgram sources = (>>= Program.fromList) <$> parseFiles' sources