{-# LANGUAGE CPP #-} {- Copyright (C) 2010 Dr. Alistair Ward This program 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. This program 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 this program. If not, see . -} {- | [@AUTHOR@] Dr. Alistair Ward [@DESCRIPTION@] * Define the requirements & capabilities of a data-type which is designed to consume data. * This module also facilitates investigation of group-behaviour, by defining operations on either concatenations or alternations of 'Consumer's. * This module is specifically for use in a /regex/-engine, in which the meta-data from which the /regex/ is composed, are considered to /consume/ input data, whilst concatenation & alternation of such elements, behave as a different types of groups of consumer. -} module RegExDot.Consumer( -- * Type-classes Consumer(..), -- * Types -- ** Type-synonyms -- StarHeight, -- * Functions accumulateConsumptionProfiles, -- accumulateConsumptionProfilesFrom, aggregateConsumptionProfilesFromAlternatives, aggregateConsumptionProfilesFromConcatenation, -- ** Query getConsumptionBounds, getFewest, getHasSpecificRequirement ) where import RegExDot.ConsumptionProfile((<>), (|+|)) import qualified Data.List import qualified RegExDot.ConsumptionBounds as ConsumptionBounds import qualified RegExDot.ConsumptionProfile as ConsumptionProfile #if MIN_VERSION_base(4,11,0) import Prelude hiding ((<>)) -- N.B.: avoid ambiguity with '(Data.Semigroup.<>)'. #endif {- | * A measure of the complexity of a /regex/, which has some baring on either the time-complexity or the space-complexity of the solution. * -} type StarHeight = Int {- | * The interface, to which types, which have a data-requirement, may conform. * Regrettably, methods requiring reference to the type of the consumable, would require multi-parameter type-classes. * 'consumptionProfile' defines the quantity-range of data which may be consumed, & any specific data-requirements. * 'starHeight' measures the complexity of the 'Consumer'. -} class Consumer c where consumptionProfile :: c -> ConsumptionProfile.ConsumptionProfile starHeight :: c -> StarHeight instance Consumer c => Consumer [c] where consumptionProfile = undefined starHeight = Data.List.foldl' (\l -> max l . starHeight) 0 -- | Convenience-function, to query the 'ConsumptionBounds.ConsumptionBounds' of a 'Consumer'. getConsumptionBounds :: Consumer c => c -> ConsumptionBounds.ConsumptionBounds getConsumptionBounds = ConsumptionProfile.consumptionBounds . consumptionProfile -- | Determine the minimum acceptable quantity of data. getFewest :: Consumer c => c -> ConsumptionBounds.DataLength getFewest = fst . getConsumptionBounds -- | Determine whether the specified 'Consumer', has a specific requirement. getHasSpecificRequirement :: Consumer c => c -> Bool getHasSpecificRequirement = ConsumptionProfile.hasSpecificRequirement . consumptionProfile -- | The aggregate of the specified alternation of 'ConsumptionProfile.ConsumptionProfile's. aggregateConsumptionProfilesFromAlternatives :: Consumer c => [c] -> ConsumptionProfile.ConsumptionProfile aggregateConsumptionProfilesFromAlternatives [] = error "RegExDot.Consumer.aggregateConsumptionProfilesFromAlternatives:\tnull list" aggregateConsumptionProfilesFromAlternatives l = Data.List.foldl1' (<>) $ consumptionProfile `map` l -- | Get the 'ConsumptionProfile.ConsumptionProfile's for the specified list of 'Consumer's, then find the net effect of concatenating them. aggregateConsumptionProfilesFromConcatenation :: Consumer c => [c] -> ConsumptionProfile.ConsumptionProfile aggregateConsumptionProfilesFromConcatenation = Data.List.foldl' (\acc -> (acc |+|) . consumptionProfile) ConsumptionProfile.zero {-initial value-} -- | Get the 'ConsumptionProfile.ConsumptionProfile' for the specified list of 'Consumer's, then accumulate them, from the specified initial value. accumulateConsumptionProfilesFrom :: Consumer c => ConsumptionProfile.ConsumptionProfile -> [c] -> ConsumptionProfile.AccumulatedConsumptionProfiles accumulateConsumptionProfilesFrom = scanr ((|+|) . consumptionProfile) -- | Get the 'ConsumptionProfile.ConsumptionProfile' for the specified list of 'Consumer's, then accumulate them. accumulateConsumptionProfiles :: Consumer c => [c] -> ConsumptionProfile.AccumulatedConsumptionProfiles accumulateConsumptionProfiles = {-init .-} accumulateConsumptionProfilesFrom ConsumptionProfile.zero -- It's useful to leave the initial value at the end of the list.