/* This file is part of Ellinika project. Copyright (C) 2011 Sergey Poznyakoff Ellinika is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Ellinika is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include "utf8.h" #include "elmorph.h" struct syllabificator { struct syllable *syl; size_t syl_count; size_t syl_max; struct phoneme *phon; size_t phon_cur; size_t phon_max; int err; }; #define SYL_FLAG_MASK (CHF_ACCENT_MASK|CHF_DIPHTHONG|CHF_IOTA) #define ISIOTA(ph) \ ((ph).code == PHON_I && (ph).count == 1 && \ !((ph).flags & (CHF_ACCENT_MASK|CHF_TREMA))) #define NEXT_PHONEME(sp) do { \ (sp)->phon_cur++; \ if ((sp)->phon_cur == (sp)->phon_max) \ return 0; \ } while (0) static int next_syllable(struct syllabificator *sp) { struct syllable *syl; if (sp->phon_cur == sp->phon_max) return 1; if (sp->syl_count == sp->syl_max) { struct syllable *newsyl; size_t newmax = sp->syl_max + 16; newsyl = realloc(sp->syl, sizeof(newsyl[0]) * newmax); if (!newsyl) { sp->err = errno; return 1; } sp->syl = newsyl; sp->syl_max = newmax; } syl = sp->syl + sp->syl_count++; syl->char_start = sp->phon[sp->phon_cur].start; syl->char_count = sp->phon[sp->phon_cur].count; syl->phoneme_start = sp->phon_cur; syl->phoneme_count = 1; syl->flags = sp->phon[sp->phon_cur].flags; NEXT_PHONEME(sp); /* A diphthong forms a single syllable. */ if ((syl->flags & CHF_DIPHTHONG) && !(syl->flags & CHF_CONSONANT)) return 0; /* If the syllable begins with a consonant, it includes all subsequent consonants up to the first vowel. */ if (syl->flags & CHF_CONSONANT) { for (; sp->phon_cur < sp->phon_max && (sp->phon[sp->phon_cur].flags & CHF_CONSONANT); sp->phon_cur++) { syl->char_count += sp->phon[sp->phon_cur].count; syl->phoneme_count++; } } else if ((sp->phon[sp->phon_cur].flags & CHF_VOWEL) && !ISIOTA(sp->phon[sp->phon_cur-1])) /* V-V boundary */ return 0; if (sp->phon_cur == sp->phon_max) return 0; if (ISIOTA(sp->phon[sp->phon_cur])) { /* incorporate iota */; syl->char_count += sp->phon[sp->phon_cur].count; syl->phoneme_count++; NEXT_PHONEME(sp); if (sp->phon[sp->phon_cur].flags & CHF_VOWEL) syl->flags |= CHF_DIPHTHONG|CHF_IOTA; } if (sp->phon[sp->phon_cur].flags & CHF_VOWEL) syl->flags |= sp->phon[sp->phon_cur].flags & CHF_ACCENT_MASK; syl->char_count += sp->phon[sp->phon_cur].count; syl->phoneme_count++; NEXT_PHONEME(sp); if (sp->phon[sp->phon_cur - 1].flags & CHF_VOWEL) { /* If next phoneme is a consonant, incorporate it into the current syllable */ if ((sp->phon[sp->phon_cur].flags & CHF_CONSONANT) && (sp->phon_cur + 1 == sp->phon_max || (sp->phon[sp->phon_cur + 1].flags & CHF_CONSONANT))) { syl->char_count += sp->phon[sp->phon_cur].count; syl->phoneme_count++; NEXT_PHONEME(sp); } } return 0; } int syllable_map(struct syllable **psyl, size_t *plen, struct phoneme *phon, size_t nphon) { struct syllabificator sd; sd.syl = NULL; sd.syl_count = 0; sd.syl_max = 0; sd.phon = phon; sd.phon_cur = 0; sd.phon_max = nphon; sd.err = 0; while (next_syllable(&sd) == 0) sd.syl[sd.syl_count-1].flags &= SYL_FLAG_MASK; if (sd.err) { free(sd.syl); return sd.err; } *psyl = sd.syl; *plen = sd.syl_count; return 0; }