{-# OPTIONS_HADDOCK not-home #-}

-- | The messages of parse errors. The parser only knows the position at
-- which it failed, so these functions look at the input around it to name
-- the likely mistake, e.g. a missing colon after a key.
--
-- This module is intended for internal use only, and may change without warning
-- in subsequent releases.
module Yamlet.Internal.Parser.Hints
  ( unexpected
  , flowError
  , codePointName
  , firstTab
  , tabMessage
  , keyLengthMessage
  ) where

import Control.Monad
import Data.Char
import Data.List qualified as L
import Data.Maybe
import Data.Text qualified as T
import Data.Text.Array qualified as A
import Data.Word
import Numeric

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

-- | The location and the message of the error for the furthest position at
-- which the parser failed, and a tab before the position on its line that
-- can be the cause instead, with 'tabMessage'.
unexpected :: Env -> Int -> (Maybe Int, (Int, String))
unexpected :: Env -> Int -> (Maybe Int, (Int, [Char]))
unexpected Env
input Int
i = (Maybe Int
tabCause, (Int, [Char])
other)
  where
    tabCause :: Maybe Int
    tabCause :: Maybe Int
tabCause = case Int -> Maybe Int -> Maybe Int
indentationTab (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Maybe Int
forall a. Maybe a
Nothing of
      Maybe Int
Nothing
        | Env -> Int -> Word8
byteAt Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COLON Bool -> Bool -> Bool
&& Int -> Bool
firstColonFrom Int
contentStart ->
            Env -> Int -> Int -> Maybe Int
firstTab Env
e Int
lineStart Int
contentStart
      Maybe Int
t -> Maybe Int
t

    other :: (Int, String)
    other :: (Int, [Char])
other
      | Just Int
start <- Maybe Int
propertiesLine =
          ( Int
start
          , [Char]
"an anchor or a tag cannot be on a line of its own here, write it after the key or the '-'"
          )
      | Just (Int, [Char])
r <- Maybe (Int, [Char])
blockMistake = (Int, [Char])
r
      | Bool
afterComment =
          (Int
i, [Char]
"a comment ends a plain scalar, so this line cannot continue it")
      | Just Int
colon <- Env -> Int -> Maybe Int
aliasColon Env
e Int
i = (Int
colon, [Char]
aliasColonMessage)
      | Bool
otherwise = (Int
i,) ([Char] -> (Int, [Char])) -> [Char] -> (Int, [Char])
forall a b. (a -> b) -> a -> b
$ case Env -> Int -> Word8
byteAt Env
e Int
i of
          Word8
w
            | Env -> Int -> Word8
byteBefore Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
STAR Bool -> Bool -> Bool
&& Bool -> Bool
not (Word8 -> Bool
isAnchorChar Word8
w) ->
                [Char]
"expected an alias name after '*'"
            | Env -> Int -> Word8
byteBefore Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
AMP Bool -> Bool -> Bool
&& Bool -> Bool
not (Word8 -> Bool
isAnchorChar Word8
w) ->
                [Char]
"expected an anchor name after '&'"
            | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0 -> [Char]
"unexpected end of input"
            | Bool
indented, Just [Char]
msg <- Maybe [Char]
indentationMistake -> [Char]
msg
            | Bool
indented, Just [Char]
msg <- Env -> Bool -> Int -> Maybe [Char]
mistakeIn Env
e Bool
False Int
i -> [Char]
msg
            | Bool
indented, Bool -> Bool
not Bool
alignedWithEntry -> [Char]
"unexpected indentation"
            | Word8 -> Bool
isBreak Word8
w -> [Char]
"unexpected end of line"
            | Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Env
e.base Bool -> Bool -> Bool
&& Word8 -> Bool
isBreak (Env -> Int -> Word8
byteBefore Env
e Int
i)
            , Just [Char]
msg <- Maybe [Char]
indentationMistake ->
                [Char]
msg
            | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COLON Bool -> Bool -> Bool
&& Bool
firstColon Bool -> Bool -> Bool
&& Bool -> Bool
not (Env -> Int -> Int -> Bool
fitsKey Env
e Int
entryStart Int
i) -> [Char]
keyLengthMessage
            | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COLON Bool -> Bool -> Bool
&& Bool
multiLineKey ->
                [Char]
"unexpected ':', a key must be on a single line"
            | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COLON Bool -> Bool -> Bool
&& Bool
firstColon Bool -> Bool -> Bool
&& Bool
valueColon Bool -> Bool -> Bool
&& Bool
onStartMarkerLine ->
                [Char]
"unexpected ':', a mapping cannot start on the line of '---'"
            -- A colon on the first line of a key does not fail, so the scalar
            -- before this one started on a line above.
            | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COLON
                Bool -> Bool -> Bool
&& Bool
firstColon
                Bool -> Bool -> Bool
&& Bool
valueColon
                Bool -> Bool -> Bool
&& Maybe Int -> Bool
forall a. Maybe a -> Bool
isJust (Int -> Maybe Int
lineAbove (Env -> Int -> Int
lineStartAt Env
e Int
i)) ->
                [Char]
"unexpected ':', this line continues the scalar from the line above, check the indentation and the line above"
            | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COLON Bool -> Bool -> Bool
&& Bool
valueColon ->
                [Char]
"unexpected ':', quote the value if it contains \": \""
            | Bool
itemAfterKey -> [Char]
"unexpected '-', a list cannot start on the line of its key"
            | Bool
itemAfterProperty ->
                [Char]
"unexpected '-', a list cannot start on the line of its anchor or tag"
            | Bool
itemAfterStartMarker ->
                [Char]
"unexpected '-', a list cannot start on the line of '---'"
            | Just [Char]
msg <- Env -> Bool -> Int -> Maybe [Char]
mistakeIn Env
e Bool
False Int
i -> [Char]
msg
            | Just [Char]
node <- Maybe [Char]
endBefore -> Env -> Int -> [Char]
unexpectedChar Env
e Int
i [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" after the end of " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
node
            | Bool
otherwise -> Env -> Int -> [Char]
unexpectedChar Env
e Int
i

    e :: Env
    e :: Env
e = Env -> Env
afterBoms Env
input

    -- The node that ends before the index on its line, as in
    -- "key: "value" more".
    endBefore :: Maybe String
    endBefore :: Maybe [Char]
endBefore
      | Bool -> Bool
not (Word8 -> Bool
isNsChar (Env -> Int -> Word8
byteAt Env
e Int
i)) = Maybe [Char]
forall a. Maybe a
Nothing
      | Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
RBRACKET Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
RBRACE = [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
"a flow collection"
      | (Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
DQUOTE Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
SQUOTE) Bool -> Bool -> Bool
&& Int
j Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
i = [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
"a quoted scalar"
      | Bool
otherwise = Maybe [Char]
forall a. Maybe a
Nothing
      where
        j :: Int
        j :: Int
j = Env -> Int -> Int
skipBackWhites Env
e Int
i

        b :: Word8
        b :: Word8
b = Env -> Int -> Word8
byteBefore Env
e Int
j

    -- The index starts a line that looks like the continuation of a plain
    -- scalar, the closest line above that is not blank has a comment, and
    -- the content above ends with a plain scalar.
    afterComment :: Bool
    afterComment :: Bool
afterComment =
      Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Env -> Int -> Int
skipSpaces Env
e (Env -> Int -> Int
lineStartAt Env
e Int
i)
        Bool -> Bool -> Bool
&& Word8 -> Bool
isNsChar (Env -> Int -> Word8
byteAt Env
e Int
i)
        Bool -> Bool -> Bool
&& Bool -> Bool
not (Int -> Bool
isListItem Int
i)
        Bool -> Bool -> Bool
&& Bool -> Bool
not ((Int -> Bool) -> [Int] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Int -> Bool
isKeyColon [Int
i .. Env -> Int -> Int
lineEndAt Env
e Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1])
        Bool -> Bool -> Bool
&& Maybe [Char] -> Bool
forall a. Maybe a -> Bool
isNothing (Env -> Bool -> Int -> Maybe [Char]
mistakeIn Env
e Bool
False Int
i)
        Bool -> Bool -> Bool
&& Int -> Bool
commentAbove (Env -> Int -> Int
lineStartAt Env
e Int
i)
        Bool -> Bool -> Bool
&& Bool -> (Int -> Bool) -> Maybe Int -> Bool
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Bool
False Int -> Bool
endsPlain (Int -> Maybe Int
lineAbove (Env -> Int -> Int
lineStartAt Env
e Int
i))
      where
        -- The line with the content at the index ends with a plain scalar,
        -- not with a quoted scalar, a flow collection, an alias, an anchor,
        -- a tag, an indicator or the header of a block scalar, and it is not
        -- a line of a block scalar.
        endsPlain :: Int -> Bool
        endsPlain :: Int -> Bool
endsPlain Int
k =
          let end :: Int
end = Env -> Int -> Int
lineContentEnd Env
e Int
k
              start :: Int
start = Env -> Int -> Int
wordStart Env
e Int
end
              b :: Word8
b = Env -> Int -> Word8
byteBefore Env
e Int
end
              w :: Word8
w = Env -> Int -> Word8
byteAt Env
e Int
start
          in Int
end Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
k
               Bool -> Bool -> Bool
&& Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
SQUOTE
               Bool -> Bool -> Bool
&& Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
DQUOTE
               Bool -> Bool -> Bool
&& Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
RBRACKET
               Bool -> Bool -> Bool
&& Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
RBRACE
               Bool -> Bool -> Bool
&& Word8
b 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
STAR
               Bool -> Bool -> Bool
&& Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
AMP
               Bool -> Bool -> Bool
&& Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
EXCL
               Bool -> Bool -> Bool
&& Bool -> Bool
not (Int
end Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
start Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
1 Bool -> Bool -> Bool
&& (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
QUESTION))
               Bool -> Bool -> Bool
&& Bool -> Bool
not (Int -> Bool
endsWithBlockHeader Int
k)
               Bool -> Bool -> Bool
&& Bool -> Bool
not (Int -> Bool
inBlockScalar Int
k)

        -- The closest line above that is indented less starts a block
        -- scalar.
        inBlockScalar :: Int -> Bool
        inBlockScalar :: Int -> Bool
inBlockScalar Int
k = Int -> Bool
go Int
k
          where
            go :: Int -> Bool
            go :: Int -> Bool
go Int
j = case Int -> Maybe Int
lineAbove (Env -> Int -> Int
lineStartAt Env
e Int
j) of
              Maybe Int
Nothing -> Bool
False
              Just Int
above
                | Int -> Int
columnOf Int
above Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int -> Int
columnOf Int
k -> Int -> Bool
endsWithBlockHeader Int
above
                | Bool
otherwise -> Int -> Bool
go Int
above

        commentAbove :: Int -> Bool
        commentAbove :: Int -> Bool
commentAbove Int
start
          | Int
start Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Env
e.base = Bool
False
          | Bool
otherwise =
              let prev :: Int
prev = Env -> Int -> Int
lineStartAt Env
e (Int
start Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
                  k :: Int
k = Env -> Int -> Int
skipWhites Env
e (Env -> Int -> Int
skipBoms Env
e Int
prev)
              in if Word8 -> Bool
isBreak (Env -> Int -> Word8
byteAt Env
e Int
k) then Int -> Bool
commentAbove Int
prev else Int -> Bool
hasComment Int
k

        -- A quoted scalar can contain " #", as in "\"x #y\"", so a quote
        -- that can close a scalar after the '#' shows that the '#' may not
        -- start a comment.
        hasComment :: Int -> Bool
        hasComment :: Int -> Bool
hasComment Int
j = case (Int -> Bool) -> [Int] -> Maybe Int
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
L.find Int -> Bool
startsComment [Int
j .. Int
end Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1] of
          Just Int
h -> Bool -> Bool
not ((Int -> Bool) -> [Int] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Int -> Bool
closingQuote [Int
h Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1 .. Int
end Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1])
          Maybe Int
Nothing -> Bool
False
          where
            end :: Int
            end :: Int
end = Env -> Int -> Int
lineEndAt Env
e Int
j

            startsComment :: Int -> Bool
            startsComment :: Int -> Bool
startsComment Int
k = Env -> Int -> Word8
byteAt Env
e Int
k Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
HASH Bool -> Bool -> Bool
&& (Int
k Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
j Bool -> Bool -> Bool
|| Word8 -> Bool
isWhite (Env -> Int -> Word8
byteBefore Env
e Int
k))

        closingQuote :: Int -> Bool
        closingQuote :: Int -> Bool
closingQuote Int
k =
          let b :: Word8
b = Env -> Int -> Word8
byteAt Env
e Int
k in (Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
SQUOTE Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
DQUOTE) Bool -> Bool -> Bool
&& Env -> Int -> Bool
canEndFlowNode Env
e (Int
k Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)

    -- The start of the line of the index if the line has only anchors and
    -- tags, as in "&anchor".
    propertiesLine :: Maybe Int
    propertiesLine :: Maybe Int
propertiesLine =
      let start :: Int
start = Env -> Int -> Int
skipSpaces Env
e (Env -> Int -> Int
lineStartAt Env
e Int
i)
      in if Int -> Bool
onlyProperties Int
start then Int -> Maybe Int
forall a. a -> Maybe a
Just Int
start else Maybe Int
forall a. Maybe a
Nothing
      where
        onlyProperties :: Int -> Bool
        onlyProperties :: Int -> Bool
onlyProperties Int
j =
          let b :: Word8
b = Env -> Int -> Word8
byteAt Env
e Int
j
              next :: Int
next = Env -> Int -> Int
skipWhites Env
e (Int -> Int
wordEnd Int
j)
              b' :: Word8
b' = Env -> Int -> Word8
byteAt Env
e Int
next
          in (Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
AMP Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
EXCL)
               Bool -> Bool -> Bool
&& (Word8
b' Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0 Bool -> Bool -> Bool
|| Word8 -> Bool
isBreak Word8
b' Bool -> Bool -> Bool
|| Word8
b' Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
HASH Bool -> Bool -> Bool
|| Int -> Bool
onlyProperties Int
next)

        wordEnd :: Int -> Int
        wordEnd :: Int -> Int
wordEnd Int
j = if Word8 -> Bool
isNsChar (Env -> Int -> Word8
byteAt Env
e Int
j) then Int -> Int
wordEnd (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) else Int
j

    -- A key before the index that started on a line above. A key that ends
    -- here on one line does not fail.
    multiLineKey :: Bool
    multiLineKey :: Bool
multiLineKey =
      Bool
multiLineCollection
        Bool -> Bool -> Bool
|| Bool
firstColon Bool -> Bool -> Bool
&& (Env -> Int -> Word8
byteBefore Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
DQUOTE Bool -> Bool -> Bool
|| Env -> Int -> Word8
byteBefore Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
SQUOTE)

    -- A flow collection ends before the index and starts on a line above.
    multiLineCollection :: Bool
    multiLineCollection :: Bool
multiLineCollection =
      let b :: Word8
b = Env -> Int -> Word8
byteBefore Env
e Int
i
      in (Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
RBRACKET Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
RBRACE) Bool -> Bool -> Bool
&& Int -> Int -> Bool -> Bool
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2) Int
1 Bool
False
      where
        go :: Int -> Int -> Bool -> Bool
        go :: Int -> Int -> Bool -> Bool
go Int
j Int
depth Bool
crossed
          | Int
j Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Env
e.base = Bool
False
          | Word8
c Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
RBRACKET Bool -> Bool -> Bool
|| Word8
c Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
RBRACE = Int -> Int -> Bool -> Bool
go (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Bool
crossed
          | Word8
c Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
LBRACKET Bool -> Bool -> Bool
|| Word8
c Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
LBRACE =
              if Int
depth Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
1 then Bool
crossed else Int -> Int -> Bool -> Bool
go (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Bool
crossed
          | Bool
otherwise = Int -> Int -> Bool -> Bool
go (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Int
depth (Bool
crossed Bool -> Bool -> Bool
|| Word8 -> Bool
isBreak Word8
c)
          where
            c :: Word8
            c :: Word8
c = Env -> Int -> Word8
byteAt Env
e Int
j

    onStartMarkerLine :: Bool
    onStartMarkerLine :: Bool
onStartMarkerLine = Env -> Int -> Bool
isStartMarker Env
e Int
markerStart

    -- A byte order mark can come before a marker.
    markerStart :: Int
    markerStart :: Int
markerStart = Env -> Int -> Int
skipBoms Env
e (Env -> Int -> Int
lineStartAt Env
e Int
i)

    -- The start of the entry on the line, after any "- ".
    entryStart :: Int
    entryStart :: Int
entryStart = Int -> Int
skipListItems (Env -> Int -> Int
skipSpaces Env
e (Env -> Int -> Int
lineStartAt Env
e Int
i))

    firstColon :: Bool
    firstColon :: Bool
firstColon = Int -> Bool
firstColonFrom Int
entryStart

    -- No colon that ends a key is from the given index to the index of the
    -- error, other than in a flow collection.
    firstColonFrom :: Int -> Bool
    firstColonFrom :: Int -> Bool
firstColonFrom Int
start = Int -> Int -> Bool
go Int
start Int
0
      where
        go :: Int -> Int -> Bool
        go :: Int -> Int -> Bool
go Int
j Int
depth
          | Int
j Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
i = Bool
True
          | Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
LBRACKET Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
LBRACE = Int -> Int -> Bool
go (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
          | Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
RBRACKET Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
RBRACE = Int -> Int -> Bool
go (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) (Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int
depth Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1))
          | Int
depth Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0 Bool -> Bool -> Bool
&& Int -> Bool
isKeyColon Int
j = Bool
False
          | Bool
otherwise = Int -> Int -> Bool
go (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Int
depth
          where
            b :: Word8
            b :: Word8
b = Env -> Int -> Word8
byteAt Env
e Int
j

    -- A list item right after a key, as in "a: - b".
    itemAfterKey :: Bool
    itemAfterKey :: Bool
itemAfterKey = Int -> Bool
isListItem Int
i Bool -> Bool -> Bool
&& Env -> Int -> Word8
byteBefore Env
e (Env -> Int -> Int
skipBackWhites Env
e Int
i) Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COLON

    -- A list item right after an anchor or a tag, as in "&a - b".
    itemAfterProperty :: Bool
    itemAfterProperty :: Bool
itemAfterProperty =
      let j :: Int
j = Env -> Int -> Int
skipBackWhites Env
e Int
i
          b :: Word8
b = Env -> Int -> Word8
byteAt Env
e (Env -> Int -> Int
wordStart Env
e Int
j)
      in Int -> Bool
isListItem Int
i Bool -> Bool -> Bool
&& Int
j Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
i Bool -> Bool -> Bool
&& (Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
AMP Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
EXCL)

    itemAfterStartMarker :: Bool
    itemAfterStartMarker :: Bool
itemAfterStartMarker =
      Int -> Bool
isListItem Int
i
        Bool -> Bool -> Bool
&& Bool
onStartMarkerLine
        Bool -> Bool -> Bool
&& Env -> Int -> Int
skipBackWhites Env
e Int
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
markerStart Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
markerLength

    -- A colon that ends a word and precedes white space, as in an unquoted
    -- value like "Error: file not found".
    valueColon :: Bool
    valueColon :: Bool
valueColon =
      Word8 -> Bool
isNsChar (Env -> Int -> Word8
byteBefore Env
e Int
i)
        Bool -> Bool -> Bool
&& (let w :: Word8
w = Env -> Int -> Word8
byteAt Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) in Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0 Bool -> Bool -> Bool
|| Word8 -> Bool
isWhite Word8
w Bool -> Bool -> Bool
|| Word8 -> Bool
isBreak Word8
w)

    -- Only spaces precede the index on its line.
    indented :: Bool
    indented :: Bool
indented = Int
i Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Env
e.base Bool -> Bool -> Bool
&& Env -> Int -> Word8
byteBefore Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
SPACE Bool -> Bool -> Bool
&& Int -> Bool
go (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
      where
        go :: Int -> Bool
        go :: Int -> Bool
go Int
j
          | Int
j Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Env
e.base = Bool
True
          | Bool
otherwise = case Env -> Int -> Word8
byteBefore Env
e Int
j of
              Word8
SPACE -> Int -> Bool
go (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
              Word8
w -> Word8 -> Bool
isBreak Word8
w

    -- The index is at the column of a list item or a key on a line above, so
    -- the content is the mistake, not the indentation. A line of a block
    -- scalar above is neither.
    alignedWithEntry :: Bool
    alignedWithEntry :: Bool
alignedWithEntry = case Int -> Int -> Maybe Int
entryAbove (Int -> Int
columnOf Int
i) (Env -> Int -> Int
lineStartAt Env
e Int
i) of
      Just Int
k -> Int -> Bool
isListItem Int
k Bool -> Bool -> Bool
|| (Int -> Bool) -> [Int] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Int -> Bool
isKeyColon [Int
k .. Env -> Int -> Int
lineContentEnd Env
e Int
k Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]
      Maybe Int
Nothing -> Bool
False

    lineStart :: Int
    lineStart :: Int
lineStart = Env -> Int -> Int
lineStartAt Env
e Int
i

    -- The content of the line of the index after the white space and the
    -- indicators of block entries, as in "- ? key". A plain scalar can follow
    -- a tab there, but a key cannot.
    contentStart :: Int
    contentStart :: Int
contentStart = Int -> Int
go Int
lineStart
      where
        go :: Int -> Int
        go :: Int -> Int
go Int
j =
          let k :: Int
k = Env -> Int -> Int
skipWhites Env
e Int
j
          in if Int -> Bool
isBlockIndicator Int
k then Int -> Int
go (Int
k Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) else Int
k

    -- The first tab in the indentation before the index, if only white space
    -- and the indicators of block entries precede the index on its line.
    indentationTab :: Int -> Maybe Int -> Maybe Int
    indentationTab :: Int -> Maybe Int -> Maybe Int
indentationTab Int
j Maybe Int
tab
      | Int
j Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Env
e.base = Maybe Int
tab'
      | Int -> Bool
isBlockIndicator Int
j = Int -> Maybe Int -> Maybe Int
indentationTab (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Maybe Int
tab
      | Bool
otherwise = case Array -> Int -> Word8
A.unsafeIndex Env
e.array Int
j of
          Word8
SPACE -> Int -> Maybe Int -> Maybe Int
indentationTab (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) Maybe Int
tab
          Word8
TAB -> Int -> Maybe Int -> Maybe Int
indentationTab (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) (Int -> Maybe Int
forall a. a -> Maybe a
Just Int
j)
          Word8
w
            | Word8 -> Bool
isBreak Word8
w -> Maybe Int
tab'
            | Bool
otherwise -> Maybe Int
forall a. Maybe a
Nothing
      where
        tab' :: Maybe Int
        tab' :: Maybe Int
tab' = if Env -> Int -> Word8
byteAt Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
TAB then Int -> Maybe Int
forall a. a -> Maybe a
Just (Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
i Maybe Int
tab) else Maybe Int
tab

    isBlockIndicator :: Int -> Bool
    isBlockIndicator :: Int -> Bool
isBlockIndicator Int
j =
      let 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
MINUS Bool -> Bool -> Bool
|| Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
QUESTION Bool -> Bool -> Bool
|| Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COLON) Bool -> Bool -> Bool
&& Word8 -> Bool
isWhite (Env -> Int -> Word8
byteAt Env
e (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1))

    -- The error for a line of a block collection that lacks the space after
    -- "-" or the ":" after a key, if the entries above it at the same
    -- position are list items or mapping entries.
    blockMistake :: Maybe (Int, String)
    blockMistake :: Maybe (Int, [Char])
blockMistake = do
      Bool -> Maybe ()
forall (f :: * -> *). Alternative f => Bool -> f ()
guard (Bool -> Maybe ()) -> Bool -> Maybe ()
forall a b. (a -> b) -> a -> b
$ Int
start Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
stop
      k <- Int -> Int -> Maybe Int
entryAbove Int
column (Env -> Int -> Int
lineStartAt Env
e Int
stop)
      if
        | isListItem k && byteAt e start == MINUS && stop == start + 1 ->
            Just (stop, "expected a space after '-'")
        | not (isListItem k)
            && (w == 0 || isBreak w || stop < i)
            && not (any isKeyColon [afterKey .. stop - 1]) ->
            Just $ case (keyEnd, filter tightColon [afterKey .. stop - 1]) of
              (Maybe Int
Nothing, [Int]
_) -> (Int
start, [Char]
"unterminated " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
quotedName [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" scalar")
              (Just Int
end, [Int]
_) | Int
end Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
stop -> (Int
stop, [Char]
"a key must be on a single line")
              (Maybe Int
_, Int
colon : [Int]
_) -> (Int
colon Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1, [Char]
"expected a space after ':'")
              (Maybe Int
_, []) -> (Int
stop, [Char]
"expected ':' after the key")
        | otherwise -> Nothing
      where
        -- The parser fails at a comment after the content, as in "key # note".
        stop :: Int
        stop :: Int
stop
          | Env -> Int -> Word8
byteAt Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
HASH Bool -> Bool -> Bool
&& Word8 -> Bool
isWhite (Env -> Int -> Word8
byteBefore Env
e Int
i) = Env -> Int -> Int
skipBackWhites Env
e Int
i
          | Bool
otherwise = Int
i

        -- The index after the quoted scalar that starts the line, or the start
        -- of the line without a quote, or 'Nothing' if the scalar does not
        -- end. A colon inside the scalar does not end a key.
        keyEnd :: Maybe Int
        keyEnd :: Maybe Int
keyEnd
          | Word8
quote Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
DQUOTE Bool -> Bool -> Bool
|| Word8
quote Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
SQUOTE = Int -> Maybe Int
closing (Int
start Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
          | Bool
otherwise = Int -> Maybe Int
forall a. a -> Maybe a
Just Int
start
          where
            closing :: Int -> Maybe Int
            closing :: Int -> Maybe Int
closing Int
j
              | Int
j Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Env
e.end = Maybe Int
forall a. Maybe a
Nothing
              | Word8
quote Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
SQUOTE Bool -> Bool -> Bool
&& Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
SQUOTE Bool -> Bool -> Bool
&& Env -> Int -> Word8
byteAt Env
e (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
SQUOTE =
                  Int -> Maybe Int
closing (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2)
              | Word8
quote Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
DQUOTE Bool -> Bool -> Bool
&& Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
BACKSLASH = Int -> Maybe Int
closing (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
2)
              | Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
quote = Int -> Maybe Int
forall a. a -> Maybe a
Just (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
              | Bool
otherwise = Int -> Maybe Int
closing (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
              where
                b :: Word8
                b :: Word8
b = Env -> Int -> Word8
byteAt Env
e Int
j

        -- The index after the key on the line.
        afterKey :: Int
        afterKey :: Int
afterKey = Int -> Maybe Int -> Int
forall a. a -> Maybe a -> a
fromMaybe Int
stop Maybe Int
keyEnd

        quote :: Word8
        quote :: Word8
quote = Env -> Int -> Word8
byteAt Env
e Int
start

        quotedName :: String
        quotedName :: [Char]
quotedName = if Word8
quote Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
DQUOTE then [Char]
"double-quoted" else [Char]
"single-quoted"

        w :: Word8
        w :: Word8
w = Env -> Int -> Word8
byteAt Env
e Int
stop

        start :: Int
        start :: Int
start = Env -> Int -> Int
skipSpaces Env
e (Env -> Int -> Int
lineStartAt Env
e Int
stop)

        column :: Int
        column :: Int
column = Int
start Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env -> Int -> Int
lineStartAt Env
e Int
stop

        -- A colon before a word, as in "key:value", but not in "http://".
        tightColon :: Int -> Bool
        tightColon :: Int -> Bool
tightColon Int
j = Env -> Int -> Word8
byteAt Env
e Int
j Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COLON Bool -> Bool -> Bool
&& Word8 -> Bool
startsWord (Env -> Int -> Word8
byteAt Env
e (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1))

        startsWord :: Word8 -> Bool
        startsWord :: Word8 -> Bool
startsWord Word8
b =
          (Word8 -> Bool
isAsciiByte Word8
b Bool -> Bool -> Bool
&& Char -> Bool
isAlphaNum (Int -> Char
chr (Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
b)))
            Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
SQUOTE
            Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
DQUOTE
            Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
LBRACKET
            Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
LBRACE

    -- The closest entry above the line that starts at the index, at the
    -- column. An entry can follow "- " on its line, as in "- key: value".
    entryAbove :: Int -> Int -> Maybe Int
    entryAbove :: Int -> Int -> Maybe Int
entryAbove Int
column Int
from = do
      k <- Int -> Maybe Int
lineAbove Int
from
      let indent = Int -> Int
columnOf Int
k
          entry = Int -> Int
skipListItems Int
k
      if
        | indent == column -> Just k
        | columnOf entry == column -> Just entry
        | indent < column -> Nothing
        | otherwise -> entryAbove column (lineStartAt e k)

    -- The error for content at the index that starts a line with a wrong
    -- indentation, if the lines above show the likely mistake: a list item
    -- among mapping entries or the other way round, or a line of a block
    -- scalar with too little indentation.
    indentationMistake :: Maybe String
    indentationMistake :: Maybe [Char]
indentationMistake = Int -> Maybe [Char]
go (Env -> Int -> Int
lineStartAt Env
e Int
i)
      where
        column :: Int
        column :: Int
column = Int -> Int
columnOf Int
i

        -- Look at the lines above, up to the first line with less
        -- indentation.
        go :: Int -> Maybe String
        go :: Int -> Maybe [Char]
go Int
start = do
          k <- Int -> Maybe Int
lineAbove Int
start
          let indent = Int -> Int
columnOf Int
k
          if
            | indent > column -> go (lineStartAt e k)
            | indent < column ->
                if endsWithBlockHeader k
                  then
                    Just
                      "unexpected indentation, the line has less indentation than the block scalar above it"
                  else Nothing
            | isListItem k && not (isListItem i) && not (isFlowIndicator (byteAt e i)) ->
                Just $
                  if hasKey i
                    then "unexpected key among list items"
                    else "unexpected value among list items"
            | not (isListItem k) && isListItem i ->
                Just "unexpected list item among mapping entries"
            | otherwise -> Nothing

    -- The line from the content at the index ends with the header of a block
    -- scalar, e.g. "key: |-".
    endsWithBlockHeader :: Int -> Bool
    endsWithBlockHeader :: Int -> Bool
endsWithBlockHeader Int
k =
      let h :: Int
h = Int -> Int
skipIndicators (Env -> Int -> Int
lineContentEnd Env
e Int
k)
      in Int
h Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
k
           Bool -> Bool -> Bool
&& (let b :: Word8
b = Env -> Int -> Word8
byteAt Env
e (Int
h Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) in Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
PIPE Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
GREATER)
           Bool -> Bool -> Bool
&& (Int
h Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1 Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
k Bool -> Bool -> Bool
|| Word8 -> Bool
isWhite (Env -> Int -> Word8
byteAt Env
e (Int
h Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
2)))
      where
        skipIndicators :: Int -> Int
        skipIndicators :: Int -> Int
skipIndicators Int
j =
          let b :: Word8
b = Env -> Int -> Word8
byteBefore Env
e Int
j
          in if Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
MINUS Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
PLUS Bool -> Bool -> Bool
|| Word8 -> Bool
isDecDigit Word8
b then Int -> Int
skipIndicators (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) else Int
j

    -- The first content of the closest line above the line that starts at the
    -- index. Blank lines and comment lines do not count.
    lineAbove :: Int -> Maybe Int
    lineAbove :: Int -> Maybe Int
lineAbove Int
start = Env -> Int -> Int
skipSpaces Env
e (Int -> Int) -> (Int -> Int) -> Int -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Env -> Int -> Int
skipBoms Env
e (Int -> Int) -> Maybe Int -> Maybe Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Env -> Int -> Maybe Int
contentLineAbove Env
e Int
start

    -- A byte order mark can start the first line of a document, before its
    -- indentation.
    columnOf :: Int -> Int
    columnOf :: Int -> Int
columnOf Int
j = Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Env -> Int -> Int
skipBoms Env
e (Env -> Int -> Int
lineStartAt Env
e Int
j)

    -- The index after the "- " indicators at the index, as in "- - key: value".
    skipListItems :: Int -> Int
    skipListItems :: Int -> Int
skipListItems Int
j = if Int -> Bool
isListItem Int
j then Int -> Int
skipListItems (Env -> Int -> Int
skipWhites Env
e (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)) else Int
j

    -- A colon that ends an implicit key is at the index.
    isKeyColon :: Int -> Bool
    isKeyColon :: Int -> Bool
isKeyColon Int
j =
      Env -> Int -> Word8
byteAt Env
e Int
j Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COLON
        Bool -> Bool -> Bool
&& (let b :: Word8
b = Env -> Int -> Word8
byteAt Env
e (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) in Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0 Bool -> Bool -> Bool
|| Word8 -> Bool
isWhite Word8
b Bool -> Bool -> Bool
|| Word8 -> Bool
isBreak Word8
b)

    -- The line from the content at the index has a key, explicit or
    -- implicit.
    hasKey :: Int -> Bool
    hasKey :: Int -> Bool
hasKey Int
j =
      (Env -> Int -> Word8
byteAt Env
e Int
j Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
QUESTION Bool -> Bool -> Bool
&& (let b :: Word8
b = Env -> Int -> Word8
byteAt Env
e (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) in Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0 Bool -> Bool -> Bool
|| Word8 -> Bool
isWhite Word8
b Bool -> Bool -> Bool
|| Word8 -> Bool
isBreak Word8
b))
        Bool -> Bool -> Bool
|| (Int -> Bool) -> [Int] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any Int -> Bool
isKeyColon [Int
j .. Env -> Int -> Int
lineContentEnd Env
e Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]

    -- A block sequence entry starts at the index.
    isListItem :: Int -> Bool
    isListItem :: Int -> Bool
isListItem Int
j =
      Env -> Int -> Word8
byteAt Env
e Int
j Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
MINUS
        Bool -> Bool -> Bool
&& (let b :: Word8
b = Env -> Int -> Word8
byteAt Env
e (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) in Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0 Bool -> Bool -> Bool
|| Word8 -> Bool
isWhite Word8
b Bool -> Bool -> Bool
|| Word8 -> Bool
isBreak Word8
b)

-- | The colon that ends an alias name before the index, as in @*x: 1@. An
-- alias name can contain a colon.
aliasColon :: Env -> Int -> Maybe Int
aliasColon :: Env -> Int -> Maybe Int
aliasColon Env
e Int
i =
  let j :: Int
j = Env -> Int -> Int
skipBackWhites Env
e Int
i
      start :: Int
start = Env -> Int -> Int
wordStart Env
e Int
j
  in if Int
j Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
start Bool -> Bool -> Bool
&& Env -> Int -> Word8
byteBefore Env
e Int
j Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COLON Bool -> Bool -> Bool
&& Env -> Int -> Word8
byteAt Env
e Int
start Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
STAR
       then Int -> Maybe Int
forall a. a -> Maybe a
Just (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
       else Maybe Int
forall a. Maybe a
Nothing

aliasColonMessage :: String
aliasColonMessage :: [Char]
aliasColonMessage =
  [Char]
"the name of the alias includes the ':', write a space before ':' if the alias is a key"

-- | The location and the message of the error at the index inside a flow
-- collection: a common mistake if the input there shows one, or else the
-- index and the given message.
flowError :: Env -> Int -> String -> (Int, String)
flowError :: Env -> Int -> [Char] -> (Int, [Char])
flowError Env
e Int
i [Char]
msg = case Env -> Int -> Maybe Int
aliasColon Env
e Int
i of
  Just Int
colon -> (Int
colon, [Char]
aliasColonMessage)
  Maybe Int
Nothing -> (Int
i, [Char] -> Maybe [Char] -> [Char]
forall a. a -> Maybe a -> a
fromMaybe [Char]
msg (Env -> Bool -> Int -> Maybe [Char]
mistakeIn (Env -> Env
afterBoms Env
e) Bool
True Int
i))

-- | The input without the byte order marks at its start. The hints look at
-- the content of the lines around an error, and the marks are not content
-- of the first line. The indices stay those of the input.
afterBoms :: Env -> Env
afterBoms :: Env -> Env
afterBoms Env
e = Env
e {base = skipBoms e e.base}

-- | The flag tells if the index is inside a flow collection.
mistakeIn :: Env -> Bool -> Int -> Maybe String
mistakeIn :: Env -> Bool -> Int -> Maybe [Char]
mistakeIn Env
e Bool
flow Int
i
  | Env -> Int -> Bool
isBom Env
e Int
i = [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
"unexpected byte order mark"
  -- Inside a plain scalar, a '#' after other content does not stop the
  -- parser, so here it follows the end of another node, e.g. "x"#c.
  | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
HASH Bool -> Bool -> Bool
&& Word8 -> Bool
isNsChar (Env -> Int -> Word8
byteBefore Env
e Int
i) =
      [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
"unexpected '#', a comment needs a space before it"
  | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COMMA
      Bool -> Bool -> Bool
&& ( let b :: Word8
b = Env -> Int -> Word8
byteBefore Env
e (Int -> Int
skipBack Int
i)
           in Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
COMMA Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
LBRACKET Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
LBRACE
         ) =
      [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
"unexpected ',', a flow collection cannot have an empty entry"
  -- In the block style, these characters start a block scalar.
  | Bool
flow Bool -> Bool -> Bool
&& (Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
PIPE Bool -> Bool -> Bool
|| Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
GREATER) =
      [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just ([Char] -> Maybe [Char]) -> [Char] -> Maybe [Char]
forall a b. (a -> b) -> a -> b
$ Env -> Int -> [Char]
unexpectedChar Env
e Int
i [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
", a block scalar cannot be inside a flow collection"
  | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
STAR Bool -> Bool -> Bool
&& Bool -> Bool
not (Word8 -> Bool
isAnchorChar (Env -> Int -> Word8
byteAt Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1))) =
      [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
"expected an alias name after '*'"
  | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
STAR
      Bool -> Bool -> Bool
&& (let b :: Word8
b = Env -> Int -> Word8
byteAt Env
e (Env -> Int -> Int
wordStart Env
e (Env -> Int -> Int
skipBackWhites Env
e Int
i)) in Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
AMP Bool -> Bool -> Bool
|| Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
EXCL) =
      [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
"unexpected '*', an alias cannot have an anchor or a tag"
  | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
AMP Bool -> Bool -> Bool
&& Bool -> Bool
not (Word8 -> Bool
isAnchorChar (Env -> Int -> Word8
byteAt Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1))) =
      [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just [Char]
"expected an anchor name after '&'"
  | Word8 -> Bool
afterQuote Word8
SQUOTE =
      [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just ([Char] -> Maybe [Char]) -> [Char] -> Maybe [Char]
forall a b. (a -> b) -> a -> b
$
        Env -> Int -> [Char]
unexpectedChar Env
e Int
i
          [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" after a single-quoted scalar, write '' for a quote inside it"
  | Word8 -> Bool
afterQuote Word8
DQUOTE =
      [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just ([Char] -> Maybe [Char]) -> [Char] -> Maybe [Char]
forall a b. (a -> b) -> a -> b
$
        Env -> Int -> [Char]
unexpectedChar Env
e Int
i
          [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" after a double-quoted scalar, write \\\" for a quote inside it"
  -- A '%' at the start of a line in the block style starts a directive.
  | Bool -> Bool
not Bool
flow Bool -> Bool -> Bool
&& Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
PERCENT Bool -> Bool -> Bool
&& Env -> Int -> Bool
isStartOfLine Env
e Int
i Bool -> Bool -> Bool
&& Word8 -> Bool
isNsChar (Env -> Int -> Word8
byteAt Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)) =
      [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just
        [Char]
"unexpected '%', a directive needs '...' on a line above it to end the document"
  -- Other indicators start a node of another kind, e.g. '&' an anchor.
  | Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
AT Bool -> Bool -> Bool
|| Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
GRAVE Bool -> Bool -> Bool
|| Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
PERCENT =
      [Char] -> Maybe [Char]
forall a. a -> Maybe a
Just ([Char] -> Maybe [Char]) -> [Char] -> Maybe [Char]
forall a b. (a -> b) -> a -> b
$
        Env -> Int -> [Char]
unexpectedChar Env
e Int
i [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
", a plain scalar cannot start with it, quote the value"
  | Bool
otherwise = Maybe [Char]
forall a. Maybe a
Nothing
  where
    w :: Word8
    w :: Word8
w = Env -> Int -> Word8
byteAt Env
e Int
i

    -- The index after the last content before the white space and the line
    -- breaks that end at the index.
    skipBack :: Int -> Int
    skipBack :: Int -> Int
skipBack Int
j
      | Word8 -> Bool
isWhite (Env -> Int -> Word8
byteBefore Env
e Int
j) Bool -> Bool -> Bool
|| Word8 -> Bool
isBreak (Env -> Int -> Word8
byteBefore Env
e Int
j) = Int -> Int
skipBack (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1)
      | Bool
otherwise = Int
j

    -- Content right after a quote, as in 'it's'. A plain scalar can hold a
    -- quote, so the quote closes a quoted scalar. A colon there ends a key.
    afterQuote :: Word8 -> Bool
    afterQuote :: Word8 -> Bool
afterQuote Word8
q =
      Env -> Int -> Word8
byteBefore Env
e Int
i Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
q
        Bool -> Bool -> Bool
&& Word8 -> Bool
isNsChar Word8
w
        Bool -> Bool -> Bool
&& Bool -> Bool
not (Word8 -> Bool
isFlowIndicator Word8
w)
        Bool -> Bool -> Bool
&& Word8
w Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word8
COLON
        Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
quoteInTag

    -- A quote can be a character of a tag, as in "!'".
    quoteInTag :: Bool
    quoteInTag :: Bool
quoteInTag = Env -> Int -> Word8
byteBefore Env
e (Int -> Int
tagStart Int
i) Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
EXCL
      where
        tagStart :: Int -> Int
        tagStart :: Int -> Int
tagStart Int
j = if Word8 -> Bool
isTagChar (Env -> Int -> Word8
byteBefore Env
e Int
j) then Int -> Int
tagStart (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) else Int
j

-- | The index after the content of the line from the content at the index,
-- before its comment.
lineContentEnd :: Env -> Int -> Int
lineContentEnd :: Env -> Int -> Int
lineContentEnd Env
e = Env -> Int -> Int
skipBackWhites Env
e (Int -> Int) -> (Int -> Int) -> Int -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Int
go
  where
    go :: Int -> Int
    go :: Int -> Int
go Int
j
      | Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
0 Bool -> Bool -> Bool
|| Word8 -> Bool
isBreak Word8
b = Int
j
      | Word8
b Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
HASH Bool -> Bool -> Bool
&& Word8 -> Bool
isWhite (Env -> Int -> Word8
byteBefore Env
e Int
j) = Int
j
      | Bool
otherwise = Int -> Int
go (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1)
      where
        b :: Word8
        b :: Word8
b = Env -> Int -> Word8
byteAt Env
e Int
j

-- | The index after the last content before the white space that ends at the
-- index.
skipBackWhites :: Env -> Int -> Int
skipBackWhites :: Env -> Int -> Int
skipBackWhites Env
e Int
i = if Word8 -> Bool
isWhite (Env -> Int -> Word8
byteBefore Env
e Int
i) then Env -> Int -> Int
skipBackWhites Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) else Int
i

-- | The start of the word that ends at the index, e.g. of an anchor or an
-- alias with its indicator.
wordStart :: Env -> Int -> Int
wordStart :: Env -> Int -> Int
wordStart Env
e Int
i = if Word8 -> Bool
isAnchorChar (Env -> Int -> Word8
byteBefore Env
e Int
i) then Env -> Int -> Int
wordStart Env
e (Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1) else Int
i

unexpectedChar :: Env -> Int -> String
unexpectedChar :: Env -> Int -> [Char]
unexpectedChar Env
e Int
i
  | Word8 -> Bool
isAsciiByte Word8
w = [Char]
"unexpected " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Char -> [Char]
forall a. Show a => a -> [Char]
show (Int -> Char
chr (Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
w))
  | Char -> Bool
isPrint Char
c = [Char]
"unexpected '" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char
c] [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
"'"
  | Bool
otherwise = [Char]
"unexpected " [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Char -> [Char]
codePointName Char
c
  where
    w :: Word8
    w :: Word8
w = Env -> Int -> Word8
byteAt Env
e Int
i

    c :: Char
    c :: Char
c = HasCallStack => Text -> Char
Text -> Char
T.head (Env -> Int -> Int -> Text
slice Env
e Int
i Env
e.end)

-- | The first tab from the first index to before the second.
firstTab :: Env -> Int -> Int -> Maybe Int
firstTab :: Env -> Int -> Int -> Maybe Int
firstTab Env
e Int
i Int
j = (Int -> Bool) -> [Int] -> Maybe Int
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
L.find (\Int
k -> Env -> Int -> Word8
byteAt Env
e Int
k Word8 -> Word8 -> Bool
forall a. Eq a => a -> a -> Bool
== Word8
TAB) [Int
i .. Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1]

tabMessage :: String
tabMessage :: [Char]
tabMessage = [Char]
"tabs cannot be used for indentation"

keyLengthMessage :: String
keyLengthMessage :: [Char]
keyLengthMessage =
  [Char]
"a key can be at most "
    [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> [Char]
forall a. Show a => a -> [Char]
show Int
maxImplicitKeyLength
    [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
" characters long, write a longer key after '? '"

-- | The code point of a character, e.g. U+0007, for a character that an error
-- cannot show.
codePointName :: Char -> String
codePointName :: Char -> [Char]
codePointName Char
c =
  let hex :: [Char]
hex = (Char -> Char) -> [Char] -> [Char]
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toUpper (Int -> [Char] -> [Char]
forall a. Integral a => a -> [Char] -> [Char]
showHex (Char -> Int
ord Char
c) [Char]
"")
  in [Char]
"U+" [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ Int -> Char -> [Char]
forall a. Int -> a -> [a]
replicate (Int
4 Int -> Int -> Int
forall a. Num a => a -> a -> a
- [Char] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Char]
hex) Char
'0' [Char] -> [Char] -> [Char]
forall a. [a] -> [a] -> [a]
++ [Char]
hex