Browse Source

Use only double quotes for strings

See #443
redux
David Majda 5 years ago
parent
commit
12112310f2
  1. 58
      lib/compiler/js.js
  2. 44
      lib/compiler/passes/generate-bytecode.js
  3. 1620
      lib/compiler/passes/generate-js.js
  4. 38
      lib/parser.js
  5. 18
      spec/api/generated-parser-api.spec.js
  6. 24
      spec/api/pegjs-api.spec.js
  7. 38
      spec/api/plugin-api.spec.js
  8. 452
      spec/behavior/generated-parser-behavior.spec.js
  9. 208
      spec/unit/compiler/passes/generate-bytecode.spec.js
  10. 12
      spec/unit/compiler/passes/remove-proxy-rules.spec.js
  11. 36
      spec/unit/compiler/passes/report-duplicate-labels.spec.js
  12. 8
      spec/unit/compiler/passes/report-duplicate-rules.spec.js
  13. 108
      spec/unit/compiler/passes/report-infinite-recursion.spec.js
  14. 92
      spec/unit/compiler/passes/report-infinite-repetition.spec.js
  15. 4
      spec/unit/compiler/passes/report-undefined-rules.spec.js
  16. 334
      spec/unit/parser.spec.js
  17. 2
      src/parser.pegjs

58
lib/compiler/js.js

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

44
lib/compiler/passes/generate-bytecode.js

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

1620
lib/compiler/passes/generate-js.js
File diff suppressed because it is too large
View File

38
lib/parser.js

@ -62,28 +62,28 @@ peg$SyntaxError.buildMessage = function(expected, found) {
function literalEscape(s) {
return s
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\0/g, '\\0')
.replace(/\t/g, '\\t')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/[\x00-\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
.replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return '\\x' + hex(ch); });
.replace(/\\/g, "\\\\")
.replace(/"/g, "\\\"")
.replace(/\0/g, "\\0")
.replace(/\t/g, "\\t")
.replace(/\n/g, "\\n")
.replace(/\r/g, "\\r")
.replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); })
.replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); });
}
function classEscape(s) {
return s
.replace(/\\/g, '\\\\')
.replace(/\]/g, '\\]')
.replace(/\^/g, '\\^')
.replace(/-/g, '\\-')
.replace(/\0/g, '\\0')
.replace(/\t/g, '\\t')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r')
.replace(/[\x00-\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
.replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return '\\x' + hex(ch); });
.replace(/\\/g, "\\\\")
.replace(/\]/g, "\\]")
.replace(/\^/g, "\\^")
.replace(/-/g, "\\-")
.replace(/\0/g, "\\0")
.replace(/\t/g, "\\t")
.replace(/\n/g, "\\n")
.replace(/\r/g, "\\r")
.replace(/[\x00-\x0F]/g, function(ch) { return "\\x0" + hex(ch); })
.replace(/[\x10-\x1F\x7F-\x9F]/g, function(ch) { return "\\x" + hex(ch); });
}
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
// nodes that already isolate label scope themselves. This leaves us with
// "labeled" and "sequence".
return expression.type === 'labeled' || expression.type === 'sequence'
return expression.type === "labeled" || expression.type === "sequence"
? { type: "group", expression: expression }
: expression;
};

18
spec/api/generated-parser-api.spec.js

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

24
spec/api/pegjs-api.spec.js

@ -5,25 +5,25 @@ let peg = require("../../lib/peg");
describe("PEG.js API", function() {
describe("generate", function() {
it("generates a parser", function() {
let parser = peg.generate('start = "a"');
let parser = peg.generate("start = 'a'");
expect(typeof parser).toBe("object");
expect(parser.parse("a")).toBe("a");
});
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() {
expect(() => { peg.generate('start = undefined'); }).toThrow();
expect(() => { peg.generate("start = undefined"); }).toThrow();
});
describe("allowed start rules", function() {
let grammar = [
'a = "x"',
'b = "x"',
'c = "x"'
"a = 'x'",
"b = 'x'",
"c = 'x'"
].join("\n");
// The |allowedStartRules| option is implemented separately for each
@ -82,9 +82,9 @@ describe("PEG.js API", function() {
describe("intermediate results caching", function() {
let grammar = [
'{ var n = 0; }',
'start = (a "b") / (a "c") { return n; }',
'a = "a" { n++; }'
"{ var n = 0; }",
"start = (a 'b') / (a 'c') { return n; }",
"a = 'a' { n++; }"
].join("\n");
describe("when |cache| is not set", function() {
@ -113,7 +113,7 @@ describe("PEG.js API", function() {
});
describe("tracing", function() {
let grammar = 'start = "a"';
let grammar = "start = 'a'";
describe("when |trace| is not set", 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.
describe("output", function() {
let grammar = 'start = "a"';
let grammar = "start = 'a'";
describe("when |output| is not set", 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.
it("accepts custom options", function() {
peg.generate('start = "a"', { foo: 42 });
peg.generate("start = 'a'", { foo: 42 });
});
});
});

38
spec/api/plugin-api.spec.js

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

452
spec/behavior/generated-parser-behavior.spec.js
File diff suppressed because it is too large
View File

208
spec/unit/compiler/passes/generate-bytecode.spec.js

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

12
spec/unit/compiler/passes/remove-proxy-rules.spec.js

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

36
spec/unit/compiler/passes/report-duplicate-labels.spec.js

@ -7,8 +7,8 @@ describe("compiler pass |reportDuplicateLabels|", function() {
describe("in a sequence", function() {
it("reports labels duplicate with labels of preceding elements", function() {
expect(pass).toReportError('start = a:"a" a:"a"', {
message: 'Label "a" is already defined at line 1, column 9.',
expect(pass).toReportError("start = a:'a' a:'a'", {
message: "Label \"a\" is already defined at line 1, column 9.",
location: {
start: { offset: 14, line: 1, column: 15 },
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() {
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") 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') 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 = 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'");
});
});
describe("in a choice", 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() {
it("reports labels duplicate with labels of preceding elements", function() {
expect(pass).toReportError('start = a:"a" (a:"a")', {
message: 'Label "a" is already defined at line 1, column 9.',
expect(pass).toReportError("start = a:'a' (a:'a')", {
message: "Label \"a\" is already defined at line 1, column 9.",
location: {
start: { offset: 15, line: 1, column: 16 },
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() {
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() {
expect(pass).not.toReportError('start = (a:"a") a:"a"');
expect(pass).not.toReportError("start = (a:'a') a:'a'");
});
});
});

8
spec/unit/compiler/passes/report-duplicate-rules.spec.js

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

108
spec/unit/compiler/passes/report-infinite-recursion.spec.js

@ -6,8 +6,8 @@ describe("compiler pass |reportInfiniteRecursion|", function() {
let pass = peg.compiler.passes.check.reportInfiniteRecursion;
it("reports direct left recursion", function() {
expect(pass).toReportError('start = start', {
message: 'Possible infinite loop when parsing (left recursion: start -> start).',
expect(pass).toReportError("start = start", {
message: "Possible infinite loop when parsing (left recursion: start -> start).",
location: {
start: { offset: 8, line: 1, column: 9 },
end: { offset: 13, line: 1, column: 14 }
@ -17,10 +17,10 @@ describe("compiler pass |reportInfiniteRecursion|", function() {
it("reports indirect left recursion", function() {
expect(pass).toReportError([
'start = stop',
'stop = start'
"start = stop",
"stop = start"
].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: {
start: { offset: 21, line: 2, column: 9 },
end: { offset: 26, line: 2, column: 14 }
@ -30,86 +30,86 @@ describe("compiler pass |reportInfiniteRecursion|", function() {
describe("in sequences", 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() {
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.
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() {
expect(pass).toReportError([
'start = a start',
'a "a" = ""'
].join('\n'));
"start = a start",
"a 'a' = ''"
].join("\n"));
expect(pass).not.toReportError([
'start = a start',
'a "a" = "a"'
].join('\n'));
"start = a start",
"a 'a' = 'a'"
].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).not.toReportError('start = ("a" / "b" / "c") 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).toReportError('start = ("" { }) start');
expect(pass).not.toReportError('start = ("a" { }) start');
expect(pass).toReportError("start = ('' { }) start");
expect(pass).not.toReportError("start = ('a' { }) 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).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).toReportError('start = a:"" start');
expect(pass).not.toReportError('start = a:"a" start');
expect(pass).toReportError("start = a:'' start");
expect(pass).not.toReportError("start = a:'a' start");
expect(pass).toReportError('start = $"" start');
expect(pass).not.toReportError('start = $"a" start');
expect(pass).toReportError("start = $'' start");
expect(pass).not.toReportError("start = $'a' start");
expect(pass).toReportError('start = &"" start');
expect(pass).toReportError('start = &"a" start');
expect(pass).toReportError("start = &'' start");
expect(pass).toReportError("start = &'a' start");
expect(pass).toReportError('start = !"" start');
expect(pass).toReportError('start = !"a" start');
expect(pass).toReportError("start = !'' start");
expect(pass).toReportError("start = !'a' start");
expect(pass).toReportError('start = ""? start');
expect(pass).toReportError('start = "a"? start');
expect(pass).toReportError("start = ''? start");
expect(pass).toReportError("start = 'a'? start");
expect(pass).toReportError('start = ""* start');
expect(pass).toReportError('start = "a"* start');
expect(pass).toReportError("start = ''* start");
expect(pass).toReportError("start = 'a'* start");
expect(pass).toReportError('start = ""+ start');
expect(pass).not.toReportError('start = "a"+ start');
expect(pass).toReportError("start = ''+ start");
expect(pass).not.toReportError("start = 'a'+ start");
expect(pass).toReportError('start = ("") start');
expect(pass).not.toReportError('start = ("a") start');
expect(pass).toReportError("start = ('') 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([
'start = a start',
'a = ""'
].join('\n'));
"start = a start",
"a = ''"
].join("\n"));
expect(pass).not.toReportError([
'start = a start',
'a = "a"'
].join('\n'));
"start = a start",
"a = 'a'"
].join("\n"));
expect(pass).toReportError('start = "" start');
expect(pass).not.toReportError('start = "a" start');
expect(pass).toReportError("start = '' 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");
});
});
});

92
spec/unit/compiler/passes/report-infinite-repetition.spec.js

@ -6,7 +6,7 @@ describe("compiler pass |reportInfiniteRepetition|", function() {
let pass = peg.compiler.passes.check.reportInfiniteRepetition;
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).",
location: {
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() {
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).",
location: {
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() {
expect(pass).toReportError([
'start = a*',
'a "a" = ""'
].join('\n'));
"start = a*",
"a 'a' = ''"
].join("\n"));
expect(pass).not.toReportError([
'start = a*',
'a "a" = "a"'
].join('\n'));
"start = a*",
"a 'a' = 'a'"
].join("\n"));
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).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).toReportError('start = ("" { })*');
expect(pass).not.toReportError('start = ("a" { })*');
expect(pass).toReportError("start = ('' { })*");
expect(pass).not.toReportError("start = ('a' { })*");
expect(pass).toReportError('start = ("" "" "")*');
expect(pass).not.toReportError('start = ("a" "" "")*');
expect(pass).not.toReportError('start = ("" "a" "")*');
expect(pass).not.toReportError('start = ("" "" "a")*');
expect(pass).toReportError("start = ('' '' '')*");
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).not.toReportError('start = (a:"a")*');
expect(pass).toReportError("start = (a:'')*");
expect(pass).not.toReportError("start = (a:'a')*");
expect(pass).toReportError('start = ($"")*');
expect(pass).not.toReportError('start = ($"a")*');
expect(pass).toReportError("start = ($'')*");
expect(pass).not.toReportError("start = ($'a')*");
expect(pass).toReportError('start = (&"")*');
expect(pass).toReportError('start = (&"a")*');
expect(pass).toReportError("start = (&'')*");
expect(pass).toReportError("start = (&'a')*");
expect(pass).toReportError('start = (!"")*');
expect(pass).toReportError('start = (!"a")*');
expect(pass).toReportError("start = (!'')*");
expect(pass).toReportError("start = (!'a')*");
expect(pass).toReportError('start = (""?)*');
expect(pass).toReportError('start = ("a"?)*');
expect(pass).toReportError("start = (''?)*");
expect(pass).toReportError("start = ('a'?)*");
expect(pass).toReportError('start = (""*)*');
expect(pass).toReportError('start = ("a"*)*');
expect(pass).toReportError("start = (''*)*");
expect(pass).toReportError("start = ('a'*)*");
expect(pass).toReportError('start = (""+)*');
expect(pass).not.toReportError('start = ("a"+)*');
expect(pass).toReportError("start = (''+)*");
expect(pass).not.toReportError("start = ('a'+)*");
expect(pass).toReportError('start = ("")*');
expect(pass).not.toReportError('start = ("a")*');
expect(pass).toReportError("start = ('')*");
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([
'start = a*',
'a = ""'