packages feed

cuddle-1.7.0.0: src/Codec/CBOR/Cuddle/Parser/Lexer.hs

{-# LANGUAGE OverloadedStrings #-}

module Codec.CBOR.Cuddle.Parser.Lexer (
  Parser,
  charInRange,
  space,
  trailingSpace,
  pComment,
  (|||),
  pCommentBlock,
) where

import Codec.CBOR.Cuddle.Comments (Comment, commentLine)
import Control.Applicative.Combinators.NonEmpty qualified as NE
import Data.Foldable1 (Foldable1 (..))
import Data.Text (Text)
import Data.Void (Void)
import Text.Megaparsec (
  MonadParsec (..),
  Parsec,
  many,
  sepEndBy,
  (<|>),
 )
import Text.Megaparsec.Char (char, eol)
import Text.Megaparsec.Char qualified as L

type Parser = Parsec Void Text

charInRange :: Char -> Char -> Char -> Bool
charInRange lb ub x = lb <= x && x <= ub

(|||) :: (a -> Bool) -> (a -> Bool) -> (a -> Bool)
(x ||| y) a = x a || y a

pComment :: Parser Comment
pComment =
  commentLine <$> label "comment" (char ';' *> takeWhileP Nothing validChar <* eol)
  where
    validChar = charInRange '\x20' '\x7e' ||| charInRange '\x80' '\x10fffd'

pCommentBlock :: Parser Comment
pCommentBlock = fold1 <$> NE.some (L.hspace *> pComment)

space :: Parser Comment
space = mconcat <$> (L.space *> sepEndBy pComment L.space)

-- | Non-greedy space consumer for use at construct boundaries.
-- Consumes a same-line comment and any immediately adjacent comment lines
-- (no blank line gap), then eats trailing blank lines.
-- Does NOT cross blank-line boundaries to consume further comment blocks.
trailingSpace :: Parser Comment
trailingSpace = do
  _ <- L.hspace
  comments <- sameLineComments
  L.space
  pure $ mconcat comments
  where
    sameLineComments =
      ( do
          c <- pComment
          rest <- adjacentComments
          pure (c : rest)
      )
        <|> (eol *> adjacentComments)
        <|> pure []

    adjacentComments = many (try (L.hspace *> pComment))