It's not just you, I totally agree about standard recursive descent. The way that precedence is decided by the control flow makes it feel quite inflexible, and you have to read a lot of code to actually understand the operator ordering.
I've got a couple of personal projects on the go involving Pratt parsers, which are a variant on the operator precedence parser you link. The first being a JS parser written in JS and the second being a language prototype called Radiance(private repo. ATM) that compiles to JS.
This is basically the core part of the Pratt parser logic...
function parse (tokens, precedence) {
let next_token = tokens.lookahead();
let parselet = prefix_parselets.get(next_token);
if (parselet == null) {
throw UnexpectedToken(next_token);
}
let left = parselet.use(tokens);
while ( precedence < get_precedence( tokens.lookahead() ) ) {
let next_token = tokens.lookahead();
let parselet = mixfix_parselets.get(next_token);
left = parselet.use(left, tokens);
}
return left;
}
It's quite flexible, you can do complex operators with multiple sub expression and non-standard precedence rules. Also changing the precedence is as simple as changing 1 number, which is nice.
I find your parser experiments pretty impressive, it's obviously possible to implement such things using the event sheet but the type limitations must be pretty frustrating at times. Getting good error messages can require a fair bit of work, I think one of the most important parts is including an easy to understand location in the message. Some languages actually ascii diagrams of source code with arrows to the problem parts now... which is pretty nuts!