NoneClass operators

Jun 25, 2012 at 8:51 AM


Irony is an immensely useful kit. I've modified the ExpressionEvaluatorGrammar to my needs, and so far it's working pretty good. I've added operators and type converters for bool and DateTime.

However, working with null values is a bit of a pain, and I can't figure out how to handle these properly. Irony needs the NoneValue of the language runtime to determine, if a variable is set to nothing (i.e. defined but has no value).

See the following simple expression: "SomeDate < AnotherDate"

SomeDate is null, so I initialize the "SomeDate" global with NoneValue, since using null would cause the runtime to believe that the variable does not exist.

Evaluation of the above expression will fail, saying that I'm missing an operator between NoneClass and DateTime. In this case, I would like it to simply return false.

How do I accomplish this?

I can implement operators for objects of the same type (e.g. DateTime vs. DateTime), but apparently not for two different types (e.g. DateTime vs. NoneClass). Maybe I'm going about this all wrong.




Jun 25, 2012 at 9:36 PM

I know, NoneClass might be a pain, the reason is that it is a base class for supposedly inherited custom None's (like in Python or Ruby) that would define it's behavior. I think there are two ways to accomplish what you want:

1. The right way, but quite complicated. Enhance operator implementation lookup mechanism. When operator handler fails to find an operator implementation or converters for a pair of types (in dictionary of implmentations), it should try to find custom operator implementation on one of the classes (the one defined in c# class using "operator" keyword). If it finds any, it should use this. Then you can define these implementations on None class (subclass it and add operator implementations) and return false all the time. That's the way it should be done in Irony anyway.

2. Easier path - define converters to NoneClass, the same way Irony defines and uses converter like 'int->double' for expr involving ints and doubles.  Add the NoneClass to 'convertables' sequence there, so Irony will know that it can convert any type to None, and add actual converters there. Then define operators for None class, by adding them to the dictionary of implementations.


Jun 26, 2012 at 3:10 PM
Edited Jun 26, 2012 at 3:12 PM

Hi Roman,

Thank you for the swift reply! As an initial experiment, I've gone with the second approach: adding converters from all types to NoneClass, and adding binary operators for all operations on NoneClass. Still, however, it complains. Example:

In InitTypeConverters:

      targetType = typeof(NoneClass);
      AddConverter(typeof(string), targetType, ConvertAnyToNoneValue);
      AddConverter(typeof(short), targetType, ConvertAnyToNoneValue);
      AddConverter(typeof(int), targetType, ConvertAnyToNoneValue);
      // etc.

In InitBinaryOperatorImplementationsForMatchedTypes:

     // NoneClass operators
     Type none = typeof(NoneClass);
     AddBinary(ExpressionType.NotEqual, none, NoneValueBinaryOperator, BoolResultConverter);
     AddBinary(ExpressionType.Equal, none, NoneValueBinaryOperator, BoolResultConverter);
     AddBinary(ExpressionType.GreaterThan, none, NoneValueBinaryOperator, BoolResultConverter);
     // etc.

ConvertAnyToNoneValue always returns NullValue, and NoneValueBinaryOperator always returns false.

The expression...

null == 1

...still gives me the same error: "Operator 'Equal' is not defined for types Irony.Interpreter.NoneClass and System.Int32. At (1:1)." 

Shouldn't it be able to determine NoneClass as a common type in this case, convert Int32 to NoneClass, and use the operators defined for NoneClass?


Thank you,


Jun 26, 2012 at 4:59 PM

there's one more thing: you should add NoneClass to the _typeSequence list in LanguageRuntime. This list defines which types can be converted to other types - earlier types in the sequence are converted to later types.

One detail: when NonClass is one of the arguments, then other arg should be converted to NoneClass - except if other arg is string. I think the expression ["abc" + None ] should return "abc". Like in c#: [ "abc" + null ] returns "abc". To make it work for string and plus operator, you should put NoneClass right before the string (which is last element) in _typeSequence. You should also provide converter from None to string returning empty string.


Jun 26, 2012 at 7:45 PM

Perfect, thanks again, your help is very much appreciated! It is now working almost as expected. Comparing any value to null returns false, and adding, subtracting etc. results in an empty (NoneValue) result.

I now realize a possible downside of this: the expressions null == null and 1 != null both return false. This mimics the behaviour of ANSI/SQL-92 standard. In c#, however, these two expressions would return true.

I guess the only way to handle this properly is by either somehow allowing for cross-type operators implemented directly in Irony (as opposed to only matched types), or as you suggest, using operator implementations as they are defined on types in .NET.

Lastly, for some reason it won't compare null to strings. null == "" fails. I would expect it to return true, since NoneValue would be converted to an empty string.

Jun 26, 2012 at 8:11 PM

... allowing for cross-type operators implemented directly in Irony (as opposed to only matched types),

Well, actually you can have cross-type operators, you can add them directly for each pair of types. The mechanism you see in default init for regular operators and types is a shortcut, when you don't have to provide handler for every op, every arg1 type and arg2 type combination. Instead, you specify handler for op and matching type, plus converters from type to type, plus conversion sequence in _typeSequence - Irony engine builds all combinations. But you can explicitly add handlers for mismatching types - try it. Use null for both arg converters.