sparsec

simple Haskell parser combinator library
git clone git://git.rr3.xyz/sparsec
Log | Files | Refs | README | LICENSE

commit efc35ad1105f9fa62017806adc9fcb9b5b96133d
parent c70d21a9968a3521a4b5cd4b9fb6d3a988c204c2
Author: Robert Russell <robert@rr3.xyz>
Date:   Thu, 29 Aug 2024 20:47:43 -0700

Add some combinators; remove some others

In particular, I forgot optional in Control.Applicative is a
thing, so we didn't need "opt".

Diffstat:
MREADME | 4++++
MSparsec.hs | 37++++++++++++++++++++++++++-----------
Mexample/Main.hs | 6+++---
3 files changed, 33 insertions(+), 14 deletions(-)

diff --git a/README b/README @@ -2,5 +2,9 @@ Simple Haskell parser combinator library I use this for toy PL experiments. It's not particularly special. +Some of the combinators are inspired by/taken from flatparse [1]. + See the example directory for an example of parsing a sequent-based language similar to Downen and Ariola's System CD. + +[1] https://github.com/AndrasKovacs/flatparse diff --git a/Sparsec.hs b/Sparsec.hs @@ -205,6 +205,9 @@ p `catch` h = ParseT \input loc -> -------------------------------------------------------------------------------- -- General parse combinators +eof :: Monad m => ParseT e m () +eof = (ByteString.null <$> read) >>= guard + -- Run a parser without changing the parser state. lookahead :: Monad m => ParseT e m a -> ParseT e m a lookahead p = do @@ -236,25 +239,37 @@ p `cut` e = p <|> err e try :: Monad m => ParseT e m a -> ParseT e m a try p = p `catch` \_ -> fail -opt :: Monad m => ParseT e m a -> ParseT e m (Maybe a) -opt p = (Just <$> p) <|> pure Nothing - -eof :: Monad m => ParseT e m () -eof = (ByteString.null <$> read) >>= guard - +-- Iterate a parsing function until it fails. I.e., +-- iter f a = pure a >>= f >>= f >>= ... +-- where the chain of binds is as long as possible without failure. iter :: Monad m => (a -> ParseT e m a) -> a -> ParseT e m a iter f a = (f a >>= iter f) <|> pure a --- (branch pc pt pf) runs pc; if it succeeds, it continues with pc, and +-- Parse a `b` and then zero or more `a`s, and then combine in a left-nested +-- fashion. +chainl :: Monad m => (b -> a -> b) -> ParseT e m b -> ParseT e m a -> ParseT e m b +chainl f pb pa = pb >>= iter \b -> (f b <$> pa) + +-- Parse zero or more `a`s (greedily) and then a `b`, and then combine in a +-- right-nested fashion. +chainr :: Monad m => (a -> b -> b) -> ParseT e m a -> ParseT e m b -> ParseT e m b +chainr f pa pb = f <$> pa <*> chainr f pa pb <|> pb + +-- `branch pc pt pf` runs pc; if it succeeds, it continues with pt, and -- otherwise it continues with pf. branch :: Monad m => ParseT e m a -> ParseT e m b -> ParseT e m b -> ParseT e m b branch pc pt pf = pc *> pt <|> pf -someUntil :: Monad m => ParseT e m a -> ParseT e m b -> ParseT e m [a] -someUntil p end = some (notp end *> p) +choice :: (Monad m, Foldable t) => t (ParseT e m a) -> ParseT e m a +choice = asum -manyUntil :: Monad m => ParseT e m a -> ParseT e m b -> ParseT e m [a] -manyUntil p end = many (notp end *> p) +-- `match scrut cases` binds scrut to the first non-failing case and returns +-- the result. +match :: (Monad m, Functor t, Foldable t) + => ParseT e m a + -> t (a -> ParseT e m b) + -> ParseT e m b +match scrut cases = choice $ (scrut >>=) <$> cases someSepBy :: Monad m => ParseT e m a -> ParseT e m b -> ParseT e m [a] someSepBy p sep = (:) <$> p <*> many (sep *> p) diff --git a/example/Main.hs b/example/Main.hs @@ -76,7 +76,7 @@ pIdent = do guard $ not $ isKw x pure x pDelim l p r = pSym l *> (p <* pSym r) `cut` Err -pManySepByWithTrailer p sep = p `someSepBy` sep <* opt sep <|> pure [] +pManySepByWithTrailer p sep = p `someSepBy` sep <* optional sep <|> pure [] pList p = p `pManySepByWithTrailer` pSym "," pXtorName = XtorName <$> pToken (char '#' *> pWord `cut` Err) @@ -126,7 +126,7 @@ pKind = \case pTypeLam = do pSym "\\" x <- pTypeName `cut` Err - mk <- opt (pSym ":" *> pKind 0 `cut` Err) + mk <- optional (pSym ":" *> pKind 0 `cut` Err) pSym "=>" `cut` Err body <- pType 0 `cut` Err pure $ TypeLam x mk body @@ -150,7 +150,7 @@ pCase = do pTermMu = do pSym "\\" x <- pTermName `cut` Err - ma <- opt (pSym ":" *> pType 1 `cut` Err) + ma <- optional (pSym ":" *> pType 1 `cut` Err) pSym "=>" `cut` Err body <- pStmt `cut` Err pure $ TermMu x ma body