Changeset 14015

Show
Ignore:
Timestamp:
10/07/06 15:25:28 (2 years ago)
Author:
audreyt
Message:

* Pugs.Monad: The generalized enterSub now sets a resetT

barrier for both routines and blocks, and enforces various
closure traits on them.

* Support for PRE/POST design-by-contract assertions.

* Support for KEEP/UNDO blocks, with the provision that

"return 1" is counted as KEEP, not UNDO. (The spec
is not clear on this, but agentzh++'s test wants this
behaviour.) They are now interleaved as part of the
LEAVE queue as specced.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • src/Pugs/Monads.hs

    r12924 r14015  
    1616    enterLValue, enterRValue, 
    1717    enterLex, enterContext, enterEvalContext, enterPackage, enterCaller, 
    18     enterGiven, enterWhen, enterWhile, genSymPrim, genSymCC, 
     18    enterGiven, enterWhen, enterLoop, genSymPrim, genSymCC, 
    1919    enterBlock, enterSub, envEnterCaller, 
    2020    evalVal, tempVar, 
     
    2525import Pugs.AST 
    2626import Pugs.Types 
    27 import Control.Monad.RWS 
     27import Control.Monad.RWS (MonadPlus(..)) 
    2828import qualified Data.Map as Map 
    2929 
     
    106106        { envLexical = MkPad (lex `Map.intersection` envImplicit env) 
    107107        } 
    108     , envDepth = envDepth env + 1 
     108    , envDepth = (FrameRoutine:envDepth env) 
    109109    , envImplicit = Map.fromList [(cast "$_", ())] 
    110110    } 
     
    118118Used by "Pugs.Eval"'s implementation of 'Pugs.Eval.reduce' for @\"given\"@. 
    119119-} 
    120 enterGiven :: VRef   -- ^ Reference to the value to topicalise 
    121            -> Eval a -- ^ Action to perform within the new scope 
     120enterGiven :: Eval a -- ^ Action to perform within the new scope 
    122121           -> Eval a 
    123 enterGiven topic action = do 
    124     sym <- genSym (cast "$_") topic 
    125     enterLex [sym] action 
    126  
    127 {-| 
    128 Bind @&continue@ and @&break@ to subs that break out of the @when@ body 
    129 and topicalising block respectively, then perform the given evaluation 
    130 in the new lexical scope. 
    131  
     122enterGiven = local (\e -> e{ envDepth = (FrameGiven:envDepth e) }) 
     123 
     124{-| 
    132125Note that this function is /not/ responsible for performing the actual @when@ 
    133126test, nor is it responsible for adding the implicit @break@ to the end of the 
     
    135128(see the entry for @('Syn' \"when\" ... )@). 
    136129-} 
    137 enterWhen :: Exp      -- ^ The expression that @&break@ should be bound to 
    138           -> Eval Val -- ^ The @when@'s body block, as an evaluation 
     130enterWhen :: Eval Val -- ^ The @when@'s body block, as an evaluation 
    139131          -> Eval Val 
    140 enterWhen break action = callCC $ \esc -> do 
    141     env <- ask 
    142     contRec  <- genSubs env "&continue" $ continueSub esc 
    143     breakRec <- genSubs env "&break" $ breakSub 
    144     enterLex (contRec ++ breakRec) action 
    145     where 
    146     continueSub esc env = mkPrim 
    147         { subName = __"continue" 
    148         , subParams = makeParams env 
    149         , subBody = Prim ((esc =<<) . headVal) 
    150         } 
    151     breakSub env = mkPrim 
    152         { subName = __"break" 
    153         , subParams = makeParams env 
    154         , subBody = break 
    155         } 
    156  
    157 {-| 
    158 Bind @&last@ and @&next@ to subs that respectively break-out-of and repeat the  
    159 @while@\/@until@, then perform the given evaluation in the new lexical scope. 
    160  
    161 Note that this function is /not/ responsible for performing the actual 
    162 @while@\/@until@ test; it is the responsibility of the caller to add such a 
    163 test to the top of the body evaluation. 
    164 -} 
    165 enterWhile :: Eval Val -- ^ Evaluation representing loop test & body 
    166            -> Eval Val 
    167 enterWhile action = genSymCC "&last" $ \symLast -> do 
    168     -- genSymPrim "&next" (const action) $ \symNext -> do 
    169     callCC $ \esc -> genSymPrim "&next" (const $ action >>= esc) $ \symNext -> do 
    170         enterLex [symLast, symNext] action 
     132enterWhen action = do 
     133    rv  <- action 
     134    case rv of 
     135        VControl (ControlGiven GivenContinue)   -> retEmpty 
     136        VControl (ControlGiven GivenBreak)      -> retShiftEmpty 
     137        _                                       -> retShift rv 
     138 
     139{-| 
     140Register the fact that we are inside a loop block. 
     141-} 
     142enterLoop :: Eval Val -- ^ Evaluation representing loop test & body 
     143          -> Eval Val 
     144enterLoop = local (\e -> e{ envDepth = (FrameLoop:envDepth e) }) 
    171145 
    172146{-| 
     
    216190 
    217191{-| 
    218 Create a Perl 6 @&?BLOCK_EXIT@ function that, when activated, breaks out of 
    219 the block scope by activating the current continuation. The block body 
    220 evaluation is then performed in a new lexical scope with @&?BLOCK_EXIT@ 
    221 installed. 
    222  
    223192Used by 'Pugs.Eval.reduce' when evaluating @('Syn' \"block\" ... )@  
    224193expressions. 
    225194-} 
    226195enterBlock :: Eval Val -> Eval Val 
    227 enterBlock action = callCC $ \esc -> do 
    228     env <- ask 
    229     exitRec <- genSubs env "&?BLOCK_EXIT" $ escSub esc 
    230     local (\e -> e{ envOuter = Just env }) $ enterLex exitRec action 
    231     where 
    232     escSub esc env = mkPrim 
    233         { subName = __"BLOCK_EXIT" 
    234         , subParams = makeParams env 
    235         , subBody = Prim ((esc =<<) . headVal) 
    236         } 
     196enterBlock action = local (\e -> e{ envOuter = Just e }) action 
    237197 
    238198enterSub :: VCode -> Eval Val -> Eval Val 
    239199enterSub sub action 
    240     | typ >= SubPrim = action -- primitives just happen 
     200    | typ >= SubPrim = runAction -- primitives just happen 
    241201    | otherwise     = do 
    242202        env <- ask 
    243         if typ >= SubBlock 
    244             then do 
     203        rv  <- if typ >= SubBlock 
     204            then resetT $ do 
    245205                doFix <- fixEnv return env 
    246                 local doFix action 
    247             else resetT $ callCC $ \cc -> do 
     206                local doFix runAction 
     207            else resetT . callCC $ \cc -> do 
    248208                doFix <- fixEnv cc env 
    249                 local doFix action 
     209                local doFix runAction 
     210        runBlocks (filter (rejectKeepUndo rv . subName) . subLeaveBlocks) 
     211 
     212        when (rv == VControl (ControlLoop LoopLast)) $ 
     213            -- We won't have a chance to run the LAST block 
     214            -- once we exit outside the lexical block, so do it now 
     215            runBlocks subLastBlocks 
     216 
     217        assertBlocks subPostBlocks "POST" 
     218        case rv of 
     219            VControl l@(ControlLeave ftyp depth val) -> do 
     220                let depth' = if ftyp typ then depth - 1 else depth 
     221                if depth' < 0 
     222                    then return val 
     223                    else retControl l{ leaveDepth = depth' } 
     224            VControl ControlExit{} -> retShift rv 
     225            _ -> return rv 
    250226    where 
     227    rejectKeepUndo VUndef     = (/= __"KEEP") 
     228    rejectKeepUndo (VControl (ControlLeave _ _ val)) = \n -> rejectKeepUndo val n && (n /= __"NEXT") 
     229    rejectKeepUndo (VControl (ControlLoop LoopNext)) = (/= __"KEEP") 
     230    rejectKeepUndo VControl{} = \n -> (n /= __"KEEP") && (n /= __"NEXT") 
     231    rejectKeepUndo VError{}   = \n -> (n /= __"KEEP") && (n /= __"NEXT") 
     232    rejectKeepUndo _          = (/= __"UNDO") 
     233    runAction = do 
     234        assertBlocks subPreBlocks "PRE" 
     235        runBlocks subEnterBlocks 
     236        action 
     237    runBlocks f = mapM_ (evalExp . Syn "block" . (:[]) . Syn "sub" . (:[]) . Val . castV) (f sub) 
     238    assertBlocks f name = forM_ (f sub) $ \cv -> do 
     239        rv <- fromVal =<< (evalExp . Syn "block" . (:[]) . Syn "sub" . (:[]) . Val . castV $ cv) 
     240        if rv then return () else retError (name ++ " assertion failed") (subName sub) 
    251241    typ = subType sub 
    252242    doCC :: (Val -> Eval b) -> [Val] -> Eval b 
     
    323313            return val 
    324314 
    325 headVal :: [Val] -> Eval Val 
    326 headVal []    = retEmpty 
    327 headVal (v:_) = return v 
    328  
    329315tempVar :: Var -> Val -> Eval a -> Eval a 
    330316tempVar var val action = do