mirror of
https://github.com/samsonjs/elisp.js.git
synced 2026-03-25 09:15:49 +00:00
Renamed EL to elisp. Use absolute path to spidermonkey (till CommonJS conversion).
This commit is contained in:
parent
f87cfe33d2
commit
fdfca00fe4
16 changed files with 508 additions and 507 deletions
3
el.sh
3
el.sh
|
|
@ -1,3 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
rlwrap js el.js
|
||||
103
el/list.js
103
el/list.js
|
|
@ -1,103 +0,0 @@
|
|||
////
|
||||
// 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.
|
||||
|
||||
EL.consPair = function(pair) {
|
||||
var cons = ['cons', pair];
|
||||
cons.isList = true;
|
||||
return cons;
|
||||
};
|
||||
|
||||
EL.cons = function(car, cdr) {
|
||||
return EL.consPair([car, cdr]);
|
||||
};
|
||||
|
||||
EL.car = function(cons) {
|
||||
return EL.isNil(cons) ? cons : EL.val(cons)[0];
|
||||
};
|
||||
|
||||
EL.cdr = function(cons) {
|
||||
return EL.isNil(cons) ? cons : EL.val(cons)[1];
|
||||
};
|
||||
|
||||
EL.cadr = function(cons) {
|
||||
return EL.car(EL.cdr(cons));
|
||||
};
|
||||
|
||||
EL.caddr = function(cons) {
|
||||
return EL.car(EL.cdr(EL.cdr(cons)));
|
||||
};
|
||||
|
||||
EL.cadddr = function(cons) {
|
||||
return EL.car(EL.cdr(EL.cdr(EL.cdr(cons))));
|
||||
};
|
||||
|
||||
EL.listLength = function(cons) {
|
||||
var n = 0;
|
||||
while (!EL.isNil(cons)) {
|
||||
cons = EL.cdr(cons);
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
};
|
||||
|
||||
EL.listLast = function(cons) {
|
||||
var last;
|
||||
while (!EL.isNil(cons)) {
|
||||
last = cons;
|
||||
cons = EL.cdr(cons);
|
||||
}
|
||||
return EL.car(last);
|
||||
};
|
||||
|
||||
EL.listMap = function(cons, fn) {
|
||||
var list = [],
|
||||
i = 0;
|
||||
while (!EL.isNil(cons)) {
|
||||
list.push(fn(EL.car(cons), i));
|
||||
cons = EL.cdr(cons);
|
||||
++i;
|
||||
}
|
||||
return list.length > 0 ? EL.list(list) : EL.nil;
|
||||
};
|
||||
|
||||
EL.listReduce = function(fn, accum, cons) {
|
||||
var i = 0,
|
||||
n = EL.listLength(cons);
|
||||
while (i < n) {
|
||||
accum = fn(accum, EL.nth(i++, cons));
|
||||
}
|
||||
return accum;
|
||||
};
|
||||
|
||||
EL.idFunction = function(x){return x;};
|
||||
|
||||
EL.unlist = function(cons) {
|
||||
return EL.listReduce(EL.idFunction, [], cons);
|
||||
};
|
||||
|
||||
EL.nth = function(n, cons) {
|
||||
var i = 0,
|
||||
e;
|
||||
while (i <= n && !EL.isNil(cons)) {
|
||||
e = EL.car(cons);
|
||||
cons = EL.cdr(cons);
|
||||
++i;
|
||||
}
|
||||
return n > --i ? EL.nil : e;
|
||||
};
|
||||
|
||||
EL.nthcdr = function(n, cons) {
|
||||
var e = EL.cdr(cons),
|
||||
i = 0;
|
||||
while (i < n && !EL.isNil(e)) {
|
||||
e = EL.cdr(e);
|
||||
++i;
|
||||
}
|
||||
return n > i ? EL.nil : e;
|
||||
};
|
||||
|
||||
131
el/primitives.js
131
el/primitives.js
|
|
@ -1,131 +0,0 @@
|
|||
////
|
||||
// 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.
|
||||
|
||||
|
||||
EL.PrimitiveVariables = [
|
||||
['t', {
|
||||
type: 'variable',
|
||||
value: ['symbol', 't'],
|
||||
docstring: "true"
|
||||
}],
|
||||
['nil', {
|
||||
type: 'variable',
|
||||
value: ['symbol', 'nil'],
|
||||
docstring: "nil"
|
||||
}]
|
||||
];
|
||||
|
||||
EL.PrimitiveFunctions = [];
|
||||
|
||||
// 'this' is bound to the EL.Evaluator object when executing primitve functions
|
||||
EL.definePrimitive = function(name, params, body, docstring) {
|
||||
EL.PrimitiveFunctions.push([name, {
|
||||
type: 'primitive',
|
||||
name: name,
|
||||
params: params, // unused right now but should be checked
|
||||
docstring: docstring,
|
||||
body: body
|
||||
}]);
|
||||
};
|
||||
|
||||
EL.notFunc = function(fn) {
|
||||
return function(x){ return !fn(x); };
|
||||
};
|
||||
|
||||
EL.makePrimitiveBooleanFunc = function(fn) {
|
||||
return function(x){ return fn(x) ? EL.t : EL.nil; };
|
||||
};
|
||||
|
||||
EL._definePrimitives = function() {
|
||||
EL.definePrimitive('consp', ['symbol'], EL.makePrimitiveBooleanFunc(EL.isCons),
|
||||
"Return T if symbol is a cons, nil otherwise.");
|
||||
|
||||
EL.definePrimitive('atom', ['symbol'], EL.makePrimitiveBooleanFunc(EL.isAtom),
|
||||
"Return T if symbol is not a cons or is nil, nil otherwise.");
|
||||
|
||||
EL.definePrimitive('symbol-name', ['symbol'],
|
||||
function(symbol) { return EL.string(EL.val(symbol)); },
|
||||
"Return a symbol's name, a string.");
|
||||
|
||||
EL.definePrimitive('string-match', ['regex', 'string', '&optional', 'start'],
|
||||
function(regex, string, start) {
|
||||
var index = start ? EL.val(start) : 0,
|
||||
s = EL.val(string).substring(index),
|
||||
match = s.match(new RegExp(EL.val(regex))),
|
||||
found = match ? EL.number(s.indexOf(match[0])) : EL.nil;
|
||||
return found;},
|
||||
"Return the index of the char matching regex in string, beginning from start if available.");
|
||||
|
||||
// Right now a single string in the arg list will cause all the arguments
|
||||
// to be converted to strings similar to JavaScript. These
|
||||
// semantics suck and should change, not only for real emacs lisp compatibility.
|
||||
EL.definePrimitive('+', [/*...*/],
|
||||
function() {
|
||||
var args = EL.Util.shallowCopy(arguments),
|
||||
initial = EL.inferType(args),
|
||||
type = initial[0];
|
||||
return EL.Util.reduce(function(sum, n) {
|
||||
return [type, EL.val(sum) + EL.val(n)];
|
||||
}, initial, args);},
|
||||
"add two numbers");
|
||||
|
||||
EL.definePrimitive('-', [/*...*/],
|
||||
function() {
|
||||
return EL.Util.foldr(function(diff, n) {
|
||||
return EL.number(EL.val(diff) - EL.val(n));
|
||||
}, EL.number(0), EL.Util.shallowCopy(arguments));},
|
||||
"subtract two numbers");
|
||||
|
||||
EL.definePrimitive('*', [/*...*/],
|
||||
function() {
|
||||
return EL.Util.reduce(function(prod, n) {
|
||||
return EL.number(EL.val(prod) * EL.val(n));
|
||||
}, EL.number(1), EL.Util.shallowCopy(arguments));},
|
||||
"multiply two numbers");
|
||||
|
||||
EL.definePrimitive('/', [/*...*/],
|
||||
function() {
|
||||
return EL.Util.foldr(function(quot, n) {
|
||||
return EL.number(EL.val(quot) / EL.val(n));
|
||||
}, EL.number(1), EL.Util.shallowCopy(arguments));
|
||||
},
|
||||
"divide two numbers");
|
||||
|
||||
EL.definePrimitive('print', ['x'],
|
||||
function(x, tostring) {
|
||||
var buffer = "",
|
||||
tag = EL.tag(x);
|
||||
function p(s) {
|
||||
if (tostring) buffer += s;
|
||||
else print(s);
|
||||
}
|
||||
if (tag == 'number' || tag == 'symbol' || tag == 'string') {
|
||||
p(EL.val(x));
|
||||
}
|
||||
else if (tag == 'lambda') {
|
||||
var fn = EL.val(x);
|
||||
p('(lambda ' + fn.name + ' (' + fn.params + ')\n');
|
||||
p(fn.body); // TODO lisp pretty print
|
||||
p(')');
|
||||
}
|
||||
else if (tag == 'list') {
|
||||
var recurse = arguments.callee; // far easier to remember than Y
|
||||
print('(', El.val(x).map(function(e){return (recurse(e, true) + ' ');}), ")");
|
||||
}
|
||||
else {
|
||||
print('unknown type: ' + x);
|
||||
}
|
||||
return EL.nil;
|
||||
},
|
||||
"print an expression");
|
||||
|
||||
EL.definePrimitive('hide-prompt', ['yes-or-no'],
|
||||
function(bool){ EL.hidePrompt = !EL.isNil(bool); },
|
||||
"Call with T to hide the prompt or nil to show it.");
|
||||
};
|
||||
EL.initHook(EL._definePrimitives);
|
||||
113
el/types.js
113
el/types.js
|
|
@ -1,113 +0,0 @@
|
|||
////
|
||||
// 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.
|
||||
|
||||
// data types are simple tags
|
||||
EL._defineTags = function() {
|
||||
EL.tags = ['symbol', 'string', 'number', 'cons', 'lambda', 'regex'];
|
||||
EL.tags.each = Array.prototype.each;
|
||||
|
||||
// define constructors for the primitive types (box values)
|
||||
// e.g. EL.symbol('foo') => ['symbol', 'foo']
|
||||
EL.tags.each(function(tag) {
|
||||
// don't clobber custom constructors
|
||||
if (EL[tag] === undefined) {
|
||||
EL[tag] = function(value) {
|
||||
return [tag, value];
|
||||
};
|
||||
}
|
||||
// tag type tests
|
||||
var isTag = function(expr) {
|
||||
return (EL.tag(expr) == tag);
|
||||
};
|
||||
EL['is' + tag.camelize()] = isTag;
|
||||
});
|
||||
};
|
||||
EL.initHook(EL._defineTags);
|
||||
|
||||
// shorthands to save my fingers
|
||||
EL._defineConstants = function() {
|
||||
EL.nil = EL.symbol('nil');
|
||||
EL.t = EL.symbol('t');
|
||||
};
|
||||
EL.initHook(EL._defineConstants);
|
||||
|
||||
|
||||
// retrieve the tag from a value
|
||||
EL.tag = function(expr) {
|
||||
EL.assert(function() { var f='tag'; return EL.typeOf(expr) == 'array'; }, expr);
|
||||
return expr[0];
|
||||
};
|
||||
|
||||
// unbox a value
|
||||
EL.val = function(expr) {
|
||||
EL.assert(function() { var f='val'; return EL.typeOf(expr) == 'array'; }, expr);
|
||||
return expr[1];
|
||||
};
|
||||
|
||||
EL.symbolName = function(symbol) {
|
||||
// EL.Util.pp(symbol);
|
||||
EL.assert(function(){ var f='symbolName'; return EL.isSymbol(symbol); }, symbol);
|
||||
return EL.val(symbol);
|
||||
};
|
||||
|
||||
EL.isNilSymbol = function(expr) {
|
||||
return (EL.isSymbol(expr) && EL.symbolName(expr) == 'nil');
|
||||
};
|
||||
|
||||
EL.isNil = function(expr) {
|
||||
return EL.isNilSymbol(expr);
|
||||
};
|
||||
|
||||
EL.list = function(exprs) {
|
||||
var list = EL.nil,
|
||||
i = exprs.length;
|
||||
while (--i >= 0) {
|
||||
list = EL.cons(exprs[i], list);
|
||||
}
|
||||
return list;
|
||||
};
|
||||
|
||||
EL.isList = function(expr) {
|
||||
return (EL.isNil(expr) || EL.isCons(expr));
|
||||
};
|
||||
|
||||
EL.isAtom = function(expr) {
|
||||
return !EL.isCons(expr);
|
||||
};
|
||||
|
||||
EL.inferType = function(exprs) {
|
||||
var type = 'number',
|
||||
initial = 0,
|
||||
i = exprs.length-1;
|
||||
while(i >= 0) {
|
||||
if (EL.isString(exprs[i--])) {
|
||||
type = 'string';
|
||||
initial = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return EL[type](initial);
|
||||
};
|
||||
|
||||
|
||||
// special forms
|
||||
|
||||
EL.isSpecialForm = function(name, expr) {
|
||||
var tag = EL.tag(expr),
|
||||
car = EL.typeOf(expr) == 'array' && EL.val(expr)[0],
|
||||
thisName = car && EL.symbolName(car);
|
||||
return (tag == 'cons' && thisName == name);
|
||||
};
|
||||
|
||||
EL.isQuote = function(expr){return EL.isSpecialForm('quote', expr);};
|
||||
EL.isDefVar = function(expr){return EL.isSpecialForm('defvar', expr);};
|
||||
EL.isDefFunc = function(expr){return EL.isSpecialForm('defun', expr);};
|
||||
EL.isSet = function(expr){return EL.isSpecialForm('set', expr);};
|
||||
EL.isSetq = function(expr){return EL.isSpecialForm('setq', expr);};
|
||||
EL.isCond = function(expr){return EL.isSpecialForm('cond', expr);};
|
||||
EL.isIf = function(expr){return EL.isSpecialForm('if', expr);};
|
||||
|
|
@ -25,23 +25,24 @@
|
|||
|
||||
|
||||
// our namespace
|
||||
var EL = function(){};
|
||||
var elisp = function(){};
|
||||
|
||||
load('el/init.js'); // simple init system
|
||||
load('el/jsExt.js'); // a few extensions to native types
|
||||
load('el/util.js'); // utilities
|
||||
|
||||
load('elisp/init.js'); // simple init system
|
||||
load('elisp/jsExt.js'); // a few extensions to native types
|
||||
load('elisp/util.js'); // utilities
|
||||
|
||||
// main lisp system
|
||||
load('el/types.js');
|
||||
load('el/list.js');
|
||||
load('el/symtab.js');
|
||||
load('el/parser.js');
|
||||
load('el/evaluator.js');
|
||||
load('el/primitives.js');
|
||||
load('el/repl.js');
|
||||
load('elisp/types.js');
|
||||
load('elisp/list.js');
|
||||
load('elisp/symtab.js');
|
||||
load('elisp/parser.js');
|
||||
load('elisp/evaluator.js');
|
||||
load('elisp/primitives.js');
|
||||
load('elisp/repl.js');
|
||||
|
||||
// everything is defined, initialize
|
||||
EL.init();
|
||||
elisp.init();
|
||||
|
||||
// q to quit
|
||||
EL.repl();
|
||||
elisp.repl();
|
||||
3
elisp.sh
Executable file
3
elisp.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
|
||||
rlwrap /opt/local/bin/js elisp.js
|
||||
|
|
@ -7,30 +7,30 @@
|
|||
// LICENSE.
|
||||
|
||||
|
||||
EL.Evaluator = function(exprs) {
|
||||
elisp.Evaluator = function(exprs) {
|
||||
this.expressions = exprs;
|
||||
this.variables = new EL.SymbolTable(EL.PrimitiveVariables);
|
||||
this.functions = new EL.SymbolTable(EL.PrimitiveFunctions);
|
||||
this.variables = new elisp.SymbolTable(elisp.PrimitiveVariables);
|
||||
this.functions = new elisp.SymbolTable(elisp.PrimitiveFunctions);
|
||||
};
|
||||
|
||||
EL.Evaluator.Error = function(name, message, expr) {
|
||||
elisp.Evaluator.Error = function(name, message, expr) {
|
||||
this.evalError = true;
|
||||
this.name = name;
|
||||
this.message = message;
|
||||
this.expression = expr;
|
||||
};
|
||||
|
||||
EL.Evaluator.Error.messages = {
|
||||
elisp.Evaluator.Error.messages = {
|
||||
'not-expr': "not an expression",
|
||||
'undefined-func': "undefined function",
|
||||
'undefined-var': "variable not defined"
|
||||
};
|
||||
|
||||
EL.Evaluator.prototype.error = function(name, expr) {
|
||||
throw(new EL.Evaluator.Error(name, EL.Evaluator.Error.messages[name], expr));
|
||||
elisp.Evaluator.prototype.error = function(name, expr) {
|
||||
throw(new elisp.Evaluator.Error(name, elisp.Evaluator.Error.messages[name], expr));
|
||||
};
|
||||
|
||||
EL.Evaluator.prototype.evalExpressions = function(expressions) {
|
||||
elisp.Evaluator.prototype.evalExpressions = function(expressions) {
|
||||
var exprs = expressions || this.expressions,
|
||||
i = 0,
|
||||
n = exprs.length,
|
||||
|
|
@ -44,7 +44,7 @@ EL.Evaluator.prototype.evalExpressions = function(expressions) {
|
|||
if (e.expression) {
|
||||
print("got: " + e.expression);
|
||||
}
|
||||
result = EL.nil;
|
||||
result = elisp.nil;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
|
|
@ -52,39 +52,39 @@ EL.Evaluator.prototype.evalExpressions = function(expressions) {
|
|||
}
|
||||
}
|
||||
}
|
||||
// EL.Util.pp(result);
|
||||
// elisp.Util.pp(result);
|
||||
return result;
|
||||
};
|
||||
|
||||
EL.Evaluator.prototype.lookupVar = function(symbol) {
|
||||
elisp.Evaluator.prototype.lookupVar = function(symbol) {
|
||||
return this.variables.lookup(symbol);
|
||||
};
|
||||
|
||||
EL.Evaluator.prototype.lookupFunc = function(symbol) {
|
||||
elisp.Evaluator.prototype.lookupFunc = function(symbol) {
|
||||
return this.functions.lookup(symbol);
|
||||
};
|
||||
|
||||
EL.Evaluator.prototype.apply = function(func, args) {
|
||||
elisp.Evaluator.prototype.apply = function(func, args) {
|
||||
var result;
|
||||
if (func.type === 'primitive') {
|
||||
// print('APPLY: ');
|
||||
// EL.print(func);
|
||||
// elisp.print(func);
|
||||
// print('WITH: ');
|
||||
// EL.print(args);
|
||||
// elisp.print(args);
|
||||
// print('------');
|
||||
result = func.body.apply(this, args);
|
||||
}
|
||||
else {
|
||||
this.functions.pushScope();
|
||||
this.variables.pushScope(EL.listMap(func.params, function(e, i){
|
||||
var name = EL.symbolName(e),
|
||||
this.variables.pushScope(elisp.listMap(func.params, function(e, i){
|
||||
var name = elisp.symbolName(e),
|
||||
value = {
|
||||
type: 'variable',
|
||||
value: this.eval(args[i])
|
||||
};
|
||||
return [name, value];
|
||||
}));
|
||||
result = EL.listLast(EL.listMap(func.body,
|
||||
result = elisp.listLast(elisp.listMap(func.body,
|
||||
function(e) {return this.eval(e); }));
|
||||
this.functions.popScope();
|
||||
this.variables.popScope();
|
||||
|
|
@ -92,16 +92,16 @@ EL.Evaluator.prototype.apply = function(func, args) {
|
|||
return result;
|
||||
};
|
||||
|
||||
EL.Evaluator.prototype.eval = function(expr) {
|
||||
// print("EVAL: " + EL.typeOf(expr));
|
||||
// EL.print(expr);
|
||||
elisp.Evaluator.prototype.eval = function(expr) {
|
||||
// print("EVAL: " + elisp.typeOf(expr));
|
||||
// elisp.print(expr);
|
||||
var result, x,
|
||||
tag = EL.tag(expr);
|
||||
if (EL.isAtom(expr)) {
|
||||
tag = elisp.tag(expr);
|
||||
if (elisp.isAtom(expr)) {
|
||||
result = expr;
|
||||
}
|
||||
else if (EL.isSymbol(expr)) {
|
||||
var name = EL.val(expr);
|
||||
else if (elisp.isSymbol(expr)) {
|
||||
var name = elisp.val(expr);
|
||||
x = this.lookupVar(name);
|
||||
if (x == null) this.error('undefined-var', name);
|
||||
result = x.value;
|
||||
|
|
@ -112,72 +112,72 @@ EL.Evaluator.prototype.eval = function(expr) {
|
|||
///////////////////
|
||||
// (many could be in lisp when there are macros) //
|
||||
|
||||
else if (EL.isQuote(expr)) {
|
||||
result = EL.cdr(expr);
|
||||
else if (elisp.isQuote(expr)) {
|
||||
result = elisp.cdr(expr);
|
||||
}
|
||||
else if (EL.isDefVar(expr)) {
|
||||
var name = EL.symbolName(EL.cadr(expr)), // 2nd param
|
||||
value = this.eval(EL.caddr(expr)), // 3rd param
|
||||
docstring = EL.cadddr(expr); // 4th param
|
||||
else if (elisp.isDefVar(expr)) {
|
||||
var name = elisp.symbolName(elisp.cadr(expr)), // 2nd param
|
||||
value = this.eval(elisp.caddr(expr)), // 3rd param
|
||||
docstring = elisp.cadddr(expr); // 4th param
|
||||
// TODO check for re-definitions
|
||||
this.defineVar(name, value, docstring);
|
||||
result = EL.nil;
|
||||
result = elisp.nil;
|
||||
}
|
||||
else if (EL.isDefFunc(expr)) {
|
||||
var name = EL.symbolName(EL.nth(1, expr)),
|
||||
params = EL.nth(2, expr),
|
||||
d = EL.nth(3, expr),
|
||||
docstring = EL.isString(d) && d,
|
||||
body = EL.nthcdr(docstring ? 3 : 2, expr);
|
||||
else if (elisp.isDefFunc(expr)) {
|
||||
var name = elisp.symbolName(elisp.nth(1, expr)),
|
||||
params = elisp.nth(2, expr),
|
||||
d = elisp.nth(3, expr),
|
||||
docstring = elisp.isString(d) && d,
|
||||
body = elisp.nthcdr(docstring ? 3 : 2, expr);
|
||||
this.defineFunc(name, params, body, docstring);
|
||||
result = EL.nil;
|
||||
result = elisp.nil;
|
||||
}
|
||||
else if (EL.isSet(expr)) {
|
||||
var val = EL.val(expr),
|
||||
name = EL.symbolName(val[1]),
|
||||
else if (elisp.isSet(expr)) {
|
||||
var val = elisp.val(expr),
|
||||
name = elisp.symbolName(val[1]),
|
||||
value = this.eval(val[2]);
|
||||
this.setVar(name, value);
|
||||
result = value;
|
||||
}
|
||||
else if (EL.isSetq(expr)) {
|
||||
else if (elisp.isSetq(expr)) {
|
||||
var i = 1,
|
||||
n = EL.listLength(expr),
|
||||
n = elisp.listLength(expr),
|
||||
e;
|
||||
while (i < n && EL.isSymbol((e=EL.nth(i,expr)))) {
|
||||
var name = EL.symbolName(EL.nth(i, expr)),
|
||||
value = this.eval(EL.nth(i+1, expr));
|
||||
while (i < n && elisp.isSymbol((e=elisp.nth(i,expr)))) {
|
||||
var name = elisp.symbolName(elisp.nth(i, expr)),
|
||||
value = this.eval(elisp.nth(i+1, expr));
|
||||
this.setVar(name, value, true);
|
||||
result = value;
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
else if (EL.isIf(expr)) {
|
||||
var val = EL.val(expr),
|
||||
else if (elisp.isIf(expr)) {
|
||||
var val = elisp.val(expr),
|
||||
condition = this.eval(val[1]),
|
||||
trueBlock = val[2],
|
||||
nilBlock = val[3];
|
||||
result = this.doIf(condition, trueBlock, nilBlock);
|
||||
}
|
||||
else if (EL.isCond(expr)) {
|
||||
var val = EL.val(expr),
|
||||
else if (elisp.isCond(expr)) {
|
||||
var val = elisp.val(expr),
|
||||
list = val[1],
|
||||
condition = EL.car(list),
|
||||
body = EL.cdr(list),
|
||||
condition = elisp.car(list),
|
||||
body = elisp.cdr(list),
|
||||
rest = val.slice(2);
|
||||
result = this.doCond(exprs);
|
||||
}
|
||||
|
||||
// function application
|
||||
else if (EL.isCons(expr)) {
|
||||
var name = EL.car(expr),
|
||||
rest = EL.cdr(expr),
|
||||
else if (elisp.isCons(expr)) {
|
||||
var name = elisp.car(expr),
|
||||
rest = elisp.cdr(expr),
|
||||
func, args;
|
||||
while (!EL.isSymbol(name)) {
|
||||
while (!elisp.isSymbol(name)) {
|
||||
name = this.eval(name);
|
||||
}
|
||||
if ((func = this.lookupFunc(EL.symbolName(name)))) {
|
||||
if ((func = this.lookupFunc(elisp.symbolName(name)))) {
|
||||
var self = this;
|
||||
args = EL.listReduce(function(a,e){
|
||||
args = elisp.listReduce(function(a,e){
|
||||
a.push(self.eval(e));
|
||||
return a;
|
||||
}, [], rest);
|
||||
|
|
@ -195,7 +195,7 @@ EL.Evaluator.prototype.eval = function(expr) {
|
|||
};
|
||||
|
||||
|
||||
EL.Evaluator.prototype.defineVar = function(symbol, value, docstring) {
|
||||
elisp.Evaluator.prototype.defineVar = function(symbol, value, docstring) {
|
||||
this.variables.define(symbol, {
|
||||
type: 'variable',
|
||||
value: value,
|
||||
|
|
@ -203,11 +203,11 @@ EL.Evaluator.prototype.defineVar = function(symbol, value, docstring) {
|
|||
});
|
||||
};
|
||||
|
||||
EL.Evaluator.prototype.setVar = function(symbol, value, create) {
|
||||
elisp.Evaluator.prototype.setVar = function(symbol, value, create) {
|
||||
var valueObject = this.lookupVar(symbol);
|
||||
if (!valueObject) {
|
||||
if (create) {
|
||||
this.defineVar(symbol, EL.nil);
|
||||
this.defineVar(symbol, elisp.nil);
|
||||
valueObject = this.lookupVar(symbol);
|
||||
}
|
||||
else {
|
||||
|
|
@ -218,7 +218,7 @@ EL.Evaluator.prototype.setVar = function(symbol, value, create) {
|
|||
this.variables.set(symbol, valueObject);
|
||||
};
|
||||
|
||||
EL.Evaluator.prototype.defineFunc = function(symbol, params, body, docstring) {
|
||||
elisp.Evaluator.prototype.defineFunc = function(symbol, params, body, docstring) {
|
||||
this.functions.define(symbol, {
|
||||
type: 'lambda',
|
||||
name: symbol,
|
||||
|
|
@ -228,14 +228,14 @@ EL.Evaluator.prototype.defineFunc = function(symbol, params, body, docstring) {
|
|||
});
|
||||
};
|
||||
|
||||
EL.Evaluator.prototype.doIf = function(condition, trueBlock, nilBlock) {
|
||||
return EL.isNil(condition) ? this.eval(nilBlock) : this.eval(trueBlock);
|
||||
elisp.Evaluator.prototype.doIf = function(condition, trueBlock, nilBlock) {
|
||||
return elisp.isNil(condition) ? this.eval(nilBlock) : this.eval(trueBlock);
|
||||
};
|
||||
|
||||
EL.Evaluator.prototype.doCond = function(exprs) {
|
||||
elisp.Evaluator.prototype.doCond = function(exprs) {
|
||||
print('----- COND (doCond) -----');
|
||||
EL.print(exprs);
|
||||
return EL.nil;
|
||||
elisp.print(exprs);
|
||||
return elisp.nil;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -10,14 +10,14 @@
|
|||
// of the file we call init when everything is defined, regardless of
|
||||
// the order it appears in the file. The order of the hooks still
|
||||
// matters though, it's not fool-proof.
|
||||
EL._initHooks = [];
|
||||
EL.initHook = function(hook) {
|
||||
EL._initHooks.push(hook);
|
||||
elisp._initHooks = [];
|
||||
elisp.initHook = function(hook) {
|
||||
elisp._initHooks.push(hook);
|
||||
};
|
||||
EL.init = function() {
|
||||
elisp.init = function() {
|
||||
var i = 0,
|
||||
n = EL._initHooks.length;
|
||||
n = elisp._initHooks.length;
|
||||
while (i < n) {
|
||||
EL._initHooks[i++].call();
|
||||
elisp._initHooks[i++].call();
|
||||
}
|
||||
};
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
// LICENSE.
|
||||
|
||||
// Just a little sugar
|
||||
EL.initHook(function() {
|
||||
elisp.initHook(function() {
|
||||
Array.prototype.each = function(fn) {
|
||||
var i = 0,
|
||||
n = this.length;
|
||||
|
|
@ -35,7 +35,7 @@ EL.initHook(function() {
|
|||
|
||||
// A typeOf function that distinguishes between objects, arrays,
|
||||
// and null.
|
||||
EL.typeOf = function(value) {
|
||||
elisp.typeOf = function(value) {
|
||||
var s = typeof value;
|
||||
if (s === 'object') {
|
||||
if (value) {
|
||||
|
|
@ -52,7 +52,7 @@ EL.initHook(function() {
|
|||
};
|
||||
|
||||
// TODO throw something more informative
|
||||
EL.assert = function(condition, message) {
|
||||
elisp.assert = function(condition, message) {
|
||||
if (!condition()) {
|
||||
throw("assertion failed: " + condition + " (" + message + ")");
|
||||
}
|
||||
103
elisp/list.js
Normal file
103
elisp/list.js
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
////
|
||||
// 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.
|
||||
|
||||
elisp.consPair = function(pair) {
|
||||
var cons = ['cons', pair];
|
||||
cons.isList = true;
|
||||
return cons;
|
||||
};
|
||||
|
||||
elisp.cons = function(car, cdr) {
|
||||
return elisp.consPair([car, cdr]);
|
||||
};
|
||||
|
||||
elisp.car = function(cons) {
|
||||
return elisp.isNil(cons) ? cons : elisp.val(cons)[0];
|
||||
};
|
||||
|
||||
elisp.cdr = function(cons) {
|
||||
return elisp.isNil(cons) ? cons : elisp.val(cons)[1];
|
||||
};
|
||||
|
||||
elisp.cadr = function(cons) {
|
||||
return elisp.car(elisp.cdr(cons));
|
||||
};
|
||||
|
||||
elisp.caddr = function(cons) {
|
||||
return elisp.car(elisp.cdr(elisp.cdr(cons)));
|
||||
};
|
||||
|
||||
elisp.cadddr = function(cons) {
|
||||
return elisp.car(elisp.cdr(elisp.cdr(elisp.cdr(cons))));
|
||||
};
|
||||
|
||||
elisp.listLength = function(cons) {
|
||||
var n = 0;
|
||||
while (!elisp.isNil(cons)) {
|
||||
cons = elisp.cdr(cons);
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
};
|
||||
|
||||
elisp.listLast = function(cons) {
|
||||
var last;
|
||||
while (!elisp.isNil(cons)) {
|
||||
last = cons;
|
||||
cons = elisp.cdr(cons);
|
||||
}
|
||||
return elisp.car(last);
|
||||
};
|
||||
|
||||
elisp.listMap = function(cons, fn) {
|
||||
var list = [],
|
||||
i = 0;
|
||||
while (!elisp.isNil(cons)) {
|
||||
list.push(fn(elisp.car(cons), i));
|
||||
cons = elisp.cdr(cons);
|
||||
++i;
|
||||
}
|
||||
return list.length > 0 ? elisp.list(list) : elisp.nil;
|
||||
};
|
||||
|
||||
elisp.listReduce = function(fn, accum, cons) {
|
||||
var i = 0,
|
||||
n = elisp.listLength(cons);
|
||||
while (i < n) {
|
||||
accum = fn(accum, elisp.nth(i++, cons));
|
||||
}
|
||||
return accum;
|
||||
};
|
||||
|
||||
elisp.idFunction = function(x){return x;};
|
||||
|
||||
elisp.unlist = function(cons) {
|
||||
return elisp.listReduce(elisp.idFunction, [], cons);
|
||||
};
|
||||
|
||||
elisp.nth = function(n, cons) {
|
||||
var i = 0,
|
||||
e;
|
||||
while (i <= n && !elisp.isNil(cons)) {
|
||||
e = elisp.car(cons);
|
||||
cons = elisp.cdr(cons);
|
||||
++i;
|
||||
}
|
||||
return n > --i ? elisp.nil : e;
|
||||
};
|
||||
|
||||
elisp.nthcdr = function(n, cons) {
|
||||
var e = elisp.cdr(cons),
|
||||
i = 0;
|
||||
while (i < n && !elisp.isNil(e)) {
|
||||
e = elisp.cdr(e);
|
||||
++i;
|
||||
}
|
||||
return n > i ? elisp.nil : e;
|
||||
};
|
||||
|
||||
|
|
@ -7,53 +7,53 @@
|
|||
// LICENSE.
|
||||
|
||||
|
||||
EL.Parser = function(data) {
|
||||
elisp.Parser = function(data) {
|
||||
this.data = data || '';
|
||||
};
|
||||
|
||||
EL.Parser.Error = function(name, message) {
|
||||
elisp.Parser.Error = function(name, message) {
|
||||
this.parserError = true;
|
||||
this.name = name;
|
||||
this.message = message;
|
||||
};
|
||||
|
||||
EL.Parser.Error.messages = {
|
||||
elisp.Parser.Error.messages = {
|
||||
'eof': "no more input"
|
||||
};
|
||||
|
||||
EL.Parser.prototype.error = function(name) {
|
||||
throw(new EL.Parser.Error(name, EL.Parser.Error.messages[name]));
|
||||
elisp.Parser.prototype.error = function(name) {
|
||||
throw(new elisp.Parser.Error(name, elisp.Parser.Error.messages[name]));
|
||||
};
|
||||
|
||||
EL.Parser.prototype.peek = function() {
|
||||
elisp.Parser.prototype.peek = function() {
|
||||
return this.data[this.pos];
|
||||
};
|
||||
|
||||
EL.Parser.prototype.consumeChar = function() {
|
||||
elisp.Parser.prototype.consumeChar = function() {
|
||||
if (this.pos >= this.data.length) this.error('eof');
|
||||
return this.data[this.pos++];
|
||||
};
|
||||
|
||||
EL.Parser.prototype.consumeWhitespace = function() {
|
||||
elisp.Parser.prototype.consumeWhitespace = function() {
|
||||
var c;
|
||||
while ((c = this.peek()) && c.match(/[\s\n]/)) {
|
||||
this.consumeChar();
|
||||
}
|
||||
};
|
||||
|
||||
EL.Parser.prototype.rewind = function() {
|
||||
elisp.Parser.prototype.rewind = function() {
|
||||
this.pos = 0;
|
||||
};
|
||||
|
||||
EL.Parser.prototype.rest = function() {
|
||||
elisp.Parser.prototype.rest = function() {
|
||||
return this.data.substring(this.pos);
|
||||
};
|
||||
|
||||
EL.Parser.prototype.moreInput = function() {
|
||||
elisp.Parser.prototype.moreInput = function() {
|
||||
return (this.pos < this.data.length);
|
||||
};
|
||||
|
||||
EL.Parser.prototype.parse = function(string) {
|
||||
elisp.Parser.prototype.parse = function(string) {
|
||||
if (string) this.data = string;
|
||||
this.rewind();
|
||||
var exprs = [];
|
||||
|
|
@ -72,16 +72,16 @@ EL.Parser.prototype.parse = function(string) {
|
|||
}
|
||||
this.expressions = exprs;
|
||||
// print('');
|
||||
// EL.Util.pp(exprs);
|
||||
// elisp.Util.pp(exprs);
|
||||
// print('');
|
||||
return exprs;
|
||||
};
|
||||
|
||||
EL.Parser.prototype.parseOne = function(string) {
|
||||
elisp.Parser.prototype.parseOne = function(string) {
|
||||
return this.parse(string)[0];
|
||||
};
|
||||
|
||||
EL.Parser.prototype.parseUntil = function(regex, initial, next, consumeTerminator) {
|
||||
elisp.Parser.prototype.parseUntil = function(regex, initial, next, consumeTerminator) {
|
||||
var c,
|
||||
token = initial,
|
||||
condition = function(c){ return c.match(regex) == null; };
|
||||
|
|
@ -92,7 +92,7 @@ EL.Parser.prototype.parseUntil = function(regex, initial, next, consumeTerminato
|
|||
return token;
|
||||
};
|
||||
|
||||
EL.Parser.prototype.parseList = function() {
|
||||
elisp.Parser.prototype.parseList = function() {
|
||||
var list = [],
|
||||
expr;
|
||||
// consume initial paren '('
|
||||
|
|
@ -103,7 +103,7 @@ EL.Parser.prototype.parseList = function() {
|
|||
return list;
|
||||
};
|
||||
|
||||
EL.Parser.prototype.parseCons = function() {
|
||||
elisp.Parser.prototype.parseCons = function() {
|
||||
var cons = [],
|
||||
expr;
|
||||
// consume initial paren '('
|
||||
|
|
@ -115,7 +115,7 @@ EL.Parser.prototype.parseCons = function() {
|
|||
return cons;
|
||||
};
|
||||
|
||||
EL.Parser.prototype.parseString = function() {
|
||||
elisp.Parser.prototype.parseString = function() {
|
||||
// consume initial quotation mark
|
||||
this.consumeChar();
|
||||
var self = this;
|
||||
|
|
@ -127,13 +127,13 @@ EL.Parser.prototype.parseString = function() {
|
|||
}, true /* consume terminator */);
|
||||
};
|
||||
|
||||
EL.Parser.prototype.parseSymbol = function() {
|
||||
elisp.Parser.prototype.parseSymbol = function() {
|
||||
var symbol = this.parseUntil(/[\s()]/, '', function(t,c){return t + c;});
|
||||
return symbol;
|
||||
};
|
||||
|
||||
// Probably easy to break
|
||||
EL.Parser.prototype.parseRegex = function() {
|
||||
elisp.Parser.prototype.parseRegex = function() {
|
||||
// consume initial slash
|
||||
this.consumeChar();
|
||||
var self = this;
|
||||
|
|
@ -159,7 +159,7 @@ EL.Parser.prototype.parseRegex = function() {
|
|||
//
|
||||
// Binary, octal, hex, or arbitrary radix integers not yet parsed.
|
||||
// (e.g. #x100 == #o400 == #b100000000 == #24rag
|
||||
EL.Parser.prototype.parseNumber = function() {
|
||||
elisp.Parser.prototype.parseNumber = function() {
|
||||
var value = this.parseIntOrFloat(),
|
||||
exponentAllowed = value === parseInt(value),
|
||||
exp;
|
||||
|
|
@ -179,7 +179,7 @@ EL.Parser.prototype.parseNumber = function() {
|
|||
};
|
||||
|
||||
// Pack int and float parsing together for simplicity's sake.
|
||||
EL.Parser.prototype.parseIntOrFloat = function() {
|
||||
elisp.Parser.prototype.parseIntOrFloat = function() {
|
||||
this.exponentAllowed = true;
|
||||
var sign = this.peek() == '-' || this.peek() == '+' ? this.consumeChar() : '+',
|
||||
value;
|
||||
|
|
@ -217,7 +217,7 @@ EL.Parser.prototype.parseIntOrFloat = function() {
|
|||
// string or some chars in the same regex.
|
||||
//
|
||||
// TODO: pick up Friedl and find a way to consolidate these.
|
||||
EL.Parser.prototype.lookingAtNumber = function() {
|
||||
elisp.Parser.prototype.lookingAtNumber = function() {
|
||||
var pos = this.pos,
|
||||
rest = this.rest(),
|
||||
match = rest.match(/^[+-]?\d+(\.\d*)?[)\s\n]/)
|
||||
|
|
@ -229,46 +229,46 @@ EL.Parser.prototype.lookingAtNumber = function() {
|
|||
return (match != null);
|
||||
};
|
||||
|
||||
EL.Parser.prototype.lookingAtCons = function() {
|
||||
elisp.Parser.prototype.lookingAtCons = function() {
|
||||
var orig_pos = this.pos,
|
||||
_ = this.consumeChar(),
|
||||
__ = _ && this.peek() && this.parseExpression(),
|
||||
cdr = __ && this.peek() &&this.parseExpression();
|
||||
this.pos = orig_pos; // rewind, like it never happened.
|
||||
return _ == ')' || cdr && EL.typeOf(cdr) == 'array' && EL.isSymbol(cdr) && EL.val(cdr) == '.';
|
||||
return _ == ')' || cdr && elisp.typeOf(cdr) == 'array' && elisp.isSymbol(cdr) && elisp.val(cdr) == '.';
|
||||
};
|
||||
|
||||
EL.Parser.prototype.parseExpression = function() {
|
||||
elisp.Parser.prototype.parseExpression = function() {
|
||||
var value,
|
||||
c = this.peek();
|
||||
if (c == '(' && this.lookingAtCons()) {
|
||||
value = EL.consPair(this.parseCons());
|
||||
value = elisp.consPair(this.parseCons());
|
||||
}
|
||||
else if (c == '(') {
|
||||
var list = this.parseList();
|
||||
value = (list.length > 0) ? EL.list(list) : EL.nil;
|
||||
value = (list.length > 0) ? elisp.list(list) : elisp.nil;
|
||||
}
|
||||
else if (c == ')') {
|
||||
return this.consumeChar();
|
||||
}
|
||||
else if (c == "'") {
|
||||
this.consumeChar();
|
||||
value = EL.cons(EL.symbol('quote'), this.parseExpression());
|
||||
value = elisp.cons(elisp.symbol('quote'), this.parseExpression());
|
||||
}
|
||||
else if (c == '"') {
|
||||
value = EL.string(this.parseString());
|
||||
value = elisp.string(this.parseString());
|
||||
}
|
||||
else if (this.lookingAtNumber()) {
|
||||
value = EL.number(this.parseNumber());
|
||||
value = elisp.number(this.parseNumber());
|
||||
}
|
||||
else if (c) {
|
||||
value = EL.symbol(this.parseSymbol());
|
||||
value = elisp.symbol(this.parseSymbol());
|
||||
}
|
||||
else {
|
||||
if (this.pos == this.data.length) {
|
||||
print('[error] no more input. unterminated string or list? (continuing anyway)');
|
||||
}
|
||||
print('[warning] in EL.Parser.parseExpression: unrecognized char "' + c + '"');
|
||||
print('[warning] in elisp.Parser.parseExpression: unrecognized char "' + c + '"');
|
||||
print('this.pos = ' + this.pos);
|
||||
print('this.data.length = ' + this.data.length);
|
||||
print('this.rest = ' + this.rest());
|
||||
131
elisp/primitives.js
Normal file
131
elisp/primitives.js
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
////
|
||||
// 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.
|
||||
|
||||
|
||||
elisp.PrimitiveVariables = [
|
||||
['t', {
|
||||
type: 'variable',
|
||||
value: ['symbol', 't'],
|
||||
docstring: "true"
|
||||
}],
|
||||
['nil', {
|
||||
type: 'variable',
|
||||
value: ['symbol', 'nil'],
|
||||
docstring: "nil"
|
||||
}]
|
||||
];
|
||||
|
||||
elisp.PrimitiveFunctions = [];
|
||||
|
||||
// 'this' is bound to the elisp.Evaluator object when executing primitve functions
|
||||
elisp.definePrimitive = function(name, params, body, docstring) {
|
||||
elisp.PrimitiveFunctions.push([name, {
|
||||
type: 'primitive',
|
||||
name: name,
|
||||
params: params, // unused right now but should be checked
|
||||
docstring: docstring,
|
||||
body: body
|
||||
}]);
|
||||
};
|
||||
|
||||
elisp.notFunc = function(fn) {
|
||||
return function(x){ return !fn(x); };
|
||||
};
|
||||
|
||||
elisp.makePrimitiveBooleanFunc = function(fn) {
|
||||
return function(x){ return fn(x) ? elisp.t : elisp.nil; };
|
||||
};
|
||||
|
||||
elisp._definePrimitives = function() {
|
||||
elisp.definePrimitive('consp', ['symbol'], elisp.makePrimitiveBooleanFunc(elisp.isCons),
|
||||
"Return T if symbol is a cons, nil otherwise.");
|
||||
|
||||
elisp.definePrimitive('atom', ['symbol'], elisp.makePrimitiveBooleanFunc(elisp.isAtom),
|
||||
"Return T if symbol is not a cons or is nil, nil otherwise.");
|
||||
|
||||
elisp.definePrimitive('symbol-name', ['symbol'],
|
||||
function(symbol) { return elisp.string(elisp.val(symbol)); },
|
||||
"Return a symbol's name, a string.");
|
||||
|
||||
elisp.definePrimitive('string-match', ['regex', 'string', '&optional', 'start'],
|
||||
function(regex, string, start) {
|
||||
var index = start ? elisp.val(start) : 0,
|
||||
s = elisp.val(string).substring(index),
|
||||
match = s.match(new RegExp(elisp.val(regex))),
|
||||
found = match ? elisp.number(s.indexOf(match[0])) : elisp.nil;
|
||||
return found;},
|
||||
"Return the index of the char matching regex in string, beginning from start if available.");
|
||||
|
||||
// Right now a single string in the arg list will cause all the arguments
|
||||
// to be converted to strings similar to JavaScript. These
|
||||
// semantics suck and should change, not only for real emacs lisp compatibility.
|
||||
elisp.definePrimitive('+', [/*...*/],
|
||||
function() {
|
||||
var args = elisp.Util.shallowCopy(arguments),
|
||||
initial = elisp.inferType(args),
|
||||
type = initial[0];
|
||||
return elisp.Util.reduce(function(sum, n) {
|
||||
return [type, elisp.val(sum) + elisp.val(n)];
|
||||
}, initial, args);},
|
||||
"add two numbers");
|
||||
|
||||
elisp.definePrimitive('-', [/*...*/],
|
||||
function() {
|
||||
return elisp.Util.foldr(function(diff, n) {
|
||||
return elisp.number(elisp.val(diff) - elisp.val(n));
|
||||
}, elisp.number(0), elisp.Util.shallowCopy(arguments));},
|
||||
"subtract two numbers");
|
||||
|
||||
elisp.definePrimitive('*', [/*...*/],
|
||||
function() {
|
||||
return elisp.Util.reduce(function(prod, n) {
|
||||
return elisp.number(elisp.val(prod) * elisp.val(n));
|
||||
}, elisp.number(1), elisp.Util.shallowCopy(arguments));},
|
||||
"multiply two numbers");
|
||||
|
||||
elisp.definePrimitive('/', [/*...*/],
|
||||
function() {
|
||||
return elisp.Util.foldr(function(quot, n) {
|
||||
return elisp.number(elisp.val(quot) / elisp.val(n));
|
||||
}, elisp.number(1), elisp.Util.shallowCopy(arguments));
|
||||
},
|
||||
"divide two numbers");
|
||||
|
||||
elisp.definePrimitive('print', ['x'],
|
||||
function(x, tostring) {
|
||||
var buffer = "",
|
||||
tag = elisp.tag(x);
|
||||
function p(s) {
|
||||
if (tostring) buffer += s;
|
||||
else print(s);
|
||||
}
|
||||
if (tag == 'number' || tag == 'symbol' || tag == 'string') {
|
||||
p(elisp.val(x));
|
||||
}
|
||||
else if (tag == 'lambda') {
|
||||
var fn = elisp.val(x);
|
||||
p('(lambda ' + fn.name + ' (' + fn.params + ')\n');
|
||||
p(fn.body); // TODO lisp pretty print
|
||||
p(')');
|
||||
}
|
||||
else if (tag == 'list') {
|
||||
var recurse = arguments.callee; // far easier to remember than Y
|
||||
print('(', El.val(x).map(function(e){return (recurse(e, true) + ' ');}), ")");
|
||||
}
|
||||
else {
|
||||
print('unknown type: ' + x);
|
||||
}
|
||||
return elisp.nil;
|
||||
},
|
||||
"print an expression");
|
||||
|
||||
elisp.definePrimitive('hide-prompt', ['yes-or-no'],
|
||||
function(bool){ elisp.hidePrompt = !elisp.isNil(bool); },
|
||||
"Call with T to hide the prompt or nil to show it.");
|
||||
};
|
||||
elisp.initHook(elisp._definePrimitives);
|
||||
|
|
@ -6,32 +6,32 @@
|
|||
// Released under the terms of the MIT license. See the included file
|
||||
// LICENSE.
|
||||
|
||||
EL.eval = function(exprs) {
|
||||
var e = new EL.Evaluator();
|
||||
elisp.eval = function(exprs) {
|
||||
var e = new elisp.Evaluator();
|
||||
return e.evalExpressions(exprs);
|
||||
};
|
||||
|
||||
EL.parse = function(string) {
|
||||
var p = new EL.Parser();
|
||||
elisp.parse = function(string) {
|
||||
var p = new elisp.Parser();
|
||||
return p.parse(string);
|
||||
};
|
||||
|
||||
EL.parseOne = function(string) {
|
||||
return EL.parse(string)[0];
|
||||
elisp.parseOne = function(string) {
|
||||
return elisp.parse(string)[0];
|
||||
};
|
||||
|
||||
EL.read = EL.parseOne;
|
||||
EL.print = EL.Util.pp;
|
||||
elisp.read = elisp.parseOne;
|
||||
elisp.print = elisp.Util.pp;
|
||||
|
||||
EL.rep = function(string) {
|
||||
EL.print(EL.eval(EL.parse(string)));
|
||||
elisp.rep = function(string) {
|
||||
elisp.print(elisp.eval(elisp.parse(string)));
|
||||
};
|
||||
|
||||
EL.repl = function() {
|
||||
var p = new EL.Parser(),
|
||||
e = new EL.Evaluator();
|
||||
elisp.repl = function() {
|
||||
var p = new elisp.Parser(),
|
||||
e = new elisp.Evaluator();
|
||||
while (true) {
|
||||
if (!EL.hidePrompt) {
|
||||
if (!elisp.hidePrompt) {
|
||||
print("elisp> "); // i don't want a newline, grrrr
|
||||
}
|
||||
try {
|
||||
|
|
@ -40,11 +40,11 @@ EL.repl = function() {
|
|||
line = readline();
|
||||
}
|
||||
if (line.substring(0,1).toLowerCase() == 'q') return;
|
||||
EL.print(e.eval(p.parseOne(line)));
|
||||
elisp.print(e.eval(p.parseOne(line)));
|
||||
} catch (x) {
|
||||
if (x.evalError) {
|
||||
print('[error] ' + x.message + ': ' + x.expression);
|
||||
EL.print(x);
|
||||
elisp.print(x);
|
||||
}
|
||||
else throw(x);
|
||||
}
|
||||
|
|
@ -10,13 +10,13 @@
|
|||
**** Symbol Table **********************************************
|
||||
****************************************************************/
|
||||
|
||||
EL.SymbolTable = function(bindings) {
|
||||
elisp.SymbolTable = function(bindings) {
|
||||
this.symbols = [[]];
|
||||
this.level = 0;
|
||||
if (bindings) this.define(bindings);
|
||||
};
|
||||
|
||||
EL.SymbolTable.prototype.lookupWithScope = function(name) {
|
||||
elisp.SymbolTable.prototype.lookupWithScope = function(name) {
|
||||
var i = this.level,
|
||||
symbol;
|
||||
while (i >= 0) {
|
||||
|
|
@ -29,14 +29,14 @@ EL.SymbolTable.prototype.lookupWithScope = function(name) {
|
|||
return null;
|
||||
};
|
||||
|
||||
EL.SymbolTable.prototype.lookup = function(name) {
|
||||
elisp.SymbolTable.prototype.lookup = function(name) {
|
||||
var pair = this.lookupWithScope(name);
|
||||
return pair && pair[1];
|
||||
};
|
||||
|
||||
// store the given symbol/value pair in the symbol table at the current level.
|
||||
EL.SymbolTable.prototype.define = function(name, value) {
|
||||
if (value === undefined && EL.typeOf(name) == 'array') {
|
||||
elisp.SymbolTable.prototype.define = function(name, value) {
|
||||
if (value === undefined && elisp.typeOf(name) == 'array') {
|
||||
var bindings = name,
|
||||
i = 0,
|
||||
n = bindings.length,
|
||||
|
|
@ -55,7 +55,7 @@ EL.SymbolTable.prototype.define = function(name, value) {
|
|||
}
|
||||
};
|
||||
|
||||
EL.SymbolTable.prototype.pushScope = function(bindings) {
|
||||
elisp.SymbolTable.prototype.pushScope = function(bindings) {
|
||||
// print('>>> pushing scope <<<');
|
||||
// print('>>> level going from ' + this.level + ' to ' + (1+this.level));
|
||||
// print(bindings);
|
||||
|
|
@ -63,11 +63,11 @@ EL.SymbolTable.prototype.pushScope = function(bindings) {
|
|||
if (bindings) this.define(bindings);
|
||||
};
|
||||
|
||||
EL.SymbolTable.prototype.popScope = function() {
|
||||
elisp.SymbolTable.prototype.popScope = function() {
|
||||
--this.level;
|
||||
};
|
||||
|
||||
EL.SymbolTable.prototype.set = function(name, value) {
|
||||
elisp.SymbolTable.prototype.set = function(name, value) {
|
||||
var pair = this.lookupWithScope(name),
|
||||
level = pair[0];
|
||||
this.symbols[level][name] = value;
|
||||
113
elisp/types.js
Normal file
113
elisp/types.js
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
////
|
||||
// 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.
|
||||
|
||||
// data types are simple tags
|
||||
elisp._defineTags = function() {
|
||||
elisp.tags = ['symbol', 'string', 'number', 'cons', 'lambda', 'regex'];
|
||||
elisp.tags.each = Array.prototype.each;
|
||||
|
||||
// define constructors for the primitive types (box values)
|
||||
// e.g. elisp.symbol('foo') => ['symbol', 'foo']
|
||||
elisp.tags.each(function(tag) {
|
||||
// don't clobber custom constructors
|
||||
if (elisp[tag] === undefined) {
|
||||
elisp[tag] = function(value) {
|
||||
return [tag, value];
|
||||
};
|
||||
}
|
||||
// tag type tests
|
||||
var isTag = function(expr) {
|
||||
return (elisp.tag(expr) == tag);
|
||||
};
|
||||
elisp['is' + tag.camelize()] = isTag;
|
||||
});
|
||||
};
|
||||
elisp.initHook(elisp._defineTags);
|
||||
|
||||
// shorthands to save my fingers
|
||||
elisp._defineConstants = function() {
|
||||
elisp.nil = elisp.symbol('nil');
|
||||
elisp.t = elisp.symbol('t');
|
||||
};
|
||||
elisp.initHook(elisp._defineConstants);
|
||||
|
||||
|
||||
// retrieve the tag from a value
|
||||
elisp.tag = function(expr) {
|
||||
elisp.assert(function() { var f='tag'; return elisp.typeOf(expr) == 'array'; }, expr);
|
||||
return expr[0];
|
||||
};
|
||||
|
||||
// unbox a value
|
||||
elisp.val = function(expr) {
|
||||
elisp.assert(function() { var f='val'; return elisp.typeOf(expr) == 'array'; }, expr);
|
||||
return expr[1];
|
||||
};
|
||||
|
||||
elisp.symbolName = function(symbol) {
|
||||
// elisp.Util.pp(symbol);
|
||||
elisp.assert(function(){ var f='symbolName'; return elisp.isSymbol(symbol); }, symbol);
|
||||
return elisp.val(symbol);
|
||||
};
|
||||
|
||||
elisp.isNilSymbol = function(expr) {
|
||||
return (elisp.isSymbol(expr) && elisp.symbolName(expr) == 'nil');
|
||||
};
|
||||
|
||||
elisp.isNil = function(expr) {
|
||||
return elisp.isNilSymbol(expr);
|
||||
};
|
||||
|
||||
elisp.list = function(exprs) {
|
||||
var list = elisp.nil,
|
||||
i = exprs.length;
|
||||
while (--i >= 0) {
|
||||
list = elisp.cons(exprs[i], list);
|
||||
}
|
||||
return list;
|
||||
};
|
||||
|
||||
elisp.isList = function(expr) {
|
||||
return (elisp.isNil(expr) || elisp.isCons(expr));
|
||||
};
|
||||
|
||||
elisp.isAtom = function(expr) {
|
||||
return !elisp.isCons(expr);
|
||||
};
|
||||
|
||||
elisp.inferType = function(exprs) {
|
||||
var type = 'number',
|
||||
initial = 0,
|
||||
i = exprs.length-1;
|
||||
while(i >= 0) {
|
||||
if (elisp.isString(exprs[i--])) {
|
||||
type = 'string';
|
||||
initial = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return elisp[type](initial);
|
||||
};
|
||||
|
||||
|
||||
// special forms
|
||||
|
||||
elisp.isSpecialForm = function(name, expr) {
|
||||
var tag = elisp.tag(expr),
|
||||
car = elisp.typeOf(expr) == 'array' && elisp.val(expr)[0],
|
||||
thisName = car && elisp.symbolName(car);
|
||||
return (tag == 'cons' && thisName == name);
|
||||
};
|
||||
|
||||
elisp.isQuote = function(expr){return elisp.isSpecialForm('quote', expr);};
|
||||
elisp.isDefVar = function(expr){return elisp.isSpecialForm('defvar', expr);};
|
||||
elisp.isDefFunc = function(expr){return elisp.isSpecialForm('defun', expr);};
|
||||
elisp.isSet = function(expr){return elisp.isSpecialForm('set', expr);};
|
||||
elisp.isSetq = function(expr){return elisp.isSpecialForm('setq', expr);};
|
||||
elisp.isCond = function(expr){return elisp.isSpecialForm('cond', expr);};
|
||||
elisp.isIf = function(expr){return elisp.isSpecialForm('if', expr);};
|
||||
|
|
@ -10,10 +10,10 @@
|
|||
**** Utilities *************************************************
|
||||
****************************************************************/
|
||||
|
||||
EL.Util = function(){};
|
||||
elisp.Util = function(){};
|
||||
|
||||
// aka foldl
|
||||
EL.Util.reduce = function(fn, accum, list) {
|
||||
elisp.Util.reduce = function(fn, accum, list) {
|
||||
var i = 0,
|
||||
n = list.length;
|
||||
while (i < n) {
|
||||
|
|
@ -22,7 +22,7 @@ EL.Util.reduce = function(fn, accum, list) {
|
|||
return accum;
|
||||
};
|
||||
|
||||
EL.Util.foldr = function(fn, end, list) {
|
||||
elisp.Util.foldr = function(fn, end, list) {
|
||||
var i = list.length-1;
|
||||
while (i >= 0) {
|
||||
end = fn(list[i--], end);
|
||||
|
|
@ -30,7 +30,7 @@ EL.Util.foldr = function(fn, end, list) {
|
|||
return end;
|
||||
};
|
||||
|
||||
EL.Util.shallowCopy = function(list) {
|
||||
elisp.Util.shallowCopy = function(list) {
|
||||
var i = 0,
|
||||
n = list.length,
|
||||
result = [];
|
||||
|
|
@ -42,7 +42,7 @@ EL.Util.shallowCopy = function(list) {
|
|||
|
||||
|
||||
// i'm sorry
|
||||
EL.Util.pp = function(x, indent, key, noprint) {
|
||||
elisp.Util.pp = function(x, indent, key, noprint) {
|
||||
if (indent === undefined) {
|
||||
indent = 0;
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@ EL.Util.pp = function(x, indent, key, noprint) {
|
|||
// space += " ";
|
||||
// }
|
||||
|
||||
switch (EL.typeOf(x)) {
|
||||
switch (elisp.typeOf(x)) {
|
||||
case 'object':
|
||||
if (key) {
|
||||
printB(space + key + ': {');
|
||||
|
|
@ -85,7 +85,7 @@ EL.Util.pp = function(x, indent, key, noprint) {
|
|||
printB(space + ' {');
|
||||
}
|
||||
for (var a in x) {
|
||||
printB(EL.Util.pp(x[a], 1+indent, a, true));
|
||||
printB(elisp.Util.pp(x[a], 1+indent, a, true));
|
||||
printB(', ');
|
||||
}
|
||||
printB(space + "} ");
|
||||
|
|
@ -102,8 +102,8 @@ EL.Util.pp = function(x, indent, key, noprint) {
|
|||
|
||||
case 'array':
|
||||
// nil special case
|
||||
if (x.length == 2 && EL.tag(x) == 'symbol' && EL.val(x) == 'nil') {
|
||||
return EL.Util.pp(null, indent, key);
|
||||
if (x.length == 2 && elisp.tag(x) == 'symbol' && elisp.val(x) == 'nil') {
|
||||
return elisp.Util.pp(null, indent, key);
|
||||
}
|
||||
|
||||
if (key) {
|
||||
|
|
@ -115,7 +115,7 @@ EL.Util.pp = function(x, indent, key, noprint) {
|
|||
var n = x.length, i = 0;
|
||||
while (i < n) {
|
||||
if (i > 0) printB(', ');
|
||||
printB(EL.Util.pp(x[i++], 1+indent, undefined, true));
|
||||
printB(elisp.Util.pp(x[i++], 1+indent, undefined, true));
|
||||
}
|
||||
printB(space + ']');
|
||||
break;
|
||||
Loading…
Reference in a new issue