You know that in C++ a template instatiation like:
Foo<Bar<Baz>>
is problematic to parse. Nevertheless, Java seems to repeat history and chosed the same simbols for it’s generics, but nobody seems to complain about it. Do you happen to know why? I sat for a moment to think about it, consulted the Java Language Specification, and it turns out a Type
is defined as follows:
Type:
Identifier [TypeArguments]{ . Identifier [TypeArguments]} {[]}
BasicType
TypeArguments:
< TypeArgument {, TypeArgument} >
TypeArgument:
Type
? [( extends |super ) Type]
Note that as a TypeArgument
, only another Type
is allowed. This gives us the clue about why this problem is much easier to solve in C++: arguments to generic types are always types, never expressions.
So if a C++ parser already read
Foo<Bar<Baz>>
it may be that Baz
resolves to an integer, that Bar
is a template that receives an integer as an argument, and so the parser doesn’t know if Baz
is the argument of Bar
, or if Baz >> somethingElse
(which the parser didn’t already read, that is, Baz
right shift something else) is it’s argument.
In Java, if the parser is in the same situation, by simply using a stack of previously encountered generic types, this is very easy to solve. Debugging JDT’s code I found these lines:
// when consuming a rule...
case 538:
if (DEBUG) {
System.out.println(
"ReferenceType2 ::= ReferenceType RIGHT_SHIFT");
}
consumeReferenceType2();
break;
case 544:
if (DEBUG) {
System.out.println(
"ReferenceType3 ::= ReferenceType UNSIGNED_RIGHT_SHIFT");
}
consumeReferenceType3();
break;
As you can see, the case where a type is encountered followed by a right shift is built in the parser, and the consumeReferenceTypeN()
methods simply check their generic types stack to see if everything is ok.