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
* 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
* 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

View file

@ -7,10 +7,6 @@
// LICENSE.
/****************************************************************
**** Evaluation ************************************************
****************************************************************/
EL.Evaluator = function(exprs) {
this.expressions = exprs;
this.variables = new EL.SymbolTable(EL.PrimitiveVariables);
@ -242,6 +238,4 @@ EL.Evaluator.prototype.doCond = function(exprs) {
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)
// (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() {
var value = this.parseIntOrFloat(),
exponentAllowed = value === parseInt(value),
@ -179,41 +182,60 @@ EL.Parser.prototype.parseNumber = function() {
EL.Parser.prototype.parseIntOrFloat = function() {
this.exponentAllowed = true;
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) {
return n*10 + parseInt(c);
});
}
// if we see a . there might be a float to parse
if (this.peek() == '.') {
this.exponentAllowed = false;
this.consumeChar();
if (this.peek() && this.peek().match(/\d/)) {
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;
};
// 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() {
var pos = this.pos,
rest = this.rest(),
match = rest.match(/^[+-]?\d+(\.\d*)?[)\s\n]/)
|| rest.match(/^[+-]?\d+(\.\d*)?$/)
|| 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);
};
EL.Parser.prototype.lookingAtCons = function() {
var orig_pos = this.pos,
_ = this.consumeChar(),
_ = this.parseExpression(),
cdr = this.parseExpression();
__ = _ && this.peek() && this.parseExpression(),
cdr = __ && this.peek() &&this.parseExpression();
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() {

View file

@ -7,10 +7,6 @@
// LICENSE.
/****************************************************************
**** Primitives ************************************************
****************************************************************/
EL.PrimitiveVariables = [
['t', {
type: 'variable',
@ -24,82 +20,84 @@ EL.PrimitiveVariables = [
}]
];
// 'this' is bound to the EL.Evaluator object
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."
}],
EL.PrimitiveFunctions = [];
['string-match', {
type: 'primitive',
name: 'string-match',
params: ['regex', 'string', '&optional', 'start'],
body: function(regex, string, start) {
// '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;
},
docstring: "Return the index of the char matching regex in string, beginning from start if available."
}],
return found;},
"Return the index of the char matching regex in string, beginning from start if available.");
['+', {
type: 'primitive',
name: '+',
params: [/* ... */],
body: function() {
// 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);
},
docstring: "add two numbers"
}],
['-', {
type: 'primitive',
name: '-',
params: [/* ... */],
body: function() {
return EL.Util.foldr(function(diff, n) {
return EL.number(EL.val(diff) - EL.val(n));
}, EL.number(0), EL.Util.shallowCopy(arguments));
},
docstring: "subtract two numbers"
}],
['*', {
type: 'primitive',
name: '*',
params: [/* ... */],
body: function() {
}, 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));
},
docstring: "multiply two numbers"
}],
['/', {
type: 'primitive',
name: '/',
params: [/* ... */],
body: function() {
}, 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));
},
docstring: "divide two numbers"
}],
['print', {
type: 'primitive',
name: 'print',
params: ['x'],
body: function(x, tostring) {
"divide two numbers");
EL.definePrimitive('print', ['x'],
function(x, tostring) {
var buffer = "",
tag = EL.tag(x);
function p(s) {
@ -124,10 +122,6 @@ EL.PrimitiveFunctions = [
}
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) {
return EL.isString(expr) || EL.isNumber(expr) || EL.isRegex(expr);
return !EL.isCons(expr);
};
EL.inferType = function(exprs) {