packages feed

cimple-0.0.28: tools/cimplefmt.hs

module Main (main) where

import qualified Data.ByteString        as BS
import qualified Data.ByteString.Lazy   as LBS
import           Data.Text              (Text)
import qualified Data.Text.Encoding     as Text
import           Language.Cimple        (Lexeme, Node)
import           Language.Cimple.IO     (parseText, parseUnit)
import           Language.Cimple.Pretty (plain, ppTranslationUnit, render)
import           Options.Applicative    (Parser, argument, execParser, fullDesc,
                                         header, help, helper, info, long, many,
                                         metavar, progDesc, str, switch)


data Options = Options
    { reparse      :: Bool
    , noTreeParser :: Bool
    , files        :: [FilePath]
    }


options :: Parser Options
options = Options
    <$> switch
        ( long "reparse"
       <> help "Reparse the output to ensure validity" )
    <*> switch
        ( long "no-tree-parser"
       <> help "Do not use the tree parser (faster, less strict)" )
    <*> many (argument str (metavar "FILES..."))


format :: Bool -> [Node (Lexeme Text)] -> Text
format color = render . maybePlain . ppTranslationUnit
  where
    maybePlain = if color then id else plain


reparseText :: Text -> IO ()
reparseText code =
    case parseText code of
        Left err -> do
            BS.putStr . Text.encodeUtf8 $ code
            fail $ "re-parsing our own pretty-printed"
                <> " output failed: " <> err
        Right _ -> return ()


processInput :: Options -> FilePath -> LBS.ByteString -> IO ()
processInput opts source input = do
    let ast = parseUnit (not $ noTreeParser opts) input
    case ast of
        Left err -> putStrLn (source <> err) >> fail "aborting after parse error"
        Right ok ->
            if reparse opts
               then reparseText $ format False ok
               else BS.putStr . Text.encodeUtf8 . format True $ ok


main :: IO ()
main = do
    opts <- execParser optsInfo
    let sources = files opts
    if null sources
        then do
            content <- LBS.fromStrict <$> BS.getContents
            processInput opts "<stdin>" content
        else mapM_ (\f -> LBS.readFile f >>= processInput opts f) sources
  where
    optsInfo = info (helper <*> options)
        ( fullDesc
       <> progDesc "Format Cimple source files"
       <> header "cimplefmt - a formatter for Cimple" )