Permalink
Browse files

Added .chainl()

  • Loading branch information...
weaver committed Aug 18, 2010
1 parent e856a6b commit a60e5d9544242fae9ef319d4a89b2844b58a963f
Showing with 139 additions and 14 deletions.
  1. +33 −5 README.md
  2. +61 −0 examples/calc.js
  3. +31 −9 lib/reparse.js
  4. +14 −0 package.json
View
@@ -1,13 +1,12 @@
# ReParse #
-ReParse is a parser combinator library for Javascript like [Parsec][1]
-for Haskell.
+ReParse is a parser combinator library for Javascript like Haskell's
+[Parsec](http://legacy.cs.uu.nl/daan/parsec.html).
## Installation ##
-Download `lib/reparse.js` and add it to your project.
-
-[1]: http://legacy.cs.uu.nl/daan/parsec.html
+Download `lib/reparse.js` and add it to your project or use `npm
+install reparse`.
## API ##
@@ -155,6 +154,35 @@ Return an array of one or more values produced by `method`. Each
value is separated by `sep` and the entire sequence is optionally
terminated by `sep`.
+#### .chainl(method, op, otherwise) ####
+
+Parse zero or more values produced by `method` and separated by `op`.
+Returns a value obtained by left associative application of functions
+returned by `op`. This can be used to eliminate direct
+left-recursion.
+
+#### .chainl1(method, op, otherwise) ####
+
+Like `.chainl`, but at least one value must be produced by `method`.
+For example, this grammar:
+
+ expr ::= expr '+' term | term
+ term ::= term '*' factor | factor
+
+Might be implemented like this (see `examples/calc.js`):
+
+ function expr() {
+ return this.chainl1(term, addop);
+ }
+
+ function term() {
+ return this.chainl1(factor, mulop);
+ }
+
+ function factor() {
+ ...
+ }
+
## Compatibility ##
ReParse has been tested with Node.JS version `v0.1.103`. It should
View
@@ -0,0 +1,61 @@
+/// calc -- a simple calculator
+//
+// Parses the this grammar and calculates the result:
+//
+// expr ::= expr '+' term | term
+// term ::= term '*' factor | factor
+// factor ::= '(' expr ')' | number
+// number ::= '-' digit+ | digit+
+// digit ::= '0' | '1' | ... | '9'
+
+var sys = require('sys'),
+ ReParse = require('../lib/reparse').ReParse;
+
+function read(input) {
+ return (new ReParse(input, true)).start(expr);
+}
+
+function expr() {
+ return this.chainl1(term, addop);
+}
+
+function term() {
+ return this.chainl1(factor, mulop);
+}
+
+function factor() {
+ return this.choice(group, number);
+}
+
+function group() {
+ return this.between(/^\(/, /^\)/, expr);
+}
+
+function number() {
+ return parseInt(this.match(/^\-?\d+/));
+}
+
+function mulop() {
+ return OPS[this.match(/^[\*\/]/)];
+}
+
+function addop() {
+ return OPS[this.match(/^[\+\-]/)];
+}
+
+var OPS = {
+ '+': function(a, b) { return a + b; },
+ '-': function(a, b) { return a - b; },
+ '*': function(a, b) { return a * b; },
+ '/': function(a, b) { return a / b; }
+};
+
+
+/// --- Main Program
+
+if (process.argv.length != 3) {
+ sys.puts('Usage: node ' + process.argv[1] + ' expression');
+ process.exit(1);
+}
+
+sys.puts(read(process.argv[2]));
View
@@ -185,6 +185,7 @@ ReParse.prototype.many1 = function(method) {
// value.
ReParse.prototype.sepBy = function(method, sep, min) {
var result = [],
+ orig = this.input,
input;
try {
@@ -204,7 +205,7 @@ ReParse.prototype.sepBy = function(method, sep, min) {
throw err;
}
- return (min && (result.length < min)) ? this.fail(input) : result;
+ return (min && (result.length < min)) ? this.fail(orig) : result;
};
ReParse.prototype.sepBy1 = function(method, sep) {
@@ -235,12 +236,33 @@ ReParse.prototype.sepEndBy1 = function(method, sep) {
return this.sepEndBy(method, sep, 1);
};
-if (!Error) {
- function Error(message) {
- this.message = message;
- };
+ReParse.prototype.chainl = function(method, op, otherwise, min) {
+ var found = 0,
+ result = otherwise,
+ orig = this.input,
+ input;
+
+ try {
+ result = this.maybe(method);
+ found++;
+ while (!this.eof())
+ try {
+ input = this.input;
+ result = this.produce(op)(result, this.produce(method));
+ found++;
+ } catch (err) {
+ if (err !== this.fail)
+ throw err;
+ this.fail(input);
+ }
+ } catch (err) {
+ if (err !== this.fail)
+ throw err;
+ }
+
+ return (min && (found < min)) ? this.fail(input) : result;
+};
- Error.prototype.toString = function() {
- return 'ERROR: ' + this.message;
- };
-}
+ReParse.prototype.chainl1 = function(method, op, otherwise) {
+ return this.chainl(method, op, otherwise, 1);
+};
View
@@ -0,0 +1,14 @@
+{
+ "name": "reparse",
+ "description": "A parser combinator library like Haskell's Parsec.",
+ "version": "0.1.0",
+ "author": "Ben Weaver <ben@orangesoda.net>",
+ "contributors": [],
+ "dependencies": {},
+ "keywords": ["parse", "parser", "combinator", "regexp", "regular", "expression"],
+ "directories": { "lib": "./lib" },
+ "main": "./lib/reparse",
+ "scripts": {},
+ "bin": {},
+ "engines": { "node": ">= 0.1.103" }
+}

0 comments on commit a60e5d9

Please sign in to comment.