@@ -45,11 +45,13 @@ module Parsing.String
4545 , eof
4646 , match
4747 , regex
48+ , anyTill
4849 , consumeWith
4950 ) where
5051
5152import Prelude hiding (between )
5253
54+ import Control.Monad.Rec.Class (Step (..), tailRecM )
5355import Control.Monad.State (get )
5456import Data.Array.NonEmpty as NonEmptyArray
5557import Data.Either (Either (..))
@@ -62,9 +64,9 @@ import Data.String.CodeUnits as SCU
6264import Data.String.Regex as Regex
6365import Data.String.Regex.Flags (RegexFlags )
6466import Data.Tuple (Tuple (..))
65- import Partial.Unsafe (unsafePartial )
6667import Parsing (ParseError (..), ParseState (..), ParserT (..), Position (..))
67- import Parsing.Combinators ((<?>))
68+ import Parsing.Combinators (alt , try , (<?>))
69+ import Partial.Unsafe (unsafePartial )
6870
6971-- | Match “end-of-file,” the end of the input stream.
7072eof :: forall m . ParserT String m Unit
@@ -263,11 +265,13 @@ regex pattern flags =
263265-- | Consume a portion of the input string while yielding a value.
264266-- |
265267-- | 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:
267269-- |
268270-- | * `value` is the value to return.
269271-- | * `consumed` is the input `String` that was consumed. It is used to update the parser position.
270272-- | * `remainder` is the new remaining input `String`.
273+ -- |
274+ -- | This function is used internally to construct primitive `String` parsers.
271275consumeWith
272276 :: forall m a
273277 . (String -> Either String { value :: a , consumed :: String , remainder :: String } )
@@ -280,3 +284,33 @@ consumeWith f = ParserT
280284 Right { value, consumed, remainder } ->
281285 runFn2 done (ParseState remainder (updatePosString pos consumed remainder) true ) value
282286 )
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 = alt
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
316+ )
0 commit comments