[SOLVED] Parsing: how to correctly advance on fail?

On that one, found a solution too, posting to have it recorded in the annals of PureScript tutoriality. Comes down to tail recursion:

foldParse ::  ∀ a. (a -> TextParser a) -> a -> TextParser a
foldParse f init = do
  acc :: Maybe a <- optionMaybe $ f init
  case acc of
    Nothing   -> pure init
    Just acc' -> foldParse f acc'

It’s your typical foldl, except we’re folding the underlying monad. Internally it uses optionMaybe to check/ignore if parser couldn’t consume any input. So no “Unexpected EOF” error, for example. A failure with having input consumed is propagated as usual.

A small usage example in terms of the thread-starter case (i.e. “grep implementation” via Parsing), where I use foldParse to concatenate each subsequent match to a string. So running over foo ffoo foo returns Found: foo, foo, foo, (didn’t handle the trailing comma for simplicity):

Code (click to unroll)
module Main where

import Prelude

import Data.Maybe (Maybe(..))
import Data.String.CodeUnits as Str
import Effect (Effect)
import Effect.Console (logShow)
import Parsing (Parser, runParser)
import Parsing.Combinators (optionMaybe, try, (<|>))
import Parsing.String (anyChar, string)

type TextParser = Parser String

foldParse ::  ∀ a. (a -> TextParser a) -> a -> TextParser a
foldParse f init = do
  acc :: Maybe a <- optionMaybe $ f init
  case acc of
    Nothing   -> pure init
    Just acc' -> foldParse f acc'

anyCharS :: TextParser String
anyCharS = anyChar >>= pure <<< Str.singleton

skipChar :: ∀ a. a -> TextParser a
skipChar marker = anyCharS *> pure marker

myParser :: TextParser String
myParser = string "foo"

main :: Effect Unit
main = logShow $ runParser "foo ffoo foo" $ foldParse concatMyParseToString "Found: "
  where
    concatMyParseToString acc = do
      ret <- try myParser <|> skipChar "skipped"
      pure if ret == "skipped" then acc
           else acc <> ret <> ", "