"Why in the world would you implement lisp in Python?"

This is an excellent question. I didn't start out trying to write a lisp interpreter. I was aiming for a CLIPS-like syntax for a simple expert system I wanted to build into my system monitoring tool, Mom.v3. After a very short while it became apparent that some actual Lisp functionality would be useful, and before I knew it, I had a small but working lisp interpreter.

This software is wildly alpha at this stage. I'm making it public in response to some recent discussion on comp.lang.python about Lisp and Python. I thought some people might find it cute or interesting as a novelty.

Grab it here.

This is a toy. Don't use it in production code, I beg you. Use it learn lispy wisdom, or to stare in slack-jawed horror at my implementation of macros.

Some day I may write a more stable version. But probably not. It seems PyLisp has generated offspring, pyLisp-NG.

There are two much nicer implementations of Scheme in Python: Psyche and pyscheme. You should use those.

Peter Norvig has written some smaller, more elegant Scheme interpreters in Python: lispy, a simple interpreter in 90 lines of Python, and lispy2 which is more sophisticated.

To run, just do python You'll get the output from the many tests and then you'll be dropped into a very brittle lisp interpreter. I've not added any nice error handling, so the littlest thing will kill it right now. You may wish to take a look at the more extensive testing code. Just save it in test.l or something, then import it into the lisp interpreter (load "test.l")

Individualistic Lisp

PyLisp is a very idiosyncratic creation. For example, the booleans are *true* and *false* and I don't follow the convention that everything that isn't false is true common to many lisps. In fact, all booleans in PyLisp are based on fuzzy logic, so you can do trickier things. Macros are, quite strangely, first class objects in PyLisp which I suspect is novel:

;;; This is standard scheme-ish behavior.
((lambda (x) (* x x)) 2)

;;; This, I suspect is not.  I haven't checked yet.
(setq foo 22)
((macro (x) `(setq ,x (+ ,x 1))) foo)

The default values for *true* and *false* are 0.0 and 1.0 respectively. The conditional operators in PyLisp (if and cond) determine truth by comparing an expression's value against the variable *true-enough* and execute the truth consequent if the expression is equal to or greater than *true-enough*:

;;; all logic operations are fuzzy:
(logic 0.5) : (logic 0.5)
(logic 1.0) : *true*
(not (logic 0.0)) : *true*
(not (logic 0.35)) : (logic 0.65)
(and *true* (not *true*)) : *false*
(or *true* (not *true*)) : *true*
(setq *true* (logic .95)) : (logic 0.95)
(and *true* (not *true*)) : (logic 0.05)
(or *true* (not *true*)) : (logic 0.95)
(setq *true* (logic 1.0)) : *true*        ;; the default

The Future

I've just started in on the documentation. This'll change a lot in the near future as PyLisp itself settles.

I hope to hijack the code from Lutz's holmes expert system package and incorporate that into the base PyLisp interpreter, which'll meet my original goal for this library, a simple, portable expert system library for Python.

This software will change a lot in the future. The links to code above are actually links to the live source code, so it's entirely possible for you to grab a version mid-edit. Once the code has settled a bit and after the logic goo is added I'll package it up sensibly.

Go to my software page.
Send comments or problems to

William Annis.