calliope-ts 0.0.4 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -3
- package/dist/calliope/bracketing.d.ts.map +1 -1
- package/dist/calliope/bracketing.js +59 -5
- package/dist/calliope/deps.d.ts +5 -1
- package/dist/calliope/deps.d.ts.map +1 -1
- package/dist/calliope/deps.js +53 -3
- package/dist/calliope/engine.d.ts.map +1 -1
- package/dist/calliope/engine.js +6 -1
- package/dist/calliope/relstress.d.ts.map +1 -1
- package/dist/calliope/relstress.js +411 -15
- package/dist/calliope/syntax.d.ts +33 -0
- package/dist/calliope/syntax.d.ts.map +1 -1
- package/dist/calliope/syntax.js +199 -0
- package/dist/data/en_common_words.json +1 -0
- package/dist/display.d.ts +15 -1
- package/dist/display.d.ts.map +1 -1
- package/dist/display.js +112 -10
- package/dist/fabbhalle.d.ts +54 -0
- package/dist/fabbhalle.d.ts.map +1 -0
- package/dist/fabbhalle.js +404 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +86 -33
- package/dist/phonological.d.ts.map +1 -1
- package/dist/phonological.js +7 -1
- package/dist/rewriteEn.d.ts +14 -0
- package/dist/rewriteEn.d.ts.map +1 -0
- package/dist/rewriteEn.js +392 -0
- package/dist/rhyme.d.ts +35 -3
- package/dist/rhyme.d.ts.map +1 -1
- package/dist/rhyme.js +176 -11
- package/dist/russian/accentuator.d.ts +31 -0
- package/dist/russian/accentuator.d.ts.map +1 -0
- package/dist/russian/accentuator.js +546 -0
- package/dist/russian/collocations.d.ts +7 -0
- package/dist/russian/collocations.d.ts.map +1 -0
- package/dist/russian/collocations.js +38 -0
- package/dist/russian/compounds.d.ts +6 -0
- package/dist/russian/compounds.d.ts.map +1 -0
- package/dist/russian/compounds.js +81 -0
- package/dist/russian/download.d.ts +23 -0
- package/dist/russian/download.d.ts.map +1 -0
- package/dist/russian/download.js +117 -0
- package/dist/russian/engine.d.ts +6 -0
- package/dist/russian/engine.d.ts.map +1 -0
- package/dist/russian/engine.js +1424 -0
- package/dist/russian/parser.d.ts +8 -0
- package/dist/russian/parser.d.ts.map +1 -0
- package/dist/russian/parser.js +92 -0
- package/dist/russian/paths.d.ts +55 -0
- package/dist/russian/paths.d.ts.map +1 -0
- package/dist/russian/paths.js +114 -0
- package/dist/russian/rewrite.d.ts +6 -0
- package/dist/russian/rewrite.d.ts.map +1 -0
- package/dist/russian/rewrite.js +400 -0
- package/dist/russian/rhyme.d.ts +18 -0
- package/dist/russian/rhyme.d.ts.map +1 -0
- package/dist/russian/rhyme.js +230 -0
- package/dist/russian/syllabifier.d.ts +14 -0
- package/dist/russian/syllabifier.d.ts.map +1 -0
- package/dist/russian/syllabifier.js +475 -0
- package/dist/russian/types.d.ts +94 -0
- package/dist/russian/types.d.ts.map +1 -0
- package/dist/russian/types.js +27 -0
- package/dist/scandroidNative/constants.d.ts +16 -0
- package/dist/scandroidNative/constants.d.ts.map +1 -0
- package/dist/scandroidNative/constants.js +60 -0
- package/dist/scandroidNative/deduceParameters.d.ts +16 -0
- package/dist/scandroidNative/deduceParameters.d.ts.map +1 -0
- package/dist/scandroidNative/deduceParameters.js +69 -0
- package/dist/scandroidNative/dictionary-data.d.ts +2 -0
- package/dist/scandroidNative/dictionary-data.d.ts.map +1 -0
- package/dist/scandroidNative/dictionary-data.js +744 -0
- package/dist/scandroidNative/dictionary.d.ts +12 -0
- package/dist/scandroidNative/dictionary.d.ts.map +1 -0
- package/dist/scandroidNative/dictionary.js +50 -0
- package/dist/scandroidNative/engine.d.ts +41 -0
- package/dist/scandroidNative/engine.d.ts.map +1 -0
- package/dist/scandroidNative/engine.js +119 -0
- package/dist/scandroidNative/machine.d.ts +93 -0
- package/dist/scandroidNative/machine.d.ts.map +1 -0
- package/dist/scandroidNative/machine.js +795 -0
- package/dist/scandroidNative/positioner.d.ts +32 -0
- package/dist/scandroidNative/positioner.d.ts.map +1 -0
- package/dist/scandroidNative/positioner.js +208 -0
- package/dist/scandroidNative/syllabizer.d.ts +3 -0
- package/dist/scandroidNative/syllabizer.d.ts.map +1 -0
- package/dist/scandroidNative/syllabizer.js +215 -0
- package/dist/scandroidNative/utilities.d.ts +37 -0
- package/dist/scandroidNative/utilities.d.ts.map +1 -0
- package/dist/scandroidNative/utilities.js +86 -0
- package/dist/scansion.d.ts.map +1 -1
- package/dist/scansion.js +118 -14
- package/dist/stress.d.ts +1 -0
- package/dist/stress.d.ts.map +1 -1
- package/dist/stress.js +502 -42
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +27 -6
- package/tools/fetch-russian-data.mjs +30 -0
- package/webapp/public/app.js +2418 -0
- package/webapp/public/assets/GithubLogo.png +0 -0
- package/webapp/public/assets/NPM2.png +0 -0
- package/webapp/public/assets/fonts/MurenaBold-webfont.woff2 +0 -0
- package/webapp/public/assets/fonts/MurenaItalic-webfont.woff2 +0 -0
- package/webapp/public/assets/fonts/MurenaRegular-webfont.woff2 +0 -0
- package/webapp/public/assets/fonts/Myra_4F_Caps_Bold-webfont.woff2 +0 -0
- package/webapp/public/assets/fonts/SinkinSans-700Bold.otf +0 -0
- package/webapp/public/assets/fonts/butterflies.ttf +0 -0
- package/webapp/public/assets/fonts/midcase_blackline-webfont.woff2 +0 -0
- package/webapp/public/assets/fonts/neueral-bold-webfont.woff2 +0 -0
- package/webapp/public/assets/fonts/neueral-regular-webfont.woff2 +0 -0
- package/webapp/public/assets/fonts/popelka-webfont.woff2 +0 -0
- package/webapp/public/assets/fonts/vremenagrotesk-webfont.woff +0 -0
- package/webapp/public/assets/fonts/wicky_javick_bold-webfont.woff2 +0 -0
- package/webapp/public/assets/logos/Logo_Var_10_Celtic_Knot_SVG.svg +116 -0
- package/webapp/public/assets/logos/Logo_Var_11_Graph_SVG.svg +313 -0
- package/webapp/public/assets/logos/Logo_Var_12_Limacon_Knot_SVG.svg +3942 -0
- package/webapp/public/assets/logos/Logo_Var_13_Crescents_SVG.svg +1030 -0
- package/webapp/public/assets/logos/Logo_Var_1_Spiral_SVG.svg +1491 -0
- package/webapp/public/assets/logos/Logo_Var_2_Tunnel_SVG.svg +12 -0
- package/webapp/public/assets/logos/Logo_Var_3_Triskelion_SVG.svg +10 -0
- package/webapp/public/assets/logos/Logo_Var_4_Quinquetra_PNG.png +0 -0
- package/webapp/public/assets/logos/Logo_Var_5_Penrose_Pentagram_SVG.svg +37 -0
- package/webapp/public/assets/logos/Logo_Var_6_Triskelion2_SVG.svg +10 -0
- package/webapp/public/assets/logos/Logo_Var_7_SixfoldSpirals_SVG.svg +20 -0
- package/webapp/public/assets/logos/Logo_Var_8_Radial_Labyrinth_SVG.svg +163 -0
- package/webapp/public/assets/logos/Logo_Var_9_Radial_Labyrinth_2_SVG.svg +106 -0
- package/webapp/public/index.html +316 -0
- package/webapp/public/styles.css +1114 -0
- package/webapp/server.mjs +680 -0
- package/src/caesura.ts +0 -201
- package/src/calliope/boundaries.ts +0 -190
- package/src/calliope/bracketing.ts +0 -390
- package/src/calliope/deps.ts +0 -160
- package/src/calliope/engine.ts +0 -77
- package/src/calliope/feats.ts +0 -46
- package/src/calliope/names.ts +0 -44
- package/src/calliope/postag.ts +0 -253
- package/src/calliope/prosodic.ts +0 -262
- package/src/calliope/relstress.ts +0 -645
- package/src/calliope/stressrules.ts +0 -147
- package/src/calliope/syntax.ts +0 -218
- package/src/calliope/udpos.ts +0 -152
- package/src/calliope_src_contents.md +0 -19049
- package/src/clio/caesura.ts +0 -145
- package/src/clio/depfix.ts +0 -88
- package/src/clio/display.ts +0 -1042
- package/src/clio/engine.ts +0 -38
- package/src/clio/parser.ts +0 -845
- package/src/clio/phonological.ts +0 -849
- package/src/clio/phrasestress.ts +0 -108
- package/src/clio/pipeline.ts +0 -154
- package/src/clio/rhyme.ts +0 -740
- package/src/clio/scandroid.ts +0 -434
- package/src/clio/scansion.ts +0 -1130
- package/src/clio/semantics.ts +0 -134
- package/src/clio/stress.ts +0 -1731
- package/src/clio/tagfix.ts +0 -104
- package/src/depfix.ts +0 -88
- package/src/display.ts +0 -1138
- package/src/engine.ts +0 -22
- package/src/index.ts +0 -690
- package/src/parser.ts +0 -501
- package/src/phonological.ts +0 -917
- package/src/phrasestress.ts +0 -108
- package/src/rhyme.ts +0 -748
- package/src/scandroid.ts +0 -434
- package/src/scansion.ts +0 -1174
- package/src/semantics.ts +0 -134
- package/src/stress.ts +0 -2111
- package/src/tagfix.ts +0 -104
- package/src/types.ts +0 -265
- package/tests/DataForHayesLinesOnly.txt +0 -364
- package/tests/DataForHayesStressSymbolsRevised.txt +0 -728
- package/tests/basic.test.ts +0 -1102
- package/tests/bench-hayes.mjs +0 -72
- package/tests/epg64.meter.train.txt +0 -8139
- package/tests/litlab-sample-2016.txt +0 -1738
- package/tests/prosodic.meter.train.txt +0 -13192
- package/tests/wagner-stress.test.ts +0 -188
- package/tsconfig.json +0 -29
- package/vitest.config.d.ts +0 -2
- package/vitest.config.js +0 -14
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
// utterance nuclear), the symbolic contour is local (one beat per φ) and then
|
|
29
29
|
// modified (clash resolution), so they legitimately diverge — never forced equal.
|
|
30
30
|
import { collectPPTokens } from '../phonological.js';
|
|
31
|
-
import { isTransitiveFunctionWord, isPronoun, isInherentlyGiven } from './syntax.js';
|
|
31
|
+
import { isTransitiveFunctionWord, isPronoun, isInherentlyGiven, focusAssociateOf, isMarginalModalUse, isCapitalizedFocalPronoun, isPostposedPreposition, isInvertedOperatorAux, isSemiModalHaveTo, } from './syntax.js';
|
|
32
32
|
const RANK = { x: 0, w: 1, n: 2, m: 3, s: 4 };
|
|
33
33
|
const LEVELS = ['x', 'w', 'n', 'm', 's'];
|
|
34
34
|
const CONTENT = /^(NN|NNS|NNP|NNPS|JJ|JJR|JJS|VB|VBD|VBG|VBN|VBP|VBZ|RB|RBR|RBS|CD|UH)$/;
|
|
@@ -37,7 +37,13 @@ const QUANTIFIERS = new Set([
|
|
|
37
37
|
'all', 'both', 'each', 'every', 'some', 'any', 'many', 'much', 'few', 'no',
|
|
38
38
|
'most', 'half', 'several', 'either', 'neither', 'enough',
|
|
39
39
|
]);
|
|
40
|
-
|
|
40
|
+
// "such" joins the demonstratives for its ATTRIBUTIVE use ("no such roses",
|
|
41
|
+
// "such a night") — a determiner-class weak monosyllable (Quirk's
|
|
42
|
+
// predeterminer; Fabb 2001 on weak monosyllables in iambic verse), whatever
|
|
43
|
+
// the tagger labels it. Its anchor exclusion is gated separately on the
|
|
44
|
+
// attributive relation (see isAttributiveSuch in relativisePP), so the
|
|
45
|
+
// predicative/pronominal use ("such is life") is untouched.
|
|
46
|
+
const DEMONSTRATIVES = new Set(['this', 'that', 'these', 'those', 'such']);
|
|
41
47
|
// "no" is a NEGATOR, not a reducible determiner: "made with NO loss of time" keeps
|
|
42
48
|
// a beat on "no". Listed here (ahead of the rel==='DET' → x floor in functionLevel)
|
|
43
49
|
// so the negative determiner is never flattened to the clitic tier.
|
|
@@ -50,6 +56,13 @@ const BE_FORMS = new Set(['be', 'am', 'is', 'are', 'was', 'were', 'been', 'being
|
|
|
50
56
|
// to a single proclitic syllable — never a stressed content peak (en-pos mis-tags
|
|
51
57
|
// the capitalised "'Tis" as NNP). Apostrophes already stripped by bare().
|
|
52
58
|
const APHAERESIS = new Set(['tis', 'twas', 'twere', 'twill', 'twould', 'tisnt']);
|
|
59
|
+
// Archaic interrogative wh-adverbs — a closed grammatical class the tagger
|
|
60
|
+
// routinely mislabels (Coleridge's "wherefore" comes back IN): they carry the
|
|
61
|
+
// wh n-tier like their modern counterparts (why/where/whence…).
|
|
62
|
+
const WH_ADV_LEMMAS = new Set([
|
|
63
|
+
'wherefore', 'whence', 'whither', 'whereby', 'wherein', 'whereof',
|
|
64
|
+
'whereto', 'whereat', 'whereupon',
|
|
65
|
+
]);
|
|
53
66
|
function bare(w) {
|
|
54
67
|
return w.word.toLowerCase().replace(/['’]/g, '');
|
|
55
68
|
}
|
|
@@ -60,6 +73,17 @@ function isContent(w) {
|
|
|
60
73
|
function isReducedVerb(w, words) {
|
|
61
74
|
if (isAphaeresis(w))
|
|
62
75
|
return true;
|
|
76
|
+
// Marginal-modal dare/need with a BARE infinitive ("dare he aspire", "dare
|
|
77
|
+
// seize the fire") — the auxiliary usage (it is what licenses the inversion):
|
|
78
|
+
// reduces like a modal; the beat belongs to the infinitive it governs.
|
|
79
|
+
if (words && isMarginalModalUse(w, words))
|
|
80
|
+
return true;
|
|
81
|
+
// Semi-modal "have to" (necessity — "do I have to bless", "we had to
|
|
82
|
+
// laugh"): the reduced ("hafta") functor use; the beat belongs to its
|
|
83
|
+
// infinitive. Lexical "have" (possession, causative, existential) never
|
|
84
|
+
// matches — the gate is the complement's shape, not the lemma.
|
|
85
|
+
if (words && isSemiModalHaveTo(w, words))
|
|
86
|
+
return true;
|
|
63
87
|
const rel = w.canonicalRel ?? '';
|
|
64
88
|
// "to be" (infinitive main verb) and ROOT be-forms ("Where WAS I?",
|
|
65
89
|
// "I have BEEN") carry a beat — NOT reduced. Must check BEFORE the AUX
|
|
@@ -89,6 +113,30 @@ function functionLevel(w, words) {
|
|
|
89
113
|
const lemma = bare(w);
|
|
90
114
|
const pos = w.lexicalClass;
|
|
91
115
|
const rel = w.canonicalRel ?? '';
|
|
116
|
+
// RHYME-FELLOW positive override (set by the stanza pass in index.ts): a
|
|
117
|
+
// line-final function MONOSYLLABLE that strong-rhymes with a content
|
|
118
|
+
// line-final elsewhere in the stanza carries the rhyme's beat — "Now
|
|
119
|
+
// WHEREfore STOPP'ST thou ME?" (me ~ three). 'n', not 's': a real beat,
|
|
120
|
+
// still graded below the clause's content anchors.
|
|
121
|
+
if (w.rhymeFocal && w.syllables.length === 1)
|
|
122
|
+
return 'n';
|
|
123
|
+
// wh-words FIRST — even in the determiner slot ("WHAT immortal hand or eye"):
|
|
124
|
+
// Krifka (13), wh-words and quantifiers as arguments take accents like content
|
|
125
|
+
// words ("Éverybody has escáped"); an interrogative determiner is the focus
|
|
126
|
+
// exponent of its question and never floors to the article tier. The archaic
|
|
127
|
+
// wh-adverbs ("Now WHEREfore stopp'st thou me?") are routinely mis-tagged IN,
|
|
128
|
+
// so the closed lemma class backs the POS test up.
|
|
129
|
+
if (/^(WDT|WP|WP\$|WRB)$/.test(pos) || WH_ADV_LEMMAS.has(lemma)) {
|
|
130
|
+
// …but a wh-word immediately followed by its POSTPOSED preposition ("what
|
|
131
|
+
// for", "who with", "where from") defers to the preposition's strong form
|
|
132
|
+
// — the orphaned functor is the accent exponent of the inverted PP ("what
|
|
133
|
+
// FOR is this form"). Keeping both at 'n' would only feed the n-n clash
|
|
134
|
+
// rule, whose lexical-stress tiebreak would demote the preposition back.
|
|
135
|
+
const nxt = words.find(x => x.absoluteIndex === w.absoluteIndex + 1 && x.syllables.length > 0);
|
|
136
|
+
if (nxt && isPostposedPreposition(nxt, words))
|
|
137
|
+
return 'w';
|
|
138
|
+
return 'n';
|
|
139
|
+
}
|
|
92
140
|
// A quantifier / demonstrative in an ATTRIBUTIVE determiner slot ("each street",
|
|
93
141
|
// "this cat", "that day") is a reduced determiner (x), NOT a standalone n-tier
|
|
94
142
|
// quantifier. Only the determiner USE reduces — a predeterminer ("ALL the",
|
|
@@ -97,9 +145,14 @@ function functionLevel(w, words) {
|
|
|
97
145
|
// preposition's beat ("through", "in") outrank an interior "each".
|
|
98
146
|
if (rel === 'DET' && pos !== 'PDT' && !NEGATORS.has(lemma))
|
|
99
147
|
return 'x';
|
|
100
|
-
// n — quantifiers, demonstratives,
|
|
101
|
-
|
|
102
|
-
|
|
148
|
+
// n — quantifiers, demonstratives, negators (carry real stress) — but NOT
|
|
149
|
+
// when the token is a subordinating MARK: "that" in "that I were clay" and
|
|
150
|
+
// "so" in "so that I seem" are complementisers (functors with clausal
|
|
151
|
+
// complements, Wagner §6.5.5), not demonstratives, and floor with the other
|
|
152
|
+
// complementisers below.
|
|
153
|
+
if (rel !== 'COMPMARK' && rel !== 'ADVMARK' &&
|
|
154
|
+
(pos === 'PDT' || QUANTIFIERS.has(lemma) || DEMONSTRATIVES.has(lemma) ||
|
|
155
|
+
NEGATORS.has(lemma)))
|
|
103
156
|
return 'n';
|
|
104
157
|
// "out of" is a compound preposition with fixed stress on "out" (OUT of, not
|
|
105
158
|
// out OF). "out" carries the directional content; "of" is the grammatical
|
|
@@ -113,26 +166,59 @@ function functionLevel(w, words) {
|
|
|
113
166
|
if (next && bare(next) === 'of')
|
|
114
167
|
return 'n';
|
|
115
168
|
}
|
|
169
|
+
// A POSTPOSED preposition — orphaned from its complement, which stands
|
|
170
|
+
// immediately to its LEFT as a wh-word ("what FOR is this form" = "for
|
|
171
|
+
// what"; "who WITH?", "where FROM?") — cannot procliticize rightward and
|
|
172
|
+
// takes its STRONG form (Selkirk 1996: function words are weak only where
|
|
173
|
+
// they can cliticize onto a following host). Accent-capable 'n', so the
|
|
174
|
+
// meter realises the beat; a preposition with a genuine rightward
|
|
175
|
+
// complement never matches ("who for the WORLD…" stays reduced below).
|
|
176
|
+
if (isPostposedPreposition(w, words))
|
|
177
|
+
return 'n';
|
|
116
178
|
// A STRANDED / intransitive preposition ("what are you waiting FOR", "she walked
|
|
117
179
|
// IN") is NOT the reducible proclitic of "in the house" — Wagner §6.5.5: only
|
|
118
180
|
// transitive functors have the weak allomorph. It keeps an overt beat ('w'), so
|
|
119
181
|
// the metrical fitter can promote it; a TRANSITIVE preposition floors at 'x'.
|
|
120
|
-
|
|
182
|
+
// POSSESSIVE pronouns and the clitic 's also carry the CASE relation but are
|
|
183
|
+
// not prepositions — they take the pronoun tier below, not the clitic floor
|
|
184
|
+
// (the CASE catch-all had crushed "For His Civility"'s His to 'x').
|
|
185
|
+
if (pos === 'IN' || pos === 'TO' ||
|
|
186
|
+
(rel === 'CASE' && pos !== 'PRP$' && pos !== 'PRP' && pos !== 'POS')) {
|
|
121
187
|
return isTransitiveFunctionWord(w, words) ? 'x' : 'w';
|
|
122
188
|
}
|
|
123
|
-
// x — pure clitics: articles, coordinators, complementisers
|
|
124
|
-
|
|
189
|
+
// x — pure clitics: articles, coordinators, complementisers, adverbial marks
|
|
190
|
+
// (wh-adverb marks — "when", "where" — keep the overt 'w' tier below: they
|
|
191
|
+
// carry framing content and verse freely promotes them)
|
|
192
|
+
if (ARTICLES.has(lemma) || pos === 'CC' || rel === 'CC' || rel === 'COMPMARK' ||
|
|
193
|
+
(rel === 'ADVMARK' && !/^(WDT|WP|WP\$|WRB)$/.test(pos)))
|
|
125
194
|
return 'x';
|
|
195
|
+
// A CAPITALIZED mid-sentence pronoun ("For His Civility", "but just
|
|
196
|
+
// Ourselves") is the poet's typographic focus mark — narrow focus per
|
|
197
|
+
// Krifka (78): it carries real (promotable) stress, not the given-pronoun w.
|
|
198
|
+
if (isCapitalizedFocalPronoun(w, words))
|
|
199
|
+
return 'n';
|
|
126
200
|
// w — possessives, pronouns, auxiliaries, modals, leftover determiners
|
|
127
201
|
return 'w';
|
|
128
202
|
}
|
|
129
|
-
/** The peak (highest lexical-stress) syllable of a word.
|
|
203
|
+
/** The peak (highest lexical-stress) syllable of a word.
|
|
204
|
+
*
|
|
205
|
+
* TIES (the lexicon's double-stressed entries — "outside" 1·1) resolve by the
|
|
206
|
+
* English diatone: the nominal/adjectival member of such a pair is LEFT-stressed
|
|
207
|
+
* (the OUTside of the house) while the prepositional/adverbial/particle member is
|
|
208
|
+
* RIGHT-stressed (she waited outSIDE; outSIDE the swallows roundelay) — so a tie
|
|
209
|
+
* takes the LAST tied syllable when the word is tagged as an adposition/particle,
|
|
210
|
+
* or is a preposition-built adverb (out·side, with·in, through·out, be·yond…).
|
|
211
|
+
* The compound indefinites (SOMEwhere, ANYthing) are left-stressed and do not
|
|
212
|
+
* match the particle-prefix test, so they keep the default first-syllable tie. */
|
|
213
|
+
const PREP_PREFIX = /^(out|in|up|down|with|through|be|a(?=bove|bout|round|cross|long|mid|gainst))/;
|
|
130
214
|
function peakSyllable(w) {
|
|
215
|
+
const rightwardTies = /^(IN|TO|RP)$/.test(w.lexicalClass) ||
|
|
216
|
+
(/^RB/.test(w.lexicalClass) && PREP_PREFIX.test(w.word.toLowerCase()));
|
|
131
217
|
let best = null;
|
|
132
218
|
let bestV = -1;
|
|
133
219
|
for (const s of w.syllables) {
|
|
134
220
|
const v = s.lexicalStress ?? s.stress;
|
|
135
|
-
if (v > bestV) {
|
|
221
|
+
if (v > bestV || (rightwardTies && v === bestV)) {
|
|
136
222
|
bestV = v;
|
|
137
223
|
best = s;
|
|
138
224
|
}
|
|
@@ -212,8 +298,21 @@ function relativisePP(toks, words) {
|
|
|
212
298
|
// subordinated relative to its sister (Wagner Ch.7) — but only as an ANCHOR
|
|
213
299
|
// demotion, and never when it would empty the φ of content. A word given a higher
|
|
214
300
|
// (worse) effective phrase-stress is less likely to win the φ nuclear.
|
|
301
|
+
// The line's FINAL syllable-bearing word: the RHYME position. Verse-final
|
|
302
|
+
// position is focal by construction (the rhyme is the form's own contrast
|
|
303
|
+
// device — Krifka: focus overrides givenness deaccenting), so givenness
|
|
304
|
+
// demotion never applies there: Shakespeare's "…more red than her lips'
|
|
305
|
+
// RED" keeps its rhyme-carrying beat even though "red" is discourse-given.
|
|
306
|
+
// Deaccented rhymes remain possible — as the marked, wrenched case the
|
|
307
|
+
// meter layer prices — but the RELATIVISER does not impose them.
|
|
308
|
+
const lineFinal = [...words]
|
|
309
|
+
.filter(x => x.syllables.length > 0)
|
|
310
|
+
.sort((a, b) => a.absoluteIndex - b.absoluteIndex)
|
|
311
|
+
.pop() ?? null;
|
|
215
312
|
const ps = (w) => {
|
|
216
313
|
const base = w.phraseStress || Infinity;
|
|
314
|
+
if (w === lineFinal)
|
|
315
|
+
return base; // rhyme position: never given-demoted
|
|
217
316
|
// A given content word — discourse-given (repeated from an earlier line), an
|
|
218
317
|
// inherently-given indefinite pronoun ("something", Wagner §7.2.3), or
|
|
219
318
|
// anaphorically given via a shared-head coordinate structure ("young blood
|
|
@@ -233,7 +332,12 @@ function relativisePP(toks, words) {
|
|
|
233
332
|
const promotePronoun = (w) => {
|
|
234
333
|
if (w.lexicalClass !== 'PRP' || w.word.includes("'") || w.word.includes("’"))
|
|
235
334
|
return false;
|
|
236
|
-
|
|
335
|
+
// ≤ 2, not === 1: the utterance-Newman pass (bracketing.ts) demotes an
|
|
336
|
+
// EARLIER co-equal of the utterance nuclear by one — a subject pronoun that
|
|
337
|
+
// matched a branching VP ("I have no right" — I co-equal with "right")
|
|
338
|
+
// lands at 2 yet is still the syntactically prominent pronoun this
|
|
339
|
+
// inversion test wants.
|
|
340
|
+
if (w.phraseStress > 2)
|
|
237
341
|
return false;
|
|
238
342
|
const idx = toks.indexOf(w);
|
|
239
343
|
if (idx !== 0)
|
|
@@ -254,10 +358,115 @@ function relativisePP(toks, words) {
|
|
|
254
358
|
return true;
|
|
255
359
|
return false;
|
|
256
360
|
};
|
|
361
|
+
// Subordinating marks are functors regardless of POS (the RB-tagged "so" of
|
|
362
|
+
// "so that I seem" must not anchor a beat over its clause's verb).
|
|
363
|
+
const isMark = (w) => {
|
|
364
|
+
const r = w.canonicalRel ?? '';
|
|
365
|
+
return r === 'COMPMARK' || r === 'ADVMARK';
|
|
366
|
+
};
|
|
367
|
+
// A pure negator ("not") is a functor over its predicate (negation takes the
|
|
368
|
+
// VP as complement); it keeps its n-tier stress — real, promotable — but must
|
|
369
|
+
// not claim a beat-anchor away from the verb it negates ("could not STOP for
|
|
370
|
+
// DEATH", not "could NOT stop"). Restored below if the φ has nothing else.
|
|
371
|
+
const isNegatorTok = (w) => NEGATORS.has(bare(w));
|
|
372
|
+
// Association with Focus (Krifka §4.4.6): a premodifying "just/only/even"
|
|
373
|
+
// F-marks its associate — the PARTICLE reduces (functor) and a PRONOUN
|
|
374
|
+
// associate is promoted to anchor status ("held but just OURSELVES": the
|
|
375
|
+
// exclusive reading accents the pronoun, never the particle).
|
|
376
|
+
const isFocusParticleTok = (w) => focusAssociateOf(w, words) !== null;
|
|
377
|
+
// A clause-initial temporal deictic directly introducing a wh-word ("Now
|
|
378
|
+
// wherefore stopp'st thou me?", "Now, what shall we do?") is a DISCOURSE
|
|
379
|
+
// MARKER, not a time adverb — Krifka §4.5.4 groups situational deictics with
|
|
380
|
+
// the non-accentable expressions in exactly this transitional use. It cedes
|
|
381
|
+
// its beat to the wh-focus it introduces (kept at 'n': real, promotable).
|
|
382
|
+
// The genuinely temporal use ("Now sleeps the crimson petal") is untouched —
|
|
383
|
+
// the gate is the following wh-word, not the lemma.
|
|
384
|
+
const isDiscourseDeictic = (w) => {
|
|
385
|
+
if (!/^(now|then)$/.test(bare(w)) || !/^RB/.test(w.lexicalClass))
|
|
386
|
+
return false;
|
|
387
|
+
if (toks.indexOf(w) !== 0)
|
|
388
|
+
return false;
|
|
389
|
+
const nxt = words.find(x => x.absoluteIndex === w.absoluteIndex + 1 && x.syllables.length > 0);
|
|
390
|
+
return !!nxt && (/^(WDT|WP|WP\$|WRB)$/.test(nxt.lexicalClass) || WH_ADV_LEMMAS.has(bare(nxt)));
|
|
391
|
+
};
|
|
392
|
+
// A sentence-initial INFERENTIAL CONNECTIVE set off by a comma ("So, may
|
|
393
|
+
// then each moment…", "Well, I declare", "Now, where was I?") is the
|
|
394
|
+
// discourse-marker use (Schiffrin) — transitional, prosodically reduced,
|
|
395
|
+
// never the φ anchor. The gate is the FULL configuration: first word of
|
|
396
|
+
// the sentence + immediately followed by a comma/dash + the closed
|
|
397
|
+
// inferential class. The accent-taking clause-initial adverbs stay out:
|
|
398
|
+
// spatial presentationals ("Here, take this"), concessives ("Still, she
|
|
399
|
+
// persisted"), narrative-temporal "Then," (freely stressed) are not listed;
|
|
400
|
+
// degree/manner "so" ("so much my own") fails the comma test.
|
|
401
|
+
const CONNECTIVES = new Set(['so', 'now', 'well', 'why', 'anyway']);
|
|
402
|
+
const isDiscourseConnective = (w) => {
|
|
403
|
+
if (!CONNECTIVES.has(bare(w)) || !/^(RB|UH|IN)/.test(w.lexicalClass))
|
|
404
|
+
return false;
|
|
405
|
+
const firstAlpha = words
|
|
406
|
+
.filter(x => /[A-Za-z]/.test(x.word))
|
|
407
|
+
.sort((a, b) => a.absoluteIndex - b.absoluteIndex)[0];
|
|
408
|
+
if (firstAlpha !== w)
|
|
409
|
+
return false;
|
|
410
|
+
const nxt = words.find(x => x.absoluteIndex === w.absoluteIndex + 1);
|
|
411
|
+
return !!nxt && nxt.syllables.length === 0 && /^[,—–-]+$/.test(nxt.word);
|
|
412
|
+
};
|
|
413
|
+
// "then/now" wedged between an INVERTED operator auxiliary and its delayed
|
|
414
|
+
// subject ("So, may then each moment drip off" — "may(beat) then(weak)") is
|
|
415
|
+
// the inferential use: the aux-to-subject gap of an inversion is a proclitic
|
|
416
|
+
// zone, and the operator aux, not the deictic, carries the inversion's
|
|
417
|
+
// accent. Normal-order "I would then go" (subject already passed) and
|
|
418
|
+
// clause-initial narrative "Then," are untouched.
|
|
419
|
+
const isPostOperatorDeictic = (w) => {
|
|
420
|
+
if (!/^(now|then)$/.test(bare(w)) || !/^RB/.test(w.lexicalClass))
|
|
421
|
+
return false;
|
|
422
|
+
const prev = words
|
|
423
|
+
.filter(x => x.syllables.length > 0 && x.absoluteIndex < w.absoluteIndex)
|
|
424
|
+
.sort((a, b) => b.absoluteIndex - a.absoluteIndex)[0];
|
|
425
|
+
if (!prev || prev.absoluteIndex !== w.absoluteIndex - 1)
|
|
426
|
+
return false;
|
|
427
|
+
return isInvertedOperatorAux(prev, words);
|
|
428
|
+
};
|
|
429
|
+
// Attributive "such" ("no such roses see I…") is determiner-class and must
|
|
430
|
+
// not out-anchor the noun it modifies — which cross-line givenness demotion
|
|
431
|
+
// would otherwise let it do (L5's "roses" made L6's "roses" given, handing
|
|
432
|
+
// the φ nuclear to "such" and wrecking the iambic grid). The gate is the
|
|
433
|
+
// attributive relation to a FOLLOWING governor; predicative/pronominal
|
|
434
|
+
// "such" ("such is life") never matches and keeps content status.
|
|
435
|
+
const isAttributiveSuch = (w) => {
|
|
436
|
+
if (bare(w) !== 'such')
|
|
437
|
+
return false;
|
|
438
|
+
const rel = w.canonicalRel ?? '';
|
|
439
|
+
if (rel !== 'AMOD' && rel !== 'DET')
|
|
440
|
+
return false;
|
|
441
|
+
const gov = w.dependency?.governor;
|
|
442
|
+
return !!gov && gov.absoluteIndex > w.absoluteIndex;
|
|
443
|
+
};
|
|
444
|
+
const focusAssociates = new Set();
|
|
445
|
+
for (const t of toks) {
|
|
446
|
+
const a = focusAssociateOf(t, words);
|
|
447
|
+
if (a)
|
|
448
|
+
focusAssociates.add(a);
|
|
449
|
+
}
|
|
450
|
+
// A pronoun made FOCAL — by a focus particle, or by the poet's mid-sentence
|
|
451
|
+
// capital ("Ourselves", "His") — sheds its inherent givenness (Wagner §7.2.3
|
|
452
|
+
// is a default, not a sentence): it anchors like content.
|
|
453
|
+
const isFocalPronoun = (w) => isPronoun(w) && w.syllables.length > 0 &&
|
|
454
|
+
(focusAssociates.has(w) || isCapitalizedFocalPronoun(w, words));
|
|
257
455
|
// The set the gradient is measured against: content words (non-reduced) first;
|
|
258
456
|
// for a content-less φ fall back to its prominent function words ("for THAT",
|
|
259
457
|
// "to YOU"), then to any content, then to anything — so every φ has an anchor.
|
|
260
|
-
|
|
458
|
+
// An inverted operator aux ("So, MAY then each moment…", "HAD I known") is a
|
|
459
|
+
// positive anchor — the operator carries the inversion's accent even though
|
|
460
|
+
// it is neither content nor a promoted pronoun.
|
|
461
|
+
let anchors = toks.filter(w => promotePronoun(w) || isFocalPronoun(w) ||
|
|
462
|
+
isInvertedOperatorAux(w, words) ||
|
|
463
|
+
(isContent(w) && !isReducedVerb(w, words) && !isMark(w) &&
|
|
464
|
+
!isNegatorTok(w) && !isFocusParticleTok(w) && !isDiscourseDeictic(w) &&
|
|
465
|
+
!isDiscourseConnective(w) && !isPostOperatorDeictic(w) &&
|
|
466
|
+
!isAttributiveSuch(w)));
|
|
467
|
+
if (anchors.length === 0) {
|
|
468
|
+
anchors = toks.filter(w => isContent(w) && !isReducedVerb(w, words) && !isMark(w));
|
|
469
|
+
}
|
|
261
470
|
if (anchors.length === 0)
|
|
262
471
|
anchors = toks.filter(w => !isReducedVerb(w, words) && functionLevel(w, words) !== 'x');
|
|
263
472
|
if (anchors.length === 0)
|
|
@@ -291,6 +500,14 @@ function relativisePP(toks, words) {
|
|
|
291
500
|
if (BE_FORMS.has(bare(w))) {
|
|
292
501
|
const i = toks.indexOf(w);
|
|
293
502
|
const prev = i > 0 ? toks[i - 1] : null;
|
|
503
|
+
// EXISTENTIAL be ("There WAS an Old Man with a beard"): after the
|
|
504
|
+
// expletive THERE (EX tag) the be-form is the verb of EXISTENCE — the
|
|
505
|
+
// assertion itself — not a copula linking a subject to a predicate.
|
|
506
|
+
// It keeps the overt-weak tier (promotable to the beat verse wants),
|
|
507
|
+
// never the clitic floor. Deictic-locative "THERE was…" is tagged RB
|
|
508
|
+
// and takes a different path entirely, so it is untouched.
|
|
509
|
+
if (prev && prev.lexicalClass === 'EX')
|
|
510
|
+
return 'w';
|
|
294
511
|
const followingContent = toks.slice(i + 1).find(x => isContent(x));
|
|
295
512
|
if (prev && !isContent(prev) && followingContent &&
|
|
296
513
|
/^(JJ|NN)/.test(followingContent.lexicalClass))
|
|
@@ -298,6 +515,19 @@ function relativisePP(toks, words) {
|
|
|
298
515
|
}
|
|
299
516
|
return 'w';
|
|
300
517
|
}
|
|
518
|
+
// A premodifying focus particle ("just/only/even" = exclusives) carries
|
|
519
|
+
// real-but-subordinate stress: 'n' — its associate holds the beat. The
|
|
520
|
+
// user-facing reading: "held but just(n) OURSELVES(s)".
|
|
521
|
+
if (isFocusParticleTok(w))
|
|
522
|
+
return 'n';
|
|
523
|
+
// Discourse-marker "now/then" before a wh-word: real-but-subordinate 'n'.
|
|
524
|
+
if (isDiscourseDeictic(w))
|
|
525
|
+
return 'n';
|
|
526
|
+
// Inferential connective ("So, …") and the deictic wedged inside an
|
|
527
|
+
// inversion ("may then each moment"): overt-weak — the beat belongs to
|
|
528
|
+
// the operator / the clause it introduces.
|
|
529
|
+
if (isDiscourseConnective(w) || isPostOperatorDeictic(w))
|
|
530
|
+
return 'w';
|
|
301
531
|
// Function word → its category gradient; a POLYSYLLABIC one floors its peak at
|
|
302
532
|
// 'w' so its stressed syllable outranks the reduced one (be·CAUSE, u·PON).
|
|
303
533
|
let tier = functionLevel(w, words);
|
|
@@ -346,6 +576,79 @@ function relativisePP(toks, words) {
|
|
|
346
576
|
break; // hit content before any prep
|
|
347
577
|
}
|
|
348
578
|
}
|
|
579
|
+
// Interrogative focus fronting (Krifka §4.6: focus introduces the phrase
|
|
580
|
+
// boundary and its accent). A φ-initial preposition heading a wh-containing
|
|
581
|
+
// PP is the left edge of a FRONTED FOCUS and carries a genuine light beat:
|
|
582
|
+
// "IN what distant deeps", "ON what wings". The licence is either an overt
|
|
583
|
+
// question mark closing the clause, or the pied-piped wh-PP standing at the
|
|
584
|
+
// very START of the utterance — wh-fronting is itself the interrogative /
|
|
585
|
+
// exclamative construction, and a verse line often carries the "?" on a later
|
|
586
|
+
// line. (A mid-sentence relative "…the house in which he lived" is φ-initial
|
|
587
|
+
// but not utterance-initial, so it stays reduced.) The interrogative
|
|
588
|
+
// DETERMINER just after the preposition defers to the noun it determines
|
|
589
|
+
// ("on WHAT wings" ↛ ; "ON what WINGS" ✓), while a free-standing wh
|
|
590
|
+
// ("WHAT the hand") keeps its own 'n'.
|
|
591
|
+
{
|
|
592
|
+
const lastIdx = toks[toks.length - 1].absoluteIndex;
|
|
593
|
+
const isQuestionClause = (() => {
|
|
594
|
+
const after = words
|
|
595
|
+
.filter(w => w.absoluteIndex > lastIdx)
|
|
596
|
+
.sort((a, b) => a.absoluteIndex - b.absoluteIndex);
|
|
597
|
+
for (const w of after) {
|
|
598
|
+
if (w.word === '?')
|
|
599
|
+
return true;
|
|
600
|
+
if (/^[.!;:]+$/.test(w.word) || w.word === '…')
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
return false;
|
|
604
|
+
})();
|
|
605
|
+
const firstAlpha = words
|
|
606
|
+
.filter(w => /[A-Za-z]/.test(w.word))
|
|
607
|
+
.sort((a, b) => a.absoluteIndex - b.absoluteIndex)[0];
|
|
608
|
+
const isUtteranceInitial = !!firstAlpha && toks[0] === firstAlpha;
|
|
609
|
+
const isWh = (x) => /^(WDT|WP|WP\$|WRB)$/.test(x.lexicalClass);
|
|
610
|
+
if ((isQuestionClause || isUtteranceInitial) && toks.length >= 2) {
|
|
611
|
+
const p = toks[0];
|
|
612
|
+
if ((p.lexicalClass === 'IN' || p.lexicalClass === 'TO') && p !== nuclear &&
|
|
613
|
+
(isWh(toks[1]) || (toks.length > 2 && isWh(toks[2])))) {
|
|
614
|
+
levels[0] = 'n';
|
|
615
|
+
// wh functioning as a determiner (a nominal follows it in the φ) defers
|
|
616
|
+
if (isWh(toks[1]) && toks.length > 2 && /^(JJ|NN)/.test(toks[2].lexicalClass)) {
|
|
617
|
+
levels[1] = 'w';
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
// Left-edge beat for a φ-INITIAL DISYLLABIC subordinator ("BeCAUSE I could
|
|
623
|
+
// not stop for Death", "BeCAUSE at least the past were passed away"). The
|
|
624
|
+
// subordinator opens its own clause-φ; its stressable syllable carries the
|
|
625
|
+
// light φ-initial beat ("beginnings free") exactly as a φ-initial preposition
|
|
626
|
+
// does — promotable by the fitter, never a forced ictus. The raise lands on
|
|
627
|
+
// the lexically stressed syllable; for a zero-contour entry ("because" 0·0)
|
|
628
|
+
// English subordinators of this shape are end-stressed (be·CAUSE, un·TIL,
|
|
629
|
+
// al·THOUGH), so the tie falls to the FINAL syllable. Monosyllabic marks
|
|
630
|
+
// ("if", "since") and mid-φ subordinators are untouched.
|
|
631
|
+
if (toks.length >= 2) {
|
|
632
|
+
const first = toks[0];
|
|
633
|
+
const rel0 = first.canonicalRel ?? '';
|
|
634
|
+
if ((rel0 === 'COMPMARK' || rel0 === 'ADVMARK') && first !== nuclear &&
|
|
635
|
+
first.syllables.length === 2 &&
|
|
636
|
+
(levels[0] === 'x' || levels[0] === 'w')) {
|
|
637
|
+
const [s1, s2] = first.syllables;
|
|
638
|
+
const l1 = s1.lexicalStress ?? s1.stress ?? 0;
|
|
639
|
+
const l2 = s2.lexicalStress ?? s2.stress ?? 0;
|
|
640
|
+
// Paint explicitly (the zero contour would misplace paintWord's peak).
|
|
641
|
+
if (l1 > l2) {
|
|
642
|
+
s1.relativeStress = 'n';
|
|
643
|
+
s2.relativeStress = 'x';
|
|
644
|
+
}
|
|
645
|
+
else {
|
|
646
|
+
s1.relativeStress = 'x';
|
|
647
|
+
s2.relativeStress = 'n';
|
|
648
|
+
}
|
|
649
|
+
levels[0] = '__painted__'; // sentinel: skip paintWord below
|
|
650
|
+
}
|
|
651
|
+
}
|
|
349
652
|
// Givenness escape (Wagner §6.1.3, maintainer's no-flat-run-on directive): when a
|
|
350
653
|
// φ's nuclear is an inherently-given PRONOUN immediately preceded by a transitive
|
|
351
654
|
// PREPOSITION ("of HIM", "to THEE"), do NOT leave them on a par or stress the
|
|
@@ -362,7 +665,11 @@ function relativisePP(toks, words) {
|
|
|
362
665
|
levels[pi] = 'n'; // preposition takes the differentiating beat
|
|
363
666
|
}
|
|
364
667
|
}
|
|
365
|
-
toks.forEach((w, i) =>
|
|
668
|
+
toks.forEach((w, i) => {
|
|
669
|
+
if (levels[i] === '__painted__')
|
|
670
|
+
return; // subordinator raise painted directly
|
|
671
|
+
paintWord(w, levels[i]);
|
|
672
|
+
});
|
|
366
673
|
// NOTE: the clash invariant (no adjacent equal n/m/s) is enforced once, globally,
|
|
367
674
|
// by resolveStressClashes over TRUE adjacent syllables in computeRelativeStress —
|
|
368
675
|
// not here. A per-word peak comparison would wrongly treat two polysyllables'
|
|
@@ -385,6 +692,11 @@ function isCompoundPair(a, b) {
|
|
|
385
692
|
return true;
|
|
386
693
|
if ((b.canonicalRel ?? '') === 'NOMD' && b.dependency?.governor === a)
|
|
387
694
|
return true;
|
|
695
|
+
// A POSSESSIVE noun (nmod:poss → canonical CASE: "love's sway") is a phrasal
|
|
696
|
+
// modifier, NOT a compound member — the NSR right-stresses it (love's SWAY).
|
|
697
|
+
// Without this the N+N adjacency fallback fore-stressed possessives.
|
|
698
|
+
if ((a.canonicalRel ?? '') === 'CASE' || (b.canonicalRel ?? '') === 'CASE')
|
|
699
|
+
return false;
|
|
388
700
|
return CLASH_NOUN.test(a.lexicalClass) && CLASH_NOUN.test(b.lexicalClass) &&
|
|
389
701
|
Math.abs(a.absoluteIndex - b.absoluteIndex) === 1;
|
|
390
702
|
}
|
|
@@ -422,10 +734,53 @@ function isCompoundPair(a, b) {
|
|
|
422
734
|
*/
|
|
423
735
|
function resolveStressClashes(words, sylWord) {
|
|
424
736
|
const syls = [];
|
|
737
|
+
// An OVERT punctuation boundary (comma, dash, IU stop) between two words is a
|
|
738
|
+
// genuine prosodic pause: beats on opposite sides keep BEAT status (never
|
|
739
|
+
// crushed below 'm' — the old cross-comma cascade stripped "blood" two
|
|
740
|
+
// tiers), but they still GRADE — two nuclear strengths cannot abut even
|
|
741
|
+
// across a pause, and a comma list thins its medial members to subordinate
|
|
742
|
+
// beats ("BLOOD, bone(m), MARrow…"). Quotes/brackets are
|
|
743
|
+
// phrasing-transparent and do not block.
|
|
744
|
+
const boundaryAfter = new Set(); // syllable index i: boundary between i and i+1
|
|
425
745
|
for (const w of [...words].sort((a, b) => a.absoluteIndex - b.absoluteIndex)) {
|
|
746
|
+
if (w.syllables.length === 0) {
|
|
747
|
+
if (/^[,;:.!?…()—–-]+$/.test(w.word) && syls.length > 0)
|
|
748
|
+
boundaryAfter.add(syls.length - 1);
|
|
749
|
+
continue;
|
|
750
|
+
}
|
|
426
751
|
for (const s of w.syllables)
|
|
427
752
|
syls.push(s);
|
|
428
753
|
}
|
|
754
|
+
// Alternation over a CLASH RUN (beat deletion at the weak/medial position):
|
|
755
|
+
// three or more consecutive beat-level syllables (≥ m) resolve by demoting
|
|
756
|
+
// the MEDIAL members and keeping both edges — "my young soul sick" →
|
|
757
|
+
// young(m) soul(n) sick(s), where the old pairwise-left cascade stripped the
|
|
758
|
+
// first two beats in sequence. A run may now CROSS overt boundaries: a
|
|
759
|
+
// medial that abuts a comma/dash stays a real (subordinate) beat — demoted
|
|
760
|
+
// only to 'm', so "As I am BLOOD, bone, MARrow…" grades s·m·s instead of
|
|
761
|
+
// holding three co-equal nuclei — while a boundary-free medial thins to 'n'
|
|
762
|
+
// as before. Pairs (exactly two) fall through to the pairwise logic below,
|
|
763
|
+
// which knows about compounds, phrase stress, and boundaries.
|
|
764
|
+
{
|
|
765
|
+
let i = 0;
|
|
766
|
+
while (i < syls.length) {
|
|
767
|
+
if (RANK[syls[i].relativeStress ?? 'w'] < RANK.m) {
|
|
768
|
+
i++;
|
|
769
|
+
continue;
|
|
770
|
+
}
|
|
771
|
+
let j = i;
|
|
772
|
+
while (j + 1 < syls.length &&
|
|
773
|
+
RANK[syls[j + 1].relativeStress ?? 'w'] >= RANK.m)
|
|
774
|
+
j++;
|
|
775
|
+
if (j - i >= 2) {
|
|
776
|
+
for (let k = i + 1; k < j; k++) {
|
|
777
|
+
const bounded = boundaryAfter.has(k - 1) || boundaryAfter.has(k);
|
|
778
|
+
syls[k].relativeStress = bounded ? 'm' : 'n';
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
i = j + 1;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
429
784
|
// Lexical stress value of a syllable (2=primary, 1=secondary, 0=unstressed).
|
|
430
785
|
// Used to break n-n ties: a syllable with HIGHER lexical stress has greater
|
|
431
786
|
// phonological integrity (Hayes' stress-maximum principle) and is protected
|
|
@@ -442,6 +797,24 @@ function resolveStressClashes(words, sylWord) {
|
|
|
442
797
|
const a = syls[i], b = syls[i + 1];
|
|
443
798
|
const ra = RANK[a.relativeStress ?? 'w'];
|
|
444
799
|
const rb = RANK[b.relativeStress ?? 'w'];
|
|
800
|
+
if (boundaryAfter.has(i)) {
|
|
801
|
+
// Across a comma/dash the pause separates the beats, so subordinate
|
|
802
|
+
// combinations (m·s, s·m, m·m) stand — but two NUCLEAR strengths still
|
|
803
|
+
// cannot abut even across the pause: the less prominent (higher
|
|
804
|
+
// phrase-stress) of an s·s pair grades to 'm' (still a beat), a tie
|
|
805
|
+
// resolving rightward toward the nuclear (Newman).
|
|
806
|
+
if (ra === RANK.s && rb === RANK.s) {
|
|
807
|
+
const wa = sylWord.get(a), wb = sylWord.get(b);
|
|
808
|
+
const pa = wa?.phraseStress || Infinity;
|
|
809
|
+
const pb = wb?.phraseStress || Infinity;
|
|
810
|
+
if (pa < pb)
|
|
811
|
+
b.relativeStress = 'm';
|
|
812
|
+
else
|
|
813
|
+
a.relativeStress = 'm';
|
|
814
|
+
changed = true;
|
|
815
|
+
}
|
|
816
|
+
continue;
|
|
817
|
+
}
|
|
445
818
|
if (ra >= RANK.m && rb >= RANK.m) {
|
|
446
819
|
const wa = sylWord.get(a), wb = sylWord.get(b);
|
|
447
820
|
let demoteA;
|
|
@@ -550,6 +923,14 @@ function promoteWeakTroughs(words) {
|
|
|
550
923
|
}
|
|
551
924
|
}
|
|
552
925
|
const isW = (i) => i >= 0 && i < syls.length && syls[i].relativeStress === 'w';
|
|
926
|
+
// A flank counts as weak when it is 'w' OR 'x' — a clitic flank is weaker
|
|
927
|
+
// still, and refusing it silenced real troughs ("And(x) I(w) had(w) put" —
|
|
928
|
+
// "I" is the alternation's natural light beat; "Be(x)cause(w) I(w)…" — the
|
|
929
|
+
// subordinator's stressed syllable likewise). The CANDIDATE must still be an
|
|
930
|
+
// overt 'w' (a clitic itself never promotes), and the polysyllable-toneless
|
|
931
|
+
// exclusion below still applies.
|
|
932
|
+
const isWeakFlank = (i) => i >= 0 && i < syls.length &&
|
|
933
|
+
(syls[i].relativeStress === 'w' || syls[i].relativeStress === 'x');
|
|
553
934
|
const isLexicallyToneless = (i) => {
|
|
554
935
|
const s = syls[i];
|
|
555
936
|
if ((s.lexicalStress ?? s.stress) !== 0)
|
|
@@ -559,12 +940,27 @@ function promoteWeakTroughs(words) {
|
|
|
559
940
|
};
|
|
560
941
|
let lastProm = -2;
|
|
561
942
|
for (let i = 1; i < syls.length - 1; i++) {
|
|
562
|
-
if (!isW(i) || !
|
|
563
|
-
continue; //
|
|
943
|
+
if (!isW(i) || !isWeakFlank(i - 1) || !isWeakFlank(i + 1))
|
|
944
|
+
continue; // 'w' amid weakness
|
|
564
945
|
if (isLexicallyToneless(i))
|
|
565
946
|
continue; // no fabricated secondaries
|
|
566
947
|
if (i - 1 === lastProm)
|
|
567
948
|
continue; // keep promotions non-adjacent
|
|
949
|
+
// Argument over functor at the alternation level (Wagner §6.2.2): when a
|
|
950
|
+
// REDUCED VERB (auxiliary / semi-modal — a functor) and a PRONOUN (an
|
|
951
|
+
// argument) are both eligible neighbours in the same trough, the
|
|
952
|
+
// left-greedy scan must not hand the beat to the functor — "do I have to
|
|
953
|
+
// bless" wants do(w) I(n) have(w), not do(n) I(w) have(n). The reduced
|
|
954
|
+
// verb yields to an immediately-following eligible pronoun.
|
|
955
|
+
if (i + 2 < syls.length) {
|
|
956
|
+
const wHere = sylWord.get(syls[i]);
|
|
957
|
+
const wNext = sylWord.get(syls[i + 1]);
|
|
958
|
+
if (wHere && wNext && wHere !== wNext &&
|
|
959
|
+
isReducedVerb(wHere, words) && isPronoun(wNext) &&
|
|
960
|
+
isW(i + 1) && !isLexicallyToneless(i + 1) && isWeakFlank(i + 2)) {
|
|
961
|
+
continue; // the pronoun promotes instead
|
|
962
|
+
}
|
|
963
|
+
}
|
|
568
964
|
syls[i].relativeStress = 'n';
|
|
569
965
|
lastProm = i;
|
|
570
966
|
}
|
|
@@ -12,6 +12,39 @@ export declare function isInherentlyGiven(w: ClsWord): boolean;
|
|
|
12
12
|
* whose post-nominal PP modifier cannot be hosted inside the head's own group and
|
|
13
13
|
* so needs its OWN ϕ (the phrasing break the ear hears after "Something …"). */
|
|
14
14
|
export declare function isLightNominalHead(w: ClsWord): boolean;
|
|
15
|
+
export declare function focusAssociateOf(w: ClsWord, words: ClsWord[]): ClsWord | null;
|
|
16
|
+
export declare function isMarginalModalUse(w: ClsWord, words: ClsWord[]): boolean;
|
|
17
|
+
/** A CAPITALIZED personal/possessive pronoun in mid-sentence ("but just
|
|
18
|
+
* Ourselves", "For His Civility" — Dickinson's reverential capitals).
|
|
19
|
+
* English capitalizes pronouns only sentence-initially and in "I", so a
|
|
20
|
+
* capital here is the poet's deliberate typographic FOCUS mark: the pronoun
|
|
21
|
+
* is referential/contrastive — Wagner's "functor role" ceded to a full
|
|
22
|
+
* referring use, Krifka (78): an accented pronoun signals narrow focus. */
|
|
23
|
+
export declare function isCapitalizedFocalPronoun(w: ClsWord, words: ClsWord[]): boolean;
|
|
24
|
+
/** A POSTPOSED preposition: orphaned from its complement, which stands
|
|
25
|
+
* immediately to its LEFT as a wh-word — the inverted pied-piping of "what
|
|
26
|
+
* FOR is this form" (= "for what"), and the bare "Who WITH?", "Where FROM?".
|
|
27
|
+
* Such a preposition cannot procliticize (no rightward complement to lean on)
|
|
28
|
+
* and takes its STRONG form (Selkirk 1996: function words are weak only where
|
|
29
|
+
* they can cliticize onto a following host) — the same phonology as clause-
|
|
30
|
+
* final stranding, caught here in the wh-adjacent inversion the transitivity
|
|
31
|
+
* test misses. Subordinators never match ("what if…" is a mark, not case),
|
|
32
|
+
* and neither does a preposition with a genuine rightward complement
|
|
33
|
+
* ("who for the WORLD would…", "when in ROME…" stay reduced). */
|
|
34
|
+
export declare function isPostposedPreposition(w: ClsWord, words: ClsWord[]): boolean;
|
|
35
|
+
/** Subject–auxiliary INVERSION without interrogative force: the aux/modal
|
|
36
|
+
* precedes its clause's subject in a sentence that is NOT a question —
|
|
37
|
+
* optative "So, may then each moment drip off…" / "May she rest", negative
|
|
38
|
+
* inversion "Never did I see", conditional inversion "Had I known". The
|
|
39
|
+
* inverted auxiliary is the overt exponent of the operator that triggered the
|
|
40
|
+
* inversion (wish / negation / condition — Krifka: illocutionary and polarity
|
|
41
|
+
* operators are accentable focus exponents), so it is NOT the reduced given-
|
|
42
|
+
* auxiliary of a plain declarative: it anchors a light beat ("HAD I but
|
|
43
|
+
* known", "never DID I see"). In a QUESTION the inversion is discharged by
|
|
44
|
+
* the interrogative operator instead (wh-word or final "?") and the aux stays
|
|
45
|
+
* reduced ("won't you GUIDE me…?", "do I have to BLESS?"). */
|
|
46
|
+
export declare function isInvertedOperatorAux(w: ClsWord, words: ClsWord[]): boolean;
|
|
47
|
+
export declare function isSemiModalHaveTo(w: ClsWord, words: ClsWord[]): boolean;
|
|
15
48
|
export declare function verbHasArgChild(verb: ClsWord, children: Map<number, ClsWord[]>): boolean;
|
|
16
49
|
export declare function isUnaccusativeOrPassive(subj: ClsWord, verb: ClsWord): boolean;
|
|
17
50
|
/** The preposition lemma governing an oblique noun: its `case`/`CASE` dependent. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syntax.d.ts","sourceRoot":"","sources":["../../src/calliope/syntax.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAQtC,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAE3C;AACD,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAsC;AACjF,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAsC;AAGjF;;;gDAGgD;AAChD,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAG7C;AAaD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAIrD;AAED;;iFAEiF;AACjF,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAEtD;AAMD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,GAC9C,OAAO,CAQT;AASD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAI7E;AAmBD,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,IAAI,CAQzE;AAED;2EAC2E;AAC3E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAKzE;AAED;iFACiF;AACjF,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAYrF;AAED;+EAC+E;AAC/E,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAStE;AAkBD;;qEAEqE;AACrE,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAqB9E;AAGD,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AAEnD;;;gFAGgF;AAChF,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAcnD"}
|
|
1
|
+
{"version":3,"file":"syntax.d.ts","sourceRoot":"","sources":["../../src/calliope/syntax.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAQtC,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAE3C;AACD,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAsC;AACjF,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAsC;AAGjF;;;gDAGgD;AAChD,wBAAgB,SAAS,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAG7C;AAaD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAIrD;AAED;;iFAEiF;AACjF,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAEtD;AAWD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,GAAG,IAAI,CAW7E;AAUD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAkBxE;AAED;;;;;4EAK4E;AAC5E,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAuB/E;AAED;;;;;;;;;kEASkE;AAClE,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAoB5E;AAED;;;;;;;;;;+DAU+D;AAC/D,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAiB3E;AAeD,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAkBvE;AAMD,wBAAgB,eAAe,CAC7B,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,GAC9C,OAAO,CAQT;AASD,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAI7E;AAmBD,oFAAoF;AACpF,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,MAAM,GAAG,IAAI,CAQzE;AAED;2EAC2E;AAC3E,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAKzE;AAED;iFACiF;AACjF,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAYrF;AAED;+EAC+E;AAC/E,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAStE;AAkBD;;qEAEqE;AACrE,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAqB9E;AAGD,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAC;AAEnD;;;gFAGgF;AAChF,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,GAAG,WAAW,CAcnD"}
|