packages feed

descript-lang-0.2.0.0: src/Descript/BasicInj/Process/Reduce/Match.hs

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE ApplicativeDo #-}

module Descript.BasicInj.Process.Reduce.Match
  ( Match (..)
  , MatchT (..)
  , emptyMatch
  , matchAgain
  , matchAgainF
  , mapLeftover
  , bimapMatch
  ) where

import Data.Semigroup

-- | The result of successfully matching an input value (or other
-- expression - whatever @a@ is) on a regular value.
data Match a
  = Match
  { matched :: a -- ^ Part of the value the input matched/consumed.
  , leftover :: a -- ^ Part of the value the input didn't match/consume.
  } deriving (Functor)

-- | Turns 'Match' (and 'MatchA') into a transformer.
newtype MatchT u a = MatchT{ runMatchT :: u (Match a) } deriving (Functor)

instance Applicative Match where
  pure x
    = Match
    { matched = x
    , leftover = x
    }
  f <*> x
    = Match
    { matched = matched f $ matched x
    , leftover = leftover f $ leftover x
    }

instance (Applicative u) => Applicative (MatchT u) where
  pure = MatchT . pure . pure
  MatchT f <*> MatchT x = MatchT $ (<*>) <$> f <*> x

-- | A 'Match' with no 'matched' and the given value as 'leftover'.
-- A match with an empty input.
emptyMatch :: (Monoid a) => a -> Match a
emptyMatch x
  = Match
  { matched = mempty
  , leftover = x
  }

-- | Applies the matching function on the old 'leftover', returns a
-- 'Match' with the old and new 'matched' combined and the new 'leftover'.
matchAgain :: (Semigroup a) => (a -> Match a) -> Match a -> Match a
matchAgain f old
  = Match
  { matched = matched old <> matched new
  , leftover = leftover new
  }
  where new = f $ leftover old

-- | 'matchAgain' with a side effect.
matchAgainF :: (Semigroup a, Functor w) => (a -> w (Match a)) -> Match a -> w (Match a)
matchAgainF f old = do
  new <- f $ leftover old
  pure Match
    { matched = matched old <> matched new
    , leftover = leftover new
    }

-- | Transforms the 'leftover'.
mapLeftover :: (a -> a) -> Match a -> Match a
mapLeftover f x
  = Match
  { matched = matched x
  , leftover = f $ leftover x
  }

-- | Transforms 'matched' with the first function and 'leftover' with
-- the second.
bimapMatch :: (a -> b) -> (a -> b) -> Match a -> Match b
bimapMatch fMatched fLeftover x
  = Match
  { matched = fMatched $ matched x
  , leftover = fLeftover $ leftover x
  }