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

83
README
View file

@ -14,10 +14,9 @@ Latest version available on github:
(or "You must be kidding")
==========================
I'm not 100% sure why I think this might be useful to somebody. The
idea of editing code directly on github or bitbucket in a web browser
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
The idea of editing code directly on github or bitbucket in a web
browser is pretty cool, and if you're going to do such a thing why not
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
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
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/
[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
===============
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
JavaScript shell of your choice, and I recommend you install rlwrap
(readline wrap). Edit el.sh to reflect whether or not you use rlwrap
and your shell's name or path. Then run el.sh. I've only run this
under SpiderMonkey so I have no idea if it works under any other
implementations. Glad to accept info and/or patches.
[1] http://narwhaljs.org/
If you have rlwrap and narwhal then just run elisp.sh. If not install
a JavaScript shell of your choice, and I recommend you install rlwrap
(readline wrap). Edit elisp.sh to reflect whether or not you use
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,
like 'quickly quit lisp!'. (If lisp freaks out because you entered ^D
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:
% cd Projects/elisp.js
% rlwrap js
js> load('el.js')
elisp>
% ./elisp.sh
elisp>
(defvar *directory* "/Users/sjs" "My home directory")
nil
elisp>
*directory*
["string", "/Users/sjs"]
"/Users/sjs"
elisp>
(setq foo 1 bar 2 baz 3)
["number", 3]
3
elisp>
(/ (+ foo bar baz) 3)
["number", 2]
2
elisp>
(string-match "[a-z]lurgh" (symbol-name 'foo-blurgh))
["number", 4]
4
elisp>
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.)
There are other interfaces into the parser and evaluator besides the
shortcuts. new EL.Parser([input]) returns a parser object and likewise
new EL.Evaluator([exprs]) returns an evaluator object.
shortcuts. new elisp.parser.Parser([input]) returns a parser object
and likewise new elisp.evaluator.Evaluator([exprs]) returns an
evaluator object.
Mainly toy functions that do extremely simple operations on numbers
and strings can be implemented right now. Stay tuned, or better yet
hack away and submit a pull request on github[6].
[6] http://github.com/samsonjs/elisp.js
hack away and submit a pull request on github.
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.
* parser/reader. there's no lexing to tokens we go straight to tagged
@ -115,7 +107,7 @@ lines.
* expression evaluator
* simple tagged primitive types
* primitive types
(string, symbol, lambda, number, cons)
* special forms for defvar, defun, set, setq, if, and quote
@ -124,6 +116,3 @@ lines.
* a few primitive math ops
(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;
};
@ -84,13 +83,10 @@ Evaluator.prototype.apply = function(func, args) {
this.functions.pushScope();
this.variables.pushScope(func.params.map(function(e, i){
var name = e.symbolName(),
value = {
type: 'variable',
value: this.eval(args[i])
};
return [name, value];
}));
result = func.body.map(function(e) {return this.eval(e); }).last();
value = this.eval(args[i]);
return [name, {type:'variable', value:value}];
}).unlist());
result = this.evalExpressions(func.body);
this.functions.popScope();
this.variables.popScope();
}
@ -102,7 +98,7 @@ Evaluator.prototype.eval = function(expr) {
//utils.pp(expr);
var result, x,
tag = expr.tag();
if (expr.isAtom()) {
if (expr.isString() || expr.isNumber()) {
result = expr;
}
else if (expr.isSymbol()) {
@ -121,9 +117,9 @@ Evaluator.prototype.eval = function(expr) {
result = expr.cdr();
}
else if (expr.isDefvar()) {
var name = expr.cadr().symbolName(), // 2nd param
value = this.eval(expr.caddr()), // 3rd param
docstring = expr.cadddr(); // 4th param
var name = expr.nth(1).symbolName(),
value = this.eval(expr.nth(2)),
docstring = expr.nth(3);
// TODO check for re-definitions
this.defineVar(name, value, docstring);
result = type.NIL;
@ -138,8 +134,8 @@ Evaluator.prototype.eval = function(expr) {
result = type.NIL;
}
else if (expr.isSet()) {
var name = expr.car().symbolName(),
value = this.eval(expr.cdr());
var name = expr.nth(1).symbolName(),
value = this.eval(expr.nth(2));
this.setVar(name, value);
result = value;
}
@ -188,10 +184,12 @@ Evaluator.prototype.eval = function(expr) {
result = this.apply(func, args);
}
else {
result = type.NIL;
this.error('undefined-func', name);
}
}
else {
result = type.NIL;
this.error('not-expr', expr);
}
// print('RESULT: ' + result);

View file

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

View file

@ -60,7 +60,6 @@ var repl = function() {
} catch (x) {
if (x.evalError) {
print('[error] ' + x.message + ': ' + x.expression);
utils.pp(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.
SymbolTable.prototype.define = function(name, value) {
// print('###### REAL DEFINE: ' + name + ' = ' + value);
// print(' (TYPES): ' + utils.typeOf(name) + ' = ' + utils.typeOf(value));
if (value === undefined && utils.typeOf(name) == 'array') {
var bindings = name,
i = 0,

View file

@ -218,7 +218,7 @@ var inferType = function(exprs) {
var type_name = 'number',
initial = 0,
i = exprs.length-1;
while(i >= 0) {
while (i >= 0) {
if (!exprs[i--].isNumber()) {
type_name = 'string';
initial = '';

View file

@ -69,7 +69,11 @@ var pp = function(x, toString) {
s = '';
}
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 {
s = x.repr();