僕 Lisp

が作った僕 Lispです

JavaScript 1.8 (Firefox 3) でしか動かないので注意してね><

テストコード

出力


JavaScript による実装

var tokenize = function(source) source.match(/\"(?:[^"])*\"|\|[^\|]*\||(\)|\(|\s+|[^\(\)\s]+)/gm).filter(/^[^\s]/);

var parse = function(tokens) {
    var list = [], token;
    while ((token = tokens.shift()) && token != ')')
        list.push((token == '(')         ? parse(tokens) :
                  (token[0] == '"')      ? token.substring(1, token.length - 1) :
                  (token.match(/^-?\d/)) ? eval(token) :
                  new Symbol(token));
    return list;
};
var scope = function(proto) ({ __proto__: proto });

var Symbol = function(name) { this.name = name.match(/^\|/) ? name.substring(1, name.length - 1) : name };
Symbol.prototype.toString = function() this.name;

var SpecialForm = function(fn) { fn.special = true; return fn };

var ev = function(o, _) {
    if (o instanceof Array) {
        var [fn, args] = [ev(o[0], _), o.slice(1)]
        return fn.apply(_, fn.special ? args : args.map(function(a) ev(a, _)));
    }
    else if (o instanceof Symbol) return _[o];
    else return o
};

var define = new SpecialForm(function() {
    var islambda = arguments[0] instanceof Array;
    var symbol = islambda ? arguments[0][0] : arguments[0];
    var value = islambda ?
        lambda.apply(null, [arguments[0].slice(1)].concat(Array.slice(arguments, 1))) : arguments[1];
    return this[symbol] = ev(value, this);
});

var jsspform = function(___source) new SpecialForm(function() {
    with(this)
        return (eval('(' + ___source + ')')).apply(this, arguments)
});

ライブラリ部分

(define jslambda (jsspform "function(___source) {
    var ___scope = this;
    var ___fn = function() {
        with(___scope)
            return eval('(' + ___source + ')')
                    .apply(null, Array.map(arguments, function(a) ev(a, ___scope)));
    };
    return ___fn;
}"))

(define -> (jsspform "function(self, prop) {
    var self = ev(self, this), prop = self[prop];
    return (typeof prop == 'function') ?
        function() { return prop.apply(self, arguments) } :
        prop
}"))

(define if (jsspform "function(cond, t, f) ev(cond, this) ? ev(t, this) : ev(f, this)"))

(define set! (jsspform "function(symbol, value) {
    var scp = this;

    do {
        // just hack
        if (scp instanceof Window && scp.location === window.location) scp = window;

        if (scp.hasOwnProperty(symbol.name)) {
            scp[symbol] = ev(value, this);
            return 0;
        }
    } while (scp = scp.__proto__);
}"))

(define lambda (jsspform "function() {
    var args = Array.shift(arguments);
    var exprs = arguments;
    var pscope = this;
    return function() { 
            var _ = scope(pscope);
            for (var i = 0; i < args.length; i ++) _[args[i]] = arguments[i];
            for (var i = 0; i < exprs.length; i++) var r = ev(exprs[i], _);
            return r;
    };
}"))

(define let (jsspform "function() {
    var pairs = Array.shift(arguments), ps = [], vs = [];
    pairs.forEach(function([p, v]) { ps.push(p); vs.push(v) });
    Array.unshift(arguments, ps);
    var list = [this.lambda.apply(null, arguments)].concat(vs);
    return ev(list, this);
}"))

(define begin (jslambda "function() { return arguments[arguments.length - 1] }"))

(define + (jslambda "function() { 
    var result = arguments[0];
    for (var i = 1; i < arguments.length; i ++) result += arguments[i];
    return result
}"))

(define - (jslambda "function() {
    var result = arguments[0];
    for (var i = 1; i < arguments.length; i ++) result -= arguments[i];
    return result
}"))

(define / (jslambda "function() {
    var result = arguments[0];
    for (var i = 1; i < arguments.length; i ++) result /= arguments[i];
    return result
}"))

(define * (jslambda "function() {
    var result = arguments[0];
    for (var i = 1; i < arguments.length; i ++) result *= arguments[i];
    return result
}"))

(define =       (jslambda "function(a, b) a == b ? 1 : 0"))
(define <       (jslambda "function(a, b) a < b ? 1 : 0"))
(define >       (jslambda "function(a, b) a > b ? 1 : 0"))
(define expt    (jslambda "function(a, b) Math.pow(a, b)"))
(define or      (jslambda "function(a, b) a || b ? 1 : 0"))
(define and     (jslambda "function(a, b) a && b ? 1 : 0"))

(define display (jslambda "function(a) {
    document.getElementById('log').appendChild(document.createTextNode(a + '\n'));
    return a;
}"))