mirror of
https://github.com/samsonjs/elisp.js.git
synced 2026-04-27 15:07:47 +00:00
[NEW] if, negative numbers, regex type & literal, more...
Sorry for the massive commit. I'll try not to do this. Several new features: * Proper conses and lists (probably slow, can optimize later) * Parse negative numbers * Regular expressions (piggyback off js regex, not emacs compatible) * string-match and symbol-name primitives * if special form * car, cdr, cadr, caddr, cadddr, nth, nthcdr, map, length, null, symbolp, listp, stringp, numberp, etc. only in JS now but I will expose them in lisp as primitives soon. Fixed: * setq now silently defines undefined variables some miscellaneous things: * simple init system to specify init code that loads after all defs have been read * String.camelize function stolen from Prototype
This commit is contained in:
parent
3fae23b89d
commit
77f1a9eb93
3 changed files with 484 additions and 240 deletions
4
README
4
README
|
|
@ -2,7 +2,7 @@ Emacs Lisp implementation in JavaScript.
|
||||||
|
|
||||||
Copyright (c) 2009 Sami Samhuri - sami.samhuri@gmail.com
|
Copyright (c) 2009 Sami Samhuri - sami.samhuri@gmail.com
|
||||||
|
|
||||||
Released under the terms of the MIT license. See the include file
|
Released under the terms of the MIT license. See the included file
|
||||||
LICENSE.
|
LICENSE.
|
||||||
|
|
||||||
Latest version available on github:
|
Latest version available on github:
|
||||||
|
|
@ -71,7 +71,7 @@ lines.
|
||||||
* symbol table
|
* symbol table
|
||||||
(functions & variables separate)
|
(functions & variables separate)
|
||||||
|
|
||||||
* lexical scope
|
* (broken) lexical scoping
|
||||||
|
|
||||||
* expression evaluator
|
* expression evaluator
|
||||||
|
|
||||||
|
|
|
||||||
14
TODO
14
TODO
|
|
@ -3,11 +3,11 @@ TODO
|
||||||
|
|
||||||
* implement all Emacs Lisp types/objects
|
* implement all Emacs Lisp types/objects
|
||||||
|
|
||||||
* relational operators: < > <= >= =
|
* relational operators: < > <= >= = not
|
||||||
|
|
||||||
* boolean operator: not
|
* special forms: lambda, if, cond, and, or, let, let*, letf, letf*
|
||||||
|
|
||||||
* special forms: if, cond, and, or, let, let*, letf, letf*
|
* successfully interpret ~/config/emacs one sexp at a time
|
||||||
|
|
||||||
* primitives: list/cons functions, string functions,
|
* primitives: list/cons functions, string functions,
|
||||||
apply, eval,
|
apply, eval,
|
||||||
|
|
@ -15,8 +15,10 @@ TODO
|
||||||
|
|
||||||
* macros
|
* macros
|
||||||
|
|
||||||
* dynamic scoping
|
* dynamic scoping (replace the current symbol table entirely)
|
||||||
|
|
||||||
* merge with Ymacs?
|
|
||||||
|
|
||||||
* look into CommonJS
|
* look into CommonJS
|
||||||
|
|
||||||
|
* merge with Ymacs? (probably not going to happen, would have to
|
||||||
|
steal what we can from them)
|
||||||
|
|
||||||
|
|
|
||||||
706
el.js
706
el.js
|
|
@ -1,47 +1,332 @@
|
||||||
var spidermonkeykludge = 0;
|
////
|
||||||
// spidermonkey doesn't like a C-style comment at the beginning
|
// Emacs Lisp implementation in JavaScript.
|
||||||
|
//
|
||||||
/*
|
// Copyright (c) 2009 Sami Samhuri - sami.samhuri@gmail.com
|
||||||
* Emacs Lisp implementation in JavaScript.
|
//
|
||||||
*
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
* Copyright (c) 2009 Sami Samhuri - sami.samhuri@gmail.com
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
*
|
// in the Software without restriction, including without limitation the rights
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
* of this software and associated documentation files (the "Software"), to deal
|
// copies of the Software, and to permit persons to whom the Software is
|
||||||
* in the Software without restriction, including without limitation the rights
|
// furnished to do so, subject to the following conditions:
|
||||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
//
|
||||||
* copies of the Software, and to permit persons to whom the Software is
|
// The above copyright notice and this permission notice shall be included in
|
||||||
* furnished to do so, subject to the following conditions:
|
// all copies or substantial portions of the Software.
|
||||||
*
|
//
|
||||||
* The above copyright notice and this permission notice shall be included in
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
* all copies or substantial portions of the Software.
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
*
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
// THE SOFTWARE.
|
||||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
//
|
||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
///
|
||||||
* THE SOFTWARE.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// can't live without the basics
|
|
||||||
|
|
||||||
Array.prototype.each = function(fn) {
|
|
||||||
var i = 0,
|
|
||||||
n = this.length;
|
|
||||||
while (i < n) {
|
|
||||||
fn(this[i], i);
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// our namespace
|
// our namespace
|
||||||
var EL = function(){};
|
var EL = function(){};
|
||||||
|
|
||||||
|
// Use initHook() to specify initialization routines at the very end
|
||||||
|
// 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);
|
||||||
|
};
|
||||||
|
EL.init = function() {
|
||||||
|
var i = 0,
|
||||||
|
n = EL._initHooks.length;
|
||||||
|
while (i < n) {
|
||||||
|
EL._initHooks[i++].call();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Just a little sugar
|
||||||
|
EL._jsExt = function() {
|
||||||
|
Array.prototype.each = function(fn) {
|
||||||
|
var i = 0,
|
||||||
|
n = this.length;
|
||||||
|
while (i < n) {
|
||||||
|
fn(this[i], i);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Thanks Prototype
|
||||||
|
String.prototype.camelize = function() {
|
||||||
|
var oStringList = this.split('_');
|
||||||
|
if (oStringList.length == 1)
|
||||||
|
return oStringList[0][0].toUpperCase() + oStringList[0].substring(1);
|
||||||
|
|
||||||
|
var camelizedString = oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1);
|
||||||
|
|
||||||
|
for (var i = 1, len = oStringList.length; i < len; i++) {
|
||||||
|
var s = oStringList[i];
|
||||||
|
camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return camelizedString;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A typeOf function that distinguishes between objects, arrays,
|
||||||
|
// and null.
|
||||||
|
EL.typeOf = function(value) {
|
||||||
|
var s = typeof value;
|
||||||
|
if (s === 'object') {
|
||||||
|
if (value) {
|
||||||
|
if (typeof value.length === 'number' &&
|
||||||
|
!(value.propertyIsEnumerable('length')) &&
|
||||||
|
typeof value.splice === 'function') {
|
||||||
|
s = 'array';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s = 'null';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO throw something more informative
|
||||||
|
EL.assert = function(condition, message) {
|
||||||
|
if (!condition()) {
|
||||||
|
throw("assertion failed: " + condition + " (" + message + ")");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
EL.initHook(EL._jsExt);
|
||||||
|
|
||||||
|
|
||||||
|
//****************************************************************
|
||||||
|
// **** Lisp Support **********************************************
|
||||||
|
// ****************************************************************/
|
||||||
|
|
||||||
|
// 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.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.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;
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
list.isList = true;
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
|
EL.isList = function(expr) {
|
||||||
|
return (EL.isNil(expr) || EL.isCons(expr));
|
||||||
|
};
|
||||||
|
|
||||||
|
EL.isAtom = function(expr) {
|
||||||
|
return EL.isString(expr) || EL.isNumber(expr) || EL.isRegex(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);
|
||||||
|
};
|
||||||
|
|
||||||
|
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);};
|
||||||
|
|
||||||
|
EL.eval = function(exprs) {
|
||||||
|
var e = new EL.Evaluator();
|
||||||
|
return e.evalExpressions(exprs);
|
||||||
|
};
|
||||||
|
|
||||||
|
EL.parse = function(string) {
|
||||||
|
var p = new EL.Parser();
|
||||||
|
return p.parse(string);
|
||||||
|
};
|
||||||
|
|
||||||
|
EL.parseOne = function(string) {
|
||||||
|
return EL.parse(string)[0];
|
||||||
|
};
|
||||||
|
EL.read = EL.parseOne;
|
||||||
|
|
||||||
|
EL.rep = function(string) {
|
||||||
|
EL.print(EL.eval(EL.parse(string)));
|
||||||
|
};
|
||||||
|
|
||||||
|
EL.repl = function() {
|
||||||
|
var p = new EL.Parser(),
|
||||||
|
e = new EL.Evaluator();
|
||||||
|
while (true) {
|
||||||
|
print("elisp> "); // i don't want a newline, grrrr
|
||||||
|
try {
|
||||||
|
var line = readline();
|
||||||
|
if (line && line[0] && line[0].toLowerCase() == 'q') return;
|
||||||
|
EL.print(e.eval(p.parseOne(line)));
|
||||||
|
} catch (x) {
|
||||||
|
if (x.evalError) {
|
||||||
|
print('[error] ' + x.message + ': ' + x.expression);
|
||||||
|
EL.print(x);
|
||||||
|
}
|
||||||
|
else throw(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************************
|
||||||
|
****************************************************************/
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************
|
/****************************************************************
|
||||||
**** Parser ****************************************************
|
**** Parser ****************************************************
|
||||||
|
|
@ -172,8 +457,22 @@ EL.Parser.prototype.parseSymbol = function() {
|
||||||
return symbol;
|
return symbol;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Probably easy to break
|
||||||
|
EL.Parser.prototype.parseRegex = function() {
|
||||||
|
// consume initial slash
|
||||||
|
this.consumeChar();
|
||||||
|
var self = this;
|
||||||
|
return new RegExp(this.parseUntil(/\//, '', function(s,c){
|
||||||
|
if (c == '\\') {
|
||||||
|
c = self.consumeChar();
|
||||||
|
}
|
||||||
|
return s + c;
|
||||||
|
}, true /* consume terminator */));
|
||||||
|
};
|
||||||
|
|
||||||
EL.Parser.prototype.parseNumber = function() {
|
EL.Parser.prototype.parseNumber = function() {
|
||||||
var value = this.parseUntil(/[^\d]/, 0, function(n,c) {
|
var sign = this.peek() == '-' ? this.consumeChar() : '+',
|
||||||
|
value = this.parseUntil(/[^\d]/, 0, function(n,c) {
|
||||||
return n*10 + parseInt(c);
|
return n*10 + parseInt(c);
|
||||||
});
|
});
|
||||||
if (this.peek() == '.') {
|
if (this.peek() == '.') {
|
||||||
|
|
@ -181,13 +480,13 @@ EL.Parser.prototype.parseNumber = function() {
|
||||||
var decimal = this.parseUntil(/[^\d]/, '', function(s,c) {return s + c;});
|
var decimal = this.parseUntil(/[^\d]/, '', function(s,c) {return s + c;});
|
||||||
value = parseFloat('' + value + '.' + decimal);
|
value = parseFloat('' + value + '.' + decimal);
|
||||||
}
|
}
|
||||||
return value;
|
return sign == '-' ? -1*value : value;
|
||||||
};
|
};
|
||||||
|
|
||||||
EL.Parser.prototype.lookingAtNumber = function() {
|
EL.Parser.prototype.lookingAtNumber = function() {
|
||||||
var pos = this.pos,
|
var pos = this.pos,
|
||||||
rest = this.rest(),
|
rest = this.rest(),
|
||||||
match = rest.match(/^\d+(\.\d+)?[\s)\n]/) || rest.match(/^\d+(\.\d+)?$/);
|
match = rest.match(/^(-)?\d+(\.\d+)?[\s)\n]/) || rest.match(/^(-)?\d+(\.\d+)?$/);
|
||||||
return (match != null);
|
return (match != null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -197,35 +496,46 @@ EL.Parser.prototype.lookingAtCons = function() {
|
||||||
_ = this.parseExpression(),
|
_ = this.parseExpression(),
|
||||||
cdr = this.parseExpression();
|
cdr = this.parseExpression();
|
||||||
this.pos = orig_pos; // rewind, like it never happened.
|
this.pos = orig_pos; // rewind, like it never happened.
|
||||||
return EL.typeOf(cdr) == 'array' && EL.tag(cdr) == 'symbol' && EL.val(cdr) == '.';
|
return EL.typeOf(cdr) == 'array' && EL.isSymbol(cdr) && EL.val(cdr) == '.';
|
||||||
};
|
};
|
||||||
|
|
||||||
EL.Parser.prototype.parseExpression = function() {
|
EL.Parser.prototype.parseExpression = function() {
|
||||||
var value,
|
var value,
|
||||||
c = this.peek();
|
c = this.peek();
|
||||||
if (c == '(' && this.lookingAtCons()) {
|
if (c == '(' && this.lookingAtCons()) {
|
||||||
value = ['cons', this.parseCons()];
|
value = EL.consPair(this.parseCons());
|
||||||
}
|
}
|
||||||
else if (c == '(') {
|
else if (c == '(') {
|
||||||
var list = this.parseList();
|
var list = this.parseList();
|
||||||
value = (list.length > 0) ? ['list', list] : EL.nil;
|
value = (list.length > 0) ? EL.list(list) : EL.nil;
|
||||||
}
|
}
|
||||||
else if (c == ')') {
|
else if (c == ')') {
|
||||||
return this.consumeChar();
|
return this.consumeChar();
|
||||||
}
|
}
|
||||||
else if (c == "'") {
|
else if (c == "'") {
|
||||||
this.consumeChar();
|
this.consumeChar();
|
||||||
value = ['list', [['symbol', 'quote']]];
|
value = EL.cons(EL.symbol('quote'), this.parseExpression());
|
||||||
value[1].push(this.parseExpression());
|
|
||||||
}
|
}
|
||||||
else if (c == '"') {
|
else if (c == '"') {
|
||||||
value = ['string', this.parseString()];
|
value = EL.string(this.parseString());
|
||||||
|
}
|
||||||
|
else if (c == '/') {
|
||||||
|
value = EL.regex(this.parseRegex());
|
||||||
}
|
}
|
||||||
else if (this.lookingAtNumber()) {
|
else if (this.lookingAtNumber()) {
|
||||||
value = ['number', this.parseNumber()];
|
value = EL.number(this.parseNumber());
|
||||||
}
|
}
|
||||||
else if (c) {
|
else if (c) {
|
||||||
value = ['symbol', this.parseSymbol()];
|
value = EL.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('this.pos = ' + this.pos);
|
||||||
|
print('this.data.length = ' + this.data.length);
|
||||||
|
print('this.rest = ' + this.rest());
|
||||||
}
|
}
|
||||||
this.consumeWhitespace();
|
this.consumeWhitespace();
|
||||||
return value;
|
return value;
|
||||||
|
|
@ -314,22 +624,44 @@ EL.SymbolTable.prototype.set = function(name, value) {
|
||||||
EL.PrimitiveVariables = [
|
EL.PrimitiveVariables = [
|
||||||
['t', {
|
['t', {
|
||||||
type: 'variable',
|
type: 'variable',
|
||||||
value: true,
|
value: ['symbol', 't'],
|
||||||
docstring: "true"
|
docstring: "true"
|
||||||
}],
|
}],
|
||||||
['nil', {
|
['nil', {
|
||||||
type: 'variable',
|
type: 'variable',
|
||||||
value: null,
|
value: ['symbol', 'nil'],
|
||||||
docstring: "nil"
|
docstring: "nil"
|
||||||
}]
|
}]
|
||||||
];
|
];
|
||||||
|
|
||||||
// this is bound to the EL.Evaluator object
|
// 'this' is bound to the EL.Evaluator object
|
||||||
EL.PrimitiveFunctions = [
|
EL.PrimitiveFunctions = [
|
||||||
|
['symbol-name', {
|
||||||
|
type: 'primitive',
|
||||||
|
name: 'symbol-name',
|
||||||
|
params: ['symbol'],
|
||||||
|
body: function(symbol) { return EL.string(EL.val(symbol)); },
|
||||||
|
docstring: "Return a symbol's name, a string."
|
||||||
|
}],
|
||||||
|
|
||||||
|
['string-match', {
|
||||||
|
type: 'primitive',
|
||||||
|
name: 'string-match',
|
||||||
|
params: ['regex', 'string', '&optional', 'start'],
|
||||||
|
body: function(regex, string, start) {
|
||||||
|
var index = start ? EL.val(start) : 0,
|
||||||
|
s = EL.val(string).substring(index),
|
||||||
|
match = s.match(EL.val(regex)),
|
||||||
|
found = match ? EL.number(s.indexOf(match[0])) : EL.nil;
|
||||||
|
return found;
|
||||||
|
},
|
||||||
|
docstring: "Return the index of the char matching regex in string, beginning from start if available."
|
||||||
|
}],
|
||||||
|
|
||||||
['+', {
|
['+', {
|
||||||
type: 'primitive',
|
type: 'primitive',
|
||||||
name: '+',
|
name: '+',
|
||||||
params: [],
|
params: [/* ... */],
|
||||||
body: function() {
|
body: function() {
|
||||||
var args = EL.Util.shallowCopy(arguments),
|
var args = EL.Util.shallowCopy(arguments),
|
||||||
initial = EL.inferType(args),
|
initial = EL.inferType(args),
|
||||||
|
|
@ -343,33 +675,33 @@ EL.PrimitiveFunctions = [
|
||||||
['-', {
|
['-', {
|
||||||
type: 'primitive',
|
type: 'primitive',
|
||||||
name: '-',
|
name: '-',
|
||||||
params: [],
|
params: [/* ... */],
|
||||||
body: function() {
|
body: function() {
|
||||||
return EL.Util.foldr(function(diff, n) {
|
return EL.Util.foldr(function(diff, n) {
|
||||||
return ['number', EL.val(diff) - EL.val(n)];
|
return EL.number(EL.val(diff) - EL.val(n));
|
||||||
}, ['number', 0], EL.Util.shallowCopy(arguments));
|
}, EL.number(0), EL.Util.shallowCopy(arguments));
|
||||||
},
|
},
|
||||||
docstring: "subtract two numbers"
|
docstring: "subtract two numbers"
|
||||||
}],
|
}],
|
||||||
['*', {
|
['*', {
|
||||||
type: 'primitive',
|
type: 'primitive',
|
||||||
name: '*',
|
name: '*',
|
||||||
params: [],
|
params: [/* ... */],
|
||||||
body: function() {
|
body: function() {
|
||||||
return EL.Util.reduce(function(prod, n) {
|
return EL.Util.reduce(function(prod, n) {
|
||||||
return ['number', EL.val(prod) * EL.val(n)];
|
return EL.number(EL.val(prod) * EL.val(n));
|
||||||
}, ['number', 1], EL.Util.shallowCopy(arguments));
|
}, EL.number(1), EL.Util.shallowCopy(arguments));
|
||||||
},
|
},
|
||||||
docstring: "multiply two numbers"
|
docstring: "multiply two numbers"
|
||||||
}],
|
}],
|
||||||
['/', {
|
['/', {
|
||||||
type: 'primitive',
|
type: 'primitive',
|
||||||
name: '/',
|
name: '/',
|
||||||
params: [],
|
params: [/* ... */],
|
||||||
body: function() {
|
body: function() {
|
||||||
return EL.Util.foldr(function(quot, n) {
|
return EL.Util.foldr(function(quot, n) {
|
||||||
return ['number', EL.val(quot) / EL.val(n)];
|
return EL.number(EL.val(quot) / EL.val(n));
|
||||||
}, ['number', 1], EL.Util.shallowCopy(arguments));
|
}, EL.number(1), EL.Util.shallowCopy(arguments));
|
||||||
},
|
},
|
||||||
docstring: "divide two numbers"
|
docstring: "divide two numbers"
|
||||||
}],
|
}],
|
||||||
|
|
@ -387,7 +719,7 @@ EL.PrimitiveFunctions = [
|
||||||
if (tag == 'number' || tag == 'symbol' || tag == 'string') {
|
if (tag == 'number' || tag == 'symbol' || tag == 'string') {
|
||||||
p(EL.val(x));
|
p(EL.val(x));
|
||||||
}
|
}
|
||||||
else if (tag == 'function') {
|
else if (tag == 'lambda') {
|
||||||
var fn = EL.val(x);
|
var fn = EL.val(x);
|
||||||
p('(lambda ' + fn.name + ' (' + fn.params + ')\n');
|
p('(lambda ' + fn.name + ' (' + fn.params + ')\n');
|
||||||
p(fn.body); // TODO lisp pretty print
|
p(fn.body); // TODO lisp pretty print
|
||||||
|
|
@ -480,30 +812,54 @@ EL.Evaluator.prototype.defineVar = function(symbol, value, docstring) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
EL.Evaluator.prototype.setVar = function(symbol, value) {
|
EL.Evaluator.prototype.setVar = function(symbol, value, create) {
|
||||||
var valueObject = this.lookupVar(symbol);
|
var valueObject = this.lookupVar(symbol);
|
||||||
|
if (!valueObject) {
|
||||||
|
if (create) {
|
||||||
|
this.defineVar(symbol, value);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.error('undefined-var', symbol);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
valueObject.value = value;
|
valueObject.value = value;
|
||||||
this.variables.set(symbol, valueObject);
|
this.variables.set(symbol, valueObject);
|
||||||
};
|
};
|
||||||
|
|
||||||
EL.Evaluator.prototype.defineFunc = function(symbol, params, body, docstring) {
|
EL.Evaluator.prototype.defineFunc = function(symbol, params, body, docstring) {
|
||||||
this.functions.define(symbol, {
|
this.functions.define(symbol, {
|
||||||
type: 'function',
|
type: 'lambda',
|
||||||
name: symbol,
|
name: symbol,
|
||||||
params: EL.val(params),
|
params: params,
|
||||||
body: body,
|
body: body,
|
||||||
docstring: docstring || "(undocumented)"
|
docstring: docstring || "(undocumented)"
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
EL.Evaluator.prototype.call = function(func, args) {
|
EL.Evaluator.prototype.doIf = function(condition, trueBlock, nilBlock) {
|
||||||
|
return EL.isNil(condition) ? this.eval(nilBlock) : this.eval(trueBlock);
|
||||||
|
};
|
||||||
|
|
||||||
|
EL.Evaluator.prototype.doCond = function(exprs) {
|
||||||
|
print('----- COND (doCond) -----');
|
||||||
|
EL.print(exprs);
|
||||||
|
return EL.nil;
|
||||||
|
};
|
||||||
|
|
||||||
|
EL.Evaluator.prototype.apply = function(func, args) {
|
||||||
var result;
|
var result;
|
||||||
if (func.type === 'primitive') {
|
if (func.type === 'primitive') {
|
||||||
|
// print('APPLY: ');
|
||||||
|
// EL.print(func);
|
||||||
|
// print('WITH: ');
|
||||||
|
// EL.print(args);
|
||||||
|
// print('------');
|
||||||
result = func.body.apply(this, args);
|
result = func.body.apply(this, args);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.functions.pushScope();
|
this.functions.pushScope();
|
||||||
this.variables.pushScope(func.params.map(function(e, i){
|
this.variables.pushScope(EL.listMap(func.params, function(e, i){
|
||||||
var name = EL.symbolName(e),
|
var name = EL.symbolName(e),
|
||||||
value = {
|
value = {
|
||||||
type: 'variable',
|
type: 'variable',
|
||||||
|
|
@ -511,7 +867,8 @@ EL.Evaluator.prototype.call = function(func, args) {
|
||||||
};
|
};
|
||||||
return [name, value];
|
return [name, value];
|
||||||
}));
|
}));
|
||||||
result = this.evalExpressions(func.body);
|
result = EL.listLast(EL.listMap(func.body,
|
||||||
|
function(e) {return this.eval(e); }));
|
||||||
this.functions.popScope();
|
this.functions.popScope();
|
||||||
this.variables.popScope();
|
this.variables.popScope();
|
||||||
}
|
}
|
||||||
|
|
@ -519,21 +876,19 @@ EL.Evaluator.prototype.call = function(func, args) {
|
||||||
};
|
};
|
||||||
|
|
||||||
EL.Evaluator.prototype.eval = function(expr) {
|
EL.Evaluator.prototype.eval = function(expr) {
|
||||||
|
// print("EVAL: " + EL.typeOf(expr));
|
||||||
|
// EL.print(expr);
|
||||||
var result, x,
|
var result, x,
|
||||||
tag = EL.tag(expr);
|
tag = EL.tag(expr);
|
||||||
if (EL.isAtom(expr)) {
|
if (EL.isAtom(expr)) {
|
||||||
return expr;
|
result = expr;
|
||||||
}
|
}
|
||||||
else if (tag == 'symbol') {
|
else if (EL.isSymbol(expr)) {
|
||||||
var name = EL.val(expr);
|
var name = EL.val(expr);
|
||||||
x = this.lookupVar(name);
|
x = this.lookupVar(name);
|
||||||
if (x == null) this.error('undefined-var', name);
|
if (x == null) this.error('undefined-var', name);
|
||||||
result = x.value;
|
result = x.value;
|
||||||
}
|
}
|
||||||
// else if (expr[0] == 'cons') {
|
|
||||||
// var cons = expr[1];
|
|
||||||
// result = [this.eval(cons[0]), this.eval(cons[1])];
|
|
||||||
// }
|
|
||||||
|
|
||||||
///////////////////
|
///////////////////
|
||||||
// special forms //
|
// special forms //
|
||||||
|
|
@ -541,24 +896,22 @@ EL.Evaluator.prototype.eval = function(expr) {
|
||||||
// (many could be in lisp when there are macros) //
|
// (many could be in lisp when there are macros) //
|
||||||
|
|
||||||
else if (EL.isQuote(expr)) {
|
else if (EL.isQuote(expr)) {
|
||||||
result = EL.val(EL.val(expr));
|
result = EL.cdr(expr);
|
||||||
}
|
}
|
||||||
else if (EL.isDefVar(expr)) {
|
else if (EL.isDefVar(expr)) {
|
||||||
var val = EL.val(expr),
|
var name = EL.symbolName(EL.cadr(expr)), // 2nd param
|
||||||
name = EL.symbolName(val[1]),
|
value = this.eval(EL.caddr(expr)), // 3rd param
|
||||||
value = this.eval(val[2]),
|
docstring = EL.cadddr(expr); // 4th param
|
||||||
docstring = val[3];
|
|
||||||
// TODO check for re-definitions
|
// TODO check for re-definitions
|
||||||
this.defineVar(name, value, docstring);
|
this.defineVar(name, value, docstring);
|
||||||
result = EL.nil;
|
result = EL.nil;
|
||||||
}
|
}
|
||||||
else if (EL.isDefFunc(expr)) {
|
else if (EL.isDefFunc(expr)) {
|
||||||
var val = EL.val(expr),
|
var name = EL.symbolName(EL.nth(1, expr)),
|
||||||
name = EL.symbolName(val[1]),
|
params = EL.nth(2, expr),
|
||||||
params = val[2],
|
d = EL.nth(3, expr),
|
||||||
docstring = EL.isString(val[3]) && val[3],
|
docstring = EL.isString(d) && d,
|
||||||
body = val.slice(docstring ? 4 : 3);
|
body = EL.nthcdr(docstring ? 3 : 2, expr);
|
||||||
// TODO check for re-definitions
|
|
||||||
this.defineFunc(name, params, body, docstring);
|
this.defineFunc(name, params, body, docstring);
|
||||||
result = EL.nil;
|
result = EL.nil;
|
||||||
}
|
}
|
||||||
|
|
@ -566,15 +919,18 @@ EL.Evaluator.prototype.eval = function(expr) {
|
||||||
var val = EL.val(expr),
|
var val = EL.val(expr),
|
||||||
name = EL.symbolName(val[1]),
|
name = EL.symbolName(val[1]),
|
||||||
value = this.eval(val[2]);
|
value = this.eval(val[2]);
|
||||||
result = this.setVar(name, value);
|
this.setVar(name, value);
|
||||||
|
result = value;
|
||||||
}
|
}
|
||||||
else if (EL.isSetq(expr)) {
|
else if (EL.isSetq(expr)) {
|
||||||
var val = EL.val(expr),
|
var val = EL.val(expr),
|
||||||
i = 0;
|
i = 0,
|
||||||
while (EL.isSymbol(val[i+1])) {
|
n = val.length;
|
||||||
|
while (i+1 < n && EL.isSymbol(val[i+1])) {
|
||||||
var name = EL.symbolName(val[i+1]),
|
var name = EL.symbolName(val[i+1]),
|
||||||
value = this.eval(val[i+2]);
|
value = this.eval(val[i+2]);
|
||||||
result = this.setVar(name, value);
|
this.setVar(name, value, true);
|
||||||
|
result = value;
|
||||||
i += 2;
|
i += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -583,30 +939,38 @@ EL.Evaluator.prototype.eval = function(expr) {
|
||||||
condition = this.eval(val[1]),
|
condition = this.eval(val[1]),
|
||||||
trueBlock = val[2],
|
trueBlock = val[2],
|
||||||
nilBlock = val[3];
|
nilBlock = val[3];
|
||||||
result = this.primitiveIf(condition, trueBlock, nilBlock);
|
result = this.doIf(condition, trueBlock, nilBlock);
|
||||||
|
}
|
||||||
|
else if (EL.isCond(expr)) {
|
||||||
|
var val = EL.val(expr),
|
||||||
|
list = val[1],
|
||||||
|
condition = EL.car(list),
|
||||||
|
body = EL.cdr(list),
|
||||||
|
rest = val.slice(2);
|
||||||
|
result = this.doCond(exprs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// function application
|
// function application
|
||||||
else if (EL.tag(expr) == 'list') {
|
else if (EL.isCons(expr)) {
|
||||||
var list = expr[1],
|
var name = EL.car(expr),
|
||||||
car = list[0],
|
rest = EL.cdr(expr),
|
||||||
cdr = list.slice(1),
|
|
||||||
func, args;
|
func, args;
|
||||||
while (!(EL.isFunction(car) || EL.isSymbol(car))) {
|
while (!EL.isSymbol(name)) {
|
||||||
car = this.eval(car);
|
name = this.eval(name);
|
||||||
}
|
}
|
||||||
if ((func = this.lookupFunc(car[1]))) {
|
if ((func = this.lookupFunc(EL.symbolName(name)))) {
|
||||||
var self = this;
|
var self = this;
|
||||||
args = cdr.map(function(e){return self.eval(e);});
|
args = EL.listMap(rest, function(e){return self.eval(e);});
|
||||||
result = this.call(func, args);
|
result = this.apply(func, args);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.error('undefined-func', car);
|
this.error('undefined-func', name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.error('not-expr', expr);
|
this.error('not-expr', expr);
|
||||||
}
|
}
|
||||||
|
// print('RESULT: ' + result);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -615,131 +979,6 @@ EL.Evaluator.prototype.eval = function(expr) {
|
||||||
****************************************************************/
|
****************************************************************/
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************
|
|
||||||
**** Lisp Support **********************************************
|
|
||||||
****************************************************************/
|
|
||||||
|
|
||||||
EL.nil = ['symbol', 'nil'];
|
|
||||||
EL.t = ['symbol', 't'];
|
|
||||||
|
|
||||||
EL.typeOf = function(value) {
|
|
||||||
var s = typeof value;
|
|
||||||
if (s === 'object') {
|
|
||||||
if (value) {
|
|
||||||
if (typeof value.length === 'number' &&
|
|
||||||
!(value.propertyIsEnumerable('length')) &&
|
|
||||||
typeof value.splice === 'function') {
|
|
||||||
s = 'array';
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
s = 'null';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
};
|
|
||||||
|
|
||||||
EL.assert = function(condition, message) {
|
|
||||||
if (!condition()) {
|
|
||||||
throw("assertion failed: " + condition + " (" + message + ")");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
EL.tag = function(expr) {
|
|
||||||
EL.assert(function() { var f='tag'; return EL.typeOf(expr) == 'array'; }, expr);
|
|
||||||
return expr[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
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 symbol[1];
|
|
||||||
};
|
|
||||||
|
|
||||||
EL.isSymbol = function(expr) {
|
|
||||||
return (EL.tag(expr) == 'symbol');
|
|
||||||
};
|
|
||||||
|
|
||||||
EL.isString = function(expr) {
|
|
||||||
return (EL.tag(expr) == 'string');
|
|
||||||
};
|
|
||||||
|
|
||||||
EL.isList = function(expr) {
|
|
||||||
return (EL.tag(expr) == 'list');
|
|
||||||
};
|
|
||||||
|
|
||||||
EL.isFunction = function(expr) {
|
|
||||||
return (EL.tag(expr) == 'function');
|
|
||||||
};
|
|
||||||
|
|
||||||
EL.isAtom = function(expr) {
|
|
||||||
return EL.tag(expr) == 'string' || EL.tag(expr) == 'number';
|
|
||||||
};
|
|
||||||
|
|
||||||
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 [type, initial];
|
|
||||||
};
|
|
||||||
|
|
||||||
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 == 'list' && 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);};
|
|
||||||
|
|
||||||
EL.eval = function(exprs) {
|
|
||||||
var e = new EL.Evaluator();
|
|
||||||
return e.evalExpressions(exprs);
|
|
||||||
};
|
|
||||||
|
|
||||||
EL.parse = function(string) {
|
|
||||||
var p = new EL.Parser();
|
|
||||||
return p.parse(string);
|
|
||||||
};
|
|
||||||
|
|
||||||
EL.parseOne = function(string) {
|
|
||||||
return EL.parse(string)[0];
|
|
||||||
};
|
|
||||||
EL.read = EL.parseOne;
|
|
||||||
|
|
||||||
EL.rep = function(string) {
|
|
||||||
EL.print(EL.eval(EL.read(string)));
|
|
||||||
};
|
|
||||||
|
|
||||||
EL.repl = function() {
|
|
||||||
var p = new EL.Parser(),
|
|
||||||
e = new EL.Evaluator();
|
|
||||||
while (true) {
|
|
||||||
print("elisp> ");
|
|
||||||
EL.print(e.eval(p.parseOne(readline())));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************
|
|
||||||
****************************************************************/
|
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************
|
/****************************************************************
|
||||||
**** Utilities *************************************************
|
**** Utilities *************************************************
|
||||||
|
|
@ -817,12 +1056,13 @@ EL.Util.pp = function(x, indent, key, noprint) {
|
||||||
printB(space + key + ': {');
|
printB(space + key + ': {');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printB(space + '{');
|
printB(space + ' {');
|
||||||
}
|
}
|
||||||
for (var a in x) {
|
for (var a in x) {
|
||||||
printB(EL.Util.pp(x[a], 1+indent, a, true));
|
printB(EL.Util.pp(x[a], 1+indent, a, true));
|
||||||
|
printB(', ');
|
||||||
}
|
}
|
||||||
printB(space + "}");
|
printB(space + "} ");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'string':
|
case 'string':
|
||||||
|
|
@ -872,11 +1112,13 @@ EL.Util.pp = function(x, indent, key, noprint) {
|
||||||
}
|
}
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
var s = buffer;
|
if (noprint) return buffer;
|
||||||
if (!noprint) dumpBuffer();
|
else dumpBuffer();
|
||||||
return s;
|
|
||||||
};
|
};
|
||||||
EL.print = EL.Util.pp;
|
EL.print = EL.Util.pp;
|
||||||
|
|
||||||
|
// everything is defined, initialize
|
||||||
|
EL.init();
|
||||||
|
|
||||||
// spidermonkey doesn't like a C-style comment at the end either
|
// spidermonkey doesn't like a C-style comment at the end either
|
||||||
spidermonkeykludge = 1;
|
spidermonkeykludge = 1;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue