| 1 | {-# OPTIONS_GHC -fglasgow-exts #-} |
|---|
| 2 | |
|---|
| 3 | {-| |
|---|
| 4 | Junction logic. |
|---|
| 5 | |
|---|
| 6 | > Still round the corner there may wait |
|---|
| 7 | > A new road or a secret gate, |
|---|
| 8 | > And though we pass them by today, |
|---|
| 9 | > Tomorrow we may come this way... |
|---|
| 10 | -} |
|---|
| 11 | |
|---|
| 12 | module Pugs.Junc ( |
|---|
| 13 | ApplyArg(..), |
|---|
| 14 | opJunc, opJuncNone, opJuncAll, opJuncAny, opJuncOne, |
|---|
| 15 | juncApply, |
|---|
| 16 | ) where |
|---|
| 17 | import Pugs.Types |
|---|
| 18 | import Pugs.Internals |
|---|
| 19 | import Pugs.AST |
|---|
| 20 | import qualified Data.Set as Set |
|---|
| 21 | |
|---|
| 22 | {-| |
|---|
| 23 | Construct a @none(...)@ junction from a list of values. |
|---|
| 24 | |
|---|
| 25 | Delegates to 'opJunc'. |
|---|
| 26 | -} |
|---|
| 27 | opJuncNone :: [Val] -> Val |
|---|
| 28 | opJuncNone = opJunc JNone |
|---|
| 29 | |
|---|
| 30 | {-| |
|---|
| 31 | Construct an @all(...)@ junction from a list of values. |
|---|
| 32 | |
|---|
| 33 | Delegates to 'opJunc'. |
|---|
| 34 | -} |
|---|
| 35 | opJuncAll :: [Val] -> Val |
|---|
| 36 | opJuncAll = opJunc JAll |
|---|
| 37 | |
|---|
| 38 | {-| |
|---|
| 39 | Construct a n@any(...)@ junction from a list of values. |
|---|
| 40 | |
|---|
| 41 | Delegates to 'opJunc'. |
|---|
| 42 | -} |
|---|
| 43 | opJuncAny :: [Val] -> Val |
|---|
| 44 | opJuncAny = opJunc JAny |
|---|
| 45 | |
|---|
| 46 | {-| |
|---|
| 47 | Construct a @one(...)@ junction from a list of values. |
|---|
| 48 | |
|---|
| 49 | Does /not/ delegate to 'opJunc'! |
|---|
| 50 | -} |
|---|
| 51 | opJuncOne :: [Val] -> Val |
|---|
| 52 | opJuncOne args = VJunc (MkJunc JOne dups vals) |
|---|
| 53 | where |
|---|
| 54 | vals = Set.fromList [ v | [v] <- groups ] |
|---|
| 55 | dups = Set.fromList [ v | (v:_:_) <- groups ] |
|---|
| 56 | groups = group $ sort args |
|---|
| 57 | |
|---|
| 58 | {-| |
|---|
| 59 | Construct a junction of the specified junctive type, containing all the |
|---|
| 60 | values in the list. |
|---|
| 61 | -} |
|---|
| 62 | opJunc :: JuncType -> [Val] -> Val |
|---|
| 63 | opJunc t vals = VJunc $ MkJunc t Set.empty (joined `Set.union` Set.fromList vs) |
|---|
| 64 | where |
|---|
| 65 | joined = Set.unions $ map (\(VJunc s) -> juncSet s) js |
|---|
| 66 | (js, vs) = partition sameType vals |
|---|
| 67 | sameType (VJunc (MkJunc t' _ _)) = t == t' |
|---|
| 68 | sameType _ = False |
|---|
| 69 | |
|---|
| 70 | {-| |
|---|
| 71 | Merge the contents of two @any@ or @one@ junctions into a single, combined |
|---|
| 72 | junction value. |
|---|
| 73 | |
|---|
| 74 | For 'Pugs.Internals.JAny', values are simply collapsed into @Set@s (duplicate |
|---|
| 75 | values are discarded). |
|---|
| 76 | |
|---|
| 77 | For 'Pugs.Internals.JOne', newly-created duplicates are extracted from the |
|---|
| 78 | combined list of values and moved into the combined set of duplicates. |
|---|
| 79 | -} |
|---|
| 80 | mergeJunc :: JuncType -- ^ Type of the junctions being combined |
|---|
| 81 | -> [Val] -- ^ Concatenated list of duplicates (only used for @one@) |
|---|
| 82 | -> [Val] -- ^ Concatenated list of regular values |
|---|
| 83 | -> VJunc |
|---|
| 84 | mergeJunc j ds vs |
|---|
| 85 | = case j of |
|---|
| 86 | JAny -> MkJunc j (Set.fromList ds) (Set.fromList vs) |
|---|
| 87 | JOne -> MkJunc j dups vals |
|---|
| 88 | x -> internalError $ "mergeJunc pattern failure: " ++ (show x) |
|---|
| 89 | where |
|---|
| 90 | vals = Set.fromList [ v | [v] <- group $ sort vs ] |
|---|
| 91 | dups = Set.fromList (ds ++ [ v | (v:_:_) <- group $ sort (vs ++ ds) ]) |
|---|
| 92 | |
|---|
| 93 | -- OK... Now let's implement the hideously clever autothreading algorithm. |
|---|
| 94 | -- First pass - thread thru all() and none() |
|---|
| 95 | -- Second pass - thread thru any() and one() |
|---|
| 96 | |
|---|
| 97 | {-| |
|---|
| 98 | Core of the \"hideously clever\" autothreading algorithm. |
|---|
| 99 | |
|---|
| 100 | This function scans through the list of 'ApplyArg's, finds the first |
|---|
| 101 | uncollapsed junction, and transposes e.g. @foo($a|$b|$c)@ into |
|---|
| 102 | @( foo($a) | foo($b) | foo($c) )@. |
|---|
| 103 | |
|---|
| 104 | It then recursively applies itself to each of those newly-created \'threads\', |
|---|
| 105 | so ultimately all the call's arguments are properly collapsed. |
|---|
| 106 | |
|---|
| 107 | The scanning process will thread through @all@ and @none@ before it threads |
|---|
| 108 | through @any@ and @one@. |
|---|
| 109 | |
|---|
| 110 | >[09:09] <scook0> any specific reason for all() & none() getting autothreaded before one() & any()? |
|---|
| 111 | >[09:10] <autrijus> scook0: specced this way. |
|---|
| 112 | >[09:10] <autrijus> I don't think S09 gives reasons |
|---|
| 113 | |
|---|
| 114 | <http://dev.perl.org/perl6/doc/design/syn/S09.html> |
|---|
| 115 | |
|---|
| 116 | Once all the args /are/ collapsed, we call the (Haskell) function that |
|---|
| 117 | 'juncApply'\'s was given, passing to it the final list of collapsed args. |
|---|
| 118 | This happens once for /each/ possible combination of (collapsed) arguments. |
|---|
| 119 | The function is expected to perform the actual subroutine call. |
|---|
| 120 | |
|---|
| 121 | Note that 'juncApply' takes place /after/ parameter binding (because it must), |
|---|
| 122 | but /before/ we actually introduce any bindings into the sub's lexical scope |
|---|
| 123 | (because otherwise we wouldn't know which value to bind). |
|---|
| 124 | -} |
|---|
| 125 | juncApply :: ([ApplyArg] -> Eval Val) -- ^ Function to call once we know the |
|---|
| 126 | -- collapsed arg values |
|---|
| 127 | -> [ApplyArg] -- ^ List of arguments to autothread over |
|---|
| 128 | -> Eval Val |
|---|
| 129 | juncApply f args |
|---|
| 130 | | this@(_, (pivot:_)) <- break isTotalJunc args |
|---|
| 131 | , VJunc (MkJunc j dups vals) <- argValue pivot |
|---|
| 132 | = do |
|---|
| 133 | vals' <- appList this vals |
|---|
| 134 | return $ VJunc (MkJunc j dups (Set.fromList vals')) |
|---|
| 135 | | this@(_, (pivot:_)) <- break isPartialJunc args |
|---|
| 136 | , VJunc (MkJunc j dups vals) <- argValue pivot |
|---|
| 137 | = do |
|---|
| 138 | dups' <- appList this dups |
|---|
| 139 | vals' <- appList this vals |
|---|
| 140 | return $ VJunc (mergeJunc j dups' vals') |
|---|
| 141 | | (val:_) <- [ val | (ApplyArg _ val@(VError _ _) _) <- args ] |
|---|
| 142 | = return val |
|---|
| 143 | | otherwise |
|---|
| 144 | = f args |
|---|
| 145 | where |
|---|
| 146 | appList :: ([ApplyArg], [ApplyArg]) -> Set Val -> Eval [Val] |
|---|
| 147 | appList (before, (ApplyArg name _ coll):after) vs = do |
|---|
| 148 | env <- ask |
|---|
| 149 | io $ do |
|---|
| 150 | mvars <- forM (Set.elems vs) $ \v -> do |
|---|
| 151 | mv <- newEmptyMVar |
|---|
| 152 | forkIO $ do |
|---|
| 153 | val <- runEvalIO env $ juncApply f (before ++ (ApplyArg name v coll:after)) |
|---|
| 154 | putMVar mv val |
|---|
| 155 | return mv |
|---|
| 156 | mapM takeMVar mvars |
|---|
| 157 | appList _ _ = internalError "appList: list doesn't begin with ApplyArg" |
|---|
| 158 | |
|---|
| 159 | {-| |
|---|
| 160 | Return @True@ if the given 'ApplyArg' (autothreaded argument) represents a |
|---|
| 161 | junction value that is @all@ or @none@, /and/ still needs to autothreaded. |
|---|
| 162 | |
|---|
| 163 | Other junctions, total junctions that don't need collapsing, and non-junction |
|---|
| 164 | values will all produce @False@. |
|---|
| 165 | -} |
|---|
| 166 | isTotalJunc :: ApplyArg -> Bool |
|---|
| 167 | isTotalJunc arg |
|---|
| 168 | | (ApplyArg _ (VJunc j) b) <- arg |
|---|
| 169 | , (juncType j ==) `any` [JAll, JNone] |
|---|
| 170 | = not b |
|---|
| 171 | | otherwise |
|---|
| 172 | = False |
|---|
| 173 | |
|---|
| 174 | {-| |
|---|
| 175 | Return @True@ if the given 'ApplyArg' (autothreaded argument) represents a |
|---|
| 176 | junction value that is @one@ or @any@, /and/ still needs to be autothreaded. |
|---|
| 177 | |
|---|
| 178 | Other junctions, partial junctions that don't need collapsing, and non-junction |
|---|
| 179 | values will all produce @False@. |
|---|
| 180 | -} |
|---|
| 181 | isPartialJunc :: ApplyArg -> Bool |
|---|
| 182 | isPartialJunc arg |
|---|
| 183 | | (ApplyArg _ (VJunc j) b) <- arg |
|---|
| 184 | , (juncType j ==) `any` [JOne, JAny] |
|---|
| 185 | = not b |
|---|
| 186 | | otherwise |
|---|
| 187 | = False |
|---|
| 188 | |
|---|
| 189 | {-| |
|---|
| 190 | Represents a sub argument during the junction autothreading process. |
|---|
| 191 | |
|---|
| 192 | Note that 'argCollapsed' is set to @True@ only if the corresponding sub param |
|---|
| 193 | is explicitly specified as accepting the Perl 6 type @Junc@. |
|---|
| 194 | -} |
|---|
| 195 | data ApplyArg = ApplyArg |
|---|
| 196 | { argName :: !Var -- ^ Name of the param that this arg is for |
|---|
| 197 | , argValue :: !Val -- ^ Actual argument value, which may still be |
|---|
| 198 | -- a junction |
|---|
| 199 | , argCollapsed :: !Bool -- ^ @True@ if we have confirmed that this arg |
|---|
| 200 | -- doesn't need any further autothreading |
|---|
| 201 | } |
|---|
| 202 | deriving (Show, Eq, Ord) |
|---|
| 203 | |
|---|
| 204 | --------------------- |
|---|
| 205 | |
|---|
| 206 | {- |
|---|
| 207 | |
|---|
| 208 | bool = ["0", "1"] |
|---|
| 209 | test = do |
|---|
| 210 | sequence [ testJunc out inn nest | out <- junc, inn <- junc, nest <- [True, False] ] |
|---|
| 211 | |
|---|
| 212 | testJunc out inn nest = do |
|---|
| 213 | let foo = [ blah a b c out inn nest | a <- bool, b <- bool, c <- bool ] |
|---|
| 214 | when (all id foo) $ if nest |
|---|
| 215 | then print (out, inn) |
|---|
| 216 | else print (out) |
|---|
| 217 | |
|---|
| 218 | junc = ["any", "one", "all", "none"] |
|---|
| 219 | out1 = "any" |
|---|
| 220 | inn1 = "any" |
|---|
| 221 | |
|---|
| 222 | blah a b c out inn nest = want == has |
|---|
| 223 | where |
|---|
| 224 | want = opEval emptyEnv $ "? " ++ out1 ++ "( " ++ inn1 ++ "( " ++ a ++ ", " ++ b ++ " ), " ++ inn1 ++ "( " ++ b ++ ", " ++ " " ++ c ++ " ) )" |
|---|
| 225 | has | nest = opEval emptyEnv $ "? " ++ out ++ "( " ++ b ++ ", " ++ inn ++ "(" ++ a ++ ", " ++ c ++ "))" |
|---|
| 226 | | otherwise = opEval emptyEnv $ "? " ++ out ++ "( " ++ b ++ ", " ++ a ++ ", " ++ c ++ ")" |
|---|
| 227 | -} |
|---|