3

I've just started looking at ABCL to mix some Lisp into Java. For now, loading some Lisp from a file will be sufficient, and I've been looking at the examples. In every case, the pattern is:

Interpreter interpreter = Interpreter.createInstance();
interpreter.eval("(load \"lispfunctions.lisp\")");

But say I'm building a Maven project with a view to packaging as a JAR: how can I load lispfunctions.lisp from src/main/resources? I can easily get an InputStream—can I go somewhere with that? Or is there another idiom I'm missing here for loading Lisp source from a resource like this?

Paul A. Hoadley
  • 1,518
  • 1
  • 13
  • 17
  • This is probably not the best way, but you can eval the lisp code compile or load your .lisp. Like "(load-file \"lispfunctions.lisp\")". – Riley Jun 24 '20 at 00:30
  • I think the reason you haven't found a specific function to do what you want is because it can be done through the general Lisp Object interface. Since `load-file` is a lisp function, it is a lisp object. You can access it through the lisp-object java interface as well. I think it would go something like `Packages.findPackage("CL").findAccessibleSymbol("FIND-PACKAGE").getSymbolFunction().execute("lispfunctions.lisp");` I don't know if you have to do something special with the "lispfunctions.lisp" string to make it a lisp object. – Riley Jun 24 '20 at 01:05
  • Great question. As you say, you can get an InputStream for the file in the jar. I see that the CLHS says (http://www.lispworks.com/documentation/HyperSpec/Body/f_load.htm) that LOAD can take a stream as the source. So it looks to me that the question is just how to get a Common Lisp stream from a Java InputStream, and then supply that to LOAD. – Robert Dodier Jul 04 '20 at 04:47
  • 1
    Some tinkering suggests that you can create a Lisp stream via `new Stream(Symbol.SYSTEM_STREAM, f, Symbol.CHARACTER)` in Java, where `f` is a `InputStream`. – Robert Dodier Jul 04 '20 at 06:07
  • Thanks Robert. I can now get a `Stream`, but what next? To be clear, all I'm trying to do is something like [this example](https://abcl.org/svn/trunk/abcl/examples/java-to-lisp-2/Main.java). How do I get the `CL-USER` `Package` and call Lisp functions from Java, as in the example? – Paul A. Hoadley Jul 05 '20 at 10:18

1 Answers1

2

I've gotten the following to work. I am working with ABCL 1.7.0 on MacOS, although I'm pretty sure this isn't version-specific.

/* load_lisp_within_jar.java -- use ABCL to load Lisp file as resource in jar
 * copyright 2020 by Robert Dodier
 * I release this work under terms of the GNU General Public License
 */

/* To run this example:
$ javac -cp /path/to/abcl.jar -d . load_lisp_within_jar.java
$ cat << EOF > foo.lisp
(defun f (x) (1+ x))
EOF
$ jar cvf load_lisp_within_jar.jar load_lisp_within_jar.class foo.lisp
$ java -cp load_lisp_within_jar.jar:/path/to/abcl.jar load_lisp_within_jar
 *
 * Expected output:
(F 100) => 101
 */
import org.armedbear.lisp.*;
import java.io.*;

public class load_lisp_within_jar {
    public static void main (String [] args) {
        try {
            // It appears that interpreter instance is required even though
            // it isn't used directly; I guess it arranges global resources.
            Interpreter I = Interpreter.createInstance ();

            LispObject LOAD_function = Symbol.LOAD.getSymbolFunction ();

            // Obtain an input stream for Lisp source code in jar.
            ClassLoader L = load_lisp_within_jar.class.getClassLoader ();
            InputStream f = L.getResourceAsStream ("foo.lisp");
            Stream S = new Stream (Symbol.SYSTEM_STREAM, f, Symbol.CHARACTER);

            // Call COMMON-LISP:LOAD with input stream as argument.
            LOAD_function.execute (S);

            // Verify that function F has been defined.
            Symbol F = Packages.findPackage ("COMMON-LISP-USER").findAccessibleSymbol ("F");
            LispObject F_function = F.getSymbolFunction ();
            LispObject x = F_function.execute (LispInteger.getInstance (100));
            System.out.println ("(F 100) => " + x.javaInstance ());
        }
        catch (Exception e) {
            System.err.println ("oops: " + e);
            e.printStackTrace ();
        }
    }
}

As you can see, the program first gets the function associated with the symbol LOAD. (For convenience, many, maybe all of COMMON-LISP symbols have static definitions, so you can just say Symbol.LOAD instead of looking up the symbol via findAccessibleSymbol.) Then the input stream is supplied to the load function. Afterwards we verify that our function F is indeed defined.

I know this stuff can be kind of obscure; I'll be happy to try to answer any questions.

Robert Dodier
  • 16,905
  • 2
  • 31
  • 48