root/src/Pugs/Junc.hs

Revision 15616, 7.4 kB (checked in by audreyt, 19 months ago)

* Change all liftSTM into stm and all liftIO into io.

  • Property svn:mime-type set to text/plain; charset=UTF-8
  • Property svn:eol-style set to native
Line 
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
12module Pugs.Junc (
13    ApplyArg(..),
14    opJunc, opJuncNone, opJuncAll, opJuncAny, opJuncOne,
15    juncApply,
16) where
17import Pugs.Types
18import Pugs.Internals
19import Pugs.AST
20import qualified Data.Set as Set
21
22{-|
23Construct a @none(...)@ junction from a list of values.
24
25Delegates to 'opJunc'.
26-}
27opJuncNone :: [Val] -> Val
28opJuncNone = opJunc JNone
29
30{-|
31Construct an @all(...)@ junction from a list of values.
32
33Delegates to 'opJunc'.
34-}
35opJuncAll :: [Val] -> Val
36opJuncAll = opJunc JAll
37
38{-|
39Construct a n@any(...)@ junction from a list of values.
40
41Delegates to 'opJunc'.
42-}
43opJuncAny :: [Val] -> Val
44opJuncAny = opJunc JAny
45
46{-|
47Construct a @one(...)@ junction from a list of values.
48
49Does /not/ delegate to 'opJunc'!
50-}
51opJuncOne :: [Val] -> Val
52opJuncOne 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{-|
59Construct a junction of the specified junctive type, containing all the
60values in the list.
61-}
62opJunc :: JuncType -> [Val] -> Val
63opJunc 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{-|
71Merge the contents of two @any@ or @one@ junctions into a single, combined
72junction value.
73
74For 'Pugs.Internals.JAny', values are simply collapsed into @Set@s (duplicate
75values are discarded).
76
77For 'Pugs.Internals.JOne', newly-created duplicates are extracted from the
78combined list of values and moved into the combined set of duplicates.
79-}
80mergeJunc :: 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
84mergeJunc 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{-|
98Core of the \"hideously clever\" autothreading algorithm.
99
100This function scans through the list of 'ApplyArg's, finds the first
101uncollapsed junction, and transposes e.g. @foo($a|$b|$c)@ into
102@( foo($a) | foo($b) | foo($c) )@.
103
104It then recursively applies itself to each of those newly-created \'threads\',
105so ultimately all the call's arguments are properly collapsed.
106
107The scanning process will thread through @all@ and @none@ before it threads
108through @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
116Once 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.
118This happens once for /each/ possible combination of (collapsed) arguments.
119The function is expected to perform the actual subroutine call.
120
121Note that 'juncApply' takes place /after/ parameter binding (because it must),
122but /before/ we actually introduce any bindings into the sub's lexical scope
123(because otherwise we wouldn't know which value to bind).
124-}
125juncApply :: ([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
129juncApply 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{-|
160Return @True@ if the given 'ApplyArg' (autothreaded argument) represents a
161junction value that is @all@ or @none@, /and/ still needs to autothreaded.
162
163Other junctions, total junctions that don't need collapsing, and non-junction
164values will all produce @False@.
165-}
166isTotalJunc :: ApplyArg -> Bool
167isTotalJunc arg
168    | (ApplyArg _ (VJunc j) b) <- arg
169    , (juncType j ==) `any` [JAll, JNone]
170    = not b
171    | otherwise
172    = False
173
174{-|
175Return @True@ if the given 'ApplyArg' (autothreaded argument) represents a
176junction value that is @one@ or @any@, /and/ still needs to be autothreaded.
177
178Other junctions, partial junctions that don't need collapsing, and non-junction
179values will all produce @False@.
180-}
181isPartialJunc :: ApplyArg -> Bool
182isPartialJunc arg
183    | (ApplyArg _ (VJunc j) b) <- arg
184    , (juncType j ==) `any` [JOne, JAny]
185    = not b
186    | otherwise
187    = False
188
189{-|
190Represents a sub argument during the junction autothreading process.
191
192Note that 'argCollapsed' is set to @True@ only if the corresponding sub param
193is explicitly specified as accepting the Perl 6 type @Junc@.
194-}
195data 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
208bool = ["0", "1"]
209test = do
210    sequence [ testJunc out inn nest | out <- junc, inn <- junc, nest <- [True, False] ]
211
212testJunc 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
218junc = ["any", "one", "all", "none"]
219out1 = "any"
220inn1 = "any"
221
222blah 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-}
Note: See TracBrowser for help on using the browser.