| Allegro CL version 10.0 Unrevised from 9.0 to 10.0. 9.0 version |
This document contains the following sections:
1.0 IntroductionJava 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.
When you use jil, first load the jil module into Lisp by evaluating:
(require :jil)
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.htm.
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.
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).
byte
char
short
int
long
float
double
boolean
void
Classname
(array type)
[same as (array type *)
]
(array type *)
(array type * ...)
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.
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 Section 15.0 Test suite example and Section 16.0 TicTacToe example below.
The symbol name def-java-class is also defined in
the net.jlinker
package
described jlinker.htm. 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 ) )
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 ... [(:throws exception-class ...)] 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.
A few symbols have special meaning inside a jil method.
The special forms in Jil are:
(progn expr ...)
Evaluate each expression in sequence and return the value of the last one
(block tagname expr ....)
Like progn except that this sets up a tag for use by return-from.
(return-from name [result])
Like return-from, jumps out of
a block
named name, returning result
if supplied or nil
.
(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 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 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 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 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 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 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 ((var [ initform [ type ]]) ....) 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 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 expr ...) (logior expr ...) (logxor expr ...)
perform the bitwise operation on the expressions (which must be of type int or long)
(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.
(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 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 () .....)
.
Users can add macros to the jil compiler with def-java-macro. The macros cond, if*, and s+ are already defined:
(cond (pred-expr value-expr1 ... value-exprN) ... [(t value-expr1 .. value-exprN) ])
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* pred then expr1 ... exprN [ elseif pred then expr1 .... exprN ] ... [ else expr1 .... exprN ] )
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+ 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.
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.
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.
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 ...)
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:
To run the examples, you must have a Java Software Development Toolkit or Java Runtime installed on your machine (as said in Section 14.0 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): (require :jil) ; Fast loading /usr/local/acl/code/jil.fasl cl-user(2): (use-package :javatools.jil) 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 %
To run the examples, you must have a Java Software Development Toolkit or Java Runtime installed on your machine (as said in Section 14.0 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) 1998-2019, Franz Inc. Oakland, CA., USA. All rights reserved.
This page was not revised from the 9.0 page.
Created 2015.5.21.
| Allegro CL version 10.0 Unrevised from 9.0 to 10.0. 9.0 version |