You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pegjs/test/compiler-test.js

711 lines
22 KiB
JavaScript

(function() {
module("PEG.compiler");
test("choices", function() {
var parser = PEG.buildParser('start = "a" / "b" / "c"');
parses(parser, "a", "a");
parses(parser, "b", "b");
parses(parser, "c", "c");
doesNotParse(parser, "");
doesNotParse(parser, "ab");
doesNotParse(parser, "d");
});
test("sequences", function() {
var emptySequenceParser = PEG.buildParser('start = ');
parses(emptySequenceParser, "", []);
doesNotParse(emptySequenceParser, "abc");
var nonEmptySequenceParser = PEG.buildParser('start = "a" "b" "c"');
parses(nonEmptySequenceParser, "abc", ["a", "b", "c"]);
doesNotParse(nonEmptySequenceParser, "");
doesNotParse(nonEmptySequenceParser, "ab");
doesNotParse(nonEmptySequenceParser, "abcd");
doesNotParse(nonEmptySequenceParser, "efg");
/*
* Test that the parsing position returns after unsuccessful parsing of a
* sequence.
*/
var posTestParser = PEG.buildParser('start = ("a" "b") / "a"');
parses(posTestParser, "a", "a");
});
test("labels", function() {
var parser = PEG.buildParser('start = label:"a"');
parses(parser, "a", "a");
doesNotParse(parser, "b");
});
test("simple and", function() {
var parser = PEG.buildParser('start = "a" &"b" "b"');
parses(parser, "ab", ["a", "", "b"]);
doesNotParse(parser, "ac");
/*
* Test that the parsing position returns after successful parsing of a
* predicate is not needed, it is implicit in the tests above.
*/
});
test("simple not", function() {
var parser = PEG.buildParser('start = "a" !"b"');
parses(parser, "a", ["a", ""]);
doesNotParse(parser, "ab");
/*
* Test that the parsing position returns after successful parsing of a
* predicate.
*/
var posTestParser = PEG.buildParser('start = "a" !"b" "c"');
parses(posTestParser, "ac", ["a", "", "c"]);
});
test("semantic and", function() {
var acceptingParser = PEG.buildParser('start = "a" &{ return true; } "b"');
parses(acceptingParser, "ab", ["a", "", "b"]);
var rejectingParser = PEG.buildParser('start = "a" &{ return false; } "b"');
doesNotParse(rejectingParser, "ab");
var singleElementUnlabeledParser = PEG.buildParser(
'start = "a" &{ return arguments.length === 0; }'
);
parses(singleElementUnlabeledParser, "a", ["a", ""]);
var singleElementLabeledParser = PEG.buildParser(
'start = a:"a" &{ return arguments.length === 1 && a === "a"; }'
);
parses(singleElementLabeledParser, "a", ["a", ""]);
var multiElementUnlabeledParser = PEG.buildParser(
'start = "a" "b" "c" &{ return arguments.length === 0; }'
);
parses(multiElementUnlabeledParser, "abc", ["a", "b", "c", ""]);
var multiElementLabeledParser = PEG.buildParser([
'start = a:"a" "b" c:"c"',
' &{ return arguments.length === 2 && a === "a" && c === "c"; }'
].join("\n"));
parses(multiElementLabeledParser, "abc", ["a", "b", "c", ""]);
var innerElementsUnlabeledParser = PEG.buildParser(
'start = "a" ("b" "c" "d" &{ return arguments.length === 0; }) "e"'
);
parses(innerElementsUnlabeledParser, "abcde", ["a", ["b", "c", "d", ""], "e"]);
var innerElementsLabeledParser = PEG.buildParser([
'start = "a"',
' (',
' b:"b" "c" d:"d"',
' &{ return arguments.length === 2 && b === "b" && d === "d"; }',
' )',
' "e"'
].join("\n"));
parses(innerElementsLabeledParser, "abcde", ["a", ["b", "c", "d", ""], "e"]);
});
test("semantic not", function() {
var acceptingParser = PEG.buildParser('start = "a" !{ return false; } "b"');
parses(acceptingParser, "ab", ["a", "", "b"]);
var rejectingParser = PEG.buildParser('start = "a" !{ return true; } "b"');
doesNotParse(rejectingParser, "ab");
var singleElementUnlabeledParser = PEG.buildParser(
'start = "a" !{ return arguments.length !== 0; }'
);
parses(singleElementUnlabeledParser, "a", ["a", ""]);
var singleElementLabeledParser = PEG.buildParser(
'start = a:"a" !{ return arguments.length !== 1 || a !== "a"; }'
);
parses(singleElementLabeledParser, "a", ["a", ""]);
var multiElementUnlabeledParser = PEG.buildParser(
'start = "a" "b" "c" !{ return arguments.length !== 0; }'
);
parses(multiElementUnlabeledParser, "abc", ["a", "b", "c", ""]);
var multiElementLabeledParser = PEG.buildParser([
'start = a:"a" "b" c:"c"',
' !{ return arguments.length !== 2 || a !== "a" || c !== "c"; }'
].join("\n"));
parses(multiElementLabeledParser, "abc", ["a", "b", "c", ""]);
var innerElementsUnlabeledParser = PEG.buildParser(
'start = "a" ("b" "c" "d" !{ return arguments.length !== 0; }) "e"'
);
parses(innerElementsUnlabeledParser, "abcde", ["a", ["b", "c", "d", ""], "e"]);
var innerElementsLabeledParser = PEG.buildParser([
'start = "a"',
' (',
' b:"b" "c" d:"d"',
' !{ return arguments.length !== 2 || b !== "b" || d !== "d"; }',
' )',
' "e"'
].join("\n"));
parses(innerElementsLabeledParser, "abcde", ["a", ["b", "c", "d", ""], "e"]);
});
test("optional expressions", function() {
var parser = PEG.buildParser('start = "a"?');
parses(parser, "", "");
parses(parser, "a", "a");
});
test("zero or more expressions", function() {
var parser = PEG.buildParser('start = "a"*');
parses(parser, "", []);
parses(parser, "a", ["a"]);
parses(parser, "aaa", ["a", "a", "a"]);
});
test("one or more expressions", function() {
var parser = PEG.buildParser('start = "a"+');
doesNotParse(parser, "");
parses(parser, "a", ["a"]);
parses(parser, "aaa", ["a", "a", "a"]);
});
test("actions", function() {
var singleElementUnlabeledParser = PEG.buildParser(
'start = "a" { return arguments.length; }'
);
parses(singleElementUnlabeledParser, "a", 0);
var singleElementLabeledParser = PEG.buildParser(
'start = a:"a" { return [arguments.length, a]; }'
);
parses(singleElementLabeledParser, "a", [1, "a"]);
var multiElementUnlabeledParser = PEG.buildParser(
'start = "a" "b" "c" { return arguments.length; }'
);
parses(multiElementUnlabeledParser, "abc", 0);
var multiElementLabeledParser = PEG.buildParser(
'start = a:"a" "b" c:"c" { return [arguments.length, a, c]; }'
);
parses(multiElementLabeledParser, "abc", [2, "a", "c"]);
var innerElementsUnlabeledParser = PEG.buildParser(
'start = "a" ("b" "c" "d" { return arguments.length; }) "e"'
);
parses(innerElementsUnlabeledParser, "abcde", ["a", 0, "e"]);
var innerElementsLabeledParser = PEG.buildParser(
'start = "a" (b:"b" "c" d:"d" { return [arguments.length, b, d]; }) "e"'
);
parses(innerElementsLabeledParser, "abcde", ["a", [2, "b", "d"], "e"]);
/*
* Test that the parsing position returns after successfull parsing of the
* action expression and action returning |null|.
*/
var posTestParser = PEG.buildParser('start = "a" { return null; } / "a"');
parses(posTestParser, "a", "a");
/* Test that the action is not called when its expression does not match. */
var notAMatchParser = PEG.buildParser(
'start = "a" { ok(false, "action got called when it should not be"); }'
);
doesNotParse(notAMatchParser, "b");
});
test("initializer", function() {
var variableInActionParser = PEG.buildParser(
'{ a = 42; }; start = "a" { return a; }'
);
parses(variableInActionParser, "a", 42);
var functionInActionParser = PEG.buildParser(
'{ function f() { return 42; } }; start = "a" { return f(); }'
);
parses(functionInActionParser, "a", 42);
var variableInSemanticAndParser = PEG.buildParser(
'{ a = 42; }; start = "a" &{ return a === 42; }'
);
parses(variableInSemanticAndParser, "a", ["a", ""]);
var functionInSemanticAndParser = PEG.buildParser(
'{ function f() { return 42; } }; start = "a" &{ return f() === 42; }'
);
parses(functionInSemanticAndParser, "a", ["a", ""]);
var variableInSemanticNotParser = PEG.buildParser(
'{ a = 42; }; start = "a" !{ return a !== 42; }'
);
parses(variableInSemanticNotParser, "a", ["a", ""]);
var functionInSemanticNotParser = PEG.buildParser(
'{ function f() { return 42; } }; start = "a" !{ return f() !== 42; }'
);
parses(functionInSemanticNotParser, "a", ["a", ""]);
});
test("rule references", function() {
var parser = PEG.buildParser([
'start = static / dynamic',
'static = "C" / "C++" / "Java" / "C#"',
'dynamic = "Ruby" / "Python" / "JavaScript"'
].join("\n"));
parses(parser, "Java", "Java");
parses(parser, "Python", "Python");
});
test("literals", function() {
var zeroCharParser = PEG.buildParser('start = ""');
parses(zeroCharParser, "", "");
doesNotParse(zeroCharParser, "a");
var oneCharCaseSensitiveParser = PEG.buildParser('start = "a"');
parses(oneCharCaseSensitiveParser, "a", "a");
doesNotParse(oneCharCaseSensitiveParser, "");
doesNotParse(oneCharCaseSensitiveParser, "A");
doesNotParse(oneCharCaseSensitiveParser, "b");
var multiCharCaseSensitiveParser = PEG.buildParser('start = "abcd"');
parses(multiCharCaseSensitiveParser, "abcd", "abcd");
doesNotParse(multiCharCaseSensitiveParser, "");
doesNotParse(multiCharCaseSensitiveParser, "abc");
doesNotParse(multiCharCaseSensitiveParser, "abcde");
doesNotParse(multiCharCaseSensitiveParser, "ABCD");
doesNotParse(multiCharCaseSensitiveParser, "efgh");
var oneCharCaseInsensitiveParser = PEG.buildParser('start = "a"i');
parses(oneCharCaseInsensitiveParser, "a", "a");
parses(oneCharCaseInsensitiveParser, "A", "A");
doesNotParse(oneCharCaseInsensitiveParser, "");
doesNotParse(oneCharCaseInsensitiveParser, "b");
var multiCharCaseInsensitiveParser = PEG.buildParser('start = "abcd"i');
parses(multiCharCaseInsensitiveParser, "abcd", "abcd");
parses(multiCharCaseInsensitiveParser, "ABCD", "ABCD");
doesNotParse(multiCharCaseInsensitiveParser, "");
doesNotParse(multiCharCaseInsensitiveParser, "abc");
doesNotParse(multiCharCaseInsensitiveParser, "abcde");
doesNotParse(multiCharCaseInsensitiveParser, "efgh");
/*
* Test that the parsing position moves forward after successful parsing of
* a literal.
*/
var posTestParser = PEG.buildParser('start = "a" "b"');
parses(posTestParser, "ab", ["a", "b"]);
});
test("anys", function() {
var parser = PEG.buildParser('start = .');
parses(parser, "a", "a");
doesNotParse(parser, "");
doesNotParse(parser, "ab");
/*
* Test that the parsing position moves forward after successful parsing of
* an any.
*/
var posTestParser = PEG.buildParser('start = . .');
parses(posTestParser, "ab", ["a", "b"]);
});
test("classes", function() {
var emptyClassParser = PEG.buildParser('start = []');
doesNotParse(emptyClassParser, "");
doesNotParse(emptyClassParser, "a");
doesNotParse(emptyClassParser, "ab");
var invertedEmptyClassParser = PEG.buildParser('start = [^]');
doesNotParse(invertedEmptyClassParser, "");
parses(invertedEmptyClassParser, "a", "a");
doesNotParse(invertedEmptyClassParser, "ab");
var nonEmptyCaseSensitiveClassParser = PEG.buildParser('start = [ab-d]');
parses(nonEmptyCaseSensitiveClassParser, "a", "a");
parses(nonEmptyCaseSensitiveClassParser, "b", "b");
parses(nonEmptyCaseSensitiveClassParser, "c", "c");
parses(nonEmptyCaseSensitiveClassParser, "d", "d");
doesNotParse(nonEmptyCaseSensitiveClassParser, "");
doesNotParse(nonEmptyCaseSensitiveClassParser, "A");
doesNotParse(nonEmptyCaseSensitiveClassParser, "B");
doesNotParse(nonEmptyCaseSensitiveClassParser, "C");
doesNotParse(nonEmptyCaseSensitiveClassParser, "D");
doesNotParse(nonEmptyCaseSensitiveClassParser, "e");
doesNotParse(nonEmptyCaseSensitiveClassParser, "ab");
var invertedNonEmptyCaseSensitiveClassParser = PEG.buildParser('start = [^ab-d]');
parses(invertedNonEmptyCaseSensitiveClassParser, "A", "A");
parses(invertedNonEmptyCaseSensitiveClassParser, "B", "B");
parses(invertedNonEmptyCaseSensitiveClassParser, "C", "C");
parses(invertedNonEmptyCaseSensitiveClassParser, "D", "D");
parses(invertedNonEmptyCaseSensitiveClassParser, "e", "e");
doesNotParse(invertedNonEmptyCaseSensitiveClassParser, "a", "a");
doesNotParse(invertedNonEmptyCaseSensitiveClassParser, "b", "b");
doesNotParse(invertedNonEmptyCaseSensitiveClassParser, "c", "c");
doesNotParse(invertedNonEmptyCaseSensitiveClassParser, "d", "d");
doesNotParse(invertedNonEmptyCaseSensitiveClassParser, "");
doesNotParse(invertedNonEmptyCaseSensitiveClassParser, "ab");
var nonEmptyCaseInsensitiveClassParser = PEG.buildParser('start = [ab-d]i');
parses(nonEmptyCaseInsensitiveClassParser, "a", "a");
parses(nonEmptyCaseInsensitiveClassParser, "b", "b");
parses(nonEmptyCaseInsensitiveClassParser, "c", "c");
parses(nonEmptyCaseInsensitiveClassParser, "d", "d");
parses(nonEmptyCaseInsensitiveClassParser, "A", "A");
parses(nonEmptyCaseInsensitiveClassParser, "B", "B");
parses(nonEmptyCaseInsensitiveClassParser, "C", "C");
parses(nonEmptyCaseInsensitiveClassParser, "D", "D");
doesNotParse(nonEmptyCaseInsensitiveClassParser, "");
doesNotParse(nonEmptyCaseInsensitiveClassParser, "e");
doesNotParse(nonEmptyCaseInsensitiveClassParser, "ab");
var invertedNonEmptyCaseInsensitiveClassParser = PEG.buildParser('start = [^ab-d]i');
parses(invertedNonEmptyCaseInsensitiveClassParser, "e", "e");
doesNotParse(invertedNonEmptyCaseInsensitiveClassParser, "a", "a");
doesNotParse(invertedNonEmptyCaseInsensitiveClassParser, "b", "b");
doesNotParse(invertedNonEmptyCaseInsensitiveClassParser, "c", "c");
doesNotParse(invertedNonEmptyCaseInsensitiveClassParser, "d", "d");
doesNotParse(invertedNonEmptyCaseInsensitiveClassParser, "A", "A");
doesNotParse(invertedNonEmptyCaseInsensitiveClassParser, "B", "B");
doesNotParse(invertedNonEmptyCaseInsensitiveClassParser, "C", "C");
doesNotParse(invertedNonEmptyCaseInsensitiveClassParser, "D", "D");
doesNotParse(invertedNonEmptyCaseInsensitiveClassParser, "");
doesNotParse(invertedNonEmptyCaseInsensitiveClassParser, "ab");
/*
* Test that the parsing position moves forward after successful parsing of
* a class.
*/
var posTestParser = PEG.buildParser('start = [ab-d] [ab-d]');
parses(posTestParser, "ab", ["a", "b"]);
});
test("cache", function() {
/*
* Should trigger a codepath where the cache is used (for the "a" rule).
*/
var parser = PEG.buildParser([
'start = (a b) / (a c)',
'a = "a"',
'b = "b"',
'c = "c"'
].join("\n"));
parses(parser, "ac", ["a", "c"]);
});
test("indempotence", function() {
var parser1 = PEG.buildParser('start = "abcd"');
var parser2 = PEG.buildParser('start = "abcd"');
strictEqual(parser1.toSource(), parser2.toSource());
});
test("error details", function() {
var literalParser = PEG.buildParser('start = "abcd"');
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]');
doesNotParseWithDetails(
classParser,
"",
["[a-d]"],
null,
'Expected [a-d] but end of input found.'
);
var negativeClassParser = PEG.buildParser('start = [^a-d]');
doesNotParseWithDetails(
negativeClassParser,
"",
["[^a-d]"],
null,
'Expected [^a-d] but end of input found.'
);
var anyParser = PEG.buildParser('start = .');
doesNotParseWithDetails(
anyParser,
"",
["any character"],
null,
'Expected any character but end of input found.'
);
var namedRuleWithLiteralParser = PEG.buildParser('start "digit" = [0-9]');
doesNotParseWithDetails(
namedRuleWithLiteralParser,
"a",
["digit"],
"a",
'Expected digit but "a" found.'
);
var namedRuleWithAnyParser = PEG.buildParser('start "whatever" = .');
doesNotParseWithDetails(
namedRuleWithAnyParser,
"",
["whatever"],
null,
'Expected whatever but end of input found.'
);
var namedRuleWithNamedRuleParser = PEG.buildParser([
'start "digits" = digit+',
'digit "digit" = [0-9]'
].join("\n"));
doesNotParseWithDetails(
namedRuleWithNamedRuleParser,
"",
["digits"],
null,
'Expected digits but end of input found.'
);
var choiceParser1 = PEG.buildParser('start = "a" / "b" / "c"');
doesNotParseWithDetails(
choiceParser1,
"def",
["\"a\"", "\"b\"", "\"c\""],
"d",
'Expected "a", "b" or "c" but "d" found.'
);
var choiceParser2 = PEG.buildParser('start = "a" "b" "c" / "a"');
doesNotParseWithDetails(
choiceParser2,
"abd",
["\"c\""],
"d",
'Expected "c" but "d" found.'
);
var simpleNotParser = PEG.buildParser('start = !"a" "b"');
doesNotParseWithDetails(
simpleNotParser,
"c",
["\"b\""],
"c",
'Expected "b" but "c" found.'
);
var simpleAndParser = PEG.buildParser('start = &"a" [a-b]');
doesNotParseWithDetails(
simpleAndParser,
"c",
[],
"c",
'Expected end of input but "c" found.'
);
var emptyParser = PEG.buildParser('start = ');
doesNotParseWithDetails(
emptyParser,
"something",
[],
"s",
'Expected end of input but "s" found.'
);
var duplicateErrorParser = PEG.buildParser('start = "a" / "a"');
doesNotParseWithDetails(
duplicateErrorParser,
"",
["\"a\""],
null,
'Expected "a" but end of input found.'
);
var unsortedErrorsParser = PEG.buildParser('start = "b" / "a"');
doesNotParseWithDetails(
unsortedErrorsParser,
"",
["\"a\"", "\"b\""],
null,
'Expected "a" or "b" but end of input found.'
);
});
test("error positions", function() {
var simpleParser = PEG.buildParser('start = "a"');
/* 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"));
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
});
test("start rule", function() {
var parser = PEG.buildParser([
'a = .* { return "alpha"; }',
'b = .* { return "beta"; }'
].join("\n"));
/* 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\".";
}
);
});
/*
* Following examples are from Wikipedia, see
* http://en.wikipedia.org/w/index.php?title=Parsing_expression_grammar&oldid=335106938.
*/
test("arithmetics", function() {
/*
* Value ← [0-9]+ / '(' Expr ')'
* Product ← Value (('*' / '/') Value)*
* Sum ← Product (('+' / '-') Product)*
* Expr ← Sum
*/
var parser = PEG.buildParser([
'Expr = Sum',
'Sum = head:Product tail:(("+" / "-") Product)* {',
' var result = head;',
' for (var i = 0; i < tail.length; i++) {',
' if (tail[i][0] == "+") { result += tail[i][1]; }',
' if (tail[i][0] == "-") { result -= tail[i][1]; }',
' }',
' return result;',
' }',
'Product = head:Value tail:(("*" / "/") Value)* {',
' var result = head;',
' for (var i = 0; i < tail.length; i++) {',
' if (tail[i][0] == "*") { result *= tail[i][1]; }',
' if (tail[i][0] == "/") { result /= tail[i][1]; }',
' }',
' return result;',
' }',
'Value = digits:[0-9]+ { return parseInt(digits.join("")); }',
' / "(" expr:Expr ")" { return expr; }'
].join("\n"));
/* Test "value" rule. */
parses(parser, "0", 0);
parses(parser, "123", 123);
parses(parser, "(42+43)", 42+43);
/* Test "product" rule. */
parses(parser, "42", 42);
parses(parser, "42*43", 42*43);
parses(parser, "42*43*44*45", 42*43*44*45);
parses(parser, "42/43", 42/43);
parses(parser, "42/43/44/45", 42/43/44/45);
/* Test "sum" rule. */
parses(parser, "42*43", 42*43);
parses(parser, "42*43+44*45", 42*43+44*45);
parses(parser, "42*43+44*45+46*47+48*49", 42*43+44*45+46*47+48*49);
parses(parser, "42*43-44*45", 42*43-44*45);
parses(parser, "42*43-44*45-46*47-48*49", 42*43-44*45-46*47-48*49);
/* Test "expr" rule. */
parses(parser, "42+43", 42+43);
/* Complex test */
parses(parser, "(1+2)*(3+4)",(1+2)*(3+4));
});
test("non-context-free language", function() {
/* The following parsing expression grammar describes the classic
* non-context-free language { a^n b^n c^n : n >= 1 }:
*
* S ← &(A c) a+ B !(a/b/c)
* A ← a A? b
* B ← b B? c
*/
var parser = PEG.buildParser([
'S = &(A "c") a:"a"+ B:B !("a" / "b" / "c") { return a.join("") + B; }',
'A = a:"a" A:A? b:"b" { return a + A + b; }',
'B = b:"b" B:B? c:"c" { return b + B + c; }'
].join("\n"));
parses(parser, "abc", "abc");
parses(parser, "aaabbbccc", "aaabbbccc");
doesNotParse(parser, "aabbbccc");
doesNotParse(parser, "aaaabbbccc");
doesNotParse(parser, "aaabbccc");
doesNotParse(parser, "aaabbbbccc");
doesNotParse(parser, "aaabbbcc");
doesNotParse(parser, "aaabbbcccc");
});
test("nested comments", function() {
/*
* Begin ← "(*"
* End ← "*)"
* C ← Begin N* End
* N ← C / (!Begin !End Z)
* Z ← any single character
*/
var parser = PEG.buildParser([
'C = begin:Begin ns:N* end:End { return begin + ns.join("") + end; }',
'N = C',
' / !Begin !End z:Z { return z; }',
'Z = .',
'Begin = "(*"',
'End = "*)"'
].join("\n"));
parses(parser, "(**)", "(**)");
parses(parser, "(*abc*)", "(*abc*)");
parses(parser, "(*(**)*)", "(*(**)*)");
parses(
parser,
"(*abc(*def*)ghi(*(*(*jkl*)*)*)mno*)",
"(*abc(*def*)ghi(*(*(*jkl*)*)*)mno*)"
);
});
})();