Fixed exponent notation. Changed primitive definitions.

This commit is contained in:
Sami Samhuri 2009-12-13 21:06:13 -08:00
parent 22856cc925
commit 39a5b52e15
5 changed files with 102 additions and 84 deletions

10
TODO
View file

@ -3,9 +3,17 @@ TODO
* TESTS * TESTS
* create a separate float type
* parse simple character literals e.g. ?a ?\s ?\\ ... the rest can wait.
* parse all the symbols on this page:
http://www.gnu.org/software/emacs/manual/html_node/elisp/Symbol-Type.html#Symbol-Type
* look into na-cl for better performance * look into na-cl for better performance
* implement all Emacs Lisp types/objects * implement all Emacs Lisp types/objects on this page:
http://www.gnu.org/software/emacs/manual/html_node/elisp/Programming-Types.html#Programming-Types
* relational operators: < > <= >= = not * relational operators: < > <= >= = not

View file

@ -7,10 +7,6 @@
// LICENSE. // LICENSE.
/****************************************************************
**** Evaluation ************************************************
****************************************************************/
EL.Evaluator = function(exprs) { EL.Evaluator = function(exprs) {
this.expressions = exprs; this.expressions = exprs;
this.variables = new EL.SymbolTable(EL.PrimitiveVariables); this.variables = new EL.SymbolTable(EL.PrimitiveVariables);
@ -242,6 +238,4 @@ EL.Evaluator.prototype.doCond = function(exprs) {
return EL.nil; return EL.nil;
}; };
/****************************************************************
****************************************************************/

View file

@ -156,6 +156,9 @@ EL.Parser.prototype.parseRegex = function() {
// //
// * Exponential notation for floats, e.g. 1.5e2 (150.0) or 420e-1 (42.0) // * Exponential notation for floats, e.g. 1.5e2 (150.0) or 420e-1 (42.0)
// (There is no trailing . allowed anywhere in exponent notation) // (There is no trailing . allowed anywhere in exponent notation)
//
// Binary, octal, hex, or arbitrary radix integers not yet parsed.
// (e.g. #x100 == #o400 == #b100000000 == #24rag
EL.Parser.prototype.parseNumber = function() { EL.Parser.prototype.parseNumber = function() {
var value = this.parseIntOrFloat(), var value = this.parseIntOrFloat(),
exponentAllowed = value === parseInt(value), exponentAllowed = value === parseInt(value),
@ -179,41 +182,60 @@ EL.Parser.prototype.parseNumber = function() {
EL.Parser.prototype.parseIntOrFloat = function() { EL.Parser.prototype.parseIntOrFloat = function() {
this.exponentAllowed = true; this.exponentAllowed = true;
var sign = this.peek() == '-' || this.peek() == '+' ? this.consumeChar() : '+', var sign = this.peek() == '-' || this.peek() == '+' ? this.consumeChar() : '+',
value;
// There may or may not be an integer part of the number.
if (this.peek() != '.') {
value = this.parseUntil(/[^\d]/, 0, function(n,c) { value = this.parseUntil(/[^\d]/, 0, function(n,c) {
return n*10 + parseInt(c); return n*10 + parseInt(c);
}); });
}
// if we see a . there might be a float to parse // if we see a . there might be a float to parse
if (this.peek() == '.') { if (this.peek() == '.') {
this.exponentAllowed = false;
this.consumeChar(); this.consumeChar();
if (this.peek() && this.peek().match(/\d/)) { if (this.peek() && this.peek().match(/\d/)) {
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 may be undefined at this point
value = parseFloat('' + (value||'') + '.' + decimal);
}
else {
this.exponentAllowed = false;
} }
} }
// Value can technically be undefined but the regex prevents it from
// ever being so.
return sign == '-' ? -1*value : value; return sign == '-' ? -1*value : value;
}; };
// These regexes matches all the inputs specified above parseNumber. // These regexes matches all the inputs specified above parseNumber.
// They are paramount as they exclude some invalid cases the parser
// itself doesn't catch. Sloppy, should be fixed in the future.
// The reason there are so many is that we can't match the end of
// string or some chars in the same regex.
//
// TODO: pick up Friedl and find a way to consolidate these.
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]/) match = rest.match(/^[+-]?\d+(\.\d*)?[)\s\n]/)
|| rest.match(/^[+-]?\d+(\.\d*)?$/) || rest.match(/^[+-]?\d+(\.\d*)?$/)
|| rest.match(/^[+-]?\d+(\.\d+)?([eE][+-]?\d+)?[)\s\n]/) || rest.match(/^[+-]?\d+(\.\d+)?([eE][+-]?\d+)?[)\s\n]/)
|| rest.match(/^[+-]?\d+(\.\d+)?([eE][+-]?\d+)?$/); || rest.match(/^[+-]?\d+(\.\d+)?([eE][+-]?\d+)?$/)
|| rest.match(/^[+-]?(\d+)?\.\d+([eE][+-]?\d+)?[)\s\n]/)
|| rest.match(/^[+-]?(\d+)?\.\d+([eE][+-]?\d+)?$/);
return (match != null); return (match != null);
}; };
EL.Parser.prototype.lookingAtCons = function() { EL.Parser.prototype.lookingAtCons = function() {
var orig_pos = this.pos, var orig_pos = this.pos,
_ = this.consumeChar(), _ = this.consumeChar(),
_ = this.parseExpression(), __ = _ && this.peek() && this.parseExpression(),
cdr = this.parseExpression(); cdr = __ && this.peek() &&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.isSymbol(cdr) && EL.val(cdr) == '.'; return _ == ')' || cdr && EL.typeOf(cdr) == 'array' && EL.isSymbol(cdr) && EL.val(cdr) == '.';
}; };
EL.Parser.prototype.parseExpression = function() { EL.Parser.prototype.parseExpression = function() {

View file

@ -7,10 +7,6 @@
// LICENSE. // LICENSE.
/****************************************************************
**** Primitives ************************************************
****************************************************************/
EL.PrimitiveVariables = [ EL.PrimitiveVariables = [
['t', { ['t', {
type: 'variable', type: 'variable',
@ -24,82 +20,84 @@ EL.PrimitiveVariables = [
}] }]
]; ];
// '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', { // 'this' is bound to the EL.Evaluator object when executing primitve functions
type: 'primitive', EL.definePrimitive = function(name, params, body, docstring) {
name: 'string-match', EL.PrimitiveFunctions.push([name, {
params: ['regex', 'string', '&optional', 'start'], type: 'primitive',
body: function(regex, string, start) { 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, var index = start ? EL.val(start) : 0,
s = EL.val(string).substring(index), s = EL.val(string).substring(index),
match = s.match(new RegExp(EL.val(regex))), match = s.match(new RegExp(EL.val(regex))),
found = match ? EL.number(s.indexOf(match[0])) : EL.nil; found = match ? EL.number(s.indexOf(match[0])) : EL.nil;
return found; return found;},
}, "Return the index of the char matching regex in string, beginning from start if available.");
docstring: "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
type: 'primitive', // to be converted to strings similar to JavaScript. These
name: '+', // semantics suck and should change, not only for real emacs lisp compatibility.
params: [/* ... */], EL.definePrimitive('+', [/*...*/],
body: function() { function() {
var args = EL.Util.shallowCopy(arguments), var args = EL.Util.shallowCopy(arguments),
initial = EL.inferType(args), initial = EL.inferType(args),
type = initial[0]; type = initial[0];
return EL.Util.reduce(function(sum, n) { return EL.Util.reduce(function(sum, n) {
return [type, EL.val(sum) + EL.val(n)]; return [type, EL.val(sum) + EL.val(n)];
}, initial, args); }, initial, args);},
}, "add two numbers");
docstring: "add two numbers"
}], EL.definePrimitive('-', [/*...*/],
['-', { function() {
type: 'primitive', return EL.Util.foldr(function(diff, n) {
name: '-', return EL.number(EL.val(diff) - EL.val(n));
params: [/* ... */], }, EL.number(0), EL.Util.shallowCopy(arguments));},
body: function() { "subtract two numbers");
return EL.Util.foldr(function(diff, n) {
return EL.number(EL.val(diff) - EL.val(n)); EL.definePrimitive('*', [/*...*/],
}, EL.number(0), EL.Util.shallowCopy(arguments)); function() {
},
docstring: "subtract two numbers"
}],
['*', {
type: 'primitive',
name: '*',
params: [/* ... */],
body: function() {
return EL.Util.reduce(function(prod, n) { return EL.Util.reduce(function(prod, n) {
return EL.number(EL.val(prod) * EL.val(n)); return EL.number(EL.val(prod) * EL.val(n));
}, EL.number(1), EL.Util.shallowCopy(arguments)); }, EL.number(1), EL.Util.shallowCopy(arguments));},
}, "multiply two numbers");
docstring: "multiply two numbers"
}], EL.definePrimitive('/', [/*...*/],
['/', { function() {
type: 'primitive',
name: '/',
params: [/* ... */],
body: function() {
return EL.Util.foldr(function(quot, n) { return EL.Util.foldr(function(quot, n) {
return EL.number(EL.val(quot) / EL.val(n)); return EL.number(EL.val(quot) / EL.val(n));
}, EL.number(1), EL.Util.shallowCopy(arguments)); }, EL.number(1), EL.Util.shallowCopy(arguments));
}, },
docstring: "divide two numbers" "divide two numbers");
}],
['print', { EL.definePrimitive('print', ['x'],
type: 'primitive', function(x, tostring) {
name: 'print',
params: ['x'],
body: function(x, tostring) {
var buffer = "", var buffer = "",
tag = EL.tag(x); tag = EL.tag(x);
function p(s) { function p(s) {
@ -124,10 +122,6 @@ EL.PrimitiveFunctions = [
} }
return EL.nil; return EL.nil;
}, },
docstring: "print an expression" "print an expression");
}] };
]; EL.initHook(EL._definePrimitives);
/****************************************************************
****************************************************************/

View file

@ -77,7 +77,7 @@ EL.isList = function(expr) {
}; };
EL.isAtom = function(expr) { EL.isAtom = function(expr) {
return EL.isString(expr) || EL.isNumber(expr) || EL.isRegex(expr); return !EL.isCons(expr);
}; };
EL.inferType = function(exprs) { EL.inferType = function(exprs) {