Search code examples
javalispcommon-lispabcl

Can ABCL's Interpreter load Lisp source from an InputStream?


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?


Solution

  • 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.