|
|
|
@ -122,7 +122,7 @@ describe("generated parser behavior", function() {
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
varyOptimizationOptions(function(options) {
|
|
|
|
|
describe("initializer code", function() {
|
|
|
|
|
describe("initializer", function() {
|
|
|
|
|
it("runs before the parsing begins", function() {
|
|
|
|
|
var parser = PEG.buildParser([
|
|
|
|
|
'{ var result = 42; }',
|
|
|
|
@ -178,7 +178,22 @@ describe("generated parser behavior", function() {
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("rule matching", function() {
|
|
|
|
|
describe("named rule", function() {
|
|
|
|
|
var parser = PEG.buildParser('start "start" = "a"');
|
|
|
|
|
|
|
|
|
|
it("delegates to the expression", function() {
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toFailToParse("b");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("overwrites expected string on failure", function() {
|
|
|
|
|
expect(parser).toFailToParse("b", {
|
|
|
|
|
expected: [{ type: "other", description: "start" }]
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("rule", function() {
|
|
|
|
|
var grammar = [
|
|
|
|
|
'{ var n = 0; }',
|
|
|
|
|
'start = (a "b") / (a "c") { return n; }',
|
|
|
|
@ -200,238 +215,156 @@ describe("generated parser behavior", function() {
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("named matching", function() {
|
|
|
|
|
var parser = PEG.buildParser('start "start" = "a"');
|
|
|
|
|
|
|
|
|
|
it("delegates to the expression", function() {
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toFailToParse("b");
|
|
|
|
|
});
|
|
|
|
|
describe("literal", function() {
|
|
|
|
|
it("matches empty literal correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = ""', options);
|
|
|
|
|
|
|
|
|
|
it("overwrites expected string on failure", function() {
|
|
|
|
|
expect(parser).toFailToParse("b", {
|
|
|
|
|
expected: [{ type: "other", description: "start" }]
|
|
|
|
|
});
|
|
|
|
|
expect(parser).toParse("", "");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("choice matching", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a" / "b" / "c"', options);
|
|
|
|
|
it("matches one-character literal correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toParse("b", "b");
|
|
|
|
|
expect(parser).toParse("c", "c");
|
|
|
|
|
expect(parser).toFailToParse("d");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("action code", function() {
|
|
|
|
|
it("tranforms the expression result by returnung a value", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a" { return 42; }', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", 42);
|
|
|
|
|
expect(parser).toFailToParse("b");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("is not called when the expression does not match", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" { throw "Boom!"; } / "b"',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
it("matches multi-character literal correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "abcd"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("b", "b");
|
|
|
|
|
expect(parser).toParse("abcd", "abcd");
|
|
|
|
|
expect(parser).toFailToParse("ebcd");
|
|
|
|
|
expect(parser).toFailToParse("afcd");
|
|
|
|
|
expect(parser).toFailToParse("abgd");
|
|
|
|
|
expect(parser).toFailToParse("abch");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use label variables", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = a:"a" { return a; }', options);
|
|
|
|
|
it("is case sensitive without the \"i\" flag", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toFailToParse("A");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use the |text| function to get the text matched by the expression", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" "b" "c" { return text(); }',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("abc", "abc");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use the |offset| function to get the current parse position", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" ("b" { return offset(); })',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("ab", ["a", 1]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use the |line| and |column| functions to get the current line and column", function() {
|
|
|
|
|
var parser = PEG.buildParser([
|
|
|
|
|
'{ var result; }',
|
|
|
|
|
'start = line (nl+ line)* { return result; }',
|
|
|
|
|
'line = thing (" "+ thing)*',
|
|
|
|
|
'thing = digit / mark',
|
|
|
|
|
'digit = [0-9]',
|
|
|
|
|
'mark = "x" { result = [line(), column()]; }',
|
|
|
|
|
'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")'
|
|
|
|
|
].join("\n"), options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [7, 5]);
|
|
|
|
|
|
|
|
|
|
/* Non-Unix newlines */
|
|
|
|
|
expect(parser).toParse("1\rx", [2, 1]); // Old Mac
|
|
|
|
|
expect(parser).toParse("1\r\nx", [2, 1]); // Windows
|
|
|
|
|
expect(parser).toParse("1\n\rx", [3, 1]); // mismatched
|
|
|
|
|
it("is case insensitive with the \"i\" flag", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a"i', options);
|
|
|
|
|
|
|
|
|
|
/* Strange newlines */
|
|
|
|
|
expect(parser).toParse("1\u2028x", [2, 1]); // line separator
|
|
|
|
|
expect(parser).toParse("1\u2029x", [2, 1]); // paragraph separator
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toParse("A", "A");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use variables defined in the initializer", function() {
|
|
|
|
|
var parser = PEG.buildParser([
|
|
|
|
|
'{ var v = 42 }',
|
|
|
|
|
'start = "a" { return v; }'
|
|
|
|
|
].join("\n"), options);
|
|
|
|
|
it("advances position on success", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a" .', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", 42);
|
|
|
|
|
expect(parser).toParse("ab", ["a", "b"]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use the |expected| function to trigger an error", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" { expected("a"); }',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
it("sets expectation correctly on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toFailToParse("a", {
|
|
|
|
|
message: 'Expected a but "a" found.',
|
|
|
|
|
expected: [{ type: "other", description: "a" }],
|
|
|
|
|
found: "a",
|
|
|
|
|
offset: 0,
|
|
|
|
|
line: 1,
|
|
|
|
|
column: 1
|
|
|
|
|
expect(parser).toFailToParse("b", {
|
|
|
|
|
expected: [{ type: "literal", value: "a", description: '"a"' }]
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use the |error| function to trigger an error", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" { error("a"); }',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
describe("character class", function() {
|
|
|
|
|
it("matches empty class correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = []', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toFailToParse("a", {
|
|
|
|
|
message: "a",
|
|
|
|
|
expected: null,
|
|
|
|
|
found: "a",
|
|
|
|
|
offset: 0,
|
|
|
|
|
line: 1,
|
|
|
|
|
column: 1
|
|
|
|
|
});
|
|
|
|
|
expect(parser).toFailToParse("a");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use functions defined in the initializer", function() {
|
|
|
|
|
var parser = PEG.buildParser([
|
|
|
|
|
'{ function f() { return 42; } }',
|
|
|
|
|
'start = "a" { return f(); }'
|
|
|
|
|
].join("\n"), options);
|
|
|
|
|
it("matches class with a character list correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [abc]', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", 42);
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toParse("b", "b");
|
|
|
|
|
expect(parser).toParse("c", "c");
|
|
|
|
|
expect(parser).toFailToParse("d");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use the |parser| variable to access the parser object", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" { return parser; }',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
it("matches class with a range correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [a-c]', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", parser);
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toParse("b", "b");
|
|
|
|
|
expect(parser).toParse("c", "c");
|
|
|
|
|
expect(parser).toFailToParse("d");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use options passed to the parser", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" { return options; }',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
it("matches inverted class correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [^a]', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", { a: 42 }, { a: 42 });
|
|
|
|
|
expect(parser).toFailToParse("a");
|
|
|
|
|
expect(parser).toParse("b", "b");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("sequence matching", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a" "b" "c"', options);
|
|
|
|
|
it("is case sensitive without the \"i\" flag", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [a]', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("abc", ["a", "b", "c"]);
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toFailToParse("A");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("does not advance position on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a" "b" / "a"', options);
|
|
|
|
|
it("is case insensitive with the \"i\" flag", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [a]i', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toParse("A", "A");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("labeled matching", function() {
|
|
|
|
|
it("delegates to the expression", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = a:"a"', options);
|
|
|
|
|
it("advances position on success", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [a] .', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toFailToParse("b");
|
|
|
|
|
expect(parser).toParse("ab", ["a", "b"]);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("text matching", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = $("a" "b" "c")', options);
|
|
|
|
|
it("sets expectation correctly on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [a]', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("abc", "abc");
|
|
|
|
|
expect(parser).toFailToParse("b", {
|
|
|
|
|
expected: [{ type: "class", value: "[a]", description: "[a]" }]
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("simple and matching", function() {
|
|
|
|
|
describe("dot", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = &"a" "a"', options);
|
|
|
|
|
var parser = PEG.buildParser('start = .', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", [undefined, "a"]);
|
|
|
|
|
expect(parser).toFailToParse("b");
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("does not advance position on success", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = &"a" "a"', options);
|
|
|
|
|
it("advances position on success", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = . .', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", [undefined, "a"]);
|
|
|
|
|
expect(parser).toParse("ab", ["a", "b"]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("does not influence expected strings on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = &"a"', options);
|
|
|
|
|
it("sets expectation correctly on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = .', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toFailToParse("b", { expected: [] });
|
|
|
|
|
expect(parser).toFailToParse("", {
|
|
|
|
|
expected: [{ type: "any", description: "any character" }]
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("simple not matching", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = !"a" "b"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("b", [undefined, "b"]);
|
|
|
|
|
expect(parser).toFailToParse("a");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("does not advance position on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = !"a" / "a"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("does not influence expected strings on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = !"a"', options);
|
|
|
|
|
describe("rule reference", function() {
|
|
|
|
|
it("follows rule references", function() {
|
|
|
|
|
var parser = PEG.buildParser([
|
|
|
|
|
'start = static / dynamic',
|
|
|
|
|
'static = "C" / "C++" / "Java" / "C#"',
|
|
|
|
|
'dynamic = "Ruby" / "Python" / "JavaScript"'
|
|
|
|
|
].join("\n"), options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toFailToParse("a", { expected: [] });
|
|
|
|
|
expect(parser).toParse("Java", "Java");
|
|
|
|
|
expect(parser).toParse("Python", "Python");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("semantic and code", function() {
|
|
|
|
|
describe("positive semantic predicate", function() {
|
|
|
|
|
it("causes successful match by returning |true|", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = &{ return true; }', options);
|
|
|
|
|
|
|
|
|
@ -531,7 +464,7 @@ describe("generated parser behavior", function() {
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("semantic not code", function() {
|
|
|
|
|
describe("negative semantic predicate", function() {
|
|
|
|
|
it("causes successful match by returning |false|", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = !{ return false; }', options);
|
|
|
|
|
|
|
|
|
@ -631,7 +564,7 @@ describe("generated parser behavior", function() {
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("optional matching", function() {
|
|
|
|
|
describe("optional", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a"?', options);
|
|
|
|
|
|
|
|
|
@ -640,7 +573,7 @@ describe("generated parser behavior", function() {
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("zero or more matching", function() {
|
|
|
|
|
describe("zero or more", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a"*', options);
|
|
|
|
|
|
|
|
|
@ -650,7 +583,7 @@ describe("generated parser behavior", function() {
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("one or more matching", function() {
|
|
|
|
|
describe("one or more", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a"+', options);
|
|
|
|
|
|
|
|
|
@ -660,153 +593,220 @@ describe("generated parser behavior", function() {
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("rule reference matching", function() {
|
|
|
|
|
it("follows rule references", function() {
|
|
|
|
|
var parser = PEG.buildParser([
|
|
|
|
|
'start = static / dynamic',
|
|
|
|
|
'static = "C" / "C++" / "Java" / "C#"',
|
|
|
|
|
'dynamic = "Ruby" / "Python" / "JavaScript"'
|
|
|
|
|
].join("\n"), options);
|
|
|
|
|
describe("text", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = $("a" "b" "c")', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("Java", "Java");
|
|
|
|
|
expect(parser).toParse("Python", "Python");
|
|
|
|
|
expect(parser).toParse("abc", "abc");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("literal matching", function() {
|
|
|
|
|
it("matches empty literal correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = ""', options);
|
|
|
|
|
describe("positive simple predicate", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = &"a" "a"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("", "");
|
|
|
|
|
expect(parser).toParse("a", [undefined, "a"]);
|
|
|
|
|
expect(parser).toFailToParse("b");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("matches one-character literal correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a"', options);
|
|
|
|
|
it("does not advance position on success", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = &"a" "a"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toFailToParse("b");
|
|
|
|
|
expect(parser).toParse("a", [undefined, "a"]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("matches multi-character literal correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "abcd"', options);
|
|
|
|
|
it("does not influence expected strings on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = &"a"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("abcd", "abcd");
|
|
|
|
|
expect(parser).toFailToParse("ebcd");
|
|
|
|
|
expect(parser).toFailToParse("afcd");
|
|
|
|
|
expect(parser).toFailToParse("abgd");
|
|
|
|
|
expect(parser).toFailToParse("abch");
|
|
|
|
|
expect(parser).toFailToParse("b", { expected: [] });
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("is case sensitive without the \"i\" flag", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a"', options);
|
|
|
|
|
describe("negative simple predicate", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = !"a" "b"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toFailToParse("A");
|
|
|
|
|
expect(parser).toParse("b", [undefined, "b"]);
|
|
|
|
|
expect(parser).toFailToParse("a");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("is case insensitive with the \"i\" flag", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a"i', options);
|
|
|
|
|
it("does not advance position on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = !"a" / "a"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toParse("A", "A");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("advances position on success", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a" .', options);
|
|
|
|
|
it("does not influence expected strings on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = !"a"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("ab", ["a", "b"]);
|
|
|
|
|
expect(parser).toFailToParse("a", { expected: [] });
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("sets expectation correctly on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a"', options);
|
|
|
|
|
describe("label", function() {
|
|
|
|
|
it("delegates to the expression", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = a:"a"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toFailToParse("b", {
|
|
|
|
|
expected: [{ type: "literal", value: "a", description: '"a"' }]
|
|
|
|
|
});
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toFailToParse("b");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("class matching", function() {
|
|
|
|
|
it("matches empty class correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = []', options);
|
|
|
|
|
describe("sequence", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a" "b" "c"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toFailToParse("a");
|
|
|
|
|
expect(parser).toParse("abc", ["a", "b", "c"]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("matches class with a character list correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [abc]', options);
|
|
|
|
|
it("does not advance position on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a" "b" / "a"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toParse("b", "b");
|
|
|
|
|
expect(parser).toParse("c", "c");
|
|
|
|
|
expect(parser).toFailToParse("d");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("matches class with a range correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [a-c]', options);
|
|
|
|
|
describe("action", function() {
|
|
|
|
|
it("tranforms the expression result by returnung a value", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a" { return 42; }', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toParse("b", "b");
|
|
|
|
|
expect(parser).toParse("c", "c");
|
|
|
|
|
expect(parser).toFailToParse("d");
|
|
|
|
|
expect(parser).toParse("a", 42);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("matches inverted class correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [^a]', options);
|
|
|
|
|
it("is not called when the expression does not match", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" { throw "Boom!"; } / "b"',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(parser).toFailToParse("a");
|
|
|
|
|
expect(parser).toParse("b", "b");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("is case sensitive without the \"i\" flag", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [a]', options);
|
|
|
|
|
it("can use label variables", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = a:"a" { return a; }', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toFailToParse("A");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("is case insensitive with the \"i\" flag", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [a]i', options);
|
|
|
|
|
it("can use the |text| function to get the text matched by the expression", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" "b" "c" { return text(); }',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toParse("A", "A");
|
|
|
|
|
expect(parser).toParse("abc", "abc");
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("advances position on success", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [a] .', options);
|
|
|
|
|
it("can use the |offset| function to get the current parse position", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" ("b" { return offset(); })',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("ab", ["a", "b"]);
|
|
|
|
|
expect(parser).toParse("ab", ["a", 1]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("sets expectation correctly on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = [a]', options);
|
|
|
|
|
it("can use the |line| and |column| functions to get the current line and column", function() {
|
|
|
|
|
var parser = PEG.buildParser([
|
|
|
|
|
'{ var result; }',
|
|
|
|
|
'start = line (nl+ line)* { return result; }',
|
|
|
|
|
'line = thing (" "+ thing)*',
|
|
|
|
|
'thing = digit / mark',
|
|
|
|
|
'digit = [0-9]',
|
|
|
|
|
'mark = "x" { result = [line(), column()]; }',
|
|
|
|
|
'nl = ("\\r" / "\\n" / "\\u2028" / "\\u2029")'
|
|
|
|
|
].join("\n"), options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toFailToParse("b", {
|
|
|
|
|
expected: [{ type: "class", value: "[a]", description: "[a]" }]
|
|
|
|
|
});
|
|
|
|
|
expect(parser).toParse("1\n2\n\n3\n\n\n4 5 x", [7, 5]);
|
|
|
|
|
|
|
|
|
|
/* Non-Unix newlines */
|
|
|
|
|
expect(parser).toParse("1\rx", [2, 1]); // Old Mac
|
|
|
|
|
expect(parser).toParse("1\r\nx", [2, 1]); // Windows
|
|
|
|
|
expect(parser).toParse("1\n\rx", [3, 1]); // mismatched
|
|
|
|
|
|
|
|
|
|
/* Strange newlines */
|
|
|
|
|
expect(parser).toParse("1\u2028x", [2, 1]); // line separator
|
|
|
|
|
expect(parser).toParse("1\u2029x", [2, 1]); // paragraph separator
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("any matching", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = .', options);
|
|
|
|
|
it("can use variables defined in the initializer", function() {
|
|
|
|
|
var parser = PEG.buildParser([
|
|
|
|
|
'{ var v = 42 }',
|
|
|
|
|
'start = "a" { return v; }'
|
|
|
|
|
].join("\n"), options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toParse("a", 42);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("advances position on success", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = . .', options);
|
|
|
|
|
it("can use the |expected| function to trigger an error", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" { expected("a"); }',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("ab", ["a", "b"]);
|
|
|
|
|
expect(parser).toFailToParse("a", {
|
|
|
|
|
message: 'Expected a but "a" found.',
|
|
|
|
|
expected: [{ type: "other", description: "a" }],
|
|
|
|
|
found: "a",
|
|
|
|
|
offset: 0,
|
|
|
|
|
line: 1,
|
|
|
|
|
column: 1
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("sets expectation correctly on failure", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = .', options);
|
|
|
|
|
it("can use the |error| function to trigger an error", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" { error("a"); }',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(parser).toFailToParse("", {
|
|
|
|
|
expected: [{ type: "any", description: "any character" }]
|
|
|
|
|
expect(parser).toFailToParse("a", {
|
|
|
|
|
message: "a",
|
|
|
|
|
expected: null,
|
|
|
|
|
found: "a",
|
|
|
|
|
offset: 0,
|
|
|
|
|
line: 1,
|
|
|
|
|
column: 1
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use functions defined in the initializer", function() {
|
|
|
|
|
var parser = PEG.buildParser([
|
|
|
|
|
'{ function f() { return 42; } }',
|
|
|
|
|
'start = "a" { return f(); }'
|
|
|
|
|
].join("\n"), options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", 42);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use the |parser| variable to access the parser object", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" { return parser; }',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", parser);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it("can use options passed to the parser", function() {
|
|
|
|
|
var parser = PEG.buildParser(
|
|
|
|
|
'start = "a" { return options; }',
|
|
|
|
|
options
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", { a: 42 }, { a: 42 });
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("choice", function() {
|
|
|
|
|
it("matches correctly", function() {
|
|
|
|
|
var parser = PEG.buildParser('start = "a" / "b" / "c"', options);
|
|
|
|
|
|
|
|
|
|
expect(parser).toParse("a", "a");
|
|
|
|
|
expect(parser).toParse("b", "b");
|
|
|
|
|
expect(parser).toParse("c", "c");
|
|
|
|
|
expect(parser).toFailToParse("d");
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe("error reporting", function() {
|
|
|
|
|