currycarbon-0.3.0.0: src/Currycarbon/ParserHelpers.hs
module Currycarbon.ParserHelpers where
import qualified Text.Parsec as P
import qualified Text.Parsec.Error as P
import qualified Text.Parsec.String as P
-- * High level building blocks
parseRecordType :: String -> P.Parser a -> P.Parser a
parseRecordType typeName parser = do
_ <- P.string typeName
parseInParens parser
parseNamedVector :: P.Parser a -> P.Parser b -> P.Parser [(a,b)]
parseNamedVector parseKey parseValue =
parseVector $ parseKeyValuePair parseKey parseValue
parseVector :: P.Parser a -> P.Parser [a]
parseVector parser = do
_ <- P.char 'c'
parseInParens (P.sepBy parser consumeCommaSep)
parseArgumentWithDefault :: String -> P.Parser b -> b -> P.Parser b
parseArgumentWithDefault argumentName parseValue defaultValue =
P.option defaultValue (parseArgument argumentName parseValue)
parseArgumentOptional :: String -> P.Parser b -> P.Parser (Maybe b)
parseArgumentOptional argumentName parseValue =
P.optionMaybe $ P.try (parseArgument argumentName parseValue)
parseArgument :: String -> P.Parser b -> P.Parser b
parseArgument argumentName parseValue = do
res <- parseArgumentWithoutComma argumentName parseValue
P.optional consumeCommaSep
return res
parseNamedArgumentOptional :: String -> P.Parser b -> P.Parser (Maybe b)
parseNamedArgumentOptional argumentName parseValue =
P.optionMaybe $ P.try (parseNamedArgument argumentName parseValue)
-- * Low level blocks
parseArgumentWithoutComma :: String -> P.Parser b -> P.Parser b
parseArgumentWithoutComma argumentName parseValue =
P.try (parseNamedArgument argumentName parseValue) P.<|> parseUnnamedArgument parseValue
parseNamedArgument :: String -> P.Parser b -> P.Parser b
parseNamedArgument argumentName parseValue = do
(_,b) <- parseKeyValuePair (P.string argumentName) parseValue
return b
parseUnnamedArgument :: P.Parser b -> P.Parser b
parseUnnamedArgument parseValue = parseValue
parseKeyValuePair :: P.Parser a -> P.Parser b -> P.Parser (a,b)
parseKeyValuePair parseKey parseValue = do
key <- parseKey
consumeEqualSep
value <- parseValue
return (key, value)
parseInParens :: P.Parser b -> P.Parser b
parseInParens parser = do
_ <- P.char '('
_ <- P.spaces
res <- parser
_ <- P.spaces
_ <- P.char ')'
return res
consumeEqualSep :: P.Parser ()
consumeEqualSep = do
_ <- P.spaces *> P.char '=' <* P.spaces
return ()
consumeCommaSep :: P.Parser ()
consumeCommaSep = do
_ <- P.spaces *> P.char ',' <* P.spaces
return ()
parseCharInSpace :: Char -> P.Parser Char
parseCharInSpace c = P.between P.spaces P.spaces (P.char c)
parseAnyString :: P.Parser String
parseAnyString =
P.try inDoubleQuotes P.<|> P.try inSingleQuotes P.<|> inNoQuotes
where
inDoubleQuotes = P.between (P.char '"') (P.char '"') (P.many P.anyChar)
inSingleQuotes = P.between (P.char '\'') (P.char '\'') (P.many P.anyChar)
inNoQuotes = P.many (P.noneOf ",):")
-- * Sequence parsers
parseDoubleSequence :: P.Parser [Double]
parseDoubleSequence = do
start <- parseDouble
_ <- P.oneOf ":"
stop <- parseDouble
_ <- P.oneOf ":"
by <- parsePositiveFloatNumber
return [start,(start+by)..stop]
-- * Number parsers
parseDouble :: P.Parser Double
parseDouble = do
P.try parseNegativeFloatNumber P.<|> parsePositiveFloatNumber
parseNegativeFloatNumber :: P.Parser Double
parseNegativeFloatNumber = do
_ <- P.oneOf "-"
i <- parsePositiveFloatNumber
return (-i)
parseFraction :: P.Parser Double
parseFraction = do
num <- parsePositiveFloatNumber
if num > 1
then fail "must be between zero and one"
else return num
parsePositiveFloatNumber :: P.Parser Double
parsePositiveFloatNumber = do
num <- parseNumber
optionalMore <- P.option "" $ (:) <$> P.char '.' <*> parseNumber
return $ read $ num ++ optionalMore
parseIntegerSequence :: P.Parser [Int]
parseIntegerSequence = do
start <- parseInt
_ <- P.oneOf ":"
stop <- parseInt
_ <- P.oneOf ":"
by <- fromIntegral <$> parsePositiveInt
return [start,(start+by)..stop]
parseInt :: P.Parser Int
parseInt = do
P.try parseNegativeInt P.<|> parsePositiveInt
parseNegativeInt :: P.Parser Int
parseNegativeInt = do
_ <- P.oneOf "-"
i <- parsePositiveInt
return (-i)
parsePositiveInt :: P.Parser Int
parsePositiveInt = fromIntegral <$> parseWord
-- https://hackage.haskell.org/package/base-4.19.0.0/docs/Data-Word.html
parseWord :: P.Parser Word
parseWord = do
read <$> parseNumber
parsePositiveDouble :: P.Parser Double
parsePositiveDouble = do
read <$> parseNumber
parseNumber :: P.Parser [Char]
parseNumber = P.many1 P.digit
-- * Error helpers
showParsecErr :: P.ParseError -> String
showParsecErr err =
P.showErrorMessages
"or" "unknown parse error"
"expecting" "unexpected" "end of input"
(P.errorMessages err)