{-# 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 <http://www.gnu.org/licenses/>.
-}
{- |
 [@AUTHOR@]	Dr. Alistair Ward

 [@DESCRIPTION@]

	Permits /Perl-style shortcut/s to be canned & assigned a single-'Char' mnemonic for subsequent reference;
	the implementation of 'Read' looks for a back-slashed 'Char', for which it expects there to be a corresponding canned 'ShowablePredicate.ShowablePredicate'.

 [@CAVEATS@]

	Since the underlying polymorphic data-type isn't required to implement neither 'Enum' nor 'Ord', the implementation of 'Read' can't cope with range-specifications.
	Lacking this, Bracket-expression members must be enumerated exhaustively.
-}

module RegExDot.BracketExpressionMember(
-- * Type-classes
	ShortcutExpander(..),
-- * Types
-- ** Data-types
	Member(..),
-- * Functions
-- ** Operators
	(=~)
) where

import qualified	Control.Arrow
import qualified	RegExDot.ShowablePredicate	as ShowablePredicate

#ifdef HAVE_DEEPSEQ
import			Control.DeepSeq(NFData, rnf)
#else
import			Control.Parallel.Strategies(NFData, rnf)
#endif

infix 4 =~	--Same as (==).

{- |
	* The interface via which /Perl-style shortcut/s are expanded (when they occur within a /bracket-expression/), in a manner appropriate to the chosen type-parameter.

	* The expansion of /Perl-style shortcut/s, is more restricted inside than outside, a /bracket-expression/,
	& consequently are merely represented here by a 'ShowablePredicate.ShowablePredicate', rather than providing a more general form suitable also for those /Perl-style shortcuts/ found outside /bracket-expression/s.

	* This interface is implemented elsewhere, where the specific type-parameter & consequently the appropriate set of /Perl-style shortcut/s, are defined.
-}
class ShortcutExpander a	where
	findPredicate	:: Char -> Maybe (ShowablePredicate.ShowablePredicate a)	-- ^ Attempt to find the appropriate 'ShowablePredicate.ShowablePredicate' to implement this /Perl-style shortcut/.

{- |
	* A /BracketExpression/ can contain either a literal, a range of literals given @(Enum a, Ord a)@, a /Perl-style shortcut/, or when 'Char' is the type-parameter, a /POSIX Character-class/.

	* This data-type reduces the representation of all these possibilities to either a predicate or a literal.
-}
data Member m	=
	Predicate (ShowablePredicate.ShowablePredicate m)	-- ^ This 'Member' is described using a /predicate/, which is run to determine whether the datum conforms & is a member of the "BracketExpression".
	| Literal m						-- ^ This 'Member' is defined literally, using an item of the polymorphic type.
	deriving Eq

instance Show m => Show (Member m)	where
	showsPrec _ (Predicate showablePredicate)	= shows showablePredicate
	showsPrec _ (Literal literal)			= shows literal

instance (ShortcutExpander m, Read m) => Read (Member m)	where
	readsPrec _ []				= []		--No parse.
	readsPrec _ (' ' : s)			= reads s	--Consume white-space.
	readsPrec _ ('\t' : s)			= reads s	--Consume white-space.
	readsPrec _ ('\\' : shortcut : s)	= case findPredicate shortcut of
		Just showablePredicate	-> [(Predicate showablePredicate, s)]
		_			-> error $ "readsPrec RegExDot.BracketExpressionMember.Member:\tfindPredicate failed for shortcut " ++ show shortcut
	readsPrec _ literal			= Control.Arrow.first Literal `map` reads literal

instance NFData m => NFData (Member m)	where
	rnf (Predicate showablePredicate)	= rnf showablePredicate
	rnf (Literal literal)			= rnf literal

-- | Match-operator.
(=~) :: Eq m
	=> m		-- ^ The input datum.
	-> Member m	-- ^ The member of the bracket-expression against which the input-datum is to be matched.
	-> Bool		-- ^ The result of the match-operation.
datum =~ Predicate showablePredicate	= ShowablePredicate.predicate showablePredicate datum
datum =~ Literal literal		= datum == literal