Home

Awesome

esp-lisp

BETA: A small fast lisp interpeter for a ESP8266 as alternative to lua on the nodemcu.

why

Who doesn't need a lisp? I always wanted my own "lispmachine" anyway ;) It's ideally small and interactive for experimentation, very little typing, compared to lua for example, easy to extend with FFI to add new functions.

what is lisp?

Lisp is syntactically a very simple language. It has in principle just function calls:

lisp> (+ 3 4)
=> 7

I'll teach you lisp in 15 minutes ;-) - teach you lisp video, from my talk at Chiang Mai Makerfaire, Thailand 2016 slides.

design goals

internals presentations

You can learn some about this lisp internals in my talk and the discussion at Hackware: Holiday 2015 Special, which took place in Singapore end of last year.

A more detailed presentation takes place/took place in Hong Kong Functional Programming meetup at HKU. slides, video

status

The core is about 1000 lines of code. Total around 4000 with extentions functions and xml/web server support.

It is now at it's 4th release relase, it now even got a full screen editor functions (ala emacs), got a editable readline interface! See the docs in wiki for simple examples.

The esp-lisp has been used to build some smaller game devices by other persons, still things being added.

features

performace

The esp-lisp is interpreted, to keep the code small and simple. Compared to lua from the NodeMCU it's about 2x slower, but lua is compiled and uses lots of memory for functions. Lua uses about 600 bytes for a simple function, whereas esp-lisp about 100 bytes for a function printing hello.

Comparing it to guile by running (fib 33) takes about 10s on guile, but only 5s on esp-lisp!

advanced terminal interaction

In the read-eval loop:

Debugging

When your code is running you can press CTRL-T to get a compressed stackview.

lisp> (define (fib n) (if (< n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))
#fib
lisp> (fib 30)
   each time you press CTRL-T it'll print the stack: 21 nested fib
[% 0:07 load: 0.99  @ 21 #fib]
[% 0:07 load: 0.99  @ 21 #fib]
[% 0:07 load: 0.99  @ 20 #fib]
[% 0:07 load: 0.98  @ 23 #fib]
[% 0:07 load: 0.98  @ 23 #fib]
[% 0:08 load: 0.99  @ 19 #fib]
[% 0:08 load: 0.99  @ 20 #fib]
[% 0:08 load: 0.99  @ 23 #fib]
[% 0:08 load: 0.98  @ 20 #fib]
[% 0:08 load: 0.98  @ 21 #fib]
[% 0:08 load: 0.97  @ 19 #fib]
[% 0:08 load: 0.96  @ 22 #fib]
[% 0:11 load: 0.99  @ 20 #fib]
2178309
lisp> 

CTRL-C to break the execution and do some debugging:

lisp> (define (fib n) (if (< n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))
(define (fib n) (if (< n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))
#fib
lisp> (fib 30)
(fib 30)

^C

 @ 22 #fib

(#< n 1) 
debug 22] 
---------
nil
debug 22] p
p
---------
  STACK:  @ 22 #fib
CURRENT: (#< n 1)
	ENV: ((n . 0) (nil))
debug 22] bt
bt
---------

   0 : (#fib 30) ENV: [#fib n=30]
   1 : (#fib (#- n 1)) ENV: [#fib n=29]
   2 : (#fib (#- n 1)) ENV: [#fib n=28]
   3 : (#fib (#- n 2)) ENV: [#fib n=26]
   4 : (#fib (#- n 1)) ENV: [#fib n=25]
   5 : (#fib (#- n 2)) ENV: [#fib n=23]
   6 : (#fib (#- n 1)) ENV: [#fib n=22]
   7 : (#fib (#- n 2)) ENV: [#fib n=20]
   8 : (#fib (#- n 1)) ENV: [#fib n=19]
   9 : (#fib (#- n 2)) ENV: [#fib n=17]
  10 : (#fib (#- n 2)) ENV: [#fib n=15]
  11 : (#fib (#- n 1)) ENV: [#fib n=14]
  12 : (#fib (#- n 2)) ENV: [#fib n=12]
  13 : (#fib (#- n 1)) ENV: [#fib n=11]
  14 : (#fib (#- n 1)) ENV: [#fib n=10]
  15 : (#fib (#- n 1)) ENV: [#fib n=9]
  16 : (#fib (#- n 1)) ENV: [#fib n=8]
  17 : (#fib (#- n 1)) ENV: [#fib n=7]
  18 : (#fib (#- n 2)) ENV: [#fib n=5]
  19 : (#fib (#- n 1)) ENV: [#fib n=4]
  20 : (#fib (#- n 1)) ENV: [#fib n=3]
  21 : (#fib (#- n 1)) ENV: [#fib n=2]
==>  22 : (#fib (#- n 2)) ENV: [#fib n=0]
  23 : (#< n 1) ENV: [#< ... ] 
debug 22] h
h
---------
Debug help: c(ontiue) q(uit) h(elp) p(rint env) u(p) d(own) b(ack)t(race) EXPR
debug 22] u
u
---------
  STACK:  @ 22 #fib
CURRENT: (#fib (#- n 2))
	ENV: ((n . 2) (nil))
debug 22] u
u
---------
  STACK:  @ 22 #fib
CURRENT: (#fib (#- n 2))
	ENV: ((n . 2) (nil))
debug 21] u
u
---------
  STACK:  @ 22 #fib
CURRENT: (#fib (#- n 1))
	ENV: ((n . 3) (nil))

Commands in debugger

Full screen emacs-style editing

Uses the imacs minimal editor implementation as a sub-project.

lisp> (define txt "foobar")
lisp> (set! txt (edit txt))
... edit in fullscreen! ...

(+ 3 4) ctrl-x ctrl-e  ==> 7

During evalation

how to build

In the example below I "assume" you have a directory GIT where you checkout all your projects.

I want to run it on my linux/cygwin, I have GCC

It'll compile and run it for you, you'll have a lisp prompt.

lisp> help
...

try out the commands, it also shows what functions/symbols there are

lisp> (+ 3 4)
7

lisp> (setq fac (lambda (n) (if (= n 0) 1 (* n (fac (- n 1))))))
#func[]

lisp> (fac 6)
720

build embeddable image and flash it to a nodemcu/EPS8266 device

In a directory:

These will now be in the same directory.

Create a file path-add-esp (one level up from esp-lisp). It should contain something like:

      unix:GIT> cat path-add-esp
      export PATH=/home/USER/...GIT/esp-open-sdk/xtensa-lx106-elf/bin:$PATH
      unix:GIT> source path-add-esp

That will compile and run it on your desktop.

Flashes it to your esp-8266 device.

To connect to it and run it. Requires screen.

Memory optimizations

TODO

Readings idea for embedded ROM lisps

Optimize more for storage/simplify and avoid using malloc here are some ideas from elsewhere.

Related stuff

Flash based filesystems or flash database log systems