@@ -45,11 +45,13 @@ module Parsing.String
45
45
, eof
46
46
, match
47
47
, regex
48
+ , anyTill
48
49
, consumeWith
49
50
) where
50
51
51
52
import Prelude hiding (between )
52
53
54
+ import Control.Monad.Rec.Class (Step (..), tailRecM )
53
55
import Control.Monad.State (get )
54
56
import Data.Array.NonEmpty as NonEmptyArray
55
57
import Data.Either (Either (..))
@@ -62,9 +64,9 @@ import Data.String.CodeUnits as SCU
62
64
import Data.String.Regex as Regex
63
65
import Data.String.Regex.Flags (RegexFlags )
64
66
import Data.Tuple (Tuple (..))
65
- import Partial.Unsafe (unsafePartial )
66
67
import Parsing (ParseError (..), ParseState (..), ParserT (..), Position (..))
67
- import Parsing.Combinators ((<?>))
68
+ import Parsing.Combinators (try , (<?>), (<|>))
69
+ import Partial.Unsafe (unsafePartial )
68
70
69
71
-- | Match “end-of-file,” the end of the input stream.
70
72
eof :: forall m . ParserT String m Unit
@@ -263,11 +265,13 @@ regex pattern flags =
263
265
-- | Consume a portion of the input string while yielding a value.
264
266
-- |
265
267
-- | Takes a consumption function which takes the remaining input `String`
266
- -- | as its argument and returns three fields:
268
+ -- | as its argument and returns either an error message, or three fields:
267
269
-- |
268
270
-- | * `value` is the value to return.
269
271
-- | * `consumed` is the input `String` that was consumed. It is used to update the parser position.
270
272
-- | * `remainder` is the new remaining input `String`.
273
+ -- |
274
+ -- | This function is used internally to construct primitive `String` parsers.
271
275
consumeWith
272
276
:: forall m a
273
277
. (String -> Either String { value :: a , consumed :: String , remainder :: String } )
@@ -280,3 +284,32 @@ consumeWith f = ParserT
280
284
Right { value, consumed, remainder } ->
281
285
runFn2 done (ParseState remainder (updatePosString pos consumed remainder) true ) value
282
286
)
287
+
288
+ -- | Combinator which finds the first position in the input `String` where the
289
+ -- | phrase can parse. Returns both the
290
+ -- | parsed result and the unparsable input section searched before the parse.
291
+ -- | Will fail if no section of the input is parseable. To backtrack the input
292
+ -- | stream on failure, combine with `tryRethrow`.
293
+ -- |
294
+ -- | This combinator is equivalent to `manyTill_ anyCodePoint`, but it will be
295
+ -- | faster because it returns a slice of the input `String` for the
296
+ -- | section preceding the parse instead of a `List CodePoint`.
297
+ anyTill
298
+ :: forall m a
299
+ . Monad m
300
+ => ParserT String m a
301
+ -> ParserT String m (Tuple String a )
302
+ anyTill p = do
303
+ ParseState input1 _ _ <- get
304
+ Tuple input2 t <- tailRecM go unit
305
+ pure $ Tuple (SCU .take (SCU .length input1 - SCU .length input2) input1) t
306
+ where
307
+ go unit =
308
+ do
309
+ ParseState input2 _ _ <- get
310
+ t <- try p
311
+ pure $ Done $ Tuple input2 t
312
+ <|>
313
+ do
314
+ _ <- anyCodePoint
315
+ pure $ Loop unit
0 commit comments