calliope-ts 0.0.1 → 0.0.2
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 +111 -205
- package/dist/index.js +9 -1
- package/package.json +2 -2
- package/src/index.ts +7 -2
- package/tests/basic.test.ts +696 -0
- package/tests/epg64.meter.train.txt +8139 -0
- package/tests/litlab-sample-2016.txt +1738 -0
- package/tests/prosodic.meter.train.txt +13192 -0
- package/tsconfig.json +29 -0
- package/vitest.config.d.ts +2 -0
- package/vitest.config.js +14 -0
- package/vitest.config.ts +15 -0
- package/.claude/settings.local.json +0 -8
- package/.opencode/skills/calliope-ts/SKILL.md +0 -74
package/README.md
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
# Calliope TS
|
|
2
2
|
|
|
3
|
-
**Calliope TS** is a phonological poetry
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
3
|
+
**Calliope TS** is a phonological poetry scansion toolkit for TypeScript / Node.js.
|
|
4
|
+
|
|
5
|
+
Its name, of course, recalls a muse.
|
|
6
|
+
Hand to this muse a song or verse,
|
|
7
|
+
And she might show you how it curves,
|
|
8
|
+
Which syllables hold weight in time,
|
|
9
|
+
How much of it, and where a rhyme,
|
|
10
|
+
Which meter dances through each line,
|
|
11
|
+
And where should land each beat
|
|
12
|
+
Your voice might drawl, or music drawn...
|
|
13
|
+
She even knows poetic form!
|
|
14
|
+
|
|
15
|
+
The premise of this toolkit is that rhythm, and therefore also meter, is much more reliably *recovered from pronunciation* than from text as such. What?! But there's no Text-to-Speech here?! (Sorry!) A lapse? A paradox? Nah. Instead, Calliope parses a stanza or verse, reproducing roughly how an attentive (but silent) reader might do so: first, working out how the words are likely to be said aloud — deduced by following contextual patterns, cohering a syntax, wielding phonotactics, evading constraints, gathering prosody, accounting for the ways phrases clump or bounce, for how a voice might subtly perk over clause-ends, and all the rest of it; and only thereafter scanning for what sort of rhythms a phrase such as that might be spectrally reaching for, or settling into. The scholars dwelling on Generative Metrics, a liminal little field somewhere in-between linguistics, phonetics, and literary studies, call this sort of approach **phonological scansion**, and the bulk of this README is about what that means and how the toolkit puts it to work.
|
|
14
16
|
|
|
15
17
|
```
|
|
16
18
|
$ calliope-ts --reading exile.txt
|
|
17
19
|
|
|
18
20
|
Reading View — stress gradient over the original text
|
|
19
21
|
|
|
20
|
-
He happens to be a French poet, that thin,
|
|
21
|
-
book-carrying man with a bristly gray chin;
|
|
22
|
+
He happens to be a French poet, that thin, ← each syllable is tinted by how
|
|
23
|
+
book-carrying man with a bristly gray chin; much stress it carries (see below)
|
|
22
24
|
...
|
|
23
25
|
|
|
24
|
-
Stress Maps & Meter — top-3
|
|
25
|
-
S1L1 nsw|xwx|msw ‖ xs amphibrachic tetrameter (amph 1.20 · iamb 1.14 ·
|
|
26
|
-
|
|
27
|
-
S1L2 m|-mww|sxx|mwm|s amphibrachic tetrameter ≈ continuity; standalone:
|
|
28
|
-
dactylic tetrameter A(perfect)
|
|
26
|
+
Stress Maps & Meter — top-3 candidate meters per line
|
|
27
|
+
S1L1 nsw|xwx|msw ‖ xs amphibrachic tetrameter (amph 1.20 · iamb 1.14 · anap 1.13) A
|
|
28
|
+
S1L2 m-mw|wsx|xmw|ms amphibrachic tetrameter ≈ continuity; standalone: dactylic tetrameter A
|
|
29
29
|
...
|
|
30
30
|
```
|
|
31
31
|
|
|
@@ -36,9 +36,12 @@ Stress Maps & Meter — top-3 fit scores per line
|
|
|
36
36
|
- [Installation](#installation)
|
|
37
37
|
- [Quick start (CLI)](#quick-start-cli)
|
|
38
38
|
- [Reading the output](#reading-the-output)
|
|
39
|
-
- [
|
|
39
|
+
- [What "phonological scansion" means](#what-phonological-scansion-means)
|
|
40
|
+
- [The pipeline, stage by stage](#the-pipeline-stage-by-stage)
|
|
41
|
+
- [A note on rules vs. constraints](#a-note-on-rules-vs-constraints)
|
|
40
42
|
- [Programmatic usage (API)](#programmatic-usage-api)
|
|
41
43
|
- [Examples](#examples)
|
|
44
|
+
- [How it compares to other scanners](#how-it-compares-to-other-scanners)
|
|
42
45
|
- [Background and lineage](#background-and-lineage)
|
|
43
46
|
- [Scope, strengths, and honest limitations](#scope-strengths-and-honest-limitations)
|
|
44
47
|
- [Development](#development)
|
|
@@ -48,15 +51,13 @@ Stress Maps & Meter — top-3 fit scores per line
|
|
|
48
51
|
|
|
49
52
|
## Installation
|
|
50
53
|
|
|
51
|
-
Requires **Node.js ≥ 20** (≥ 22 recommended — one optional
|
|
52
|
-
dependency-tree repair, needs Node's `require(esm)` support and degrades
|
|
53
|
-
gracefully without it).
|
|
54
|
+
Requires **Node.js ≥ 20** (≥ 22 recommended — one optional stage, the dependency-tree repair, uses Node's `require(esm)` support and simply switches itself off on older runtimes).
|
|
54
55
|
|
|
55
56
|
```bash
|
|
56
57
|
# As a global CLI tool
|
|
57
58
|
npm install -g calliope-ts
|
|
58
59
|
|
|
59
|
-
# Or as a library
|
|
60
|
+
# Or as a library inside your own project
|
|
60
61
|
npm install calliope-ts
|
|
61
62
|
```
|
|
62
63
|
|
|
@@ -79,14 +80,14 @@ calliope-ts "Shall I compare thee to a summer's day?"
|
|
|
79
80
|
# 2. Scan a whole poem from a file (blank lines separate stanzas)
|
|
80
81
|
calliope-ts poem.txt
|
|
81
82
|
|
|
82
|
-
# 3.
|
|
83
|
-
#
|
|
84
|
-
|
|
83
|
+
# 3. Reading view — the poem itself, syllables tinted by stress
|
|
84
|
+
# (the nicest way to read a whole poem; first option in the CLI;
|
|
85
|
+
or add -r or --reading)
|
|
85
86
|
|
|
86
87
|
# 4. Pipe text in
|
|
87
88
|
cat poem.txt | calliope-ts --reading
|
|
88
89
|
|
|
89
|
-
# 5. Interactive menu (run
|
|
90
|
+
# 5. Interactive menu (run without typing arguments in a terminal each time)
|
|
90
91
|
calliope-ts
|
|
91
92
|
```
|
|
92
93
|
|
|
@@ -119,31 +120,34 @@ core representation everything else is built on):
|
|
|
119
120
|
| `s` | strong | primary stresses, phrase peaks, line-final nuclei |
|
|
120
121
|
|
|
121
122
|
A line's **stress map** is simply its syllables in order, e.g.
|
|
122
|
-
`xs|wxm|wxs|xws` — with
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
`xs|wxm|wxs|xws` — with:
|
|
124
|
+
`|` marking foot boundaries;
|
|
125
|
+
`‖` a strong caesura (aka syntactic pause) at major phonological/clausal or punctuation breaks;
|
|
126
|
+
`¦` a lighter phrase break;
|
|
127
|
+
`-` a "silent beat" inserted to neutralize a stress clash wherever two strongly-stressed syllables would otherwise collide, it forces the second to await its turn.
|
|
125
128
|
|
|
126
129
|
### Meter lines
|
|
127
130
|
|
|
131
|
+
In the reading view each verse-line gets a single summary line.
|
|
132
|
+
Reading one such line left to right:
|
|
133
|
+
|
|
128
134
|
```
|
|
129
135
|
S1L2 m|-mww|sxx|mwm|s amphibrachic tetrameter (dact 1.21 · amph 1.17 ·
|
|
130
136
|
anap 1.16) ≈ continuity; standalone: dactylic tetrameter A(perfect)
|
|
131
137
|
```
|
|
132
138
|
|
|
133
|
-
- **`amphibrachic tetrameter`** — the line's meter: foot type +
|
|
139
|
+
- **`amphibrachic tetrameter`** — the line's meter: foot type + count.
|
|
134
140
|
- **`(dact 1.21 · amph 1.17 · anap 1.16)`** — the top three candidate meters
|
|
135
141
|
with their raw fit scores. The named meter need not be the numerically
|
|
136
142
|
first candidate: ties between sibling meters are resolved by principled
|
|
137
143
|
criteria (word integrity, caesura alignment, stanza context — see below).
|
|
138
144
|
- **`≈ continuity; standalone: dactylic tetrameter`** — this line, taken
|
|
139
|
-
alone, fits dactylic a hair better; but the stanza
|
|
140
|
-
line fits amphibrachic nearly as well, so the continuity reading is
|
|
141
|
-
promoted to the base reading and the standalone result is kept as a note.
|
|
145
|
+
alone, fits dactylic a hair better; but if most of the other lines in the same stanza are amphibrachic and the line fits amphibrachic nearly as well as it fits dactylic, the primary reading is promoted to the stanza-dominant base meter, while the line-prevalent meter is co-registered as the "standalone" top fit via an added note.
|
|
142
146
|
- **`↔ aligns w/ stanza …`** — a weaker version of the same: the line stays
|
|
143
147
|
with its own meter but is flagged as compatible with the stanza's.
|
|
144
|
-
- **`♪ 4-beat accentual`** / `♪ 3-ictus dolnik` — a *rhythm note*: the stanza
|
|
145
|
-
|
|
146
|
-
with varying syllable counts (see "Beyond classical meters" below).
|
|
148
|
+
- **`♪ 4-beat accentual`** / `♪ 3-ictus dolnik` — a *rhythm note*: if the stanza
|
|
149
|
+
does not appear to be accentual-syllabic, but keeps a constant count of strong beats
|
|
150
|
+
with varying syllable counts (see "Beyond classical meters" below), it is marked as acentual.
|
|
147
151
|
- **`A`, `B(perfect)`, `·`** — the rhyme-scheme letter for the line's end
|
|
148
152
|
word, with the rhyme type when the line rhymes with an earlier one
|
|
149
153
|
(`perfect`, `rich`, `family`, `assonant`, `consonant`, `augmented`,
|
|
@@ -151,7 +155,7 @@ S1L2 m|-mww|sxx|mwm|s amphibrachic tetrameter (dact 1.21 · amph 1.17 ·
|
|
|
151
155
|
- **`❡ ballad stanza (ABCB, 4·3)`** — a stanza-level *form* verdict (shown in
|
|
152
156
|
the stanza header): ballad stanza, blank verse, couplet, limerick,
|
|
153
157
|
Shakespearean/Petrarchan sonnet, terza rima, etc.
|
|
154
|
-
- **Certainty / fit %** (detailed view) — the share of the line
|
|
158
|
+
- **Certainty / fit %** (detailed view) — the share of the line realized by
|
|
155
159
|
clean, unsubstituted feet, tempered by phrase-edge agreement. A perfectly
|
|
156
160
|
regular line reads 100%; real verse usually lands between 50 and 90.
|
|
157
161
|
|
|
@@ -159,99 +163,52 @@ S1L2 m|-mww|sxx|mwm|s amphibrachic tetrameter (dact 1.21 · amph 1.17 ·
|
|
|
159
163
|
|
|
160
164
|
## How it works: the scansion pipeline
|
|
161
165
|
|
|
162
|
-
Most automatic scanners pattern-match syllable counts against meter templates.
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
word
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
*
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
and archaic *-'d* / *-'st* forms (*fix'd*, *stopp'st*) keep their elided
|
|
189
|
-
syllable counts.
|
|
190
|
-
|
|
191
|
-
**3. The prosodic hierarchy (how the line phrases).**
|
|
192
|
-
Words are grouped the way speech groups them: each content word attracts its
|
|
193
|
-
function-word satellites into a **clitic group** (CP); clitic groups join into
|
|
194
|
-
**phonological phrases** (PP) along the syntactic dependencies; phrases join
|
|
195
|
-
into **intonational units** (IU) bounded by major punctuation. Crucially, the
|
|
196
|
-
**line is the scansion domain**: a verse line containing several grammatical
|
|
197
|
-
sentences is still ONE metrical unit (the internal full stops become strong
|
|
198
|
-
caesurae), and quotation marks are *not* treated as prosodic breaks.
|
|
166
|
+
Most automatic scanners pattern-match syllable counts against meter templates, or "from the outside in": count syllables, then match the count and a guessed stress pattern against a list of meter templates. Calliope TS instead follows the **phonological scansion** method. Phonological scansion works "from the inside out": it derives the line's normative spoken prominence first, then fits meters to it.
|
|
167
|
+
|
|
168
|
+
Two simple principles carry a lot of the work here, both articulated in Bruce Hayes and Abigail Kaun's study of how words are set to music (*The Role of Phonological Phrasing in Sung and Chanted Verse*, 1996):
|
|
169
|
+
|
|
170
|
+
1. **The ends of phonological units is what matters the most (in English, at least).**: Stresses at the *right edges* of prosodic units are more reliable metrical evidence. Generative metrics has long summarized this as **"beginnings free, endings strict."**
|
|
171
|
+
|
|
172
|
+
2. **Bigger units matter more.**
|
|
173
|
+
So, a stress at the end of a whole intonational unit is stronger evidence than one at the end of a phonological phrase, which is stronger evidence than the distribution of a given clitic phrase, which is more significant than the pattern of an individual word (however longer polysyllabic words may likewise hold more "weight" here). And in English as a whole, the pattern generally leans to the right (with certain significant exceptions, like numerous classes of compound expressions). But this bias as such aligns with the infamous iambic inclination.
|
|
174
|
+
|
|
175
|
+
Bruce Hayes himself (2005) and Gareth McAleese (2007/2008) built the first faithful algorithmic/computational implementations of a verse scansion procedure around those two ideas. Calliope TS re-implements that architecture on a modern JavaScript/TypeScript stack, while attempting to push it into distinctive directions. Among these: deliberately looser pre-attunement to standard iamb-centered English canons (while preserving and seeking to refine English phonological alignment accuracy); better accommodation of World poetry in meter-matching English translation (in other words, English canon levels of iambic base meter predominance should not be pre-assumed for Englished World (aka "Worldish") poetry canons); likewise, the mechanics of Calliope TS proceed from a choice not to essentialize ternary meters as inherently rare solely from English canon distributions (statistical fatalism); we, furthermore, aim to extend scansion accuracy over accentual verse forms; and, beyond scansion itself, hope to gradually incorporate nuanced and diversified identification of a broad range of poetic forms and devices (currently supported: rhyme types; in the future: alliterations, anaphoras, trope/cliche identifications, and more).
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
The current pipeline has eight stages.
|
|
179
|
+
|
|
180
|
+
**1. Grammatical parsing.**
|
|
181
|
+
The line is tokenized, part-of-speech tagged, and dependency-parsed — that is, the engine works out which word grammatically governs which (subject of what verb, object of what preposition) — using the FinNLP family of libraries (`lexed`, `en-pos`, `en-parse`). Two correction layers sit inside this stage,
|
|
182
|
+
First, because poetry tends to break part-of-speech and grammatical dependency taggers in predictable ways, a *tag-repair* pass fixes systematic errors before the dependency tree is built (this appropriately accounts for rare exotics and awkward/shifty commonplaces alike: from archaic forms like *thou/thy/doth/wherefore*, to the pronoun *I*, to perfect-tense participles like *had quit*).
|
|
183
|
+
Then we leverage a *tree-repair* pass (using the [depedits](https://www.npmjs.com/package/depedits) rule engine, our TypeScript port of the DepEdit library, originally in Python), which fixes systematic phrasal role attachment errors (e.g. noun compounds parsed as double objects, and the like). Hyphenated compounds and contractions (like *we'll*, *don't*, archaic *fix'd*, etc) are re-merged into single metrical words.
|
|
184
|
+
|
|
185
|
+
**2. Lexical stress.**
|
|
186
|
+
Every word is looked up for its pronunciation — syllable count, primary/secondary/unstressed pattern, syllable weights, vowel quantities — via our [nounsing-pro](https://www.npmjs.com/package/nounsing-pro) NLP toolkit, build over a full-scope CMU dictionary augmented with phonological and morphological data: syllable count, stress pattern (primary / secondary / unstressed), syllable weights, consonant types, morphological complexity and
|
|
187
|
+
vowel quantities. Words not in the dictionary go through a morphological fallback (strip a productive suffix, look up the stem, restore) coupled with a quantity-sensitive English Stress Rule. Poetic elisions are honored: *heav'n* is parsed as one syllable, so is *o'er*, *th'expense* as two, *'tis/'twas* reduces, while archaic *-'d* / *-'st* forms (*fix'd*, *stopp'st*) retain their elided syllable counts.
|
|
188
|
+
|
|
189
|
+
**3. The prosodic hierarchy.**
|
|
190
|
+
Words are grouped the way speech groups them, in the nested structure linguists call the prosodic hierarchy (Selkirk 1978; Hayes 1989):
|
|
191
|
+
Each content word attracts its function-word satellites into a **clitic group** (CP); clitic groups are joined into **phonological phrases** (PP) in accordance with syntactic inter-dependencies and primacies parsed earlier; phrases are organized into **intonational units** (IU) bounded by major punctuation or/and delineation. Crucially, the **line is the scansion domain**: a verse line containing several grammatical sentences is still parsed as a singular metrical unit (the internal full stops are treated as strong caesurae).
|
|
199
192
|
|
|
200
193
|
**4. Phrase-level stress rules.**
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
of
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
Following McAleese's central insight, the stresses at the *right edges* of
|
|
210
|
-
prosodic units are the reliable ones — speakers may start a phrase loosely
|
|
211
|
-
but they land its ending. Each unit contributes its right-edge "key stress"
|
|
212
|
-
with a weight (intonational unit > phrase > long word > clitic group), and
|
|
213
|
-
meters that place beats on those key stresses are rewarded.
|
|
214
|
-
|
|
215
|
-
**6. Meter fitting (dynamic programming).**
|
|
216
|
-
For each of seven candidate meters — iambic, trochaic, anapestic, dactylic,
|
|
217
|
-
**amphibrachic**, bacchic, spondaic — the engine finds the optimal division of
|
|
218
|
-
the line into feet, using that meter's full inventory of legitimate
|
|
219
|
-
variations: inversions at line-start or after a caesura, pyrrhic and spondaic
|
|
220
|
-
substitutions, catalexis (truncated final foot), anacrusis (extrametrical
|
|
221
|
-
upbeat), feminine endings, acephalous openings. Each syllable is scored
|
|
222
|
-
against the metrical position it lands in (a strong syllable in a beat slot
|
|
223
|
-
is ideal; a reduced clitic forced onto a beat is the cardinal violation), and
|
|
224
|
-
an **Attridge promotion** rule lets a weak syllable carry a beat when it is
|
|
225
|
-
flanked by even weaker ones (*"happens to BE a French poet"*) — which is how
|
|
226
|
-
real readers actually deliver such lines.
|
|
194
|
+
Several well-established rules of *English stress* mediate dictionary-derived lexical stresses within the consolidated phonological/phrasal context: primarily, the **compound stress rule** (left element generally stressed, *CITY hall*, with various exceptions), the **nuclear stress rule** (the last content word of an intonational unit receives a stress boost), and a set of **clash resolutions** (two adjacent strong syllables cannot both keep full prominence — one yields, chosen by syntactic direction). The result is mapped onto the five-tier `x w n m s` scale.
|
|
195
|
+
|
|
196
|
+
**5. Key stresses.**
|
|
197
|
+
Following generative metrics, the stresses at the *right edges* of prosodic units are treated as more reliable — speakers may start a phrase loosely but they land its ending. Each unit contributes its right-edge "key stress" with a weight (intonational unit > phrase > long word / clitic group > short word ), and meters that place beats on those key stresses are promoted.
|
|
198
|
+
|
|
199
|
+
**6. Meter fitting.**
|
|
200
|
+
For each of seven candidate metrical foot types — iambic, trochaic, anapestic, dactylic, amphibrachic, bacchic, spondaic — the engine finds the optimal division of the line into feet, using that meter's inventory of most plausible variations/divergences: inversions at line-start or after a caesura, pyrrhic and spondaic foot substitutions, catalexis (truncated final foot), anacrusis (extrametrical upbeat), feminine endings, acephalous openings, and so forth. Each syllable is scored against the metrical position it lands in (a strong syllable in a beat slot
|
|
201
|
+
is ideal; while a reduced clitic forced onto a beat is perhaps the heaviest cardinal violation). An additional **promotion** rule (drawn from Derik Attridge) lets a weak syllable carry a beat when it is flanked by even weaker ones (*"happens to BE a French poet"*).
|
|
227
202
|
|
|
228
203
|
**7. Arbitration and context.**
|
|
229
|
-
The winning meter is not
|
|
230
|
-
- **Ternary siblings** (anapest / amphibrach / dactyl)
|
|
231
|
-
|
|
232
|
-
division avoids splitting words across feet and aligns with the line's
|
|
233
|
-
pauses, not by score noise.
|
|
234
|
-
- **Stanza consensus**: each stanza's dominant meter is identified (for
|
|
235
|
-
ternary verse, by the stanza's *anacrusis profile* — how many slack
|
|
236
|
-
syllables open the lines, following Russian metrics). A line that fits the
|
|
237
|
-
dominant meter nearly as well as its own standalone winner **adopts the
|
|
238
|
-
dominant meter** as its base reading, with the standalone reading preserved
|
|
239
|
-
as a note. Metrical continuity outranks a hair of fit score, exactly as it
|
|
240
|
-
does for a human scander.
|
|
204
|
+
The winning meter is not simply the top raw score:
|
|
205
|
+
- **Ternary siblings** (anapest / amphibrach / dactyl meters) may sometimes fit a line via an *identical* distribution of beats — the difference being only where one might draw the foot boundaries. In such a situation, the base meter determination is decided by which foot division avoids slicing through words and best aligns with the line's pauses.
|
|
206
|
+
- **Stanza consensus**: each stanza's dominant meter is identified. An additional consideration is taken into account for ternary meters at this point: *anacrusis profile* — how many unstressed syllables are found at the beginning of a given line, and the stanza as a whole (a sensible heuristic suggested by Russian metrics).
|
|
241
207
|
|
|
242
208
|
**8. Beyond classical meters.**
|
|
243
|
-
If a stanza's syllable counts vary while its strong-beat
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
between beats) or **taktovik** (1–3), the categories of Russian verse theory
|
|
247
|
-
that also fit much English song, ballad, and modernist verse. Free verse is
|
|
248
|
-
recognised both line-by-line and stanza-wide. Independently, a **rhyme
|
|
249
|
-
layer** classifies every line-end pair (perfect / rich / family / slant /
|
|
250
|
-
eye / … rhymes, masculine / feminine / dactylic), detects the rhyme scheme,
|
|
251
|
-
and a **form layer** names what the stanzas amount to: ballad stanza (rhyme
|
|
252
|
-
scheme *and* alternating 4·3 beats), blank verse (unrhymed iambic
|
|
253
|
-
pentameter), couplets, quatrains, limerick, rhyme royal, Shakespearean or
|
|
254
|
-
Petrarchan sonnet, terza rima.
|
|
209
|
+
If a stanza's syllable counts vary significantly while its strong-beat count stays constant, the stanza is read as **accentual verse** , rather than classically footed accentual-syllabic (aka syllabotonic), and labeled as (`n-beat accentual`).
|
|
210
|
+
|
|
211
|
+
Alongside this, a **rhyme layer** classifies every line-end pair (perfect / rich / family / slant /eye / … rhymes, in masculine / feminine / dactylic shapes), detects the poem's rhyme scheme (if discernible), as well as its apparent **poetic form** (if documented by us). For now, the range of supported forms is very limited and the mechanics of discernment leave much to be desired. As of this version, the engine may (when particularly inspired) identify a ballad stanza, blank verse (unrhymed iambic pentameter), couplets, quatrains, limericks, rhyme royal, Shakespearean or Petrarchan sonnets, and Dante-style terza rima.
|
|
255
212
|
|
|
256
213
|
---
|
|
257
214
|
|
|
@@ -353,20 +310,15 @@ interface PhonologicalScansionDetail {
|
|
|
353
310
|
}
|
|
354
311
|
```
|
|
355
312
|
|
|
356
|
-
Lower-level functions (`parseDocument`, `assignLexicalStress`,
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
individual pipeline stages. The optional Scandroid comparison engines
|
|
360
|
-
(Charles Hartman's "Corral the Weird" and "Maximize the Normal") are exported
|
|
361
|
-
from `calliope-ts/dist/scandroid.js`.
|
|
313
|
+
Lower-level functions (`parseDocument`, `assignLexicalStress`, `buildPhonologicalHierarchy`, `scoreMeters`, …) are exported from their modules under `calliope-ts/dist/*` for users who want to run or modify individual pipeline stages.
|
|
314
|
+
|
|
315
|
+
The optional Scandroid comparison engines (Charles Hartman's "Corral the Weird" and "Maximize the Normal") are exported from `calliope-ts/dist/scandroid.js`.
|
|
362
316
|
|
|
363
317
|
---
|
|
364
318
|
|
|
365
319
|
## Examples
|
|
366
320
|
|
|
367
|
-
**A Shakespeare sonnet** (`calliope-ts --reading sonnet130.txt`) — every line
|
|
368
|
-
identified as iambic pentameter; the scheme letters spell ABAB CDCD EFEF GG;
|
|
369
|
-
the stanza header reads `❡ Shakespearean Sonnet`.
|
|
321
|
+
**A Shakespeare sonnet** (`calliope-ts --reading sonnet130.txt`) — every line identified as iambic pentameter; the scheme letters spell ABAB CDCD EFEF GG; the stanza header reads `❡ Shakespearean Sonnet`.
|
|
370
322
|
|
|
371
323
|
**A ballad quatrain:**
|
|
372
324
|
|
|
@@ -377,82 +329,42 @@ And he stoppeth one of three.
|
|
|
377
329
|
Now wherefore stopp'st thou me?
|
|
378
330
|
```
|
|
379
331
|
|
|
380
|
-
→ lines of iambic tetrameter / trimeter, scheme `·A·A`, and the form verdict
|
|
381
|
-
`❡ ballad stanza (ABCB, 4·3)` — the rhyme scheme *and* the alternating
|
|
382
|
-
4-beat/3-beat design both check out.
|
|
332
|
+
→ lines of iambic tetrameter / trimeter, scheme `·A·A`, and the form verdict `❡ ballad stanza (ABCB, 4·3)` — the rhyme scheme *and* the alternating 4-beat/3-beat design both check out.
|
|
383
333
|
|
|
384
|
-
**Accentual verse** (Wyatt, *They flee from me*) — no classical meter
|
|
385
|
-
dominates and syllable counts vary, but every line carries four strong beats:
|
|
386
|
-
each line is annotated `♪ 4-beat accentual`.
|
|
334
|
+
**Accentual verse** (Wyatt, *They flee from me*) — no classical meter dominates and syllable counts vary, but every line carries four strong beats: each line is annotated `♪ 4-beat accentual`.
|
|
387
335
|
|
|
388
|
-
**Amphibrachic verse** (Nabokov wrote his poem "Exile" as a demonstration
|
|
389
|
-
of English amphibrachs — the `x S x` foot): stanza consensus reads the poem's
|
|
390
|
-
constant one-syllable anacrusis as amphibrachic, names near-tie lines
|
|
391
|
-
accordingly with `≈ continuity` notes, and reports the `aabccb` rhyme
|
|
392
|
-
envelope.
|
|
336
|
+
**Amphibrachic verse** (Nabokov wrote his poem "Exile" as a demonstration of English amphibrachs — the `x S x` foot): stanza consensus reads the poem's constant one-syllable anacrusis as amphibrachic, names near-tie lines accordingly with `≈ continuity` notes, and reports the `aabccb` rhyme envelope.
|
|
393
337
|
|
|
394
|
-
**Blank verse** (Frost, *Mending Wall*) — unrhymed lines, dominant iambic
|
|
395
|
-
pentameter: `❡ blank verse`.
|
|
338
|
+
**Blank verse** (Frost, *Mending Wall*) — unrhymed lines, dominant iambic pentameter: `❡ blank verse`.
|
|
396
339
|
|
|
397
340
|
---
|
|
398
341
|
|
|
399
342
|
## Background and lineage
|
|
400
343
|
|
|
401
|
-
|
|
402
|
-
McAleese's *Calliope*, developed for his M.Sc. at the Open University
|
|
403
|
-
(*"Improving Scansion with Syntax: an Investigation into the Effectiveness of
|
|
404
|
-
a Syntactic Analysis of Poetry by Computer using Phonological Scansion
|
|
405
|
-
Theory"*, Technical Report 2007/26, submitted 2008). McAleese's central idea:
|
|
406
|
-
scansion should be computed from a line's *phonology* — the prosodic
|
|
407
|
-
hierarchy and the stresses at the right edges of its units ("beginnings free,
|
|
408
|
-
endings strict") — rather than from orthographic pattern-matching. Calliope TS
|
|
409
|
-
re-implements that architecture on a modern Node.js stack and extends it.
|
|
410
|
-
|
|
411
|
-
**Scandroid (1996/2005).** Charles O. Hartman's Scandroid, a classic
|
|
412
|
-
foot-by-foot scanner (GNU GPL), is included as an optional comparison engine:
|
|
413
|
-
its "Corral the Weird" and "Maximize the Normal" algorithms can be run side
|
|
414
|
-
by side with the phonological scansion.
|
|
415
|
-
|
|
416
|
-
**Extensions beyond McAleese.** The five-tier stress gradient with a
|
|
417
|
-
zero-provision level; a unified dynamic-programming meter fitter with full
|
|
418
|
-
substitution inventories (and first-class **amphibrach** support); beat
|
|
419
|
-
promotion after Derek Attridge's beat/offbeat analysis; the dolnik/taktovik
|
|
420
|
-
classification from Russian verse theory (Gasparov); stanza-consensus and
|
|
421
|
-
anacrusis-profile naming; the rhyme and form layers; and the parse-correction
|
|
422
|
-
layers for verse-specific tagging.
|
|
423
|
-
|
|
424
|
-
**Data.** Lexical phonology comes from the augmented CMU dictionary published
|
|
425
|
-
as `nounsing-pro` (syllabification, stress transcription, syllable structure,
|
|
426
|
-
morphological stress classes). Parsing uses the FinNLP family. Dependency
|
|
427
|
-
repairs use `depedits`, a TypeScript port of the DepEdit rule engine.
|
|
428
|
-
|
|
429
|
-
Calliope TS is developed by **Aleksey Calvin** /
|
|
430
|
-
[SilverAgePoets.com](https://www.SilverAgePoets.com), with particular care for
|
|
431
|
-
verse traditions that standard English-centric tools marginalize: ternary
|
|
432
|
-
meters, Russian Silver-Age forms in translation, song lyrics, and accentual
|
|
433
|
-
verse.
|
|
344
|
+
The method implemented here substantially follows the example set by Gareth McAleese's *Calliope* (2007.2008), developed for his M.Sc. at the Open University (*"Improving Scansion with Syntax: an Investigation into the Effectiveness of a Syntactic Analysis of Poetry by Computer using Phonological Scansion Theory"*, Technical Report 2007/26, submitted 2008). For it, McAleese devises a computational scansion framework substantially grounded in the phonological scansion methodologies of UCLA's renowned linguist and phonologist **Bruce Hayes**, developed by him across the 1980s, 90s, and 2000s — most relevantly in *Extrametricality and English Stress* (1982), *The Phonology of Rhythm in English* (1984), *The Prosodic Hierarchy in Meter* (1989), the *Metrical Stress Theory* (1995), and *The Role of Phonological Phrasing in Sung and Chanted Verse* with Abigail Kaun (1996). Hayes in turn built on the groundwork laid by Halle & Chomsky, Liberman & Prince, and Kiparsky (some of the seminal Generative Metrics, Optimality Theory, and related domains. Most pertinent to approach taken up by McAleese (as well as ourselves presently) is Hayes's work from the early 1990s onward, in which he drew increasingly on the nascent **Optimality Theory** (OT) (much credit to Prince & Smolensky, whose *Optimality Theory: Constraint Interaction in Generative Grammar*, 1993/2002, much enriched the empirical toolkit of generative grammar-adjacent theory domains). In the 2000s Hayes would contribute to placing OT onto the more precise rails of **MaxEnt** (Maximum Entropy) methods. McAleese's work, however, preceded this turn. Backing up somewhat to peruse the field, it is worth acknowledging the remarkable breadth of synthesis McAleese draws on, which beyond those above-named, harnesses the influence of Paul Kiparsky (e.g. *The Rhythmic Structure of English Verse*, 1977), Kristin Hanson (*A Parametric Theory of Poetic Meter*, 1996), the prosodic-phrasing and stress-shift related work of Elisabeth Selkirk (1978), as well as studies by Richard Cureton (1992), Peter Groves (1998), and many others (see McAleese's paper at https://oro.open.ac.uk/90197/ for a full bibliography).
|
|
434
345
|
|
|
435
|
-
|
|
346
|
+
| Source | What it grounds in Calliope |
|
|
347
|
+
|---|---|
|
|
348
|
+
| Hayes & Kaun (1996) | the core method: right-edge key stresses, weighted by prosodic-unit size (stage 5) |
|
|
349
|
+
| Hayes (1989), *Prosodic Hierarchy in Meter* | the CP → PP → IU phrasing (stage 3) |
|
|
350
|
+
| Hayes (1982), *Extrametricality* | extrametrical syllables and the OOV English Stress Rule (stage 2) |
|
|
351
|
+
| Chomsky & Halle (1968); Liberman & Prince (1977); Hayes (1984) | the lexical and phrasal stress rules — compound, nuclear, clash (stage 4) |
|
|
352
|
+
| Prince & Smolensky (1993/2002); Hayes et al., MaxEnt | the constraint-based, weighted meter fitter (stages 5–7) |
|
|
353
|
+
| Selkirk (1978) | the phonological phrase as a prosodic constituent (stage 3) |
|
|
354
|
+
| Attridge, *The Rhythms of English Poetry* / *The Rhythms of the English Dolnik* | beat/offbeat promotion (stage 6) and the dolnik (stage 8) |
|
|
355
|
+
| Gasparov, *A History of European Versification* | the dolnik / taktovik / accentual taxonomy (stage 8) |
|
|
356
|
+
| McAleese (2008) | the core outline for the scansion procedure, with further solutions drawn by us from Hayes 2008 & Prosodic (MaxEnt weighing), as well as original extrapolations |
|
|
436
357
|
|
|
437
|
-
|
|
358
|
+
**Scandroid (1996/2005).** Charles O. Hartman's Scandroid, a classic foot-by-foot scanner (GNU GPL), is included as an optional comparison engine: its "Corral the Weird" and "Maximize the Normal" algorithms can be run side by side with the phonological scansion.
|
|
438
359
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
and dolnik verse get a real classification instead of a "free verse" shrug.
|
|
443
|
-
Whole-poem context (stanza consensus, rhyme, form) informs line verdicts the
|
|
444
|
-
way it does for human readers.
|
|
360
|
+
Calliope TS is developed by **Aleksey Calvin** / [SilverAgePoets.com](https://www.SilverAgePoets.com)
|
|
361
|
+
|
|
362
|
+
---
|
|
445
363
|
|
|
446
364
|
**Limitations to know about.**
|
|
447
|
-
- English only (the dictionary and parser are English; other languages will
|
|
448
|
-
|
|
449
|
-
- Stress-doublet words (*rebel*, *content*, names like *Hugo*) are read with
|
|
450
|
-
their dictionary stress; a rhyme-driven correction is on the roadmap.
|
|
365
|
+
- English only (the dictionary and parser are English; other languages will produce nonsense rather than errors).
|
|
366
|
+
- Stress-doublet words (*rebel*, *content*, names like *Hugo*) are read with their dictionary stress; a correction is on the roadmap.
|
|
451
367
|
- Rare or foreign proper names fall back to rule-based stress guesses.
|
|
452
|
-
- The form layer recognises the common stanza forms; it does not yet attempt
|
|
453
|
-
villanelle/pantoum repetition checking or syllabic forms.
|
|
454
|
-
- Meter identification on single lines without context is genuinely harder
|
|
455
|
-
than with stanza context — when possible, scan whole poems.
|
|
456
368
|
|
|
457
369
|
---
|
|
458
370
|
|
|
@@ -468,18 +380,12 @@ node trials/mcaleese_benchmark.mjs # McAleese's own trial poems + expert keys
|
|
|
468
380
|
node trials/corpus_benchmark.mjs # litlab / prosodic / epg64 meter corpora
|
|
469
381
|
```
|
|
470
382
|
|
|
471
|
-
The repository's `AGENTS.md` carries a dated engineering log of every
|
|
472
|
-
substantive change with its rationale and verification.
|
|
473
|
-
|
|
474
383
|
---
|
|
475
384
|
|
|
476
385
|
## License and credits
|
|
477
386
|
|
|
478
387
|
Apache-2.0. © Aleksey Calvin Tsukanov / SilverAgePoets.com.
|
|
388
|
+
My email: alekseycalvin@gmail.com
|
|
479
389
|
|
|
480
|
-
Methodological debts: Gareth McAleese (Calliope, the phonological scansion
|
|
481
|
-
|
|
482
|
-
rhythm and the English dolnik); M. L. Gasparov (dolnik/taktovik taxonomy);
|
|
483
|
-
Bruce Hayes, Paul Kiparsky, and the generative-metrics tradition (stress
|
|
484
|
-
theory, syllable quantity, extrametricality); the CMU Pronouncing Dictionary
|
|
485
|
-
and its `nounsing-pro` augmentation.
|
|
390
|
+
Methodological and conceptual debts: Bruce Hayes (1982/1984/1995/1996 with Abigail Kaun/2005) (the phonological scansion procedure as such, extrametricality insights, text-setting methodologies, MaxEnt OT, and who knows what else), Gareth McAleese (for a single 2008 paper, for detailing the original Calliope implementation, for exhibiting a remarkable field-spanning purview, an uncanny industriousness, and an uncommon – perhaps a tad obsessive – dedication to testing, refining, fusing, and extending all sorts of methodologies in a single-minded pursuit of bringing constraint-based computational scansion far beyond the best documented practices and results at that time; and for so obviously succeeding, if only to seemingly vanish from the field as abruptly and unreservedly as he entered and absorbed it*); Charles O. Hartman (Scandroid); Claire Moore Cantwell (morphological/phonological tagging algorithms), Austin Pursley (implementing finer-grained rhyme-matching heuristics over a corpus), Allison Parrish (Pronouncing-py and being a real life computational poet hero), Derek Attridge (beat/offbeat rhythm theory and insightful writings on the English dolnik); M. L. Gasparov (dolnik/taktovik taxonomy); the compilers of the CMU Pronouncing Dictionary; the makers of Prosodic (Heuser et al, for establishing an admirable state-of-the-art to compare against, differentiate from, and hopefully surpass in due time, in select ways), as well as broader generative-metrics, constraint-based metrics, and OT traditions, including Kiparsky, Prince & Smolensky, Groves, Blumenfeld, Lilja, Chomsky & Halle, Fabb & Halle (rule-based grid scansion theory), Einarsson (Metremic theory), Russom (Universalist metrics), K. M. Ryan (gradient syllable weight), big daddy Jakobson who had once roped the whole world with subtle strings and often hung out with Mayakovsky, and many others.
|
|
391
|
+
*Gareth McAleese: If you're reading this, please do email me!
|
package/dist/index.js
CHANGED
|
@@ -495,7 +495,15 @@ async function main() {
|
|
|
495
495
|
// No arguments: launch interactive REPL
|
|
496
496
|
await replMode();
|
|
497
497
|
}
|
|
498
|
-
|
|
498
|
+
let isMain = false;
|
|
499
|
+
if (process.argv[1]) {
|
|
500
|
+
try {
|
|
501
|
+
isMain = fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url));
|
|
502
|
+
}
|
|
503
|
+
catch {
|
|
504
|
+
isMain = false;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
499
507
|
if (isMain) {
|
|
500
508
|
main().catch(err => {
|
|
501
509
|
console.error(chalk.red('Fatal error:'), err);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "calliope-ts",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "A phonological poetry scansion framework in TypeScript, attempting to retrace and extend the
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "A phonological poetry scansion framework in TypeScript, attempting to retrace and extend the work of (primarily) Bruce Hayes (1996/2005), Gareth McAleese (2008), and the makers of Scandroid (2005)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
package/src/index.ts
CHANGED
|
@@ -532,10 +532,15 @@ async function main(): Promise<void> {
|
|
|
532
532
|
await replMode();
|
|
533
533
|
}
|
|
534
534
|
|
|
535
|
-
|
|
535
|
+
let isMain = false;
|
|
536
|
+
if (process.argv[1]) {
|
|
537
|
+
try {
|
|
538
|
+
isMain = fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url));
|
|
539
|
+
} catch { isMain = false; }
|
|
540
|
+
}
|
|
536
541
|
if (isMain) {
|
|
537
542
|
main().catch(err => {
|
|
538
543
|
console.error(chalk.red('Fatal error:'), err);
|
|
539
544
|
process.exit(1);
|
|
540
545
|
});
|
|
541
|
-
}
|
|
546
|
+
}
|