Kevin P. Barry
Kevin P. Barry

Reputation: 190

Permutation parsing with `ParsecT`?

I have a file parser that uses permute from parsec to combine various Parsers. It looks like permute only allows the Identity monad, which I'm sure cuts down on the algorithmic complexity.

Now I'm trying to convert all of my parsing code to use MyErrorClass m => ParsecT String () m, where Monad m => MyErrorClass m is a class with special error handling. Specifically, the code that uses permute now needs to combine parsers that require MyErrorClass m so that they can provide structured errors rather than just using fail. (permute is used only when parsing module config files for a programming language. The file format allows embedding of some language constructs, reusing parsers from the compiler.)

I only use permute in 3 places and that number isn't likely to increase, so it isn't out of the question to hack together some sort of one-off function for each of the 3. Alternatively, I can just remove the flexibility in field ordering, but I'd rather not.


Before I do anything major, am I missing a simple solution in parsec? Maybe I can somehow stuff ParsecT String () m into permute from parsers as opposed to parsec? (This would add a dependency, but might be worth it.)


Edit:

Here is some background regarding why structured errors are useful for parsing.

For the most part, I do parsing and validation separately. For example, checking for duplicate symbol definitions happens after parsing succeeds.

I frequently get parsing errors similar to expected //, /* or argument type, and it can take a while (for me, the language creator) to understand what parser is being invoked.

MyErrorClass allows structuring of error messages, e.g.

("In parsing of function declaration for " ++ show f) ??> do
  -- parse the components of function f
  -- any error messages here will be nested under the message above

Being able to add context like this will at least tell the user what the parser was trying to do at the time of failure.

Lastly, using MyErrorClass will also allow fail-fast errors that cannot be suppressed by try or <|>. (In a few places I have explicit error messages for using obsolete syntax.)

Upvotes: 3

Views: 228

Answers (1)

Kevin P. Barry
Kevin P. Barry

Reputation: 190

As @danidiaz suggested, parser-combinators easily solves the literal problem of permuting ParsecT.

The caveat is that using ParsecT with a Monad m that transforms errors (even its own) isn't particularly useful, other than being able to use fail-fast errors that can't be ignored by <|> and try.

  • You can't "demote" the error state of the ParsecT to m's own type of error because (AFAIK) there is no way to access the current error state of the ParsecT.

  • ParsecT lacks something comparable to StateT's mapStateT. This means that there is no way to transform an error stored in m without actually executing the parser with runPT (etc.), which isn't possible to do mid-parse.

In summary, ParsecT doesn't leak m in the same way that StateT does, making some of m's functionality inaccessible from within ParsecT.

Upvotes: 1

Related Questions