I tried using parser generators; it's been quite a while, and the state of the art is almost certainly improved over lex+yacc and bison. But I got sick of diagnostics no more specific than "Error in line 15", and sticking intermediate states into yacc to improve errors broke the grammars. Probably I should have looked more closely at ANTLR.
So I wound up at state-machine driven lexers with recursive descent for parsing, and since the code is hand crafted it's relatively easy to print errors that make sense to users. The parsers might be harder to maintain. But they are easy to understand.
I am not a career compiler jockey, for the most part I've just wanted to get languages of medium complexity into tree form so I can reason about things and generate some code or do some analysis. Mostly I want to get another job done, but emitting quality error messages is a general good.
While I certainly understand the desire to hand-write parsers, I think people often underestimate how useful it can be to have an explicit grammar for your language (even if you hand-write the parser then just fuzz test it against the grammar or something like that). It can help so much with external tooling, and you can often analyse explicit grammars in ways you could never analyse a hand-written parser.
As a concrete example, I recently had to write a parser for i3 config files and trying to figure out what was and wasn't syntactically valid was so horrendous that I ended up just hacking together some half-arsed thing just about works for the one or two files I tested it but that I'm fairly sure is going to break horrendously at some point.
So I wound up at state-machine driven lexers with recursive descent for parsing, and since the code is hand crafted it's relatively easy to print errors that make sense to users. The parsers might be harder to maintain. But they are easy to understand.
I am not a career compiler jockey, for the most part I've just wanted to get languages of medium complexity into tree form so I can reason about things and generate some code or do some analysis. Mostly I want to get another job done, but emitting quality error messages is a general good.