Boolean Expressions

Nov 10, 2007 at 12:15 AM
Hi Eugene,

Sorry in advance for the beginner question but I am trying to use your code to evaluate a boolean expression and I am not sure how to do it. I have successfully run your examples here, but they basically give a numeric answer, but I would like to evaluate something like...

string s = ((x==3)||(x==4))&&(y==0)) ? “Print this if True” : ”Print this if false”;

or even just to get

bool b = ((x==3)||(x==4))&&(y==0));

I am pretty sure that this is possible, but I just can't find any examples to do it.

Thanks in advance,

-a
Nov 10, 2007 at 9:03 AM
Edited Nov 10, 2007 at 9:12 AM
Hi Eugene,

I think I answered my own question already, I am doing it like this...


ExpressionContext context = new ExpressionContext();
context.Variables.DefineVariable("a", typeof(int));
context.Variables.DefineVariable("B", typeof(bool));
IDynamicExpression [] array = new IDynamicExpression[2];
array[0] = ExpressionFactory.CreateDynamic("b and (a < 100)AND(A>0)", context);
array[1] = ExpressionFactory.CreateDynamic("b or (a < 100) or (A>0)", context);
context.Variables.SetVariableValue("A", 10);
context.Variables.SetVariableValue("B", true);
object o = array[0].Evaluate();
object p = array[1].Evaluate();
bool result = (bool)o;
bool result2 = (bool)p;
Console.WriteLine("FleeTest result is {0}. {1}.", result, result2);


Questions:

1. is there another way that I should be doing the booleans or is this ok?
2. is using an array ok for the expressions and contexts? I basically want to load up some expressions and vary the context then then evaluate.
3. is the limit for the expression length basically the C# max length of the string?

Thank you.

-a
Coordinator
Nov 10, 2007 at 2:52 PM
The AND/OR operators perform double duty. If both their operands are boolean, they do a logical operation. If both operands are integers, they perform a bitwise operation. Since there is no conversion between integers and booleans this works out.

> ((x==3)||(x==4))&&(y==0)) ? “Print this if True” : ”Print this if false”;
The Flee equivalent would be: If((x=3 OR x=4) AND y=0, "True!", "False!")
The comparison operators (=, >, <) are evaluated before the AND/OR operators so you don't have to wrap them in brackets.

>is there another way that I should be doing the booleans or is this ok?
Well, since you know that your expressions will always evaluate to a Boolean, you can use a generic expression: ExpressionFactory.CreateGeneric<bool>("a > 1", context). This will make the expression evaluate to a boolean (no casting required) and Flee won't compile your expression unless it evaluates to a Boolean (catches type errors).

>is using an array ok for the expressions and contexts? I basically want to load up some expressions and vary the context then then evaluate.
Have a look at using an ExpressionOwner. It lets you use the members of a class as the variables of an expression and lets you swap in other instances of a class after an expression is compiled thus letting you change the context of an expression.

>is the limit for the expression length basically the C# max length of the string?
I'm sure there are other limitations (memory, IL size), but I don't set any limits on expression size.

I will also put in an example for evaluating boolean expressions since it seems it's a popular use for Flee.
Nov 11, 2007 at 6:12 AM
Hi Eugene,

thank you for your suggestion, it seems like this will work.

-a
Nov 29, 2007 at 3:40 AM
Hi again Eugene,

I am getting an issue when the expression is long that says...

Unhandled Exception: System.NotSupportedException: Illegal one-byte branch at position: 473. Requested branch was: 144.
at System.Reflection.Emit.ILGenerator.BakeByteArray()
at System.Reflection.Emit.DynamicResolver..ctor(DynamicILGenerator ilGenerator)
at System.Reflection.Emit.DynamicILGenerator.GetCallableMethod(Void* module)
at System.Reflection.Emit.DynamicMethod.GetMethodDescriptor()
at System.Reflection.Emit.DynamicMethod.CreateDelegate(Type delegateType)
at Ciloci.Flee.Expression`1.Compile(String expression, Object owner, ExpressionOptions options) in D:\Visual Studio 2005\Flee\Flee-0.9.13.0\src\Lib\Expression.vb:line 197
at Ciloci.Flee.Expression`1..ctor(String expression, Object owner, ExpressionContext context) in D:\Visual Studio 2005\Flee\Flee-0.9.13.0\src\Lib\Expression.vb:line 122
at Ciloci.Flee.ExpressionFactory.CreateGenericT(String expression, Object owner, ExpressionContext context) in D:\Visual Studio 2005\Flee\Flee-0.9.13.0\src\Lib\Expression.vb:line 75
at Ciloci.Flee.ExpressionFactory.CreateGenericT(String expression, ExpressionContext context) in D:\Visual Studio 2005\Flee\Flee-0.9.13.0\src\Lib\Expression.vb:line 82
at B1Rules.B1RuleSetItem.compile(ExpressionContext context) in D:\Visual Studio 2005\Projects\B1RulesTest\B1RulesTest\B1RulesTest\B1RuleSetItem.cs:line 31
at B1Rules.B1RuleSet.compile() in D:\Visual Studio 2005\Projects\B1RulesTest\B1RulesTest\B1RulesTest\B1RuleSet.cs:line 314
at B1Rules.B1RuleSet.compile(ExpressionContext B1RecordContext) in D:\VisualStudio 2005\Projects\B1RulesTest\B1RulesTest\B1RulesTest\B1RuleSet.cs:line 328
at B1Rules.B1Evaluator.processB1string(String B1String) in D:\Visual Studio 2005\Projects\B1RulesTest\B1RulesTest\B1RulesTest\B1Evaluator.cs:line 87
at B1RulesTest.Program.Main(String[] args) in D:\Visual Studio 2005\Projects\B1RulesTest\B1RulesTest\B1RulesTest\Program.cs:line 56


Line 31 of B1RuleSetItem.cs is the following...

public void compile(ExpressionContext context) { iexpression = ExpressionFactory.CreateGeneric<bool>(condition, context); }


The expression that is having an issue is the following:

((M0100_ASSMT_REASON = "01" OR M0100_ASSMT_REASON = "03") AND (M0220_PRIOR_UR_INCON_bool OR M0220_PRIOR_CATH_bool OR M0220_PRIOR_INTRACT_PAIN_bool OR M0220_PRIOR_IMPR_DECSN_bool OR M0220_PRIOR_DISRUPTIVE_bool OR M0220_PRIOR_MEM_LOSS_bool OR M0220_PRIOR_NONE_bool OR M0220_PRIOR_NOCHG_14D_bool OR M0220_PRIOR_UNKNOWN_bool) AND ((M0220_PRIOR_UR_INCON = "0" OR M0220_PRIOR_UR_INCON = "0") AND (M0220_PRIOR_CATH = "0" OR M0220_PRIOR_CATH = "0") AND (M0220_PRIOR_INTRACT_PAIN = "0" OR M0220_PRIOR_INTRACT_PAIN = "0") AND (M0220_PRIOR_IMPR_DECSN = "0" OR M0220_PRIOR_IMPR_DECSN = "0") AND (M0220_PRIOR_DISRUPTIVE = "0" OR M0220_PRIOR_DISRUPTIVE = "0") AND (M0220_PRIOR_MEM_LOSS = "0" OR M0220_PRIOR_MEM_LOSS = "0") AND (M0220_PRIOR_NONE = "0" OR M0220_PRIOR_NONE = "0") AND (M0220_PRIOR_NOCHG_14D = "0" OR M0220_PRIOR_NOCHG_14D = "0") AND (M0220_PRIOR_UNKNOWN = "0" OR M0220_PRIOR_UNKNOWN = "0")))

All variables are defined.

Contrast this with the following which is working...

((M0100_ASSMT_REASON = "01") OR (M0100_ASSMT_REASON = "03") OR (M0100_ASSMT_REASON = "04") OR (M0100_ASSMT_REASON = "05") OR (M0100_ASSMT_REASON = "06") OR (M0100_ASSMT_REASON = "07") OR (M0100_ASSMT_REASON = "08") OR (M0100_ASSMT_REASON = "09"))

QUESTION:

Do you think there is an inherent limit in how long the expression can be? Or is there another issue that I can't see...?


Thank you.

-a
Coordinator
Nov 30, 2007 at 1:50 AM
Judging from the exception, it looks like a bug in the code that emits logical expressions. I thought I was handling long logical expressions, but apparently I must have missed a case somewhere.

I'll have a look and put in a fix over the weekend.
Dec 1, 2007 at 9:00 AM
Hi Eugene,

that would be great!

Thank you.

-a
Coordinator
Dec 4, 2007 at 2:20 AM
Looks like I'm running behind schedule. I'll put in the fix tomorrow.
Dec 4, 2007 at 7:13 PM
No problem,

I appreciate the heads up.

-a
Coordinator
Dec 5, 2007 at 1:10 AM
This issue should be fixed in Flee-0.9.14.0. I put in your expression as a unit test and passes and generates correct IL. You should still check that it evaluates to the correct result as I can't test that since it is such a complex expression.
Jul 7, 2008 at 3:28 PM
Edited Jul 7, 2008 at 3:34 PM
I'm having a similar [well, actually, the same] problem using 0.9.18.0.  Deeply-nested IFs seems to be a problem, although even a depth of three is tanking.

Here's one example (formatted for clarity):

IF(
    MovementType = \"C\",
    RailFee * XctnQty ,
    IF(
        MovementType = \"P\",
        PipeFee * XctnQty ,
        IF(
            MovementType = \"R\",
            TruckFee * XctnQty ,
            IF(
                MovementType = \"V\",
                VesselFee * XctnQty ,
                InFacilityFee * XctnQty
            )
        )
    )
)

Here's the stack trace:
at System.Reflection.Emit.ILGenerator.BakeByteArray()
at System.Reflection.Emit.DynamicResolver..ctor(DynamicILGenerator ilGenerator)
at System.Reflection.Emit.DynamicILGenerator.GetCallableMethod(Void* module)
at System.Reflection.Emit.DynamicMethod.GetMethodDescriptor()
at System.Reflection.Emit.DynamicMethod.CreateDelegate(Type delegateType)
at Ciloci.Flee.Expression`1.Compile(String expression, Object owner, ExpressionOptions options)
at Ciloci.Flee.Expression`1..ctor(String expression, ExpressionContext context)
at Ciloci.Flee.ExpressionFactory.CreateDynamic(String expression, ExpressionContext context)
at [my code]

Any thoughts?