packages feed

uniplate 1.6 → 1.6.1

raw patch · 11 files changed

+129/−210 lines, 11 filesdep ~base

Dependency ranges changed: base

Files

Data/Generics/Biplate.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE MultiParamTypeClasses #-}  {- |-    /RECOMMENDATION/: Use "Data.Generics.Uniplate.Operations" instead.+    /DEPRECATED/: Use "Data.Generics.Uniplate.Operations" instead.      Requires multi-parameter type classes, so is no longer Haskell 98. These operations     are easier to use and construct than the equivalent "Data.Generics.UniplateStrOn" methods,@@ -19,7 +19,9 @@     available on GHC, but automatically infers instances. -} -module Data.Generics.Biplate(+module Data.Generics.Biplate+    {- DEPRECATED "Use Data.Generics.Uniplate.Operations instead" -}+    (     module Data.Generics.UniplateStrOn,     module Data.Generics.Biplate     ) where
Data/Generics/PlateData.hs view
@@ -1,22 +1,26 @@ {-# LANGUAGE ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances, ExistentialQuantification, Rank2Types, CPP #-}  {- |-    /RECOMMENDATION/: Use "Data.Generics.Uniplate.Data" instead.+    /DEPRECATED/: Use "Data.Generics.Uniplate.Data" instead.      This module exports 'Biplate' instances for everything with 'Data' defined.     Using GHC the 'Data' instances can be constructed with @deriving Data@. -}-module Data.Generics.PlateData(+module Data.Generics.PlateData+    {-# DEPRECATED "Use Data.Generics.Uniplate.Data instead" #-}+    (     module Data.Generics.Biplate     ) where  import Data.Generics.Biplate import Data.Generics.PlateInternal import Data.Generics++#if !(__GLASGOW_HASKELL__ < 606 || __GLASGOW_HASKELL__ >= 702) import Data.List import qualified Data.IntSet as IntSet import Data.Ratio-+#endif   -- | An existential box representing a type which supports SYB@@ -35,7 +39,7 @@                  start -> find ->                  Box find -#if __GLASGOW_HASKELL__ < 606+#if __GLASGOW_HASKELL__ < 606 || __GLASGOW_HASKELL__ >= 702 -- GHC 6.4.2 does not export typeRepKey, so we can't do the trick -- as efficiently, so we just give up and revert to always following 
Data/Generics/PlateDirect.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE MultiParamTypeClasses #-}  {- |-    /RECOMMENDATION/: Use "Data.Generics.Uniplate.Direct" instead.+    /DEPRECATED/: Use "Data.Generics.Uniplate.Direct" instead.      This module supplies a method for writing 'Biplate' instances more easily.     This module requires fewest extensions, highest performance, and most instance@@ -36,7 +36,9 @@ -}      -module Data.Generics.PlateDirect(+module Data.Generics.PlateDirect+    {-# DEPRECATED "Use Data.Generics.Uniplate.Direct instead" #-}+    (     module Data.Generics.Biplate,     -- * The Combinators     plate, plateSelf,
Data/Generics/PlateTypeable.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances #-}  {- |-    /RECOMMENDATION/: Use "Data.Generics.Uniplate.Typeable" instead.+    /DEPRECATED/: Use "Data.Generics.Uniplate.Typeable" instead.      This module supplies a method for writing 'Biplate' instances more easily.     @@ -20,7 +20,9 @@     >   uniplate = uniplateAll -} -module Data.Generics.PlateTypeable(+module Data.Generics.PlateTypeable+    {-# DEPRECATED "Use Data.Generics.Uniplate.Typeable instead" #-}+    (     module Data.Generics.Biplate,     module Data.Typeable,     -- * The Class
Data/Generics/Uniplate.hs view
@@ -1,5 +1,5 @@ {- |-/RECOMMENDATION/ Use "Data.Generics.UniplateStr" instead.+/DEPRECATED/ Use "Data.Generics.Uniplate.Operations" instead.  This is the main Uniplate module, which defines all the essential operations in a Haskell 98 compatible manner.@@ -14,7 +14,7 @@   module Data.Generics.Uniplate-    {- DEPRECATED "Use Data.Generics.UniplateStr instead" -}+    {- DEPRECATED "Use Data.Generics.Uniplate.Operations instead" -}     where  import Control.Monad
Data/Generics/Uniplate/Internal/Data.hs view
@@ -12,146 +12,48 @@ import Data.Generics import Data.Maybe import Data.List-import qualified Data.IntSet as IntSet-import Data.IntSet(IntSet)-import qualified Data.IntMap as IntMap-import Data.IntMap(IntMap) import Data.IORef import Control.Exception--------------------------------------------------------------------------- HIT TEST---data Answer a = Hit {fromHit :: a} -- you just hit the element you were after (here is a cast)-              | Follow -- go forward, you will find something-              | Miss -- you failed to sink my battleship!--data Oracle to = Oracle {fromOracle :: forall on . Typeable on => on -> Answer to}--{-# INLINE hitTest #-}-hitTest :: (Data from, Data to) => from -> to -> Oracle to---#if __GLASGOW_HASKELL__ < 606--- GHC 6.4.2 does not export typeRepKey, so we can't do the trick--- as efficiently, so we just give up and revert to always following--hitTest _ _ = Oracle . maybe Follow Hit . cast---#elif 0---hitTest from to =-    let kto = typeKey to-    in case hitTestQuery (dataBox from) kto of-           Nothing -> Oracle $ \on -> if typeKey on == kto then Hit $ unsafeCoerce on else Follow-           Just cache -> let test = cacheHitTest cache in-               Oracle $ \on -> let kon = typeKey on in-                   if kon == kto then Hit $ unsafeCoerce on-                   else if test kon then Follow-                   else Miss----- A cache hit test, but partially evaluated-{-# INLINE cacheHitTest #-}-cacheHitTest :: Cache -> TypeKey -> Bool-cacheHitTest (Cache hit miss)-    | IntSet.null hit = const False-    | IntSet.null miss = const True-    | otherwise = \x -> x `IntSet.member` hit----- hit means that this value may result in a hit--- miss means that this value will never result in a hit-data Cache = Cache {hit :: IntSet, miss :: IntSet} deriving Show-newCache = Cache IntSet.empty IntSet.empty----- Indexed by the @from@ type, then the @to@ type--- Nothing means that we can't perform the trick on the set-{-# NOINLINE hitTestCache #-}-hitTestCache :: IORef (IntMap (IntMap (Maybe Cache)))-hitTestCache = unsafePerformIO $ newIORef IntMap.empty+import qualified Data.IntMap as IntMap; import Data.IntMap(IntMap)  -hitTestQuery :: DataBox -> TypeKey -> Maybe Cache-hitTestQuery from@(DataBox kfrom vfrom) kto = inlinePerformIO $ do-    mp <- readIORef hitTestCache-    let res = IntMap.lookup kfrom mp >>= IntMap.lookup kto-    case res of-        Just ans -> return ans-        Nothing -> do-            let res = toCache $ hitTestAdd from kto IntMap.empty-            res2 <- Control.Exception.catch (return $! res) (\(_ :: SomeException) -> return Nothing)-            -- -- uncomment these lines to see where type search fails-            -- if isNothing res2 then print ("failure",show (typeOf vfrom),kfrom,kto) else return ()--            atomicModifyIORef hitTestCache $ \mp -> flip (,) () $-                IntMap.insertWith (const $ IntMap.insert kto res2) kfrom (IntMap.singleton kto res2) mp-            return res2-+#if __GLASGOW_HASKELL__ >= 606 && __GLASGOW_HASKELL__ < 702+import qualified Data.IntSet as Set+import qualified Data.IntMap as Map --- need to classify each item as one of the following-data Res = RHit | RMiss | RFollow | RBad deriving (Show,Eq)+type TypeKey = Int+type TypeSet = Set.IntSet+type TypeMap = Map.IntMap +typeKey :: Typeable a => a -> TypeKey+typeKey x = inlinePerformIO $ typeRepKey $ typeOf x -toCache :: IntMap Res -> Maybe Cache-toCache res | not $ IntSet.null $ f RBad = Nothing-            | otherwise = Just $ Cache (f RFollow) (f RMiss)-    where f x = IntMap.keysSet $ IntMap.filter (== x) res+#else+import qualified Data.Set as Set+import qualified Data.Map as Map -hitTestAdd :: DataBox -> TypeKey -> IntMap Res -> IntMap Res-hitTestAdd from@(DataBox kfrom _) kto res = case sybChildren from of-    _ | kfrom `IntMap.member` res -> res-    Nothing -> IntMap.insert kfrom RBad res+type TypeKey = TypeRep+type TypeSet = Set.Set TypeKey+type TypeMap = Map.Map TypeKey -    -- make an inductive hypothesis that this value is a miss-    -- if it turns out you were wrong, start again-    -- uses backtracking, so could be expensive-    Just xs | kto == kfrom -> hitTestAdds xs kto $ IntMap.insert kfrom RHit res-            | correct -> res2-            | otherwise -> hitTestAdds xs kto $ IntMap.insert kfrom RFollow res-        where res2 = hitTestAdds xs kto $ IntMap.insert kfrom RMiss res-              correct = all ((==) RMiss . (res2 IntMap.!) . dataBoxKey) xs+typeKey :: Typeable a => a -> TypeKey+typeKey = typeOf -hitTestAdds :: [DataBox] -> TypeKey -> IntMap Res -> IntMap Res-hitTestAdds [] kto res = res-hitTestAdds (x:xs) kto res = hitTestAdds xs kto $ hitTestAdd x kto res+#endif  -type TypeKey = Int--typeKey :: Typeable a => a -> Int-typeKey x = inlinePerformIO $ typeRepKey $ typeOf x+---------------------------------------------------------------------+-- HIT TEST  --- | An existential box representing a type which supports SYB--- operations.-data DataBox = forall a . (Data a) => DataBox {dataBoxKey :: TypeKey, dataBoxVal :: a}--dataBox :: Data a => a -> DataBox-dataBox x = DataBox (typeKey x) x---- return all the possible children of a node--- if you can't do so, just return Nothing-sybChildren :: DataBox -> Maybe [DataBox]-sybChildren (DataBox k x)-    | k == typeRational = Just [dataBox (0 :: Integer)]-    | isAlgType dtyp = Just $ concatMap f ctrs-    | isNorepType dtyp = Nothing-    | otherwise = Just []-    where-        f ctr = gmapQ dataBox (asTypeOf (fromConstr ctr) x)-        ctrs = dataTypeConstrs dtyp-        dtyp = dataTypeOf x--typeRational = typeKey (undefined :: Rational)+data Answer a = Hit {fromHit :: a} -- you just hit the element you were after (here is a cast)+              | Follow -- go forward, you will find something+              | Miss -- you failed to sink my battleship! -#else+data Oracle to = Oracle {fromOracle :: forall on . Typeable on => on -> Answer to} +{-# INLINE hitTest #-}+hitTest :: (Data from, Data to) => from -> to -> Oracle to hitTest from to =     let kto = typeKey to     in case readCacheFollower (dataBox from) kto of@@ -167,13 +69,13 @@ -- CACHE -- Store and compute the Follower and HitMap -data Cache = Cache HitMap (IntMap2 (Maybe Follower))+data Cache = Cache HitMap (TypeMap2 (Maybe Follower))  -- Indexed by the @from@ type, then the @to@ type -- Nothing means that we can't perform the trick on the set {-# NOINLINE cache #-} cache :: IORef Cache-cache = unsafePerformIO $ newIORef $ Cache emptyHitMap IntMap.empty+cache = unsafePerformIO $ newIORef $ Cache emptyHitMap Map.empty   readCacheFollower :: DataBox -> TypeKey -> Maybe Follower@@ -197,7 +99,7 @@ readCacheHitMap :: DataBox -> Maybe HitMap readCacheHitMap from@(DataBox kfrom vfrom) = inlinePerformIO $ do     Cache hit _ <- readIORef cache-    case IntMap.lookup kfrom hit of+    case Map.lookup kfrom hit of         Just _ -> return $ Just hit         Nothing -> do             res <- Control.Exception.catch (return $! Just $! insertHitMap from hit) (\(_ :: SomeException) -> return Nothing)@@ -209,15 +111,24 @@   ------------------------------------------------------------------------ INTMAP2+-- TYPEMAP2/INTMAP2 +type TypeMap2 a = TypeMap (TypeMap a)++lookup2 :: TypeKey -> TypeKey -> TypeMap2 a -> Maybe a+lookup2 x y mp = Map.lookup x mp >>= Map.lookup y++insert2 :: TypeKey -> TypeKey -> a -> TypeMap2 a -> TypeMap2 a+insert2 x y v mp = Map.insertWith (const $ Map.insert y v) x (Map.singleton y v) mp++ type IntMap2 a = IntMap (IntMap a) -lookup2 :: Int -> Int -> IntMap (IntMap x) -> Maybe x-lookup2 x y mp = IntMap.lookup x mp >>= IntMap.lookup y+intLookup2 :: Int -> Int -> IntMap2 a -> Maybe a+intLookup2 x y mp = IntMap.lookup x mp >>= IntMap.lookup y -insert2 :: Int -> Int -> x -> IntMap (IntMap x) -> IntMap (IntMap x)-insert2 x y v mp = IntMap.insertWith (const $ IntMap.insert y v) x (IntMap.singleton y v) mp+intInsert2 :: Int -> Int -> a -> IntMap2 a -> IntMap2 a+intInsert2 x y v mp = IntMap.insertWith (const $ IntMap.insert y v) x (IntMap.singleton y v) mp   ---------------------------------------------------------------------@@ -230,23 +141,17 @@ -- HitMap must have addHitMap on the key follower :: TypeKey -> TypeKey -> HitMap -> Follower follower from to mp-    | IntSet.null hit = const False-    | IntSet.null miss = const True-    | otherwise = \now -> now `IntSet.member` hit+    | Set.null hit = const False+    | Set.null miss = const True+    | otherwise = \now -> now `Set.member` hit     where-        (hit,miss) = IntSet.partition (\x -> to `IntSet.member` grab x) (IntSet.insert from $ grab from)-        grab x = IntMap.findWithDefault (error "couldn't grab in follower") x mp+        (hit,miss) = Set.partition (\x -> to `Set.member` grab x) (Set.insert from $ grab from)+        grab x = Map.findWithDefault (error "couldn't grab in follower") x mp   --------------------------------------------------------------------- -- DATA/TYPEABLE OPERATIONS -type TypeKey = Int--typeKey :: Typeable a => a -> Int-typeKey x = inlinePerformIO $ typeRepKey $ typeOf x-- -- | An existential box representing a type which supports SYB -- operations. data DataBox = forall a . (Data a) => DataBox {dataBoxKey :: TypeKey, dataBoxVal :: a}@@ -271,26 +176,26 @@ -- HITMAP -- What is the transitive closure of a type key -type HitMap = IntMap IntSet+type HitMap = TypeMap TypeSet  emptyHitMap :: HitMap-emptyHitMap = IntMap.fromList-        [(tRational, IntSet.singleton tInteger)-        ,(tInteger, IntSet.empty)]+emptyHitMap = Map.fromList+        [(tRational, Set.singleton tInteger)+        ,(tInteger, Set.empty)]     where tRational = typeKey (undefined :: Rational)           tInteger = typeKey (0 :: Integer)   insertHitMap :: DataBox -> HitMap -> HitMap-insertHitMap box hit = fixEq trans (populate box) `IntMap.union` hit+insertHitMap box hit = fixEq trans (populate box) `Map.union` hit     where         -- create a fresh box with all the necessary children that aren't in hit         populate :: DataBox -> HitMap-        populate x = f x IntMap.empty+        populate x = f x Map.empty             where                 f (DataBox key val) mp-                    | key `IntMap.member` hit || key `IntMap.member` mp = mp-                    | otherwise = fs cs $ IntMap.insert key (IntSet.fromList $ map dataBoxKey cs) mp+                    | key `Map.member` hit || key `Map.member` mp = mp+                    | otherwise = fs cs $ Map.insert key (Set.fromList $ map dataBoxKey cs) mp                         where cs = sybChildren val                  fs [] mp = mp@@ -299,10 +204,10 @@          -- update every one to be the transitive closure         trans :: HitMap -> HitMap-        trans mp = IntMap.map f mp+        trans mp = Map.map f mp             where-                f x = IntSet.unions $ x : map g (IntSet.toList x)-                g x = IntMap.findWithDefault (hit IntMap.! x) x mp+                f x = Set.unions $ x : map g (Set.toList x)+                g x = Map.findWithDefault (hit Map.! x) x mp   fixEq :: Eq a => (a -> a) -> a -> a@@ -310,9 +215,6 @@     where x2 = f x  -#endif-- --------------------------------------------------------------------- -- INSTANCE FUNCTIONS @@ -382,9 +284,6 @@  transformBis_ :: forall a . Data a => [[Transformer]] -> a -> a --#if __GLASGOW_HASKELL__ >= 606- -- basic algorithm: -- as you go down, given transformBis [fN..f1] --   if x is not in the set reachable by fN..f1, return x@@ -397,38 +296,36 @@         on = dataBox (undefined :: a)         hitBoxM = readCacheHitMap on         hitBox = fromJust hitBoxM-        univ = IntSet.toAscList $ IntSet.insert (dataBoxKey on) $ hitBox IntMap.! dataBoxKey on+        univ = Set.toAscList $ Set.insert (dataBoxKey on) $ hitBox Map.! dataBoxKey on         n = length ts          -- (a,b), where a < b, and both in range 1..n-        sliceMe i j = fromMaybe IntMap.empty $ lookup2 i j slices-        slices :: IntMap2 (IntMap (Maybe Transformer))+        sliceMe i j = fromMaybe Map.empty $ intLookup2 i j slices+        slices :: IntMap2 (TypeMap (Maybe Transformer))         slices = IntMap.fromAscList             [ (i, IntMap.fromAscList [(j, slice i j ts) | (j,ts) <- zip [i..n] (tail $ inits ts)])             | (i,ts) <- zip [1..n] (tails $ reverse ts)] -        slice :: Int -> Int -> [[Transformer]] -> IntMap (Maybe Transformer)+        slice :: Int -> Int -> [[Transformer]] -> TypeMap (Maybe Transformer)         slice from to tts = self             where-                self = f IntMap.empty (zip [from..] tts) -- FIXME: flattening out here gives different results...+                self = f Map.empty (zip [from..] tts) -- FIXME: flattening out here gives different results...                 f a ((i,[Transformer tk tr]):ts)-                    | tk `IntMap.member` a = f a ts-                    | otherwise = f (IntMap.insert tk t a) ts+                    | tk `Map.member` a = f a ts+                    | otherwise = f (Map.insert tk t a) ts                     where                         t = Just $ Transformer tk $ op (sliceMe (i+1) to) . tr . gmapT (op $ sliceMe from i) -                f a [] = a `IntMap.union` IntMap.fromAscList (mapMaybe (g $ IntMap.keysSet a) $ univ)+                f a [] = a `Map.union` Map.fromAscList (mapMaybe (g $ Map.keysSet a) univ)                  g a t = if b then Nothing else Just (t, Nothing)-                    where b = IntSet.null $ a `IntSet.intersection` (hitBox IntMap.! t)+                    where b = Set.null $ a `Set.intersection` (hitBox Map.! t) -        op :: forall b . Data b => IntMap (Maybe Transformer) -> b -> b-        op slice = case IntMap.lookup (typeKey (undefined :: b)) slice of+        op :: forall b . Data b => TypeMap (Maybe Transformer) -> b -> b+        op slice = case Map.lookup (typeKey (undefined :: b)) slice of             Nothing -> id             Just Nothing -> gmapT (op slice)             Just (Just (Transformer _ t)) -> unsafeCoerce . t . unsafeCoerce--#endif   transformBis_ [] = id
Data/Generics/Uniplate/Internal/Utils.hs view
@@ -8,7 +8,11 @@     unsafeCoerce, builder, unsafePerformIO, inlinePerformIO, concatCont     ) where +#if __GLASGOW_HASKELL__ >= 702+import System.IO.Unsafe(unsafePerformIO)+#else import Foreign(unsafePerformIO)+#endif import Unsafe.Coerce(unsafeCoerce)  #ifdef __GLASGOW_HASKELL__
Data/Generics/UniplateOn.hs view
@@ -1,6 +1,6 @@ {-# OPTIONS_GHC -fno-warn-deprecations #-} {- |-/RECOMMENDATION/: Use "Data.Generics.Uniplate.Operations" instead.+/DEPRECATED/: Use "Data.Generics.Uniplate.Operations" instead.  This module retained Haskell 98 compatability, but users who are happy with multi-parameter type classes should look towards "Data.Generics.Biplate".@@ -13,7 +13,7 @@ -}  module Data.Generics.UniplateOn-    {- DEPRECATED "Use Data.Generics.UniplateStrOn instead" -}+    {- DEPRECATED "Use Data.Generics.Uniplate.Operations instead" -}     (     module Data.Generics.Uniplate,     module Data.Generics.UniplateOn
Data/Generics/UniplateStr.hs view
@@ -1,5 +1,5 @@ {- |-/RECOMMENDATION/: Use "Data.Generics.Uniplate.Operations" instead.+/DEPRECATED/: Use "Data.Generics.Uniplate.Operations" instead.  This is the main Uniplate module, which defines all the essential operations in a Haskell 98 compatible manner.@@ -13,7 +13,9 @@ -}  -module Data.Generics.UniplateStr(+module Data.Generics.UniplateStr+    {- DEPRECATED "Use Data.Generics.Uniplate.Operations instead" -}+    (     module Data.Generics.UniplateStr,     module Data.Generics.Str     ) where
Data/Generics/UniplateStrOn.hs view
@@ -1,5 +1,5 @@ {- |-/RECOMMENDATION/: Use "Data.Generics.Uniplate.Operations" instead.+/DEPRECATED/: Use "Data.Generics.Uniplate.Operations" instead.  This module retained Haskell 98 compatability, but users who are happy with multi-parameter type classes should look towards "Data.Generics.Biplate".@@ -11,7 +11,9 @@ identically to their non @On@ counterparts. -} -module Data.Generics.UniplateStrOn(+module Data.Generics.UniplateStrOn+    {- DEPRECATED "Use Data.Generics.Uniplate.Operations instead" -}+    (     module Data.Generics.UniplateStr,     module Data.Generics.UniplateStrOn     ) where
uniplate.cabal view
@@ -1,16 +1,16 @@-Cabal-Version:      >= 1.2-Build-Type:         Simple-Name:               uniplate-Version:            1.6-Copyright:          2006-10, Neil Mitchell-Maintainer:         ndmitchell@gmail.com-Homepage:           http://community.haskell.org/~ndm/uniplate/-License:            BSD3-License-File:       LICENSE-Author:             Neil Mitchell-Synopsis:           Help writing simple, concise and fast generic operations.-Category:           Generics-Description:+cabal-version:      >= 1.6+build-type:         Simple+name:               uniplate+version:            1.6.1+author:             Neil Mitchell <ndmitchell@gmail.com>+maintainer:         Neil Mitchell <ndmitchell@gmail.com>+copyright:          Neil Mitchell 2006-2011+homepage:           http://community.haskell.org/~ndm/uniplate/+license:            BSD3+license-file:       LICENSE+synopsis:           Help writing simple, concise and fast generic operations.+category:           Generics+description:     Uniplate is library for writing simple and concise generic operations.     Uniplate has similar goals to the original Scrap Your Boilerplate work,     but is substantially simpler and faster.@@ -39,20 +39,24 @@     * "Data.Generics.Uniplate.DataOnly" - users making use of both @Data@ and @Direct@     to avoid getting instance conflicts. -Extra-Source-Files:+extra-source-files:     uniplate.htm     Data/Generics/Uniplate/Internal/DataInc.hs     Data/Generics/Uniplate/Internal/OperationsInc.hs -Flag ghc_6_10+source-repository head+    type:     darcs+    location: http://community.haskell.org/~ndm/darcs/hlint/ -Library+flag ghc_6_10++library     if flag(ghc_6_10)         build-depends: base >=4 && <5, containers, syb     else         build-depends: base >=3 && <4, containers -    Exposed-modules:+    exposed-modules:         Data.Generics.Str         Data.Generics.Compos         Data.Generics.SYB@@ -73,10 +77,10 @@         Data.Generics.PlateTypeable         Data.Generics.PlateData -    Other-modules:+    other-modules:         Data.Generics.PlateInternal         Data.Generics.Uniplate.Internal.Data         Data.Generics.Uniplate.Internal.DataOnlyOperations         Data.Generics.Uniplate.Internal.Utils -    Extensions: CPP+    extensions: CPP