Javalution

Play around with Snobol and Infiqs

Since Java's inception, the language's usefulness has increased through Sun Microsystems' introduction of new language features—ranging from inner classes to generics, annotations, covariant return types, and more. But Sun isn't the only one to extend the Java language: various third-party products have made Java more useful by introducing new language features and translating extended Java source code to Sun-standard Java.

Many programming languages pre-date Java, resulting in an enormous amount of legacy source code. Because migrating all of this code to Java is costly, other third-party products have been developed to compile legacy source code to classfiles and to interpret legacy source code. These products increase Java's usefulness by increasing Java's software base; they also extend the useful life of legacy source code.

Third-party products that extend Java or migrate legacy source code to Java (resulting in software that is part Java and part non-Java) contribute to Java's evolution—or Javalution. In this installment of Java Fun and Games, I introduce two such products: the Infiqs macro expander and the Snobol3 language interpreter.

Along with their practical uses, both products make Java programming more enjoyable and interesting. Use Infiqs to specify big decimal-based numeric expressions with simple operators, instead of writing these expressions as lengthy, hard-to-read, and error-prone sequences of constructor and method calls. Use Snobol3 to merge Snobol3 source code with Java, resulting in interesting part-Java/part-Snobol3 hybrids.

Note
Unlike previous Java Fun and Games installments, which were written from the perspective of J2SE 1.4, this installment requires Java SE 5.0 (Java SE is Sun's new name for J2SE).

Infiqs macro expander

Harold Kaplan's Infiqs—an intentional misspelling of the word infix—is a macro expander that lets programmers use infix operators to perform various arithmetic operations on instances of java.math.BigDecimal. This product is distributed in the infiqs.zip distribution file, requires Java SE 5.0, and is freeware. (See Resources for a link to Kaplan's Website, where you can download infiqs.zip.)

The infiqs.zip distribution file contains the Infiqs application's Infiqs.class and Inf.class classfiles. It also contains this application's Infiqs.java source file, making it possible to modify Infiqs. Several example source files with .infiqs file extensions and an HTML documentation file round out the distribution.

Infiqs can save you time and frustration in situations where you specify complex mathematical expressions that involve BigDecimal objects. Rather than build lengthy sequences of BigDecimal object-creation expressions and method calls (which can become tedious to write and are error-prone), you specify short macros that begin with the dec keyword. Consider the following code fragment:

 BigDecimal x;
dec x = "1" + "2" ;

After specifying BigDecimal variable x, the code fragment employs an Infiqs macro to create two BigDecimal objects containing 1 and 2, to add the second object's 2 to the first object's 1, and to assign the first object's reference to x. (Notice the mandatory space character after dec and before the also mandatory semicolon.) Macro expansion yields the code fragment below:

 BigDecimal x;
x=new BigDecimal("1",mc).add(new BigDecimal("2",mc),mc);

What's with mc? Java SE 5.0 introduced java.math.MathContext to support the concept of precision (number of decimal digits) for big decimals. Infiqs requires that a MathContext object be created and assigned to MathContext variable mc before specifying macros. Behind the scenes, Infiqs passes mc to BigDecimal constructors and method calls.

Cube roots

To demonstrate the usefulness of Infiqs, Kaplan presents an example that obtains a number's cube root via Newton-Raphson—a numerical analysis algorithm that repeatedly evaluates expression xi+1=xi-f(x)/f'(x). The xi+1 is the latest approximation of a root, xi is the previous approximation, f(x) is the function whose root is being sought, and f'(x) is the first derivative (slope) of f(x).

Whereas Kaplan focuses on finding the cube root of 17, I've generalized the example to locate any number's cube root. My example expresses Newton-Raphson expression xi+1=xi-(xi3-n)/3xi2) as Infiqs macro dec x = x - ( x ^ 3 - n ) / ( "3" * x ^ 2 ) ;. Listing 1's source code puts this macro into the context of an application that finds the cube root for arbitrary n.

Listing 1. CubeRoot.infiqs

 

// CubeRoot.infiqs

import java.math.*;

public class CubeRoot { // Maximum number of iterations performed by Newton-Raphson loop.

final static int MAXITER = 15;

public static void main (String [] args) { // Validate number of command line arguments.

if (args.length != 1) { System.err.println ("usage : java CubeRoot n"); System.err.println ("example: java CubeRoot 19"); System.err.println (" Find the cube root of 19"); return; }

// Specify a math context with 40 digits of precision.

MathContext mc = new MathContext (40);

// Specify the number whose cube root is being sought.

BigDecimal n = new BigDecimal (args [0], mc);

// Specify the starting value in the search for the cube root.

BigDecimal x; dec x = "1" ;

// Search for the cube root via the Newton-Raphson loop. Output each // successive iteration's value.

for (int i = 0; i < MAXITER; i++) { dec x = x - ( x ^ 3 - n ) / ( "3" * x ^ 2 ) ; System.out.println (x); } } }

By convention, Infiqs source files are assigned .infiqs file extensions. To convert an Infiqs source file to an equivalent Java source file, run the Infiqs application, identifying the Infiqs source file as the standard input source. You should also specify a filename with a .java file extension as the standard output destination (unless you want to examine macro expansion on the screen):

 java Infiqs <CubeRoot.infiqs >CubeRoot.java

Infiqs reads from the standard input source one line at a time. Lines not beginning with dec are passed unchanged to the standard output destination. If a line begins with dec and is terminated with a semicolon character, Infiqs evaluates the macro and copies equivalent Java source code to the standard output destination. Listing 2 presents CubeRoot's equivalent Java source code.

Listing 2. CubeRoot.java

 

// CubeRoot.infiqs

import java.math.*;

public class CubeRoot { // Maximum number of iterations performed by Newton-Raphson loop.

final static int MAXITER = 15;

public static void main (String [] args) { // Validate number of command line arguments.

if (args.length != 1) { System.err.println ("usage : java CubeRoot n"); System.err.println ("example: java CubeRoot 19"); System.err.println (" Find the cube root of 19"); return; }

// Specify a math context with 40 digits of precision.

MathContext mc = new MathContext (40);

// Specify the number whose cube root is being sought.

BigDecimal n = new BigDecimal (args [0], mc);

// Specify the starting value in the search for the cube root.

BigDecimal x; x=new BigDecimal("1",mc);

// Search for the cube root via the Newton-Raphson loop. Output each // successive iteration's value.

for (int i = 0; i < MAXITER; i++) { x=x.subtract(x.pow(3,mc).subtract(n,mc).divide(new BigDecimal("3",mc). multiply(x.pow(2,mc),mc),mc),mc); System.out.println (x); } } }

After comparing Listings 1 and 2, I think you'll agree with me that the dec x = x - ( x ^ 3 - n ) / ( "3" * x ^ 2 ) ; macro is much easier to understand than the longer x=x.subtract(x.pow(3,mc).subtract(n,mc).divide(new BigDecimal("3",mc).multiply(x.pow(2,mc),mc),mc),mc); expression.

Note
Whenever you introduce a new big decimal into an Infiqs macro, such as 3 in the "3" * x ^ 2 portion of the previous macro, you must surround the big decimal with double quotes, which tells Infiqs to create a new BigDecimal object.

Compile CubeRoot.java and run the resulting application. For example, invoke java CubeRoot 11.5 to discover how Newton-Raphson finds 11.5's cube root. In response, you should notice the following output, which indicates (at least to 40 digits of precision) that 2.257178717737000689722531351332293570729 is the cube root of 11.5:

 4.5
3.189300411522633744855967078189300411523
2.503065206710545285332351274464042799496
2.280542237899026579351609436198669505057
2.257417253127543076236357536600910449407
2.257178742941525167756167000357339173250
2.257178717737000971165927797364245467219
2.257178717737000689722531351332328663380
2.257178717737000689722531351332293570729
2.257178717737000689722531351332293570729
2.257178717737000689722531351332293570729
2.257178717737000689722531351332293570729
2.257178717737000689722531351332293570729
2.257178717737000689722531351332293570729
2.257178717737000689722531351332293570729

Additional examples

The Infiqs distribution file includes several examples that enhance BigDecimal with trigonometric and other capabilities. For instance, SinD.infiqs introduces a static BigDecimal sin(BigDecimal x, MathContext mc) method for calculating a big decimal's sine value. These examples use Infiqs macros to simplify their operations.

Snobol3 language interpreter

From 1962 to 1967, AT&T Bell Laboratories' David J. Farber, Ralph E. Griswold, and Ivan P. Polonsky developed Snobol (String Oriented Symbolic Language), a text-manipulation language with a pattern-matching algorithm that is considered more powerful (in many respects) than regular expressions (strings whose patterns describe sets of strings).

The first version of Snobol did not come with built-in functions. This limitation was addressed by Snobol2. The third version, Snobol3, supported user-defined functions. Snobol4, the last major implementation of this language, came with improved patterns, numeric data types, arrays, structures, and tables. Furthermore, Snobol4 also influenced its Icon language descendent.

In late 2005, Dennis Heimbigner of the University of Colorado's Software Engineering Research Laboratory released a Java implementation of Snobol3. This product is distributed in the s3-1.0.jar distribution file, requires Java SE 5.0, and is released under the BSD license. (See Resources for a link to Heimbigner's Website, where you can download s3-1.0.jar.)

The s3-1.0.jar distribution file contains the Java source code to a Snobol3 language interpreter, examples, documentation, and more. Because this is a source-only distribution, you will need to build the interpreter's classfiles. The easiest (and recommended) way to accomplish this task is to use Apache's Ant build tool. I used version 1.6.5.

Before running Apache Ant, I copied s3-1.0.jar to c:\snobol3 on my Microsoft Windows platform. I then invoked jar xvf s3-1.0.jar to extract all files from this jar file. I changed to the automatically created c:\snobol3\s3-1.0 directory, where I ran Ant against the extracted build.xml file. After a successful build, I discovered s3.jar in s3-1.0.

Greetings from Snobol3

The s3.jar file contains the Snobol3 interpreter's classfiles. Before running this application, you need a Snobol3 program's source code to interpret. Because it's traditional to demonstrate an unfamiliar language with a short program that outputs a Hello, World message, look at Listing 3, which presents the appropriate source code.

Listing 3. hw.sno

 

* hw.sno

* Traditional "Hello, World" program in Snobol3.

SYSPOT = 'Hello, World'

Listing 3 shows hw.sno's contents. (Although file extensions are not required, Snobol source files are usually given the extension .sno.) Lines with asterisks (*) in Column 1 are considered to be comments; the interpreter ignores them. Blank lines are also ignored. The line containing SYSPOT = 'Hello, World' outputs Hello, World to the standard output device.

To interpret Snobol3 source code, specify java -jar s3.jar <options> filename. The <options> is a whitespace-delimited list of command line options (I identify some command line options later). filename identifies the source file to interpret. To interpret hw.sno, for example, invoke java -jar s3.jar hw.sno. You'll see the following output:

 Hello, World

Language tour

Unlike many other programming languages (including Snobol4), Snobol3 has only a single data type—the string. Because the Snobol3 language interpreter is written in Java, strings may contain any Unicode character, although the -escapes command line option must be specified to include Unicode-based escape sequences (like '\u00ff'), in addition to standard escape sequences (such as '\n').

Strings may appear literally in source code by placing them between single-quote characters. Double-quote characters may be used as well, but the -dquotes command line option must be specified. Keep in mind that a literal string's beginning and ending quotes must be the same (either single or double). If no characters appear between the quotes, the string is considered to be empty.

1 2 3 Page 1
Page 1 of 3