-- | Numeric primitives and string conversions for "Circuit.Parser".
module Circuit.Parser.Primitives
  ( -- * Character predicates
    isDigit,
    isLatinLetter,

    -- * Numeric parsers
    digit,
    digits,
    int,
    double,
    signed,

    -- * String / ByteString conversions
    strToUtf8,
    utf8ToStr,
  )
where

import Circuit.Parser
import Data.ByteString (ByteString)
import Data.Char (isAsciiLower, isAsciiUpper, ord)
import Data.Functor.Identity (Identity)
import Data.Text qualified as T
import Data.Text.Encoding
import Data.Text.Encoding.Error

-- | Is the character an ASCII digit?
isDigit :: Char -> Bool
isDigit :: Char -> Bool
isDigit Char
c = Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
>= Char
'0' Bool -> Bool -> Bool
&& Char
c Char -> Char -> Bool
forall a. Ord a => a -> a -> Bool
<= Char
'9'

-- | Is the character a Latin letter?
isLatinLetter :: Char -> Bool
isLatinLetter :: Char -> Bool
isLatinLetter Char
c = Char -> Bool
isAsciiLower Char
c Bool -> Bool -> Bool
|| Char -> Bool
isAsciiUpper Char
c

-- | Parse a single digit.
digit :: Parser Identity ByteString Char Int
digit :: Parser Identity ByteString Char Int
digit = (\Char
c -> Char -> Int
ord Char
c Int -> Int -> Int
forall a. Num a => a -> a -> a
- Char -> Int
ord Char
'0') (Char -> Int)
-> Parser Identity ByteString Char Char
-> Parser Identity ByteString Char Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Char -> Bool) -> Parser Identity ByteString Char Char
forall (m :: * -> *) f s.
(Monad m, Uncons f s) =>
(s -> Bool) -> Parser m f s s
satisfy Char -> Bool
isDigit

-- | Parse one or more digits, returning @(place, value)@ where @place@ is
-- @10 ^ number_of_digits@.
--
-- >>> import Circuit.Parser (runParserIdentity)
-- >>> import Data.ByteString.Char8 qualified as B
-- >>> runParserIdentity digits (B.pack "123")
-- These (1000,123) ""
digits :: Parser Identity ByteString Char (Int, Int)
digits :: Parser Identity ByteString Char (Int, Int)
digits = do
  ds <- Parser Identity ByteString Char Int
-> Parser Identity ByteString Char [Int]
forall (m :: * -> *) f s a.
(Monad m, Uncons f s) =>
Parser m f s a -> Parser m f s [a]
some Parser Identity ByteString Char Int
digit
  let place = Int
10 Int -> Int -> Int
forall a b. (Num a, Integral b) => a -> b -> a
^ [Int] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Int]
ds
      n = (Int -> Int -> Int) -> Int -> [Int] -> Int
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl (\Int
acc Int
d -> Int
acc Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
10 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
d) Int
0 [Int]
ds
  return (place, n)

-- | Parse a non-empty sequence of digits as an integer.
int :: Parser Identity ByteString Char Int
int :: Parser Identity ByteString Char Int
int = do
  (place, n) <- Parser Identity ByteString Char (Int, Int)
digits
  if place == 1 then empty else return n

-- | Parse a floating-point number.
double :: Parser Identity ByteString Char Double
double :: Parser Identity ByteString Char Double
double = do
  (placel, nl) <- Parser Identity ByteString Char (Int, Int)
digits
  mfrac <- optional (char '.' *> digits)
  case mfrac of
    Maybe (Int, Int)
Nothing ->
      if Int
placel Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
1 then Parser Identity ByteString Char Double
forall a. Parser Identity ByteString Char a
forall (f :: * -> *) a. Alternative f => f a
empty else Double -> Parser Identity ByteString Char Double
forall a. a -> Parser Identity ByteString Char a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
nl)
    Just (Int
placer, Int
nr) ->
      Double -> Parser Identity ByteString Char Double
forall a. a -> Parser Identity ByteString Char a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
nl Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
nr Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
placer)

-- | Optionally negate the result of a parser.
signed :: (Num a) => Parser Identity ByteString Char a -> Parser Identity ByteString Char a
signed :: forall a.
Num a =>
Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
signed Parser Identity ByteString Char a
p = do
  m <- Parser Identity ByteString Char Char
-> Parser Identity ByteString Char (Maybe Char)
forall (m :: * -> *) f s a.
(Monad m, Uncons f s) =>
Parser m f s a -> Parser m f s (Maybe a)
optional (Char -> Parser Identity ByteString Char Char
forall (m :: * -> *) f s.
(Monad m, Uncons f s, Eq s) =>
s -> Parser m f s s
char Char
'-')
  case m of
    Maybe Char
Nothing -> Parser Identity ByteString Char a
p
    Just Char
_ -> a -> a
forall a. Num a => a -> a
negate (a -> a)
-> Parser Identity ByteString Char a
-> Parser Identity ByteString Char a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Identity ByteString Char a
p

-- | Encode a 'String' as UTF-8 'ByteString'.
strToUtf8 :: String -> ByteString
strToUtf8 :: String -> ByteString
strToUtf8 = Text -> ByteString
encodeUtf8 (Text -> ByteString) -> (String -> Text) -> String -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
T.pack

-- | Decode a UTF-8 'ByteString' to 'String'.
utf8ToStr :: ByteString -> String
utf8ToStr :: ByteString -> String
utf8ToStr = Text -> String
T.unpack (Text -> String) -> (ByteString -> Text) -> ByteString -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. OnDecodeError -> ByteString -> Text
decodeUtf8With OnDecodeError
lenientDecode