-
Notifications
You must be signed in to change notification settings - Fork 32
The goal of miniMAL is to be a Lisp implementation that is as small as possible without sacrificing powerful features like macros, closures, TCO and host language interop.
Lisp-2 languages (e.g. Common Lisp) is one in which data variable names and function names live in separate namespaces. In Lisp-1 languages (e.g. Scheme and Clojure), data variable names and function names live in the same namespace. The term Lisp-0 was invented to describe languages (e.g. miniMAL) where strings live in the same namespace as functions and data variables. In other words a string such as "foo"
is actually evaluated as a symbol. To prevent evaluation of strings as symbols, the string must be quoted: ['`', "foo"]
.
Forgetting to quote strings is a common mistake when writing miniMAL programs. For example, the following program will result in an error (unless toString happens to be defined in the environment):
[".", 123, "toString"]
To call the toString() method of the number, the method name must be quoted to prevent symbol evaluation:
[".", 123, ["`", "toString"]]
When creating an instance of miniMAL, you can pass it an environment. You need to do this when using Node.js, where examples would be to pass the global
object or just an empty object, {}
. In a browser, the global environment is inserted by default, but you can override this default behavior by passing some object, for instance miniMAL({})
.
Due to the fact that miniMAL has the "js"
function (linked to the native eval
in JS), the global environment is accessible through it. That is, you can execute any JavaScript in the global scope through a string:
['js', ['`', 'Date.now() + Math.random()']];
If you wish to control the environment that miniMAL will run in, to for instance limit access possibilities into any and all of your other JS code, but at the same time communicate both to and from miniMAL, consider something like this:
const miniMAL = require('minimal-lisp');
const _ = require('lodash');
const vars = { foo: 3, bar: { baz: 7 } };
const m = miniMAL({
Date,
Math,
get: k => _.get(vars, k),
set: (k, v) => _.set(vars, k, v)
});
m.js = function() {
throw new Error('Permission denied');
};
m.eval(['js', ['`', 'Date.now() + Math.random()']]); // Permission denied
m.eval(['get', ['`', 'bar.baz']]); // 7
m.eval(['set', ['`', 'bar.baz'], 8]); // { foo: 3, bar: { baz: 8 } }