Write lisp in python
Tags: python, lisp
Published: 2019-07-04 12:29:32

I recently thought about how I can merge lisp programming language and python, and tried to write a lisp syntax like compiler in python. Some thing that can convert the following lisp code:

```lisp
(repeat 2 (write-ln (+ 10 20)))
```

to this tree:

```txt
word: "repeat"
    int: 2
    word: "write-ln"
        symbol: +
            int: 10
            int: 20
```

And then write an executor for executing tree.

But I figured out that I don't need to write a lisp syntax parser. Instead I can use python builtins types directly.

for example:
```python
('repeat', ('print', ('add', 10, 20)))
```
And then the result of my thoughts was that I wrote this simple `python builtins types compiler`. (`pbtc` for shorthand)

```python
#!/usr/bin/python3

# filename: main.py

import itertools
import sys

def add(*args):
    result = args[0] if len(args) != 0 else None
    for arg in args[1:]:
        result += arg
    return result

def sub(*args):
    result = args[0] if len(args) != 0 else None
    for arg in args[1:]:
        result -= arg
    return result

def mul(*args):
    result = args[0] if len(args) != 0 else None
    for arg in args[1:]:
        result *= arg
    return result

def div(*args):
    result = args[0] if len(args) != 0 else None
    for arg in args[1:]:
        result /= arg
    return result

def true_div(*args):
    result = args[0] if len(args) != 0 else None
    for arg in args[1:]:
        result //= arg
    return result

def join(sep, args):
    return sep.join(args)

tuple_compilers = {
    'add': add,
    'sub': sub,
    'mul': mul,
    'div': div,
    'tdiv': true_div,
    'print': lambda *args: print(*args),

    'join': join,
    'repeat': itertools.repeat,
}

def compile_tuple(tree, memory, compile):
    if len(tree) == 0:
        raise ValueError('invalid tuple length: {}'.format(len(tree)))
    if not isinstance(tree[0], str):
        raise ValueError('invalid tuple instruction: {}'.format(tree[0]))
    if tree[0] not in tuple_compilers:
        raise ValueError('unknown tuple instruction: {}'.format(tree[0]))
    args = []
    for node in tree[1:]:
        args.append(compile(node, memory))
    return tuple_compilers[tree[0]](*args)

compilers = {
    tuple: compile_tuple,
    (list, dict, str, int, float, bytes): 
        lambda tree, memory, compile: tree,
}

def compile_tree(tree, compilers, memory = None):
    self = lambda tree, memory: compile_tree(tree, compilers, memory)
    for _type, compile in compilers.items():
        if isinstance(tree, _type):
            return compile(tree, memory, self)
    raise TypeError(type(tree).__name__)

with open(sys.argv[1]) as infile:
    globals_dict = {}
    exec('grammar = ' + infile.read())

compile_tree(grammar, compilers)
```

it's not safe, it's slow, but works.

__testing:__

```python

# filename: lispy

('print', 
    ('join', ' ', ('repeat', ('join', ' ', ['hi', 'bye']), 10))
    )
```

__`$./main.py lispy`:__
```txt
hi bye hi bye hi bye hi bye hi bye hi bye hi bye hi bye hi bye hi bye
```

__It's fun...__

Maybe later, I wrote a more complete version of this and publish it somewhere, as open source.