Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ Steven Spungin <steven@spungin.tv> (https://github.com/flamenco/)
XenoS (https://github.com/XenoS-ITA)
Samuel Bronson (https://github.com/SamB)
knoan (https://github.com/knoan)
Yarchik <spoko.dev@gmail.com> (https://github.com/spokodev/)
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,18 @@ Change Log

This file documents all notable changes to Peggy.

Unreleased
----------

### Bug fixes

- Fixed case-insensitive string literals whose lower case has a different
length than the original (for example `"İ"i`, where U+0130 is one code unit
but lower-cases to two). The generated parser now reads and consumes the
original code units instead of the lower-cased length, so the match no
longer fails.
[#662](https://github.com/peggyjs/peggy/pull/662)

5.1.0
-----

Expand Down
10 changes: 9 additions & 1 deletion lib/compiler/passes/generate-js.js
Original file line number Diff line number Diff line change
Expand Up @@ -737,10 +737,18 @@ function generateJS(ast, options) {

case op.MATCH_STRING_IC: { // MATCH_STRING_IC s, a, f, ...
const litNum = bc[ip + 1];
// The stored literal is already lower-cased, so its length can
// differ from the original literal when `.toLowerCase()` changes
// the number of code units (e.g. "İ" -> "i̇"). The input
// chunk we read and consume must span the original code units, so
// use the paired ACCEPT_N count instead of the lower-cased length.
const inputChunkLength = bc[ip + 4] === op.ACCEPT_N
? bc[ip + 5]
: literals[litNum].length;
compileInputChunkCondition(
inputChunk => `${inputChunk}.toLowerCase() === ${l(litNum)}`,
1,
literals[litNum].length
inputChunkLength
);
break;
}
Expand Down
9 changes: 9 additions & 0 deletions test/behavior/generated-parser-behavior.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,15 @@ describe("generated parser behavior", () => {
expect(parser).to.parse("a");
expect(parser).to.parse("A");
});

it("matches a case-insensitive literal whose lower case changes its length", () => {
// "İ" (U+0130) is one UTF-16 code unit, but `.toLowerCase()` yields
// two ("i" + U+0307). The case-insensitive match must read and
// consume the original code units, not the lower-cased length.
const parser = peg.generate("start = \"İ\"i .", options);

expect(parser).to.parse("İx", ["İ", "x"]);
});
});

describe("when it matches", () => {
Expand Down
Loading