@@ -20,6 +20,10 @@ module Database.ClickHouse.Internal.Parser
2020 text ,
2121 takeN ,
2222 uLEB128 ,
23+ checkBounds ,
24+ parserAp ,
25+ parserBind ,
26+ parserFmap ,
2327 )
2428where
2529
@@ -58,39 +62,63 @@ instance MonadIO Parser where
5862 pure (ParseSuccess pos result)
5963
6064instance Functor Parser where
61- fmap f (Parser p) = Parser $ \ end pos -> do
62- result <- p end pos
63- case result of
64- ParseSuccess pos' x -> return $ ParseSuccess pos' (f x)
65- ParseFailure err -> return $ ParseFailure err
66- UnexpectedEndOfInput -> return UnexpectedEndOfInput
65+ fmap = parserFmap
66+ {-# INLINE fmap #-}
6767
6868instance Applicative Parser where
6969 pure x = Parser $ \ _ pos -> return $ ParseSuccess pos x
7070
71- Parser pf <*> Parser px = Parser $ \ end pos -> do
72- result <- pf end pos
73- case result of
74- ParseSuccess pos' f -> do
75- result' <- px end pos'
76- case result' of
77- ParseSuccess pos'' x -> return $ ParseSuccess pos'' (f x)
78- ParseFailure err -> return $ ParseFailure err
79- UnexpectedEndOfInput -> return UnexpectedEndOfInput
80- ParseFailure err -> return $ ParseFailure err
81- UnexpectedEndOfInput -> return UnexpectedEndOfInput
71+ (<*>) = parserAp
72+ {-# INLINE (<*>) #-}
8273
8374instance Monad Parser where
8475 return = pure
8576
86- Parser px >>= f = Parser $ \ end pos -> do
87- result <- px end pos
88- case result of
89- ParseSuccess pos' x -> do
90- let Parser py = f x
91- py end pos'
92- ParseFailure err -> return $ ParseFailure err
93- UnexpectedEndOfInput -> return UnexpectedEndOfInput
77+ (>>=) = parserBind
78+ {-# INLINE (>>=) #-}
79+
80+ -- | Apply a function to the result of a parser.
81+ --
82+ -- Named wrapper used in rewrite rules to fuse 'checkBounds' through 'fmap'.
83+ parserFmap :: (a -> b ) -> Parser a -> Parser b
84+ parserFmap f (Parser p) = Parser $ \ end pos -> do
85+ result <- p end pos
86+ case result of
87+ ParseSuccess pos' x -> return $ ParseSuccess pos' (f x)
88+ ParseFailure err -> return $ ParseFailure err
89+ UnexpectedEndOfInput -> return UnexpectedEndOfInput
90+ {-# NOINLINE [1] parserFmap #-}
91+
92+ -- | Applicative sequencing for parsers.
93+ --
94+ -- Named wrapper used in rewrite rules to fuse consecutive 'checkBounds'.
95+ parserAp :: Parser (a -> b ) -> Parser a -> Parser b
96+ parserAp (Parser pf) (Parser px) = Parser $ \ end pos -> do
97+ result <- pf end pos
98+ case result of
99+ ParseSuccess pos' f -> do
100+ result' <- px end pos'
101+ case result' of
102+ ParseSuccess pos'' x -> return $ ParseSuccess pos'' (f x)
103+ ParseFailure err -> return $ ParseFailure err
104+ UnexpectedEndOfInput -> return UnexpectedEndOfInput
105+ ParseFailure err -> return $ ParseFailure err
106+ UnexpectedEndOfInput -> return UnexpectedEndOfInput
107+ {-# NOINLINE [1] parserAp #-}
108+
109+ -- | Monadic bind for parsers.
110+ --
111+ -- Named wrapper used in rewrite rules to fuse consecutive 'checkBounds'.
112+ parserBind :: Parser a -> (a -> Parser b ) -> Parser b
113+ parserBind (Parser pa) f = Parser $ \ end pos -> do
114+ result <- pa end pos
115+ case result of
116+ ParseSuccess pos' x -> do
117+ let Parser pb = f x
118+ pb end pos'
119+ ParseFailure err -> return $ ParseFailure err
120+ UnexpectedEndOfInput -> return UnexpectedEndOfInput
121+ {-# NOINLINE [1] parserBind #-}
94122
95123runParser :: Parser a -> ByteString -> (ParseResult a , ByteString )
96124runParser (Parser p) bs = unsafeDupablePerformIO $ do
@@ -135,6 +163,29 @@ checkBounds n (Parser k) = Parser $ \end pos ->
135163 if pos `plusPtr` n <= end
136164 then k end pos
137165 else return UnexpectedEndOfInput
166+ {-# NOINLINE [1] checkBounds #-}
167+
168+ -- Rewrite rules that fuse consecutive checkBounds into a single check.
169+ --
170+ -- When two parsers each guarded by checkBounds are sequenced via parserAp,
171+ -- the two bounds checks can be merged: if the first parser consumes exactly
172+ -- n bytes and the second needs m bytes, checking (n + m) bytes upfront
173+ -- suffices and eliminates the second check.
174+ --
175+ -- The rules are keyed on parserAp/parserBind/parserFmap (named functions
176+ -- we control) rather than on class methods, which ensures they fire
177+ -- reliably across module boundaries.
178+ {-# RULES
179+ "checkBounds/parserAp" forall n m p q.
180+ parserAp (checkBounds n p) (checkBounds m q) =
181+ checkBounds (n + m) (parserAp p q)
182+ "checkBounds/parserBind" forall n m p f.
183+ parserBind (checkBounds n p) (\x -> checkBounds m (f x)) =
184+ checkBounds (n + m) (parserBind p f)
185+ "checkBounds/parserFmap" forall f n p.
186+ parserFmap f (checkBounds n p) =
187+ checkBounds n (parserFmap f p)
188+ #-}
138189
139190{-# INLINE word8 #-}
140191word8 :: Parser Word8
0 commit comments