parsley-core-1.0.0.0: src/ghc/Parsley/Internal/Frontend/Optimiser.hs
{-# LANGUAGE LambdaCase,
PatternSynonyms,
ViewPatterns #-}
module Parsley.Internal.Frontend.Optimiser (optimise) where
import Prelude hiding ((<$>))
import Parsley.Internal.Common (Fix(In), Quapplicative(..))
import Parsley.Internal.Core.CombinatorAST (Combinator(..))
import Parsley.Internal.Core.Defunc (Defunc(..), pattern FLIP_H, pattern COMPOSE_H, pattern FLIP_CONST, pattern UNIT)
pattern (:<$>:) :: Defunc (a -> b) -> Fix Combinator a -> Combinator (Fix Combinator) b
pattern f :<$>: p = In (Pure f) :<*>: p
pattern (:$>:) :: Fix Combinator a -> Defunc b -> Combinator (Fix Combinator) b
pattern p :$>: x = p :*>: In (Pure x)
pattern (:<$:) :: Defunc a -> Fix Combinator b -> Combinator (Fix Combinator) a
pattern x :<$: p = In (Pure x) :<*: p
optimise :: Combinator (Fix Combinator) a -> Fix Combinator a
-- DESTRUCTIVE OPTIMISATION
-- Right Absorption Law: empty <*> u = empty
optimise (In Empty :<*>: _) = In Empty
-- Failure Weakening Law: u <*> empty = u *> empty
optimise (u :<*>: In Empty) = optimise (u :*>: In Empty)
-- Right Absorption Law: empty *> u = empty
optimise (In Empty :*>: _) = In Empty
-- Right Absorption Law: empty <* u = empty
optimise (In Empty :<*: _) = In Empty
-- Failure Weakening Law: u <* empty = u *> empty
optimise (u :<*: In Empty) = optimise (u :*>: In Empty)
-- Branch Absorption Law: branch empty p q = empty
optimise (Branch (In Empty) _ _) = In Empty
-- Branch Weakening Law: branch b empty empty = b *> empty
optimise (Branch b (In Empty) (In Empty)) = optimise (b :*>: In Empty)
-- Match Absorption Law: match _ empty _ def = def
optimise (Match (In Empty) _ _ def) = def
-- Match Weakening Law: match _ p (const empty) empty = p *> empty
optimise (Match p _ qs (In Empty))
| all (\case {In Empty -> True; _ -> False}) qs = optimise (p :*>: In Empty)
-- APPLICATIVE OPTIMISATION
-- Identity Law: id <$> u = u
optimise (ID :<$>: u) = u
-- Flip const optimisation: flip const <$> u = u *> pure id
optimise (FLIP_CONST :<$>: u) = optimise (u :*>: In (Pure ID))
-- Homomorphism Law: pure f <*> pure x = pure (f x)
optimise (f :<$>: In (Pure x)) = In (Pure (APP_H f x))
-- NOTE: This is basically a shortcut, it can be caught by the Composition Law and Homomorphism law
-- Functor Composition Law: f <$> (g <$> p) = (f . g) <$> p
optimise (f :<$>: In (g :<$>: p)) = optimise (COMPOSE_H f g :<$>: p)
-- Composition Law: u <*> (v <*> w) = (.) <$> u <*> v <*> w
optimise (u :<*>: In (v :<*>: w)) = optimise (optimise (optimise (COMPOSE :<$>: u) :<*>: v) :<*>: w)
-- Definition of *>
optimise (In (FLIP_CONST :<$>: p) :<*>: q) = In (p :*>: q)
-- Definition of <*
optimise (In (CONST :<$>: p) :<*>: q) = In (p :<*: q)
-- Reassociation Law 1: (u *> v) <*> w = u *> (v <*> w)
optimise (In (u :*>: v) :<*>: w) = optimise (u :*>: (optimise (v :<*>: w)))
-- Interchange Law: u <*> pure x = pure ($ x) <*> u
optimise (u :<*>: In (Pure x)) = optimise (APP_H (FLIP_H ID) x :<$>: u)
-- Right Absorption Law: (f <$> p) *> q = p *> q
optimise (In (_ :<$>: p) :*>: q) = In (p :*>: q)
-- Left Absorption Law: p <* (f <$> q) = p <* q
optimise (p :<*: (In (_ :<$>: q))) = In (p :<*: q)
-- Reassociation Law 2: u <*> (v <* w) = (u <*> v) <* w
optimise (u :<*>: In (v :<*: w)) = optimise (optimise (u :<*>: v) :<*: w)
-- Reassociation Law 3: u <*> (v $> x) = (u <*> pure x) <* v
optimise (u :<*>: In (v :$>: x)) = optimise (optimise (u :<*>: In (Pure x)) :<*: v)
-- ALTERNATIVE OPTIMISATION
-- Left Catch Law: pure x <|> u = pure x
optimise (p@(In (Pure _)) :<|>: _) = p
-- Left Neutral Law: empty <|> u = u
optimise (In Empty :<|>: u) = u
-- Right Neutral Law: u <|> empty = u
optimise (u :<|>: In Empty) = u
-- Associativity Law: (u <|> v) <|> w = u <|> (v <|> w)
optimise (In (u :<|>: v) :<|>: w) = In (u :<|>: optimise (v :<|>: w))
-- SEQUENCING OPTIMISATION
-- Identity law: pure x *> u = u
optimise (In (Pure _) :*>: u) = u
-- Identity law: (u $> x) *> v = u *> v
optimise (In (u :$>: _) :*>: v) = In (u :*>: v)
-- Associativity Law: u *> (v *> w) = (u *> v) *> w
optimise (u :*>: In (v :*>: w)) = optimise (optimise (u :*>: v) :*>: w)
-- Identity law: u <* pure x = u
optimise (u :<*: In (Pure _)) = u
-- Identity law: u <* (v $> x) = u <* v
optimise (u :<*: In (v :$>: _)) = optimise (u :<*: v)
-- Commutativity Law: x <$ u = u $> x
optimise (x :<$: u) = optimise (u :$>: x)
-- Associativity Law (u <* v) <* w = u <* (v <* w)
optimise (In (u :<*: v) :<*: w) = optimise (u :<*: optimise (v :<*: w))
-- Pure lookahead: lookAhead (pure x) = pure x
optimise (LookAhead p@(In (Pure _))) = p
-- Dead lookahead: lookAhead empty = empty
optimise (LookAhead p@(In Empty)) = p
-- Pure negative-lookahead: notFollowedBy (pure x) = empty
optimise (NotFollowedBy (In (Pure _))) = In Empty
-- Dead negative-lookahead: notFollowedBy empty = unit
optimise (NotFollowedBy (In Empty)) = In (Pure UNIT)
-- Double Negation Law: notFollowedBy . notFollowedBy = lookAhead . try . void
optimise (NotFollowedBy (In (NotFollowedBy p))) = optimise (LookAhead (In (In (Try p) :*>: In (Pure UNIT))))
-- Zero Consumption Law: notFollowedBy (try p) = notFollowedBy p
optimise (NotFollowedBy (In (Try p))) = optimise (NotFollowedBy p)
-- Idempotence Law: lookAhead . lookAhead = lookAhead
optimise (LookAhead (In (LookAhead p))) = In (LookAhead p)
-- Right Identity Law: notFollowedBy . lookAhead = notFollowedBy
optimise (NotFollowedBy (In (LookAhead p))) = optimise (NotFollowedBy p)
-- Left Identity Law: lookAhead . notFollowedBy = notFollowedBy
optimise (LookAhead (In (NotFollowedBy p))) = In (NotFollowedBy p)
-- Transparency Law: notFollowedBy (try p <|> q) = notFollowedBy p *> notFollowedBy q
optimise (NotFollowedBy (In (In (Try p) :<|>: q))) = optimise (optimise (NotFollowedBy p) :*>: optimise (NotFollowedBy q))
-- Distributivity Law: lookAhead p <|> lookAhead q = lookAhead (try p <|> q)
optimise (In (LookAhead p) :<|>: In (LookAhead q)) = optimise (LookAhead (optimise (In (Try p) :<|>: q)))
-- Interchange Law: lookAhead (p $> x) = lookAhead p $> x
optimise (LookAhead (In (p :$>: x))) = optimise (optimise (LookAhead p) :$>: x)
-- Interchange law: lookAhead (f <$> p) = f <$> lookAhead p
optimise (LookAhead (In (f :<$>: p))) = optimise (f :<$>: optimise (LookAhead p))
-- Absorption Law: p <*> notFollowedBy q = (p <*> unit) <* notFollowedBy q
optimise (p :<*>: In (NotFollowedBy q)) = optimise (optimise (p :<*>: In (Pure UNIT)) :<*: In (NotFollowedBy q))
-- Idempotence Law: notFollowedBy (p $> x) = notFollowedBy p
optimise (NotFollowedBy (In (p :$>: _))) = optimise (NotFollowedBy p)
-- Idempotence Law: notFollowedBy (f <$> p) = notFollowedBy p
optimise (NotFollowedBy (In (_ :<$>: p))) = optimise (NotFollowedBy p)
-- Interchange Law: try (p $> x) = try p $> x
optimise (Try (In (p :$>: x))) = optimise (optimise (Try p) :$>: x)
-- Interchange law: try (f <$> p) = f <$> try p
optimise (Try (In (f :<$>: p))) = optimise (f :<$>: optimise (Try p))
-- pure Left law: branch (pure (Left x)) p q = p <*> pure x
optimise (Branch (In (Pure (l@(_val -> Left x)))) p _) = optimise (p :<*>: In (Pure (makeQ x qx))) where qx = [||case $$(_code l) of Left x -> x||]
-- pure Right law: branch (pure (Right x)) p q = q <*> pure x
optimise (Branch (In (Pure (r@(_val -> Right x)))) _ q) = optimise (q :<*>: In (Pure (makeQ x qx))) where qx = [||case $$(_code r) of Right x -> x||]
-- Generalised Identity law: branch b (pure f) (pure g) = either f g <$> b
optimise (Branch b (In (Pure f)) (In (Pure g))) = optimise (makeQ (either (_val f) (_val g)) [||either $$(_code f) $$(_code g)||] :<$>: b)
-- Interchange law: branch (x *> y) p q = x *> branch y p q
optimise (Branch (In (x :*>: y)) p q) = optimise (x :*>: optimise (Branch y p q))
-- Negated Branch law: branch b p empty = branch (swapEither <$> b) empty p
optimise (Branch b p (In Empty)) = In (Branch (In (In (Pure (makeQ (either Right Left) [||either Right Left||])) :<*>: b)) (In Empty) p)
-- Branch Fusion law: branch (branch b empty (pure f)) empty k = branch (g <$> b) empty k where g is a monad transforming (>>= f)
optimise (Branch (In (Branch b (In Empty) (In (Pure f)))) (In Empty) k) = optimise (Branch (optimise (In (Pure (makeQ g qg)) :<*>: b)) (In Empty) k)
where
g (Left _) = Left ()
g (Right x) = case _val f x of
Left _ -> Left ()
Right x -> Right x
qg = [||\case Left _ -> Left ()
Right x -> case $$(_code f) x of
Left _ -> Left ()
Right y -> Right y||]
-- Distributivity Law: f <$> branch b p q = branch b ((f .) <$> p) ((f .) <$> q)
optimise (f :<$>: In (Branch b p q)) = optimise (Branch b (optimise (APP_H COMPOSE f :<$>: p)) (optimise (APP_H COMPOSE f :<$>: q)))
-- pure Match law: match vs (pure x) f def = if elem x vs then f x else def
optimise (Match (In (Pure x)) fs qs def) = foldr (\(f, q) k -> if _val f (_val x) then q else k) def (zip fs qs)
-- TODO I'm not actually sure this one is a good optimisation? might have some size constraint on it
-- Generalised Identity Match law: match vs p (pure . f) def = f <$> (p >?> flip elem vs) <|> def
{-optimise (Match p fs qs def)
| all (\case {In (Pure _) -> True; _ -> False}) qs = optimise (optimise (makeQ apply qapply :<$>: (p >?> (makeQ validate qvalidate))) :<|>: def)
where apply x = foldr (\(f, In (Pure y)) k -> if _val f x then _val y else k) (error "whoopsie") (zip fs qs)
qapply = [||\x -> $$(foldr (\(f, In (Pure y)) k -> [||if $$(_code f) x then $$(_code y) else $$k||]) ([||error "whoopsie"||]) (zip fs qs))||]
validate x = foldr (\f b -> _val f x || b) False fs
qvalidate = [||\x -> $$(foldr (\f k -> [||$$(_code f) x || $$k||]) [||False||] fs)||]-}
-- Distributivity Law: f <$> match vs p g def = match vs p ((f <$>) . g) (f <$> def)
optimise (f :<$>: (In (Match p fs qs def))) = In (Match p fs (map (optimise . (f :<$>:)) qs) (optimise (f :<$>: def)))
-- Trivial let-bindings - NOTE: These will get moved when Let nodes no longer have the "source" in them
optimise (Let False _ p@(In (Pure _))) = p
optimise (Let False _ p@(In Empty)) = p
optimise (Let False _ p@(In (Satisfy _))) = p
optimise (Let False _ p@(In (In (Satisfy _) :$>: _))) = p
optimise (Let False _ p@(In (GetRegister _))) = p
optimise (Let False _ p@(In (In (Pure _) :<*>: In (GetRegister _)))) = p
optimise p = In p
-- try (lookAhead p *> p *> lookAhead q) = lookAhead (p *> q) <* try p
{-(>?>) :: Fix Combinator a -> Defunc (a -> Bool) -> Fix Combinator a
p >?> f = In (Branch (In (makeQ g qg :<$>: p)) (In Empty) (In (Pure ID)))
where
g x = if _val f x then Right x else Left ()
qg = [||\x -> if $$(_code f) x then Right x else Left ()||]-}