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
24 import Symantic.Parser.Grammar.Production
25 import Symantic.Derive
26 import Symantic.ObserveSharing
27 import qualified Symantic.Data as Prod
28 import qualified Symantic.Lang as Prod
31 import Data.Function (($), flip)
32 import Debug.Trace (trace)
38 -- * Type 'OptimizeGrammar'
39 type OptimizeGrammar = SomeComb
42 Derivable (SomeComb repr) =>
43 SomeComb repr a -> repr a
44 optimizeGrammar = derive
46 -- * Data family 'Comb'
47 -- | 'Comb'inators of the 'Grammar'.
48 -- This is an extensible data-type.
50 (comb :: ReprComb -> Constraint)
51 :: ReprComb -> ReprComb
52 type instance Derived (Comb comb repr) = repr
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 (Derivable (Comb comb repr), Typeable comb) =>
72 SomeComb (Comb comb repr a)
74 type instance Derived (SomeComb repr) = repr
75 instance Derivable (SomeComb repr) where
76 derive (SomeComb x) = derive x
78 -- | @(unSomeComb c :: 'Maybe' ('Comb' comb repr a))@
79 -- extract the data-constructor from the given 'SomeComb'
80 -- iif. it belongs to the @('Comb' comb repr a)@ data-instance.
84 SomeComb repr a -> Maybe (Comb comb repr a)
85 unSomeComb (SomeComb (c::Comb c repr a)) =
86 case typeRep @comb `eqTypeRep` typeRep @c of
91 data instance Comb CombAlternable repr a where
92 Alt :: Exception -> SomeComb repr a -> SomeComb repr a -> Comb CombAlternable repr a
93 Empty :: Comb CombAlternable repr a
94 Failure :: SomeFailure -> Comb CombAlternable repr a
95 Throw :: ExceptionLabel -> Comb CombAlternable repr a
96 Try :: SomeComb repr a -> Comb CombAlternable repr a
97 instance CombAlternable repr => Derivable (Comb CombAlternable repr) where
99 Alt exn x y -> alt exn (derive x) (derive y)
101 Failure sf -> failure sf
102 Throw exn -> throw exn
103 Try x -> try (derive x)
105 ( CombAlternable repr
106 , CombApplicable repr
109 , CombSelectable repr
110 ) => CombAlternable (SomeComb repr) where
111 empty = SomeComb Empty
112 failure sf = SomeComb (Failure sf)
114 alt _exn p@(Comb Pure{}) _ = p
115 -- & trace "Left Catch Law"
116 alt _exn (Comb Empty) u = u
117 -- & trace "Left Neutral Law"
118 alt _exn u (Comb Empty) = u
119 -- & trace "Right Neutral Law"
120 alt exn (Comb (Alt exn' u v)) w | exn' == exn = u <|> (v <|> w)
121 -- See Lemma 1 (Associativity of choice for labeled PEGs)
122 -- in https://doi.org/10.1145/2851613.2851750
123 -- & trace "Associativity Law"
124 alt exn (Comb (Look p)) (Comb (Look q)) = look (alt exn (try p) q)
125 -- & trace "Distributivity Law"
126 alt exn x y = SomeComb (Alt exn x y)
128 throw exn = SomeComb (Throw exn)
130 try (Comb (p :$>: x)) = try p $> x
131 -- & trace "Try Interchange Law"
132 try (Comb (f :<$>: p)) = f <$> try p
133 -- & trace "Try Interchange Law"
134 try x = SomeComb (Try x)
137 data instance Comb CombApplicable repr a where
138 Pure :: Production a -> Comb CombApplicable repr a
139 (:<*>:) :: SomeComb repr (a -> b) -> SomeComb repr a -> Comb CombApplicable repr b
140 (:<*:) :: SomeComb repr a -> SomeComb repr b -> Comb CombApplicable repr a
141 (:*>:) :: SomeComb repr a -> SomeComb repr b -> Comb CombApplicable repr b
142 infixl 4 :<*>:, :<*:, :*>:
143 pattern (:<$>:) :: Production (a -> b) -> SomeComb repr a -> Comb CombApplicable repr b
144 pattern t :<$>: x <- Comb (Pure t) :<*>: x
145 pattern (:$>:) :: SomeComb repr a -> Production b -> Comb CombApplicable repr b
146 pattern x :$>: t <- x :*>: Comb (Pure t)
147 instance CombApplicable repr => Derivable (Comb CombApplicable repr) where
149 Pure x -> pure (optimizeProduction x)
150 f :<*>: x -> derive f <*> derive x
151 x :<*: y -> derive x <* derive y
152 x :*>: y -> derive x *> derive y
154 ( CombApplicable repr
155 , CombAlternable repr
158 , CombSelectable repr
159 ) => CombApplicable (SomeComb repr) where
160 pure = SomeComb . Pure
161 f <$> Comb (Branch b l r) =
163 ((Prod..) Prod..@ f <$> l)
164 ((Prod..) Prod..@ f <$> r)
165 -- & trace "Branch Distributivity Law"
166 f <$> Comb (Conditional a ps bs d) =
168 ((f <$>) Functor.<$> bs)
170 -- & trace "Conditional Distributivity Law"
171 -- Being careful here to use (<*>),
172 -- instead of SomeComb (f <$> unOptComb x),
173 -- in order to apply the optimizations of (<*>).
174 f <$> x = pure f <*> x
177 -- & trace "Commutativity Law"
179 Comb Empty <*> _ = empty
180 -- & trace "App Right Absorption Law"
181 u <*> Comb Empty = u *> empty
182 -- & trace "App Failure Weakening Law"
183 Comb (Pure f) <*> Comb (Pure x) = pure (f Prod..@ x)
184 -- & trace "Homomorphism Law"
186 Comb (Pure f) <*> Comb (g :<$>: p) =
187 -- This is basically a shortcut,
188 -- it can be caught by one Composition Law
189 -- and two Homomorphism Law.
190 (Prod..) Prod..@ f Prod..@ g <$> p
191 -- & trace "Functor Composition Law"
193 u <*> Comb (Pure x) = Prod.flip Prod..@ (Prod.$) Prod..@ x <$> u
194 -- & trace "Interchange Law"
195 u <*> Comb (v :<*>: w) = (((Prod..) <$> u) <*> v) <*> w
196 -- & trace "Composition Law"
197 Comb (u :*>: v) <*> w = u *> (v <*> w)
198 -- & trace "Reassociation Law 1"
199 u <*> Comb (v :<*: w) = (u <*> v) <* w
200 -- & trace "Reassociation Law 2"
201 u <*> Comb (v :$>: x) = (u <*> pure x) <* v
202 -- & trace "Reassociation Law 3"
203 p <*> Comb (NegLook q) =
204 (p <*> pure Prod.unit) <* negLook q
205 -- & trace "Absorption Law"
206 x <*> y = SomeComb (x :<*>: y)
208 Comb Empty *> _ = empty
209 -- & trace "App Right Absorption Law"
210 Comb (_ :<$>: p) *> q = p *> q
211 -- & trace "Right Absorption Law"
213 -- & trace "Identity Law"
214 Comb (u :$>: _) *> v = u *> v
215 -- & trace "Identity Law"
216 u *> Comb (v :*>: w) = (u *> v) *> w
217 -- & trace "Associativity Law"
218 x *> y = SomeComb (x :*>: y)
220 Comb Empty <* _ = empty
221 -- & trace "App Right Absorption Law"
222 u <* Comb Empty = u *> empty
223 -- & trace "App Failure Weakening Law"
224 p <* Comb (_ :<$>: q) = p <* q
225 -- & trace "Left Absorption Law"
227 -- & trace "Identity Law"
228 u <* Comb (v :$>: _) = u <* v
229 -- & trace "Identity Law"
230 Comb (u :<*: v) <* w = u <* (v <* w)
231 -- & trace "Associativity Law"
232 x <* y = SomeComb (x :<*: y)
235 data instance Comb CombFoldable repr a where
236 ChainPreC :: SomeComb repr (a -> a) -> SomeComb repr a -> Comb CombFoldable repr a
237 ChainPostC :: SomeComb repr a -> SomeComb repr (a -> a) -> Comb CombFoldable repr a
238 instance CombFoldable repr => Derivable (Comb CombFoldable repr) where
240 ChainPreC x y -> chainPre (derive x) (derive y)
241 ChainPostC x y -> chainPost (derive x) (derive y)
242 instance CombFoldable repr => CombFoldable (SomeComb repr) where
243 chainPre x = SomeComb . ChainPreC x
244 chainPost x = SomeComb . ChainPostC x
247 data instance Comb (Letable letName) repr a where
248 Shareable :: letName -> SomeComb repr a -> Comb (Letable letName) repr a
249 Ref :: Bool -> letName -> Comb (Letable letName) repr a
251 Letable letName repr =>
252 Derivable (Comb (Letable letName) repr) where
254 Shareable n x -> shareable n (derive x)
255 Ref isRec n -> ref isRec n
257 (Letable letName repr, Typeable letName) =>
258 Letable letName (SomeComb repr) where
259 shareable n = SomeComb . Shareable n
260 ref isRec = SomeComb . Ref isRec
263 data instance Comb (Letsable letName) repr a where
264 Lets :: LetBindings letName (SomeComb repr) ->
265 SomeComb repr a -> Comb (Letsable letName) repr a
267 Letsable letName repr =>
268 Derivable (Comb (Letsable letName) repr) where
270 Lets defs x -> lets ((\(SomeLet sub) -> SomeLet (derive sub)) Functor.<$> defs) (derive x)
272 (Letsable letName repr, Typeable letName) =>
273 Letsable letName (SomeComb repr) where
274 lets defs = SomeComb . Lets defs
277 data instance Comb CombLookable repr a where
278 Look :: SomeComb repr a -> Comb CombLookable repr a
279 NegLook :: SomeComb repr a -> Comb CombLookable repr ()
280 Eof :: Comb CombLookable repr ()
281 instance CombLookable repr => Derivable (Comb CombLookable repr) where
283 Look x -> look (derive x)
284 NegLook x -> negLook (derive x)
287 ( CombAlternable repr
288 , CombApplicable repr
290 , CombSelectable repr
292 ) => CombLookable (SomeComb repr) where
293 look p@(Comb Pure{}) = p
294 -- & trace "Pure Look Law"
295 look p@(Comb Empty) = p
296 -- & trace "Dead Look Law"
297 look (Comb (Look x)) = look x
298 -- & trace "Idempotence Law"
299 look (Comb (NegLook x)) = negLook x
300 -- & trace "Left Identity Law"
301 look (Comb (p :$>: x)) = look p $> x
302 -- & trace "Interchange Law"
303 look (Comb (f :<$>: p)) = f <$> look p
304 -- & trace "Interchange Law"
305 look x = SomeComb (Look x)
307 negLook (Comb Pure{}) = empty
308 -- & trace "Pure Negative-Look"
309 negLook (Comb Empty) = pure Prod.unit
310 -- & trace "Dead Negative-Look"
311 negLook (Comb (NegLook x)) = look (try x *> pure Prod.unit)
312 -- & trace "Double Negation Law"
313 negLook (Comb (Try x)) = negLook x
314 -- & trace "Zero Consumption Law"
315 negLook (Comb (Look x)) = negLook x
316 -- & trace "Right Identity Law"
317 negLook (Comb (Alt _exn (Comb (Try p)) q)) = negLook p *> negLook q
318 -- FIXME: see if this really holds for all exceptions.
319 -- & trace "Transparency Law"
320 negLook (Comb (p :$>: _)) = negLook p
321 -- & trace "NegLook Idempotence Law"
322 negLook x = SomeComb (NegLook x)
327 data instance Comb CombMatchable repr a where
328 Conditional :: Eq a =>
330 [Production (a -> Bool)] ->
333 Comb CombMatchable repr b
334 instance CombMatchable repr => Derivable (Comb CombMatchable repr) where
336 Conditional a ps bs b ->
337 conditional (derive a)
338 (optimizeProduction Functor.<$> ps)
339 (derive Functor.<$> bs) (derive b)
341 ( CombApplicable repr
342 , CombAlternable repr
344 , CombSelectable repr
346 ) => CombMatchable (SomeComb repr) where
347 conditional (Comb Empty) _ _ d = d
348 -- & trace "Conditional Absorption Law"
349 conditional p _ qs (Comb Empty)
350 | Foldable.all (\case { Comb Empty -> True; _ -> False }) qs = p *> empty
351 -- & trace "Conditional Weakening Law"
352 conditional a _ps bs (Comb Empty)
353 | Foldable.all (\case { Comb Empty -> True; _ -> False }) bs = a *> empty
354 -- & trace "Conditional Weakening Law"
355 conditional (Comb (Pure a)) ps bs d =
356 Foldable.foldr (\(p, b) next ->
357 if runValue (p Prod..@ a) then b else next
359 -- & trace "Conditional Pure Law"
360 conditional a ps bs d = SomeComb (Conditional a ps bs d)
363 data instance Comb (CombSatisfiable tok) repr a where
364 -- | To include the @('Set' 'SomeFailure')@ is a little kludge
365 -- it would be cleaner to be able to pattern-match
366 -- on @(alt exn (Comb 'Satisfy'{}) (Failure{}))@
367 -- when generating 'Program', but this is not easily possible then
368 -- because data types have already been converted back to class methods,
369 -- hence pattern-matching is no longer possible
370 -- (at least not without reintroducing data types).
372 CombSatisfiable tok repr =>
374 Production (tok -> Bool) ->
375 Comb (CombSatisfiable tok) repr tok
377 CombSatisfiable tok repr =>
378 Derivable (Comb (CombSatisfiable tok) repr) where
380 SatisfyOrFail fs p -> satisfyOrFail fs (optimizeProduction p)
382 (CombSatisfiable tok repr, Typeable tok) =>
383 CombSatisfiable tok (SomeComb repr) where
384 satisfyOrFail fs = SomeComb . SatisfyOrFail fs
387 data instance Comb CombSelectable repr a where
389 SomeComb repr (Either a b) ->
390 SomeComb repr (a -> c) ->
391 SomeComb repr (b -> c) ->
392 Comb CombSelectable repr c
393 instance CombSelectable repr => Derivable (Comb CombSelectable repr) where
395 Branch lr l r -> branch (derive lr) (derive l) (derive r)
397 ( CombApplicable repr
398 , CombAlternable repr
400 , CombSelectable repr
402 ) => CombSelectable (SomeComb repr) where
403 branch (Comb Empty) _ _ = empty
404 -- & trace "Branch Absorption Law"
405 branch b (Comb Empty) (Comb Empty) = b *> empty
406 -- & trace "Branch Weakening Law"
407 branch (Comb (Pure lr)) l r =
409 Left value -> l <*> pure (Pair v c)
411 v = Prod.SomeData $ Prod.Var $ Identity value
412 c = Prod.SomeData $ Prod.Var
413 [|| case $$(runCode lr) of Left x -> x ||]
414 Right value -> r <*> pure (Pair v c)
416 v = Prod.SomeData $ Prod.Var $ Identity value
417 c = Prod.SomeData $ Prod.Var
418 [|| case $$(runCode lr) of Right x -> x ||]
419 -- & trace "Branch Pure Either Law"
420 branch b (Comb (Pure l)) (Comb (Pure r)) =
422 -- & trace "Branch Generalised Identity Law"
424 v = Prod.SomeData $ Prod.Var $ Identity $ either (runValue l) (runValue r)
425 c = Prod.SomeData $ Prod.Var [|| either $$(runCode l) $$(runCode r) ||]
426 branch (Comb (x :*>: y)) p q = x *> branch y p q
427 -- & trace "Interchange Law"
428 branch b l (Comb Empty) =
429 branch (pure (Pair v c) <*> b) empty l
430 -- & trace "Negated Branch Law"
432 v = Prod.SomeData $ Prod.Var $ Identity $ either Right Left
433 c = Prod.SomeData $ Prod.Var $ [||either Right Left||]
434 branch (Comb (Branch b (Comb Empty) (Comb (Pure lr)))) (Comb Empty) br =
435 branch (pure (Pair v c) <*> b) empty br
436 -- & trace "Branch Fusion Law"
438 v = Prod.SomeData $ Prod.Var $ Identity $ \case
441 case runValue lr r of
444 c = Prod.SomeData $ Prod.Var
445 [|| \case Left{} -> Left ()
446 Right r -> case $$(runCode lr) r of
448 Right rr -> Right rr ||]
449 branch b l r = SomeComb (Branch b l r)