9
votes

I've been working with parsec and I have trouble debugging my code. For example, I can set a breakpoint in ghci, but I'm not sure how to see how much of the input has been consumed, or things like that.

Are there tools / guidelines to help with debugging parsec code?

3
When you run a parser - it should return an Either ParseError a - printing the ParseError will show you the source position and any error messages. Having your sample input in a text editor is probably the easiest way to see where the source position corresponds to, although you could write your own error throwing function to capture the rest-of-input and print its initial content (getInput returns the remaining input).stephen tetley

3 Answers

9
votes

This page might help.

Debug.trace is your friend, it allows you to essentially do some printf debugging. It evaluates and prints its first argument and then returns its second. So if you have something like

foo :: Show a => a -> a
foo = bar . quux

You can debug the 'value' of foo's parameter by changing foo to the following:

import Debug.Trace(trace)

foo :: Show a => a -> a
foo x = bar $ quux $ trace ("x is: " ++ show x) x

foo will now work the same way as it did before, but when you call foo 1 it will now print x is: 1 to stderr when evaluated.

For more in-depth debugging, you'll want to use GHCI's debugging commands. Specifically, it sounds like you're looking for the :force command, which forces the evaluation of a variable and prints it out. (The alternative is the :print command, which prints as much of the variable as has been evaluated, without evaluating any more.)

Note that :force is more helpful in figuring out the contents of a variable, but may also change the semantics of your program (if your program depends upon laziness).

A general GHCI debugging workflow looks something like this:

  • Use :break to set breakpoints
  • Use :list and :show context to check where you are in the code
  • Use :show bindings to check the variable bindings
  • Try using :print to see what's currently bound
  • Use :force if necessary to check your bindings

If you're trying to debug an infinite loop, it also helps to use

  • :set -fbreak-on-error
  • :trace myLoopingFunc x y

Then you can hit Ctrl-C during the loop and use :history to see what's looping.

5
votes

You might be able to use the <?> operator in Text.Parsec.Prim to make better error messages for you and your users. There are some examples in Real World Haskell. If your parser has good sub-parts then you could setup a few simple tests (or use HUnit) to ensure they work separately as expected.

1
votes

Another useful trick:

_ <- many anyChar >>= fail this will generate an error (Left) of:

unexpected end of input
the remaining 'string'

I think the parserTrace and parserTraced functions mentioned here http://hackage.haskell.org/package/parsec-3.1.13.0/docs/Text-Parsec-Combinator.html#g:1 do something similar to the above.