mirror of
https://github.com/samsonjs/elisp.js.git
synced 2026-04-27 15:07:47 +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
|
* 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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/****************************************************************
|
|
||||||
****************************************************************/
|
|
||||||
|
|
||||||
|
|
|
||||||
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)
|
// * 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() {
|
||||||
|
|
|
||||||
134
el/primitives.js
134
el/primitives.js
|
|
@ -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);
|
||||||
|
|
||||||
|
|
||||||
/****************************************************************
|
|
||||||
****************************************************************/
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue