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 Data.Functor.Identity (Identity(..))
17 import Data.Functor.Product (Product(..))
18 import Type.Reflection (Typeable, typeRep, eqTypeRep, (:~~:)(..))
19 import qualified Data.Foldable as Foldable
20 import qualified Data.Functor as Functor
21 import qualified Data.List as List
23 import Symantic.Parser.Grammar.Combinators hiding (code)
24 import Symantic.Parser.Grammar.Production
25 import Symantic.Univariant.Letable
26 import Symantic.Univariant.Trans
27 import qualified Symantic.Parser.Grammar.Production as Prod
28 import qualified Symantic.Univariant.Data as H
29 import qualified Symantic.Univariant.Lang as H
32 import Data.Function (($), flip)
33 import Debug.Trace (trace)
39 -- * Type 'OptimizeGrammar'
40 type OptimizeGrammar = SomeComb
43 Trans (SomeComb repr) repr =>
44 SomeComb repr a -> repr a
45 optimizeGrammar = trans
47 -- * Data family 'Comb'
48 -- | 'Comb'inators of the 'Grammar'.
49 -- This is an extensible data-type.
51 (comb :: ReprComb -> Constraint)
52 :: ReprComb -> ReprComb
54 -- | Convenient utility to pattern-match a 'SomeComb'.
55 pattern Comb :: Typeable comb => Comb comb repr a -> SomeComb repr a
56 pattern Comb x <- (unSomeComb -> Just x)
59 -- | Some 'Comb'inator existentialized over the actual combinator symantic class.
60 -- Useful to handle a list of 'Comb'inators
61 -- without requiring impredicative quantification.
62 -- Must be used by pattern-matching
63 -- on the 'SomeComb' data-constructor,
64 -- to bring the constraints in scope.
66 -- The optimizations are directly applied within it,
67 -- to avoid introducing an extra newtype,
68 -- this also give a more understandable code.
69 data SomeComb repr a =
71 (Trans (Comb comb repr) repr, Typeable comb) =>
72 SomeComb (Comb comb repr a)
74 instance Trans (SomeComb repr) repr where
75 trans (SomeComb x) = trans x
77 -- | @(unSomeComb c :: 'Maybe' ('Comb' comb repr a))@
78 -- extract the data-constructor from the given 'SomeComb'
79 -- iif. it belongs to the @('Comb' comb repr a)@ data-instance.
83 SomeComb repr a -> Maybe (Comb comb repr a)
84 unSomeComb (SomeComb (c::Comb c repr a)) =
85 case typeRep @comb `eqTypeRep` typeRep @c of
90 data instance Comb CombAlternable repr a where
91 Alt :: Exception -> SomeComb repr a -> SomeComb repr a -> Comb CombAlternable repr a
92 Empty :: Comb CombAlternable repr a
93 Failure :: SomeFailure -> Comb CombAlternable repr a
94 Throw :: ExceptionLabel -> Comb CombAlternable repr a
95 Try :: SomeComb repr a -> Comb CombAlternable repr a
96 instance CombAlternable repr => Trans (Comb CombAlternable repr) repr where
98 Alt exn x y -> alt exn (trans x) (trans y)
100 Failure sf -> failure sf
101 Throw exn -> throw exn
102 Try x -> try (trans x)
104 ( CombAlternable repr
105 , CombApplicable repr
108 , CombSelectable repr
109 ) => CombAlternable (SomeComb repr) where
110 empty = SomeComb Empty
111 failure sf = SomeComb (Failure sf)
113 alt _exn p@(Comb Pure{}) _ = p
114 -- & trace "Left Catch Law"
115 alt _exn (Comb Empty) u = u
116 -- & trace "Left Neutral Law"
117 alt _exn u (Comb Empty) = u
118 -- & trace "Right Neutral Law"
119 alt exn (Comb (Alt exn' u v)) w | exn' == exn = u <|> (v <|> w)
120 -- See Lemma 1 (Associativity of choice for labeled PEGs)
121 -- in https://doi.org/10.1145/2851613.2851750
122 -- & trace "Associativity Law"
123 alt exn (Comb (Look p)) (Comb (Look q)) = look (alt exn (try p) q)
124 -- & trace "Distributivity Law"
125 alt exn x y = SomeComb (Alt exn x y)
127 throw exn = SomeComb (Throw exn)
129 try (Comb (p :$>: x)) = try p $> x
130 -- & trace "Try Interchange Law"
131 try (Comb (f :<$>: p)) = f <$> try p
132 -- & trace "Try Interchange Law"
133 try x = SomeComb (Try x)
136 data instance Comb CombApplicable repr a where
137 Pure :: Production a -> Comb CombApplicable repr a
138 (:<*>:) :: SomeComb repr (a -> b) -> SomeComb repr a -> Comb CombApplicable repr b
139 (:<*:) :: SomeComb repr a -> SomeComb repr b -> Comb CombApplicable repr a
140 (:*>:) :: SomeComb repr a -> SomeComb repr b -> Comb CombApplicable repr b
141 infixl 4 :<*>:, :<*:, :*>:
142 pattern (:<$>:) :: Production (a -> b) -> SomeComb repr a -> Comb CombApplicable repr b
143 pattern t :<$>: x <- Comb (Pure t) :<*>: x
144 pattern (:$>:) :: SomeComb repr a -> Production b -> Comb CombApplicable repr b
145 pattern x :$>: t <- x :*>: Comb (Pure t)
146 instance CombApplicable repr => Trans (Comb CombApplicable repr) repr where
148 Pure x -> pure (optimizeProduction x)
149 f :<*>: x -> trans f <*> trans x
150 x :<*: y -> trans x <* trans y
151 x :*>: y -> trans x *> trans y
153 ( CombApplicable repr
154 , CombAlternable repr
157 , CombSelectable repr
158 ) => CombApplicable (SomeComb repr) where
159 pure = SomeComb . Pure
160 f <$> Comb (Branch b l r) =
164 -- & trace "Branch Distributivity Law"
165 f <$> Comb (Conditional a ps bs d) =
167 ((f <$>) Functor.<$> bs)
169 -- & trace "Conditional Distributivity Law"
170 -- Being careful here to use (<*>),
171 -- instead of SomeComb (f <$> unOptComb x),
172 -- in order to apply the optimizations of (<*>).
173 f <$> x = pure f <*> x
176 -- & trace "Commutativity Law"
178 Comb Empty <*> _ = empty
179 -- & trace "App Right Absorption Law"
180 u <*> Comb Empty = u *> empty
181 -- & trace "App Failure Weakening Law"
182 Comb (Pure f) <*> Comb (Pure x) = pure (f H..@ x)
183 -- & trace "Homomorphism Law"
185 Comb (Pure f) <*> Comb (g :<$>: p) =
186 -- This is basically a shortcut,
187 -- it can be caught by one Composition Law
188 -- and two Homomorphism Law.
189 (H..) H..@ f H..@ g <$> p
190 -- & trace "Functor Composition Law"
192 u <*> Comb (Pure x) = H.flip H..@ (H.$) H..@ x <$> u
193 -- & trace "Interchange Law"
194 u <*> Comb (v :<*>: w) = (((H..) <$> u) <*> v) <*> w
195 -- & trace "Composition Law"
196 Comb (u :*>: v) <*> w = u *> (v <*> w)
197 -- & trace "Reassociation Law 1"
198 u <*> Comb (v :<*: w) = (u <*> v) <* w
199 -- & trace "Reassociation Law 2"
200 u <*> Comb (v :$>: x) = (u <*> pure x) <* v
201 -- & trace "Reassociation Law 3"
202 p <*> Comb (NegLook q) =
203 (p <*> pure H.unit) <* negLook q
204 -- & trace "Absorption Law"
205 x <*> y = SomeComb (x :<*>: y)
207 Comb Empty *> _ = empty
208 -- & trace "App Right Absorption Law"
209 Comb (_ :<$>: p) *> q = p *> q
210 -- & trace "Right Absorption Law"
212 -- & trace "Identity Law"
213 Comb (u :$>: _) *> v = u *> v
214 -- & trace "Identity Law"
215 u *> Comb (v :*>: w) = (u *> v) *> w
216 -- & trace "Associativity Law"
217 x *> y = SomeComb (x :*>: y)
219 Comb Empty <* _ = empty
220 -- & trace "App Right Absorption Law"
221 u <* Comb Empty = u *> empty
222 -- & trace "App Failure Weakening Law"
223 p <* Comb (_ :<$>: q) = p <* q
224 -- & trace "Left Absorption Law"
226 -- & trace "Identity Law"
227 u <* Comb (v :$>: _) = u <* v
228 -- & trace "Identity Law"
229 Comb (u :<*: v) <* w = u <* (v <* w)
230 -- & trace "Associativity Law"
231 x <* y = SomeComb (x :<*: y)
234 data instance Comb CombFoldable repr a where
235 ChainPreC :: SomeComb repr (a -> a) -> SomeComb repr a -> Comb CombFoldable repr a
236 ChainPostC :: SomeComb repr a -> SomeComb repr (a -> a) -> Comb CombFoldable repr a
237 instance CombFoldable repr => Trans (Comb CombFoldable repr) repr where
239 ChainPreC x y -> chainPre (trans x) (trans y)
240 ChainPostC x y -> chainPost (trans x) (trans y)
241 instance CombFoldable repr => CombFoldable (SomeComb repr) where
242 chainPre x = SomeComb . ChainPreC x
243 chainPost x = SomeComb . ChainPostC x
246 data instance Comb (Letable letName) repr a where
247 Shareable :: letName -> SomeComb repr a -> Comb (Letable letName) repr a
248 Ref :: Bool -> letName -> Comb (Letable letName) repr a
250 Letable letName repr =>
251 Trans (Comb (Letable letName) repr) repr where
253 Shareable n x -> shareable n (trans x)
254 Ref isRec n -> ref isRec n
256 (Letable letName repr, Typeable letName) =>
257 Letable letName (SomeComb repr) where
258 shareable n = SomeComb . Shareable n
259 ref isRec = SomeComb . Ref isRec
262 data instance Comb (Letsable letName) repr a where
263 Lets :: LetBindings letName (SomeComb repr) ->
264 SomeComb repr a -> Comb (Letsable letName) repr a
266 Letsable letName repr =>
267 Trans (Comb (Letsable letName) repr) repr where
269 Lets defs x -> lets ((\(SomeLet sub) -> SomeLet (trans sub)) Functor.<$> defs) (trans x)
271 (Letsable letName repr, Typeable letName) =>
272 Letsable letName (SomeComb repr) where
273 lets defs = SomeComb . Lets defs
276 data instance Comb CombLookable repr a where
277 Look :: SomeComb repr a -> Comb CombLookable repr a
278 NegLook :: SomeComb repr a -> Comb CombLookable repr ()
279 Eof :: Comb CombLookable repr ()
280 instance CombLookable repr => Trans (Comb CombLookable repr) repr where
282 Look x -> look (trans x)
283 NegLook x -> negLook (trans x)
286 ( CombAlternable repr
287 , CombApplicable repr
289 , CombSelectable repr
291 ) => CombLookable (SomeComb repr) where
292 look p@(Comb Pure{}) = p
293 -- & trace "Pure Look Law"
294 look p@(Comb Empty) = p
295 -- & trace "Dead Look Law"
296 look (Comb (Look x)) = look x
297 -- & trace "Idempotence Law"
298 look (Comb (NegLook x)) = negLook x
299 -- & trace "Left Identity Law"
300 look (Comb (p :$>: x)) = look p $> x
301 -- & trace "Interchange Law"
302 look (Comb (f :<$>: p)) = f <$> look p
303 -- & trace "Interchange Law"
304 look x = SomeComb (Look x)
306 negLook (Comb Pure{}) = empty
307 -- & trace "Pure Negative-Look"
308 negLook (Comb Empty) = pure H.unit
309 -- & trace "Dead Negative-Look"
310 negLook (Comb (NegLook x)) = look (try x *> pure H.unit)
311 -- & trace "Double Negation Law"
312 negLook (Comb (Try x)) = negLook x
313 -- & trace "Zero Consumption Law"
314 negLook (Comb (Look x)) = negLook x
315 -- & trace "Right Identity Law"
316 negLook (Comb (Alt _exn (Comb (Try p)) q)) = negLook p *> negLook q
317 -- FIXME: see if this really holds for all exceptions.
318 -- & trace "Transparency Law"
319 negLook (Comb (p :$>: _)) = negLook p
320 -- & trace "NegLook Idempotence Law"
321 negLook x = SomeComb (NegLook x)
326 data instance Comb CombMatchable repr a where
327 Conditional :: Eq a =>
329 [Production (a -> Bool)] ->
332 Comb CombMatchable repr b
333 instance CombMatchable repr => Trans (Comb CombMatchable repr) repr where
335 Conditional a ps bs b ->
336 conditional (trans a)
337 (optimizeProduction Functor.<$> ps)
338 (trans Functor.<$> bs) (trans b)
340 ( CombApplicable repr
341 , CombAlternable repr
343 , CombSelectable repr
345 ) => CombMatchable (SomeComb repr) where
346 conditional (Comb Empty) _ _ d = d
347 -- & trace "Conditional Absorption Law"
348 conditional p _ qs (Comb Empty)
349 | Foldable.all (\case { Comb Empty -> True; _ -> False }) qs = p *> empty
350 -- & trace "Conditional Weakening Law"
351 conditional a _ps bs (Comb Empty)
352 | Foldable.all (\case { Comb Empty -> True; _ -> False }) bs = a *> empty
353 -- & trace "Conditional Weakening Law"
354 conditional (Comb (Pure a)) ps bs d =
355 Foldable.foldr (\(p, b) next ->
356 if runValue (p H..@ a) then b else next
358 -- & trace "Conditional Pure Law"
359 conditional a ps bs d = SomeComb (Conditional a ps bs d)
362 data instance Comb (CombSatisfiable tok) repr a where
363 -- | To include the @('Set' 'SomeFailure')@ is a little kludge
364 -- it would be cleaner to be able to pattern-match
365 -- on @(alt exn (Comb 'Satisfy'{}) (Failure{}))@
366 -- when generating 'Program', but this is not easily possible then
367 -- because data types have already been converted back to class methods,
368 -- hence pattern-matching is no longer possible
369 -- (at least not without reintroducing data types).
371 CombSatisfiable tok repr =>
373 Production (tok -> Bool) ->
374 Comb (CombSatisfiable tok) repr tok
376 CombSatisfiable tok repr =>
377 Trans (Comb (CombSatisfiable tok) repr) repr where
379 SatisfyOrFail fs p -> satisfyOrFail fs (optimizeProduction p)
381 (CombSatisfiable tok repr, Typeable tok) =>
382 CombSatisfiable tok (SomeComb repr) where
383 satisfyOrFail fs = SomeComb . SatisfyOrFail fs
386 data instance Comb CombSelectable repr a where
388 SomeComb repr (Either a b) ->
389 SomeComb repr (a -> c) ->
390 SomeComb repr (b -> c) ->
391 Comb CombSelectable repr c
392 instance CombSelectable repr => Trans (Comb CombSelectable repr) repr where
394 Branch lr l r -> branch (trans lr) (trans l) (trans r)
396 ( CombApplicable repr
397 , CombAlternable repr
399 , CombSelectable repr
401 ) => CombSelectable (SomeComb repr) where
402 branch (Comb Empty) _ _ = empty
403 -- & trace "Branch Absorption Law"
404 branch b (Comb Empty) (Comb Empty) = b *> empty
405 -- & trace "Branch Weakening Law"
406 branch (Comb (Pure lr)) l r =
408 Left value -> l <*> pure (Pair v c)
410 v = H.SomeData $ H.Var $ Identity value
411 c = H.SomeData $ H.Var
412 [|| case $$(runCode lr) of Left x -> x ||]
413 Right value -> r <*> pure (Pair v c)
415 v = H.SomeData $ H.Var $ Identity value
416 c = H.SomeData $ H.Var
417 [|| case $$(runCode lr) of Right x -> x ||]
418 -- & trace "Branch Pure Either Law"
419 branch b (Comb (Pure l)) (Comb (Pure r)) =
421 -- & trace "Branch Generalised Identity Law"
423 v = H.SomeData $ H.Var $ Identity $ either (runValue l) (runValue r)
424 c = H.SomeData $ H.Var [|| either $$(runCode l) $$(runCode r) ||]
425 branch (Comb (x :*>: y)) p q = x *> branch y p q
426 -- & trace "Interchange Law"
427 branch b l (Comb Empty) =
428 branch (pure (Pair v c) <*> b) empty l
429 -- & trace "Negated Branch Law"
431 v = H.SomeData $ H.Var $ Identity $ either Right Left
432 c = H.SomeData $ H.Var $ [||either Right Left||]
433 branch (Comb (Branch b (Comb Empty) (Comb (Pure lr)))) (Comb Empty) br =
434 branch (pure (Pair v c) <*> b) empty br
435 -- & trace "Branch Fusion Law"
437 v = H.SomeData $ H.Var $ Identity $ \case
440 case runValue lr r of
443 c = H.SomeData $ H.Var
444 [|| \case Left{} -> Left ()
445 Right r -> case $$(runCode lr) r of
447 Right rr -> Right rr ||]
448 branch b l r = SomeComb (Branch b l r)