{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
module Waargonaut.Types.JChar
(
JChar (..)
, AsJChar (..)
, HasJChar (..)
, parseJChar
, utf8CharToJChar
, jCharToUtf8Char
, jCharToChar
, charToJChar
) where
import Prelude (Char, Eq, Ord, Show,
otherwise, (/=))
import Control.Category (id, (.))
import Control.Lens (Lens', Prism', preview,
prism, review)
import Control.Applicative ((<$>), (<|>))
import Data.Bits ((.&.))
import Data.Char (ord)
import Data.Either (Either (..))
import Data.Foldable (Foldable, asum)
import Data.Function (($))
import Data.Functor (Functor)
import Data.Maybe (Maybe (..), fromMaybe)
import Data.Traversable (Traversable)
import qualified Data.Text.Internal as Text
import Data.Digit (HeXDigit, HeXaDeCiMaL)
import qualified Data.Digit as D
import Text.Parser.Char (CharParsing)
import Waargonaut.Types.JChar.HexDigit4 (HexDigit4 (..))
import Waargonaut.Types.JChar.Escaped (AsEscaped (..), Escaped (..),
charToEscaped, escapedToChar,
parseEscaped)
import Waargonaut.Types.JChar.Unescaped (AsUnescaped (..), Unescaped,
parseUnescaped)
data JChar digit
= EscapedJChar ( Escaped digit )
| UnescapedJChar Unescaped
deriving (Eq, Ord, Show, Functor, Foldable, Traversable)
class HasJChar c digit | c -> digit where
jChar :: Lens' c (JChar digit)
instance HasJChar (JChar digit) digit where
jChar = id
class AsJChar r digit | r -> digit where
_JChar :: Prism' r (JChar digit)
_EscapedJChar :: Prism' r (Escaped digit)
_UnescapedJChar :: Prism' r Unescaped
_EscapedJChar = _JChar . _EscapedJChar
_UnescapedJChar = _JChar . _UnescapedJChar
instance AsJChar (JChar digit) digit where
_JChar = id
_EscapedJChar = prism EscapedJChar
(\ x -> case x of
EscapedJChar y1 -> Right y1
_ -> Left x
)
_UnescapedJChar = prism UnescapedJChar
(\ x -> case x of
UnescapedJChar y1 -> Right y1
_ -> Left x
)
instance AsEscaped (JChar digit) digit where
_Escaped = _JChar . _Escaped
instance AsUnescaped (JChar digit) where
_Unescaped = _JChar . _Unescaped
jCharToChar :: JChar HeXDigit -> Char
jCharToChar (UnescapedJChar uejc) = review _Unescaped uejc
jCharToChar (EscapedJChar ejc) = escapedToChar ejc
charToJChar :: Char -> Maybe (JChar HeXDigit)
charToJChar c =
(UnescapedJChar <$> preview _Unescaped c) <|>
(EscapedJChar <$> charToEscaped c)
utf8SafeChar :: Char -> Maybe Char
utf8SafeChar c | ord c .&. 0x1ff800 /= 0xd800 = Just c
| otherwise = Nothing
utf8CharToJChar :: Char -> JChar HeXDigit
utf8CharToJChar c = fromMaybe scalarReplacement (charToJChar $ Text.safe c)
where scalarReplacement = EscapedJChar (Hex (HexDigit4 D.xf D.xf D.xf D.xd))
{-# INLINE utf8CharToJChar #-}
jCharToUtf8Char :: JChar HeXDigit -> Maybe Char
jCharToUtf8Char = utf8SafeChar . jCharToChar
{-# INLINE jCharToUtf8Char #-}
parseJChar ::
(CharParsing f, HeXaDeCiMaL digit) =>
f ( JChar digit )
parseJChar = asum
[ EscapedJChar <$> parseEscaped
, UnescapedJChar <$> parseUnescaped
]