calliope-ts 0.0.2 → 0.0.4
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/README.md +32 -20
- package/dist/caesura.d.ts +33 -0
- package/dist/caesura.d.ts.map +1 -0
- package/dist/caesura.js +202 -0
- package/dist/calliope/boundaries.d.ts +19 -0
- package/dist/calliope/boundaries.d.ts.map +1 -0
- package/dist/calliope/boundaries.js +182 -0
- package/dist/calliope/bracketing.d.ts +11 -0
- package/dist/calliope/bracketing.d.ts.map +1 -0
- package/dist/calliope/bracketing.js +416 -0
- package/dist/calliope/deps.d.ts +4 -0
- package/dist/calliope/deps.d.ts.map +1 -0
- package/dist/calliope/deps.js +181 -0
- package/dist/calliope/engine.d.ts +3 -0
- package/dist/calliope/engine.d.ts.map +1 -0
- package/dist/calliope/engine.js +71 -0
- package/dist/calliope/feats.d.ts +9 -0
- package/dist/calliope/feats.d.ts.map +1 -0
- package/dist/calliope/feats.js +45 -0
- package/dist/calliope/names.d.ts +7 -0
- package/dist/calliope/names.d.ts.map +1 -0
- package/dist/calliope/names.js +42 -0
- package/dist/calliope/postag.d.ts +8 -0
- package/dist/calliope/postag.d.ts.map +1 -0
- package/dist/calliope/postag.js +250 -0
- package/dist/calliope/prosodic.d.ts +3 -0
- package/dist/calliope/prosodic.d.ts.map +1 -0
- package/dist/calliope/prosodic.js +275 -0
- package/dist/calliope/relstress.d.ts +4 -0
- package/dist/calliope/relstress.d.ts.map +1 -0
- package/dist/calliope/relstress.js +688 -0
- package/dist/calliope/stressrules.d.ts +28 -0
- package/dist/calliope/stressrules.d.ts.map +1 -0
- package/dist/calliope/stressrules.js +147 -0
- package/dist/calliope/syntax.d.ts +38 -0
- package/dist/calliope/syntax.d.ts.map +1 -0
- package/dist/calliope/syntax.js +234 -0
- package/dist/calliope/udpos.d.ts +13 -0
- package/dist/calliope/udpos.d.ts.map +1 -0
- package/dist/calliope/udpos.js +156 -0
- package/dist/clio/caesura.d.ts +27 -0
- package/dist/clio/caesura.d.ts.map +1 -0
- package/dist/clio/caesura.js +148 -0
- package/dist/clio/depfix.d.ts +13 -0
- package/dist/clio/depfix.d.ts.map +1 -0
- package/dist/clio/depfix.js +84 -0
- package/dist/clio/display.d.ts +32 -0
- package/dist/clio/display.d.ts.map +1 -0
- package/dist/clio/display.js +976 -0
- package/dist/clio/engine.d.ts +3 -0
- package/dist/clio/engine.d.ts.map +1 -0
- package/dist/clio/engine.js +28 -0
- package/dist/clio/parser.d.ts +10 -0
- package/dist/clio/parser.d.ts.map +1 -0
- package/dist/clio/parser.js +696 -0
- package/dist/clio/phonological.d.ts +41 -0
- package/dist/clio/phonological.d.ts.map +1 -0
- package/dist/clio/phonological.js +788 -0
- package/dist/clio/phrasestress.d.ts +6 -0
- package/dist/clio/phrasestress.d.ts.map +1 -0
- package/dist/clio/phrasestress.js +106 -0
- package/dist/clio/pipeline.d.ts +11 -0
- package/dist/clio/pipeline.d.ts.map +1 -0
- package/dist/clio/pipeline.js +147 -0
- package/dist/clio/rhyme.d.ts +65 -0
- package/dist/clio/rhyme.d.ts.map +1 -0
- package/dist/clio/rhyme.js +761 -0
- package/dist/clio/scandroid.d.ts +17 -0
- package/dist/clio/scandroid.d.ts.map +1 -0
- package/dist/clio/scandroid.js +435 -0
- package/dist/clio/scansion.d.ts +46 -0
- package/dist/clio/scansion.d.ts.map +1 -0
- package/dist/clio/scansion.js +1086 -0
- package/dist/clio/semantics.d.ts +44 -0
- package/dist/clio/semantics.d.ts.map +1 -0
- package/dist/clio/semantics.js +139 -0
- package/dist/clio/stress.d.ts +83 -0
- package/dist/clio/stress.d.ts.map +1 -0
- package/dist/clio/stress.js +1737 -0
- package/dist/clio/tagfix.d.ts +6 -0
- package/dist/clio/tagfix.d.ts.map +1 -0
- package/dist/clio/tagfix.js +101 -0
- package/dist/display.d.ts +0 -6
- package/dist/display.d.ts.map +1 -1
- package/dist/display.js +322 -141
- package/dist/engine.d.ts +9 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +12 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +169 -26
- package/dist/parser.d.ts +3 -3
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +320 -563
- package/dist/phonological.d.ts +1 -1
- package/dist/phonological.d.ts.map +1 -1
- package/dist/phonological.js +73 -4
- package/dist/phrasestress.d.ts +6 -0
- package/dist/phrasestress.d.ts.map +1 -0
- package/dist/phrasestress.js +106 -0
- package/dist/rhyme.d.ts +40 -1
- package/dist/rhyme.d.ts.map +1 -1
- package/dist/rhyme.js +435 -7
- package/dist/scansion.d.ts +9 -0
- package/dist/scansion.d.ts.map +1 -1
- package/dist/scansion.js +145 -18
- package/dist/semantics.d.ts +44 -0
- package/dist/semantics.d.ts.map +1 -0
- package/dist/semantics.js +139 -0
- package/dist/stress.d.ts +127 -4
- package/dist/stress.d.ts.map +1 -1
- package/dist/stress.js +834 -61
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -3
- package/src/caesura.ts +201 -0
- package/src/calliope/boundaries.ts +190 -0
- package/src/calliope/bracketing.ts +390 -0
- package/src/calliope/deps.ts +160 -0
- package/src/calliope/engine.ts +77 -0
- package/src/calliope/feats.ts +46 -0
- package/src/calliope/names.ts +44 -0
- package/src/calliope/postag.ts +253 -0
- package/src/calliope/prosodic.ts +262 -0
- package/src/calliope/relstress.ts +645 -0
- package/src/calliope/stressrules.ts +147 -0
- package/src/calliope/syntax.ts +218 -0
- package/src/calliope/udpos.ts +152 -0
- package/src/calliope_src_contents.md +19049 -0
- package/src/clio/caesura.ts +145 -0
- package/src/clio/depfix.ts +88 -0
- package/src/clio/display.ts +1042 -0
- package/src/clio/engine.ts +38 -0
- package/src/clio/parser.ts +845 -0
- package/src/clio/phonological.ts +849 -0
- package/src/clio/phrasestress.ts +108 -0
- package/src/clio/pipeline.ts +154 -0
- package/src/clio/rhyme.ts +740 -0
- package/src/clio/scandroid.ts +434 -0
- package/src/clio/scansion.ts +1130 -0
- package/src/clio/semantics.ts +134 -0
- package/src/clio/stress.ts +1731 -0
- package/src/clio/tagfix.ts +104 -0
- package/src/display.ts +321 -137
- package/src/engine.ts +22 -0
- package/src/index.ts +176 -32
- package/src/parser.ts +346 -682
- package/src/phonological.ts +72 -4
- package/src/phrasestress.ts +108 -0
- package/src/rhyme.ts +428 -8
- package/src/scansion.ts +136 -15
- package/src/semantics.ts +134 -0
- package/src/stress.ts +794 -64
- package/src/types.ts +39 -4
- package/tests/DataForHayesLinesOnly.txt +364 -0
- package/tests/DataForHayesStressSymbolsRevised.txt +728 -0
- package/tests/basic.test.ts +443 -37
- package/tests/bench-hayes.mjs +72 -0
- package/tests/wagner-stress.test.ts +188 -0
- package/vitest.config.ts +0 -15
package/dist/phonological.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export declare function syllableVowelLengths(syllables: {
|
|
|
30
30
|
stress?: number;
|
|
31
31
|
lexicalStress?: number;
|
|
32
32
|
}[]): VowelLength[];
|
|
33
|
-
export declare function syllabifyWord(word: string, syllableCount: number, vowelLengths?: VowelLength[], morphSuffix?: string): string[];
|
|
33
|
+
export declare function syllabifyWord(word: string, syllableCount: number, vowelLengths?: VowelLength[], morphSuffix?: string, morphPrefix?: string): string[];
|
|
34
34
|
/**
|
|
35
35
|
* Flatten all syllables into display entries with word context.
|
|
36
36
|
* Each entry carries the original word text, the syllable text
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phonological.d.ts","sourceRoot":"","sources":["../src/phonological.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,EACX,OAAO,EAGP,kBAAkB,EAClB,gBAAgB,EAChB,SAAS,EAET,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAIpB;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,WAAW,GACpB,gBAAgB,EAAE,CAoBpB;AA0UD,wBAAgB,eAAe,CAAC,EAAE,EAAE,gBAAgB,GAAG,OAAO,EAAE,CAM/D;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,kBAAkB,GAAG,OAAO,EAAE,CAMjE;AA+ED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,gBAAgB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,CAGjF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,gBAAgB,EAAE,EACvB,KAAK,EAAE,OAAO,EAAE,EAChB,WAAW,EAAE,SAAS,EAAE,GACvB,MAAM,CASR;AAsBD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvD,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAQzD;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,CAClC,SAAS,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GACvE,WAAW,EAAE,CASf;AAsDD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,WAAW,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"phonological.d.ts","sourceRoot":"","sources":["../src/phonological.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,EACX,OAAO,EAGP,kBAAkB,EAClB,gBAAgB,EAChB,SAAS,EAET,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAIpB;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,WAAW,GACpB,gBAAgB,EAAE,CAoBpB;AA0UD,wBAAgB,eAAe,CAAC,EAAE,EAAE,gBAAgB,GAAG,OAAO,EAAE,CAM/D;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,kBAAkB,GAAG,OAAO,EAAE,CAMjE;AA+ED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,gBAAgB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,CAGjF;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,gBAAgB,EAAE,EACvB,KAAK,EAAE,OAAO,EAAE,EAChB,WAAW,EAAE,SAAS,EAAE,GACvB,MAAM,CASR;AAsBD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;AAEvD,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAQzD;AAED,mFAAmF;AACnF,wBAAgB,oBAAoB,CAClC,SAAS,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GACvE,WAAW,EAAE,CASf;AAsDD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,WAAW,EAAE,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CA8RrJ;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,oBAAoB,EAAE,CAwB9E"}
|
package/dist/phonological.js
CHANGED
|
@@ -527,9 +527,32 @@ function quickSyllableCount(s) {
|
|
|
527
527
|
groups--;
|
|
528
528
|
return Math.max(1, groups);
|
|
529
529
|
}
|
|
530
|
-
export function syllabifyWord(word, syllableCount, vowelLengths, morphSuffix) {
|
|
530
|
+
export function syllabifyWord(word, syllableCount, vowelLengths, morphSuffix, morphPrefix) {
|
|
531
531
|
if (syllableCount <= 1)
|
|
532
532
|
return [word];
|
|
533
|
+
// Morpheme-aware prefix peel: when OOV stress assignment validated a productive
|
|
534
|
+
// prefix ("dis"), split it off as the first syllable(s) so the stem keeps its
|
|
535
|
+
// spelling (dis·il·lu·sions, not di·sil·lu·sions — the Maximal Onset principle
|
|
536
|
+
// would otherwise pull the prefix's final consonant into the next syllable).
|
|
537
|
+
// The prefix's own syllable count is estimated orthographically; the stem gets
|
|
538
|
+
// the remaining syllables.
|
|
539
|
+
if (morphPrefix && syllableCount >= 2
|
|
540
|
+
&& word.toLowerCase().startsWith(morphPrefix)
|
|
541
|
+
&& word.length > morphPrefix.length + 1) {
|
|
542
|
+
const prefixSylls = quickSyllableCount(morphPrefix);
|
|
543
|
+
if (prefixSylls < syllableCount) {
|
|
544
|
+
const prefixChunk = word.slice(0, morphPrefix.length);
|
|
545
|
+
const stemChunk = word.slice(morphPrefix.length);
|
|
546
|
+
const stemChunks = syllabifyWord(stemChunk, syllableCount - prefixSylls, vowelLengths ? vowelLengths.slice(prefixSylls) : undefined, morphSuffix);
|
|
547
|
+
// If the prefix is polysyllabic, syllabify it on its own; monosyllabic → as-is.
|
|
548
|
+
const prefixChunks = prefixSylls > 1
|
|
549
|
+
? syllabifyWord(prefixChunk, prefixSylls)
|
|
550
|
+
: [prefixChunk];
|
|
551
|
+
const result = [...prefixChunks, ...stemChunks];
|
|
552
|
+
if (result.length === syllableCount)
|
|
553
|
+
return result;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
533
556
|
// Lexical compound boundary (someone → some·one, not so·meone). Only when the
|
|
534
557
|
// constituents' own syllable counts add up to the requested total.
|
|
535
558
|
{
|
|
@@ -581,8 +604,14 @@ export function syllabifyWord(word, syllableCount, vowelLengths, morphSuffix) {
|
|
|
581
604
|
}
|
|
582
605
|
const lower = cleanWord.toLowerCase();
|
|
583
606
|
const n = lower.length;
|
|
584
|
-
// Common English consonant digraphs
|
|
585
|
-
|
|
607
|
+
// Common English consonant digraphs. "kn"/"gn"/"wr" are EXCLUDED: they are
|
|
608
|
+
// digraphs only WORD-INITIALLY (where the first consonant is silent — "know",
|
|
609
|
+
// "gnaw", "write"), and the digraph set is used only for MEDIAL boundary
|
|
610
|
+
// placement. In medial position ("frankness") "kn" is two pronounced
|
|
611
|
+
// consonants: /k/ closes the first syllable (frank), /n/ opens the second
|
|
612
|
+
// (ness) — treating it as a medial digraph sends both to the next syllable
|
|
613
|
+
// ("fran-kness"), which is wrong.
|
|
614
|
+
const DIGRAPHS = new Set(['ch', 'sh', 'th', 'wh', 'ph', 'gh', 'ck', 'ng']);
|
|
586
615
|
// Digraphs that commonly end syllables (codas)
|
|
587
616
|
const CODA_DIGRAPHS = new Set(['ch', 'sh', 'ck', 'ng', 'th']);
|
|
588
617
|
// "Muta cum liquida": an obstruent + liquid/glide that, between vowels, stays
|
|
@@ -607,6 +636,17 @@ export function syllabifyWord(word, syllableCount, vowelLengths, morphSuffix) {
|
|
|
607
636
|
// (want·ed, embed·ded), so those are excluded.
|
|
608
637
|
const endsSilentEd = n >= 3 && lower.endsWith('ed')
|
|
609
638
|
&& !VOWEL_CHARS.has(lower[n - 3]) && lower[n - 3] !== 't' && lower[n - 3] !== 'd';
|
|
639
|
+
// Non-syllabic inflectional "-es": the 'e' in a final "…Xes" (X a consonant
|
|
640
|
+
// other than a vowel — so "goes"/"shoes" are excluded, where 'e' is part of a
|
|
641
|
+
// vowel digraph) is silent when the nucleus count exceeds the CMU syllable
|
|
642
|
+
// count. This is the VCe pattern extended to plurals/3sg: the 'e' makes the
|
|
643
|
+
// preceding vowel long and is not itself pronounced (receives = re·CEIVES,
|
|
644
|
+
// makes = MAKES, writes = WRITES). When the stem ends in a sibilant
|
|
645
|
+
// (bus→buses, bush→bushes) the 'e' IS syllabic, but in that case the nucleus
|
|
646
|
+
// count already matches the syllable count and this check never fires — the
|
|
647
|
+
// surplus-nucleus guard makes it safe.
|
|
648
|
+
const endsSilentEs = n >= 3 && lower.endsWith('es')
|
|
649
|
+
&& !VOWEL_CHARS.has(lower[n - 3]);
|
|
610
650
|
const nuclei = [];
|
|
611
651
|
let i = 0;
|
|
612
652
|
while (i < n) {
|
|
@@ -636,6 +676,35 @@ export function syllabifyWord(word, syllableCount, vowelLengths, morphSuffix) {
|
|
|
636
676
|
if (last.start === n - 2 && last.end === n - 1)
|
|
637
677
|
nuclei.pop();
|
|
638
678
|
}
|
|
679
|
+
// Same for non-syllabic "-es": drop the silent 'e' when there's a surplus
|
|
680
|
+
// (receives = 3 nuclei → 2 syllables → drop → re·ceives, not recei·ves).
|
|
681
|
+
if (nuclei.length > syllableCount && endsSilentEs) {
|
|
682
|
+
const last = nuclei[nuclei.length - 1];
|
|
683
|
+
if (last.start === n - 2 && last.end === n - 1)
|
|
684
|
+
nuclei.pop();
|
|
685
|
+
}
|
|
686
|
+
// Vowel hiatus: if we have TOO FEW nuclei, a multi-vowel nucleus (≥2 vowel
|
|
687
|
+
// characters scanned as one group) is a HIATUS — two syllabic vowels in
|
|
688
|
+
// adjacent syllables — not a diphthong. Split the rightmost multi-vowel
|
|
689
|
+
// nucleus, peeling off its last vowel char as a new nucleus, until nuclei
|
|
690
|
+
// match the syllable count. English hiatus tends word-ward ("-ia", "-eo"),
|
|
691
|
+
// so rightmost-first is the right priority. This respects the authoritative
|
|
692
|
+
// CMU syllable count ("hysterias" = 4: hys·te·ri·as, not 3) and prevents the
|
|
693
|
+
// crude fallback chunker from producing nonsensical splits like "hy·ST·eri·as"
|
|
694
|
+
// (treating "ST" as a syllable nucleus).
|
|
695
|
+
while (nuclei.length < syllableCount) {
|
|
696
|
+
let splitIdx = -1;
|
|
697
|
+
for (let j = nuclei.length - 1; j >= 0; j--) {
|
|
698
|
+
if (nuclei[j].end - nuclei[j].start >= 2) {
|
|
699
|
+
splitIdx = j;
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
if (splitIdx < 0)
|
|
704
|
+
break; // no multi-vowel nucleus left
|
|
705
|
+
const nuc = nuclei[splitIdx];
|
|
706
|
+
nuclei.splice(splitIdx, 1, { start: nuc.start, end: nuc.end - 1 }, { start: nuc.end - 1, end: nuc.end });
|
|
707
|
+
}
|
|
639
708
|
while (nuclei.length > syllableCount && nuclei.length > 1) {
|
|
640
709
|
let minConsonants = Infinity;
|
|
641
710
|
let mergeIdx = 0;
|
|
@@ -770,7 +839,7 @@ export function flattenDisplayEntries(words) {
|
|
|
770
839
|
if (isPunctuation(w.lexicalClass))
|
|
771
840
|
continue;
|
|
772
841
|
const sylCount = w.syllables.length;
|
|
773
|
-
const chunks = syllabifyWord(w.word, sylCount, syllableVowelLengths(w.syllables), w.morphSuffix);
|
|
842
|
+
const chunks = syllabifyWord(w.word, sylCount, syllableVowelLengths(w.syllables), w.morphSuffix, w.morphPrefix);
|
|
774
843
|
for (let si = 0; si < sylCount; si++) {
|
|
775
844
|
result.push({
|
|
776
845
|
wordText: w.word,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phrasestress.d.ts","sourceRoot":"","sources":["../src/phrasestress.ts"],"names":[],"mappings":"AAgCA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAyBrC;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,IAAI,CAkB1D"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// phrasestress.ts — McAleese's Phrase-Stress phase (previously SKIPPED).
|
|
2
|
+
//
|
|
3
|
+
// The pipeline used to map lexical stress straight to relative stress (plus a
|
|
4
|
+
// single "+1 on the rightmost content word"), leaving `ClsWord.phraseStress`
|
|
5
|
+
// a vestigial field stuck at 0. This module restores the genuine phase that
|
|
6
|
+
// sits BETWEEN lexical and relative stress in McAleese's procedure (thesis
|
|
7
|
+
// Appendix A; Chomsky & Halle's Nuclear + Compound Stress Rules):
|
|
8
|
+
//
|
|
9
|
+
// 1. every word starts at 1;
|
|
10
|
+
// 2. the Compound Stress Rule pins a compound's subordinate element at the
|
|
11
|
+
// floor while its principal stays in the ramp;
|
|
12
|
+
// 3. the recursive Nuclear Stress Rule ramps the principal stress of each
|
|
13
|
+
// successive *stressed* word left-to-right, so prominence rises to the
|
|
14
|
+
// phrase's nuclear (rightmost) peak.
|
|
15
|
+
//
|
|
16
|
+
// For the right-branching structure of English declaratives the recursive NSR
|
|
17
|
+
// reduces to a monotone ramp over the stressed words — function words and
|
|
18
|
+
// compound-subordinates pinned at 1. NOTE the convention: here 1 is the FLOOR
|
|
19
|
+
// and the ramp climbs RIGHTWARD to the nuclear peak (the "If hairs…" example
|
|
20
|
+
// below). McAleese's "Mary ate sweet ice cream" worked example uses the
|
|
21
|
+
// OPPOSITE (SPE) numbering — 1 = PRIMARY/strongest, weakened outward — so his
|
|
22
|
+
// ICE cream (primary on "ice") is reproduced here as ice=PEAK, cream=floor
|
|
23
|
+
// (the compound's HEAD "cream" is the subordinate, pinned at 1):
|
|
24
|
+
//
|
|
25
|
+
// "Mary ate sweet ice cream" -> Mary2 ate3 sweet4 ice5 cream1 (ICE cream: head subordinate)
|
|
26
|
+
// "If hairs be wires, black wires grow on her head" -> 1 2 3 4 5 6 7 1 1 8
|
|
27
|
+
//
|
|
28
|
+
// (Marked / left-branching structures are approximated by the same ramp; a
|
|
29
|
+
// constituent-tree-driven refinement over `sentence.nodes` is a flagged
|
|
30
|
+
// follow-up. This module reads only POS / content flags and surface order,
|
|
31
|
+
// and writes only the previously-unused phraseStress field — purely additive.)
|
|
32
|
+
import { isPunctuation } from './parser.js';
|
|
33
|
+
import { compoundStressSide } from './stress.js';
|
|
34
|
+
/** Sentence-final punctuation resets the nuclear ramp (the NSR is
|
|
35
|
+
* sentence-bounded). A comma / colon / dash does NOT — the ramp runs across
|
|
36
|
+
* an internal intonational break (cf. "If hairs…, black wires…" → 1234…5,678). */
|
|
37
|
+
const SENTENCE_FINAL = new Set(['.', '!', '?', '…']);
|
|
38
|
+
/** Common-/proper-noun POS tags, gating which compounds floor an element. */
|
|
39
|
+
const NOUN_TAGS = new Set(['NN', 'NNS', 'NNP', 'NNPS']);
|
|
40
|
+
/**
|
|
41
|
+
* Proclitic POS tags pinned at the phrase-stress floor: the NSR ramp applies to
|
|
42
|
+
* *stressed words* (nouns, adjectives, adverbs, and VERBS — including the
|
|
43
|
+
* copula/auxiliary "be" and modals, which bear lexical stress even though they
|
|
44
|
+
* reduce in the contour), while pure clitics carry none. Verbs are NOT here,
|
|
45
|
+
* so McAleese's "if hairs BE wires…" keeps `be` in the ramp (=3). A word the
|
|
46
|
+
* earlier pipeline promoted to content (a phrasal-verb particle, a focus
|
|
47
|
+
* demonstrative — `isContent === true`) is un-pinned and ramps.
|
|
48
|
+
*/
|
|
49
|
+
const PINNED_POS = new Set([
|
|
50
|
+
'IN', 'TO', 'DT', 'CC', 'PRP', 'PRP$', 'WP', 'WP$', 'WDT', 'EX', 'POS',
|
|
51
|
+
]);
|
|
52
|
+
/**
|
|
53
|
+
* Populate `word.phraseStress` for every word of a parsed sentence.
|
|
54
|
+
*/
|
|
55
|
+
export function computePhraseStress(words) {
|
|
56
|
+
const subordinate = compoundSubordinates(words);
|
|
57
|
+
let ramp = 1; // running nuclear ramp; the floor for pinned words is 1
|
|
58
|
+
for (const w of words) {
|
|
59
|
+
if (isPunctuation(w.lexicalClass)) {
|
|
60
|
+
w.phraseStress = 0;
|
|
61
|
+
if (SENTENCE_FINAL.has(w.word))
|
|
62
|
+
ramp = 1;
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
// Pinned at the floor: a proclitic function word bears no nuclear ramp, and
|
|
66
|
+
// a compound's subordinate element is demoted by the Compound Stress Rule.
|
|
67
|
+
const isClitic = PINNED_POS.has(w.lexicalClass) && !w.isContent;
|
|
68
|
+
if (isClitic || subordinate.has(w)) {
|
|
69
|
+
w.phraseStress = 1;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
w.phraseStress = ++ramp;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Identify the subordinate (pinned) element of each surface-adjacent compound,
|
|
77
|
+
* delegating the direction to `compoundStressSide` (the shared rule, so phrase
|
|
78
|
+
* stress and the lexical compound pass cannot disagree).
|
|
79
|
+
* - fore-stress ('left') → the HEAD (right) element is subordinate: SEA·shore,
|
|
80
|
+
* ICE cream, KITCHen table — the Compound Stress Rule default for N+N;
|
|
81
|
+
* - right-stress ('right') → the MODIFIER (left) is subordinate: apple PIE,
|
|
82
|
+
* and proper-name pairs (New YORK);
|
|
83
|
+
* - Adjective+noun ("sweet cream") is phrasal, not a compound (`side` is
|
|
84
|
+
* 'right' there too, so the modifier "sweet" is pinned and "cream" ramps —
|
|
85
|
+
* i.e. end-stress, as desired).
|
|
86
|
+
*/
|
|
87
|
+
function compoundSubordinates(words) {
|
|
88
|
+
const subs = new Set();
|
|
89
|
+
for (let i = 0; i + 1 < words.length; i++) {
|
|
90
|
+
const a = words[i], b = words[i + 1];
|
|
91
|
+
if (isPunctuation(a.lexicalClass) || isPunctuation(b.lexicalClass))
|
|
92
|
+
continue;
|
|
93
|
+
if (!a.isContent || !b.isContent)
|
|
94
|
+
continue;
|
|
95
|
+
const side = compoundStressSide(a.word, a.lexicalClass, b.word, b.lexicalClass);
|
|
96
|
+
// Floor an element only for a genuine N+N compound; an Adj+N ("sweet ice",
|
|
97
|
+
// "red car") is phrasal — its end-stress emerges from the ramp itself, so we
|
|
98
|
+
// pin nothing and let the adjective ramp (McAleese: sweet=4, above cream).
|
|
99
|
+
const bothNouns = NOUN_TAGS.has(a.lexicalClass) && NOUN_TAGS.has(b.lexicalClass);
|
|
100
|
+
if (side === 'left')
|
|
101
|
+
subs.add(b); // fore-stress compound: head subordinate (ICE cream)
|
|
102
|
+
else if (side === 'right' && bothNouns)
|
|
103
|
+
subs.add(a); // right-stress N+N: modifier subordinate (apple PIE)
|
|
104
|
+
}
|
|
105
|
+
return subs;
|
|
106
|
+
}
|
package/dist/rhyme.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LineResult } from './types.js';
|
|
1
|
+
import { LineResult, StressLevel } from './types.js';
|
|
2
2
|
export type RhymeTypeName = 'identical' | 'rich' | 'perfect' | 'family' | 'assonant' | 'consonant' | 'augmented' | 'diminished' | 'wrenched' | 'eye';
|
|
3
3
|
export interface RhymePair {
|
|
4
4
|
type: RhymeTypeName;
|
|
@@ -23,4 +23,43 @@ export declare function detectScheme(endWords: string[]): LineRhyme[];
|
|
|
23
23
|
* meter/scansion/certainty is touched.
|
|
24
24
|
*/
|
|
25
25
|
export declare function applyRhymeAndForm(stanzas: LineResult[][]): void;
|
|
26
|
+
export interface RhymeRel {
|
|
27
|
+
fromWord: string;
|
|
28
|
+
fromLabel: string;
|
|
29
|
+
toWord: string;
|
|
30
|
+
toLabel: string;
|
|
31
|
+
letter: string;
|
|
32
|
+
type?: RhymeTypeName;
|
|
33
|
+
kind: 'end' | 'caesural' | 'head';
|
|
34
|
+
topStress: StressLevel;
|
|
35
|
+
}
|
|
36
|
+
export interface Phonopoetics {
|
|
37
|
+
endScheme: string;
|
|
38
|
+
end: RhymeRel[];
|
|
39
|
+
caesural: RhymeRel[];
|
|
40
|
+
head: RhymeRel[];
|
|
41
|
+
alliteration: {
|
|
42
|
+
label: string;
|
|
43
|
+
words: string[];
|
|
44
|
+
}[];
|
|
45
|
+
acrostics: {
|
|
46
|
+
labels: string[];
|
|
47
|
+
firsts: string[];
|
|
48
|
+
word: string;
|
|
49
|
+
}[];
|
|
50
|
+
}
|
|
51
|
+
/** Whole-poem phonopoetic analysis: structured data for the Phonopoetics
|
|
52
|
+
* synopsis section. Read-only — never mutates the per-line scansion. */
|
|
53
|
+
export declare function analyzePhonopoetics(stanzas: LineResult[][]): Phonopoetics;
|
|
54
|
+
/** A labelled line of the poem synopsis (display renders each). */
|
|
55
|
+
export interface SynopsisRow {
|
|
56
|
+
label: string;
|
|
57
|
+
value: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Build the cumulative poem synopsis: top meter(s)/mixed meters (accentual forms
|
|
61
|
+
* reported in beats), the poem-wide rhyme scheme (with internal rhymes), and the
|
|
62
|
+
* poetic-form alignment. Reads only existing per-line determinations.
|
|
63
|
+
*/
|
|
64
|
+
export declare function summarizePoem(stanzas: LineResult[][]): SynopsisRow[];
|
|
26
65
|
//# sourceMappingURL=rhyme.d.ts.map
|
package/dist/rhyme.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rhyme.d.ts","sourceRoot":"","sources":["../src/rhyme.ts"],"names":[],"mappings":"AAgBA,OAAO,
|
|
1
|
+
{"version":3,"file":"rhyme.d.ts","sourceRoot":"","sources":["../src/rhyme.ts"],"names":[],"mappings":"AAgBA,OAAO,EAAW,UAAU,EAA8B,WAAW,EAAE,MAAM,YAAY,CAAC;AAK1F,MAAM,MAAM,aAAa,GACrB,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAC3C,UAAU,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,GACrD,UAAU,GAAG,KAAK,CAAC;AAEvB,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,aAAa,CAAC;IACpB,uFAAuF;IACvF,SAAS,CAAC,EAAE,WAAW,GAAG,UAAU,GAAG,UAAU,CAAC;CACnD;AA6DD,wEAAwE;AACxE,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAkDxE;AAQD,MAAM,WAAW,SAAS;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;yEACyE;AACzE,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CA6C5D;AA6MD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,GAAG,IAAI,CAkB/D;AAsCD,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IACpC,MAAM,EAAE,MAAM,CAAC;IAAG,OAAO,EAAE,MAAM,CAAC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,aAAa,CAAC;IACrB,IAAI,EAAE,KAAK,GAAG,UAAU,GAAG,MAAM,CAAC;IAClC,SAAS,EAAE,WAAW,CAAC;CACxB;AACD,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,QAAQ,EAAE,CAAC;IAChB,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrB,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,YAAY,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IACnD,SAAS,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACnE;AAED;0EAC0E;AAC1E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,GAAG,YAAY,CAsIzE;AAgCD,mEAAmE;AACnE,MAAM,WAAW,WAAW;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;CAAE;AA2B9D;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,UAAU,EAAE,EAAE,GAAG,WAAW,EAAE,CA0DpE"}
|