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

920 lines
30 KiB
JavaScript

(function() {
var global = this;
/* ===== Helpers ===== */
global.throws = function(block, exceptionType) {
var exception = null;
try {
block();
} catch (e) {
exception = e;
}
ok(
exception !== null,
exception !== null ? "okay: thrown something" : "failed, nothing thrown"
);
if (exception !== null) {
ok(
exception instanceof exceptionType,
exception instanceof exceptionType
? "okay: thrown " + exceptionType.name
: "failed, thrown " + exception.name + " instead of " + exceptionType.name
);
}
return exception;
};
global.parses = function(parser, input, expected) {
deepEqual(parser.parse(input), expected);
};
global.doesNotParse = function(parser, input) {
throws(function() { parser.parse(input); }, PEG.Parser.SyntaxError);
};
global.doesNotParseWithMessage = function(parser, input, message) {
var exception = throws(
function() { parser.parse(input); },
PEG.Parser.SyntaxError
);
if (exception) {
strictEqual(exception.message, message);
}
};
global.doesNotParseWithPos = function(parser, input, line, column) {
var exception = throws(
function() { parser.parse(input); },
PEG.Parser.SyntaxError
);
if (exception) {
strictEqual(exception.line, line);
strictEqual(exception.column, column);
}
};
global.grammarParserParses = function(input, expected) {
global.parses(PEG.grammarParser, input, expected);
};
global.grammarParserDoesNotParse = function(input) {
global.doesNotParse(PEG.grammarParser, input);
}
/* ===== PEG.Compiler ===== */
module("PEG.Compiler");
test("formatCode joins parts", function() {
strictEqual(PEG.Compiler.formatCode("foo", "bar"), "foo\nbar");
});
test("formatCode interpolates variables", function() {
strictEqual(
PEG.Compiler.formatCode("foo", "${bar}", { bar: "baz" }),
"foo\nbaz"
);
throws(function() {
PEG.Compiler.formatCode("foo", "${bar}");
}, Error);
});
test("formatCode filters variables", function() {
strictEqual(
PEG.Compiler.formatCode("foo", "${bar|string}", { bar: "baz" }),
"foo\n\"baz\""
);
throws(function() {
PEG.Compiler.formatCode("foo", "${bar|eeek}", { bar: "baz" });
}, Error);
});
test("formatCode indents multiline parts", function() {
strictEqual(
PEG.Compiler.formatCode("foo", "${bar}", { bar: " baz\nqux" }),
"foo\n baz\n qux"
);
});
test("generateUniqueIdentifier", function() {
notStrictEqual(
PEG.Compiler.generateUniqueIdentifier("prefix"),
PEG.Compiler.generateUniqueIdentifier("prefix")
);
});
/* ===== PEG ===== */
module("PEG");
test("buildParser reports invalid grammar object", function() {
throws(function() { PEG.buildParser(42); }, PEG.Grammar.GrammarError);
});
test("buildParser reports missing start rule", function() {
throws(function() { PEG.buildParser({}); }, PEG.Grammar.GrammarError);
});
test("buildParser allows custom start rule", function() {
var parser = PEG.buildParser('s: "abcd"', "s");
parses(parser, "abcd", "abcd");
});
/* ===== Generated Parser ===== */
module("Generated Parser");
test("literals", function() {
var parser = PEG.buildParser('start: "abcd"');
parses(parser, "abcd", "abcd");
doesNotParse(parser, "");
doesNotParse(parser, "abc");
doesNotParse(parser, "abcde");
doesNotParse(parser, "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 nonEmptyClassParser = PEG.buildParser('start: [ab-d]');
parses(nonEmptyClassParser, "a", "a");
parses(nonEmptyClassParser, "b", "b");
parses(nonEmptyClassParser, "c", "c");
parses(nonEmptyClassParser, "d", "d");
doesNotParse(nonEmptyClassParser, "");
doesNotParse(nonEmptyClassParser, "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("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("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("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("and predicate", 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("not predicate", 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("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("actions", function() {
var singleMatchParser = PEG.buildParser(
'start: "a" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }'
);
parses(singleMatchParser, "a", "A");
var multiMatchParser = PEG.buildParser(
'start: "a" "b" "c" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }'
);
parses(multiMatchParser, "abc", "ABC");
var innerMatchParser = PEG.buildParser(
'start: "a" ("b" "c" "d" { return Array.prototype.slice.call(arguments).join("").toUpperCase(); }) "e"'
);
parses(innerMatchParser, "abcde", ["a", "BCD", "e"]);
/* 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");
var variablesParser = PEG.buildParser([
'start: "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" {',
' return [$1, $2, $3, $4, $5, $6, $7, $8, $9, $10].join("").toUpperCase();',
' }'
].join("\n"));
parses(variablesParser, "abcdefghij", "ABCDEFGHIJ");
});
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("error messages", function() {
var literalParser = PEG.buildParser('start: "abcd"');
doesNotParseWithMessage(
literalParser,
"",
'Expected "abcd" but end of input found.'
);
doesNotParseWithMessage(
literalParser,
"efgh",
'Expected "abcd" but "e" found.'
);
doesNotParseWithMessage(
literalParser,
"abcde",
'Expected end of input but "e" found.'
);
var anyParser = PEG.buildParser('start: .');
doesNotParseWithMessage(
anyParser,
"",
'Expected any character but end of input found.'
);
var namedRuleWithLiteralParser = PEG.buildParser('start "digit": [0-9]');
doesNotParseWithMessage(
namedRuleWithLiteralParser,
"a",
'Expected digit but "a" found.'
);
var namedRuleWithAnyParser = PEG.buildParser('start "whatever": .');
doesNotParseWithMessage(
namedRuleWithAnyParser,
"",
'Expected whatever but end of input found.'
);
var namedRuleWithNamedRuleParser = PEG.buildParser([
'start "digits": digit+',
'digit "digit": [0-9]'
].join("\n"));
doesNotParseWithMessage(
namedRuleWithNamedRuleParser,
"",
'Expected digits but end of input found.'
);
var choiceParser = PEG.buildParser('start: "a" / "b" / "c"');
doesNotParseWithMessage(
choiceParser,
"def",
'Expected "a", "b" or "c" but "d" found.'
);
var emptyParser = PEG.buildParser('start: ');
doesNotParseWithMessage(
emptyParser,
"something",
'Expected nothing but "s" found.'
);
});
test("error positions", function() {
var parser = PEG.buildParser([
'start: line (("\\r" / "\\n" / "\\u2028" / "\\u2029")+ line)*',
'line: digit (" "+ digit)*',
'digit: [0-9]+ { return $1.join(""); }'
].join("\n"));
doesNotParseWithPos(parser, "a", 1, 1);
doesNotParseWithPos(parser, "1\n2\n\n3\n\n\n4 5 x", 7, 5);
/* Non-Unix newlines */
doesNotParseWithPos(parser, "1\rx", 2, 1); // Old Mac
doesNotParseWithPos(parser, "1\r\nx", 2, 1); // Windows
doesNotParseWithPos(parser, "1\n\rx", 3, 1); // mismatched
/* Strange newlines */
doesNotParseWithPos(parser, "1\u2028x", 2, 1); // line separator
doesNotParseWithPos(parser, "1\u2029x", 2, 1); // paragraph separator
});
/*
* 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([
'Value : [0-9]+ { return parseInt($1.join("")); }',
' / "(" Expr ")" { return $2; }',
'Product : Value (("*" / "/") Value)* {',
' var result = $1;',
' for (var i = 0; i < $2.length; i++) {',
' if ($2[i][0] == "*") { result *= $2[i][1]; }',
' if ($2[i][0] == "/") { result /= $2[i][1]; }',
' }',
' return result;',
' }',
'Sum : Product (("+" / "-") Product)* {',
' var result = $1;',
' for (var i = 0; i < $2.length; i++) {',
' if ($2[i][0] == "+") { result += $2[i][1]; }',
' if ($2[i][0] == "-") { result -= $2[i][1]; }',
' }',
' return result;',
' }',
'Expr : Sum'
].join("\n"), "Expr");
/* Test "value" rule. */
parses(parser, "0", 0);
parses(parser, "123", 123);
parses(parser, "(42+43)", 42+43);
/* Test "product" rule. */
parses(parser, "42*43", 42*43);
parses(parser, "42*43*44*45", 42*43*44*45);
/* Test "sum" rule. */
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"+ B !("a" / "b" / "c") { return $2.join("") + $3; }',
'A: "a" A? "b" { return $1 + $2 + $3; }',
'B: "b" B? "c" { return $1 + $2 + $3; }',
].join("\n"), "S");
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([
'Begin : "(*"',
'End : "*)"',
'C : Begin N* End { return $1 + $2.join("") + $3; }',
'N : C',
' / (!Begin !End Z) { return $3; }',
'Z : .'
].join("\n"), "C");
parses(parser, "(**)", "(**)");
parses(parser, "(*abc*)", "(*abc*)");
parses(parser, "(*(**)*)", "(*(**)*)");
parses(
parser,
"(*abc(*def*)ghi(*(*(*jkl*)*)*)mno*)",
"(*abc(*def*)ghi(*(*(*jkl*)*)*)mno*)"
);
});
/* ===== Grammar Parser ===== */
module("Grammar Parser");
with (PEG.Grammar) {
var literalEmpty = new Literal("");
var literalAbcd = new Literal("abcd");
var literalEfgh = new Literal("efgh");
var literalIjkl = new Literal("ijkl");
var choice = new Choice([literalAbcd, literalEmpty]);
var notAbcd = new NotPredicate(literalAbcd);
var notEfgh = new NotPredicate(literalEfgh);
var notIjkl = new NotPredicate(literalIjkl);
var sequenceEmpty = new Sequence([]);
var sequenceNots = new Sequence([notAbcd, notEfgh, notIjkl]);
var sequenceLiterals = new Sequence([literalAbcd, literalEfgh, literalIjkl]);
function oneRuleGrammar(expression) {
return { start: new PEG.Grammar.Rule("start", null, expression) };
}
var simpleGrammar = oneRuleGrammar(new Literal("abcd"));
function identifierGrammar(identifier) {
return oneRuleGrammar(new PEG.Grammar.RuleRef(identifier));
}
function literalGrammar(literal) {
return oneRuleGrammar(new PEG.Grammar.Literal(literal));
}
function classGrammar(chars) {
return oneRuleGrammar(new PEG.Grammar.Choice(
PEG.ArrayUtils.map(
chars.split(""),
function(char) { return new PEG.Grammar.Literal(char); }
)
));
}
var anyGrammar = oneRuleGrammar(new Any());
function actionGrammar(action) {
return oneRuleGrammar(new PEG.Grammar.Action(new PEG.Grammar.Literal("a"), action));
}
/* Canonical grammar is "a: \"abcd\";\nb: \"efgh\";\nc: \"ijkl\";". */
test("parses grammar", function() {
grammarParserParses('a: "abcd"', { a: new Rule("a", null, literalAbcd) });
grammarParserParses(
'a: "abcd"\nb: "efgh"\nc: "ijkl"',
{
a: new Rule("a", null, literalAbcd),
b: new Rule("b", null, literalEfgh),
c: new Rule("c", null, literalIjkl)
}
);
});
/* Canonical rule is "a: \"abcd\"". */
test("parses rule", function() {
grammarParserParses(
'start: "abcd" / "efgh" / "ijkl"',
oneRuleGrammar(new Choice([literalAbcd, literalEfgh, literalIjkl]))
);
grammarParserParses(
'start "start rule": "abcd" / "efgh" / "ijkl"',
{
start:
new Rule(
"start",
"start rule",
new Choice([literalAbcd, literalEfgh, literalIjkl])
)
}
);
});
/* Canonical expression is "\"abcd\" / \"efgh\" / \"ijkl\"". */
test("parses expression", function() {
grammarParserParses(
'start: "abcd" / "efgh" / "ijkl"',
oneRuleGrammar(new Choice([literalAbcd, literalEfgh, literalIjkl]))
);
});
/* Canonical choice is "\"abcd\" / \"efgh\" / \"ijkl\"". */
test("parses choice", function() {
grammarParserParses(
'start: "abcd" "efgh" "ijkl"',
oneRuleGrammar(sequenceLiterals)
);
grammarParserParses(
'start: "abcd" "efgh" "ijkl" / "abcd" "efgh" "ijkl" / "abcd" "efgh" "ijkl"',
oneRuleGrammar(new Choice([
sequenceLiterals,
sequenceLiterals,
sequenceLiterals
]))
);
});
/* Canonical sequence is "\"abcd\" \"efgh\" \"ijkl\"". */
test("parses sequence", function() {
grammarParserParses(
'start: { code }',
oneRuleGrammar(new Action(sequenceEmpty, " code "))
);
grammarParserParses(
'start: !"abcd" { code }',
oneRuleGrammar(new Action(notAbcd, " code "))
);
grammarParserParses(
'start: !"abcd" !"efgh" !"ijkl" { code }',
oneRuleGrammar(new Action(sequenceNots, " code "))
);
grammarParserParses('start: ', oneRuleGrammar(sequenceEmpty));
grammarParserParses('start: !"abcd"', oneRuleGrammar(notAbcd));
grammarParserParses(
'start: !"abcd" !"efgh" !"ijkl"',
oneRuleGrammar(sequenceNots)
);
});
/* Canonical prefixed is "!\"abcd\"". */
test("parses prefixed", function() {
grammarParserParses(
'start: &"abcd"?',
oneRuleGrammar(new NotPredicate(new NotPredicate(choice)))
);
grammarParserParses('start: !"abcd"?', oneRuleGrammar(new NotPredicate(choice)));
grammarParserParses('start: "abcd"?', oneRuleGrammar(choice));
});
/* Canonical suffixed is "\"abcd\"?". */
test("parses suffixed", function() {
grammarParserParses('start: "abcd"?', oneRuleGrammar(choice));
grammarParserParses('start: "abcd"*', oneRuleGrammar(new ZeroOrMore(literalAbcd)));
grammarParserParses(
'start: "abcd"+',
oneRuleGrammar(new Action(
new Sequence([literalAbcd, new ZeroOrMore(literalAbcd)]),
function(first, rest) { return [first].concat(rest); }
))
);
grammarParserParses('start: "abcd"', literalGrammar("abcd"));
});
/* Canonical primary is "\"abcd\"". */
test("parses primary", function() {
grammarParserParses('start: a', identifierGrammar("a"));
grammarParserParses('start: "abcd"', literalGrammar("abcd"));
grammarParserParses('start: .', anyGrammar);
grammarParserParses('start: [a-d]', classGrammar("abcd"));
grammarParserParses('start: ("abcd")', literalGrammar("abcd"));
});
/* Canonical action is "{ code }". */
test("parses action", function() {
grammarParserParses('start: "a" { code }', actionGrammar(" code "));
});
/* Canonical braced is "{ code }". */
test("parses braced", function() {
grammarParserParses('start: "a" {}', actionGrammar(""));
grammarParserParses('start: "a" {a}', actionGrammar("a"));
grammarParserParses('start: "a" {{a}}', actionGrammar("{a}"));
grammarParserParses('start: "a" {aaa}', actionGrammar("aaa"));
});
/* Trivial character rules are not tested. */
/* Canonical identifier is "a". */
test("parses identifier", function() {
grammarParserParses('start: a', identifierGrammar("a"));
grammarParserParses('start: z', identifierGrammar("z"));
grammarParserParses('start: A', identifierGrammar("A"));
grammarParserParses('start: Z', identifierGrammar("Z"));
grammarParserParses('start: _', identifierGrammar("_"));
grammarParserParses('start: $', identifierGrammar("$"));
grammarParserParses('start: aa', identifierGrammar("aa"));
grammarParserParses('start: az', identifierGrammar("az"));
grammarParserParses('start: aA', identifierGrammar("aA"));
grammarParserParses('start: aZ', identifierGrammar("aZ"));
grammarParserParses('start: a0', identifierGrammar("a0"));
grammarParserParses('start: a9', identifierGrammar("a9"));
grammarParserParses('start: a_', identifierGrammar("a_"));
grammarParserParses('start: a$', identifierGrammar("a$"));
grammarParserParses('start: abcd', identifierGrammar("abcd"));
grammarParserParses('start: a\n', identifierGrammar("a"));
});
/* Canonical literal is "\"abcd\"". */
test("parses literal", function() {
grammarParserParses('start: "abcd"', literalGrammar("abcd"));
grammarParserParses("start: 'abcd'", literalGrammar("abcd"));
});
/* Canonical doubleQuotedLiteral is "\"abcd\"". */
test("parses doubleQuotedLiteral", function() {
grammarParserParses('start: ""', literalGrammar(""));
grammarParserParses('start: "a"', literalGrammar("a"));
grammarParserParses('start: "abc"', literalGrammar("abc"));
grammarParserParses('start: "abcd"\n', literalGrammar("abcd"));
});
/* Canonical doubleQuotedCharacter is "a". */
test("parses doubleQuotedCharacter", function() {
grammarParserParses('start: "a"', literalGrammar("a"));
grammarParserParses('start: "\\n"', literalGrammar("\n"));
grammarParserParses('start: "\\0"', literalGrammar("\0"));
grammarParserParses('start: "\\x00"', literalGrammar("\x00"));
grammarParserParses('start: "\\u0120"', literalGrammar("\u0120"));
grammarParserParses('start: "\\\n"', literalGrammar("\n"));
});
/* Canonical simpleDoubleQuotedCharacter is "a". */
test("parses simpleDoubleQuotedCharacter", function() {
grammarParserParses('start: "a"', literalGrammar("a"));
grammarParserParses('start: "\'"', literalGrammar("'"));
grammarParserDoesNotParse('start: """');
grammarParserDoesNotParse('start: "\\"');
grammarParserDoesNotParse('start: "\n"');
grammarParserDoesNotParse('start: "\r"');
grammarParserDoesNotParse('start: "\u2028"');
grammarParserDoesNotParse('start: "\u2029"');
});
/* Canonical singleQuotedLiteral is "'abcd'". */
test("parses singleQuotedLiteral", function() {
grammarParserParses("start: ''", literalGrammar(""));
grammarParserParses("start: 'a'", literalGrammar("a"));
grammarParserParses("start: 'abc'", literalGrammar("abc"));
grammarParserParses("start: 'abcd'\n", literalGrammar("abcd"));
});
/* Canonical singleQuotedCharacter is "a". */
test("parses singleQuotedCharacter", function() {
grammarParserParses("start: 'a'", literalGrammar("a"));
grammarParserParses("start: '\\n'", literalGrammar("\n"));
grammarParserParses("start: '\\0'", literalGrammar("\0"));
grammarParserParses("start: '\\x00'", literalGrammar("\x00"));
grammarParserParses("start: '\\u0120'", literalGrammar("\u0120"));
grammarParserParses("start: '\\\n'", literalGrammar("\n"));
});
/* Canonical simpleSingleQuotedCharacter is "a". */
test("parses simpleSingleQuotedCharacter", function() {
grammarParserParses("start: 'a'", literalGrammar("a"));
grammarParserParses("start: '\"'", literalGrammar("\""));
grammarParserDoesNotParse("start: '''");
grammarParserDoesNotParse("start: '\\'");
grammarParserDoesNotParse("start: '\n'");
grammarParserDoesNotParse("start: '\r'");
grammarParserDoesNotParse("start: '\u2028'");
grammarParserDoesNotParse("start: '\u2029'");
});
/* Canonical class is "[a-d]". */
test("parses classCharacterRange", function() {
grammarParserParses("start: []", classGrammar(""));
grammarParserParses("start: [a-d]", classGrammar("abcd"));
grammarParserParses("start: [a]", classGrammar("a"));
grammarParserParses("start: [a-de-hi-l]", classGrammar("abcdefghijkl"));
grammarParserParses("start: [a-d]\n", classGrammar("abcd"));
});
/* Canonical classCharacterRange is "a-d". */
test("parses classCharacterRange", function() {
grammarParserParses("start: [a-d]", classGrammar("abcd"));
grammarParserParses("start: [a-a]", classGrammar("a"));
grammarParserDoesNotParse("start: [b-a]");
});
/* Canonical classCharacter is "a". */
test("parses classCharacter", function() {
grammarParserParses("start: [a]", classGrammar("a"));
});
/* Canonical bracketDelimitedCharacter is "a". */
test("parses bracketDelimitedCharacter", function() {
grammarParserParses("start: [a]", classGrammar("a"));
grammarParserParses("start: [\\n]", classGrammar("\n"));
grammarParserParses("start: [\\0]", classGrammar("\0"));
grammarParserParses("start: [\\x00]", classGrammar("\x00"));
grammarParserParses("start: [\\u0120]", classGrammar("\u0120"));
grammarParserParses("start: [\\\n]", classGrammar("\n"));
});
/* Canonical simpleBracketDelimiedCharacter is "a". */
test("parses simpleBracketDelimitedCharacter", function() {
grammarParserParses("start: [a]", classGrammar("a"));
grammarParserParses("start: [[]", classGrammar("["));
grammarParserDoesNotParse("start: []]");
grammarParserDoesNotParse("start: [\\]");
grammarParserDoesNotParse("start: [\n]");
grammarParserDoesNotParse("start: [\r]");
grammarParserDoesNotParse("start: [\u2028]");
grammarParserDoesNotParse("start: [\u2029]");
});
/* Canonical simpleEscapeSequence is "\\n". */
test("parses simpleEscapeSequence", function() {
grammarParserParses('start: "\\\'"', literalGrammar("'"));
grammarParserParses('start: "\\""', literalGrammar("\""));
grammarParserParses('start: "\\\\"', literalGrammar("\\"));
grammarParserParses('start: "\\b"', literalGrammar("\b"));
grammarParserParses('start: "\\f"', literalGrammar("\f"));
grammarParserParses('start: "\\n"', literalGrammar("\n"));
grammarParserParses('start: "\\r"', literalGrammar("\r"));
grammarParserParses('start: "\\t"', literalGrammar("\t"));
grammarParserParses('start: "\\v"', literalGrammar("\v"));
grammarParserParses('start: "\\a"', literalGrammar("a"));
});
/* Canonical zeroEscapeSequence is "\\0". */
test("parses zeroEscapeSequence", function() {
grammarParserParses('start: "\\0"', literalGrammar("\0"));
grammarParserDoesNotParse('start: "\\00"');
grammarParserDoesNotParse('start: "\\09"');
});
/* Canonical hexEscapeSequence is "\\x00". */
test("parses hexEscapeSequence", function() {
grammarParserParses('start: "\\x00"', literalGrammar("\x00"));
grammarParserParses('start: "\\x09"', literalGrammar("\x09"));
grammarParserParses('start: "\\x0a"', literalGrammar("\x0a"));
grammarParserParses('start: "\\x0f"', literalGrammar("\x0f"));
grammarParserParses('start: "\\x0A"', literalGrammar("\x0A"));
grammarParserParses('start: "\\x0F"', literalGrammar("\x0F"));
grammarParserDoesNotParse('start: "\\x0"');
grammarParserParses('start: "\\x000"', literalGrammar("\x000"));
});
/* Canonical unicodeEscapeSequence is "\\u0120". */
test("parses unicodeEscapeSequence", function() {
grammarParserParses('start: "\\u0120"', literalGrammar("\u0120"));
grammarParserParses('start: "\\u0129"', literalGrammar("\u0129"));
grammarParserParses('start: "\\u012a"', literalGrammar("\u012a"));
grammarParserParses('start: "\\u012f"', literalGrammar("\u012f"));
grammarParserParses('start: "\\u012A"', literalGrammar("\u012A"));
grammarParserParses('start: "\\u012F"', literalGrammar("\u012F"));
grammarParserDoesNotParse('start: "\\u012"');
grammarParserParses('start: "\\u01234"', literalGrammar("\u01234"));
});
/* Canonical eolEscapeSequence is "\\\n". */
test("parses eolEscapeSequence", function() {
grammarParserParses('start: "\\\n"', literalGrammar("\n"));
grammarParserParses('start: "\\\r\n"', literalGrammar("\r\n"));
grammarParserParses('start: "\\\r"', literalGrammar("\r"));
grammarParserParses('start: "\\\u2028"', literalGrammar("\u2028"));
grammarParserParses('start: "\\\u2029"', literalGrammar("\u2029"));
});
/* Canonical __ is "\n". */
test("parses __", function() {
grammarParserParses('start:"abcd"', simpleGrammar);
grammarParserParses('start: "abcd"', simpleGrammar);
grammarParserParses('start:\n"abcd"', simpleGrammar);
grammarParserParses('start: "abcd"', simpleGrammar);
});
/* Trivial character class rules are not tested. */
/* Canonical eol is "\n". */
test("parses eol", function() {
grammarParserParses('start:\n"abcd"', simpleGrammar);
grammarParserParses('start:\r\n"abcd"', simpleGrammar);
grammarParserParses('start:\r"abcd"', simpleGrammar);
grammarParserParses('start:\u2028"abcd"', simpleGrammar);
grammarParserParses('start:\u2029"abcd"', simpleGrammar);
});
/* Canonical eolChar is "\n". */
test("parses eolChar", function() {
grammarParserParses('start:\n"abcd"', simpleGrammar);
grammarParserParses('start:\r"abcd"', simpleGrammar);
grammarParserParses('start:\u2028"abcd"', simpleGrammar);
grammarParserParses('start:\u2029"abcd"', simpleGrammar);
});
/* Canonical whitespace is " ". */
test("parses whitespace", function() {
grammarParserParses('start:\t"abcd"', simpleGrammar);
grammarParserParses('start:\v"abcd"', simpleGrammar);
grammarParserParses('start:\f"abcd"', simpleGrammar);
grammarParserParses('start: "abcd"', simpleGrammar);
grammarParserParses('start:\xA0"abcd"', simpleGrammar);
grammarParserParses('start:\uFEFF"abcd"', simpleGrammar);
grammarParserParses('start:\u1680"abcd"', simpleGrammar);
grammarParserParses('start:\u180E"abcd"', simpleGrammar);
grammarParserParses('start:\u2000"abcd"', simpleGrammar);
grammarParserParses('start:\u2001"abcd"', simpleGrammar);
grammarParserParses('start:\u2002"abcd"', simpleGrammar);
grammarParserParses('start:\u2003"abcd"', simpleGrammar);
grammarParserParses('start:\u2004"abcd"', simpleGrammar);
grammarParserParses('start:\u2005"abcd"', simpleGrammar);
grammarParserParses('start:\u2006"abcd"', simpleGrammar);
grammarParserParses('start:\u2007"abcd"', simpleGrammar);
grammarParserParses('start:\u2008"abcd"', simpleGrammar);
grammarParserParses('start:\u2009"abcd"', simpleGrammar);
grammarParserParses('start:\u200A"abcd"', simpleGrammar);
grammarParserParses('start:\u202F"abcd"', simpleGrammar);
grammarParserParses('start:\u205F"abcd"', simpleGrammar);
grammarParserParses('start:\u3000"abcd"', simpleGrammar);
});
}
})();