diff --git a/spec/api/generated-parser.spec.js b/spec/api/generated-parser.spec.js
index 97e22ce..252b0e1 100644
--- a/spec/api/generated-parser.spec.js
+++ b/spec/api/generated-parser.spec.js
@@ -1024,71 +1024,6 @@ describe("generated parser", function() {
});
});
- describe("allowed start rules", function() {
- var grammar = [
- 'a = "x"',
- 'b = "x"',
- 'c = "x"'
- ].join("\n");
-
- describe("without the |allowedStartRules| option", function() {
- var parser = PEG.buildParser(grammar);
-
- it("allows the first rule", function() {
- expect(parser).toParse("x", { startRule: "a" }, "x");
- });
-
- it("does not allow any other rules", function() {
- expect(parser).toFailToParse("x", { startRule: "b" }, { });
- expect(parser).toFailToParse("x", { startRule: "c" }, { });
- });
- });
-
- describe("with the |allowedStartRules| option", function() {
- var parser = PEG.buildParser(grammar, { allowedStartRules: ["b", "c"] });
-
- it("allows the specified rules", function() {
- expect(parser).toParse("x", { startRule: "b" }, "x");
- expect(parser).toParse("x", { startRule: "c" }, "x");
- });
-
- it("does not allow any other rules", function() {
- expect(parser).toFailToParse("x", { startRule: "a" }, { });
- });
- });
- });
-
- describe("output", function() {
- var grammar = 'start = "a"';
-
- describe("without the |output| option", function() {
- it("returns a parser object", function() {
- var parser = PEG.buildParser(grammar);
-
- expect(typeof parser).toBe("object");
- expect(parser).toParse("a", "a");
- });
- });
-
- describe("when the |output| option is set to \"parser\"", function() {
- it("returns a parser object", function() {
- var parser = PEG.buildParser(grammar, { output: "parser" });
-
- expect(typeof parser).toBe("object");
- expect(parser).toParse("a", "a");
- });
- });
-
- describe("when the |output| option is set to \"source\"", function() {
- it("returns a parser source code", function() {
- var source = PEG.buildParser(grammar, { output: "source" });
-
- expect(typeof source).toBe("string");
- expect(eval(source)).toParse("a", "a");
- });
- });
- });
-
/*
* Following examples are from Wikipedia, see
* http://en.wikipedia.org/w/index.php?title=Parsing_expression_grammar&oldid=335106938.
diff --git a/spec/api/pegjs-api.spec.js b/spec/api/pegjs-api.spec.js
new file mode 100644
index 0000000..9bea6a1
--- /dev/null
+++ b/spec/api/pegjs-api.spec.js
@@ -0,0 +1,167 @@
+describe("PEG.js API", function() {
+ describe("buildParser", function() {
+ it("builds parsers", function() {
+ var parser = PEG.buildParser('start = "a"');
+
+ expect(typeof parser).toBe("object");
+ expect(parser.parse("a")).toBe("a");
+ });
+
+ it("throws an exception on syntax error", function() {
+ expect(function() { PEG.buildParser('start = @'); }).toThrow();
+ });
+
+ it("throws an exception on semantic error", function() {
+ expect(function() { PEG.buildParser('start = missing'); }).toThrow();
+ });
+
+ describe("allowed start rules", function() {
+ var grammar = [
+ 'a = "x"',
+ 'b = "x"',
+ 'c = "x"'
+ ].join("\n");
+
+ /*
+ * The |allowedStartRules| option is implemented separately for each
+ * optimization mode, so we need to test it in both.
+ */
+
+ describe("when optimizing for parsing speed", function() {
+ describe("when |allowedStartRules| is not set", function() {
+ it("the generated parser can start only from the first rule", function() {
+ var parser = PEG.buildParser(grammar, { optimize: "speed" });
+
+ expect(parser.parse("x", { startRule: "a" })).toBe("x");
+ expect(
+ function() { parser.parse("x", { startRule: "b" }); }
+ ).toThrow();
+ expect(
+ function() { parser.parse("x", { startRule: "c" }); }
+ ).toThrow();
+ });
+ });
+
+ describe("when |allowedStartRules| is set", function() {
+ it("the generated parser can start only from specified rules", function() {
+ var parser = PEG.buildParser(grammar, {
+ optimize: "speed",
+ allowedStartRules: ["b", "c"]
+ });
+
+ expect(
+ function() { parser.parse("x", { startRule: "a" }); }
+ ).toThrow();
+ expect(parser.parse("x", { startRule: "b" })).toBe("x");
+ expect(parser.parse("x", { startRule: "c" })).toBe("x");
+ });
+ });
+ });
+
+ describe("when optimizing for code size", function() {
+ describe("when |allowedStartRules| is not set", function() {
+ it("the generated parser can start only from the first rule", function() {
+ var parser = PEG.buildParser(grammar, { optimize: "size" });
+
+ expect(parser.parse("x", { startRule: "a" })).toBe("x");
+ expect(
+ function() { parser.parse("x", { startRule: "b" }); }
+ ).toThrow();
+ expect(
+ function() { parser.parse("x", { startRule: "c" }); }
+ ).toThrow();
+ });
+ });
+
+ describe("when |allowedStartRules| is set", function() {
+ it("the generated parser can start only from specified rules", function() {
+ var parser = PEG.buildParser(grammar, {
+ optimize: "size",
+ allowedStartRules: ["b", "c"]
+ });
+
+ expect(
+ function() { parser.parse("x", { startRule: "a" }); }
+ ).toThrow();
+ expect(parser.parse("x", { startRule: "b" })).toBe("x");
+ expect(parser.parse("x", { startRule: "c" })).toBe("x");
+ });
+ });
+ });
+ });
+
+ describe("intermediate results caching", function() {
+ var grammar = [
+ '{ var n = 0; }',
+ 'start = (a "b") / (a "c") { return n; }',
+ 'a = "a" { n++; }'
+ ].join("\n");
+
+ describe("when |cache| is not set", function() {
+ it("the generated parser doesn't cache intermediate parse results", function() {
+ var parser = PEG.buildParser(grammar);
+
+ expect(parser.parse("ac")).toBe(2);
+ });
+ });
+
+ describe("when |cache| is set to |false|", function() {
+ it("the generated parser doesn't cache intermediate parse results", function() {
+ var parser = PEG.buildParser(grammar, { cache: false });
+
+ expect(parser.parse("ac")).toBe(2);
+ });
+ });
+
+ describe("when |cache| is set to |true|", function() {
+ it("the generated parser caches intermediate parse results", function() {
+ var parser = PEG.buildParser(grammar, { cache: true });
+
+ expect(parser.parse("ac")).toBe(1);
+ });
+ });
+ });
+
+ /*
+ * The |optimize| option isn't tested because there is no meaningful way to
+ * write the specs without turning this into a performance test.
+ */
+
+ describe("output", function() {
+ var grammar = 'start = "a"';
+
+ describe("when |output| is not set", function() {
+ it("defaults to \"parser\"", function() {
+ var parser = PEG.buildParser(grammar);
+
+ expect(typeof parser).toBe("object");
+ expect(parser.parse("a")).toBe("a");
+ });
+ });
+
+ describe("when |output| is set to |\"parser\"|", function() {
+ it("returns generated parser object", function() {
+ var parser = PEG.buildParser(grammar, { output: "parser" });
+
+ expect(typeof parser).toBe("object");
+ expect(parser.parse("a")).toBe("a");
+ });
+ });
+
+ describe("when |output| is set to |\"source\"|", function() {
+ it("returns generated parser source code", function() {
+ var source = PEG.buildParser(grammar, { output: "source" });
+
+ expect(typeof source).toBe("string");
+ expect(eval(source).parse("a")).toBe("a");
+ });
+ });
+ });
+
+ /* The |plugins| option is tested in plugin API specs. */
+
+ it("accepts custom options", function() {
+ PEG.buildParser('start = "a"', { foo: 42 });
+ });
+ });
+});
diff --git a/spec/index.html b/spec/index.html
index 4f3ba53..c931c11 100644
--- a/spec/index.html
+++ b/spec/index.html
@@ -13,6 +13,7 @@
+