SLip — a Lisp system in your browser

I've put some love into my browser-based Lisp system. This project is so old that I could actually say it's new and nobody would know. I just let it rot for something like 12 years, but since I revamped Ymacs recently, I wanted to fix the “IDE”, which was broken; and then I got carried away.

Before reading further, if you are familiar with Common Lisp and Emacs/SLIME, check out this fine REPL! There you'll also find an info file with some information about the environment. And here's the old “crazy clock demo”, which I showcased in a video when I first announced this project.

This is a Lisp implementation (Common Lisp wannabe, but oh well, we're still far from that) that runs in a JavaScript VM. See the project page for more information about existing features. Recent work includes:

I think performance is OK, for interpreted “bytecode”. It can never get on par with JS code, but a lot can be achieved by making the VM more powerful. For example, the form (pop x) used to compile to 9 instructions (consing an environment frame in between). That felt like a good opportunity to add a dedicated instruction POPLIST, which can do the job without needing a new environment frame; besides a significant reduction in the object file size, it also executes much faster simply because it's a single instruction instead of 9 (and it doesn't cons!).

I've done a similar optimization for (or ...). It was previously a macro, which used let to save the current value (because OR has to return the first non-false value). I thought I could use a VM jump instruction which jumps if the top of the stack is non-NIL, but without popping the stack — so the value would just remain available for whatever code follows, and we don't need to cons an environment frame to save it. To make it work, OR is now a special case in the compiler, rather than just a macro — but it's worth the trouble!

On the other hand, even more could be achieved by making the compiler smarter (and the cases above could partly be improved by a smarter compiler, without the need for special instructions). But I don't feel smart enough to do it 😂. Here's one pattern that occurs a lot in macros:

(defmacro foo (thing ...)
  (let ((val (gensym)))
    `(let ((,val ,thing))  ;; conses a new env frame
       ;; ... do stuff with ,val
      )))

Many times “thing” will be a constant, or a symbol (a variable defined elsewhere), so we could optimize this by using “thing” directly, instead of giving it a new local name (of course, only if it's not reassigned). My compiler currently doesn't do this. Whether it would be faster, I can't be sure without testing — variable access is expensive too, because the VM needs to look up the right environment frame; the closer it is, the faster it is. So a nested environment is not necessarily a bad thing (it does add pressure on the garbage collector though). A VM with registers might work better, but again, I don't feel smart enough for this yet.

All in all, performance is good enough for having fun, which I hope you will! Feedback would be nice — please comment here or drop me an email if you do anything interesting with this!

Get in the REPL!

No comments yet. Wanna add one? Please?

Add your comment

Apr
12
2025

SLip — a Lisp system in your browser

  • Published: 2025-04-12
  • Modified: 2025-04-12 21:45
  • By: Mishoo
  • Tags: lisp, javascript, compiler
  • Comments: 0 (add)