packages feed

bookhound-0.1.26.0: src/Bookhound/ParserCombinators.hs

module Bookhound.ParserCombinators (IsMatch(..), satisfy, char, string, times, many, some, multiple,
                          between, maybeBetween, surroundedBy, maybeSurroundedBy,
                          manySepBy, someSepBy, multipleSepBy, sepByOps, sepByOp, manyEndBy, someEndBy, multipleEndBy,
                          (<?>), (<#>), (</\>), (<:>), (->>-), (|?), (|*), (|+), (|++), (||?), (||*), (||+), (||++))  where
import Bookhound.Parser (Parser, allOf, anyChar, anyOf, except, satisfy,
                         withError)

import Bookhound.Utils.List (hasMultiple, hasSome)
import Bookhound.Utils.Text (ToText (..))
import Control.Applicative  (liftA2, optional, (<|>))

import qualified Data.Foldable as Foldable
import           Data.Text     (Text, pack, unpack)
import qualified Data.Text     as Text


class IsMatch a where
  is      :: a -> Parser a
  isNot   :: a -> Parser a
  inverse :: Parser a -> Parser a
  oneOf   :: [a] -> Parser a
  noneOf  :: [a] -> Parser a

  oneOf xs  = anyOf $ is <$> xs
  noneOf xs = allOf $ isNot <$> xs

instance   IsMatch Char where
  is      = isMatch (==) anyChar
  isNot   = isMatch (/=) anyChar
  inverse = except anyChar

instance   IsMatch String where
  is      = traverse is
  isNot   = traverse is
  inverse = except (anyChar |*)

instance   IsMatch Text where
  is      = fmap pack . is . unpack
  isNot   = fmap pack . is . unpack
  inverse = except (anyChar ||*)


isMatch :: (Char -> Char -> Bool) -> Parser Char -> Char -> Parser Char
isMatch cond p c1 = satisfy (cond c1) p

char :: Char -> Parser Char
char = is

string :: Text -> Parser Text
string = is

 -- Frequency combinators
many :: Parser a -> Parser [a]
many p = (p >>= \x -> (x :) <$> many p)
  <|> pure []

some :: Parser a -> Parser [a]
some = satisfy hasSome . many

multiple :: Parser a -> Parser [a]
multiple = satisfy hasMultiple . many

times :: Int -> Parser a  -> Parser [a]
times n p
  | n < 1 = pure []
  | otherwise = sequence $ p <$ [1 .. n]


-- Between combinators
between :: Parser a -> Parser b -> Parser c -> Parser c
between start end p = start *> p <* end

maybeBetween :: Parser a -> Parser b -> Parser c -> Parser c
maybeBetween p1 p2 = between (p1 |?) (p2 |?)

surroundedBy :: Parser a -> Parser b -> Parser b
surroundedBy p = between p p

maybeSurroundedBy :: Parser a -> Parser b -> Parser b
maybeSurroundedBy p = surroundedBy (p |?)


-- Sep by combinators
sepBy :: (Parser b -> Parser (Maybe b)) -> (Parser b -> Parser [b])
                -> Parser a -> Parser b -> Parser [b]
sepBy freq1 freq2 sep p = (<>) <$> (Foldable.toList <$> freq1 p)
                               <*> freq2 (sep *> p)

manySepBy :: Parser a -> Parser b -> Parser [b]
manySepBy = sepBy optional many

someSepBy :: Parser a -> Parser b -> Parser [b]
someSepBy = sepBy (fmap Just) many

multipleSepBy :: Parser a -> Parser b -> Parser [b]
multipleSepBy = sepBy (fmap Just) some

sepByOps :: Parser a -> Parser b -> Parser ([a], [b])
sepByOps sep p = do x <-  p
                    y <- (|+) (sep </\> p)
                    pure (fmap fst y, x : fmap snd y)

sepByOp :: Parser a -> Parser b -> Parser (a, [b])
sepByOp sepP p = do
  x1 <- p
  sep <- sepP
  x2 <- p
  xs <- (|*) (sepP *> p)
  pure (sep, x1 : x2 : xs)

-- End by combinators
endBy
  :: forall a b
   . (Parser b -> Parser (Maybe b))
  -> (Parser b -> Parser [b])
  -> Parser a
  -> Parser b
  -> Parser [b]
endBy freq1 freq2 sep p =
  sepBy freq1 freq2 sep p <* sep

manyEndBy :: forall a b. Parser a -> Parser b -> Parser [ b]
manyEndBy = endBy optional many

someEndBy :: forall a b. Parser a -> Parser b -> Parser [ b]
someEndBy = endBy (fmap Just) many

multipleEndBy :: forall a b. Parser a -> Parser b -> Parser [ b]
multipleEndBy = endBy (fmap Just) some


-- Parser Binary Operators
infixl 6 <#>
(<#>) :: Parser a -> Int -> Parser [a]
(<#>) = flip times

infixl 6 <?>
(<?>) :: Parser a -> Text -> Parser a
(<?>) = flip withError

infixl 6 ->>-
(->>-) :: (ToText a, ToText b) => Parser a -> Parser b -> Parser Text
(->>-) p1 p2 = (<>) <$> fmap toText p1
                 <*> fmap toText p2

-- Apply Binary Operators
infixl 6 </\>
(</\>) :: Applicative f => f a -> f b -> f (a, b)
(</\>) = liftA2 (,)

infixl 6 <:>
(<:>) :: Applicative f => f a -> f [a] -> f [a]
(<:>) = liftA2 (:)

-- Parser Unary Operators
(|?) :: Parser a -> Parser (Maybe a)
(|?) = optional

(|*) :: Parser a -> Parser [a]
(|*) = many

(|+) :: Parser a -> Parser [a]
(|+) = some

(|++) :: Parser a -> Parser [a]
(|++) = multiple

(||?) :: Parser Char -> Parser Text
(||?) = fmap (maybe mempty Text.singleton) . optional

(||*) :: Parser Char -> Parser Text
(||*) = fmap pack . many

(||+) :: Parser Char -> Parser Text
(||+) = fmap pack . some

(||++) :: Parser Char -> Parser Text
(||++) = fmap pack . multiple