pegjs/spec/api/pegjs-api.spec.js
David Majda 9717dc3417 Talk about "undefined rules" instead of "missing rules" internally
The new terminology is more precise and in line with commonly used
programming languages.

The change involves mainly renaming related compiler pass and files
associated with it.
2016-07-29 15:16:56 +02:00

215 lines
6.9 KiB
JavaScript

/* global peg */
"use strict";
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 });
});
});
});