Skip to main content

Python's Lisp

Common Lisp programming language

The best python lisp implementation!

Implemented entirely in python 3.9 and above.

Embed lisp in python code and python in lisp code; or interact with lisp in the included repl.

Lisp Documentation

Goals: Implement a substantial subset of the features of common lisp.

Features

  • Full macro system: defmacro, macroexpand, and a runtime library written in Lisp
  • Complete lambda lists: &optional, &rest, &key, and &aux for functions and macros
  • Closures, tail call optimization, and fully implemented continuations
  • Condition system: handler-case, signal, make-condition
  • Multiple return values: values, multiple-value-bind
  • Embed Lisp in Python or call Python from Lisp
  • Full interactive REPL with persistent history and session logging

Design Features

  • Implemented from scratch with no external dependencies
  • Extensible: add new primitives or Lisp definitions without modifying the interpreter
  • Complete lexical analyzer and LL(1) recursive descent parser
  • Full test suite

A Taste of the Language

Closures and higher-order functions:

      ; make-adder returns a closure over n
      (defun make-adder (n)
        (lambda (x) (+ x n)))

      (setf add10 (make-adder 10))
      (mapcar add10 '(1 2 3 4 5))   ;==> (11 12 13 14 15)

      ; filter and transform in one pipeline
      (mapcar (lambda (x) (* x x))
              (remove-if 'oddp '(1 2 3 4 5 6)))  ;==> (4 16 36)

Macros that write code:

      ; swap! expands to a let at compile time -- no helper function needed
      (defmacro swap! (a b)
        `(let ((tmp ,a))
           (setf ,a ,b)
           (setf ,b tmp)))

      (setf x 1  y 100)
      (swap! x y)
      x   ;==> 100
      y   ;==> 1

Structs with generated constructors, predicates, and accessors:

      (defstruct point x y)

      (setf p (make-point :x 3 :y 4))
      (point-p p)              ;==> T
      (point-x p)              ;==> 3
      (setf (point-x p) 10)
      (list (point-x p) (point-y p))   ;==> (10 4)

CLI USAGE

Run the repl.

      > python3 -m pythonslisp

Execute a lisp source file.

      > python3 -m pythonslisp <lispSourceFile.lisp>

For various information requests there are.

      > python3 -m pythonslisp (-h|--help|-v|--version)

Note that the repl provides access to two separate online documentation systems. There is the listener command help system accessible by typing ']help' in the repl. Then there is the Lisp online help system (LOHS) accessible by evaluating the sexpression '(help)'.

These two help systems document access to different things. The listener command help system provides documentation for commands recognized by the listener from inside the repl to control the listener in various ways such as working with session logs. The LOHS provides access to documentation for all lisp callables (primitives, functions and macros) as well as to various help topics of interest to the lisp programmer.

The LOHS is dynamic. As the user defines new functions and macros their documentation becomes available in the help system. Specifically the documentation system will display a "function header" which includes the function name and its lambda list (formal parameter list). This will be followed by any text in the documentation string coded by the lisp programmer.

If you want a lisp startup script to run whenever the interpreter is initialized/reinitialized you can add .pythonslisp_rc to your home directory.

API: Using Lisp as a Package

The easiest way to use the package is to import and instantiate an Interpreter. The constructor loads the full runtime automatically. Use the interpreter's eval() functions to call into Lisp. Everything is persistent across calls so you can mix and match which eval function you use at any given time. To reset the interpreter to a clean state, simply instantiate a new one.

The example below defines a fibonacci function in Lisp, then calls it from Python and prints the results.

      from pythonslisp.Interpreter import Interpreter
      from pythonslisp.AST import prettyPrintSExpr

      interp = Interpreter( )
      interp.rawEval( '''(defun fibo (num)
                             (if (< num 2)
                                 1
                                 (+ (fibo (- num 1))
                                    (fibo (- num 2)))))''' )

      def fibo_wrapper( nth ):
         return interp.rawEval(f'(fibo {nth})')

      print( fibo_wrapper(10) )
      print( prettyPrintSExpr(fibo_wrapper(15)) )

If the lisp code returns an int,float,fraction,string,list or dict then it just returns the python versions of those things. Calls to rawEval will return the AST of an sexpression. A subsequent call to prettyPrintSExpr(ast) will convert the complex structure to a python string representation.

The interpreter's eval functions can handle any number of lisp expressions in the string argument. The return value is always the result of evaluating the last expression in the string.

Calling python from lisp is just as easy. Just call the python lisp primitive and pass it some python code in a lisp string.

      # Simply call python
      interp.rawEval( '(python "3 + 4")' )

      # Passing values from lisp
      interp.rawEval( '(setf num 3)' )
      interp.rawEval( '(python (string num " + 4"))' )

Primarily your program will interact with pythonslisp.AST, pythonslisp.Interpreter, and pythonslisp.Parser. Methods prefixed by _ are private implementation details subject to change; all other methods and attributes form the public interface.

eval() functions in the interpreter that include the label 'instrumented' are designed for testing the performance of the interpreter. These special versions of eval() return a tuple of three values: return value, parse time in seconds, evaluation time in seconds. They are used by the listener's repl to report performance characteristics during interactive sessions.

To extend or modify the interpreter, evaluate '(help "MODIFYING-DOC")' in the REPL.

Metadata

Release files for pythonslisp 0.40.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pythonslisp 0.40.1
File Size Uploaded
pythonslisp-0.40.1.tar.gz 133.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pythonslisp 0.40.1
File Interpreter ABI Platform
pythonslisp-0.40.1-py3-none-any.whl Python 3 none any Details

Total release size: 283.8 kB

Release files / pythonslisp-0.40.1.tar.gz

Download URL pythonslisp-0.40.1.tar.gz
Size 133.5 kB
Tags Source
SHA-256 checksum
How to use checksums
5874161160cdbfa478046e05b263d9fc1bced910df026dc538a1c88e175e715d
BLAKE2b-256 checksum
How to use checksums
9d09e8173a02e89e395551ec0d87dc9152a988076870ad0aac554b4a92b84a95
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.0

Release files / pythonslisp-0.40.1-py3-none-any.whl

Download URL pythonslisp-0.40.1-py3-none-any.whl
Size 150.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0ea07dd99c4e867e30e70e160509a1f1fd08c8c3054be5df22018ad7c1f61858
BLAKE2b-256 checksum
How to use checksums
c0ae83ea4553264c51924fec7e173ac18620495329133d012d0e4356dc7e4fdb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.9.0

Release history Release notifications | RSS feed

This release

0.40.1 This release

2 release files

0.39.7

2 release files

0.37.1

2 release files

0.36.0

2 release files

0.34.1

2 release files

0.34.0

2 release files

0.30.21

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page