@heroiclands/package-build 25.6.0 → 25.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/docs/api.md +2 -2
- package/docs/encoding-core.md +62 -9
- package/docs/guides/writing-in-invented-scripts.md +8 -2
- package/docs/reference/format-details.md +69 -68
- package/docs/reference/note-types.md +56 -55
- package/engine/encoding-integrity.mjs +3 -3
- package/engine/encoding-template.mjs +52 -4
- package/engine/encoding-terms.mjs +28 -13
- package/engine/encoding.mjs +174 -23
- package/engine/language-terms.mjs +3 -3
- package/package.json +1 -1
- package/types/engine/content-scripts.d.mts +6 -0
- package/types/engine/encoding-integrity.d.mts +2 -1
- package/types/engine/encoding-lexicon.d.mts +6 -0
- package/types/engine/encoding-template.d.mts +6 -0
- package/types/engine/encoding.d.mts +12 -0
- package/types/engine/language-terms.d.mts +2 -2
|
@@ -636,7 +636,7 @@ These fields are accepted by every note type. A field's value can still be irrel
|
|
|
636
636
|
| `data.sounds.marks` | list of `{ mark, letters, kind, limit }` | no | The marks the language writes on its letters. |
|
|
637
637
|
| `data.sounds.marks[].mark` | a string, such as `acute` | yes | The mark's name. |
|
|
638
638
|
| `data.sounds.marks[].letters` | a list of letters | yes | The letters that carry it. |
|
|
639
|
-
| `data.sounds.marks[].kind` | one of stress, length, apart, quality
|
|
639
|
+
| `data.sounds.marks[].kind` | one of stress, length, apart, quality, tone | yes | What the mark says of its letter. |
|
|
640
640
|
| `data.sounds.marks[].limit` | one of one, any | yes | How many a word may carry: `one` at most, or `any`. |
|
|
641
641
|
| `data.sounds.skeleton` | list of `{ second, between? }` | no | The vowel set between a root's second and third consonants. |
|
|
642
642
|
| `data.sounds.skeleton[].second` | a list of sounds, or `any` | yes | The second consonants of a root the row holds for; `any` for every other. |
|
|
@@ -1027,60 +1027,61 @@ Lore records in-world knowledge. A `culture` describes a people; a `custom` desc
|
|
|
1027
1027
|
|
|
1028
1028
|
**Inner keys** — a key a field's value does not declare is an error at its own line and column.
|
|
1029
1029
|
|
|
1030
|
-
| Key | Shape | Required | Meaning
|
|
1031
|
-
| ------------------------------------------------------ | --------------------------------------------------------------------- | -------- |
|
|
1032
|
-
| `data.rules.normalization` | "NFC" | yes | Required value: "NFC".
|
|
1033
|
-
| `data.rules.matching` | "longest-first" | yes | Required value: "longest-first".
|
|
1034
|
-
| `data.rules.roundTrip` | a string | no | Exact by default; canonical permits aliases and empty code sequences and decodes an authored conventional spelling.
|
|
1035
|
-
| `data.rules.word-separators` | a list of nonempty NFC source sequences | no | Source word dividers written as shared spaces, after contextual alternatives; cannot also be ordinary mappings or replace shared punctuation.
|
|
1036
|
-
| `data.rules.case` | a map | no | Case handling for letters and consonant groups.
|
|
1037
|
-
| `data.rules.case.single-letter` | "preserve" | yes | Required value: "preserve".
|
|
1038
|
-
| `data.rules.case.consonant-group` | "lowercase-or-initial-uppercase" | yes | Required value: "lowercase-or-initial-uppercase".
|
|
1039
|
-
| `data.rules.case.other-group-cases` | "forbidden" | yes | Required value: "forbidden".
|
|
1040
|
-
| `data.rules.stress` | a map | no | The font's nonprinting stress modifier after an acute vowel.
|
|
1041
|
-
| `data.rules.stress.modifier` | a string | yes | The font code for U+E0114.
|
|
1042
|
-
| `data.rules.stress.position` | "after-stressed-vowel" | yes | Required value: "after-stressed-vowel".
|
|
1043
|
-
| `data.rules.stress.printing` | false | yes | Required value: false.
|
|
1044
|
-
| `data.rules.stress.effect` | "stress-only" | yes | Required value: "stress-only".
|
|
1045
|
-
| `data.rules.stress.changes-sound` | false | yes | Required value: false.
|
|
1046
|
-
| `data.rules.stress.changes-length` | false | yes | Required value: false.
|
|
1047
|
-
| `data.rules.stress.vowel-group-position` | "first-vowel" | yes | Required value: "first-vowel".
|
|
1048
|
-
| `data.rules.vowel-groups` | a map | no | Explicit recognized vowel groups and their acute placement.
|
|
1049
|
-
| `data.rules.vowel-groups.reading` | "established" | yes | Required value: "established".
|
|
1050
|
-
| `data.rules.vowel-groups.allow-splitting` | false | yes | Required value: false.
|
|
1051
|
-
| `data.rules.vowel-groups.unrecognized-adjacent-vowels` | "separate" | yes | Required value: "separate".
|
|
1052
|
-
| `data.rules.vowel-groups.groups` | a list of vowel groups | yes | The complete authored group inventory.
|
|
1053
|
-
| `data.rules.diaeresis` | a map | no | Whether diaeresis
|
|
1054
|
-
| `data.rules.diaeresis.allowed` | true or false | yes | False forbids the
|
|
1055
|
-
| `data.rules.grave-accent` | a map | no | Whether grave-accent mappings are allowed.
|
|
1056
|
-
| `data.rules.grave-accent.allowed` | true or false | yes | False forbids the accent; true permits explicitly mapped and accepted characters.
|
|
1057
|
-
| `data.rules.contextual-mappings` | a list of conditional mappings | no | Alternatives conditioned on the following letter or source position, applied before unconditional mappings.
|
|
1058
|
-
| `data.rules.contextual-mappings[].match` | a string | yes | The source sequence.
|
|
1059
|
-
| `data.rules.contextual-mappings[].
|
|
1060
|
-
| `data.rules.contextual-mappings[].
|
|
1061
|
-
| `data.rules.contextual-mappings[].
|
|
1062
|
-
| `data.rules.
|
|
1063
|
-
| `data.rules.y
|
|
1064
|
-
| `data.rules.y.
|
|
1065
|
-
| `data.rules.y.
|
|
1066
|
-
| `data.
|
|
1067
|
-
| `data.concepts[].
|
|
1068
|
-
| `data.concepts[].
|
|
1069
|
-
| `data.
|
|
1070
|
-
| `data.sounds.letters
|
|
1071
|
-
| `data.sounds.letters[].
|
|
1072
|
-
| `data.sounds.letters[].
|
|
1073
|
-
| `data.sounds.letters[].
|
|
1074
|
-
| `data.sounds.letters[].
|
|
1075
|
-
| `data.sounds.letters[].
|
|
1076
|
-
| `data.sounds.
|
|
1077
|
-
| `data.sounds.marks
|
|
1078
|
-
| `data.sounds.marks[].
|
|
1079
|
-
| `data.sounds.marks[].
|
|
1080
|
-
| `data.sounds.marks[].
|
|
1081
|
-
| `data.
|
|
1082
|
-
| `data.metres[].
|
|
1083
|
-
| `data.metres[].
|
|
1030
|
+
| Key | Shape | Required | Meaning |
|
|
1031
|
+
| ------------------------------------------------------ | --------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1032
|
+
| `data.rules.normalization` | "NFC" | yes | Required value: "NFC". |
|
|
1033
|
+
| `data.rules.matching` | "longest-first" | yes | Required value: "longest-first". |
|
|
1034
|
+
| `data.rules.roundTrip` | a string | no | Exact by default; canonical permits aliases and empty code sequences and decodes an authored conventional spelling. |
|
|
1035
|
+
| `data.rules.word-separators` | a list of nonempty NFC source sequences | no | Source word dividers written as shared spaces, after contextual alternatives; cannot also be ordinary mappings or replace shared punctuation. |
|
|
1036
|
+
| `data.rules.case` | a map | no | Case handling for letters and consonant groups. |
|
|
1037
|
+
| `data.rules.case.single-letter` | "preserve" | yes | Required value: "preserve". |
|
|
1038
|
+
| `data.rules.case.consonant-group` | "lowercase-or-initial-uppercase" | yes | Required value: "lowercase-or-initial-uppercase". |
|
|
1039
|
+
| `data.rules.case.other-group-cases` | "forbidden" | yes | Required value: "forbidden". |
|
|
1040
|
+
| `data.rules.stress` | a map | no | The font's nonprinting stress modifier after an acute vowel. |
|
|
1041
|
+
| `data.rules.stress.modifier` | a string | yes | The font code for U+E0114. |
|
|
1042
|
+
| `data.rules.stress.position` | "after-stressed-vowel" | yes | Required value: "after-stressed-vowel". |
|
|
1043
|
+
| `data.rules.stress.printing` | false | yes | Required value: false. |
|
|
1044
|
+
| `data.rules.stress.effect` | "stress-only" | yes | Required value: "stress-only". |
|
|
1045
|
+
| `data.rules.stress.changes-sound` | false | yes | Required value: false. |
|
|
1046
|
+
| `data.rules.stress.changes-length` | false | yes | Required value: false. |
|
|
1047
|
+
| `data.rules.stress.vowel-group-position` | "first-vowel" | yes | Required value: "first-vowel". |
|
|
1048
|
+
| `data.rules.vowel-groups` | a map | no | Explicit recognized vowel groups and their acute placement. |
|
|
1049
|
+
| `data.rules.vowel-groups.reading` | "established" | yes | Required value: "established". |
|
|
1050
|
+
| `data.rules.vowel-groups.allow-splitting` | false | yes | Required value: false. |
|
|
1051
|
+
| `data.rules.vowel-groups.unrecognized-adjacent-vowels` | "separate" | yes | Required value: "separate". |
|
|
1052
|
+
| `data.rules.vowel-groups.groups` | a list of vowel groups | yes | The complete authored group inventory. |
|
|
1053
|
+
| `data.rules.diaeresis` | a map | no | Whether diaeresis vowels are allowed, and how they are written. |
|
|
1054
|
+
| `data.rules.diaeresis.allowed` | true or false | yes | False forbids the diaeresis; true derives plain-vowel mappings and restores splits where a declared group's native mapping distinguishes joined and separate vowels. Exact conversion refuses an unrecoverable derived group split; canonical conversion keeps its conventional reading. |
|
|
1055
|
+
| `data.rules.grave-accent` | a map | no | Whether grave-accent mappings are allowed. |
|
|
1056
|
+
| `data.rules.grave-accent.allowed` | true or false | yes | False forbids the accent; true permits explicitly mapped and accepted characters. |
|
|
1057
|
+
| `data.rules.contextual-mappings` | a list of conditional mappings | no | Alternatives conditioned on the preceding letter, following letter or source position, applied before unconditional mappings. |
|
|
1058
|
+
| `data.rules.contextual-mappings[].match` | a string | yes | The source sequence. |
|
|
1059
|
+
| `data.rules.contextual-mappings[].after` | one NFC Unicode letter, vowel or consonant | no | Match after one exact NFC Unicode letter, a declared vowel or another letter. |
|
|
1060
|
+
| `data.rules.contextual-mappings[].before` | a string | no | Match before a declared vowel or another letter. |
|
|
1061
|
+
| `data.rules.contextual-mappings[].at` | a string | no | Match at word start, word end or line start. |
|
|
1062
|
+
| `data.rules.contextual-mappings[].emit` | a glyph code or list of codes (empty only with canonical round trips) | yes | The font codes written by this alternative. |
|
|
1063
|
+
| `data.rules.y` | a map | no | The consonant reading of y. |
|
|
1064
|
+
| `data.rules.y.kind` | "consonant" | yes | Required value: "consonant". |
|
|
1065
|
+
| `data.rules.y.sound` | "y in yes" | yes | Required value: "y in yes". |
|
|
1066
|
+
| `data.rules.y.vowel-reading` | false | yes | Required value: false. |
|
|
1067
|
+
| `data.concepts[].id` | lowercase letters, digits and `-` | yes | The concept's id, which a word's `concept` names. Unique in the package. |
|
|
1068
|
+
| `data.concepts[].gloss` | a short gloss, such as `a smell; a scent` | yes | What the concept means, in brief. |
|
|
1069
|
+
| `data.concepts[].group` | a group name, such as `senses` | yes | The group the report lists the concept under. |
|
|
1070
|
+
| `data.sounds.letters` | list of `{ letters, kind, like?, place?, ipa?, tongue? }` | no | The letters the language writes, and the sounds they stand for. |
|
|
1071
|
+
| `data.sounds.letters[].letters` | a list of letters | yes | The letters or digraphs the row describes, as written. |
|
|
1072
|
+
| `data.sounds.letters[].kind` | a string, such as `consonant` or `long vowel` | yes | What kind of sound they write. |
|
|
1073
|
+
| `data.sounds.letters[].like` | a string | no | What they sound like; absent where the row states only their kind and place. |
|
|
1074
|
+
| `data.sounds.letters[].place` | a string, such as `lips` or `tongue-tip` | no | Where in the mouth they are made. |
|
|
1075
|
+
| `data.sounds.letters[].ipa` | a string in the International Phonetic Alphabet, such as `ʃ` | no | The sounds they write, in the International Phonetic Alphabet. |
|
|
1076
|
+
| `data.sounds.letters[].tongue` | a tag or a language note's Address, or a list of them | no | The tongues that write them, each a tag `data.tongues` declares or the Address of a language note; every one where absent. A `doc` note's rows name language notes only. |
|
|
1077
|
+
| `data.sounds.marks` | list of `{ mark, letters, kind, limit }` | no | The marks the language writes on its letters. |
|
|
1078
|
+
| `data.sounds.marks[].mark` | a string, such as `acute` | yes | The mark's name. |
|
|
1079
|
+
| `data.sounds.marks[].letters` | a list of letters | yes | The letters that carry it. |
|
|
1080
|
+
| `data.sounds.marks[].kind` | one of stress, length, apart, quality, tone | yes | What the mark says of its letter. |
|
|
1081
|
+
| `data.sounds.marks[].limit` | one of one, any | yes | How many a word may carry: `one` at most, or `any`. |
|
|
1082
|
+
| `data.metres[].name` | string | yes | What a verse div's `scan=` names the metre by: lowercase letters, digits and `-`, unique in the package. |
|
|
1083
|
+
| `data.metres[].beats` | a whole number above zero | yes | The beats of one line. |
|
|
1084
|
+
| `data.metres[].kind` | one of accentual | yes | How the beats are counted: `accentual`, one beat for each stressed syllable the language's stress rule and the beat convention give. |
|
|
1084
1085
|
|
|
1085
1086
|
### macro
|
|
1086
1087
|
|
|
@@ -16,20 +16,20 @@
|
|
|
16
16
|
/**
|
|
17
17
|
* Check codeword integrity with a trie and finite Sardinas–Patterson residuals.
|
|
18
18
|
* Source-equivalent compositions are redundant rules, not distinct spellings.
|
|
19
|
-
* @param {Array<{from:string,to:string,key:string,before?:string,at?:string}>} rows - Validated ordinary rows.
|
|
19
|
+
* @param {Array<{from:string,to:string,key:string,after?:string,before?:string,at?:string}>} rows - Validated ordinary rows.
|
|
20
20
|
* @param {(key:string,message:string)=>never} fault - Positioned schema-error constructor.
|
|
21
21
|
*/
|
|
22
22
|
export function assertUniqueOutputs(rows, fault) {
|
|
23
23
|
const bySource = new Map();
|
|
24
24
|
for (const row of rows) {
|
|
25
|
-
if (row.before || row.at) continue;
|
|
25
|
+
if (row.after || row.before || row.at) continue;
|
|
26
26
|
const first = [...row.from][0];
|
|
27
27
|
if (!bySource.has(first)) bySource.set(first, []);
|
|
28
28
|
bySource.get(first).push(row);
|
|
29
29
|
}
|
|
30
30
|
for (const group of bySource.values()) group.sort((a, b) => b.from.length - a.from.length);
|
|
31
31
|
const redundant = (row) => {
|
|
32
|
-
if (row.before || row.at || [...row.from].length < 2) return false;
|
|
32
|
+
if (row.after || row.before || row.at || [...row.from].length < 2) return false;
|
|
33
33
|
let at = 0,
|
|
34
34
|
output = "";
|
|
35
35
|
while (at < row.from.length) {
|
|
@@ -21,6 +21,12 @@ const scalar = (value) =>
|
|
|
21
21
|
!/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/u.test(value);
|
|
22
22
|
const base = (value) => value.normalize("NFD").replace(/\p{M}/gu, "");
|
|
23
23
|
const slug = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
24
|
+
/** Each diaeresis vowel and the plain vowel whose mapping writes it. */
|
|
25
|
+
const DIAERESIS_VOWELS = Object.freeze(
|
|
26
|
+
Object.fromEntries(
|
|
27
|
+
[..."aeiouAEIOU"].map((plain) => [(plain + "\u0308").normalize("NFC"), plain]),
|
|
28
|
+
),
|
|
29
|
+
);
|
|
24
30
|
const codeName = /^[A-Za-z0-9_]+$/;
|
|
25
31
|
|
|
26
32
|
/**
|
|
@@ -265,11 +271,25 @@ export function templateProgram(data, options, fault) {
|
|
|
265
271
|
const seen = new Set();
|
|
266
272
|
for (const [i, row] of rules["contextual-mappings"].entries()) {
|
|
267
273
|
const key = `rules.contextual-mappings.${i}`;
|
|
268
|
-
closed(row, ["match", "before", "at", "emit"], key);
|
|
274
|
+
closed(row, ["match", "after", "before", "at", "emit"], key);
|
|
269
275
|
if (!scalar(row.match) || !row.match || row.match !== row.match.normalize("NFC"))
|
|
270
276
|
fault(`${key}.match`, "A contextual match must be nonempty NFC text");
|
|
271
|
-
if (row.before === undefined && row.at === undefined)
|
|
272
|
-
fault(key, "A contextual mapping requires before or at");
|
|
277
|
+
if (row.after === undefined && row.before === undefined && row.at === undefined)
|
|
278
|
+
fault(key, "A contextual mapping requires after, before or at");
|
|
279
|
+
if (row.after !== undefined) {
|
|
280
|
+
if (
|
|
281
|
+
!["vowel", "consonant"].includes(row.after) &&
|
|
282
|
+
(typeof row.after !== "string" ||
|
|
283
|
+
!/^\p{L}$/u.test(row.after) ||
|
|
284
|
+
row.after !== row.after.normalize("NFC"))
|
|
285
|
+
)
|
|
286
|
+
fault(`${key}.after`, "after is one NFC Unicode letter, vowel or consonant");
|
|
287
|
+
if (["vowel", "consonant"].includes(row.after) && !vowels)
|
|
288
|
+
fault(
|
|
289
|
+
`${key}.after`,
|
|
290
|
+
"After-vowel or after-consonant mappings require declared vowels",
|
|
291
|
+
);
|
|
292
|
+
}
|
|
273
293
|
if (row.before !== undefined && !["vowel", "consonant"].includes(row.before))
|
|
274
294
|
fault(`${key}.before`, "before is vowel or consonant");
|
|
275
295
|
if (row.at !== undefined && !["start", "end", "line-start"].includes(row.at))
|
|
@@ -279,7 +299,12 @@ export function templateProgram(data, options, fault) {
|
|
|
279
299
|
`${key}.before`,
|
|
280
300
|
"Before-vowel or before-consonant mappings require declared vowels",
|
|
281
301
|
);
|
|
282
|
-
const condition = JSON.stringify([
|
|
302
|
+
const condition = JSON.stringify([
|
|
303
|
+
row.match,
|
|
304
|
+
row.after ?? null,
|
|
305
|
+
row.before ?? null,
|
|
306
|
+
row.at ?? null,
|
|
307
|
+
]);
|
|
283
308
|
if (seen.has(condition))
|
|
284
309
|
fault(
|
|
285
310
|
`${key}.match`,
|
|
@@ -292,6 +317,7 @@ export function templateProgram(data, options, fault) {
|
|
|
292
317
|
contextual.push({
|
|
293
318
|
from: row.match,
|
|
294
319
|
to,
|
|
320
|
+
...(row.after !== undefined ? { after: row.after } : {}),
|
|
295
321
|
...(row.before !== undefined ? { before: row.before } : {}),
|
|
296
322
|
...(row.at !== undefined ? { at: row.at } : {}),
|
|
297
323
|
});
|
|
@@ -339,12 +365,27 @@ export function templateProgram(data, options, fault) {
|
|
|
339
365
|
]),
|
|
340
366
|
];
|
|
341
367
|
if (!Array.isArray(accepts)) fault("accepts", "accepts must be a list of characters or ranges");
|
|
368
|
+
// A diaeresis vowel no source names is written with its plain vowel's rows,
|
|
369
|
+
// and decoding restores it by rule.
|
|
370
|
+
const named = [...mappings.map(([from]) => from), ...contextual.map((r) => r.from)];
|
|
371
|
+
const derived =
|
|
372
|
+
rules.diaeresis?.allowed === true ?
|
|
373
|
+
Object.fromEntries(
|
|
374
|
+
Object.entries(DIAERESIS_VOWELS).filter(
|
|
375
|
+
([letter, plain]) =>
|
|
376
|
+
named.includes(plain) &&
|
|
377
|
+
!named.some((from) => from.includes(letter)) &&
|
|
378
|
+
!separators.some((row) => row.from.includes(letter)),
|
|
379
|
+
),
|
|
380
|
+
)
|
|
381
|
+
: undefined;
|
|
342
382
|
return {
|
|
343
383
|
definition: {
|
|
344
384
|
name: "encoding-note",
|
|
345
385
|
fonts: [data.font],
|
|
346
386
|
accepts: [
|
|
347
387
|
...accepts,
|
|
388
|
+
...Object.keys(derived ?? {}),
|
|
348
389
|
...STANDARD_ROWS.flatMap((r) => [...(Array.isArray(r) ? r[0] : r.from)]),
|
|
349
390
|
],
|
|
350
391
|
separator: " ",
|
|
@@ -366,6 +407,13 @@ export function templateProgram(data, options, fault) {
|
|
|
366
407
|
maxVowelGroup: Math.max(0, ...vowelGroups.map((group) => [...group].length)),
|
|
367
408
|
stress,
|
|
368
409
|
diaeresis: rules.diaeresis?.allowed === false,
|
|
410
|
+
apart:
|
|
411
|
+
derived ?
|
|
412
|
+
Object.freeze({
|
|
413
|
+
letters: Object.freeze(derived),
|
|
414
|
+
groups: Object.freeze(vowelGroups),
|
|
415
|
+
})
|
|
416
|
+
: undefined,
|
|
369
417
|
grave: rules["grave-accent"]?.allowed === false,
|
|
370
418
|
}),
|
|
371
419
|
},
|
|
@@ -89,29 +89,44 @@ export const ENCODING_RULE_FIELDS = Object.freeze([
|
|
|
89
89
|
describe: "The complete authored group inventory.",
|
|
90
90
|
},
|
|
91
91
|
]),
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
92
|
+
block("diaeresis", "Whether diaeresis vowels are allowed, and how they are written.", [
|
|
93
|
+
{
|
|
94
|
+
name: "allowed",
|
|
95
|
+
kind: "boolean",
|
|
96
|
+
shape: "true or false",
|
|
97
|
+
required: true,
|
|
98
|
+
describe:
|
|
99
|
+
"False forbids the diaeresis; true derives plain-vowel mappings and restores splits where a declared group's native mapping distinguishes joined and separate vowels. Exact conversion refuses an unrecoverable derived group split; canonical conversion keeps its conventional reading.",
|
|
100
|
+
},
|
|
101
|
+
]),
|
|
102
|
+
block("grave-accent", "Whether grave-accent mappings are allowed.", [
|
|
103
|
+
{
|
|
104
|
+
name: "allowed",
|
|
105
|
+
kind: "boolean",
|
|
106
|
+
shape: "true or false",
|
|
107
|
+
required: true,
|
|
108
|
+
describe:
|
|
109
|
+
"False forbids the accent; true permits explicitly mapped and accepted characters.",
|
|
110
|
+
},
|
|
111
|
+
]),
|
|
104
112
|
{
|
|
105
113
|
name: "contextual-mappings",
|
|
106
114
|
kind: "list",
|
|
107
115
|
shape: "a list of conditional mappings",
|
|
108
116
|
describe:
|
|
109
|
-
"Alternatives conditioned on the following letter or source position, applied before unconditional mappings.",
|
|
117
|
+
"Alternatives conditioned on the preceding letter, following letter or source position, applied before unconditional mappings.",
|
|
110
118
|
entries: {
|
|
111
119
|
kind: "map",
|
|
112
120
|
shape: "a contextual mapping",
|
|
113
121
|
fields: [
|
|
114
122
|
text("match", "The source sequence.", true),
|
|
123
|
+
{
|
|
124
|
+
...text(
|
|
125
|
+
"after",
|
|
126
|
+
"Match after one exact NFC Unicode letter, a declared vowel or another letter.",
|
|
127
|
+
),
|
|
128
|
+
shape: "one NFC Unicode letter, vowel or consonant",
|
|
129
|
+
},
|
|
115
130
|
{
|
|
116
131
|
...text("before", "Match before a declared vowel or another letter."),
|
|
117
132
|
allowed: ["vowel", "consonant"],
|