pegjs/spec/api/pegjs-api.spec.js
David Majda d1fe86683b Improve location info in tracing events
Replace |line|, |column|, and |offset| properties of tracing events with
the |location| property. It contains an object similar to the one
returned by the |location| function available in action code:

  {
    start: { offset: 23, line: 5, column: 6 },
    end:   { offset: 25, line: 5, column: 8 }
  }

For the |rule.match| event, |start| refers to the position at the
beginning of the matched input and |end| refers to the position after
the end of the matched input.

For |rule.enter| and |rule.fail| events, both |start| and |end| refer to
the current position at the time the rule was entered.
2015-04-04 10:04:09 +02:00

209 lines
6.7 KiB
JavaScript

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);
});
});
});
describe("tracing", function() {
var grammar = 'start = "a"';
describe("when |trace| is not set", function() {
it("generated parser doesn't trace", function() {
var parser = PEG.buildParser(grammar);
spyOn(console, "log");
parser.parse("a");
expect(console.log).not.toHaveBeenCalled();
});
});
describe("when |trace| is set to |false|", function() {
it("generated parser doesn't trace", function() {
var parser = PEG.buildParser(grammar, { trace: false });
spyOn(console, "log");
parser.parse("a");
expect(console.log).not.toHaveBeenCalled();
});
});
describe("when |trace| is set to |true|", function() {
it("generated parser traces", function() {
var parser = PEG.buildParser(grammar, { trace: true });
spyOn(console, "log");
parser.parse("a");
expect(console.log).toHaveBeenCalledWith("1:1-1:1 rule.enter start");
expect(console.log).toHaveBeenCalledWith("1:1-1:2 rule.match start");
});
});
});
/*
* 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 });
});
});
});