composeKoreanJamo function
Composes modern conjoining Jamo using the pinned g2pkc helper semantics.
A standalone modern vowel first receives the silent onset ᄋ, matching
upstream's non-overlapping regular-expression substitution. Consecutive
standalone vowels therefore receive an onset only at alternating
positions. Compatibility and archaic Jamo are otherwise preserved.
Implementation
String composeKoreanJamo(String input) {
final source = input.runes.toList(growable: false);
final expanded = <int>[];
var sourceIndex = 0;
while (sourceIndex < source.length) {
final scalar = source[sourceIndex];
if (sourceIndex == 0 && _isModernVowel(scalar)) {
expanded
..add(_silentLead)
..add(scalar);
sourceIndex++;
continue;
}
if (!_isModernLead(scalar) &&
sourceIndex + 1 < source.length &&
_isModernVowel(source[sourceIndex + 1])) {
expanded
..add(scalar)
..add(_silentLead)
..add(source[sourceIndex + 1]);
sourceIndex += 2;
continue;
}
expanded.add(scalar);
sourceIndex++;
}
final output = StringBuffer();
var index = 0;
while (index < expanded.length) {
final lead = expanded[index];
if (_isModernLead(lead) &&
index + 1 < expanded.length &&
_isModernVowel(expanded[index + 1])) {
final vowel = expanded[index + 1];
var tail = 0;
var consumed = 2;
if (index + 2 < expanded.length && _isModernTail(expanded[index + 2])) {
tail = expanded[index + 2] - _tailBase;
consumed = 3;
}
final syllable =
_hangulBase +
(lead - _leadBase) * _vowelCount * _tailCount +
(vowel - _vowelBase) * _tailCount +
tail;
output.writeCharCode(syllable);
index += consumed;
continue;
}
output.writeCharCode(lead);
index++;
}
return output.toString();
}