Jackpot Rule Language

Jackpot has a mini-language for defining pattern matching and replacement its Java source model.  This language allows developers to create many types of  Jackpot operators and transformers without learning its API.  It is not intended to be a complete language or replacement for the underlying API, however, but is focused on simplifying common cases.  If you have ideas on improving the rule mini-language or Jackpot in general, please join the project and discuss them with us.

Rule Basics

A rule has two required parts: a pattern and a replacement action.  These are separated by the => (pronounced "such that") token, as shown:
<pattern> => <replacement> ;
A pattern is a Java expression or statement that Jackpot should match.  The replacement action is normally (but not always) a similar pattern to replace the found pattern.  This is perhaps the simplest rule (but not very useful):
false => true;  // replace all false constants with true
Here are slightly more useful rules from Jackpot's Cleanup command; since "this" in Java can never be equal to null, such tests can be replaced by the equivalent boolean constant:
this == null => false;
this != null => true;
and this is a deprecated variable cleanup rule:
java.awt.Frame.CROSSHAIR_CURSOR=>java.awt.Cursor.CROSSHAIR_CURSOR;

Meta-Variables

Most rules are more complex, however, because they need to be applied to many different nodes with shared characteristics.  These rules use meta-variables to provide wildcard-like matching to the source model's nodes.  Meta-variables are regular Java identifiers preceeded by a '$' character, such as $a or $anotherMetaVariable (even $1 and $_ are valid, but may be hard to read if you don't use Perl or AWK much).  Here are some rules that use meta-variables:
$a && false => false;       // unnecessary test
!($a <= $b) => $a > $b; // overly complex boolean expression
do $a; while(false) => $a; // eliminate unnecessary do-while loop
Since Jackpot works on source tree nodes instead of source text, rules can be applied to parts of an expression.  This allows them to combine to automatically make more powerful rules.  For example, if we apply the above rules to:
do {
someMethod();
} while (isAnotherMethod() && this == null);
It will be turned into:
do {
someMethod();
} while (isAnotherMethod() && false); // this == null => false;
...
do {
someMethod();
} while (false); // $a && false => false;
...
{
someMethod(); // do $a; while(false) => $a;
}
while Jackpot then "de-blocks" to:
someMethod();

Guard Expressions

One thing not obvious from the above section is how Jackpot matches some nodes but not others.  For example, the "$a && false => false;" rule should only match boolean expressions, but $a is not typed to be boolean.  The answer is that the type of $a is inferred by the expression; in other words, since only boolean nodes are valid in that expression, those are the only nodes which are tested. 

The issue arises with expressions which can accept multiple types, but you only want to match specific ones.  For example, suppose you want to replace use of the deprecated java.awt.Component.enable() method with java.awt.Component.setEnabled(true):
java.awt.Component.enable() => java.awt.Component.setEnabled(true);
This doesn't work since Component is an abstract class; we want it to fix all Component subclass references.  So we use a meta-variable (remember, there is nothing special about meta-variable names.  I picked $C only because 'C' is the first character of Component):
$C.enable() => $C.setEnabled(true);
The problem now is that my project has non-Component classes which have an enable() method, since it is a common name.  What we want is a rule that matches only those expressions where enable() is invoked on a Component instance. 

Rules optionally constrain possible matches using a guard expression, which is a boolean expression that accepts or rejects possible matches.  A guard expression is separated from the rule body by the :: (pronounced "where") token.  Here is our complete rule:
$C.enable() => $C.setEnabled(true) :: $C instanceof java.awt.Component;

Meta-Lists

Some rules need to match several statements at a time; one example are rules to translate references of one variable-arguments method (new to Java 5) to another such method.  Meta-lists are similar to meta-variables but have both a beginning and ending '$', such as "$list$" or "$args$", and match on zero or more statements.  Here is an example which finds blocks which define a local variable to hold a method's result value only to immediately return it, and so it eliminates the temporary variable:
{ $stmts$; $type $value = $expr; return $value; } => { $stmts$; return $expr; }

mapclass Rules

One important use of Jackpot is for migrating API usage; in other words, converting source code that references one API to another.  The "enable() => setEnabled(true)" example above demonstrates how to move use of one method to another, but sometimes whole classes get renamed or moved.  To avoid having to write a rule for each field in such a class, a mapclass statement is used instead.  Its syntax is simple:
mapclass <fully_qualified_old_class> => <fully_qualified_new_class>;
Note:  since the Jackpot engine compares symbols when mapping classes, both the old and new classes have to be in the project source or classpath so they can be resolved.  If either class cannot be resolved, then this rule will not find any matches.

Built-In Replacement Actions

note(String note)
Display a note regarding the match in the Results pane.
comment(String comment)
Add a comment to the matched node's source code.
transformationFailure(String note)
Report that the rule file failed, with the note displaying the Results pane.

Built-In Guard Expressions

Several boolean expressions are built into the rule language to support writing guards.  Each of these takes a node as a parameter, which is normally a meta-variable.  Note: no flow analysis is done during execution of a rule file, so guard expressions such as sideEffectFree or isTrue only return true if the immediate node has such a property.

couldThrow(node)
True if a method invocation can throw a declared exception.
hasComment(node)
True if the node has a comment associated with it.
isConstant(node)
True if node is a static final variable.
isLiteral(node)
True if node is a primitive type or string instance. Note: all nodes that return true from isLiteral() will also be true for isConstant(), but not necessarily the reverse.
isEmpty(node)
True if node is either an empty statement or block.
sideEffectFree(node)
True if the expression does not potentially modify variables.
referenced(node)
True if the variable node is referenced somewhere or false if it is not.
hasVariableDeclarations(node)
True if the node is either a variable declaration, or a block containing variable declarations.
isStatement(node)
True if the node is a statement, as defined by the Java Language Specification.
isTrue(node)
True if the expression is always true (includes the true Java constant).
isFalse(node)
True if the expression is always false (includes the false Java constant).
isNull(node)
True if this is a node for the null Java constant.
assignedIn(node, list)
True if the node variable is assigned to in the statement list.
declaredIn(node, list)
True if the node variable is declared in the statement list.
referencedIn(node, list)
True if the node variable is referenced in the statement list.

Miscellaneous Built-In Expressions

parent(node)
Returns the parent of a fully-qualified identifier, such as "foo.bar" from the identifier "foo.bar.Mumble".