From fdfca00fe4cfa92e36d23486127488dd2955e00c Mon Sep 17 00:00:00 2001 From: Sami Samhuri Date: Mon, 14 Dec 2009 09:47:29 -0800 Subject: [PATCH] Renamed EL to elisp. Use absolute path to spidermonkey (till CommonJS conversion). --- el.sh | 3 - el/list.js | 103 ---------------------------- el/primitives.js | 131 ----------------------------------- el/types.js | 113 ------------------------------ el.js => elisp.js | 27 ++++---- elisp.sh | 3 + {el => elisp}/evaluator.js | 136 ++++++++++++++++++------------------- {el => elisp}/init.js | 12 ++-- {el => elisp}/jsExt.js | 6 +- elisp/list.js | 103 ++++++++++++++++++++++++++++ {el => elisp}/parser.js | 66 +++++++++--------- elisp/primitives.js | 131 +++++++++++++++++++++++++++++++++++ {el => elisp}/repl.js | 32 ++++----- {el => elisp}/symtab.js | 16 ++--- elisp/types.js | 113 ++++++++++++++++++++++++++++++ {el => elisp}/util.js | 20 +++--- 16 files changed, 508 insertions(+), 507 deletions(-) delete mode 100755 el.sh delete mode 100644 el/list.js delete mode 100644 el/primitives.js delete mode 100644 el/types.js rename el.js => elisp.js (77%) create mode 100755 elisp.sh rename {el => elisp}/evaluator.js (51%) rename {el => elisp}/init.js (72%) rename {el => elisp}/jsExt.js (92%) create mode 100644 elisp/list.js rename {el => elisp}/parser.js (77%) create mode 100644 elisp/primitives.js rename {el => elisp}/repl.js (55%) rename {el => elisp}/symtab.js (78%) create mode 100644 elisp/types.js rename {el => elisp}/util.js (82%) diff --git a/el.sh b/el.sh deleted file mode 100755 index f65829b..0000000 --- a/el.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -rlwrap js el.js diff --git a/el/list.js b/el/list.js deleted file mode 100644 index 6641a56..0000000 --- a/el/list.js +++ /dev/null @@ -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; -}; - diff --git a/el/primitives.js b/el/primitives.js deleted file mode 100644 index 2a5f1a4..0000000 --- a/el/primitives.js +++ /dev/null @@ -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); \ No newline at end of file diff --git a/el/types.js b/el/types.js deleted file mode 100644 index 5acd320..0000000 --- a/el/types.js +++ /dev/null @@ -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);}; diff --git a/el.js b/elisp.js similarity index 77% rename from el.js rename to elisp.js index 39da85b..bc42005 100644 --- a/el.js +++ b/elisp.js @@ -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(); \ No newline at end of file +elisp.repl(); \ No newline at end of file diff --git a/elisp.sh b/elisp.sh new file mode 100755 index 0000000..82efbff --- /dev/null +++ b/elisp.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +rlwrap /opt/local/bin/js elisp.js diff --git a/el/evaluator.js b/elisp/evaluator.js similarity index 51% rename from el/evaluator.js rename to elisp/evaluator.js index bc16804..208a71f 100644 --- a/el/evaluator.js +++ b/elisp/evaluator.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; }; diff --git a/el/init.js b/elisp/init.js similarity index 72% rename from el/init.js rename to elisp/init.js index dacd70d..d76547c 100644 --- a/el/init.js +++ b/elisp/init.js @@ -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(); } }; diff --git a/el/jsExt.js b/elisp/jsExt.js similarity index 92% rename from el/jsExt.js rename to elisp/jsExt.js index 8fd5b58..eba99a8 100644 --- a/el/jsExt.js +++ b/elisp/jsExt.js @@ -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 + ")"); } diff --git a/elisp/list.js b/elisp/list.js new file mode 100644 index 0000000..8c9d161 --- /dev/null +++ b/elisp/list.js @@ -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; +}; + diff --git a/el/parser.js b/elisp/parser.js similarity index 77% rename from el/parser.js rename to elisp/parser.js index 7f9edf6..b3307db 100644 --- a/el/parser.js +++ b/elisp/parser.js @@ -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()); diff --git a/elisp/primitives.js b/elisp/primitives.js new file mode 100644 index 0000000..58687ad --- /dev/null +++ b/elisp/primitives.js @@ -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); diff --git a/el/repl.js b/elisp/repl.js similarity index 55% rename from el/repl.js rename to elisp/repl.js index 25b41d9..b04b8da 100644 --- a/el/repl.js +++ b/elisp/repl.js @@ -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); } diff --git a/el/symtab.js b/elisp/symtab.js similarity index 78% rename from el/symtab.js rename to elisp/symtab.js index 435bae6..6b2b493 100644 --- a/el/symtab.js +++ b/elisp/symtab.js @@ -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; diff --git a/elisp/types.js b/elisp/types.js new file mode 100644 index 0000000..5200c22 --- /dev/null +++ b/elisp/types.js @@ -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);}; diff --git a/el/util.js b/elisp/util.js similarity index 82% rename from el/util.js rename to elisp/util.js index a0e1e96..23d2dd0 100644 --- a/el/util.js +++ b/elisp/util.js @@ -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;