Jlisp 0.2 Overview
Jlisp is a small, fast, scheme-like LISP interpreter, based on code released by Gregory J. Chaitin, under the GNU General Public License. Jlisp 0.2 is a significant upgrade over previous versions.
Why Jlisp?
The primary goal of Jlisp is easy extensibility of the LISP interpreter through external Java and LISP libraries.
Download
The Jlisp download is a single zip file containing:
- A JAR file (jlisp.jar) containing all class binaries and optional LISP libraries.
- All Java source code for Jlisp
- Optional LISP libraries to extend the basic functionality of Jlisp and
- A copy of the GNU General Public License, under which Jlisp is released.
Installation
Simply put the JAR file (jlisp.jar) in your classpath. If your Jlisp program uses the optional LISP libraries, this JAR file is searched for them.
Java package overview
The base path is net.thinksquared.jlisp. The main Jlisp classes are:
Loader
- Used to run Jlisp as a standalone LISP interpreter.
Lisp
- The LISP interpreter.
LispThread
- Runs the LISP interpreter in a separate thread.
Library
- Interface you implement to extend functionality of Jlisp.
string
- Implements
Library to add basic string handling.
io
- Implements
Library to add basic I/O support.
math
- Implements
Library to add basic math functions.
hash
- Implements
Library to add hash table capabilities to Jlisp.
Tokenizer
- Used to parse a LISP input file.
Classes in the net.thinksquared.jlisp.test path are used to test various parts of Jlisp. They are not
intended to be part of the normal releases, and may be removed in the future.
LISP library overview
The Jlisp JAR file contains LISP libraries that you can "import" to extend the functionality of Jlisp.
You may view the source code for these LISP libraries within the "lisp" directory in the zip file. At present, the LISP libraries are:
common.lisp
- Commonly used LISP functions.
matrix.lisp
- Used to manipulate matrices in LISP.
Running Jlisp as a standalone LISP interpreter
You use the Loader class to run Jlisp in standalone mode:
- Write your LISP program with any text editor, saving it to a file, say "main.lisp"
- Use
java -classpath jlisp.jar net.thinksquared.jlisp.Loader main.lisp
Extending Jlisp with the built-in Java libraries
Simply use the import keyword with the built-in Java library name:
(import string)
define mylist ( a b c d )
display (cat mylist)
Extending Jlisp with LISP libraries
To use a LISP library, simply import with the LISP library file name:
For example, to use the in function in the common.lisp
library:
(import common.lisp)
define mylist ( a b c d )
display ( in b mylist )
Writing your own Java libraries for Jlisp
The Library interface contains 2 functions, register() and apply(), which you have to implement.
register() allows you to register new keywords with Jlisp, and when they are encountered in a LISP program, Jlisp calls your apply()
allowing you to dispatch the call to the appropriate handler in your Java library. You may define as many new keywords as you wish in this way.
Of course, you should avoid using built-in functions or keywords, because these may never be "overridden".
As an example, suppose you want to create a Java library function, callable from LISP, to print out a given S-expression to the screen.
Let's call the LISP keyword "print". So, (print (hello world)) would display "(hello world)" on screen.
The first step is to implement net.thinksquared.jlisp.Library in a class of our own (we'll call it "mylib", the convention being to use lowercase to name Java libraries that extend Jlisp):
package com.mycompany.lisp;
import java.util.Map;
import net.thinksquared.jlisp.*;
public class mylib implements Library{
Sexp PRINT;
public void register(Map registry, Lisp runtime){
PRINT = runtime.atom("print");
registry.put(PRINT,this);
}
public Sexp apply(Sexp function, Sexp args){
if(function == PRINT) System.out.print(args.toS());
return args;
}
}
The second step is to import our Java extension within our LISP programs. For example:
(import com.mycompany.lisp.mylib)
(print (hello world!))
NOTE: Notice how in apply() the arguments of the called LISP function
are presented as a Sexp. This class represents S-expressions, and lists
are represented as a binary tree, with Sexp.head() giving the first
element of the list and Sexp.tail() giving the rest of the list
(equivalent to car and cdr).
How to embed Jlisp in your Java projects
Once you know how to extend Jlisp functionality with either Java or LISP libraries, the problem
of embedding Jlisp within your Java project boils down to: (a) Controlling execution of LISP programs
from Java. (b) Allowing some communication between Java libraries.
(a) is easily accomplished using net.thinksquared.jlisp.LispThread.
The constructor for this class takes a single String argument, which is the LISP
program to be run. Upon instantiation, it parses the LISP program in a new thread. This thread
is "frozen" initially. You may "freeze" or "unfreeze" it using the Java functions freeze()
and unfreeze(). You may even freeze this thread from within LISP by calling (yield)
at any point of the program. Note that (yield) only works in LISP programs run by
net.thinksquared.jlisp.LispThread. As an example, consider:
package com.mycompany.lisp;
import net.thinksquared.jlisp.*;
public class MyClass{
String lispProgram = "loop 100000 display apply read";
public void runInteractiveLispInterpreter(){
LispThread lt = new LispThread(lispProgram);
lt.unfreeze(); //start the LISP interpreter
}
}
In this example, after the program starts, LISP commands typed in by the user are evaluated and
displayed on screen. If the user types in yield, then the interpreter grinds to a halt,
but does not exit.
As for (b), communication between Java libraries (needed sometimes to initialize a Java library with information obtainable only during runtime)
is done by calling Lisp.getLibrary(), usually in register(). This function gives a handle to the LISP runtime.
You call getLibrary() on this runtime, with the class name of the Java library you want. getLibrary()
returns an Object, which you must correctly cast to the right class.
As an example, suppose we wanted to extend our trivial "print" library in the earlier section, to include
a function to print to a HTML file, but this depended on the presence of another library called
com.mycompany.lisp.html. This might seem contrived, but it may happen in some projects. Suppose our new
LISP function is print-html. The way to do this is:
com.mycompany.lisp.html htmlPrinter = null;
Sexp PRINT = HTML_PRINT= null;
public void register(Map registry, Lisp runtime){
PRINT = runtime.atom("print");
registry.put(PRINT,this);
Object o = runtime.getLibrary("com.mycompany.lisp.html");
htmlPrinter = (o == null)? null:(com.mycompany.lisp.html) o;
if(htmlPrinter != null){
HTML_PRINT = runtime.atom("html-print");
registry.put(HTML_PRINT,this);
}
}
public Sexp apply(String function, Sexp args){
if(function == PRINT){
System.out.print(args.toS());
}else if(function == HTML_PRINT){
htmlPrinter.print(args);
}
return args;
}enquiries@thinksquared.net
Syntax Guide
Defining functions
Use the "define" keyword to define a function, or bind a name to a list:
define (add a b)
+ a b
display (add 2 3)
Jlisp supports flexible parameters
Use the "&rest" keyword to define a flexible parameter:
define (add-prefix prefix &rest)
(mapcar &rest 'lambda(x)(cat prefix x))
display (add-prefix ? a b c d e f)
returns (?a ?b ?c ?d ?e ?f)
The following sections are a compendium of LISP functions implemented so far, either in the Jlisp interpreter, "external" Java libraries or the optional LISP libraries.
Built-in functions and keywords:
' (quote)- Quotes lists within an S-expression
" (double quote)- Quotes S-expressions within an M-expression
-,+,*,>,<,<=,>=,=- Builtin arithmetic operators.
atomatom <s-expression> Tests if the S-expression is an atom
append- Appends a list to another:
append (a) (b) gives (a b)
apply- Evaluates an S-expression in the current environment (as opposed to
eval, which runs code in a new environment): apply '(display (hello world)) displays (hello world) on screen
car- Returns the first element of a list:
car (a b) returns a
cadr- Returns the second element of a list, and
nil if there is no such element.
caddr- Returns the third element of a list, and
nil if there is no such element.
cdr- Returns the list without the first element
cadr- Returns the second element of a list, and
nil if there is no such element.
cat- Concatenates S-expressions to give an atom:
cat hello (world) gives the atom helloworld
cons- Concatenates two lists to give a third
define- Used to define new functions or global variables:
define message (hello world) binds the atom message to (hello world)
define (echo msg) display x defines a function, echo to display the S-expression bound to the argument msg
eval- Evaluates an S-expression in a new environment. (cf
apply)
fork- UNIMPLEMENTED! Starts a separate thread of execution. Proposed syntax:
(fork <S-expression>)
if- Conditional.
if <cond> <then> <else>
import- A keyword, used to extend Jlisp through the use of external Java libraries or LISP libraries. See the sections above for more detail.
lambda- Denotes the lambda function.
(lambda (<arguments>)(<function-body>))(<values>)
let- Syntactic sugar for variable assignment.
let x v e is "let x be v in expression e"
length- Counts the number of elements in a lispt
loop- Iteration:
loop <count> <loop-body> . You may abort the loop using the keyword break
prog- Executes M-expressions
prog (<m-expression>)
read- Reads in an S-expression from the standard input (usually keyboard input).
read terminates and returns as soon as it encounters a line feed \n ("Enter")
setq- Sets the value of a variable. If the variable doesn't exist, a global one is created; if it exists, its value is set; if within a
let, the temporary variable is set. (setq <variable> <value>)
setf- Sets the value of an evaluated variable. This differes from setq in that <variable> is also evaluated, before it is set.
size- Gives the number of characters in the string representation of a given S-expression.
size <s-expression>
try- Attempts to
eval a given S-expression, but gives up after a "time" limit (strictly speaking, a limit on the number of evaluations performed recursively in order to evaluate the given S-expression).
try <time-limit> <s-expression>. To indicate no time limit, pass the atom no_time_limit as a time limit.
The return value is a pair, (success/failure <s-expression>). In the case of a failure, the returned S-expression is out_of_time
This try is significantly different from Chaitin's original try.
Object Orientation:
defclass- Denotes a class definition:
(defclass <classname> [<super-classname>] <function and variable declarations> )
def- Used like
define, but only within a class, in order to define variables and functions.
call- Calls a function on an object or class:
(call <handler> <function-name> <parameters>). <handler> can be a reference to an object, or a class name (you're calling a static function) or the current object, via the this keyword.
this- Used to refer to the current object, eg:
(call this <function-name> <parameters>)
set- Sets a value on an object. May ONLY be use within a class definition.
(set <variable-name> <value>)
get- Gets the value of a variable in an object. May ONLY be use within a class definition.
(get <variable-name>)
new- Instantiates an object:
(new <class-name>)
undefined- A return value indicating a function call failure.
String functions with (import string) :
Use (import string)
cat- Concatenates the arguments to form a new string.
(cat <argument>+)
catx- Concatenates all arguments with the first one as a delimiter.
(catx <delimiter> <argument>+)
match?- Returns "true" if the given regular expression matches the given string.
(match? <string> <pattern>)
subst- Substitutes one string into another, whenever matched by the given regular expression.
(subst <string> <pattern> <replace-with>)
split- Splits a string using a given regular exression.
(split <string> <pattern>) Returns a list.
camel- Converts to "camel case".
(camel 'my_doc) returns MyDoc
jcase- Converts to "Java" case.
(jcase 'ab_c12e) returns abC12e
ucase- Converts to upper case.
(ucase <string>)
read-file- Reads the given file into a single atom. The file is searched for in the given directory as well as on the current classpath.
(read-file <filename>)
intern- Returns a canonical representation of the string. To enable Jlisp to use the Java garbage collector, all strings returned from this string library are non-canonical, meaning that they may not be used in "apply" for metaprogramming. "intern" makes them canonical (but not garbage collectable).
(intern <string>)
I/O functions with (import io) :
exists?- Searches for a file, both on the given path and on all JAR files in the classpath. Returns "true" is it succeeds, "false" otherwise.
(exists? <filename>)
open- Opens a new file:
(open <filename>). Here, <filename> may be any S-expression.
close- Closes a file:
(close <filename>)
print- Prints the String representation of the given S-expression to the file. Outer brackets are always removed:
(print <filename> <s-expression>)
println- Similar to
print, but outputs a UNIX linefeed (\n) at the end.
Math functions with (import math) :
^- Exponentiation:
^ 3 2 gives 9
abs- Absolute value of the argument.
asin- Gives the arc sine of the argument.
cos,sin- Cosine and sine of their respective arguments.
degrees- Converts radians to degrees.
div- Division. Note that division is NOT a built-in function.
ln- Gives the logarithm of its argument.
max- Gives the maximum of two arguments.
mod(mod a b) gives the remainder after a is divided by b.
min- Gives the minimum of two arguments.
pi- A constant representing pi.
radians- Converts degrees to radians
rani(rani N) gives a random integer between 0 and N.
Hash table functions with (import hash) :
hput- Creates a hash table, or puts elements into an existing hashtable. The two forms are:
( hput <first element> ) to create a hash table with the given first element. ( hput <hashtable> <an-element> ) to add an element into an existing hash table
hget- Gets a value from a hash table given a key:
( hget <hashtable> <key> )
hdel- Deletes an element from a hash table given a key:
( hdel <hashtable> <key> )
hempty?- Checks if a hashtable is empty:
( hempty? <hashtable> ).
hfree- Releases resources associated with a hash table:
( hfree <hashtable> ).
Common LISP functions with (import common.lisp):
and- Logical AND of 2 S-expressions.
or- Logical OR of 2 S-expressions.
xor- Logical XOR of 2 S-expressions.
in- Returns true if an element is in a list.
assoc- Returns an element in a LISP "hashmap".
(assoc lst key return-pair). If the "hashmap" is my-map := ((company-name Thinksquared) (location Singapore) consultancy) then (assoc my-map location) returns Singapore but (assoc my-map location true) returns (location Singapore)
put- Puts a value into a list "hashmap".
(put my-map lst key value)
del- Removes an entry from a list "hashmap".
(del my-map lst key)
compact- Removes all "nil" entries in a list.
(compact lst)
mapcar- Applies a function to a every argument in a list.
(mapcar lst function) The function must be wrapped in a lambda. For example: (mapcar (1 2 3) 'lambda(x) * 2 x) returns (2 4 6). If you have defined a function, say (my-func a b), where a and b are parameters then use: (mapcar lst 'lambda(x)(my-func x b)). The value of the argument "a" is bound to elements of the given list, whereas the value of "b" is depends on the current context:
(import common.lisp)
define (add a b)
+ a b
let const 7
display (mapcar (1 2 3) 'lambda(x)(add x const))
displays (8 9 10)
list- Returns a list of its arguments
catsp- Returns an atom formed out of concatenating all arguments with a "space" delimiter.
Matrix functions with (import matrix.lisp):
equal?- Checks if two matrices are equal.
add- Adds two matrices
subtract- Subtracts one matrix from another.
multiply- Returns the multiplication of two matrices.
hadamard- Returns the hadamard multiplication (elementwise multiplication) of two matrices.
trace- Returns the trace of a matrix
transpose- Returns the transpose of a matrix.
Participate!
If you'd like to help or if you've spotted a bug, just drop us a note at: dev@thinksquared.net. We welcome your participation!
|