@lokascript/semantic 1.0.0 → 1.1.0
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-ar.ar.global.js +2 -2
- package/dist/browser-core.core.global.js +2 -2
- package/dist/browser-de.de.global.js +2 -2
- package/dist/browser-east-asian.east-asian.global.js +2 -2
- package/dist/browser-en-tr.en-tr.global.js +2 -2
- package/dist/browser-en.en.global.js +2 -2
- package/dist/browser-es-en.es-en.global.js +2 -2
- package/dist/browser-es.es.global.js +2 -2
- package/dist/browser-fr.fr.global.js +2 -2
- package/dist/browser-id.id.global.js +2 -2
- package/dist/browser-ja.ja.global.js +2 -2
- package/dist/browser-ko.ko.global.js +2 -2
- package/dist/browser-lazy.lazy.global.js +2 -2
- package/dist/browser-priority.priority.global.js +2 -2
- package/dist/browser-pt.pt.global.js +2 -2
- package/dist/browser-qu.qu.global.js +2 -2
- package/dist/browser-sw.sw.global.js +2 -2
- package/dist/browser-tr.tr.global.js +2 -2
- package/dist/browser-western.western.global.js +2 -2
- package/dist/browser-zh.zh.global.js +2 -2
- package/dist/browser.global.js +2 -2
- package/dist/browser.global.js.map +1 -1
- package/dist/index.cjs +13042 -17462
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +49 -5
- package/dist/index.d.ts +49 -5
- package/dist/index.js +14044 -18464
- package/dist/index.js.map +1 -1
- package/dist/languages/ar.d.ts +1 -1
- package/dist/languages/ar.js +31 -44
- package/dist/languages/ar.js.map +1 -1
- package/dist/languages/de.d.ts +1 -1
- package/dist/languages/de.js +14 -2
- package/dist/languages/de.js.map +1 -1
- package/dist/languages/en.d.ts +1 -1
- package/dist/languages/en.js +558 -12
- package/dist/languages/en.js.map +1 -1
- package/dist/languages/es.d.ts +1 -1
- package/dist/languages/es.js +16 -0
- package/dist/languages/es.js.map +1 -1
- package/dist/languages/fr.d.ts +1 -1
- package/dist/languages/fr.js +14 -2
- package/dist/languages/fr.js.map +1 -1
- package/dist/languages/id.d.ts +1 -1
- package/dist/languages/id.js +14 -2
- package/dist/languages/id.js.map +1 -1
- package/dist/languages/ja.d.ts +1 -1
- package/dist/languages/ja.js +18 -3
- package/dist/languages/ja.js.map +1 -1
- package/dist/languages/ko.d.ts +8 -1
- package/dist/languages/ko.js +75 -43
- package/dist/languages/ko.js.map +1 -1
- package/dist/languages/pt.d.ts +1 -1
- package/dist/languages/pt.js +17 -0
- package/dist/languages/pt.js.map +1 -1
- package/dist/languages/qu.d.ts +12 -1
- package/dist/languages/qu.js +77 -2
- package/dist/languages/qu.js.map +1 -1
- package/dist/languages/sw.d.ts +1 -1
- package/dist/languages/sw.js.map +1 -1
- package/dist/languages/tr.d.ts +9 -1
- package/dist/languages/tr.js +96 -72
- package/dist/languages/tr.js.map +1 -1
- package/dist/languages/zh.d.ts +1 -1
- package/dist/languages/zh.js +16 -0
- package/dist/languages/zh.js.map +1 -1
- package/dist/{types-C4dcj53L.d.ts → types-BY3Id07j.d.ts} +20 -5
- package/package.json +20 -29
- package/src/generators/command-schemas.ts +21 -10
- package/src/generators/event-handler-generator.ts +50 -44
- package/src/generators/language-profiles.ts +6 -0
- package/src/generators/pattern-generator.ts +883 -1
- package/src/generators/profiles/arabic.ts +19 -3
- package/src/generators/profiles/bengali.ts +12 -1
- package/src/generators/profiles/chinese.ts +15 -0
- package/src/generators/profiles/french.ts +12 -1
- package/src/generators/profiles/german.ts +12 -1
- package/src/generators/profiles/hebrew.ts +148 -0
- package/src/generators/profiles/hindi.ts +12 -1
- package/src/generators/profiles/index.ts +2 -0
- package/src/generators/profiles/indonesian.ts +12 -1
- package/src/generators/profiles/italian.ts +16 -0
- package/src/generators/profiles/japanese.ts +11 -2
- package/src/generators/profiles/korean.ts +15 -1
- package/src/generators/profiles/polish.ts +12 -0
- package/src/generators/profiles/portuguese.ts +16 -0
- package/src/generators/profiles/russian.ts +11 -0
- package/src/generators/profiles/spanish.ts +15 -0
- package/src/generators/profiles/spanishMexico.ts +176 -0
- package/src/generators/profiles/thai.ts +11 -0
- package/src/generators/profiles/turkish.ts +49 -7
- package/src/generators/profiles/types.ts +21 -5
- package/src/generators/profiles/ukrainian.ts +11 -0
- package/src/generators/profiles/vietnamese.ts +11 -0
- package/src/language-building-schema.ts +111 -0
- package/src/languages/_all.ts +5 -1
- package/src/languages/es-MX.ts +32 -0
- package/src/languages/he.ts +15 -0
- package/src/parser/pattern-matcher.ts +10 -1
- package/src/parser/semantic-parser.ts +3 -0
- package/src/patterns/add/ar.ts +3 -59
- package/src/patterns/add/index.ts +5 -1
- package/src/patterns/add/ja.ts +3 -81
- package/src/patterns/add/ko.ts +3 -62
- package/src/patterns/add/qu.ts +69 -0
- package/src/patterns/add/tr.ts +3 -59
- package/src/patterns/builders.ts +1 -0
- package/src/patterns/decrement/tr.ts +3 -36
- package/src/patterns/event-handler/ar.ts +3 -139
- package/src/patterns/event-handler/he.ts +15 -0
- package/src/patterns/event-handler/index.ts +5 -1
- package/src/patterns/event-handler/ja.ts +3 -106
- package/src/patterns/event-handler/ko.ts +3 -121
- package/src/patterns/event-handler/ms.ts +45 -20
- package/src/patterns/event-handler/tr.ts +3 -158
- package/src/patterns/get/ar.ts +3 -37
- package/src/patterns/get/ja.ts +3 -41
- package/src/patterns/get/ko.ts +3 -41
- package/src/patterns/grammar-transformed/ja.ts +3 -1701
- package/src/patterns/grammar-transformed/ko.ts +3 -1299
- package/src/patterns/grammar-transformed/tr.ts +3 -1055
- package/src/patterns/hide/ar.ts +3 -55
- package/src/patterns/hide/ja.ts +3 -57
- package/src/patterns/hide/ko.ts +3 -57
- package/src/patterns/hide/tr.ts +3 -53
- package/src/patterns/increment/tr.ts +3 -40
- package/src/patterns/put/ar.ts +3 -62
- package/src/patterns/put/ja.ts +3 -63
- package/src/patterns/put/ko.ts +3 -55
- package/src/patterns/put/tr.ts +3 -55
- package/src/patterns/remove/ar.ts +3 -59
- package/src/patterns/remove/index.ts +5 -1
- package/src/patterns/remove/ja.ts +3 -62
- package/src/patterns/remove/ko.ts +3 -66
- package/src/patterns/remove/qu.ts +69 -0
- package/src/patterns/remove/tr.ts +3 -66
- package/src/patterns/set/ar.ts +3 -72
- package/src/patterns/set/ja.ts +3 -74
- package/src/patterns/set/ko.ts +3 -73
- package/src/patterns/set/tr.ts +3 -95
- package/src/patterns/show/ar.ts +3 -55
- package/src/patterns/show/ja.ts +3 -57
- package/src/patterns/show/ko.ts +3 -61
- package/src/patterns/show/tr.ts +3 -53
- package/src/patterns/take/ar.ts +3 -39
- package/src/patterns/toggle/ar.ts +3 -49
- package/src/patterns/toggle/index.ts +5 -1
- package/src/patterns/toggle/ja.ts +3 -144
- package/src/patterns/toggle/ko.ts +3 -101
- package/src/patterns/toggle/qu.ts +90 -0
- package/src/patterns/toggle/tr.ts +3 -76
- package/src/registry.ts +179 -15
- package/src/tokenizers/arabic.ts +13 -46
- package/src/tokenizers/bengali.ts +2 -16
- package/src/tokenizers/he.ts +542 -0
- package/src/tokenizers/index.ts +1 -0
- package/src/tokenizers/japanese.ts +3 -1
- package/src/tokenizers/korean.ts +104 -48
- package/src/tokenizers/ms.ts +3 -0
- package/src/tokenizers/quechua.ts +101 -2
- package/src/tokenizers/turkish.ts +64 -69
- package/src/types.ts +13 -0
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { LanguagePattern, PatternToken, ExtractionRule } from '../types';
|
|
10
|
-
import type { LanguageProfile } from './language-profiles';
|
|
10
|
+
import type { LanguageProfile, KeywordTranslation, RoleMarker } from './language-profiles';
|
|
11
11
|
import type { CommandSchema, RoleSpec } from './command-schemas';
|
|
12
12
|
import { getDefinedSchemas } from './command-schemas';
|
|
13
13
|
|
|
@@ -167,8 +167,16 @@ export function generatePatternsForLanguage(
|
|
|
167
167
|
continue;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
// Generate simple command patterns
|
|
170
171
|
const variants = generatePatternVariants(schema, profile, config);
|
|
171
172
|
patterns.push(...variants);
|
|
173
|
+
|
|
174
|
+
// Generate event handler patterns (on [event] [command] [patient])
|
|
175
|
+
// Only generate for languages with eventHandler configuration
|
|
176
|
+
if (profile.eventHandler?.eventMarker) {
|
|
177
|
+
const eventHandlerPatterns = generateEventHandlerPatterns(schema, profile, config);
|
|
178
|
+
patterns.push(...eventHandlerPatterns);
|
|
179
|
+
}
|
|
172
180
|
}
|
|
173
181
|
|
|
174
182
|
return patterns;
|
|
@@ -227,6 +235,880 @@ export function generateAllPatterns(
|
|
|
227
235
|
return patterns;
|
|
228
236
|
}
|
|
229
237
|
|
|
238
|
+
/**
|
|
239
|
+
* Generate event handler patterns for a command in a specific language.
|
|
240
|
+
*
|
|
241
|
+
* Creates patterns that wrap commands with event handlers (e.g., "on click toggle .active").
|
|
242
|
+
* Automatically handles SOV, SVO, and VSO word orders based on language profile.
|
|
243
|
+
*
|
|
244
|
+
* @param commandSchema - The command to wrap (toggle, add, remove, etc.)
|
|
245
|
+
* @param profile - Language profile with eventHandler configuration
|
|
246
|
+
* @param config - Generator configuration
|
|
247
|
+
* @returns Array of event handler patterns (empty if profile lacks eventHandler config)
|
|
248
|
+
*/
|
|
249
|
+
export function generateEventHandlerPatterns(
|
|
250
|
+
commandSchema: CommandSchema,
|
|
251
|
+
profile: LanguageProfile,
|
|
252
|
+
config: GeneratorConfig = defaultConfig
|
|
253
|
+
): LanguagePattern[] {
|
|
254
|
+
// Only generate if profile has eventHandler configuration
|
|
255
|
+
if (!profile.eventHandler || !profile.eventHandler.eventMarker) {
|
|
256
|
+
return [];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const patterns: LanguagePattern[] = [];
|
|
260
|
+
const eventMarker = profile.eventHandler.eventMarker;
|
|
261
|
+
const keyword = profile.keywords[commandSchema.action];
|
|
262
|
+
|
|
263
|
+
if (!keyword) {
|
|
264
|
+
return []; // No translation for this command
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Check if this is a two-role command (like put, set)
|
|
268
|
+
const requiredRoles = commandSchema.roles.filter(r => r.required);
|
|
269
|
+
const hasTwoRequiredRoles = requiredRoles.length === 2;
|
|
270
|
+
|
|
271
|
+
// Generate pattern based on word order
|
|
272
|
+
if (profile.wordOrder === 'SOV') {
|
|
273
|
+
if (hasTwoRequiredRoles) {
|
|
274
|
+
// Two-role SOV pattern for put/set commands
|
|
275
|
+
// Japanese put: 入力 で "test" を #output に 入れる
|
|
276
|
+
// Korean set: 변경 할 때 x 를 10 으로 설정
|
|
277
|
+
patterns.push(
|
|
278
|
+
generateSOVTwoRoleEventHandlerPattern(commandSchema, profile, keyword, eventMarker, config)
|
|
279
|
+
);
|
|
280
|
+
} else {
|
|
281
|
+
// SOV: [event] [eventMarker] [destination? destMarker?] [patient] [patientMarker] [verb]
|
|
282
|
+
// Japanese: クリック で #button の .active を 切り替え
|
|
283
|
+
// Korean: 클릭 할 때 #button 의 .active 를 토글
|
|
284
|
+
// Turkish: tıklama da #button ın .active i değiştir
|
|
285
|
+
patterns.push(
|
|
286
|
+
generateSOVEventHandlerPattern(commandSchema, profile, keyword, eventMarker, config)
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
// For multi-word event markers with no-space alternatives (Korean compact forms),
|
|
290
|
+
// also generate a pattern that accepts the compact form
|
|
291
|
+
// Example: 클릭할때 .active를토글 (할때 as single token)
|
|
292
|
+
const markerWords = eventMarker.primary.split(/\s+/);
|
|
293
|
+
const hasNoSpaceAlternative = eventMarker.alternatives?.some(
|
|
294
|
+
alt => !alt.includes(' ') && alt.length > 1
|
|
295
|
+
);
|
|
296
|
+
if (markerWords.length > 1 && hasNoSpaceAlternative) {
|
|
297
|
+
patterns.push(
|
|
298
|
+
generateSOVCompactEventHandlerPattern(
|
|
299
|
+
commandSchema,
|
|
300
|
+
profile,
|
|
301
|
+
keyword,
|
|
302
|
+
eventMarker,
|
|
303
|
+
config
|
|
304
|
+
)
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Add simple pattern (no patient required, defaults to 'me')
|
|
309
|
+
// Supports: クリック で 増加 (click on increment)
|
|
310
|
+
patterns.push(
|
|
311
|
+
generateSOVSimpleEventHandlerPattern(commandSchema, profile, keyword, eventMarker, config)
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
// Add temporal pattern if temporalMarkers defined
|
|
315
|
+
// Supports: クリック の 時 .active を 切り替え (click's time toggle .active)
|
|
316
|
+
const temporalPattern = generateSOVTemporalEventHandlerPattern(
|
|
317
|
+
commandSchema,
|
|
318
|
+
profile,
|
|
319
|
+
keyword,
|
|
320
|
+
config
|
|
321
|
+
);
|
|
322
|
+
if (temporalPattern) {
|
|
323
|
+
patterns.push(temporalPattern);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
} else if (profile.wordOrder === 'VSO') {
|
|
327
|
+
if (hasTwoRequiredRoles) {
|
|
328
|
+
// Two-role VSO pattern for put/set commands
|
|
329
|
+
// Arabic put: عند الإدخال ضع "test" في #output
|
|
330
|
+
patterns.push(
|
|
331
|
+
generateVSOTwoRoleEventHandlerPattern(commandSchema, profile, keyword, eventMarker, config)
|
|
332
|
+
);
|
|
333
|
+
} else {
|
|
334
|
+
// VSO: [eventMarker] [event] [verb] [patient] [على destination?]
|
|
335
|
+
// Arabic: عند النقر بدّل .active على #button
|
|
336
|
+
patterns.push(
|
|
337
|
+
generateVSOEventHandlerPattern(commandSchema, profile, keyword, eventMarker, config)
|
|
338
|
+
);
|
|
339
|
+
|
|
340
|
+
// Add negated event pattern variant for languages with negation markers
|
|
341
|
+
// Pattern: [eventMarker] [negation] [event] [verb] [patient]
|
|
342
|
+
// Example: عند عدم التركيز أخف #tooltip = "on blur hide #tooltip"
|
|
343
|
+
if (profile.eventHandler?.negationMarker) {
|
|
344
|
+
patterns.push(
|
|
345
|
+
generateVSONegatedEventHandlerPattern(
|
|
346
|
+
commandSchema,
|
|
347
|
+
profile,
|
|
348
|
+
keyword,
|
|
349
|
+
eventMarker,
|
|
350
|
+
config
|
|
351
|
+
)
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Add proclitic-prefixed pattern variant for Arabic
|
|
356
|
+
// Pattern: [proclitic]? [event] [verb] [patient]
|
|
357
|
+
// Example: والنقر بدّل .active (and click toggle .active)
|
|
358
|
+
if (profile.tokenization?.prefixes) {
|
|
359
|
+
patterns.push(
|
|
360
|
+
generateVSOProcliticEventHandlerPattern(commandSchema, profile, keyword, config)
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
} else {
|
|
365
|
+
// SVO: Use VSO pattern structure for event handlers
|
|
366
|
+
if (hasTwoRequiredRoles) {
|
|
367
|
+
patterns.push(
|
|
368
|
+
generateVSOTwoRoleEventHandlerPattern(commandSchema, profile, keyword, eventMarker, config)
|
|
369
|
+
);
|
|
370
|
+
} else {
|
|
371
|
+
patterns.push(
|
|
372
|
+
generateVSOEventHandlerPattern(commandSchema, profile, keyword, eventMarker, config)
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return patterns;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Generate SOV event handler pattern (Japanese, Korean, Turkish).
|
|
382
|
+
*/
|
|
383
|
+
function generateSOVEventHandlerPattern(
|
|
384
|
+
commandSchema: CommandSchema,
|
|
385
|
+
profile: LanguageProfile,
|
|
386
|
+
keyword: KeywordTranslation,
|
|
387
|
+
eventMarker: RoleMarker,
|
|
388
|
+
config: GeneratorConfig
|
|
389
|
+
): LanguagePattern {
|
|
390
|
+
const tokens: PatternToken[] = [];
|
|
391
|
+
|
|
392
|
+
// Event role
|
|
393
|
+
tokens.push({ type: 'role', role: 'event', optional: false });
|
|
394
|
+
|
|
395
|
+
// Event marker (after event in SOV)
|
|
396
|
+
// Handle multi-word markers like Korean "할 때" by splitting into separate tokens
|
|
397
|
+
if (eventMarker.position === 'after') {
|
|
398
|
+
const markerWords = eventMarker.primary.split(/\s+/);
|
|
399
|
+
if (markerWords.length > 1) {
|
|
400
|
+
// Multi-word marker: create a token for each word
|
|
401
|
+
for (const word of markerWords) {
|
|
402
|
+
tokens.push({ type: 'literal', value: word });
|
|
403
|
+
}
|
|
404
|
+
} else {
|
|
405
|
+
// Single-word marker: include alternatives
|
|
406
|
+
const markerToken: PatternToken = eventMarker.alternatives
|
|
407
|
+
? { type: 'literal', value: eventMarker.primary, alternatives: eventMarker.alternatives }
|
|
408
|
+
: { type: 'literal', value: eventMarker.primary };
|
|
409
|
+
tokens.push(markerToken);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Optional destination with its marker
|
|
414
|
+
const destMarker = profile.roleMarkers.destination;
|
|
415
|
+
if (destMarker) {
|
|
416
|
+
tokens.push({
|
|
417
|
+
type: 'group',
|
|
418
|
+
optional: true,
|
|
419
|
+
tokens: [
|
|
420
|
+
{ type: 'role', role: 'destination', optional: true },
|
|
421
|
+
destMarker.alternatives
|
|
422
|
+
? { type: 'literal', value: destMarker.primary, alternatives: destMarker.alternatives }
|
|
423
|
+
: { type: 'literal', value: destMarker.primary },
|
|
424
|
+
],
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// Patient role
|
|
429
|
+
tokens.push({ type: 'role', role: 'patient', optional: false });
|
|
430
|
+
|
|
431
|
+
// Patient marker (postposition/particle after patient)
|
|
432
|
+
const patientMarker = profile.roleMarkers.patient;
|
|
433
|
+
if (patientMarker) {
|
|
434
|
+
const patMarkerToken: PatternToken = patientMarker.alternatives
|
|
435
|
+
? { type: 'literal', value: patientMarker.primary, alternatives: patientMarker.alternatives }
|
|
436
|
+
: { type: 'literal', value: patientMarker.primary };
|
|
437
|
+
tokens.push(patMarkerToken);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Command verb at end (SOV)
|
|
441
|
+
const verbToken: PatternToken = keyword.alternatives
|
|
442
|
+
? { type: 'literal', value: keyword.primary, alternatives: keyword.alternatives }
|
|
443
|
+
: { type: 'literal', value: keyword.primary };
|
|
444
|
+
tokens.push(verbToken);
|
|
445
|
+
|
|
446
|
+
return {
|
|
447
|
+
id: `${commandSchema.action}-event-${profile.code}-sov`,
|
|
448
|
+
language: profile.code,
|
|
449
|
+
command: 'on', // This is an event handler pattern
|
|
450
|
+
priority: (config.basePriority ?? 100) + 50, // Higher priority than simple commands
|
|
451
|
+
template: {
|
|
452
|
+
format: `{event} ${eventMarker.primary} {destination?} {patient} ${patientMarker?.primary || ''} ${keyword.primary}`,
|
|
453
|
+
tokens,
|
|
454
|
+
},
|
|
455
|
+
extraction: {
|
|
456
|
+
action: { value: commandSchema.action }, // Extract the wrapped command
|
|
457
|
+
event: { fromRole: 'event' },
|
|
458
|
+
patient: { fromRole: 'patient' },
|
|
459
|
+
destination: { fromRole: 'destination', default: { type: 'reference', value: 'me' } },
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Generate SOV compact event handler pattern for languages with no-space forms.
|
|
466
|
+
*
|
|
467
|
+
* This handles Korean compact forms where the event marker is attached directly
|
|
468
|
+
* to the event word without a space:
|
|
469
|
+
* - 클릭할때 .active를토글 (click+when toggle .active)
|
|
470
|
+
*
|
|
471
|
+
* The pattern uses a single token for the no-space marker alternatives.
|
|
472
|
+
*/
|
|
473
|
+
function generateSOVCompactEventHandlerPattern(
|
|
474
|
+
commandSchema: CommandSchema,
|
|
475
|
+
profile: LanguageProfile,
|
|
476
|
+
keyword: KeywordTranslation,
|
|
477
|
+
eventMarker: RoleMarker,
|
|
478
|
+
config: GeneratorConfig
|
|
479
|
+
): LanguagePattern {
|
|
480
|
+
const tokens: PatternToken[] = [];
|
|
481
|
+
|
|
482
|
+
// Event role
|
|
483
|
+
tokens.push({ type: 'role', role: 'event', optional: false });
|
|
484
|
+
|
|
485
|
+
// Event marker as single token (using no-space alternatives)
|
|
486
|
+
// Filter alternatives to only include no-space versions
|
|
487
|
+
const noSpaceAlternatives =
|
|
488
|
+
eventMarker.alternatives?.filter(alt => !alt.includes(' ') && alt.length > 1) || [];
|
|
489
|
+
|
|
490
|
+
if (noSpaceAlternatives.length > 0) {
|
|
491
|
+
tokens.push({
|
|
492
|
+
type: 'literal',
|
|
493
|
+
value: noSpaceAlternatives[0],
|
|
494
|
+
alternatives: noSpaceAlternatives.slice(1),
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Optional destination with its marker
|
|
499
|
+
const destMarker = profile.roleMarkers.destination;
|
|
500
|
+
if (destMarker) {
|
|
501
|
+
tokens.push({
|
|
502
|
+
type: 'group',
|
|
503
|
+
optional: true,
|
|
504
|
+
tokens: [
|
|
505
|
+
{ type: 'role', role: 'destination', optional: true },
|
|
506
|
+
destMarker.alternatives
|
|
507
|
+
? { type: 'literal', value: destMarker.primary, alternatives: destMarker.alternatives }
|
|
508
|
+
: { type: 'literal', value: destMarker.primary },
|
|
509
|
+
],
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Patient role
|
|
514
|
+
tokens.push({ type: 'role', role: 'patient', optional: false });
|
|
515
|
+
|
|
516
|
+
// Patient marker (postposition/particle after patient)
|
|
517
|
+
const patientMarker = profile.roleMarkers.patient;
|
|
518
|
+
if (patientMarker) {
|
|
519
|
+
const patMarkerToken: PatternToken = patientMarker.alternatives
|
|
520
|
+
? { type: 'literal', value: patientMarker.primary, alternatives: patientMarker.alternatives }
|
|
521
|
+
: { type: 'literal', value: patientMarker.primary };
|
|
522
|
+
tokens.push(patMarkerToken);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// Command verb at end (SOV)
|
|
526
|
+
const verbToken: PatternToken = keyword.alternatives
|
|
527
|
+
? { type: 'literal', value: keyword.primary, alternatives: keyword.alternatives }
|
|
528
|
+
: { type: 'literal', value: keyword.primary };
|
|
529
|
+
tokens.push(verbToken);
|
|
530
|
+
|
|
531
|
+
return {
|
|
532
|
+
id: `${commandSchema.action}-event-${profile.code}-sov-compact`,
|
|
533
|
+
language: profile.code,
|
|
534
|
+
command: 'on', // This is an event handler pattern
|
|
535
|
+
priority: (config.basePriority ?? 100) + 52, // Slightly higher priority for compact forms
|
|
536
|
+
template: {
|
|
537
|
+
format: `{event}${noSpaceAlternatives[0] || ''} {patient} ${keyword.primary}`,
|
|
538
|
+
tokens,
|
|
539
|
+
},
|
|
540
|
+
extraction: {
|
|
541
|
+
action: { value: commandSchema.action }, // Extract the wrapped command
|
|
542
|
+
event: { fromRole: 'event' },
|
|
543
|
+
patient: { fromRole: 'patient' },
|
|
544
|
+
destination: { fromRole: 'destination', default: { type: 'reference', value: 'me' } },
|
|
545
|
+
},
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Generate SOV simple event handler pattern (patient optional, defaults to 'me').
|
|
551
|
+
*
|
|
552
|
+
* Supports patterns like:
|
|
553
|
+
* - Japanese: クリック で 増加 (click on increment)
|
|
554
|
+
* - Korean: 클릭 할 때 증가 (click when increment)
|
|
555
|
+
* - Turkish: tıklama da artır (click on increment)
|
|
556
|
+
*
|
|
557
|
+
* The patient is not explicitly specified - it defaults to 'me' (current element).
|
|
558
|
+
*/
|
|
559
|
+
function generateSOVSimpleEventHandlerPattern(
|
|
560
|
+
commandSchema: CommandSchema,
|
|
561
|
+
profile: LanguageProfile,
|
|
562
|
+
keyword: KeywordTranslation,
|
|
563
|
+
eventMarker: RoleMarker,
|
|
564
|
+
config: GeneratorConfig
|
|
565
|
+
): LanguagePattern {
|
|
566
|
+
const tokens: PatternToken[] = [];
|
|
567
|
+
|
|
568
|
+
// Event role
|
|
569
|
+
tokens.push({ type: 'role', role: 'event', optional: false });
|
|
570
|
+
|
|
571
|
+
// Event marker (after event in SOV)
|
|
572
|
+
if (eventMarker.position === 'after') {
|
|
573
|
+
const markerWords = eventMarker.primary.split(/\s+/);
|
|
574
|
+
if (markerWords.length > 1) {
|
|
575
|
+
// Multi-word marker: create a token for each word
|
|
576
|
+
for (const word of markerWords) {
|
|
577
|
+
tokens.push({ type: 'literal', value: word });
|
|
578
|
+
}
|
|
579
|
+
} else {
|
|
580
|
+
const markerToken: PatternToken = eventMarker.alternatives
|
|
581
|
+
? { type: 'literal', value: eventMarker.primary, alternatives: eventMarker.alternatives }
|
|
582
|
+
: { type: 'literal', value: eventMarker.primary };
|
|
583
|
+
tokens.push(markerToken);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// Command verb at end (SOV) - no patient required
|
|
588
|
+
const verbToken: PatternToken = keyword.alternatives
|
|
589
|
+
? { type: 'literal', value: keyword.primary, alternatives: keyword.alternatives }
|
|
590
|
+
: { type: 'literal', value: keyword.primary };
|
|
591
|
+
tokens.push(verbToken);
|
|
592
|
+
|
|
593
|
+
return {
|
|
594
|
+
id: `${commandSchema.action}-event-${profile.code}-sov-simple`,
|
|
595
|
+
language: profile.code,
|
|
596
|
+
command: 'on',
|
|
597
|
+
priority: (config.basePriority ?? 100) + 48, // Lower than full pattern (50) but higher than base
|
|
598
|
+
template: {
|
|
599
|
+
format: `{event} ${eventMarker.primary} ${keyword.primary}`,
|
|
600
|
+
tokens,
|
|
601
|
+
},
|
|
602
|
+
extraction: {
|
|
603
|
+
action: { value: commandSchema.action },
|
|
604
|
+
event: { fromRole: 'event' },
|
|
605
|
+
patient: { default: { type: 'reference', value: 'me' } }, // Default to 'me'
|
|
606
|
+
},
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Generate SOV temporal event handler pattern.
|
|
612
|
+
*
|
|
613
|
+
* Supports patterns with temporal markers like:
|
|
614
|
+
* - Japanese: クリック 時 .active を 切り替え (click time toggle .active)
|
|
615
|
+
* - Japanese: クリック の 時 .active を 切り替え (click's time toggle .active)
|
|
616
|
+
*
|
|
617
|
+
* Uses profile.eventHandler.temporalMarkers for language-specific temporal words.
|
|
618
|
+
*/
|
|
619
|
+
function generateSOVTemporalEventHandlerPattern(
|
|
620
|
+
commandSchema: CommandSchema,
|
|
621
|
+
profile: LanguageProfile,
|
|
622
|
+
keyword: KeywordTranslation,
|
|
623
|
+
config: GeneratorConfig
|
|
624
|
+
): LanguagePattern | null {
|
|
625
|
+
const temporalMarkers = profile.eventHandler?.temporalMarkers;
|
|
626
|
+
if (!temporalMarkers || temporalMarkers.length === 0) return null;
|
|
627
|
+
|
|
628
|
+
const tokens: PatternToken[] = [];
|
|
629
|
+
|
|
630
|
+
// Event role
|
|
631
|
+
tokens.push({ type: 'role', role: 'event', optional: false });
|
|
632
|
+
|
|
633
|
+
// Optional possessive marker (の in Japanese)
|
|
634
|
+
if (profile.possessive?.marker) {
|
|
635
|
+
tokens.push({
|
|
636
|
+
type: 'group',
|
|
637
|
+
optional: true,
|
|
638
|
+
tokens: [{ type: 'literal', value: profile.possessive.marker }],
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Temporal marker (時, とき in Japanese)
|
|
643
|
+
tokens.push({
|
|
644
|
+
type: 'literal',
|
|
645
|
+
value: temporalMarkers[0],
|
|
646
|
+
alternatives: temporalMarkers.slice(1),
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
// Patient role
|
|
650
|
+
tokens.push({ type: 'role', role: 'patient', optional: false });
|
|
651
|
+
|
|
652
|
+
// Patient marker
|
|
653
|
+
const patientMarker = profile.roleMarkers.patient;
|
|
654
|
+
if (patientMarker?.primary) {
|
|
655
|
+
const patMarkerToken: PatternToken = patientMarker.alternatives
|
|
656
|
+
? { type: 'literal', value: patientMarker.primary, alternatives: patientMarker.alternatives }
|
|
657
|
+
: { type: 'literal', value: patientMarker.primary };
|
|
658
|
+
tokens.push(patMarkerToken);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// Command verb at end
|
|
662
|
+
const verbToken: PatternToken = keyword.alternatives
|
|
663
|
+
? { type: 'literal', value: keyword.primary, alternatives: keyword.alternatives }
|
|
664
|
+
: { type: 'literal', value: keyword.primary };
|
|
665
|
+
tokens.push(verbToken);
|
|
666
|
+
|
|
667
|
+
return {
|
|
668
|
+
id: `${commandSchema.action}-event-${profile.code}-sov-temporal`,
|
|
669
|
+
language: profile.code,
|
|
670
|
+
command: 'on',
|
|
671
|
+
priority: (config.basePriority ?? 100) + 49, // Between simple and full pattern
|
|
672
|
+
template: {
|
|
673
|
+
format: `{event} ${temporalMarkers[0]} {patient} ${keyword.primary}`,
|
|
674
|
+
tokens,
|
|
675
|
+
},
|
|
676
|
+
extraction: {
|
|
677
|
+
action: { value: commandSchema.action },
|
|
678
|
+
event: { fromRole: 'event' },
|
|
679
|
+
patient: { fromRole: 'patient' },
|
|
680
|
+
},
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* Generate VSO event handler pattern (Arabic).
|
|
686
|
+
*/
|
|
687
|
+
function generateVSOEventHandlerPattern(
|
|
688
|
+
commandSchema: CommandSchema,
|
|
689
|
+
profile: LanguageProfile,
|
|
690
|
+
keyword: KeywordTranslation,
|
|
691
|
+
eventMarker: RoleMarker,
|
|
692
|
+
config: GeneratorConfig
|
|
693
|
+
): LanguagePattern {
|
|
694
|
+
const tokens: PatternToken[] = [];
|
|
695
|
+
|
|
696
|
+
// Event marker (before event in VSO)
|
|
697
|
+
if (eventMarker.position === 'before') {
|
|
698
|
+
const markerToken: PatternToken = eventMarker.alternatives
|
|
699
|
+
? { type: 'literal', value: eventMarker.primary, alternatives: eventMarker.alternatives }
|
|
700
|
+
: { type: 'literal', value: eventMarker.primary };
|
|
701
|
+
tokens.push(markerToken);
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
// Event role
|
|
705
|
+
tokens.push({ type: 'role', role: 'event', optional: false });
|
|
706
|
+
|
|
707
|
+
// Command verb (verb comes early in VSO)
|
|
708
|
+
const verbToken: PatternToken = keyword.alternatives
|
|
709
|
+
? { type: 'literal', value: keyword.primary, alternatives: keyword.alternatives }
|
|
710
|
+
: { type: 'literal', value: keyword.primary };
|
|
711
|
+
tokens.push(verbToken);
|
|
712
|
+
|
|
713
|
+
// Patient role
|
|
714
|
+
tokens.push({ type: 'role', role: 'patient', optional: false });
|
|
715
|
+
|
|
716
|
+
// Optional destination with preposition
|
|
717
|
+
const destMarker = profile.roleMarkers.destination;
|
|
718
|
+
if (destMarker) {
|
|
719
|
+
tokens.push({
|
|
720
|
+
type: 'group',
|
|
721
|
+
optional: true,
|
|
722
|
+
tokens: [
|
|
723
|
+
destMarker.alternatives
|
|
724
|
+
? { type: 'literal', value: destMarker.primary, alternatives: destMarker.alternatives }
|
|
725
|
+
: { type: 'literal', value: destMarker.primary },
|
|
726
|
+
{ type: 'role', role: 'destination', optional: true },
|
|
727
|
+
],
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
return {
|
|
732
|
+
id: `${commandSchema.action}-event-${profile.code}-vso`,
|
|
733
|
+
language: profile.code,
|
|
734
|
+
command: 'on', // This is an event handler pattern
|
|
735
|
+
priority: (config.basePriority ?? 100) + 50, // Higher priority than simple commands
|
|
736
|
+
template: {
|
|
737
|
+
format: `${eventMarker.primary} {event} ${keyword.primary} {patient} ${destMarker?.primary || ''} {destination?}`,
|
|
738
|
+
tokens,
|
|
739
|
+
},
|
|
740
|
+
extraction: {
|
|
741
|
+
action: { value: commandSchema.action }, // Extract the wrapped command
|
|
742
|
+
event: { fromRole: 'event' },
|
|
743
|
+
patient: { fromRole: 'patient' },
|
|
744
|
+
destination: { fromRole: 'destination', default: { type: 'reference', value: 'me' } },
|
|
745
|
+
},
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Generate SOV two-role event handler pattern (for put/set commands).
|
|
751
|
+
*
|
|
752
|
+
* Patterns:
|
|
753
|
+
* - Japanese put: 入力 で "test" を #output に 入れる
|
|
754
|
+
* [event] [eventMarker] [patient] [patientMarker] [destination] [destMarker] [verb]
|
|
755
|
+
* - Korean set: 변경 할 때 x 를 10 으로 설정
|
|
756
|
+
* [event] [eventMarker] [role1] [role1Marker] [role2] [role2Marker] [verb]
|
|
757
|
+
* - Turkish put: giriş de "test" i #output a koy
|
|
758
|
+
* [event] [eventMarker] [patient] [patientMarker] [destination] [destMarker] [verb]
|
|
759
|
+
*/
|
|
760
|
+
function generateSOVTwoRoleEventHandlerPattern(
|
|
761
|
+
commandSchema: CommandSchema,
|
|
762
|
+
profile: LanguageProfile,
|
|
763
|
+
keyword: KeywordTranslation,
|
|
764
|
+
eventMarker: RoleMarker,
|
|
765
|
+
config: GeneratorConfig
|
|
766
|
+
): LanguagePattern {
|
|
767
|
+
const tokens: PatternToken[] = [];
|
|
768
|
+
|
|
769
|
+
// Event role
|
|
770
|
+
tokens.push({ type: 'role', role: 'event', optional: false });
|
|
771
|
+
|
|
772
|
+
// Event marker (after event in SOV)
|
|
773
|
+
// Handle multi-word markers like Korean "할 때" by splitting into separate tokens
|
|
774
|
+
if (eventMarker.position === 'after') {
|
|
775
|
+
const markerWords = eventMarker.primary.split(/\s+/);
|
|
776
|
+
if (markerWords.length > 1) {
|
|
777
|
+
// Multi-word marker: create a token for each word
|
|
778
|
+
for (const word of markerWords) {
|
|
779
|
+
tokens.push({ type: 'literal', value: word });
|
|
780
|
+
}
|
|
781
|
+
} else {
|
|
782
|
+
// Single-word marker: include alternatives
|
|
783
|
+
const markerToken: PatternToken = eventMarker.alternatives
|
|
784
|
+
? { type: 'literal', value: eventMarker.primary, alternatives: eventMarker.alternatives }
|
|
785
|
+
: { type: 'literal', value: eventMarker.primary };
|
|
786
|
+
tokens.push(markerToken);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
// Get the two required roles from the schema
|
|
791
|
+
const requiredRoles = commandSchema.roles.filter(r => r.required);
|
|
792
|
+
|
|
793
|
+
// Sort by SOV position (lower number = earlier in sentence)
|
|
794
|
+
const sortedRoles = [...requiredRoles].sort((a, b) => {
|
|
795
|
+
const aPos = a.sovPosition ?? 999;
|
|
796
|
+
const bPos = b.sovPosition ?? 999;
|
|
797
|
+
return aPos - bPos;
|
|
798
|
+
});
|
|
799
|
+
|
|
800
|
+
// Add each role with its marker
|
|
801
|
+
for (const roleSpec of sortedRoles) {
|
|
802
|
+
// Add the role
|
|
803
|
+
tokens.push({ type: 'role', role: roleSpec.role, optional: false });
|
|
804
|
+
|
|
805
|
+
// Get marker for this role - check for override first
|
|
806
|
+
let marker: string | undefined;
|
|
807
|
+
let markerAlternatives: string[] | undefined;
|
|
808
|
+
|
|
809
|
+
if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code]) {
|
|
810
|
+
// Use the override marker
|
|
811
|
+
marker = roleSpec.markerOverride[profile.code];
|
|
812
|
+
} else {
|
|
813
|
+
// Use default role marker from profile
|
|
814
|
+
const roleMarker = profile.roleMarkers[roleSpec.role];
|
|
815
|
+
if (roleMarker) {
|
|
816
|
+
marker = roleMarker.primary;
|
|
817
|
+
markerAlternatives = roleMarker.alternatives;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// Add the marker token
|
|
822
|
+
if (marker) {
|
|
823
|
+
const markerToken: PatternToken = markerAlternatives
|
|
824
|
+
? { type: 'literal', value: marker, alternatives: markerAlternatives }
|
|
825
|
+
: { type: 'literal', value: marker };
|
|
826
|
+
tokens.push(markerToken);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
// Command verb at end (SOV)
|
|
831
|
+
const verbToken: PatternToken = keyword.alternatives
|
|
832
|
+
? { type: 'literal', value: keyword.primary, alternatives: keyword.alternatives }
|
|
833
|
+
: { type: 'literal', value: keyword.primary };
|
|
834
|
+
tokens.push(verbToken);
|
|
835
|
+
|
|
836
|
+
// Build format string
|
|
837
|
+
const roleNames = sortedRoles.map(r => `{${r.role}}`).join(' ');
|
|
838
|
+
|
|
839
|
+
return {
|
|
840
|
+
id: `${commandSchema.action}-event-${profile.code}-sov-2role`,
|
|
841
|
+
language: profile.code,
|
|
842
|
+
command: 'on', // This is an event handler pattern
|
|
843
|
+
priority: (config.basePriority ?? 100) + 55, // Higher priority than single-role patterns
|
|
844
|
+
template: {
|
|
845
|
+
format: `{event} ${eventMarker.primary} ${roleNames} ${keyword.primary}`,
|
|
846
|
+
tokens,
|
|
847
|
+
},
|
|
848
|
+
extraction: {
|
|
849
|
+
action: { value: commandSchema.action }, // Extract the wrapped command
|
|
850
|
+
event: { fromRole: 'event' },
|
|
851
|
+
patient: { fromRole: 'patient' },
|
|
852
|
+
destination: { fromRole: 'destination' },
|
|
853
|
+
},
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* Generate VSO two-role event handler pattern (for put/set commands).
|
|
859
|
+
*
|
|
860
|
+
* Patterns:
|
|
861
|
+
* - Arabic put: عند الإدخال ضع "test" في #output
|
|
862
|
+
* [eventMarker] [event] [verb] [patient] [destPrep] [destination]
|
|
863
|
+
* - Arabic set: عند التغيير عيّن x إلى 10
|
|
864
|
+
* [eventMarker] [event] [verb] [destination] [patientPrep] [patient]
|
|
865
|
+
*/
|
|
866
|
+
function generateVSOTwoRoleEventHandlerPattern(
|
|
867
|
+
commandSchema: CommandSchema,
|
|
868
|
+
profile: LanguageProfile,
|
|
869
|
+
keyword: KeywordTranslation,
|
|
870
|
+
eventMarker: RoleMarker,
|
|
871
|
+
config: GeneratorConfig
|
|
872
|
+
): LanguagePattern {
|
|
873
|
+
const tokens: PatternToken[] = [];
|
|
874
|
+
|
|
875
|
+
// Event marker (before event in VSO)
|
|
876
|
+
if (eventMarker.position === 'before') {
|
|
877
|
+
const markerToken: PatternToken = eventMarker.alternatives
|
|
878
|
+
? { type: 'literal', value: eventMarker.primary, alternatives: eventMarker.alternatives }
|
|
879
|
+
: { type: 'literal', value: eventMarker.primary };
|
|
880
|
+
tokens.push(markerToken);
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// Event role
|
|
884
|
+
tokens.push({ type: 'role', role: 'event', optional: false });
|
|
885
|
+
|
|
886
|
+
// Command verb (verb comes early in VSO)
|
|
887
|
+
const verbToken: PatternToken = keyword.alternatives
|
|
888
|
+
? { type: 'literal', value: keyword.primary, alternatives: keyword.alternatives }
|
|
889
|
+
: { type: 'literal', value: keyword.primary };
|
|
890
|
+
tokens.push(verbToken);
|
|
891
|
+
|
|
892
|
+
// Get the two required roles from the schema
|
|
893
|
+
const requiredRoles = commandSchema.roles.filter(r => r.required);
|
|
894
|
+
|
|
895
|
+
// Sort by SVO position for VSO (role order is similar)
|
|
896
|
+
const sortedRoles = [...requiredRoles].sort((a, b) => {
|
|
897
|
+
const aPos = a.svoPosition ?? 999;
|
|
898
|
+
const bPos = b.svoPosition ?? 999;
|
|
899
|
+
return aPos - bPos;
|
|
900
|
+
});
|
|
901
|
+
|
|
902
|
+
// Add each role with its preposition/marker
|
|
903
|
+
for (const roleSpec of sortedRoles) {
|
|
904
|
+
// Get marker for this role - check for override first
|
|
905
|
+
let marker: string | undefined;
|
|
906
|
+
let markerAlternatives: string[] | undefined;
|
|
907
|
+
|
|
908
|
+
if (roleSpec.markerOverride && roleSpec.markerOverride[profile.code]) {
|
|
909
|
+
// Use the override marker
|
|
910
|
+
marker = roleSpec.markerOverride[profile.code];
|
|
911
|
+
} else {
|
|
912
|
+
// Use default role marker from profile
|
|
913
|
+
const roleMarker = profile.roleMarkers[roleSpec.role];
|
|
914
|
+
if (roleMarker) {
|
|
915
|
+
marker = roleMarker.primary;
|
|
916
|
+
markerAlternatives = roleMarker.alternatives;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// In VSO, prepositions come BEFORE the noun (prepositional languages)
|
|
921
|
+
if (marker) {
|
|
922
|
+
const markerToken: PatternToken = markerAlternatives
|
|
923
|
+
? { type: 'literal', value: marker, alternatives: markerAlternatives }
|
|
924
|
+
: { type: 'literal', value: marker };
|
|
925
|
+
tokens.push(markerToken);
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
// Add the role
|
|
929
|
+
tokens.push({ type: 'role', role: roleSpec.role, optional: false });
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// Build format string
|
|
933
|
+
const roleNames = sortedRoles.map(r => `{${r.role}}`).join(' ');
|
|
934
|
+
|
|
935
|
+
return {
|
|
936
|
+
id: `${commandSchema.action}-event-${profile.code}-vso-2role`,
|
|
937
|
+
language: profile.code,
|
|
938
|
+
command: 'on', // This is an event handler pattern
|
|
939
|
+
priority: (config.basePriority ?? 100) + 55, // Higher priority than single-role patterns
|
|
940
|
+
template: {
|
|
941
|
+
format: `${eventMarker.primary} {event} ${keyword.primary} ${roleNames}`,
|
|
942
|
+
tokens,
|
|
943
|
+
},
|
|
944
|
+
extraction: {
|
|
945
|
+
action: { value: commandSchema.action }, // Extract the wrapped command
|
|
946
|
+
event: { fromRole: 'event' },
|
|
947
|
+
patient: { fromRole: 'patient' },
|
|
948
|
+
destination: { fromRole: 'destination' },
|
|
949
|
+
},
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
/**
|
|
954
|
+
* Generate VSO negated event handler pattern.
|
|
955
|
+
*
|
|
956
|
+
* Patterns:
|
|
957
|
+
* - Arabic: عند عدم التركيز أخف #tooltip
|
|
958
|
+
* [eventMarker] [negation] [event] [verb] [patient]
|
|
959
|
+
*
|
|
960
|
+
* Used for events expressed as negation + opposite action:
|
|
961
|
+
* - عدم التركيز = "not focusing" = blur
|
|
962
|
+
*/
|
|
963
|
+
function generateVSONegatedEventHandlerPattern(
|
|
964
|
+
commandSchema: CommandSchema,
|
|
965
|
+
profile: LanguageProfile,
|
|
966
|
+
keyword: KeywordTranslation,
|
|
967
|
+
eventMarker: RoleMarker,
|
|
968
|
+
config: GeneratorConfig
|
|
969
|
+
): LanguagePattern {
|
|
970
|
+
const tokens: PatternToken[] = [];
|
|
971
|
+
const negationMarker = profile.eventHandler?.negationMarker;
|
|
972
|
+
|
|
973
|
+
// Event marker (before event in VSO)
|
|
974
|
+
if (eventMarker.position === 'before') {
|
|
975
|
+
const markerToken: PatternToken = eventMarker.alternatives
|
|
976
|
+
? { type: 'literal', value: eventMarker.primary, alternatives: eventMarker.alternatives }
|
|
977
|
+
: { type: 'literal', value: eventMarker.primary };
|
|
978
|
+
tokens.push(markerToken);
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// Negation marker (e.g., عدم = "not/lack of")
|
|
982
|
+
if (negationMarker) {
|
|
983
|
+
const negToken: PatternToken = negationMarker.alternatives
|
|
984
|
+
? {
|
|
985
|
+
type: 'literal',
|
|
986
|
+
value: negationMarker.primary,
|
|
987
|
+
alternatives: negationMarker.alternatives,
|
|
988
|
+
}
|
|
989
|
+
: { type: 'literal', value: negationMarker.primary };
|
|
990
|
+
tokens.push(negToken);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// Event role (the action being negated, e.g., التركيز = "the focusing")
|
|
994
|
+
tokens.push({ type: 'role', role: 'event', optional: false });
|
|
995
|
+
|
|
996
|
+
// Command verb (verb comes early in VSO)
|
|
997
|
+
const verbToken: PatternToken = keyword.alternatives
|
|
998
|
+
? { type: 'literal', value: keyword.primary, alternatives: keyword.alternatives }
|
|
999
|
+
: { type: 'literal', value: keyword.primary };
|
|
1000
|
+
tokens.push(verbToken);
|
|
1001
|
+
|
|
1002
|
+
// Patient role
|
|
1003
|
+
tokens.push({ type: 'role', role: 'patient', optional: false });
|
|
1004
|
+
|
|
1005
|
+
// Optional destination with preposition
|
|
1006
|
+
const destMarker = profile.roleMarkers.destination;
|
|
1007
|
+
if (destMarker) {
|
|
1008
|
+
tokens.push({
|
|
1009
|
+
type: 'group',
|
|
1010
|
+
optional: true,
|
|
1011
|
+
tokens: [
|
|
1012
|
+
destMarker.alternatives
|
|
1013
|
+
? { type: 'literal', value: destMarker.primary, alternatives: destMarker.alternatives }
|
|
1014
|
+
: { type: 'literal', value: destMarker.primary },
|
|
1015
|
+
{ type: 'role', role: 'destination', optional: true },
|
|
1016
|
+
],
|
|
1017
|
+
});
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
return {
|
|
1021
|
+
id: `${commandSchema.action}-event-${profile.code}-vso-negated`,
|
|
1022
|
+
language: profile.code,
|
|
1023
|
+
command: 'on', // This is an event handler pattern
|
|
1024
|
+
priority: (config.basePriority ?? 100) + 48, // Slightly lower priority than standard patterns
|
|
1025
|
+
template: {
|
|
1026
|
+
format: `${eventMarker.primary} ${negationMarker?.primary || ''} {event} ${keyword.primary} {patient} ${destMarker?.primary || ''} {destination?}`,
|
|
1027
|
+
tokens,
|
|
1028
|
+
},
|
|
1029
|
+
extraction: {
|
|
1030
|
+
action: { value: commandSchema.action }, // Extract the wrapped command
|
|
1031
|
+
event: { fromRole: 'event' },
|
|
1032
|
+
patient: { fromRole: 'patient' },
|
|
1033
|
+
destination: { fromRole: 'destination', default: { type: 'reference', value: 'me' } },
|
|
1034
|
+
},
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* Generate VSO proclitic event handler pattern (for Arabic chained events).
|
|
1040
|
+
*
|
|
1041
|
+
* Patterns:
|
|
1042
|
+
* - Arabic: والنقر بدّل .active
|
|
1043
|
+
* [proclitic] [event] [verb] [patient]
|
|
1044
|
+
* - Arabic: فالتحويم أضف .highlight
|
|
1045
|
+
* [proclitic] [event] [verb] [patient]
|
|
1046
|
+
*
|
|
1047
|
+
* These patterns have a conjunction proclitic (و = and, ف = then) attached to
|
|
1048
|
+
* the event, without the عند event marker. Used for chained/consequent events.
|
|
1049
|
+
*/
|
|
1050
|
+
function generateVSOProcliticEventHandlerPattern(
|
|
1051
|
+
commandSchema: CommandSchema,
|
|
1052
|
+
profile: LanguageProfile,
|
|
1053
|
+
keyword: KeywordTranslation,
|
|
1054
|
+
config: GeneratorConfig
|
|
1055
|
+
): LanguagePattern {
|
|
1056
|
+
const tokens: PatternToken[] = [];
|
|
1057
|
+
|
|
1058
|
+
// Required conjunction token (و = and, ف = then)
|
|
1059
|
+
// The conjunction must be present for this pattern - it distinguishes chained events from regular commands
|
|
1060
|
+
// Use normalized values since proclitics are tokenized as conjunction tokens
|
|
1061
|
+
tokens.push({
|
|
1062
|
+
type: 'literal',
|
|
1063
|
+
value: 'and', // Matches normalized 'and' (Arabic: و)
|
|
1064
|
+
alternatives: ['then'], // Also matches normalized 'then' (Arabic: ف)
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
// Event role (the event name, e.g., النقر = the click)
|
|
1068
|
+
tokens.push({ type: 'role', role: 'event', optional: false });
|
|
1069
|
+
|
|
1070
|
+
// Command verb (verb comes early in VSO)
|
|
1071
|
+
const verbToken: PatternToken = keyword.alternatives
|
|
1072
|
+
? { type: 'literal', value: keyword.primary, alternatives: keyword.alternatives }
|
|
1073
|
+
: { type: 'literal', value: keyword.primary };
|
|
1074
|
+
tokens.push(verbToken);
|
|
1075
|
+
|
|
1076
|
+
// Patient role
|
|
1077
|
+
tokens.push({ type: 'role', role: 'patient', optional: false });
|
|
1078
|
+
|
|
1079
|
+
// Optional destination with preposition
|
|
1080
|
+
const destMarker = profile.roleMarkers.destination;
|
|
1081
|
+
if (destMarker) {
|
|
1082
|
+
tokens.push({
|
|
1083
|
+
type: 'group',
|
|
1084
|
+
optional: true,
|
|
1085
|
+
tokens: [
|
|
1086
|
+
destMarker.alternatives
|
|
1087
|
+
? { type: 'literal', value: destMarker.primary, alternatives: destMarker.alternatives }
|
|
1088
|
+
: { type: 'literal', value: destMarker.primary },
|
|
1089
|
+
{ type: 'role', role: 'destination', optional: true },
|
|
1090
|
+
],
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
return {
|
|
1095
|
+
id: `${commandSchema.action}-event-${profile.code}-vso-proclitic`,
|
|
1096
|
+
language: profile.code,
|
|
1097
|
+
command: 'on', // This is an event handler pattern
|
|
1098
|
+
priority: (config.basePriority ?? 100) + 45, // Lower priority than standard patterns
|
|
1099
|
+
template: {
|
|
1100
|
+
format: `[proclitic?] {event} ${keyword.primary} {patient} ${destMarker?.primary || ''} {destination?}`,
|
|
1101
|
+
tokens,
|
|
1102
|
+
},
|
|
1103
|
+
extraction: {
|
|
1104
|
+
action: { value: commandSchema.action }, // Extract the wrapped command
|
|
1105
|
+
event: { fromRole: 'event' },
|
|
1106
|
+
patient: { fromRole: 'patient' },
|
|
1107
|
+
destination: { fromRole: 'destination', default: { type: 'reference', value: 'me' } },
|
|
1108
|
+
},
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
|
|
230
1112
|
// =============================================================================
|
|
231
1113
|
// Token Building
|
|
232
1114
|
// =============================================================================
|