From 39a5b52e15c675c713284864d5b4c6e50220ac01 Mon Sep 17 00:00:00 2001 From: Sami Samhuri Date: Sun, 13 Dec 2009 21:06:13 -0800 Subject: [PATCH] Fixed exponent notation. Changed primitive definitions. --- TODO | 10 +++- el/evaluator.js | 6 --- el/parser.js | 34 +++++++++--- el/primitives.js | 134 ++++++++++++++++++++++------------------------- el/types.js | 2 +- 5 files changed, 102 insertions(+), 84 deletions(-) diff --git a/TODO b/TODO index c6c2f91..3aa39f4 100644 --- a/TODO +++ b/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 diff --git a/el/evaluator.js b/el/evaluator.js index cf4d2a1..bc16804 100644 --- a/el/evaluator.js +++ b/el/evaluator.js @@ -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; }; -/**************************************************************** - ****************************************************************/ diff --git a/el/parser.js b/el/parser.js index 0d85994..7f9edf6 100644 --- a/el/parser.js +++ b/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() { diff --git a/el/primitives.js b/el/primitives.js index a6d01d3..282ed6f 100644 --- a/el/primitives.js +++ b/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); \ No newline at end of file diff --git a/el/types.js b/el/types.js index dd02f0b..5acd320 100644 --- a/el/types.js +++ b/el/types.js @@ -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) {