{-# LANGUAGE QuasiQuotes #-} {-# OPTIONS_GHC -fspec-constr-recursive=4 #-} import Data.Function ((&)) import Data.Functor.Identity (runIdentity) import Streamly.Data.Parser (Parser) import Streamly.Unicode.String (str) import qualified Data.Char as Char import qualified Streamly.Data.Fold as Fold import qualified Streamly.Data.Parser as Parser import qualified Streamly.Data.Stream as Stream -- Example of a quoted log string to be parsed quoted :: String quoted = [str| "[2023-02-04T09:15:20.549Z] \"GET /cards?customer_id=XXXXP/1.1\" 200 - \"-\" \"-\" 0 75 23 23 \"131.26.22.133,127.16.101.49,127.18.4.69\" \"edge\" \"7ccc821a-03ff-49c1-a721-977e7cbd78f3\" \"api.example.in\" \"127.18.55.25:1108\" outbound|443|v21945461|svc.cluster.local 127.18.44.67:75523 127.18.44.67:8443 127.18.4.69:14462 mum.example.net confirmtkt\n" |] -- Use double quote as the quoting char isQuote :: Char -> Maybe Char isQuote x = case x of '"' -> Just x _ -> Nothing -- Transliterate \" and \n tr :: Char -> Char -> Maybe Char tr q x = case x of _ | x == q -> Just x 'n' -> Just '\n' _ -> Nothing -- quoted string parser, reads everything inside quotes as a single word -- and processes the quotes and escapes to expose the words inside it. parser :: Monad m => Parser Char m [Char] parser = Parser.wordWithQuotes False tr '\\' isQuote Char.isSpace Fold.toList main :: IO () main = do -- Strip outer quotes let res = runIdentity $ Stream.parse parser $ Stream.fromList quoted -- parse words inside quotes case res of Left err -> error $ "Malformed quoted string: " ++ show err Right unquoted -> Stream.fromList unquoted & Stream.parseMany parser & Stream.catRights & Stream.toList >>= print