calliope-ts 0.0.5 → 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 +26 -5
- 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/vitest.config.d.ts +0 -2
- package/vitest.config.js +0 -14
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# Calliope TS 0.0.
|
|
1
|
+
# Calliope TS 0.0.7
|
|
2
2
|
|
|
3
|
-
**Calliope TS** is a phonological poetry-scansion toolkit for TypeScript / Node.js. Hand it a poem (or a single line), and it will tell you — and *show* you — how that verse actually moves: which syllables carry weight and how much, where the beats fall, what meter each line is in and how confidently, what rhymes with what, and what form the stanzas add up to.
|
|
3
|
+
**Calliope TS** is a bilingual (English & Russian) phonological poetry-scansion toolkit for TypeScript / Node.js. Hand it a poem (or a single line), and it will tell you — and *show* you — how that verse actually moves: which syllables carry weight and how much, where the beats fall, what meter each line is in and how confidently, what rhymes with what, and what form the stanzas add up to. The English pipeline is the primary and most fully documented engine; a parallel Russian pipeline (accentology + SynTagRus dependency parsing) is described under [Russian support](#russian-support).
|
|
4
4
|
|
|
5
5
|
The premise is that meter is something you *recover from pronunciation*, not something you stamp onto spelling. So Calliope reads a line roughly the way an attentive reader does: it first works out how the words would be said aloud — dictionary stress, the way phrases clump, where the voice peaks at a clause-end — and only then looks for the regular beat that saying settles into. This approach has a name in the linguistics literature, **phonological scansion**, and the bulk of this README is about what that means and how the toolkit puts it to work.
|
|
6
6
|
|
|
@@ -24,6 +24,7 @@ Stress Maps & Meter — top-3 candidate meters per line
|
|
|
24
24
|
## Contents
|
|
25
25
|
|
|
26
26
|
- [Installation](#installation)
|
|
27
|
+
- [Russian support](#russian-support)
|
|
27
28
|
- [Quick start (CLI)](#quick-start-cli)
|
|
28
29
|
- [Reading the output](#reading-the-output)
|
|
29
30
|
- [What "phonological scansion" means](#what-phonological-scansion-means)
|
|
@@ -61,6 +62,31 @@ npm test # vitest suite
|
|
|
61
62
|
|
|
62
63
|
---
|
|
63
64
|
|
|
65
|
+
## Russian support
|
|
66
|
+
|
|
67
|
+
Calliope's Russian pipeline (accentology + ёфикация + SynTagRus dependency parsing + Russian rhyme/meter analysis) runs on a set of large data assets — the SynTagRus UDPipe model plus stress dictionaries and neural-accentuator weights (~122 MB total). To keep the npm package small, **these assets are not bundled in the published package**; the English engine has no such dependency and works immediately after install.
|
|
68
|
+
|
|
69
|
+
The Russian assets are fetched **automatically on first Russian analysis** and written into the toolkit's `russian/data` folder, after which they are reused offline. You can also pre-fetch them explicitly:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx calliope-fetch-russian # from an installed package (global or local)
|
|
73
|
+
npm run fetch-russian # from a clone of this repository
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Configuration (all optional):
|
|
77
|
+
|
|
78
|
+
| Variable | Effect |
|
|
79
|
+
|---|---|
|
|
80
|
+
| `CALLIOPE_RUSSIAN_DATA` | Absolute path to a directory holding the Russian data files (skips download; use a pre-populated location). |
|
|
81
|
+
| `CALLIOPE_RUSSIAN_DATA_URL` | Base URL to fetch the assets from (defaults to the project's HuggingFace space). |
|
|
82
|
+
| `CALLIOPE_RUSSIAN_UDPIPE_MODEL` | Absolute path to just the `.udpipe` model file. |
|
|
83
|
+
|
|
84
|
+
Asset resolution searches, in order: `$CALLIOPE_RUSSIAN_DATA` → `data/` beside the running module (`src/russian/data` or `dist/russian/data`) → the sibling `src/russian/data` when running compiled code from `dist/` (the Docker / HuggingFace layout) → cwd-relative fallbacks. This means a source checkout, a compiled `dist/`, a Docker/HF deployment, and an npm-installed dependency all locate the data without any manual copying.
|
|
85
|
+
|
|
86
|
+
The Russian assets — dictionaries, methodology, and the SynTagRus UDPipe model — are redistributed here under the **MIT License** of Ilya Koziev's RussianPoetryScansionTool distribution, which is how they reached this project and which permits commercial use. A CC BY-NC-SA 4.0 reference file is also retained for the SynTagRus model, reflecting the terms under which it is distributed elsewhere; see [License and credits](#license-and-credits).
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
64
90
|
## Quick start (CLI)
|
|
65
91
|
|
|
66
92
|
```bash
|
|
@@ -98,6 +124,36 @@ There are two display modes:
|
|
|
98
124
|
|
|
99
125
|
---
|
|
100
126
|
|
|
127
|
+
## The web app (browser interface)
|
|
128
|
+
|
|
129
|
+
Everything the CLI prints — and several instruments beyond it — is also available
|
|
130
|
+
as a local web app with two visual skins (a bright-on-black **Terminal** display
|
|
131
|
+
and a parchment **Manuscript** display, switchable in ⚙ Settings):
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# from a clone or an installed package
|
|
135
|
+
npm run web # then open http://localhost:4321
|
|
136
|
+
|
|
137
|
+
# or, installed globally / via npx
|
|
138
|
+
calliope-web
|
|
139
|
+
PORT=8080 calliope-web # any port you like
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The app serves four instruments in one place:
|
|
143
|
+
|
|
144
|
+
| Tab | What it does |
|
|
145
|
+
|---|---|
|
|
146
|
+
| **Scansion** | The full reading view: every syllable tinted by stress, per-line meter tags with fit %, rhyme letters, a per-line detailed scan (feet, syllable anatomy, prosodic phrasing, key stresses, dependency arcs, Scandroid comparison), poem synopsis, phonopoetics, and rich word/syllable hover-profiles backed by Nounsing Pro. A **⧉ Copy stress map** button exports the whole poem's stress keys as CLI-style plain text. |
|
|
147
|
+
| **Rhyme Forge** | Word dossiers from the augmented lexicon: rhymes (strict or by syllable count), rime phonemes, metrical feet inside words, and Meter Match — cast a stress contour (`010`) and summon every word that carries it. |
|
|
148
|
+
| **Rewrites** | Nounsing Pro's transmutations: stress-skeleton, phoneme-echo, or rhyme-swap rewrites with POS-fidelity and word-frequency (Zipf floor) controls. |
|
|
149
|
+
| **Syntax** | The UDPipe dependency parse (with DepEdit-TS repairs) as arc diagrams and a glossed register, line-by-line or fused into one continuous text. |
|
|
150
|
+
|
|
151
|
+
The web app has **no extra dependencies** — it is a single zero-dependency Node
|
|
152
|
+
server (`webapp/server.mjs`) importing the same `dist/` build the CLI uses, plus
|
|
153
|
+
a vanilla-JS front-end. It only talks to `localhost`; nothing leaves your machine.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
101
157
|
## Reading the output
|
|
102
158
|
|
|
103
159
|
### The five stress levels
|
|
@@ -360,7 +416,7 @@ Calliope TS is developed by **Aleksey Calvin** / [SilverAgePoets.com](https://ww
|
|
|
360
416
|
---
|
|
361
417
|
|
|
362
418
|
**Limitations to know about.**
|
|
363
|
-
- English
|
|
419
|
+
- Two languages are supported: **English** (the primary engine — its dictionary and phonological rules) and **Russian** (a parallel accentological engine, see [Russian support](#russian-support)). Other languages will produce nonsense rather than errors, since the UDPipe backend parses them but the prosodic rules are language-specific.
|
|
364
420
|
- Stress-doublet words (*rebel*, *content*, names like *Hugo*) are read with their dictionary stress; a correction is on the roadmap.
|
|
365
421
|
- Rare or foreign proper names fall back to rule-based stress guesses.
|
|
366
422
|
|
|
@@ -400,4 +456,14 @@ Methodological and conceptual debts:
|
|
|
400
456
|
- 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),
|
|
401
457
|
- Milan Straka and the UDPipe project (for the robust neural parsing architecture now driving the core mechanics),
|
|
402
458
|
- 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. <br>
|
|
459
|
+
|
|
460
|
+
### The Russian pipeline
|
|
461
|
+
|
|
462
|
+
Calliope's Russian side owes an especial and foundational debt to **Ilya Koziev** and the **"Natural Language Processing in Russian"** group, whose [RussianPoetryScansionTool](https://github.com/Koziev/RussianPoetryScansionTool) served as the principal blueprint for much (though not all) of Calliope's Russian accentology and scansion methodology. Koziev's tool is generously distributed under the **MIT License** (which permits commercial use), and it is through it that the **SynTagRus UDPipe model** first came to this project. With deep respect and gratitude for their excellent work, any questions concerning the SynTagRus model or the underlying Russian-scansion methodology are most appropriately directed to Ilya Koziev and the NLP-in-Russian group.
|
|
463
|
+
|
|
464
|
+
The Russian data assets bundle their license files, mirrored in the project's [HuggingFace deployment](https://huggingface.co/spaces/AlekseyCalvin/cts/tree/main/src/russian/data):
|
|
465
|
+
- `src/russian/data/LICENSE.txt` — the **MIT** license of the RussianPoetryScansionTool / NLP-in-Russian group. This is the license under which the Russian assets used here — dictionaries, methodology, and the SynTagRus UDPipe model — were obtained and are redistributed. It permits commercial use.
|
|
466
|
+
- `src/russian/data/LICENSE_UDpipe_SynTagRus.txt` — a **CC BY-NC-SA 4.0** reference file, retained because SynTagRus/UDPipe models are distributed under those terms in other (upstream) channels. It is kept for transparency around that residual ambiguity; it does not govern the copy bundled here, which came via the MIT distribution above.
|
|
467
|
+
|
|
468
|
+
**A note on commercial use:** the Calliope-TS code (Apache-2.0) and the Russian assets as obtained here (MIT, via Koziev's distribution) permit commercial use. Because SynTagRus/UDPipe models are elsewhere distributed under CC BY-NC-SA 4.0 (non-commercial), anyone with strict compliance requirements may wish to independently confirm the provenance of their model file or substitute one whose licensing they control (point `CALLIOPE_RUSSIAN_UDPIPE_MODEL` at it). The **SynTagRus** treebank and the **UDPipe** framework are the work of Milan Straka et al. (Charles University / LINDAT); the Russian stress dictionaries and neural accentuator derive from the RussianPoetryScansionTool corpus.
|
|
403
469
|
*Gareth McAleese: If you're reading this, please do email me!
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bracketing.d.ts","sourceRoot":"","sources":["../../src/calliope/bracketing.ts"],"names":[],"mappings":"AAsCA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"bracketing.d.ts","sourceRoot":"","sources":["../../src/calliope/bracketing.ts"],"names":[],"mappings":"AAsCA,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAiJnD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAsG3D;AAyHD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,GAAG,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CA6BzE"}
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
// Output is per-word `phraseStress` (1 = strongest, punctuation = 0): the structural
|
|
37
37
|
// prominence the relativiser then reads PER PHONOLOGICAL PHRASE to place beats.
|
|
38
38
|
import { isPunct, isVerb, verbHasArgChild, isUnaccusativeOrPassive, isLowLocative, isFrameSetting, isLightNominalHead, } from './syntax.js';
|
|
39
|
+
import { compoundStressSide } from '../stress.js';
|
|
40
|
+
import { isMarginalModalUse, isSemiModalHaveTo } from './syntax.js';
|
|
39
41
|
const NOUN = /^(NN|NNS|NNP|NNPS)$/;
|
|
40
42
|
/** A head-dependent combination is a COMPOUND (Compound Stress Rule → primary LEFT)
|
|
41
43
|
* when the parse labels it NOMD, or when a common-/proper-noun modifier sits
|
|
@@ -46,6 +48,12 @@ function isCompound(dep, head) {
|
|
|
46
48
|
return true;
|
|
47
49
|
if ((dep.canonicalRel ?? '') === 'EXT')
|
|
48
50
|
return false;
|
|
51
|
+
// A POSSESSIVE noun modifier ("love's sway", "the belfies' bee-buzz" —
|
|
52
|
+
// nmod:poss, canonical CASE) is a syntactic PHRASE, not a compound: the NSR
|
|
53
|
+
// right-stresses it (love's SWAY). Without this the N+N surface-adjacency
|
|
54
|
+
// fallback swallowed possessives and fore-stressed them.
|
|
55
|
+
if ((dep.canonicalRel ?? '') === 'CASE')
|
|
56
|
+
return false;
|
|
49
57
|
return NOUN.test(dep.lexicalClass) && NOUN.test(head.lexicalClass)
|
|
50
58
|
&& Math.abs(dep.absoluteIndex - head.absoluteIndex) === 1;
|
|
51
59
|
}
|
|
@@ -88,9 +96,17 @@ function combineDecision(dep, head, children, words) {
|
|
|
88
96
|
const depBefore = dep.absoluteIndex < head.absoluteIndex;
|
|
89
97
|
const rightWins = depBefore ? 'head' : 'dep'; // NSR rightmost
|
|
90
98
|
const leftWins = depBefore ? 'dep' : 'head'; // CSR leftmost
|
|
91
|
-
// Compound Stress Rule first (handles the backwards-parsed N+N the tagger
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
// Compound Stress Rule first (handles the backwards-parsed N+N the tagger
|
|
100
|
+
// mislabels) — but through compoundStressSide, the SINGLE source of truth the
|
|
101
|
+
// lexical pass and scenario rules already share, so the curated right-stress
|
|
102
|
+
// heads hold here too: a temporal/product head keeps phrasal end-stress
|
|
103
|
+
// ("a wondrous Moscow DAY", "apple PIE") — those are N+N by POS yet phrasal
|
|
104
|
+
// by prosody, and fore-stressing them handed the φ nuclear to the modifier.
|
|
105
|
+
if (isCompound(dep, head)) {
|
|
106
|
+
const [w1, w2] = depBefore ? [dep, head] : [head, dep];
|
|
107
|
+
const side = compoundStressSide(w1.word, w1.lexicalClass, w2.word, w2.lexicalClass);
|
|
108
|
+
return side === 'right' ? rightWins : leftWins;
|
|
109
|
+
}
|
|
94
110
|
if (rel === 'EXT')
|
|
95
111
|
return rightWins; // proper-name head (New YORK)
|
|
96
112
|
switch (rel) {
|
|
@@ -126,8 +142,13 @@ function combineDecision(dep, head, children, words) {
|
|
|
126
142
|
case 'AMOD':
|
|
127
143
|
return depBefore ? 'match' : 'head'; // pre-modifier → own domain; post → subordinate
|
|
128
144
|
case 'ADVMOD':
|
|
129
|
-
|
|
130
|
-
|
|
145
|
+
// A degree/measure adverb premodifying an ADJECTIVE or another ADVERB
|
|
146
|
+
// integrates with its head — the head is the argument and keeps the
|
|
147
|
+
// accent ("very TALL", "far aWAY", "so MUCH"): adverb-over-adverb
|
|
148
|
+
// premodification is essentially always degree, unlike VP-adverbs,
|
|
149
|
+
// which keep their own accentual domain.
|
|
150
|
+
if (/^(JJ|RB)/.test(head.lexicalClass))
|
|
151
|
+
return 'head';
|
|
131
152
|
return depBefore ? 'match' : 'head'; // pre-adv → own domain; post → subordinate
|
|
132
153
|
case 'ACL':
|
|
133
154
|
case 'ADVCL':
|
|
@@ -260,6 +281,25 @@ export function computePhraseStress(sent) {
|
|
|
260
281
|
// Accent-level clash reduction (Wagner §6.4.1): runs BEFORE the syllable-level
|
|
261
282
|
// resolveStressClashes (relstress.ts) and is additional to it. See below.
|
|
262
283
|
reduceAccentClashes(words);
|
|
284
|
+
// Newman's generalization (1946; Wagner §2.4.1): "nuclear stress is perceived
|
|
285
|
+
// on the LAST of the grid marks that project highest." MATCH cycles
|
|
286
|
+
// legitimately co-designate sisters (coordination, the specifier restriction),
|
|
287
|
+
// so the cyclic levels can tie at the top — but the utterance NUCLEAR is
|
|
288
|
+
// unique: the final co-equal keeps the top rank and earlier co-equals are
|
|
289
|
+
// perceived one step lower (Mary(2) ate sweet ICE(1) cream — the subject
|
|
290
|
+
// matched the branching VP, yet the ear resolves rightward). One demotion
|
|
291
|
+
// step only; the rest of the gradient is untouched, so per-φ beat selection
|
|
292
|
+
// (which reads the LOCAL minimum) is unaffected.
|
|
293
|
+
{
|
|
294
|
+
const ranked = words.filter(w => (w.phraseStress || 0) > 0);
|
|
295
|
+
if (ranked.length > 1) {
|
|
296
|
+
const min = Math.min(...ranked.map(w => w.phraseStress));
|
|
297
|
+
const atMin = ranked.filter(w => w.phraseStress === min)
|
|
298
|
+
.sort((a, b) => a.absoluteIndex - b.absoluteIndex);
|
|
299
|
+
for (let i = 0; i < atMin.length - 1; i++)
|
|
300
|
+
atMin[i].phraseStress = min + 1;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
263
303
|
}
|
|
264
304
|
/** Accent-level clash reduction (Wagner §6.4.1). Operates on which CONTENT words
|
|
265
305
|
* bear a phrasal accent (a local prominence), NOT on syllable stress. A run of
|
|
@@ -330,6 +370,20 @@ function phiProjects(dep, head, children, words) {
|
|
|
330
370
|
switch (dep.canonicalRel ?? '') {
|
|
331
371
|
case 'CCOMP':
|
|
332
372
|
case 'XCOMP':
|
|
373
|
+
// A BARE-infinitive complement of a marginal modal ("dare [he] aspire",
|
|
374
|
+
// "dare seize the fire") is one prosodic verb cluster with its functor —
|
|
375
|
+
// the modal reduces and the infinitive holds the beat, so splitting them
|
|
376
|
+
// into separate φs would strand the reduced modal as a beat-less φ (which
|
|
377
|
+
// the anchor fallback would then wrongly re-promote).
|
|
378
|
+
if (isMarginalModalUse(head, words))
|
|
379
|
+
return false;
|
|
380
|
+
// The TO-infinitive complement of semi-modal "have to" is likewise one
|
|
381
|
+
// prosodic verb cluster with its reduced functor ("do I have to bless"):
|
|
382
|
+
// splitting φ between "have" and "to bless" strands the reduced "have",
|
|
383
|
+
// which the anchor fallback then wrongly re-promotes to the φ nuclear.
|
|
384
|
+
if (isSemiModalHaveTo(head, words))
|
|
385
|
+
return false;
|
|
386
|
+
return true;
|
|
333
387
|
case 'ADVCL':
|
|
334
388
|
case 'CONJ':
|
|
335
389
|
return true;
|
package/dist/calliope/deps.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { ClsSentence } from '../types.js';
|
|
2
|
-
/** Populate `canonicalRel` for every word, then apply label-only repairs.
|
|
2
|
+
/** Populate `canonicalRel` for every word, then apply label-only repairs.
|
|
3
|
+
* One HEAD-CHANGING repair (fixPossessiveApposition) runs first, before the
|
|
4
|
+
* labels are computed — it is the single exception to this file's additive
|
|
5
|
+
* contract, and like the POS corrections of Stage F1 it mutates only the
|
|
6
|
+
* Calliope view of the parse (Clio never runs this pass). */
|
|
3
7
|
export declare function normalizeDeps(sent: ClsSentence): void;
|
|
4
8
|
//# sourceMappingURL=deps.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../src/calliope/deps.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,WAAW,EAAW,MAAM,aAAa,CAAC;AAkBnD
|
|
1
|
+
{"version":3,"file":"deps.d.ts","sourceRoot":"","sources":["../../src/calliope/deps.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,WAAW,EAAW,MAAM,aAAa,CAAC;AAkBnD;;;;8DAI8D;AAC9D,wBAAgB,aAAa,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,CAKrD"}
|
package/dist/calliope/deps.js
CHANGED
|
@@ -26,18 +26,68 @@ function gov(w) {
|
|
|
26
26
|
function preHead(w, head) {
|
|
27
27
|
return w.absoluteIndex + 1 === head.absoluteIndex;
|
|
28
28
|
}
|
|
29
|
-
/** Populate `canonicalRel` for every word, then apply label-only repairs.
|
|
29
|
+
/** Populate `canonicalRel` for every word, then apply label-only repairs.
|
|
30
|
+
* One HEAD-CHANGING repair (fixPossessiveApposition) runs first, before the
|
|
31
|
+
* labels are computed — it is the single exception to this file's additive
|
|
32
|
+
* contract, and like the POS corrections of Stage F1 it mutates only the
|
|
33
|
+
* Calliope view of the parse (Clio never runs this pass). */
|
|
30
34
|
export function normalizeDeps(sent) {
|
|
35
|
+
fixPossessiveApposition(sent.words);
|
|
31
36
|
for (const w of sent.words)
|
|
32
37
|
w.canonicalRel = canonical(w);
|
|
33
38
|
fixDitransitive(sent.words);
|
|
34
39
|
inferPrenominalModifiers(sent.words);
|
|
35
40
|
}
|
|
36
|
-
/**
|
|
41
|
+
/** A POSSESSIVE-marked noun cannot head a following noun as its APPOSITION —
|
|
42
|
+
* a possessor is a dependent by definition. UDPipe parses Shakespeare's
|
|
43
|
+
* "…more red than her lips' red" with red₂ ←appos← lips' (and lips' ←obl←
|
|
44
|
+
* red₁), inverting the real structure ("the red of her lips"): the deep
|
|
45
|
+
* demotion this hands red₂ wrecked the line's iambic grid. Repair: the
|
|
46
|
+
* appositive noun takes over the possessive's relation and governor; the
|
|
47
|
+
* possessive becomes its poss dependent — which the NSR right-stresses
|
|
48
|
+
* (her lips' RED, like love's SWAY). Gated on the OVERT possessive mark
|
|
49
|
+
* (trailing 's or s'), so genuine appositions ("my brother the doctor")
|
|
50
|
+
* never match. */
|
|
51
|
+
function fixPossessiveApposition(words) {
|
|
52
|
+
for (const b of words) {
|
|
53
|
+
if (rawRel(b) !== 'appos')
|
|
54
|
+
continue;
|
|
55
|
+
if (!NOUN.test(b.lexicalClass))
|
|
56
|
+
continue;
|
|
57
|
+
const a = gov(b);
|
|
58
|
+
if (!a || !NOUN.test(a.lexicalClass))
|
|
59
|
+
continue;
|
|
60
|
+
if (a.absoluteIndex >= b.absoluteIndex)
|
|
61
|
+
continue; // possessive precedes
|
|
62
|
+
if (!/['’]s$|s['’]$/.test(a.word))
|
|
63
|
+
continue; // overt possessive mark
|
|
64
|
+
const aGov = gov(a);
|
|
65
|
+
if (!aGov || aGov === b || !a.dependency || !b.dependency)
|
|
66
|
+
continue;
|
|
67
|
+
// B inherits A's governor and relation; A becomes B's possessive.
|
|
68
|
+
b.dependency.governor = aGov;
|
|
69
|
+
b.dependency.dependentType = a.dependency.dependentType;
|
|
70
|
+
a.dependency.governor = b;
|
|
71
|
+
a.dependency.dependentType = 'nmod:poss';
|
|
72
|
+
// A's case marker ("than") re-points to B, the true oblique head.
|
|
73
|
+
for (const c of words) {
|
|
74
|
+
if (c !== a && c !== b && gov(c) === a && c.dependency &&
|
|
75
|
+
(rawRel(c) === 'case' || c.lexicalClass === 'IN' || c.lexicalClass === 'TO')) {
|
|
76
|
+
c.dependency.governor = b;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Reliable structural relations whose label adjacency must NOT override.
|
|
82
|
+
* CASE is here for the POSSESSIVE noun ("her lips' red" — nmod:poss →
|
|
83
|
+
* CASE): a possessive NNS immediately precedes its noun head, and letting
|
|
84
|
+
* surface adjacency relabel it NOMD hands it to the Compound Stress Rule,
|
|
85
|
+
* fore-stressing what the NSR must right-stress (her lips' RED, love's
|
|
86
|
+
* SWAY). */
|
|
37
87
|
const STRUCTURAL = new Set([
|
|
38
88
|
'ROOT', 'NSUBJ', 'NSUBJPASS', 'DOBJ', 'IOBJ', 'OBL', 'AUX', 'AUXPASS',
|
|
39
89
|
'CCOMP', 'XCOMP', 'ADVCL', 'ADVMOD', 'AMOD', 'ACL', 'CC', 'CONJ', 'EXPL',
|
|
40
|
-
'INTJ', 'DISCOURSE', 'VPRT', 'COMPMARK', 'ADVMARK', 'EXT',
|
|
90
|
+
'INTJ', 'DISCOURSE', 'VPRT', 'COMPMARK', 'ADVMARK', 'EXT', 'CASE',
|
|
41
91
|
]);
|
|
42
92
|
/**
|
|
43
93
|
* Pre-head modifier inference by SURFACE ADJACENCY — independent of en-parse's
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/calliope/engine.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/calliope/engine.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AA+D7C,eAAO,MAAM,cAAc,EAAE,aAG5B,CAAC"}
|
package/dist/calliope/engine.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// replace the body with: canonical deps (Stage 1) → Match-Theory hierarchy
|
|
11
11
|
// (Stage 2) → Scenario A–O stress (Stage 3) → phrase-stress ramp + relativise
|
|
12
12
|
// (Stage 4). The legacy path remains untouched in `src/clio/engine.ts`.
|
|
13
|
-
import { assignLexicalStress, applySurfacePostProcessing, detectDisplayPrefixes, applyStressShift } from '../stress.js';
|
|
13
|
+
import { assignLexicalStress, applySurfacePostProcessing, detectDisplayPrefixes, applyStressShift, applyRhythmicPeakPlacement } from '../stress.js';
|
|
14
14
|
import { correctPosWithLexicon } from './postag.js';
|
|
15
15
|
import { tagNames } from './names.js';
|
|
16
16
|
import { parseFeats } from './feats.js';
|
|
@@ -55,6 +55,11 @@ function analyzeSentenceCalliope(sent) {
|
|
|
55
55
|
// Rhythm Rule clash). Runs after hierarchy (needs PP-initial info) but before
|
|
56
56
|
// phrase stress (so the shifted peak flows into the NSR ramp).
|
|
57
57
|
applyStressShift(sent.words, ius);
|
|
58
|
+
// Rhythm-Rule peak placement for bistable disyllables (contourless
|
|
59
|
+
// shift-likely words like "into"; fused negatives like "cannot"): the peak
|
|
60
|
+
// is selected by grid alternation with the neighbouring lexical stresses,
|
|
61
|
+
// before phrase stress reads the contour.
|
|
62
|
+
applyRhythmicPeakPlacement(sent.words);
|
|
58
63
|
computePhraseStress(sent);
|
|
59
64
|
computeRelativeStress(sent.words, ius);
|
|
60
65
|
// Surface-order post-processing passes shared with the Clio engine: compound
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relstress.d.ts","sourceRoot":"","sources":["../../src/calliope/relstress.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAyB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"relstress.d.ts","sourceRoot":"","sources":["../../src/calliope/relstress.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAyB,MAAM,aAAa,CAAC;AAq7B/E,uEAAuE;AACvE,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,EAAE,gBAAgB,EAAE,GAAG,IAAI,CAkCrF"}
|