Skip to content

Commit d96a858

Browse files
committed
Generate the No_Block default for gaps between blocks
Assisted-By: GPT-6
1 parent 138d645 commit d96a858

3 files changed

Lines changed: 44 additions & 4 deletions

File tree

‎scripts/parse-blocks-scripts-properties.mjs‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ const parseDerivedGeneralCategory = async (version) => {
283283
};
284284

285285
const parseBlocks = async (version) => {
286+
let map;
286287
if (
287288
version === '3.0.1' ||
288289
version === '3.0.0' ||
@@ -292,7 +293,7 @@ const parseBlocks = async (version) => {
292293
if (!source) {
293294
return;
294295
}
295-
const map = {};
296+
map = {};
296297
for (const line of source.trimEnd().split('\n')) {
297298
const record = line.split('#')[0].trim();
298299
if (!record) {
@@ -307,10 +308,21 @@ const parseBlocks = async (version) => {
307308
parseInt(end, 16),
308309
);
309310
}
310-
return map;
311311
} else {
312-
return await parseBlocksScriptsProperties('blocks', version);
312+
map = await parseBlocksScriptsProperties('blocks', version);
313+
}
314+
if (!map) {
315+
return;
313316
}
317+
// Block=No_Block covers every gap between the explicitly named blocks.
318+
const noBlock = regenerate().addRange(0, CODEPOINT_MAX);
319+
for (const [block, codePoints] of Object.entries(map)) {
320+
if (block !== 'No_Block') {
321+
noBlock.remove(codePoints);
322+
}
323+
}
324+
map.No_Block = noBlock;
325+
return map;
314326
};
315327

316328
const parseProperties = async (version) => {

‎tests/parser-formats.mjs‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,13 @@ test('Block names exclude inline comments in modern and legacy formats', async (
5555
);
5656
for (const version of ['3.0.1', '18.0.0']) {
5757
const map = await parseBlocks(version);
58-
t.assert.deepEqual(Object.keys(map), ['Basic_Latin']);
58+
t.assert.deepEqual(Object.keys(map).sort(), ['Basic_Latin', 'No_Block']);
5959
t.assert.ok(map.Basic_Latin.contains(0));
6060
t.assert.ok(map.Basic_Latin.contains(0x7f));
6161
t.assert.ok(!map.Basic_Latin.contains(0x80));
62+
t.assert.ok(!map.No_Block.contains(0x7f));
63+
t.assert.ok(map.No_Block.contains(0x80));
64+
t.assert.ok(map.No_Block.contains(0x10ffff));
6265
}
6366
});
6467

‎tests/parsers.mjs‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,37 @@ import parseDecompositionMapping from '../scripts/parse-decomposition-mapping.mj
88
import parseIndicConjunctBreak from '../scripts/parse-indic-conjunct-break.mjs';
99
import parseJoiningType from '../scripts/parse-joining-type.mjs';
1010
import {
11+
parseBlocks,
1112
parseDerivedCoreProperties,
1213
parseDerivedNormalizationProperties,
1314
parseProperties,
1415
parseScripts,
1516
} from '../scripts/parse-blocks-scripts-properties.mjs';
1617

18+
test('Block values cover Unicode, including No_Block gaps', async (t) => {
19+
for (const version of ['2.0.14', '3.1.0', '18.0.0']) {
20+
const blocks = await parseBlocks(version);
21+
t.assert.ok(blocks.No_Block.contains(0x40000), version);
22+
t.assert.ok(!blocks.No_Block.contains(0x41), version);
23+
const ranges = Object.values(blocks)
24+
.flatMap((set) => {
25+
const result = [];
26+
for (let i = 0; i < set.data.length; i += 2) {
27+
result.push(set.data.slice(i, i + 2));
28+
}
29+
return result;
30+
})
31+
.sort((a, b) => a[0] - b[0]);
32+
let end = 0;
33+
for (const [start, next] of ranges) {
34+
t.assert.equal(start, end, `${version}: Block must partition Unicode`);
35+
end = next;
36+
}
37+
t.assert.equal(end, 0x110000);
38+
}
39+
t.assert.equal(await parseBlocks('1.1.5'), undefined);
40+
});
41+
1742
test('Hangul has pairwise canonical decomposition mappings', async (t) => {
1843
for (const version of ['2.0.14', '3.2.0', '18.0.0']) {
1944
const { Canonical } = await parseDecompositionMapping(version);

0 commit comments

Comments
 (0)