Fixed symbol evaluation to actually lookup variables. Updated README.

This commit is contained in:
Sami Samhuri 2009-12-20 16:56:44 -08:00
parent fe67ea2e82
commit 2919672af3
7 changed files with 60 additions and 70 deletions

81
README
View file

@ -14,10 +14,9 @@ Latest version available on github:
(or "You must be kidding") (or "You must be kidding")
========================== ==========================
I'm not 100% sure why I think this might be useful to somebody. The The idea of editing code directly on github or bitbucket in a web
idea of editing code directly on github or bitbucket in a web browser browser is pretty cool, and if you're going to do such a thing why not
is pretty cool though, and if you're going to do such a thing why not use the best tools available? IMO those tools are written in Emacs
use the best tools available. IMO those tools are written in Emacs
Lisp so I would like to use them in the browser. Maybe with some Lisp so I would like to use them in the browser. Maybe with some
HTML5 offline goodness thrown in. HTML5 offline goodness thrown in.
@ -25,84 +24,77 @@ Seeing Ymacs[1] in action[2] was also an inspiration to start this
project as I've had it on my TODO list for several months now. Emacs project as I've had it on my TODO list for several months now. Emacs
in the browser could be a reality; Ymacs is proof. in the browser could be a reality; Ymacs is proof.
With Palm's release of "Project Ares"[3] the need for such tools is
beginning to be a reality. I'm no longer that uncertain why such a
beast would be useful. The question now is whether using something
like Google's Native Client and original C version of Emacs is the way
to go, or if a slower implementation in JavaScript is feasible for
real use.
[1] http://www.ymacs.org/ [1] http://www.ymacs.org/
[2] http://www.ymacs.org/demo/ [2] http://www.ymacs.org/demo/
[3] http://pdnblog.palm.com/2009/12/project-ares-open-beta/
[4] http://code.google.com/p/nativeclient/
Getting Started Getting Started
=============== ===============
Command line I'm currently using CommonJS via Narwhal[1] with the JavaScriptCore
------------ engine. I've run the tests under v8 as well and they all pass, but
not everything is tested by any means.
If you have rlwrap and js then just run el.sh. If not install a [1] http://narwhaljs.org/
JavaScript shell of your choice, and I recommend you install rlwrap
(readline wrap). Edit el.sh to reflect whether or not you use rlwrap If you have rlwrap and narwhal then just run elisp.sh. If not install
and your shell's name or path. Then run el.sh. I've only run this a JavaScript shell of your choice, and I recommend you install rlwrap
under SpiderMonkey so I have no idea if it works under any other (readline wrap). Edit elisp.sh to reflect whether or not you use
implementations. Glad to accept info and/or patches. rlwrap and your shell's name or path, and then run elisp.sh.
Type a 'Q' or 'q' to quit the repl. Or anything that starts with Q, Type a 'Q' or 'q' to quit the repl. Or anything that starts with Q,
like 'quickly quit lisp!'. (If lisp freaks out because you entered ^D like 'quickly quit lisp!'. (If lisp freaks out because you entered ^D
or something ^C or ^Z should still get you out.) or something ^C or ^Z should still get you out.)
Emacs
-----
I use Emacs and js-comint.el[3] with inferior-js set to
/opt/local/bin/js, which is SpiderMonkey[4] from MacPorts[5] on Snow
Leopard.
[3] http://js-comint-el.sourceforge.net/
[4] http://www.mozilla.org/js/spidermonkey/
[5] http://www.macports.org/
For loading code C-c b sends the buffer to the JS repl. I also use
the EL.rep (read-eval-print), EL.parse, EL.eval, and EL.print
functions defined in el/repl.js
Here's an example: Here's an example:
% cd Projects/elisp.js % cd Projects/elisp.js
% rlwrap js % ./elisp.sh
js> load('el.js')
elisp> elisp>
(defvar *directory* "/Users/sjs" "My home directory") (defvar *directory* "/Users/sjs" "My home directory")
nil
elisp> elisp>
*directory* *directory*
["string", "/Users/sjs"] "/Users/sjs"
elisp> elisp>
(setq foo 1 bar 2 baz 3) (setq foo 1 bar 2 baz 3)
["number", 3] 3
elisp> elisp>
(/ (+ foo bar baz) 3) (/ (+ foo bar baz) 3)
["number", 2] 2
elisp> elisp>
(string-match "[a-z]lurgh" (symbol-name 'foo-blurgh)) (string-match "[a-z]lurgh" (symbol-name 'foo-blurgh))
["number", 4] 4
elisp> elisp>
q q
js> %
(If you know how to print without a trailing newline in SpiderMonkey (If you know how to print without a trailing newline in JavaScript
please let me know.) please let me know.)
There are other interfaces into the parser and evaluator besides the There are other interfaces into the parser and evaluator besides the
shortcuts. new EL.Parser([input]) returns a parser object and likewise shortcuts. new elisp.parser.Parser([input]) returns a parser object
new EL.Evaluator([exprs]) returns an evaluator object. and likewise new elisp.evaluator.Evaluator([exprs]) returns an
evaluator object.
Mainly toy functions that do extremely simple operations on numbers Mainly toy functions that do extremely simple operations on numbers
and strings can be implemented right now. Stay tuned, or better yet and strings can be implemented right now. Stay tuned, or better yet
hack away and submit a pull request on github[6]. hack away and submit a pull request on github.
[6] http://github.com/samsonjs/elisp.js
What's here? What's here?
============ ============
Not much compared to the real thing but it's a decent start for < 1000 Not much compared to the real thing but it's a decent start for 1500
lines. lines.
* parser/reader. there's no lexing to tokens we go straight to tagged * parser/reader. there's no lexing to tokens we go straight to tagged
@ -115,7 +107,7 @@ lines.
* expression evaluator * expression evaluator
* simple tagged primitive types * primitive types
(string, symbol, lambda, number, cons) (string, symbol, lambda, number, cons)
* special forms for defvar, defun, set, setq, if, and quote * special forms for defvar, defun, set, setq, if, and quote
@ -124,6 +116,3 @@ lines.
* a few primitive math ops * a few primitive math ops
(thanks to JS' overloading + works on strings too) (thanks to JS' overloading + works on strings too)
* 2 horrible print functions
(JS "pretty" printer & a half-assed Lisp print)

View file

@ -58,7 +58,6 @@ Evaluator.prototype.evalExpressions = function(expressions) {
} }
} }
} }
// Utils.pp(result);
return result; return result;
}; };
@ -84,13 +83,10 @@ Evaluator.prototype.apply = function(func, args) {
this.functions.pushScope(); this.functions.pushScope();
this.variables.pushScope(func.params.map(function(e, i){ this.variables.pushScope(func.params.map(function(e, i){
var name = e.symbolName(), var name = e.symbolName(),
value = { value = this.eval(args[i]);
type: 'variable', return [name, {type:'variable', value:value}];
value: this.eval(args[i]) }).unlist());
}; result = this.evalExpressions(func.body);
return [name, value];
}));
result = func.body.map(function(e) {return this.eval(e); }).last();
this.functions.popScope(); this.functions.popScope();
this.variables.popScope(); this.variables.popScope();
} }
@ -102,7 +98,7 @@ Evaluator.prototype.eval = function(expr) {
//utils.pp(expr); //utils.pp(expr);
var result, x, var result, x,
tag = expr.tag(); tag = expr.tag();
if (expr.isAtom()) { if (expr.isString() || expr.isNumber()) {
result = expr; result = expr;
} }
else if (expr.isSymbol()) { else if (expr.isSymbol()) {
@ -121,9 +117,9 @@ Evaluator.prototype.eval = function(expr) {
result = expr.cdr(); result = expr.cdr();
} }
else if (expr.isDefvar()) { else if (expr.isDefvar()) {
var name = expr.cadr().symbolName(), // 2nd param var name = expr.nth(1).symbolName(),
value = this.eval(expr.caddr()), // 3rd param value = this.eval(expr.nth(2)),
docstring = expr.cadddr(); // 4th param docstring = expr.nth(3);
// TODO check for re-definitions // TODO check for re-definitions
this.defineVar(name, value, docstring); this.defineVar(name, value, docstring);
result = type.NIL; result = type.NIL;
@ -138,8 +134,8 @@ Evaluator.prototype.eval = function(expr) {
result = type.NIL; result = type.NIL;
} }
else if (expr.isSet()) { else if (expr.isSet()) {
var name = expr.car().symbolName(), var name = expr.nth(1).symbolName(),
value = this.eval(expr.cdr()); value = this.eval(expr.nth(2));
this.setVar(name, value); this.setVar(name, value);
result = value; result = value;
} }
@ -188,10 +184,12 @@ Evaluator.prototype.eval = function(expr) {
result = this.apply(func, args); result = this.apply(func, args);
} }
else { else {
result = type.NIL;
this.error('undefined-func', name); this.error('undefined-func', name);
} }
} }
else { else {
result = type.NIL;
this.error('not-expr', expr); this.error('not-expr', expr);
} }
// print('RESULT: ' + result); // print('RESULT: ' + result);

View file

@ -30,15 +30,15 @@ LispCons.prototype.cdr = function() {
}; };
LispCons.prototype.cadr = function() { LispCons.prototype.cadr = function() {
return this.cdr().car(); return this.nth(1);
}; };
LispCons.prototype.caddr = function() { LispCons.prototype.caddr = function() {
return this.cdr().cdr().car(); return this.nth(2);
}; };
LispCons.prototype.cadddr = function() { LispCons.prototype.cadddr = function() {
return this.cdr().cdr().cdr().car(); return this.nth(3);
}; };
LispCons.prototype.length = function() { LispCons.prototype.length = function() {
@ -60,7 +60,6 @@ LispCons.prototype.map = function(fn) {
var list = [], var list = [],
i = 0, i = 0,
cons = this; cons = this;
// print('[LispCons.map] calling cons.isNil - cons: ' + cons + ' - _car: ' + cons._car + ' _cdr: ' + this._cdr);
while (!cons.isNil()) { while (!cons.isNil()) {
list.push(fn(cons.car(), i)); list.push(fn(cons.car(), i));
cons = cons.cdr(); cons = cons.cdr();
@ -79,7 +78,7 @@ LispCons.prototype.reduce = function(accum, fn) {
}; };
LispCons.prototype.unlist = function() { LispCons.prototype.unlist = function() {
return this.reduce([], function(x){return x;}); return this.reduce([], function(acc, x){acc.push(x); return acc;});
}; };
LispCons.prototype.nth = function(n) { LispCons.prototype.nth = function(n) {
@ -92,7 +91,7 @@ LispCons.prototype.nth = function(n) {
cons = cons.cdr(); cons = cons.cdr();
++i; ++i;
} }
return n > --i ? type.NIL : e; return n > (i-1) ? type.NIL : e;
}; };
LispCons.prototype.nthcdr = function(n) { LispCons.prototype.nthcdr = function(n) {

View file

@ -60,7 +60,6 @@ var repl = function() {
} catch (x) { } catch (x) {
if (x.evalError) { if (x.evalError) {
print('[error] ' + x.message + ': ' + x.expression); print('[error] ' + x.message + ': ' + x.expression);
utils.pp(x);
} }
else throw(x); else throw(x);
} }

View file

@ -39,6 +39,7 @@ SymbolTable.prototype.lookup = function(name) {
// store the given symbol/value pair in the symbol table at the current level. // store the given symbol/value pair in the symbol table at the current level.
SymbolTable.prototype.define = function(name, value) { SymbolTable.prototype.define = function(name, value) {
// print('###### REAL DEFINE: ' + name + ' = ' + value); // print('###### REAL DEFINE: ' + name + ' = ' + value);
// print(' (TYPES): ' + utils.typeOf(name) + ' = ' + utils.typeOf(value));
if (value === undefined && utils.typeOf(name) == 'array') { if (value === undefined && utils.typeOf(name) == 'array') {
var bindings = name, var bindings = name,
i = 0, i = 0,

View file

@ -69,7 +69,11 @@ var pp = function(x, toString) {
s = ''; s = '';
} }
else if (!x.repr) { else if (!x.repr) {
s = '[UNKNOWN VALUE: ' + x + ']'; // what are you?! s = '[UNKNOWN VALUE: ' + x + ' = {\n'; // what are you?!
for (var y in x) {
s += ' ' + y + ': ' + x[y] + "\n";
}
s += '}';
} }
else { else {
s = x.repr(); s = x.repr();