|
|
|
(function() {
|
|
|
|
|
|
|
|
module("PEG.compiler");
|
|
|
|
|
|
|
|
function testWithVaryingTrackLineAndColumn(name, callback) {
|
|
|
|
test(
|
|
|
|
name + " (with trackLineAndColumn: false) ",
|
|
|
|
function() { callback({ trackLineAndColumn: false }); }
|
|
|
|
);
|
|
|
|
test(
|
|
|
|
name + " (with trackLineAndColumn: true) ",
|
|
|
|
function() { callback({ trackLineAndColumn: true }); }
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
testWithVaryingTrackLineAndColumn("indempotence", function(options) {
|
|
|
|
var parser1 = PEG.buildParser('start = "abcd"', options);
|
|
|
|
var parser2 = PEG.buildParser('start = "abcd"', options);
|
|
|
|
|
|
|
|
strictEqual(parser1.toSource(), parser2.toSource());
|
|
|
|
});
|
|
|
|
|
|
|
|
testWithVaryingTrackLineAndColumn("error details", function(options) {
|
|
|
|
var literalParser = PEG.buildParser('start = "abcd"', options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
literalParser,
|
|
|
|
"",
|
|
|
|
["\"abcd\""],
|
|
|
|
null,
|
|
|
|
'Expected "abcd" but end of input found.'
|
|
|
|
);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
literalParser,
|
|
|
|
"efgh",
|
|
|
|
["\"abcd\""],
|
|
|
|
"e",
|
|
|
|
'Expected "abcd" but "e" found.'
|
|
|
|
);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
literalParser,
|
|
|
|
"abcde",
|
|
|
|
[],
|
|
|
|
"e",
|
|
|
|
'Expected end of input but "e" found.'
|
|
|
|
);
|
|
|
|
|
|
|
|
var classParser = PEG.buildParser('start = [a-d]', options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
classParser,
|
|
|
|
"",
|
|
|
|
["[a-d]"],
|
|
|
|
null,
|
|
|
|
'Expected [a-d] but end of input found.'
|
|
|
|
);
|
|
|
|
var negativeClassParser = PEG.buildParser('start = [^a-d]', options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
negativeClassParser,
|
|
|
|
"",
|
|
|
|
["[^a-d]"],
|
|
|
|
null,
|
|
|
|
'Expected [^a-d] but end of input found.'
|
|
|
|
);
|
|
|
|
|
|
|
|
var anyParser = PEG.buildParser('start = .', options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
anyParser,
|
|
|
|
"",
|
|
|
|
["any character"],
|
|
|
|
null,
|
|
|
|
'Expected any character but end of input found.'
|
|
|
|
);
|
|
|
|
|
|
|
|
var namedRuleWithLiteralParser = PEG.buildParser(
|
|
|
|
'start "digit" = [0-9]',
|
|
|
|
options
|
|
|
|
);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
namedRuleWithLiteralParser,
|
|
|
|
"a",
|
|
|
|
["digit"],
|
|
|
|
"a",
|
|
|
|
'Expected digit but "a" found.'
|
|
|
|
);
|
|
|
|
|
|
|
|
var namedRuleWithAnyParser = PEG.buildParser('start "whatever" = .', options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
namedRuleWithAnyParser,
|
|
|
|
"",
|
|
|
|
["whatever"],
|
|
|
|
null,
|
|
|
|
'Expected whatever but end of input found.'
|
|
|
|
);
|
|
|
|
|
|
|
|
var namedRuleWithNamedRuleParser = PEG.buildParser([
|
|
|
|
'start "digits" = digit+',
|
|
|
|
'digit "digit" = [0-9]'
|
|
|
|
].join("\n"), options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
namedRuleWithNamedRuleParser,
|
|
|
|
"",
|
|
|
|
["digits"],
|
|
|
|
null,
|
|
|
|
'Expected digits but end of input found.'
|
|
|
|
);
|
|
|
|
|
|
|
|
var choiceParser1 = PEG.buildParser('start = "a" / "b" / "c"', options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
choiceParser1,
|
|
|
|
"def",
|
|
|
|
["\"a\"", "\"b\"", "\"c\""],
|
|
|
|
"d",
|
|
|
|
'Expected "a", "b" or "c" but "d" found.'
|
|
|
|
);
|
|
|
|
|
|
|
|
var choiceParser2 = PEG.buildParser('start = "a" "b" "c" / "a"', options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
choiceParser2,
|
|
|
|
"abd",
|
|
|
|
["\"c\""],
|
|
|
|
"d",
|
|
|
|
'Expected "c" but "d" found.'
|
|
|
|
);
|
|
|
|
|
|
|
|
var simpleNotParser = PEG.buildParser('start = !"a" "b"', options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
simpleNotParser,
|
|
|
|
"c",
|
|
|
|
["\"b\""],
|
|
|
|
"c",
|
|
|
|
'Expected "b" but "c" found.'
|
|
|
|
);
|
|
|
|
|
|
|
|
var simpleAndParser = PEG.buildParser('start = &"a" [a-b]', options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
simpleAndParser,
|
|
|
|
"c",
|
|
|
|
[],
|
|
|
|
"c",
|
|
|
|
'Expected end of input but "c" found.'
|
|
|
|
);
|
|
|
|
|
|
|
|
var emptyParser = PEG.buildParser('start = ', options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
emptyParser,
|
|
|
|
"something",
|
|
|
|
[],
|
|
|
|
"s",
|
|
|
|
'Expected end of input but "s" found.'
|
|
|
|
);
|
|
|
|
|
|
|
|
var duplicateErrorParser = PEG.buildParser('start = "a" / "a"', options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
duplicateErrorParser,
|
|
|
|
"",
|
|
|
|
["\"a\""],
|
|
|
|
null,
|
|
|
|
'Expected "a" but end of input found.'
|
|
|
|
);
|
|
|
|
|
|
|
|
var unsortedErrorsParser = PEG.buildParser('start = "b" / "a"', options);
|
|
|
|
doesNotParseWithDetails(
|
|
|
|
unsortedErrorsParser,
|
|
|
|
"",
|
|
|
|
["\"a\"", "\"b\""],
|
|
|
|
null,
|
|
|
|
'Expected "a" or "b" but end of input found.'
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
testWithVaryingTrackLineAndColumn("error positions", function(options) {
|
|
|
|
var simpleParser = PEG.buildParser('start = "a"', options);
|
|
|
|
|
|
|
|
/* Regular match failure */
|
|
|
|
doesNotParseWithPos(simpleParser, "b", 0, 1, 1);
|
|
|
|
|
|
|
|
/* Trailing input */
|
|
|
|
doesNotParseWithPos(simpleParser, "ab", 1, 1, 2);
|
|
|
|
|
|
|
|
var digitsParser = PEG.buildParser([
|
|
|
|
'start = line (("\\r" / "\\n" / "\\u2028" / "\\u2029")+ line)*',
|
|
|
|
'line = digits (" "+ digits)*',
|
|
|
|
'digits = digits:[0-9]+ { return digits.join(""); }'
|
|
|
|
].join("\n"), options);
|
|
|
|
|
|
|
|
doesNotParseWithPos(digitsParser, "1\n2\n\n3\n\n\n4 5 x", 13, 7, 5);
|
|
|
|
|
|
|
|
/* Non-Unix newlines */
|
|
|
|
doesNotParseWithPos(digitsParser, "1\rx", 2, 2, 1); // Old Mac
|
|
|
|
doesNotParseWithPos(digitsParser, "1\r\nx", 3, 2, 1); // Windows
|
|
|
|
doesNotParseWithPos(digitsParser, "1\n\rx", 3, 3, 1); // mismatched
|
|
|
|
|
|
|
|
/* Strange newlines */
|
|
|
|
doesNotParseWithPos(digitsParser, "1\u2028x", 2, 2, 1); // line separator
|
|
|
|
doesNotParseWithPos(digitsParser, "1\u2029x", 2, 2, 1); // paragraph separator
|
|
|
|
});
|
|
|
|
|
|
|
|
testWithVaryingTrackLineAndColumn("start rule", function(options) {
|
|
|
|
var parser = PEG.buildParser([
|
|
|
|
'a = .* { return "alpha"; }',
|
|
|
|
'b = .* { return "beta"; }'
|
|
|
|
].join("\n"), options);
|
|
|
|
|
|
|
|
/* Default start rule = the first one */
|
|
|
|
parses(parser, "whatever", "alpha");
|
|
|
|
|
|
|
|
/* Explicit specification of the start rule */
|
|
|
|
parsesWithStartRule(parser, "whatever", "a", "alpha");
|
|
|
|
parsesWithStartRule(parser, "whatever", "b", "beta");
|
|
|
|
|
|
|
|
/* Invalid rule name */
|
|
|
|
raises(
|
|
|
|
function() { parser.parse("whatever", "c"); },
|
|
|
|
function(e) {
|
|
|
|
return e instanceof Error && e.message === "Invalid rule name: \"c\".";
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
})();
|