syllables function
Count syllables in word
.
Heavily inspired by https://github.com/wooorm/syllable.
Implementation
int syllables(String word) {
assert(
RegExp(r'^\w+$').hasMatch(word),
"Word '$word' contains non-alphabetic characters. "
"Have you trimmed the word of whitespace?");
if (word.length <= 3 && _allCaps.hasMatch(word)) {
// USA, PC, TV, ...
return word.length;
}
if (word.length < 3) return 1;
final problematicCount = problematic[word];
if (problematicCount != null) {
return problematicCount;
}
// TODO: if this is plural, make it singular and try again with problematic
int count = 0;
/// Adjusts [count] and returns string without the pattern.
String adjust(String string, Pattern pattern, int adjustment) {
return string.replaceAllMapped(pattern, (_) {
count += adjustment;
return '';
});
}
// We have to chop off prefixes (like 'afore' or 'hyper') and suffixes
// (like 'ment' or 'ology') so that we can than scan only the "root"
// of the word. For example, "abatement" becomes "abate" (-ment), which
// ends with "-ate", which looks like 2 syllables but actualy is just one
// (which is covered by [monosyllabic2] below).
String wordRoot = adjust(word, trisyllabicPrefixSuffix, 3);
wordRoot = adjust(wordRoot, disyllabicPrefixSuffix, 2);
wordRoot = adjust(wordRoot, monosyllabicPrefixSuffix, 1);
final scanner = StringScanner(wordRoot);
bool precedingVowel = false;
while (!scanner.isDone) {
if (scanner.matches(monosyllabic1) || scanner.matches(monosyllabic2)) {
// The following should count for one less than what it looks like
// from vowels and consonants alone.
count -= 1;
}
if (scanner.matches(disyllabic1) ||
scanner.matches(disyllabic2) ||
scanner.matches(disyllabic3) ||
scanner.matches(disyllabic4)) {
// The following should count for one more than what it looks like
// from vowels and consonants alone.
count += 1;
}
if (scanner.scan(_vowel)) {
if (!precedingVowel) {
count += 1;
}
precedingVowel = true;
continue;
}
scanner.expect(_alpha);
precedingVowel = false;
}
if (count == 0) return 1;
return count;
}