-- |
-- Module      : Streamly.Internal.Unicode.Char
-- Copyright   : (c) 2018 Composewell Technologies
--
-- License     : BSD-3-Clause
-- Maintainer  : streamly@composewell.com
-- Stability   : experimental
-- Portability : GHC

-- XXX This module should have the reader/writer unfold/refold and read/write
-- stream/fold routines to convert a char to/from stream.
--
module Streamly.Internal.Unicode.Char
    (
    -- * Predicates
      isAsciiAlpha -- XXX Remove or move to unicode-data

    -- XXX move to Unicode.Char.Case?
    -- * Unicode aware operations
    {-
      toCaseFold
    , toLower
    , toUpper
    , toTitle
    -}

    -- XXX Move to Unicode.Stream.Normalize or Unicode.Normalize?
    -- * Unicode normalization
    , NormalizationMode(..)
    , normalize
    )
where

#include "inline.hs"

import Data.Char (isAsciiUpper, isAsciiLower, chr, ord)
import Unicode.Char (DecomposeMode(..))
import Streamly.Internal.Data.Stream.StreamD (Stream(..), Step (..))

import qualified Streamly.Internal.Data.Stream.StreamD as Stream (Stream)

import qualified Unicode.Char as Char

-------------------------------------------------------------------------------
-- Unicode aware operations on strings
-------------------------------------------------------------------------------

-- | Select alphabetic characters in the ascii character set.
--
-- /Pre-release/
--
{-# INLINE isAsciiAlpha #-}
isAsciiAlpha :: Char -> Bool
isAsciiAlpha :: Char -> Bool
isAsciiAlpha Char
c = Char -> Bool
isAsciiUpper Char
c Bool -> Bool -> Bool
|| Char -> Bool
isAsciiLower Char
c

-------------------------------------------------------------------------------
-- Unicode aware operations on strings
-------------------------------------------------------------------------------

{-
-- |
-- /undefined/
toCaseFold :: IsStream t => Char -> t m Char
toCaseFold = undefined

-- |
-- /undefined/
toLower :: IsStream t => Char -> t m Char
toLower = undefined

-- |
-- /undefined/
toUpper :: IsStream t => Char -> t m Char
toUpper = undefined

-- |
-- /undefined/
toTitle :: IsStream t => Char -> t m Char
toTitle = undefined
-}

-------------------------------------------------------------------------------
-- Unicode normalization
-------------------------------------------------------------------------------

data NormalizationMode
    = NFD    -- ^ Canonical decomposition.
    | NFKD   -- ^ Compatibility decomposition.
    | NFC    -- ^ Canonical decomposition followed by canonical composition.
    | NFKC   -- ^ Compatibility decomposition followed by canonical composition.
      deriving (NormalizationMode -> NormalizationMode -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: NormalizationMode -> NormalizationMode -> Bool
$c/= :: NormalizationMode -> NormalizationMode -> Bool
== :: NormalizationMode -> NormalizationMode -> Bool
$c== :: NormalizationMode -> NormalizationMode -> Bool
Eq, Int -> NormalizationMode -> ShowS
[NormalizationMode] -> ShowS
NormalizationMode -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [NormalizationMode] -> ShowS
$cshowList :: [NormalizationMode] -> ShowS
show :: NormalizationMode -> String
$cshow :: NormalizationMode -> String
showsPrec :: Int -> NormalizationMode -> ShowS
$cshowsPrec :: Int -> NormalizationMode -> ShowS
Show, Int -> NormalizationMode
NormalizationMode -> Int
NormalizationMode -> [NormalizationMode]
NormalizationMode -> NormalizationMode
NormalizationMode -> NormalizationMode -> [NormalizationMode]
NormalizationMode
-> NormalizationMode -> NormalizationMode -> [NormalizationMode]
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: NormalizationMode
-> NormalizationMode -> NormalizationMode -> [NormalizationMode]
$cenumFromThenTo :: NormalizationMode
-> NormalizationMode -> NormalizationMode -> [NormalizationMode]
enumFromTo :: NormalizationMode -> NormalizationMode -> [NormalizationMode]
$cenumFromTo :: NormalizationMode -> NormalizationMode -> [NormalizationMode]
enumFromThen :: NormalizationMode -> NormalizationMode -> [NormalizationMode]
$cenumFromThen :: NormalizationMode -> NormalizationMode -> [NormalizationMode]
enumFrom :: NormalizationMode -> [NormalizationMode]
$cenumFrom :: NormalizationMode -> [NormalizationMode]
fromEnum :: NormalizationMode -> Int
$cfromEnum :: NormalizationMode -> Int
toEnum :: Int -> NormalizationMode
$ctoEnum :: Int -> NormalizationMode
pred :: NormalizationMode -> NormalizationMode
$cpred :: NormalizationMode -> NormalizationMode
succ :: NormalizationMode -> NormalizationMode
$csucc :: NormalizationMode -> NormalizationMode
Enum)

-------------------------------------------------------------------------------
-- Normalization combinators
-------------------------------------------------------------------------------

type ReBuf = [Char]

{-# INLINE insertIntoReBuf #-}
insertIntoReBuf :: Char -> ReBuf -> ReBuf
insertIntoReBuf :: Char -> ShowS
insertIntoReBuf Char
c [] = [Char
c]
insertIntoReBuf Char
c xxs :: String
xxs@(Char
x:String
xs)
    | Char -> Int
Char.combiningClass Char
c forall a. Ord a => a -> a -> Bool
< Char -> Int
Char.combiningClass Char
x = Char
c forall a. a -> [a] -> [a]
: String
xxs
    | Bool
otherwise = Char
x forall a. a -> [a] -> [a]
: Char -> ShowS
insertIntoReBuf Char
c String
xs

-- {-# ANN type DecomposeState Fuse #-}
data DecomposeState st
    = YieldCharList [Char] (DecomposeState st)
    | ReadInputChar ReBuf st
    | IsHangul Char st
    | IsDecomposable [Char] ReBuf st
    | DecomposeStop

{-# INLINE_NORMAL decomposeD #-}
decomposeD ::
       Monad m => Bool -> DecomposeMode -> Stream m Char -> Stream m Char
decomposeD :: forall (m :: * -> *).
Monad m =>
Bool -> DecomposeMode -> Stream m Char -> Stream m Char
decomposeD Bool
decomposeHangul DecomposeMode
mode (Stream State StreamK m Char -> s -> m (Step s Char)
step s
state) =
    forall (m :: * -> *) a s.
(State StreamK m a -> s -> m (Step s a)) -> s -> Stream m a
Stream State StreamK m Char
-> DecomposeState s -> m (Step (DecomposeState s) Char)
sstep (forall st. String -> st -> DecomposeState st
ReadInputChar [] s
state)

    where

    {-# INLINE_LATE sstep #-}
    -- XXX Does this cause any problem?
    sstep :: State StreamK m Char
-> DecomposeState s -> m (Step (DecomposeState s) Char)
sstep State StreamK m Char
_ (YieldCharList [] DecomposeState s
ns) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall s a. s -> Step s a
Skip DecomposeState s
ns
    sstep State StreamK m Char
_ (YieldCharList (Char
ch:String
chs) DecomposeState s
ns) =
        forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall s a. a -> s -> Step s a
Yield Char
ch (forall st. String -> DecomposeState st -> DecomposeState st
YieldCharList String
chs DecomposeState s
ns)
    sstep State StreamK m Char
gst (ReadInputChar String
rebuf s
st) = do
        Step s Char
res <- State StreamK m Char -> s -> m (Step s Char)
step State StreamK m Char
gst s
st
        forall (m :: * -> *) a. Monad m => a -> m a
return
          forall a b. (a -> b) -> a -> b
$ forall s a. s -> Step s a
Skip
          forall a b. (a -> b) -> a -> b
$ case Step s Char
res of
                Yield Char
ch s
st1
                    | Char -> Bool
Char.isHangul Char
ch ->
                        if Bool
decomposeHangul
                        then forall st. String -> DecomposeState st -> DecomposeState st
YieldCharList String
rebuf (forall st. Char -> st -> DecomposeState st
IsHangul Char
ch s
st1)
                        else forall st. String -> DecomposeState st -> DecomposeState st
YieldCharList
                                 (String
rebuf forall a. [a] -> [a] -> [a]
++ [Char
ch])
                                 (forall st. String -> st -> DecomposeState st
ReadInputChar [] s
st1)
                    | DecomposeMode -> Char -> Bool
Char.isDecomposable DecomposeMode
mode Char
ch ->
                        forall st. String -> String -> st -> DecomposeState st
IsDecomposable (DecomposeMode -> Char -> String
Char.decompose DecomposeMode
mode Char
ch) String
rebuf s
st1
                    | Bool
otherwise ->
                        if Char -> Bool
Char.isCombining Char
ch
                        then forall st. String -> st -> DecomposeState st
ReadInputChar (Char -> ShowS
insertIntoReBuf Char
ch String
rebuf) s
st1
                        else forall st. String -> DecomposeState st -> DecomposeState st
YieldCharList
                                 (String
rebuf forall a. [a] -> [a] -> [a]
++ [Char
ch])
                                 (forall st. String -> st -> DecomposeState st
ReadInputChar [] s
st1)
                Skip s
st1 -> forall st. String -> st -> DecomposeState st
ReadInputChar String
rebuf s
st1
                Step s Char
Stop -> forall st. String -> DecomposeState st -> DecomposeState st
YieldCharList String
rebuf forall st. DecomposeState st
DecomposeStop
    sstep State StreamK m Char
_ (IsHangul Char
ch s
st) =
        forall (m :: * -> *) a. Monad m => a -> m a
return
          forall a b. (a -> b) -> a -> b
$ forall s a. s -> Step s a
Skip
          forall a b. (a -> b) -> a -> b
$ let (Char
l, Char
v, Char
t) = Char -> (Char, Char, Char)
Char.decomposeHangul Char
ch
             in if Char
t forall a. Eq a => a -> a -> Bool
== Int -> Char
chr Int
Char.jamoTFirst
                then forall st. String -> DecomposeState st -> DecomposeState st
YieldCharList [Char
l, Char
v] (forall st. String -> st -> DecomposeState st
ReadInputChar [] s
st)
                else forall st. String -> DecomposeState st -> DecomposeState st
YieldCharList [Char
l, Char
v, Char
t] (forall st. String -> st -> DecomposeState st
ReadInputChar [] s
st)
    sstep State StreamK m Char
_ (IsDecomposable [] String
rebuf s
st) =
        forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall s a. s -> Step s a
Skip forall a b. (a -> b) -> a -> b
$ forall st. String -> st -> DecomposeState st
ReadInputChar String
rebuf s
st
    sstep State StreamK m Char
_ (IsDecomposable (Char
ch:String
chs) String
rebuf s
st)
        | DecomposeMode -> Char -> Bool
Char.isDecomposable DecomposeMode
mode Char
ch =
            forall (m :: * -> *) a. Monad m => a -> m a
return
              forall a b. (a -> b) -> a -> b
$ forall s a. s -> Step s a
Skip forall a b. (a -> b) -> a -> b
$ forall st. String -> String -> st -> DecomposeState st
IsDecomposable (DecomposeMode -> Char -> String
Char.decompose DecomposeMode
mode Char
ch forall a. [a] -> [a] -> [a]
++ String
chs) String
rebuf s
st
        | Bool
otherwise =
            forall (m :: * -> *) a. Monad m => a -> m a
return
              forall a b. (a -> b) -> a -> b
$ forall s a. s -> Step s a
Skip
              forall a b. (a -> b) -> a -> b
$ if Char -> Bool
Char.isCombining Char
ch
                then forall st. String -> String -> st -> DecomposeState st
IsDecomposable String
chs (Char -> ShowS
insertIntoReBuf Char
ch String
rebuf) s
st
                else forall st. String -> DecomposeState st -> DecomposeState st
YieldCharList (String
rebuf forall a. [a] -> [a] -> [a]
++ [Char
ch]) (forall st. String -> String -> st -> DecomposeState st
IsDecomposable String
chs [] s
st)
    sstep State StreamK m Char
_ DecomposeState s
DecomposeStop = forall (m :: * -> *) a. Monad m => a -> m a
return forall s a. Step s a
Stop

-- Hold an L to wait for V, hold an LV to wait for T.
data JamoBuf
    = Jamo !Char -- Jamo L, V or T
    | Hangul !Char -- Hangul Syllable LV or LVT
    | HangulLV !Char

{-# INLINE fromJamoBuf #-}
fromJamoBuf :: JamoBuf -> Char
fromJamoBuf :: JamoBuf -> Char
fromJamoBuf (Jamo Char
ch) = Char
ch
fromJamoBuf (Hangul Char
ch) = Char
ch
fromJamoBuf (HangulLV Char
ch) = Char
ch

-- {-# ANN type ComposeState Fuse #-}
data ComposeState st
    = YieldChar Char (ComposeState st)
    | YieldList [Char] (ComposeState st)
    | ComposeNone st
    | ComposeReg Int [Char] st
    | ComposeJamo JamoBuf st
    | ComposeStop

-- Assumes every character except hangul characters are fully decomposed and the
-- combining characters are reordered. Hangul characters may or may not be
-- decomposed.
{-# INLINE_EARLY partialComposeD #-}
partialComposeD :: Monad m => Stream m Char -> Stream m Char
partialComposeD :: forall (m :: * -> *). Monad m => Stream m Char -> Stream m Char
partialComposeD (Stream State StreamK m Char -> s -> m (Step s Char)
step s
state) = forall (m :: * -> *) a s.
(State StreamK m a -> s -> m (Step s a)) -> s -> Stream m a
Stream State StreamK m Char
-> ComposeState s -> m (Step (ComposeState s) Char)
step' (forall st. st -> ComposeState st
ComposeNone s
state)

    where

    {-# INLINE_NORMAL step' #-}
    step' :: State StreamK m Char
-> ComposeState s -> m (Step (ComposeState s) Char)
step' State StreamK m Char
_ ComposeState s
ComposeStop = forall (m :: * -> *) a. Monad m => a -> m a
return forall s a. Step s a
Stop
    step' State StreamK m Char
_ (YieldChar Char
ch ComposeState s
ns) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall s a. a -> s -> Step s a
Yield Char
ch ComposeState s
ns
    step' State StreamK m Char
_ (YieldList [] ComposeState s
ns) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall s a. s -> Step s a
Skip ComposeState s
ns
    step' State StreamK m Char
_ (YieldList (Char
x:String
xs) ComposeState s
ns) = forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall s a. a -> s -> Step s a
Yield Char
x forall a b. (a -> b) -> a -> b
$ forall st. String -> ComposeState st -> ComposeState st
YieldList String
xs ComposeState s
ns
    step' State StreamK m Char
gst (ComposeNone s
st) = do
        Step s Char
r <- State StreamK m Char -> s -> m (Step s Char)
step State StreamK m Char
gst s
st
        forall (m :: * -> *) a. Monad m => a -> m a
return
          forall a b. (a -> b) -> a -> b
$ case Step s Char
r of
                Yield Char
x s
st1 -> forall s a. s -> Step s a
Skip forall a b. (a -> b) -> a -> b
$ forall {st}. Char -> st -> ComposeState st
composeNone Char
x s
st1
                Skip s
st1 -> forall s a. s -> Step s a
Skip forall a b. (a -> b) -> a -> b
$ forall st. st -> ComposeState st
ComposeNone s
st1
                Step s Char
Stop -> forall s a. Step s a
Stop
    step' State StreamK m Char
gst (ComposeJamo JamoBuf
jbuf s
st) = do
        Step s Char
r <- State StreamK m Char -> s -> m (Step s Char)
step State StreamK m Char
gst s
st
        forall (m :: * -> *) a. Monad m => a -> m a
return
          forall a b. (a -> b) -> a -> b
$ case Step s Char
r of
                Yield Char
x s
st1 -> forall s a. s -> Step s a
Skip forall a b. (a -> b) -> a -> b
$ forall {st}. JamoBuf -> Char -> st -> ComposeState st
composeJamo JamoBuf
jbuf Char
x s
st1
                Skip s
st1 -> forall s a. s -> Step s a
Skip forall a b. (a -> b) -> a -> b
$ forall st. JamoBuf -> st -> ComposeState st
ComposeJamo JamoBuf
jbuf s
st1
                Step s Char
Stop -> forall s a. s -> Step s a
Skip forall a b. (a -> b) -> a -> b
$ forall st. Char -> ComposeState st -> ComposeState st
YieldChar (JamoBuf -> Char
fromJamoBuf JamoBuf
jbuf) forall st. ComposeState st
ComposeStop
    step' State StreamK m Char
gst (ComposeReg Int
i String
rbuf s
st) = do
        Step s Char
r <- State StreamK m Char -> s -> m (Step s Char)
step State StreamK m Char
gst s
st
        forall (m :: * -> *) a. Monad m => a -> m a
return
          forall a b. (a -> b) -> a -> b
$ case Step s Char
r of
                Yield Char
x s
st1 -> forall s a. s -> Step s a
Skip forall a b. (a -> b) -> a -> b
$ forall {st}. Int -> String -> Char -> st -> ComposeState st
composeReg Int
i String
rbuf Char
x s
st1
                Skip s
st1 -> forall s a. s -> Step s a
Skip forall a b. (a -> b) -> a -> b
$ forall st. Int -> String -> st -> ComposeState st
ComposeReg Int
i String
rbuf s
st1
                Step s Char
Stop -> forall s a. s -> Step s a
Skip forall a b. (a -> b) -> a -> b
$ forall st. String -> ComposeState st -> ComposeState st
YieldList String
rbuf forall st. ComposeState st
ComposeStop

    {-# INLINE initHangul #-}
    initHangul :: Char -> st -> ComposeState st
initHangul Char
c st
st = forall st. JamoBuf -> st -> ComposeState st
ComposeJamo (Char -> JamoBuf
Hangul Char
c) st
st

    {-# INLINE initJamo #-}
    initJamo :: Char -> st -> ComposeState st
initJamo Char
c st
st = forall st. JamoBuf -> st -> ComposeState st
ComposeJamo (Char -> JamoBuf
Jamo Char
c) st
st

    {-# INLINE initReg #-}
    initReg :: Char -> st -> ComposeState st
initReg !Char
c st
st = forall st. Int -> String -> st -> ComposeState st
ComposeReg Int
0 [Char
c] st
st

    {-# INLINE composeNone #-}
    composeNone :: Char -> st -> ComposeState st
composeNone Char
ch st
st
        | Char -> Bool
Char.isHangul Char
ch = forall {st}. Char -> st -> ComposeState st
initHangul Char
ch st
st
        | Char -> Bool
Char.isJamo Char
ch = forall {st}. Char -> st -> ComposeState st
initJamo Char
ch st
st
        | Bool
otherwise = forall {st}. Char -> st -> ComposeState st
initReg Char
ch st
st

    {-# INLINE composeCharHangul #-}
    composeCharHangul :: JamoBuf -> Char -> st -> ComposeState st
composeCharHangul JamoBuf
jbuf Char
ch st
st =
        forall st. Char -> ComposeState st -> ComposeState st
YieldChar (JamoBuf -> Char
fromJamoBuf JamoBuf
jbuf) forall a b. (a -> b) -> a -> b
$ forall st. JamoBuf -> st -> ComposeState st
ComposeJamo (Char -> JamoBuf
Hangul Char
ch) st
st

    {-# INLINE composeCharJamo #-}
    composeCharJamo :: JamoBuf -> Char -> st -> ComposeState st
composeCharJamo JamoBuf
jbuf Char
ch st
st
        | Int
ich forall a. Ord a => a -> a -> Bool
<= Int
Char.jamoLLast =
            forall st. Char -> ComposeState st -> ComposeState st
YieldChar (JamoBuf -> Char
fromJamoBuf JamoBuf
jbuf) forall a b. (a -> b) -> a -> b
$ forall st. JamoBuf -> st -> ComposeState st
ComposeJamo (Char -> JamoBuf
Jamo Char
ch) st
st
        | Int
ich forall a. Ord a => a -> a -> Bool
< Int
Char.jamoVFirst = forall {st}. JamoBuf -> Char -> st -> ComposeState st
flushAndWrite JamoBuf
jbuf Char
ch st
st
        | Int
ich forall a. Ord a => a -> a -> Bool
<= Int
Char.jamoVLast =
            case JamoBuf
jbuf of
                Jamo Char
c ->
                    case Char -> Maybe Int
Char.jamoLIndex Char
c of
                        Just Int
li ->
                            let vi :: Int
vi = Int
ich forall a. Num a => a -> a -> a
- Int
Char.jamoVFirst
                                lvi :: Int
lvi = Int
li forall a. Num a => a -> a -> a
* Int
Char.jamoNCount forall a. Num a => a -> a -> a
+ Int
vi forall a. Num a => a -> a -> a
* Int
Char.jamoTCount
                                lv :: Char
lv = Int -> Char
chr (Int
Char.hangulFirst forall a. Num a => a -> a -> a
+ Int
lvi)
                             in forall st. JamoBuf -> st -> ComposeState st
ComposeJamo (Char -> JamoBuf
HangulLV Char
lv) st
st
                        Maybe Int
Nothing -> forall {st}. Char -> Char -> st -> ComposeState st
writeTwo Char
c Char
ch st
st
                Hangul Char
c -> forall {st}. Char -> Char -> st -> ComposeState st
writeTwo Char
c Char
ch st
st
                HangulLV Char
c -> forall {st}. Char -> Char -> st -> ComposeState st
writeTwo Char
c Char
ch st
st
        | Int
ich forall a. Ord a => a -> a -> Bool
<= Int
Char.jamoTFirst = forall {st}. JamoBuf -> Char -> st -> ComposeState st
flushAndWrite JamoBuf
jbuf Char
ch st
st
        | Bool
otherwise = do
            let ti :: Int
ti = Int
ich forall a. Num a => a -> a -> a
- Int
Char.jamoTFirst
            case JamoBuf
jbuf of
                Jamo Char
c -> forall {st}. Char -> Char -> st -> ComposeState st
writeTwo Char
c Char
ch st
st
                Hangul Char
c
                    | Char -> Bool
Char.isHangulLV Char
c -> forall {st}. Char -> Int -> st -> ComposeState st
writeLVT Char
c Int
ti st
st
                    | Bool
otherwise -> forall {st}. Char -> Char -> st -> ComposeState st
writeTwo Char
c Char
ch st
st
                HangulLV Char
c -> forall {st}. Char -> Int -> st -> ComposeState st
writeLVT Char
c Int
ti st
st

        where

        flushAndWrite :: JamoBuf -> Char -> st -> ComposeState st
flushAndWrite JamoBuf
jb Char
c st
s = forall st. String -> ComposeState st -> ComposeState st
YieldList [JamoBuf -> Char
fromJamoBuf JamoBuf
jb, Char
c] forall a b. (a -> b) -> a -> b
$ forall st. st -> ComposeState st
ComposeNone st
s

        writeLVT :: Char -> Int -> st -> ComposeState st
writeLVT Char
lv Int
ti st
s =
            let lvt :: Char
lvt = Int -> Char
chr forall a b. (a -> b) -> a -> b
$ Char -> Int
ord Char
lv forall a. Num a => a -> a -> a
+ Int
ti
             in forall st. Char -> ComposeState st -> ComposeState st
YieldChar Char
lvt forall a b. (a -> b) -> a -> b
$ forall st. st -> ComposeState st
ComposeNone st
s

        writeTwo :: Char -> Char -> st -> ComposeState st
writeTwo Char
c1 Char
c2 st
s = forall st. String -> ComposeState st -> ComposeState st
YieldList [Char
c1, Char
c2] forall a b. (a -> b) -> a -> b
$ forall st. st -> ComposeState st
ComposeNone st
s

        ich :: Int
ich = Char -> Int
ord Char
ch

    {-# INLINE composeJamo #-}
    composeJamo :: JamoBuf -> Char -> st -> ComposeState st
composeJamo JamoBuf
jbuf Char
ch st
st
        | Char -> Bool
Char.isJamo Char
ch = forall {st}. JamoBuf -> Char -> st -> ComposeState st
composeCharJamo JamoBuf
jbuf Char
ch st
st
        | Char -> Bool
Char.isHangul Char
ch = forall {st}. JamoBuf -> Char -> st -> ComposeState st
composeCharHangul JamoBuf
jbuf Char
ch st
st
        | Bool
otherwise = forall st. Char -> ComposeState st -> ComposeState st
YieldChar (JamoBuf -> Char
fromJamoBuf JamoBuf
jbuf) (forall st. Int -> String -> st -> ComposeState st
ComposeReg Int
0 [Char
ch] st
st)

    -- i ~ Char.combiningClass (last rbuf)
    {-# INLINE composeCharCombining #-}
    composeCharCombining :: Int -> String -> Char -> st -> ComposeState st
composeCharCombining Int
i String
rbuf Char
ch st
st =
        if Int
cch forall a. Ord a => a -> a -> Bool
> Int
i
        then case Char -> Char -> Maybe Char
Char.compose Char
str Char
ch of
                 Maybe Char
Nothing -> forall st. Int -> String -> st -> ComposeState st
ComposeReg Int
cch (String
rbuf forall a. [a] -> [a] -> [a]
++ [Char
ch]) st
st
                 Just Char
x -> forall st. Int -> String -> st -> ComposeState st
ComposeReg Int
i (Char
x forall a. a -> [a] -> [a]
: forall a. [a] -> [a]
tail String
rbuf) st
st
        else forall st. Int -> String -> st -> ComposeState st
ComposeReg Int
i (String
rbuf forall a. [a] -> [a] -> [a]
++ [Char
ch]) st
st

        where

        str :: Char
str = forall a. [a] -> a
head String
rbuf
        cch :: Int
cch = Char -> Int
Char.combiningClass Char
ch

    {-# INLINE composeReg #-}
    composeReg :: Int -> String -> Char -> st -> ComposeState st
composeReg Int
i String
rbuf !Char
ch !st
st
        | Char -> Bool
Char.isHangul Char
ch = forall st. String -> ComposeState st -> ComposeState st
YieldList String
rbuf forall a b. (a -> b) -> a -> b
$ forall {st}. Char -> st -> ComposeState st
initHangul Char
ch st
st
        | Char -> Bool
Char.isJamo Char
ch = forall st. String -> ComposeState st -> ComposeState st
YieldList String
rbuf forall a b. (a -> b) -> a -> b
$ forall {st}. Char -> st -> ComposeState st
initJamo Char
ch st
st
        | Char -> Bool
Char.isCombining Char
ch = forall {st}. Int -> String -> Char -> st -> ComposeState st
composeCharCombining Int
i String
rbuf Char
ch st
st
        | [Char
s] <- String
rbuf
        , Char -> Bool
Char.isCombiningStarter Char
ch
        , Just Char
x <- Char -> Char -> Maybe Char
Char.composeStarters Char
s Char
ch = forall st. Int -> String -> st -> ComposeState st
ComposeReg Int
0 [Char
x] st
st
        | Bool
otherwise = forall st. String -> ComposeState st -> ComposeState st
YieldList String
rbuf forall a b. (a -> b) -> a -> b
$ forall st. Int -> String -> st -> ComposeState st
ComposeReg Int
0 [Char
ch] st
st

normalizeD :: Monad m => NormalizationMode -> Stream m Char -> Stream m Char
normalizeD :: forall (m :: * -> *).
Monad m =>
NormalizationMode -> Stream m Char -> Stream m Char
normalizeD NormalizationMode
NFD = forall (m :: * -> *).
Monad m =>
Bool -> DecomposeMode -> Stream m Char -> Stream m Char
decomposeD Bool
True DecomposeMode
Canonical
normalizeD NormalizationMode
NFKD = forall (m :: * -> *).
Monad m =>
Bool -> DecomposeMode -> Stream m Char -> Stream m Char
decomposeD Bool
True DecomposeMode
Kompat
normalizeD NormalizationMode
NFC = forall (m :: * -> *). Monad m => Stream m Char -> Stream m Char
partialComposeD forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *).
Monad m =>
Bool -> DecomposeMode -> Stream m Char -> Stream m Char
decomposeD Bool
False DecomposeMode
Canonical
normalizeD NormalizationMode
NFKC = forall (m :: * -> *). Monad m => Stream m Char -> Stream m Char
partialComposeD forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *).
Monad m =>
Bool -> DecomposeMode -> Stream m Char -> Stream m Char
decomposeD Bool
False DecomposeMode
Kompat

normalize ::
       Monad m
    => NormalizationMode
    -> Stream.Stream m Char
    -> Stream.Stream m Char
normalize :: forall (m :: * -> *).
Monad m =>
NormalizationMode -> Stream m Char -> Stream m Char
normalize = forall (m :: * -> *).
Monad m =>
NormalizationMode -> Stream m Char -> Stream m Char
normalizeD