mirror of
https://github.com/samsonjs/elisp.js.git
synced 2026-03-25 09:15:49 +00:00
Fixed exponent notation. Changed primitive definitions.
This commit is contained in:
parent
22856cc925
commit
39a5b52e15
5 changed files with 102 additions and 84 deletions
10
TODO
10
TODO
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/****************************************************************
|
||||
****************************************************************/
|
||||
|
||||
|
|
|
|||
34
el/parser.js
34
el/parser.js
|
|
@ -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() {
|
||||
|
|
|
|||
134
el/primitives.js
134
el/primitives.js
|
|
@ -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);
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue