remarks-0.1.0: app/Main.hs
module Main where
import Ast
import Parser
import Validator
import Control.Monad ( void, filterM )
import Data.List ( sort, partition )
import System.Directory
( doesFileExist, doesDirectoryExist, listDirectory )
import System.FilePath
( (<.>), (</>), takeDirectory, takeExtension, dropExtension )
import System.Environment( getArgs )
import System.Exit ( exitWith, ExitCode ( ExitFailure ) )
import System.IO ( hPutStrLn, stderr )
import Text.PrettyPrint.GenericPretty
report :: String -> IO ()
report = hPutStrLn stderr
noCommand :: IO a
noCommand = do
report "Tell me what to do!"
exitWith (ExitFailure 1)
notAPath :: String -> IO a
notAPath s = do
report $ s ++ " is not a filesystem path!"
exitWith (ExitFailure 1)
invalidCommand :: String -> [String] -> IO a
invalidCommand c _ = do
report $ c ++ " is not a valid command."
exitWith (ExitFailure 1)
noMrkFile :: FilePath -> IO a
noMrkFile path = do
report $ "Can't find a .mrk file\n" ++ path
exitWith (ExitFailure 1)
noTopLevelJudgement :: FilePath -> IO a
noTopLevelJudgement path = do
report $ path ++ " has no top-level judgement."
report "I don't know how to handle this, sorry."
exitWith (ExitFailure 1)
mustHaveJudgement :: FilePath -> IO a
mustHaveJudgement path = do
report $ path ++ " must have at least one judgement."
exitWith (ExitFailure 1)
parseError :: ParseError -> IO a
parseError e = do
report $ show e
exitWith (ExitFailure 1)
parseTopFile :: FilePath -> IO [Judgement]
parseTopFile path = do
p <- parseFile path
case p of
Right js -> pure js
Left e -> parseError e
parseFileInDir :: FilePath -> IO Judgement
parseFileInDir path = do
js <- parsePath path
case js of
[j] -> pure j
_ -> noTopLevelJudgement path
parseFileWithDir :: FilePath -> IO ([Judgement], Judgement)
parseFileWithDir path = do
p <- parseFile path
case p of
Right js ->
case length js of
0 -> mustHaveJudgement path
n -> pure $ (take (n - 1) js, last js)
Left e -> parseError e
isMrkPath :: FilePath -> Bool
isMrkPath = (== ".mrk") . takeExtension
parseDir :: FilePath -> IO [Judgement]
parseDir path = do
paths <- listDirectory path
let (mrkPaths, otherPaths) = partition isMrkPath paths
let fullMrkPaths = map (path </>) (sort mrkPaths)
mapM parseFileInDir fullMrkPaths
extPaths :: FilePath -> (FilePath, FilePath)
extPaths path =
if isMrkPath path
then (path, dropExtension path)
else (path <.> "mrk", path)
parsePath :: FilePath -> IO [Judgement]
parsePath path = do
let (pathWithExt, pathWithoutExt) = extPaths path
hasFile <- doesFileExist pathWithExt
hasDir <- doesDirectoryExist pathWithoutExt
if not hasFile
then
if hasDir
then parseDir path
else noMrkFile pathWithExt
else -- we have a .mrk file
if not hasDir
then parseTopFile pathWithExt
else do -- we now also have directory
(fjs, (Judgement (h, cs, js))) <- parseFileWithDir pathWithExt
dirJs <- parseDir pathWithoutExt
pure $ fjs ++ [Judgement (h, cs, js ++ dirJs)]
parsePaths :: [FilePath] -> IO [[Judgement]]
parsePaths = mapM parsePath
check :: [Judgement] -> IO ()
check js = putStrLn $ pretty $ map validate js
main :: IO ()
main = do
args <- getArgs
case args of
[] -> noCommand
("parse" : paths) -> parsePaths paths >>= putStrLn . pretty
("check" : paths) -> parsePaths paths >>= mapM_ check
(c:args) -> invalidCommand c args