checkmate-0.4.0: src/Checkmate/Parser/CheckFile.hs
{-# LANGUAGE OverloadedStrings #-}
module Checkmate.Parser.CheckFile
( ParseError
, parseCheckFile
, parseCheckFileText
, parser
) where
import Control.Monad
import Data.Void
import Data.Set
import qualified Data.Text as T
import Data.Text.IO
import System.FilePath
import Text.Megaparsec hiding (ParseError)
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Error as E
import Checkmate.Check
type Parser = Parsec Void T.Text
type ParseError = E.ParseError Char Void
parseCheckFile :: FilePath -> IO (Either ParseError Checklist)
parseCheckFile filePath = do
input <- Data.Text.IO.readFile filePath
return $ parseCheckFileText filePath input
parseCheckFileText :: FilePath -> T.Text -> Either ParseError Checklist
parseCheckFileText filePath =
parse (parser $ takeDirectory filePath) filePath
parser :: FilePath -> Parser Checklist
parser dirPath = do
space
checkTexts <- many checkP
eof
return $ Data.Set.fromList
[Check (Directory dirPath) i ct | (i, ct) <- zip [1..] checkTexts]
where
bulletP :: Parser ()
bulletP = do
skipMany (spaceChar <|> tab)
bullet <- choice [ string "*"
, string "+"
, string "-"
, string "CHECK:"
, string "CHECK"
, some digitChar >> (string "." <|> string ")")
]
skipSome (spaceChar <|> tab)
unless (bullet == "CHECK") $ void . optional $ do
void $ string "CHECK"
skipSome (spaceChar <|> tab)
checkP :: Parser T.Text
checkP = do
bulletP
texts <- (`sepBy1` try (eol >> notFollowedBy bulletP)) $ do
chars <- many $ noneOf ['\n']
return $ T.pack chars
let texts' = texts :: [T.Text]
return $ T.strip $ T.intercalate (T.singleton '\n') texts'