// types.ts — Complete type declarations for Calliope_TS pipeline // Reflects McAleese’s class diagrams (Figure 14) and additional phonological / // metrical types required by stress, phonological hierarchy, scansion, and Scandroid modules. /** * Stress levels in McAleese’s relative system, in ascending order * `x < w < n < m < s`. `x` is the "Zero Provision" tier (Hayes; Lerdahl & * Jackendoff 1983): a level *weaker than a stressless overt syllable*, borne by * maximally-reduced clitics (the/a/of/and…) and unfilled positions. */ export type StressLevel = 'x' | 'w' | 'n' | 'm' | 's'; /** A single syllable within a word, with phonetic and stress information */ export interface Syllable { text: string; // the orthographic syllable (or entire word if unsplit) phones: string; // CMU phonetic transcription (per-syllable ARPAbet) weight?: 'H' | 'L'; // syllable weight (Heavy/Light) stress: number; // numeric stress from CMU; modified by compound + nuclear rules lexicalStress?: number; // stress before nuclear rule; used for relative mapping / meter detection relativeStress?: StressLevel; // assigned after phrase‑stress rules and relative adjustment extrametrical?: 'morphological' | 'light_noun' | 'derivational'; // extrametricality classification } /** Represents a word in the dependency‑parse graph */ export interface ClsWord { index: number; // 1‑based index in the sentence (matching Antelope’s XML) lexicalClass: string; // POS tag, e.g., 'VBD', 'NN', 'PRP' lexicalDetails: string; // additional morphological info (empty if none) lexicalPlural: boolean; // true if plural position: string; // textual position (not always used) word: string; // the surface form of the word (case-normalised for lookups: // a sentence-initial capital is lowered unless proper noun) displayWord?: string; // the ORIGINAL surface form when it differs from `word` // (set by the parser's de-capitalisation) — what reports // and the phonopoetics show to the reader absoluteIndex: number; // 0‑based index among all words in the text isContent: boolean; // extended properties syllables: Syllable[]; // array of syllables for the word morphSuffix?: string; // productive suffix split off for OOV stress (e.g. 'est'); guides display syllabification morphPrefix?: string; // productive prefix split off for OOV stress (e.g. 'dis'); guides display syllabification phraseStress: number; // numeric phrase‑level stress after Nuclear Stress Rule dependency?: ClsDependency; // back‑reference to dependency edge (if any) node?: ClsNode; // back‑reference to the constituent node (if any) // ─── Calliope engine substrate (additive; ignored by the legacy/Clio path) ─── canonicalRel?: string; // normalised Scenario relation (NOMD/AMOD/VPRT/DOBJ/IOBJ/OBL/…) isPersonName?: boolean; // token is in the `humannames` list (proper noun = person) isPlaceName?: boolean; // token is in the `cities-list` list (proper name = place) // ─── Wagner/Krifka substrate (additive; 2026-06-29) ─── featsMap?: Record; // UD morphological FEATS parsed from lexicalDetails // (VerbForm/Voice/PronType/Number/Definite/Degree/Tense/…) discourseGiven?: boolean; // a content word repeated from an earlier line of the same // stanza — set only by the optional stanza-givenness pass // (analyzeStanzas / analyzeReadingDocument), never single-line coordinateGiven?: boolean; // a content word whose lemma is repeated as the HEAD of a // coordinate structure within the same line ("young blood and // high blood" → the second "blood" is anaphorically given; // contrastive focus falls on the modifier "high"). Set by the // relativiser's coordinate-givenness pre-pass. rhymeFocal?: boolean; // a line-final FUNCTION word that carries a strong end-rhyme // with a stressed (content) line-final elsewhere in the stanza // ("…one of THREE" / "…stopp'st thou ME") — the rhyme template // transfers a beat. Set only by the stanza rhyme-fellow pass // (analyzeStanzas / analyzeReadingDocument), never single-line. acuteSyllable?: number; // syllable index carrying the poet's explicit ACUTE diacritic // ("Milésien") — an overriding stress cue: the lexical primary // moves here, and the surface grade never sinks below 'm'. // Set by assignLexicalStress; honoured at the very end of // applySurfacePostProcessing, after every demotion pass. } /** A typed dependency edge between two words (as in Antelope’s XML) */ export interface ClsDependency { index: number; // 1‑based dependency index governorIndex: number; // word index of the governor (head) dependentIndex: number; // word index of the dependent dependentType: string; // type label: 'aux','nsubj','dobj','prep','det','poss','possessive','pobj',… governorName: string; // surface form of the governor dependentName: string; // surface form of the dependent governor: ClsWord; // reference to the governor word object dependent: ClsWord; // reference to the dependent word object } /** A constituent node in the parse tree (mirrors Figure 14) */ export interface ClsNode { index: string; // node identifier, e.g., '1', '1.2' nodeName: string; // label (e.g., 'SQ', 'NP', 'VP', 'PP', or a word index) parent: ClsNode | null; // parent node, null for root contains: (ClsNode | ClsWord)[]; // children: either sub‑nodes or words } /** A single parsed sentence */ export interface ClsSentence { index: number; // sentence number (1‑based) nodes: ClsNode | null; // root node of the parse tree dependencies: ClsDependency[]; // all dependency edges in this sentence words: ClsWord[]; // word objects in order xml: string; // serialised XML representation (optional) } /** Top‑level document containing parsed sentences */ export interface ClsDocument { sentences: ClsSentence[]; // list of parsed sentences xml: string; // full XML document (optional) } // ─── Phonological Hierarchy (CP, PP, IU) ──────────────────────────── /** A clitic group: one content word plus its attached function words */ export interface CliticGroup { tokens: ClsWord[]; } /** A phonological phrase: one or more clitic groups related syntactically */ export interface PhonologicalPhrase { cliticGroups: CliticGroup[]; } /** An intonational unit: one or more phonological phrases bounded by punctuation/line‑end */ export interface IntonationalUnit { phonologicalPhrases: PhonologicalPhrase[]; } // ─── Metre‑ and scansion‑related types ───────────────────────────── /** Recognised metre names */ export type MetreName = | 'iambic' | 'trochaic' | 'spondaic' | 'pyrrhic' | 'anapestic' | 'dactylic' | 'amphibrachic' | 'bacchic'; /** Definition of a metre candidate: its foot shape and syllable count per foot */ export interface MetreCandidate { name: MetreName; foot: string; // e.g., 'ws', 'sw', 'wws' syllableCount: number; // number of syllables in one foot (2 or 3) } /** A key‑stress pattern extracted from one unit of the phonological hierarchy */ export interface KeyStress { unitType: 'PW' | 'CP' | 'PP' | 'IU'; // type of prosodic unit pattern: string; // stress pattern, e.g., 'ws', 'sw', 'wsw' weight: number; // importance weight (1–3) as per McAleese’s scoring positions: number[]; // global syllable indices involved in this key stress } /** Result of scansion for a single line */ export interface ScansionResult { meter: MetreName | 'free verse'; // identified metre, or free verse if below threshold scansion: string; // the foot‑delimited scansion string, e.g., "ws|ws|ws|ws|ws" certainty: number; // percentage of maximum possible weighted score weightScore: number; // actual accumulated weight maxPossibleWeight: number; // theoretical maximum weight for the line algorithm?: string; // optional, to distinguish Phonological vs Scandroid results } /** One candidate meter's overall fit score (internal composite, not a probability) */ export interface MeterScore { meter: MetreName | 'free verse'; score: number; // scoreMeters' finalScore — a relative fit score, higher = better } /** Detailed phonological scansion for a single line */ export interface PhonologicalScansionDetail { all: string; // hierarchical string, e.g. "<{[nm/ws\n]}mn/sw\]m]}>" keyStresses: string; // key‑stress string, e.g. "<{[xx/ws\n]}xx/sw\]m]}>" meter: string; // e.g. "iambic pentameter" meterName: MetreName | 'free verse'; // enum value for tests footCount: number; // e.g. 5 summary: string; // e.g. "IU=1 PP=1 PW=1" counts per metre direction scansion: string; // foot‑separated, e.g. "xx/ws|nx/xs/wm" certainty: number; // 0‑100 weightScore: number; maxPossibleWeight: number; ranking?: MeterScore[]; // candidate meters ranked by fit score (best first); optional consensusMeter?: string; // set by applyStanzaConsensus when this line's standalone meter // diverges from, yet closely fits, the stanza's dominant meter. // The continuity-rename pass (index.ts) then normally CONVERTS // this annotation: the line adopts the dominant meter as its BASE // reading (meter/scansion/footCount/certainty re-fitted under it), // consensusMeter is cleared, and standaloneMeter records the // numerically-best standalone reading. consensusMeter survives // only when the forced re-fit fails. standaloneMeter?: string; // the line's numerically best standalone meter (e.g. "dactylic // tetrameter"), kept when stanza/poem continuity renamed the base // reading to the dominant meter. rhythmNote?: string; // non-classical rhythm classification (Russian-metrics taxonomy): // "4-ictus dolnik", "3-ictus taktovik", "4-beat accentual", // "alternating 4·3-ictus accentual" — set at stanza level when // beat counts are regular but syllable counts vary and no // classical meter dominates; or per-line to refine a free-verse // reading. NOT a form verdict: "ballad" etc. belong to the // rhyme-aware form layer (rhyme.ts). metricalityNote?: string; // advisory prose-likeness hedge (scansion.ts), set when // a long, non-committal, weak-fit line reads as plausible // prose: "No consistent metered rhythm(s) discerned. …". // Display-only — never alters meter/scansion/certainty. rhyme?: { // rhyme annotation (rhyme.ts), LYRICAL-compatible typology. // Letters are assigned POEM-WIDE (a rhyme sound keeps its // letter across stanza breaks), in reading order. endWord: string; letter: string; // END-rhyme scheme letter 'A'/'B'/…, or '·' when unrhymed type?: string; // perfect/rich/family/assonant/consonant/augmented/ // diminished/wrenched/eye/identical matchedLine?: number; // 0-based poem line index this end-rhyme first binds to internal?: { // pre-caesural INTERNAL rhymes on this line (strong-tier // only), in left-to-right order; share the poem-wide // letter space with the end rhymes. word: string; letter: string; type?: string; }[]; notation?: string; // assembled scheme cell: internal letters parenthesised // then the end letter — e.g. "(A)B", "(C)C", "A", "·". }; formNote?: string; // stanza/poem FORM verdict (rhyme-aware): "ballad stanza // (ABCB, 4·3)", "blank verse", "Shakespearean Sonnet", // "terza rima (ABA BCB CDC…)", "limerick"… } /** Complete per‑line result from the pipeline */ export interface LineResult { sentence: ClsSentence; // parsed sentence with words, dependencies, nodes phonologicalHierarchy: IntonationalUnit[]; // CP/PP/IU structure keyStresses: KeyStress[]; // extracted key patterns with weights phonologicalScansion: PhonologicalScansionDetail; // scansion via phonological scoring // Charles Hartman's Scandroid (2005) run as a genuinely INDEPENDENT second // opinion: its own dictionary, its own syllabifier, its own two iambic // algorithms and its own anapestic engine, working ONLY from the raw line // text (src/scandroidNative/). Never derived from anything above. This is // the Calliope (main) pipeline's field. scandroidNative?: import('./scandroidNative/engine.js').ScandroidNativeLineResult; // Clio (the frozen legacy pipeline, src/clio/) still derives its Scandroid // comparison from Calliope-style stress grades via its own untouched // src/clio/scandroid.ts — kept here only so Clio's frozen code continues to // compile; the main pipeline never sets these. scandroidCorral?: ScansionResult; scandroidMaximise?: ScansionResult; } // ─── Display formatting types ────────────────────────────────────── /** Per-syllable information for display rendering */ export interface SyllableDisplayEntry { wordText: string; sylText: string; // orthographic text of this syllable sylIndex: number; // 0‑based index within the word sylCount: number; // total syllables in the word relativeStress: StressLevel; globalIndex: number; wordIndex: number; // 0‑based index among non-punctuation words } /** A single foot in the display, mapping scansion pattern to its syllables */ export interface FootDisplayEntry { footIndex: number; footPattern: string; // raw foot pattern from scansion, e.g., 'ws', '-ms' syllables: SyllableDisplayEntry[]; } /** All formatted display representations for a single line */ export interface FormattedDisplay { originalText: string; diacriticText: string; uppercaseText: string; ansiText: string; sylColoredText: string; footAligned: string; syllableBreakdown: string; } /** Options controlling display formatting */ export interface DisplayOptions { ansi: boolean; diacritics: boolean; footAligned: boolean; verbose: boolean; phrasal: boolean; }