@lokascript/framework 4.6.0 → 4.8.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/CHANGELOG.md +121 -1
- package/dist/api/create-dsl.d.ts +18 -2
- package/dist/api/create-dsl.d.ts.map +1 -1
- package/dist/api/index.d.ts +1 -0
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +1799 -713
- package/dist/api/index.js.map +1 -1
- package/dist/api/translation-loss.d.ts +84 -0
- package/dist/api/translation-loss.d.ts.map +1 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1057 -684
- package/dist/core/index.js.map +1 -1
- package/dist/core/matching/confidence-model.d.ts +54 -0
- package/dist/core/matching/confidence-model.d.ts.map +1 -0
- package/dist/core/matching/index.d.ts +12 -0
- package/dist/core/matching/index.d.ts.map +1 -0
- package/dist/core/matching/match-skeleton.d.ts +114 -0
- package/dist/core/matching/match-skeleton.d.ts.map +1 -0
- package/dist/core/matching/stock-confidence.d.ts +15 -0
- package/dist/core/matching/stock-confidence.d.ts.map +1 -0
- package/dist/core/matching/stock-reader.d.ts +63 -0
- package/dist/core/matching/stock-reader.d.ts.map +1 -0
- package/dist/core/matching/stock-reading.d.ts +44 -0
- package/dist/core/matching/stock-reading.d.ts.map +1 -0
- package/dist/core/matching/value-reader.d.ts +62 -0
- package/dist/core/matching/value-reader.d.ts.map +1 -0
- package/dist/core/pattern-matching/index.js +561 -660
- package/dist/core/pattern-matching/index.js.map +1 -1
- package/dist/core/pattern-matching/pattern-matcher.d.ts +0 -117
- package/dist/core/pattern-matching/pattern-matcher.d.ts.map +1 -1
- package/dist/core/types.d.ts +53 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/generation/index.d.ts +2 -0
- package/dist/generation/index.d.ts.map +1 -1
- package/dist/generation/index.js +593 -1
- package/dist/generation/index.js.map +1 -1
- package/dist/generation/pattern-render.d.ts +72 -0
- package/dist/generation/pattern-render.d.ts.map +1 -0
- package/dist/generation/schema-patterns.d.ts +152 -0
- package/dist/generation/schema-patterns.d.ts.map +1 -0
- package/dist/index.cjs +2016 -904
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1998 -903
- package/dist/index.js.map +1 -1
- package/dist/ir/index.js.map +1 -1
- package/dist/parsing/index.js +561 -660
- package/dist/parsing/index.js.map +1 -1
- package/dist/testing/gate/index.d.ts +8 -0
- package/dist/testing/gate/index.d.ts.map +1 -0
- package/dist/testing/gate/index.js +664 -0
- package/dist/testing/gate/index.js.map +1 -0
- package/dist/testing/gate/lint/format.d.ts +7 -0
- package/dist/testing/gate/lint/format.d.ts.map +1 -0
- package/dist/testing/gate/lint/index.d.ts +30 -0
- package/dist/testing/gate/lint/index.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/extractor-coverage.d.ts +18 -0
- package/dist/testing/gate/lint/rules/extractor-coverage.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/index.d.ts +14 -0
- package/dist/testing/gate/lint/rules/index.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/keyword-classification.d.ts +19 -0
- package/dist/testing/gate/lint/rules/keyword-classification.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/keyword-coverage.d.ts +13 -0
- package/dist/testing/gate/lint/rules/keyword-coverage.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/marker-tokenization.d.ts +20 -0
- package/dist/testing/gate/lint/rules/marker-tokenization.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/position-ordering.d.ts +25 -0
- package/dist/testing/gate/lint/rules/position-ordering.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/renderer-coherence.d.ts +24 -0
- package/dist/testing/gate/lint/rules/renderer-coherence.d.ts.map +1 -0
- package/dist/testing/gate/lint/rules/schema-rules.d.ts +10 -0
- package/dist/testing/gate/lint/rules/schema-rules.d.ts.map +1 -0
- package/dist/testing/gate/lint/types.d.ts +74 -0
- package/dist/testing/gate/lint/types.d.ts.map +1 -0
- package/dist/testing/gate/round-trip.d.ts +184 -0
- package/dist/testing/gate/round-trip.d.ts.map +1 -0
- package/package.json +6 -2
- package/src/__test__/fixtures/sql-dsl.ts +8 -2
- package/src/api/create-dsl-engine.test.ts +150 -0
- package/src/api/create-dsl.test.ts +7 -0
- package/src/api/create-dsl.ts +173 -22
- package/src/api/index.ts +1 -0
- package/src/api/translation-loss.test.ts +89 -0
- package/src/api/translation-loss.ts +128 -0
- package/src/core/index.ts +1 -0
- package/src/core/matching/confidence-model.ts +58 -0
- package/src/core/matching/index.ts +12 -0
- package/src/core/matching/match-skeleton.test.ts +179 -0
- package/src/core/matching/match-skeleton.ts +621 -0
- package/src/core/matching/stock-confidence.ts +238 -0
- package/src/core/matching/stock-reader.test.ts +265 -0
- package/src/core/matching/stock-reader.ts +205 -0
- package/src/core/matching/stock-reading.ts +681 -0
- package/src/core/matching/value-reader.ts +85 -0
- package/src/core/pattern-matching/pattern-matcher.ts +11 -876
- package/src/core/types.ts +53 -0
- package/src/generation/index.ts +2 -0
- package/src/generation/pattern-render.ts +378 -0
- package/src/generation/schema-patterns.test.ts +139 -0
- package/src/generation/schema-patterns.ts +974 -0
- package/src/testing/gate/index.ts +8 -0
- package/src/testing/gate/lint/format.ts +23 -0
- package/src/testing/gate/lint/index.ts +73 -0
- package/src/testing/gate/lint/lint.test.ts +131 -0
- package/src/testing/gate/lint/rules/extractor-coverage.ts +63 -0
- package/src/testing/gate/lint/rules/index.ts +37 -0
- package/src/testing/gate/lint/rules/keyword-classification.ts +52 -0
- package/src/testing/gate/lint/rules/keyword-coverage.ts +38 -0
- package/src/testing/gate/lint/rules/marker-tokenization.ts +129 -0
- package/src/testing/gate/lint/rules/position-ordering.ts +87 -0
- package/src/testing/gate/lint/rules/renderer-coherence.ts +100 -0
- package/src/testing/gate/lint/rules/schema-rules.ts +35 -0
- package/src/testing/gate/lint/types.ts +83 -0
- package/src/testing/gate/round-trip.test.ts +208 -0
- package/src/testing/gate/round-trip.ts +503 -0
package/dist/generation/index.js
CHANGED
|
@@ -292,19 +292,611 @@ function renderFromSchema(node, language, tables) {
|
|
|
292
292
|
|
|
293
293
|
// src/generation/diagnostics.ts
|
|
294
294
|
import { createDiagnosticCollector, fromError, filterBySeverity } from "@lokascript/intent";
|
|
295
|
+
|
|
296
|
+
// src/generation/schema-patterns.ts
|
|
297
|
+
function createSchemaPatternGenerator(grammar) {
|
|
298
|
+
return {
|
|
299
|
+
generatePattern: (schema, profile, config) => generatePattern2(grammar, schema, profile, config),
|
|
300
|
+
generateSimplePattern: (schema, profile, config) => generateSimplePattern(grammar, schema, profile, config),
|
|
301
|
+
generateVerbFirstPattern: (schema, profile, config) => generateVerbFirstPattern(schema, profile, config),
|
|
302
|
+
generatePatternVariants: (schema, profile, config) => generatePatternVariants2(grammar, schema, profile, config),
|
|
303
|
+
buildRoleToken
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function withoutVariantOnlyRoles(schema) {
|
|
307
|
+
return schema.roles.some((r) => r.variantOnly) ? { ...schema, roles: schema.roles.filter((r) => !r.variantOnly) } : schema;
|
|
308
|
+
}
|
|
309
|
+
function sortRolesByPosition(roles, wordOrder, order) {
|
|
310
|
+
const sortKey = wordOrder === "SOV" ? "sovPosition" : "svoPosition";
|
|
311
|
+
return [...roles].sort(
|
|
312
|
+
(a, b) => order === "ascending" ? (a[sortKey] ?? 99) - (b[sortKey] ?? 99) : (b[sortKey] ?? 0) - (a[sortKey] ?? 0)
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
function resolveMarkerForRole(roleSpec, profile) {
|
|
316
|
+
const overrideMarker = roleSpec.markerOverride?.[profile.code];
|
|
317
|
+
const defaultMarker = profile.roleMarkers[roleSpec.role];
|
|
318
|
+
if (overrideMarker !== void 0) {
|
|
319
|
+
const alternatives = legacyMarkerAlternatives(roleSpec, profile.code, overrideMarker);
|
|
320
|
+
return {
|
|
321
|
+
primary: overrideMarker,
|
|
322
|
+
...alternatives && { alternatives },
|
|
323
|
+
position: defaultMarker?.position ?? "before",
|
|
324
|
+
isOverride: true
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
if (defaultMarker && defaultMarker.primary) {
|
|
328
|
+
const result = {
|
|
329
|
+
primary: defaultMarker.primary,
|
|
330
|
+
position: defaultMarker.position,
|
|
331
|
+
isOverride: false
|
|
332
|
+
};
|
|
333
|
+
if (defaultMarker.alternatives) {
|
|
334
|
+
result.alternatives = defaultMarker.alternatives;
|
|
335
|
+
}
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
338
|
+
return null;
|
|
339
|
+
}
|
|
340
|
+
function legacyMarkerAlternatives(roleSpec, languageCode, overrideMarker) {
|
|
341
|
+
const legacy = roleSpec.markerLegacy?.[languageCode];
|
|
342
|
+
if (!legacy?.length) return void 0;
|
|
343
|
+
const alternatives = [...new Set(legacy)].filter((a) => a && a !== overrideMarker);
|
|
344
|
+
return alternatives.length ? alternatives : void 0;
|
|
345
|
+
}
|
|
346
|
+
function schemaMarkerAlternatives(roleSpec, languageCode, marker) {
|
|
347
|
+
const legacy = roleSpec.markerLegacy?.[languageCode] ?? [];
|
|
348
|
+
const variants = roleSpec.methodCarrier ? [] : roleSpec.markerVariants?.[languageCode] ?? [];
|
|
349
|
+
const alternatives = [.../* @__PURE__ */ new Set([...legacy, ...variants])].filter((a) => a && a !== marker);
|
|
350
|
+
return alternatives.length ? alternatives : void 0;
|
|
351
|
+
}
|
|
352
|
+
var defaultConfig2 = {
|
|
353
|
+
basePriority: 100,
|
|
354
|
+
generateSimpleVariants: true,
|
|
355
|
+
generateAlternatives: true
|
|
356
|
+
};
|
|
357
|
+
function generatePattern2(grammar, schema, profile, config = defaultConfig2) {
|
|
358
|
+
const id = `${schema.action}-${profile.code}-generated`;
|
|
359
|
+
const priority = config.basePriority ?? 100;
|
|
360
|
+
const keyword = profile.keywords[schema.action];
|
|
361
|
+
if (!keyword) {
|
|
362
|
+
throw new Error(`No keyword translation for '${schema.action}' in ${profile.code}`);
|
|
363
|
+
}
|
|
364
|
+
const tokens = buildTokens2(grammar, schema, profile, keyword);
|
|
365
|
+
const extraction = buildExtractionRulesWithDefaults(schema, profile);
|
|
366
|
+
const format = buildFormatString2(grammar, schema, profile, keyword);
|
|
367
|
+
return {
|
|
368
|
+
id,
|
|
369
|
+
language: profile.code,
|
|
370
|
+
command: schema.action,
|
|
371
|
+
priority,
|
|
372
|
+
template: {
|
|
373
|
+
format,
|
|
374
|
+
tokens
|
|
375
|
+
},
|
|
376
|
+
extraction
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
function generateSimplePattern(grammar, schema, profile, config = defaultConfig2) {
|
|
380
|
+
const optionalRoles = schema.roles.filter((r) => !r.required);
|
|
381
|
+
if (optionalRoles.length === 0) {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
const requiredRoles = schema.roles.filter((r) => r.required);
|
|
385
|
+
const simpleSchema = {
|
|
386
|
+
...schema,
|
|
387
|
+
roles: requiredRoles
|
|
388
|
+
};
|
|
389
|
+
const pattern = generatePattern2(grammar, simpleSchema, profile, config);
|
|
390
|
+
return {
|
|
391
|
+
...pattern,
|
|
392
|
+
id: `${schema.action}-${profile.code}-generated-simple`,
|
|
393
|
+
priority: (config.basePriority ?? 100) - 5,
|
|
394
|
+
// Lower priority than full pattern (was -10)
|
|
395
|
+
extraction: buildExtractionRulesWithDefaults(schema, profile)
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function generateVerbFirstPattern(schema, profile, config = defaultConfig2) {
|
|
399
|
+
if (profile.wordOrder !== "SOV") return null;
|
|
400
|
+
const requiredRoles = schema.roles.filter((r) => r.required);
|
|
401
|
+
if (requiredRoles.length !== 1) return null;
|
|
402
|
+
const keyword = profile.keywords[schema.action];
|
|
403
|
+
if (!keyword) return null;
|
|
404
|
+
const verbToken = keyword.alternatives ? { type: "literal", value: keyword.primary, alternatives: keyword.alternatives } : { type: "literal", value: keyword.primary };
|
|
405
|
+
const roleTokens = requiredRoles.flatMap((r) => {
|
|
406
|
+
const prefix = r.valuePrefixLiteral?.[profile.code];
|
|
407
|
+
const words = wordsFor(r, profile.code);
|
|
408
|
+
const roleToken = {
|
|
409
|
+
type: "role",
|
|
410
|
+
role: r.role,
|
|
411
|
+
optional: false,
|
|
412
|
+
expectedTypes: r.expectedTypes,
|
|
413
|
+
...words ? { words } : {}
|
|
414
|
+
};
|
|
415
|
+
return prefix ? [{ type: "literal", value: prefix }, roleToken] : [roleToken];
|
|
416
|
+
});
|
|
417
|
+
return {
|
|
418
|
+
id: `${schema.action}-${profile.code}-generated-verb-first`,
|
|
419
|
+
language: profile.code,
|
|
420
|
+
command: schema.action,
|
|
421
|
+
// Strictly below the canonical (basePriority) and simple (basePriority - 5)
|
|
422
|
+
// patterns so native order is always preferred.
|
|
423
|
+
priority: (config.basePriority ?? 100) - 20,
|
|
424
|
+
template: {
|
|
425
|
+
format: `${keyword.primary} <${requiredRoles[0].role}>`,
|
|
426
|
+
tokens: [verbToken, ...roleTokens]
|
|
427
|
+
},
|
|
428
|
+
extraction: buildExtractionRulesWithDefaults(schema, profile)
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function generatePatternVariants2(grammar, schema, profile, config = defaultConfig2) {
|
|
432
|
+
const patterns = [];
|
|
433
|
+
const full = schema;
|
|
434
|
+
schema = withoutVariantOnlyRoles(schema);
|
|
435
|
+
patterns.push(generatePattern2(grammar, schema, profile, config));
|
|
436
|
+
if (config.generateSimpleVariants) {
|
|
437
|
+
const simple = generateSimplePattern(grammar, schema, profile, config);
|
|
438
|
+
if (simple) {
|
|
439
|
+
patterns.push(simple);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
for (const omitted of schema.omitRoleVariants ?? []) {
|
|
443
|
+
const variantSchema = {
|
|
444
|
+
...schema,
|
|
445
|
+
roles: schema.roles.filter((r) => r.role !== omitted)
|
|
446
|
+
};
|
|
447
|
+
const variant = generatePattern2(grammar, variantSchema, profile, config);
|
|
448
|
+
patterns.push({
|
|
449
|
+
...variant,
|
|
450
|
+
id: `${schema.action}-${profile.code}-generated-no-${omitted}`,
|
|
451
|
+
priority: (config.basePriority ?? 100) - 8
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
for (const widen of schema.widenTypeVariants ?? []) {
|
|
455
|
+
if (widen.excludeLanguages?.includes(profile.code)) continue;
|
|
456
|
+
const cloneSchema = {
|
|
457
|
+
...schema,
|
|
458
|
+
roles: schema.roles.map(
|
|
459
|
+
(r) => r.role === widen.role ? { ...r, expectedTypes: [...widen.types] } : r
|
|
460
|
+
)
|
|
461
|
+
};
|
|
462
|
+
const main = generatePattern2(grammar, cloneSchema, profile, config);
|
|
463
|
+
patterns.push({
|
|
464
|
+
...main,
|
|
465
|
+
id: `${schema.action}-${profile.code}-generated-${widen.idSuffix}`,
|
|
466
|
+
priority: (config.basePriority ?? 100) - (widen.priorityDelta ?? 15)
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
for (const v of full.requiredRoleVariants ?? []) {
|
|
470
|
+
const variantSchema = {
|
|
471
|
+
...full,
|
|
472
|
+
roles: full.roles.filter((r) => !v.omit?.includes(r.role)).map((r) => {
|
|
473
|
+
const required = r.role === v.role ? { ...r, required: true } : r;
|
|
474
|
+
return r.role === v.widen?.role ? { ...required, expectedTypes: [...v.widen.types] } : required;
|
|
475
|
+
})
|
|
476
|
+
};
|
|
477
|
+
const variant = generatePattern2(grammar, variantSchema, profile, config);
|
|
478
|
+
patterns.push({
|
|
479
|
+
...variant,
|
|
480
|
+
id: `${schema.action}-${profile.code}-generated-${v.idSuffix}`,
|
|
481
|
+
priority: (config.basePriority ?? 100) + (v.priorityDelta ?? 5)
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
if (config.generateVerbFirstVariants !== false) {
|
|
485
|
+
const verbFirst = generateVerbFirstPattern(schema, profile, config);
|
|
486
|
+
if (verbFirst) {
|
|
487
|
+
patterns.push(verbFirst);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
for (const v of schema.rolePrefixLiteralVariants ?? []) {
|
|
491
|
+
const literal = v.literal[profile.code];
|
|
492
|
+
if (!literal) continue;
|
|
493
|
+
const { rolePrefixLiteralVariants: _omitted, ...baseSchema } = schema;
|
|
494
|
+
const cloneSchema = {
|
|
495
|
+
...baseSchema,
|
|
496
|
+
roles: schema.roles.map(
|
|
497
|
+
(r) => r.role === v.role ? { ...r, valuePrefixLiteral: { [profile.code]: literal } } : r
|
|
498
|
+
)
|
|
499
|
+
};
|
|
500
|
+
const delta = v.priorityDelta ?? 5;
|
|
501
|
+
const carrier = v.methodCarrier ? { [v.methodCarrier]: { value: literal } } : {};
|
|
502
|
+
const main = generatePattern2(grammar, cloneSchema, profile, config);
|
|
503
|
+
patterns.push({
|
|
504
|
+
...main,
|
|
505
|
+
id: `${schema.action}-${profile.code}-generated-${v.idSuffix}`,
|
|
506
|
+
priority: (config.basePriority ?? 100) + delta,
|
|
507
|
+
extraction: { ...main.extraction, ...carrier }
|
|
508
|
+
});
|
|
509
|
+
if (config.generateVerbFirstVariants !== false) {
|
|
510
|
+
const verbFirstUrl = generateVerbFirstPattern(cloneSchema, profile, config);
|
|
511
|
+
if (verbFirstUrl) {
|
|
512
|
+
patterns.push({
|
|
513
|
+
...verbFirstUrl,
|
|
514
|
+
id: `${schema.action}-${profile.code}-generated-verb-first-${v.idSuffix}`,
|
|
515
|
+
priority: (config.basePriority ?? 100) - 20 + delta,
|
|
516
|
+
extraction: { ...verbFirstUrl.extraction, ...carrier }
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return patterns;
|
|
522
|
+
}
|
|
523
|
+
function buildTokens2(grammar, schema, profile, keyword) {
|
|
524
|
+
const tokens = [];
|
|
525
|
+
const verbToken = keyword.alternatives ? { type: "literal", value: keyword.primary, alternatives: keyword.alternatives } : { type: "literal", value: keyword.primary };
|
|
526
|
+
const postVerbRoles = profile.wordOrder === "SOV" ? schema.roles.filter((r) => r.sovSlot === "postVerb") : [];
|
|
527
|
+
const roleTokens = buildRoleTokens(
|
|
528
|
+
grammar,
|
|
529
|
+
postVerbRoles.length ? { ...schema, roles: schema.roles.filter((r) => r.sovSlot !== "postVerb") } : schema,
|
|
530
|
+
profile
|
|
531
|
+
);
|
|
532
|
+
const postVerbTokens = postVerbRoles.length ? buildRoleTokens(grammar, { ...schema, roles: postVerbRoles }, profile) : [];
|
|
533
|
+
switch (profile.wordOrder) {
|
|
534
|
+
case "SVO":
|
|
535
|
+
tokens.push(verbToken);
|
|
536
|
+
tokens.push(...roleTokens);
|
|
537
|
+
break;
|
|
538
|
+
case "SOV":
|
|
539
|
+
tokens.push(...roleTokens);
|
|
540
|
+
tokens.push(verbToken);
|
|
541
|
+
tokens.push(...postVerbTokens);
|
|
542
|
+
break;
|
|
543
|
+
case "VSO":
|
|
544
|
+
tokens.push(verbToken);
|
|
545
|
+
tokens.push(...roleTokens);
|
|
546
|
+
break;
|
|
547
|
+
default:
|
|
548
|
+
tokens.push(verbToken);
|
|
549
|
+
tokens.push(...roleTokens);
|
|
550
|
+
}
|
|
551
|
+
return collapseAdjacentLiterals(tokens);
|
|
552
|
+
}
|
|
553
|
+
function collapseAdjacentLiterals(tokens) {
|
|
554
|
+
const out = [];
|
|
555
|
+
for (const tok of tokens) {
|
|
556
|
+
const prev = out[out.length - 1];
|
|
557
|
+
if (prev && prev.type === "literal" && tok.type === "literal" && prev.value === tok.value) {
|
|
558
|
+
const alts = [...prev.alternatives ?? [], ...tok.alternatives ?? []];
|
|
559
|
+
if (alts.length) out[out.length - 1] = { ...prev, alternatives: [...new Set(alts)] };
|
|
560
|
+
continue;
|
|
561
|
+
}
|
|
562
|
+
out.push(tok);
|
|
563
|
+
}
|
|
564
|
+
return out;
|
|
565
|
+
}
|
|
566
|
+
function buildRoleTokens(grammar, schema, profile) {
|
|
567
|
+
const tokens = [];
|
|
568
|
+
const sortedRoles = sortRolesByPosition(schema.roles, profile.wordOrder, grammar.positionOrder);
|
|
569
|
+
for (const roleSpec of sortedRoles) {
|
|
570
|
+
const roleToken = buildRoleToken(roleSpec, profile);
|
|
571
|
+
if (!roleSpec.required) {
|
|
572
|
+
tokens.push({
|
|
573
|
+
type: "group",
|
|
574
|
+
optional: true,
|
|
575
|
+
tokens: roleToken
|
|
576
|
+
});
|
|
577
|
+
} else {
|
|
578
|
+
tokens.push(...roleToken);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
return tokens;
|
|
582
|
+
}
|
|
583
|
+
function wordsFor(roleSpec, language) {
|
|
584
|
+
if (!roleSpec.words) return void 0;
|
|
585
|
+
const words = [...roleSpec.words["*"] ?? [], ...roleSpec.words[language] ?? []];
|
|
586
|
+
return words.length > 0 ? words : void 0;
|
|
587
|
+
}
|
|
588
|
+
function buildRoleToken(roleSpec, profile) {
|
|
589
|
+
const tokens = [];
|
|
590
|
+
const overrideMarker = roleSpec.markerOverride?.[profile.code];
|
|
591
|
+
const defaultMarker = profile.roleMarkers[roleSpec.role];
|
|
592
|
+
const suppressMarker = roleSpec.renderOverride?.[profile.code] === "";
|
|
593
|
+
const words = wordsFor(roleSpec, profile.code);
|
|
594
|
+
const roleValueToken = {
|
|
595
|
+
type: "role",
|
|
596
|
+
role: roleSpec.role,
|
|
597
|
+
optional: !roleSpec.required,
|
|
598
|
+
expectedTypes: roleSpec.expectedTypes,
|
|
599
|
+
// The matcher reads this too — a `'keyword'`-shaped slot holds a fixed
|
|
600
|
+
// keyword phrase (`using view transition`) and must not be skipped by the
|
|
601
|
+
// trailing-slot verb guard.
|
|
602
|
+
...roleSpec.valueShape !== void 0 ? { valueShape: roleSpec.valueShape } : {},
|
|
603
|
+
// A greedy role reads up to the next marker the pattern owes (a domain's
|
|
604
|
+
// SQL condition); its value reader does the reading.
|
|
605
|
+
...roleSpec.greedy ? { greedy: true } : {},
|
|
606
|
+
// A role with a word list reads only one of its words.
|
|
607
|
+
...words ? { words } : {}
|
|
608
|
+
};
|
|
609
|
+
const prefixLiteral = roleSpec.valuePrefixLiteral?.[profile.code];
|
|
610
|
+
const pushPrefixed = () => {
|
|
611
|
+
if (prefixLiteral) tokens.push({ type: "literal", value: prefixLiteral });
|
|
612
|
+
tokens.push(roleValueToken);
|
|
613
|
+
};
|
|
614
|
+
if (overrideMarker !== void 0) {
|
|
615
|
+
const markerWords = overrideMarker ? overrideMarker.split(/\s+/).filter(Boolean) : [];
|
|
616
|
+
const isPassthroughOverride = overrideMarker !== void 0 && overrideMarker === roleSpec.markerOverride?.en;
|
|
617
|
+
const position = roleSpec.markerPositionOverride?.[profile.code] ?? roleSpec.markerPosition ?? defaultMarker?.position ?? (profile.wordOrder === "SOV" && !isPassthroughOverride ? "after" : "before");
|
|
618
|
+
const optionalMarker = roleSpec.markerOptional?.[profile.code] === true;
|
|
619
|
+
const pushWord = (word) => {
|
|
620
|
+
const alternatives = markerWords.length === 1 ? schemaMarkerAlternatives(roleSpec, profile.code, word) ?? [] : [];
|
|
621
|
+
const literal = {
|
|
622
|
+
type: "literal",
|
|
623
|
+
value: word,
|
|
624
|
+
...alternatives.length ? { alternatives } : {},
|
|
625
|
+
...suppressMarker ? { renderSuppress: true } : {}
|
|
626
|
+
};
|
|
627
|
+
tokens.push(optionalMarker ? { type: "group", optional: true, tokens: [literal] } : literal);
|
|
628
|
+
};
|
|
629
|
+
if (position === "before") {
|
|
630
|
+
for (const word of markerWords) pushWord(word);
|
|
631
|
+
pushPrefixed();
|
|
632
|
+
} else {
|
|
633
|
+
pushPrefixed();
|
|
634
|
+
for (const word of markerWords) pushWord(word);
|
|
635
|
+
}
|
|
636
|
+
} else if (defaultMarker) {
|
|
637
|
+
const asMarker = () => {
|
|
638
|
+
const alternatives = [
|
|
639
|
+
.../* @__PURE__ */ new Set([
|
|
640
|
+
...defaultMarker.alternatives ?? [],
|
|
641
|
+
...schemaMarkerAlternatives(roleSpec, profile.code, defaultMarker.primary) ?? []
|
|
642
|
+
])
|
|
643
|
+
].filter((a) => a !== defaultMarker.primary);
|
|
644
|
+
return {
|
|
645
|
+
type: "literal",
|
|
646
|
+
value: defaultMarker.primary,
|
|
647
|
+
...alternatives.length ? { alternatives } : {},
|
|
648
|
+
...suppressMarker ? { renderSuppress: true } : {}
|
|
649
|
+
};
|
|
650
|
+
};
|
|
651
|
+
const pushMarker = (marker) => {
|
|
652
|
+
if (!profile.markersOptional && !roleSpec.markerOptional?.[profile.code]) {
|
|
653
|
+
tokens.push(marker);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
tokens.push({
|
|
657
|
+
type: "group",
|
|
658
|
+
optional: true,
|
|
659
|
+
...profile.markersOptional && !roleSpec.markerOptional?.[profile.code] ? { renderRequired: true } : {},
|
|
660
|
+
tokens: [marker]
|
|
661
|
+
});
|
|
662
|
+
};
|
|
663
|
+
if ((roleSpec.markerPosition ?? defaultMarker.position) === "before") {
|
|
664
|
+
if (defaultMarker.primary) {
|
|
665
|
+
pushMarker(asMarker());
|
|
666
|
+
}
|
|
667
|
+
pushPrefixed();
|
|
668
|
+
} else {
|
|
669
|
+
pushPrefixed();
|
|
670
|
+
pushMarker(asMarker());
|
|
671
|
+
}
|
|
672
|
+
} else {
|
|
673
|
+
pushPrefixed();
|
|
674
|
+
}
|
|
675
|
+
return tokens;
|
|
676
|
+
}
|
|
677
|
+
function buildExtractionRules2(schema, profile) {
|
|
678
|
+
const rules = {};
|
|
679
|
+
for (const roleSpec of schema.roles) {
|
|
680
|
+
const overrideMarker = roleSpec.markerOverride?.[profile.code];
|
|
681
|
+
const defaultMarker = profile.roleMarkers[roleSpec.role];
|
|
682
|
+
if (roleSpec.valuePrefixLiteral?.[profile.code]) {
|
|
683
|
+
rules[roleSpec.role] = { marker: roleSpec.valuePrefixLiteral[profile.code] };
|
|
684
|
+
} else if (overrideMarker !== void 0) {
|
|
685
|
+
if (!overrideMarker) {
|
|
686
|
+
rules[roleSpec.role] = {};
|
|
687
|
+
} else {
|
|
688
|
+
const isSingleWord = !/\s/.test(overrideMarker.trim());
|
|
689
|
+
const markerAlternatives = isSingleWord ? schemaMarkerAlternatives(roleSpec, profile.code, overrideMarker) ?? [] : [];
|
|
690
|
+
rules[roleSpec.role] = markerAlternatives.length ? { marker: overrideMarker, markerAlternatives } : { marker: overrideMarker };
|
|
691
|
+
}
|
|
692
|
+
} else if (defaultMarker && defaultMarker.primary) {
|
|
693
|
+
const markerAlternatives = [
|
|
694
|
+
.../* @__PURE__ */ new Set([
|
|
695
|
+
...defaultMarker.alternatives ?? [],
|
|
696
|
+
...schemaMarkerAlternatives(roleSpec, profile.code, defaultMarker.primary) ?? []
|
|
697
|
+
])
|
|
698
|
+
].filter((a) => a !== defaultMarker.primary);
|
|
699
|
+
rules[roleSpec.role] = markerAlternatives.length ? { marker: defaultMarker.primary, markerAlternatives } : { marker: defaultMarker.primary };
|
|
700
|
+
} else {
|
|
701
|
+
rules[roleSpec.role] = {};
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return rules;
|
|
705
|
+
}
|
|
706
|
+
function buildExtractionRulesWithDefaults(schema, profile) {
|
|
707
|
+
const baseRules = buildExtractionRules2(schema, profile);
|
|
708
|
+
const rules = {};
|
|
709
|
+
for (const roleSpec of schema.roles) {
|
|
710
|
+
const baseRule = baseRules[roleSpec.role] || {};
|
|
711
|
+
if (!roleSpec.required && roleSpec.default) {
|
|
712
|
+
rules[roleSpec.role] = { ...baseRule, default: roleSpec.default };
|
|
713
|
+
} else {
|
|
714
|
+
rules[roleSpec.role] = baseRule;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
return rules;
|
|
718
|
+
}
|
|
719
|
+
function buildFormatString2(grammar, schema, profile, keyword) {
|
|
720
|
+
const parts = [];
|
|
721
|
+
const sortedRoles = sortRolesByPosition(schema.roles, profile.wordOrder, grammar.positionOrder);
|
|
722
|
+
const roleParts = sortedRoles.map((roleSpec) => {
|
|
723
|
+
const resolved = resolveMarkerForRole(roleSpec, profile);
|
|
724
|
+
let part = "";
|
|
725
|
+
if (resolved && resolved.primary) {
|
|
726
|
+
if (resolved.position === "before") {
|
|
727
|
+
part = `${resolved.primary} {${roleSpec.role}}`;
|
|
728
|
+
} else {
|
|
729
|
+
part = `{${roleSpec.role}} ${resolved.primary}`;
|
|
730
|
+
}
|
|
731
|
+
} else {
|
|
732
|
+
part = `{${roleSpec.role}}`;
|
|
733
|
+
}
|
|
734
|
+
return roleSpec.required ? part : `[${part}]`;
|
|
735
|
+
});
|
|
736
|
+
switch (profile.wordOrder) {
|
|
737
|
+
case "SVO":
|
|
738
|
+
case "VSO":
|
|
739
|
+
parts.push(keyword.primary, ...roleParts);
|
|
740
|
+
break;
|
|
741
|
+
case "SOV":
|
|
742
|
+
parts.push(...roleParts, keyword.primary);
|
|
743
|
+
break;
|
|
744
|
+
default:
|
|
745
|
+
parts.push(keyword.primary, ...roleParts);
|
|
746
|
+
}
|
|
747
|
+
return parts.join(" ");
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
// src/generation/pattern-render.ts
|
|
751
|
+
var NESTED_ROLE_BONUS = 5;
|
|
752
|
+
var UNCOVERED_ROLE_PENALTY = 30;
|
|
753
|
+
function pinnedLiteral(rule) {
|
|
754
|
+
if (rule?.default?.type === "literal") return String(rule.default.value).trim().toLowerCase();
|
|
755
|
+
if (rule?.value !== void 0) return String(rule.value).trim().toLowerCase();
|
|
756
|
+
return void 0;
|
|
757
|
+
}
|
|
758
|
+
function selectRenderPattern(node, patterns, hooks) {
|
|
759
|
+
let candidates = patterns.filter((pattern) => !pattern.parseOnly);
|
|
760
|
+
if (candidates.length === 0) candidates = patterns;
|
|
761
|
+
if (hooks.candidates) candidates = hooks.candidates(node, candidates);
|
|
762
|
+
const pinnedValues = /* @__PURE__ */ new Map();
|
|
763
|
+
for (const pattern of candidates) {
|
|
764
|
+
for (const [role, rule] of Object.entries(pattern.extraction ?? {})) {
|
|
765
|
+
const key = pinnedLiteral(rule);
|
|
766
|
+
if (key === void 0) continue;
|
|
767
|
+
const set = pinnedValues.get(role) ?? /* @__PURE__ */ new Set();
|
|
768
|
+
set.add(key);
|
|
769
|
+
pinnedValues.set(role, set);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
const scored = candidates.map((pattern) => {
|
|
773
|
+
let score = pattern.priority;
|
|
774
|
+
const hasRealRole = (role) => hooks.hasRealRole(node, role);
|
|
775
|
+
const scoreTokens = (tokens, inGroup) => {
|
|
776
|
+
for (const token of tokens) {
|
|
777
|
+
if (token.type === "group") {
|
|
778
|
+
scoreTokens(token.tokens, true);
|
|
779
|
+
} else if (token.type === "role") {
|
|
780
|
+
if (hasRealRole(token.role)) {
|
|
781
|
+
score += inGroup ? NESTED_ROLE_BONUS : 10;
|
|
782
|
+
} else if (!inGroup && !token.optional) {
|
|
783
|
+
score -= 50;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
scoreTokens(pattern.template.tokens, false);
|
|
789
|
+
const slots = /* @__PURE__ */ new Set();
|
|
790
|
+
const collectSlots = (tokens) => {
|
|
791
|
+
for (const token of tokens) {
|
|
792
|
+
if (token.type === "group") collectSlots(token.tokens);
|
|
793
|
+
else if (token.type === "role") slots.add(token.role);
|
|
794
|
+
}
|
|
795
|
+
};
|
|
796
|
+
collectSlots(pattern.template.tokens);
|
|
797
|
+
for (const role of node.roles.keys()) {
|
|
798
|
+
if (hasRealRole(role) && !slots.has(role)) score -= UNCOVERED_ROLE_PENALTY;
|
|
799
|
+
}
|
|
800
|
+
for (const [role, rule] of Object.entries(pattern.extraction ?? {})) {
|
|
801
|
+
const pinnedKey = pinnedLiteral(rule);
|
|
802
|
+
if (pinnedKey === void 0) continue;
|
|
803
|
+
const actual = node.roles.get(role);
|
|
804
|
+
const actualKey = actual?.type === (rule.valueIsExpression ? "expression" : "literal") ? String(
|
|
805
|
+
actual.type === "expression" ? actual.raw : actual.value
|
|
806
|
+
).trim().toLowerCase() : void 0;
|
|
807
|
+
if (actualKey === pinnedKey) {
|
|
808
|
+
score += 60;
|
|
809
|
+
} else if (actualKey === void 0 || pinnedValues.get(role)?.has(actualKey)) {
|
|
810
|
+
score -= 200;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
score += hooks.adjustScore?.(pattern, node) ?? 0;
|
|
814
|
+
return { pattern, score };
|
|
815
|
+
});
|
|
816
|
+
scored.sort((a, b) => b.score - a.score);
|
|
817
|
+
return scored.length > 0 ? scored[0].pattern : null;
|
|
818
|
+
}
|
|
819
|
+
function renderPattern(node, pattern, hooks) {
|
|
820
|
+
const parts = [];
|
|
821
|
+
const language = pattern.language;
|
|
822
|
+
const tokens = pattern.template.tokens;
|
|
823
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
824
|
+
hooks.beforeToken?.(tokens, i, parts, node, language);
|
|
825
|
+
const span = hooks.span?.(tokens, i, node, language, true);
|
|
826
|
+
if (span !== void 0) {
|
|
827
|
+
parts.push(span.text);
|
|
828
|
+
i += span.tokens - 1;
|
|
829
|
+
continue;
|
|
830
|
+
}
|
|
831
|
+
const token = tokens[i];
|
|
832
|
+
const rendered = renderPatternToken(token, node, language, hooks);
|
|
833
|
+
if (rendered !== null) {
|
|
834
|
+
pushPart(hooks, parts, rendered, token, tokens[i - 1], node, language);
|
|
835
|
+
hooks.afterToken?.(token, parts.length - 1, node);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
return parts;
|
|
839
|
+
}
|
|
840
|
+
function pushPart(hooks, parts, text, token, previous, node, language) {
|
|
841
|
+
if (hooks.pushPart) hooks.pushPart(parts, text, token, previous, node, language);
|
|
842
|
+
else parts.push(text);
|
|
843
|
+
}
|
|
844
|
+
function renderPatternToken(token, node, language, hooks) {
|
|
845
|
+
switch (token.type) {
|
|
846
|
+
case "literal":
|
|
847
|
+
return token.renderSuppress ? null : token.value;
|
|
848
|
+
case "role":
|
|
849
|
+
return hooks.writeRole(token, node, language);
|
|
850
|
+
case "group": {
|
|
851
|
+
const hasRequired = token.tokens.filter((t) => t.type === "role" && !t.optional).every((t) => node.roles.has(t.role));
|
|
852
|
+
if (!hasRequired && token.optional) {
|
|
853
|
+
return null;
|
|
854
|
+
}
|
|
855
|
+
if (hooks.writeGroup && !hooks.writeGroup(token, node, language)) return null;
|
|
856
|
+
const groupParts = [];
|
|
857
|
+
let hasRoleValue = false;
|
|
858
|
+
for (let i = 0; i < token.tokens.length; i++) {
|
|
859
|
+
const span = hooks.span?.(token.tokens, i, node, language, false);
|
|
860
|
+
if (span !== void 0) {
|
|
861
|
+
groupParts.push(span.text);
|
|
862
|
+
hasRoleValue = true;
|
|
863
|
+
i += span.tokens - 1;
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
const subToken = token.tokens[i];
|
|
867
|
+
const rendered = renderPatternToken(subToken, node, language, hooks);
|
|
868
|
+
if (rendered !== null) {
|
|
869
|
+
pushPart(hooks, groupParts, rendered, subToken, token.tokens[i - 1], node, language);
|
|
870
|
+
if (subToken.type === "role") hasRoleValue = true;
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (token.optional && !hasRoleValue && !token.renderRequired) return null;
|
|
874
|
+
return groupParts.length > 0 ? groupParts.join(" ") : null;
|
|
875
|
+
}
|
|
876
|
+
default:
|
|
877
|
+
return null;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
295
880
|
export {
|
|
296
881
|
buildPhrase,
|
|
297
882
|
buildTablesFromProfiles,
|
|
298
883
|
createDiagnosticCollector,
|
|
299
884
|
createDomainRenderer,
|
|
885
|
+
createSchemaPatternGenerator,
|
|
300
886
|
createSchemaRenderer,
|
|
301
887
|
detectWordOrders,
|
|
302
888
|
filterBySeverity,
|
|
303
889
|
fromError,
|
|
304
890
|
generatePattern,
|
|
305
891
|
generatePatternVariants,
|
|
892
|
+
legacyMarkerAlternatives,
|
|
306
893
|
lookupKeyword,
|
|
307
894
|
lookupMarker,
|
|
308
|
-
|
|
895
|
+
renderPattern,
|
|
896
|
+
resolveMarkerForRole,
|
|
897
|
+
schemaMarkerAlternatives,
|
|
898
|
+
selectRenderPattern,
|
|
899
|
+
sortRolesByWordOrder,
|
|
900
|
+
withoutVariantOnlyRoles
|
|
309
901
|
};
|
|
310
902
|
//# sourceMappingURL=index.js.map
|