diff --git a/spec/behavior/generated-parser-behavior.spec.js b/spec/behavior/generated-parser-behavior.spec.js index 8d8c90b..d72c23a 100644 --- a/spec/behavior/generated-parser-behavior.spec.js +++ b/spec/behavior/generated-parser-behavior.spec.js @@ -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() {