calliope-ts 0.0.2 → 0.0.3

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 (159) hide show
  1. package/README.md +3 -13
  2. package/dist/caesura.d.ts +33 -0
  3. package/dist/caesura.d.ts.map +1 -0
  4. package/dist/caesura.js +202 -0
  5. package/dist/calliope/boundaries.d.ts +19 -0
  6. package/dist/calliope/boundaries.d.ts.map +1 -0
  7. package/dist/calliope/boundaries.js +182 -0
  8. package/dist/calliope/bracketing.d.ts +11 -0
  9. package/dist/calliope/bracketing.d.ts.map +1 -0
  10. package/dist/calliope/bracketing.js +416 -0
  11. package/dist/calliope/deps.d.ts +4 -0
  12. package/dist/calliope/deps.d.ts.map +1 -0
  13. package/dist/calliope/deps.js +181 -0
  14. package/dist/calliope/engine.d.ts +3 -0
  15. package/dist/calliope/engine.d.ts.map +1 -0
  16. package/dist/calliope/engine.js +71 -0
  17. package/dist/calliope/feats.d.ts +9 -0
  18. package/dist/calliope/feats.d.ts.map +1 -0
  19. package/dist/calliope/feats.js +45 -0
  20. package/dist/calliope/names.d.ts +7 -0
  21. package/dist/calliope/names.d.ts.map +1 -0
  22. package/dist/calliope/names.js +42 -0
  23. package/dist/calliope/postag.d.ts +8 -0
  24. package/dist/calliope/postag.d.ts.map +1 -0
  25. package/dist/calliope/postag.js +250 -0
  26. package/dist/calliope/prosodic.d.ts +3 -0
  27. package/dist/calliope/prosodic.d.ts.map +1 -0
  28. package/dist/calliope/prosodic.js +275 -0
  29. package/dist/calliope/relstress.d.ts +4 -0
  30. package/dist/calliope/relstress.d.ts.map +1 -0
  31. package/dist/calliope/relstress.js +688 -0
  32. package/dist/calliope/stressrules.d.ts +28 -0
  33. package/dist/calliope/stressrules.d.ts.map +1 -0
  34. package/dist/calliope/stressrules.js +147 -0
  35. package/dist/calliope/syntax.d.ts +38 -0
  36. package/dist/calliope/syntax.d.ts.map +1 -0
  37. package/dist/calliope/syntax.js +234 -0
  38. package/dist/calliope/udpos.d.ts +13 -0
  39. package/dist/calliope/udpos.d.ts.map +1 -0
  40. package/dist/calliope/udpos.js +156 -0
  41. package/dist/clio/caesura.d.ts +27 -0
  42. package/dist/clio/caesura.d.ts.map +1 -0
  43. package/dist/clio/caesura.js +148 -0
  44. package/dist/clio/depfix.d.ts +13 -0
  45. package/dist/clio/depfix.d.ts.map +1 -0
  46. package/dist/clio/depfix.js +84 -0
  47. package/dist/clio/display.d.ts +32 -0
  48. package/dist/clio/display.d.ts.map +1 -0
  49. package/dist/clio/display.js +976 -0
  50. package/dist/clio/engine.d.ts +3 -0
  51. package/dist/clio/engine.d.ts.map +1 -0
  52. package/dist/clio/engine.js +28 -0
  53. package/dist/clio/parser.d.ts +10 -0
  54. package/dist/clio/parser.d.ts.map +1 -0
  55. package/dist/clio/parser.js +696 -0
  56. package/dist/clio/phonological.d.ts +41 -0
  57. package/dist/clio/phonological.d.ts.map +1 -0
  58. package/dist/clio/phonological.js +788 -0
  59. package/dist/clio/phrasestress.d.ts +6 -0
  60. package/dist/clio/phrasestress.d.ts.map +1 -0
  61. package/dist/clio/phrasestress.js +106 -0
  62. package/dist/clio/pipeline.d.ts +11 -0
  63. package/dist/clio/pipeline.d.ts.map +1 -0
  64. package/dist/clio/pipeline.js +147 -0
  65. package/dist/clio/rhyme.d.ts +65 -0
  66. package/dist/clio/rhyme.d.ts.map +1 -0
  67. package/dist/clio/rhyme.js +761 -0
  68. package/dist/clio/scandroid.d.ts +17 -0
  69. package/dist/clio/scandroid.d.ts.map +1 -0
  70. package/dist/clio/scandroid.js +435 -0
  71. package/dist/clio/scansion.d.ts +46 -0
  72. package/dist/clio/scansion.d.ts.map +1 -0
  73. package/dist/clio/scansion.js +1086 -0
  74. package/dist/clio/semantics.d.ts +44 -0
  75. package/dist/clio/semantics.d.ts.map +1 -0
  76. package/dist/clio/semantics.js +139 -0
  77. package/dist/clio/stress.d.ts +83 -0
  78. package/dist/clio/stress.d.ts.map +1 -0
  79. package/dist/clio/stress.js +1737 -0
  80. package/dist/clio/tagfix.d.ts +6 -0
  81. package/dist/clio/tagfix.d.ts.map +1 -0
  82. package/dist/clio/tagfix.js +101 -0
  83. package/dist/display.d.ts +0 -6
  84. package/dist/display.d.ts.map +1 -1
  85. package/dist/display.js +322 -141
  86. package/dist/engine.d.ts +9 -0
  87. package/dist/engine.d.ts.map +1 -0
  88. package/dist/engine.js +12 -0
  89. package/dist/index.d.ts +4 -3
  90. package/dist/index.d.ts.map +1 -1
  91. package/dist/index.js +169 -26
  92. package/dist/parser.d.ts +3 -3
  93. package/dist/parser.d.ts.map +1 -1
  94. package/dist/parser.js +320 -563
  95. package/dist/phonological.d.ts +1 -1
  96. package/dist/phonological.d.ts.map +1 -1
  97. package/dist/phonological.js +73 -4
  98. package/dist/phrasestress.d.ts +6 -0
  99. package/dist/phrasestress.d.ts.map +1 -0
  100. package/dist/phrasestress.js +106 -0
  101. package/dist/rhyme.d.ts +40 -1
  102. package/dist/rhyme.d.ts.map +1 -1
  103. package/dist/rhyme.js +435 -7
  104. package/dist/scansion.d.ts +9 -0
  105. package/dist/scansion.d.ts.map +1 -1
  106. package/dist/scansion.js +145 -18
  107. package/dist/semantics.d.ts +44 -0
  108. package/dist/semantics.d.ts.map +1 -0
  109. package/dist/semantics.js +139 -0
  110. package/dist/stress.d.ts +127 -4
  111. package/dist/stress.d.ts.map +1 -1
  112. package/dist/stress.js +834 -61
  113. package/dist/types.d.ts +15 -0
  114. package/dist/types.d.ts.map +1 -1
  115. package/package.json +5 -3
  116. package/src/caesura.ts +201 -0
  117. package/src/calliope/boundaries.ts +190 -0
  118. package/src/calliope/bracketing.ts +390 -0
  119. package/src/calliope/deps.ts +160 -0
  120. package/src/calliope/engine.ts +77 -0
  121. package/src/calliope/feats.ts +46 -0
  122. package/src/calliope/names.ts +44 -0
  123. package/src/calliope/postag.ts +253 -0
  124. package/src/calliope/prosodic.ts +262 -0
  125. package/src/calliope/relstress.ts +645 -0
  126. package/src/calliope/stressrules.ts +147 -0
  127. package/src/calliope/syntax.ts +218 -0
  128. package/src/calliope/udpos.ts +152 -0
  129. package/src/clio/caesura.ts +145 -0
  130. package/src/clio/depfix.ts +88 -0
  131. package/src/clio/display.ts +1042 -0
  132. package/src/clio/engine.ts +38 -0
  133. package/src/clio/parser.ts +845 -0
  134. package/src/clio/phonological.ts +849 -0
  135. package/src/clio/phrasestress.ts +108 -0
  136. package/src/clio/pipeline.ts +154 -0
  137. package/src/clio/rhyme.ts +740 -0
  138. package/src/clio/scandroid.ts +434 -0
  139. package/src/clio/scansion.ts +1130 -0
  140. package/src/clio/semantics.ts +134 -0
  141. package/src/clio/stress.ts +1731 -0
  142. package/src/clio/tagfix.ts +104 -0
  143. package/src/display.ts +321 -137
  144. package/src/engine.ts +22 -0
  145. package/src/index.ts +176 -32
  146. package/src/parser.ts +346 -682
  147. package/src/phonological.ts +72 -4
  148. package/src/phrasestress.ts +108 -0
  149. package/src/rhyme.ts +428 -8
  150. package/src/scansion.ts +136 -15
  151. package/src/semantics.ts +134 -0
  152. package/src/stress.ts +794 -64
  153. package/src/types.ts +39 -4
  154. package/tests/DataForHayesLinesOnly.txt +364 -0
  155. package/tests/DataForHayesStressSymbolsRevised.txt +728 -0
  156. package/tests/basic.test.ts +443 -37
  157. package/tests/bench-hayes.mjs +72 -0
  158. package/tests/wagner-stress.test.ts +188 -0
  159. package/vitest.config.ts +0 -15
@@ -0,0 +1,275 @@
1
+ // calliope/prosodic.ts — the Match-Theory prosodic hierarchy for Calliope.
2
+ //
3
+ // Builds the κ (clitic group) / ϕ (phonological phrase) / ι (intonational unit)
4
+ // structure from the canonical dependency relations and the REAL utterance
5
+ // boundaries — replacing the legacy builder (phonological.ts), which split an IU
6
+ // at every comma and grouped phrases off the constituent tree. The corrections,
7
+ // per McAleese A1/A2 and the maintainer's critique:
8
+ //
9
+ // ι only at genuine intonational breaks: terminal punctuation (. ? !), colon,
10
+ // semicolon, and parentheticals. A plain COMMA is NOT an ι — it is a minor
11
+ // ϕ break. (This is the "utterance boundaries, not line breaks / not every
12
+ // comma" fix.)
13
+ // ϕ at: a comma; the head of an oblique PP (a preposition opens a new phrase —
14
+ // "…compare thee | to a summer's day"); each coordinate conjunct; a clause
15
+ // complement (CCOMP/XCOMP/ADVCL); and the junction between a full nominal
16
+ // SUBJECT and its predicate verb.
17
+ // κ a content head plus its function words by DIRECTION: determiners, case
18
+ // markers/prepositions, auxiliaries, coordinators, complementisers, numerals,
19
+ // possessives and (sub/obj) pronouns procliticise rightward onto the following
20
+ // head; the possessive 's and verb particles encliticise leftward. A trailing
21
+ // proclitic with no head to its right attaches to the preceding group (so
22
+ // "…compare thee" keeps the object pronoun in the verb's group).
23
+ //
24
+ // Output is the shared IntonationalUnit[] shape, so display/scansion are unchanged.
25
+ // It reads only POS + canonicalRel + surface order; it never mutates the parse.
26
+ import { computePhiDomains } from './bracketing.js';
27
+ // ─── punctuation → break kind ──────────────────────────────────────
28
+ const IU_PUNCT_TAGS = new Set(['.', '!', '?', ':', ';', '-LRB-', '-RRB-', '(', ')']);
29
+ const IU_PUNCT_WORDS = new Set(['.', '!', '?', '…', ':', ';', '(', ')']);
30
+ function breakKind(w) {
31
+ if (IU_PUNCT_TAGS.has(w.lexicalClass) || IU_PUNCT_WORDS.has(w.word))
32
+ return 'iu';
33
+ if (w.lexicalClass === ',' || w.word === ',')
34
+ return 'phi';
35
+ return null; // other punctuation (quotes, etc.) — transparent to phrasing
36
+ }
37
+ function isAnyPunct(w) {
38
+ return /^[^A-Za-z0-9]+$/.test(w.lexicalClass) ||
39
+ w.lexicalClass === '-LRB-' || w.lexicalClass === '-RRB-';
40
+ }
41
+ // ─── κ: directional cliticisation ──────────────────────────────────
42
+ const PROCLITIC_REL = new Set([
43
+ 'DET', 'CASE', 'AUX', 'AUXPASS', 'CC', 'COMPMARK', 'ADVMARK', 'NUMMOD', 'EXPL',
44
+ ]);
45
+ const PROCLITIC_POS = /^(DT|PDT|IN|TO|CC|MD|WDT|WP|WP\$|EX|PRP\$|PRP)$/;
46
+ const OBJECT_REL = new Set(['DOBJ', 'IOBJ', 'OBL', 'OBJ']);
47
+ /** A clitic that leans LEFTWARD onto the preceding head (possessive 's, particle,
48
+ * OBJECT pronoun). An object pronoun encliticises to its verb ("compare thee",
49
+ * "give me") — so it must NOT be pulled rightward into the next phrase, which is
50
+ * what buried the preposition in "…compare thee | to a summer's day". */
51
+ function isEnclitic(w) {
52
+ if (w.lexicalClass === 'POS' || w.lexicalClass === 'RP' || w.canonicalRel === 'VPRT')
53
+ return true;
54
+ if (w.lexicalClass === 'PRP' && OBJECT_REL.has(w.canonicalRel ?? ''))
55
+ return true;
56
+ return false;
57
+ }
58
+ /** A function word that leans RIGHTWARD onto the following content head. */
59
+ function isProclitic(w) {
60
+ if (w.isContent)
61
+ return false; // a promoted particle/demonstrative is a head
62
+ if (isEnclitic(w))
63
+ return false;
64
+ if (PROCLITIC_REL.has(w.canonicalRel ?? ''))
65
+ return true;
66
+ return PROCLITIC_POS.test(w.lexicalClass);
67
+ }
68
+ /** Build clitic groups over one IU segment's content/function words (no punct). */
69
+ function buildCliticGroups(words) {
70
+ const groups = [];
71
+ let pending = []; // proclitics awaiting their head
72
+ for (const w of words) {
73
+ if (isEnclitic(w) && groups.length > 0 && pending.length === 0) {
74
+ groups[groups.length - 1].tokens.push(w);
75
+ continue;
76
+ }
77
+ if (isProclitic(w)) {
78
+ pending.push(w);
79
+ continue;
80
+ }
81
+ // content head (or any non-clitic): open a CP with its pending proclitics.
82
+ groups.push({ tokens: [...pending, w] });
83
+ pending = [];
84
+ }
85
+ if (pending.length) {
86
+ if (groups.length)
87
+ groups[groups.length - 1].tokens.push(...pending);
88
+ else
89
+ groups.push({ tokens: pending });
90
+ }
91
+ return groups;
92
+ }
93
+ // ─── ϕ: phrase grouping (by dependency ϕ-domain) ───────────────────
94
+ // A ϕ boundary opens between two clitic groups when they fall in DIFFERENT
95
+ // ϕ-domains of the dependency tree (computePhiDomains, bracketing.ts) — the
96
+ // SAME constituent structure the cyclic stress rules use — replacing the old
97
+ // POS-keyed opensPhrase heuristic. So a stranded particle no longer opens a
98
+ // spurious oblique ϕ ("thought of" stays together), a branching object NP gets
99
+ // its own ϕ (reading | the latest biography), and a clause does not flatten
100
+ // when its head is mis-tagged (the relations, not the POS, decide).
101
+ function rightmostContent(cg) {
102
+ for (let i = cg.tokens.length - 1; i >= 0; i--)
103
+ if (cg.tokens[i].isContent)
104
+ return cg.tokens[i];
105
+ return cg.tokens[cg.tokens.length - 1];
106
+ }
107
+ /** The ϕ-domain id a clitic group sits in: the domain of its head (rightmost
108
+ * content word, else last token); a function-only group falls back to any of
109
+ * its tokens' domains (its proclitics share the domain of the head they lean
110
+ * onto). A new ϕ opens whenever this id changes between adjacent groups. */
111
+ function domainOf(cg, dom) {
112
+ const head = rightmostContent(cg) ?? cg.tokens[cg.tokens.length - 1];
113
+ const d = dom.get(head);
114
+ if (d !== undefined)
115
+ return d;
116
+ for (const t of cg.tokens) {
117
+ const dd = dom.get(t);
118
+ if (dd !== undefined)
119
+ return dd;
120
+ }
121
+ return -1;
122
+ }
123
+ /** Two PARSE-ROBUST ϕ-boundary markers from McAleese's Table-1 that the
124
+ * dependency-domain core cannot see when en-parse mis-attaches:
125
+ *
126
+ * • a COORDINATOR (CC "and / or / but") — each coordinate conjunct is its own
127
+ * ϕ; en-parse often flattens "old and gray and full" into a list of AMODs,
128
+ * so the conj relation is gone, but the CC token survives reliably.
129
+ * • a RELATIVE PRONOUN (WDT/WP "that / which / who") — it opens the relative
130
+ * clause's ϕ; en-parse routinely fails to build the ACL ("…the cat that
131
+ * caught the rat" flattened into one clause), but the relativiser is tagged.
132
+ *
133
+ * These are the two Table-1 triggers that survive attachment errors, so they
134
+ * supplement the dependency domains rather than re-introducing POS guesswork. */
135
+ function startsCoordOrRelative(cg) {
136
+ const t = cg.tokens[0];
137
+ if (!t)
138
+ return false;
139
+ if (t.lexicalClass === 'CC' || t.canonicalRel === 'CC')
140
+ return true;
141
+ return /^(WDT|WP|WP\$)$/.test(t.lexicalClass);
142
+ }
143
+ // A φ needs a stress-bearing ANCHOR to stand on its own. A content word is the
144
+ // usual one, but the 'n'-tier function words — demonstratives, quantifiers, and
145
+ // wh-words (relstress.functionLevel → 'n') — also carry a real beat, so a phrase
146
+ // built around one ("for THAT", "to EACH", "by WHICH") is a genuine φ with a
147
+ // nuclear and must NOT be dissolved into a neighbour the way a pure article /
148
+ // preposition / pronoun run ("around it") is. (Lemma/POS list mirrors relstress'
149
+ // 'n' category; kept local so the hierarchy layer does not depend on the stress one.)
150
+ const ANCHOR_LEMMAS = new Set([
151
+ 'this', 'that', 'these', 'those',
152
+ 'all', 'both', 'each', 'every', 'some', 'any', 'many', 'much', 'few', 'most',
153
+ 'half', 'several', 'either', 'neither', 'enough', 'none',
154
+ ]);
155
+ const ANCHOR_POS = /^(PDT|WDT|WP|WP\$|WRB)$/;
156
+ function canAnchorBeat(pp) {
157
+ return pp.cliticGroups.some(cg => cg.tokens.some(t => t.isContent ||
158
+ /^VB/.test(t.lexicalClass) || // a verb group ("has been") is a real ϕ even
159
+ // when its tokens are non-content auxiliaries —
160
+ // it must not fold back into the subject NP
161
+ ANCHOR_LEMMAS.has(t.word.toLowerCase().replace(/['’]/g, '')) ||
162
+ ANCHOR_POS.test(t.lexicalClass)));
163
+ }
164
+ function groupIntoPhrases(cgs, commaBeforeCG, dom) {
165
+ const phrases = [];
166
+ let current = [];
167
+ let prev = null;
168
+ let currentCommaPreceded = false;
169
+ for (const cg of cgs) {
170
+ const brk = current.length > 0 && prev !== null &&
171
+ (commaBeforeCG.has(cg) || startsCoordOrRelative(cg) ||
172
+ domainOf(cg, dom) !== domainOf(prev, dom));
173
+ if (brk) {
174
+ phrases.push({ cgs: current, commaPreceded: currentCommaPreceded });
175
+ current = [];
176
+ currentCommaPreceded = commaBeforeCG.has(cg);
177
+ }
178
+ current.push(cg);
179
+ prev = cg;
180
+ }
181
+ if (current.length)
182
+ phrases.push({ cgs: current, commaPreceded: currentCommaPreceded });
183
+ // A ϕ must have a beat-anchor: a phrase of only un-anchored function words (a
184
+ // stranded preposition + pronoun like "around it", an orphan determiner) has no
185
+ // nuclear to carry a beat, so it MERGES into its neighbour — into the previous
186
+ // phrase when there is one (it leans back onto the head it modifies), else the
187
+ // next. A phrase anchored by a demonstrative/quantifier/wh ("for THAT") is NOT
188
+ // merged — it is a genuine φ.
189
+ //
190
+ // BUT: a phrase preceded by a COMMA is NEVER merged, even if it lacks a
191
+ // beat-anchor. A comma is an OVERT prosodic boundary the poet placed; merging
192
+ // across it destroys the boundary and flattens the post-comma material into the
193
+ // pre-comma phrase ("among them" after "And of the best," → xww instead of xnw).
194
+ // A function-word-only post-comma phrase keeps its separate ϕ so the
195
+ // relativiser can apply the phrase-initial beat and the givenness escape.
196
+ const merged = [];
197
+ for (const p of phrases) {
198
+ const pp = { cliticGroups: p.cgs };
199
+ if (!canAnchorBeat(pp) && merged.length > 0 && !p.commaPreceded) {
200
+ merged[merged.length - 1].cliticGroups.push(...p.cgs);
201
+ }
202
+ else {
203
+ merged.push(pp);
204
+ }
205
+ }
206
+ // A leading un-anchored phrase folds forward into the next — but NOT if it was
207
+ // comma-preceded (a comma before the first phrase would be line-initial, which
208
+ // doesn't happen; this guard is for safety).
209
+ if (merged.length >= 2 && !canAnchorBeat(merged[0]) && !phrases[0].commaPreceded) {
210
+ merged[1].cliticGroups.unshift(...merged[0].cliticGroups);
211
+ merged.shift();
212
+ }
213
+ return merged;
214
+ }
215
+ // ─── top level ─────────────────────────────────────────────────────
216
+ export function buildProsodicHierarchy(sent) {
217
+ // ϕ-domains over the dependency tree (the same constituent structure the
218
+ // cyclic stress rules use) — the grouping signal for phonological phrases.
219
+ const dom = computePhiDomains(sent);
220
+ const ius = [];
221
+ let segWords = [];
222
+ let commaAfterPos = new Set(); // positions in segWords with a comma after
223
+ const flush = () => {
224
+ if (segWords.length === 0) {
225
+ commaAfterPos = new Set();
226
+ return;
227
+ }
228
+ // A φ-break (comma) is a HARD boundary for clitic-group formation: a proclitic
229
+ // must not lean across a comma onto a head in the next phrase. (Letting it do
230
+ // so silently glued "for that ," onto the following "a specialized branch" — both
231
+ // "for" and "that" procliticise rightward onto "specialized" — so the comma fell
232
+ // INSIDE one clitic group and the φ-break was lost.) So we split the IU segment
233
+ // into RUNS at the comma positions, build clitic groups WITHIN each run, and mark
234
+ // the first CG of every run after the first as φ-preceded.
235
+ const runs = [];
236
+ let run = [];
237
+ segWords.forEach((w, i) => {
238
+ run.push(w);
239
+ if (commaAfterPos.has(i)) {
240
+ runs.push(run);
241
+ run = [];
242
+ }
243
+ });
244
+ if (run.length)
245
+ runs.push(run);
246
+ const cgs = [];
247
+ const commaBeforeCG = new Set();
248
+ runs.forEach((r, ri) => {
249
+ const rcgs = buildCliticGroups(r);
250
+ if (ri > 0 && rcgs.length > 0)
251
+ commaBeforeCG.add(rcgs[0]);
252
+ cgs.push(...rcgs);
253
+ });
254
+ ius.push({ phonologicalPhrases: groupIntoPhrases(cgs, commaBeforeCG, dom) });
255
+ segWords = [];
256
+ commaAfterPos = new Set();
257
+ };
258
+ for (const w of sent.words) {
259
+ const kind = breakKind(w);
260
+ if (kind === 'iu') {
261
+ flush();
262
+ continue;
263
+ }
264
+ if (kind === 'phi') {
265
+ if (segWords.length)
266
+ commaAfterPos.add(segWords.length - 1);
267
+ continue;
268
+ }
269
+ if (isAnyPunct(w))
270
+ continue; // transparent punctuation
271
+ segWords.push(w);
272
+ }
273
+ flush();
274
+ return ius;
275
+ }
@@ -0,0 +1,4 @@
1
+ import { ClsWord, IntonationalUnit } from '../types.js';
2
+ /** Assign relative stress for the whole sentence, one PP at a time. */
3
+ export declare function computeRelativeStress(words: ClsWord[], ius: IntonationalUnit[]): void;
4
+ //# sourceMappingURL=relstress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"relstress.d.ts","sourceRoot":"","sources":["../../src/calliope/relstress.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAyB,MAAM,aAAa,CAAC;AAmkB/E,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAkCrF"}