on master help sign in
Update Parser docs, expose doctest setup
commit789e7d241097bc5f32caed109405b1a5f2c396cf authorHarendra Kumar <harendra@composewell.com> dateSun 12 Mar 2023, 01:50 (4 years ago) parent68cb972
8 files changed+127−110
core/src/DocTestDataParser.hs+20 core/src/Streamly/Data/Fold.hs+2−1 core/src/Streamly/Data/Parser.hs+69−40 core/src/Streamly/Data/ParserK.hs+1−1 core/src/Streamly/Internal/Data/Parser/ParserD.hs+6−49 core/src/Streamly/Internal/Data/Parser/ParserD/Type.hs+19−15 core/src/Streamly/Internal/Data/Parser/ParserK/Type.hs+9−4 core/streamly-core.cabal+1
core/src/DocTestDataParser.hsnew file+20
@@ -0,0 +1,20 @@
1+{- $setup
2+>>> :m
3+>>> import Control.Applicative ((<|>))
4+>>> import Data.Bifunctor (second)
5+>>> import Data.Char (isSpace)
6+>>> import qualified Data.Foldable as Foldable
7+>>> import qualified Data.Maybe as Maybe
8+
9+>>> import Streamly.Data.Fold (Fold)
10+>>> import Streamly.Data.Parser (Parser)
11+
12+>>> import qualified Streamly.Data.Fold as Fold
13+>>> import qualified Streamly.Data.Parser as Parser
14+>>> import qualified Streamly.Data.Stream as Stream
15+
16+For APIs that have not been released yet.
17+
18+>>> import qualified Streamly.Internal.Data.Fold as Fold
19+>>> import qualified Streamly.Internal.Data.Parser as Parser
20+-}
core/src/Streamly/Data/Fold.hs+2−1
@@ -7,7 +7,8 @@
77-- Stability : released
88-- Portability : GHC
99--
10−-- Composable stream consumers.
10+-- Fast, composable stream consumers with ability to terminate, supporting
11+-- stream fusion.
1112--
1213-- Please refer to "Streamly.Internal.Data.Fold" for more functions that have
1314-- not yet been released.
core/src/Streamly/Data/Parser.hs+69−40
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Data.Parser
34-- Copyright : (c) 2020 Composewell Technologies
@@ -6,50 +7,24 @@
67-- Stability : pre-release
78-- Portability : GHC
89--
9−-- Fast backtracking parsers with stream fusion and native streaming
10−-- capability. Parsers are just more powerful folds, folds and parsers can be
11−-- interconverted.
10+-- Fast, composable stream consumers with ability to terminate, backtrack and
11+-- fail, supporting stream fusion. Parsers are a natural extension of
12+-- "Streamly.Data.Fold". Parsers and folds can be interconverted.
1213--
13−-- Note that these parsers do not report the error context (e.g. line number or
14−-- column). This may be supported in future.
15−--
16−-- mtl instances are not provided. If the 'Parser' type is the top most layer
17−-- (which should be the case almost always) you can just use 'fromEffect' to
18−-- execute the lower layer monad effects.
19−--
20−-- == Performance Notes
21−--
22−-- This module is designed for fusion, inline the operations in this module for
23−-- fusion to occur, avoid using these operations in recursive calls, avoid
24−-- operations like 'sequence', 'asum' on these parsers. If you need these then
25−-- use the 'ParserK' module instead.
26−--
27−-- The 'Parser' type represents a stream consumer by composing state as data
28−-- which enables stream fusion. Stream fusion generates a tight loop without
29−-- any constructor allocations between the stages, providing C like performance
30−-- for the loop. Stream fusion works when multiple functions are combined in a
31−-- pipeline statically. Therefore, the operations in this module must be
32−-- inlined and must not be used recursively to allow for stream fusion.
33−--
34−-- Using the 'Parser' type parsing operations like 'one', 'splitWith' etc.
35−-- degrade quadratically (O(n^2)) when combined many times. If you need to
36−-- combine these operations, say more than 50 times in a single loop, then you
37−-- should use the continuation style parser type 'ParserK' instead. Also, if
38−-- you need to use these operations in a recursive loop you should use
39−-- 'ParserK' instead.
40−--
41−-- The 'ParserK' type represents a stream consumer by composing function calls,
42−-- therefore, a function call overhead is incurred at each composition. It is
43−-- quite fast in general but may be a few times slower than a fused parser.
44−-- However, it allows for scalable dynamic composition especially parsers can
45−-- be used in recursive calls. Using the 'ParserK' type operations like
46−-- 'splitWith' provide linear (O(n)) performance with respect to the number of
47−-- compositions..
48−--
49−-- 'Parser' and 'ParserK' types can be interconverted.
14+-- Please refer to "Streamly.Internal.Data.Parser" for functions that have
15+-- not yet been released.
5016--
5117module Streamly.Data.Parser
5218 (
19+ -- * Setup
20+ -- | To execute the code examples provided in this module in ghci, please
21+ -- run the following commands first.
22+ --
23+ -- $setup
24+
25+ -- * Overview
26+ -- $overview
27+
5328 -- * Parser Type
5429 Parser
5530
@@ -146,3 +121,57 @@ where
146121
147122import Streamly.Internal.Data.Parser
148123import Prelude hiding (dropWhile, takeWhile, filter)
124+
125+#include "DocTestDataParser.hs"
126+
127+-- $overview
128+--
129+-- Several combinators in this module can be many times faster than CPS based
130+-- parsers because of stream fusion. For example,
131+-- 'Streamly.Internal.Data.Parser.many' combinator in this module is much
132+-- faster than the 'Control.Applicative.many' combinator of
133+-- 'Control.Applicative.Alternative' type class used by CPS based parsers.
134+--
135+-- The use of 'Alternative' type class, in parsers has another drawback.
136+-- Alternative based parsers use plain Haskell lists to collect the results. In
137+-- a strict Monad like IO, the results are necessarily buffered before they can
138+-- be consumed. This may not perform optimally in streaming applications
139+-- processing large amounts of data. Equivalent combinators in this module can
140+-- consume the results of parsing using a 'Fold' or another parser, thus
141+-- providing a scalable and composable consumer.
142+--
143+-- Note that these parsers do not report the error context (e.g. line number or
144+-- column). This may be supported in future.
145+--
146+-- mtl instances are not provided. If the 'Parser' type is the top most layer
147+-- (which should be the case almost always) you can just use 'fromEffect' to
148+-- execute the lower layer monad effects.
149+--
150+-- == Performance Notes
151+--
152+-- The 'Parser' type represents a stream consumer by composing state as data
153+-- which enables stream fusion. Stream fusion generates a tight loop without
154+-- any constructor allocations between the stages, providing C like performance
155+-- for the loop. Stream fusion works when multiple functions are combined in a
156+-- pipeline statically. Therefore, the operations in this module must be
157+-- inlined and must not be used recursively to allow for stream fusion. Note
158+-- that operations like 'sequence', and 'asum' that compose pasrers using
159+-- recursion should be avoided with these parsers. You can use these with the
160+-- 'ParserK' module instead.
161+--
162+-- Using the 'Parser' type, parsing operations like 'one', 'splitWith' etc.
163+-- degrade quadratically (O(n^2)) when combined many times. If you need to
164+-- combine these operations, say more than 8 times in a single loop, then you
165+-- should consider using the continuation style parser type 'ParserK' instead.
166+-- Also, if you need to use these operations in a recursive loop you should use
167+-- 'ParserK' instead.
168+--
169+-- The 'ParserK' type represents a stream consumer by composing function calls,
170+-- therefore, a function call overhead is incurred at each composition. It is
171+-- quite fast in general but may be a few times slower than a fused parser.
172+-- However, it allows for scalable dynamic composition especially parsers can
173+-- be used in recursive calls. Using the 'ParserK' type operations like
174+-- 'splitWith' provide linear (O(n)) performance with respect to the number of
175+-- compositions..
176+--
177+-- 'Parser' and 'ParserK' types can be interconverted.
core/src/Streamly/Data/ParserK.hs+1−1
@@ -9,7 +9,7 @@
99-- Parsers using Continuation Passing Style (CPS). See notes in
1010-- "Streamly.Data.Parser" module to know when to use this module.
1111--
12−-- To run a 'ParserK' convert it to a 'Parser' and then run it.
12+-- To run a 'ParserK' use 'Streamly.Data.StreamK.parseChunks'.
1313--
1414module Streamly.Data.ParserK
1515 (
core/src/Streamly/Internal/Data/Parser/ParserD.hs+6−49
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Parser.ParserD
34-- Copyright : (c) 2020 Composewell Technologies
@@ -5,46 +6,13 @@
56-- Maintainer : streamly@composewell.com
67-- Stability : experimental
78-- Portability : GHC
8−--
9−-- Fast backtracking parsers with stream fusion and native streaming
10−-- capability.
11−--
12−-- 'Applicative' and 'Control.Applicative.Alternative' type class based
13−-- combinators from the
14−-- <http://hackage.haskell.org/package/parser-combinators parser-combinators>
15−-- package can also be used with the 'Parser' type. However, there are two
16−-- important differences between @parser-combinators@ and the equivalent ones
17−-- provided in this module in terms of performance:
18−--
19−-- 1) @parser-combinators@ use plain Haskell lists to collect the results, in a
20−-- strict Monad like IO, the results are necessarily buffered before they can
21−-- be consumed. This may not perform optimally in streaming applications
22−-- processing large amounts of data. Equivalent combinators in this module can
23−-- consume the results of parsing using a 'Fold', thus providing a scalability
24−-- and a composable consumer.
25−--
26−-- 2) Several combinators in this module can be many times faster because of
27−-- stream fusion. For example, 'Streamly.Internal.Data.Parser.many' combinator
28−-- in this module is much faster than the 'Control.Applicative.many' combinator
29−-- of 'Control.Applicative.Alternative' type class.
30−--
31−-- = Errors
32−--
33−-- Failing parsers in this module throw the 'D.ParseError' exception.
34−--
35−-- = Naming
36−--
37−-- As far as possible, try that the names of the combinators in this module are
38−-- consistent with:
39−--
40−-- * <https://hackage.haskell.org/package/base/docs/Text-ParserCombinators-ReadP.html base/Text.ParserCombinators.ReadP>
41−-- * <http://hackage.haskell.org/package/parser-combinators parser-combinators>
42−-- * <http://hackage.haskell.org/package/megaparsec megaparsec>
43−-- * <http://hackage.haskell.org/package/attoparsec attoparsec>
44−-- * <http://hackage.haskell.org/package/parsec parsec>
459
4610module Streamly.Internal.Data.Parser.ParserD
4711 (
12+ -- * Setup
13+ -- $setup
14+
15+ -- * Types
4816 Parser (..)
4917 , ParseError (..)
5018 , Step (..)
@@ -311,18 +279,7 @@ import Prelude hiding
311279-- import Streamly.Internal.Data.Parser.ParserD.Tee
312280import Streamly.Internal.Data.Parser.ParserD.Type
313281
314−--
315−-- $setup
316−-- >>> :m
317−-- >>> import Prelude hiding ()
318−-- >>> import Control.Applicative
319−-- >>> import Data.Char (isSpace)
320−-- >>> import qualified Data.Foldable as Foldable
321−-- >>> import qualified Data.Maybe as Maybe
322−-- >>> import qualified Streamly.Data.Stream as Stream
323−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
324−-- >>> import qualified Streamly.Internal.Data.Fold as Fold
325−-- >>> import qualified Streamly.Internal.Data.Parser as Parser
282+#include "DocTestDataParser.hs"
326283
327284-------------------------------------------------------------------------------
328285-- Downgrade a parser to a Fold
core/src/Streamly/Internal/Data/Parser/ParserD/Type.hs+19−15
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Parser.ParserD.Type
34-- Copyright : (c) 2020 Composewell Technologies
@@ -160,9 +161,23 @@
160161-- We can distribute and scan/parse a stream using both folds and parsers and
161162-- merge the resulting streams using different merge strategies (e.g.
162163-- interleaving or serial).
164+--
165+-- == Naming
166+--
167+-- As far as possible, try that the names of the combinators in this module are
168+-- consistent with:
169+--
170+-- * <https://hackage.haskell.org/package/base/docs/Text-ParserCombinators-ReadP.html base/Text.ParserCombinators.ReadP>
171+-- * <http://hackage.haskell.org/package/parser-combinators parser-combinators>
172+-- * <http://hackage.haskell.org/package/megaparsec megaparsec>
173+-- * <http://hackage.haskell.org/package/attoparsec attoparsec>
174+-- * <http://hackage.haskell.org/package/parsec parsec>
163175
164176module Streamly.Internal.Data.Parser.ParserD.Type
165177 (
178+ -- * Setup
179+ -- $setup
180+
166181 -- * Types
167182 Initial (..)
168183 , Step (..)
@@ -214,19 +229,8 @@ import qualified Control.Monad.Fail as Fail
214229import qualified Streamly.Internal.Data.Fold.Type as FL
215230
216231import Prelude hiding (concatMap, filter)
217−--
218−-- $setup
219−-- >>> :m
220−-- >>> import Control.Applicative ((<|>))
221−-- >>> import Data.Bifunctor (second)
222−-- >>> import Prelude hiding (concatMap, span)
223−-- >>> import Streamly.Data.Fold (Fold)
224−-- >>> import Streamly.Data.Parser (Parser)
225−-- >>> import qualified Streamly.Data.Fold as Fold
226−-- >>> import qualified Streamly.Data.Stream as Stream
227−-- >>> import qualified Streamly.Internal.Data.Stream as Stream (parse)
228−-- >>> import qualified Streamly.Internal.Data.Parser as Parser
229−-- >>> import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
232+
233+#include "DocTestDataParser.hs"
230234
231235-- XXX The only differences between Initial and Step types are:
232236--
@@ -240,7 +244,7 @@ import Prelude hiding (concatMap, filter)
240244-- Step itself. That will also simplify the implementation of various parsers
241245-- where the processing in intiial is just a sepcial case of step, see
242246-- takeBetween for example.
243−--
247+
244248-- | The type of a 'Parser''s initial action.
245249--
246250-- /Internal/
@@ -1177,7 +1181,7 @@ dieM err = Parser undefined (IError <$> err) undefined
11771181-- Note: The implementation of '<|>' is not lazy in the second
11781182-- argument. The following code will fail:
11791183--
1180−-- >>> Stream.parse (ParserD.satisfy (> 0) <|> undefined) $ Stream.fromList [1..10]
1184+-- >>> Stream.parse (Parser.satisfy (> 0) <|> undefined) $ Stream.fromList [1..10]
11811185-- *** Exception: Prelude.undefined
11821186-- ...
11831187--
core/src/Streamly/Internal/Data/Parser/ParserK/Type.hs+9−4
@@ -8,13 +8,18 @@
88--
99-- CPS style implementation of parsers.
1010--
11−-- The CPS representation allows linear performance for Applicative, sequenceA,
12−-- Monad, sequence, and Alternative, choice operations compared to the
13−-- quadratic complexity of the corresponding direct style operations. However,
14−-- direct style operations allow fusion with ~10x better performance than CPS.
11+-- The CPS representation allows linear performance for Applicative, sequence,
12+-- Monad, Alternative, and choice operations compared to the quadratic
13+-- complexity of the corresponding direct style operations. However, direct
14+-- style operations allow fusion with ~10x better performance than CPS.
1515--
1616-- The direct style representation does not allow for recursive definitions of
1717-- "some" and "many" whereas CPS allows that.
18+--
19+-- 'Applicative' and 'Control.Applicative.Alternative' type class based
20+-- combinators from the
21+-- <http://hackage.haskell.org/package/parser-combinators parser-combinators>
22+-- package can also be used with the 'ParserK' type.
1823
1924module Streamly.Internal.Data.Parser.ParserK.Type
2025 (
core/streamly-core.cabal+1
@@ -42,6 +42,7 @@ extra-source-files:
4242 src/DocTestDataFold.hs
4343 src/DocTestDataMutArray.hs
4444 src/DocTestDataMutArrayGeneric.hs
45+ src/DocTestDataParser.hs
4546
4647 -- This is duplicated
4748 src/Streamly/Internal/Data/Array/ArrayMacros.h