PBPP: A Properties-Based Preprocessor for Java

John D. Ramsdell
The MITRE Corporation
December 2003

PBPP is a properties-based preprocessor for Java source files. The preprocessor conditionally removes sections of Java source files. It is patterned after the C preprocessor, except it provides no macro replacement functionality or file inclusion. The preprocessor uses a properties file as an environment for mapping variables to strings. Preprocessor conditionals evaluate expressions that contain equality tests between strings and variables to determine what text is elided.

Text to be elided is specified by PBPP directives. Each directive begins with the characters /*# and ends with */, so a directive is treated as a comment by a Java compiler. An example of Java code annotated with PBPP directives follows.

$ cat Yes.java
public class Yes {
    public static void main(String[] args) {
        System.out.print("Yes"); /*#if polite == "yes" */
        System.out.println(", please"); /*#endif*/
        System.out.println(".");
    }
}

One creates a polite version of the program as follows.

$ cat yes.properties 
polite = yes
$ java -jar pbpp.jar yes.properties Yes.java
public class Yes {
    public static void main(String[] args) {
        System.out.print("Yes"); 
        System.out.println(", please"); 
        System.out.println(".");
    }
}

One creates an impolite version of the program as follows.

$ java -jar pbpp.jar /dev/null Yes.java
public class Yes {
    public static void main(String[] args) {
        System.out.print("Yes"); 
        System.out.println(".");
    }
}

Conditionals

The conditional omission of sections of text is specified using directives as follows.

preprocessor-conditional:
if-directive text elif-partsopt else-partopt endif-directive
if-directive:
/*#if expression */
elif-parts:
elif-directive text elif-partsopt
elif-directive:
/*#elif expression */
else-part:
else-directive text
else-directive:
/*#else*/
endif-directive:
/*#endif*/

The expressions in #if and subsequent #elif directives are evaluated in order until one evaluates to true. The text following a directive with a false value is elided, and the text following the successful directive is retained. Once a successful #if or #elif directive has been found, succeeding #elif and #else directives, together with their text are elided. If all expressions evaluate to false, and there is an #else directive, the text following the #else directive is retained. Text controlled by inactive arms of the conditional is ignored except for checking the nesting of conditionals.

Expressions

Conditionals specify boolean valued expressions.

expression:
value == value
value != value
defined identifier
!expression
expression & expression
expression ^ expression
expression | expression
(expression)
value:
identifier
constant

String comparisons form the basis of expressions. The argument to an equality test may be either a constant string or an identifier. A constant string is delimited by double quotes. Its contents follow the rules for quoting characters in properties files, not Java source files. In particular, the character sequence \b does not represent a backspace character.

The value bound to an identifier is determined by the properties object given as input to the preprocessor. If the object has a property for the identifier, that property is used as the value of the identifier. The properties object is called the environment.

Two values are equal if they denote strings that contain the same sequence of characters. Additionally, two identifiers are considered equal if the environment has no value for both identifiers.

The expression defined identifier is true when the environment has a value for the given identifier. The ! operator negates the boolean value of its argument. The expression value1 != value2 is equivalent to the expression !(value1 == value2). The binary operators are logical and &, exclusive or ^, and inclusive or |. The binary operators have different precedence, with & having the highest precedence and | the lowest precedence.

Definitions

Definitions modify the environment.

definitions:
/*#define identifier value
/*#undef identifier

When a #define directive is processed, the environment is modified so that any previous value for the given identifier is replaced by the value given by the directive. When an #undef directive is processed, any previous value for the given identifier is removed from the environment. A definition is ignored if it is in an inactive arm of a conditional.

License

Copyright (C) 2003 The MITRE Corporation

This package is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA