@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.
Files changed (116) hide show
  1. package/CHANGELOG.md +121 -1
  2. package/dist/api/create-dsl.d.ts +18 -2
  3. package/dist/api/create-dsl.d.ts.map +1 -1
  4. package/dist/api/index.d.ts +1 -0
  5. package/dist/api/index.d.ts.map +1 -1
  6. package/dist/api/index.js +1799 -713
  7. package/dist/api/index.js.map +1 -1
  8. package/dist/api/translation-loss.d.ts +84 -0
  9. package/dist/api/translation-loss.d.ts.map +1 -0
  10. package/dist/core/index.d.ts +1 -0
  11. package/dist/core/index.d.ts.map +1 -1
  12. package/dist/core/index.js +1057 -684
  13. package/dist/core/index.js.map +1 -1
  14. package/dist/core/matching/confidence-model.d.ts +54 -0
  15. package/dist/core/matching/confidence-model.d.ts.map +1 -0
  16. package/dist/core/matching/index.d.ts +12 -0
  17. package/dist/core/matching/index.d.ts.map +1 -0
  18. package/dist/core/matching/match-skeleton.d.ts +114 -0
  19. package/dist/core/matching/match-skeleton.d.ts.map +1 -0
  20. package/dist/core/matching/stock-confidence.d.ts +15 -0
  21. package/dist/core/matching/stock-confidence.d.ts.map +1 -0
  22. package/dist/core/matching/stock-reader.d.ts +63 -0
  23. package/dist/core/matching/stock-reader.d.ts.map +1 -0
  24. package/dist/core/matching/stock-reading.d.ts +44 -0
  25. package/dist/core/matching/stock-reading.d.ts.map +1 -0
  26. package/dist/core/matching/value-reader.d.ts +62 -0
  27. package/dist/core/matching/value-reader.d.ts.map +1 -0
  28. package/dist/core/pattern-matching/index.js +561 -660
  29. package/dist/core/pattern-matching/index.js.map +1 -1
  30. package/dist/core/pattern-matching/pattern-matcher.d.ts +0 -117
  31. package/dist/core/pattern-matching/pattern-matcher.d.ts.map +1 -1
  32. package/dist/core/types.d.ts +53 -0
  33. package/dist/core/types.d.ts.map +1 -1
  34. package/dist/core/types.js.map +1 -1
  35. package/dist/generation/index.d.ts +2 -0
  36. package/dist/generation/index.d.ts.map +1 -1
  37. package/dist/generation/index.js +593 -1
  38. package/dist/generation/index.js.map +1 -1
  39. package/dist/generation/pattern-render.d.ts +72 -0
  40. package/dist/generation/pattern-render.d.ts.map +1 -0
  41. package/dist/generation/schema-patterns.d.ts +152 -0
  42. package/dist/generation/schema-patterns.d.ts.map +1 -0
  43. package/dist/index.cjs +2016 -904
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.js +1998 -903
  46. package/dist/index.js.map +1 -1
  47. package/dist/ir/index.js.map +1 -1
  48. package/dist/parsing/index.js +561 -660
  49. package/dist/parsing/index.js.map +1 -1
  50. package/dist/testing/gate/index.d.ts +8 -0
  51. package/dist/testing/gate/index.d.ts.map +1 -0
  52. package/dist/testing/gate/index.js +664 -0
  53. package/dist/testing/gate/index.js.map +1 -0
  54. package/dist/testing/gate/lint/format.d.ts +7 -0
  55. package/dist/testing/gate/lint/format.d.ts.map +1 -0
  56. package/dist/testing/gate/lint/index.d.ts +30 -0
  57. package/dist/testing/gate/lint/index.d.ts.map +1 -0
  58. package/dist/testing/gate/lint/rules/extractor-coverage.d.ts +18 -0
  59. package/dist/testing/gate/lint/rules/extractor-coverage.d.ts.map +1 -0
  60. package/dist/testing/gate/lint/rules/index.d.ts +14 -0
  61. package/dist/testing/gate/lint/rules/index.d.ts.map +1 -0
  62. package/dist/testing/gate/lint/rules/keyword-classification.d.ts +19 -0
  63. package/dist/testing/gate/lint/rules/keyword-classification.d.ts.map +1 -0
  64. package/dist/testing/gate/lint/rules/keyword-coverage.d.ts +13 -0
  65. package/dist/testing/gate/lint/rules/keyword-coverage.d.ts.map +1 -0
  66. package/dist/testing/gate/lint/rules/marker-tokenization.d.ts +20 -0
  67. package/dist/testing/gate/lint/rules/marker-tokenization.d.ts.map +1 -0
  68. package/dist/testing/gate/lint/rules/position-ordering.d.ts +25 -0
  69. package/dist/testing/gate/lint/rules/position-ordering.d.ts.map +1 -0
  70. package/dist/testing/gate/lint/rules/renderer-coherence.d.ts +24 -0
  71. package/dist/testing/gate/lint/rules/renderer-coherence.d.ts.map +1 -0
  72. package/dist/testing/gate/lint/rules/schema-rules.d.ts +10 -0
  73. package/dist/testing/gate/lint/rules/schema-rules.d.ts.map +1 -0
  74. package/dist/testing/gate/lint/types.d.ts +74 -0
  75. package/dist/testing/gate/lint/types.d.ts.map +1 -0
  76. package/dist/testing/gate/round-trip.d.ts +184 -0
  77. package/dist/testing/gate/round-trip.d.ts.map +1 -0
  78. package/package.json +6 -2
  79. package/src/__test__/fixtures/sql-dsl.ts +8 -2
  80. package/src/api/create-dsl-engine.test.ts +150 -0
  81. package/src/api/create-dsl.test.ts +7 -0
  82. package/src/api/create-dsl.ts +173 -22
  83. package/src/api/index.ts +1 -0
  84. package/src/api/translation-loss.test.ts +89 -0
  85. package/src/api/translation-loss.ts +128 -0
  86. package/src/core/index.ts +1 -0
  87. package/src/core/matching/confidence-model.ts +58 -0
  88. package/src/core/matching/index.ts +12 -0
  89. package/src/core/matching/match-skeleton.test.ts +179 -0
  90. package/src/core/matching/match-skeleton.ts +621 -0
  91. package/src/core/matching/stock-confidence.ts +238 -0
  92. package/src/core/matching/stock-reader.test.ts +265 -0
  93. package/src/core/matching/stock-reader.ts +205 -0
  94. package/src/core/matching/stock-reading.ts +681 -0
  95. package/src/core/matching/value-reader.ts +85 -0
  96. package/src/core/pattern-matching/pattern-matcher.ts +11 -876
  97. package/src/core/types.ts +53 -0
  98. package/src/generation/index.ts +2 -0
  99. package/src/generation/pattern-render.ts +378 -0
  100. package/src/generation/schema-patterns.test.ts +139 -0
  101. package/src/generation/schema-patterns.ts +974 -0
  102. package/src/testing/gate/index.ts +8 -0
  103. package/src/testing/gate/lint/format.ts +23 -0
  104. package/src/testing/gate/lint/index.ts +73 -0
  105. package/src/testing/gate/lint/lint.test.ts +131 -0
  106. package/src/testing/gate/lint/rules/extractor-coverage.ts +63 -0
  107. package/src/testing/gate/lint/rules/index.ts +37 -0
  108. package/src/testing/gate/lint/rules/keyword-classification.ts +52 -0
  109. package/src/testing/gate/lint/rules/keyword-coverage.ts +38 -0
  110. package/src/testing/gate/lint/rules/marker-tokenization.ts +129 -0
  111. package/src/testing/gate/lint/rules/position-ordering.ts +87 -0
  112. package/src/testing/gate/lint/rules/renderer-coherence.ts +100 -0
  113. package/src/testing/gate/lint/rules/schema-rules.ts +35 -0
  114. package/src/testing/gate/lint/types.ts +83 -0
  115. package/src/testing/gate/round-trip.test.ts +208 -0
  116. package/src/testing/gate/round-trip.ts +503 -0
@@ -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
- sortRolesByWordOrder
895
+ renderPattern,
896
+ resolveMarkerForRole,
897
+ schemaMarkerAlternatives,
898
+ selectRenderPattern,
899
+ sortRolesByWordOrder,
900
+ withoutVariantOnlyRoles
309
901
  };
310
902
  //# sourceMappingURL=index.js.map