@lokascript/framework 4.5.0 → 4.7.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.
Files changed (123) hide show
  1. package/CHANGELOG.md +220 -8
  2. package/dist/api/create-dsl.d.ts +59 -1
  3. package/dist/api/create-dsl.d.ts.map +1 -1
  4. package/dist/api/domain-registry.d.ts.map +1 -1
  5. package/dist/api/index.d.ts +1 -0
  6. package/dist/api/index.d.ts.map +1 -1
  7. package/dist/api/index.js +1938 -724
  8. package/dist/api/index.js.map +1 -1
  9. package/dist/api/reading.d.ts +8 -0
  10. package/dist/api/reading.d.ts.map +1 -0
  11. package/dist/api/translation-loss.d.ts +84 -0
  12. package/dist/api/translation-loss.d.ts.map +1 -0
  13. package/dist/core/index.d.ts +1 -0
  14. package/dist/core/index.d.ts.map +1 -1
  15. package/dist/core/index.js +1047 -684
  16. package/dist/core/index.js.map +1 -1
  17. package/dist/core/matching/confidence-model.d.ts +54 -0
  18. package/dist/core/matching/confidence-model.d.ts.map +1 -0
  19. package/dist/core/matching/index.d.ts +12 -0
  20. package/dist/core/matching/index.d.ts.map +1 -0
  21. package/dist/core/matching/match-skeleton.d.ts +114 -0
  22. package/dist/core/matching/match-skeleton.d.ts.map +1 -0
  23. package/dist/core/matching/stock-confidence.d.ts +15 -0
  24. package/dist/core/matching/stock-confidence.d.ts.map +1 -0
  25. package/dist/core/matching/stock-reader.d.ts +56 -0
  26. package/dist/core/matching/stock-reader.d.ts.map +1 -0
  27. package/dist/core/matching/stock-reading.d.ts +44 -0
  28. package/dist/core/matching/stock-reading.d.ts.map +1 -0
  29. package/dist/core/matching/value-reader.d.ts +62 -0
  30. package/dist/core/matching/value-reader.d.ts.map +1 -0
  31. package/dist/core/pattern-matching/index.js +561 -660
  32. package/dist/core/pattern-matching/index.js.map +1 -1
  33. package/dist/core/pattern-matching/pattern-matcher.d.ts +0 -117
  34. package/dist/core/pattern-matching/pattern-matcher.d.ts.map +1 -1
  35. package/dist/core/types.d.ts +48 -0
  36. package/dist/core/types.d.ts.map +1 -1
  37. package/dist/core/types.js.map +1 -1
  38. package/dist/generation/index.d.ts +2 -0
  39. package/dist/generation/index.d.ts.map +1 -1
  40. package/dist/generation/index.js +583 -1
  41. package/dist/generation/index.js.map +1 -1
  42. package/dist/generation/pattern-render.d.ts +72 -0
  43. package/dist/generation/pattern-render.d.ts.map +1 -0
  44. package/dist/generation/schema-patterns.d.ts +152 -0
  45. package/dist/generation/schema-patterns.d.ts.map +1 -0
  46. package/dist/index.cjs +2132 -890
  47. package/dist/index.cjs.map +1 -1
  48. package/dist/index.js +2110 -887
  49. package/dist/index.js.map +1 -1
  50. package/dist/ir/index.js.map +1 -1
  51. package/dist/parsing/index.js +561 -660
  52. package/dist/parsing/index.js.map +1 -1
  53. package/dist/testing/gate/index.d.ts +8 -0
  54. package/dist/testing/gate/index.d.ts.map +1 -0
  55. package/dist/testing/gate/index.js +632 -0
  56. package/dist/testing/gate/index.js.map +1 -0
  57. package/dist/testing/gate/lint/format.d.ts +7 -0
  58. package/dist/testing/gate/lint/format.d.ts.map +1 -0
  59. package/dist/testing/gate/lint/index.d.ts +30 -0
  60. package/dist/testing/gate/lint/index.d.ts.map +1 -0
  61. package/dist/testing/gate/lint/rules/extractor-coverage.d.ts +18 -0
  62. package/dist/testing/gate/lint/rules/extractor-coverage.d.ts.map +1 -0
  63. package/dist/testing/gate/lint/rules/index.d.ts +14 -0
  64. package/dist/testing/gate/lint/rules/index.d.ts.map +1 -0
  65. package/dist/testing/gate/lint/rules/keyword-classification.d.ts +19 -0
  66. package/dist/testing/gate/lint/rules/keyword-classification.d.ts.map +1 -0
  67. package/dist/testing/gate/lint/rules/keyword-coverage.d.ts +13 -0
  68. package/dist/testing/gate/lint/rules/keyword-coverage.d.ts.map +1 -0
  69. package/dist/testing/gate/lint/rules/marker-tokenization.d.ts +20 -0
  70. package/dist/testing/gate/lint/rules/marker-tokenization.d.ts.map +1 -0
  71. package/dist/testing/gate/lint/rules/position-ordering.d.ts +25 -0
  72. package/dist/testing/gate/lint/rules/position-ordering.d.ts.map +1 -0
  73. package/dist/testing/gate/lint/rules/renderer-coherence.d.ts +24 -0
  74. package/dist/testing/gate/lint/rules/renderer-coherence.d.ts.map +1 -0
  75. package/dist/testing/gate/lint/rules/schema-rules.d.ts +10 -0
  76. package/dist/testing/gate/lint/rules/schema-rules.d.ts.map +1 -0
  77. package/dist/testing/gate/lint/types.d.ts +74 -0
  78. package/dist/testing/gate/lint/types.d.ts.map +1 -0
  79. package/dist/testing/gate/round-trip.d.ts +163 -0
  80. package/dist/testing/gate/round-trip.d.ts.map +1 -0
  81. package/package.json +7 -3
  82. package/src/__test__/fixtures/sql-dsl.ts +11 -2
  83. package/src/__test__/sql-integration.test.ts +2 -2
  84. package/src/api/create-dsl-engine.test.ts +86 -0
  85. package/src/api/create-dsl.test.ts +120 -0
  86. package/src/api/create-dsl.ts +314 -39
  87. package/src/api/domain-registry.test.ts +70 -5
  88. package/src/api/domain-registry.ts +82 -1
  89. package/src/api/index.ts +1 -0
  90. package/src/api/reading.ts +29 -0
  91. package/src/api/translation-loss.test.ts +89 -0
  92. package/src/api/translation-loss.ts +128 -0
  93. package/src/core/index.ts +1 -0
  94. package/src/core/matching/confidence-model.ts +58 -0
  95. package/src/core/matching/index.ts +12 -0
  96. package/src/core/matching/match-skeleton.test.ts +179 -0
  97. package/src/core/matching/match-skeleton.ts +621 -0
  98. package/src/core/matching/stock-confidence.ts +238 -0
  99. package/src/core/matching/stock-reader.test.ts +181 -0
  100. package/src/core/matching/stock-reader.ts +186 -0
  101. package/src/core/matching/stock-reading.ts +681 -0
  102. package/src/core/matching/value-reader.ts +85 -0
  103. package/src/core/pattern-matching/pattern-matcher.ts +11 -876
  104. package/src/core/types.ts +48 -0
  105. package/src/generation/index.ts +2 -0
  106. package/src/generation/pattern-render.ts +378 -0
  107. package/src/generation/schema-patterns.test.ts +99 -0
  108. package/src/generation/schema-patterns.ts +962 -0
  109. package/src/testing/gate/index.ts +8 -0
  110. package/src/testing/gate/lint/format.ts +23 -0
  111. package/src/testing/gate/lint/index.ts +73 -0
  112. package/src/testing/gate/lint/lint.test.ts +131 -0
  113. package/src/testing/gate/lint/rules/extractor-coverage.ts +63 -0
  114. package/src/testing/gate/lint/rules/index.ts +37 -0
  115. package/src/testing/gate/lint/rules/keyword-classification.ts +52 -0
  116. package/src/testing/gate/lint/rules/keyword-coverage.ts +38 -0
  117. package/src/testing/gate/lint/rules/marker-tokenization.ts +129 -0
  118. package/src/testing/gate/lint/rules/position-ordering.ts +87 -0
  119. package/src/testing/gate/lint/rules/renderer-coherence.ts +100 -0
  120. package/src/testing/gate/lint/rules/schema-rules.ts +35 -0
  121. package/src/testing/gate/lint/types.ts +83 -0
  122. package/src/testing/gate/round-trip.test.ts +118 -0
  123. package/src/testing/gate/round-trip.ts +438 -0
@@ -292,19 +292,601 @@ 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 roleToken = {
408
+ type: "role",
409
+ role: r.role,
410
+ optional: false,
411
+ expectedTypes: r.expectedTypes
412
+ };
413
+ return prefix ? [{ type: "literal", value: prefix }, roleToken] : [roleToken];
414
+ });
415
+ return {
416
+ id: `${schema.action}-${profile.code}-generated-verb-first`,
417
+ language: profile.code,
418
+ command: schema.action,
419
+ // Strictly below the canonical (basePriority) and simple (basePriority - 5)
420
+ // patterns so native order is always preferred.
421
+ priority: (config.basePriority ?? 100) - 20,
422
+ template: {
423
+ format: `${keyword.primary} <${requiredRoles[0].role}>`,
424
+ tokens: [verbToken, ...roleTokens]
425
+ },
426
+ extraction: buildExtractionRulesWithDefaults(schema, profile)
427
+ };
428
+ }
429
+ function generatePatternVariants2(grammar, schema, profile, config = defaultConfig2) {
430
+ const patterns = [];
431
+ const full = schema;
432
+ schema = withoutVariantOnlyRoles(schema);
433
+ patterns.push(generatePattern2(grammar, schema, profile, config));
434
+ if (config.generateSimpleVariants) {
435
+ const simple = generateSimplePattern(grammar, schema, profile, config);
436
+ if (simple) {
437
+ patterns.push(simple);
438
+ }
439
+ }
440
+ for (const omitted of schema.omitRoleVariants ?? []) {
441
+ const variantSchema = {
442
+ ...schema,
443
+ roles: schema.roles.filter((r) => r.role !== omitted)
444
+ };
445
+ const variant = generatePattern2(grammar, variantSchema, profile, config);
446
+ patterns.push({
447
+ ...variant,
448
+ id: `${schema.action}-${profile.code}-generated-no-${omitted}`,
449
+ priority: (config.basePriority ?? 100) - 8
450
+ });
451
+ }
452
+ for (const widen of schema.widenTypeVariants ?? []) {
453
+ if (widen.excludeLanguages?.includes(profile.code)) continue;
454
+ const cloneSchema = {
455
+ ...schema,
456
+ roles: schema.roles.map(
457
+ (r) => r.role === widen.role ? { ...r, expectedTypes: [...widen.types] } : r
458
+ )
459
+ };
460
+ const main = generatePattern2(grammar, cloneSchema, profile, config);
461
+ patterns.push({
462
+ ...main,
463
+ id: `${schema.action}-${profile.code}-generated-${widen.idSuffix}`,
464
+ priority: (config.basePriority ?? 100) - (widen.priorityDelta ?? 15)
465
+ });
466
+ }
467
+ for (const v of full.requiredRoleVariants ?? []) {
468
+ const variantSchema = {
469
+ ...full,
470
+ roles: full.roles.filter((r) => !v.omit?.includes(r.role)).map((r) => {
471
+ const required = r.role === v.role ? { ...r, required: true } : r;
472
+ return r.role === v.widen?.role ? { ...required, expectedTypes: [...v.widen.types] } : required;
473
+ })
474
+ };
475
+ const variant = generatePattern2(grammar, variantSchema, profile, config);
476
+ patterns.push({
477
+ ...variant,
478
+ id: `${schema.action}-${profile.code}-generated-${v.idSuffix}`,
479
+ priority: (config.basePriority ?? 100) + (v.priorityDelta ?? 5)
480
+ });
481
+ }
482
+ if (config.generateVerbFirstVariants !== false) {
483
+ const verbFirst = generateVerbFirstPattern(schema, profile, config);
484
+ if (verbFirst) {
485
+ patterns.push(verbFirst);
486
+ }
487
+ }
488
+ for (const v of schema.rolePrefixLiteralVariants ?? []) {
489
+ const literal = v.literal[profile.code];
490
+ if (!literal) continue;
491
+ const { rolePrefixLiteralVariants: _omitted, ...baseSchema } = schema;
492
+ const cloneSchema = {
493
+ ...baseSchema,
494
+ roles: schema.roles.map(
495
+ (r) => r.role === v.role ? { ...r, valuePrefixLiteral: { [profile.code]: literal } } : r
496
+ )
497
+ };
498
+ const delta = v.priorityDelta ?? 5;
499
+ const carrier = v.methodCarrier ? { [v.methodCarrier]: { value: literal } } : {};
500
+ const main = generatePattern2(grammar, cloneSchema, profile, config);
501
+ patterns.push({
502
+ ...main,
503
+ id: `${schema.action}-${profile.code}-generated-${v.idSuffix}`,
504
+ priority: (config.basePriority ?? 100) + delta,
505
+ extraction: { ...main.extraction, ...carrier }
506
+ });
507
+ if (config.generateVerbFirstVariants !== false) {
508
+ const verbFirstUrl = generateVerbFirstPattern(cloneSchema, profile, config);
509
+ if (verbFirstUrl) {
510
+ patterns.push({
511
+ ...verbFirstUrl,
512
+ id: `${schema.action}-${profile.code}-generated-verb-first-${v.idSuffix}`,
513
+ priority: (config.basePriority ?? 100) - 20 + delta,
514
+ extraction: { ...verbFirstUrl.extraction, ...carrier }
515
+ });
516
+ }
517
+ }
518
+ }
519
+ return patterns;
520
+ }
521
+ function buildTokens2(grammar, schema, profile, keyword) {
522
+ const tokens = [];
523
+ const verbToken = keyword.alternatives ? { type: "literal", value: keyword.primary, alternatives: keyword.alternatives } : { type: "literal", value: keyword.primary };
524
+ const postVerbRoles = profile.wordOrder === "SOV" ? schema.roles.filter((r) => r.sovSlot === "postVerb") : [];
525
+ const roleTokens = buildRoleTokens(
526
+ grammar,
527
+ postVerbRoles.length ? { ...schema, roles: schema.roles.filter((r) => r.sovSlot !== "postVerb") } : schema,
528
+ profile
529
+ );
530
+ const postVerbTokens = postVerbRoles.length ? buildRoleTokens(grammar, { ...schema, roles: postVerbRoles }, profile) : [];
531
+ switch (profile.wordOrder) {
532
+ case "SVO":
533
+ tokens.push(verbToken);
534
+ tokens.push(...roleTokens);
535
+ break;
536
+ case "SOV":
537
+ tokens.push(...roleTokens);
538
+ tokens.push(verbToken);
539
+ tokens.push(...postVerbTokens);
540
+ break;
541
+ case "VSO":
542
+ tokens.push(verbToken);
543
+ tokens.push(...roleTokens);
544
+ break;
545
+ default:
546
+ tokens.push(verbToken);
547
+ tokens.push(...roleTokens);
548
+ }
549
+ return collapseAdjacentLiterals(tokens);
550
+ }
551
+ function collapseAdjacentLiterals(tokens) {
552
+ const out = [];
553
+ for (const tok of tokens) {
554
+ const prev = out[out.length - 1];
555
+ if (prev && prev.type === "literal" && tok.type === "literal" && prev.value === tok.value) {
556
+ const alts = [...prev.alternatives ?? [], ...tok.alternatives ?? []];
557
+ if (alts.length) out[out.length - 1] = { ...prev, alternatives: [...new Set(alts)] };
558
+ continue;
559
+ }
560
+ out.push(tok);
561
+ }
562
+ return out;
563
+ }
564
+ function buildRoleTokens(grammar, schema, profile) {
565
+ const tokens = [];
566
+ const sortedRoles = sortRolesByPosition(schema.roles, profile.wordOrder, grammar.positionOrder);
567
+ for (const roleSpec of sortedRoles) {
568
+ const roleToken = buildRoleToken(roleSpec, profile);
569
+ if (!roleSpec.required) {
570
+ tokens.push({
571
+ type: "group",
572
+ optional: true,
573
+ tokens: roleToken
574
+ });
575
+ } else {
576
+ tokens.push(...roleToken);
577
+ }
578
+ }
579
+ return tokens;
580
+ }
581
+ function buildRoleToken(roleSpec, profile) {
582
+ const tokens = [];
583
+ const overrideMarker = roleSpec.markerOverride?.[profile.code];
584
+ const defaultMarker = profile.roleMarkers[roleSpec.role];
585
+ const suppressMarker = roleSpec.renderOverride?.[profile.code] === "";
586
+ const roleValueToken = {
587
+ type: "role",
588
+ role: roleSpec.role,
589
+ optional: !roleSpec.required,
590
+ expectedTypes: roleSpec.expectedTypes,
591
+ // The matcher reads this too — a `'keyword'`-shaped slot holds a fixed
592
+ // keyword phrase (`using view transition`) and must not be skipped by the
593
+ // trailing-slot verb guard.
594
+ ...roleSpec.valueShape !== void 0 ? { valueShape: roleSpec.valueShape } : {},
595
+ // A greedy role reads up to the next marker the pattern owes (a domain's
596
+ // SQL condition); its value reader does the reading.
597
+ ...roleSpec.greedy ? { greedy: true } : {}
598
+ };
599
+ const prefixLiteral = roleSpec.valuePrefixLiteral?.[profile.code];
600
+ const pushPrefixed = () => {
601
+ if (prefixLiteral) tokens.push({ type: "literal", value: prefixLiteral });
602
+ tokens.push(roleValueToken);
603
+ };
604
+ if (overrideMarker !== void 0) {
605
+ const markerWords = overrideMarker ? overrideMarker.split(/\s+/).filter(Boolean) : [];
606
+ const isPassthroughOverride = overrideMarker !== void 0 && overrideMarker === roleSpec.markerOverride?.en;
607
+ const position = roleSpec.markerPositionOverride?.[profile.code] ?? roleSpec.markerPosition ?? defaultMarker?.position ?? (profile.wordOrder === "SOV" && !isPassthroughOverride ? "after" : "before");
608
+ const optionalMarker = roleSpec.markerOptional?.[profile.code] === true;
609
+ const pushWord = (word) => {
610
+ const alternatives = markerWords.length === 1 ? schemaMarkerAlternatives(roleSpec, profile.code, word) ?? [] : [];
611
+ const literal = {
612
+ type: "literal",
613
+ value: word,
614
+ ...alternatives.length ? { alternatives } : {},
615
+ ...suppressMarker ? { renderSuppress: true } : {}
616
+ };
617
+ tokens.push(optionalMarker ? { type: "group", optional: true, tokens: [literal] } : literal);
618
+ };
619
+ if (position === "before") {
620
+ for (const word of markerWords) pushWord(word);
621
+ pushPrefixed();
622
+ } else {
623
+ pushPrefixed();
624
+ for (const word of markerWords) pushWord(word);
625
+ }
626
+ } else if (defaultMarker) {
627
+ const asMarker = () => {
628
+ const alternatives = [
629
+ .../* @__PURE__ */ new Set([
630
+ ...defaultMarker.alternatives ?? [],
631
+ ...schemaMarkerAlternatives(roleSpec, profile.code, defaultMarker.primary) ?? []
632
+ ])
633
+ ].filter((a) => a !== defaultMarker.primary);
634
+ return {
635
+ type: "literal",
636
+ value: defaultMarker.primary,
637
+ ...alternatives.length ? { alternatives } : {},
638
+ ...suppressMarker ? { renderSuppress: true } : {}
639
+ };
640
+ };
641
+ const pushMarker = (marker) => {
642
+ if (!profile.markersOptional && !roleSpec.markerOptional?.[profile.code]) {
643
+ tokens.push(marker);
644
+ return;
645
+ }
646
+ tokens.push({
647
+ type: "group",
648
+ optional: true,
649
+ ...profile.markersOptional && !roleSpec.markerOptional?.[profile.code] ? { renderRequired: true } : {},
650
+ tokens: [marker]
651
+ });
652
+ };
653
+ if ((roleSpec.markerPosition ?? defaultMarker.position) === "before") {
654
+ if (defaultMarker.primary) {
655
+ pushMarker(asMarker());
656
+ }
657
+ pushPrefixed();
658
+ } else {
659
+ pushPrefixed();
660
+ pushMarker(asMarker());
661
+ }
662
+ } else {
663
+ pushPrefixed();
664
+ }
665
+ return tokens;
666
+ }
667
+ function buildExtractionRules2(schema, profile) {
668
+ const rules = {};
669
+ for (const roleSpec of schema.roles) {
670
+ const overrideMarker = roleSpec.markerOverride?.[profile.code];
671
+ const defaultMarker = profile.roleMarkers[roleSpec.role];
672
+ if (roleSpec.valuePrefixLiteral?.[profile.code]) {
673
+ rules[roleSpec.role] = { marker: roleSpec.valuePrefixLiteral[profile.code] };
674
+ } else if (overrideMarker !== void 0) {
675
+ if (!overrideMarker) {
676
+ rules[roleSpec.role] = {};
677
+ } else {
678
+ const isSingleWord = !/\s/.test(overrideMarker.trim());
679
+ const markerAlternatives = isSingleWord ? schemaMarkerAlternatives(roleSpec, profile.code, overrideMarker) ?? [] : [];
680
+ rules[roleSpec.role] = markerAlternatives.length ? { marker: overrideMarker, markerAlternatives } : { marker: overrideMarker };
681
+ }
682
+ } else if (defaultMarker && defaultMarker.primary) {
683
+ const markerAlternatives = [
684
+ .../* @__PURE__ */ new Set([
685
+ ...defaultMarker.alternatives ?? [],
686
+ ...schemaMarkerAlternatives(roleSpec, profile.code, defaultMarker.primary) ?? []
687
+ ])
688
+ ].filter((a) => a !== defaultMarker.primary);
689
+ rules[roleSpec.role] = markerAlternatives.length ? { marker: defaultMarker.primary, markerAlternatives } : { marker: defaultMarker.primary };
690
+ } else {
691
+ rules[roleSpec.role] = {};
692
+ }
693
+ }
694
+ return rules;
695
+ }
696
+ function buildExtractionRulesWithDefaults(schema, profile) {
697
+ const baseRules = buildExtractionRules2(schema, profile);
698
+ const rules = {};
699
+ for (const roleSpec of schema.roles) {
700
+ const baseRule = baseRules[roleSpec.role] || {};
701
+ if (!roleSpec.required && roleSpec.default) {
702
+ rules[roleSpec.role] = { ...baseRule, default: roleSpec.default };
703
+ } else {
704
+ rules[roleSpec.role] = baseRule;
705
+ }
706
+ }
707
+ return rules;
708
+ }
709
+ function buildFormatString2(grammar, schema, profile, keyword) {
710
+ const parts = [];
711
+ const sortedRoles = sortRolesByPosition(schema.roles, profile.wordOrder, grammar.positionOrder);
712
+ const roleParts = sortedRoles.map((roleSpec) => {
713
+ const resolved = resolveMarkerForRole(roleSpec, profile);
714
+ let part = "";
715
+ if (resolved && resolved.primary) {
716
+ if (resolved.position === "before") {
717
+ part = `${resolved.primary} {${roleSpec.role}}`;
718
+ } else {
719
+ part = `{${roleSpec.role}} ${resolved.primary}`;
720
+ }
721
+ } else {
722
+ part = `{${roleSpec.role}}`;
723
+ }
724
+ return roleSpec.required ? part : `[${part}]`;
725
+ });
726
+ switch (profile.wordOrder) {
727
+ case "SVO":
728
+ case "VSO":
729
+ parts.push(keyword.primary, ...roleParts);
730
+ break;
731
+ case "SOV":
732
+ parts.push(...roleParts, keyword.primary);
733
+ break;
734
+ default:
735
+ parts.push(keyword.primary, ...roleParts);
736
+ }
737
+ return parts.join(" ");
738
+ }
739
+
740
+ // src/generation/pattern-render.ts
741
+ var NESTED_ROLE_BONUS = 5;
742
+ var UNCOVERED_ROLE_PENALTY = 30;
743
+ function pinnedLiteral(rule) {
744
+ if (rule?.default?.type === "literal") return String(rule.default.value).trim().toLowerCase();
745
+ if (rule?.value !== void 0) return String(rule.value).trim().toLowerCase();
746
+ return void 0;
747
+ }
748
+ function selectRenderPattern(node, patterns, hooks) {
749
+ let candidates = patterns.filter((pattern) => !pattern.parseOnly);
750
+ if (candidates.length === 0) candidates = patterns;
751
+ if (hooks.candidates) candidates = hooks.candidates(node, candidates);
752
+ const pinnedValues = /* @__PURE__ */ new Map();
753
+ for (const pattern of candidates) {
754
+ for (const [role, rule] of Object.entries(pattern.extraction ?? {})) {
755
+ const key = pinnedLiteral(rule);
756
+ if (key === void 0) continue;
757
+ const set = pinnedValues.get(role) ?? /* @__PURE__ */ new Set();
758
+ set.add(key);
759
+ pinnedValues.set(role, set);
760
+ }
761
+ }
762
+ const scored = candidates.map((pattern) => {
763
+ let score = pattern.priority;
764
+ const hasRealRole = (role) => hooks.hasRealRole(node, role);
765
+ const scoreTokens = (tokens, inGroup) => {
766
+ for (const token of tokens) {
767
+ if (token.type === "group") {
768
+ scoreTokens(token.tokens, true);
769
+ } else if (token.type === "role") {
770
+ if (hasRealRole(token.role)) {
771
+ score += inGroup ? NESTED_ROLE_BONUS : 10;
772
+ } else if (!inGroup && !token.optional) {
773
+ score -= 50;
774
+ }
775
+ }
776
+ }
777
+ };
778
+ scoreTokens(pattern.template.tokens, false);
779
+ const slots = /* @__PURE__ */ new Set();
780
+ const collectSlots = (tokens) => {
781
+ for (const token of tokens) {
782
+ if (token.type === "group") collectSlots(token.tokens);
783
+ else if (token.type === "role") slots.add(token.role);
784
+ }
785
+ };
786
+ collectSlots(pattern.template.tokens);
787
+ for (const role of node.roles.keys()) {
788
+ if (hasRealRole(role) && !slots.has(role)) score -= UNCOVERED_ROLE_PENALTY;
789
+ }
790
+ for (const [role, rule] of Object.entries(pattern.extraction ?? {})) {
791
+ const pinnedKey = pinnedLiteral(rule);
792
+ if (pinnedKey === void 0) continue;
793
+ const actual = node.roles.get(role);
794
+ const actualKey = actual?.type === (rule.valueIsExpression ? "expression" : "literal") ? String(
795
+ actual.type === "expression" ? actual.raw : actual.value
796
+ ).trim().toLowerCase() : void 0;
797
+ if (actualKey === pinnedKey) {
798
+ score += 60;
799
+ } else if (actualKey === void 0 || pinnedValues.get(role)?.has(actualKey)) {
800
+ score -= 200;
801
+ }
802
+ }
803
+ score += hooks.adjustScore?.(pattern, node) ?? 0;
804
+ return { pattern, score };
805
+ });
806
+ scored.sort((a, b) => b.score - a.score);
807
+ return scored.length > 0 ? scored[0].pattern : null;
808
+ }
809
+ function renderPattern(node, pattern, hooks) {
810
+ const parts = [];
811
+ const language = pattern.language;
812
+ const tokens = pattern.template.tokens;
813
+ for (let i = 0; i < tokens.length; i++) {
814
+ hooks.beforeToken?.(tokens, i, parts, node, language);
815
+ const span = hooks.span?.(tokens, i, node, language, true);
816
+ if (span !== void 0) {
817
+ parts.push(span.text);
818
+ i += span.tokens - 1;
819
+ continue;
820
+ }
821
+ const token = tokens[i];
822
+ const rendered = renderPatternToken(token, node, language, hooks);
823
+ if (rendered !== null) {
824
+ pushPart(hooks, parts, rendered, token, tokens[i - 1], node, language);
825
+ hooks.afterToken?.(token, parts.length - 1, node);
826
+ }
827
+ }
828
+ return parts;
829
+ }
830
+ function pushPart(hooks, parts, text, token, previous, node, language) {
831
+ if (hooks.pushPart) hooks.pushPart(parts, text, token, previous, node, language);
832
+ else parts.push(text);
833
+ }
834
+ function renderPatternToken(token, node, language, hooks) {
835
+ switch (token.type) {
836
+ case "literal":
837
+ return token.renderSuppress ? null : token.value;
838
+ case "role":
839
+ return hooks.writeRole(token, node, language);
840
+ case "group": {
841
+ const hasRequired = token.tokens.filter((t) => t.type === "role" && !t.optional).every((t) => node.roles.has(t.role));
842
+ if (!hasRequired && token.optional) {
843
+ return null;
844
+ }
845
+ if (hooks.writeGroup && !hooks.writeGroup(token, node, language)) return null;
846
+ const groupParts = [];
847
+ let hasRoleValue = false;
848
+ for (let i = 0; i < token.tokens.length; i++) {
849
+ const span = hooks.span?.(token.tokens, i, node, language, false);
850
+ if (span !== void 0) {
851
+ groupParts.push(span.text);
852
+ hasRoleValue = true;
853
+ i += span.tokens - 1;
854
+ continue;
855
+ }
856
+ const subToken = token.tokens[i];
857
+ const rendered = renderPatternToken(subToken, node, language, hooks);
858
+ if (rendered !== null) {
859
+ pushPart(hooks, groupParts, rendered, subToken, token.tokens[i - 1], node, language);
860
+ if (subToken.type === "role") hasRoleValue = true;
861
+ }
862
+ }
863
+ if (token.optional && !hasRoleValue && !token.renderRequired) return null;
864
+ return groupParts.length > 0 ? groupParts.join(" ") : null;
865
+ }
866
+ default:
867
+ return null;
868
+ }
869
+ }
295
870
  export {
296
871
  buildPhrase,
297
872
  buildTablesFromProfiles,
298
873
  createDiagnosticCollector,
299
874
  createDomainRenderer,
875
+ createSchemaPatternGenerator,
300
876
  createSchemaRenderer,
301
877
  detectWordOrders,
302
878
  filterBySeverity,
303
879
  fromError,
304
880
  generatePattern,
305
881
  generatePatternVariants,
882
+ legacyMarkerAlternatives,
306
883
  lookupKeyword,
307
884
  lookupMarker,
308
- sortRolesByWordOrder
885
+ renderPattern,
886
+ resolveMarkerForRole,
887
+ schemaMarkerAlternatives,
888
+ selectRenderPattern,
889
+ sortRolesByWordOrder,
890
+ withoutVariantOnlyRoles
309
891
  };
310
892
  //# sourceMappingURL=index.js.map