From d0ff834a3d5e02d1b60f1ef4681f3e8d1b383081 Mon Sep 17 00:00:00 2001 From: David Majda Date: Sat, 17 May 2014 07:52:49 +0200 Subject: [PATCH] Specs cleanup: Implement PEG.js API specs Some parts were previously part of generated parser specs, these were moved. --- spec/api/generated-parser.spec.js | 65 ------------ spec/api/pegjs-api.spec.js | 167 ++++++++++++++++++++++++++++++ spec/index.html | 1 + 3 files changed, 168 insertions(+), 65 deletions(-) create mode 100644 spec/api/pegjs-api.spec.js 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 @@ +