seonbi-0.1.0: src/Text/Seonbi/Hangul.hs
module Text.Seonbi.Hangul
( JamoTriple
, fromJamoTriple
, isHangulSyllable
, toJamoTriple
) where
-- $setup
-- >>> import qualified Text.Show.Unicode
-- >>> :set -interactive-print=Text.Show.Unicode.uprint
-- | A triple of an initial consonant, a vowel, and an optional final consonant.
type JamoTriple = (Char, Char, Maybe Char)
-- | Checks if a character is a hangul letter and a complete syllable.
--
-- >>> isHangulSyllable '가'
-- True
-- >>> isHangulSyllable 'ㄱ'
-- False
isHangulSyllable :: Char -> Bool
isHangulSyllable c =
c >= '\xac00' && c <= '\xd7a3';
syllableBase :: Int
syllableBase = 0xac00
initialBase :: Int
initialBase = 0x1100
vowelBase :: Int
vowelBase = 0x1161
finalBase :: Int
finalBase = 0x11a7
vowelCount :: Int
vowelCount = 21;
finalCount :: Int
finalCount = 28;
-- | Takes a complete hangul syllable apart into consonants and a vowel.
-- Returns 'Nothing' for non-hangul letters.
--
-- >>> toJamoTriple '가'
-- Just ('ᄀ','ᅡ',Nothing)
-- >>> toJamoTriple '글'
-- Just ('ᄀ','ᅳ',Just 'ᆯ')
-- >>> toJamoTriple 'A'
-- Nothing
toJamoTriple :: Char -> Maybe JamoTriple
toJamoTriple c
| isHangulSyllable c = Just
( toEnum $ initialBase + ((syllable `div` finalCount) `div` vowelCount)
, toEnum $ vowelBase + ((syllable `div` finalCount) `mod` vowelCount)
, case syllable `mod` finalCount of
0 -> Nothing
f -> Just $ toEnum (finalBase + f)
)
| otherwise = Nothing
where
syllable :: Int
syllable = fromEnum c - syllableBase
-- | Composes hangul jamo triple into a hangul syllable.
--
-- >>> fromJamoTriple ('ᄀ', 'ᅡ', Nothing)
-- Just '가'
-- >>> fromJamoTriple ('ᄀ', 'ᅳ', Just 'ᆯ')
-- Just '글'
fromJamoTriple :: JamoTriple -> Maybe Char
fromJamoTriple (initial, vowel, final)
| initialIndex < 0 = Nothing
| initialIndex > 18 = Nothing
| vowelIndex < 0 = Nothing
| vowelIndex > 20 = Nothing
| finalIndex < 0 = Nothing
| finalIndex > 27 = Nothing
| otherwise = Just $ toEnum $ syllableBase +
(initialIndex * vowelCount + vowelIndex) * finalCount + finalIndex
where
initialIndex :: Int
initialIndex = fromEnum initial - initialBase
vowelIndex :: Int
vowelIndex = fromEnum vowel - vowelBase
finalIndex :: Int
finalIndex = maybe 0 (\ f -> fromEnum f - finalBase) final