Home

Awesome

miniMAL

A Delightfully Dimuntive Lisp.

The miniMAL core interpreter is implemented in less than 1024 bytes of JavaScript (uglify/regpack). There is also an implementation of miniMAL in python (1.1K as a pyz file) and ClojureScript (1.8K after minification).

The design of miniMAL started with mal (a Clojure-insipred pedagogical Lisp interpreter with implementations in over eighty languages). And in fact, in the miniMAL repository you can see the incremental steps to build up the interpreter just like for each of the mal implementations. However, the syntax and functionality of miniMAL is fairly different from mal so it is a standalone project.

Even though miniMAL is tiny it is actually a very powerful Lisp with advanced features including: higher-order functions, tail-call optimization, macros, JavaScript interop, and error-handling. miniMAL is powerful enough that it has been used to create a full implementation of mal.

Usage

You can try out miniMAL with the online REPL.

Install the miniMAL binary using npm:

sudo npm install -g minimal-lisp

There are several different ways to use and/or integrate miniMAL:

miniMAL
miniMAL hello.json
echo "#!/usr/bin/env miniMAL" > hello2.json
cat hello.json >> hello2.json
chmod +x hello2.json
./hello2.json

To use miniMAL as a library in another project, first install the module locally using npm:

sudo npm install minimal-lisp
var miniMAL = require('minimal-lisp'),
    m = miniMAL(global);
m.eval(["+", 2, 3]);
<script src="node_modules/minimal-lisp/js/web/miniMAL-min.js"></script>
<script>
var m = miniMAL();
m.eval(["+", 2, 3]);
</script>

Features and Examples

["+", 2, 3]
=>5
["if", ["=", 5, 5], 7, 8]
=>7
["+", 2, ["*", 3, 4]]
=>14
["def", "a_symbol", 3]
=>3
"a_symbol"
=>3
["*", "a_symbol", 6]
=>18
["`", "a quoted symbol is a string"]
=>"a quoted symbol is a string"
[ ["fn", ["a"], ["*", "a", "a"]], 8]
=>64
["def", "sqr", ["fn", ["a"], ["*", "a", "a"]]]
["sqr", 7]
=>49
["def", "drop1", ["fn", ["a", "&", "b"], "b"]]
["drop1", 1, 2, 3]
=>[2,3]
["def", "add5", ["let", ["x", 5], ["fn", ["a"], ["+", "x", "a"]]]]
["add5", 7]
=>12
"x"
=>__ERROR__
["def", "addX", ["fn", ["X"], ["fn", ["a"], ["+", "X", "a"]]]]
["def", "add9", ["addX", 9]]
["add9", 20]
=>29
["map", "add9", ["`", [2, 3, 4]]]
=>[11,12,13]
["def", "sum1", ["fn", ["n"], ["if", ["=", "n", 0], 0, ["+", "n", ["sum1", ["-", "n", 1]]]]]]
["sum1", 10000]
=>__ERROR: stack overflow__
["def", "sum2", ["fn", ["n", "a"], ["if", ["=", "n", 0], "a", ["sum2", ["-", "n", 1], ["+", "n", "a"]]]]]
["sum2", 10000, 0]
=>500500
["def", "randInt", ["fn", ["max"], ["parseInt", ["*", "max", [".", "Math", ["`", "random"]]]]]]
["randInt", 100]
=>16
["def", "rand-hsl", ["fn", [], ["+", ["+", ["`", "hsl("], ["randInt", 360]], ["`", ", 50%, 70%)"]]]]
["def", "set-style", ["fn", ["o", "k", "v"],  [".-",  [".-", "o", ["`", "style"]], "k", "v"]]]
["def", "by-tag", ["fn", ["tag"], [".", "document", ["`", "getElementsByTagName"], "tag"]]]
["set-style", [".-", ["by-tag", ["`", "body"]],0], ["`", "backgroundColor"], ["rand-hsl"]]
=>__background color set to random hsl value__

The following features are omitted from JS1K version of the implementation in order to make space for example code.

["try", "abc", ["catch", "exc", ["list", ["`", "exc was:"], "exc"]]]
=>["exc was:","abc not found"]
["try", ["throw", 123], ["catch", "exc", ["list", ["`", "exc was:"], "exc"]]]
=>["exc was:",123]
["try", ["throw", 123], ["catch", "exc", ["list", ["`", "exc was:"], "exc"]]]
=>["exc was:",123]
["def", "unless", ["~", ["fn", ["p", "a", "b"], ["list", ["`", "if"], "p", "b", "a"]]]]
["unless", false, 7, 8]
=>7
["unless", true, 7, 8]
=>8

Rationale

I originally started implementing a tiny Lisp interpreter as a quick hack to submit to the 2015 JS1K competition (demo 2209). However, I soon realized that I could fit far more functionality into 1024 bytes of JavaScript than I expected and so miniMAL was born as a "full-fledged" Lisp in its own right.

License

miniMAL is licensed under the MPL 2.0 (Mozilla Public License 2.0). See LICENSE for more details.