The amount of edge cases Quickcheck catches is beyond the most comprehensove unit test suite.
On a higher level though I would use parser combinators. If you have a grammar specification you can then directly transform it into a parser and thus would only need to test that you transformed it correctly (i.e. prove the proof as opposed to prove the implementation if that makes sense).
Basically - try to capture most of your invariants in the types and then you know your code is correct. The invariants may be wrong though, in which case integration testing may help.
However, I still have no idea how to test programs with complex state and IO in a way that isn't basically "script the 5 behaviors I can come up with and pray for the best", in which case I mostly don't even bother (i.e. UI etc...)
(I know this is a very short answer but I'm typing on a phone)
I can't see how exhaustively testing a small unit like this with high-level tests would be practical in terms of setup effort and run-time.