packages feed

cimple-0.0.26: src/Language/Cimple/IO.hs

{-# LANGUAGE Strict #-}
module Language.Cimple.IO
    ( parseExpr
    , parseFile
    , parseFiles
    , parseProgram
    , parseStmt
    , parseText
    ) where

import           Control.Monad                   ((>=>))
import qualified Control.Monad.Parallel          as P
import           Control.Monad.State.Strict      (State, evalState, get, put)
import qualified Data.ByteString.Lazy            as LBS
import           Data.Map.Strict                 (Map)
import qualified Data.Map.Strict                 as Map
import           Data.Text                       (Text)
import qualified Data.Text.Encoding              as Text
import           Language.Cimple.Ast             (Node)
import           Language.Cimple.Lexer           (Alex, Lexeme, runAlex)
import           Language.Cimple.MapAst          (TextActions, mapAst,
                                                  textActions)
import qualified Language.Cimple.Parser          as Parser
import qualified Language.Cimple.ParseResult     as ParseResult
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 TextNode = Node (Lexeme Text)

cacheText :: [TextNode] -> [TextNode]
cacheText textAst =
    evalState (mapAst cacheActions textAst) Map.empty
  where
    cacheActions :: TextActions (State (Map Text Text)) Text Text
    cacheActions = textActions $ \s -> do
        m <- get
        case Map.lookup s m of
            Nothing -> do
                put $ Map.insert s s m
                return s
            Just text ->
                return text


runText :: Alex a -> Text -> Either String a
runText f = flip runAlex f . LBS.fromStrict . Text.encodeUtf8

parseExpr :: Text -> Either String TextNode
parseExpr = runText Parser.parseExpr

parseStmt :: Text -> Either String TextNode
parseStmt = runText Parser.parseStmt

parseText :: Text -> Either String [TextNode]
parseText = fmap cacheText . runText Parser.parseTranslationUnit >=> ParseResult.toEither . TreeParser.parseTranslationUnit

parseBytes :: LBS.ByteString -> Either String [TextNode]
parseBytes = flip runAlex Parser.parseTranslationUnit

parseBytesPedantic :: LBS.ByteString -> Either String [TextNode]
parseBytesPedantic = parseBytes >=> ParseResult.toEither . TreeParser.parseTranslationUnit


parseFile :: FilePath -> IO (Either String (TranslationUnit Text))
parseFile source =
    addSource . parseBytesPedantic <$> LBS.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 <$> P.mapM parseFile sources


parseProgram :: [FilePath] -> IO (Either String (Program Text))
parseProgram sources = (>>= Program.fromList) <$> parseFiles sources