{-# LANGUAGE PatternSynonyms #-}
{-|
Module      : Parsley.Combinator
Description : The parsing combinators
License     : BSD-3-Clause
Maintainer  : Jamie Willis
Stability   : stable

This module contains the classic parser combinator operations specific to parsers themselves.
This means any combinators that deal with input consumption at a primitive level.

@since 0.1.0.0
-}
module Parsley.Combinator (
    satisfy, char, item,
    string, token,
    oneOf, noneOf,
    eof, more,
    someTill,
    try,
    lookAhead, notFollowedBy
  ) where

import Prelude hiding      (traverse, (*>))
import Parsley.Alternative (manyTill)
import Parsley.Applicative (($>), void, traverse, (<:>), (*>))
import Parsley.Internal    (Code, makeQ, Parser, Defunc(LIFTED, EQ_H, CONST), pattern APP_H, satisfy, lookAhead, try, notFollowedBy)

{-|
This combinator will attempt match a given string. If the parser fails midway through, this
combinator will fail having consumed input. On success, the string itself is returned and input
will be consumed.

@since 0.1.0.0
-}
string :: String -> Parser String
string :: String -> Parser String
string = (Char -> Parser Char) -> String -> Parser String
forall a b. (a -> Parser b) -> [a] -> Parser [b]
traverse Char -> Parser Char
char

{-|
This combinator will attempt to match any one of the provided list of characters. If one of those
characters is found, it will be returned and the input consumed. If not, the combinator will fail
having consumed no input.

@since 0.1.0.0
-}
oneOf :: [Char] -> Parser Char
oneOf :: String -> Parser Char
oneOf String
cs = Defunc (Char -> Bool) -> Parser Char
forall (rep :: Type -> Type).
ParserOps rep =>
rep (Char -> Bool) -> Parser Char
satisfy ((Char -> Bool) -> Code (Char -> Bool) -> Defunc (Char -> Bool)
forall (q :: Type -> Type) a. Quapplicative q => a -> Code a -> q a
makeQ ((Char -> String -> Bool) -> String -> Char -> Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip Char -> String -> Bool
forall (t :: Type -> Type) a.
(Foldable t, Eq a) =>
a -> t a -> Bool
elem String
cs) [||\c -> $$(ofChars cs [||c||])||])

{-|
This combinator will attempt to not match any one of the provided list of characters. If one of those
characters is found, the combinator will fail having consumed no input. If not, it will return
the character that was not an element of the provided list.

@since 0.1.0.0
-}
noneOf :: [Char] -> Parser Char
noneOf :: String -> Parser Char
noneOf String
cs = Defunc (Char -> Bool) -> Parser Char
forall (rep :: Type -> Type).
ParserOps rep =>
rep (Char -> Bool) -> Parser Char
satisfy ((Char -> Bool) -> Code (Char -> Bool) -> Defunc (Char -> Bool)
forall (q :: Type -> Type) a. Quapplicative q => a -> Code a -> q a
makeQ (Bool -> Bool
not (Bool -> Bool) -> (Char -> Bool) -> Char -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> String -> Bool) -> String -> Char -> Bool
forall a b c. (a -> b -> c) -> b -> a -> c
flip Char -> String -> Bool
forall (t :: Type -> Type) a.
(Foldable t, Eq a) =>
a -> t a -> Bool
elem String
cs) [||\c -> not $$(ofChars cs [||c||])||])

ofChars :: [Char] -> Code Char -> Code Bool
ofChars :: String -> Code Char -> Code Bool
ofChars = (Char -> (Code Char -> Code Bool) -> Code Char -> Code Bool)
-> (Code Char -> Code Bool) -> String -> Code Char -> Code Bool
forall (t :: Type -> Type) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\Char
c Code Char -> Code Bool
rest Code Char
qc -> [|| c == $$qc || $$(rest qc) ||]) (Code Bool -> Code Char -> Code Bool
forall a b. a -> b -> a
const [||False||])

{-|
Like `string`, excepts parses the given string atomically using `try`. Never consumes input on
failure.

@since 0.1.0.0
-}
token :: String -> Parser String
token :: String -> Parser String
token = Parser String -> Parser String
forall a. Parser a -> Parser a
try (Parser String -> Parser String)
-> (String -> Parser String) -> String -> Parser String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Parser String
string

{-|
This parser succeeds only if there is no input left to consume, and fails without consuming input
otherwise.

@since 0.1.0.0
-}
eof :: Parser ()
eof :: Parser ()
eof = Parser Char -> Parser ()
forall a. Parser a -> Parser ()
notFollowedBy Parser Char
item

{-|
This parser succeeds if there is still input left to consume, and fails otherwise.

@since 0.1.0.0
-}
more :: Parser ()
more :: Parser ()
more = Parser () -> Parser ()
forall a. Parser a -> Parser a
lookAhead (Parser Char -> Parser ()
forall a. Parser a -> Parser ()
void Parser Char
item)

-- Parsing Primitives
{-|
This combinator will attempt to match a given character. If that character is the next input token,
the parser succeeds and the character is returned. Otherwise, the combinator will fail having not
consumed any input.

@since 0.1.0.0
-}
char :: Char -> Parser Char
char :: Char -> Parser Char
char Char
c = Defunc (Char -> Bool) -> Parser Char
forall (rep :: Type -> Type).
ParserOps rep =>
rep (Char -> Bool) -> Parser Char
satisfy (Defunc Char -> Defunc (Char -> Bool)
forall a1. Eq a1 => Defunc a1 -> Defunc (a1 -> Bool)
EQ_H (Char -> Defunc Char
forall a. (Show a, Lift a) => a -> Defunc a
LIFTED Char
c)) Parser Char -> Defunc Char -> Parser Char
forall (rep :: Type -> Type) a b.
ParserOps rep =>
Parser a -> rep b -> Parser b
$> Char -> Defunc Char
forall a. (Show a, Lift a) => a -> Defunc a
LIFTED Char
c

{-|
Reads any single character. This combinator will only fail if there is no more input remaining.
The parsed character is returned.

@since 0.1.0.0
-}
item :: Parser Char
item :: Parser Char
item = Defunc (Char -> Bool) -> Parser Char
forall (rep :: Type -> Type).
ParserOps rep =>
rep (Char -> Bool) -> Parser Char
satisfy (Defunc (Bool -> Char -> Bool)
-> Defunc Bool -> Defunc (Char -> Bool)
forall a1 a. Defunc (a1 -> a) -> Defunc a1 -> Defunc a
APP_H Defunc (Bool -> Char -> Bool)
forall a1 b. Defunc (a1 -> b -> a1)
CONST (Bool -> Defunc Bool
forall a. (Show a, Lift a) => a -> Defunc a
LIFTED Bool
True))

-- Composite Combinators
{-|
The combinator @someTill p end@ will try and parse @p@ as many times as possible (but at least once)
so long as @end@ cannot be successfully parsed. It will return the results from the successful parses of @p@.

@since 0.1.0.0
-}
someTill :: Parser a -> Parser b -> Parser [a]
someTill :: Parser a -> Parser b -> Parser [a]
someTill Parser a
p Parser b
end = Parser b -> Parser ()
forall a. Parser a -> Parser ()
notFollowedBy Parser b
end Parser () -> Parser [a] -> Parser [a]
forall a b. Parser a -> Parser b -> Parser b
*> (Parser a
p Parser a -> Parser [a] -> Parser [a]
forall a. Parser a -> Parser [a] -> Parser [a]
<:> Parser a -> Parser b -> Parser [a]
forall a b. Parser a -> Parser b -> Parser [a]
manyTill Parser a
p Parser b
end)