diff --git a/spec/generated-parser.spec.js b/spec/generated-parser.spec.js
new file mode 100644
index 0000000..fddd272
--- /dev/null
+++ b/spec/generated-parser.spec.js
@@ -0,0 +1,140 @@
+describe("generated parser", function() {
+ function vary(names, block) {
+ var values = {
+ trackLineAndColumn: [false, true],
+ cache: [false, true]
+ };
+
+ function varyStep(names, options) {
+ var clonedOptions = {}, key, name, i;
+
+ if (names.length === 0) {
+ /*
+ * We have to clone the options so that the block can save them safely
+ * (e.g. by capturing in a closure) without the risk that they will be
+ * changed later.
+ */
+ for (key in options) {
+ clonedOptions[key] = options[key];
+ }
+
+ describe(
+ "with options " + jasmine.pp(clonedOptions),
+ function() { block(clonedOptions); }
+ );
+ } else {
+ name = names[0];
+ for (i = 0; i < values[name].length; i++) {
+ options[name] = values[name][i];
+ varyStep(names.slice(1), options);
+ }
+ }
+ }
+
+ varyStep(names, {});
+ }
+
+ function varyAll(block) {
+ vary(["cache", "trackLineAndColumn"], block);
+ }
+
+ beforeEach(function() {
+ this.addMatchers({
+ toParse: function(input, expected) {
+ var result;
+
+ try {
+ result = this.actual.parse(input);
+
+ this.message = function() {
+ return "Expected " + jasmine.pp(input) + " "
+ + (this.isNot ? "not " : "")
+ + "to parse as " + jasmine.pp(expected) + ", "
+ + "but it parsed as " + jasmine.pp(result) + ".";
+ };
+
+ return this.env.equals_(result, expected);
+ } catch (e) {
+ this.message = function() {
+ return "Expected " + jasmine.pp(input) + " "
+ + "to parse as " + jasmine.pp(expected) + ", "
+ + "but it failed to parse with message "
+ + jasmine.pp(e.message) + ".";
+ };
+
+ return false;
+ }
+ },
+
+ toFailToParse: function(input) {
+ var result;
+
+ try {
+ result = this.actual.parse(input);
+
+ this.message = function() {
+ return "Expected " + jasmine.pp(input) + " to fail to parse, "
+ + "but it parsed as " + jasmine.pp(result) + ".";
+ };
+
+ return false;
+ } catch (e) {
+ this.message = function() {
+ return "Expected " + jasmine.pp(input) + " to parse, "
+ + "but it failed with message "
+ + jasmine.pp(e.message) + ".";
+ };
+
+ return true;
+ }
+ }
+ });
+ });
+
+ describe("literal matching", function() {
+ varyAll(function(options) {
+ it("matches empty literal correctly", function() {
+ var parser = PEG.buildParser('start = ""', options);
+
+ expect(parser).toParse("", "");
+ });
+
+ it("matches one-character literal correctly", function() {
+ var parser = PEG.buildParser('start = "a"', options);
+
+ expect(parser).toParse("a", "a");
+ expect(parser).toFailToParse("b");
+ });
+
+ it("matches multiple-character literal correctly", function() {
+ var parser = PEG.buildParser('start = "abcd"', options);
+
+ expect(parser).toParse("abcd", "abcd");
+ expect(parser).toFailToParse("ebcd");
+ expect(parser).toFailToParse("afcd");
+ expect(parser).toFailToParse("abgd");
+ expect(parser).toFailToParse("abch");
+ });
+
+ it("is case sensitive without the \"i\" flag", function() {
+ var parser = PEG.buildParser('start = "a"', options);
+
+ expect(parser).toParse("a", "a");
+ expect(parser).toFailToParse("A");
+ });
+
+ it("is case insensitive with the \"i\" flag", function() {
+ var parser = PEG.buildParser('start = "a"i', options);
+
+ expect(parser).toParse("a", "a");
+ expect(parser).toParse("A", "A");
+ });
+
+ it("advances position on success", function() {
+ var parser = PEG.buildParser('start = "a" .', options);
+
+ expect(parser).toParse("ab", ["a", "b"]);
+ });
+ });
+ });
+});
diff --git a/spec/index.html b/spec/index.html
index d7ea319..068dc1f 100644
--- a/spec/index.html
+++ b/spec/index.html
@@ -7,6 +7,7 @@
+