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
package/dist/calliope/syntax.js
CHANGED
|
@@ -51,6 +51,205 @@ export function isInherentlyGiven(w) {
|
|
|
51
51
|
export function isLightNominalHead(w) {
|
|
52
52
|
return isPronoun(w) || isInherentlyGiven(w);
|
|
53
53
|
}
|
|
54
|
+
/** Focus-sensitive particles (Krifka §4.4.6, Association with Focus): a
|
|
55
|
+
* premodifying "just / only / even / merely" marks its sister as the FOCUS —
|
|
56
|
+
* the ASSOCIATE bears the accent, the particle is a functor and reduces
|
|
57
|
+
* ("the carriage held but just OURSELVES": "just" = "only", so the accent
|
|
58
|
+
* belongs to the pronoun it exclusivises). Returns the associate, or null
|
|
59
|
+
* when the word is not a premodifying focus particle. A postmodifying or
|
|
60
|
+
* clause-final "just/only" ("he only just made it") is temporal/degree use
|
|
61
|
+
* and returns null. */
|
|
62
|
+
const FOCUS_PARTICLES = new Set(['just', 'only', 'even', 'merely']);
|
|
63
|
+
export function focusAssociateOf(w, words) {
|
|
64
|
+
if (!FOCUS_PARTICLES.has(bare(w)))
|
|
65
|
+
return null;
|
|
66
|
+
if (!/^RB/.test(w.lexicalClass))
|
|
67
|
+
return null;
|
|
68
|
+
const gov = w.dependency?.governor;
|
|
69
|
+
// canonical case: the particle is an ADVMOD premodifier of its associate
|
|
70
|
+
if (gov && gov !== w && !isPunct(gov) && gov.absoluteIndex > w.absoluteIndex)
|
|
71
|
+
return gov;
|
|
72
|
+
// parse-robust fallback: the particle immediately precedes a pronoun the
|
|
73
|
+
// parser attached elsewhere ("but just Ourselves" with "just" hung on the verb)
|
|
74
|
+
const next = words.find(x => x.absoluteIndex === w.absoluteIndex + 1 && x.syllables.length > 0);
|
|
75
|
+
if (next && (PRON.test(next.lexicalClass) || feat(next, 'PronType') === 'Prs'))
|
|
76
|
+
return next;
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
/** Marginal-modal use of dare/need (Quirk et al.): the BARE-infinitive
|
|
80
|
+
* complement construction — "dare he aspire", "What the hand dare seize" —
|
|
81
|
+
* is the AUXILIARY usage (the one that licenses subject inversion), so the
|
|
82
|
+
* verb reduces like a modal and the beat belongs to its infinitive. The
|
|
83
|
+
* LEXICAL usage takes a to-infinitive ("dared to aspire") or a nominal
|
|
84
|
+
* object ("dared the leap") and keeps full content stress — the gate is the
|
|
85
|
+
* complement's shape, not the lemma alone. */
|
|
86
|
+
const MARGINAL_MODALS = new Set(['dare', 'dares', 'dared', 'durst', 'need']);
|
|
87
|
+
export function isMarginalModalUse(w, words) {
|
|
88
|
+
if (!MARGINAL_MODALS.has(bare(w)) || !/^VB/.test(w.lexicalClass))
|
|
89
|
+
return false;
|
|
90
|
+
// A clausal-complement child that is a bare verb (no "to" mark below it).
|
|
91
|
+
const comp = words.find(d => d !== w && d.dependency?.governor === w &&
|
|
92
|
+
/^(XCOMP|CCOMP)$/.test(d.canonicalRel ?? '') && /^VB/.test(d.lexicalClass));
|
|
93
|
+
if (comp) {
|
|
94
|
+
const hasTo = words.some(m => m !== comp && m.dependency?.governor === comp &&
|
|
95
|
+
(m.lexicalClass === 'TO' || bare(m) === 'to'));
|
|
96
|
+
return !hasTo;
|
|
97
|
+
}
|
|
98
|
+
// Surface fallback for mis-parses: an immediately-following bare infinitive
|
|
99
|
+
// ("dare seize"), or inverted pronoun subject + bare verb ("dare he aspire").
|
|
100
|
+
const at = (k) => words.find(x => x.absoluteIndex === w.absoluteIndex + k);
|
|
101
|
+
const n1 = at(1);
|
|
102
|
+
if (n1 && n1.lexicalClass === 'VB')
|
|
103
|
+
return true;
|
|
104
|
+
const n2 = at(2);
|
|
105
|
+
if (n1 && n1.lexicalClass === 'PRP' && n2 && /^VBP?$/.test(n2.lexicalClass))
|
|
106
|
+
return true;
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
/** A CAPITALIZED personal/possessive pronoun in mid-sentence ("but just
|
|
110
|
+
* Ourselves", "For His Civility" — Dickinson's reverential capitals).
|
|
111
|
+
* English capitalizes pronouns only sentence-initially and in "I", so a
|
|
112
|
+
* capital here is the poet's deliberate typographic FOCUS mark: the pronoun
|
|
113
|
+
* is referential/contrastive — Wagner's "functor role" ceded to a full
|
|
114
|
+
* referring use, Krifka (78): an accented pronoun signals narrow focus. */
|
|
115
|
+
export function isCapitalizedFocalPronoun(w, words) {
|
|
116
|
+
if (!PRON.test(w.lexicalClass) && feat(w, 'PronType') !== 'Prs')
|
|
117
|
+
return false;
|
|
118
|
+
if (!/^[A-Z]/.test(w.word))
|
|
119
|
+
return false;
|
|
120
|
+
const lower = bare(w);
|
|
121
|
+
if (lower === 'i' || lower.startsWith('i')) {
|
|
122
|
+
// "I" and its contractions are conventionally capital — no signal there.
|
|
123
|
+
if (lower === 'i' || /^i(ll|m|d|ve)$/.test(lower))
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
// Conventional capitalization is NOT a focus mark: a pronoun opening a
|
|
127
|
+
// QUOTED sentence ("Who said, "It is just as I feared!"") or following
|
|
128
|
+
// sentence-internal terminal punctuation or a colon is capitalized by
|
|
129
|
+
// orthographic rule, not by the poet's emphasis. Scan back over the
|
|
130
|
+
// punctuation tokens abutting the pronoun; a quote mark or sentence
|
|
131
|
+
// punctuation among them disqualifies. (Dashes and commas do NOT
|
|
132
|
+
// disqualify — Dickinson's mid-clause capitals stand.)
|
|
133
|
+
for (const x of [...words]
|
|
134
|
+
.filter(p => p.absoluteIndex < w.absoluteIndex)
|
|
135
|
+
.sort((a, b) => b.absoluteIndex - a.absoluteIndex)) {
|
|
136
|
+
if (/[A-Za-z]/.test(x.word))
|
|
137
|
+
break; // reached a real word
|
|
138
|
+
if (/["“”'‘’.!?:]/.test(x.word))
|
|
139
|
+
return false; // conventional capital
|
|
140
|
+
}
|
|
141
|
+
// Mid-sentence only: some earlier alphabetic word must exist.
|
|
142
|
+
return words.some(x => x.absoluteIndex < w.absoluteIndex && /[A-Za-z]/.test(x.word));
|
|
143
|
+
}
|
|
144
|
+
/** A POSTPOSED preposition: orphaned from its complement, which stands
|
|
145
|
+
* immediately to its LEFT as a wh-word — the inverted pied-piping of "what
|
|
146
|
+
* FOR is this form" (= "for what"), and the bare "Who WITH?", "Where FROM?".
|
|
147
|
+
* Such a preposition cannot procliticize (no rightward complement to lean on)
|
|
148
|
+
* and takes its STRONG form (Selkirk 1996: function words are weak only where
|
|
149
|
+
* they can cliticize onto a following host) — the same phonology as clause-
|
|
150
|
+
* final stranding, caught here in the wh-adjacent inversion the transitivity
|
|
151
|
+
* test misses. Subordinators never match ("what if…" is a mark, not case),
|
|
152
|
+
* and neither does a preposition with a genuine rightward complement
|
|
153
|
+
* ("who for the WORLD would…", "when in ROME…" stay reduced). */
|
|
154
|
+
export function isPostposedPreposition(w, words) {
|
|
155
|
+
const pos = w.lexicalClass;
|
|
156
|
+
const rel = w.canonicalRel ?? '';
|
|
157
|
+
if (pos !== 'IN' && pos !== 'TO' && rel !== 'CASE')
|
|
158
|
+
return false;
|
|
159
|
+
if (rel === 'COMPMARK' || rel === 'ADVMARK')
|
|
160
|
+
return false;
|
|
161
|
+
const prev = words
|
|
162
|
+
.filter(x => x.syllables.length > 0 && x.absoluteIndex < w.absoluteIndex)
|
|
163
|
+
.sort((a, b) => b.absoluteIndex - a.absoluteIndex)[0];
|
|
164
|
+
if (!prev || prev.absoluteIndex !== w.absoluteIndex - 1)
|
|
165
|
+
return false;
|
|
166
|
+
if (!/^(WDT|WP|WP\$|WRB)$/.test(prev.lexicalClass))
|
|
167
|
+
return false;
|
|
168
|
+
// No genuine rightward complement: neither a following nominal governor (UD
|
|
169
|
+
// attaches the prep as CASE dependent of the noun it precedes) nor a
|
|
170
|
+
// following nominal/pronominal child.
|
|
171
|
+
const gov = w.dependency?.governor;
|
|
172
|
+
if (gov && !isPunct(gov) && (NOUN.test(gov.lexicalClass) || PRON.test(gov.lexicalClass)) &&
|
|
173
|
+
gov.absoluteIndex > w.absoluteIndex)
|
|
174
|
+
return false;
|
|
175
|
+
if (words.some(d => d.dependency?.governor === w &&
|
|
176
|
+
(NOUN.test(d.lexicalClass) || PRON.test(d.lexicalClass)) &&
|
|
177
|
+
d.absoluteIndex > w.absoluteIndex))
|
|
178
|
+
return false;
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
/** Subject–auxiliary INVERSION without interrogative force: the aux/modal
|
|
182
|
+
* precedes its clause's subject in a sentence that is NOT a question —
|
|
183
|
+
* optative "So, may then each moment drip off…" / "May she rest", negative
|
|
184
|
+
* inversion "Never did I see", conditional inversion "Had I known". The
|
|
185
|
+
* inverted auxiliary is the overt exponent of the operator that triggered the
|
|
186
|
+
* inversion (wish / negation / condition — Krifka: illocutionary and polarity
|
|
187
|
+
* operators are accentable focus exponents), so it is NOT the reduced given-
|
|
188
|
+
* auxiliary of a plain declarative: it anchors a light beat ("HAD I but
|
|
189
|
+
* known", "never DID I see"). In a QUESTION the inversion is discharged by
|
|
190
|
+
* the interrogative operator instead (wh-word or final "?") and the aux stays
|
|
191
|
+
* reduced ("won't you GUIDE me…?", "do I have to BLESS?"). */
|
|
192
|
+
export function isInvertedOperatorAux(w, words) {
|
|
193
|
+
const rel = w.canonicalRel ?? '';
|
|
194
|
+
if (rel !== 'AUX' && rel !== 'AUXPASS' && w.lexicalClass !== 'MD')
|
|
195
|
+
return false;
|
|
196
|
+
if (!/^(MD|VB)/.test(w.lexicalClass))
|
|
197
|
+
return false;
|
|
198
|
+
const head = w.dependency?.governor;
|
|
199
|
+
if (!head || head === w || isPunct(head))
|
|
200
|
+
return false;
|
|
201
|
+
const subj = words.find(d => d.dependency?.governor === head &&
|
|
202
|
+
/^(NSUBJ|NSUBJPASS)$/.test(d.canonicalRel ?? ''));
|
|
203
|
+
if (!subj || subj.absoluteIndex < w.absoluteIndex)
|
|
204
|
+
return false; // normal order / imperative
|
|
205
|
+
if (words.some(x => x.word.includes('?')))
|
|
206
|
+
return false; // overt question
|
|
207
|
+
const firstAlpha = words
|
|
208
|
+
.filter(x => /[A-Za-z]/.test(x.word))
|
|
209
|
+
.sort((a, b) => a.absoluteIndex - b.absoluteIndex)[0];
|
|
210
|
+
// A wh-fronted clause is interrogative even when verse carries the "?" on a
|
|
211
|
+
// later line.
|
|
212
|
+
if (firstAlpha && /^(WDT|WP|WP\$|WRB)$/.test(firstAlpha.lexicalClass))
|
|
213
|
+
return false;
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
/** Semi-modal "have to" (necessity): have/has/had governing a TO-infinitive
|
|
217
|
+
* open complement with no intervening object — "do I have to bless", "we had
|
|
218
|
+
* to laugh". This is the reduced ("hafta") functor use: under broad focus
|
|
219
|
+
* the whole chain subordinates and the beat belongs to the infinitive (or
|
|
220
|
+
* falls to the subject by alternation). The LEXICAL uses keep full content
|
|
221
|
+
* stress and never match: possession with a purpose infinitive ("I have
|
|
222
|
+
* BOOKS to read" — the object intervenes, the infinitive is ACL on the
|
|
223
|
+
* noun), causative ("have him CALL me" — bare infinitive), existential /
|
|
224
|
+
* plain possession ("what do I HAVE here" — no infinitive at all). A
|
|
225
|
+
* narrow/verum focus on the necessity itself ("Who do I HAVE to bless?") is
|
|
226
|
+
* a marked reading that needs a cue the text must supply — an all-caps HAVE
|
|
227
|
+
* is honored as that typographic cue. */
|
|
228
|
+
const HAVE_FORMS = new Set(['have', 'has', 'had', 'hath', 'having']);
|
|
229
|
+
export function isSemiModalHaveTo(w, words) {
|
|
230
|
+
if (!HAVE_FORMS.has(bare(w)) || !/^VB/.test(w.lexicalClass))
|
|
231
|
+
return false;
|
|
232
|
+
if (/^[A-Z]{2,}$/.test(w.word))
|
|
233
|
+
return false; // typographic narrow focus
|
|
234
|
+
const kids = words.filter(d => d !== w && d.dependency?.governor === w);
|
|
235
|
+
if (kids.some(d => /^(DOBJ|OBJ|IOBJ)$/.test(d.canonicalRel ?? '')))
|
|
236
|
+
return false;
|
|
237
|
+
const comp = kids.find(d => /^(XCOMP|CCOMP)$/.test(d.canonicalRel ?? '') &&
|
|
238
|
+
/^VB/.test(d.lexicalClass));
|
|
239
|
+
if (comp) {
|
|
240
|
+
const hasTo = words.some(m => m !== comp && m.dependency?.governor === comp &&
|
|
241
|
+
(m.lexicalClass === 'TO' || bare(m) === 'to'));
|
|
242
|
+
if (hasTo)
|
|
243
|
+
return true;
|
|
244
|
+
}
|
|
245
|
+
// Parse-robust surface fallback: have + "to" + verb, immediately adjacent.
|
|
246
|
+
const n1 = words.find(x => x.absoluteIndex === w.absoluteIndex + 1);
|
|
247
|
+
const n2 = words.find(x => x.absoluteIndex === w.absoluteIndex + 2);
|
|
248
|
+
if (n1 && (n1.lexicalClass === 'TO' || bare(n1) === 'to') &&
|
|
249
|
+
n2 && /^VB/.test(n2.lexicalClass))
|
|
250
|
+
return true;
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
54
253
|
/** A child relation that makes a verb BRANCH (carry an internal argument) — the
|
|
55
254
|
* specifier-restriction trigger (Wagner §6.3.2): a transitive/clausal VP whose
|
|
56
255
|
* branchingness blocks subordination of its subject. */
|