phonemize
Version:
Fast phonemizer with rule-based G2P prediction. Pure JavaScript implementation.
106 lines (105 loc) • 4.69 kB
TypeScript
/**
* English suffix table (P2.0 of G2P redesign).
*
* Each entry describes one productive English suffix: its orthography,
* its IPA realization, its stress-shift class, and whether it triggers
* vowel reduction in the base. The decomposer uses this to peel a word
* down to its base, the stress FSM uses the class to re-place stress,
* and the reduction rules use the trigger flag to decide whether
* unstressed base vowels collapse.
*
* Stress classes follow Chomsky-Halle / Hayes:
* - "neutral": base stress preserved; suffix unstressed.
* Germanic suffixes: -ness, -less, -ly, -ed, -ing, …
* - "pre": stress on syllable immediately before suffix.
* Latinate Class-I suffixes: -ity, -tion, -ic, -ial, …
* - "pre2": stress on syllable two before suffix.
* -ative, -atory, -ical (in some cases)
* - "self": suffix bears primary stress.
* -ee, -eer, -ese, -ette, -oon, -aire
*
* The reduction flag defaults from class: pre/pre2/self reduce, neutral
* does not. Override only for known irregulars.
*
* Order in SUFFIXES matters only for the decomposer's longest-match
* heuristic; the table is indexed by length at module load.
*/
/**
* Stress class — positional, measured from the end of the word:
*
* - "neutral": inherit base stress (suffix doesn't alter it).
* Germanic Class-II suffixes: -ness, -less, -ly, -ed, -ing, …
* - "penult": primary stress on penultimate syllable of the whole word.
* Class-I: -ation, -ition, -ic, -ous, -al, -ant/-ent,
* -ization, -tion, -sion, -ial.
* - "antepenult": primary stress on antepenultimate (3rd-from-end).
* Class-I: -ity, -ical, -ative, -ology, -ability, -ological.
* - "final": primary stress on the last syllable.
* Self-stressing: -ee, -eer, -ese, -ette, -oon, -aire.
*
* This single positional view replaces the older "pre/pre2/first/self"
* vocabulary, which was relative to the suffix's start position and turned
* out to overlap depending on suffix syllable count. Position-from-end is
* both simpler and matches the underlying Latin penult/antepenult rule.
*/
export type StressClass = "neutral" | "penult" | "antepenult" | "final";
export interface SuffixEntry {
/** Orthographic suffix matched at word end. */
suffix: string;
/** Phoneme realization (no stress marks; stress comes from class). */
ipa: string;
/** Stress-shift class. */
stress: StressClass;
/** Whether the base undergoes vowel reduction; default by class. */
reduces?: boolean;
/** Minimum base length required after stripping; guards over-stripping. */
minBase?: number;
/** Base recovery: undo orthographic mutation. e.g. -iness ← -y + -ness. */
recover?: {
strip: string;
add: string;
};
/** Optional alternative base spellings to also try (e.g., "abat" → "abate"). */
baseAlts?: string[];
/** Optional context guard: regex matched against the base after stripping. */
baseGuard?: RegExp;
/** Phoneme-level allomorphs that may also realize this suffix. Caller
* picks the one matching the actual IPA tail. Used for -s/-ed allomorphy. */
ipaAlts?: string[];
}
export declare const SUFFIXES: SuffixEntry[];
/**
* Find the longest suffix entry matching the end of `word`. Returns the
* primary base (after any orthographic recovery) plus a list of alternative
* base spellings to try (e.g., for -ed/-ing also try base+"e", since
* abated→abate dropped the final e). Caller can pick which alternative
* resolves against a dictionary.
*
* Returns null if no entry matches under its minBase / baseGuard constraints.
*/
export declare function matchSuffix(word: string): {
entry: SuffixEntry;
base: string;
baseAlts: string[];
} | null;
/**
* Strip suffixes iteratively until no more match. Returns the chain of
* (entry, intermediate base) and the final base.
*/
export interface DecompositionStep {
entry: SuffixEntry;
/** Word/base before this suffix is stripped. */
before: string;
/** Resulting base after stripping (input to the next iteration). */
after: string;
/** Alternative base spellings (e-drop / consonant doubling recovery). */
afterAlts: string[];
}
export interface Decomposition {
base: string;
baseAlts: string[];
steps: DecompositionStep[];
}
export declare function decompose(word: string, maxDepth?: number): Decomposition;
/** Default reduction trigger by class. */
export declare function reduces(entry: SuffixEntry): boolean;