1 {-# LANGUAGE PatternSynonyms #-} -- For Comb
2 {-# LANGUAGE TemplateHaskell #-} -- For branch
3 {-# LANGUAGE ViewPatterns #-} -- For unSomeComb
4 {-# OPTIONS_GHC -fno-warn-orphans #-} -- For MakeLetName TH.Name
5 -- | Bottom-up optimization of 'Comb'inators,
6 -- reexamining downward as needed after each optimization.
7 module Symantic.Parser.Grammar.Optimize where
9 import Data.Bool (Bool(..))
10 import Data.Either (Either(..), either)
11 import Data.Eq (Eq(..))
12 import Data.Function ((.))
13 import Data.Kind (Constraint)
14 import Data.Maybe (Maybe(..))
16 import Type.Reflection (Typeable, typeRep, eqTypeRep, (:~~:)(..))
17 import qualified Data.Foldable as Foldable
18 import qualified Data.Functor as Functor
19 import qualified Data.List as List
21 import Symantic.Parser.Grammar.Combinators hiding (code)
22 import Symantic.Parser.Haskell ()
23 import Symantic.Univariant.Letable
24 import Symantic.Univariant.Trans
25 import qualified Symantic.Parser.Haskell as H
28 import Data.Function (($), flip)
29 import Debug.Trace (trace)
35 -- * Type 'OptimizeGrammar'
36 type OptimizeGrammar = SomeComb
39 Trans (SomeComb repr) repr =>
40 SomeComb repr a -> repr a
41 optimizeGrammar = trans
43 -- * Data family 'Comb'
44 -- | 'Comb'inators of the 'Grammar'.
45 -- This is an extensible data-type.
47 (comb :: ReprComb -> Constraint)
51 -- | Convenient utility to pattern-match a 'SomeComb'.
52 pattern Comb :: Typeable comb => Comb comb repr a -> SomeComb repr a
53 pattern Comb x <- (unSomeComb -> Just x)
56 -- | Some 'Comb'inator existentialized over the actual combinator symantic class.
57 -- Useful to handle a list of 'Comb'inators
58 -- without requiring impredicative quantification.
59 -- Must be used by pattern-matching
60 -- on the 'SomeComb' data-constructor,
61 -- to bring the constraints in scope.
63 -- The optimizations are directly applied within it,
64 -- to avoid introducing an extra newtype,
65 -- this also give a more understandable code.
66 data SomeComb repr a =
68 (Trans (Comb comb repr) repr, Typeable comb) =>
69 SomeComb (Comb comb repr a)
71 instance Trans (SomeComb repr) repr where
72 trans (SomeComb x) = trans x
74 -- | @(unSomeComb c :: 'Maybe' ('Comb' comb repr a))@
75 -- extract the data-constructor from the given 'SomeComb'
76 -- iif. it belongs to the @('Comb' comb repr a)@ data-instance.
80 SomeComb repr a -> Maybe (Comb comb repr a)
81 unSomeComb (SomeComb (c::Comb c repr a)) =
82 case typeRep @comb `eqTypeRep` typeRep @c of
87 data instance Comb CombAlternable repr a where
88 Alt :: Exception -> SomeComb repr a -> SomeComb repr a -> Comb CombAlternable repr a
89 Empty :: Comb CombAlternable repr a
90 Failure :: SomeFailure -> Comb CombAlternable repr a
91 Throw :: ExceptionLabel -> Comb CombAlternable repr a
92 Try :: SomeComb repr a -> Comb CombAlternable repr a
93 instance CombAlternable repr => Trans (Comb CombAlternable repr) repr where
95 Alt exn x y -> alt exn (trans x) (trans y)
97 Failure sf -> failure sf
98 Throw exn -> throw exn
99 Try x -> try (trans x)
101 ( CombAlternable repr
102 , CombApplicable repr
105 , CombSelectable repr
106 ) => CombAlternable (SomeComb repr) where
107 empty = SomeComb Empty
108 failure sf = SomeComb (Failure sf)
110 alt _exn p@(Comb Pure{}) _ = p
111 -- & trace "Left Catch Law"
112 alt _exn (Comb Empty) u = u
113 -- & trace "Left Neutral Law"
114 alt _exn u (Comb Empty) = u
115 -- & trace "Right Neutral Law"
116 alt exn (Comb (Alt exn' u v)) w | exn' == exn = u <|> (v <|> w)
117 -- See Lemma 1 (Associativity of choice for labeled PEGs)
118 -- in https://doi.org/10.1145/2851613.2851750
119 -- & trace "Associativity Law"
120 alt exn (Comb (Look p)) (Comb (Look q)) = look (alt exn (try p) q)
121 -- & trace "Distributivity Law"
122 alt exn x y = SomeComb (Alt exn x y)
124 throw exn = SomeComb (Throw exn)
126 try (Comb (p :$>: x)) = try p $> x
127 -- & trace "Try Interchange Law"
128 try (Comb (f :<$>: p)) = f <$> try p
129 -- & trace "Try Interchange Law"
130 try x = SomeComb (Try x)
133 data instance Comb CombApplicable repr a where
134 Pure :: TermGrammar a -> Comb CombApplicable repr a
135 (:<*>:) :: SomeComb repr (a -> b) -> SomeComb repr a -> Comb CombApplicable repr b
136 (:<*:) :: SomeComb repr a -> SomeComb repr b -> Comb CombApplicable repr a
137 (:*>:) :: SomeComb repr a -> SomeComb repr b -> Comb CombApplicable repr b
138 infixl 4 :<*>:, :<*:, :*>:
139 pattern (:<$>:) :: TermGrammar (a -> b) -> SomeComb repr a -> Comb CombApplicable repr b
140 pattern t :<$>: x <- Comb (Pure t) :<*>: x
141 pattern (:$>:) :: SomeComb repr a -> TermGrammar b -> Comb CombApplicable repr b
142 pattern x :$>: t <- x :*>: Comb (Pure t)
143 instance CombApplicable repr => Trans (Comb CombApplicable repr) repr where
145 Pure x -> pure (H.optimizeTerm x)
146 f :<*>: x -> trans f <*> trans x
147 x :<*: y -> trans x <* trans y
148 x :*>: y -> trans x *> trans y
150 ( CombApplicable repr
151 , CombAlternable repr
154 , CombSelectable repr
155 ) => CombApplicable (SomeComb repr) where
156 pure = SomeComb . Pure
157 f <$> Comb (Branch b l r) =
161 -- & trace "Branch Distributivity Law"
162 f <$> Comb (Conditional a ps bs d) =
164 ((f <$>) Functor.<$> bs)
166 -- & trace "Conditional Distributivity Law"
167 -- Being careful here to use (<*>),
168 -- instead of SomeComb (f <$> unOptComb x),
169 -- in order to apply the optimizations of (<*>).
170 f <$> x = pure f <*> x
173 -- & trace "Commutativity Law"
175 Comb Empty <*> _ = empty
176 -- & trace "App Right Absorption Law"
177 u <*> Comb Empty = u *> empty
178 -- & trace "App Failure Weakening Law"
179 Comb (Pure f) <*> Comb (Pure x) = pure (f H..@ x)
180 -- & trace "Homomorphism Law"
182 Comb (Pure f) <*> Comb (g :<$>: p) =
183 -- This is basically a shortcut,
184 -- it can be caught by one Composition Law
185 -- and two Homomorphism Law.
186 (H..) H..@ f H..@ g <$> p
187 -- & trace "Functor Composition Law"
189 u <*> Comb (Pure x) = H.flip H..@ (H.$) H..@ x <$> u
190 -- & trace "Interchange Law"
191 u <*> Comb (v :<*>: w) = (((H..) <$> u) <*> v) <*> w
192 -- & trace "Composition Law"
193 Comb (u :*>: v) <*> w = u *> (v <*> w)
194 -- & trace "Reassociation Law 1"
195 u <*> Comb (v :<*: w) = (u <*> v) <* w
196 -- & trace "Reassociation Law 2"
197 u <*> Comb (v :$>: x) = (u <*> pure x) <* v
198 -- & trace "Reassociation Law 3"
199 p <*> Comb (NegLook q) =
200 (p <*> pure H.unit) <* negLook q
201 -- & trace "Absorption Law"
202 x <*> y = SomeComb (x :<*>: y)
204 Comb Empty *> _ = empty
205 -- & trace "App Right Absorption Law"
206 Comb (_ :<$>: p) *> q = p *> q
207 -- & trace "Right Absorption Law"
209 -- & trace "Identity Law"
210 Comb (u :$>: _) *> v = u *> v
211 -- & trace "Identity Law"
212 u *> Comb (v :*>: w) = (u *> v) *> w
213 -- & trace "Associativity Law"
214 x *> y = SomeComb (x :*>: y)
216 Comb Empty <* _ = empty
217 -- & trace "App Right Absorption Law"
218 u <* Comb Empty = u *> empty
219 -- & trace "App Failure Weakening Law"
220 p <* Comb (_ :<$>: q) = p <* q
221 -- & trace "Left Absorption Law"
223 -- & trace "Identity Law"
224 u <* Comb (v :$>: _) = u <* v
225 -- & trace "Identity Law"
226 Comb (u :<*: v) <* w = u <* (v <* w)
227 -- & trace "Associativity Law"
228 x <* y = SomeComb (x :<*: y)
231 data instance Comb CombFoldable repr a where
232 ChainPreC :: SomeComb repr (a -> a) -> SomeComb repr a -> Comb CombFoldable repr a
233 ChainPostC :: SomeComb repr a -> SomeComb repr (a -> a) -> Comb CombFoldable repr a
234 instance CombFoldable repr => Trans (Comb CombFoldable repr) repr where
236 ChainPreC x y -> chainPre (trans x) (trans y)
237 ChainPostC x y -> chainPost (trans x) (trans y)
238 instance CombFoldable repr => CombFoldable (SomeComb repr) where
239 chainPre x = SomeComb . ChainPreC x
240 chainPost x = SomeComb . ChainPostC x
243 data instance Comb (Letable letName) repr a where
244 Shareable :: letName -> SomeComb repr a -> Comb (Letable letName) repr a
245 Ref :: Bool -> letName -> Comb (Letable letName) repr a
247 Letable letName repr =>
248 Trans (Comb (Letable letName) repr) repr where
250 Shareable n x -> shareable n (trans x)
251 Ref isRec n -> ref isRec n
253 (Letable letName repr, Typeable letName) =>
254 Letable letName (SomeComb repr) where
255 shareable n = SomeComb . Shareable n
256 ref isRec = SomeComb . Ref isRec
259 data instance Comb (Letsable letName) repr a where
260 Lets :: LetBindings letName (SomeComb repr) ->
261 SomeComb repr a -> Comb (Letsable letName) repr a
263 Letsable letName repr =>
264 Trans (Comb (Letsable letName) repr) repr where
266 Lets defs x -> lets ((\(SomeLet sub) -> SomeLet (trans sub)) Functor.<$> defs) (trans x)
268 (Letsable letName repr, Typeable letName) =>
269 Letsable letName (SomeComb repr) where
270 lets defs = SomeComb . Lets defs
273 data instance Comb CombLookable repr a where
274 Look :: SomeComb repr a -> Comb CombLookable repr a
275 NegLook :: SomeComb repr a -> Comb CombLookable repr ()
276 Eof :: Comb CombLookable repr ()
277 instance CombLookable repr => Trans (Comb CombLookable repr) repr where
279 Look x -> look (trans x)
280 NegLook x -> negLook (trans x)
283 ( CombAlternable repr
284 , CombApplicable repr
286 , CombSelectable repr
288 ) => CombLookable (SomeComb repr) where
289 look p@(Comb Pure{}) = p
290 -- & trace "Pure Look Law"
291 look p@(Comb Empty) = p
292 -- & trace "Dead Look Law"
293 look (Comb (Look x)) = look x
294 -- & trace "Idempotence Law"
295 look (Comb (NegLook x)) = negLook x
296 -- & trace "Left Identity Law"
297 look (Comb (p :$>: x)) = look p $> x
298 -- & trace "Interchange Law"
299 look (Comb (f :<$>: p)) = f <$> look p
300 -- & trace "Interchange Law"
301 look x = SomeComb (Look x)
303 negLook (Comb Pure{}) = empty
304 -- & trace "Pure Negative-Look"
305 negLook (Comb Empty) = pure H.unit
306 -- & trace "Dead Negative-Look"
307 negLook (Comb (NegLook x)) = look (try x *> pure H.unit)
308 -- & trace "Double Negation Law"
309 negLook (Comb (Try x)) = negLook x
310 -- & trace "Zero Consumption Law"
311 negLook (Comb (Look x)) = negLook x
312 -- & trace "Right Identity Law"
313 negLook (Comb (Alt _exn (Comb (Try p)) q)) = negLook p *> negLook q
314 -- FIXME: see if this really holds for all exceptions.
315 -- & trace "Transparency Law"
316 negLook (Comb (p :$>: _)) = negLook p
317 -- & trace "NegLook Idempotence Law"
318 negLook x = SomeComb (NegLook x)
323 data instance Comb CombMatchable repr a where
324 Conditional :: Eq a =>
326 [TermGrammar (a -> Bool)] ->
329 Comb CombMatchable repr b
330 instance CombMatchable repr => Trans (Comb CombMatchable repr) repr where
332 Conditional a ps bs b ->
333 conditional (trans a)
334 (H.optimizeTerm Functor.<$> ps)
335 (trans Functor.<$> bs) (trans b)
337 ( CombApplicable repr
338 , CombAlternable repr
340 , CombSelectable repr
342 ) => CombMatchable (SomeComb repr) where
343 conditional (Comb Empty) _ _ d = d
344 -- & trace "Conditional Absorption Law"
345 conditional p _ qs (Comb Empty)
346 | Foldable.all (\case { Comb Empty -> True; _ -> False }) qs = p *> empty
347 -- & trace "Conditional Weakening Law"
348 conditional a _ps bs (Comb Empty)
349 | Foldable.all (\case { Comb Empty -> True; _ -> False }) bs = a *> empty
350 -- & trace "Conditional Weakening Law"
351 conditional (Comb (Pure (trans -> a))) ps bs d =
352 Foldable.foldr (\(trans -> p, b) next ->
353 if H.value p (H.value a) then b else next
355 -- & trace "Conditional Pure Law"
356 conditional a ps bs d = SomeComb (Conditional a ps bs d)
359 data instance Comb (CombSatisfiable tok) repr a where
360 -- | To include the @('Set' 'SomeFailure')@ is a little kludge
361 -- it would be cleaner to be able to pattern-match
362 -- on @(alt exn (Comb 'Satisfy'{}) (Failure{}))@
363 -- when generating 'Program', but this is not easily possible then
364 -- because data types have already been converted back to class methods,
365 -- hence pattern-matching is no longer possible
366 -- (at least not without reintroducing data types).
368 CombSatisfiable tok repr =>
370 TermGrammar (tok -> Bool) ->
371 Comb (CombSatisfiable tok) repr tok
373 CombSatisfiable tok repr =>
374 Trans (Comb (CombSatisfiable tok) repr) repr where
376 SatisfyOrFail fs p -> satisfyOrFail fs (H.optimizeTerm p)
378 (CombSatisfiable tok repr, Typeable tok) =>
379 CombSatisfiable tok (SomeComb repr) where
380 satisfyOrFail fs = SomeComb . SatisfyOrFail fs
383 data instance Comb CombSelectable repr a where
385 SomeComb repr (Either a b) ->
386 SomeComb repr (a -> c) ->
387 SomeComb repr (b -> c) ->
388 Comb CombSelectable repr c
389 instance CombSelectable repr => Trans (Comb CombSelectable repr) repr where
391 Branch lr l r -> branch (trans lr) (trans l) (trans r)
393 ( CombApplicable repr
394 , CombAlternable repr
396 , CombSelectable repr
398 ) => CombSelectable (SomeComb repr) where
399 branch (Comb Empty) _ _ = empty
400 -- & trace "Branch Absorption Law"
401 branch b (Comb Empty) (Comb Empty) = b *> empty
402 -- & trace "Branch Weakening Law"
403 branch (Comb (Pure (trans -> lr))) l r =
405 Left value -> l <*> pure (trans H.ValueCode{..})
406 where code = [|| case $$(H.code lr) of Left x -> x ||]
407 Right value -> r <*> pure (trans H.ValueCode{..})
408 where code = [|| case $$(H.code lr) of Right x -> x ||]
409 -- & trace "Branch Pure Left/Right Law"
410 branch b (Comb (Pure (trans -> l))) (Comb (Pure (trans -> r))) =
411 trans H.ValueCode{..} <$> b
412 -- & trace "Branch Generalised Identity Law"
414 value = either (H.value l) (H.value r)
415 code = [|| either $$(H.code l) $$(H.code r) ||]
416 branch (Comb (x :*>: y)) p q = x *> branch y p q
417 -- & trace "Interchange Law"
418 branch b l (Comb Empty) =
419 branch (pure (trans (H.ValueCode{..})) <*> b) empty l
420 -- & trace "Negated Branch Law"
422 value = either Right Left
423 code = [||either Right Left||]
424 branch (Comb (Branch b (Comb Empty) (Comb (Pure (trans -> lr))))) (Comb Empty) br =
425 branch (pure (trans H.ValueCode{..}) <*> b) empty br
426 -- & trace "Branch Fusion Law"
428 value Left{} = Left ()
429 value (Right r) = case H.value lr r of
432 code = [|| \case Left{} -> Left ()
433 Right r -> case $$(H.code lr) r of
435 Right rr -> Right rr ||]
436 branch b l r = SomeComb (Branch b l r)