{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Data.Aeromess.Parser
( Error(message, column)
, Parser
, (<|>)
, between
, char
, choice
, dash
, enumeration
, identifier
, lookAhead
, many
, manyTillSpace
, natural
, noneOf
, octal
, oneOf
, optional
, runParser
, slash
, some
, space
, string
, stringTill
, try
, unexpected
, word
, words
) where
import Control.Monad (mplus)
import Control.Monad.Fail
import Data.Either
import Data.Functor.Identity
import Data.List hiding (words)
import Data.Maybe
import Data.Ord (comparing)
import Data.String (IsString, fromString)
import Prelude hiding (words, fail)
import qualified Text.Parsec as P
import qualified Text.Parsec.Char as C
import qualified Text.Parsec.Error as E
data Error = Error
{ message :: String
, column :: Int
} deriving (Eq, Show)
instance IsString Error where
fromString s = Error s 0
type Parser = P.ParsecT String () Identity
instance MonadFail Parser where
fail = unexpected
instance IsString str =>
MonadFail (Either str) where
fail = Left . fromString
(<|>) :: Parser a -> Parser a -> Parser a
p1 <|> p2 = mplus p1 p2
between :: Parser Char -> Parser Char -> Parser a -> Parser a
between = P.between
char :: Char -> Parser Char
char = C.char
choice :: [Parser a] -> Parser a
choice = P.choice
dash :: Parser Char
dash = char '-'
enumeration
:: (Bounded a, Enum a, Show a, Read a)
=> Parser a
enumeration = enum' show read
where
enum'
:: (Bounded a, Enum a)
=> (a -> String) -> (String -> a) -> Parser a
enum' s r =
let sorted = sortBy (flip (comparing length)) (map s [minBound .. maxBound])
in r <$> choice (map (try . string) sorted)
identifier :: Parser String
identifier = some (C.upper <|> C.digit)
lookAhead :: Parser a -> Parser a
lookAhead = P.lookAhead
many :: Parser a -> Parser [a]
many = P.many
manyTillSpace :: Parser a -> Parser [a]
manyTillSpace p = P.manyTill p space
natural :: Int -> Parser Int
natural n = fmap read (P.count n P.digit)
noneOf :: String -> Parser Char
noneOf = P.noneOf
octal :: Int -> Parser String
octal n = P.count n C.octDigit
oneOf :: String -> Parser Char
oneOf = P.oneOf
optional :: Parser a -> Parser (Maybe a)
optional = P.optionMaybe
runParser :: Parser a -> String -> Either Error a
runParser p s = mapLeft err (P.parse p "" s)
slash :: Parser Char
slash = char '/'
some :: Parser a -> Parser [a]
some = P.many1
space :: Parser Char
space = P.space
string :: String -> Parser String
string = C.string
stringTill :: Char -> Parser String
stringTill c = P.manyTill C.anyChar (try (string [c]))
try :: Parser a -> Parser a
try = P.try
unexpected :: String -> Parser a
unexpected = P.unexpected
word :: Parser String
word = some P.upper
words :: Parser String
words = some (P.upper <|> space)
err :: E.ParseError -> Error
err e = Error (errMessage e) (col e)
errMessage :: E.ParseError -> String
errMessage e = E.messageString (head (E.errorMessages e))
mapLeft :: (a -> c) -> Either a b -> Either c b
mapLeft f (Left x) = Left (f x)
mapLeft _ (Right x) = Right x
col :: E.ParseError -> Int
col e = read (show (P.sourceColumn (P.errorPos e)))