{-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE NoImplicitPrelude #-} {-# OPTIONS_GHC -Wno-overflowed-literals #-} module Data.Morpheus.Parsing.Internal.SourceText ( parseStringBS, ignoredTokens, ignoredTokens1, ) where import Data.ByteString.Lazy.Internal ( ByteString, ) import Data.Morpheus.Parsing.Internal.Internal ( Parser, ) import Relude hiding (ByteString, empty, many) import Text.Megaparsec ( choice, label, many, satisfy, takeWhile1P, takeWhileP, unexpected, ) import Text.Megaparsec.Byte ( char, space1, string, ) import Text.Megaparsec.Error ( ErrorItem (..), ) -- White Space #define TABULATION 0x0009 #define NEW_LINE 0x000A #define SPACE 0x0020 #define CARRIAGE_RETURN 0x000D #define UNICODE_BOM 0xFEFF #define NON_CHARACTER 0xFFFF -- Non-alphabetic characters #define DOUBLE_QUOTE 34 #define BACKSLASH 92 #define COMMA 44 #define HASH_TAG 35 -- Alphabetic characters #define CHAR_b 98 #define CHAR_f 102 #define CHAR_n 110 #define CHAR_r 114 #define CHAR_t 116 -- https://spec.graphql.org/June2018/#sec-Source-Text -- SourceCharacter : [\u0009\u000A\u000D\u0020-\uFFFF]/ isSourceCharacter :: Word8 -> Bool isSourceCharacter TABULATION = True isSourceCharacter NEW_LINE = True isSourceCharacter CARRIAGE_RETURN = True isSourceCharacter x = SPACE <= x && x <= NON_CHARACTER {-# INLINE isSourceCharacter #-} inlineString :: Parser ByteString inlineString = label "String" $ char DOUBLE_QUOTE *> parseContent <* ignoredTokens {-# INLINE inlineString #-} parseContent :: Parser ByteString parseContent = do xs <- takeWhileP Nothing (\x -> isSourceCharacter x && DOUBLE_QUOTE /= x && x /= BACKSLASH && NEW_LINE /= x) z <- satisfy (const True) case z of DOUBLE_QUOTE -> pure xs BACKSLASH -> (xs <>) <$> ((<>) <$> escapeChar <*> parseContent) w -> unexpected (Tokens (w :| [])) where escapeChar :: Parser ByteString escapeChar = choice [ char CHAR_b $> "\b", char CHAR_f $> "\f", char CHAR_n $> "\n", char CHAR_r $> "\r", char CHAR_t $> "\t", char BACKSLASH $> "\\", char DOUBLE_QUOTE $> "\"", char 47 $> "/" ] {-# INLINE escapeChar #-} {-# INLINE parseContent #-} blockString :: Parser ByteString blockString = string "\"\"\"" *> content <* ignoredTokens where content :: Parser ByteString content = do text <- takeWhileP Nothing (\x -> isSourceCharacter x && x /= DOUBLE_QUOTE) doubleQuotes <- takeWhileP Nothing (== DOUBLE_QUOTE) case doubleQuotes of "\"\"\"" -> pure text _ -> ((text <> doubleQuotes) <>) <$> content {-# INLINE content #-} {-# INLINE blockString #-} parseStringBS :: Parser ByteString parseStringBS = blockString <|> inlineString {-# INLINE parseStringBS #-} -- Ignored Tokens : https://graphql.github.io/graphql-spec/June2018/#sec-Source-Text.Ignored-Tokens ignoredTokens :: Parser () ignoredTokens = label "IgnoredTokens" $ many ignored $> () {-# INLINE ignoredTokens #-} -- isIgnored :: UnicodeBOM, WhiteSpace, LineTerminator, Comment , Comma ignored :: Parser () ignored = (takeWhile1P Nothing isIgnored <|> comment) $> () where isIgnored x = (x >= TABULATION && x <= CARRIAGE_RETURN) || x == SPACE || x == COMMA || x == UNICODE_BOM {-# INLINE isIgnored #-} comment = char HASH_TAG *> takeWhileP Nothing (\x -> isSourceCharacter x && x /= NEW_LINE) {-# INLINE comment #-} {-# INLINE ignored #-} ignoredTokens1 :: Parser () ignoredTokens1 = space1 *> ignoredTokens {-# INLINE ignoredTokens1 #-}