Python as a LISP
Project description
Lythp
It's Python turned into a LISP!
(def greet (name)
"""A function which greets someone.
>>> (greet "Jim")
Hello Jim!
"""
(print (+ "Hello " name "!"))
)
The goal is not to preserve LISP traditions, keywords, and features; rather, the goal is to have fun fitting Python into a LISP syntax in the most natural way I can find, using Python's built-in tokenizer.
Here is a slightly more interesting example:
# A global dict for caching function values:
(= _fib_cache (dict))
(def fib (n)
"""Returns the nth Fibonacci number.
Maintains a global cache of values, to avoid needless recalculation."""
(if
((< n 0) (raise (ValueError "Less than 0")))
((< n 2) n) # Base cases: 0, 1
((in _fib_cache n) ([n] _fib_cache))
(else
(= value (+ (fib (- n 1)) (fib (- n 2)) ))
(= [n] _fib_cache value)
value
)
)
)
# Print out the first 10 Fibonacci numbers:
(for x (range 10) (print (fib x)))
For more examples, see: examples
The interpreter
Currently, Lythp has its own runtime, instead of transpiling to Python.
For instance, variables are stored in a stack of contexts, i.e. a list of
dicts.
It would be nice to transpile to Python instead, either by using exec
or by generating bytecode directly.
In any case, make sure you're in a python3 virtual environment:
python3 -m venv venv
. venv/bin/activate
Then run files like so:
./lythp.py examples/fac.lsp
You can also run the interpreter in REPL mode like so:
./lythp.py
It may be helpful to install rlwrap to improve the REPL experience:
rlwrap ./lythp.py
You can run the unit tests like so:
pip install pytest pytest-cov
pytest
And run all example programs like so:
./run.sh
Syntax
The basic syntax is quite simple: the source code is chopped into tokens using Python's built-in tokenize module, and a tree structure representing the program is then built, with the following types of node:
- Names:
x
,None
,True
,,
,*
,==
- Literals:
100
,"Hello!"
,"""Docstrings too!"""
- Parentheses:
(...)
- Brackets:
[...]
- Braces:
{...}
NOTE: f-strings (e.g. f"Hello, {name}!"
) are not currently supported.
As usual in a LISP, parentheses represent statements and function calls. Here is the syntax of specific statements:
Built-ins and literals
The following values have the same syntax as in Python:
None
True
False
123
"hello"
r"(\n+)"
b"beep boop"
... # the "ellipsis" object
TODO: describe lists, dicts, sets, etc
Attribute and item lookup: .
, [...]
Basic usage:
# Python
obj.x
arr[i]
obj.x.y.z[n + 1]
# Lythp
(.x obj)
([i] arr)
(.x.y.z[+ n 1] obj)
Assignment: =
Basic usage:
# Python
x = 1
x += 1
# Lythp
(= x 1)
(+= x 1)
Assigning with setattr/setitem:
# Python
obj.x.y.z[n + 1] = value
# Lythp
(= .x.y.z[+ n 1] obj value)
NOTE: there is no equivalent of Python's destructuring (e.g. x, y = a, b
).
Function calls
Basic usage:
# Python
x + y
f(x, y, z=3)
obj.method(x)
# Lythp
(+ x y)
(f x y [z 3])
((.method obj) x)
Args and kwargs:
# Python
f(x, y, *args, z=3, **kwargs)
# Lythp
(f x y [*args] [z 3] [**kwargs])
Lambdas and function definitions: lambda
, def
Basic usage:
# Python
def f(x, y, z=3): ...etc...
f = lambda x: x + 1
# Lythp
(def f (x y [z 3]) ...etc...)
(= f (lambda (x) (+ x 1)))
NOTE: while Python only allows a single expression per lambda, Lythp allows statements, like a regular function definition.
Args and kwargs:
# Python
def f(x, /. y, *args, z=3, **kwargs): ...etc...
# Lythp
(def f (x [/] y [*args] [z 3] [**kwargs]) ...etc...)
Classes
# Python
class A(B, C):
x = 3
def __init__(self, value):
...etc...
a = A(value)
# Lythp
(class A (B C)
(= x 3)
(def __init__ (self value)
...etc...
)
)
(= a (A value))
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.