Ben's blog

rambling through cyberspace

Today I'm going to write about another old project of mine where development stopped but where I'm still somewhat excited about the overall idea. It's my attempt at writing my own Lisp-like programming language called Nujel - a lightweight, dependency-free language implementation with both functional and object-oriented features.

What is Nujel?

Nujel is a Lisp-like language with a syntax that's heavily inspired by Scheme and Clojure. It supports multiple paradigms including functional and object-oriented programming, allowing the programmer to choose whichever style better suits the project at hand.

Nujel is a Lisp

Nujel uses S-Expressions for its syntax making it homoiconic, it also has a powerful macro system and most of the language is actually built up using the macro system from a bunch of primitives. These primitives in turn are created by a compiler written in Nujel which expands these macros which in turn gets compiled into bytecode instructions for a custom VM that is rather optimized, even surpassing some simpler JIT's in a couple of benchmarks. Of course proper JIT's or other highly optimized runtimes are much faster.

Nujel is functional

Functions are first-class values in Nujel allowing you to use them like any other value. Additionally there are many functional iteration primitives like map or fold. It also prominently features Cons cells as an immutable data structure. Although it does allow for mutability if it makes things easier/faster.

Nujel is object-oriented

This one might be somewhat controversial but after a while I've added an object system including inheritance to Nujel. It is prototype based rather than class based since that was most natural to implement in the runtime, and seemed to allow for the most flexibility, since we mainly needed to add method dispatch to the VM (which wasn't really necessary, but made things much faster). Apart from that it's mainly just some syntax sugar and some macros.

This got started mainly due to the IO subsystem which is somewhat object oriented but with a strange syntax and very slow method dispatch, though I suppose that a sufficiently smart compiler might hide that fact.

What makes Nujel special?

Nujel distinguishes itself from other embedded language implementations through its minimal footprint and impressive performance characteristics. The language runtime is designed with simplicity and embeddability as core principles.

Nujel is remarkably small

A static executable for Linux using musl weighs in at about 312KB in size, it's also rather fast, regularly beating Python/Ruby in some benchmarks of mine (also beats most embedded Lisp/Scheme implementations). The entire VM with bindings to standard unix/libc functions is about 6000 lines of C code.

Nujel has no dependencies

Nujel just requires a standard C library, nothing more, and even that could be worked around if one is willing to give up on math functions like sin/cos.

Additionally Nujel can be compiled into a single C Source and Header file pair, which should make it dead simple to use it in a C/C++ codebase, similar to the stb libs. This makes Nujel particularly suitable for embedding in resource-constrained environments or as a scripting language for applications.

Future Nujel Development

Once I have some more time I'd like to write the reader/parser in Nujel instead of C, while this should make things a little slower it would make the VM smaller and allow for reader macros, which right now would be very problematic to do.

The good thing is that since the VM supports a binary image format we don't need to read/parse any code to start the runtime, only when a user types in some code or we compile some new code do we need to use the reader.

Apart from that there are still some ideas about static typing, similar to TypeScript in that things default to an Any type, but you're free to specialize to make things more readable, we could then also use this in the compiler to emit type specific code, since there already are some type specific opcodes for things like addition/comparison operations (mainly for fast loops).

Conclusion

Nujel was a rather nice project where I've learned a ton, in the beginning it was actually a Scheme and I implemented most of R5RS including a number of SRFI's, but over time things shifted into a different direction. While I sadly am not using it that often I still really like it and think it helped me become a much better programmer in a lot of ways.

For developers interested in embedded language runtimes or Lisp implementations, Nujel offers a compelling example of how to create a powerful language with minimal overhead. Though development has slowed, the project remains open source and could serve as an excellent foundation for those looking to explore programming language implementation or add scripting capabilities to resource-constrained applications.