ToC DocOverview CGDoc RelNotes FAQ Index PermutedIndex
Allegro CL version 11.0

JiL - Java in Lisp


1.0 Introduction

Java in Lisp (jil) is a language for writing programs to run on the Java Virtual Machine (jvm). It uses a syntax familiar to Lisp programmers. jil programs can be generated by Lisp programs. This allows a Lisp program to migrate functionality over to a jvm.

Loading the jil module

When you use jil, first load the jil module into Lisp by evaluating:

(require :jil)

2.0 Lexical items

Java is a case sensitive language and thus jil is as well. Jil can only be loaded and run in an Allegro Common Lisp in modern mode (also known as :case-sensitive-lower mode). See case.html.

All jil source is read into the package javatools.jil.source (with a nickname jvs). This page uses only one package: the common-lisp-user package.

When a jil program is read, the normal Lisp reader does the work, thus everything must be valid Lisp syntax. This also means that normal Lisp reader macros are in effect (e.g. #x3ff4 or #.(+ 234 4) will work). Also the comment characters are semicolon and #| followed by |#.

At present jil is strictly an ASCII system -- it does not permit Unicode characters outside the 8-bit Ascii range to be expressed.

Identifiers in Java are more restricted than they are in Lisp. A Java identifier begins with a letter and contains letters or numbers. The underscore and dollar sign characters are considered letters. The jil programmer would be advised to follow that restriction on naming identifiers, although the only restriction jil places on identifiers is that they cannot contain a period.


3.0 Types

In jil all values have a type. Types are denoted by the following expressions (note again that symbols mentioned are in the jvs package or inherited from the common-lisp package).

Primitive Types

Reference types

Array types


4.0 Files

A file containing jil code should contain zero or more import forms, an optional package form, a def-java-class form followed by a sequence of zero or more def-java-method forms. def-java-macro forms can be placed anywhere in the file.

The name of the file should be foo.jil where 'foo' is the name of the class defined in the file.


5.0 def-java-class

In jil, as in java, everything is class-centric. You define a class and then methods that are associated with that class. This is also how you define interfaces, which for the purposes of this function are so similar to classes, that we'll just use the term class to refer to classes and interfaces.

We give the general description of def-java-class forms and then some specific examples. But def-java-class is not a defined operator. You cannot evaluate these for at a Lisp prompt. Instead, they should be in a file which you then compile with jcomp-file. See the example in Test suite example and TicTacToe example below.

The symbol name def-java-class is also defined in the net.jlinker package described jlinker.html. That use, which does define an operator, has no relation to the jil module.

The form of a def-java-class is

(def-java-class class-name (superclass superinterface ...)
      flag ...
      (field-desc ...)
      [:methods (name (arg1 ..) expr) ...]
      [:classes (name (superclass ...) [flag ...] (field-desc ...)) ... ]
)

The class-name is an identifier (which is a Lisp symbol). You can use a fully qualified name such as com.franz.jkf.myclass, but you're more likely to want to set the package to "com.franz.jkf" and just use the class-name myclass in the def-java-class form. Every class you define will have one superclass and zero or more superinterfaces. If no superclass is given, then the superclass used is java.lang.Object.

flags are used to specify attributes of the class. Flags are also used to specify attributes of fields in a class and attributes of methods. The table below describes all of the flags and which flags can be used to describe classes, fields and methods:

Flag
:abstract class method
:final class field method
:interface class
:native method
:private field method
:protected field method
:public class field method
:static Inner class only field method
:synchronized method
:transient field
:volatile field

Following the zero or more flags is a list of field descriptions. The format of a field-desc is a list beginning with the field name followed optionally by a sequence of keyword value pairs.

(field-name :type type  :initform expression :flags (flag1 ....))

The initform expression, if given, will be evaluated inside the default constructor for this class.

The table above shows the flag values valid inside this field description.

It is possible to list some of the methods of the class in the def-java-class form by putting them after a :methods specifier. This is normally not done for top level classes but it is necessary to use this syntax when defining methods for inner classes.

It is possible to define inner classes by specifying class descriptions after the :classes keyword.

Here are some sample class definitions:

(def-java-class htest ()
    ; simple one slot public class
    :public 
    ((x :type int))

   )

(def-java-class zip (nil intx inty)
    ; interface with one slot (which must be static in an interface).
    ; by specifying nil for the superclass, we get 
    ; the default (java.lang.Object)
    :public :interface
    ((teeth :type int :initform 20 :flags (:static)))
    :methods
    (brush () :void)  ; normal method
    (teeth-count () :int )

   )

6.0 def-java-method

This form is used to create a method that is associated with the class being defined in the same file.

(def-java-method method-name ((arg1 type)... (argn type)) return-type
        flag ...
        <strong>[</strong>(:throws exception-class ...)<strong>]</strong>
        expr ....
)

The set of allowable flags is given in the table above.

The body of a method is a sequence of expressions. The allowable expressions are described below. While java is a statement based language, jil is, like Lisp, an expression based language. In jil every expression returns zero or one value. The value of the last expression in the body of a method is the value returned by the method.


7.0 Special Symbols

A few symbols have special meaning inside a jil method.


8.0 Special Forms

The special forms in Jil are:

progn

(progn expr ...)

Evaluate each expression in sequence and return the value of the last one

block

(block tagname expr ....)

Like progn except that this sets up a tag for use by return-from.

return-from

(return-from name [result])

Like return, jumps out of a block named name, returning result if supplied or nil.

ref

(ref expr slotname1 [ ... slotnameN ])

(ref classname static-slotname [ slotname1 ... slotnameN ])

(ref package1 ... packageN classname static-slotname [ slotname1 ... slotnameN ] )

The ref form is used to

A shorthand for this form can be used in many cases. In jil the expression foo.bar.baz is converted into (ref foo bar baz) by jil compiler. This dot convention only works when in Java you have a sequence of symbols separated by dots. For example there is no way to use the dot convention to write (ref (myfun x) slot).

if

(if expr true-expr [ false-expr ] )

This is just like the Common Lisp if special form. In Java conditionals only test boolean values. This is not the case in jil which is more like Lisp in this regard. In jil the following values are considered true:

The following things are considered false:

and

(and expr ....)

This is like the Common Lisp and form except that the result is always the boolean true or false value. It never returns the last value of the form.

or

(or expr ...)

This is like the Common Lisp or form except that the result is always the boolean true or false value. It never returns the value of any subexpression in the form.

handler-case

(handler-case hexpr (exception-class (var) expr ...) ...)

This is how the Java try...except is expressed in jil. While hexpr is evaluated, if an exception is raised that is a subclass of one of the exception-classes given in this form, then that exception object is bound to the var and the control continues in the body of the exception handler.

unwind-protect

(unwind-protect protected-expr expr1 .... exprN)

This is how the java try...finally is expressed in jil. While protected-expr is evaluated a condition handler is setup so that any attempt to throw control out of the protected-expr will result in control going instead to expr1 and the following forms. If all the forms up to and including exprN evaluate, then the throw that caused control to reach expr is continued.

throw

(throw expr)

The expr should evaluate to an object that is a subclass of java.lang.Exception. The program resumes execution at the innermost handler for the exception being thrown.

let

(let ((var <strong>[</strong> initform <strong>[</strong> type <strong>]]</strong>) ....) expr ...)

A set of local variables are bound during the evaluation of a sequence of expressions. If the initform is not given then it is assumed to be 0 (zero). If the type is not given, then it is assumed to be the type of the initform. In this case the type of a number is the most restrictive type that describes it.

setq

(setq place expr ... ...)

Like the Common Lisp setf this stores values not only in local variables but in slots of objects (described by ref expressions). The value of the setq expression is the value of the last expression stored.

<, <=, =, >=, >

(<  expr1 expr2 ...)
(<= expr1 expr2 ...)
(=  expr1 expr2 ...)
(>= expr1 expr2 ...)
(>  expr1 expr2 ...)

These are a mixture of the Common Lisp functions by the same names and the and special form. They compute a relation between numbers yet short circuit evaluation as soon as they determine that the answer is false. The return value is the boolean value true or false.

+, *, /, -

(+ [ expr ... ])
(* [ expr ... ])
(/ [ expr ... ])
(- [ expr ... ])

These take zero or more arguments and compute the appropriate function over the argument values. The exprs are only evaluated as needed before the function is applied to them. This means that an arithmetic exception may be signaled before all of the arguments have been evaluated.

<<

(<< value shiftamount)

shift the value left by the given shift amount. The value should be a type that can be widened to an int or a long. The result of the shift will either be an int or a long depending on the type of the value. The low bits of the shiftamount are used to determine the number of places left the value is shifted. If an int will be shifted then the shiftamount is anded with #x1f and that value is the shift amount. If value is a long then the shiftamount is anded with #x3f. The resulting shift amount is thus always a non-negative number.

>>

(>> value shiftamount)

shift the value right by the given shiftamount, copying the sign bit into vacated bits. This is an arithmetic shift. See the description for << to see how the shiftamount value is modified before it is used

>>>

(>>> value shiftamount)

shift the value right by the given shiftamount, copying zero into vacated bits. This is a logical shift. See the description for << to see how the shiftamount value is modified before it is used.

logand, logior, logxor

(logand expr ...)
(logior expr ...)
(logxor expr ...)

perform the bitwise operation on the expressions (which must be of type int or long)

instanceof

(instanceof expr type)

returns true if the object that is the value of the expr is an instance of the named type. The expression expr must return a reference to an object, not a primitive type such as int. The type is not quoted.

Thus, (instanceof x String) tests if x points to a String object.


9.0 Functions

The functions, not and new.

not

and
(not expr)

Compute the boolean value that is the inverse of the given value, where the description of what is considered true and false is described with the if special form.

new

(new type expr ...)

Allocate a new object of the given type and call its constructor passing the value of the exprs as arguments.

The type is normally a symbol. It can also be an anonymous type description.

The form of an anonymous type description is

(:class superclass (superinterface ...) [flag ...] (field-desc
...) [:methods method-desc] [:classes class-desc])

Essentially it looks just like a def-java-class form except that instead of naming the class you name the class and/or interfaces that the class inherits from. If the anonymous class is to inherit from only an interface and no class then that interface should be put in the superclass spot, i.e. (:class superint () .....).


10.0 macros

Users can add macros to the jil compiler with def-java-macro. The macros cond, if*, and s+ are already defined:

cond

(cond  (pred-expr value-expr1 ... value-exprN) ... <strong>[</strong>(t value-expr1 .. value-exprN) <strong>]</strong>)

This is very close to the Common Lisp cond form except that one type of clause is not permitted: that is the clause where the value of the predicate is the value returned (if it is true).

if*

(if* pred then expr1 ... exprN <strong> [ </strong>elseif pred then expr1 .... exprN <strong>]</strong> ... <strong>[ </strong>else expr1 .... exprN <strong>] </strong>)

The if* macro from Allegro Common Lisp. This is designed to do the work of the Common Lisp if, when and unless forms and offer greater program readability.

s+

(s+ expr1 expr2 ... exprN)

In Java the + operator is overloaded to do string concatenation. We chose not to do this in JiL. Instead we offer the s+ macro that generates code just like the Java compiler when it determines that + must do string concatenation. Each expression can return any type of value and as long as there is an appropriate append method in the class java.lang.StringBuffer that accepts that type of value.


11.0 package

A jil source file can contain a package form

(package packagename)

This form should appear before the class and any methods are defined. It specifies the package prefix for the class. Failure to include a package form in a file sets the package prefix to the empty string.


12.0 import

The import form specifies a class or set of classes which should be capable of being referenced without the full package qualification. There can be zero or more import forms in a jil source file.

(import classname)
(import packagename.*)

The first form imports just the single class, the second imports all classes in the given package (but not subpackages of the given package).

Just as in Java, there is an implicit import done of java.lang.* before a jil file is compiled.


13.0 def-java-macro

This defines a macro for use in compiling jil expressions only. The form is identical to Lisp's defmacro:

(def-java-macro name (arg ...)  expr ...)

14.0 Using the jil compiler

First you must install a Java Software Development Toolkit or Java Runtime on your machine. Jil needs access to the jar file of standard Java classfiles (which define such classes as java.lang.Object). You tell jil where to find the java classfiles either by giving a non-nil value to *jil-classpath* or by setting the CLASSPATH environment variable. Newer java development toolkits don't require you to set CLASSPATH for doing Java development since they compute the class path based on where the java component being run is located in the filesystem. This will not work for jil programming. You need to specify a CLASSPATH or a value for *jil-classpath*.

The *jil-classpath* variable is designed to allow you to easily switch between different sets of java classfiles. If you will use only one set, you should specify a value for CLASSPATH and leave (or make) the value of *jil-classpath* nil.

Next you place the files comprising the jil compiler on your machine, and we suggest that they be in the jil subdirectory of the main directory in which you intend to work. Start lisp and evaluate

(require :jil)

The jil compiler will be loaded into Lisp. Functions and variables are named by symbols in the javatools.jil package.

The function jcomp-file will compile a jil sourcefile into a classfile. A source file can contain multiple class definitions. The def-java-method's that follow a def-java-class are associated with that class. The output of jcomp-file is one or more class files. The names of the class files are the names of the classes defined, with the extension or type .class. Inner and anonymous classes are given names generated by the jil compiler and are written out one per file as well.

The toplevel forms permitted in a file processed by jil are:


15.0 Test suite example

To run the examples, you must have a Java Software Development Toolkit or Java Runtime installed on your machine (as said in Using the jil compiler).

The jil distribution includes the jil test harness tester.jil and the the jil test suite jiltest.jil. In order to compile the test suite to a java class file start lisp and change to the directory that contains the jil directory as a subdirectory and do the following:

cl-user(1): <strong>(require :jil)</strong>
; Fast loading /usr/local/acl/code/jil.fasl
cl-user(2): <strong>(use-package :javatools.jil)</strong>
t


;; First we must compile the test harness into a class file
;; since the jiltest.jil file imports it


cl-user(3): (jcomp-file "jil/tester")

-- Constructor building
Class: "tester"

-- Method compilation
Class: "tester"
Compiling check(int long long)void
Compiling check(int int int)void
Compiling check(int float float)void
Compiling check(int double double)void
Compiling <init>()void
Compiling <clinit>()void
Class: "tester"
Writing file "jil/tester.class"
nil

;; now we compile the test suite itself


cl-user(4): (jcomp-file "jil/jiltest")

-- Constructor building
Class: "mactest1"
Class: "mactest2"
Class: "superguy"
Class: "subguy"
Class: "innertestfoo"
Class: "innertestfoo$innera"
Class: "innertestfoo$innera$ultraina"
Class: "innertestfoo$innerb"
Class: "innertestfoo$barintf"
Class: "jiltest"
Class: "blartly"

-- Method compilation
Class: "mactest1"
Compiling <init>()void
Class: "mactest2"
Compiling <init>()void
Class: "superguy"
Compiling testfcn()int
Compiling testdowncast()int
Compiling <init>()void
Class: "subguy"
Compiling testfcn()int
Compiling overloading-2()void
Compiling randomstuff()void
Compiling test_widen()superguy
Compiling <init>()void
Compiling <clinit>()void
Class: "innertestfoo"
Compiling <init>()void
Compiling test_inner()void
Compiling sum(int int)int
Compiling diff(int int)int
Compiling <init>(innertestfoo)void
Compiling clonetest()void
Class: "innertestfoo$innera"
Compiling <init>(innertestfoo)void
Class: "innertestfoo$innera$ultraina"
Compiling <init>(innertestfoo$innera)void
Class: "innertestfoo$innerb"
Compiling <init>(innertestfoo)void
Class: "innertestfoo$barintf"
Class: "jiltest"
Compiling simplemath()void
Compiling promotions()void
Compiling relopstarget(int int int)void
Compiling relopscc(int int int)void
Compiling relopstarget(float float float)void
Compiling relopstarget(double float double)void
Compiling andornot(int double)void
Compiling setqs()void
Compiling lets()void
Compiling doexcept()int
Compiling doexcept2()int
Compiling excepts()void
Compiling dofinally()int
Compiling arraystuff()void
Compiling methodfun(int)void
Compiling overloading()void
Compiling thetests()void
Compiling shifttests()void
Compiling logtests()void
Compiling emptytests()void
Compiling miscstuff(String)void
Compiling main((array String))void
Compiling <init>()void
Class: "blartly"
Compiling snort(int)void
Compiling <init>()void
Class: "mactest1"
Writing file "jil/mactest1.class"
Class: "mactest2"
Writing file "jil/mactest2.class"
Class: "superguy"
Writing file "jil/superguy.class"
Class: "subguy"
Writing file "jil/subguy.class"
Class: "innertestfoo"
Writing file "jil/innertestfoo.class"
Class: "innertestfoo$innera"
Writing file "jil/innertestfoo$innera.class"
Class: "innertestfoo$innera$ultraina"
Writing file "jil/innertestfoo$innera$ultraina.class"
Class: "innertestfoo$innerb"
Writing file "jil/innertestfoo$innerb.class"
Class: "innertestfoo$barintf"
Writing file "jil/innertestfoo$barintf.class"
Class: "jiltest"
Writing file "jil/jiltest.class"
Class: "blartly"
Writing file "jil/blartly.class"
Class: "innertestfoo$1"
Writing file "jil/innertestfoo$1.class"
nil
cl-user(5)

The result of these two calls to jcomp-file are a number of class files, once for each class defined in the files (including anonymous classes). To run the test suite start a shell (or Command Prompt on Windows) and change to the jil directory. Then run the Java vm as shown below. Each test suite entry that works will print an Ok message. If it fails a message stating what was expected and returned will be printed. There should be no errors in the test suite (although errors were reported by older and buggier Java virtual machines).

% java jiltest
Ok 1
Ok 2
Ok 3
Ok 4
Ok 5
Ok 6
Ok 7
Ok 8
Ok 9
Ok 10
Ok 11
Ok 12
Ok 100
Ok 101
Ok 102
Ok 103
Ok 104
Ok 105
Ok 106
Ok 107
Ok 108
Ok 200
Ok 201
Ok 202
Ok 203
Ok 204
Ok 205
Ok 205
Ok 206
Ok 207
Ok 208
Ok 209
Ok 210
Ok 211
Ok 212
Ok 213
Ok 214
Ok 215
Ok 216
Ok 217
Ok 218
Ok 219
Ok 220
Ok 300
Ok 301
Ok 302
Ok 303
Ok 303
Ok 304
Ok 305
Ok 306
Ok 307
Ok 308
Ok 400
Ok 401
Ok 402
Ok 403
Ok 404
Ok 405
Ok 405
Ok 406
Ok 407
Ok 408
Ok 409
Ok 410
Ok 411
Ok 412
Ok 413
Ok 414
Ok 415
Ok 416
Ok 417
Ok 418
Ok 419
Ok 420
Ok 500
Ok 501
Ok 502
Ok 503
Ok 504
Ok 505
Ok 505
Ok 506
Ok 507
Ok 508
Ok 509
Ok 510
Ok 511
Ok 512
Ok 513
Ok 514
Ok 515
Ok 516
Ok 517
Ok 518
Ok 519
Ok 520
Ok 600
Ok 601
Ok 602
Ok 602
Ok 603
Ok 604
Ok 700
Ok 701
Ok 702
Ok 703
Ok 704
Ok 705
Ok 706
Ok 707
Ok 708
Ok 709
Ok 800
Ok 801
Ok 802
Ok 803
Ok 900
Ok 901
Ok 1000
Ok 1001
Ok 1100
Ok 1101
Ok 1102
Ok 1103
Ok 1104
Ok 1105
Ok 1106
Ok 1107
Ok 1108
Ok 1109
Ok 1110
Ok 1111
Ok 1113
Ok 1114
Ok 1115
Ok 1116
Ok 1117
Ok 1118
Ok 1119
Ok 1120
Ok 1121
Ok 1122
Ok 1123
Ok 1124
Ok 1125
Ok 1126
Ok 1127
Ok 1300
Ok 1301
Ok 1302
Ok 1303
Ok 1304
Ok 1305
Ok 1306
Ok 1307
Ok 1308
Ok 1309
Ok 1200
Ok 1201
Ok 1400
Ok 1401
Ok 1500
Ok 1501
Ok 1502
Ok 1504
Ok 1505
Ok 1506
Ok 1507
Ok 1508
Ok 1509
Ok 1510
Ok 1511
Ok 1512
Ok 1513
Ok 1514
Ok 1515
Ok 1515
Ok 1516
Ok 1517
Ok 1600
Ok 1601
Ok 1602
Ok 1603
Ok 1604
Ok 1604
Ok 1605
Ok 1700
Ok 1701
Ok 1702
Ok 1703
Ok 1704
Ok 1705
Ok 1801
Ok 1802
Ok 1803
Ok 1804
Ok 1805
Ok 1806
Ok 1807
Ok 1808
Ok 1809
Ok 1810
Ok 1811
Ok 1812
Ok 1900
Ok 1901
Ok 1902
Ok 1903
Ok 1904
Ok 1905
% 

16.0 TicTacToe example

To run the examples, you must have a Java Software Development Toolkit or Java Runtime installed on your machine (as said in Using the jil compiler).

This example shows how an applet is converted from Java syntax to jil syntax. The Java source for the applet can be found in the Java SDK if you have that installed on your machine.

The jil version of the TicTacToe program is in TicTac.jil. Assuming that the current directory is the one that contains the jil/ subdirectory, you compile the applet as follows:

cl-user(1): (require :jil)
; Fast loading /usr/local/acl/code/jil.fasl
cl-user(2): (javatools.jil:jcomp-file "jil/TicTac")


-- Constructor building
Class: "TicTac"

-- Method compilation
Class: "TicTac"
Compiling isWon(int)void
Compiling bestMove(int int)int
Compiling yourMove(int)boolean
Compiling myMove()boolean
Compiling status()int
Compiling init()void
Compiling destroy()void
Compiling paint(Graphics)void
Compiling mouseReleased(MouseEvent)void
Compiling mousePressed(MouseEvent)void
Compiling mouseClicked(MouseEvent)void
Compiling mouseEntered(MouseEvent)void
Compiling mouseExited(MouseEvent)void
Compiling getAppletInfo()String
Compiling <init>()void
Compiling <clinit>()void
Class: "TicTac"
Writing file "jil/TicTac.class"
nil
cl-user(3):

To run the demo start up a web browser viewing the file TicTac.html in the jil directory. That will cause the TicTac.class file to be loaded into the browser and started. Then start clicking on free squares to make a move.


Copyright (c) Franz Inc. Lafayette, CA., USA. All rights reserved.

ToC DocOverview CGDoc RelNotes FAQ Index PermutedIndex
Allegro CL version 11.0