ATJ (ACL2 To Java) is a Java code generator for ACL2.
This manual page contains user-level reference documentation for ATJ. If you are new to ATJ, you should start with the tutorial, which provides user-level information on how ATJ works and how to use ATJ effectively. Some of the material in this manual page will likely be moved to the tutorial, which is in progress.
ATJ translates ACL2 to Java, enabling ACL2 code to be used in Java code (in the sense explained below).
For instance, ATJ is useful to generate Java code at the end of an APT program synthesis derivation.
This manual page provides reference documentation for ATJ.
A separate tutorial in being written, as noted above.
See the files under
ATJ translates ACL2 named functions to corresponding Java code whose execution mimics the execution of the ACL2 functions.
The ACL2 functions accepted by ATJ may manipulate any ACL2 value: characters, strings, symbols, numbers, and cons pairs. The Java code that corresponds to the ACL2 functions manipulates Java representations of the ACL2 values.
ATJ accepts all the ACL2 functions that
(1) have either an unnormalized body or an attachment and
(2) either do not have raw Lisp code
or have raw Lisp code but belong to a whitelist
(but also see the
ATJ also accepts the ACL2 function return-last
(which has raw Lisp code and is not in the whitelist),
but only when its first argument is
ATJ also accepts all the ACL2 primitive functions. The Java code that corresponds to these ACL2 functions has the input/output behavior documented for these functions.
ATJ accepts both logic-mode and program-mode functions.
Some ACL2 functions have side effects when executed,
e.g. hard-error prints an error message
and returns control to the top level.
All the ACL2 functions with side effects have raw Lisp code
and are not in the whitelist mentioned above.
Therefore, the generated Java code
does not mimic any of the side effects exhibited by ACL2 functions.
In particular, calls of prog2$ and progn$ are accepted
(as explained above about return-last
with first argument
ATJ does not accept functions that access stobjs. Support for stobjs, and destructive updates of stobjs, may be added in the future.
ATJ does not translate macro definitions to Java code. However, the use of macros in function bodies is fully supported, because ATJ operates on ACL2 translated terms, where macros are expanded.
ATJ does not translate named constant definitions to Java code. However, the use of named constants in function bodies is fully supported, because ATJ operates on ACL2 translated terms, where constants are expanded.
The generated Java code can be called by external Java code, but not vice versa. The ability to have the generated Java code call external Java code may be added in the future; this may involve the use of ACL2 stubs corresponding to the Java code to be invoked by the (Java-translated) ACL2 code.
External Java code can call the generated Java code on (Java representations of) explicit ACL2 values. Access to global variables like state or user-defined stobjs is therefore not supported; in particular, the generated Java code has no access to the ACL2/Lisp environment. Support for global variables, in particular state, may be added in the future.
ATJ is supported by AIJ, which is a deep embedding in Java of the executable subset of ACL2 (subject to the limitations outlined above).
ATJ translates the target ACL2 functions into Java representations, based on their unnormalized bodies or attachments. It does so recursively, starting from the top-level functions specified by the user and stopping at the ACL2 functions that either are implemented natively in AIJ or (under certain conditions; see below) represent Java primitive operations or primitive array operations. If a function is encountered that is not natively implemented in AIJ and has no unnormalized body and no attachment, ATJ stops with an error. If a function is encountered that has raw Lisp code and is not in the whitelist (except for the treament of return-last explained above), ATJ stops with an error.
ATJ generates Java code with public methods to (1) initialize AIJ's Java representation of the ACL2 environment and (2) call the Java representations of the ACL2 functions (see the `Generated Java Code' section for details). AIJ provides public classes and methods to translate certain Java values to AIJ's Java representations of ACL2 values and vice versa. Thus, by loading into the Java Virtual Machine both AIJ and the Java code generated by ATJ, external Java code can ``use'' ACL2 code. Under some conditions, ATJ can also generate Java code that does not use AIJ, and can be run without AIJ; see below for details.
ATJ generates either deeply embedded or shallowly embedded Java representations of the ACL2 functions. The choice is controlled by a user option.
In the deep embedding approach, ATJ generates Java code to build the deeply embedded ACL2 functions, and to call and execute them via AIJ's interpreter.
This deep embedding approach is simple and thus fairly high-assurance. On the other hand, the Java code is not efficient or idiomatic. However, the approach may work well for some simple applications.
In the shallow embedding approach, ATJ generates Java code that mimics the computations of the shallowly embedded ACL2 functions, one Java method for each ACL2 function. These methods are executed without using AIJ's interpreter. However, the shallowly embedded ACL2 functions still use AIJ's representation of the ACL2 values and AIJ's native implementations of ACL2 functions. Under certain conditions, the shallowly embedded ACL2 functions use Java values that are not AIJ's Java representations of ACL2 values; see below for details.
This shallow embedding approach is more complex than the deep embedding approach, but produces code that is more efficient and more idiomatic.
(atj fn1 ... fnp :deep ... :guards ... :no-aij-types ... :java-package ... :java-class ... :output-dir ... :tests ... :ignore-whitelist ... :verbose ... )
Names of the target ACL2 functions to be translated to Java.
Each
fni must be a symbol that names a function that either has an unnormalized body and no raw Lisp code (unless it is in the whitelist; but also see the:ignore-whitelist option below), or has an attachment, or is natively implemented in AIJ. Each of these functions must have no input or output stobjs. Each of these functions must transitively call (in the unnormalized body or attachment, if not natively implemented in AIJ) only functions that satisfy the same constraints, except for calls of return-last as described below.None of the
fni functions may be return-last. However, thefni functions may transitively call return-last, under two possible conditions:
- The first argument of return-last is
'acl2::mbe-raw1 , i.e. the call results from the translation of mbe. Even though Java code is generated for one of the second and third arguments but not for the other one (based on the:guars input; see below), the restrictions on called functions, and in particular the absence of side effects, are enforced on all the argument of the call.- The first argument of return-last is
'acl2::progn , i.e. the call results from the translation of prog2$ or progn$. Even though Java code is generated for the last argument of the call but not for the previous one(s), the restrictions on called functions, and in particular the absence of side effects, are enforced on all the argument of the call.If the
:deep input isnil and the:guards input ist , then none of thefni may be one of the functions listed in *atj-jprim-fns* or one of the functions listed in *atj-jprimarr-fns*. These functions are treated specially in the shallow embedding when guard satisfaction is assumed (see below).There must be at least one function, i.e.
p > 0. All thefni names must be distinct.
Chooses the deep or shallow embedding approach described above:
t , for the deep embedding.nil , for the shallow embedding.
Specifies whether the generated code should assume that all the guards are satisfied or not:
t , to assume that they are satisfied. In this case, the generated code may run faster; in particular, only the:exec part of mbe is executed. Furthermore, if the:deep input isnil , the Java methods in the generated code have the argument and return types specified via atj-main-function-type and atj-other-function-type (see the `Generated Java Code' section for more information), and the generated Java code may manipulate Java primitive values and Java primitive arrays directly.nil , to not assume that the guards are satisfied. In this case, the generated code runs ``in the logic''; in particular, only the:logic part of mbe is executed.Regardless of the value of this input, the generated code never checks guards. The difference is whether guards are ignored altogether (i.e. execution ``in the logic'') or assumed to hold (i.e. possibly faster execution). This input should be
t only when generating Java code from guard-verified ACL2 code. Furthermore, external Java code that calls the generated code should do so only with values that satisfy the guards of the called ACL2 functions, if this input ist . Otherwise, erroneous computations may occur.
Specifies whether the generated code should not make use of the AIJ types:
t , to require the code not to use any of the AIJ types, which represent ACL2 values in Java. In other words, the generated code can only use the Java primitive types and the Java primitive array types; this means that the code is generally more idiomatic Java, as opposed to mimicking ACL2 code in Java.nil , to not require that.This input can be
t only if:deep isnil and:guards ist , because that is the only situation in which the generated Java code may use the Java primitive types and primitive array types. If this input ist , ATJ checks that every functionfn translated to Java satisfies the following requirements:
- The argument and return types of
fn , specified via atj-main-function-type, are all:aboolean ,:acharacter , and:j... . Recall that ACL2 booleans and characters are mapped to Java booleans and characters when:deep isnil and:guards ist . The built-in functions equal, if, and not are excepted from this requirement.- The unnormalized body (or attachment, as explained earlier) of
fn , after the pre-translation steps that remove code (i.e. atj-pre-translation-remove-return-last, atj-pre-translation-remove-dead-if-branches, and atj-pre-translation-unused-vars), only uses, as an untranslated term:
- Variables.
- Quoted
t andnil , which ATJ maps to the corresponding Java boolean literals when:deep isnil and:guards ist .- Quoted boolean and integer constants used as arguments of functions in *atj-jprim-constr-fns*.
- Lambda expressions (i.e. let, including mv-let).
- Calls of:
- The function
fn (as a recursive call).- The other functions translated to Java.
- The functions in *atj-jprim-constr-fns*, but only on quoted constants.
- The function boolean-value->bool.
- The functions in *atj-jprim-unop-fns*.
- The functions in *atj-jprim-binop-fns*.
- The functions in *atj-jprim-conv-fns*.
- The functions in *atj-jprimarr-read-fns*.
- The functions in *atj-jprimarr-length-fns*.
- The functions in *atj-jprimarr-write-fns*.
- The functions in *atj-jprimarr-new-len-fns*.
- The functions in *atj-jprimarr-new-init-fns*, but only on arguments that are nested conses (i.e. translated list calls).
- The function equal.
- The function if.
- The function not.
- The function cons, but only in translated mv calls that are returned by
fn , either at the top level of the function's body or recursively in the `then' or `else' branches of ifs starting at the top level.If
:no-aij-types ist , ATJ also checks that the target functionsfn1 , ...,fnp do not include equal, if, not, or cons. These functions may only be called, directly or indirectly, by the target functions, but cannot be target functions.
Name of the Java package of the generated Java code.
It must be either an ACL2 string or
nil . If it is an ACL2 string, it must be a valid Java package name consisting of only ASCII characters; it must also be distinct from AIJ's Java package name, i.e.edu.kestrel.acl2.aij . If this input isnil , the generated Java code is in an unnamed Java package.
Name of the generated Java class.
It must be either an ACL2 string or
nil . If it is an ACL2 string, it must be a valid Java class name consisting of only ASCII characters. If this input isnil , the generated Java class is calledAcl2Code .Unless
:no-aij-type ist , an additional auxiliary class is generated, whose name is obtained by appendingEnvironment at the end of the name of the main class. This auxiliary class contains boilerplate code to build a Java representation of the ACL2 environment (i.e. ACL2 package definitions, and also ACL2 function definitions if the:deep input ist ).If the
:tests input (see below) is notnil , a third Java class for testing is generated, whose name is obtained by appendingTests at the end of the name of the main class.
Path of the directory where the generated Java files are created.
It must be an ACL2 string that is a valid path to an existing directory in the file system; the path may be absolute, or relative to the connected book directory. The default is the connected book directory.
If the
:java-package input specifies an unnamed package, the generated Java files are written in that directory. If the:java-package input specifies a named package, subdirectories are created in that directory that correspond to the dot-separated identifiers of the package name, and the generated Java files are written in the innermost subdirectory. This directory structure matches the typical organization of Java source files. If the subdirectories do not exist, they are created; if they exist, they are used as they are, without regard to whether they may contain additional files and directories (however, it is recommended that the subdirectories do not contain any such additional files and directories.One file per class is generated: one file if the
:no-aij-types input ist and the:tests input isnil ; two files if either the:no-aij-types input isnil and the:tests input isnil , or the:no-aij-types input ist and the:tests input ist ; and three files if the:no-aij-types input isnil and the:tests input ist . The name of each generated file is the name of the corresponding class followed by.java . If the file already exists, it is overwritten.
Optional tests to generate Java code for.
This input must evaluate to a list of doublets
((name1 term1) ... (nameq termq)) , where eachnamej is a string consisting of only letters and digits, and eachtermj is an untranslated ground term whose translation is(fn in1 in2 ...) , wherefn is among the target functionsfn1 , ...,fnp ,fn returns single results (i.e. not multiple results) (support for generating tests for functions that return multiple results will be added in the future), and eachin amongin1 ,in2 satisfies the following conditions:
- If
:deep ist or:guards isnil , thenin must be a quoted constant.- If
:deep isnil and:guards ist , then requirements onin depend on the type assigned, via atj-main-function-type, to the input offn corresponding toin :
- If the type is
:a... , thenin must be a quoted constant.- If the type is
:jboolean , thenin must be a term(java::boolean-value <boolean>) where<boolean> is a quoted boolean (i.e.t ornil ).- If the type is
:jchar , thenin must be a term(java::char-value <char>) where<char> is a quoted unsigned 16-bit integer.- If the type is
:jbyte , thenin must be a term(java::byte-value <byte>) where<byte> is a quoted signed 8-bit integer.- If the type is
:jshort , thenin must be a term(java::short-value <short>) where<short> is a quoted signed 16-bit integer.- If the type is
:jint , thenin must be a term(java::int-value <int>) where<int> is a quoted signed 32-bit integer.- If the type is
:jlong , thenin must be a term(java::long-value <long>) where<long> is a quoted signed 64-bit integer.- If the type is
:jboolean[] , thenin must be a term(java::boolean-array-new-init <booleans>) where<booleans> is the translation of a term(list <elem1> <elem2> ...) where each<elem> is a term(java::boolean-value <boolean>) as in the case above in which the type is:jboolean .- If the type is
:jchar[] , thenin must be a term(java::char-array-new-init <chars>) where<chars> is the translation of a term(list <elem1> <elem2> ...) where each<elem> is a term(java::char-value <char>) as in the case above in which the type is:jchar .- If the type is
:jbyte[] , thenin must be a term(java::byte-array-new-init <bytes>) where<bytes> is the translation of a term(list <elem1> <elem2> ...) where each<elem> is a term(java::byte-value <byte>) as in the case above in which the type is:jbyte .- If the type is
:jshort[] , thenin must be a term(java::short-array-new-init <short>) where<short> is the translation of a term(list <elem1> <elem2> ...) where each<elem> is a term(java::short-value <short>) as in the case above in which the type is:jshort .- If the type is
:jint[] , thenin must be a term(java::int-array-new-init <ints>) where<ints> is the translation of a term(list <elem1> <elem2> ...) where each<elem> is a term(java::int-value <int>) as in the case above in which the type is:jint .- If the type is
:jlong[] , thenin must be a term(java::long-array-new-init <longs>) where<longs> is the translation of a term(list <elem1> <elem2> ...) where each<elem> is a term(java::long-value <long>) as in the case above in which the type is:jlong .All the
namej strings must be distinct.Each doublet
(namej termj) specifies a test, in which the result of(fn in1 in2 ...) calculated by ACL2 is compared with the result of the same call calculated via the generated Java code forfn . These tests can be run via additional generated Java code (see below).Note that the
:tests input is evaluated.Test inputs of the form
(java::boolean-value <boolean>) ,(java::char-value <char>) ,(java::byte-value <byte>) ,(java::short-value <short>) ,(java::int-value <int>) ,(java::long-value <long>) ,(java::boolean-array-new-init <booleans>) ,(java::char-array-new-init <chars>) ,(java::byte-array-new-init <bytes>) ,(java::short-array-new-init <shorts>) ,(java::int-array-new-init <ints>) , or(java::long-array-new-init <longs>) can be used only for ACL2 functions that have the ATJ type:jboolean ,:jchar ,:jbyte ,:jshort ,:jint ,:jlong ,:jboolean[] ,:jchar[] ,:jbyte[] ,:jshort[] ,:jint[] , or:jlong[] assigned to the corresponding argument via atj-main-function-type.
If
t , this tells ATJ to ignore the whitelist of functions with raw Lisp code, i.e. to accept any function with raw Lisp code, provided that it has an unnormalized body. This means that any side effects that happen in ACL2 execution will not happen in the generated Java code. This should be only used in special circumstances, e.g. when the non-whitelisted functions are unreachable under guard verification.
Controls the amount of screen output:
t , to show all the output.nil , to suppress all the non-error output.
ATJ generates a Java file that contains
a single public class named as specified by the
// if :deep is t: public class <name> { ... public static void initialize() ... public static Acl2Value call(Acl2Symbol function, Acl2Value[] arguments) ... } // if :deep is nil: public class <name> { ... public static void initialize() ... public static class <pkg> { public static <type> <fn>(<type> ...) ... } // other public static classes with public static methods }
This Java class has a public static method
In the deep embedding approach,
the Java class also has a public static method
In the shallow embedding approach,
the Java class contains public static methods
for the functions among
If
If the
public class <name>Tests { ... public static void main(String[] args) ... }
This Java class includes code
for each test
This Java class has a public static
ATJ generates Java 8 code. This means that the code can be compiled using a compiler for Java 8 or later.
The generated Java code can be compiled and run as any other Java code.
The
When the