mirror of
https://github.com/samsonjs/elisp.js.git
synced 2026-03-25 09:15:49 +00:00
131 lines
4.1 KiB
JavaScript
131 lines
4.1 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.
|
|
|
|
|
|
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);
|