Help with looping parser

I’m trying parsing a very simple syntax which is a list of 3 possible operations represented by:

  • the character “H”,
  • the character “V”
  • the character “S” followed by an integer.

The operations are represented by the following type:

type Operation
    = Horizontal
    | Vertical
    | Shift Int

There can be any number of operations in succession. So, a valid input would look something like: VVHS3HHV. I was able to implement it using 3 simple parsers (one for each operation), and combining them with oneOf. this seems to work as expected, and anything other than these 3 operations results in an error, just as I want.

Then I tried to use Parser.loop to be able to read an arbitrary number of these operations, and return a List Operation, again, this works as expected, however, I would like the parser to fail if it comes across an invalid character, right now, when this happens, the parser succeeds and returns an empty list (or rather; it will parse as long as it encounters valid operations, and then return this list and stop parsing if it encounters an invalid one).

I suspect that the problem is in my loop helper function, but I’m not quite sure how to express that I want the parser to fail if it encounters an invalid character. Here’s the implementation of the loop and its helper:

operations : Parser (List Operation)
operations =
    loop [] operationsHelp


operationsHelp : List Transform -> Parser (Step (List Operation) (List Operation))
operationsHelp reverseOps =
    oneOf
        [ succeed (\op -> Loop (op :: reverseOps))
            |= operation
        , succeed ()
            |> map (\_ -> Done (List.reverse reverseOps))
        ]

Any help would be appreciated! (I can also post the implementation of the simple operation parsers if it would help)

Parser.succeed, as the name might suggest, will always succeed with a given value; regardless of its input.

The effect you’re seeing is a consequence of that, it’ll happily parse any of the operations and if it can’t find one it succeeds with nothing and then returns Done.

Instead of succeeding, you want to finish the loop only when you’ve got to the end of the input (I assume). Simply replace your succeed () with end. Now the parser will crash and burn if it hits some unexpected input and fail.

This indeed does exactly what I want! Thank you!

1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.