Use only double quotes for strings

See #443
redux
David Majda 8 years ago
parent 6294bb5b13
commit 12112310f2

@ -12,19 +12,19 @@ let js = {
// //
// For portability, we also escape all control and non-ASCII characters. // For portability, we also escape all control and non-ASCII characters.
return s return s
.replace(/\\/g, '\\\\') // backslash .replace(/\\/g, "\\\\") // backslash
.replace(/"/g, '\\"') // closing double quote .replace(/"/g, "\\\"") // closing double quote
.replace(/\0/g, '\\0') // null .replace(/\0/g, "\\0") // null
.replace(/\x08/g, '\\b') // backspace .replace(/\x08/g, "\\b") // backspace
.replace(/\t/g, '\\t') // horizontal tab .replace(/\t/g, "\\t") // horizontal tab
.replace(/\n/g, '\\n') // line feed .replace(/\n/g, "\\n") // line feed
.replace(/\v/g, '\\v') // vertical tab .replace(/\v/g, "\\v") // vertical tab
.replace(/\f/g, '\\f') // form feed .replace(/\f/g, "\\f") // form feed
.replace(/\r/g, '\\r') // carriage return .replace(/\r/g, "\\r") // carriage return
.replace(/[\x00-\x0F]/g, ch => '\\x0' + hex(ch)) .replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch))
.replace(/[\x10-\x1F\x7F-\xFF]/g, ch => '\\x' + hex(ch)) .replace(/[\x10-\x1F\x7F-\xFF]/g, ch => "\\x" + hex(ch))
.replace(/[\u0100-\u0FFF]/g, ch => '\\u0' + hex(ch)) .replace(/[\u0100-\u0FFF]/g, ch => "\\u0" + hex(ch))
.replace(/[\u1000-\uFFFF]/g, ch => '\\u' + hex(ch)); .replace(/[\u1000-\uFFFF]/g, ch => "\\u" + hex(ch));
}, },
regexpClassEscape: function(s) { regexpClassEscape: function(s) {
@ -32,22 +32,22 @@ let js = {
// //
// For portability, we also escape all control and non-ASCII characters. // For portability, we also escape all control and non-ASCII characters.
return s return s
.replace(/\\/g, '\\\\') // backslash .replace(/\\/g, "\\\\") // backslash
.replace(/\//g, '\\/') // closing slash .replace(/\//g, "\\/") // closing slash
.replace(/\]/g, '\\]') // closing bracket .replace(/\]/g, "\\]") // closing bracket
.replace(/\^/g, '\\^') // caret .replace(/\^/g, "\\^") // caret
.replace(/-/g, '\\-') // dash .replace(/-/g, "\\-") // dash
.replace(/\0/g, '\\0') // null .replace(/\0/g, "\\0") // null
.replace(/\x08/g, '\\b') // backspace .replace(/\x08/g, "\\b") // backspace
.replace(/\t/g, '\\t') // horizontal tab .replace(/\t/g, "\\t") // horizontal tab
.replace(/\n/g, '\\n') // line feed .replace(/\n/g, "\\n") // line feed
.replace(/\v/g, '\\v') // vertical tab .replace(/\v/g, "\\v") // vertical tab
.replace(/\f/g, '\\f') // form feed .replace(/\f/g, "\\f") // form feed
.replace(/\r/g, '\\r') // carriage return .replace(/\r/g, "\\r") // carriage return
.replace(/[\x00-\x0F]/g, ch => '\\x0' + hex(ch)) .replace(/[\x00-\x0F]/g, ch => "\\x0" + hex(ch))
.replace(/[\x10-\x1F\x7F-\xFF]/g, ch => '\\x' + hex(ch)) .replace(/[\x10-\x1F\x7F-\xFF]/g, ch => "\\x" + hex(ch))
.replace(/[\u0100-\u0FFF]/g, ch => '\\u0' + hex(ch)) .replace(/[\u0100-\u0FFF]/g, ch => "\\u0" + hex(ch))
.replace(/[\u1000-\uFFFF]/g, ch => '\\u' + hex(ch)); .replace(/[\u1000-\uFFFF]/g, ch => "\\u" + hex(ch));
} }
}; };

@ -304,7 +304,7 @@ function generateBytecode(ast) {
named: function(node, context) { named: function(node, context) {
let nameIndex = addConst( let nameIndex = addConst(
'peg$otherExpectation("' + js.stringEscape(node.name) + '")' "peg$otherExpectation(\"" + js.stringEscape(node.name) + "\")"
); );
// The code generated below is slightly suboptimal because |FAIL| pushes // The code generated below is slightly suboptimal because |FAIL| pushes
@ -535,17 +535,17 @@ function generateBytecode(ast) {
literal: function(node) { literal: function(node) {
if (node.value.length > 0) { if (node.value.length > 0) {
let stringIndex = addConst('"' let stringIndex = addConst("\""
+ js.stringEscape( + js.stringEscape(
node.ignoreCase ? node.value.toLowerCase() : node.value node.ignoreCase ? node.value.toLowerCase() : node.value
) )
+ '"' + "\""
); );
let expectedIndex = addConst( let expectedIndex = addConst(
'peg$literalExpectation(' "peg$literalExpectation("
+ '"' + js.stringEscape(node.value) + '", ' + "\"" + js.stringEscape(node.value) + "\", "
+ node.ignoreCase + node.ignoreCase
+ ')' + ")"
); );
// For case-sensitive strings the value must match the beginning of the // For case-sensitive strings the value must match the beginning of the
@ -561,37 +561,37 @@ function generateBytecode(ast) {
[op.FAIL, expectedIndex] [op.FAIL, expectedIndex]
); );
} else { } else {
let stringIndex = addConst('""'); let stringIndex = addConst("\"\"");
return [op.PUSH, stringIndex]; return [op.PUSH, stringIndex];
} }
}, },
"class": function(node) { "class": function(node) {
let regexp = '/^[' let regexp = "/^["
+ (node.inverted ? '^' : '') + (node.inverted ? "^" : "")
+ node.parts.map(part => + node.parts.map(part =>
Array.isArray(part) Array.isArray(part)
? js.regexpClassEscape(part[0]) ? js.regexpClassEscape(part[0])
+ '-' + "-"
+ js.regexpClassEscape(part[1]) + js.regexpClassEscape(part[1])
: js.regexpClassEscape(part) : js.regexpClassEscape(part)
).join('') ).join("")
+ ']/' + (node.ignoreCase ? 'i' : ''); + "]/" + (node.ignoreCase ? "i" : "");
let parts = '[' let parts = "["
+ node.parts.map(part => + node.parts.map(part =>
Array.isArray(part) Array.isArray(part)
? '["' + js.stringEscape(part[0]) + '", "' + js.stringEscape(part[1]) + '"]' ? "[\"" + js.stringEscape(part[0]) + "\", \"" + js.stringEscape(part[1]) + "\"]"
: '"' + js.stringEscape(part) + '"' : "\"" + js.stringEscape(part) + "\""
).join(', ') ).join(", ")
+ ']'; + "]";
let regexpIndex = addConst(regexp); let regexpIndex = addConst(regexp);
let expectedIndex = addConst( let expectedIndex = addConst(
'peg$classExpectation(' "peg$classExpectation("
+ parts + ', ' + parts + ", "
+ node.inverted + ', ' + node.inverted + ", "
+ node.ignoreCase + node.ignoreCase
+ ')' + ")"
); );
return buildCondition( return buildCondition(
@ -602,7 +602,7 @@ function generateBytecode(ast) {
}, },
any: function() { any: function() {
let expectedIndex = addConst('peg$anyExpectation()'); let expectedIndex = addConst("peg$anyExpectation()");
return buildCondition( return buildCondition(
[op.MATCH_ANY], [op.MATCH_ANY],

File diff suppressed because it is too large Load Diff

@ -62,28 +62,28 @@ peg$SyntaxError.buildMessage = function(expected, found) {
function literalEscape(s) { function literalEscape(s) {
return s return s
.replace(/\\/g, '\\\\') .replace(/\\/g, "\\\\")
.replace(/"/g, '\\"') .replace(/"/g, "\\\"")
.replace(/\0/g, '\\0') .replace(/\0/g, "\\0")
.replace(/\t/g, '\\t') .replace(/\t/g, "\\t")
.replace(/\n/g, '\\n') .replace(/\n/g, "\\n")
.replace(/\r/g, '\\r') .replace(/\r/g, "\\r")
.replace(/[\x00-\x0F]/g, function(ch) { return '\\x0' + hex(ch); }) .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); })
.replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return '\\x' + hex(ch); }); .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); });
} }
function classEscape(s) { function classEscape(s) {
return s return s
.replace(/\\/g, '\\\\') .replace(/\\/g, "\\\\")
.replace(/\]/g, '\\]') .replace(/\]/g, "\\]")
.replace(/\^/g, '\\^') .replace(/\^/g, "\\^")
.replace(/-/g, '\\-') .replace(/-/g, "\\-")
.replace(/\0/g, '\\0') .replace(/\0/g, "\\0")
.replace(/\t/g, '\\t') .replace(/\t/g, "\\t")
.replace(/\n/g, '\\n') .replace(/\n/g, "\\n")
.replace(/\r/g, '\\r') .replace(/\r/g, "\\r")
.replace(/[\x00-\x0F]/g, function(ch) { return '\\x0' + hex(ch); }) .replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); })
.replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return '\\x' + hex(ch); }); .replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); });
} }
function describeExpectation(expectation) { function describeExpectation(expectation) {
@ -238,7 +238,7 @@ function peg$parse(input, options) {
// don't need to put it around nodes that can't contain any labels or // don't need to put it around nodes that can't contain any labels or
// nodes that already isolate label scope themselves. This leaves us with // nodes that already isolate label scope themselves. This leaves us with
// "labeled" and "sequence". // "labeled" and "sequence".
return expression.type === 'labeled' || expression.type === 'sequence' return expression.type === "labeled" || expression.type === "sequence"
? { type: "group", expression: expression } ? { type: "group", expression: expression }
: expression; : expression;
}; };

@ -8,22 +8,22 @@ let peg = require("../../lib/peg");
describe("generated parser API", function() { describe("generated parser API", function() {
describe("parse", function() { describe("parse", function() {
it("parses input", function() { it("parses input", function() {
let parser = peg.generate('start = "a"'); let parser = peg.generate("start = 'a'");
expect(parser.parse("a")).toBe("a"); expect(parser.parse("a")).toBe("a");
}); });
it("throws an exception on syntax error", function() { it("throws an exception on syntax error", function() {
let parser = peg.generate('start = "a"'); let parser = peg.generate("start = 'a'");
expect(() => { parser.parse("b"); }).toThrow(); expect(() => { parser.parse("b"); }).toThrow();
}); });
describe("start rule", function() { describe("start rule", function() {
let parser = peg.generate([ let parser = peg.generate([
'a = "x" { return "a"; }', "a = 'x' { return 'a'; }",
'b = "x" { return "b"; }', "b = 'x' { return 'b'; }",
'c = "x" { return "c"; }' "c = 'x' { return 'c'; }"
].join("\n"), { allowedStartRules: ["b", "c"] }); ].join("\n"), { allowedStartRules: ["b", "c"] });
describe("when |startRule| is not set", function() { describe("when |startRule| is not set", function() {
@ -48,9 +48,9 @@ describe("generated parser API", function() {
describe("tracing", function() { describe("tracing", function() {
let parser = peg.generate([ let parser = peg.generate([
'start = a / b', "start = a / b",
'a = "a"', "a = 'a'",
'b = "b"' "b = 'b'"
].join("\n"), { trace: true }); ].join("\n"), { trace: true });
describe("default tracer", function() { describe("default tracer", function() {
@ -135,7 +135,7 @@ describe("generated parser API", function() {
}); });
it("accepts custom options", function() { it("accepts custom options", function() {
let parser = peg.generate('start = "a"'); let parser = peg.generate("start = 'a'");
parser.parse("a", { foo: 42 }); parser.parse("a", { foo: 42 });
}); });

@ -5,25 +5,25 @@ let peg = require("../../lib/peg");
describe("PEG.js API", function() { describe("PEG.js API", function() {
describe("generate", function() { describe("generate", function() {
it("generates a parser", function() { it("generates a parser", function() {
let parser = peg.generate('start = "a"'); let parser = peg.generate("start = 'a'");
expect(typeof parser).toBe("object"); expect(typeof parser).toBe("object");
expect(parser.parse("a")).toBe("a"); expect(parser.parse("a")).toBe("a");
}); });
it("throws an exception on syntax error", function() { it("throws an exception on syntax error", function() {
expect(() => { peg.generate('start = @'); }).toThrow(); expect(() => { peg.generate("start = @"); }).toThrow();
}); });
it("throws an exception on semantic error", function() { it("throws an exception on semantic error", function() {
expect(() => { peg.generate('start = undefined'); }).toThrow(); expect(() => { peg.generate("start = undefined"); }).toThrow();
}); });
describe("allowed start rules", function() { describe("allowed start rules", function() {
let grammar = [ let grammar = [
'a = "x"', "a = 'x'",
'b = "x"', "b = 'x'",
'c = "x"' "c = 'x'"
].join("\n"); ].join("\n");
// The |allowedStartRules| option is implemented separately for each // The |allowedStartRules| option is implemented separately for each
@ -82,9 +82,9 @@ describe("PEG.js API", function() {
describe("intermediate results caching", function() { describe("intermediate results caching", function() {
let grammar = [ let grammar = [
'{ var n = 0; }', "{ var n = 0; }",
'start = (a "b") / (a "c") { return n; }', "start = (a 'b') / (a 'c') { return n; }",
'a = "a" { n++; }' "a = 'a' { n++; }"
].join("\n"); ].join("\n");
describe("when |cache| is not set", function() { describe("when |cache| is not set", function() {
@ -113,7 +113,7 @@ describe("PEG.js API", function() {
}); });
describe("tracing", function() { describe("tracing", function() {
let grammar = 'start = "a"'; let grammar = "start = 'a'";
describe("when |trace| is not set", function() { describe("when |trace| is not set", function() {
it("generated parser doesn't trace", function() { it("generated parser doesn't trace", function() {
@ -153,7 +153,7 @@ describe("PEG.js API", function() {
// write the specs without turning this into a performance test. // write the specs without turning this into a performance test.
describe("output", function() { describe("output", function() {
let grammar = 'start = "a"'; let grammar = "start = 'a'";
describe("when |output| is not set", function() { describe("when |output| is not set", function() {
it("returns generated parser object", function() { it("returns generated parser object", function() {
@ -190,7 +190,7 @@ describe("PEG.js API", function() {
// The |plugins| option is tested in plugin API specs. // The |plugins| option is tested in plugin API specs.
it("accepts custom options", function() { it("accepts custom options", function() {
peg.generate('start = "a"', { foo: 42 }); peg.generate("start = 'a'", { foo: 42 });
}); });
}); });
}); });

@ -35,7 +35,7 @@ describe("plugin API", function() {
}); });
describe("use", function() { describe("use", function() {
let grammar = 'start = "a"'; let grammar = "start = 'a'";
it("is called for each plugin", function() { it("is called for each plugin", function() {
let pluginsUsed = [false, false, false]; let pluginsUsed = [false, false, false];
@ -56,7 +56,7 @@ describe("plugin API", function() {
expect(config).toBeObject(); expect(config).toBeObject();
expect(config.parser).toBeObject(); expect(config.parser).toBeObject();
expect(config.parser.parse('start = "a"')).toBeObject(); expect(config.parser.parse("start = 'a'")).toBeObject();
expect(config.passes).toBeObject(); expect(config.passes).toBeObject();
@ -95,24 +95,24 @@ describe("plugin API", function() {
let plugin = { let plugin = {
use: function(config) { use: function(config) {
let parser = peg.generate([ let parser = peg.generate([
'start = .* {', "start = .* {",
' return {', " return {",
' type: "grammar",', " type: 'grammar',",
' rules: [', " rules: [",
' {', " {",
' type: "rule",', " type: 'rule',",
' name: "start",', " name: 'start',",
' expression: { type: "literal", value: text(), ignoreCase: false }', " expression: { type: 'literal', value: text(), ignoreCase: false }",
' }', " }",
' ]', " ]",
' };', " };",
'}' "}"
].join("\n")); ].join("\n"));
config.parser = parser; config.parser = parser;
} }
}; };
let parser = peg.generate('a', { plugins: [plugin] }); let parser = peg.generate("a", { plugins: [plugin] });
expect(parser.parse("a")).toBe("a"); expect(parser.parse("a")).toBe("a");
}); });
@ -121,7 +121,7 @@ describe("plugin API", function() {
let plugin = { let plugin = {
use: function(config) { use: function(config) {
let pass = ast => { let pass = ast => {
ast.code = '({ parse: function() { return 42; } })'; ast.code = "({ parse: function() { return 42; } })";
}; };
config.passes.generate = [pass]; config.passes.generate = [pass];
@ -134,9 +134,9 @@ describe("plugin API", function() {
it("can change options", function() { it("can change options", function() {
let grammar = [ let grammar = [
'a = "x"', "a = 'x'",
'b = "x"', "b = 'x'",
'c = "x"' "c = 'x'"
].join("\n"); ].join("\n");
let plugin = { let plugin = {
use: function(config, options) { use: function(config, options) {

File diff suppressed because it is too large Load Diff

@ -16,9 +16,9 @@ describe("compiler pass |generateBytecode|", function() {
describe("for grammar", function() { describe("for grammar", function() {
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST([ expect(pass).toChangeAST([
'a = "a"', "a = 'a'",
'b = "b"', "b = 'b'",
'c = "c"' "c = 'c'"
].join("\n"), { ].join("\n"), {
rules: [ rules: [
{ bytecode: [18, 0, 2, 2, 22, 0, 23, 1] }, { bytecode: [18, 0, 2, 2, 22, 0, 23, 1] },
@ -30,30 +30,30 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST([ expect(pass).toChangeAST([
'a = "a"', "a = 'a'",
'b = "b"', "b = 'b'",
'c = "c"' "c = 'c'"
].join("\n"), constsDetails([ ].join("\n"), constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)', "peg$literalExpectation(\"a\", false)",
'"b"', "\"b\"",
'peg$literalExpectation("b", false)', "peg$literalExpectation(\"b\", false)",
'"c"', "\"c\"",
'peg$literalExpectation("c", false)' "peg$literalExpectation(\"c\", false)"
])); ]));
}); });
}); });
describe("for rule", function() { describe("for rule", function() {
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST('start = "a"', bytecodeDetails([ expect(pass).toChangeAST("start = 'a'", bytecodeDetails([
18, 0, 2, 2, 22, 0, 23, 1 // <expression> 18, 0, 2, 2, 22, 0, 23, 1 // <expression>
])); ]));
}); });
}); });
describe("for named", function() { describe("for named", function() {
let grammar = 'start "start" = "a"'; let grammar = "start 'start' = 'a'";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -67,16 +67,16 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'peg$otherExpectation("start")', "peg$otherExpectation(\"start\")",
'"a"', "\"a\"",
'peg$literalExpectation("a", false)' "peg$literalExpectation(\"a\", false)"
])); ]));
}); });
}); });
describe("for choice", function() { describe("for choice", function() {
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST('start = "a" / "b" / "c"', bytecodeDetails([ expect(pass).toChangeAST("start = 'a' / 'b' / 'c'", bytecodeDetails([
18, 0, 2, 2, 22, 0, 23, 1, // <alternatives[0]> 18, 0, 2, 2, 22, 0, 23, 1, // <alternatives[0]>
14, 21, 0, // IF_ERROR 14, 21, 0, // IF_ERROR
6, // * POP 6, // * POP
@ -90,7 +90,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for action", function() { describe("for action", function() {
describe("without labels", function() { describe("without labels", function() {
let grammar = 'start = "a" { code }'; let grammar = "start = 'a' { code }";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -105,15 +105,15 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)', "peg$literalExpectation(\"a\", false)",
'function() { code }' "function() { code }"
])); ]));
}); });
}); });
describe("with one label", function() { describe("with one label", function() {
let grammar = 'start = a:"a" { code }'; let grammar = "start = a:'a' { code }";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -128,15 +128,15 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)', "peg$literalExpectation(\"a\", false)",
'function(a) { code }' "function(a) { code }"
])); ]));
}); });
}); });
describe("with multiple labels", function() { describe("with multiple labels", function() {
let grammar = 'start = a:"a" b:"b" c:"c" { code }'; let grammar = "start = a:'a' b:'b' c:'c' { code }";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -164,20 +164,20 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)', "peg$literalExpectation(\"a\", false)",
'"b"', "\"b\"",
'peg$literalExpectation("b", false)', "peg$literalExpectation(\"b\", false)",
'"c"', "\"c\"",
'peg$literalExpectation("c", false)', "peg$literalExpectation(\"c\", false)",
'function(a, b, c) { code }' "function(a, b, c) { code }"
])); ]));
}); });
}); });
}); });
describe("for sequence", function() { describe("for sequence", function() {
let grammar = 'start = "a" "b" "c"'; let grammar = "start = 'a' 'b' 'c'";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -204,19 +204,19 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)', "peg$literalExpectation(\"a\", false)",
'"b"', "\"b\"",
'peg$literalExpectation("b", false)', "peg$literalExpectation(\"b\", false)",
'"c"', "\"c\"",
'peg$literalExpectation("c", false)' "peg$literalExpectation(\"c\", false)"
])); ]));
}); });
}); });
describe("for labeled", function() { describe("for labeled", function() {
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST('start = a:"a"', bytecodeDetails([ expect(pass).toChangeAST("start = a:'a'", bytecodeDetails([
18, 0, 2, 2, 22, 0, 23, 1 // <expression> 18, 0, 2, 2, 22, 0, 23, 1 // <expression>
])); ]));
}); });
@ -224,7 +224,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for text", function() { describe("for text", function() {
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST('start = $"a"', bytecodeDetails([ expect(pass).toChangeAST("start = $'a'", bytecodeDetails([
5, // PUSH_CURR_POS 5, // PUSH_CURR_POS
18, 0, 2, 2, 22, 0, 23, 1, // <expression> 18, 0, 2, 2, 22, 0, 23, 1, // <expression>
15, 2, 1, // IF_NOT_ERROR 15, 2, 1, // IF_NOT_ERROR
@ -236,7 +236,7 @@ describe("compiler pass |generateBytecode|", function() {
}); });
describe("for simple_and", function() { describe("for simple_and", function() {
let grammar = 'start = &"a"'; let grammar = "start = &'a'";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -256,14 +256,14 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)' "peg$literalExpectation(\"a\", false)"
])); ]));
}); });
}); });
describe("for simple_not", function() { describe("for simple_not", function() {
let grammar = 'start = !"a"'; let grammar = "start = !'a'";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -283,14 +283,14 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)' "peg$literalExpectation(\"a\", false)"
])); ]));
}); });
}); });
describe("for optional", function() { describe("for optional", function() {
let grammar = 'start = "a"?'; let grammar = "start = 'a'?";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -303,14 +303,14 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)' "peg$literalExpectation(\"a\", false)"
])); ]));
}); });
}); });
describe("for zero_or_more", function() { describe("for zero_or_more", function() {
let grammar = 'start = "a"*'; let grammar = "start = 'a'*";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -325,14 +325,14 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)' "peg$literalExpectation(\"a\", false)"
])); ]));
}); });
}); });
describe("for one_or_more", function() { describe("for one_or_more", function() {
let grammar = 'start = "a"+'; let grammar = "start = 'a'+";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -351,15 +351,15 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)' "peg$literalExpectation(\"a\", false)"
])); ]));
}); });
}); });
describe("for group", function() { describe("for group", function() {
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST('start = ("a")', bytecodeDetails([ expect(pass).toChangeAST("start = ('a')", bytecodeDetails([
18, 0, 2, 2, 22, 0, 23, 1 // <expression> 18, 0, 2, 2, 22, 0, 23, 1 // <expression>
])); ]));
}); });
@ -367,7 +367,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for semantic_and", function() { describe("for semantic_and", function() {
describe("without labels", function() { describe("without labels", function() {
let grammar = 'start = &{ code }'; let grammar = "start = &{ code }";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -384,13 +384,13 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST( expect(pass).toChangeAST(
grammar, grammar,
constsDetails(['function() { code }']) constsDetails(["function() { code }"])
); );
}); });
}); });
describe("with labels", function() { describe("with labels", function() {
let grammar = 'start = a:"a" b:"b" c:"c" &{ code }'; let grammar = "start = a:'a' b:'b' c:'c' &{ code }";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -428,13 +428,13 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)', "peg$literalExpectation(\"a\", false)",
'"b"', "\"b\"",
'peg$literalExpectation("b", false)', "peg$literalExpectation(\"b\", false)",
'"c"', "\"c\"",
'peg$literalExpectation("c", false)', "peg$literalExpectation(\"c\", false)",
'function(a, b, c) { code }' "function(a, b, c) { code }"
])); ]));
}); });
}); });
@ -442,7 +442,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for semantic_not", function() { describe("for semantic_not", function() {
describe("without labels", function() { describe("without labels", function() {
let grammar = 'start = !{ code }'; let grammar = "start = !{ code }";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -459,13 +459,13 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST( expect(pass).toChangeAST(
grammar, grammar,
constsDetails(['function() { code }']) constsDetails(["function() { code }"])
); );
}); });
}); });
describe("with labels", function() { describe("with labels", function() {
let grammar = 'start = a:"a" b:"b" c:"c" !{ code }'; let grammar = "start = a:'a' b:'b' c:'c' !{ code }";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -503,13 +503,13 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)', "peg$literalExpectation(\"a\", false)",
'"b"', "\"b\"",
'peg$literalExpectation("b", false)', "peg$literalExpectation(\"b\", false)",
'"c"', "\"c\"",
'peg$literalExpectation("c", false)', "peg$literalExpectation(\"c\", false)",
'function(a, b, c) { code }' "function(a, b, c) { code }"
])); ]));
}); });
}); });
@ -518,8 +518,8 @@ describe("compiler pass |generateBytecode|", function() {
describe("for rule_ref", function() { describe("for rule_ref", function() {
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST([ expect(pass).toChangeAST([
'start = other', "start = other",
'other = "other"' "other = 'other'"
].join("\n"), { ].join("\n"), {
rules: [ rules: [
{ {
@ -533,7 +533,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for literal", function() { describe("for literal", function() {
describe("empty", function() { describe("empty", function() {
let grammar = 'start = ""'; let grammar = "start = ''";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -542,12 +542,12 @@ describe("compiler pass |generateBytecode|", function() {
}); });
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails(['""'])); expect(pass).toChangeAST(grammar, constsDetails(["\"\""]));
}); });
}); });
describe("non-empty case-sensitive", function() { describe("non-empty case-sensitive", function() {
let grammar = 'start = "a"'; let grammar = "start = 'a'";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -559,14 +559,14 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("a", false)' "peg$literalExpectation(\"a\", false)"
])); ]));
}); });
}); });
describe("non-empty case-insensitive", function() { describe("non-empty case-insensitive", function() {
let grammar = 'start = "A"i'; let grammar = "start = 'A'i";
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -578,8 +578,8 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST(grammar, constsDetails([ expect(pass).toChangeAST(grammar, constsDetails([
'"a"', "\"a\"",
'peg$literalExpectation("A", true)' "peg$literalExpectation(\"A\", true)"
])); ]));
}); });
}); });
@ -587,7 +587,7 @@ describe("compiler pass |generateBytecode|", function() {
describe("for class", function() { describe("for class", function() {
it("generates correct bytecode", function() { it("generates correct bytecode", function() {
expect(pass).toChangeAST('start = [a]', bytecodeDetails([ expect(pass).toChangeAST("start = [a]", bytecodeDetails([
20, 0, 2, 2, // MATCH_REGEXP 20, 0, 2, 2, // MATCH_REGEXP
21, 1, // * ACCEPT_N 21, 1, // * ACCEPT_N
23, 1 // * FAIL 23, 1 // * FAIL
@ -596,43 +596,43 @@ describe("compiler pass |generateBytecode|", function() {
describe("non-inverted case-sensitive", function() { describe("non-inverted case-sensitive", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST('start = [a]', constsDetails([ expect(pass).toChangeAST("start = [a]", constsDetails([
'/^[a]/', "/^[a]/",
'peg$classExpectation(["a"], false, false)' "peg$classExpectation([\"a\"], false, false)"
])); ]));
}); });
}); });
describe("inverted case-sensitive", function() { describe("inverted case-sensitive", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST('start = [^a]', constsDetails([ expect(pass).toChangeAST("start = [^a]", constsDetails([
'/^[^a]/', "/^[^a]/",
'peg$classExpectation(["a"], true, false)' "peg$classExpectation([\"a\"], true, false)"
])); ]));
}); });
}); });
describe("non-inverted case-insensitive", function() { describe("non-inverted case-insensitive", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST('start = [a]i', constsDetails([ expect(pass).toChangeAST("start = [a]i", constsDetails([
'/^[a]/i', "/^[a]/i",
'peg$classExpectation(["a"], false, true)' "peg$classExpectation([\"a\"], false, true)"
])); ]));
}); });
}); });
describe("complex", function() { describe("complex", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST('start = [ab-def-hij-l]', constsDetails([ expect(pass).toChangeAST("start = [ab-def-hij-l]", constsDetails([
'/^[ab-def-hij-l]/', "/^[ab-def-hij-l]/",
'peg$classExpectation(["a", ["b", "d"], "e", ["f", "h"], "i", ["j", "l"]], false, false)' "peg$classExpectation([\"a\", [\"b\", \"d\"], \"e\", [\"f\", \"h\"], \"i\", [\"j\", \"l\"]], false, false)"
])); ]));
}); });
}); });
}); });
describe("for any", function() { describe("for any", function() {
let grammar = 'start = .'; let grammar = "start = .";
it("generates bytecode", function() { it("generates bytecode", function() {
expect(pass).toChangeAST(grammar, bytecodeDetails([ expect(pass).toChangeAST(grammar, bytecodeDetails([
@ -645,7 +645,7 @@ describe("compiler pass |generateBytecode|", function() {
it("defines correct constants", function() { it("defines correct constants", function() {
expect(pass).toChangeAST( expect(pass).toChangeAST(
grammar, grammar,
constsDetails(['peg$anyExpectation()']) constsDetails(["peg$anyExpectation()"])
); );
}); });
}); });

@ -9,9 +9,9 @@ describe("compiler pass |removeProxyRules|", function() {
it("updates references and removes it", function() { it("updates references and removes it", function() {
expect(pass).toChangeAST( expect(pass).toChangeAST(
[ [
'start = proxy', "start = proxy",
'proxy = proxied', "proxy = proxied",
'proxied = "a"' "proxied = 'a'"
].join("\n"), ].join("\n"),
{ {
rules: [ rules: [
@ -31,9 +31,9 @@ describe("compiler pass |removeProxyRules|", function() {
it("updates references but doesn't remove it", function() { it("updates references but doesn't remove it", function() {
expect(pass).toChangeAST( expect(pass).toChangeAST(
[ [
'start = proxy', "start = proxy",
'proxy = proxied', "proxy = proxied",
'proxied = "a"' "proxied = 'a'"
].join("\n"), ].join("\n"),
{ {
rules: [ rules: [

@ -7,8 +7,8 @@ describe("compiler pass |reportDuplicateLabels|", function() {
describe("in a sequence", function() { describe("in a sequence", function() {
it("reports labels duplicate with labels of preceding elements", function() { it("reports labels duplicate with labels of preceding elements", function() {
expect(pass).toReportError('start = a:"a" a:"a"', { expect(pass).toReportError("start = a:'a' a:'a'", {
message: 'Label "a" is already defined at line 1, column 9.', message: "Label \"a\" is already defined at line 1, column 9.",
location: { location: {
start: { offset: 14, line: 1, column: 15 }, start: { offset: 14, line: 1, column: 15 },
end: { offset: 19, line: 1, column: 20 } end: { offset: 19, line: 1, column: 20 }
@ -17,30 +17,30 @@ describe("compiler pass |reportDuplicateLabels|", function() {
}); });
it("doesn't report labels duplicate with labels in subexpressions", function() { it("doesn't report labels duplicate with labels in subexpressions", function() {
expect(pass).not.toReportError('start = ("a" / a:"a" / "a") a:"a"'); expect(pass).not.toReportError("start = ('a' / a:'a' / 'a') a:'a'");
expect(pass).not.toReportError('start = (a:"a" { }) a:"a"'); expect(pass).not.toReportError("start = (a:'a' { }) a:'a'");
expect(pass).not.toReportError('start = ("a" a:"a" "a") a:"a"'); expect(pass).not.toReportError("start = ('a' a:'a' 'a') a:'a'");
expect(pass).not.toReportError('start = b:(a:"a") a:"a"'); expect(pass).not.toReportError("start = b:(a:'a') a:'a'");
expect(pass).not.toReportError('start = $(a:"a") a:"a"'); expect(pass).not.toReportError("start = $(a:'a') a:'a'");
expect(pass).not.toReportError('start = &(a:"a") a:"a"'); expect(pass).not.toReportError("start = &(a:'a') a:'a'");
expect(pass).not.toReportError('start = !(a:"a") a:"a"'); expect(pass).not.toReportError("start = !(a:'a') a:'a'");
expect(pass).not.toReportError('start = (a:"a")? a:"a"'); expect(pass).not.toReportError("start = (a:'a')? a:'a'");
expect(pass).not.toReportError('start = (a:"a")* a:"a"'); expect(pass).not.toReportError("start = (a:'a')* a:'a'");
expect(pass).not.toReportError('start = (a:"a")+ a:"a"'); expect(pass).not.toReportError("start = (a:'a')+ a:'a'");
expect(pass).not.toReportError('start = (a:"a") a:"a"'); expect(pass).not.toReportError("start = (a:'a') a:'a'");
}); });
}); });
describe("in a choice", function() { describe("in a choice", function() {
it("doesn't report labels duplicate with labels of preceding alternatives", function() { it("doesn't report labels duplicate with labels of preceding alternatives", function() {
expect(pass).not.toReportError('start = a:"a" / a:"a"'); expect(pass).not.toReportError("start = a:'a' / a:'a'");
}); });
}); });
describe("in outer sequence", function() { describe("in outer sequence", function() {
it("reports labels duplicate with labels of preceding elements", function() { it("reports labels duplicate with labels of preceding elements", function() {
expect(pass).toReportError('start = a:"a" (a:"a")', { expect(pass).toReportError("start = a:'a' (a:'a')", {
message: 'Label "a" is already defined at line 1, column 9.', message: "Label \"a\" is already defined at line 1, column 9.",
location: { location: {
start: { offset: 15, line: 1, column: 16 }, start: { offset: 15, line: 1, column: 16 },
end: { offset: 20, line: 1, column: 21 } end: { offset: 20, line: 1, column: 21 }
@ -49,11 +49,11 @@ describe("compiler pass |reportDuplicateLabels|", function() {
}); });
it("doesn't report labels duplicate with the label of the current element", function() { it("doesn't report labels duplicate with the label of the current element", function() {
expect(pass).not.toReportError('start = a:(a:"a")'); expect(pass).not.toReportError("start = a:(a:'a')");
}); });
it("doesn't report labels duplicate with labels of following elements", function() { it("doesn't report labels duplicate with labels of following elements", function() {
expect(pass).not.toReportError('start = (a:"a") a:"a"'); expect(pass).not.toReportError("start = (a:'a') a:'a'");
}); });
}); });
}); });

@ -7,10 +7,10 @@ describe("compiler pass |reportDuplicateRules|", function() {
it("reports duplicate rules", function() { it("reports duplicate rules", function() {
expect(pass).toReportError([ expect(pass).toReportError([
'start = "a"', "start = 'a'",
'start = "b"' "start = 'b'"
].join('\n'), { ].join("\n"), {
message: 'Rule "start" is already defined at line 1, column 1.', message: "Rule \"start\" is already defined at line 1, column 1.",
location: { location: {
start: { offset: 12, line: 2, column: 1 }, start: { offset: 12, line: 2, column: 1 },
end: { offset: 23, line: 2, column: 12 } end: { offset: 23, line: 2, column: 12 }

@ -6,8 +6,8 @@ describe("compiler pass |reportInfiniteRecursion|", function() {
let pass = peg.compiler.passes.check.reportInfiniteRecursion; let pass = peg.compiler.passes.check.reportInfiniteRecursion;
it("reports direct left recursion", function() { it("reports direct left recursion", function() {
expect(pass).toReportError('start = start', { expect(pass).toReportError("start = start", {
message: 'Possible infinite loop when parsing (left recursion: start -> start).', message: "Possible infinite loop when parsing (left recursion: start -> start).",
location: { location: {
start: { offset: 8, line: 1, column: 9 }, start: { offset: 8, line: 1, column: 9 },
end: { offset: 13, line: 1, column: 14 } end: { offset: 13, line: 1, column: 14 }
@ -17,10 +17,10 @@ describe("compiler pass |reportInfiniteRecursion|", function() {
it("reports indirect left recursion", function() { it("reports indirect left recursion", function() {
expect(pass).toReportError([ expect(pass).toReportError([
'start = stop', "start = stop",
'stop = start' "stop = start"
].join("\n"), { ].join("\n"), {
message: 'Possible infinite loop when parsing (left recursion: start -> stop -> start).', message: "Possible infinite loop when parsing (left recursion: start -> stop -> start).",
location: { location: {
start: { offset: 21, line: 2, column: 9 }, start: { offset: 21, line: 2, column: 9 },
end: { offset: 26, line: 2, column: 14 } end: { offset: 26, line: 2, column: 14 }
@ -30,86 +30,86 @@ describe("compiler pass |reportInfiniteRecursion|", function() {
describe("in sequences", function() { describe("in sequences", function() {
it("reports left recursion if all preceding elements match empty string", function() { it("reports left recursion if all preceding elements match empty string", function() {
expect(pass).toReportError('start = "" "" "" start'); expect(pass).toReportError("start = '' '' '' start");
}); });
it("doesn't report left recursion if some preceding element doesn't match empty string", function() { it("doesn't report left recursion if some preceding element doesn't match empty string", function() {
expect(pass).not.toReportError('start = "a" "" "" start'); expect(pass).not.toReportError("start = 'a' '' '' start");
expect(pass).not.toReportError('start = "" "a" "" start'); expect(pass).not.toReportError("start = '' 'a' '' start");
expect(pass).not.toReportError('start = "" "" "a" start'); expect(pass).not.toReportError("start = '' '' 'a' start");
}); });
// Regression test for #359. // Regression test for #359.
it("reports left recursion when rule reference is wrapped in an expression", function() { it("reports left recursion when rule reference is wrapped in an expression", function() {
expect(pass).toReportError('start = "" start?'); expect(pass).toReportError("start = '' start?");
}); });
it("computes expressions that always consume input on success correctly", function() { it("computes expressions that always consume input on success correctly", function() {
expect(pass).toReportError([ expect(pass).toReportError([
'start = a start', "start = a start",
'a "a" = ""' "a 'a' = ''"
].join('\n')); ].join("\n"));
expect(pass).not.toReportError([ expect(pass).not.toReportError([
'start = a start', "start = a start",
'a "a" = "a"' "a 'a' = 'a'"
].join('\n')); ].join("\n"));
expect(pass).toReportError('start = ("" / "a" / "b") start'); expect(pass).toReportError("start = ('' / 'a' / 'b') start");
expect(pass).toReportError('start = ("a" / "" / "b") start'); expect(pass).toReportError("start = ('a' / '' / 'b') start");
expect(pass).toReportError('start = ("a" / "b" / "") start'); expect(pass).toReportError("start = ('a' / 'b' / '') start");
expect(pass).not.toReportError('start = ("a" / "b" / "c") start'); expect(pass).not.toReportError("start = ('a' / 'b' / 'c') start");
expect(pass).toReportError('start = ("" { }) start'); expect(pass).toReportError("start = ('' { }) start");
expect(pass).not.toReportError('start = ("a" { }) start'); expect(pass).not.toReportError("start = ('a' { }) start");
expect(pass).toReportError('start = ("" "" "") start'); expect(pass).toReportError("start = ('' '' '') start");
expect(pass).not.toReportError('start = ("a" "" "") start'); expect(pass).not.toReportError("start = ('a' '' '') start");
expect(pass).not.toReportError('start = ("" "a" "") start'); expect(pass).not.toReportError("start = ('' 'a' '') start");
expect(pass).not.toReportError('start = ("" "" "a") start'); expect(pass).not.toReportError("start = ('' '' 'a') start");
expect(pass).toReportError('start = a:"" start'); expect(pass).toReportError("start = a:'' start");
expect(pass).not.toReportError('start = a:"a" start'); expect(pass).not.toReportError("start = a:'a' start");
expect(pass).toReportError('start = $"" start'); expect(pass).toReportError("start = $'' start");
expect(pass).not.toReportError('start = $"a" start'); expect(pass).not.toReportError("start = $'a' start");
expect(pass).toReportError('start = &"" start'); expect(pass).toReportError("start = &'' start");
expect(pass).toReportError('start = &"a" start'); expect(pass).toReportError("start = &'a' start");
expect(pass).toReportError('start = !"" start'); expect(pass).toReportError("start = !'' start");
expect(pass).toReportError('start = !"a" start'); expect(pass).toReportError("start = !'a' start");
expect(pass).toReportError('start = ""? start'); expect(pass).toReportError("start = ''? start");
expect(pass).toReportError('start = "a"? start'); expect(pass).toReportError("start = 'a'? start");
expect(pass).toReportError('start = ""* start'); expect(pass).toReportError("start = ''* start");
expect(pass).toReportError('start = "a"* start'); expect(pass).toReportError("start = 'a'* start");
expect(pass).toReportError('start = ""+ start'); expect(pass).toReportError("start = ''+ start");
expect(pass).not.toReportError('start = "a"+ start'); expect(pass).not.toReportError("start = 'a'+ start");
expect(pass).toReportError('start = ("") start'); expect(pass).toReportError("start = ('') start");
expect(pass).not.toReportError('start = ("a") start'); expect(pass).not.toReportError("start = ('a') start");
expect(pass).toReportError('start = &{ } start'); expect(pass).toReportError("start = &{ } start");
expect(pass).toReportError('start = !{ } start'); expect(pass).toReportError("start = !{ } start");
expect(pass).toReportError([ expect(pass).toReportError([
'start = a start', "start = a start",
'a = ""' "a = ''"
].join('\n')); ].join("\n"));
expect(pass).not.toReportError([ expect(pass).not.toReportError([
'start = a start', "start = a start",
'a = "a"' "a = 'a'"
].join('\n')); ].join("\n"));
expect(pass).toReportError('start = "" start'); expect(pass).toReportError("start = '' start");
expect(pass).not.toReportError('start = "a" start'); expect(pass).not.toReportError("start = 'a' start");
expect(pass).not.toReportError('start = [a-d] start'); expect(pass).not.toReportError("start = [a-d] start");
expect(pass).not.toReportError('start = . start'); expect(pass).not.toReportError("start = . start");
}); });
}); });
}); });

@ -6,7 +6,7 @@ describe("compiler pass |reportInfiniteRepetition|", function() {
let pass = peg.compiler.passes.check.reportInfiniteRepetition; let pass = peg.compiler.passes.check.reportInfiniteRepetition;
it("reports infinite loops for zero_or_more", function() { it("reports infinite loops for zero_or_more", function() {
expect(pass).toReportError('start = ("")*', { expect(pass).toReportError("start = ('')*", {
message: "Possible infinite loop when parsing (repetition used with an expression that may not consume any input).", message: "Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
location: { location: {
start: { offset: 8, line: 1, column: 9 }, start: { offset: 8, line: 1, column: 9 },
@ -16,7 +16,7 @@ describe("compiler pass |reportInfiniteRepetition|", function() {
}); });
it("reports infinite loops for one_or_more", function() { it("reports infinite loops for one_or_more", function() {
expect(pass).toReportError('start = ("")+', { expect(pass).toReportError("start = ('')+", {
message: "Possible infinite loop when parsing (repetition used with an expression that may not consume any input).", message: "Possible infinite loop when parsing (repetition used with an expression that may not consume any input).",
location: { location: {
start: { offset: 8, line: 1, column: 9 }, start: { offset: 8, line: 1, column: 9 },
@ -27,69 +27,69 @@ describe("compiler pass |reportInfiniteRepetition|", function() {
it("computes expressions that always consume input on success correctly", function() { it("computes expressions that always consume input on success correctly", function() {
expect(pass).toReportError([ expect(pass).toReportError([
'start = a*', "start = a*",
'a "a" = ""' "a 'a' = ''"
].join('\n')); ].join("\n"));
expect(pass).not.toReportError([ expect(pass).not.toReportError([
'start = a*', "start = a*",
'a "a" = "a"' "a 'a' = 'a'"
].join('\n')); ].join("\n"));
expect(pass).toReportError('start = ("" / "a" / "b")*'); expect(pass).toReportError("start = ('' / 'a' / 'b')*");
expect(pass).toReportError('start = ("a" / "" / "b")*'); expect(pass).toReportError("start = ('a' / '' / 'b')*");
expect(pass).toReportError('start = ("a" / "b" / "")*'); expect(pass).toReportError("start = ('a' / 'b' / '')*");
expect(pass).not.toReportError('start = ("a" / "b" / "c")*'); expect(pass).not.toReportError("start = ('a' / 'b' / 'c')*");
expect(pass).toReportError('start = ("" { })*'); expect(pass).toReportError("start = ('' { })*");
expect(pass).not.toReportError('start = ("a" { })*'); expect(pass).not.toReportError("start = ('a' { })*");
expect(pass).toReportError('start = ("" "" "")*'); expect(pass).toReportError("start = ('' '' '')*");
expect(pass).not.toReportError('start = ("a" "" "")*'); expect(pass).not.toReportError("start = ('a' '' '')*");
expect(pass).not.toReportError('start = ("" "a" "")*'); expect(pass).not.toReportError("start = ('' 'a' '')*");
expect(pass).not.toReportError('start = ("" "" "a")*'); expect(pass).not.toReportError("start = ('' '' 'a')*");
expect(pass).toReportError('start = (a:"")*'); expect(pass).toReportError("start = (a:'')*");
expect(pass).not.toReportError('start = (a:"a")*'); expect(pass).not.toReportError("start = (a:'a')*");
expect(pass).toReportError('start = ($"")*'); expect(pass).toReportError("start = ($'')*");
expect(pass).not.toReportError('start = ($"a")*'); expect(pass).not.toReportError("start = ($'a')*");
expect(pass).toReportError('start = (&"")*'); expect(pass).toReportError("start = (&'')*");
expect(pass).toReportError('start = (&"a")*'); expect(pass).toReportError("start = (&'a')*");
expect(pass).toReportError('start = (!"")*'); expect(pass).toReportError("start = (!'')*");
expect(pass).toReportError('start = (!"a")*'); expect(pass).toReportError("start = (!'a')*");
expect(pass).toReportError('start = (""?)*'); expect(pass).toReportError("start = (''?)*");
expect(pass).toReportError('start = ("a"?)*'); expect(pass).toReportError("start = ('a'?)*");
expect(pass).toReportError('start = (""*)*'); expect(pass).toReportError("start = (''*)*");
expect(pass).toReportError('start = ("a"*)*'); expect(pass).toReportError("start = ('a'*)*");
expect(pass).toReportError('start = (""+)*'); expect(pass).toReportError("start = (''+)*");
expect(pass).not.toReportError('start = ("a"+)*'); expect(pass).not.toReportError("start = ('a'+)*");
expect(pass).toReportError('start = ("")*'); expect(pass).toReportError("start = ('')*");
expect(pass).not.toReportError('start = ("a")*'); expect(pass).not.toReportError("start = ('a')*");
expect(pass).toReportError('start = (&{ })*'); expect(pass).toReportError("start = (&{ })*");
expect(pass).toReportError('start = (!{ })*'); expect(pass).toReportError("start = (!{ })*");
expect(pass).toReportError([ expect(pass).toReportError([
'start = a*', "start = a*",
'a = ""' "a = ''"
].join('\n')); ].join("\n"));
expect(pass).not.toReportError([ expect(pass).not.toReportError([
'start = a*', "start = a*",
'a = "a"' "a = 'a'"
].join('\n')); ].join("\n"));
expect(pass).toReportError('start = ""*'); expect(pass).toReportError("start = ''*");
expect(pass).not.toReportError('start = "a"*'); expect(pass).not.toReportError("start = 'a'*");
expect(pass).not.toReportError('start = [a-d]*'); expect(pass).not.toReportError("start = [a-d]*");
expect(pass).not.toReportError('start = .*'); expect(pass).not.toReportError("start = .*");
}); });
}); });

@ -6,8 +6,8 @@ describe("compiler pass |reportUndefinedRules|", function() {
let pass = peg.compiler.passes.check.reportUndefinedRules; let pass = peg.compiler.passes.check.reportUndefinedRules;
it("reports undefined rules", function() { it("reports undefined rules", function() {
expect(pass).toReportError('start = undefined', { expect(pass).toReportError("start = undefined", {
message: 'Rule "undefined" is not defined.', message: "Rule \"undefined\" is not defined.",
location: { location: {
start: { offset: 8, line: 1, column: 9 }, start: { offset: 8, line: 1, column: 9 },
end: { offset: 17, line: 1, column: 18 } end: { offset: 17, line: 1, column: 18 }

@ -236,252 +236,252 @@ describe("PEG.js grammar parser", function() {
}); });
}); });
// Canonical Grammar is "a = \"abcd\"; b = \"efgh\"; c = \"ijkl\";". // Canonical Grammar is "a = 'abcd'; b = 'efgh'; c = 'ijkl';".
it("parses Grammar", function() { it("parses Grammar", function() {
expect('\na = "abcd";\n').toParseAs( expect("\na = 'abcd';\n").toParseAs(
{ type: "grammar", initializer: null, rules: [ruleA] } { type: "grammar", initializer: null, rules: [ruleA] }
); );
expect('\na = "abcd";\nb = "efgh";\nc = "ijkl";\n').toParseAs( expect("\na = 'abcd';\nb = 'efgh';\nc = 'ijkl';\n").toParseAs(
{ type: "grammar", initializer: null, rules: [ruleA, ruleB, ruleC] } { type: "grammar", initializer: null, rules: [ruleA, ruleB, ruleC] }
); );
expect('\n{ code };\na = "abcd";\n').toParseAs( expect("\n{ code };\na = 'abcd';\n").toParseAs(
{ type: "grammar", initializer: initializer, rules: [ruleA] } { type: "grammar", initializer: initializer, rules: [ruleA] }
); );
}); });
// Canonical Initializer is "{ code }". // Canonical Initializer is "{ code }".
it("parses Initializer", function() { it("parses Initializer", function() {
expect('{ code };start = "abcd"').toParseAs( expect("{ code };start = 'abcd'").toParseAs(
{ type: "grammar", initializer: initializer, rules: [ruleStart] } { type: "grammar", initializer: initializer, rules: [ruleStart] }
); );
}); });
// Canonical Rule is "a = \"abcd\";". // Canonical Rule is "a = 'abcd';".
it("parses Rule", function() { it("parses Rule", function() {
expect('start\n=\n"abcd";').toParseAs( expect("start\n=\n'abcd';").toParseAs(
oneRuleGrammar(literalAbcd) oneRuleGrammar(literalAbcd)
); );
expect('start\n"start rule"\n=\n"abcd";').toParseAs( expect("start\n'start rule'\n=\n'abcd';").toParseAs(
oneRuleGrammar(named) oneRuleGrammar(named)
); );
}); });
// Canonical Expression is "\"abcd\"". // Canonical Expression is "'abcd'".
it("parses Expression", function() { it("parses Expression", function() {
expect('start = "abcd" / "efgh" / "ijkl"').toParseAs( expect("start = 'abcd' / 'efgh' / 'ijkl'").toParseAs(
oneRuleGrammar(choice) oneRuleGrammar(choice)
); );
}); });
// Canonical ChoiceExpression is "\"abcd\" / \"efgh\" / \"ijkl\"". // Canonical ChoiceExpression is "'abcd' / 'efgh' / 'ijkl'".
it("parses ChoiceExpression", function() { it("parses ChoiceExpression", function() {
expect('start = "abcd" { code }').toParseAs( expect("start = 'abcd' { code }").toParseAs(
oneRuleGrammar(actionAbcd) oneRuleGrammar(actionAbcd)
); );
expect('start = "abcd" { code }\n/\n"efgh" { code }').toParseAs( expect("start = 'abcd' { code }\n/\n'efgh' { code }").toParseAs(
oneRuleGrammar(choice2) oneRuleGrammar(choice2)
); );
expect( expect(
'start = "abcd" { code }\n/\n"efgh" { code }\n/\n"ijkl" { code }\n/\n"mnop" { code }' "start = 'abcd' { code }\n/\n'efgh' { code }\n/\n'ijkl' { code }\n/\n'mnop' { code }"
).toParseAs( ).toParseAs(
oneRuleGrammar(choice4) oneRuleGrammar(choice4)
); );
}); });
// Canonical ActionExpression is "\"abcd\" { code }". // Canonical ActionExpression is "'abcd' { code }".
it("parses ActionExpression", function() { it("parses ActionExpression", function() {
expect('start = "abcd" "efgh" "ijkl"').toParseAs( expect("start = 'abcd' 'efgh' 'ijkl'").toParseAs(
oneRuleGrammar(sequence) oneRuleGrammar(sequence)
); );
expect('start = "abcd" "efgh" "ijkl"\n{ code }').toParseAs( expect("start = 'abcd' 'efgh' 'ijkl'\n{ code }").toParseAs(
oneRuleGrammar(actionSequence) oneRuleGrammar(actionSequence)
); );
}); });
// Canonical SequenceExpression is "\"abcd\" \"efgh\" \"ijkl\"". // Canonical SequenceExpression is "'abcd' 'efgh' 'ijkl'".
it("parses SequenceExpression", function() { it("parses SequenceExpression", function() {
expect('start = a:"abcd"').toParseAs( expect("start = a:'abcd'").toParseAs(
oneRuleGrammar(labeledAbcd) oneRuleGrammar(labeledAbcd)
); );
expect('start = a:"abcd"\nb:"efgh"').toParseAs( expect("start = a:'abcd'\nb:'efgh'").toParseAs(
oneRuleGrammar(sequence2) oneRuleGrammar(sequence2)
); );
expect('start = a:"abcd"\nb:"efgh"\nc:"ijkl"\nd:"mnop"').toParseAs( expect("start = a:'abcd'\nb:'efgh'\nc:'ijkl'\nd:'mnop'").toParseAs(
oneRuleGrammar(sequence4) oneRuleGrammar(sequence4)
); );
}); });
// Canonical LabeledExpression is "a:\"abcd\"". // Canonical LabeledExpression is "a:'abcd'".
it("parses LabeledExpression", function() { it("parses LabeledExpression", function() {
expect('start = a\n:\n!"abcd"').toParseAs(oneRuleGrammar(labeledSimpleNot)); expect("start = a\n:\n!'abcd'").toParseAs(oneRuleGrammar(labeledSimpleNot));
expect('start = !"abcd"' ).toParseAs(oneRuleGrammar(simpleNotAbcd)); expect("start = !'abcd'" ).toParseAs(oneRuleGrammar(simpleNotAbcd));
}); });
// Canonical PrefixedExpression is "!\"abcd\"". // Canonical PrefixedExpression is "!'abcd'".
it("parses PrefixedExpression", function() { it("parses PrefixedExpression", function() {
expect('start = !\n"abcd"?' ).toParseAs(oneRuleGrammar(simpleNotOptional)); expect("start = !\n'abcd'?" ).toParseAs(oneRuleGrammar(simpleNotOptional));
expect('start = "abcd"?' ).toParseAs(oneRuleGrammar(optional)); expect("start = 'abcd'?" ).toParseAs(oneRuleGrammar(optional));
}); });
// Canonical PrefixedOperator is "!". // Canonical PrefixedOperator is "!".
it("parses PrefixedOperator", function() { it("parses PrefixedOperator", function() {
expect('start = $"abcd"?').toParseAs(oneRuleGrammar(textOptional)); expect("start = $'abcd'?").toParseAs(oneRuleGrammar(textOptional));
expect('start = &"abcd"?').toParseAs(oneRuleGrammar(simpleAndOptional)); expect("start = &'abcd'?").toParseAs(oneRuleGrammar(simpleAndOptional));
expect('start = !"abcd"?').toParseAs(oneRuleGrammar(simpleNotOptional)); expect("start = !'abcd'?").toParseAs(oneRuleGrammar(simpleNotOptional));
}); });
// Canonical SuffixedExpression is "\"ebcd\"?". // Canonical SuffixedExpression is "'ebcd'?".
it("parses SuffixedExpression", function() { it("parses SuffixedExpression", function() {
expect('start = "abcd"\n?').toParseAs(oneRuleGrammar(optional)); expect("start = 'abcd'\n?").toParseAs(oneRuleGrammar(optional));
expect('start = "abcd"' ).toParseAs(oneRuleGrammar(literalAbcd)); expect("start = 'abcd'" ).toParseAs(oneRuleGrammar(literalAbcd));
}); });
// Canonical SuffixedOperator is "?". // Canonical SuffixedOperator is "?".
it("parses SuffixedOperator", function() { it("parses SuffixedOperator", function() {
expect('start = "abcd"?').toParseAs(oneRuleGrammar(optional)); expect("start = 'abcd'?").toParseAs(oneRuleGrammar(optional));
expect('start = "abcd"*').toParseAs(oneRuleGrammar(zeroOrMore)); expect("start = 'abcd'*").toParseAs(oneRuleGrammar(zeroOrMore));
expect('start = "abcd"+').toParseAs(oneRuleGrammar(oneOrMore)); expect("start = 'abcd'+").toParseAs(oneRuleGrammar(oneOrMore));
}); });
// Canonical PrimaryExpression is "\"abcd\"". // Canonical PrimaryExpression is "'abcd'".
it("parses PrimaryExpression", function() { it("parses PrimaryExpression", function() {
expect('start = "abcd"' ).toParseAs(trivialGrammar); expect("start = 'abcd'" ).toParseAs(trivialGrammar);
expect('start = [a-d]' ).toParseAs(classGrammar([["a", "d"]], false, false)); expect("start = [a-d]" ).toParseAs(classGrammar([["a", "d"]], false, false));
expect('start = .' ).toParseAs(anyGrammar()); expect("start = ." ).toParseAs(anyGrammar());
expect('start = a' ).toParseAs(ruleRefGrammar("a")); expect("start = a" ).toParseAs(ruleRefGrammar("a"));
expect('start = &{ code }').toParseAs(oneRuleGrammar(semanticAnd)); expect("start = &{ code }").toParseAs(oneRuleGrammar(semanticAnd));
expect('start = (\na:"abcd"\n)' ).toParseAs(oneRuleGrammar(groupLabeled)); expect("start = (\na:'abcd'\n)" ).toParseAs(oneRuleGrammar(groupLabeled));
expect('start = (\n"abcd" "efgh" "ijkl"\n)').toParseAs(oneRuleGrammar(groupSequence)); expect("start = (\n'abcd' 'efgh' 'ijkl'\n)").toParseAs(oneRuleGrammar(groupSequence));
expect('start = (\n"abcd"\n)' ).toParseAs(trivialGrammar); expect("start = (\n'abcd'\n)" ).toParseAs(trivialGrammar);
}); });
// Canonical RuleReferenceExpression is "a". // Canonical RuleReferenceExpression is "a".
it("parses RuleReferenceExpression", function() { it("parses RuleReferenceExpression", function() {
expect('start = a').toParseAs(ruleRefGrammar("a")); expect("start = a").toParseAs(ruleRefGrammar("a"));
expect('start = a\n=' ).toFailToParse(); expect("start = a\n=" ).toFailToParse();
expect('start = a\n"abcd"\n=').toFailToParse(); expect("start = a\n'abcd'\n=").toFailToParse();
}); });
// Canonical SemanticPredicateExpression is "!{ code }". // Canonical SemanticPredicateExpression is "!{ code }".
it("parses SemanticPredicateExpression", function() { it("parses SemanticPredicateExpression", function() {
expect('start = !\n{ code }').toParseAs(oneRuleGrammar(semanticNot)); expect("start = !\n{ code }").toParseAs(oneRuleGrammar(semanticNot));
}); });
// Canonical SemanticPredicateOperator is "!". // Canonical SemanticPredicateOperator is "!".
it("parses SemanticPredicateOperator", function() { it("parses SemanticPredicateOperator", function() {
expect('start = &{ code }').toParseAs(oneRuleGrammar(semanticAnd)); expect("start = &{ code }").toParseAs(oneRuleGrammar(semanticAnd));
expect('start = !{ code }').toParseAs(oneRuleGrammar(semanticNot)); expect("start = !{ code }").toParseAs(oneRuleGrammar(semanticNot));
}); });
// The SourceCharacter rule is not tested. // The SourceCharacter rule is not tested.
// Canonical WhiteSpace is " ". // Canonical WhiteSpace is " ".
it("parses WhiteSpace", function() { it("parses WhiteSpace", function() {
expect('start =\t"abcd"' ).toParseAs(trivialGrammar); expect("start =\t'abcd'" ).toParseAs(trivialGrammar);
expect('start =\v"abcd"' ).toParseAs(trivialGrammar); expect("start =\v'abcd'" ).toParseAs(trivialGrammar);
expect('start =\f"abcd"' ).toParseAs(trivialGrammar); expect("start =\f'abcd'" ).toParseAs(trivialGrammar);
expect('start = "abcd"' ).toParseAs(trivialGrammar); expect("start = 'abcd'" ).toParseAs(trivialGrammar);
expect('start =\u00A0"abcd"').toParseAs(trivialGrammar); expect("start =\u00A0'abcd'").toParseAs(trivialGrammar);
expect('start =\uFEFF"abcd"').toParseAs(trivialGrammar); expect("start =\uFEFF'abcd'").toParseAs(trivialGrammar);
expect('start =\u1680"abcd"').toParseAs(trivialGrammar); expect("start =\u1680'abcd'").toParseAs(trivialGrammar);
}); });
// Canonical LineTerminator is "\n". // Canonical LineTerminator is "\n".
it("parses LineTerminator", function() { it("parses LineTerminator", function() {
expect('start = "\n"' ).toFailToParse(); expect("start = '\n'" ).toFailToParse();
expect('start = "\r"' ).toFailToParse(); expect("start = '\r'" ).toFailToParse();
expect('start = "\u2028"').toFailToParse(); expect("start = '\u2028'").toFailToParse();
expect('start = "\u2029"').toFailToParse(); expect("start = '\u2029'").toFailToParse();
}); });
// Canonical LineTerminatorSequence is "\r\n". // Canonical LineTerminatorSequence is "\r\n".
it("parses LineTerminatorSequence", function() { it("parses LineTerminatorSequence", function() {
expect('start =\n"abcd"' ).toParseAs(trivialGrammar); expect("start =\n'abcd'" ).toParseAs(trivialGrammar);
expect('start =\r\n"abcd"' ).toParseAs(trivialGrammar); expect("start =\r\n'abcd'" ).toParseAs(trivialGrammar);
expect('start =\r"abcd"' ).toParseAs(trivialGrammar); expect("start =\r'abcd'" ).toParseAs(trivialGrammar);
expect('start =\u2028"abcd"').toParseAs(trivialGrammar); expect("start =\u2028'abcd'").toParseAs(trivialGrammar);
expect('start =\u2029"abcd"').toParseAs(trivialGrammar); expect("start =\u2029'abcd'").toParseAs(trivialGrammar);
}); });
// Canonical Comment is "/* comment */". // Canonical Comment is "/* comment */".
it("parses Comment", function() { it("parses Comment", function() {
expect('start =// comment\n"abcd"' ).toParseAs(trivialGrammar); expect("start =// comment\n'abcd'" ).toParseAs(trivialGrammar);
expect('start =/* comment */"abcd"').toParseAs(trivialGrammar); expect("start =/* comment */'abcd'").toParseAs(trivialGrammar);
}); });
// Canonical MultiLineComment is "/* comment */". // Canonical MultiLineComment is "/* comment */".
it("parses MultiLineComment", function() { it("parses MultiLineComment", function() {
expect('start =/**/"abcd"' ).toParseAs(trivialGrammar); expect("start =/**/'abcd'" ).toParseAs(trivialGrammar);
expect('start =/*a*/"abcd"' ).toParseAs(trivialGrammar); expect("start =/*a*/'abcd'" ).toParseAs(trivialGrammar);
expect('start =/*abc*/"abcd"').toParseAs(trivialGrammar); expect("start =/*abc*/'abcd'").toParseAs(trivialGrammar);
expect('start =/**/*/"abcd"').toFailToParse(); expect("start =/**/*/'abcd'").toFailToParse();
}); });
// Canonical MultiLineCommentNoLineTerminator is "/* comment */". // Canonical MultiLineCommentNoLineTerminator is "/* comment */".
it("parses MultiLineCommentNoLineTerminator", function() { it("parses MultiLineCommentNoLineTerminator", function() {
expect('a = "abcd"/**/\r\nb = "efgh"' ).toParseAs(twoRuleGrammar); expect("a = 'abcd'/**/\r\nb = 'efgh'" ).toParseAs(twoRuleGrammar);
expect('a = "abcd"/*a*/\r\nb = "efgh"' ).toParseAs(twoRuleGrammar); expect("a = 'abcd'/*a*/\r\nb = 'efgh'" ).toParseAs(twoRuleGrammar);
expect('a = "abcd"/*abc*/\r\nb = "efgh"').toParseAs(twoRuleGrammar); expect("a = 'abcd'/*abc*/\r\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect('a = "abcd"/**/*/\r\nb = "efgh"').toFailToParse(); expect("a = 'abcd'/**/*/\r\nb = 'efgh'").toFailToParse();
expect('a = "abcd"/*\n*/\r\nb = "efgh"').toFailToParse(); expect("a = 'abcd'/*\n*/\r\nb = 'efgh'").toFailToParse();
}); });
// Canonical SingleLineComment is "// comment". // Canonical SingleLineComment is "// comment".
it("parses SingleLineComment", function() { it("parses SingleLineComment", function() {
expect('start =//\n"abcd"' ).toParseAs(trivialGrammar); expect("start =//\n'abcd'" ).toParseAs(trivialGrammar);
expect('start =//a\n"abcd"' ).toParseAs(trivialGrammar); expect("start =//a\n'abcd'" ).toParseAs(trivialGrammar);
expect('start =//abc\n"abcd"').toParseAs(trivialGrammar); expect("start =//abc\n'abcd'").toParseAs(trivialGrammar);
expect('start =//\n@\n"abcd"').toFailToParse(); expect("start =//\n@\n'abcd'").toFailToParse();
}); });
// Canonical Identifier is "a". // Canonical Identifier is "a".
it("parses Identifier", function() { it("parses Identifier", function() {
expect('start = a:"abcd"').toParseAs(oneRuleGrammar(labeledAbcd)); expect("start = a:'abcd'").toParseAs(oneRuleGrammar(labeledAbcd));
}); });
// Canonical IdentifierName is "a". // Canonical IdentifierName is "a".
it("parses IdentifierName", function() { it("parses IdentifierName", function() {
expect('start = a' ).toParseAs(ruleRefGrammar("a")); expect("start = a" ).toParseAs(ruleRefGrammar("a"));
expect('start = ab' ).toParseAs(ruleRefGrammar("ab")); expect("start = ab" ).toParseAs(ruleRefGrammar("ab"));
expect('start = abcd').toParseAs(ruleRefGrammar("abcd")); expect("start = abcd").toParseAs(ruleRefGrammar("abcd"));
}); });
// Canonical IdentifierStart is "a". // Canonical IdentifierStart is "a".
it("parses IdentifierStart", function() { it("parses IdentifierStart", function() {
expect('start = a' ).toParseAs(ruleRefGrammar("a")); expect("start = a" ).toParseAs(ruleRefGrammar("a"));
expect('start = $' ).toParseAs(ruleRefGrammar("$")); expect("start = $" ).toParseAs(ruleRefGrammar("$"));
expect('start = _' ).toParseAs(ruleRefGrammar("_")); expect("start = _" ).toParseAs(ruleRefGrammar("_"));
expect('start = \\u0061').toParseAs(ruleRefGrammar("a")); expect("start = \\u0061").toParseAs(ruleRefGrammar("a"));
}); });
// Canonical IdentifierPart is "a". // Canonical IdentifierPart is "a".
it("parses IdentifierPart", function() { it("parses IdentifierPart", function() {
expect('start = aa' ).toParseAs(ruleRefGrammar("aa")); expect("start = aa" ).toParseAs(ruleRefGrammar("aa"));
expect('start = a\u0300').toParseAs(ruleRefGrammar("a\u0300")); expect("start = a\u0300").toParseAs(ruleRefGrammar("a\u0300"));
expect('start = a0' ).toParseAs(ruleRefGrammar("a0")); expect("start = a0" ).toParseAs(ruleRefGrammar("a0"));
expect('start = a\u203F').toParseAs(ruleRefGrammar("a\u203F")); expect("start = a\u203F").toParseAs(ruleRefGrammar("a\u203F"));
expect('start = a\u200C').toParseAs(ruleRefGrammar("a\u200C")); expect("start = a\u200C").toParseAs(ruleRefGrammar("a\u200C"));
expect('start = a\u200D').toParseAs(ruleRefGrammar("a\u200D")); expect("start = a\u200D").toParseAs(ruleRefGrammar("a\u200D"));
}); });
// Unicode rules and reserved word rules are not tested. // Unicode rules and reserved word rules are not tested.
// Canonical LiteralMatcher is "\"abcd\"". // Canonical LiteralMatcher is "'abcd'".
it("parses LiteralMatcher", function() { it("parses LiteralMatcher", function() {
expect('start = "abcd"' ).toParseAs(literalGrammar("abcd", false)); expect("start = 'abcd'" ).toParseAs(literalGrammar("abcd", false));
expect('start = "abcd"i').toParseAs(literalGrammar("abcd", true)); expect("start = 'abcd'i").toParseAs(literalGrammar("abcd", true));
}); });
// Canonical StringLiteral is "\"abcd\"". // Canonical StringLiteral is "'abcd'".
it("parses StringLiteral", function() { it("parses StringLiteral", function() {
expect('start = ""' ).toParseAs(literalGrammar("", false)); expect("start = \"\"" ).toParseAs(literalGrammar("", false));
expect('start = "a"' ).toParseAs(literalGrammar("a", false)); expect("start = \"a\"" ).toParseAs(literalGrammar("a", false));
expect('start = "abc"').toParseAs(literalGrammar("abc", false)); expect("start = \"abc\"").toParseAs(literalGrammar("abc", false));
expect("start = ''" ).toParseAs(literalGrammar("", false)); expect("start = ''" ).toParseAs(literalGrammar("", false));
expect("start = 'a'" ).toParseAs(literalGrammar("a", false)); expect("start = 'a'" ).toParseAs(literalGrammar("a", false));
@ -490,13 +490,13 @@ describe("PEG.js grammar parser", function() {
// Canonical DoubleStringCharacter is "a". // Canonical DoubleStringCharacter is "a".
it("parses DoubleStringCharacter", function() { it("parses DoubleStringCharacter", function() {
expect('start = "a"' ).toParseAs(literalGrammar("a", false)); expect("start = \"a\"" ).toParseAs(literalGrammar("a", false));
expect('start = "\\n"' ).toParseAs(literalGrammar("\n", false)); expect("start = \"\\n\"" ).toParseAs(literalGrammar("\n", false));
expect('start = "\\\n"').toParseAs(literalGrammar("", false)); expect("start = \"\\\n\"").toParseAs(literalGrammar("", false));
expect('start = """' ).toFailToParse(); expect("start = \"\"\"").toFailToParse();
expect('start = "\\"').toFailToParse(); expect("start = \"\\\"").toFailToParse();
expect('start = "\n"').toFailToParse(); expect("start = \"\n\"").toFailToParse();
}); });
// Canonical SingleStringCharacter is "a". // Canonical SingleStringCharacter is "a".
@ -512,92 +512,92 @@ describe("PEG.js grammar parser", function() {
// Canonical CharacterClassMatcher is "[a-d]". // Canonical CharacterClassMatcher is "[a-d]".
it("parses CharacterClassMatcher", function() { it("parses CharacterClassMatcher", function() {
expect('start = []').toParseAs( expect("start = []").toParseAs(
classGrammar([], false, false) classGrammar([], false, false)
); );
expect('start = [a-d]').toParseAs( expect("start = [a-d]").toParseAs(
classGrammar([["a", "d"]], false, false) classGrammar([["a", "d"]], false, false)
); );
expect('start = [a]').toParseAs( expect("start = [a]").toParseAs(
classGrammar(["a"], false, false) classGrammar(["a"], false, false)
); );
expect('start = [a-de-hi-l]').toParseAs( expect("start = [a-de-hi-l]").toParseAs(
classGrammar( classGrammar(
[["a", "d"], ["e", "h"], ["i", "l"]], [["a", "d"], ["e", "h"], ["i", "l"]],
false, false,
false false
) )
); );
expect('start = [^a-d]').toParseAs( expect("start = [^a-d]").toParseAs(
classGrammar([["a", "d"]], true, false) classGrammar([["a", "d"]], true, false)
); );
expect('start = [a-d]i').toParseAs( expect("start = [a-d]i").toParseAs(
classGrammar([["a", "d"]], false, true) classGrammar([["a", "d"]], false, true)
); );
expect('start = [\\\n]').toParseAs( expect("start = [\\\n]").toParseAs(
classGrammar([], false, false) classGrammar([], false, false)
); );
}); });
// Canonical ClassCharacterRange is "a-d". // Canonical ClassCharacterRange is "a-d".
it("parses ClassCharacterRange", function() { it("parses ClassCharacterRange", function() {
expect('start = [a-d]').toParseAs(classGrammar([["a", "d"]], false, false)); expect("start = [a-d]").toParseAs(classGrammar([["a", "d"]], false, false));
expect('start = [a-a]').toParseAs(classGrammar([["a", "a"]], false, false)); expect("start = [a-a]").toParseAs(classGrammar([["a", "a"]], false, false));
expect('start = [b-a]').toFailToParse({ expect("start = [b-a]").toFailToParse({
message: "Invalid character range: b-a." message: "Invalid character range: b-a."
}); });
}); });
// Canonical ClassCharacter is "a". // Canonical ClassCharacter is "a".
it("parses ClassCharacter", function() { it("parses ClassCharacter", function() {
expect('start = [a]' ).toParseAs(classGrammar(["a"], false, false)); expect("start = [a]" ).toParseAs(classGrammar(["a"], false, false));
expect('start = [\\n]' ).toParseAs(classGrammar(["\n"], false, false)); expect("start = [\\n]" ).toParseAs(classGrammar(["\n"], false, false));
expect('start = [\\\n]').toParseAs(classGrammar([], false, false)); expect("start = [\\\n]").toParseAs(classGrammar([], false, false));
expect('start = []]' ).toFailToParse(); expect("start = []]" ).toFailToParse();
expect('start = [\\]').toFailToParse(); expect("start = [\\]").toFailToParse();
expect('start = [\n]').toFailToParse(); expect("start = [\n]").toFailToParse();
}); });
// Canonical LineContinuation is "\\\n". // Canonical LineContinuation is "\\\n".
it("parses LineContinuation", function() { it("parses LineContinuation", function() {
expect('start = "\\\r\n"').toParseAs(literalGrammar("", false)); expect("start = '\\\r\n'").toParseAs(literalGrammar("", false));
}); });
// Canonical EscapeSequence is "n". // Canonical EscapeSequence is "n".
it("parses EscapeSequence", function() { it("parses EscapeSequence", function() {
expect('start = "\\n"' ).toParseAs(literalGrammar("\n", false)); expect("start = '\\n'" ).toParseAs(literalGrammar("\n", false));
expect('start = "\\0"' ).toParseAs(literalGrammar("\x00", false)); expect("start = '\\0'" ).toParseAs(literalGrammar("\x00", false));
expect('start = "\\xFF"' ).toParseAs(literalGrammar("\xFF", false)); expect("start = '\\xFF'" ).toParseAs(literalGrammar("\xFF", false));
expect('start = "\\uFFFF"').toParseAs(literalGrammar("\uFFFF", false)); expect("start = '\\uFFFF'").toParseAs(literalGrammar("\uFFFF", false));
expect('start = "\\09"').toFailToParse(); expect("start = '\\09'").toFailToParse();
}); });
// Canonical CharacterEscapeSequence is "n". // Canonical CharacterEscapeSequence is "n".
it("parses CharacterEscapeSequence", function() { it("parses CharacterEscapeSequence", function() {
expect('start = "\\n"').toParseAs(literalGrammar("\n", false)); expect("start = '\\n'").toParseAs(literalGrammar("\n", false));
expect('start = "\\a"').toParseAs(literalGrammar("a", false)); expect("start = '\\a'").toParseAs(literalGrammar("a", false));
}); });
// Canonical SingleEscapeCharacter is "n". // Canonical SingleEscapeCharacter is "n".
it("parses SingleEscapeCharacter", function() { it("parses SingleEscapeCharacter", function() {
expect('start = "\\\'"').toParseAs(literalGrammar("'", false)); expect("start = '\\''" ).toParseAs(literalGrammar("'", false));
expect('start = "\\""' ).toParseAs(literalGrammar('"', false)); expect("start = '\\\"'").toParseAs(literalGrammar("\"", false));
expect('start = "\\\\"').toParseAs(literalGrammar("\\", false)); expect("start = '\\\\'").toParseAs(literalGrammar("\\", false));
expect('start = "\\b"' ).toParseAs(literalGrammar("\b", false)); expect("start = '\\b'" ).toParseAs(literalGrammar("\b", false));
expect('start = "\\f"' ).toParseAs(literalGrammar("\f", false)); expect("start = '\\f'" ).toParseAs(literalGrammar("\f", false));
expect('start = "\\n"' ).toParseAs(literalGrammar("\n", false)); expect("start = '\\n'" ).toParseAs(literalGrammar("\n", false));
expect('start = "\\r"' ).toParseAs(literalGrammar("\r", false)); expect("start = '\\r'" ).toParseAs(literalGrammar("\r", false));
expect('start = "\\t"' ).toParseAs(literalGrammar("\t", false)); expect("start = '\\t'" ).toParseAs(literalGrammar("\t", false));
expect('start = "\\v"' ).toParseAs(literalGrammar("\v", false)); expect("start = '\\v'" ).toParseAs(literalGrammar("\v", false));
}); });
// Canonical NonEscapeCharacter is "a". // Canonical NonEscapeCharacter is "a".
it("parses NonEscapeCharacter", function() { it("parses NonEscapeCharacter", function() {
expect('start = "\\a"').toParseAs(literalGrammar("a", false)); expect("start = '\\a'").toParseAs(literalGrammar("a", false));
// The negative predicate is impossible to test with PEG.js grammar // The negative predicate is impossible to test with PEG.js grammar
// structure. // structure.
@ -608,66 +608,66 @@ describe("PEG.js grammar parser", function() {
// Canonical HexEscapeSequence is "xFF". // Canonical HexEscapeSequence is "xFF".
it("parses HexEscapeSequence", function() { it("parses HexEscapeSequence", function() {
expect('start = "\\xFF"').toParseAs(literalGrammar("\xFF", false)); expect("start = '\\xFF'").toParseAs(literalGrammar("\xFF", false));
}); });
// Canonical UnicodeEscapeSequence is "uFFFF". // Canonical UnicodeEscapeSequence is "uFFFF".
it("parses UnicodeEscapeSequence", function() { it("parses UnicodeEscapeSequence", function() {
expect('start = "\\uFFFF"').toParseAs(literalGrammar("\uFFFF", false)); expect("start = '\\uFFFF'").toParseAs(literalGrammar("\uFFFF", false));
}); });
// Digit rules are not tested. // Digit rules are not tested.
// Canonical AnyMatcher is ".". // Canonical AnyMatcher is ".".
it("parses AnyMatcher", function() { it("parses AnyMatcher", function() {
expect('start = .').toParseAs(anyGrammar()); expect("start = .").toParseAs(anyGrammar());
}); });
// Canonical CodeBlock is "{ code }". // Canonical CodeBlock is "{ code }".
it("parses CodeBlock", function() { it("parses CodeBlock", function() {
expect('start = "abcd" { code }').toParseAs(actionGrammar(" code ")); expect("start = 'abcd' { code }").toParseAs(actionGrammar(" code "));
}); });
// Canonical Code is " code ". // Canonical Code is " code ".
it("parses Code", function() { it("parses Code", function() {
expect('start = "abcd" {a}' ).toParseAs(actionGrammar("a")); expect("start = 'abcd' {a}" ).toParseAs(actionGrammar("a"));
expect('start = "abcd" {abc}' ).toParseAs(actionGrammar("abc")); expect("start = 'abcd' {abc}" ).toParseAs(actionGrammar("abc"));
expect('start = "abcd" {{a}}' ).toParseAs(actionGrammar("{a}")); expect("start = 'abcd' {{a}}" ).toParseAs(actionGrammar("{a}"));
expect('start = "abcd" {{a}{b}{c}}').toParseAs(actionGrammar("{a}{b}{c}")); expect("start = 'abcd' {{a}{b}{c}}").toParseAs(actionGrammar("{a}{b}{c}"));
expect('start = "abcd" {{}').toFailToParse(); expect("start = 'abcd' {{}").toFailToParse();
expect('start = "abcd" {}}').toFailToParse(); expect("start = 'abcd' {}}").toFailToParse();
}); });
// Unicode character category rules and token rules are not tested. // Unicode character category rules and token rules are not tested.
// Canonical __ is "\n". // Canonical __ is "\n".
it("parses __", function() { it("parses __", function() {
expect('start ="abcd"' ).toParseAs(trivialGrammar); expect("start ='abcd'" ).toParseAs(trivialGrammar);
expect('start = "abcd"' ).toParseAs(trivialGrammar); expect("start = 'abcd'" ).toParseAs(trivialGrammar);
expect('start =\r\n"abcd"' ).toParseAs(trivialGrammar); expect("start =\r\n'abcd'" ).toParseAs(trivialGrammar);
expect('start =/* comment */"abcd"').toParseAs(trivialGrammar); expect("start =/* comment */'abcd'").toParseAs(trivialGrammar);
expect('start = "abcd"' ).toParseAs(trivialGrammar); expect("start = 'abcd'" ).toParseAs(trivialGrammar);
}); });
// Canonical _ is " ". // Canonical _ is " ".
it("parses _", function() { it("parses _", function() {
expect('a = "abcd"\r\nb = "efgh"' ).toParseAs(twoRuleGrammar); expect("a = 'abcd'\r\nb = 'efgh'" ).toParseAs(twoRuleGrammar);
expect('a = "abcd" \r\nb = "efgh"' ).toParseAs(twoRuleGrammar); expect("a = 'abcd' \r\nb = 'efgh'" ).toParseAs(twoRuleGrammar);
expect('a = "abcd"/* comment */\r\nb = "efgh"').toParseAs(twoRuleGrammar); expect("a = 'abcd'/* comment */\r\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect('a = "abcd" \r\nb = "efgh"' ).toParseAs(twoRuleGrammar); expect("a = 'abcd' \r\nb = 'efgh'" ).toParseAs(twoRuleGrammar);
}); });
// Canonical EOS is ";". // Canonical EOS is ";".
it("parses EOS", function() { it("parses EOS", function() {
expect('a = "abcd"\n;b = "efgh"' ).toParseAs(twoRuleGrammar); expect("a = 'abcd'\n;b = 'efgh'" ).toParseAs(twoRuleGrammar);
expect('a = "abcd" \r\nb = "efgh"' ).toParseAs(twoRuleGrammar); expect("a = 'abcd' \r\nb = 'efgh'" ).toParseAs(twoRuleGrammar);
expect('a = "abcd" // comment\r\nb = "efgh"').toParseAs(twoRuleGrammar); expect("a = 'abcd' // comment\r\nb = 'efgh'").toParseAs(twoRuleGrammar);
expect('a = "abcd"\nb = "efgh"' ).toParseAs(twoRuleGrammar); expect("a = 'abcd'\nb = 'efgh'" ).toParseAs(twoRuleGrammar);
}); });
// Canonical EOF is the end of input. // Canonical EOF is the end of input.
it("parses EOF", function() { it("parses EOF", function() {
expect('start = "abcd"\n').toParseAs(trivialGrammar); expect("start = 'abcd'\n").toParseAs(trivialGrammar);
}); });
}); });

@ -179,7 +179,7 @@ PrimaryExpression
// don't need to put it around nodes that can't contain any labels or // don't need to put it around nodes that can't contain any labels or
// nodes that already isolate label scope themselves. This leaves us with // nodes that already isolate label scope themselves. This leaves us with
// "labeled" and "sequence". // "labeled" and "sequence".
return expression.type === 'labeled' || expression.type === 'sequence' return expression.type === "labeled" || expression.type === "sequence"
? { type: "group", expression: expression } ? { type: "group", expression: expression }
: expression; : expression;
} }

Loading…
Cancel
Save