A prosody engine, built on the mora.
Published as mora 0.1.0. The engine is complete and tested; no pronunciation
lexicon ships with it.
mora turns pronunciations into the units verse is measured in. It reads
phones and knows nothing about spelling.
- Syllabification by the sonority hierarchy with onset maximization.
am.bersplits because sonority falls across the cluster;po.e.trykeepstrwhole because it rises. What varies between languages is a rule struct, not the procedure. - Weight, counted in morae. A syllable is light, heavy, or superheavy depending on whether its nucleus is long and whether the language counts a coda consonant. That one flag is most of the difference between stress-timed, syllable-timed, and mora-timed languages, and it is why a haiku counted in English syllables comes out short.
- Meter, accentual and quantitative from the same matcher. English iambic
pentameter reads stress; Latin dactylic hexameter reads weight;
Modechooses, and nothing else changes. Scansion reports how much of a line fit, so a trochaic first foot registers as a substitution inside iambic pentameter rather than as a different meter. - Sonance: rhyme anchored at the last primary-stressed nucleus, plus slant
rhyme, assonance, consonance, and alliteration.
cat/hatrhyme,cat/cabare slant,cat/kitalliterate and share a consonant frame, and no word rhymes with itself.
The mora sits underneath the usual prosodic typology: syllable weight is counted in morae, weight-sensitive stress rules read that count, and quantitative meter is moraic outright. So only the bottom layer is language-specific.
| layer | per-language cost |
|---|---|
| phones | a symbol table |
| syllables | a SyllableRule |
| weight | a WeightRule |
| meter | nothing |
| sound kinship | nothing |
English ships as one module: a 39-entry ARPAbet table and two constants. A second language costs the same. The Latin stress rule is included as the proof that weight is the deeper layer, since it reads weight alone and no stress at all.
#![no_std], #![forbid(unsafe_code)], zero dependencies. Every collecting
function has an _into form that writes into a caller buffer, so the whole
engine runs without an allocator.
First consumer is a writing surface that draws a poem as a wheel, with rhyme and meter as chords across it. Usable from any Rust project that needs to measure sound in text: verse tools, songwriting, TTS front ends, language teaching, or search over how words sound rather than what they mean.
cargo add mora
cargo add mora --no-default-features # language-neutral core, no allocatoruse mora::english::{SYLLABLE_RULE, WEIGHT_RULE, pronounce};
use mora::meter::{Foot, Mode, beats, scan_best};
use mora::sonance::is_perfect_rhyme;
use mora::syllable::syllabify;
let cat = pronounce("K AE1 T").unwrap();
let hat = pronounce("HH AE1 T").unwrap();
let (a, b) = (syllabify(&cat, SYLLABLE_RULE), syllabify(&hat, SYLLABLE_RULE));
assert!(is_perfect_rhyme((&cat, &a), (&hat, &b)));
// "the CAT is HERE" scans as two iambs.
let line = pronounce("DH AH0 K AE1 T IH0 Z HH IY1 R").unwrap();
let syllables = syllabify(&line, SYLLABLE_RULE);
let scansion = scan_best(
&beats(&line, &syllables, Mode::Accentual, WEIGHT_RULE),
&Foot::COMMON,
).unwrap();
assert_eq!(scansion.meter.foot, Foot::Iamb);None are bundled. Pronunciation lexicons are large and separately licensed, so
this crate parses the format CMUdict publishes (english::cmudict_entry) and
leaves storage to the consumer. Anything that can produce ARPAbet symbols will
do, including a phonemizer of your own.
mora measures sound. It is not a phonemizer, not a semantic analyzer, and
not a rhyming dictionary: it answers whether two given pronunciations rhyme,
not which words in a language do. Meaning-side kinship belongs to an embedding
model, and the two are meant to be read side by side rather than merged.
The canonical sound-kinship API is mora::sonance. The separately named
sonance repository is an archive
pointer, not another implementation or dependency surface.
MPL-2.0 (see LICENSE). Provenance is recorded in
LICENSES.md: nothing here is third-party, and no lexicon
ships. Published mora 0.1.0 keeps the MIT OR Apache-2.0 grant it shipped
with; MPL-2.0 reaches crates.io at the next functional bump.
This README was generated by AI and will be edited by the author upon release.