{-# LANGUAGE PatternSynonyms #-} {-| Module : Parsley.Fold Description : The "folding" combinators: chains and iterators License : BSD-3-Clause Maintainer : Jamie Willis Stability : stable This module contains the combinator concerned with some form of iteration or input folding. Notably, this includes the traditional `many` and `some` combinators. @since 0.1.0.0 -} module Parsley.Fold ( many, some, manyN, skipMany, skipSome, skipManyN, --loop, sepBy, sepBy1, endBy, endBy1, sepEndBy, sepEndBy1, chainl1, chainr1, chainl, chainr, infixl1, infixr1, prefix, postfix, manyr, manyl, somer, somel ) where import Prelude hiding (pure, (<*>), (<$>), (*>), (<*)) import Parsley.Alternative ((<|>), option) import Parsley.Applicative (pure, (<*>), (<$>), (*>), (<*), (<:>), (<**>), void) import Parsley.Defunctionalized (Defunc(FLIP, ID, COMPOSE, EMPTY, CONS, CONST), pattern FLIP_H, pattern UNIT) import Parsley.Internal (Parser) import Parsley.ParserOps (ParserOps) import Parsley.Register (get, modify, newRegister) import qualified Parsley.Internal as Internal (loop) {-| The combinator @loop body exit@ parses @body@ zero or more times until it fails. If the final @body@ failed having not consumed input, @exit@ is performed, otherwise the combinator fails: > loop body exit = let go = body *> go <|> exit in go @since 2.0.0.0 -} loop :: Parser () -> Parser a -> Parser a loop = Internal.loop {-| This combinator parses repeated applications of an operator to a single final operand. This is primarily used to parse prefix operators in expressions. @since 2.0.0.0 -} prefix :: Parser (a -> a) -> Parser a -> Parser a prefix op p = postfix (pure ID) (FLIP_H COMPOSE <$> op) <*> p {-| This combinator parses repeated applications of an operator to a single initial operand. This is primarily used to parse postfix operators in expressions. @since 2.0.0.0 -} postfix :: Parser a -> Parser (a -> a) -> Parser a postfix p op = newRegister p $ \r -> loop (modify r op) (get r) -- Parser Folds {-| @manyr f k p@ parses __zero__ or more @p@s and combines the results using the function @f@. When @p@ fails without consuming input, the terminal result @k@ is returned. > many = manyr CONS EMPTY @since 2.0.0.0 -} manyr :: (ParserOps repf, ParserOps repk) => repf (a -> b -> b) -> repk b -> Parser a -> Parser b manyr f k p = prefix (f <$> p) (pure k) {-| @somer f k p@ parses __one__ or more @p@s and combines the results using the function @f@. When @p@ fails without consuming input, the terminal result @k@ is returned. > some = somer CONS EMPTY @since 2.0.0.0 -} somer :: (ParserOps repf, ParserOps repk) => repf (a -> b -> b) -> repk b -> Parser a -> Parser b somer f k p = f <$> p <*> manyr f k p {-| @manyl f k p@ parses __zero__ or more @p@s and combines the results using the function @f@. The accumulator is initialised with the value @k@. @since 2.0.0.0 -} manyl :: (ParserOps repf, ParserOps repk) => repf (b -> a -> b) -> repk b -> Parser a -> Parser b manyl f k p = postfix (pure k) ((FLIP <$> pure f) <*> p) {-| @somel f k p@ parses __one__ or more @p@s and combines the results using the function @f@. The accumulator is initialised with the value @k@. @since 2.0.0.0 -} somel :: (ParserOps repf, ParserOps repk) => repf (b -> a -> b) -> repk b -> Parser a -> Parser b somel f k p = postfix (f <$> pure k <*> p) ((FLIP <$> pure f) <*> p) -- Chain Combinators {-| @infixl1 wrap p op @ parses one or more occurrences of @p@, separated by @op@. Returns a value obtained by a /left/ associative application of all functions returned by @op@ to the values returned by @p@. The function @wrap@ is used to transform the initial value from @p@ into the correct form. @since 2.0.0.0 -} infixl1 :: ParserOps rep => rep (a -> b) -> Parser a -> Parser (b -> a -> b) -> Parser b infixl1 wrap p op = postfix (wrap <$> p) (FLIP <$> op <*> p) {-| The classic version of the left-associative chain combinator. See 'infixl1'. > chainl1 p op = infixl1 ID p op @since 0.1.0.0 -} chainl1 :: Parser a -> Parser (a -> a -> a) -> Parser a chainl1 = infixl1 ID {-| @infixr1 wrap p op @ parses one or more occurrences of @p@, separated by @op@. Returns a value obtained by a /right/ associative application of all functions returned by @op@ to the values returned by @p@. The function @wrap@ is used to transform the final value from @p@ into the correct form. @since 2.0.0.0 -} infixr1 :: ParserOps rep => rep (a -> b) -> Parser a -> Parser (a -> b -> b) -> Parser b infixr1 wrap p op = let go = p <**> (FLIP <$> op <*> go <|> pure wrap) in go {-| The classic version of the right-associative chain combinator. See 'infixr1'. > chainr1 p op = infixr1 ID p op @since 0.1.0.0 -} chainr1 :: Parser a -> Parser (a -> a -> a) -> Parser a chainr1 = infixr1 ID {-| Like `chainr1`, but may parse zero occurences of @p@ in which case the value is returned. @since 0.1.0.0 -} chainr :: ParserOps rep => Parser a -> Parser (a -> a -> a) -> rep a -> Parser a chainr p op x = option x (chainr1 p op) {-| Like `chainl1`, but may parse zero occurences of @p@ in which case the value is returned. @since 0.1.0.0 -} chainl :: ParserOps rep => Parser a -> Parser (a -> a -> a) -> rep a -> Parser a chainl p op x = option x (chainl1 p op) -- Derived Combinators {-| Attempts to parse the given parser __zero__ or more times, collecting all of the successful results into a list. Same as @manyN 0@ @since 0.1.0.0 -} many :: Parser a -> Parser [a] many = manyr CONS EMPTY {-| Attempts to parse the given parser __n__ or more times, collecting all of the successful results into a list. @since 0.1.0.0 -} manyN :: Int -> Parser a -> Parser [a] manyN n p = foldr (const (p <:>)) (many p) [1..n] {-| Attempts to parse the given parser __one__ or more times, collecting all of the successful results into a list. Same as @manyN 1@ @since 0.1.0.0 -} some :: Parser a -> Parser [a] some = manyN 1 {-| Like `many`, excepts discards its results. @since 0.1.0.0 -} skipMany :: Parser a -> Parser () --skipMany p = loop (void p) unit skipMany = void . manyl CONST UNIT -- This is still faster, the above generates better code, but GHC starts doing weird things! {-| Like `manyN`, excepts discards its results. @since 0.1.0.0 -} skipManyN :: Int -> Parser a -> Parser () skipManyN n p = foldr (const (p *>)) (skipMany p) [1..n] {-| Like `some`, excepts discards its results. @since 0.1.0.0 -} skipSome :: Parser a -> Parser () skipSome = skipManyN 1 {-| @sepBy p sep@ parses __zero__ or more occurrences of @p@, separated by @sep@. Returns a list of values returned by @p@. @since 0.1.0.0 -} sepBy :: Parser a -> Parser b -> Parser [a] sepBy p sep = option EMPTY (sepBy1 p sep) {-| @sepBy1 p sep@ parses __one__ or more occurrences of @p@, separated by @sep@. Returns a list of values returned by @p@. @since 0.1.0.0 -} sepBy1 :: Parser a -> Parser b -> Parser [a] sepBy1 p sep = p <:> many (sep *> p) {-| @endBy p sep@ parses __zero__ or more occurrences of @p@, separated and ended by @sep@. Returns a list of values returned by @p@. @since 0.1.0.0 -} endBy :: Parser a -> Parser b -> Parser [a] endBy p sep = many (p <* sep) {-| @endBy1 p sep@ parses __one__ or more occurrences of @p@, separated and ended by @sep@. Returns a list of values returned by @p@. @since 0.1.0.0 -} endBy1 :: Parser a -> Parser b -> Parser [a] endBy1 p sep = some (p <* sep) {-| @sepEndBy p sep@ parses __zero__ or more occurrences of @p@, separated and /optionally/ ended by @sep@. Returns a list of values returned by @p@. @since 0.1.0.0 -} sepEndBy :: Parser a -> Parser b -> Parser [a] sepEndBy p sep = option EMPTY (sepEndBy1 p sep) {-| @sepEndBy1 p sep@ parses __one__ or more occurrences of @p@, separated and /optionally/ ended by @sep@. Returns a list of values returned by @p@. @since 0.1.0.0 -} sepEndBy1 :: Parser a -> Parser b -> Parser [a] sepEndBy1 p sep = let seb1 = p <:> option EMPTY (sep *> option EMPTY seb1) in seb1