{-# OPTIONS_HADDOCK not-home #-}

-- | Scans of the input around an index, outside of the parser monad.
--
-- This module is intended for internal use only, and may change without warning
-- in subsequent releases.
module Yamlet.Internal.Parser.Scan
  ( isBom
  , skipBoms
  , skipSpaces
  , skipWhites
  , breakEnd
  , isStartOfLine
  , lineStartAt
  , previousLineStart
  , lineEndAt
  , nextLineStart
  , contentLineAbove
  , markerLength
  , isMarker
  , isStartMarker
  , isEndMarker
  , startsPrefix
  , bomBeforeContent
  , canEndFlowNode
  , fitsKey
  ) where

import Yamlet.Internal.Chars
import Yamlet.Internal.Parser.Monad
import Yamlet.Internal.Utils

isBom :: Env -> Int -> Bool
isBom :: Env -> Int -> Bool
isBom Env
e = Array -> Int -> Int -> Bool
isBomIn Env
e.array Env
e.end

skipBoms :: Env -> Int -> Int
skipBoms :: Env -> Int -> Int
skipBoms Env
e = Array -> Int -> Int -> Int
skipBomsIn Env
e.array Env
e.end

skipSpaces :: Env -> Int -> Int
skipSpaces :: Env -> Int -> Int
skipSpaces Env
e Int
i = if Env -> Int -> Word8
byteAt Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
SPACE then Env -> Int -> Int
skipSpaces Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) else Int
i

skipWhites :: Env -> Int -> Int
skipWhites :: Env -> Int -> Int
skipWhites Env
e Int
i = if Word8 -> Bool
isWhite (Env -> Int -> Word8
byteAt Env
e Int
i) then Env -> Int -> Int
skipWhites Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) else Int
i

-- | The index after the line break at the index.
breakEnd :: Env -> Int -> Int
breakEnd :: Env -> Int -> Int
breakEnd Env
e Int
i
  | Env -> Int -> Word8
byteAt Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
CR Bool -> Bool -> Bool
&& Env -> Int -> Word8
byteAt Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
LF = Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2
  | Bool
otherwise = Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1

isStartOfLine :: Env -> Int -> Bool
isStartOfLine :: Env -> Int -> Bool
isStartOfLine Env
e Int
i
  | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Env
e.base = Bool
True
  | Word8 -> Bool
isBreak (Env -> Int -> Word8
byteBefore Env
e Int
i) = Bool
True
  | Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
bomLength Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Env
e.base Bool -> Bool -> Bool
&& Env -> Int -> Bool
isBom Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
bomLength) = Env -> Int -> Bool
isStartOfLine Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
bomLength)
  | Bool
otherwise = Bool
False

-- | The start of the line that contains the index.
lineStartAt :: Env -> Int -> Int
lineStartAt :: Env -> Int -> Int
lineStartAt Env
e Int
i
  | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Env
e.base Bool -> Bool -> Bool
&& Bool -> Bool
not (Word8 -> Bool
isBreak (Env -> Int -> Word8
byteBefore Env
e Int
i)) = Env -> Int -> Int
lineStartAt Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
  | Bool
otherwise = Int
i

-- | The start of the line above the line that starts at the index, which is
-- not the first line.
previousLineStart :: Env -> Int -> Int
previousLineStart :: Env -> Int -> Int
previousLineStart Env
e Int
i = Env -> Int -> Int
lineStartAt Env
e (Int -> Int
breakStart (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1))
  where
    -- The start of the line break that ends at the index, e.g. of CR LF.
    breakStart :: Int -> Int
    breakStart :: Int -> Int
breakStart Int
j
      | Int
j Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Env
e.base Bool -> Bool -> Bool
&& Env -> Int -> Word8
byteBefore Env
e Int
j Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
CR Bool -> Bool -> Bool
&& Env -> Int -> Word8
byteAt Env
e Int
j Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
LF = Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
      | Bool
otherwise = Int
j

-- | The index of the line break that ends the line of the index, or the end
-- of the input.
lineEndAt :: Env -> Int -> Int
lineEndAt :: Env -> Int -> Int
lineEndAt Env
e Int
i
  | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Env
e.end Bool -> Bool -> Bool
&& Bool -> Bool
not (Word8 -> Bool
isBreak (Env -> Int -> Word8
byteAt Env
e Int
i)) = Env -> Int -> Int
lineEndAt Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
  | Bool
otherwise = Int
i

-- | The start of the line below the line of the index, or the end of the
-- input.
nextLineStart :: Env -> Int -> Int
nextLineStart :: Env -> Int -> Int
nextLineStart Env
e Int
i = let j :: Int
j = Env -> Int -> Int
lineEndAt Env
e Int
i in if Int
j Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Env
e.end then Env -> Int -> Int
breakEnd Env
e Int
j else Int
j

-- | The start of the closest line above the line that starts at the index
-- with content other than a comment. Byte order marks at the start of a line
-- do not count as content.
contentLineAbove :: Env -> Int -> Maybe Int
contentLineAbove :: Env -> Int -> Maybe Int
contentLineAbove Env
e Int
start
  | Int
start Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Env
e.base = Maybe Int
forall a. Maybe a
Nothing
  | Bool
otherwise =
      let prev :: Int
prev = Env -> Int -> Int
previousLineStart Env
e Int
start
          b :: Word8
b = Env -> Int -> Word8
byteAt Env
e (Env -> Int -> Int
skipWhites Env
e (Env -> Int -> Int
skipBoms Env
e Int
prev))
      in if Word8 -> Bool
isBreak Word8
b Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
HASH then Env -> Int -> Maybe Int
contentLineAbove Env
e Int
prev else Int -> Maybe Int
forall a. a -> Maybe a
Just Int
prev

-- | The number of characters of a @---@ or @...@ marker.
markerLength :: Int
markerLength :: Int
markerLength = Int
3

-- | A @---@ or @...@ marker at the start of a line.
isMarker :: Env -> Int -> Bool
isMarker :: Env -> Int -> Bool
isMarker Env
e Int
i =
  let w :: Word8
w = Env -> Int -> Word8
byteAt Env
e Int
i
      after :: Word8
after = Env -> Int -> Word8
byteAt Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
markerLength)
  in (Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
MINUS Bool -> Bool -> Bool
|| Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
DOT)
       Bool -> Bool -> Bool
&& (Int -> Bool) -> [Int] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all (\Int
j -> Env -> Int -> Word8
byteAt Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
j) Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
w) [Int
1 .. Int
markerLength Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]
       Bool -> Bool -> Bool
&& (Word8
after Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0 Bool -> Bool -> Bool
|| Word8 -> Bool
isWhite Word8
after Bool -> Bool -> Bool
|| Word8 -> Bool
isBreak Word8
after)
       Bool -> Bool -> Bool
&& Env -> Int -> Bool
isStartOfLine Env
e Int
i

-- | A @---@ marker at the start of a line.
isStartMarker :: Env -> Int -> Bool
isStartMarker :: Env -> Int -> Bool
isStartMarker Env
e Int
i = Env -> Int -> Bool
isMarker Env
e Int
i Bool -> Bool -> Bool
&& Env -> Int -> Word8
byteAt Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
MINUS

-- | A @...@ marker at the start of a line.
isEndMarker :: Env -> Int -> Bool
isEndMarker :: Env -> Int -> Bool
isEndMarker Env
e Int
i = Env -> Int -> Bool
isMarker Env
e Int
i Bool -> Bool -> Bool
&& Env -> Int -> Word8
byteAt Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
DOT

-- | A byte order mark at the start of a line. Outside a quoted scalar, it
-- starts the prefix of the next document, so the content of a document ends
-- before it.
startsPrefix :: Env -> Int -> Bool
startsPrefix :: Env -> Int -> Bool
startsPrefix Env
e Int
i = Env -> Int -> Bool
isBom Env
e Int
i Bool -> Bool -> Bool
&& Env -> Int -> Bool
isStartOfLine Env
e Int
i

-- | Byte order marks at the index before something other than a document
-- marker or a directive. Such marks at the start of a line inside a document
-- are an error.
bomBeforeContent :: Env -> Int -> Bool
bomBeforeContent :: Env -> Int -> Bool
bomBeforeContent Env
e Int
i =
  Env -> Int -> Bool
isBom Env
e Int
i Bool -> Bool -> Bool
&& let j :: Int
j = Env -> Int -> Int
skipBoms Env
e Int
i in Bool -> Bool
not (Env -> Int -> Bool
isMarker Env
e Int
j Bool -> Bool -> Bool
|| Env -> Int -> Word8
byteAt Env
e Int
j Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
PERCENT)

-- | A quoted scalar or a flow collection can end before the index: white
-- space, a comment, a line break, the end of the input, a colon or the end
-- of a flow entry follows.
canEndFlowNode :: Env -> Int -> Bool
canEndFlowNode :: Env -> Int -> Bool
canEndFlowNode Env
e Int
i =
  let j :: Int
j = Env -> Int -> Int
skipWhites Env
e Int
i
      w :: Word8
w = Env -> Int -> Word8
byteAt Env
e Int
j
  in Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0
       Bool -> Bool -> Bool
|| Word8 -> Bool
isBreak Word8
w
       Bool -> Bool -> Bool
|| Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COLON
       Bool -> Bool -> Bool
|| Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COMMA
       Bool -> Bool -> Bool
|| Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
RBRACKET
       Bool -> Bool -> Bool
|| Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
RBRACE
       Bool -> Bool -> Bool
|| (Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
HASH Bool -> Bool -> Bool
&& Int
j Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
i)

-- | The input between the indices fits in an implicit key.
fitsKey :: Env -> Int -> Int -> Bool
fitsKey :: Env -> Int -> Int -> Bool
fitsKey Env
e Int
p Int
q =
  Int
q Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
p Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
maxImplicitKeyLength
    Bool -> Bool -> Bool
|| ( Int
q Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
p Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
maxImplicitKeyLength Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
maxCharBytes
           Bool -> Bool -> Bool
&& Int
countChars Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
maxImplicitKeyLength
       )
  where
    -- The longest UTF-8 encoding of a character.
    maxCharBytes :: Int
    maxCharBytes :: Int
maxCharBytes = Int
4

    countChars :: Int
    countChars :: Int
countChars =
      [()] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length
        [() | Int
x <- [Int
p .. Int
q Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1], Word8 -> Bool
isCharStart (Env -> Int -> Word8
byteAt Env
e Int
x)]