{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}

-- | Stream algebra and coalgebra for token streams.
--
-- This module re-exports the neutral stream interface from @circuits@ and
-- supplies the concrete instances for lists, 'ByteString', and 'Text'.
module Circuit.Parser.Stream
  ( -- * Boundary result
    These (..),

    -- * Stream coalgebra
    Uncons (..),

    -- * Stream algebra (left construction dual)
    Cons (..),

    -- * Stream algebra (right construction dual)
    Snoc (..),
  )
where

import Circuit.Stream (Cons (..), Snoc (..), These (..), Uncons (..))
import Data.ByteString (ByteString)
import Data.ByteString qualified as B
import Data.Text (Text)
import Data.Text qualified as T
import Data.Word (Word8)

-- | Strict 'ByteString' as a stream of 'Char'.
--
-- /Warning/: bytes @>= 0x80@ are exposed as raw-byte 'Char's via
-- @toEnum . fromIntegral@, not decoded UTF-8. If you parse text content
-- char-wise and convert to 'String' or 'Text', multibyte characters will
-- mojibake. The safe pattern is to recognise structure byte-wise and decode
-- complete spans once (see the json/csv dialects for examples).
instance Uncons ByteString Char where
  uncons :: ByteString -> These Char ByteString
uncons ByteString
bs' = case ByteString -> Maybe (Word8, ByteString)
B.uncons ByteString
bs' of
    Maybe (Word8, ByteString)
Nothing -> ByteString -> These Char ByteString
forall a b. b -> These a b
That ByteString
bs'
    Just (Word8
w, ByteString
rest)
      | ByteString -> Bool
B.null ByteString
rest -> Char -> These Char ByteString
forall a b. a -> These a b
This (Word8 -> Char
w2c Word8
w)
      | Bool
otherwise -> Char -> ByteString -> These Char ByteString
forall a b. a -> b -> These a b
These (Word8 -> Char
w2c Word8
w) ByteString
rest
    where
      w2c :: Word8 -> Char
w2c = Int -> Char
forall a. Enum a => Int -> a
toEnum (Int -> Char) -> (Word8 -> Int) -> Word8 -> Char
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral
  nil :: ByteString
nil = ByteString
B.empty

instance Uncons ByteString Word8 where
  uncons :: ByteString -> These Word8 ByteString
uncons ByteString
bs' = case ByteString -> Maybe (Word8, ByteString)
B.uncons ByteString
bs' of
    Maybe (Word8, ByteString)
Nothing -> ByteString -> These Word8 ByteString
forall a b. b -> These a b
That ByteString
bs'
    Just (Word8
w, ByteString
rest)
      | ByteString -> Bool
B.null ByteString
rest -> Word8 -> These Word8 ByteString
forall a b. a -> These a b
This Word8
w
      | Bool
otherwise -> Word8 -> ByteString -> These Word8 ByteString
forall a b. a -> b -> These a b
These Word8
w ByteString
rest
  nil :: ByteString
nil = ByteString
B.empty

instance Uncons Text Char where
  uncons :: Text -> These Char Text
uncons Text
t = case Text -> Maybe (Char, Text)
T.uncons Text
t of
    Maybe (Char, Text)
Nothing -> Text -> These Char Text
forall a b. b -> These a b
That Text
t
    Just (Char
c, Text
rest)
      | Text -> Bool
T.null Text
rest -> Char -> These Char Text
forall a b. a -> These a b
This Char
c
      | Bool
otherwise -> Char -> Text -> These Char Text
forall a b. a -> b -> These a b
These Char
c Text
rest
  nil :: Text
nil = Text
T.empty