mirror of
https://github.com/samsonjs/elisp.js.git
synced 2026-03-25 09:15:49 +00:00
245 lines
5.3 KiB
JavaScript
245 lines
5.3 KiB
JavaScript
////
|
|
// Emacs Lisp implementation in JavaScript.
|
|
//
|
|
// Copyright (c) 2009 Sami Samhuri - sami.samhuri@gmail.com
|
|
//
|
|
// Released under the terms of the MIT license. See the included file
|
|
// LICENSE.
|
|
|
|
var tags = ['symbol', 'string', 'number', 'cons', 'lambda'];
|
|
exports.tags = tags;
|
|
|
|
|
|
// All types descend from this object
|
|
var LispObject = function(value) {
|
|
this._tag = 'object';
|
|
this._value = value;
|
|
};
|
|
|
|
LispObject.prototype.tag = function() {
|
|
return this._tag;
|
|
};
|
|
|
|
LispObject.prototype.value = function() {
|
|
return this._value;
|
|
};
|
|
|
|
LispObject.prototype.repr = function() {
|
|
return '#<object value:' + this._value + '>';
|
|
};
|
|
|
|
LispObject.prototype.eq = function(other) {
|
|
return other && this._tag == other._tag && this._value == other._value;
|
|
};
|
|
|
|
LispObject.prototype.isA = function(what) {
|
|
return this._tag == what;
|
|
};
|
|
|
|
LispObject.prototype.isSymbol = function() {
|
|
return this.isA('symbol');
|
|
};
|
|
|
|
LispObject.prototype.isString = function() {
|
|
return this.isA('string');
|
|
};
|
|
|
|
LispObject.prototype.isNumber = function() {
|
|
return this.isA('number');
|
|
};
|
|
|
|
LispObject.prototype.isCons = function() {
|
|
return this.isA('cons');
|
|
};
|
|
|
|
LispObject.prototype.isLambda = function() {
|
|
return this.isA('lambda');
|
|
};
|
|
|
|
LispObject.prototype.isNilSymbol = function() {
|
|
return (this.isSymbol() && this.symbolName() == 'nil');
|
|
};
|
|
|
|
LispObject.prototype.isNilList = function() {
|
|
return (this.isCons() && this.car().isNil() && this.cdr().isNil());
|
|
};
|
|
|
|
LispObject.prototype.isNil = function() {
|
|
return this.isNilSymbol() || this.isEmptyList;
|
|
};
|
|
|
|
LispObject.prototype.isList = function() {
|
|
return (this.isNil() || this.isCons());
|
|
};
|
|
|
|
LispObject.prototype.isAtom = function() {
|
|
return !this.isCons();
|
|
};
|
|
|
|
exports.LispObject = LispObject;
|
|
|
|
|
|
// Symbols
|
|
|
|
var LispSymbol = function(value) {
|
|
this.prototype = new LispObject().prototype;
|
|
//this.prototype.constructor = this;
|
|
this._tag = 'symbol';
|
|
this._value = value;
|
|
};
|
|
LispSymbol.extend(LispObject);
|
|
|
|
LispSymbol.prototype.symbolName = function() {
|
|
return this._value;
|
|
};
|
|
|
|
LispSymbol.prototype.repr = function() {
|
|
// TODO escape symbol name for real reading
|
|
return "'" + this._value;
|
|
};
|
|
|
|
exports.LispSymbol = LispSymbol;
|
|
|
|
|
|
// Strings
|
|
|
|
var LispString = function(value) {
|
|
this._tag = 'string';
|
|
this._value = value;
|
|
};
|
|
LispString.extend(LispObject);
|
|
|
|
LispString.prototype.repr = function() {
|
|
// TODO escape string for real reading
|
|
return '"' + this._value + '"';
|
|
};
|
|
|
|
exports.LispString = LispString;
|
|
|
|
|
|
// Numbers
|
|
|
|
var LispNumber = function(value) {
|
|
this._tag = 'number';
|
|
this._value = value;
|
|
};
|
|
LispNumber.extend(LispObject);
|
|
|
|
LispNumber.prototype.repr = function() {
|
|
return '' + this._value;
|
|
};
|
|
|
|
exports.LispNumber = LispNumber;
|
|
|
|
|
|
// Constants
|
|
|
|
var T = new LispSymbol('t'),
|
|
NIL = new LispSymbol('nil');
|
|
exports.T = T;
|
|
exports.NIL = NIL;
|
|
|
|
T.repr = function(){return 't';};
|
|
NIL.repr = function(){return 'nil';};
|
|
|
|
|
|
// Conses
|
|
|
|
var LispCons = function(car, cdr) {
|
|
this._tag = 'cons';
|
|
this._car = car || NIL;
|
|
this._cdr = cdr || NIL;
|
|
this._length = 1 + (cdr._length || 0);
|
|
};
|
|
LispCons.extend(LispObject);
|
|
|
|
LispCons.prototype.repr = function() {
|
|
// TODO pretty print lists as a special case
|
|
// (only dotted pairs should look like this)
|
|
return '(' + this._car.repr() + ' . ' + this._cdr.repr() + ')';
|
|
};
|
|
|
|
LispCons.prototype.eq = function(other) {
|
|
return other.isCons() && this.car().eq(other.car()) &&
|
|
this.cdr().eq(other.cdr());
|
|
};
|
|
|
|
exports.LispCons = LispCons;
|
|
|
|
|
|
// Lambdas
|
|
|
|
var LispLambda = function(value) {
|
|
this._tag = 'lambda';
|
|
this._value = value;
|
|
};
|
|
LispLambda.extend(LispObject);
|
|
|
|
LispLambda.prototype.repr = function() {
|
|
// TODO implement me
|
|
return '(lambda (...) [body:' + this._value + '] )';
|
|
};
|
|
|
|
exports.LispLambda = LispLambda;
|
|
|
|
|
|
|
|
// JavaScript Array -> linked list
|
|
var mkList = function(exprs) {
|
|
var list = NIL,
|
|
i = exprs.length;
|
|
while (--i >= 0) {
|
|
list = new LispCons(exprs[i], list);
|
|
}
|
|
return list;
|
|
};
|
|
exports.mkList = mkList;
|
|
|
|
|
|
|
|
var tagToTypeMap = {
|
|
symbol: LispSymbol,
|
|
string: LispString,
|
|
number: LispNumber,
|
|
cons: LispCons,
|
|
lambda: LispLambda
|
|
};
|
|
|
|
var construct = function(typeName, value) {
|
|
baseObject = tagToTypeMap[typeName] || LispObject;
|
|
return new baseObject(value);
|
|
};
|
|
exports.construct = construct;
|
|
|
|
|
|
|
|
// should probably be called guessType or LCDType
|
|
var inferType = function(exprs) {
|
|
var type_name = 'number',
|
|
initial = 0,
|
|
i = exprs.length-1;
|
|
while (i >= 0) {
|
|
if (!exprs[i--].isNumber()) {
|
|
type_name = 'string';
|
|
initial = '';
|
|
break;
|
|
}
|
|
}
|
|
return construct(type_name, initial);
|
|
};
|
|
exports.inferType = inferType;
|
|
|
|
|
|
// special forms
|
|
|
|
LispObject.prototype._isSpecialForm = function(name) {
|
|
return (this.isCons() && this.car().isSymbol() && this.car().symbolName() == name);
|
|
};
|
|
|
|
LispObject.prototype.isQuote = function(){return this._isSpecialForm('quote');};
|
|
LispObject.prototype.isDefvar = function(){return this._isSpecialForm('defvar');};
|
|
LispObject.prototype.isDefun = function(){return this._isSpecialForm('defun');};
|
|
LispObject.prototype.isSet = function(){return this._isSpecialForm('set');};
|
|
LispObject.prototype.isSetq = function(){return this._isSpecialForm('setq');};
|
|
LispObject.prototype.isCond = function(){return this._isSpecialForm('cond');};
|
|
LispObject.prototype.isIf = function(){return this._isSpecialForm('if');};
|