207 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			HTML
		
	
	
	
			
		
		
	
	
			207 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			HTML
		
	
	
	
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
 | 
						|
   "http://www.w3.org/TR/html4/loose.dtd">
 | 
						|
<html>
 | 
						|
<head>
 | 
						|
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" >
 | 
						|
<title>femtoLisp</title>
 | 
						|
</head>
 | 
						|
<body>
 | 
						|
<h1>femtoLisp</h1>
 | 
						|
<hr>
 | 
						|
femtoLisp is an elegant Lisp implementation. Its goal is to be a
 | 
						|
reasonably efficient and capable interpreter with the shortest, simplest
 | 
						|
code possible. As its name implies, it is small (10<sup>-15</sup>).
 | 
						|
Right now it is just 1000 lines of C (give or take). It would make a great
 | 
						|
teaching example, or a useful system anywhere a very small Lisp is wanted.
 | 
						|
It is also a useful basis for developing other interpreters or related
 | 
						|
languages.
 | 
						|
 | 
						|
 | 
						|
<h2>The language implemented</h2>
 | 
						|
 | 
						|
femtoLisp tries to be a generic, simple Lisp dialect, influenced by McCarthy's
 | 
						|
original.
 | 
						|
 | 
						|
<ul>
 | 
						|
<li>Types: cons, symbol, 30-bit integer, builtin function
 | 
						|
<li>Self-evaluating lambda, macro, and label forms
 | 
						|
<li>Full Common Lisp-style macros
 | 
						|
<li>Case-sensitive symbol names
 | 
						|
<li>Scheme-style evaluation rule where any expression may appear in head
 | 
						|
    position as long as it evaluates to a callable
 | 
						|
<li>Scheme-style formal argument lists (dotted lists for varargs)
 | 
						|
<li>Transparent closure representation <tt>(lambda args body . env)</tt>
 | 
						|
<li>A lambda body may contain only one form. Use explicit <tt>progn</tt> for
 | 
						|
    multiple forms. Included macros, however, allow <tt>defun</tt>,
 | 
						|
    <tt>let</tt>, etc. to accept multiple body forms.
 | 
						|
<li>Builtin function names are constants and cannot be redefined.
 | 
						|
<li>Symbols have one binding, as in Scheme.
 | 
						|
</ul>
 | 
						|
<b>Builtin special forms:</b><br>
 | 
						|
<tt>quote, cond, if, and, or, lambda, macro, label, while, progn, prog1</tt>
 | 
						|
<p>
 | 
						|
<b>Builtin functions:</b><br>
 | 
						|
<tt>eq, atom, not, symbolp, numberp, boundp, cons, car, cdr,
 | 
						|
    read, eval, print, load, set, 
 | 
						|
    +, -, *, /, <, apply, rplaca, rplacd</tt>
 | 
						|
<p>
 | 
						|
<b>Included library functions and macros:</b><br>
 | 
						|
<tt>
 | 
						|
setq, setf, defmacro, defun, define, let, let*, labels, dotimes,
 | 
						|
macroexpand-1, macroexpand, backquote,
 | 
						|
 | 
						|
null, consp, builtinp, self-evaluating-p, listp, eql, equal, every, any,
 | 
						|
when, unless,
 | 
						|
 | 
						|
=, !=, >, <=, >=, compare, mod, abs, identity,
 | 
						|
 | 
						|
list, list*, length, last, nthcdr, lastcdr, list-ref, reverse, nreverse,
 | 
						|
assoc, member, append, nconc, copy-list, copy-tree, revappend, nreconc,
 | 
						|
 | 
						|
mapcar, filter, reduce, map-int,
 | 
						|
 | 
						|
symbol-plist, set-symbol-plist, put, get
 | 
						|
</tt>
 | 
						|
<p>
 | 
						|
<a href="system.lsp">system.lsp</a>
 | 
						|
 | 
						|
 | 
						|
<h2>The implementation</h2>
 | 
						|
 | 
						|
<ul>
 | 
						|
<li>Compacting copying garbage collector (<tt>O(1)</tt> in number of dead
 | 
						|
    objects)
 | 
						|
<li>Tagged pointers for efficient type checking and fast integers
 | 
						|
<li>Tail-recursive evaluator (tail calls use no stack space)
 | 
						|
<li>Minimally-consing <tt>apply</tt>
 | 
						|
<li>Interactive and script execution modes
 | 
						|
</ul>
 | 
						|
<p>
 | 
						|
<a href="lisp.c">lisp.c</a>
 | 
						|
 | 
						|
 | 
						|
<h2>femtoLisp2</h2>
 | 
						|
 | 
						|
This version includes robust reading and printing capabilities for
 | 
						|
circular structures and escaped symbol names. It adds read and print support
 | 
						|
for the Common Lisp read-macros <tt>#., #n#,</tt> and <tt>#n=</tt>.
 | 
						|
This allows builtins to be printed in a readable fashion as e.g.
 | 
						|
"<tt>#.eq</tt>".
 | 
						|
<p>
 | 
						|
The net result is that the interpreter achieves a highly satisfying property
 | 
						|
of closure under I/O. In other words, every representable Lisp value can be
 | 
						|
read and printed.
 | 
						|
<p>
 | 
						|
The traditional builtin <tt>label</tt> provides a purely-functional,
 | 
						|
non-circular way
 | 
						|
to write an anonymous recursive function. In femtoLisp2 you can
 | 
						|
achieve the same effect "manually" using nothing more than the reader:
 | 
						|
<br>
 | 
						|
<tt>#0=(lambda (x) (if (<= x 0) 1 (* x (#0# (- x 1)))))</tt>
 | 
						|
<p>
 | 
						|
femtoLisp2 has the following extra features and optimizations:
 | 
						|
<ul>
 | 
						|
<li> builtin functions <tt>error, exit,</tt> and <tt>princ</tt>
 | 
						|
<li> read support for backquote expressions
 | 
						|
<li> delayed environment consing
 | 
						|
<li> collective allocation of cons chains
 | 
						|
</ul>
 | 
						|
Those two optimizations are a Big Deal.
 | 
						|
<p>
 | 
						|
<a href="lisp2.c">lisp2.c</a> (uses <a href="flutils.c">flutils.c</a>)
 | 
						|
 | 
						|
 | 
						|
<h2>Performance</h2>
 | 
						|
 | 
						|
femtoLisp's performance is surprising. It is faster than most
 | 
						|
interpreters, and it is usually within a factor of 2-5 of compiled CLISP.
 | 
						|
 | 
						|
<table border=1>
 | 
						|
<tr>
 | 
						|
<td colspan=3><center><b>solve 5 queens problem 100x</b></center></td>
 | 
						|
<tr>
 | 
						|
<td>          <td>interpreted<td>compiled
 | 
						|
<tr>
 | 
						|
<td>CLISP     <td>4.02 sec   <td>0.68 sec
 | 
						|
<tr>
 | 
						|
<td>femtoLisp2<td>2.62 sec   <td>2.03 sec**
 | 
						|
<tr>
 | 
						|
<td>femtoLisp <td>6.02 sec   <td>5.64 sec**
 | 
						|
<tr>
 | 
						|
 | 
						|
<td colspan=3><center><b>recursive fib(34)</b></center></td>
 | 
						|
<tr>
 | 
						|
<td>          <td>interpreted<td>compiled
 | 
						|
<tr>
 | 
						|
<td>CLISP     <td>23.12 sec  <td>4.04 sec
 | 
						|
<tr>
 | 
						|
<td>femtoLisp2<td>4.71 sec   <td>n/a
 | 
						|
<tr>
 | 
						|
<td>femtoLisp <td>7.25 sec   <td>n/a
 | 
						|
<tr>
 | 
						|
 | 
						|
</table>
 | 
						|
** femtoLisp is not a compiler; in this context "compiled" means macros
 | 
						|
were pre-expanded.
 | 
						|
 | 
						|
 | 
						|
<h2>"Installation"</h2>
 | 
						|
 | 
						|
Here is a <a href="Makefile">Makefile</a>. Type <tt>make</tt> to build
 | 
						|
femtoLisp, <tt>make NAME=lisp2</tt> to build femtoLisp2.
 | 
						|
 | 
						|
 | 
						|
<h2>Tail recursion</h2>
 | 
						|
The femtoLisp evaluator is tail-recursive, following the idea in
 | 
						|
<a href="http://library.readscheme.org/servlets/cite.ss?pattern=Ste-76b">
 | 
						|
Lambda: The Ultimate Declarative</a> (should be required reading
 | 
						|
for all schoolchildren).
 | 
						|
<p>
 | 
						|
The femtoLisp source provides a simple concrete example showing why a function
 | 
						|
call is best viewed as a "renaming plus goto" rather than as a set of stack
 | 
						|
operations.
 | 
						|
<p>
 | 
						|
Here is the non-tail-recursive evaluator code to evaluate the body of a
 | 
						|
lambda (function), from <a href="lisp-nontail.c">lisp-nontail.c</a>:
 | 
						|
<pre>
 | 
						|
        PUSH(*lenv);    // preserve environment on stack
 | 
						|
        lenv = &Stack[SP-1];
 | 
						|
        v = eval(*body, lenv);
 | 
						|
        POP();
 | 
						|
        return v;
 | 
						|
</pre>
 | 
						|
(Note that because of the copying garbage collector, values are referenced
 | 
						|
through relocatable handles.)
 | 
						|
<p>
 | 
						|
Superficially, the call to <tt>eval</tt> is not a tail call, because work
 | 
						|
remains after it returns—namely, popping the environment off the stack.
 | 
						|
In other words, the control stack must be saved and restored to allow us to
 | 
						|
eventually restore the environment stack. However, restoring the environment
 | 
						|
stack is the <i>only</i> work to be done. Yet after this point the old
 | 
						|
environment is not used! So restoring the environment stack isn't
 | 
						|
necessary, therefore restoring the control stack isn't either.
 | 
						|
<p>
 | 
						|
This perspective makes proper tail recursion seem like more than an
 | 
						|
alternate design or optimization. It seems more correct.
 | 
						|
<p>
 | 
						|
Here is the corrected, tail-recursive version of the code:
 | 
						|
<pre>
 | 
						|
        SP = saveSP;    // restore stack completely
 | 
						|
        e = *body;      // reassign arguments
 | 
						|
        *penv = *lenv;
 | 
						|
        goto eval_top;
 | 
						|
</pre>
 | 
						|
<tt>penv</tt> is a pointer to the old environment, which we overwrite.
 | 
						|
(Notice that the variable <tt>penv</tt> does not even appear in the first code
 | 
						|
example.)
 | 
						|
So where is the environment saved and restored, if not here? The answer
 | 
						|
is that the burden is shifted to the caller; a caller to <tt>eval</tt> must
 | 
						|
expect that its environment might be overwritten, and take steps to save it
 | 
						|
if it will be needed further after the call. In practice, this means
 | 
						|
the environment is saved and restored around the evaluation of
 | 
						|
arguments, rather than around function applications. Hence <tt>(f x)</tt>
 | 
						|
might be a tail call to <tt>f</tt>, but <tt>(+ y (f x))</tt> is not.
 | 
						|
 | 
						|
</body>
 | 
						|
</html>
 |