Skip to content

Commit

Permalink
Ranges (pegjs/pegjs#30): Add testcases for delimiter support in range…
Browse files Browse the repository at this point in the history
…s and regenerate parser
  • Loading branch information
Mingun committed Feb 21, 2023
1 parent 22feab4 commit da03254
Show file tree
Hide file tree
Showing 10 changed files with 2,466 additions and 274 deletions.
574 changes: 301 additions & 273 deletions lib/parser.js

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions test/api/pegjs-api.spec.js
Expand Up @@ -254,12 +254,16 @@ describe("Peggy API", () => {
const NOT_BLOCK = "NOT\nBLOCK";
const AND_BLOCK = "AND\nBLOCK";
const ACTION_BLOCK = "ACTION\nBLOCK";
const MIN_BLOCK = "MIN\nBLOCK";
const MAX_BLOCK = "MAX\nBLOCK";
const EXACT_BLOCK = "EXACT\nBLOCK";
const SOURCE = `
{{${GLOBAL_INITIALIZER}}}
{${PER_PARSE_INITIALIZER}}
RULE_1 = !{${NOT_BLOCK}} 'a' rule:RULE_2 {${ACTION_BLOCK}};
RULE_2 'named' = &{${AND_BLOCK}} @'b' [abc] 'def';
RULE_3 = RULE_1 / RULE_2;
RULE_4 = RULE_1|{${MIN_BLOCK}} .. {${MAX_BLOCK}}, RULE_2|{${EXACT_BLOCK}}| |;
`;

function check(chunk, source, name, generatedChunk = chunk) {
Expand Down Expand Up @@ -304,6 +308,9 @@ describe("Peggy API", () => {
it("action block", () => check(ACTION_BLOCK, source, null));
it("semantic and predicate", () => check(AND_BLOCK, source, null));
it("semantic not predicate", () => check(NOT_BLOCK, source, null));
it("min function boundary", () => check(MIN_BLOCK, source, null));
it("max function boundary", () => check(MAX_BLOCK, source, null));
it("exact function boundary", () => check(EXACT_BLOCK, source, null));

it("rule name", () => check("RULE_1", source, "RULE_1", "peg$parseRULE_1() {"));
it("labelled rule name", () => check("RULE_2 'named'", source, "RULE_2", "peg$parseRULE_2() {"));
Expand Down
555 changes: 555 additions & 0 deletions test/behavior/generated-parser-behavior.spec.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions test/types/peg.test-d.ts
Expand Up @@ -355,6 +355,7 @@ describe("peg.d.ts", () => {
expectType<peggy.LocationRange>(node.location);
expectType<peggy.ast.RepeatedBoundary | null>(node.min);
expectType<peggy.ast.RepeatedBoundary>(node.max);
expectType<peggy.ast.Expression | null>(node.delimiter);
expectType<peggy.ast.Primary>(node.expression);
visit(node.expression);
},
Expand Down
1,032 changes: 1,032 additions & 0 deletions test/unit/compiler/passes/generate-bytecode.spec.js

Large diffs are not rendered by default.

219 changes: 219 additions & 0 deletions test/unit/compiler/passes/inference-match-result.spec.js
Expand Up @@ -199,6 +199,225 @@ describe("compiler pass |inferenceMatchResult|", () => {
});
});
});

describe("with delimiter", () => {
describe("with constant boundaries", () => {
it("for | .. , delimiter| correctly", () => {
expect(pass).to.changeAST("start = .| .. , .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = .| .. ,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = .| .. ,[]|", { rules: [{ match: 1 }] });

expect(pass).to.changeAST("start = ''| .. , .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''| .. ,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''| .. ,[]|", { rules: [{ match: 1 }] });

expect(pass).to.changeAST("start = []| .. , .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = []| .. ,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = []| .. ,[]|", { rules: [{ match: 1 }] });
});
it("for | ..1, delimiter| correctly", () => {
expect(pass).to.changeAST("start = .| ..1, .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = .| ..1,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = .| ..1,[]|", { rules: [{ match: 1 }] });

expect(pass).to.changeAST("start = ''| ..1, .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''| ..1,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''| ..1,[]|", { rules: [{ match: 1 }] });

expect(pass).to.changeAST("start = []| ..1, .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = []| ..1,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = []| ..1,[]|", { rules: [{ match: 1 }] });
});
it("for | ..3, delimiter| correctly", () => {
expect(pass).to.changeAST("start = .| ..3, .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = .| ..3,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = .| ..3,[]|", { rules: [{ match: 1 }] });

expect(pass).to.changeAST("start = ''| ..3, .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''| ..3,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''| ..3,[]|", { rules: [{ match: 1 }] });

expect(pass).to.changeAST("start = []| ..3, .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = []| ..3,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = []| ..3,[]|", { rules: [{ match: 1 }] });
});
it("for |0.. , delimiter| correctly", () => {
expect(pass).to.changeAST("start = .|0.. , .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = .|0.. ,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = .|0.. ,[]|", { rules: [{ match: 1 }] });

expect(pass).to.changeAST("start = ''|0.. , .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''|0.. ,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''|0.. ,[]|", { rules: [{ match: 1 }] });

expect(pass).to.changeAST("start = []|0.. , .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = []|0.. ,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = []|0.. ,[]|", { rules: [{ match: 1 }] });
});
it("for |1.. , delimiter| correctly", () => {
expect(pass).to.changeAST("start = .|1.. , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|1.. ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|1.. ,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = ''|1.. , .|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''|1.. ,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''|1.. ,[]|", { rules: [{ match: 1 }] });

expect(pass).to.changeAST("start = []|1.. , .|", { rules: [{ match: -1 }] });
expect(pass).to.changeAST("start = []|1.. ,''|", { rules: [{ match: -1 }] });
expect(pass).to.changeAST("start = []|1.. ,[]|", { rules: [{ match: -1 }] });
});
it("for |2.. , delimiter| correctly", () => {
expect(pass).to.changeAST("start = .|2.. , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|2.. ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|2.. ,[]|", { rules: [{ match: -1 }] });

expect(pass).to.changeAST("start = ''|2.. , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''|2.. ,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''|2.. ,[]|", { rules: [{ match: -1 }] });

expect(pass).to.changeAST("start = []|2.. , .|", { rules: [{ match: -1 }] });
expect(pass).to.changeAST("start = []|2.. ,''|", { rules: [{ match: -1 }] });
expect(pass).to.changeAST("start = []|2.. ,[]|", { rules: [{ match: -1 }] });
});
it("for |2..3, delimiter| correctly", () => {
expect(pass).to.changeAST("start = .|2..3, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|2..3,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|2..3,[]|", { rules: [{ match: -1 }] });

expect(pass).to.changeAST("start = ''|2..3, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''|2..3,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''|2..3,[]|", { rules: [{ match: -1 }] });

expect(pass).to.changeAST("start = []|2..3, .|", { rules: [{ match: -1 }] });
expect(pass).to.changeAST("start = []|2..3,''|", { rules: [{ match: -1 }] });
expect(pass).to.changeAST("start = []|2..3,[]|", { rules: [{ match: -1 }] });
});
it("for | 42 , delimiter| correctly", () => {
expect(pass).to.changeAST("start = .| 42 , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .| 42 ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .| 42 ,[]|", { rules: [{ match: -1 }] });

expect(pass).to.changeAST("start = ''| 42 , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''| 42 ,''|", { rules: [{ match: 1 }] });
expect(pass).to.changeAST("start = ''| 42 ,[]|", { rules: [{ match: -1 }] });

expect(pass).to.changeAST("start = []| 42 , .|", { rules: [{ match: -1 }] });
expect(pass).to.changeAST("start = []| 42 ,''|", { rules: [{ match: -1 }] });
expect(pass).to.changeAST("start = []| 42 ,[]|", { rules: [{ match: -1 }] });
});
});

describe("with variable boundaries", () => {
it("for | ..max, delimiter| correctly", () => {
expect(pass).to.changeAST("start = .| ..max, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .| ..max,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .| ..max,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = ''| ..max, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''| ..max,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''| ..max,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = []| ..max, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []| ..max,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []| ..max,[]|", { rules: [{ match: 0 }] });
});
it("for |min.. , delimiter| correctly", () => {
expect(pass).to.changeAST("start = .|min.. , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|min.. ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|min.. ,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = ''|min.. , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''|min.. ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''|min.. ,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = []|min.. , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []|min.. ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []|min.. ,[]|", { rules: [{ match: 0 }] });
});
it("for |min..max, delimiter| correctly", () => {
expect(pass).to.changeAST("start = .|min..max, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|min..max,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|min..max,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = ''|min..max, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''|min..max,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''|min..max,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = []|min..max, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []|min..max,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []|min..max,[]|", { rules: [{ match: 0 }] });
});
it("for | exact , delimiter| correctly", () => {
expect(pass).to.changeAST("start = .| exact , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .| exact ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .| exact ,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = ''| exact , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''| exact ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''| exact ,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = []| exact , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []| exact ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []| exact ,[]|", { rules: [{ match: 0 }] });
});
});

describe("with function boundaries", () => {
it("for | ..{}, delimiter| correctly", () => {
expect(pass).to.changeAST("start = .| ..{}, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .| ..{},''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .| ..{},[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = ''| ..{}, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''| ..{},''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''| ..{},[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = []| ..{}, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []| ..{},''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []| ..{},[]|", { rules: [{ match: 0 }] });
});
it("for |{}.. , delimiter| correctly", () => {
expect(pass).to.changeAST("start = .|{}.. , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|{}.. ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|{}.. ,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = ''|{}.. , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''|{}.. ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''|{}.. ,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = []|{}.. , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []|{}.. ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []|{}.. ,[]|", { rules: [{ match: 0 }] });
});
it("for |{}..{}, delimiter| correctly", () => {
expect(pass).to.changeAST("start = .|{}..{}, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|{}..{},''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .|{}..{},[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = ''|{}..{}, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''|{}..{},''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''|{}..{},[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = []|{}..{}, .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []|{}..{},''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []|{}..{},[]|", { rules: [{ match: 0 }] });
});
it("for | {} , delimiter| correctly", () => {
expect(pass).to.changeAST("start = .| {} , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .| {} ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = .| {} ,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = ''| {} , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''| {} ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = ''| {} ,[]|", { rules: [{ match: 0 }] });

expect(pass).to.changeAST("start = []| {} , .|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []| {} ,''|", { rules: [{ match: 0 }] });
expect(pass).to.changeAST("start = []| {} ,[]|", { rules: [{ match: 0 }] });
});
});
});
});

it("calculate |match| property for |group| correctly", () => {
Expand Down
1 change: 1 addition & 0 deletions test/unit/compiler/passes/report-duplicate-labels.spec.js
Expand Up @@ -41,6 +41,7 @@ describe("compiler pass |reportDuplicateLabels|", () => {
expect(pass).to.not.reportError("start = (a:'a')* a:'a'");
expect(pass).to.not.reportError("start = (a:'a')+ a:'a'");
expect(pass).to.not.reportError("start = (a:'a')|2..3| a:'a'");
expect(pass).to.not.reportError("start = 'a'|2..3, a:'a'| a:'a'");
expect(pass).to.not.reportError("start = (a:'a') a:'a'");
});
});
Expand Down
22 changes: 22 additions & 0 deletions test/unit/compiler/passes/report-infinite-recursion.spec.js
Expand Up @@ -136,4 +136,26 @@ describe("compiler pass |reportInfiniteRecursion|", () => {
expect(pass).to.not.reportError("start = . start");
});
});

describe("in repeated with delimiter", () => {
it("doesn't report left recursion for delimiter if expression not match empty string", () => {
expect(pass).to.not.reportError("start = 'a'| .. , start|");
expect(pass).to.not.reportError("start = 'a'|0.. , start|");
expect(pass).to.not.reportError("start = 'a'|1.. , start|");
expect(pass).to.not.reportError("start = 'a'|2.. , start|");
expect(pass).to.not.reportError("start = 'a'| ..3, start|");
expect(pass).to.not.reportError("start = 'a'|2..3, start|");
expect(pass).to.not.reportError("start = 'a'| 42 , start|");
});

it("reports left recursion for delimiter if expression match empty string", () => {
expect(pass).to.reportError("start = ''| .. , start|");
expect(pass).to.reportError("start = ''|0.. , start|");
expect(pass).to.reportError("start = ''|1.. , start|");
expect(pass).to.reportError("start = ''|2.. , start|");
expect(pass).to.reportError("start = ''| ..3, start|");
expect(pass).to.reportError("start = ''|2..3, start|");
expect(pass).to.reportError("start = ''| 42 , start|");
});
});
});

0 comments on commit da03254

Please sign in to comment.