Writing a recursive descent parser is pretty simple, but the downside is that you it is hard to figure out the exact spec for the grammar when looking at the code. Another downside is that it's often easy to break ambiguities in a recursive descent parser by hacking some special behaviour. Unfortunately this can end up causing things being parsed in unforeseen ways.
If you need to feed your grammar into some tool (for instance you're making a language plugin) it's also beneficial to have an actual grammar for it.
When you want to error report on the other hand, you might want to parse more than is valid in your language in order to produce good error messages.
So we roughly end up with the following cases:
- You're writing the main compiler for a general purpose language: use a hand written parser to have good control over error messages and recovery.
- You are just parsing a small DSL: use a parser generator if you want.
- You're writing a tool that works on correct code and transforms it: a parser generator is sufficient.
- You want to iterate on your grammar and see that it is consistent: use a parser generator.