-- |
-- Module      :  Text.Megaparsec.Stream
-- Copyright   :  © 2015–present Megaparsec contributors
-- License     :  FreeBSD
--
-- Maintainer  :  Mark Karpov <markkarpov92@gmail.com>
-- Stability   :  experimental
-- Portability :  portable
--
-- Megaparsec's input stream facilities.
--
-- You probably do not want to import this module directly because
-- "Text.Megaparsec" re-exports it anyway.
--
-- @since 6.0.0

{-# LANGUAGE FlexibleContexts    #-}
{-# LANGUAGE FlexibleInstances   #-}
{-# LANGUAGE LambdaCase          #-}
{-# LANGUAGE MultiWayIf          #-}
{-# LANGUAGE RankNTypes          #-}
{-# LANGUAGE RecordWildCards     #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies        #-}

module Text.Megaparsec.Stream
  ( Stream (..) )
where

import Data.Char (chr)
import Data.Foldable (foldl')
import Data.Kind (Type)
import Data.List.NonEmpty (NonEmpty (..))
import Data.Maybe (fromMaybe)
import Data.Proxy
import Data.Word (Word8)
import Text.Megaparsec.Pos
import Text.Megaparsec.State
import qualified Data.ByteString            as B
import qualified Data.ByteString.Char8      as B8
import qualified Data.ByteString.Lazy       as BL
import qualified Data.ByteString.Lazy.Char8 as BL8
import qualified Data.List.NonEmpty         as NE
import qualified Data.Text                  as T
import qualified Data.Text.Lazy             as TL

-- | Type class for inputs that can be consumed by the library.

class (Ord (Token s), Ord (Tokens s)) => Stream s where

  -- | Type of token in the stream.

  type Token s :: Type

  -- | Type of “chunk” of the stream.

  type Tokens s :: Type

  -- | Lift a single token to chunk of the stream. The default
  -- implementation is:
  --
  -- > tokenToChunk pxy = tokensToChunk pxy . pure
  --
  -- However for some types of stream there may be a more efficient way to
  -- lift.

  tokenToChunk  :: Proxy s -> Token s -> Tokens s
  tokenToChunk Proxy s
pxy = Proxy s -> [Token s] -> Tokens s
forall s. Stream s => Proxy s -> [Token s] -> Tokens s
tokensToChunk Proxy s
pxy ([Token s] -> Tokens s)
-> (Token s -> [Token s]) -> Token s -> Tokens s
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Token s -> [Token s]
forall (f :: * -> *) a. Applicative f => a -> f a
pure

  -- | The first method that establishes isomorphism between list of tokens
  -- and chunk of the stream. Valid implementation should satisfy:
  --
  -- > chunkToTokens pxy (tokensToChunk pxy ts) == ts

  tokensToChunk :: Proxy s -> [Token s] -> Tokens s

  -- | The second method that establishes isomorphism between list of tokens
  -- and chunk of the stream. Valid implementation should satisfy:
  --
  -- > tokensToChunk pxy (chunkToTokens pxy chunk) == chunk

  chunkToTokens :: Proxy s -> Tokens s -> [Token s]

  -- | Return length of a chunk of the stream.

  chunkLength :: Proxy s -> Tokens s -> Int

  -- | Check if a chunk of the stream is empty. The default implementation
  -- is in terms of the more general 'chunkLength':
  --
  -- > chunkEmpty pxy ts = chunkLength pxy ts <= 0
  --
  -- However for many streams there may be a more efficient implementation.

  chunkEmpty :: Proxy s -> Tokens s -> Bool
  chunkEmpty Proxy s
pxy Tokens s
ts = Proxy s -> Tokens s -> Int
forall s. Stream s => Proxy s -> Tokens s -> Int
chunkLength Proxy s
pxy Tokens s
ts Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0

  -- | Extract a single token form the stream. Return 'Nothing' if the
  -- stream is empty.

  take1_ :: s -> Maybe (Token s, s)

  -- | @'takeN_' n s@ should try to extract a chunk of length @n@, or if the
  -- stream is too short, the rest of the stream. Valid implementation
  -- should follow the rules:
  --
  --     * If the requested length @n@ is 0 (or less), 'Nothing' should
  --       never be returned, instead @'Just' (\"\", s)@ should be returned,
  --       where @\"\"@ stands for the empty chunk, and @s@ is the original
  --       stream (second argument).
  --     * If the requested length is greater than 0 and the stream is
  --       empty, 'Nothing' should be returned indicating end of input.
  --     * In other cases, take chunk of length @n@ (or shorter if the
  --       stream is not long enough) from the input stream and return the
  --       chunk along with the rest of the stream.

  takeN_ :: Int -> s -> Maybe (Tokens s, s)

  -- | Extract chunk of the stream taking tokens while the supplied
  -- predicate returns 'True'. Return the chunk and the rest of the stream.
  --
  -- For many types of streams, the method allows for significant
  -- performance improvements, although it is not strictly necessary from
  -- conceptual point of view.

  takeWhile_ :: (Token s -> Bool) -> s -> (Tokens s, s)

  -- | Pretty-print non-empty stream of tokens. This function is also used
  -- to print single tokens (represented as singleton lists).
  --
  -- @since 7.0.0

  showTokens :: Proxy s -> NonEmpty (Token s) -> String

  -- | Return the number of characters that a non-empty stream of tokens
  -- spans. The default implementation is sufficient if every token spans
  -- exactly 1 character.
  --
  -- @since 8.0.0

  tokensLength :: Proxy s -> NonEmpty (Token s) -> Int
  tokensLength Proxy s
Proxy = NonEmpty (Token s) -> Int
forall a. NonEmpty a -> Int
NE.length

  -- | Given an offset @o@ and initial 'PosState', adjust the state in such
  -- a way that it starts at the offset.
  --
  -- Return two values (in order):
  --
  --     * 'String' representing the line on which the given offset @o@ is
  --       located. The line should satisfy a number of conditions that are
  --       described below.
  --     * The updated 'PosState' which can be in turn used to locate
  --       another offset @o'@ given that @o' >= o@.
  --
  -- The 'String' representing the offending line in input stream should
  -- satisfy the following:
  --
  --     * It should adequately represent location of token at the offset of
  --       interest, that is, character at 'sourceColumn' of the returned
  --       'SourcePos' should correspond to the token at the offset @o@.
  --     * It should not include the newline at the end.
  --     * It should not be empty, if the line happens to be empty, it
  --       should be replaced with the string @\"\<empty line\>\"@.
  --     * Tab characters should be replaced by appropriate number of
  --       spaces, which is determined by the 'pstateTabWidth' field of
  --       'PosState'.
  --
  -- __Note__: type signature of the function was changed in the version
  -- /8.0.0/.
  --
  -- @since 7.0.0

  reachOffset
    :: Int             -- ^ Offset to reach
    -> PosState s      -- ^ Initial 'PosState' to use
    -> (String, PosState s) -- ^ See the description of the function

  -- | A version of 'reachOffset' that may be faster because it doesn't need
  -- to fetch the line at which the given offset in located.
  --
  -- The default implementation is this:
  --
  -- > reachOffsetNoLine o pst =
  -- >   snd (reachOffset o pst)
  --
  -- __Note__: type signature of the function was changed in the version
  -- /8.0.0/.
  --
  -- @since 7.0.0

  reachOffsetNoLine
    :: Int             -- ^ Offset to reach
    -> PosState s      -- ^ Initial 'PosState' to use
    -> PosState s      -- ^ Reached source position and updated state
  reachOffsetNoLine Int
o PosState s
pst =
    (String, PosState s) -> PosState s
forall a b. (a, b) -> b
snd (Int -> PosState s -> (String, PosState s)
forall s. Stream s => Int -> PosState s -> (String, PosState s)
reachOffset Int
o PosState s
pst)

instance Stream String where
  type Token String = Char
  type Tokens String = String
  tokenToChunk :: Proxy String -> Token String -> Tokens String
tokenToChunk Proxy String
Proxy = Token String -> Tokens String
forall (f :: * -> *) a. Applicative f => a -> f a
pure
  tokensToChunk :: Proxy String -> [Token String] -> Tokens String
tokensToChunk Proxy String
Proxy = [Token String] -> Tokens String
forall a. a -> a
id
  chunkToTokens :: Proxy String -> Tokens String -> [Token String]
chunkToTokens Proxy String
Proxy = Tokens String -> [Token String]
forall a. a -> a
id
  chunkLength :: Proxy String -> Tokens String -> Int
chunkLength Proxy String
Proxy = Tokens String -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length
  chunkEmpty :: Proxy String -> Tokens String -> Bool
chunkEmpty Proxy String
Proxy = Tokens String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null
  take1_ :: String -> Maybe (Token String, String)
take1_ [] = Maybe (Token String, String)
forall a. Maybe a
Nothing
  take1_ (Char
t:String
ts) = (Char, String) -> Maybe (Char, String)
forall a. a -> Maybe a
Just (Char
t, String
ts)
  takeN_ :: Int -> String -> Maybe (Tokens String, String)
takeN_ Int
n String
s
    | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0    = (String, String) -> Maybe (String, String)
forall a. a -> Maybe a
Just (String
"", String
s)
    | String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null String
s    = Maybe (Tokens String, String)
forall a. Maybe a
Nothing
    | Bool
otherwise = (String, String) -> Maybe (String, String)
forall a. a -> Maybe a
Just (Int -> String -> (String, String)
forall a. Int -> [a] -> ([a], [a])
splitAt Int
n String
s)
  takeWhile_ :: (Token String -> Bool) -> String -> (Tokens String, String)
takeWhile_ = (Token String -> Bool) -> String -> (Tokens String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
span
  showTokens :: Proxy String -> NonEmpty (Token String) -> String
showTokens Proxy String
Proxy = NonEmpty Char -> String
NonEmpty (Token String) -> String
stringPretty
  -- NOTE Do not eta-reduce these (breaks inlining)
  reachOffset :: Int -> PosState String -> (String, PosState String)
reachOffset Int
o PosState String
pst =
    (Int -> String -> (Tokens String, String))
-> (forall b. (b -> Token String -> b) -> b -> Tokens String -> b)
-> (Tokens String -> String)
-> (Token String -> Char)
-> (Token String, Token String)
-> Int
-> PosState String
-> (String, PosState String)
forall s.
Stream s =>
(Int -> s -> (Tokens s, s))
-> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
-> (Tokens s -> String)
-> (Token s -> Char)
-> (Token s, Token s)
-> Int
-> PosState s
-> (String, PosState s)
reachOffset' Int -> String -> (Tokens String, String)
forall a. Int -> [a] -> ([a], [a])
splitAt forall b. (b -> Token String -> b) -> b -> Tokens String -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Tokens String -> String
forall a. a -> a
id Token String -> Char
forall a. a -> a
id (Char
Token String
'\n',Char
Token String
'\t') Int
o PosState String
pst
  reachOffsetNoLine :: Int -> PosState String -> PosState String
reachOffsetNoLine Int
o PosState String
pst =
    (Int -> String -> (Tokens String, String))
-> (forall b. (b -> Token String -> b) -> b -> Tokens String -> b)
-> (Token String, Token String)
-> Int
-> PosState String
-> PosState String
forall s.
Stream s =>
(Int -> s -> (Tokens s, s))
-> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
-> (Token s, Token s)
-> Int
-> PosState s
-> PosState s
reachOffsetNoLine' Int -> String -> (Tokens String, String)
forall a. Int -> [a] -> ([a], [a])
splitAt forall b. (b -> Token String -> b) -> b -> Tokens String -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' (Char
Token String
'\n', Char
Token String
'\t') Int
o PosState String
pst

instance Stream B.ByteString where
  type Token B.ByteString = Word8
  type Tokens B.ByteString = B.ByteString
  tokenToChunk :: Proxy ByteString -> Token ByteString -> Tokens ByteString
tokenToChunk Proxy ByteString
Proxy = Word8 -> ByteString
Token ByteString -> Tokens ByteString
B.singleton
  tokensToChunk :: Proxy ByteString -> [Token ByteString] -> Tokens ByteString
tokensToChunk Proxy ByteString
Proxy = [Word8] -> ByteString
[Token ByteString] -> Tokens ByteString
B.pack
  chunkToTokens :: Proxy ByteString -> Tokens ByteString -> [Token ByteString]
chunkToTokens Proxy ByteString
Proxy = ByteString -> [Word8]
Tokens ByteString -> [Token ByteString]
B.unpack
  chunkLength :: Proxy ByteString -> Tokens ByteString -> Int
chunkLength Proxy ByteString
Proxy = ByteString -> Int
Tokens ByteString -> Int
B.length
  chunkEmpty :: Proxy ByteString -> Tokens ByteString -> Bool
chunkEmpty Proxy ByteString
Proxy = ByteString -> Bool
Tokens ByteString -> Bool
B.null
  take1_ :: ByteString -> Maybe (Token ByteString, ByteString)
take1_ = ByteString -> Maybe (Word8, ByteString)
ByteString -> Maybe (Token ByteString, ByteString)
B.uncons
  takeN_ :: Int -> ByteString -> Maybe (Tokens ByteString, ByteString)
takeN_ Int
n ByteString
s
    | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0    = (ByteString, ByteString) -> Maybe (ByteString, ByteString)
forall a. a -> Maybe a
Just (ByteString
B.empty, ByteString
s)
    | ByteString -> Bool
B.null ByteString
s  = Maybe (Tokens ByteString, ByteString)
forall a. Maybe a
Nothing
    | Bool
otherwise = (ByteString, ByteString) -> Maybe (ByteString, ByteString)
forall a. a -> Maybe a
Just (Int -> ByteString -> (ByteString, ByteString)
B.splitAt Int
n ByteString
s)
  takeWhile_ :: (Token ByteString -> Bool)
-> ByteString -> (Tokens ByteString, ByteString)
takeWhile_ = (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
(Token ByteString -> Bool)
-> ByteString -> (Tokens ByteString, ByteString)
B.span
  showTokens :: Proxy ByteString -> NonEmpty (Token ByteString) -> String
showTokens Proxy ByteString
Proxy = NonEmpty Char -> String
stringPretty (NonEmpty Char -> String)
-> (NonEmpty Word8 -> NonEmpty Char) -> NonEmpty Word8 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word8 -> Char) -> NonEmpty Word8 -> NonEmpty Char
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int -> Char
chr (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)
  -- NOTE Do not eta-reduce these (breaks inlining)
  reachOffset :: Int -> PosState ByteString -> (String, PosState ByteString)
reachOffset Int
o PosState ByteString
pst =
    (Int -> ByteString -> (Tokens ByteString, ByteString))
-> (forall b.
    (b -> Token ByteString -> b) -> b -> Tokens ByteString -> b)
-> (Tokens ByteString -> String)
-> (Token ByteString -> Char)
-> (Token ByteString, Token ByteString)
-> Int
-> PosState ByteString
-> (String, PosState ByteString)
forall s.
Stream s =>
(Int -> s -> (Tokens s, s))
-> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
-> (Tokens s -> String)
-> (Token s -> Char)
-> (Token s, Token s)
-> Int
-> PosState s
-> (String, PosState s)
reachOffset' Int -> ByteString -> (ByteString, ByteString)
Int -> ByteString -> (Tokens ByteString, ByteString)
B.splitAt forall a. (a -> Word8 -> a) -> a -> ByteString -> a
forall b.
(b -> Token ByteString -> b) -> b -> Tokens ByteString -> b
B.foldl' ByteString -> String
Tokens ByteString -> String
B8.unpack (Int -> Char
chr (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) (Token ByteString
10, Token ByteString
9) Int
o PosState ByteString
pst
  reachOffsetNoLine :: Int -> PosState ByteString -> PosState ByteString
reachOffsetNoLine Int
o PosState ByteString
pst =
    (Int -> ByteString -> (Tokens ByteString, ByteString))
-> (forall b.
    (b -> Token ByteString -> b) -> b -> Tokens ByteString -> b)
-> (Token ByteString, Token ByteString)
-> Int
-> PosState ByteString
-> PosState ByteString
forall s.
Stream s =>
(Int -> s -> (Tokens s, s))
-> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
-> (Token s, Token s)
-> Int
-> PosState s
-> PosState s
reachOffsetNoLine' Int -> ByteString -> (ByteString, ByteString)
Int -> ByteString -> (Tokens ByteString, ByteString)
B.splitAt forall a. (a -> Word8 -> a) -> a -> ByteString -> a
forall b.
(b -> Token ByteString -> b) -> b -> Tokens ByteString -> b
B.foldl' (Token ByteString
10, Token ByteString
9) Int
o PosState ByteString
pst

instance Stream BL.ByteString where
  type Token BL.ByteString = Word8
  type Tokens BL.ByteString = BL.ByteString
  tokenToChunk :: Proxy ByteString -> Token ByteString -> Tokens ByteString
tokenToChunk Proxy ByteString
Proxy = Word8 -> ByteString
Token ByteString -> Tokens ByteString
BL.singleton
  tokensToChunk :: Proxy ByteString -> [Token ByteString] -> Tokens ByteString
tokensToChunk Proxy ByteString
Proxy = [Word8] -> ByteString
[Token ByteString] -> Tokens ByteString
BL.pack
  chunkToTokens :: Proxy ByteString -> Tokens ByteString -> [Token ByteString]
chunkToTokens Proxy ByteString
Proxy = ByteString -> [Word8]
Tokens ByteString -> [Token ByteString]
BL.unpack
  chunkLength :: Proxy ByteString -> Tokens ByteString -> Int
chunkLength Proxy ByteString
Proxy = Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int64 -> Int) -> (ByteString -> Int64) -> ByteString -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Int64
BL.length
  chunkEmpty :: Proxy ByteString -> Tokens ByteString -> Bool
chunkEmpty Proxy ByteString
Proxy = ByteString -> Bool
Tokens ByteString -> Bool
BL.null
  take1_ :: ByteString -> Maybe (Token ByteString, ByteString)
take1_ = ByteString -> Maybe (Word8, ByteString)
ByteString -> Maybe (Token ByteString, ByteString)
BL.uncons
  takeN_ :: Int -> ByteString -> Maybe (Tokens ByteString, ByteString)
takeN_ Int
n ByteString
s
    | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0    = (ByteString, ByteString) -> Maybe (ByteString, ByteString)
forall a. a -> Maybe a
Just (ByteString
BL.empty, ByteString
s)
    | ByteString -> Bool
BL.null ByteString
s = Maybe (Tokens ByteString, ByteString)
forall a. Maybe a
Nothing
    | Bool
otherwise = (ByteString, ByteString) -> Maybe (ByteString, ByteString)
forall a. a -> Maybe a
Just (Int64 -> ByteString -> (ByteString, ByteString)
BL.splitAt (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n) ByteString
s)
  takeWhile_ :: (Token ByteString -> Bool)
-> ByteString -> (Tokens ByteString, ByteString)
takeWhile_ = (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
(Token ByteString -> Bool)
-> ByteString -> (Tokens ByteString, ByteString)
BL.span
  showTokens :: Proxy ByteString -> NonEmpty (Token ByteString) -> String
showTokens Proxy ByteString
Proxy = NonEmpty Char -> String
stringPretty (NonEmpty Char -> String)
-> (NonEmpty Word8 -> NonEmpty Char) -> NonEmpty Word8 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Word8 -> Char) -> NonEmpty Word8 -> NonEmpty Char
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int -> Char
chr (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)
  -- NOTE Do not eta-reduce these (breaks inlining)
  reachOffset :: Int -> PosState ByteString -> (String, PosState ByteString)
reachOffset Int
o PosState ByteString
pst =
    (Int -> ByteString -> (Tokens ByteString, ByteString))
-> (forall b.
    (b -> Token ByteString -> b) -> b -> Tokens ByteString -> b)
-> (Tokens ByteString -> String)
-> (Token ByteString -> Char)
-> (Token ByteString, Token ByteString)
-> Int
-> PosState ByteString
-> (String, PosState ByteString)
forall s.
Stream s =>
(Int -> s -> (Tokens s, s))
-> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
-> (Tokens s -> String)
-> (Token s -> Char)
-> (Token s, Token s)
-> Int
-> PosState s
-> (String, PosState s)
reachOffset' Int -> ByteString -> (ByteString, ByteString)
Int -> ByteString -> (Tokens ByteString, ByteString)
splitAtBL forall a. (a -> Word8 -> a) -> a -> ByteString -> a
forall b.
(b -> Token ByteString -> b) -> b -> Tokens ByteString -> b
BL.foldl' ByteString -> String
Tokens ByteString -> String
BL8.unpack (Int -> Char
chr (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) (Token ByteString
10, Token ByteString
9) Int
o PosState ByteString
pst
  reachOffsetNoLine :: Int -> PosState ByteString -> PosState ByteString
reachOffsetNoLine Int
o PosState ByteString
pst =
    (Int -> ByteString -> (Tokens ByteString, ByteString))
-> (forall b.
    (b -> Token ByteString -> b) -> b -> Tokens ByteString -> b)
-> (Token ByteString, Token ByteString)
-> Int
-> PosState ByteString
-> PosState ByteString
forall s.
Stream s =>
(Int -> s -> (Tokens s, s))
-> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
-> (Token s, Token s)
-> Int
-> PosState s
-> PosState s
reachOffsetNoLine' Int -> ByteString -> (ByteString, ByteString)
Int -> ByteString -> (Tokens ByteString, ByteString)
splitAtBL forall a. (a -> Word8 -> a) -> a -> ByteString -> a
forall b.
(b -> Token ByteString -> b) -> b -> Tokens ByteString -> b
BL.foldl' (Token ByteString
10, Token ByteString
9) Int
o PosState ByteString
pst

instance Stream T.Text where
  type Token T.Text = Char
  type Tokens T.Text = T.Text
  tokenToChunk :: Proxy Text -> Token Text -> Tokens Text
tokenToChunk Proxy Text
Proxy = Char -> Text
Token Text -> Tokens Text
T.singleton
  tokensToChunk :: Proxy Text -> [Token Text] -> Tokens Text
tokensToChunk Proxy Text
Proxy = String -> Text
[Token Text] -> Tokens Text
T.pack
  chunkToTokens :: Proxy Text -> Tokens Text -> [Token Text]
chunkToTokens Proxy Text
Proxy = Text -> String
Tokens Text -> [Token Text]
T.unpack
  chunkLength :: Proxy Text -> Tokens Text -> Int
chunkLength Proxy Text
Proxy = Text -> Int
Tokens Text -> Int
T.length
  chunkEmpty :: Proxy Text -> Tokens Text -> Bool
chunkEmpty Proxy Text
Proxy = Text -> Bool
Tokens Text -> Bool
T.null
  take1_ :: Text -> Maybe (Token Text, Text)
take1_ = Text -> Maybe (Char, Text)
Text -> Maybe (Token Text, Text)
T.uncons
  takeN_ :: Int -> Text -> Maybe (Tokens Text, Text)
takeN_ Int
n Text
s
    | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0    = (Text, Text) -> Maybe (Text, Text)
forall a. a -> Maybe a
Just (Text
T.empty, Text
s)
    | Text -> Bool
T.null Text
s  = Maybe (Tokens Text, Text)
forall a. Maybe a
Nothing
    | Bool
otherwise = (Text, Text) -> Maybe (Text, Text)
forall a. a -> Maybe a
Just (Int -> Text -> (Text, Text)
T.splitAt Int
n Text
s)
  takeWhile_ :: (Token Text -> Bool) -> Text -> (Tokens Text, Text)
takeWhile_ = (Char -> Bool) -> Text -> (Text, Text)
(Token Text -> Bool) -> Text -> (Tokens Text, Text)
T.span
  showTokens :: Proxy Text -> NonEmpty (Token Text) -> String
showTokens Proxy Text
Proxy = NonEmpty Char -> String
NonEmpty (Token Text) -> String
stringPretty
  -- NOTE Do not eta-reduce (breaks inlining of reachOffset').
  reachOffset :: Int -> PosState Text -> (String, PosState Text)
reachOffset Int
o PosState Text
pst =
    (Int -> Text -> (Tokens Text, Text))
-> (forall b. (b -> Token Text -> b) -> b -> Tokens Text -> b)
-> (Tokens Text -> String)
-> (Token Text -> Char)
-> (Token Text, Token Text)
-> Int
-> PosState Text
-> (String, PosState Text)
forall s.
Stream s =>
(Int -> s -> (Tokens s, s))
-> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
-> (Tokens s -> String)
-> (Token s -> Char)
-> (Token s, Token s)
-> Int
-> PosState s
-> (String, PosState s)
reachOffset' Int -> Text -> (Text, Text)
Int -> Text -> (Tokens Text, Text)
T.splitAt forall a. (a -> Char -> a) -> a -> Text -> a
forall b. (b -> Token Text -> b) -> b -> Tokens Text -> b
T.foldl' Text -> String
Tokens Text -> String
T.unpack Token Text -> Char
forall a. a -> a
id (Char
Token Text
'\n', Char
Token Text
'\t') Int
o PosState Text
pst
  reachOffsetNoLine :: Int -> PosState Text -> PosState Text
reachOffsetNoLine Int
o PosState Text
pst =
    (Int -> Text -> (Tokens Text, Text))
-> (forall b. (b -> Token Text -> b) -> b -> Tokens Text -> b)
-> (Token Text, Token Text)
-> Int
-> PosState Text
-> PosState Text
forall s.
Stream s =>
(Int -> s -> (Tokens s, s))
-> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
-> (Token s, Token s)
-> Int
-> PosState s
-> PosState s
reachOffsetNoLine' Int -> Text -> (Text, Text)
Int -> Text -> (Tokens Text, Text)
T.splitAt forall a. (a -> Char -> a) -> a -> Text -> a
forall b. (b -> Token Text -> b) -> b -> Tokens Text -> b
T.foldl' (Char
Token Text
'\n', Char
Token Text
'\t') Int
o PosState Text
pst

instance Stream TL.Text where
  type Token TL.Text  = Char
  type Tokens TL.Text = TL.Text
  tokenToChunk :: Proxy Text -> Token Text -> Tokens Text
tokenToChunk Proxy Text
Proxy = Char -> Text
Token Text -> Tokens Text
TL.singleton
  tokensToChunk :: Proxy Text -> [Token Text] -> Tokens Text
tokensToChunk Proxy Text
Proxy = String -> Text
[Token Text] -> Tokens Text
TL.pack
  chunkToTokens :: Proxy Text -> Tokens Text -> [Token Text]
chunkToTokens Proxy Text
Proxy = Text -> String
Tokens Text -> [Token Text]
TL.unpack
  chunkLength :: Proxy Text -> Tokens Text -> Int
chunkLength Proxy Text
Proxy = Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int64 -> Int) -> (Text -> Int64) -> Text -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Int64
TL.length
  chunkEmpty :: Proxy Text -> Tokens Text -> Bool
chunkEmpty Proxy Text
Proxy = Text -> Bool
Tokens Text -> Bool
TL.null
  take1_ :: Text -> Maybe (Token Text, Text)
take1_ = Text -> Maybe (Char, Text)
Text -> Maybe (Token Text, Text)
TL.uncons
  takeN_ :: Int -> Text -> Maybe (Tokens Text, Text)
takeN_ Int
n Text
s
    | Int
n Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
0    = (Text, Text) -> Maybe (Text, Text)
forall a. a -> Maybe a
Just (Text
TL.empty, Text
s)
    | Text -> Bool
TL.null Text
s = Maybe (Tokens Text, Text)
forall a. Maybe a
Nothing
    | Bool
otherwise = (Text, Text) -> Maybe (Text, Text)
forall a. a -> Maybe a
Just (Int64 -> Text -> (Text, Text)
TL.splitAt (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n) Text
s)
  takeWhile_ :: (Token Text -> Bool) -> Text -> (Tokens Text, Text)
takeWhile_ = (Char -> Bool) -> Text -> (Text, Text)
(Token Text -> Bool) -> Text -> (Tokens Text, Text)
TL.span
  showTokens :: Proxy Text -> NonEmpty (Token Text) -> String
showTokens Proxy Text
Proxy = NonEmpty Char -> String
NonEmpty (Token Text) -> String
stringPretty
  -- NOTE Do not eta-reduce (breaks inlining of reachOffset').
  reachOffset :: Int -> PosState Text -> (String, PosState Text)
reachOffset Int
o PosState Text
pst =
    (Int -> Text -> (Tokens Text, Text))
-> (forall b. (b -> Token Text -> b) -> b -> Tokens Text -> b)
-> (Tokens Text -> String)
-> (Token Text -> Char)
-> (Token Text, Token Text)
-> Int
-> PosState Text
-> (String, PosState Text)
forall s.
Stream s =>
(Int -> s -> (Tokens s, s))
-> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
-> (Tokens s -> String)
-> (Token s -> Char)
-> (Token s, Token s)
-> Int
-> PosState s
-> (String, PosState s)
reachOffset' Int -> Text -> (Text, Text)
Int -> Text -> (Tokens Text, Text)
splitAtTL forall a. (a -> Char -> a) -> a -> Text -> a
forall b. (b -> Token Text -> b) -> b -> Tokens Text -> b
TL.foldl' Text -> String
Tokens Text -> String
TL.unpack Token Text -> Char
forall a. a -> a
id (Char
Token Text
'\n', Char
Token Text
'\t') Int
o PosState Text
pst
  reachOffsetNoLine :: Int -> PosState Text -> PosState Text
reachOffsetNoLine Int
o PosState Text
pst =
    (Int -> Text -> (Tokens Text, Text))
-> (forall b. (b -> Token Text -> b) -> b -> Tokens Text -> b)
-> (Token Text, Token Text)
-> Int
-> PosState Text
-> PosState Text
forall s.
Stream s =>
(Int -> s -> (Tokens s, s))
-> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
-> (Token s, Token s)
-> Int
-> PosState s
-> PosState s
reachOffsetNoLine' Int -> Text -> (Text, Text)
Int -> Text -> (Tokens Text, Text)
splitAtTL forall a. (a -> Char -> a) -> a -> Text -> a
forall b. (b -> Token Text -> b) -> b -> Tokens Text -> b
TL.foldl' (Char
Token Text
'\n', Char
Token Text
'\t') Int
o PosState Text
pst

----------------------------------------------------------------------------
-- Helpers

-- | An internal helper state type combining a difference 'String' and an
-- unboxed 'SourcePos'.

data St = St SourcePos ShowS

-- | A helper definition to facilitate defining 'reachOffset' for various
-- stream types.

reachOffset'
  :: forall s. Stream s
  => (Int -> s -> (Tokens s, s))
     -- ^ How to split input stream at given offset
  -> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
     -- ^ How to fold over input stream
  -> (Tokens s -> String)
     -- ^ How to convert chunk of input stream into a 'String'
  -> (Token s -> Char)
     -- ^ How to convert a token into a 'Char'
  -> (Token s, Token s)
     -- ^ Newline token and tab token
  -> Int
     -- ^ Offset to reach
  -> PosState s
     -- ^ Initial 'PosState' to use
  -> (String, PosState s)
     -- ^ Line at which 'SourcePos' is located, updated 'PosState'
reachOffset' :: (Int -> s -> (Tokens s, s))
-> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
-> (Tokens s -> String)
-> (Token s -> Char)
-> (Token s, Token s)
-> Int
-> PosState s
-> (String, PosState s)
reachOffset' Int -> s -> (Tokens s, s)
splitAt'
             forall b. (b -> Token s -> b) -> b -> Tokens s -> b
foldl''
             Tokens s -> String
fromToks
             Token s -> Char
fromTok
             (Token s
newlineTok, Token s
tabTok)
             Int
o
             PosState {s
Int
String
SourcePos
Pos
pstateLinePrefix :: forall s. PosState s -> String
pstateTabWidth :: forall s. PosState s -> Pos
pstateSourcePos :: forall s. PosState s -> SourcePos
pstateOffset :: forall s. PosState s -> Int
pstateInput :: forall s. PosState s -> s
pstateLinePrefix :: String
pstateTabWidth :: Pos
pstateSourcePos :: SourcePos
pstateOffset :: Int
pstateInput :: s
..} =
  ( case Pos -> String -> String
expandTab Pos
pstateTabWidth
           (String -> String)
-> ((Tokens s, s) -> String) -> (Tokens s, s) -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
addPrefix
           (String -> String)
-> ((Tokens s, s) -> String) -> (Tokens s, s) -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
f
           (String -> String)
-> ((Tokens s, s) -> String) -> (Tokens s, s) -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Tokens s -> String
fromToks
           (Tokens s -> String)
-> ((Tokens s, s) -> Tokens s) -> (Tokens s, s) -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Tokens s, s) -> Tokens s
forall a b. (a, b) -> a
fst
           ((Tokens s, s) -> String) -> (Tokens s, s) -> String
forall a b. (a -> b) -> a -> b
$ (Token s -> Bool) -> s -> (Tokens s, s)
forall s. Stream s => (Token s -> Bool) -> s -> (Tokens s, s)
takeWhile_ (Token s -> Token s -> Bool
forall a. Eq a => a -> a -> Bool
/= Token s
newlineTok) s
post of
      String
"" -> String
"<empty line>"
      String
xs -> String
xs
  , PosState :: forall s. s -> Int -> SourcePos -> Pos -> String -> PosState s
PosState
      { pstateInput :: s
pstateInput = s
post
      , pstateOffset :: Int
pstateOffset = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
pstateOffset Int
o
      , pstateSourcePos :: SourcePos
pstateSourcePos = SourcePos
spos
      , pstateTabWidth :: Pos
pstateTabWidth = Pos
pstateTabWidth
      , pstateLinePrefix :: String
pstateLinePrefix =
          if Bool
sameLine
            -- NOTE We don't use difference lists here because it's
            -- desirable for 'PosState' to be an instance of 'Eq' and
            -- 'Show'. So we just do appending here. Fortunately several
            -- parse errors on the same line should be relatively rare.
            then String
pstateLinePrefix String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
f String
""
            else String -> String
f String
""
      }
  )
  where
    addPrefix :: String -> String
addPrefix String
xs =
      if Bool
sameLine
        then String
pstateLinePrefix String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
xs
        else String
xs
    sameLine :: Bool
sameLine = SourcePos -> Pos
sourceLine SourcePos
spos Pos -> Pos -> Bool
forall a. Eq a => a -> a -> Bool
== SourcePos -> Pos
sourceLine SourcePos
pstateSourcePos
    (Tokens s
pre, s
post) = Int -> s -> (Tokens s, s)
splitAt' (Int
o Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
pstateOffset) s
pstateInput
    St SourcePos
spos String -> String
f = (St -> Token s -> St) -> St -> Tokens s -> St
forall b. (b -> Token s -> b) -> b -> Tokens s -> b
foldl'' St -> Token s -> St
go (SourcePos -> (String -> String) -> St
St SourcePos
pstateSourcePos String -> String
forall a. a -> a
id) Tokens s
pre
    go :: St -> Token s -> St
go (St SourcePos
apos String -> String
g) Token s
ch =
      let SourcePos String
n Pos
l Pos
c = SourcePos
apos
          c' :: Int
c' = Pos -> Int
unPos Pos
c
          w :: Int
w  = Pos -> Int
unPos Pos
pstateTabWidth
      in if | Token s
ch Token s -> Token s -> Bool
forall a. Eq a => a -> a -> Bool
== Token s
newlineTok ->
                SourcePos -> (String -> String) -> St
St (String -> Pos -> Pos -> SourcePos
SourcePos String
n (Pos
l Pos -> Pos -> Pos
forall a. Semigroup a => a -> a -> a
<> Pos
pos1) Pos
pos1)
                   String -> String
forall a. a -> a
id
            | Token s
ch Token s -> Token s -> Bool
forall a. Eq a => a -> a -> Bool
== Token s
tabTok ->
                SourcePos -> (String -> String) -> St
St (String -> Pos -> Pos -> SourcePos
SourcePos String
n Pos
l (Int -> Pos
mkPos (Int -> Pos) -> Int -> Pos
forall a b. (a -> b) -> a -> b
$ Int
c' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- ((Int
c' Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
w)))
                   (String -> String
g (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Token s -> Char
fromTok Token s
ch Char -> String -> String
forall a. a -> [a] -> [a]
:))
            | Bool
otherwise ->
                SourcePos -> (String -> String) -> St
St (String -> Pos -> Pos -> SourcePos
SourcePos String
n Pos
l (Pos
c Pos -> Pos -> Pos
forall a. Semigroup a => a -> a -> a
<> Pos
pos1))
                   (String -> String
g (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Token s -> Char
fromTok Token s
ch Char -> String -> String
forall a. a -> [a] -> [a]
:))
{-# INLINE reachOffset' #-}

-- | Like 'reachOffset'' but for 'reachOffsetNoLine'.

reachOffsetNoLine'
  :: forall s. Stream s
  => (Int -> s -> (Tokens s, s))
     -- ^ How to split input stream at given offset
  -> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
     -- ^ How to fold over input stream
  -> (Token s, Token s)
     -- ^ Newline token and tab token
  -> Int
     -- ^ Offset to reach
  -> PosState s
     -- ^ Initial 'PosState' to use
  -> PosState s
     -- ^ Updated 'PosState'
reachOffsetNoLine' :: (Int -> s -> (Tokens s, s))
-> (forall b. (b -> Token s -> b) -> b -> Tokens s -> b)
-> (Token s, Token s)
-> Int
-> PosState s
-> PosState s
reachOffsetNoLine' Int -> s -> (Tokens s, s)
splitAt'
                   forall b. (b -> Token s -> b) -> b -> Tokens s -> b
foldl''
                   (Token s
newlineTok, Token s
tabTok)
                   Int
o
                   PosState {s
Int
String
SourcePos
Pos
pstateLinePrefix :: String
pstateTabWidth :: Pos
pstateSourcePos :: SourcePos
pstateOffset :: Int
pstateInput :: s
pstateLinePrefix :: forall s. PosState s -> String
pstateTabWidth :: forall s. PosState s -> Pos
pstateSourcePos :: forall s. PosState s -> SourcePos
pstateOffset :: forall s. PosState s -> Int
pstateInput :: forall s. PosState s -> s
..} =
  ( PosState :: forall s. s -> Int -> SourcePos -> Pos -> String -> PosState s
PosState
      { pstateInput :: s
pstateInput = s
post
      , pstateOffset :: Int
pstateOffset = Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
pstateOffset Int
o
      , pstateSourcePos :: SourcePos
pstateSourcePos = SourcePos
spos
      , pstateTabWidth :: Pos
pstateTabWidth = Pos
pstateTabWidth
      , pstateLinePrefix :: String
pstateLinePrefix = String
pstateLinePrefix
      }
  )
  where
    spos :: SourcePos
spos = (SourcePos -> Token s -> SourcePos)
-> SourcePos -> Tokens s -> SourcePos
forall b. (b -> Token s -> b) -> b -> Tokens s -> b
foldl'' SourcePos -> Token s -> SourcePos
go SourcePos
pstateSourcePos Tokens s
pre
    (Tokens s
pre, s
post) = Int -> s -> (Tokens s, s)
splitAt' (Int
o Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
pstateOffset) s
pstateInput
    go :: SourcePos -> Token s -> SourcePos
go (SourcePos String
n Pos
l Pos
c) Token s
ch =
      let c' :: Int
c' = Pos -> Int
unPos Pos
c
          w :: Int
w  = Pos -> Int
unPos Pos
pstateTabWidth
      in if | Token s
ch Token s -> Token s -> Bool
forall a. Eq a => a -> a -> Bool
== Token s
newlineTok ->
                String -> Pos -> Pos -> SourcePos
SourcePos String
n (Pos
l Pos -> Pos -> Pos
forall a. Semigroup a => a -> a -> a
<> Pos
pos1) Pos
pos1
            | Token s
ch Token s -> Token s -> Bool
forall a. Eq a => a -> a -> Bool
== Token s
tabTok ->
                String -> Pos -> Pos -> SourcePos
SourcePos String
n Pos
l (Int -> Pos
mkPos (Int -> Pos) -> Int -> Pos
forall a b. (a -> b) -> a -> b
$ Int
c' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
w Int -> Int -> Int
forall a. Num a => a -> a -> a
- ((Int
c' Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
w))
            | Bool
otherwise ->
                String -> Pos -> Pos -> SourcePos
SourcePos String
n Pos
l (Pos
c Pos -> Pos -> Pos
forall a. Semigroup a => a -> a -> a
<> Pos
pos1)
{-# INLINE reachOffsetNoLine' #-}

-- | Like 'BL.splitAt' but accepts the index as an 'Int'.

splitAtBL :: Int -> BL.ByteString -> (BL.ByteString, BL.ByteString)
splitAtBL :: Int -> ByteString -> (ByteString, ByteString)
splitAtBL Int
n = Int64 -> ByteString -> (ByteString, ByteString)
BL.splitAt (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n)
{-# INLINE splitAtBL #-}

-- | Like 'TL.splitAt' but accepts the index as an 'Int'.

splitAtTL :: Int -> TL.Text -> (TL.Text, TL.Text)
splitAtTL :: Int -> Text -> (Text, Text)
splitAtTL Int
n = Int64 -> Text -> (Text, Text)
TL.splitAt (Int -> Int64
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n)
{-# INLINE splitAtTL #-}

-- | @stringPretty s@ returns pretty representation of string @s@. This is
-- used when printing string tokens in error messages.

stringPretty :: NonEmpty Char -> String
stringPretty :: NonEmpty Char -> String
stringPretty (Char
x:|[])      = Char -> String
charPretty Char
x
stringPretty (Char
'\r':|String
"\n") = String
"crlf newline"
stringPretty NonEmpty Char
xs           = String
"\"" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> (Char -> String) -> String -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap Char -> String
f (NonEmpty Char -> String
forall a. NonEmpty a -> [a]
NE.toList NonEmpty Char
xs) String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"\""
  where
    f :: Char -> String
f Char
ch =
      case Char -> Maybe String
charPretty' Char
ch of
        Maybe String
Nothing     -> [Char
ch]
        Just String
pretty -> String
"<" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
pretty String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
">"

-- | @charPretty ch@ returns user-friendly string representation of given
-- character @ch@, suitable for using in error messages.

charPretty :: Char -> String
charPretty :: Char -> String
charPretty Char
' ' = String
"space"
charPretty Char
ch = String -> Maybe String -> String
forall a. a -> Maybe a -> a
fromMaybe (String
"'" String -> String -> String
forall a. Semigroup a => a -> a -> a
<> [Char
ch] String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
"'") (Char -> Maybe String
charPretty' Char
ch)

-- | If the given character has a pretty representation, return that,
-- otherwise 'Nothing'. This is an internal helper.

charPretty' :: Char -> Maybe String
charPretty' :: Char -> Maybe String
charPretty' = \case
  Char
'\NUL' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"null"
  Char
'\SOH' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"start of heading"
  Char
'\STX' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"start of text"
  Char
'\ETX' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"end of text"
  Char
'\EOT' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"end of transmission"
  Char
'\ENQ' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"enquiry"
  Char
'\ACK' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"acknowledge"
  Char
'\BEL' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"bell"
  Char
'\BS'  -> String -> Maybe String
forall a. a -> Maybe a
Just String
"backspace"
  Char
'\t'   -> String -> Maybe String
forall a. a -> Maybe a
Just String
"tab"
  Char
'\n'   -> String -> Maybe String
forall a. a -> Maybe a
Just String
"newline"
  Char
'\v'   -> String -> Maybe String
forall a. a -> Maybe a
Just String
"vertical tab"
  Char
'\f'   -> String -> Maybe String
forall a. a -> Maybe a
Just String
"form feed"
  Char
'\r'   -> String -> Maybe String
forall a. a -> Maybe a
Just String
"carriage return"
  Char
'\SO'  -> String -> Maybe String
forall a. a -> Maybe a
Just String
"shift out"
  Char
'\SI'  -> String -> Maybe String
forall a. a -> Maybe a
Just String
"shift in"
  Char
'\DLE' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"data link escape"
  Char
'\DC1' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"device control one"
  Char
'\DC2' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"device control two"
  Char
'\DC3' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"device control three"
  Char
'\DC4' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"device control four"
  Char
'\NAK' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"negative acknowledge"
  Char
'\SYN' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"synchronous idle"
  Char
'\ETB' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"end of transmission block"
  Char
'\CAN' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"cancel"
  Char
'\EM'  -> String -> Maybe String
forall a. a -> Maybe a
Just String
"end of medium"
  Char
'\SUB' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"substitute"
  Char
'\ESC' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"escape"
  Char
'\FS'  -> String -> Maybe String
forall a. a -> Maybe a
Just String
"file separator"
  Char
'\GS'  -> String -> Maybe String
forall a. a -> Maybe a
Just String
"group separator"
  Char
'\RS'  -> String -> Maybe String
forall a. a -> Maybe a
Just String
"record separator"
  Char
'\US'  -> String -> Maybe String
forall a. a -> Maybe a
Just String
"unit separator"
  Char
'\DEL' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"delete"
  Char
'\160' -> String -> Maybe String
forall a. a -> Maybe a
Just String
"non-breaking space"
  Char
_      -> Maybe String
forall a. Maybe a
Nothing

-- | Replace tab characters with given number of spaces.

expandTab
  :: Pos
  -> String
  -> String
expandTab :: Pos -> String -> String
expandTab Pos
w' = Int -> String -> String
go Int
0
  where
    go :: Int -> String -> String
go Int
0 []        = []
    go Int
0 (Char
'\t':String
xs) = Int -> String -> String
go Int
w String
xs
    go Int
0 (Char
x:String
xs)    = Char
x Char -> String -> String
forall a. a -> [a] -> [a]
: Int -> String -> String
go Int
0 String
xs
    go Int
n String
xs        = Char
' ' Char -> String -> String
forall a. a -> [a] -> [a]
: Int -> String -> String
go (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) String
xs
    w :: Int
w              = Pos -> Int
unPos Pos
w'