Skip to content

Commit cd05ce4

Browse files
Add chainl1, chainr1 to ParserK
1 parent e6cd01e commit cd05ce4

File tree

3 files changed

+90
-3
lines changed

3 files changed

+90
-3
lines changed

core/src/DocTestDataParserK.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
99
>>> import qualified Streamly.Data.Parser as Parser
1010
>>> import qualified Streamly.Data.ParserK as ParserK
11+
>>> import qualified Streamly.Data.Stream as Stream
12+
>>> import qualified Streamly.Data.StreamK as StreamK
13+
>>> import qualified Streamly.Unicode.Parser as Parser
1114
1215
For APIs that have not been released yet.
1316

core/src/Streamly/Data/Parser.hs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,9 @@
8686
--
8787
-- Use ParserK when recursive use is required:
8888
--
89-
-- >>> import Streamly.Data.ParserK (ParserK, parserK)
89+
-- >>> import Streamly.Data.ParserK (ParserK)
90+
-- >>> import Streamly.Data.StreamK (parserK)
9091
-- >>> import qualified Streamly.Data.StreamK as StreamK
91-
-- >>> import qualified Streamly.Internal.Data.StreamK as StreamK (parse)
9292
--
9393
-- >>> :{
9494
-- >>> p, p1, p2 :: Monad m => ParserK Char m String

core/src/Streamly/Internal/Data/ParserK/Type.hs

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE CPP #-}
12
-- |
23
-- Module : Streamly.Internal.Data.Parser.ParserK.Type
34
-- Copyright : (c) 2020 Composewell Technologies
@@ -18,17 +19,33 @@
1819
--
1920
module Streamly.Internal.Data.ParserK.Type
2021
(
22+
-- * Setup
23+
-- | To execute the code examples provided in this module in ghci, please
24+
-- run the following commands first.
25+
--
26+
-- $setup
27+
28+
-- * Types
2129
Step (..)
2230
, Input (..)
2331
, ParseResult (..)
2432
, ParserK (..)
33+
34+
-- * Adapting from Parser
35+
, parserDone
2536
, parserK -- XXX move to StreamK module
2637
, toParser -- XXX unParserK, unK, unPK
38+
39+
-- * Basic Parsers
2740
, fromPure
2841
, fromEffect
2942
, die
3043

31-
, parserDone
44+
-- * Expression Parsers
45+
, chainl
46+
, chainl1
47+
, chainr
48+
, chainr1
3249

3350
-- * Deprecated
3451
, adapt
@@ -52,6 +69,8 @@ import GHC.Types (SPEC(..))
5269
import qualified Control.Monad.Fail as Fail
5370
import qualified Streamly.Internal.Data.Parser.Type as ParserD
5471

72+
#include "DocTestDataParserK.hs"
73+
5574
-------------------------------------------------------------------------------
5675
-- Developer Notes
5776
-------------------------------------------------------------------------------
@@ -534,3 +553,68 @@ toParser parser = ParserD.Parser step initial extract
534553
forall s. toParser (parserK s) = s #-}
535554
{-# RULES "toParser/parserK fusion" [2]
536555
forall s. parserK (toParser s) = s #-}
556+
557+
-- | @chainl1 p op x@ parses /one/ or more occurrences of @p@, separated by
558+
-- @op@. Returns a value obtained by a /left/ associative application of all
559+
-- functions returned by @op@ to the values returned by @p@.
560+
--
561+
-- >>> num = Parser.decimal
562+
-- >>> plus = Parser.char '+' *> pure (+)
563+
-- >>> expr = ParserK.chainl1 (StreamK.parserK num) (StreamK.parserK plus)
564+
-- >>> StreamK.parse expr $ StreamK.fromStream $ Stream.fromList "1+2+3"
565+
-- Right 6
566+
--
567+
-- If you're building full expression parsers with operator precedence and
568+
-- associativity, consider using @makeExprParser@ from the @parser-combinators@
569+
-- package.
570+
--
571+
-- See also 'Streamly.Internal.Data.Parser.deintercalate'.
572+
--
573+
{-# INLINE chainl1 #-}
574+
chainl1 :: ParserK b IO a -> ParserK b IO (a -> a -> a) -> ParserK b IO a
575+
chainl1 p op = p >>= go
576+
577+
where
578+
579+
go l = step l <|> pure l
580+
581+
step l = do
582+
f <- op
583+
r <- p
584+
go (f l r)
585+
586+
-- | @chainl p op x@ is like 'chainl1' but allows /zero/ or more occurrences of
587+
-- @p@, separated by @op@. If there are zero occurrences of @p@, the value @x@
588+
-- is returned.
589+
{-# INLINE chainl #-}
590+
chainl :: ParserK b IO a -> ParserK b IO (a -> a -> a) -> a -> ParserK b IO a
591+
chainl p op x = chainl1 p op <|> pure x
592+
593+
-- | Like chainl1 but parses right associative application of the operator
594+
-- instead of left associative.
595+
--
596+
-- >>> num = Parser.decimal
597+
-- >>> pow = Parser.char '^' *> pure (^)
598+
-- >>> expr = ParserK.chainr1 (StreamK.parserK num) (StreamK.parserK pow)
599+
-- >>> StreamK.parse expr $ StreamK.fromStream $ Stream.fromList "2^3^2"
600+
-- Right 512
601+
--
602+
{-# INLINE chainr1 #-}
603+
chainr1 :: ParserK b IO a -> ParserK b IO (a -> a -> a) -> ParserK b IO a
604+
chainr1 p op = p >>= go
605+
606+
where
607+
608+
go l = step l <|> pure l
609+
610+
step l = do
611+
f <- op
612+
r <- chainr1 p op
613+
return (f l r)
614+
615+
-- | @chainr p op x@ is like 'chainr1' but allows /zero/ or more occurrences of
616+
-- @p@, separated by @op@. If there are zero occurrences of @p@, the value @x@
617+
-- is returned.
618+
{-# INLINE chainr #-}
619+
chainr :: ParserK b IO a -> ParserK b IO (a -> a -> a) -> a -> ParserK b IO a
620+
chainr p op x = chainr1 p op <|> pure x

0 commit comments

Comments
 (0)