@willwade/aac-processors 0.2.2 → 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.
- package/README.md +3 -1
- package/dist/browser/core/treeStructure.js +3 -1
- package/dist/browser/metrics.js +2 -0
- package/dist/browser/processors/astericsGridProcessor.js +21 -12
- package/dist/browser/processors/gridset/helpers.js +3 -3
- package/dist/browser/processors/gridsetProcessor.js +16 -8
- package/dist/browser/processors/obfProcessor.js +4 -4
- package/dist/browser/processors/snap/helpers.js +3 -3
- package/dist/browser/processors/snapProcessor.js +2 -2
- package/dist/browser/processors/touchchatProcessor.js +6 -6
- package/dist/browser/utilities/analytics/metrics/core.js +213 -8
- package/dist/browser/utilities/analytics/metrics/vocabulary.js +13 -1
- package/dist/browser/utilities/analytics/morphology/engine.js +910 -0
- package/dist/browser/utilities/analytics/morphology/grid3VerbsParser.js +455 -0
- package/dist/browser/utilities/analytics/morphology/index.js +3 -0
- package/dist/browser/utilities/analytics/morphology/types.js +1 -0
- package/dist/browser/utilities/analytics/morphology/wordFormGenerator.js +74 -0
- package/dist/browser/utils/sqlite.js +10 -6
- package/dist/core/treeStructure.d.ts +17 -1
- package/dist/core/treeStructure.js +3 -1
- package/dist/index.node.d.ts +2 -0
- package/dist/index.node.js +6 -1
- package/dist/metrics.d.ts +3 -0
- package/dist/metrics.js +5 -1
- package/dist/processors/astericsGridProcessor.js +21 -12
- package/dist/processors/excelProcessor.js +5 -1
- package/dist/processors/gridset/helpers.js +3 -3
- package/dist/processors/gridset/imageDebug.js +2 -2
- package/dist/processors/gridsetProcessor.js +16 -8
- package/dist/processors/obfProcessor.js +4 -4
- package/dist/processors/snap/helpers.js +3 -3
- package/dist/processors/snapProcessor.js +2 -2
- package/dist/processors/touchchatProcessor.js +6 -6
- package/dist/utilities/analytics/metrics/core.d.ts +14 -0
- package/dist/utilities/analytics/metrics/core.js +213 -8
- package/dist/utilities/analytics/metrics/types.d.ts +18 -3
- package/dist/utilities/analytics/metrics/vocabulary.d.ts +3 -0
- package/dist/utilities/analytics/metrics/vocabulary.js +13 -1
- package/dist/utilities/analytics/morphology/engine.d.ts +30 -0
- package/dist/utilities/analytics/morphology/engine.js +914 -0
- package/dist/utilities/analytics/morphology/grid3VerbsParser.d.ts +36 -0
- package/dist/utilities/analytics/morphology/grid3VerbsParser.js +485 -0
- package/dist/utilities/analytics/morphology/index.d.ts +5 -0
- package/dist/utilities/analytics/morphology/index.js +9 -0
- package/dist/utilities/analytics/morphology/types.d.ts +40 -0
- package/dist/utilities/analytics/morphology/types.js +2 -0
- package/dist/utilities/analytics/morphology/wordFormGenerator.d.ts +10 -0
- package/dist/utilities/analytics/morphology/wordFormGenerator.js +78 -0
- package/dist/utils/sqlite.js +10 -29
- package/package.json +13 -12
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import { XMLParser } from 'fast-xml-parser';
|
|
2
|
+
import AdmZip from 'adm-zip';
|
|
3
|
+
import { join, dirname, basename } from 'path';
|
|
4
|
+
export class Grid3VerbsParser {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.parser = new XMLParser({
|
|
7
|
+
ignoreAttributes: false,
|
|
8
|
+
ignoreDeclaration: true,
|
|
9
|
+
textNodeName: '#text',
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
parseXml(xmlContent, locale) {
|
|
13
|
+
const data = this.parser.parse(xmlContent);
|
|
14
|
+
const verbdata = data.verbdata || data.Verbdata;
|
|
15
|
+
if (!verbdata) {
|
|
16
|
+
return { locale: locale || 'unknown', verbs: new Map() };
|
|
17
|
+
}
|
|
18
|
+
const detectedLocale = verbdata['@_locale'] || verbdata.locale || locale || 'unknown';
|
|
19
|
+
const ruleSets = this.parseRuleSets(verbdata);
|
|
20
|
+
const verbs = this.parseVerbs(verbdata);
|
|
21
|
+
const result = new Map();
|
|
22
|
+
for (const verb of verbs) {
|
|
23
|
+
const forms = this.generateForms(verb, ruleSets);
|
|
24
|
+
if (forms.length > 0) {
|
|
25
|
+
result.set(verb.root, forms);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return { locale: detectedLocale, verbs: result };
|
|
29
|
+
}
|
|
30
|
+
parseXmlDetailed(xmlContent, locale) {
|
|
31
|
+
const data = this.parser.parse(xmlContent);
|
|
32
|
+
const verbdata = data.verbdata || data.Verbdata;
|
|
33
|
+
if (!verbdata) {
|
|
34
|
+
return { locale: locale || 'unknown', verbs: new Map() };
|
|
35
|
+
}
|
|
36
|
+
const detectedLocale = verbdata['@_locale'] || verbdata.locale || locale || 'unknown';
|
|
37
|
+
const ruleSets = this.parseRuleSets(verbdata);
|
|
38
|
+
const verbs = this.parseVerbs(verbdata);
|
|
39
|
+
const result = new Map();
|
|
40
|
+
for (const verb of verbs) {
|
|
41
|
+
const forms = this.generateFormsDetailed(verb, ruleSets);
|
|
42
|
+
if (forms.length > 0) {
|
|
43
|
+
result.set(verb.root, forms);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return { locale: detectedLocale, verbs: result };
|
|
47
|
+
}
|
|
48
|
+
/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-argument */
|
|
49
|
+
parseXmlFileDetailed(filePath) {
|
|
50
|
+
const fs = require('fs');
|
|
51
|
+
const xml = fs.readFileSync(filePath, 'utf-8');
|
|
52
|
+
return this.parseXmlDetailed(xml);
|
|
53
|
+
}
|
|
54
|
+
/* eslint-enable @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-argument */
|
|
55
|
+
parseZip(zipPath) {
|
|
56
|
+
const zip = new AdmZip(zipPath);
|
|
57
|
+
const entries = zip.getEntries();
|
|
58
|
+
const verbEntry = entries.find((e) => e.entryName.toLowerCase().endsWith('verbs.xml'));
|
|
59
|
+
if (!verbEntry) {
|
|
60
|
+
const locale = basename(dirname(zipPath));
|
|
61
|
+
return { locale, verbs: new Map() };
|
|
62
|
+
}
|
|
63
|
+
const xml = verbEntry.getData().toString('utf-8');
|
|
64
|
+
return this.parseXml(xml);
|
|
65
|
+
}
|
|
66
|
+
parseZipDetailed(zipPath) {
|
|
67
|
+
const zip = new AdmZip(zipPath);
|
|
68
|
+
const entries = zip.getEntries();
|
|
69
|
+
const verbEntry = entries.find((e) => e.entryName.toLowerCase().endsWith('verbs.xml'));
|
|
70
|
+
if (!verbEntry) {
|
|
71
|
+
const locale = basename(dirname(zipPath));
|
|
72
|
+
return { locale, verbs: new Map() };
|
|
73
|
+
}
|
|
74
|
+
const xml = verbEntry.getData().toString('utf-8');
|
|
75
|
+
return this.parseXmlDetailed(xml);
|
|
76
|
+
}
|
|
77
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
78
|
+
async parseLocale(locale, grid3InstallPath) {
|
|
79
|
+
const installPath = grid3InstallPath || this.getDefaultInstallPath();
|
|
80
|
+
if (!installPath) {
|
|
81
|
+
return { locale, verbs: new Map() };
|
|
82
|
+
}
|
|
83
|
+
const zipPath = join(installPath, 'Locale', locale, 'verbs', 'verbs.zip');
|
|
84
|
+
try {
|
|
85
|
+
return this.parseZip(zipPath);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return { locale, verbs: new Map() };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
async parseInstalledLocales(grid3InstallPath) {
|
|
92
|
+
const installPath = grid3InstallPath || this.getDefaultInstallPath();
|
|
93
|
+
const results = new Map();
|
|
94
|
+
if (!installPath)
|
|
95
|
+
return results;
|
|
96
|
+
const fs = await import('fs');
|
|
97
|
+
const localeDir = join(installPath, 'Locale');
|
|
98
|
+
let locales;
|
|
99
|
+
try {
|
|
100
|
+
locales = fs
|
|
101
|
+
.readdirSync(localeDir)
|
|
102
|
+
.filter((d) => fs.statSync(join(localeDir, d)).isDirectory());
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return results;
|
|
106
|
+
}
|
|
107
|
+
for (const locale of locales) {
|
|
108
|
+
const verbsZip = join(localeDir, locale, 'verbs', 'verbs.zip');
|
|
109
|
+
try {
|
|
110
|
+
fs.accessSync(verbsZip, fs.constants.R_OK);
|
|
111
|
+
const forms = this.parseZip(verbsZip);
|
|
112
|
+
results.set(locale, forms);
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
// No verbs.zip for this locale
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return results;
|
|
119
|
+
}
|
|
120
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
121
|
+
parseCustomDirectory(dirPath, detailed = false) {
|
|
122
|
+
/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return */
|
|
123
|
+
const fs = require('fs');
|
|
124
|
+
let locales;
|
|
125
|
+
try {
|
|
126
|
+
locales = fs
|
|
127
|
+
.readdirSync(dirPath)
|
|
128
|
+
.filter((d) => fs.statSync(join(dirPath, d)).isDirectory());
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return new Map();
|
|
132
|
+
}
|
|
133
|
+
if (detailed) {
|
|
134
|
+
const results = new Map();
|
|
135
|
+
for (const locale of locales) {
|
|
136
|
+
const verbsZip = join(dirPath, locale, 'verbs', 'verbs.zip');
|
|
137
|
+
try {
|
|
138
|
+
fs.accessSync(verbsZip, fs.constants.R_OK);
|
|
139
|
+
results.set(locale, this.parseZipDetailed(verbsZip));
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// No verbs.zip for this locale
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return results;
|
|
146
|
+
}
|
|
147
|
+
const results = new Map();
|
|
148
|
+
for (const locale of locales) {
|
|
149
|
+
const verbsZip = join(dirPath, locale, 'verbs', 'verbs.zip');
|
|
150
|
+
try {
|
|
151
|
+
fs.accessSync(verbsZip, fs.constants.R_OK);
|
|
152
|
+
results.set(locale, this.parseZip(verbsZip));
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
// No verbs.zip for this locale
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return results;
|
|
159
|
+
/* eslint-enable @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return */
|
|
160
|
+
}
|
|
161
|
+
getDefaultInstallPath() {
|
|
162
|
+
if (typeof process === 'undefined' || process.platform !== 'win32') {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
const paths = [
|
|
166
|
+
'C:\\Program Files (x86)\\Smartbox\\Grid 3',
|
|
167
|
+
'C:\\Program Files\\Smartbox\\Grid 3',
|
|
168
|
+
];
|
|
169
|
+
try {
|
|
170
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
171
|
+
const fs = require('fs');
|
|
172
|
+
for (const p of paths) {
|
|
173
|
+
if (fs.existsSync(p))
|
|
174
|
+
return p;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Grid 3 not installed
|
|
179
|
+
}
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access */
|
|
183
|
+
parseRuleSets(verbdata) {
|
|
184
|
+
const rulesMap = new Map();
|
|
185
|
+
const rulesList = verbdata.verbruleslist?.verbrules;
|
|
186
|
+
if (!rulesList)
|
|
187
|
+
return rulesMap;
|
|
188
|
+
const rulesArr = Array.isArray(rulesList) ? rulesList : [rulesList];
|
|
189
|
+
for (const rules of rulesArr) {
|
|
190
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
191
|
+
const id = rules.Id || rules.id || `rule_${rulesMap.size}`;
|
|
192
|
+
const placeholders = [];
|
|
193
|
+
const ph = rules.placeholders?.placeholder;
|
|
194
|
+
if (ph) {
|
|
195
|
+
const phArr = Array.isArray(ph) ? ph : [ph];
|
|
196
|
+
for (const p of phArr) {
|
|
197
|
+
const val = typeof p === 'string' ? p : p['#text'] || p;
|
|
198
|
+
if (val)
|
|
199
|
+
placeholders.push(val);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
const participleRules = new Map();
|
|
203
|
+
const pr = rules.participlerules?.participlerule;
|
|
204
|
+
if (pr) {
|
|
205
|
+
const prArr = Array.isArray(pr) ? pr : [pr];
|
|
206
|
+
for (const rule of prArr) {
|
|
207
|
+
const type = rule['@_type'] || rule.type;
|
|
208
|
+
const value = rule['@_value'] || rule.value;
|
|
209
|
+
if (type && value) {
|
|
210
|
+
participleRules.set(type, value);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
const conjugationRules = [];
|
|
215
|
+
const cr = rules.conjugationrules?.conjugationrule;
|
|
216
|
+
if (cr) {
|
|
217
|
+
const crArr = Array.isArray(cr) ? cr : [cr];
|
|
218
|
+
for (const rule of crArr) {
|
|
219
|
+
const value = rule['@_value'] || rule.value;
|
|
220
|
+
if (!value)
|
|
221
|
+
continue;
|
|
222
|
+
const conditions = new Map();
|
|
223
|
+
for (const attr of [
|
|
224
|
+
'time',
|
|
225
|
+
'number',
|
|
226
|
+
'person',
|
|
227
|
+
'aspect',
|
|
228
|
+
'mood',
|
|
229
|
+
'voice',
|
|
230
|
+
'tense',
|
|
231
|
+
'polarity',
|
|
232
|
+
]) {
|
|
233
|
+
const v = rule[`@_${attr}`] || rule[attr];
|
|
234
|
+
if (v && v !== '*') {
|
|
235
|
+
conditions.set(attr, String(v));
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
conjugationRules.push({ value, conditions });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
rulesMap.set(id, { id, placeholders, participleRules, conjugationRules });
|
|
242
|
+
}
|
|
243
|
+
return rulesMap;
|
|
244
|
+
}
|
|
245
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access */
|
|
246
|
+
parseVerbs(verbdata) {
|
|
247
|
+
const verbs = [];
|
|
248
|
+
const verbsData = verbdata.verbs?.verb;
|
|
249
|
+
if (!verbsData)
|
|
250
|
+
return verbs;
|
|
251
|
+
const arr = Array.isArray(verbsData) ? verbsData : [verbsData];
|
|
252
|
+
for (const v of arr) {
|
|
253
|
+
const root = v['@_root'] || v.root;
|
|
254
|
+
if (!root)
|
|
255
|
+
continue;
|
|
256
|
+
const ruleId = v['@_ruleid'] || v.ruleid || undefined;
|
|
257
|
+
const placeholderValues = new Map();
|
|
258
|
+
const rphv = v.ruleplaceholdervalues?.ruleplaceholdervalue;
|
|
259
|
+
if (rphv) {
|
|
260
|
+
const rphvArr = Array.isArray(rphv) ? rphv : [rphv];
|
|
261
|
+
for (const ph of rphvArr) {
|
|
262
|
+
const placeholder = ph['@_placeholder'] || ph.placeholder;
|
|
263
|
+
const value = ph['@_value'] || ph.value;
|
|
264
|
+
if (placeholder && value) {
|
|
265
|
+
placeholderValues.set(placeholder, value);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const participleOverrides = new Map();
|
|
270
|
+
const parts = v.participles?.participle;
|
|
271
|
+
if (parts) {
|
|
272
|
+
const partsArr = Array.isArray(parts) ? parts : [parts];
|
|
273
|
+
for (const p of partsArr) {
|
|
274
|
+
const type = p['@_type'] || p.type;
|
|
275
|
+
const value = p['@_value'] || p.value;
|
|
276
|
+
if (type && value) {
|
|
277
|
+
participleOverrides.set(type, value);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const conjugationOverrides = [];
|
|
282
|
+
const conjs = v.conjugations?.conjugation;
|
|
283
|
+
if (conjs) {
|
|
284
|
+
const conjArr = Array.isArray(conjs) ? conjs : [conjs];
|
|
285
|
+
for (const c of conjArr) {
|
|
286
|
+
const value = c['@_value'] || c.value;
|
|
287
|
+
if (!value)
|
|
288
|
+
continue;
|
|
289
|
+
const conditions = new Map();
|
|
290
|
+
const parts2 = c.part;
|
|
291
|
+
if (parts2) {
|
|
292
|
+
const pArr = Array.isArray(parts2) ? parts2 : [parts2];
|
|
293
|
+
for (const p of pArr) {
|
|
294
|
+
for (const attr of ['time', 'number', 'person', 'aspect', 'mood', 'voice']) {
|
|
295
|
+
const pv = p[`@_${attr}`] || p[attr];
|
|
296
|
+
if (pv && pv !== '*') {
|
|
297
|
+
conditions.set(attr, String(pv));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
conjugationOverrides.push({ value, conditions });
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
verbs.push({
|
|
306
|
+
root,
|
|
307
|
+
ruleId,
|
|
308
|
+
placeholderValues,
|
|
309
|
+
participleOverrides,
|
|
310
|
+
conjugationOverrides,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
return verbs;
|
|
314
|
+
}
|
|
315
|
+
generateForms(verb, ruleSets) {
|
|
316
|
+
const forms = new Set();
|
|
317
|
+
const resolvedParticiples = new Map();
|
|
318
|
+
for (const [type, value] of verb.participleOverrides) {
|
|
319
|
+
resolvedParticiples.set(type, value);
|
|
320
|
+
}
|
|
321
|
+
let appliedRule;
|
|
322
|
+
if (verb.ruleId && ruleSets.has(verb.ruleId)) {
|
|
323
|
+
appliedRule = ruleSets.get(verb.ruleId);
|
|
324
|
+
}
|
|
325
|
+
else if (ruleSets.size > 0) {
|
|
326
|
+
appliedRule = ruleSets.values().next().value;
|
|
327
|
+
}
|
|
328
|
+
if (appliedRule) {
|
|
329
|
+
const context = this.buildContext(verb, resolvedParticiples);
|
|
330
|
+
for (const [type, template] of appliedRule.participleRules) {
|
|
331
|
+
if (!resolvedParticiples.has(type)) {
|
|
332
|
+
const resolved = this.resolveTemplate(template, context);
|
|
333
|
+
if (resolved) {
|
|
334
|
+
resolvedParticiples.set(type, resolved);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const fullContext = this.buildContext(verb, resolvedParticiples);
|
|
339
|
+
for (const conjRule of appliedRule.conjugationRules) {
|
|
340
|
+
const resolved = this.resolveTemplate(conjRule.value, fullContext);
|
|
341
|
+
this.addIfSingleWord(resolved, forms);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
for (const [, value] of resolvedParticiples) {
|
|
345
|
+
this.addIfSingleWord(value, forms);
|
|
346
|
+
}
|
|
347
|
+
for (const conj of verb.conjugationOverrides) {
|
|
348
|
+
this.addIfSingleWord(conj.value, forms);
|
|
349
|
+
}
|
|
350
|
+
forms.delete(verb.root);
|
|
351
|
+
return Array.from(forms);
|
|
352
|
+
}
|
|
353
|
+
generateFormsDetailed(verb, ruleSets) {
|
|
354
|
+
const forms = new Map();
|
|
355
|
+
const resolvedParticiples = new Map();
|
|
356
|
+
for (const [type, value] of verb.participleOverrides) {
|
|
357
|
+
resolvedParticiples.set(type, value);
|
|
358
|
+
}
|
|
359
|
+
let appliedRule;
|
|
360
|
+
if (verb.ruleId && ruleSets.has(verb.ruleId)) {
|
|
361
|
+
appliedRule = ruleSets.get(verb.ruleId);
|
|
362
|
+
}
|
|
363
|
+
else if (ruleSets.size > 0) {
|
|
364
|
+
appliedRule = ruleSets.values().next().value;
|
|
365
|
+
}
|
|
366
|
+
if (appliedRule) {
|
|
367
|
+
const context = this.buildContext(verb, resolvedParticiples);
|
|
368
|
+
for (const [type, template] of appliedRule.participleRules) {
|
|
369
|
+
if (!resolvedParticiples.has(type)) {
|
|
370
|
+
const resolved = this.resolveTemplate(template, context);
|
|
371
|
+
if (resolved) {
|
|
372
|
+
resolvedParticiples.set(type, resolved);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
const fullContext = this.buildContext(verb, resolvedParticiples);
|
|
377
|
+
for (const conjRule of appliedRule.conjugationRules) {
|
|
378
|
+
const resolved = this.resolveTemplate(conjRule.value, fullContext);
|
|
379
|
+
if (resolved && !resolved.includes(' ') && resolved !== '-') {
|
|
380
|
+
const trimmed = resolved.trim();
|
|
381
|
+
if (trimmed.length > 0 && trimmed !== verb.root) {
|
|
382
|
+
const existing = forms.get(trimmed);
|
|
383
|
+
if (existing) {
|
|
384
|
+
for (const [k, v] of conjRule.conditions) {
|
|
385
|
+
existing.set(k, v);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
forms.set(trimmed, new Map(conjRule.conditions));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
for (const [type, value] of resolvedParticiples) {
|
|
396
|
+
if (!value.includes(' ') && value !== '-' && value.trim().length > 0 && value !== verb.root) {
|
|
397
|
+
const conditions = forms.get(value) || new Map();
|
|
398
|
+
conditions.set('participleType', type);
|
|
399
|
+
forms.set(value, conditions);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
for (const conj of verb.conjugationOverrides) {
|
|
403
|
+
if (conj.value &&
|
|
404
|
+
!conj.value.includes(' ') &&
|
|
405
|
+
conj.value !== '-' &&
|
|
406
|
+
conj.value.trim().length > 0 &&
|
|
407
|
+
conj.value !== verb.root) {
|
|
408
|
+
const existing = forms.get(conj.value) || new Map();
|
|
409
|
+
for (const [k, v] of conj.conditions) {
|
|
410
|
+
existing.set(k, v);
|
|
411
|
+
}
|
|
412
|
+
forms.set(conj.value, existing);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
forms.delete(verb.root);
|
|
416
|
+
return Array.from(forms.entries()).map(([value, conditions]) => ({
|
|
417
|
+
value,
|
|
418
|
+
conditions,
|
|
419
|
+
}));
|
|
420
|
+
}
|
|
421
|
+
buildContext(verb, resolvedParticiples) {
|
|
422
|
+
const context = new Map();
|
|
423
|
+
context.set('{root}', verb.root);
|
|
424
|
+
for (const [key, value] of verb.placeholderValues) {
|
|
425
|
+
context.set(key, value);
|
|
426
|
+
if (!key.startsWith('{')) {
|
|
427
|
+
context.set(`{${key}}`, value);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
for (const [type, value] of resolvedParticiples) {
|
|
431
|
+
context.set(`{${type}}`, value);
|
|
432
|
+
}
|
|
433
|
+
return context;
|
|
434
|
+
}
|
|
435
|
+
resolveTemplate(template, context) {
|
|
436
|
+
let result = template;
|
|
437
|
+
for (const [key, value] of context) {
|
|
438
|
+
const keyToReplace = key.startsWith('{') ? key : `{${key}}`;
|
|
439
|
+
result = result.split(keyToReplace).join(value);
|
|
440
|
+
}
|
|
441
|
+
return result;
|
|
442
|
+
}
|
|
443
|
+
addIfSingleWord(form, set) {
|
|
444
|
+
if (!form)
|
|
445
|
+
return;
|
|
446
|
+
if (form.includes(' '))
|
|
447
|
+
return;
|
|
448
|
+
if (form === '-')
|
|
449
|
+
return;
|
|
450
|
+
const trimmed = form.trim();
|
|
451
|
+
if (trimmed.length > 0) {
|
|
452
|
+
set.add(trimmed);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const SLOT_TAG_MAP = {
|
|
2
|
+
'3sg': ['3.PERS'],
|
|
3
|
+
past: ['PAST'],
|
|
4
|
+
pastPart: ['PAST', 'PARTICIPLE'],
|
|
5
|
+
presPart: ['GERUND'],
|
|
6
|
+
plural: ['PLURAL'],
|
|
7
|
+
comparative: ['COMPARATIVE'],
|
|
8
|
+
superlative: ['SUPERLATIVE'],
|
|
9
|
+
};
|
|
10
|
+
const CONDITION_TAG_MAP = {
|
|
11
|
+
person: { first: ['1.PERS'], second: ['2.PERS'], third: ['3.PERS'] },
|
|
12
|
+
number: { singular: [], plural: ['PLURAL'] },
|
|
13
|
+
time: { present: ['PRESENT'], past: ['PAST'], future: ['FUTURE'] },
|
|
14
|
+
aspect: { simple: [], continuous: ['CONTINUOUS'], perfect: ['PERFECT'] },
|
|
15
|
+
mood: {
|
|
16
|
+
imperative: ['IMPERATIVE'],
|
|
17
|
+
indicative: [],
|
|
18
|
+
conditional: ['CONDITIONAL'],
|
|
19
|
+
},
|
|
20
|
+
participleType: {
|
|
21
|
+
presentparticiple: ['GERUND'],
|
|
22
|
+
pastparticiple: ['PAST', 'PARTICIPLE'],
|
|
23
|
+
infinitive: ['BASE'],
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
export class WordFormGenerator {
|
|
27
|
+
generateFromEngineSlots(base, pos, engine, lang = 'en') {
|
|
28
|
+
const forms = engine.inflectWithSlots(base, pos);
|
|
29
|
+
const result = [{ lang, tags: ['BASE'], value: base }];
|
|
30
|
+
for (const { slot, form } of forms) {
|
|
31
|
+
const tags = SLOT_TAG_MAP[slot] || [slot.toUpperCase()];
|
|
32
|
+
result.push({ lang, tags, value: form, base });
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
generateFromGrid3Conditions(base, formsWithConditions, lang = 'en') {
|
|
37
|
+
const result = [{ lang, tags: ['BASE'], value: base }];
|
|
38
|
+
for (const form of formsWithConditions) {
|
|
39
|
+
const tags = this.conditionsToTags(form.conditions);
|
|
40
|
+
result.push({ lang, tags, value: form.value, base });
|
|
41
|
+
}
|
|
42
|
+
return this.deduplicate(result);
|
|
43
|
+
}
|
|
44
|
+
generateFromPos(base, pos, engine, grid3Parser, verbsZipPath, lang = 'en') {
|
|
45
|
+
if (verbsZipPath) {
|
|
46
|
+
const detailed = grid3Parser.parseZipDetailed(verbsZipPath);
|
|
47
|
+
const forms = detailed.verbs.get(base) || detailed.verbs.get(base.toLowerCase());
|
|
48
|
+
if (forms && forms.length > 0) {
|
|
49
|
+
return this.generateFromGrid3Conditions(base, forms, lang);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return this.generateFromEngineSlots(base, pos, engine, lang);
|
|
53
|
+
}
|
|
54
|
+
conditionsToTags(conditions) {
|
|
55
|
+
const tags = [];
|
|
56
|
+
for (const [dim, value] of conditions) {
|
|
57
|
+
const mapped = CONDITION_TAG_MAP[dim]?.[value];
|
|
58
|
+
if (mapped) {
|
|
59
|
+
tags.push(...mapped);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return tags.length > 0 ? tags : ['UNKNOWN'];
|
|
63
|
+
}
|
|
64
|
+
deduplicate(forms) {
|
|
65
|
+
const seen = new Set();
|
|
66
|
+
return forms.filter((f) => {
|
|
67
|
+
const key = `${f.value}|${f.tags.sort().join(',')}`;
|
|
68
|
+
if (seen.has(key))
|
|
69
|
+
return false;
|
|
70
|
+
seen.add(key);
|
|
71
|
+
return true;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -4,12 +4,16 @@ let sqlJsPromise = null;
|
|
|
4
4
|
export function configureSqlJs(config) {
|
|
5
5
|
sqlJsConfig = { ...(sqlJsConfig ?? {}), ...config };
|
|
6
6
|
}
|
|
7
|
-
async function
|
|
7
|
+
async function getSqlJsBrowser() {
|
|
8
8
|
if (!sqlJsPromise) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
const isBrowser = typeof globalThis !== 'undefined' && globalThis.window !== undefined;
|
|
10
|
+
if (!isBrowser)
|
|
11
|
+
throw new Error('Must be run in a browser');
|
|
12
|
+
const window = globalThis.window;
|
|
13
|
+
if (!('initSqlJs' in window))
|
|
14
|
+
throw new Error('Need to add sql-wasm.js script element to DOM');
|
|
15
|
+
const initSqlJs = window.initSqlJs;
|
|
16
|
+
sqlJsPromise = initSqlJs(sqlJsConfig ?? {});
|
|
13
17
|
}
|
|
14
18
|
return sqlJsPromise;
|
|
15
19
|
}
|
|
@@ -81,7 +85,7 @@ export async function openSqliteDatabase(input, options = {}) {
|
|
|
81
85
|
}
|
|
82
86
|
const data = await readBinaryFromInput(input);
|
|
83
87
|
if (!isNodeRuntime()) {
|
|
84
|
-
const SQL = await
|
|
88
|
+
const SQL = await getSqlJsBrowser();
|
|
85
89
|
const db = new SQL.Database(data);
|
|
86
90
|
return { db: createSqlJsAdapter(db) };
|
|
87
91
|
}
|
|
@@ -146,9 +146,17 @@ export declare class AACButton {
|
|
|
146
146
|
[key: string]: any;
|
|
147
147
|
};
|
|
148
148
|
predictions?: string[];
|
|
149
|
+
pos?: string;
|
|
150
|
+
wordForms?: Array<{
|
|
151
|
+
lang?: string;
|
|
152
|
+
tags: string[];
|
|
153
|
+
value: string;
|
|
154
|
+
pronunciation?: string;
|
|
155
|
+
base?: string;
|
|
156
|
+
}>;
|
|
149
157
|
semantic_id?: string;
|
|
150
158
|
clone_id?: string;
|
|
151
|
-
constructor({ id, label, message, targetPageId, semanticAction, audioRecording, style, contentType, contentSubType, image, resolvedImageEntry, symbolLibrary, symbolPath, x, y, columnSpan, rowSpan, scanBlocks, scanBlock, visibility, directActivate, parameters, predictions, semantic_id, clone_id, type, action, }: {
|
|
159
|
+
constructor({ id, label, message, targetPageId, semanticAction, audioRecording, style, contentType, contentSubType, image, resolvedImageEntry, symbolLibrary, symbolPath, x, y, columnSpan, rowSpan, scanBlocks, scanBlock, visibility, directActivate, parameters, predictions, pos, wordForms, semantic_id, clone_id, type, action, }: {
|
|
152
160
|
id: string;
|
|
153
161
|
label?: string;
|
|
154
162
|
message?: string;
|
|
@@ -179,6 +187,14 @@ export declare class AACButton {
|
|
|
179
187
|
[key: string]: any;
|
|
180
188
|
};
|
|
181
189
|
predictions?: string[];
|
|
190
|
+
pos?: string;
|
|
191
|
+
wordForms?: Array<{
|
|
192
|
+
lang?: string;
|
|
193
|
+
tags: string[];
|
|
194
|
+
value: string;
|
|
195
|
+
pronunciation?: string;
|
|
196
|
+
base?: string;
|
|
197
|
+
}>;
|
|
182
198
|
semantic_id?: string;
|
|
183
199
|
clone_id?: string;
|
|
184
200
|
type?: 'SPEAK' | 'NAVIGATE' | 'ACTION';
|
|
@@ -54,7 +54,7 @@ var AACScanType;
|
|
|
54
54
|
AACScanType["BLOCK_COLUMN_ROW"] = "block-column-row";
|
|
55
55
|
})(AACScanType || (exports.AACScanType = AACScanType = {}));
|
|
56
56
|
class AACButton {
|
|
57
|
-
constructor({ id, label = '', message = '', targetPageId, semanticAction, audioRecording, style, contentType, contentSubType, image, resolvedImageEntry, symbolLibrary, symbolPath, x, y, columnSpan, rowSpan, scanBlocks, scanBlock, visibility, directActivate, parameters, predictions, semantic_id, clone_id,
|
|
57
|
+
constructor({ id, label = '', message = '', targetPageId, semanticAction, audioRecording, style, contentType, contentSubType, image, resolvedImageEntry, symbolLibrary, symbolPath, x, y, columnSpan, rowSpan, scanBlocks, scanBlock, visibility, directActivate, parameters, predictions, pos, wordForms, semantic_id, clone_id,
|
|
58
58
|
// Legacy input support
|
|
59
59
|
type, action, }) {
|
|
60
60
|
this.id = id;
|
|
@@ -80,6 +80,8 @@ class AACButton {
|
|
|
80
80
|
this.directActivate = directActivate;
|
|
81
81
|
this.parameters = parameters;
|
|
82
82
|
this.predictions = predictions;
|
|
83
|
+
this.pos = pos;
|
|
84
|
+
this.wordForms = wordForms;
|
|
83
85
|
this.semantic_id = semantic_id;
|
|
84
86
|
this.clone_id = clone_id;
|
|
85
87
|
// Legacy mapping: if no semanticAction provided, derive from legacy `action` first
|
package/dist/index.node.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ export * from './processors';
|
|
|
12
12
|
export * as Analytics from './analytics';
|
|
13
13
|
export * as Validation from './validation';
|
|
14
14
|
export * as Metrics from './metrics';
|
|
15
|
+
export { Grid3VerbsParser } from './utilities/analytics/morphology/grid3VerbsParser';
|
|
16
|
+
export { WordFormGenerator } from './utilities/analytics/morphology/wordFormGenerator';
|
|
15
17
|
export * as Gridset from './gridset';
|
|
16
18
|
export * as Snap from './snap';
|
|
17
19
|
export * as OBF from './obf';
|
package/dist/index.node.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
33
33
|
return result;
|
|
34
34
|
};
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.Translation = exports.AstericsGrid = exports.ApplePanels = exports.Opml = exports.Excel = exports.Dot = exports.TouchChat = exports.Obfset = exports.OBF = exports.Snap = exports.Gridset = exports.Metrics = exports.Validation = exports.Analytics = void 0;
|
|
36
|
+
exports.Translation = exports.AstericsGrid = exports.ApplePanels = exports.Opml = exports.Excel = exports.Dot = exports.TouchChat = exports.Obfset = exports.OBF = exports.Snap = exports.Gridset = exports.WordFormGenerator = exports.Grid3VerbsParser = exports.Metrics = exports.Validation = exports.Analytics = void 0;
|
|
37
37
|
exports.getProcessor = getProcessor;
|
|
38
38
|
exports.getSupportedExtensions = getSupportedExtensions;
|
|
39
39
|
exports.isExtensionSupported = isExtensionSupported;
|
|
@@ -56,6 +56,11 @@ exports.Analytics = __importStar(require("./analytics"));
|
|
|
56
56
|
exports.Validation = __importStar(require("./validation"));
|
|
57
57
|
// Metrics namespace (pageset analytics)
|
|
58
58
|
exports.Metrics = __importStar(require("./metrics"));
|
|
59
|
+
// Node-only morphology utilities (Grid 3 verbs parser)
|
|
60
|
+
var grid3VerbsParser_1 = require("./utilities/analytics/morphology/grid3VerbsParser");
|
|
61
|
+
Object.defineProperty(exports, "Grid3VerbsParser", { enumerable: true, get: function () { return grid3VerbsParser_1.Grid3VerbsParser; } });
|
|
62
|
+
var wordFormGenerator_1 = require("./utilities/analytics/morphology/wordFormGenerator");
|
|
63
|
+
Object.defineProperty(exports, "WordFormGenerator", { enumerable: true, get: function () { return wordFormGenerator_1.WordFormGenerator; } });
|
|
59
64
|
// Processor namespaces (platform-specific utilities)
|
|
60
65
|
exports.Gridset = __importStar(require("./gridset"));
|
|
61
66
|
exports.Snap = __importStar(require("./snap"));
|
package/dist/metrics.d.ts
CHANGED
|
@@ -12,6 +12,9 @@ export { MetricsCalculator } from './utilities/analytics/metrics/core';
|
|
|
12
12
|
export { VocabularyAnalyzer } from './utilities/analytics/metrics/vocabulary';
|
|
13
13
|
export { SentenceAnalyzer } from './utilities/analytics/metrics/sentence';
|
|
14
14
|
export { ComparisonAnalyzer } from './utilities/analytics/metrics/comparison';
|
|
15
|
+
export { MorphologyEngine } from './utilities/analytics/morphology';
|
|
16
|
+
export { WordFormGenerator } from './utilities/analytics/morphology';
|
|
17
|
+
export type { MorphRuleSet, MorphRule, MorphWordForms, Grid3VerbForms, AstericsWordForm, VerbFormWithConditions, Grid3VerbFormsDetailed, } from './utilities/analytics/morphology';
|
|
15
18
|
export { ReferenceLoader } from './utilities/analytics/reference';
|
|
16
19
|
export { InMemoryReferenceLoader, createBrowserReferenceLoader, loadReferenceDataFromUrl, type ReferenceData, } from './utilities/analytics/reference/browser';
|
|
17
20
|
export * from './utilities/analytics/utils/idGenerator';
|