root/src/Pugs/Monads.hs

Revision 20058, 15.8 kB (checked in by gbacon, 7 months ago)

Build the library, but the executable still doesn't link. Why aren't we building an Executable with cabal?

  • Property svn:mime-type set to text/plain; charset=UTF-8
  • Property svn:eol-style set to native
Line 
1{-# OPTIONS_GHC -fglasgow-exts -fallow-overlapping-instances #-}
2
3{-|
4    Common operations on Eval monad.
5   
6>   One Ring to rule them all,
7>   One Ring to find them,
8>   One Ring to bring them all
9>   and in the darkness bind them...
10
11    Note that the actual monads are defined elsewhere -- try looking at
12    "Pugs.AST.SIO" and "Pugs.AST.Internals".
13-}
14
15module Pugs.Monads (
16    ApplyKind(..),
17
18    enterLValue, enterRValue,
19    enterLex, enterContext, enterEvalContext, enterPackage, enterCaller,
20    enterGiven, enterWhen, enterLoop, enterGather, genSymPrim, genSymCC,
21    enterBlock, enterSub,
22    evalVal,
23
24    enterFrame, assertFrame, emptyFrames,
25
26    reclosePad, recloseCode, recloseVal,
27   
28    MaybeT, runMaybeT
29) where
30import Pugs.Internals
31import Pugs.AST
32import Pugs.Types
33import qualified Data.Set as Set
34
35newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }
36
37instance (Monad m) => Monad (MaybeT m) where
38    (MaybeT mon) >>= f =
39        MaybeT (mon >>= maybe (return Nothing) (runMaybeT . f))
40    return              = MaybeT . return . Just
41
42instance MonadTrans MaybeT where
43    lift mon = MaybeT (mon >>= return . Just)
44
45instance (MonadIO m) => MonadIO (MaybeT m) where
46    liftIO ma = MaybeT $ do
47        a <- liftIO ma
48        return (Just a)
49
50instance (Monad m) => MonadPlus (MaybeT m) where
51    mzero                       = MaybeT (return Nothing)
52    mplus (MaybeT a) (MaybeT b) = MaybeT $ do
53        ma <- a
54        case ma of
55            Nothing -> b
56            _       -> return ma
57
58{-|
59Perform the given evaluation in an /LValue/ context.
60-}
61enterLValue :: Eval a -> Eval a
62enterLValue = local (\e -> e{ envLValue = True })
63{-|
64Perform the given evaluation in an /RValue/ (i.e. non-/LValue/) context.
65-}
66enterRValue :: Eval a -> Eval a
67enterRValue = local (\e -> e{ envLValue = False })
68
69{-|
70Create a new lexical scope by applying the list of 'Pad'-transformers
71(which install new bindings), then perform the specified evaluation in that
72new scope.
73
74(Subsequent chained 'Eval's do /not/ see this new scope.)
75-}
76enterLex :: [PadMutator] -- ^ Transformations on current 'Pad' to produce the
77                         --     new 'Pad'.
78         -> Eval a       -- ^ Evaluation to be performed in the new scope
79         -> Eval a       -- ^ Resulting evaluation (lexical scope enter & exit
80                         --     are encapsulated)
81enterLex newSyms = local (\e -> e{ envLexical = combine newSyms (envLexical e) })
82
83{-|
84Perform the specified evaluation in the specified (Perl 6) context ('Cxt').
85
86(Subsequent chained 'Eval's do /not/ see this new scope.)
87-}
88enterContext :: Cxt -> Eval a -> Eval a
89enterContext cxt = local (\e -> e{ envContext = cxt })
90
91{-|
92Evaluate the specified expression in the specified (Perl 6) context ('Cxt').
93
94(Subsequent chained 'Eval's do /not/ see this new scope.)
95-}
96enterEvalContext :: Cxt -> Exp -> Eval Val
97enterEvalContext cxt = enterContext cxt . evalExp
98
99{-|
100Perform the specified evaluation in the specified package.
101
102(Subsequent chained 'Eval's do /not/ see this package.)
103-}
104enterPackage :: ByteString -> Eval a -> Eval a
105enterPackage pkg = local (\e -> e{ envPackage = cast pkg })
106
107{-|
108Enter a new environment and mark the previous one as 'Caller'.
109Also swap in the new one's lexical environment.
110-}
111enterCaller :: Eval a -> Eval a
112enterCaller = local envEnterCaller
113
114envEnterCaller :: Env -> Env
115envEnterCaller env = env
116    { envCaller = Just env
117    , envFrames = FrameRoutine `Set.insert` envFrames env
118    }
119
120{-|
121Register the fact that we are inside a specially marked control block.
122-}
123enterFrame :: Frame -> Eval a -> Eval a
124enterFrame f = local (\e -> e{ envFrames = f `Set.insert` envFrames e})
125
126enterGather, enterLoop, enterGiven :: Eval Val -> Eval Val
127enterGather = enterFrame FrameGather
128enterLoop   = enterFrame FrameLoop
129enterGiven  = id
130
131assertFrame :: Frame -> Eval a -> Eval a
132assertFrame f action = do
133    frames <- asks envFrames
134    if Set.member f frames
135        then action
136        else fail ("Cannot use this control structure outside a '" ++ (map toLower (drop 5 (show f))) ++ "' structure")
137
138emptyFrames :: Set Frame
139emptyFrames = Set.empty
140
141{-|
142Note that this function is /not/ responsible for performing the actual @when@
143test, nor is it responsible for adding the implicit @break@ to the end of the
144@when@'s block--those are already taken care of by 'Pugs.Eval.reduce'
145(see the entry for @('Syn' \"when\" ... )@).
146-}
147enterWhen :: Eval Val -- ^ The @when@'s body block, as an evaluation
148          -> Eval Val
149enterWhen action = do
150    rv  <- enterFrame FrameWhen action
151    case rv of
152        VControl (ControlWhen WhenContinue)   -> retEmpty
153        VControl (ControlWhen WhenBreak)      -> retShiftEmpty
154        _                                     -> retShift rv
155
156{-|
157Generate a new Perl 6 operation from a Haskell function, give it a name, and
158generate a @('Pad' -> 'Pad')@ transformer that can be used to install it into
159a pad.
160
161This transformer is passed into a given \'action\' function, which is
162expected to apply the pad-transformer (e.g. in a new lexical scope), then
163perform some evaluation in that scope.
164
165Most of the time, this \'action\' is an anonymous function that passes its
166argument into 'enterLex'.
167-}
168genSymPrim :: (MonadSTM m)
169           => String                -- ^ Name installed in 'Pad'
170                                    --     (must have leading @&@ sigil)
171           -> ([Val] -> Eval Val)   -- ^ The actual primitive to wrap
172           -> (PadMutator -> m t)   -- ^ A (lambda) function that the 'Pad'
173                                    --     transformer is given to
174           -> m t -- ^ Result of passing the pad-transformer to the \'action\'
175genSymPrim symName@('&':name) prim action = do
176    newSym <- genSym (cast symName) . codeRef $ mkPrim
177        { subName = cast name
178        , subBody = Prim prim
179        }
180    action newSym
181genSymPrim _ _ _ = error "need a &name"
182
183{-|
184Generate a Perl 6 primitive that, when called, will activate the /current/
185continuation (i.e. one that can be used to immediately break out of whatever
186evaluation we are about to perform). This is great for @&last@ and the like.
187
188This produces a pad-transformer @('Pad' -> 'Pad')@. This transformer is given
189to an \'action\' function, which is expected to apply it (e.g. in a lexical
190scope), then perform some evaluation in that scope.
191-}
192genSymCC :: String -- ^ Name of the primitive in the symbol table ('Pad').
193         -> (PadMutator -> Eval Val)   -- ^ An \'action\' function that will
194                                       --     take the pad-transformer and use
195                                       --     it to perform some evaluation
196         -> Eval Val -- ^ Result of passing the pad-transformer to the
197                     --     \'action\'
198genSymCC symName action = callCC $ \esc -> do
199    genSymPrim symName (const $ esc undef) action
200
201{-|
202Used by 'Pugs.Eval.reduce' when evaluating @('Syn' \"block\" ... )@
203expressions.
204-}
205enterBlock :: Eval Val -> Eval Val
206enterBlock action = do
207    local (\e -> e{ envLexPads = (PRuntime emptyPad:envLexPads e) }) action
208
209recloseLexPad :: LexPad -> STM LexPad
210recloseLexPad (PCompiling tv) = do
211    pad <- readMPad tv
212    return (PRuntime pad)
213recloseLexPad lpad  = return lpad
214
215recloseExp :: Exp -> STM Exp
216recloseExp (Val val) = fmap Val (recloseVal val)
217recloseExp exp       = return exp
218
219recloseVal :: Val -> STM Val
220recloseVal (VRef ref)   = do
221    fmap VRef (recloseRef ref)
222recloseVal (VCode code) = do
223    fmap VCode (recloseCode code)
224recloseVal (VList list) = do
225    fmap VList (mapM recloseVal list)
226recloseVal val          = return val
227
228
229recloseTraitBlocks :: TraitBlocks -> STM TraitBlocks
230recloseTraitBlocks (MkTraitBlocks a b c d e f g h i j k) = do
231    [a', b', c', d', e', f', g', h', i', j', k'] <- mapM (mapM recloseCode) [a, b, c, d, e, f, g, h, i, j, k]
232    return $ MkTraitBlocks a' b' c' d' e' f' g' h' i' j' k'
233
234recloseCode :: VCode -> STM VCode
235recloseCode vcode
236    | Nothing    <- subStarted vcode = do
237--  , subType vcode /= SubPrim = do
238        outers'     <- mapM recloseLexPad (subOuterPads vcode)
239        inner'      <- reclosePad (subInnerPad vcode)
240        body'       <- transformExp recloseExp (subBody vcode)
241        started'    <- newTVar False
242        traits'     <- recloseTraitBlocks (subTraitBlocks vcode)
243        return $ vcode
244            { subOuterPads   = outers'
245            , subInnerPad    = inner'
246            , subBody        = body'
247            , subStarted     = Just started'
248            , subTraitBlocks = traits'
249            }
250recloseCode vcode = return vcode
251
252recloseRef :: VRef -> STM VRef
253recloseRef (MkRef (ICode cv))
254    | Just (vcode :: VCode) <- fromTypeable cv = do
255        vcode'   <- recloseCode vcode
256        return . MkRef . ICode $ vcode'
257recloseRef ref = return ref
258
259reclosePad :: Pad -> STM Pad
260reclosePad pad = fmap listToPad . forM (padToList pad) $ \(name, entry) -> do
261    entry' <- case v_twigil name of
262        TMagical    -> return entry -- XXX - Prevent &?ROUTINE recursion
263        _           -> do
264            case entry of
265                PEStatic{ pe_proto = proto, pe_store = store } -> do
266                    proto'  <- recloseRef proto
267                    ref     <- readTVar store
268                    ref'    <- recloseRef ref
269                    writeTVar store ref'
270                    return entry{ pe_proto = proto' }
271                PELexical{ pe_proto = proto, pe_store = store } -> do
272                    proto'  <- recloseRef proto
273                    ref     <- readTVar store
274                    ref'    <- recloseRef ref
275                    writeTVar store ref'
276                    return entry{ pe_proto = proto' }
277                PEConstant{ pe_proto = proto } -> do
278                    proto'  <- recloseRef proto
279                    return entry{ pe_proto = proto' }
280    return (name, entry')
281
282data ApplyKind = AKInline | AKDisplaced deriving (Show)
283
284enterSub :: ApplyKind -> VCode -> Eval Val -> Eval Val
285enterSub appKind sub action = do
286    env <- ask
287    pad <- case subStarted sub of
288        Just tvar   -> do
289            started <- stm $ readTVar tvar
290            if started
291                then refreshPad (subInnerPad sub)
292                    -- `finallyM` warn "======= REFRESHED ==========" (subInnerPad sub, sub)
293                else (stm $ do
294                    writeTVar tvar True
295                    reclosePad (subInnerPad sub))
296                    -- `finallyM` warn "======= RECLOSED ==========" (tvar, subInnerPad sub)
297        _           -> do
298            -- warn "==== NOTHING ====" (subInnerPad sub)
299            return (subInnerPad sub)
300    rv  <- case typ of
301        -- For coroutines, we secretly store a continuation into subCont
302        -- whenever "yield" occurs in it.  However, the inner CC must be
303        -- delimited on the subroutine boundary, otherwise the resuming
304        -- continuation will continue into the rest of the program,
305        -- which is now how coroutines are supposed to work.
306        -- On the other hand, the normal &?CALLER_CONTINUATION must still
307        -- work as an undelimiated continuation, which is why callCC here
308        -- occurs before resetT.
309        SubCoroutine -> tryT . callCC $ \cc -> resetT $ do
310            doFix <- fixEnv cc env pad
311            local doFix runAction
312
313--      _ | typ >= SubBlock -> tryT $ do
314        _ -> tryT $ do
315            doFix <- fixEnv return env pad
316            local doFix runAction
317{-
318        _ -> tryT . callCC $ \cc -> do
319            doFix <- fixEnv cc env pad
320            local doFix runAction
321-}
322
323    -- warn "XXX" ()
324    doFix <- fixEnv return env pad
325    local doFix $ do
326        runBlocks (filter (rejectKeepUndo rv . subName) . subLeaveBlocks)
327        when (rv == VControl (ControlLoop LoopLast)) $
328            -- We won't have a chance to run the LAST block
329            -- once we exit outside the lexical block, so do it now
330            runBlocks subLastBlocks
331        assertBlocks subPostBlocks "POST"
332    case rv of
333        VControl l@(ControlLeave ftyp depth val) -> do
334            let depth' = if ftyp typ then depth - 1 else depth
335            if depth' < 0
336                then return val
337                else retControl l{ leaveDepth = depth' }
338        VControl ControlExit{}  -> retShift rv
339        VError{}                -> retShift rv -- XXX - Implement CATCH block here
340        _ -> return rv
341    where
342    rejectKeepUndo VUndef     = (/= __"KEEP")
343    rejectKeepUndo (VControl (ControlLeave _ _ val)) = \n -> rejectKeepUndo val n && (n /= __"NEXT")
344    rejectKeepUndo (VControl (ControlLoop LoopNext)) = (/= __"KEEP")
345    rejectKeepUndo VControl{} = \n -> (n /= __"KEEP") && (n /= __"NEXT")
346    rejectKeepUndo VError{}   = \n -> (n /= __"KEEP") && (n /= __"NEXT")
347    rejectKeepUndo _          = (/= __"UNDO")
348    runAction = do
349        assertBlocks subPreBlocks "PRE"
350        runBlocks subEnterBlocks
351        action
352    runBlocks f = mapM_ (evalExp . Syn "block" . (:[]) . Syn "sub" . (:[]) . Val . castV) (f (subTraitBlocks sub))
353    assertBlocks f name = forM_ (f (subTraitBlocks sub)) $ \cv -> do
354        rv <- fromVal =<< (evalExp . Syn "block" . (:[]) . Syn "sub" . (:[]) . Val . castV $ cv)
355        if rv then return () else die (name ++ " assertion failed") (subName sub)
356    runBlocks' f = mapM_ (evalExp . Syn "block'" . (:[]) . Syn "sub" . (:[]) . Val . castV) (f (subTraitBlocks sub))
357    assertBlocks' f name = forM_ (f (subTraitBlocks sub)) $ \cv -> do
358        rv <- fromVal =<< (evalExp . Syn "block'" . (:[]) . Syn "sub" . (:[]) . Val . castV $ cv)
359        if rv then return () else die (name ++ " assertion failed") (subName sub)
360    typ = subType sub
361    doCC :: (Val -> Eval b) -> [Val] -> Eval b
362    doCC cc []  = cc undef
363    doCC cc [v] = cc =<< evalVal v
364    doCC _  _   = internalError "enterSub: doCC list length > 1"
365    orig :: VCode -> VCode
366    orig sub = sub { subBindings = [], subParams = (map fst (subBindings sub)) }
367
368    fixEnv :: (Val -> Eval Val) -> Env -> Pad -> Eval (Env -> Env)
369    fixEnv _cc env pad
370        | SubPrim <- typ = do
371            return $ \e -> e
372                { envLexical = pad `mappend` envLexical env
373                , envLexPads = (PRuntime pad:envLexPads env)
374                }
375        | AKInline <- appKind = do
376            -- Entering an inline call.
377            return $ \e -> e
378                { envLexical = pad `mappend` envLexical env
379                , envPackage = subPackage sub
380                , envLexPads = (PRuntime pad:envLexPads env)
381                }
382        | otherwise = do
383            -- callerRec <- genSym (cast "&?CALLER_CONTINUATION") (codeRef $ ccSub cc env)
384            pad'      <- fmap (pad `mappend`) $ mergeLexPads (subOuterPads sub)
385            return $ \e -> e
386                { envLexical = pad' -- combine ([callerRec]) pad'
387                , envPackage = subPackage sub
388                , envLexPads = (PRuntime pad':subOuterPads sub)
389                }
390    ccSub :: (Val -> Eval Val) -> Env -> VCode
391    ccSub cc env = mkPrim
392        { subName = __"CALLER_CONTINUATION"
393        , subParams = makeParams env
394        , subBody = Prim $ doCC cc
395        }
396
397makeParams :: Env -> [Param]
398makeParams MkEnv{ envContext = cxt, envLValue = lv }
399    = [ MkOldParam
400        { isInvocant = False
401        , isOptional = True
402        , isNamed    = False
403        , isLValue   = lv
404        , isWritable = lv
405        , isLazy     = False
406        , paramName  = cast $ case cxt of
407            CxtSlurpy _ -> "@?0"
408            _           -> "$?0"
409        , paramContext = cxt
410        , paramDefault = Val VUndef
411        } ]
412
413evalVal :: Val -> Eval Val
414evalVal val@VV{} = do
415    env <- ask
416    let cxt = envContext env
417        lv  = envLValue env
418    if lv || cxt == CxtVoid then return val else val ./ cxt
419evalVal val@(VRef ref) = do
420    lv  <- asks envLValue
421    if lv
422        then return val
423        else if refType ref == mkType "Scalar::Const"
424            then evalVal =<< readRef ref
425            else do
426                typ <- evalValType val
427                if isaType "Junction" typ
428                    then evalVal =<< readRef ref
429                    else return val
430evalVal val = return val
Note: See TracBrowser for help on using the browser.