@keymanapp/kmc-ldml 18.0.16-alpha → 18.0.18-alpha

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.
Files changed (48) hide show
  1. package/build/src/compiler/compiler.js +309 -308
  2. package/build/src/compiler/compiler.js.map +1 -1
  3. package/build/src/compiler/disp.js +84 -83
  4. package/build/src/compiler/disp.js.map +1 -1
  5. package/build/src/compiler/empty-compiler.js +116 -115
  6. package/build/src/compiler/empty-compiler.js.map +1 -1
  7. package/build/src/compiler/keymanweb-compiler.js +97 -96
  8. package/build/src/compiler/keymanweb-compiler.js.map +1 -1
  9. package/build/src/compiler/keys.d.ts.map +1 -1
  10. package/build/src/compiler/keys.js +419 -415
  11. package/build/src/compiler/keys.js.map +1 -1
  12. package/build/src/compiler/layr.d.ts.map +1 -1
  13. package/build/src/compiler/layr.js +83 -76
  14. package/build/src/compiler/layr.js.map +1 -1
  15. package/build/src/compiler/ldml-compiler-options.js +5 -4
  16. package/build/src/compiler/ldml-compiler-options.js.map +1 -1
  17. package/build/src/compiler/loca.js +61 -60
  18. package/build/src/compiler/loca.js.map +1 -1
  19. package/build/src/compiler/messages.d.ts +11 -3
  20. package/build/src/compiler/messages.d.ts.map +1 -1
  21. package/build/src/compiler/messages.js +120 -109
  22. package/build/src/compiler/messages.js.map +1 -1
  23. package/build/src/compiler/meta.js +57 -56
  24. package/build/src/compiler/meta.js.map +1 -1
  25. package/build/src/compiler/metadata-compiler.js +49 -48
  26. package/build/src/compiler/metadata-compiler.js.map +1 -1
  27. package/build/src/compiler/section-compiler.js +42 -41
  28. package/build/src/compiler/section-compiler.js.map +1 -1
  29. package/build/src/compiler/substitution-tracker.js +105 -104
  30. package/build/src/compiler/substitution-tracker.js.map +1 -1
  31. package/build/src/compiler/touch-layout-compiler.js +93 -92
  32. package/build/src/compiler/touch-layout-compiler.js.map +1 -1
  33. package/build/src/compiler/tran.d.ts +7 -0
  34. package/build/src/compiler/tran.d.ts.map +1 -1
  35. package/build/src/compiler/tran.js +382 -353
  36. package/build/src/compiler/tran.js.map +1 -1
  37. package/build/src/compiler/vars.d.ts.map +1 -1
  38. package/build/src/compiler/vars.js +236 -227
  39. package/build/src/compiler/vars.js.map +1 -1
  40. package/build/src/compiler/visual-keyboard-compiler.js +70 -69
  41. package/build/src/compiler/visual-keyboard-compiler.js.map +1 -1
  42. package/build/src/main.js +5 -4
  43. package/build/src/main.js.map +1 -1
  44. package/build/src/util/util.d.ts +2 -2
  45. package/build/src/util/util.d.ts.map +1 -1
  46. package/build/src/util/util.js +185 -179
  47. package/build/src/util/util.js.map +1 -1
  48. package/package.json +6 -6
@@ -1,354 +1,383 @@
1
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="46f9c4ca-55de-57da-8da0-b5a03e066ae1")}catch(e){}}();
2
- import { constants } from "@keymanapp/ldml-keyboard-constants";
3
- import { KMXPlus, VariableParser, MarkerParser, util } from '@keymanapp/common-types';
4
- import { SectionCompiler } from "./section-compiler.js";
5
- var Bksp = KMXPlus.Bksp;
6
- var Tran = KMXPlus.Tran;
7
- var TranReorder = KMXPlus.TranReorder;
8
- var TranTransform = KMXPlus.TranTransform;
9
- import { verifyValidAndUnique } from "../util/util.js";
10
- import { CompilerMessages } from "./messages.js";
11
- import { SubstitutionUse } from "./substitution-tracker.js";
12
- export class TransformCompiler extends SectionCompiler {
13
- static validateSubstitutions(keyboard, st) {
14
- keyboard?.transforms?.forEach(transforms => transforms.transformGroup.forEach(transformGroup => {
15
- transformGroup.transform?.forEach(({ to, from }) => {
16
- st.addSetAndStringSubtitution(SubstitutionUse.consume, from);
17
- st.addSetAndStringSubtitution(SubstitutionUse.emit, to);
18
- const mapFrom = VariableParser.CAPTURE_SET_REFERENCE.exec(from);
19
- const mapTo = VariableParser.MAPPED_SET_REFERENCE.exec(to || '');
20
- if (mapFrom) {
21
- // add the 'from' as a match
22
- st.set.add(SubstitutionUse.consume, [mapFrom[1]]);
23
- }
24
- if (mapTo) {
25
- // add the 'from' as a match
26
- st.set.add(SubstitutionUse.emit, [mapTo[1]]);
27
- }
28
- });
29
- transformGroup.reorder?.forEach(({ before }) => {
30
- st.addStringSubstitution(SubstitutionUse.consume, before);
31
- });
32
- }));
33
- return true;
34
- }
35
- type;
36
- constructor(source, callbacks) {
37
- super(source, callbacks);
38
- }
39
- validate() {
40
- const reportMessage = this.callbacks.reportMessage.bind(this.callbacks);
41
- let valid = true;
42
- const transforms = this?.keyboard3?.transforms;
43
- if (transforms) {
44
- const types = transforms.map(({ type }) => type);
45
- if (!verifyValidAndUnique(types, types => reportMessage(CompilerMessages.Error_DuplicateTransformsType({ types })), new Set(['simple', 'backspace']), types => reportMessage(CompilerMessages.Error_InvalidTransformsType({ types })))) {
46
- valid = false;
47
- }
48
- // check for mixed groups
49
- let mixed = false;
50
- let empty = false;
51
- transforms.forEach(({ transformGroup }) => transformGroup.forEach((transformGroup) => {
52
- if (transformGroup.reorder?.length && transformGroup.transform?.length) {
53
- mixed = true;
54
- }
55
- if (!transformGroup.reorder?.length && !transformGroup.transform?.length) {
56
- empty = true;
57
- }
58
- }));
59
- if (mixed) {
60
- valid = false;
61
- reportMessage(CompilerMessages.Error_MixedTransformGroup()); // report this once
62
- }
63
- if (empty) {
64
- valid = false;
65
- reportMessage(CompilerMessages.Error_EmptyTransformGroup()); // report this once
66
- }
67
- // TODO-LDML: linting here should check for identical from, but this involves a double-parse which is ugly
68
- // TODO-LDML: unicodesets means that either we fully parse them and verify conflicting rules or the linting is imperfect
69
- }
70
- return valid;
71
- }
72
- /* c8 ignore next 4 */
73
- /** allocate a new TranBase subclass */
74
- newTran() {
75
- throw Error(`Internal Error: newTran() not implemented`);
76
- }
77
- compileTransforms(sections, transforms) {
78
- let result = this.newTran();
79
- if (transforms?.transformGroup) {
80
- for (let transformGroup of transforms.transformGroup) {
81
- const tg = this.compileTransformGroup(sections, transformGroup);
82
- if (!tg)
83
- return null; //error
84
- result.groups.push(tg);
85
- }
86
- }
87
- return result;
88
- }
89
- compileTransformGroup(sections, transformGroup) {
90
- if (transformGroup.reorder.length && transformGroup.transform.length) {
91
- /* c8 ignore next 2 */
92
- // should have been caught by validate
93
- throw Error(`Internal error: transformGroup has both reorder and transform elements.`);
94
- }
95
- else if (transformGroup.reorder.length) {
96
- return this.compileReorderTranGroup(sections, transformGroup.reorder);
97
- }
98
- else if (transformGroup.transform.length) {
99
- return this.compileTransformTranGroup(sections, transformGroup.transform);
100
- }
101
- else {
102
- /* c8 ignore next */
103
- throw Error(`Internal error: transformGroup has neither reorder nor transform elements.`);
104
- }
105
- }
106
- compileTransformTranGroup(sections, transforms) {
107
- const result = {
108
- type: constants.tran_group_type_transform,
109
- transforms: transforms.map(transform => this.compileTransform(sections, transform)),
110
- reorders: [],
111
- };
112
- if (result.transforms.includes(null))
113
- return null;
114
- return result;
115
- }
116
- compileTransform(sections, transform) {
117
- let result = new TranTransform();
118
- let cookedFrom = transform.from;
119
- cookedFrom = sections.vars.substituteStrings(cookedFrom, sections);
120
- const mapFrom = VariableParser.CAPTURE_SET_REFERENCE.exec(cookedFrom);
121
- const mapTo = VariableParser.MAPPED_SET_REFERENCE.exec(transform.to || '');
122
- if (mapFrom && mapTo) { // TODO-LDML: error cases
123
- result.mapFrom = sections.strs.allocString(mapFrom[1]); // var name
124
- result.mapTo = sections.strs.allocString(mapTo[1]); // var name
125
- }
126
- else {
127
- result.mapFrom = sections.strs.allocString(''); // TODO-LDML
128
- result.mapTo = sections.strs.allocString(''); // TODO-LDML
129
- }
130
- cookedFrom = sections.vars.substituteSetRegex(cookedFrom, sections);
131
- // add in markers. idempotent if no markers.
132
- cookedFrom = sections.vars.substituteMarkerString(cookedFrom, true);
133
- // check for incorrect \uXXXX escapes
134
- cookedFrom = this.checkEscapes(cookedFrom); // check for \uXXXX escapes before normalizing
135
- if (cookedFrom === null)
136
- return null; // error
137
- // unescape from \u{} form to plain, or in some cases \uXXXX / \UXXXXXXXX for core
138
- cookedFrom = util.unescapeStringToRegex(cookedFrom);
139
- // check for denormalized ranges
140
- cookedFrom = this.checkRanges(cookedFrom); // check before normalizing
141
- if (!sections?.meta?.normalizationDisabled) {
142
- // nfd here.
143
- cookedFrom = MarkerParser.nfd_markers(cookedFrom, true);
144
- }
145
- // Verify that the regex is syntactically valid
146
- try {
147
- new RegExp(cookedFrom, 'ug');
148
- }
149
- catch (e) {
150
- this.callbacks.reportMessage(CompilerMessages.Error_UnparseableTransformFrom({ from: transform.from, message: e.message }));
151
- return null; // error
152
- }
153
- // cookedFrom is cooked above, since there's some special treatment
154
- result.from = sections.strs.allocString(cookedFrom, {
155
- unescape: false,
156
- }, sections);
157
- // 'to' is handled via allocString
158
- result.to = sections.strs.allocString(transform.to, {
159
- stringVariables: true,
160
- markers: true,
161
- unescape: true,
162
- nfd: true,
163
- }, sections);
164
- return result;
165
- }
166
- compileReorderTranGroup(sections, reorders) {
167
- const result = {
168
- type: constants.tran_group_type_reorder,
169
- transforms: [],
170
- reorders: reorders.map(reorder => this.compileReorder(sections, reorder)),
171
- };
172
- if (result.reorders.includes(null))
173
- return null; // if any of the reorders returned null, fail the entire group.
174
- return result;
175
- }
176
- compileReorder(sections, reorder) {
177
- let result = new TranReorder();
178
- if (reorder.from && this.checkEscapes(reorder.from) === null) {
179
- return null; // error'ed
180
- }
181
- if (reorder.before && this.checkEscapes(reorder.before) === null) {
182
- return null; // error'ed
183
- }
184
- result.elements = sections.elem.allocElementString(sections, reorder.from, reorder.order, reorder.tertiary, reorder.tertiaryBase, reorder.preBase);
185
- result.before = sections.elem.allocElementString(sections, reorder.before);
186
- if (!result.elements || !result.before) {
187
- return null; // already error'ed
188
- }
189
- else {
190
- return result;
191
- }
192
- }
193
- compile(sections) {
194
- for (let t of this.keyboard3.transforms) {
195
- if (t.type == this.type) {
196
- // compile only the transforms of the correct type
197
- return this.compileTransforms(sections, t);
198
- }
199
- }
200
- return this.newTran(); // empty: nothing of this type found.
201
- }
202
- get dependencies() {
203
- const defaults = new Set([
204
- constants.section.elem,
205
- constants.section.list,
206
- constants.section.meta,
207
- constants.section.strs,
208
- constants.section.uset,
209
- constants.section.vars,
210
- ]);
211
- defaults.delete(this.id);
212
- return defaults;
213
- }
214
- /**
215
- * Analyze reorders and regexes for \uXXXX escapes.
216
- * The LDML spec requires \u{XXXX} format.
217
- * @param cookedFrom the original string
218
- * @returns the original string, or null if an error was reported
219
- */
220
- checkEscapes(cookedFrom) {
221
- if (!cookedFrom)
222
- return cookedFrom;
223
- // should not follow marker prefix, nor marker prefix with range
224
- const anyQuad = /(?<!\\uffff\\u0008(?:\[[0-9a-fA-F\\u-]*)?)\\u([0-9a-fA-F]{4})/g;
225
- for (const [, sub] of cookedFrom.matchAll(anyQuad)) {
226
- const s = util.unescapeOne(sub);
227
- if (s !== '\uffff' && s !== '\u0008') { // markers
228
- this.callbacks.reportMessage(CompilerMessages.Error_InvalidQuadEscape({ cp: s.codePointAt(0) }));
229
- return null; // exit on the first error
230
- }
231
- }
232
- return cookedFrom;
233
- }
234
- /**
235
- * Analyze character classes such as '[a-z]' for denormalized characters.
236
- * Escapes non-NFD characters as hex escapes.
237
- * @param cookedFrom input regex string
238
- * @returns updated 'from' string
239
- */
240
- checkRanges(cookedFrom) {
241
- if (!cookedFrom)
242
- return cookedFrom;
243
- // extract all of the potential ranges - but don't match any-markers!
244
- const anyRange = /(?<!\\uffff\\u0008)\[([^\]]+)\]/g;
245
- const ranges = cookedFrom.matchAll(anyRange);
246
- if (!ranges)
247
- return cookedFrom;
248
- // extract inner members of a range (inside the [])
249
- const rangeRegex = /(\\u\{[0-9a-fA-F]\}{1,6}|.)-(\\u\{[0-9a-fA-F]\}{1,6}|.)|./g;
250
- const rangeExplicit = new util.NFDAnalyzer();
251
- const rangeImplicit = new util.NFDAnalyzer();
252
- /** process an explicit entry */
253
- function processExplicit(s) {
254
- if (s.startsWith('\\u{')) {
255
- s = util.unescapeString(s);
256
- }
257
- rangeExplicit.add(s);
258
- return s;
259
- }
260
- for (const [, sub] of ranges) {
261
- const subRanges = sub.matchAll(rangeRegex);
262
- for (const [all, start, end] of subRanges) {
263
- if (!start && !end) {
264
- // explicit single char
265
- processExplicit(all); // matched one char
266
- }
267
- else {
268
- // start-end range - get explicit start and end chars
269
- const s = processExplicit(start);
270
- const sch = s.codePointAt(0);
271
- const e = processExplicit(end);
272
- const ech = e.codePointAt(0);
273
- // now, process the inner chars, not including explicit
274
- for (let n = sch; n < ech; n++) {
275
- // add inner text
276
- rangeImplicit.add(String.fromCodePoint(n));
277
- }
278
- }
279
- }
280
- }
281
- // analyze ranges
282
- let needCooking = false;
283
- const explicitSet = rangeExplicit.analyze()?.get(util.BadStringType.denormalized);
284
- if (explicitSet) {
285
- this.callbacks.reportMessage(CompilerMessages.Warn_CharClassExplicitDenorm({ lowestCh: explicitSet.values().next().value }));
286
- needCooking = true;
287
- }
288
- else {
289
- // don't analyze the implicit set of THIS range, if explicit is already problematic
290
- const implicitSet = rangeImplicit.analyze()?.get(util.BadStringType.denormalized);
291
- if (implicitSet) {
292
- this.callbacks.reportMessage(CompilerMessages.Hint_CharClassImplicitDenorm({ lowestCh: implicitSet.values().next().value }));
293
- needCooking = true;
294
- }
295
- }
296
- // do we need to fixup the ranges?
297
- // don't do this unless we flagged issues above
298
- if (needCooking) {
299
- // if we get here, there are some ranges with troublesome chars.
300
- // we work around this by escaping all chars
301
- function cookOne(s) {
302
- if (s === '^') {
303
- return s; // syntax
304
- }
305
- else if (s.startsWith('\\u{') || s.startsWith('\\u')) {
306
- return s; // already escaped
307
- }
308
- else {
309
- return util.escapeRegexChar(s);
310
- }
311
- }
312
- return cookedFrom.replaceAll(anyRange, (ignored1, sub) => {
313
- return '[' + sub.replaceAll(rangeRegex, (all, start, end) => {
314
- if (!start && !end) {
315
- // explicit single char
316
- return cookOne(all); // matched one char
317
- }
318
- else {
319
- return cookOne(start) + '-' + cookOne(end);
320
- }
321
- }) + ']';
322
- });
323
- }
324
- return cookedFrom; // no change
325
- }
326
- }
327
- export class TranCompiler extends TransformCompiler {
328
- constructor(source, callbacks) {
329
- super(source, callbacks);
330
- this.type = 'simple';
331
- }
332
- newTran() {
333
- return new Tran();
334
- }
335
- get id() {
336
- return constants.section.tran;
337
- }
338
- }
339
- ;
340
- export class BkspCompiler extends TransformCompiler {
341
- constructor(source, callbacks) {
342
- super(source, callbacks);
343
- this.type = 'backspace';
344
- }
345
- newTran() {
346
- return new Bksp();
347
- }
348
- get id() {
349
- return constants.section.bksp;
350
- }
351
- }
352
- ;
353
- //# debugId=46f9c4ca-55de-57da-8da0-b5a03e066ae1
1
+
2
+ !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="0167fbd9-6012-5d87-8d47-1fec054a30bc")}catch(e){}}();
3
+ import { constants } from "@keymanapp/ldml-keyboard-constants";
4
+ import { KMXPlus, VariableParser, MarkerParser, util } from '@keymanapp/common-types';
5
+ import { SectionCompiler } from "./section-compiler.js";
6
+ var Bksp = KMXPlus.Bksp;
7
+ var Tran = KMXPlus.Tran;
8
+ var TranReorder = KMXPlus.TranReorder;
9
+ var TranTransform = KMXPlus.TranTransform;
10
+ import { verifyValidAndUnique } from "../util/util.js";
11
+ import { CompilerMessages } from "./messages.js";
12
+ import { SubstitutionUse } from "./substitution-tracker.js";
13
+ export class TransformCompiler extends SectionCompiler {
14
+ static validateSubstitutions(keyboard, st) {
15
+ keyboard?.transforms?.forEach(transforms => transforms.transformGroup.forEach(transformGroup => {
16
+ transformGroup.transform?.forEach(({ to, from }) => {
17
+ st.addSetAndStringSubtitution(SubstitutionUse.consume, from);
18
+ st.addSetAndStringSubtitution(SubstitutionUse.emit, to);
19
+ const mapFrom = VariableParser.CAPTURE_SET_REFERENCE.exec(from);
20
+ const mapTo = VariableParser.MAPPED_SET_REFERENCE.exec(to || '');
21
+ if (mapFrom) {
22
+ // add the 'from' as a match
23
+ st.set.add(SubstitutionUse.consume, [mapFrom[1]]);
24
+ }
25
+ if (mapTo) {
26
+ // add the 'from' as a match
27
+ st.set.add(SubstitutionUse.emit, [mapTo[1]]);
28
+ }
29
+ });
30
+ transformGroup.reorder?.forEach(({ before }) => {
31
+ st.addStringSubstitution(SubstitutionUse.consume, before);
32
+ });
33
+ }));
34
+ return true;
35
+ }
36
+ type;
37
+ constructor(source, callbacks) {
38
+ super(source, callbacks);
39
+ }
40
+ validate() {
41
+ const reportMessage = this.callbacks.reportMessage.bind(this.callbacks);
42
+ let valid = true;
43
+ const transforms = this?.keyboard3?.transforms;
44
+ if (transforms) {
45
+ const types = transforms.map(({ type }) => type);
46
+ if (!verifyValidAndUnique(types, types => reportMessage(CompilerMessages.Error_DuplicateTransformsType({ types })), new Set(['simple', 'backspace']), types => reportMessage(CompilerMessages.Error_InvalidTransformsType({ types })))) {
47
+ valid = false;
48
+ }
49
+ // check for mixed groups
50
+ let mixed = false;
51
+ let empty = false;
52
+ transforms.forEach(({ transformGroup }) => transformGroup.forEach((transformGroup) => {
53
+ if (transformGroup.reorder?.length && transformGroup.transform?.length) {
54
+ mixed = true;
55
+ }
56
+ if (!transformGroup.reorder?.length && !transformGroup.transform?.length) {
57
+ empty = true;
58
+ }
59
+ }));
60
+ if (mixed) {
61
+ valid = false;
62
+ reportMessage(CompilerMessages.Error_MixedTransformGroup()); // report this once
63
+ }
64
+ if (empty) {
65
+ valid = false;
66
+ reportMessage(CompilerMessages.Error_EmptyTransformGroup()); // report this once
67
+ }
68
+ // TODO-LDML: linting here should check for identical from, but this involves a double-parse which is ugly
69
+ // TODO-LDML: unicodesets means that either we fully parse them and verify conflicting rules or the linting is imperfect
70
+ }
71
+ return valid;
72
+ }
73
+ /* c8 ignore next 4 */
74
+ /** allocate a new TranBase subclass */
75
+ newTran() {
76
+ throw Error(`Internal Error: newTran() not implemented`);
77
+ }
78
+ compileTransforms(sections, transforms) {
79
+ let result = this.newTran();
80
+ if (transforms?.transformGroup) {
81
+ for (let transformGroup of transforms.transformGroup) {
82
+ const tg = this.compileTransformGroup(sections, transformGroup);
83
+ if (!tg)
84
+ return null; //error
85
+ result.groups.push(tg);
86
+ }
87
+ }
88
+ return result;
89
+ }
90
+ compileTransformGroup(sections, transformGroup) {
91
+ if (transformGroup.reorder.length && transformGroup.transform.length) {
92
+ /* c8 ignore next 2 */
93
+ // should have been caught by validate
94
+ throw Error(`Internal error: transformGroup has both reorder and transform elements.`);
95
+ }
96
+ else if (transformGroup.reorder.length) {
97
+ return this.compileReorderTranGroup(sections, transformGroup.reorder);
98
+ }
99
+ else if (transformGroup.transform.length) {
100
+ return this.compileTransformTranGroup(sections, transformGroup.transform);
101
+ }
102
+ else {
103
+ /* c8 ignore next */
104
+ throw Error(`Internal error: transformGroup has neither reorder nor transform elements.`);
105
+ }
106
+ }
107
+ compileTransformTranGroup(sections, transforms) {
108
+ const result = {
109
+ type: constants.tran_group_type_transform,
110
+ transforms: transforms.map(transform => this.compileTransform(sections, transform)),
111
+ reorders: [],
112
+ };
113
+ if (result.transforms.includes(null))
114
+ return null;
115
+ return result;
116
+ }
117
+ compileTransform(sections, transform) {
118
+ let result = new TranTransform();
119
+ let cookedFrom = transform.from;
120
+ // check for incorrect \uXXXX escapes. Do this before substituting markers or sets.
121
+ cookedFrom = this.checkEscapes(cookedFrom); // check for \uXXXX escapes before normalizing
122
+ cookedFrom = sections.vars.substituteStrings(cookedFrom, sections, true);
123
+ const mapFrom = VariableParser.CAPTURE_SET_REFERENCE.exec(cookedFrom);
124
+ const mapTo = VariableParser.MAPPED_SET_REFERENCE.exec(transform.to || '');
125
+ if (mapFrom && mapTo) { // TODO-LDML: error cases
126
+ result.mapFrom = sections.strs.allocString(mapFrom[1]); // var name
127
+ result.mapTo = sections.strs.allocString(mapTo[1]); // var name
128
+ }
129
+ else {
130
+ result.mapFrom = sections.strs.allocString('');
131
+ result.mapTo = sections.strs.allocString('');
132
+ }
133
+ if (cookedFrom === null)
134
+ return null; // error
135
+ // the set substution will not produce raw markers `\m{...}` but they will already be in sentinel form.
136
+ cookedFrom = sections.vars.substituteSetRegex(cookedFrom, sections);
137
+ // add in markers. idempotent if no markers.
138
+ cookedFrom = sections.vars.substituteMarkerString(cookedFrom, true);
139
+ // unescape from \u{} form to plain, or in some cases \uXXXX / \UXXXXXXXX for core
140
+ cookedFrom = util.unescapeStringToRegex(cookedFrom);
141
+ // check for denormalized ranges
142
+ cookedFrom = this.checkRanges(cookedFrom); // check before normalizing
143
+ if (!sections?.meta?.normalizationDisabled) {
144
+ // nfd here.
145
+ cookedFrom = MarkerParser.nfd_markers(cookedFrom, true);
146
+ }
147
+ // perform regex validation
148
+ if (!this.isValidRegex(cookedFrom, transform.from)) {
149
+ return null;
150
+ }
151
+ // cookedFrom is cooked above, since there's some special treatment
152
+ result.from = sections.strs.allocString(cookedFrom, {
153
+ unescape: false,
154
+ }, sections);
155
+ // 'to' is handled via allocString
156
+ result.to = sections.strs.allocString(transform.to, {
157
+ stringVariables: true,
158
+ markers: true,
159
+ unescape: true,
160
+ nfd: true,
161
+ }, sections);
162
+ return result;
163
+ }
164
+ /**
165
+ * Validate the final regex
166
+ * @param cookedFrom the regex to use, missing the trailing '$'
167
+ * @param from the original from - for error reporting
168
+ * @returns true if OK
169
+ */
170
+ isValidRegex(cookedFrom, from) {
171
+ // check for any unescaped dollar sign here
172
+ if (/(?<!\\)(?:\\\\)*\$/.test(cookedFrom)) {
173
+ this.callbacks.reportMessage(CompilerMessages.Error_IllegalTransformDollarsign({ from }));
174
+ return false;
175
+ }
176
+ // Verify that the regex is syntactically valid
177
+ try {
178
+ const rg = new RegExp(cookedFrom + '$', 'ug');
179
+ // Tests against the regex:
180
+ // does it match an empty string?
181
+ if (rg.test('')) {
182
+ this.callbacks.reportMessage(CompilerMessages.Error_TransformFromMatchesNothing({ from }));
183
+ return false;
184
+ }
185
+ }
186
+ catch (e) {
187
+ // We're exposing the internal regex error message here.
188
+ // In the future, CLDR plans to expose the EBNF for the transform,
189
+ // at which point we would have more precise validation prior to getting to this point.
190
+ this.callbacks.reportMessage(CompilerMessages.Error_UnparseableTransformFrom({ from, message: e.message }));
191
+ return false;
192
+ }
193
+ return true;
194
+ }
195
+ compileReorderTranGroup(sections, reorders) {
196
+ const result = {
197
+ type: constants.tran_group_type_reorder,
198
+ transforms: [],
199
+ reorders: reorders.map(reorder => this.compileReorder(sections, reorder)),
200
+ };
201
+ if (result.reorders.includes(null))
202
+ return null; // if any of the reorders returned null, fail the entire group.
203
+ return result;
204
+ }
205
+ compileReorder(sections, reorder) {
206
+ let result = new TranReorder();
207
+ if (reorder.from && this.checkEscapes(reorder.from) === null) {
208
+ return null; // error'ed
209
+ }
210
+ if (reorder.before && this.checkEscapes(reorder.before) === null) {
211
+ return null; // error'ed
212
+ }
213
+ result.elements = sections.elem.allocElementString(sections, reorder.from, reorder.order, reorder.tertiary, reorder.tertiaryBase, reorder.preBase);
214
+ result.before = sections.elem.allocElementString(sections, reorder.before);
215
+ if (!result.elements || !result.before) {
216
+ return null; // already error'ed
217
+ }
218
+ else {
219
+ return result;
220
+ }
221
+ }
222
+ compile(sections) {
223
+ for (let t of this.keyboard3.transforms) {
224
+ if (t.type == this.type) {
225
+ // compile only the transforms of the correct type
226
+ return this.compileTransforms(sections, t);
227
+ }
228
+ }
229
+ return this.newTran(); // empty: nothing of this type found.
230
+ }
231
+ get dependencies() {
232
+ const defaults = new Set([
233
+ constants.section.elem,
234
+ constants.section.list,
235
+ constants.section.meta,
236
+ constants.section.strs,
237
+ constants.section.uset,
238
+ constants.section.vars,
239
+ ]);
240
+ defaults.delete(this.id);
241
+ return defaults;
242
+ }
243
+ /**
244
+ * Analyze reorders and regexes for \uXXXX escapes.
245
+ * The LDML spec requires \u{XXXX} format.
246
+ * @param cookedFrom the original string
247
+ * @returns the original string, or null if an error was reported
248
+ */
249
+ checkEscapes(cookedFrom) {
250
+ if (!cookedFrom)
251
+ return cookedFrom;
252
+ // should not follow marker prefix, nor marker prefix with range
253
+ const anyQuad = /(?<!\\uffff\\u0008(?:\[[0-9a-fA-F\\u-]*)?)\\u([0-9a-fA-F]{4})/g;
254
+ for (const [, sub] of cookedFrom.matchAll(anyQuad)) {
255
+ const s = util.unescapeOne(sub);
256
+ if (s !== '\uffff' && s !== '\u0008') { // markers
257
+ this.callbacks.reportMessage(CompilerMessages.Error_InvalidQuadEscape({ cp: s.codePointAt(0) }));
258
+ return null; // exit on the first error
259
+ }
260
+ }
261
+ return cookedFrom;
262
+ }
263
+ /**
264
+ * Analyze character classes such as '[a-z]' for denormalized characters.
265
+ * Escapes non-NFD characters as hex escapes.
266
+ * @param cookedFrom input regex string
267
+ * @returns updated 'from' string
268
+ */
269
+ checkRanges(cookedFrom) {
270
+ if (!cookedFrom)
271
+ return cookedFrom;
272
+ // extract all of the potential ranges - but don't match any-markers!
273
+ const anyRange = /(?<!\\uffff\\u0008)\[([^\]]+)\]/g;
274
+ const ranges = cookedFrom.matchAll(anyRange);
275
+ if (!ranges)
276
+ return cookedFrom;
277
+ // extract inner members of a range (inside the [])
278
+ const rangeRegex = /(\\u\{[0-9a-fA-F]\}{1,6}|.)-(\\u\{[0-9a-fA-F]\}{1,6}|.)|./g;
279
+ const rangeExplicit = new util.NFDAnalyzer();
280
+ const rangeImplicit = new util.NFDAnalyzer();
281
+ /** process an explicit entry */
282
+ function processExplicit(s) {
283
+ if (s.startsWith('\\u{')) {
284
+ s = util.unescapeString(s);
285
+ }
286
+ rangeExplicit.add(s);
287
+ return s;
288
+ }
289
+ for (const [, sub] of ranges) {
290
+ const subRanges = sub.matchAll(rangeRegex);
291
+ for (const [all, start, end] of subRanges) {
292
+ if (!start && !end) {
293
+ // explicit single char
294
+ processExplicit(all); // matched one char
295
+ }
296
+ else {
297
+ // start-end range - get explicit start and end chars
298
+ const s = processExplicit(start);
299
+ const sch = s.codePointAt(0);
300
+ const e = processExplicit(end);
301
+ const ech = e.codePointAt(0);
302
+ // now, process the inner chars, not including explicit
303
+ for (let n = sch; n < ech; n++) {
304
+ // add inner text
305
+ rangeImplicit.add(String.fromCodePoint(n));
306
+ }
307
+ }
308
+ }
309
+ }
310
+ // analyze ranges
311
+ let needCooking = false;
312
+ const explicitSet = rangeExplicit.analyze()?.get(util.BadStringType.denormalized);
313
+ if (explicitSet) {
314
+ this.callbacks.reportMessage(CompilerMessages.Warn_CharClassExplicitDenorm({ lowestCh: explicitSet.values().next().value }));
315
+ needCooking = true;
316
+ }
317
+ else {
318
+ // don't analyze the implicit set of THIS range, if explicit is already problematic
319
+ const implicitSet = rangeImplicit.analyze()?.get(util.BadStringType.denormalized);
320
+ if (implicitSet) {
321
+ this.callbacks.reportMessage(CompilerMessages.Hint_CharClassImplicitDenorm({ lowestCh: implicitSet.values().next().value }));
322
+ needCooking = true;
323
+ }
324
+ }
325
+ // do we need to fixup the ranges?
326
+ // don't do this unless we flagged issues above
327
+ if (needCooking) {
328
+ // if we get here, there are some ranges with troublesome chars.
329
+ // we work around this by escaping all chars
330
+ function cookOne(s) {
331
+ if (s === '^') {
332
+ return s; // syntax
333
+ }
334
+ else if (s.startsWith('\\u{') || s.startsWith('\\u')) {
335
+ return s; // already escaped
336
+ }
337
+ else {
338
+ return util.escapeRegexChar(s);
339
+ }
340
+ }
341
+ return cookedFrom.replaceAll(anyRange, (ignored1, sub) => {
342
+ return '[' + sub.replaceAll(rangeRegex, (all, start, end) => {
343
+ if (!start && !end) {
344
+ // explicit single char
345
+ return cookOne(all); // matched one char
346
+ }
347
+ else {
348
+ return cookOne(start) + '-' + cookOne(end);
349
+ }
350
+ }) + ']';
351
+ });
352
+ }
353
+ return cookedFrom; // no change
354
+ }
355
+ }
356
+ export class TranCompiler extends TransformCompiler {
357
+ constructor(source, callbacks) {
358
+ super(source, callbacks);
359
+ this.type = 'simple';
360
+ }
361
+ newTran() {
362
+ return new Tran();
363
+ }
364
+ get id() {
365
+ return constants.section.tran;
366
+ }
367
+ }
368
+ ;
369
+ export class BkspCompiler extends TransformCompiler {
370
+ constructor(source, callbacks) {
371
+ super(source, callbacks);
372
+ this.type = 'backspace';
373
+ }
374
+ newTran() {
375
+ return new Bksp();
376
+ }
377
+ get id() {
378
+ return constants.section.bksp;
379
+ }
380
+ }
381
+ ;
354
382
  //# sourceMappingURL=tran.js.map
383
+ //# debugId=0167fbd9-6012-5d87-8d47-1fec054a30bc