mirror of
https://github.com/samsonjs/elisp.js.git
synced 2026-04-27 15:07:47 +00:00
Fixed symbol evaluation to actually lookup variables. Updated README.
This commit is contained in:
parent
fe67ea2e82
commit
2919672af3
7 changed files with 60 additions and 70 deletions
81
README
81
README
|
|
@ -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)
|
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -218,7 +218,7 @@ var inferType = function(exprs) {
|
||||||
var type_name = 'number',
|
var type_name = 'number',
|
||||||
initial = 0,
|
initial = 0,
|
||||||
i = exprs.length-1;
|
i = exprs.length-1;
|
||||||
while(i >= 0) {
|
while (i >= 0) {
|
||||||
if (!exprs[i--].isNumber()) {
|
if (!exprs[i--].isNumber()) {
|
||||||
type_name = 'string';
|
type_name = 'string';
|
||||||
initial = '';
|
initial = '';
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue