packages feed

parsley-core-2.3.0.0: src/ghc/Parsley/Internal/Frontend/Analysis/Inliner.hs

{-# LANGUAGE ImplicitParams #-}
{-|
Module      : Parsley.Internal.Frontend.Analysis.Inliner
Description : Decides whether to inline a let-bound parser.
License     : BSD-3-Clause
Maintainer  : Jamie Willis
Stability   : experimental

Exposes a transformation which can be used to inline let-bindings into their use-sites.

@since 1.7.0.0
-}
module Parsley.Internal.Frontend.Analysis.Inliner (inliner) where

import Data.Ratio                          ((%))
import Parsley.Internal.Common.Indexed     (Fix(..), cata)
import Parsley.Internal.Core.CombinatorAST (Combinator(..))
import Parsley.Internal.Core.Identifiers   (MVar)

import qualified Parsley.Internal.Opt   as Opt

{-|
Annotate a tree with its cut-points. We assume a cut for let-bound parsers.

@since 1.7.0.0
-}
inliner :: (?flags :: Opt.Flags) => Maybe Int -> MVar a -> Fix Combinator a -> Fix Combinator a
inliner occs _ body
  | Just n <- occs
  , Just thresh <- Opt.primaryInlineThreshold ?flags
  , shouldInline n thresh body = body
inliner _ μ _ = In (Let μ)

shouldInline :: Int -> Rational -> Fix Combinator a -> Bool
shouldInline occs inlineThreshold = (<= inlineThreshold) . (* toRational occs) . subtract callCost . getWeight . cata (InlineWeight . alg)

newtype InlineWeight a = InlineWeight { getWeight :: Rational }

callCost :: Rational
callCost = 1 % 3

handlerCost :: Rational
handlerCost = 1 % 4

registerCost :: Rational
registerCost = 1 % 3

-- Ideally these should mirror those in the backend inliner, how can we unify them?
alg :: Combinator InlineWeight a -> Rational
alg (Pure _)             = 0
alg (Satisfy _)          = 1
alg Empty                = 0
alg Let{}                = callCost
alg (Try p)              = getWeight p
alg (l :<|>: r)          = handlerCost + 1 % 5 + getWeight l + getWeight r
alg (l :<*>: r)          = 1 % 5 + getWeight l + getWeight r
alg (l :<*: r)           = getWeight l + getWeight r
alg (l :*>: r)           = getWeight l + getWeight r
alg (LookAhead c)        = getWeight c
alg (NotFollowedBy p)    = handlerCost + getWeight p
alg (Debug _ c)          = getWeight c
alg (Loop body exit)     = handlerCost + callCost + 2 % 3 + getWeight body + getWeight exit
alg (Branch b p q)       = 1 % 3 + 2 % 5 + getWeight b + getWeight p + getWeight q
alg (Match p _ qs def)   = fromIntegral (length qs + 1) % 3 + sum (map getWeight qs) + getWeight def + getWeight p
alg (MakeRegister _ l r) = registerCost + getWeight l + getWeight r
alg (GetRegister _)      = registerCost
alg (PutRegister _ c)    = registerCost + getWeight c
alg (Position _)         = 1 % 5
alg (MetaCombinator _ c) = getWeight c