Primitive library
The examples need the following primitives library. Save it as "library.lambda" and make sure it gets loaded before the actual program that requires them, for example:
cat library.lambda program.lambda | node lambda.js
All primitives receive the continuation k
as first argument.
Note that in some of them we need to use Execute
to invoke the
callback. That's because those call async API-s, and our Execute
loop will terminate before the result comes in. You must keep this in
mind when writing primitives — if you're not calling the
continuation immediately, use Execute
.
## primitives
## these assignments are required so that we create globals.
readFile = false;
readDir = false;
writeFile = false;
makeDir = false;
symlink = false;
readlink = false;
fstat = false;
lstat = false;
isFile = false;
isDirectory = false;
isSymlink = false;
pathJoin = false;
pathBasename = false;
pathDirname = false;
forEach = false;
length = false;
arrayRef = false;
arraySet = false;
stringStartsWith = false;
parallel = false;
cons = false;
isCons = false;
car = false;
cdr = false;
NIL = false;
setCar = false;
setCdr = false;
RESET = false;
SHIFT = false;
js:raw "(function(){
// filesystem
var fs = require('fs');
var path = require('path');
readFile = function(k, filename) {
fs.readFile(filename, function(err, data){
if (err) throw new Error(err);
Execute(k, [ data ]);
});
};
readDir = function(k, dirname) {
fs.readdir(dirname, function(err, files){
if (err) throw new Error(err);
Execute(k, [ files ]);
});
};
writeFile = function(k, filename, data) {
fs.writeFile(filename, data, function(err){
if (err) throw new Error(err);
Execute(k, [ false ]);
});
};
makeDir = function(k, dirname) {
fs.mkdir(dirname, function(err){
if (err) throw new Error(err);
Execute(k, [ false ]);
});
};
symlink = function(k, src, dest) {
fs.symlink(src, dest, function(err){
if (err) throw new Error(err);
Execute(k, [ false ]);
});
};
readlink = function(k, path) {
fs.readlink(path, function(err, target){
if (err) throw new Error(err);
Execute(k, [ target ]);
});
};
fstat = function(k, pathname) {
fs.stat(pathname, function(err, stat){
if (err && err.code != 'ENOENT') throw new Error(err);
Execute(k, [ err ? false : stat ]);
});
};
lstat = function(k, pathname) {
fs.lstat(pathname, function(err, stat){
if (err && err.code != 'ENOENT') throw new Error(err);
Execute(k, [ err ? false : stat ]);
});
};
isFile = function(k, stat) {
k(stat.isFile());
};
isDirectory = function(k, stat) {
k(stat.isDirectory());
};
isSymlink = function(k, stat) {
k(stat.isSymbolicLink());
};
pathJoin = function(k) {
k(path.join.apply(path, [].slice.call(arguments, 1)));
};
pathBasename = function(k, x) {
k(path.basename(x));
};
pathDirname = function(k, x) {
k(path.dirname(x));
};
// lists
function Cons(car, cdr) {
this.car = car;
this.cdr = cdr;
}
isCons = function(k, thing) {
k(thing instanceof Cons);
};
cons = function(k, car, cdr) {
k(new Cons(car, cdr));
};
car = function(k, cell) {
k(cell.car);
};
cdr = function(k, cell) {
k(cell.cdr);
};
setCar = function(k, cell, car) {
k(cell.car = car);
};
setCdr = function(k, cell, cdr) {
k(cell.cdr = cdr);
};
NIL = {};
NIL.car = NIL;
NIL.cdr = NIL;
// arrays
length = function(k, thing) {
k(thing.length);
};
arrayRef = function(k, array, index) {
k(array[index]);
};
arraySet = function(k, array, index, value) {
k(array[index] = value);
};
// strings
stringStartsWith = function(k, str, prefix) {
k(str.substr(0, prefix.length) == prefix);
};
parallel = function(k, f){
var n = 0, result = [], i = 0;
f(function(){ if (i == 0) k(false) }, pcall);
function pcall(kpcall, chunk){
var index = i++;
n++;
chunk(function(rchunk){
n--;
result[index] = rchunk;
if (n == 0) Execute(k, [ result ]);
});
kpcall(false);
}
};
// delimited continuations
var pstack = [];
function _goto(f) {
f(function KGOTO(r){
var h = pstack.pop();
h(r);
});
}
RESET = function(KRESET, th){
pstack.push(KRESET);
_goto(th);
};
SHIFT = function(KSHIFT, f){
_goto(function(KGOTO){
f(KGOTO, function SK(k1, v){
pstack.push(k1);
KSHIFT(v);
});
});
};
})()";
forEach = λ(array, f)
let (n = length(array))
let loop (i = 0)
if (i < n) {
f(arrayRef(array, i), i);
loop(i + 1);
};