Non-nullable references, again

Benji Smith dlanguage at benjismith.net
Fri Jan 2 07:37:50 PST 2009


Don wrote:
> Benji Smith wrote:
>> Daniel Keep wrote:
>>> Benji Smith wrote:
>>>> Don wrote:
>>>>> Denis Koroskin wrote:
>>>>>> Foo nonNull = new Foo();
>>>>>> Foo? possiblyNull = null;
>>>>  >
>>>>> Wouldn't this cause ambiguity with the "?:" operator?
>>>>
>>>> At first, thought you might be right, and that there would some 
>>>> ambiguity calling constructors of nullable classes (especially given 
>>>> optional parentheses).
>>>>
>>>> But for the life of me, I couldn't come up with a truly ambiguous 
>>>> example, that couldn't be resolved with an extra token or two of 
>>>> lookahead.
>>>>
>>>> The '?' nullable-type operator is only used  in type declarations, 
>>>> not in expressions, and the '?:' operator always consumes a few 
>>>> trailing expressions.
>>>>
>>>> Also (at least in C#) the null-coalesce operator (which converts 
>>>> nullable objects to either a non-null instance or a default value) 
>>>> looks like this:
>>>>
>>>>   MyClass? myNullableObj = getNullableFromSomewhere();
>>>>   MyClass myNonNullObj = myNullableObj ?? DEFAULT_VALUE;
>>>>
>>>> Since the double-hook is a single token, it's also unambiguous to 
>>>> parse.
>>>>
>>>> --benji
>>>
>>> Disclaimer: I'm not an expert on compilers.  Plus, I just got up.  :P
>>>
>>> The key is that the parser has to know what "MyClass" means before it 
>>> can figure out what the "?" is for; that's why it's 
>>> context-dependant. D avoids this dependency between compilation 
>>> stages, because it complicates the compiler.  When the parser sees 
>>> "MyClass", it *doesn't know* that it's a type, so it can't 
>>> distinguish between a nullable type and an invalid ?: expression.
>>>
>>> At least, I think that's how it works; someone feel free to correct 
>>> me if it's not.  :P
>>>
>>>   -- Daniel
>>
>> I could be wrong too. I've done a fair bit of this stuff, but I'm no 
>> expert either :)
>>
>> Nevertheless, I still don't think there's any ambiguity, as long as 
>> the parser can perform syntactic lookahead predicates. The grammar 
>> would look something like this:
>>
>> DECLARATION :=
>>   IDENTIFIER         // Type name
>>   ( HOOK )?          // Is nullable?
>>   IDENTIFIER         // Var name
>>   (
>>     SEMICOLON        // End of declaration
>>     |
>>     (
>>       OP_ASSIGN      // Assignment operator
>>       EXPRESSION     // Assigned value
>>     )
>>   )
>>
>> Whereas the ternary expression grammar would look something like this:
>>
>> TERNARY_EXPRESSION :=
>>   IDENTIFIER         // Type name
>>   HOOK               // Start of '?:' operator
>>   EXPRESSION         // Value if true
>>   COLON              // End of '?:' operator
>>   EXPRESSION         // Value if false
>>
>> The only potential ambiguity arises because the "value if true" 
>> expression could also just be an identifier. But if the parser can 
>> construct syntactic predicates to perform LL(k) lookahead with 
>> arbitrary k, then it can just keep consuming tokens until it finds 
>> either a SEMICOLON, an OP_ASSIGN, or a COLON (potentially, 
>> recursively, if it encounters another identifier and hook within the 
>> expression).
>>
>> Still, though, once it finds one of those tokens, the syntax has been 
>> successfully disambiguated, without resorting to a semantic predicate.
>>
>> It requires arbitrary lookahead, but it can be done within a 
>> context-free grammar, and all within the syntax-processing portion of 
>> the parser.
>>
>> Of course, I could be completely wrong too :)
>>
>> --benji
> 
> case a?.b:c:
>   break;
> 
> is this
> 
>   case ((a?).b):
> c:
>   break;
> 
> or is it
> 
> case (a ? b : c ) :
> break;
> 

Damn. I got so distracted with the ternary operator, I forgot about case 
statements.

--benji



More information about the Digitalmars-d mailing list