|
| 1 | +-- | These combinators will produce `Array`s, as opposed to the other combinators |
| 2 | +-- | of the same names in the __Parsing.Combinators__ module |
| 3 | +-- | which mostly produce `List`s. These `Array` combinators will run in a bit |
| 4 | +-- | less time (*~85% runtime*) than the similar `List` combinators, and they will run in a |
| 5 | +-- | lot less time (*~10% runtime*) than the similar combinators in __Data.Array__. |
| 6 | +-- | |
| 7 | +-- | If there is some other combinator which returns |
| 8 | +-- | a `List` but we want an `Array`, and there is no `Array` version of the |
| 9 | +-- | combinator in this module, then we can rely on the |
| 10 | +-- | [__`Data.Array.fromFoldable`__](https://pursuit.purescript.org/packages/purescript-arrays/docs/Data.Array#v:fromFoldable) |
| 11 | +-- | function for a pretty fast transformation from `List` to `Array`. |
| 12 | +module Parsing.Combinators.Array |
| 13 | + ( many |
| 14 | + , many1 |
| 15 | + , manyTill_ |
| 16 | + , manyIndex |
| 17 | + ) where |
| 18 | + |
| 19 | +import Prelude |
| 20 | + |
| 21 | +import Control.Alt (alt) |
| 22 | +import Control.Monad.Rec.Class (Step(..), tailRecM) |
| 23 | +import Data.Array as Array |
| 24 | +import Data.Array.NonEmpty (NonEmptyArray) |
| 25 | +import Data.Array.NonEmpty as Array.NonEmpty |
| 26 | +import Data.List (List(..), (:)) |
| 27 | +import Data.Maybe (Maybe(..)) |
| 28 | +import Data.Tuple (Tuple(..)) |
| 29 | +import Parsing (ParserT, fail) |
| 30 | +import Parsing.Combinators (try) |
| 31 | + |
| 32 | +-- | Match the phrase `p` as many times as possible. |
| 33 | +-- | |
| 34 | +-- | If `p` never consumes input when it |
| 35 | +-- | fails then `many p` will always succeed, |
| 36 | +-- | but may return an empty array. |
| 37 | +many :: forall s m a. ParserT s m a -> ParserT s m (Array a) |
| 38 | +many p = do |
| 39 | + rlist <- flip tailRecM Nil $ \xs -> alt |
| 40 | + do |
| 41 | + x <- try p |
| 42 | + pure (Loop (x : xs)) |
| 43 | + do |
| 44 | + pure (Done xs) |
| 45 | + pure $ Array.reverse $ Array.fromFoldable rlist |
| 46 | + |
| 47 | +-- | Match the phrase `p` as many times as possible, at least once. |
| 48 | +many1 :: forall s m a. ParserT s m a -> ParserT s m (NonEmptyArray a) |
| 49 | +many1 p = do |
| 50 | + xs <- many p |
| 51 | + case Array.NonEmpty.fromArray xs of |
| 52 | + Nothing -> fail "Expected at least 1" |
| 53 | + Just xs' -> pure xs' |
| 54 | + |
| 55 | +-- | Parse many phrases until the terminator phrase matches. |
| 56 | +-- | Returns the list of phrases and the terminator phrase. |
| 57 | +manyTill_ :: forall s a m e. ParserT s m a -> ParserT s m e -> ParserT s m (Tuple (Array a) e) |
| 58 | +manyTill_ p end = do |
| 59 | + Tuple rlist e <- flip tailRecM Nil \xs -> alt |
| 60 | + do |
| 61 | + t <- end |
| 62 | + pure (Done (Tuple xs t)) |
| 63 | + do |
| 64 | + x <- p |
| 65 | + pure (Loop (x : xs)) |
| 66 | + pure $ Tuple (Array.reverse $ Array.fromFoldable rlist) e |
| 67 | + |
| 68 | +-- | Parse the phrase as many times as possible, at least *N* times, but no |
| 69 | +-- | more than *M* times. |
| 70 | +-- | If the phrase can’t parse as least *N* times then the whole |
| 71 | +-- | parser fails. If the phrase parses successfully *M* times then stop. |
| 72 | +-- | The current phrase index, starting at *0*, is passed to the phrase. |
| 73 | +-- | |
| 74 | +-- | Returns the array of parse results and the number of results. |
| 75 | +-- | |
| 76 | +-- | `manyIndex n n (\_ -> p)` is equivalent to `replicateA n p`. |
| 77 | +manyIndex :: forall s m a. Int -> Int -> (Int -> ParserT s m a) -> ParserT s m (Tuple Int (Array a)) |
| 78 | +manyIndex from to p = |
| 79 | + if from > to || from < 0 then |
| 80 | + pure (Tuple 0 []) |
| 81 | + else do |
| 82 | + Tuple n rlist <- tailRecM go (Tuple 0 Nil) |
| 83 | + pure $ Tuple n $ Array.reverse $ Array.fromFoldable rlist |
| 84 | + where |
| 85 | + go (Tuple i xs) = |
| 86 | + if i >= to then |
| 87 | + pure (Done (Tuple i xs)) |
| 88 | + else alt |
| 89 | + do |
| 90 | + x <- p i |
| 91 | + pure (Loop (Tuple (i + 1) (x : xs))) |
| 92 | + do |
| 93 | + if i >= from then |
| 94 | + pure (Done (Tuple i xs)) |
| 95 | + else |
| 96 | + fail "Expected more phrases" |
0 commit comments