Permalink
Browse files

v0.0.1, initial import

  • Loading branch information...
0 parents commit 3eb687151d3692a8a288c148ba4a2367ffd79e20 @jneen committed Nov 18, 2012
Showing with 538 additions and 0 deletions.
  1. +5 −0 .gitignore
  2. +67 −0 Makefile
  3. +1 −0 build/.gitkeep
  4. +2 −0 index.js
  5. +22 −0 package.json
  6. +1 −0 src/commonjs/post.js
  7. +1 −0 src/commonjs/pre.js
  8. +204 −0 src/parsimmon.js
  9. +235 −0 test/parsimmon.test.js
@@ -0,0 +1,5 @@
+/*.tgz
+/build
+
+npm-*.log
+/node_modules
@@ -0,0 +1,67 @@
+# -*- globals -*- #
+SRC_DIR = src
+BUILD_DIR = build
+CLEAN += $(BUILD_DIR)/*
+SRC = $(SRC_DIR)/parsimmon.js
+
+.PHONY: all
+all: minify commonjs report
+
+# -*- minification -*- #
+UGLIFYJS ?= ./node_modules/.bin/uglifyjs
+UGLIFY_OPTS += --lift-vars --unsafe
+UGLY = $(BUILD_DIR)/parsimmon.min.js
+
+$(UGLY): $(SRC)
+ $(UGLIFYJS) $(UGLIFY_OPTS) $< > $@
+
+%.min.js: %.js
+ $(UGLIFYJS) $(UGLIFY_OPTS) $< > $@
+
+minify: $(UGLY)
+
+# special builds
+COMMONJS = $(BUILD_DIR)/parsimmon.commonjs.js
+
+$(BUILD_DIR)/parsimmon.%.js: $(SRC_DIR)/%/pre.js $(SRC) $(SRC_DIR)/%/post.js
+ cat $^ > $@
+
+.PHONY: commonjs
+commonjs: $(COMMONJS)
+
+.PHONY: amd
+amd: $(BUILD_DIR)/parsimmon.amd.js $(BUILD_DIR)/parsimmon.amd.min.js
+
+.PHONY: report
+report: $(UGLY)
+ wc -c $(UGLY)
+
+# -*- testing -*- #
+MOCHA ?= ./node_modules/.bin/mocha
+MOCHA_OPTS += -u tdd
+TESTS = ./test/*.test.js
+.PHONY: test
+test: $(COMMONJS)
+ $(MOCHA) $(MOCHA_OPTS) $(TESTS)
+
+# -*- packaging -*- #
+
+# XXX this is kind of awful, but hey, it keeps the version info in the right place.
+VERSION = $(shell node -e 'console.log(require("./package.json").version)')
+PACKAGE = parsimmon-$(VERSION).tgz
+CLEAN += parsimmon-*.tgz
+
+$(PACKAGE): clean commonjs test
+ npm pack .
+
+.PHONY: package
+package: $(PACKAGE)
+
+.PHONY: publish
+publish: $(PACKAGE)
+ npm publish $(PACKAGE)
+
+# -*- cleanup -*- #
+.PHONY: clean
+clean:
+ rm -f $(CLEAN)
@@ -0,0 +1 @@
+keep
@@ -0,0 +1,2 @@
+module.exports = require('./build/parsimmon.commonjs');
+exports.version = require('./package.json').version;
@@ -0,0 +1,22 @@
+{
+ "name": "parsimmon",
+ "version": "0.0.1",
+ "description": "A monadic LL(infinity) parser combinator library",
+ "keywords": ["parsing", "parse", "parser combinators"],
+ "author": "Jay Adkisson <jjmadkisson at gmail dot com>",
+ "repository": "git://github.com/jayferd/parsimmon",
+
+ "files": ["index.js", "src", "test", "Makefile", "package.json"],
+ "main": "index.js",
+ "devDependencies": {
+ "mocha": "*",
+ "uglify-js": "*"
+ },
+ "dependencies": {
+ "pjs": "*"
+ },
+ "scripts": {
+ "install": "make commonjs",
+ "test": "make test"
+ }
+}
@@ -0,0 +1 @@
+module.exports = Parsimmon;
@@ -0,0 +1 @@
+var P = require('pjs').P;
@@ -0,0 +1,204 @@
+var Parsimmon = {};
+
+Parsimmon.Parser = P(function(_, _super, Parser) {
+ // The Parser object is a wrapper for a parser function.
+ // Externally, you use one to parse a string by calling
+ // var result = SomeParser.parse('Me Me Me! Parse Me!');
+ // You should never call the constructor, rather you should
+ // construct your Parser from the base parsers and the
+ // parser combinator methods.
+
+ function parseError(stream, message) {
+ if (stream) {
+ stream = "'"+stream+"'";
+ }
+ else {
+ stream = 'EOF';
+ }
+
+ throw 'Parse Error: '+message+' at '+stream;
+ }
+
+ _.init = function(body) { this._ = body; };
+
+ _.parse = function(stream) {
+ return this.skip(eof)._(stream, success, parseError);
+
+ function success(stream, result) { return result; }
+ };
+
+ // -*- primitive combinators -*- //
+ _.or = function(alternative) {
+ var self = this;
+
+ return Parser(function(stream, onSuccess, onFailure) {
+ return self._(stream, onSuccess, failure);
+
+ function failure(newStream) {
+ return alternative._(stream, onSuccess, onFailure);
+ }
+ });
+ };
+
+ _.then = function(next) {
+ var self = this;
+
+ return Parser(function(stream, onSuccess, onFailure) {
+ return self._(stream, success, onFailure);
+
+ function success(newStream, result) {
+ var nextParser = (next instanceof Parser ? next : next(result));
+ return nextParser._(newStream, onSuccess, onFailure);
+ }
+ });
+ };
+
+ // -*- optimized iterative combinators -*- //
+ _.many = function() {
+ var self = this;
+
+ return Parser(function(stream, onSuccess, onFailure) {
+ var xs = [];
+ while (self._(stream, success, failure));
+ return onSuccess(stream, xs);
+
+ function success(newStream, x) {
+ stream = newStream;
+ xs.push(x);
+ return true;
+ }
+
+ function failure() {
+ return false;
+ }
+ });
+ };
+
+ _.times = function(min, max) {
+ if (arguments.length < 2) max = min;
+ var self = this;
+
+ return Parser(function(stream, onSuccess, onFailure) {
+ var xs = [];
+ var result = true;
+ var failure;
+
+ for (var i = 0; i < min; i += 1) {
+ result = self._(stream, success, firstFailure);
+ if (!result) return onFailure(stream, failure);
+ }
+
+ for (; i < max && result; i += 1) {
+ result = self._(stream, success, secondFailure);
+ }
+
+ return onSuccess(stream, xs);
+
+ function success(newStream, x) {
+ xs.push(x);
+ stream = newStream;
+ return true;
+ }
+
+ function firstFailure(newStream, msg) {
+ failure = msg;
+ stream = newStream;
+ return false;
+ }
+
+ function secondFailure(newStream, msg) {
+ return false;
+ }
+ });
+ };
+
+ // -*- higher-level combinators -*- //
+ _.result = function(res) { return this.then(succeed(res)); };
+ _.atMost = function(n) { return this.times(0, n); };
+ _.atLeast = function(n) {
+ var self = this;
+ return self.times(n).then(function(start) {
+ return self.many().map(function(end) {
+ return start.concat(end);
+ });
+ });
+ };
+
+ _.map = function(fn) {
+ return this.then(function(result) { return succeed(fn(result)); });
+ };
+
+ _.skip = function(two) {
+ return this.then(function(result) { return two.result(result); });
+ };
+
+ // -*- primitive parsers -*- //
+ var string = Parsimmon.string = function(str) {
+ var len = str.length;
+ var expected = "expected '"+str+"'";
+
+ return Parser(function(stream, onSuccess, onFailure) {
+ var head = stream.slice(0, len);
+
+ if (head === str) {
+ return onSuccess(stream.slice(len), head);
+ }
+ else {
+ return onFailure(stream, expected);
+ }
+ });
+ };
+
+ var regex = Parsimmon.regex = function(re) {
+ if (re.source[0] !== '^') throw 'regex '+re+' must be anchored';
+
+ var expected = 'expected '+re;
+
+ return Parser(function(stream, onSuccess, onFailure) {
+ var match = re.exec(stream);
+
+ if (match) {
+ var result = match[0];
+ return onSuccess(stream.slice(result.length), result);
+ }
+ else {
+ return onFailure(stream, expected);
+ }
+ });
+ };
+
+ var succeed = Parsimmon.succeed = function(result) {
+ return Parser(function(stream, onSuccess) {
+ return onSuccess(stream, result);
+ });
+ };
+
+ var fail = Parsimmon.fail = function(msg) {
+ return Parser(function(stream, _, onFailure) {
+ return onFailure(stream, msg);
+ });
+ };
+
+ var letter = Parsimmon.letter = regex(/^[a-z]/i);
+ var letters = Parsimmon.letters = regex(/^[a-z]*/i);
+ var digit = Parsimmon.digit = regex(/^[0-9]/);
+ var digits = Parsimmon.digits = regex(/^[0-9]*/);
+ var whitespace = Parsimmon.whitespace = regex(/^\s+/);
+ var optWhitespace = Parsimmon.optWhitespace = regex(/^\s*/);
+
+ var any = Parsimmon.any = Parser(function(stream, onSuccess, onFailure) {
+ if (!stream) return onFailure(stream, 'expected any character');
+
+ return onSuccess(stream.slice(1), stream.charAt(0));
+ });
+
+ var all = Parsimmon.all = Parser(function(stream, onSuccess, onFailure) {
+ return onSuccess('', stream);
+ });
+
+ var eof = Parsimmon.eof = Parser(function(stream, onSuccess, onFailure) {
+ if (stream) return onFailure(stream, 'expected EOF');
+
+ return onSuccess(stream, stream);
+ });
+});
Oops, something went wrong.

0 comments on commit 3eb6871

Please sign in to comment.