If

An If expression performs a test, and if the test is true it executes an expression, and if the test is false, it executes a different expression. In some functional languages, an if expression is required to have both a true and false expression. JFuncMachine allows for having only a true expression if the true expression has type SimpleTypes.UNIT.

Like a typical functional language, If is an expression, not a statement. That is, If returns the value of either its true expression or its false expression. Java does have an equivalent to this, which is the ternary operator. For example:

x = a > b ? 5 : 0

The above ternary expression is the equivalent of this in JFuncMachine:

new SetValue("x", new If(new BinaryComparison(Tests.GT, new GetValue("a", SimpleTypes.INT),
                                                        new GetValue("b", SimpleTypes.INT)),
                         new IntConstant(5), new IntConstant(0)));

Tests

The test part of the If expression determines which path to take. There are several types of comparisons.

Binary Comparisons

A BinaryComparison compares two items, which means it takes a “left” and “right” expression. For numeric and string types, you can use the following tests:

  • Tests.EQ
  • Tests.NE
  • Tests.LT
  • Tests.LE
  • Tests.GT
  • Tests.GE

Java doesn’t have instructions for comparing strings, so JFuncMachine uses the String.compare method to compare strings when you uses any of these tests. Since you may want to compare strings while ignoring case, the following tests are also available for strings:

  • Tests.EQ_IgnoreCase
  • Tests.NE_IgnoreCase
  • Tests.LT_IgnoreCase
  • Tests.LE_IgnoreCase
  • Tests.GT_IgnoreCase
  • Tests.GE_IgnoreCase

For example, the following expression tests whether local variable x is less then local variable y:

new BinaryComparison(Tests.LT, new GetValue("x", SimpleTypes.INT), new GetValue("y", SimpleTypes.INT))

Unary Comparisons

A UnaryComparison performs a test against a single expression. The following tests are available for unary comparisons:

  • Tests.IsNull
  • Tests.IsNotNull
  • Tests.IsTrue
  • Tests.IsFalse

For example, the following UnaryComparison tests whether local variable obj is non-null:

new UnaryComparison(Tests.IsNonNull, new GetValue("obj", new ObjectType()))

The Tests.IsTrue and Tests.IsFalse technically just check whether or not an int value (which includes boolean, byte, char, int, and short) is zero (false) or non-zero (true).

Java has a Comparable interface whose compareTo method returns either a negative integer for less-than, zero for equal, and positive for greater. You can use a UnaryComparison to test the result of a compareTo using the same numeric tests as in the BinaryComparison without having to explicitly compare against 0:

  • Tests.EQ
  • Tests.NE
  • Tests.LT
  • Tests.LE
  • Tests.GT
  • Tests.GE

Although it is much easier (and generates the same code) to use a BinaryComparison to compare whether local string variable x is less than local string variable y, here is how it can be done with UnaryComparison:

new UnaryComparison(Tests.LT,
  new CallJavaMethod(String.class.getName(), "compareTo",
    new Type[] { SimpleTypes.STRING }, SimpleTypes.INT,
    new GetValue("x", SimpleTypes.STRING),
    new Expression[] { new GetValue("y", SimpleTypes.STRING)}))

Instanceof Comparisons

The InstanceofComparison is sort of a binary comparison in that it takes two arguments, but at runtime it only has to determine the value of one expression. The other argument is given in the constructor for InstanceofComparison. Use Tests.EQ to test whether the item is an instance of a particular class, and Tests.NE to test whether the item is not an instance of a particular class.

For example, here is how to test whether local variable obj is a string:

new InstanceofComparison(Tests.EQ, new GetValue("obj", new ObjectType()),
  String.class.getName())

Boolean Expressions

There are three expressions that operate on one or two boolean expressions.

Not

The Not expression negates a boolean expression. For example, the following two expressions are equivalent:

new BinaryComparison(Tests.NE, new GetValue("x", SimpleTypes.INT), new IntConstant(5))
new Not(new BinaryComparison(Tests.EQ, new GetValue("x", SimpleTypes.INT), new IntConstant(5)))

While the first expression is simpler, it turns out they will generate exactly the same code because JFuncMachine eliminates not expressions by inverting the boolean expression it contains. That is, in the second expression, it rewrites the expression to be exactly the same as the first expression

And

The And expression is true if both of its arguments are true. For example:

new And(new UnaryComparison(Tests.IsNotNull, new GetValue("obj", new ObjectType())),
        new InstanceOfComparison(Tests.EQ, new GetValue("obj", new ObjectType()), "java.io.File"))

Or

The Or expression is true if either of its arguments is true. For example:

new Or(new BinaryComparison(Tests.GT, new IntConstant(4), new IntConstant(5)),
       new BinaryComparison(Tests.NE, new IntConstant(3), new IntConstant(7)))

Example If Expression

Here is an If expression from a function that is computing a factorial:

new If(new BinaryComparison(Tests.LE, new GetValue("n", SimpleTypes.LONG),
        new LongConstant(1)),
        new GetValue("acc", SimpleTypes.LONG),
        new CallMethod("fact", new Type[] { SimpleTypes.LONG, SimpleTypes.LONG},
                SimpleTypes.LONG,
                new GetValue("this", new ObjectType()),
                new Expression[] {
                    new InlineCall(Inlines.LongSub, new Expression[] {
                            new GetValue("n", SimpleTypes.LONG),
                            new LongConstant(1)}),
                    new InlineCall(Inlines.LongMul, new Expression[] {
                            new GetValue("acc", SimpleTypes.LONG),
                            new GetValue("n", SimpleTypes.LONG)
                    })

                })
        ));

The equivalent Java code for the above expression is roughly:

if (n < 1) {
    return acc;
} else {
    this.fact(n-1, n * acc);
}