@willwade/aac-processors 0.2.3 → 0.2.4

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 (47) hide show
  1. package/dist/browser/core/treeStructure.js +3 -1
  2. package/dist/browser/metrics.js +2 -0
  3. package/dist/browser/processors/astericsGridProcessor.js +21 -12
  4. package/dist/browser/processors/gridset/helpers.js +3 -3
  5. package/dist/browser/processors/gridsetProcessor.js +16 -8
  6. package/dist/browser/processors/obfProcessor.js +4 -4
  7. package/dist/browser/processors/snap/helpers.js +3 -3
  8. package/dist/browser/processors/snapProcessor.js +2 -2
  9. package/dist/browser/processors/touchchatProcessor.js +6 -6
  10. package/dist/browser/utilities/analytics/metrics/core.js +213 -8
  11. package/dist/browser/utilities/analytics/metrics/vocabulary.js +13 -1
  12. package/dist/browser/utilities/analytics/morphology/engine.js +910 -0
  13. package/dist/browser/utilities/analytics/morphology/grid3VerbsParser.js +455 -0
  14. package/dist/browser/utilities/analytics/morphology/index.js +3 -0
  15. package/dist/browser/utilities/analytics/morphology/types.js +1 -0
  16. package/dist/browser/utilities/analytics/morphology/wordFormGenerator.js +74 -0
  17. package/dist/core/treeStructure.d.ts +17 -1
  18. package/dist/core/treeStructure.js +3 -1
  19. package/dist/index.node.d.ts +2 -0
  20. package/dist/index.node.js +6 -1
  21. package/dist/metrics.d.ts +3 -0
  22. package/dist/metrics.js +5 -1
  23. package/dist/processors/astericsGridProcessor.js +21 -12
  24. package/dist/processors/excelProcessor.js +5 -1
  25. package/dist/processors/gridset/helpers.js +3 -3
  26. package/dist/processors/gridset/imageDebug.js +2 -2
  27. package/dist/processors/gridsetProcessor.js +16 -8
  28. package/dist/processors/obfProcessor.js +4 -4
  29. package/dist/processors/snap/helpers.js +3 -3
  30. package/dist/processors/snapProcessor.js +2 -2
  31. package/dist/processors/touchchatProcessor.js +6 -6
  32. package/dist/utilities/analytics/metrics/core.d.ts +14 -0
  33. package/dist/utilities/analytics/metrics/core.js +213 -8
  34. package/dist/utilities/analytics/metrics/types.d.ts +18 -3
  35. package/dist/utilities/analytics/metrics/vocabulary.d.ts +3 -0
  36. package/dist/utilities/analytics/metrics/vocabulary.js +13 -1
  37. package/dist/utilities/analytics/morphology/engine.d.ts +30 -0
  38. package/dist/utilities/analytics/morphology/engine.js +914 -0
  39. package/dist/utilities/analytics/morphology/grid3VerbsParser.d.ts +36 -0
  40. package/dist/utilities/analytics/morphology/grid3VerbsParser.js +485 -0
  41. package/dist/utilities/analytics/morphology/index.d.ts +5 -0
  42. package/dist/utilities/analytics/morphology/index.js +9 -0
  43. package/dist/utilities/analytics/morphology/types.d.ts +40 -0
  44. package/dist/utilities/analytics/morphology/types.js +2 -0
  45. package/dist/utilities/analytics/morphology/wordFormGenerator.d.ts +10 -0
  46. package/dist/utilities/analytics/morphology/wordFormGenerator.js +78 -0
  47. package/package.json +12 -11
@@ -0,0 +1,914 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MorphologyEngine = void 0;
4
+ class MorphologyEngine {
5
+ constructor(ruleSetOrLocale) {
6
+ this.cache = new Map();
7
+ if (typeof ruleSetOrLocale === 'string') {
8
+ this.ruleSet = this.loadBundled(ruleSetOrLocale);
9
+ }
10
+ else {
11
+ this.ruleSet = ruleSetOrLocale;
12
+ }
13
+ }
14
+ static fromGrid3Verbs(verbForms) {
15
+ const engine = new MorphologyEngine({
16
+ locale: verbForms.locale,
17
+ version: 1,
18
+ irregular: {},
19
+ regular: {},
20
+ });
21
+ engine.grid3Verbs = verbForms.verbs;
22
+ return engine;
23
+ }
24
+ get locale() {
25
+ return this.ruleSet.locale;
26
+ }
27
+ inflect(base, pos) {
28
+ const key = `${base.toLowerCase()}|${pos}`;
29
+ const cached = this.cache.get(key);
30
+ if (cached)
31
+ return cached;
32
+ if (this.grid3Verbs) {
33
+ const forms = this.grid3Verbs.get(base) || this.grid3Verbs.get(base.toLowerCase());
34
+ if (forms) {
35
+ this.cache.set(key, forms);
36
+ return forms;
37
+ }
38
+ }
39
+ const forms = this.computeForms(base, pos);
40
+ this.cache.set(key, forms);
41
+ return forms;
42
+ }
43
+ isFormOf(word, base, pos) {
44
+ const forms = this.inflect(base, pos);
45
+ const lower = word.toLowerCase();
46
+ return forms.some((f) => f.toLowerCase() === lower);
47
+ }
48
+ expandVocabulary(buttons) {
49
+ const result = new Map();
50
+ for (const btn of buttons) {
51
+ const pos = btn.pos;
52
+ if (!pos || pos === 'Unknown' || pos === 'Ignore')
53
+ continue;
54
+ const forms = this.inflect(btn.label, pos);
55
+ if (forms.length > 0) {
56
+ result.set(btn.label, forms);
57
+ }
58
+ }
59
+ return result;
60
+ }
61
+ inflectWithSlots(base, pos) {
62
+ const lower = base.toLowerCase();
63
+ const result = [];
64
+ const seen = new Set();
65
+ const irregular = this.ruleSet.irregular[pos]?.[lower];
66
+ const regularSlots = this.ruleSet.regular[pos];
67
+ if (irregular) {
68
+ for (const [slot, value] of Object.entries(irregular)) {
69
+ if (slot === 'extra' && Array.isArray(value)) {
70
+ value.forEach((v) => {
71
+ if (!seen.has(v)) {
72
+ seen.add(v);
73
+ result.push({ slot, form: v });
74
+ }
75
+ });
76
+ }
77
+ else if (Array.isArray(value)) {
78
+ value.forEach((v) => {
79
+ if (!seen.has(v)) {
80
+ seen.add(v);
81
+ result.push({ slot, form: v });
82
+ }
83
+ });
84
+ }
85
+ else {
86
+ if (!seen.has(value)) {
87
+ seen.add(value);
88
+ result.push({ slot, form: value });
89
+ }
90
+ }
91
+ }
92
+ }
93
+ if (!regularSlots)
94
+ return result;
95
+ for (const [slot, rulesOrAlias] of Object.entries(regularSlots)) {
96
+ if (irregular && irregular[slot] !== undefined)
97
+ continue;
98
+ let rules;
99
+ if (typeof rulesOrAlias === 'string') {
100
+ const aliased = regularSlots[rulesOrAlias];
101
+ if (!aliased || typeof aliased === 'string')
102
+ continue;
103
+ rules = aliased;
104
+ }
105
+ else {
106
+ rules = rulesOrAlias;
107
+ }
108
+ const form = this.applyRules(lower, rules);
109
+ if (form && form !== lower && !seen.has(form)) {
110
+ seen.add(form);
111
+ result.push({ slot, form });
112
+ }
113
+ }
114
+ return result;
115
+ }
116
+ computeForms(base, pos) {
117
+ const lower = base.toLowerCase();
118
+ const forms = new Set();
119
+ const irregular = this.ruleSet.irregular[pos]?.[lower];
120
+ const regularSlots = this.ruleSet.regular[pos];
121
+ if (irregular) {
122
+ for (const [slot, value] of Object.entries(irregular)) {
123
+ if (slot === 'extra' && Array.isArray(value)) {
124
+ value.forEach((v) => forms.add(v));
125
+ }
126
+ else if (Array.isArray(value)) {
127
+ value.forEach((v) => forms.add(v));
128
+ }
129
+ else {
130
+ forms.add(value);
131
+ }
132
+ }
133
+ }
134
+ if (!regularSlots) {
135
+ return Array.from(forms);
136
+ }
137
+ for (const [slot, rulesOrAlias] of Object.entries(regularSlots)) {
138
+ if (irregular && irregular[slot] !== undefined)
139
+ continue;
140
+ let rules;
141
+ if (typeof rulesOrAlias === 'string') {
142
+ const aliased = regularSlots[rulesOrAlias];
143
+ if (!aliased || typeof aliased === 'string')
144
+ continue;
145
+ rules = aliased;
146
+ }
147
+ else {
148
+ rules = rulesOrAlias;
149
+ }
150
+ const form = this.applyRules(lower, rules);
151
+ if (form && form !== lower) {
152
+ forms.add(form);
153
+ }
154
+ }
155
+ return Array.from(forms);
156
+ }
157
+ applyRules(word, rules) {
158
+ for (const rule of rules) {
159
+ const regex = new RegExp(rule.match, 'i');
160
+ if (regex.test(word)) {
161
+ return word.replace(new RegExp(rule.match, 'i'), rule.replace);
162
+ }
163
+ }
164
+ return undefined;
165
+ }
166
+ /**
167
+ * Infer the most likely POS for a word by checking the irregular tables.
168
+ * Returns the POS if found in any irregular table, or null if not found.
169
+ * Priority: Verb > Noun > Adjective > Pronoun
170
+ */
171
+ inferPOS(word) {
172
+ const lower = word.toLowerCase();
173
+ for (const pos of ['Verb', 'Noun', 'Adjective', 'Pronoun']) {
174
+ if (this.ruleSet.irregular[pos]?.[lower]) {
175
+ return pos;
176
+ }
177
+ }
178
+ return null;
179
+ }
180
+ loadBundled(locale) {
181
+ const normalized = locale.toLowerCase().replace('_', '-');
182
+ switch (normalized) {
183
+ case 'en-gb':
184
+ case 'en-us':
185
+ case 'en-au':
186
+ case 'en-ca':
187
+ case 'en-nz':
188
+ case 'en-za':
189
+ case 'en':
190
+ return builtinEn();
191
+ default:
192
+ return { locale, version: 1, irregular: {}, regular: {} };
193
+ }
194
+ }
195
+ }
196
+ exports.MorphologyEngine = MorphologyEngine;
197
+ function builtinEn() {
198
+ return {
199
+ locale: 'en-gb',
200
+ version: 1,
201
+ irregular: {
202
+ Verb: {
203
+ be: {
204
+ '3sg': 'is',
205
+ past: 'was',
206
+ pastPart: 'been',
207
+ presPart: 'being',
208
+ extra: ['am', 'are', 'were'],
209
+ },
210
+ have: {
211
+ '3sg': 'has',
212
+ past: 'had',
213
+ pastPart: 'had',
214
+ presPart: 'having',
215
+ },
216
+ do: { '3sg': 'does', past: 'did', pastPart: 'done', presPart: 'doing' },
217
+ go: {
218
+ '3sg': 'goes',
219
+ past: 'went',
220
+ pastPart: 'gone',
221
+ presPart: 'going',
222
+ },
223
+ say: {
224
+ '3sg': 'says',
225
+ past: 'said',
226
+ pastPart: 'said',
227
+ presPart: 'saying',
228
+ },
229
+ get: {
230
+ '3sg': 'gets',
231
+ past: 'got',
232
+ pastPart: 'got',
233
+ presPart: 'getting',
234
+ },
235
+ make: {
236
+ '3sg': 'makes',
237
+ past: 'made',
238
+ pastPart: 'made',
239
+ presPart: 'making',
240
+ },
241
+ come: {
242
+ '3sg': 'comes',
243
+ past: 'came',
244
+ pastPart: 'come',
245
+ presPart: 'coming',
246
+ },
247
+ take: {
248
+ '3sg': 'takes',
249
+ past: 'took',
250
+ pastPart: 'taken',
251
+ presPart: 'taking',
252
+ },
253
+ know: {
254
+ '3sg': 'knows',
255
+ past: 'knew',
256
+ pastPart: 'known',
257
+ presPart: 'knowing',
258
+ },
259
+ think: {
260
+ '3sg': 'thinks',
261
+ past: 'thought',
262
+ pastPart: 'thought',
263
+ presPart: 'thinking',
264
+ },
265
+ see: {
266
+ '3sg': 'sees',
267
+ past: 'saw',
268
+ pastPart: 'seen',
269
+ presPart: 'seeing',
270
+ },
271
+ give: {
272
+ '3sg': 'gives',
273
+ past: 'gave',
274
+ pastPart: 'given',
275
+ presPart: 'giving',
276
+ },
277
+ find: {
278
+ '3sg': 'finds',
279
+ past: 'found',
280
+ pastPart: 'found',
281
+ presPart: 'finding',
282
+ },
283
+ tell: {
284
+ '3sg': 'tells',
285
+ past: 'told',
286
+ pastPart: 'told',
287
+ presPart: 'telling',
288
+ },
289
+ feel: {
290
+ '3sg': 'feels',
291
+ past: 'felt',
292
+ pastPart: 'felt',
293
+ presPart: 'feeling',
294
+ },
295
+ run: {
296
+ '3sg': 'runs',
297
+ past: 'ran',
298
+ pastPart: 'run',
299
+ presPart: 'running',
300
+ },
301
+ fly: {
302
+ '3sg': 'flies',
303
+ past: 'flew',
304
+ pastPart: 'flown',
305
+ presPart: 'flying',
306
+ },
307
+ try: {
308
+ '3sg': 'tries',
309
+ past: 'tried',
310
+ pastPart: 'tried',
311
+ presPart: 'trying',
312
+ },
313
+ leave: {
314
+ '3sg': 'leaves',
315
+ past: 'left',
316
+ pastPart: 'left',
317
+ presPart: 'leaving',
318
+ },
319
+ call: {
320
+ '3sg': 'calls',
321
+ past: 'called',
322
+ pastPart: 'called',
323
+ presPart: 'calling',
324
+ },
325
+ ask: {
326
+ '3sg': 'asks',
327
+ past: 'asked',
328
+ pastPart: 'asked',
329
+ presPart: 'asking',
330
+ },
331
+ put: {
332
+ '3sg': 'puts',
333
+ past: 'put',
334
+ pastPart: 'put',
335
+ presPart: 'putting',
336
+ },
337
+ read: {
338
+ '3sg': 'reads',
339
+ past: 'read',
340
+ pastPart: 'read',
341
+ presPart: 'reading',
342
+ },
343
+ eat: {
344
+ '3sg': 'eats',
345
+ past: 'ate',
346
+ pastPart: 'eaten',
347
+ presPart: 'eating',
348
+ },
349
+ drink: {
350
+ '3sg': 'drinks',
351
+ past: 'drank',
352
+ pastPart: 'drunk',
353
+ presPart: 'drinking',
354
+ },
355
+ sleep: {
356
+ '3sg': 'sleeps',
357
+ past: 'slept',
358
+ pastPart: 'slept',
359
+ presPart: 'sleeping',
360
+ },
361
+ speak: {
362
+ '3sg': 'speaks',
363
+ past: 'spoke',
364
+ pastPart: 'spoken',
365
+ presPart: 'speaking',
366
+ },
367
+ write: {
368
+ '3sg': 'writes',
369
+ past: 'wrote',
370
+ pastPart: 'written',
371
+ presPart: 'writing',
372
+ },
373
+ sit: {
374
+ '3sg': 'sits',
375
+ past: 'sat',
376
+ pastPart: 'sat',
377
+ presPart: 'sitting',
378
+ },
379
+ stand: {
380
+ '3sg': 'stands',
381
+ past: 'stood',
382
+ pastPart: 'stood',
383
+ presPart: 'standing',
384
+ },
385
+ fall: {
386
+ '3sg': 'falls',
387
+ past: 'fell',
388
+ pastPart: 'fallen',
389
+ presPart: 'falling',
390
+ },
391
+ hold: {
392
+ '3sg': 'holds',
393
+ past: 'held',
394
+ pastPart: 'held',
395
+ presPart: 'holding',
396
+ },
397
+ keep: {
398
+ '3sg': 'keeps',
399
+ past: 'kept',
400
+ pastPart: 'kept',
401
+ presPart: 'keeping',
402
+ },
403
+ buy: {
404
+ '3sg': 'buys',
405
+ past: 'bought',
406
+ pastPart: 'bought',
407
+ presPart: 'buying',
408
+ },
409
+ bring: {
410
+ '3sg': 'brings',
411
+ past: 'brought',
412
+ pastPart: 'brought',
413
+ presPart: 'bringing',
414
+ },
415
+ catch: {
416
+ '3sg': 'catches',
417
+ past: 'caught',
418
+ pastPart: 'caught',
419
+ presPart: 'catching',
420
+ },
421
+ teach: {
422
+ '3sg': 'teaches',
423
+ past: 'taught',
424
+ pastPart: 'taught',
425
+ presPart: 'teaching',
426
+ },
427
+ fight: {
428
+ '3sg': 'fights',
429
+ past: 'fought',
430
+ pastPart: 'fought',
431
+ presPart: 'fighting',
432
+ },
433
+ swim: {
434
+ '3sg': 'swims',
435
+ past: 'swam',
436
+ pastPart: 'swum',
437
+ presPart: 'swimming',
438
+ },
439
+ sing: {
440
+ '3sg': 'sings',
441
+ past: 'sang',
442
+ pastPart: 'sung',
443
+ presPart: 'singing',
444
+ },
445
+ draw: {
446
+ '3sg': 'draws',
447
+ past: 'drew',
448
+ pastPart: 'drawn',
449
+ presPart: 'drawing',
450
+ },
451
+ drive: {
452
+ '3sg': 'drives',
453
+ past: 'drove',
454
+ pastPart: 'driven',
455
+ presPart: 'driving',
456
+ },
457
+ ride: {
458
+ '3sg': 'rides',
459
+ past: 'rode',
460
+ pastPart: 'ridden',
461
+ presPart: 'riding',
462
+ },
463
+ grow: {
464
+ '3sg': 'grows',
465
+ past: 'grew',
466
+ pastPart: 'grown',
467
+ presPart: 'growing',
468
+ },
469
+ throw: {
470
+ '3sg': 'throws',
471
+ past: 'threw',
472
+ pastPart: 'thrown',
473
+ presPart: 'throwing',
474
+ },
475
+ break: {
476
+ '3sg': 'breaks',
477
+ past: 'broke',
478
+ pastPart: 'broken',
479
+ presPart: 'breaking',
480
+ },
481
+ wake: {
482
+ '3sg': 'wakes',
483
+ past: 'woke',
484
+ pastPart: 'woken',
485
+ presPart: 'waking',
486
+ },
487
+ wear: {
488
+ '3sg': 'wears',
489
+ past: 'wore',
490
+ pastPart: 'worn',
491
+ presPart: 'wearing',
492
+ },
493
+ win: {
494
+ '3sg': 'wins',
495
+ past: 'won',
496
+ pastPart: 'won',
497
+ presPart: 'winning',
498
+ },
499
+ choose: {
500
+ '3sg': 'chooses',
501
+ past: 'chose',
502
+ pastPart: 'chosen',
503
+ presPart: 'choosing',
504
+ },
505
+ hide: {
506
+ '3sg': 'hides',
507
+ past: 'hid',
508
+ pastPart: 'hidden',
509
+ presPart: 'hiding',
510
+ },
511
+ steal: {
512
+ '3sg': 'steals',
513
+ past: 'stole',
514
+ pastPart: 'stolen',
515
+ presPart: 'stealing',
516
+ },
517
+ begin: {
518
+ '3sg': 'begins',
519
+ past: 'began',
520
+ pastPart: 'begun',
521
+ presPart: 'beginning',
522
+ },
523
+ ring: {
524
+ '3sg': 'rings',
525
+ past: 'rang',
526
+ pastPart: 'rung',
527
+ presPart: 'ringing',
528
+ },
529
+ swing: {
530
+ '3sg': 'swings',
531
+ past: 'swung',
532
+ pastPart: 'swung',
533
+ presPart: 'swinging',
534
+ },
535
+ blow: {
536
+ '3sg': 'blows',
537
+ past: 'blew',
538
+ pastPart: 'blown',
539
+ presPart: 'blowing',
540
+ },
541
+ show: {
542
+ '3sg': 'shows',
543
+ past: 'showed',
544
+ pastPart: 'shown',
545
+ presPart: 'showing',
546
+ },
547
+ shut: {
548
+ '3sg': 'shuts',
549
+ past: 'shut',
550
+ pastPart: 'shut',
551
+ presPart: 'shutting',
552
+ },
553
+ cut: {
554
+ '3sg': 'cuts',
555
+ past: 'cut',
556
+ pastPart: 'cut',
557
+ presPart: 'cutting',
558
+ },
559
+ hit: {
560
+ '3sg': 'hits',
561
+ past: 'hit',
562
+ pastPart: 'hit',
563
+ presPart: 'hitting',
564
+ },
565
+ hurt: {
566
+ '3sg': 'hurts',
567
+ past: 'hurt',
568
+ pastPart: 'hurt',
569
+ presPart: 'hurting',
570
+ },
571
+ let: {
572
+ '3sg': 'lets',
573
+ past: 'let',
574
+ pastPart: 'let',
575
+ presPart: 'letting',
576
+ },
577
+ set: {
578
+ '3sg': 'sets',
579
+ past: 'set',
580
+ pastPart: 'set',
581
+ presPart: 'setting',
582
+ },
583
+ cost: {
584
+ '3sg': 'costs',
585
+ past: 'cost',
586
+ pastPart: 'cost',
587
+ presPart: 'costing',
588
+ },
589
+ send: {
590
+ '3sg': 'sends',
591
+ past: 'sent',
592
+ pastPart: 'sent',
593
+ presPart: 'sending',
594
+ },
595
+ build: {
596
+ '3sg': 'builds',
597
+ past: 'built',
598
+ pastPart: 'built',
599
+ presPart: 'building',
600
+ },
601
+ spend: {
602
+ '3sg': 'spends',
603
+ past: 'spent',
604
+ pastPart: 'spent',
605
+ presPart: 'spending',
606
+ },
607
+ lend: {
608
+ '3sg': 'lends',
609
+ past: 'lent',
610
+ pastPart: 'lent',
611
+ presPart: 'lending',
612
+ },
613
+ lose: {
614
+ '3sg': 'loses',
615
+ past: 'lost',
616
+ pastPart: 'lost',
617
+ presPart: 'losing',
618
+ },
619
+ mean: {
620
+ '3sg': 'means',
621
+ past: 'meant',
622
+ pastPart: 'meant',
623
+ presPart: 'meaning',
624
+ },
625
+ meet: {
626
+ '3sg': 'meets',
627
+ past: 'met',
628
+ pastPart: 'met',
629
+ presPart: 'meeting',
630
+ },
631
+ pay: {
632
+ '3sg': 'pays',
633
+ past: 'paid',
634
+ pastPart: 'paid',
635
+ presPart: 'paying',
636
+ },
637
+ sell: {
638
+ '3sg': 'sells',
639
+ past: 'sold',
640
+ pastPart: 'sold',
641
+ presPart: 'selling',
642
+ },
643
+ hang: {
644
+ '3sg': 'hangs',
645
+ past: 'hung',
646
+ pastPart: 'hung',
647
+ presPart: 'hanging',
648
+ },
649
+ shine: {
650
+ '3sg': 'shines',
651
+ past: 'shone',
652
+ pastPart: 'shone',
653
+ presPart: 'shining',
654
+ },
655
+ dig: {
656
+ '3sg': 'digs',
657
+ past: 'dug',
658
+ pastPart: 'dug',
659
+ presPart: 'digging',
660
+ },
661
+ stick: {
662
+ '3sg': 'sticks',
663
+ past: 'stuck',
664
+ pastPart: 'stuck',
665
+ presPart: 'sticking',
666
+ },
667
+ spin: {
668
+ '3sg': 'spins',
669
+ past: 'spun',
670
+ pastPart: 'spun',
671
+ presPart: 'spinning',
672
+ },
673
+ spread: {
674
+ '3sg': 'spreads',
675
+ past: 'spread',
676
+ pastPart: 'spread',
677
+ presPart: 'spreading',
678
+ },
679
+ bite: {
680
+ '3sg': 'bites',
681
+ past: 'bit',
682
+ pastPart: 'bitten',
683
+ presPart: 'biting',
684
+ },
685
+ feed: {
686
+ '3sg': 'feeds',
687
+ past: 'fed',
688
+ pastPart: 'fed',
689
+ presPart: 'feeding',
690
+ },
691
+ lead: {
692
+ '3sg': 'leads',
693
+ past: 'led',
694
+ pastPart: 'led',
695
+ presPart: 'leading',
696
+ },
697
+ light: {
698
+ '3sg': 'lights',
699
+ past: 'lit',
700
+ pastPart: 'lit',
701
+ presPart: 'lighting',
702
+ },
703
+ shoot: {
704
+ '3sg': 'shoots',
705
+ past: 'shot',
706
+ pastPart: 'shot',
707
+ presPart: 'shooting',
708
+ },
709
+ slide: {
710
+ '3sg': 'slides',
711
+ past: 'slid',
712
+ pastPart: 'slid',
713
+ presPart: 'sliding',
714
+ },
715
+ },
716
+ Noun: {
717
+ child: { plural: 'children' },
718
+ person: { plural: 'people' },
719
+ man: { plural: 'men' },
720
+ woman: { plural: 'women' },
721
+ mouse: { plural: 'mice' },
722
+ foot: { plural: 'feet' },
723
+ tooth: { plural: 'teeth' },
724
+ goose: { plural: 'geese' },
725
+ sheep: { plural: 'sheep' },
726
+ fish: { plural: 'fish' },
727
+ deer: { plural: 'deer' },
728
+ ox: { plural: 'oxen' },
729
+ leaf: { plural: 'leaves' },
730
+ loaf: { plural: 'loaves' },
731
+ wolf: { plural: 'wolves' },
732
+ calf: { plural: 'calves' },
733
+ half: { plural: 'halves' },
734
+ knife: { plural: 'knives' },
735
+ life: { plural: 'lives' },
736
+ wife: { plural: 'wives' },
737
+ self: { plural: 'selves' },
738
+ shelf: { plural: 'shelves' },
739
+ elf: { plural: 'elves' },
740
+ thief: { plural: 'thieves' },
741
+ roof: { plural: 'roofs' },
742
+ chief: { plural: 'chiefs' },
743
+ belief: { plural: 'beliefs' },
744
+ proof: { plural: 'proofs' },
745
+ hoof: { plural: 'hooves' },
746
+ scarf: { plural: 'scarves' },
747
+ wharf: { plural: 'wharves' },
748
+ bus: { plural: 'buses' },
749
+ glass: { plural: 'glasses' },
750
+ class: { plural: 'classes' },
751
+ box: { plural: 'boxes' },
752
+ fox: { plural: 'foxes' },
753
+ watch: { plural: 'watches' },
754
+ match: { plural: 'matches' },
755
+ brush: { plural: 'brushes' },
756
+ dish: { plural: 'dishes' },
757
+ wish: { plural: 'wishes' },
758
+ wash: { plural: 'washes' },
759
+ bush: { plural: 'bushes' },
760
+ push: { plural: 'pushes' },
761
+ potato: { plural: 'potatoes' },
762
+ tomato: { plural: 'tomatoes' },
763
+ hero: { plural: 'heroes' },
764
+ echo: { plural: 'echoes' },
765
+ veto: { plural: 'vetoes' },
766
+ mango: { plural: 'mangoes' },
767
+ mosquito: { plural: 'mosquitoes' },
768
+ tornado: { plural: 'tornadoes' },
769
+ volcano: { plural: 'volcanoes' },
770
+ radio: { plural: 'radios' },
771
+ studio: { plural: 'studios' },
772
+ video: { plural: 'videos' },
773
+ piano: { plural: 'pianos' },
774
+ photo: { plural: 'photos' },
775
+ zoo: { plural: 'zoos' },
776
+ bamboo: { plural: 'bamboos' },
777
+ embryo: { plural: 'embryos' },
778
+ ratio: { plural: 'ratios' },
779
+ scenario: { plural: 'scenarios' },
780
+ analysis: { plural: 'analyses' },
781
+ basis: { plural: 'bases' },
782
+ crisis: { plural: 'crises' },
783
+ diagnosis: { plural: 'diagnoses' },
784
+ hypothesis: { plural: 'hypotheses' },
785
+ oasis: { plural: 'oases' },
786
+ parenthesis: { plural: 'parentheses' },
787
+ synthesis: { plural: 'syntheses' },
788
+ thesis: { plural: 'theses' },
789
+ phenomenon: { plural: 'phenomena' },
790
+ criterion: { plural: 'criteria' },
791
+ datum: { plural: 'data' },
792
+ medium: { plural: 'media' },
793
+ curriculum: { plural: 'curricula' },
794
+ bacterium: { plural: 'bacteria' },
795
+ stimulus: { plural: 'stimuli' },
796
+ syllabus: { plural: 'syllabi' },
797
+ focus: { plural: 'foci' },
798
+ nucleus: { plural: 'nuclei' },
799
+ fungus: { plural: 'fungi' },
800
+ cactus: { plural: 'cacti' },
801
+ appendix: { plural: 'appendices' },
802
+ index: { plural: 'indices' },
803
+ matrix: { plural: 'matrices' },
804
+ vertex: { plural: 'vertices' },
805
+ },
806
+ Adjective: {
807
+ good: { comparative: 'better', superlative: 'best' },
808
+ bad: { comparative: 'worse', superlative: 'worst' },
809
+ far: { comparative: 'farther', superlative: 'farthest' },
810
+ little: { comparative: 'less', superlative: 'least' },
811
+ much: { comparative: 'more', superlative: 'most' },
812
+ many: { comparative: 'more', superlative: 'most' },
813
+ well: { comparative: 'better', superlative: 'best' },
814
+ old: {
815
+ comparative: 'older',
816
+ superlative: 'oldest',
817
+ extra: ['elder', 'eldest'],
818
+ },
819
+ late: {
820
+ comparative: 'later',
821
+ superlative: 'latest',
822
+ extra: ['latter', 'last'],
823
+ },
824
+ },
825
+ Pronoun: {
826
+ i: {
827
+ objective: 'me',
828
+ possessive: 'my',
829
+ possessivePronoun: 'mine',
830
+ },
831
+ you: {
832
+ objective: 'you',
833
+ possessive: 'your',
834
+ possessivePronoun: 'yours',
835
+ },
836
+ he: {
837
+ objective: 'him',
838
+ possessive: 'his',
839
+ possessivePronoun: 'his',
840
+ },
841
+ she: {
842
+ objective: 'her',
843
+ possessive: 'her',
844
+ possessivePronoun: 'hers',
845
+ },
846
+ it: {
847
+ objective: 'it',
848
+ possessive: 'its',
849
+ },
850
+ we: {
851
+ objective: 'us',
852
+ possessive: 'our',
853
+ possessivePronoun: 'ours',
854
+ },
855
+ they: {
856
+ objective: 'them',
857
+ possessive: 'their',
858
+ possessivePronoun: 'theirs',
859
+ },
860
+ mine: { extra: ['my'] },
861
+ yours: { extra: ['your'] },
862
+ his: { extra: ['him'] },
863
+ hers: { extra: ['her'] },
864
+ ours: { extra: ['our'] },
865
+ theirs: { extra: ['their'] },
866
+ },
867
+ },
868
+ regular: {
869
+ Verb: {
870
+ '3sg': [
871
+ { match: '(ss|sh|ch|x|z|o)$', replace: '$1es' },
872
+ { match: '([^aeiou])y$', replace: '$1ies' },
873
+ { match: '$', replace: 's' },
874
+ ],
875
+ past: [
876
+ { match: '([^aeiou])y$', replace: '$1ied' },
877
+ { match: '([^aeiou][aeiou])([^aeiouwxy])$', replace: '$1$2$2ed' },
878
+ { match: '(.*)e$', replace: '$1ed' },
879
+ { match: '$', replace: 'ed' },
880
+ ],
881
+ pastPart: 'past',
882
+ presPart: [
883
+ { match: 'ie$', replace: 'ying' },
884
+ { match: '(.*)e$', replace: '$1ing' },
885
+ { match: '([^aeiou][aeiou])([^aeiouwxy])$', replace: '$1$2$2ing' },
886
+ { match: '$', replace: 'ing' },
887
+ ],
888
+ },
889
+ Noun: {
890
+ plural: [
891
+ { match: '(ss|sh|ch|x|z)$', replace: '$1es' },
892
+ { match: '([^aeiou])y$', replace: '$1ies' },
893
+ { match: 'fe$', replace: 'ves' },
894
+ { match: 'f$', replace: 'ves' },
895
+ { match: '$', replace: 's' },
896
+ ],
897
+ },
898
+ Adjective: {
899
+ comparative: [
900
+ { match: 'e$', replace: 'r' },
901
+ { match: '([^aeiou])y$', replace: '$1ier' },
902
+ { match: '([^aeiou][aeiou])([^aeiouwxy])$', replace: '$1$2$2er' },
903
+ { match: '$', replace: 'er' },
904
+ ],
905
+ superlative: [
906
+ { match: 'e$', replace: 'st' },
907
+ { match: '([^aeiou])y$', replace: '$1iest' },
908
+ { match: '([^aeiou][aeiou])([^aeiouwxy])$', replace: '$1$2$2est' },
909
+ { match: '$', replace: 'est' },
910
+ ],
911
+ },
912
+ },
913
+ };
914
+ }