module Camfort.Specification.Parser
(
SpecParser
, looksLikeASpec
, mkParser
, runParser
, SpecParseError
, parseError
) where
import Control.Monad.Except (throwError)
import Data.List (isPrefixOf)
import qualified Data.Text as T
data SpecParseError e
= ParseError e
| InvalidSpecificationCharacter Char
| MissingSpecificationCharacter
deriving (Eq)
instance (Show e) => Show (SpecParseError e) where
show (InvalidSpecificationCharacter c) =
"Invalid character at start of specification: " ++ show c
show MissingSpecificationCharacter = "missing start of specification"
show (ParseError e) = show e
parseError :: e -> SpecParseError e
parseError = ParseError
invalidSpecificationCharacter :: Char -> SpecParseError e
invalidSpecificationCharacter = InvalidSpecificationCharacter
missingSpecificationCharacter :: SpecParseError e
missingSpecificationCharacter = MissingSpecificationCharacter
data SpecParser e r = SpecParser
{
parser :: String -> Either e r
, specKeywords :: [String]
}
isSpecStartChar :: Char -> Bool
isSpecStartChar = (`elem` "=!<>")
runParser :: SpecParser e r -> String -> Either (SpecParseError e) r
runParser p s = case stripInitial s of
Right s' -> case parser p s' of
Left e -> throwError $ parseError e
Right r -> pure r
Left e -> throwError e
where stripInitial = stripAnnChar . stripLeadingWhiteSpace
stripAnnChar [] =
throwError missingSpecificationCharacter
stripAnnChar (c:cs) | isSpecStartChar c = pure (stripLeadingWhiteSpace cs)
| otherwise =
throwError $ invalidSpecificationCharacter c
mkParser :: (String -> Either e r)
-> [String]
-> SpecParser e r
mkParser = SpecParser
stripLeadingWhiteSpace :: String -> String
stripLeadingWhiteSpace = T.unpack . T.strip . T.pack
looksLikeASpec :: SpecParser e r -> String -> Bool
looksLikeASpec p text
| length (stripLeadingWhiteSpace text) >= 2 =
case stripLeadingWhiteSpace text of
c:cs -> isSpecStartChar c && testAnnotation cs
_ -> False
| otherwise = False
where
testAnnotation inp = case specKeywords p of
[] -> True
ks -> any (inp `hasPrefix`) ks
hasPrefix [] _ = False
hasPrefix (' ':xs) str = hasPrefix xs str
hasPrefix xs str = str `isPrefixOf` xs