Well, the machinery is there to do this. You could have your SIZE_SPECIFIER token as a separate token and handle the numerical literal as a grammatical production, like so:
<SIZE_SPECIFIER : (["l","L","u","U"])+> // NB. I'm pretty sure this has to be a one-or-more, i.e. a +
// because you can't have a token specification that matches empty input.
And then you have a grammar production like:
NumericalLiteral : <DIGITS> [<SIZE_SPECIFIER>] ;
BUT!!!! There is a problem here. You only want the LL to be matched as a SIZE_SPECIFIER in this NumericalLiteral production. Outside of that, you want this to be an IDENTIFIER, right?
Here is the solution that is in place. At the top of your grammar, you can write:
So, that token type is de-activated by default, so something like LL Is matched as a identifier. But you can write the above grammar rule as:
Oh, and by the way, I assume you know that SIZE_SPECIFIER has to be specified before IDENTIFIER because otherwise the LL will always be matched as an identifier.
I believe this is what you want. I can't resist pointing out that the ability to handle this sort of thing does not really exist in legacy JavaCC. So, when you look at JavaCC grammars in the wild, they do some very ugly, inelegant things to handle this sort of thing. Actually, that whole pesky problem of how to handle the double closing angle brackets (not having it be matched as the right-shift operator) is dealt with this way now. But actually, it's the other way round. It deactivates the RIGHT_SHIFT token type in the appropriate place. See:
Cute, eh? Came in handy recently here as well: https://github.com/javacc21/javacc21/blob/master/examples/csharp/CSharp.javacc#L187
By the way, if you (or anybody lurking....) need a C# grammar, that one is pretty complete, right up to v. 10.0 of the language.