Reputation: 190
I have a file parser that uses permute
from parsec
to combine various Parser
s. 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
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