packages feed

lens-regex (empty) → 0.1.0

raw patch · 11 files changed

+461/−0 lines, 11 filesdep +arraydep +basedep +directorysetup-changed

Dependencies added: array, base, directory, doctest, filepath, lens, lens-regex, regex-base, regex-posix, template-haskell

Files

+ .gitignore view
@@ -0,0 +1,26 @@+# General+\#*#+.*~+*~+.#*+*.swp+.DS_Store+.gdb_history+TAGS+# Object files+*.a+*.o+*.so+*.hi+*.p_hi+a.out+# autotool+autom4te.cache+stamp-h1+# misc+*.sqlite+Main+dist/+cabal-dev/+cabal.sandbox.config+.cabal-sandbox
+ .travis.yml view
@@ -0,0 +1,55 @@+language: haskell++env:+  matrix:+    - GHCVER=7.4.2+    - GHCVER=7.6.3+    - GHCVER=7.8.4+    - GHCVER=7.10.1+    - GHCVER=head++matrix:+  allow_failures:+    - env: GHCVER=head++before_install:+  - |+    if [ $GHCVER = `ghc --numeric-version` ]; then+      echo "use system ghc: `which ghc` `ghc --numeric-version`"+    else+      travis_retry sudo add-apt-repository -y ppa:hvr/ghc+      travis_retry sudo apt-get update+      travis_retry sudo apt-get install  -y --force-yes cabal-install-1.22 ghc-$GHCVER+      export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/1.22/bin:$PATH+      echo "use ppa:hvr/ghc: `which ghc` `ghc --numeric-version`"+    fi++install:+  - export PATH=$HOME/.cabal/bin:$PATH+  - which cabal+  - travis_retry cabal update+  - cabal install --only-dependencies --enable-tests+  - ghc-pkg list++script:+  - which cabal+  - cabal configure --enable-tests -fbuild-samples+  - cabal build+  - cabal test --show-details=always++  # tests source distribution package+  - cabal sdist+  - |+    PKGNAME="$(cabal info . | awk '/^\*/{print $2}')"+    SRCTGZ="${PKGNAME}.tar.gz"+    cd dist/+    if [ -f "$SRCTGZ" ]; then+      tar xvzf "$SRCTGZ"+      cd "$PKGNAME"+      NG=$(git ls-tree HEAD --full-tree -r | while read perm blob hash name; do [ -e "$name" ] || echo NG "$name"; done)+      if [ -n "$NG" ]; then+        echo "Missing files:"+        echo $NG+        exit 1+      fi+    fi
+ CHANGELOG.md view
@@ -0,0 +1,3 @@+## 0.1.0++* Initial release
+ LICENSE view
@@ -0,0 +1,26 @@+Copyright (C) 2015, Takahiro Himura++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++    * Redistributions of source code must retain the above copyright+      notice, this list of conditions and the following disclaimer.++    * Redistributions in binary form must reproduce the above+      copyright notice, this list of conditions and the following+      disclaimer in the documentation and/or other materials provided+      with the distribution.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,3 @@+# lens-regex: Lens powered regular expression #++[![Build Status](https://travis-ci.org/himura/lens-regex.svg?branch=master)](https://travis-ci.org/himura/lens-regex)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ lens-regex.cabal view
@@ -0,0 +1,66 @@+name:                lens-regex+version:             0.1.0+synopsis:            Lens powered regular expression+description:         Lens powered regular expression+homepage:            https://github.com/himura/lens-regex+license:             BSD3+license-file:        LICENSE+author:              Takahiro HIMURA+maintainer:          Takahiro HIMURA <taka@himura.jp>+copyright:           Copyright (C) 2015 Takahiro HIMURA+category:            Text+build-type:          Simple+cabal-version:       >=1.10++extra-source-files:+  .gitignore+  .travis.yml+  README.md+  CHANGELOG.md+  sample/*.hs++source-repository head+  type: git+  location: https://github.com/himura/lens-regex.git++flag build-samples+  description: build samples+  default: False++library+  exposed-modules:     Text.Regex.Lens+                       Text.Regex.Quote+  build-depends:       base >= 4.5 && < 5+                     , array+                     , lens >= 4 && < 5+                     , regex-base+                     , template-haskell+  hs-source-dirs:      src+  ghc-options:         -Wall+  default-language:    Haskell2010++executable sample+  main-is: sample.hs+  hs-source-dirs: sample++  if !flag(build-samples)+    buildable: False+  else+    build-depends:     base >= 4.5 && < 5+                     , lens+                     , lens-regex+                     , regex-posix+    ghc-options:       -Wall+    default-language:  Haskell2010++test-suite doctests+  type: exitcode-stdio-1.0+  main-is: doctests.hs+  hs-source-dirs: tests++  build-depends:       base+                     , directory+                     , doctest+                     , filepath+                     , regex-posix+  default-language:  Haskell2010
+ sample/sample.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE QuasiQuotes #-}++{-++OUTPUT:++##################+## Target String:+target1 = "hoge00 fuga hoge01 neko hoge02"++## Example: target1 ^.. regex [r|hoge[0-9]+|]+[MatchPart {_matchedString = "hoge00", _captures = []},MatchPart {_matchedString = "hoge01", _captures = []},MatchPart {_matchedString = "hoge02", _captures = []}]++## Example: target1 ^.. regex [r|hoge[0-9]+|] . matchedString+["hoge00","hoge01","hoge02"]++## Example: target1 ^? regex [r|hoge[0-9]+|] . index 1 . matchedString+Just "hoge01"++## Example: target1 ^? regex [r|hoge[0-9]+|] . index 3 . matchedString+Nothing++## Example: target1 & regex [r|hoge[0-9]+|] . matchedString .~ "HOGE"+"HOGE fuga HOGE neko HOGE"++## Example: target1 & regex [r|hoge[0-9]+|] .index 1 . matchedString .~ "HOGE"+"hoge00 fuga HOGE neko hoge02"++## Example: target1 & regex [r|hoge[0-9]+|] .index 1 . matchedString %~ (\s -> "<<" ++ s ++ ">>")+"hoge00 fuga <<hoge01>> neko hoge02"+##################+## Target String:+target2 = "<img src=\"/image/shinku0721.jpg\" alt=\"shinku birthday\"><img src=\"/image/shinku141.jpg\">"++## Example: target2 ^.. regex [r|<img src="([^"]+)"[^>]*>|] . captures . traversed . index 0+["/image/shinku0721.jpg","/image/shinku141.jpg"]++-}++module Main where++import Control.Lens+import Text.Regex.Lens+import Text.Regex.Posix+import Text.Regex.Quote++main :: IO ()+main = do+    let target1 = "hoge00 fuga hoge01 neko hoge02"+    putStrLn "##################"+    putStrLn "## Target String:"+    putStrLn $ "target1 = " ++ show target1++    putStrLn "\n## Example: target1 ^.. regex [r|hoge[0-9]+|]"+    print $ target1 ^.. regex [r|hoge[0-9]+|]++    putStrLn "\n## Example: target1 ^.. regex [r|hoge[0-9]+|] . matchedString"+    print $ target1 ^.. regex [r|hoge[0-9]+|] . matchedString++    putStrLn "\n## Example: target1 ^? regex [r|hoge[0-9]+|] . index 1 . matchedString"+    print $ target1 ^? regex [r|hoge[0-9]+|] . index 1 . matchedString++    putStrLn "\n## Example: target1 ^? regex [r|hoge[0-9]+|] . index 3 . matchedString"+    print $ target1 ^? regex [r|hoge[0-9]+|] . index 3 . matchedString++    putStrLn "\n## Example: target1 & regex [r|hoge[0-9]+|] . matchedString .~ \"HOGE\""+    print $ target1 & regex [r|hoge[0-9]+|] . matchedString .~ "HOGE"++    putStrLn "\n## Example: target1 & regex [r|hoge[0-9]+|] .index 1 . matchedString .~ \"HOGE\""+    print $ target1 & regex [r|hoge[0-9]+|] . index 1 . matchedString .~ "HOGE"++    putStrLn "\n## Example: target1 & regex [r|hoge[0-9]+|] .index 1 . matchedString %~ (\\s -> \"<<\" ++ s ++ \">>\")"+    print $ target1 & regex [r|hoge[0-9]+|] . index 1 . matchedString %~ (\s -> "<<" ++ s ++ ">>")++    let target2 = "<img src=\"/image/shinku0721.jpg\" alt=\"shinku birthday\"><img src=\"/image/shinku141.jpg\">"+    putStrLn "##################"+    putStrLn "## Target String:"+    putStrLn $ "target2 = " ++ show target2++    putStrLn "\n## Example: target2 ^.. regex [r|<img src=\"([^\"]+)\"[^>]*>|] . captures . traversed . index 0"+    print $ target2 ^.. regex [r|<img src="([^"]+)"[^>]*>|] . captures . traversed . index 0
+ src/Text/Regex/Lens.hs view
@@ -0,0 +1,125 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE TemplateHaskell #-}++module Text.Regex.Lens+       ( MatchPart(..)+       , matchedString+       , captures++       , regex+       , regex'+       , matched+       , matched'+       ) where++#if !MIN_VERSION_base(4,8,0)+import Control.Applicative+import Data.Monoid+#endif++import Control.Lens+import qualified Data.Array as A+import Text.Regex.Base++-- $setup+-- >>> import Text.Regex.Quote+-- >>> import Text.Regex.Posix+-- >>> :set -XQuasiQuotes++type RegexResult text = [RegexPartialResult text]+type RegexPartialResult text = Either text (MatchPart text)++data MatchPart text = MatchPart+    { _matchedString :: text+    , _captures :: [text]+    } deriving Show+makeLensesFor [("_matchedString", "matchedString")] ''MatchPart+makeLensesWith (lensRulesFor [("_captures", "captures")] & generateUpdateableOptics .~ False) ''MatchPart++-- | An indexed Traversal for matched part with regexp.+--+-- >>> "foo bar baz" ^? regex [r|b.*r|]+-- Just (MatchPart {_matchedString = "bar", _captures = []})+--+-- >>> "foo bar baz" ^? regex [r|hoge|]+-- Nothing+--+-- You can access to the matched string by using `matchedString`:+--+-- >>> "foo bar baz" ^? regex [r|b.*r|] . matchedString+-- Just "bar"+--+-- Multiple result:+--+-- >>> "foo bar baz" ^.. regex [r|b[^ ]+|] . matchedString+-- ["bar","baz"]+--+-- Replace:+--+-- >>> "foo bar baz" & regex [r|b[^ ]+|] . matchedString .~ "nya"+-- "foo nya nya"+--+-- Indexing:+--+-- >>> "foo bar baz" ^.. regex [r|b[^ ]+|] . index 1 . matchedString+-- ["baz"]+--+-- >>> "foo bar baz" & regex [r|b[^ ]+|] . index 1 . matchedString .~ "nya"+-- "foo bar nya"+--+-- Captures:+--+-- >>> "foo00 bar01 baz02" ^.. regex [r|([a-z]+)([0-9]+)|] . captures+-- [["foo","00"],["bar","01"],["baz","02"]]+--+-- >>> "foo00 bar01 baz02" ^.. regex [r|([a-z]+)([0-9]+)|] . captures . traversed . index 1+-- ["00","01","02"]+--+-- /Note/: This is /not/ a legal Traversal, unless you are very careful not to invalidate the predicate on the target.+-- For example, if you replace the matched part with a string which is not match with the regex, the second 'Traversal' law is violated.+--+-- @+-- let l = regex [r|t.*t|] . matchedString+-- 'Control.Lens.Setter.over' l (++ "peta") '.' 'Control.Lens.Setter.over' l (++ "nya") '/=' 'Control.Lens.Setter.over' l ((++ "peta") . (++ "nya"))+-- 'Control.Lens.Setter.over' l (++ "put") '.' 'Control.Lens.Setter.over' l (++ "hot") '==' 'Control.Lens.Setter.over' l ((++ "put") . (++ "hot"))+-- @+regex :: (RegexLike regex text, Monoid text)+      => regex -- ^ compiled regular expression+      -> IndexedTraversal' Int text (MatchPart text)+regex pat = regex' pat . matched++regex' :: (RegexLike regex text, Monoid text) => regex -> Lens' text (RegexResult text)+regex' pat f target = fromRegexResult <$> f (toRegexResult pat target)++matched :: (Indexable Int p, Applicative f)+        => p (MatchPart text) (f (MatchPart text)) -> RegexResult text -> f (RegexResult text)+matched = conjoined matched' (indexing matched')++matched' :: Traversal' (RegexResult text) (MatchPart text)+matched' f target = go target+  where+    go [] = pure []+    go ((Left x):xs) = ((Left x):) <$> go xs+    go ((Right x):xs) = (:) <$> (Right <$> f x) <*> go xs++toRegexResult :: RegexLike regex text => regex -> text -> (RegexResult text)+toRegexResult pat target = go 0 $ matchAll pat target+  where+    go pos [] = [Left (after pos target)]+    go pos (m:ms) =+        if posDiff > 0+            then Left (extract (pos, posDiff) target) : cont+            else cont+      where+        (pos', len) = m A.! 0+        posDiff = pos' - pos+        (ms0:mss) = map (flip extract target) $ A.elems m+        cont = Right (MatchPart ms0 mss) : go (pos' + len) ms++fromRegexResult :: Monoid text => (RegexResult text) -> text+fromRegexResult = mconcat . map toStr+  where+    toStr (Right (MatchPart s _)) = s+    toStr (Left s) = s
+ src/Text/Regex/Quote.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE TemplateHaskell #-}++module Text.Regex.Quote+       ( r+       )+       where++import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Text.Regex.Base++-- | Generate compiled regular expression.+--+-- This QuasiQuote is shorthand of /makeRegex with type annotations/:+--+-- @+-- [r|hogehoge|] == (makeRegex ("hogehoge" :: String) :: Regex)+-- @+--+-- The /Regex/ type signature in the above example, is the type+-- which is named as /Regex/ in this translation unit.+-- Therefore, you can choose Regex type by changing imports.+--+-- For example, the /exp/ variable in the below example has the type of Text.Regex.Posix.Regex:+--+-- @+-- import Text.Regex.Posix (Regex)+-- exp = [r|hoge|]+-- @+--+-- and, the /exp/ variable in below example has the type of Text.Regex.PCRE.Regex:+--+-- @+-- import Text.Regex.PCRE (Regex)+-- exp = [r|hoge|]+-- @+r :: QuasiQuoter+r = QuasiQuoter+    { quoteExp = \str -> do+           mk <- [|makeRegex|]+           return $ mk `AppE` (LitE (StringL str) `SigE` ConT ''String) `SigE` ConT (mkName "Regex")+    , quotePat = error "quotePat is not defined"+    , quoteType = error "quoteType is not defined"+    , quoteDec = error "quoteDec is not defined"+    }
+ tests/doctests.hs view
@@ -0,0 +1,29 @@+module Main where++import Test.DocTest+import System.Directory+import System.FilePath+import Data.List+import Control.Monad+import Control.Applicative++main :: IO ()+main = do+    sources <- findSources+    doctest $+        [ "-isrc"+        , "-idist/build/autogen"+        , "-optP-include"+        , "-optPdist/build/autogen/cabal_macros.h"+        ]+        ++ sources++findSources :: IO [FilePath]+findSources = filter (isSuffixOf ".hs") <$> go "src"+  where+    go dir = do+      (dirs, files) <- listFiles dir+      (files ++) . concat <$> mapM go dirs+    listFiles dir = do+      entries <- map (dir </>) . filter (`notElem` [".", ".."]) <$> getDirectoryContents dir+      (,) <$> filterM doesDirectoryExist entries <*> filterM doesFileExist entries