------------------------------------------------------------------------------- {- 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 -- 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 .:" | TN _ _ <- p = error "compilePat: top pattern node cannot be *:" | TW _ <- p = error "compilePat: top pattern node cannot be *:" | TI _ <- p = error "compilePat: top pattern node cannot be #:" | 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 -------------------------------------------------------------------------------