deepseq-bounded-0.5.0: src/Control/DeepSeq/Bounded/Pattern.hs
-------------------------------------------------------------------------------
{- LANGUAGE CPP #-}
#define DO_TRACE 0
#define WARN_IGNORED_SUBPATTERNS 1
#define NEVER_IGNORE_SUBPATTERNS 0
-- Formerly DEBUG_WITH_DEEPSEQ_GENERICS.
-- Now also needed to force issuance of all compilePat warnings
-- (so not strictly a debugging flag anymore).
-- [Except it didn't work...]
--- #define NFDATA_INSTANCE_PATTERN 0 -- now a .cabal flag
#define DO_DERIVE_DATA_AND_TYPEABLE 0
#define DO_DERIVE_ONLY_TYPEABLE 1
-- Now specified via --flag=[-]USE_WWW_DEEPSEQ
--- #define USE_WW_DEEPSEQ 1
-------------------------------------------------------------------------------
-- Good idea: Let * be followed by an integer N.
-- This shall have the semantics that, when that node
-- is matched in the pattern, instead of rnf it is forcen N'd.
-- There may be fusion possible (which is worth trying here
-- for practise, even if this lib is not used much):
--
-- forcep p1 . forcep p2 = forcep (unionPat [p1,p2])
--
-- This holds if pattern doesn't contain #, or any (type-)constrained
-- subpatterns -- the latter might work out, if exclude # from them too,
-- but I'm not sure. With #, we lose even monotonicity, let alone
-- the above law.
--
-- For the above to hold, remember, the union must have exactly
-- the "forcing potential" of the LHS -- no more, no less.
-------------------------------------------------------------------------------
#if DO_DERIVE_DATA_AND_TYPEABLE
{-# LANGUAGE DeriveDataTypeable #-}
#endif
-- XXX Only needed for something in Blah.hs.
-- Check into it, and see if can't get rid of the need
-- for Typeable instances in here!
#if DO_DERIVE_ONLY_TYPEABLE
{-# LANGUAGE DeriveDataTypeable #-}
#endif
#if NFDATA_INSTANCE_PATTERN
-- For testing only (controlling trace interleaving):
{-# LANGUAGE DeriveGeneric #-}
#endif
{- LANGUAGE DeriveFunctor #-}
-------------------------------------------------------------------------------
-- |
-- Module : Control.DeepSeq.Bounded.Pattern
-- Copyright : (c) 2014, Andrew G. Seniuk
-- License : BSD-style (see the file LICENSE)
--
-- Maintainer : Andrew Seniuk <rasfar@gmail.com>
-- Stability : experimental
-- Portability : portable
--
-------------------------------------------------------------------------------
module Control.DeepSeq.Bounded.Pattern
(
-- * Pattern datatype
Pattern, PatNode(..)
-- , patternShapeOK -- useful for defining instances of NFDataP
-- * Pattern DSL
-- | __Grammar__
--
-- @
-- /pat/ /->/ /[/ __=__ /]/ __.__ /[/ __{__ /{/ /pat/ /}/ __}__ /]/
-- /|/ /(/ /[/ __=__ /]/ __*__ /[/ /decimalint/ /]/ /|/ __#__ /)/
-- /|/ __.:__ /ctorname/ /{/ /space/ /ctorname/ /}/ __{__ /[/ /{/ /pat/ /}/ /]/ __}__
-- /|/ /(/ __*__ /[/ /decimalint/ /]/ /|/ __#__ /)/ __:__ /typename/ /{/ /space/ /typename/ /}/ __{}__
-- /typename/ -> /string/
-- /ctorname/ -> /string/
-- /decimalint/ -> /digit string not beginning with zero/
-- /space/ -> /space character ASCII 0x32/
-- @
--
-- [I regret that Haddock cannot offer better markup for distinguishing
-- the metasyntax. The bold is not bold enough. The alternation symbol,
-- although \/|\/ in the document comment, does not show as slanted for me.
-- Had no luck using color, also Unicode support seems pretty sketchy.
-- Embedding an image is possible via data URL, but this has been known
-- to crash Haddock except for very small images.]
--
-- __Examples__
--
-- @".{...}"@ will match any ternary constructor.
--
-- @'rnfp' ".{...}" expr@ will force evaluation of @expr@ to a depth of two,
-- provided the head of @expr@ is a ternary constructor; otherwise it behaves
-- as @'rnfp' "#" expr@ (i.e. do nothing).
--
-- @'rnfp' ".{###}" expr@ will force it to only a depth of one. That is,
-- @'rnfp' ".{###}" expr = 'rnfp' "." expr@ when the head of @expr@
-- is a ternary constructor; otherwise it won't perform any evaluation.
--
-- @'rnfp' "*" expr = 'rnf' expr@.
--
-- @'rnfp' ".{***}" expr@ will 'rnf' (deep) any ternary constructor, but
-- will not touch any constructor of other arity.
--
-- @'rnfp' ".{..{*.}.}" expr@ will match any ternary constructor, then
-- match the second subexpression constructor if it is binary, and
-- if matching got this far, then the left sub-subexpression
-- will be forced ('rnf'), but not the right.
--
-- @'rnfp' ".{.*:T{}#}" expr@ will unwrap (shallow 'seq') the first
-- subexpression of @expr@, and the third subexpression won't be touched.
-- As for the second subexpression, if its type is @T@ it will be
-- completely evaluated ('rnf'), but otherwise it won't be touched.
--
-- @'rnfp' ".{=**}" expr@ will spark the /parallel/ complete evaluation of
-- the two components of any pair. (Whether the computations actually
-- run in parallel depends on resource availability, and the discretion
-- of the RTS, as usual).
--
-- __Details__
--
-- The present pattern parser ignores any subpatterns of all
-- pattern nodes except 'WR', 'TR' and 'PR', optionally emitting a warning.
-- Hence, only 'WR', 'TR' and 'PR' patterns are potentially recursive.
--
-- When specifying a list of subpatterns with 'WR' or 'PR',
-- in order for the match to succeed, the number of subpatterns must
-- be equal to the arity of the named constructor.
--
-- Type constraints must always be followed by __{__ (opening brace) as delimiter.
-- In the case of 'TR', if no recursion is desired, provide __{}__.
-- In order for the match to succeed, the number of subpatterns must either
-- be zero (__{}__), or be equal to the arity of the named constructor.
--
, compilePat
, showPat
-- * Why depend on whole containers package, when we only want a rose tree
, Rose(..)
-- * Preferred to have this in Seqable, but had cyclical dependency issues
, SeqNodeKind(..)
)
where
-------------------------------------------------------------------------------
#if DO_DERIVE_DATA_AND_TYPEABLE
import Data.Data ( Data )
import Data.Typeable ( Typeable )
#elif DO_DERIVE_ONLY_TYPEABLE
import Data.Typeable ( Typeable )
#endif
#if USE_WW_DEEPSEQ
import Control.DeepSeq ( NFData )
#endif
import Data.List ( intersperse )
import Data.Char ( isDigit )
import Data.Maybe ( isNothing, fromJust )
import Debug.Trace ( trace )
#if USE_WW_DEEPSEQ
-- The only uses of force in this module are for debugging purposes
-- (including trying to get messages to be displayed in a timely
-- manner, although that problem has not been completely solved).
import Control.DeepSeq ( force )
#if NFDATA_INSTANCE_PATTERN
-- for helping trace debugging
import qualified Control.DeepSeq.Generics as DSG
import qualified GHC.Generics as GHC ( Generic )
#endif
#endif
-------------------------------------------------------------------------------
#if DO_TRACE
mytrace = trace
#else
mytrace _ = id
#endif
-------------------------------------------------------------------------------
data Rose a = Node a [ Rose a ]
#if NFDATA_INSTANCE_PATTERN
#if DO_DERIVE_DATA_AND_TYPEABLE
deriving (Show, Eq, GHC.Generic, Data, Typeable)
-- deriving (Show, Eq, Functor, GHC.Generic, Data, Typeable)
#elif DO_DERIVE_ONLY_TYPEABLE
deriving (Show, Eq, GHC.Generic, Typeable)
#else
deriving (Show, Eq, GHC.Generic)
#endif
#else
#if DO_DERIVE_DATA_AND_TYPEABLE
deriving (Show, Eq, Data, Typeable)
#elif DO_DERIVE_ONLY_TYPEABLE
deriving (Show, Eq, Typeable)
#else
deriving (Show, Eq)
#endif
#endif
type Pattern = Rose PatNode
instance Functor Rose where
fmap f (Node x chs) = Node (f x) (map (fmap f) chs)
#if NFDATA_INSTANCE_PATTERN
instance NFData a => NFData (Rose a) where rnf = DSG.genericRnf
#endif
-------------------------------------------------------------------------------
-- | Note that only 'WR', 'TR' and 'PR' allow for explicit recursion.
-- The other 'PatNode's are in leaf position when they occur in a 'Pattern'.
data PatNode
=
WR -- ^ Continue pattern matching descendants.
| WS -- ^ Stop recursing (nothing more forced down this branch).
| WN Int -- ^ @'rnfn' n@ the branch under this node.
#if USE_WW_DEEPSEQ
| WW -- ^ Fully force ('rnf') the whole branch under this node.
#endif
| WI -- ^ Don't even unwrap the constructor of this node.
{--} -- XXX It's still unclear whether TI should allow subpatterns;
-- the alternative is for TI, when type doesn't match, to behave
-- as "." (no subpatterns); but since I say "otherwise behave as TR",
-- and TR says "continue pattern matching descendants", this seems to
-- say that subpatterns should be permitted. Certainly it's no problem
-- to permit subpatterns in this case, but WI should still ignore
-- subpatterns since it will always be # regardless of node type.
-- (Subpatterns ought to be "safely redundant" in this case, but whether
-- they are depends on implementation and needs to be tested if allow
-- WI subpatterns to survive past the parser/compiler!)
-- And this all applies to TW and TN too, right? Yes.
-- It seems clear that TI, TW and TN should all allow subpatterns.
-- And that WI, WW and WN should elide them and issue a warning.
-- But, none of my present woes seem to be connected with this...
-- Nonetheless, it's important to pin down the semantics.
#if 1
| TR [String] -- ^ Match any of the types in the list (and continue pattern matching descendants); behave as 'WI' for nodes of type not in the list. (Note this behaviour is the complement of 'TI' behaviour.)
--- | TS [String] -- ^ Same as 'TR' except no subpatterns present.
| TN Int [String] -- ^ @'rnfn' n@ the branch under this node, if the node type matches any of the types in the list.
#if USE_WW_DEEPSEQ
| TW [String] -- ^ Fully force ('rnf') the whole branch under this node, if the node type matches any of the types in the list; otherwise behave as 'WI'.
#endif
| TI [String] -- ^ Don't even unwrap the constructor of this node, if it's type is in the list; otherwise behave as 'WR'. (Note this behaviour is the complement of 'TR' behaviour.)
#else
| TR [TypeRep] -- ...
#endif
#if PARALLELISM_EXPERIMENT
| PR -- ^ Spark the pattern matching of this subtree.
| PN Int -- ^ Spark @'rnfn' n@ of this subtree.
#if USE_WW_DEEPSEQ
| PW -- ^ Spark the full forcing ('rnf') of this subtree.
#endif
#endif
#if NFDATA_INSTANCE_PATTERN
#if DO_DERIVE_DATA_AND_TYPEABLE
deriving ( Show, Eq, Typeable, Data, GHC.Generic )
#elif DO_DERIVE_ONLY_TYPEABLE
deriving ( Show, Eq, Typeable, GHC.Generic )
#else
deriving ( Show, Eq, GHC.Generic )
#endif
#else
#if DO_DERIVE_DATA_AND_TYPEABLE
deriving ( Show, Eq, Typeable ) -- Data apparently not needed
#elif DO_DERIVE_ONLY_TYPEABLE
deriving ( Show, Eq, Typeable )
#else
deriving ( Show, Eq )
#endif
#endif
#if NFDATA_INSTANCE_PATTERN
instance NFData PatNode where rnf = DSG.genericRnf
#endif
-------------------------------------------------------------------------------
#if 0
patternShapeOK :: Data a => Pattern -> a -> Bool
patternShapeOK pat x = S.shapeOf pat == S.shapeOf x
#endif
-------------------------------------------------------------------------------
-- XXX Doing this to ensure issuance of all warning messages
-- pertaining to the pattern to be compiled!
-- Which isn't quite working?!?.... [Never did resolve this.]
compilePat :: String -> Pattern
#if NFDATA_INSTANCE_PATTERN
compilePat s = force $ compilePat_ s
--compilePat s = let pat = force $! compilePat_ s in trace (show pat) $! pat
--compilePat s = let pat = force $ compilePat_ s in trace (show pat) $! pat
--compilePat s = let !pat = force $ compilePat_ s in trace (show pat) $ pat
--compilePat s = let pat = force $ compilePat_ s in trace (show pat) $ pat
#else
compilePat = compilePat_
#endif
compilePat_ :: String -> Pattern
--compilePat_ :: String -> (Pattern, String)
--compilePat_ s = Node WW []
compilePat_ s
| null plst = error "compilePat: empty pattern (syntax error)"
| length plst > 1 = error "compilePat: disconnected pattern (not rooted)\nPerhaps you used parentheses instead of braces?"
| not $ null s' = error $ "compilePat: parse error: not all input consumed\nRemaining: " ++ s'
#if 1
| otherwise = head plst
where
#else
-- When find the time, should add a CPP switch to enable emitting
-- a warning message in these cases; but it's too common/useful to
-- brutally disallow like this!...
| WI <- p = error "compilePat: top pattern node cannot be #"
| TR _ <- p = error "compilePat: top pattern node cannot be .:<qual>"
| TN _ _ <- p = error "compilePat: top pattern node cannot be *:<qual>"
| TW _ <- p = error "compilePat: top pattern node cannot be *:<qual>"
| TI _ <- p = error "compilePat: top pattern node cannot be #:<qual>"
| otherwise = hplst
where
hplst@(Node p _) = head plst
#endif
(plst, s') = compilePat' False Nothing Nothing [] s_ [] -- XXX ??
s_ = translateStarN s
translateStarN [] = []
translateStarN ('@':cs) = error $ "compilePat: parse error: unexpected '@'"
translateStarN ('*':cs)
| isNothing mn = '*' : translateStarN cs' -- or cs
| otherwise = '@' : ( fromJust mn ++ translateStarN cs' )
where
-- !_ = trace ("Boo: " ++ show (mn, cs')) ()
(mn, cs') = parseInt cs ""
translateStarN (c:cs) = c : translateStarN cs
parseInt :: String -> String -> ( Maybe String, String )
parseInt [] acc = ( if null acc then Nothing else Just acc , "" )
parseInt s@(c:cs) acc
| length acc > 8 = error $ "compilePat: * followed by too many (>8) digits"
| isDigit c = parseInt cs (acc++[c])
| otherwise = ( if null acc then Nothing else Just acc , s )
-- compilePat' parameters:
-- spark - the next node parsed will, when matched, spark parallel
-- evaluation of its subpatterns
-- mpn - says what the last PatNode parsed was (list args are empty)
-- - what do I mean "list args are empty"?
-- - oh: T* nodes have a list arg
-- - I see that mpn is never used (except in some dead code)...
-- mn - says what n is for rnfn (eg. 3 for "*3")
-- - note that, when Just, this signals to parser that * is for
-- a WN/TN node rather than a WW/TW node
-- (t:ts) - the list of type constraints (currently, constructor names)
-- - empty list doesn't signal anything about whether W* or T* node
-- (c:cs) - is what's left of the input string we're parsing
-- acc - is an accumulator parameter, collecting patterns parsed
compilePat' :: Bool -> Maybe PatNode -> Maybe Int -> [String] -> String -> [Pattern] -> ([Pattern], String)
compilePat' spark mpn mn (t:ts) (c:cs) _
| not $ c `elem` "{.*#" = error $ "compilePat: parse error: post-treps pattern char " ++ show c ++ " not one of {.*#"
compilePat' spark mpn mn [] [] acc = mytrace "EMPTY" $ (acc, [])
compilePat' spark mpn mn [] (' ':cs) acc = mytrace "space" $ compilePat' spark mpn mn [] cs acc
compilePat' spark mpn mn [] ('}':'{':cs) acc = error $ "compilePat: opening brace cannot follow closing brace"
compilePat' spark mpn mn [] ('}':cs) acc
#if 0
-- Lenient parser tolerates subpatterns of these.
-- (The semantics is that any such subpatterns are ignored --
-- discarded with a warning.)
#if USE_WW_DEEPSEQ
| isJust mpn, Just WW <- mpn = trace "compilePat-\"}\": warning: * with subpattern" $ mytrace "}" $ (acc, cs)
#endif
| isJust mpn, Just WI <- mpn = trace "compilePat-\"}\": warning: # with subpattern" $ mytrace "}" $ (acc, cs)
#endif
| otherwise = mytrace "}" $ (acc, cs)
compilePat' spark mpn mn [] (c:':':cs) acc
| null treps = error $ "compilePat: colon must be followed by at least one type name"
| otherwise = compilePat' spark mpn mn treps (c:cs') acc
where
-- !_ = trace ("Boo: " ++ show (treps, cs')) ()
(treps, cs') = compileTypeReps cs
compilePat' spark mpn Nothing [] ('@':cs) acc
| isNothing mn = error $ "compilePat: internal error @2 (please report this bug!)"
| otherwise = compilePat' spark mpn mn [] ('@':cs') acc -- mn is Just n
where
-- !_ = trace ("Boo: " ++ show (mn, cs')) ()
(mn_, cs') = parseInt cs ""
mn | isNothing mn_ = Nothing
| otherwise = Just ( read (fromJust mn_) :: Int )
compilePat' spark mpn (Just n) [] ('@':cs) acc
= compilePat' False mpn Nothing [] cs (acc++[node])
#if PARALLELISM_EXPERIMENT
where node | spark = Node (PN n) []
| otherwise = Node (WN n) []
#else
where node = Node (WN n) []
#endif
compilePat' spark mpn (Just n) [] (c:cs) acc
= error $ "compilePat: internal error @1(" ++ [c] ++") (please report this bug!)"
compilePat' spark mpn mn treps (c:'{':cs) acc = compilePat' spark mpn mn [] cs' (acc++[node])
where
(chs, cs') = mytrace (".{-cs="++cs) $ compilePat' spark mpn mn [] cs []
node
| null treps = case c of
'.' -> mytrace (".{-recurs: "++show chs) $ Node WR chs
-- Lenient parser tolerates subpatterns of these.
-- (The semantics is that any such subpatterns are ignored --
-- discarded with a warning.)
-- It's more convenient to keep the subpatterns, if want to issue
-- a warning when they don't match for type-constrained patterns.
-- True the semantics is the same except for the warning message,
-- but, well, I want to see it at the moment!
#if NEVER_IGNORE_SUBPATTERNS
#if ! WARN_IGNORED_SUBPATTERNS
#if USE_WW_DEEPSEQ
'*' -> Node WW chs
#endif
'#' -> Node WI chs
#else
#if USE_WW_DEEPSEQ
'*' -> trace "compilePat-\"{\": warning: * with subpattern" $ Node WW chs
#endif
'#' -> trace "compilePat-\"{\": warning: # with subpattern" $ Node WI chs
#endif
#else
#if ! WARN_IGNORED_SUBPATTERNS
#if USE_WW_DEEPSEQ
'*' -> Node WW []
#endif
'#' -> Node WI []
#else
#if USE_WW_DEEPSEQ
'*' -> trace "compilePat-\"{\": warning: * with subpattern" $ Node WW []
#endif
-- Yes, we do see the error for each test we expect a warning from!
-- '#' -> error "compilePat-\"{\": warning: # with subpattern"
-- '#' -> force $! trace "compilePat-\"{\": warning: # with subpattern" $! Node WI []
'#' -> trace "compilePat-\"{\": warning: # with subpattern" $ Node WI []
#endif
#endif
_ -> error $ "compilePat-\"{\": unexpected " ++ show c ++ " (cs'=" ++ cs'
| otherwise = case c of
'.' -> mytrace ("T-.{-recurs: "++show chs) $ Node (TR treps) chs
#if NEVER_IGNORE_SUBPATTERNS
#if ! WARN_IGNORED_SUBPATTERNS
#if USE_WW_DEEPSEQ
'*' -> Node (TW treps) chs
#endif
'#' -> Node (TI treps) chs
#else
#if USE_WW_DEEPSEQ
'*' -> trace "compilePat-\"{\": warning: * with subpattern" $ Node (TW treps) chs
#endif
'#' -> trace "compilePat-\"{\": warning: # with subpattern" $ Node (TI treps) chs
#endif
#else
#if ! WARN_IGNORED_SUBPATTERNS
#if USE_WW_DEEPSEQ
'*' -> Node (TW treps) []
#endif
'#' -> Node (TI treps) []
#else
#if USE_WW_DEEPSEQ
'*' -> trace "compilePat-\"{\": warning: * with subpattern" $ Node (TW treps) []
#endif
'#' -> trace "compilePat-\"{\": warning: # with subpattern" $ Node (TI treps) []
#endif
#endif
_ -> error $ "compilePat-T-\"{\": unexpected " ++ show c ++ " (cs'=" ++ cs'
#if PARALLELISM_EXPERIMENT
compilePat' spark mpn mn treps ('=':cs) acc = compilePat' True mpn mn treps cs acc
#endif
compilePat' spark mpn mn treps (c:cs) acc = compilePat' False mpn mn [] cs (acc++[node])
--compilePat' spark mpn mn treps (c:cs) acc = compilePat' False mpn mn [] cs $ force (acc++[node])
where
node
#if PARALLELISM_EXPERIMENT
| spark = case c of
'.' -> mytrace ".:cs" $ Node PR []
#if USE_WW_DEEPSEQ
'*' -> mytrace "*:cs" $ Node PW []
#endif
_ -> error $ "compilePat-\"c:cs\"-spark: unexpected " ++ show c ++ " (cs=" ++ cs
#endif
| null treps = case c of
'.' -> mytrace ".:cs" $ Node WS [] -- sic!
#if USE_WW_DEEPSEQ
'*' -> mytrace "*:cs" $ Node WW []
#endif
'#' -> mytrace "#:cs" $ Node WI []
_ -> error $ "compilePat-\"c:cs\": unexpected " ++ show c ++ " (cs=" ++ cs
| otherwise = case c of
'.' -> mytrace ".:cs" $ Node (TR treps) []
-- '.' -> mytrace ".:cs" $ Node (TS treps) []
#if USE_WW_DEEPSEQ
'*' -> mytrace "*:cs" $ Node (TW treps) []
#endif
'#' -> mytrace "#:cs" $ Node (TI treps) []
_ -> error $ "compilePat-T-\"c:cs\": unexpected " ++ show c ++ " (cs=" ++ cs
-------------------------------------------------------------------------------
-- | Using String instead of TypeRep since I wasn't sure
-- how to avoid mandatory recursion to complete the latter.
-- (Probably it can be done -- ':~:' perhaps -- but I was
-- unsure and this is working for the moment.)
compileTypeReps :: String -> ([String], String)
--compileTypeReps :: String -> ([TypeRep], String)
compileTypeReps cs = (treps,cs')
where
(tnames, cs') = parseTyNames cs
parseTyNames :: String -> ([String], String)
parseTyNames s = (sps', s')
where
sps' = map (dropWhile pstop) sps
-- !_ = trace ("(sps,s') = " ++ show (sps,s')) ()
(sps,s') = splitPred psplit pstop s
-- (sps,s') = splitPred p s
pstop x = x == '{' || x == '}'
-- pstop x = x == '{'
psplit x = x == ' ' || pstop x
-- p x = x == ' ' || x == '{'
-- p x = not $ isAlphaNum x || x == '_' || x == '\''
#if 1
-- XXX In consideration of the recursion problem with mkTyConApp below,
-- try to use typeOf instead -- but, this won't work! Because we are
-- starting with a String encoding the ...
-- ... or will it? We have to compare two strings; one comes from
-- the user-supplied pattern string we're parsing; the other? We
-- are not "comparing equality" here, it will be done later; we're
-- only compiling a pattern... So if the treps remain strings
-- in a Pattern, until we're ready to make comparisons; it's
-- inefficient unfortunately, but I feel this will work.
-- More detail: B/c when it comes time to match the pattern,
-- you DO have a concrete value (of some type); it is THEN that
-- you apply (show . typeRepTyCon . typeOf) to it, and then
-- make your Eq String comparison. [This can be optimised later;
-- I'm concerned now with a proof-of-concept, without TH.]
treps = tnames
#else
treps = map mktrep tnames
-- XXX You need the recursion for (==) to work; that may not mean
-- we can't use it, but will need some form of pattern-matching,
-- as full equality is going to be disfunctional. (B/c user would
-- have to specify the fully-recursive pattern [when they want to
-- use wildcards or stop nodes down there] -- totally ridiculous.)
-- This could be what :~: is for? (It's recursive, but you perhaps
-- can use in patterns without going full depth?)
-- mkTyConApp (mkTyCon3 "base" "Data.Either" "Either") [typeRep (Proxy::Proxy Bool), typeRep (Proxy::Proxy Int)] == typeRep (Proxy :: Proxy (Either Bool Int))
mktrep :: String -> TypeRep
mktrep tname = trep
where
tcon = mkTyCon3 "" "" tname
trep = mkTyConApp tcon []
--mkTyCon3 :: 3xString -> TypeCon
--mkTyConApp :: TyCon -> [TypeRep] -> TypeRep
#endif
-------------------------------------------------------------------------------
-- Split on the psplit predicate, stop consuming the list
-- on the pstop predicate.
splitPred :: (a -> Bool) -> (a -> Bool) -> [a] -> ([[a]], [a])
splitPred psplit pstop list = splitPred' psplit pstop list []
splitPred' :: (a -> Bool) -> (a -> Bool) -> [a] -> [[a]] -> ([[a]], [a])
splitPred' psplit pstop list acc
| null first = {-trace "-1-" $-} (acc, rest)
| null rest = {-trace "-2-" $-} (acc', []) -- or (acc, rest), obv.
| pstop h = {-trace "-3-" $-} (acc', rest)
| otherwise = {-trace "-4-" $-} splitPred' psplit pstop t acc'
where
(first,rest) = break psplit list
(h:t) = rest
acc' = acc ++ [first]
-------------------------------------------------------------------------------
-- | Inverse of 'compilePat'.
--
-- @'showPat' . 'compilePat' patstring = patstring@
--
-- provided that @'compilePat' patstring@ succeeds. (And, only up to
-- subpatterns elided from # ('WI' or 'TI') or from * ('WW', 'WN',
-- 'TW', 'TN', 'PW' or 'PN') nodes.)
showPat :: Pattern -> String
showPat (Node p chs)
| WR <- p = "." ++ descend chs ++ perhapsEmptyBraces
| WS <- p = "." ++ descend chs
| WN n <- p = "*" ++ show n
| WI <- p = "#" ++ descend chs
#if USE_WW_DEEPSEQ
| WW <- p = "*" ++ descend chs
#endif
#if PARALLELISM_EXPERIMENT
| PR <- p = "." ++ descend chs ++ perhapsEmptyBraces
| PN n <- p = error "PN PatNode not yet supported (sorry!)"
#if USE_WW_DEEPSEQ
| PW <- p = "#" ++ descend chs
#endif
#endif
| TR treps <- p = ".:" ++ descendT treps chs ++ perhapsEmptyBraces
| TI treps <- p = "#:" ++ descendT treps chs
--- | TS treps <- p = ".:" ++ descendT treps chs
| TN n treps <- p = "*" ++ show n ++ ":" ++ descendT treps chs
#if USE_WW_DEEPSEQ
| TW treps <- p = "*:" ++ descendT treps chs
#endif
where
perhapsEmptyBraces = if null chs then "{}" else ""
descend :: [Pattern] -> String
descend chs
| null chs = ""
| otherwise = "{" ++ concatMap showPat chs ++ "}"
descendT :: [String] -> [Pattern] -> String
descendT treps chs = treps_ ++ descend chs
where treps_ = concat (intersperse " " treps)
-------------------------------------------------------------------------------
-- Note that Ord is derived, so the order that the constructors
-- are listed matters! (This only affects GHC rules, SFAIK.)
-- (This data type is here, to avoid cyclical imports which
-- GHC pretty much is useless with.)
--------
-- On the one hand, we want to keep this lightweight -- it can in
-- principle be a single bit (Insulate/Propagate), as originally planned!
-- But the Spark thing was too useful; and Print and Error would
-- also be useful. But they're more orthogonal.
#if 0
type Spark = Bool
type PrintPeriod = Int
type ErrorMsg = String
data SeqNodeKind =
Insulate Spark PrintPeriod
| Conduct Spark PrintPeriod
| Force Spark PrintPeriod
| Error ErrorMsg
deriving ( Eq, Ord )
#else
data SeqNodeKind =
Insulate
--- | Conduct
| Propagate -- XXX if include Conduct, then rename Propagate to Force
#if PARALLELISM_EXPERIMENT
| Spark
#endif
-- These would break the Ord; and besides, they're sort of orthogonal
-- (as is Spark)
--- | Print Int
--- | Error String
deriving ( Eq, Ord )
#endif
-------------------------------------------------------------------------------