|
|
|
describe("PEG.js API", function() {
|
|
|
|
describe("buildParser", function() {
|
|
|
|
it("builds a parser", 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("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("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("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("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("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("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("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("returns generated parser object", 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 });
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|