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 Type.Reflection (Typeable, typeRep, eqTypeRep, (:~~:)(..))
18 import qualified Data.Foldable as Foldable
19 import qualified Data.Functor as Functor
20 import qualified Data.List as List
22 import Symantic.Parser.Grammar.Combinators hiding (code)
23 import qualified Symantic.Parser.Grammar.Production as Prod
24 import Symantic.Parser.Grammar.Production
25 import Symantic.Univariant.Letable
26 import Symantic.Univariant.Trans
27 import qualified Symantic.Univariant.Lang as H
28 import qualified Symantic.Univariant.Data as H
31 import Data.Function (($), flip)
32 import Debug.Trace (trace)
38 -- * Type 'OptimizeGrammar'
39 type OptimizeGrammar = SomeComb
42 Trans (SomeComb repr) repr =>
43 SomeComb repr a -> repr a
44 optimizeGrammar = trans
46 -- * Data family 'Comb'
47 -- | 'Comb'inators of the 'Grammar'.
48 -- This is an extensible data-type.
50 (comb :: ReprComb -> Constraint)
51 :: ReprComb -> ReprComb
53 -- | Convenient utility to pattern-match a 'SomeComb'.
54 pattern Comb :: Typeable comb => Comb comb repr a -> SomeComb repr a
55 pattern Comb x <- (unSomeComb -> Just x)
58 -- | Some 'Comb'inator existentialized over the actual combinator symantic class.
59 -- Useful to handle a list of 'Comb'inators
60 -- without requiring impredicative quantification.
61 -- Must be used by pattern-matching
62 -- on the 'SomeComb' data-constructor,
63 -- to bring the constraints in scope.
65 -- The optimizations are directly applied within it,
66 -- to avoid introducing an extra newtype,
67 -- this also give a more understandable code.
68 data SomeComb repr a =
70 (Trans (Comb comb repr) repr, Typeable comb) =>
71 SomeComb (Comb comb repr a)
73 instance Trans (SomeComb repr) repr where
74 trans (SomeComb x) = trans x
76 -- | @(unSomeComb c :: 'Maybe' ('Comb' comb repr a))@
77 -- extract the data-constructor from the given 'SomeComb'
78 -- iif. it belongs to the @('Comb' comb repr a)@ data-instance.
82 SomeComb repr a -> Maybe (Comb comb repr a)
83 unSomeComb (SomeComb (c::Comb c repr a)) =
84 case typeRep @comb `eqTypeRep` typeRep @c of
89 data instance Comb CombAlternable repr a where
90 Alt :: Exception -> SomeComb repr a -> SomeComb repr a -> Comb CombAlternable repr a
91 Empty :: Comb CombAlternable repr a
92 Failure :: SomeFailure -> Comb CombAlternable repr a
93 Throw :: ExceptionLabel -> Comb CombAlternable repr a
94 Try :: SomeComb repr a -> Comb CombAlternable repr a
95 instance CombAlternable repr => Trans (Comb CombAlternable repr) repr where
97 Alt exn x y -> alt exn (trans x) (trans y)
99 Failure sf -> failure sf
100 Throw exn -> throw exn
101 Try x -> try (trans x)
103 ( CombAlternable repr
104 , CombApplicable repr
107 , CombSelectable repr
108 ) => CombAlternable (SomeComb repr) where
109 empty = SomeComb Empty
110 failure sf = SomeComb (Failure sf)
112 alt _exn p@(Comb Pure{}) _ = p
113 -- & trace "Left Catch Law"
114 alt _exn (Comb Empty) u = u
115 -- & trace "Left Neutral Law"
116 alt _exn u (Comb Empty) = u
117 -- & trace "Right Neutral Law"
118 alt exn (Comb (Alt exn' u v)) w | exn' == exn = u <|> (v <|> w)
119 -- See Lemma 1 (Associativity of choice for labeled PEGs)
120 -- in https://doi.org/10.1145/2851613.2851750
121 -- & trace "Associativity Law"
122 alt exn (Comb (Look p)) (Comb (Look q)) = look (alt exn (try p) q)
123 -- & trace "Distributivity Law"
124 alt exn x y = SomeComb (Alt exn x y)
126 throw exn = SomeComb (Throw exn)
128 try (Comb (p :$>: x)) = try p $> x
129 -- & trace "Try Interchange Law"
130 try (Comb (f :<$>: p)) = f <$> try p
131 -- & trace "Try Interchange Law"
132 try x = SomeComb (Try x)
135 data instance Comb CombApplicable repr a where
136 Pure :: Production a -> Comb CombApplicable repr a
137 (:<*>:) :: SomeComb repr (a -> b) -> SomeComb repr a -> Comb CombApplicable repr b
138 (:<*:) :: SomeComb repr a -> SomeComb repr b -> Comb CombApplicable repr a
139 (:*>:) :: SomeComb repr a -> SomeComb repr b -> Comb CombApplicable repr b
140 infixl 4 :<*>:, :<*:, :*>:
141 pattern (:<$>:) :: Production (a -> b) -> SomeComb repr a -> Comb CombApplicable repr b
142 pattern t :<$>: x <- Comb (Pure t) :<*>: x
143 pattern (:$>:) :: SomeComb repr a -> Production b -> Comb CombApplicable repr b
144 pattern x :$>: t <- x :*>: Comb (Pure t)
145 instance CombApplicable repr => Trans (Comb CombApplicable repr) repr where
147 Pure x -> pure (optimizeProduction x)
148 f :<*>: x -> trans f <*> trans x
149 x :<*: y -> trans x <* trans y
150 x :*>: y -> trans x *> trans y
152 ( CombApplicable repr
153 , CombAlternable repr
156 , CombSelectable repr
157 ) => CombApplicable (SomeComb repr) where
158 pure = SomeComb . Pure
159 f <$> Comb (Branch b l r) =
163 -- & trace "Branch Distributivity Law"
164 f <$> Comb (Conditional a ps bs d) =
166 ((f <$>) Functor.<$> bs)
168 -- & trace "Conditional Distributivity Law"
169 -- Being careful here to use (<*>),
170 -- instead of SomeComb (f <$> unOptComb x),
171 -- in order to apply the optimizations of (<*>).
172 f <$> x = pure f <*> x
175 -- & trace "Commutativity Law"
177 Comb Empty <*> _ = empty
178 -- & trace "App Right Absorption Law"
179 u <*> Comb Empty = u *> empty
180 -- & trace "App Failure Weakening Law"
181 Comb (Pure f) <*> Comb (Pure x) = pure (f H..@ x)
182 -- & trace "Homomorphism Law"
184 Comb (Pure f) <*> Comb (g :<$>: p) =
185 -- This is basically a shortcut,
186 -- it can be caught by one Composition Law
187 -- and two Homomorphism Law.
188 (H..) H..@ f H..@ g <$> p
189 -- & trace "Functor Composition Law"
191 u <*> Comb (Pure x) = H.flip H..@ (H.$) H..@ x <$> u
192 -- & trace "Interchange Law"
193 u <*> Comb (v :<*>: w) = (((H..) <$> u) <*> v) <*> w
194 -- & trace "Composition Law"
195 Comb (u :*>: v) <*> w = u *> (v <*> w)
196 -- & trace "Reassociation Law 1"
197 u <*> Comb (v :<*: w) = (u <*> v) <* w
198 -- & trace "Reassociation Law 2"
199 u <*> Comb (v :$>: x) = (u <*> pure x) <* v
200 -- & trace "Reassociation Law 3"
201 p <*> Comb (NegLook q) =
202 (p <*> pure H.unit) <* negLook q
203 -- & trace "Absorption Law"
204 x <*> y = SomeComb (x :<*>: y)
206 Comb Empty *> _ = empty
207 -- & trace "App Right Absorption Law"
208 Comb (_ :<$>: p) *> q = p *> q
209 -- & trace "Right Absorption Law"
211 -- & trace "Identity Law"
212 Comb (u :$>: _) *> v = u *> v
213 -- & trace "Identity Law"
214 u *> Comb (v :*>: w) = (u *> v) *> w
215 -- & trace "Associativity Law"
216 x *> y = SomeComb (x :*>: y)
218 Comb Empty <* _ = empty
219 -- & trace "App Right Absorption Law"
220 u <* Comb Empty = u *> empty
221 -- & trace "App Failure Weakening Law"
222 p <* Comb (_ :<$>: q) = p <* q
223 -- & trace "Left Absorption Law"
225 -- & trace "Identity Law"
226 u <* Comb (v :$>: _) = u <* v
227 -- & trace "Identity Law"
228 Comb (u :<*: v) <* w = u <* (v <* w)
229 -- & trace "Associativity Law"
230 x <* y = SomeComb (x :<*: y)
233 data instance Comb CombFoldable repr a where
234 ChainPreC :: SomeComb repr (a -> a) -> SomeComb repr a -> Comb CombFoldable repr a
235 ChainPostC :: SomeComb repr a -> SomeComb repr (a -> a) -> Comb CombFoldable repr a
236 instance CombFoldable repr => Trans (Comb CombFoldable repr) repr where
238 ChainPreC x y -> chainPre (trans x) (trans y)
239 ChainPostC x y -> chainPost (trans x) (trans y)
240 instance CombFoldable repr => CombFoldable (SomeComb repr) where
241 chainPre x = SomeComb . ChainPreC x
242 chainPost x = SomeComb . ChainPostC x
245 data instance Comb (Letable letName) repr a where
246 Shareable :: letName -> SomeComb repr a -> Comb (Letable letName) repr a
247 Ref :: Bool -> letName -> Comb (Letable letName) repr a
249 Letable letName repr =>
250 Trans (Comb (Letable letName) repr) repr where
252 Shareable n x -> shareable n (trans x)
253 Ref isRec n -> ref isRec n
255 (Letable letName repr, Typeable letName) =>
256 Letable letName (SomeComb repr) where
257 shareable n = SomeComb . Shareable n
258 ref isRec = SomeComb . Ref isRec
261 data instance Comb (Letsable letName) repr a where
262 Lets :: LetBindings letName (SomeComb repr) ->
263 SomeComb repr a -> Comb (Letsable letName) repr a
265 Letsable letName repr =>
266 Trans (Comb (Letsable letName) repr) repr where
268 Lets defs x -> lets ((\(SomeLet sub) -> SomeLet (trans sub)) Functor.<$> defs) (trans x)
270 (Letsable letName repr, Typeable letName) =>
271 Letsable letName (SomeComb repr) where
272 lets defs = SomeComb . Lets defs
275 data instance Comb CombLookable repr a where
276 Look :: SomeComb repr a -> Comb CombLookable repr a
277 NegLook :: SomeComb repr a -> Comb CombLookable repr ()
278 Eof :: Comb CombLookable repr ()
279 instance CombLookable repr => Trans (Comb CombLookable repr) repr where
281 Look x -> look (trans x)
282 NegLook x -> negLook (trans x)
285 ( CombAlternable repr
286 , CombApplicable repr
288 , CombSelectable repr
290 ) => CombLookable (SomeComb repr) where
291 look p@(Comb Pure{}) = p
292 -- & trace "Pure Look Law"
293 look p@(Comb Empty) = p
294 -- & trace "Dead Look Law"
295 look (Comb (Look x)) = look x
296 -- & trace "Idempotence Law"
297 look (Comb (NegLook x)) = negLook x
298 -- & trace "Left Identity Law"
299 look (Comb (p :$>: x)) = look p $> x
300 -- & trace "Interchange Law"
301 look (Comb (f :<$>: p)) = f <$> look p
302 -- & trace "Interchange Law"
303 look x = SomeComb (Look x)
305 negLook (Comb Pure{}) = empty
306 -- & trace "Pure Negative-Look"
307 negLook (Comb Empty) = pure H.unit
308 -- & trace "Dead Negative-Look"
309 negLook (Comb (NegLook x)) = look (try x *> pure H.unit)
310 -- & trace "Double Negation Law"
311 negLook (Comb (Try x)) = negLook x
312 -- & trace "Zero Consumption Law"
313 negLook (Comb (Look x)) = negLook x
314 -- & trace "Right Identity Law"
315 negLook (Comb (Alt _exn (Comb (Try p)) q)) = negLook p *> negLook q
316 -- FIXME: see if this really holds for all exceptions.
317 -- & trace "Transparency Law"
318 negLook (Comb (p :$>: _)) = negLook p
319 -- & trace "NegLook Idempotence Law"
320 negLook x = SomeComb (NegLook x)
325 data instance Comb CombMatchable repr a where
326 Conditional :: Eq a =>
328 [Production (a -> Bool)] ->
331 Comb CombMatchable repr b
332 instance CombMatchable repr => Trans (Comb CombMatchable repr) repr where
334 Conditional a ps bs b ->
335 conditional (trans a)
336 (optimizeProduction Functor.<$> ps)
337 (trans Functor.<$> bs) (trans b)
339 ( CombApplicable repr
340 , CombAlternable repr
342 , CombSelectable repr
344 ) => CombMatchable (SomeComb repr) where
345 conditional (Comb Empty) _ _ d = d
346 -- & trace "Conditional Absorption Law"
347 conditional p _ qs (Comb Empty)
348 | Foldable.all (\case { Comb Empty -> True; _ -> False }) qs = p *> empty
349 -- & trace "Conditional Weakening Law"
350 conditional a _ps bs (Comb Empty)
351 | Foldable.all (\case { Comb Empty -> True; _ -> False }) bs = a *> empty
352 -- & trace "Conditional Weakening Law"
353 conditional (Comb (Pure a)) ps bs d =
354 Foldable.foldr (\(p, b) next ->
355 if runValue (p H..@ a) then b else next
357 -- & trace "Conditional Pure Law"
358 conditional a ps bs d = SomeComb (Conditional a ps bs d)
361 data instance Comb (CombSatisfiable tok) repr a where
362 -- | To include the @('Set' 'SomeFailure')@ is a little kludge
363 -- it would be cleaner to be able to pattern-match
364 -- on @(alt exn (Comb 'Satisfy'{}) (Failure{}))@
365 -- when generating 'Program', but this is not easily possible then
366 -- because data types have already been converted back to class methods,
367 -- hence pattern-matching is no longer possible
368 -- (at least not without reintroducing data types).
370 CombSatisfiable tok repr =>
372 Production (tok -> Bool) ->
373 Comb (CombSatisfiable tok) repr tok
375 CombSatisfiable tok repr =>
376 Trans (Comb (CombSatisfiable tok) repr) repr where
378 SatisfyOrFail fs p -> satisfyOrFail fs (optimizeProduction p)
380 (CombSatisfiable tok repr, Typeable tok) =>
381 CombSatisfiable tok (SomeComb repr) where
382 satisfyOrFail fs = SomeComb . SatisfyOrFail fs
385 data instance Comb CombSelectable repr a where
387 SomeComb repr (Either a b) ->
388 SomeComb repr (a -> c) ->
389 SomeComb repr (b -> c) ->
390 Comb CombSelectable repr c
391 instance CombSelectable repr => Trans (Comb CombSelectable repr) repr where
393 Branch lr l r -> branch (trans lr) (trans l) (trans r)
395 ( CombApplicable repr
396 , CombAlternable repr
398 , CombSelectable repr
400 ) => CombSelectable (SomeComb repr) where
401 branch (Comb Empty) _ _ = empty
402 -- & trace "Branch Absorption Law"
403 branch b (Comb Empty) (Comb Empty) = b *> empty
404 -- & trace "Branch Weakening Law"
405 branch (Comb (Pure lr)) l r =
407 Left value -> l <*> pure Production{..}
409 prodValue = H.SomeData $ H.Var $ Identity value
410 prodCode = H.SomeData $ H.Var
411 [|| case $$(runCode lr) of Left x -> x ||]
412 Right value -> r <*> pure Production{..}
414 prodValue = H.SomeData $ H.Var $ Identity value
415 prodCode = H.SomeData $ H.Var
416 [|| case $$(runCode lr) of Right x -> x ||]
417 -- & trace "Branch Pure Either Law"
418 branch b (Comb (Pure l)) (Comb (Pure r)) =
420 -- & trace "Branch Generalised Identity Law"
422 prodValue = H.SomeData $ H.Var $ Identity $ either (runValue l) (runValue r)
423 prodCode = H.SomeData $ H.Var [|| either $$(runCode l) $$(runCode r) ||]
424 branch (Comb (x :*>: y)) p q = x *> branch y p q
425 -- & trace "Interchange Law"
426 branch b l (Comb Empty) =
427 branch (pure Production{..} <*> b) empty l
428 -- & trace "Negated Branch Law"
430 prodValue = H.SomeData $ H.Var $ Identity $ either Right Left
431 prodCode = H.SomeData $ H.Var $ [||either Right Left||]
432 branch (Comb (Branch b (Comb Empty) (Comb (Pure lr)))) (Comb Empty) br =
433 branch (pure Production{..} <*> b) empty br
434 -- & trace "Branch Fusion Law"
436 prodValue = H.SomeData $ H.Var $ Identity $ \case
439 case runValue lr r of
442 prodCode = H.SomeData $ H.Var
443 [|| \case Left{} -> Left ()
444 Right r -> case $$(runCode lr) r of
446 Right rr -> Right rr ||]
447 branch b l r = SomeComb (Branch b l r)