Everyone complaining about how C++ is so hard to parse is missing something so bloody simple it makes you feel stupid for not seeing it when you finally do.
The "if it can be parsed as a declaration, it is a declaration" rule is probably a direct consequence of the early C++ parsers simply trying to parse a declaration first, then an expression-statement if the declaration doesn't work.
Maybe years of academia has turned language researcher's brains into indoctrinated mush, as in fact C++ parsing is very straightforward with a variant of recursive-descent.
Here's a classic "C++ parsing is hard!" example:
int(x), y, *const z; // declaration
int(x), y, new int; // expression-statement
The academics claim you need "infinite" lookahead to parse those correctly, because there can be arbitrarily many tokens before it knows which path to take. Bullshit. All you need to do is "fork" the current thread whenever there are multiple matches and feed the incoming tokens to all the active "threads", and when one can't proceed any further, it terminates. If there are no more threads, that's an error. When you reach the ';', take the AST formed by the first (which if you arranged things correctly, will be the one that went down the declaration path) and/or only thread, and you're done. You don't even need to build the AST for both paths if you're really optimising for space - just record which path is the right one and go back to build the AST.
If you're somewhat knowledgeable of (but not indoctrinated by) the theory, that strategy should seem very familiar:
https://swtch.com/~rsc/regexp/regexp1.htmlIt's just like Thompson's NFA algorithm, except each state transition is the RD parser "swallowing" a token, and that can be done in constant time, so the parse is still linear in the length of the input.