Writing lisp code from Python

| categories: lisp, python | tags:

Some time ago I wrote about converting python data structures to lisp . I have expanded on that idea to writing lisp programs from Python! The newly expanded code that makes this possible can be found at https://github.com/jkitchin/pycse/blob/master/pycse/lisp.py .

Here are the simple data types known to pycse.lisp:

import pycse.lisp
import numpy as np

print("a string".lisp)
a = 5
b = 5.0
print(a.lisp)
print(b.lisp)
print([1, 2, 3].lisp)
print((1, 2, 3).lisp)
print({'a': 4}.lisp)
print(np.array([1, 2, 3]).lisp)
print(np.array([1.0, 2.0, 3.0]).lisp)
"a string"
5
5.0
(1 2 3)
(1 2 3)
(:a 4)
(1 2 3)
(1.0 2.0 3.0)

There are also some more complex types.

import pycse.lisp as pl

print(pl.Symbol('lambda'))
print(pl.Quote('lambda'))
print(pl.SharpQuote('lambda'))
print(pl.Cons("a", 5))
print(pl.Alist(["a", 2, "b", 5]))
print(pl.Vector([1, 2, 3]))

print(pl.Backquote([]))
print(pl.Comma([1, 2, 3]))
print(pl.Splice([1, 2, 3]))
lambda
'lambda
#'lambda
("a" . 5)
(("a" . 2) ("b" . 5))
[1 2 3]
`()
,(1 2 3)
,@(1 2 3)

You can nest these too.

import pycse.lisp as pl
print(pl.Quote(pl.Alist(["a", 2, "b", 5])))
print(pl.Backquote([pl.Symbol('+'), pl.Comma(pl.Symbol('b')), 5]))
'(("a" . 2) ("b" . 5))
`(+ ,b 5)

All that means we can use Python code to generate lisp programs. Here is an example where we make two sub-programs, and combine them into an overall program, then add one more subprogram to it. We wrap the results in an emacs-lisp block, then actually run the block!

import pycse.lisp as pl

p1 = [pl.Symbol('mapcar'),
      [pl.Symbol('lambda'),
       [pl.Symbol('x')],
       [pl.Symbol('*'),
        pl.Symbol('x'),
        pl.Symbol('x')]],
      pl.Quote([1, 2, 3, 4])]

p2 = [pl.Symbol('princ'), "Hello world"]

p = [pl.Symbol('list'), p1, p2]
p.append([pl.Symbol('+'), 5, 5])

print(p.lisp)
(list (mapcar (lambda (x) (* x x)) '(1 2 3 4)) (princ "Hello world") (+ 5 5))
(1 4 9 16) Hello world 10

Wow, it worked! Here is another example of setting up a macro and then running it.

import pycse.lisp as pl
s = pl.Symbol
bq = pl.Backquote
c = pl.Comma

p1 = [s('defmacro'), s('f'), [s('x')],
      "A docstring",
      bq([s('*'), c(s('x')), 5])]


p2 = [s('f'), 5]

print(p1.lisp)

print(p2.lisp)
(defmacro f (x) "A docstring" `(* ,x 5))
(f 5)
25

I am not too sure where this will be super useful, but it is an interesting proof of concept. I haven't tested this much beyond the original post and this one. Let me know if you find issues with it.

Copyright (C) 2016 by John Kitchin. See the License for information about copying.

org-mode source

Org-mode version = 8.2.10

Discuss on Twitter