"use strict"; var peg = require("../../lib/peg"); describe("PEG.js API", function() { describe("generate", function() { it("generates a parser", function() { var parser = peg.generate('start = "a"'); expect(typeof parser).toBe("object"); expect(parser.parse("a")).toBe("a"); }); it("throws an exception on syntax error", function() { expect(function() { peg.generate('start = @'); }).toThrow(); }); it("throws an exception on semantic error", function() { expect(function() { peg.generate('start = undefined'); }).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("generated parser can start only from the first rule", function() { var parser = peg.generate(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("generated parser can start only from specified rules", function() { var parser = peg.generate(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("generated parser can start only from the first rule", function() { var parser = peg.generate(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("generated parser can start only from specified rules", function() { var parser = peg.generate(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("generated parser doesn't cache intermediate parse results", function() { var parser = peg.generate(grammar); expect(parser.parse("ac")).toBe(2); }); }); describe("when |cache| is set to |false|", function() { it("generated parser doesn't cache intermediate parse results", function() { var parser = peg.generate(grammar, { cache: false }); expect(parser.parse("ac")).toBe(2); }); }); describe("when |cache| is set to |true|", function() { it("generated parser caches intermediate parse results", function() { var parser = peg.generate(grammar, { cache: true }); expect(parser.parse("ac")).toBe(1); }); }); }); describe("tracing", function() { var grammar = 'start = "a"'; describe("when |trace| is not set", function() { it("generated parser doesn't trace", function() { var parser = peg.generate(grammar), tracer = jasmine.createSpyObj("tracer", ["trace"]); parser.parse("a", { tracer: tracer }); expect(tracer.trace).not.toHaveBeenCalled(); }); }); describe("when |trace| is set to |false|", function() { it("generated parser doesn't trace", function() { var parser = peg.generate(grammar, { trace: false }), tracer = jasmine.createSpyObj("tracer", ["trace"]); parser.parse("a", { tracer: tracer }); expect(tracer.trace).not.toHaveBeenCalled(); }); }); describe("when |trace| is set to |true|", function() { it("generated parser traces", function() { var parser = peg.generate(grammar, { trace: true }), tracer = jasmine.createSpyObj("tracer", ["trace"]); parser.parse("a", { tracer: tracer }); expect(tracer.trace).toHaveBeenCalled(); }); }); }); /* * 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("returns generated parser object", function() { var parser = peg.generate(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.generate(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.generate(grammar, { output: "source" }); expect(typeof source).toBe("string"); expect(eval(source).parse("a")).toBe("a"); }); }); }); /* * The |format|, |exportVars|, and |dependencies| options are not tested * becasue there is no meaningful way to thest their effects without turning * this into an integration test. */ /* The |plugins| option is tested in plugin API specs. */ it("accepts custom options", function() { peg.generate('start = "a"', { foo: 42 }); }); }); });