@willwade/aac-processors 0.2.3 → 0.2.5
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/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 +269 -239
- 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/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 +269 -239
- 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/package.json +12 -11
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { Grid3VerbFormsDetailed } from './types';
|
|
2
|
+
export interface Grid3VerbForms {
|
|
3
|
+
locale: string;
|
|
4
|
+
verbs: Map<string, string[]>;
|
|
5
|
+
}
|
|
6
|
+
export declare class Grid3VerbsParser {
|
|
7
|
+
private parser;
|
|
8
|
+
parseXml(xmlContent: string, locale?: string): Grid3VerbForms;
|
|
9
|
+
parseXmlDetailed(xmlContent: string, locale?: string): Grid3VerbFormsDetailed;
|
|
10
|
+
parseXmlFileDetailed(filePath: string): Grid3VerbFormsDetailed;
|
|
11
|
+
parseZip(zipPath: string): Grid3VerbForms;
|
|
12
|
+
parseZipDetailed(zipPath: string): Grid3VerbFormsDetailed;
|
|
13
|
+
parseLocale(locale: string, grid3InstallPath?: string): Promise<Grid3VerbForms>;
|
|
14
|
+
parseInstalledLocales(grid3InstallPath?: string): Promise<Map<string, Grid3VerbForms>>;
|
|
15
|
+
/**
|
|
16
|
+
* Parse all locales from a custom directory.
|
|
17
|
+
*
|
|
18
|
+
* The directory should have the same structure as Grid 3's Locale folder:
|
|
19
|
+
* customDir/en-GB/verbs/verbs.zip
|
|
20
|
+
* customDir/nb-NO/verbs/verbs.zip
|
|
21
|
+
* customDir/de-DE/verbs/verbs.zip
|
|
22
|
+
*
|
|
23
|
+
* This allows users to supply morphology data copied from any Grid 3
|
|
24
|
+
* installation without needing Grid 3 installed on this machine.
|
|
25
|
+
*/
|
|
26
|
+
parseCustomDirectory(dirPath: string, detailed?: false): Map<string, Grid3VerbForms>;
|
|
27
|
+
parseCustomDirectory(dirPath: string, detailed: true): Map<string, import('./types').Grid3VerbFormsDetailed>;
|
|
28
|
+
private getDefaultInstallPath;
|
|
29
|
+
private parseRuleSets;
|
|
30
|
+
private parseVerbs;
|
|
31
|
+
private generateForms;
|
|
32
|
+
private generateFormsDetailed;
|
|
33
|
+
private buildContext;
|
|
34
|
+
private resolveTemplate;
|
|
35
|
+
private addIfSingleWord;
|
|
36
|
+
}
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
exports.Grid3VerbsParser = void 0;
|
|
30
|
+
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
31
|
+
const adm_zip_1 = __importDefault(require("adm-zip"));
|
|
32
|
+
const path_1 = require("path");
|
|
33
|
+
class Grid3VerbsParser {
|
|
34
|
+
constructor() {
|
|
35
|
+
this.parser = new fast_xml_parser_1.XMLParser({
|
|
36
|
+
ignoreAttributes: false,
|
|
37
|
+
ignoreDeclaration: true,
|
|
38
|
+
textNodeName: '#text',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
parseXml(xmlContent, locale) {
|
|
42
|
+
const data = this.parser.parse(xmlContent);
|
|
43
|
+
const verbdata = data.verbdata || data.Verbdata;
|
|
44
|
+
if (!verbdata) {
|
|
45
|
+
return { locale: locale || 'unknown', verbs: new Map() };
|
|
46
|
+
}
|
|
47
|
+
const detectedLocale = verbdata['@_locale'] || verbdata.locale || locale || 'unknown';
|
|
48
|
+
const ruleSets = this.parseRuleSets(verbdata);
|
|
49
|
+
const verbs = this.parseVerbs(verbdata);
|
|
50
|
+
const result = new Map();
|
|
51
|
+
for (const verb of verbs) {
|
|
52
|
+
const forms = this.generateForms(verb, ruleSets);
|
|
53
|
+
if (forms.length > 0) {
|
|
54
|
+
result.set(verb.root, forms);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return { locale: detectedLocale, verbs: result };
|
|
58
|
+
}
|
|
59
|
+
parseXmlDetailed(xmlContent, locale) {
|
|
60
|
+
const data = this.parser.parse(xmlContent);
|
|
61
|
+
const verbdata = data.verbdata || data.Verbdata;
|
|
62
|
+
if (!verbdata) {
|
|
63
|
+
return { locale: locale || 'unknown', verbs: new Map() };
|
|
64
|
+
}
|
|
65
|
+
const detectedLocale = verbdata['@_locale'] || verbdata.locale || locale || 'unknown';
|
|
66
|
+
const ruleSets = this.parseRuleSets(verbdata);
|
|
67
|
+
const verbs = this.parseVerbs(verbdata);
|
|
68
|
+
const result = new Map();
|
|
69
|
+
for (const verb of verbs) {
|
|
70
|
+
const forms = this.generateFormsDetailed(verb, ruleSets);
|
|
71
|
+
if (forms.length > 0) {
|
|
72
|
+
result.set(verb.root, forms);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return { locale: detectedLocale, verbs: result };
|
|
76
|
+
}
|
|
77
|
+
/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-argument */
|
|
78
|
+
parseXmlFileDetailed(filePath) {
|
|
79
|
+
const fs = require('fs');
|
|
80
|
+
const xml = fs.readFileSync(filePath, 'utf-8');
|
|
81
|
+
return this.parseXmlDetailed(xml);
|
|
82
|
+
}
|
|
83
|
+
/* eslint-enable @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-argument */
|
|
84
|
+
parseZip(zipPath) {
|
|
85
|
+
const zip = new adm_zip_1.default(zipPath);
|
|
86
|
+
const entries = zip.getEntries();
|
|
87
|
+
const verbEntry = entries.find((e) => e.entryName.toLowerCase().endsWith('verbs.xml'));
|
|
88
|
+
if (!verbEntry) {
|
|
89
|
+
const locale = (0, path_1.basename)((0, path_1.dirname)(zipPath));
|
|
90
|
+
return { locale, verbs: new Map() };
|
|
91
|
+
}
|
|
92
|
+
const xml = verbEntry.getData().toString('utf-8');
|
|
93
|
+
return this.parseXml(xml);
|
|
94
|
+
}
|
|
95
|
+
parseZipDetailed(zipPath) {
|
|
96
|
+
const zip = new adm_zip_1.default(zipPath);
|
|
97
|
+
const entries = zip.getEntries();
|
|
98
|
+
const verbEntry = entries.find((e) => e.entryName.toLowerCase().endsWith('verbs.xml'));
|
|
99
|
+
if (!verbEntry) {
|
|
100
|
+
const locale = (0, path_1.basename)((0, path_1.dirname)(zipPath));
|
|
101
|
+
return { locale, verbs: new Map() };
|
|
102
|
+
}
|
|
103
|
+
const xml = verbEntry.getData().toString('utf-8');
|
|
104
|
+
return this.parseXmlDetailed(xml);
|
|
105
|
+
}
|
|
106
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
|
107
|
+
async parseLocale(locale, grid3InstallPath) {
|
|
108
|
+
const installPath = grid3InstallPath || this.getDefaultInstallPath();
|
|
109
|
+
if (!installPath) {
|
|
110
|
+
return { locale, verbs: new Map() };
|
|
111
|
+
}
|
|
112
|
+
const zipPath = (0, path_1.join)(installPath, 'Locale', locale, 'verbs', 'verbs.zip');
|
|
113
|
+
try {
|
|
114
|
+
return this.parseZip(zipPath);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return { locale, verbs: new Map() };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async parseInstalledLocales(grid3InstallPath) {
|
|
121
|
+
const installPath = grid3InstallPath || this.getDefaultInstallPath();
|
|
122
|
+
const results = new Map();
|
|
123
|
+
if (!installPath)
|
|
124
|
+
return results;
|
|
125
|
+
const fs = await Promise.resolve().then(() => __importStar(require('fs')));
|
|
126
|
+
const localeDir = (0, path_1.join)(installPath, 'Locale');
|
|
127
|
+
let locales;
|
|
128
|
+
try {
|
|
129
|
+
locales = fs
|
|
130
|
+
.readdirSync(localeDir)
|
|
131
|
+
.filter((d) => fs.statSync((0, path_1.join)(localeDir, d)).isDirectory());
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return results;
|
|
135
|
+
}
|
|
136
|
+
for (const locale of locales) {
|
|
137
|
+
const verbsZip = (0, path_1.join)(localeDir, locale, 'verbs', 'verbs.zip');
|
|
138
|
+
try {
|
|
139
|
+
fs.accessSync(verbsZip, fs.constants.R_OK);
|
|
140
|
+
const forms = this.parseZip(verbsZip);
|
|
141
|
+
results.set(locale, forms);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// No verbs.zip for this locale
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return results;
|
|
148
|
+
}
|
|
149
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
150
|
+
parseCustomDirectory(dirPath, detailed = false) {
|
|
151
|
+
/* eslint-disable @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return */
|
|
152
|
+
const fs = require('fs');
|
|
153
|
+
let locales;
|
|
154
|
+
try {
|
|
155
|
+
locales = fs
|
|
156
|
+
.readdirSync(dirPath)
|
|
157
|
+
.filter((d) => fs.statSync((0, path_1.join)(dirPath, d)).isDirectory());
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
return new Map();
|
|
161
|
+
}
|
|
162
|
+
if (detailed) {
|
|
163
|
+
const results = new Map();
|
|
164
|
+
for (const locale of locales) {
|
|
165
|
+
const verbsZip = (0, path_1.join)(dirPath, locale, 'verbs', 'verbs.zip');
|
|
166
|
+
try {
|
|
167
|
+
fs.accessSync(verbsZip, fs.constants.R_OK);
|
|
168
|
+
results.set(locale, this.parseZipDetailed(verbsZip));
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// No verbs.zip for this locale
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return results;
|
|
175
|
+
}
|
|
176
|
+
const results = new Map();
|
|
177
|
+
for (const locale of locales) {
|
|
178
|
+
const verbsZip = (0, path_1.join)(dirPath, locale, 'verbs', 'verbs.zip');
|
|
179
|
+
try {
|
|
180
|
+
fs.accessSync(verbsZip, fs.constants.R_OK);
|
|
181
|
+
results.set(locale, this.parseZip(verbsZip));
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
// No verbs.zip for this locale
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return results;
|
|
188
|
+
/* eslint-enable @typescript-eslint/no-var-requires, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return */
|
|
189
|
+
}
|
|
190
|
+
getDefaultInstallPath() {
|
|
191
|
+
if (typeof process === 'undefined' || process.platform !== 'win32') {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
const paths = [
|
|
195
|
+
'C:\\Program Files (x86)\\Smartbox\\Grid 3',
|
|
196
|
+
'C:\\Program Files\\Smartbox\\Grid 3',
|
|
197
|
+
];
|
|
198
|
+
try {
|
|
199
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
200
|
+
const fs = require('fs');
|
|
201
|
+
for (const p of paths) {
|
|
202
|
+
if (fs.existsSync(p))
|
|
203
|
+
return p;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// Grid 3 not installed
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access */
|
|
212
|
+
parseRuleSets(verbdata) {
|
|
213
|
+
const rulesMap = new Map();
|
|
214
|
+
const rulesList = verbdata.verbruleslist?.verbrules;
|
|
215
|
+
if (!rulesList)
|
|
216
|
+
return rulesMap;
|
|
217
|
+
const rulesArr = Array.isArray(rulesList) ? rulesList : [rulesList];
|
|
218
|
+
for (const rules of rulesArr) {
|
|
219
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
220
|
+
const id = rules.Id || rules.id || `rule_${rulesMap.size}`;
|
|
221
|
+
const placeholders = [];
|
|
222
|
+
const ph = rules.placeholders?.placeholder;
|
|
223
|
+
if (ph) {
|
|
224
|
+
const phArr = Array.isArray(ph) ? ph : [ph];
|
|
225
|
+
for (const p of phArr) {
|
|
226
|
+
const val = typeof p === 'string' ? p : p['#text'] || p;
|
|
227
|
+
if (val)
|
|
228
|
+
placeholders.push(val);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const participleRules = new Map();
|
|
232
|
+
const pr = rules.participlerules?.participlerule;
|
|
233
|
+
if (pr) {
|
|
234
|
+
const prArr = Array.isArray(pr) ? pr : [pr];
|
|
235
|
+
for (const rule of prArr) {
|
|
236
|
+
const type = rule['@_type'] || rule.type;
|
|
237
|
+
const value = rule['@_value'] || rule.value;
|
|
238
|
+
if (type && value) {
|
|
239
|
+
participleRules.set(type, value);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const conjugationRules = [];
|
|
244
|
+
const cr = rules.conjugationrules?.conjugationrule;
|
|
245
|
+
if (cr) {
|
|
246
|
+
const crArr = Array.isArray(cr) ? cr : [cr];
|
|
247
|
+
for (const rule of crArr) {
|
|
248
|
+
const value = rule['@_value'] || rule.value;
|
|
249
|
+
if (!value)
|
|
250
|
+
continue;
|
|
251
|
+
const conditions = new Map();
|
|
252
|
+
for (const attr of [
|
|
253
|
+
'time',
|
|
254
|
+
'number',
|
|
255
|
+
'person',
|
|
256
|
+
'aspect',
|
|
257
|
+
'mood',
|
|
258
|
+
'voice',
|
|
259
|
+
'tense',
|
|
260
|
+
'polarity',
|
|
261
|
+
]) {
|
|
262
|
+
const v = rule[`@_${attr}`] || rule[attr];
|
|
263
|
+
if (v && v !== '*') {
|
|
264
|
+
conditions.set(attr, String(v));
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
conjugationRules.push({ value, conditions });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
rulesMap.set(id, { id, placeholders, participleRules, conjugationRules });
|
|
271
|
+
}
|
|
272
|
+
return rulesMap;
|
|
273
|
+
}
|
|
274
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access */
|
|
275
|
+
parseVerbs(verbdata) {
|
|
276
|
+
const verbs = [];
|
|
277
|
+
const verbsData = verbdata.verbs?.verb;
|
|
278
|
+
if (!verbsData)
|
|
279
|
+
return verbs;
|
|
280
|
+
const arr = Array.isArray(verbsData) ? verbsData : [verbsData];
|
|
281
|
+
for (const v of arr) {
|
|
282
|
+
const root = v['@_root'] || v.root;
|
|
283
|
+
if (!root)
|
|
284
|
+
continue;
|
|
285
|
+
const ruleId = v['@_ruleid'] || v.ruleid || undefined;
|
|
286
|
+
const placeholderValues = new Map();
|
|
287
|
+
const rphv = v.ruleplaceholdervalues?.ruleplaceholdervalue;
|
|
288
|
+
if (rphv) {
|
|
289
|
+
const rphvArr = Array.isArray(rphv) ? rphv : [rphv];
|
|
290
|
+
for (const ph of rphvArr) {
|
|
291
|
+
const placeholder = ph['@_placeholder'] || ph.placeholder;
|
|
292
|
+
const value = ph['@_value'] || ph.value;
|
|
293
|
+
if (placeholder && value) {
|
|
294
|
+
placeholderValues.set(placeholder, value);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const participleOverrides = new Map();
|
|
299
|
+
const parts = v.participles?.participle;
|
|
300
|
+
if (parts) {
|
|
301
|
+
const partsArr = Array.isArray(parts) ? parts : [parts];
|
|
302
|
+
for (const p of partsArr) {
|
|
303
|
+
const type = p['@_type'] || p.type;
|
|
304
|
+
const value = p['@_value'] || p.value;
|
|
305
|
+
if (type && value) {
|
|
306
|
+
participleOverrides.set(type, value);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
const conjugationOverrides = [];
|
|
311
|
+
const conjs = v.conjugations?.conjugation;
|
|
312
|
+
if (conjs) {
|
|
313
|
+
const conjArr = Array.isArray(conjs) ? conjs : [conjs];
|
|
314
|
+
for (const c of conjArr) {
|
|
315
|
+
const value = c['@_value'] || c.value;
|
|
316
|
+
if (!value)
|
|
317
|
+
continue;
|
|
318
|
+
const conditions = new Map();
|
|
319
|
+
const parts2 = c.part;
|
|
320
|
+
if (parts2) {
|
|
321
|
+
const pArr = Array.isArray(parts2) ? parts2 : [parts2];
|
|
322
|
+
for (const p of pArr) {
|
|
323
|
+
for (const attr of ['time', 'number', 'person', 'aspect', 'mood', 'voice']) {
|
|
324
|
+
const pv = p[`@_${attr}`] || p[attr];
|
|
325
|
+
if (pv && pv !== '*') {
|
|
326
|
+
conditions.set(attr, String(pv));
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
conjugationOverrides.push({ value, conditions });
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
verbs.push({
|
|
335
|
+
root,
|
|
336
|
+
ruleId,
|
|
337
|
+
placeholderValues,
|
|
338
|
+
participleOverrides,
|
|
339
|
+
conjugationOverrides,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
return verbs;
|
|
343
|
+
}
|
|
344
|
+
generateForms(verb, ruleSets) {
|
|
345
|
+
const forms = new Set();
|
|
346
|
+
const resolvedParticiples = new Map();
|
|
347
|
+
for (const [type, value] of verb.participleOverrides) {
|
|
348
|
+
resolvedParticiples.set(type, value);
|
|
349
|
+
}
|
|
350
|
+
let appliedRule;
|
|
351
|
+
if (verb.ruleId && ruleSets.has(verb.ruleId)) {
|
|
352
|
+
appliedRule = ruleSets.get(verb.ruleId);
|
|
353
|
+
}
|
|
354
|
+
else if (ruleSets.size > 0) {
|
|
355
|
+
appliedRule = ruleSets.values().next().value;
|
|
356
|
+
}
|
|
357
|
+
if (appliedRule) {
|
|
358
|
+
const context = this.buildContext(verb, resolvedParticiples);
|
|
359
|
+
for (const [type, template] of appliedRule.participleRules) {
|
|
360
|
+
if (!resolvedParticiples.has(type)) {
|
|
361
|
+
const resolved = this.resolveTemplate(template, context);
|
|
362
|
+
if (resolved) {
|
|
363
|
+
resolvedParticiples.set(type, resolved);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const fullContext = this.buildContext(verb, resolvedParticiples);
|
|
368
|
+
for (const conjRule of appliedRule.conjugationRules) {
|
|
369
|
+
const resolved = this.resolveTemplate(conjRule.value, fullContext);
|
|
370
|
+
this.addIfSingleWord(resolved, forms);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
for (const [, value] of resolvedParticiples) {
|
|
374
|
+
this.addIfSingleWord(value, forms);
|
|
375
|
+
}
|
|
376
|
+
for (const conj of verb.conjugationOverrides) {
|
|
377
|
+
this.addIfSingleWord(conj.value, forms);
|
|
378
|
+
}
|
|
379
|
+
forms.delete(verb.root);
|
|
380
|
+
return Array.from(forms);
|
|
381
|
+
}
|
|
382
|
+
generateFormsDetailed(verb, ruleSets) {
|
|
383
|
+
const forms = new Map();
|
|
384
|
+
const resolvedParticiples = new Map();
|
|
385
|
+
for (const [type, value] of verb.participleOverrides) {
|
|
386
|
+
resolvedParticiples.set(type, value);
|
|
387
|
+
}
|
|
388
|
+
let appliedRule;
|
|
389
|
+
if (verb.ruleId && ruleSets.has(verb.ruleId)) {
|
|
390
|
+
appliedRule = ruleSets.get(verb.ruleId);
|
|
391
|
+
}
|
|
392
|
+
else if (ruleSets.size > 0) {
|
|
393
|
+
appliedRule = ruleSets.values().next().value;
|
|
394
|
+
}
|
|
395
|
+
if (appliedRule) {
|
|
396
|
+
const context = this.buildContext(verb, resolvedParticiples);
|
|
397
|
+
for (const [type, template] of appliedRule.participleRules) {
|
|
398
|
+
if (!resolvedParticiples.has(type)) {
|
|
399
|
+
const resolved = this.resolveTemplate(template, context);
|
|
400
|
+
if (resolved) {
|
|
401
|
+
resolvedParticiples.set(type, resolved);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const fullContext = this.buildContext(verb, resolvedParticiples);
|
|
406
|
+
for (const conjRule of appliedRule.conjugationRules) {
|
|
407
|
+
const resolved = this.resolveTemplate(conjRule.value, fullContext);
|
|
408
|
+
if (resolved && !resolved.includes(' ') && resolved !== '-') {
|
|
409
|
+
const trimmed = resolved.trim();
|
|
410
|
+
if (trimmed.length > 0 && trimmed !== verb.root) {
|
|
411
|
+
const existing = forms.get(trimmed);
|
|
412
|
+
if (existing) {
|
|
413
|
+
for (const [k, v] of conjRule.conditions) {
|
|
414
|
+
existing.set(k, v);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
else {
|
|
418
|
+
forms.set(trimmed, new Map(conjRule.conditions));
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
for (const [type, value] of resolvedParticiples) {
|
|
425
|
+
if (!value.includes(' ') && value !== '-' && value.trim().length > 0 && value !== verb.root) {
|
|
426
|
+
const conditions = forms.get(value) || new Map();
|
|
427
|
+
conditions.set('participleType', type);
|
|
428
|
+
forms.set(value, conditions);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
for (const conj of verb.conjugationOverrides) {
|
|
432
|
+
if (conj.value &&
|
|
433
|
+
!conj.value.includes(' ') &&
|
|
434
|
+
conj.value !== '-' &&
|
|
435
|
+
conj.value.trim().length > 0 &&
|
|
436
|
+
conj.value !== verb.root) {
|
|
437
|
+
const existing = forms.get(conj.value) || new Map();
|
|
438
|
+
for (const [k, v] of conj.conditions) {
|
|
439
|
+
existing.set(k, v);
|
|
440
|
+
}
|
|
441
|
+
forms.set(conj.value, existing);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
forms.delete(verb.root);
|
|
445
|
+
return Array.from(forms.entries()).map(([value, conditions]) => ({
|
|
446
|
+
value,
|
|
447
|
+
conditions,
|
|
448
|
+
}));
|
|
449
|
+
}
|
|
450
|
+
buildContext(verb, resolvedParticiples) {
|
|
451
|
+
const context = new Map();
|
|
452
|
+
context.set('{root}', verb.root);
|
|
453
|
+
for (const [key, value] of verb.placeholderValues) {
|
|
454
|
+
context.set(key, value);
|
|
455
|
+
if (!key.startsWith('{')) {
|
|
456
|
+
context.set(`{${key}}`, value);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
for (const [type, value] of resolvedParticiples) {
|
|
460
|
+
context.set(`{${type}}`, value);
|
|
461
|
+
}
|
|
462
|
+
return context;
|
|
463
|
+
}
|
|
464
|
+
resolveTemplate(template, context) {
|
|
465
|
+
let result = template;
|
|
466
|
+
for (const [key, value] of context) {
|
|
467
|
+
const keyToReplace = key.startsWith('{') ? key : `{${key}}`;
|
|
468
|
+
result = result.split(keyToReplace).join(value);
|
|
469
|
+
}
|
|
470
|
+
return result;
|
|
471
|
+
}
|
|
472
|
+
addIfSingleWord(form, set) {
|
|
473
|
+
if (!form)
|
|
474
|
+
return;
|
|
475
|
+
if (form.includes(' '))
|
|
476
|
+
return;
|
|
477
|
+
if (form === '-')
|
|
478
|
+
return;
|
|
479
|
+
const trimmed = form.trim();
|
|
480
|
+
if (trimmed.length > 0) {
|
|
481
|
+
set.add(trimmed);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
exports.Grid3VerbsParser = Grid3VerbsParser;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { MorphologyEngine } from './engine';
|
|
2
|
+
export { Grid3VerbsParser } from './grid3VerbsParser';
|
|
3
|
+
export { WordFormGenerator } from './wordFormGenerator';
|
|
4
|
+
export type { MorphRuleSet, MorphRule, MorphWordForms, AstericsWordForm, VerbFormWithConditions, Grid3VerbFormsDetailed, } from './types';
|
|
5
|
+
export type { Grid3VerbForms } from './grid3VerbsParser';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WordFormGenerator = exports.Grid3VerbsParser = exports.MorphologyEngine = void 0;
|
|
4
|
+
var engine_1 = require("./engine");
|
|
5
|
+
Object.defineProperty(exports, "MorphologyEngine", { enumerable: true, get: function () { return engine_1.MorphologyEngine; } });
|
|
6
|
+
var grid3VerbsParser_1 = require("./grid3VerbsParser");
|
|
7
|
+
Object.defineProperty(exports, "Grid3VerbsParser", { enumerable: true, get: function () { return grid3VerbsParser_1.Grid3VerbsParser; } });
|
|
8
|
+
var wordFormGenerator_1 = require("./wordFormGenerator");
|
|
9
|
+
Object.defineProperty(exports, "WordFormGenerator", { enumerable: true, get: function () { return wordFormGenerator_1.WordFormGenerator; } });
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface MorphRule {
|
|
2
|
+
match: string;
|
|
3
|
+
replace: string;
|
|
4
|
+
}
|
|
5
|
+
export interface MorphRuleSet {
|
|
6
|
+
locale: string;
|
|
7
|
+
version: number;
|
|
8
|
+
irregular: {
|
|
9
|
+
[pos: string]: {
|
|
10
|
+
[baseWord: string]: {
|
|
11
|
+
[slot: string]: string | string[];
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
regular: {
|
|
16
|
+
[pos: string]: {
|
|
17
|
+
[slot: string]: MorphRule[] | string;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export interface MorphWordForms {
|
|
22
|
+
base: string;
|
|
23
|
+
pos: string;
|
|
24
|
+
forms: string[];
|
|
25
|
+
}
|
|
26
|
+
export interface AstericsWordForm {
|
|
27
|
+
lang?: string;
|
|
28
|
+
tags: string[];
|
|
29
|
+
value: string;
|
|
30
|
+
pronunciation?: string;
|
|
31
|
+
base?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface VerbFormWithConditions {
|
|
34
|
+
value: string;
|
|
35
|
+
conditions: Map<string, string>;
|
|
36
|
+
}
|
|
37
|
+
export interface Grid3VerbFormsDetailed {
|
|
38
|
+
locale: string;
|
|
39
|
+
verbs: Map<string, VerbFormWithConditions[]>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { MorphologyEngine } from './engine';
|
|
2
|
+
import { Grid3VerbsParser } from './grid3VerbsParser';
|
|
3
|
+
import type { AstericsWordForm, VerbFormWithConditions } from './types';
|
|
4
|
+
export declare class WordFormGenerator {
|
|
5
|
+
generateFromEngineSlots(base: string, pos: string, engine: MorphologyEngine, lang?: string): AstericsWordForm[];
|
|
6
|
+
generateFromGrid3Conditions(base: string, formsWithConditions: VerbFormWithConditions[], lang?: string): AstericsWordForm[];
|
|
7
|
+
generateFromPos(base: string, pos: string, engine: MorphologyEngine, grid3Parser: Grid3VerbsParser, verbsZipPath?: string, lang?: string): AstericsWordForm[];
|
|
8
|
+
conditionsToTags(conditions: Map<string, string>): string[];
|
|
9
|
+
private deduplicate;
|
|
10
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WordFormGenerator = void 0;
|
|
4
|
+
const SLOT_TAG_MAP = {
|
|
5
|
+
'3sg': ['3.PERS'],
|
|
6
|
+
past: ['PAST'],
|
|
7
|
+
pastPart: ['PAST', 'PARTICIPLE'],
|
|
8
|
+
presPart: ['GERUND'],
|
|
9
|
+
plural: ['PLURAL'],
|
|
10
|
+
comparative: ['COMPARATIVE'],
|
|
11
|
+
superlative: ['SUPERLATIVE'],
|
|
12
|
+
};
|
|
13
|
+
const CONDITION_TAG_MAP = {
|
|
14
|
+
person: { first: ['1.PERS'], second: ['2.PERS'], third: ['3.PERS'] },
|
|
15
|
+
number: { singular: [], plural: ['PLURAL'] },
|
|
16
|
+
time: { present: ['PRESENT'], past: ['PAST'], future: ['FUTURE'] },
|
|
17
|
+
aspect: { simple: [], continuous: ['CONTINUOUS'], perfect: ['PERFECT'] },
|
|
18
|
+
mood: {
|
|
19
|
+
imperative: ['IMPERATIVE'],
|
|
20
|
+
indicative: [],
|
|
21
|
+
conditional: ['CONDITIONAL'],
|
|
22
|
+
},
|
|
23
|
+
participleType: {
|
|
24
|
+
presentparticiple: ['GERUND'],
|
|
25
|
+
pastparticiple: ['PAST', 'PARTICIPLE'],
|
|
26
|
+
infinitive: ['BASE'],
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
class WordFormGenerator {
|
|
30
|
+
generateFromEngineSlots(base, pos, engine, lang = 'en') {
|
|
31
|
+
const forms = engine.inflectWithSlots(base, pos);
|
|
32
|
+
const result = [{ lang, tags: ['BASE'], value: base }];
|
|
33
|
+
for (const { slot, form } of forms) {
|
|
34
|
+
const tags = SLOT_TAG_MAP[slot] || [slot.toUpperCase()];
|
|
35
|
+
result.push({ lang, tags, value: form, base });
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
generateFromGrid3Conditions(base, formsWithConditions, lang = 'en') {
|
|
40
|
+
const result = [{ lang, tags: ['BASE'], value: base }];
|
|
41
|
+
for (const form of formsWithConditions) {
|
|
42
|
+
const tags = this.conditionsToTags(form.conditions);
|
|
43
|
+
result.push({ lang, tags, value: form.value, base });
|
|
44
|
+
}
|
|
45
|
+
return this.deduplicate(result);
|
|
46
|
+
}
|
|
47
|
+
generateFromPos(base, pos, engine, grid3Parser, verbsZipPath, lang = 'en') {
|
|
48
|
+
if (verbsZipPath) {
|
|
49
|
+
const detailed = grid3Parser.parseZipDetailed(verbsZipPath);
|
|
50
|
+
const forms = detailed.verbs.get(base) || detailed.verbs.get(base.toLowerCase());
|
|
51
|
+
if (forms && forms.length > 0) {
|
|
52
|
+
return this.generateFromGrid3Conditions(base, forms, lang);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return this.generateFromEngineSlots(base, pos, engine, lang);
|
|
56
|
+
}
|
|
57
|
+
conditionsToTags(conditions) {
|
|
58
|
+
const tags = [];
|
|
59
|
+
for (const [dim, value] of conditions) {
|
|
60
|
+
const mapped = CONDITION_TAG_MAP[dim]?.[value];
|
|
61
|
+
if (mapped) {
|
|
62
|
+
tags.push(...mapped);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return tags.length > 0 ? tags : ['UNKNOWN'];
|
|
66
|
+
}
|
|
67
|
+
deduplicate(forms) {
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
return forms.filter((f) => {
|
|
70
|
+
const key = `${f.value}|${f.tags.sort().join(',')}`;
|
|
71
|
+
if (seen.has(key))
|
|
72
|
+
return false;
|
|
73
|
+
seen.add(key);
|
|
74
|
+
return true;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.WordFormGenerator = WordFormGenerator;
|