More work on standalone documentation.

parent 4cf78ed5
Notes on debugging ABCL
* Need to set *PRINT-CIRCLE* to T when examining the structures in
jvm.lisp.
This diff is collapsed.
Lisp FFI
========
Mark Evenson
Created: 15-FEB-2010
Modified: 18-MAR-2010
FFI stands for "Foreign Function Interface", which is the way the
contemporary Lisp world refers to methods of "calling out" from Lisp
into "foreign" langauges and envrionments. This document describes
the various ways that one interacts with Lisp world of Abcl from Java,
considering the hosted Lisp as the "Foreign Function" that needs to be
"Interfaced".
# Lisp FFI
## Calling Lisp from Java
Note: As the entire ABCL Lisp system resides in the org.armedbear.lisp
package the following code snippets do not show the relevant import
statements in the interest of brevity.
Per JVM, there can only ever be a single Lisp interpreter. This is
started by calling the static method `Interpreter.createInstance()`.
Interpreter interpreter = Interpreter.createInstance();
If this method has already been invoked in the lifetime of the current
Java process it will return null, so if you are writing Java whose
lifecycle is a bit out of your control (like in a Java servlet), a
safer invocation pattern might be:
Interpreter interpreter = Interpreter.getInstance();
if (interpreter == null) {
interpreter = Interpreter.createInstance();
}
The Lisp `EVAL` primitive may be simply passed strings for evaluation,
as follows
String line = "(load \"file.lisp\")";
LispObject result = interpreter.eval(line);
Notice that all possible return values from an arbitrary Lisp
computation are collapsed into a single return value. Doing useful
further computation on the `LispObject` depends on knowing what the
result of the computation might be, usually involves some amount
of instanceof introspection, and forms a whole topic to itself
(c.f. [Introspecting a LispObject](#introspecting)).
Using `EVAL` involves the Lisp interpreter. Lisp functions may be
directly invoked by Java method calls as follows. One simply locates
the package containing the symbol, then obtains a reference to the
symbol, and then invokes the `execute()` method with the desired
parameters.
interpreter.eval("(defun foo (msg) (format nil \"You told me '~A'~%\" msg))");
Package pkg = Packages.findPackage("CL-USER");
Symbol foo = pkg.findAccessibleSymbol("FOO");
Function fooFunction = (Function)foo.getSymbolFunction();
JavaObject parameter = new JavaObject("Lisp is fun!");
LispObject result = fooFunction.execute(parameter);
// How to get the "naked string value"?
System.out.prinln("The result was " + result.writeToString());
If one is calling an primitive function in the CL package the syntax
becomes considerably simpler if we can locate the instance of
definition in the ABCL source, we can invoke the symbol directly. To
tell if a `LispObject` contains a reference to a symbol.
boolean nullp(LispObject object) {
LispObject result = Primitives.NULL.execute(object);
if (result == NIL) {
return false;
}
return true;
}
<a name="interpreting"/>
## Introspecting a LispObject
We present various patterns for introspecting an an arbitrary
`LispObject` which can represent the result of every Lisp evaluation
into semantics that Java can meaniningfully deal with.
### LispObject as boolean
If the LispObject a generalized boolean values, one can use
`getBooleanValue()` to convert to Java:
LispObject object = Symbol.NIL;
boolean javaValue = object.getBooleanValue();
Although since in Lisp, any value other than NIL means "true", the
use of Java equality it quite a bit easier and more optimal:
boolean javaValue = (object != Symbol.NIL);
### LispObject is a list
If LispObject is a list, it will have the type `Cons`. One can then use
the `copyToArray[]` to make things a bit more suitable for Java
iteration.
LispObject result = interpreter.eval("'(1 2 4 5)");
if (result instanceof Cons) {
LispObject array[] = ((Cons)result.copyToArray());
...
}
A more Lispy way to iterated down a list is to use the `cdr()` access
function just as like one would traverse a list in Lisp:;
LispObject result = interpreter.eval("'(1 2 4 5)");
while (result != Symbol.NIL) {
doSomething(result.car());
result = result.cdr();
}
Misc
====
Miscellaneous fragments of topics on aspects of ABCL that should be
collected into a more systematic documentation someday.
# Java FFI
## Calling Lisp from Java
Note: If you are wondering where the symbols are from in the following
text, the entire ABCL Lisp system resides in the org.armedbear.lisp
package, so they are in this package.
Per JVM, there can only ever be a single Lisp interpreter. This is
started by calling the static method Interpreter.createInstance().
Interpreter interpreter = Interpreter.createInstance();
If this method has already been invoked in the lifetime of the current
Java process it will return null, so if you are writing Java whose
lifecycle is a bit out of your control (like in a Java servlet), a
safer invocation pattern might be:
Interpreter interpreter = Interpreter.getInstance();
if (interpreter == null) {
interpreter = Interpreter.createInstance();
}
The Lisp EVAL primitive may be simply passed strings for evaluation,
as follows
String line = "(load \"file.lisp\")";
LispObject result = interpreter.eval(line);
Notice that all possible return values from an arbitrary Lisp
computation are collapsed into a single return value. Doing useful
further computation on the LispObject depends on knowing what the
result of the computation might be, usually involves some amount
of instanceof introspection, and forms a whole topic to itself
(c.f. "Interpreting a LispObject in Java").
Using EVAL involves the Lisp interpreter. Lisp functions may be
directly invoked by Java method calls as follows. One simply locates
the pacakge containing the symbol, then obtains a reference to the
symbol, and then invokes the execute() method with the desired
parameters.
interpreter.eval("(defun foo (msg) (format nil \"You told me '~A'~%\" msg))");
Package pkg = Packages.findPackage("CL-USER");
Symbol foo = pkg.findAccessibleSymbol("FOO");
Function fooFunction = (Function)foo.getSymbolFunction();
JavaObject parameter = new JavaObject("Lisp is fun!");
LispObject result = fooFunction.execute(parameter);
// How to get the "naked string value"?
System.out.prinln("The result was " + result.writeToString());
If one
## Interpreting a LispObject in Java
### LispObject as boolean
If the LispObject a generalized boolean values, one can use
getBooleanValue() to convert to Java:
### LispObject is a list
If LispObject is a list, it will have the type Cons. One can then use
the copyToArray[] to make things a bit more suitable for Java
iteration.
LispObject result = interpreter.eval("'(1 2 4 5)");
if (result instanceof Cons) {
LispObject array[] = ((Cons)result.copyToArray());
...
}
SLIME
=====
Author: Mark Evenson
Created: 16-MAR-2010
Modified: 16-MAR-2010
Author: Mark Evenson
Created: 16-MAR-2010
Modified: 18-MAR-2010
SLIME is divided conceptually in two parts: the "swank" server process
which runs in the native Lisp and the "slime" client process running
......@@ -24,12 +24,13 @@ packaging system (i.e. MacPorts on OSX) may have a version as well.
One first locates the SLIME directory on the filesystem. In the code
that follows, the SLIME top level directory is assumed to be
"~/work/slime", so adjust this value to your local value as you see
`"~/work/slime"`, so adjust this value to your local value as you see
fit.
Then one configures Emacs with the proper initialization hooks by
adding code something like the following to "~/.emacs":
:::common-lisp
(add-to-list 'load-path "~/work/slime")
(setq slime-lisp-implementations
'((abcl ("~/work/abcl/abcl"))
......@@ -39,28 +40,29 @@ adding code something like the following to "~/.emacs":
(slime-setup '(slime-fancy slime-asdf slime-banner))
One further need to customize the setting of
SLIME-LISP-IMPLEMENTATIONS to the location(s) of the Lisp(s) you wish to
`SLIME-LISP-IMPLEMENTATIONS` to the location(s) of the Lisp(s) you wish to
invoke via SLIME. The value is list of lists of the form
(SYMBOL ("/path/to/lisp"))
where SYMBOL is a mnemonic for the Lisp implementation, and the string
"/path/to/lisp" is the absolute path of the Lisp implementation that
`"/path/to/lisp"` is the absolute path of the Lisp implementation that
SLIME will associate with this symbol. In the example above, I have
defined three implementations, the main abcl implementation, a version
that corresponds to the latest version from SVN invoked by
"~/work/abcl.svn/abcl", and a version of SBCL.
`"~/work/abcl.svn/abcl"`, and a version of SBCL.
To start SLIME one simply issues M-x slime from Emacs. This will
To start SLIME one simply issues `M-x slime` from Emacs. This will
start the first entry in the SLIME-LISP-IMPLEMENTATIONS list. If you
wish to start a subsequent Lisp, prefix the invocation via M-u
(i.e. M-u M-x slime). This will present an interactive chooser over
all symbols contained in SLIME-LISP-IMPLEMENTATIONS.
wish to start a subsequent Lisp, prefix the Emacs invocation with a
negative argument (i.e. `C-- M-x slime`). This will present an
interactive chooser over all symbols contained in
`SLIME-LISP-IMPLEMENTATIONS`.
After you invoke SLIME, you'll see a buffer open up named
*inferior-lisp* where the Lisp image is started up, the required swank
`*inferior-lisp*` where the Lisp image is started up, the required swank
code is complied and then loaded, finally, you'll see the "flying
letters" resolving itself to a "CL-USER>" prompt with an inspiration
letters" resolving itself to a `"CL-USER>"` prompt with an inspiration
message in the minibuffer. Your initiation to SLIME has begun...
......@@ -71,16 +73,16 @@ connection to Emacs. The following code will both load and start the swank serv
from a Lisp image. One merely needs to change *SLIME-DIRECTORY* to
point to the top directory of the server process.
`
:::commmon-lisp
(defvar *slime-directory* #p"~/work/slime/") ;; Don't forget trailing slash
(load (merge-pathnames "swank-loader.lisp" *slime-directory*) :verbose t)
(swank-loader:init)
(swank:start-server "/tmp/swank.port") ;; remove if you don't want
;; swank to start listening for connections.
`
When this code finishes executing, an integer representing the port on
which the server starts will be written to '/tmp/swank.port' and also
returned as the result of evaluating SWANK:START-SERVER. One may
connect to this port via issuing M-x slime-connect in Emacs.
which the server starts will be written to `'/tmp/swank.port'` and also
returned as the result of evaluating `SWANK:START-SERVER`. One may
connect to this port via issuing `M-x slime-connect` in Emacs.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment