{- Copyright 2010 Mario Blazevic This file is part of the Streaming Component Combinators (SCC) project. The SCC project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. SCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with SCC. If not, see <http://www.gnu.org/licenses/>. -} -- | This module defines the Ticker cofunctor, useful for 'ticking off' a prefix of the input. -- module Control.Cofunctor.Ticker ( -- * The Ticker type Ticker(Ticker), -- * Using a Ticker cofmap, splitTicked, -- * Various Ticker constructors tickNone, tickOne, tickCount, tickPrefixOf, tickWhilePrefixOf, tickWhile, tickUntil, tickAll, andThen ) where -- | This is a cofunctor data type for selecting a prefix of an input stream. If the next input item is acceptable, the -- ticker function returns the ticker for the rest of the stream. If not, it returns 'Nothing'. newtype Ticker x = Ticker (x -> Maybe (Ticker x)) -- | 'Ticker' happens to be a cofunctor, but there is no standard class declaration to declare it an instance of. cofmap :: (x -> y) -> Ticker y -> Ticker x cofmap f (Ticker g) = Ticker (fmap (cofmap f) . g . f) -- | Extracts a list prefix accepted by the 'Ticker' argument. Returns the modified ticker, the prefix, and the -- remainder of the list. splitTicked :: Ticker x -> [x] -> (Ticker x, [x], [x]) splitTicked t [] = (t, [], []) splitTicked t@(Ticker f) l@(x:rest) = maybe (t, [], l) (\t' -> let (t'', xs1, xs2) = splitTicked t' rest in (t'', x:xs1, xs2)) (f x) -- | A ticker that accepts no input. tickNone :: Ticker x tickNone = Ticker (const Nothing) -- | A ticker that accepts a single input item. tickOne :: Ticker x tickOne = Ticker (const $ Just tickNone) -- | A ticker that accepts a given number of input items. tickCount :: Int -> Ticker x tickCount n | n > 0 = Ticker (const $ Just $ tickCount (pred n)) | otherwise = tickNone -- | A ticker that accepts the longest prefix of input that matches a prefix of the argument list. tickPrefixOf :: Eq x => [x] -> Ticker x tickPrefixOf list = tickWhilePrefixOf (map (==) list) -- | A ticker that accepts a prefix of input as long as each item satisfies the predicate at the same position in the -- argument list. The length of the predicate list thus determines the maximum number of acepted values. tickWhilePrefixOf :: [x -> Bool] -> Ticker x tickWhilePrefixOf (p : rest) = Ticker $ \x-> if p x then Just (tickWhilePrefixOf rest) else Nothing tickWhilePrefixOf [] = tickNone -- | A ticker that accepts all input as long as it matches the given predicate. tickWhile :: (x -> Bool) -> Ticker x tickWhile p = t where t = Ticker (\x-> if p x then Just t else Nothing) -- | A ticker that accepts all input items until one matches the given predicate. tickUntil :: (x -> Bool) -> Ticker x tickUntil p = t where t = Ticker (\x-> if p x then Nothing else Just t) -- | A ticker that accepts all input. tickAll :: Ticker x tickAll = Ticker (const $ Just tickAll) -- | Sequential ticker combinator: when the first argument ticker stops ticking, the second takes over. andThen :: Ticker x -> Ticker x -> Ticker x Ticker t1 `andThen` t@(Ticker t2) = Ticker (\x-> maybe (t2 x) (Just . (`andThen` t)) (t1 x))