├── .gitignore ├── AUTHORS ├── LICENSE ├── README.md ├── Setup.hs ├── Text └── Regex │ └── VerbalExpressions.hs ├── examples.hs └── verbalexpressions.cabal /.gitignore: -------------------------------------------------------------------------------- 1 | *.hi 2 | *.o 3 | examples 4 | dist 5 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Original author and maintainer: 2 | 3 | whackashoe 4 | 5 | Other authors: 6 | 7 | klrr 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 whackashoe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HaskellVerbalExpressions 2 | ==================== 3 | 4 | 5 | ## Haskell Regular Expressions made easy 6 | VerbalExpressions is a Haskell library that helps to construct difficult regular expressions. 7 | 8 | This haskell lib is based off of the (original) Javascript [VerbalExpressions](https://github.com/jehna/VerbalExpressions) library by [jehna](https://github.com/jehna/). 9 | 10 | ## Other Implementations 11 | You can see an up to date list of all ports on [VerbalExpressions.github.io](http://VerbalExpressions.github.io). 12 | - [Javascript](https://github.com/jehna/VerbalExpressions) 13 | - [Ruby](https://github.com/VerbalExpressions/RubyVerbalExpressions) 14 | - [C#](https://github.com/VerbalExpressions/CSharpVerbalExpressions) 15 | - [Python](https://github.com/VerbalExpressions/PythonVerbalExpressions) 16 | - [Java](https://github.com/VerbalExpressions/JavaVerbalExpressions) 17 | - [PHP](https://github.com/VerbalExpressions/PHPVerbalExpressions) 18 | - [C++](https://github.com/VerbalExpressions/CppVerbalExpressions) 19 | 20 | ## How to get started 21 | 22 | cd to HaskellVerbalExpressions 23 | 24 | `cabal install verbalexpressions.cabal` 25 | 26 | `ghc Text/Regex/VerbalExpressions/verbalexpressions.hs examples.hs` 27 | 28 | ## Examples 29 | 30 | Here's a couple of simple examples to give an idea of how VerbalExpressions works: 31 | 32 | ### Testing if we have a valid URL 33 | 34 | ```haskell 35 | -- Create an example of how to test for correctly formed URLs 36 | let expr = searchGlobal >>> 37 | startOfLine >>> 38 | find "http" >>> 39 | possibly "s" >>> 40 | find "://" >>> 41 | possibly "www" >>> 42 | anythingBut " " >>> 43 | endOfLine 44 | $ verEx 45 | 46 | -- Use VerEx's test() function to find if it matches 47 | print $ test "http://www.google.com" expr 48 | 49 | -- Outputs the actual expression used: ^(?:http)(?:s)?(?:://)(?:www.)?(?:[^ ]*)$ 50 | print $ expr 51 | ``` 52 | 53 | ### Replacing strings 54 | 55 | ```haskell 56 | -- Create a test string 57 | let replaceMe = "Replace bird with a duck" 58 | -- Create an expression that seeks for word "bird" 59 | let expr2 = find "bird" $ verEx; 60 | 61 | -- Execute the expression 62 | print $ replace replaceMe "duck" expr2 63 | ``` 64 | 65 | ### Shorthand for string replace: 66 | 67 | ```haskell 68 | print $ replace "We have a red house" "blue" . find "red" $ verEx 69 | ``` 70 | 71 | 72 | 73 | 74 | Here you can find the API documentation for Verbal Expressions 75 | 76 | ## Basic usage 77 | Basic usage of Verbal Expressions is through a singleton, called `verEx`, that creates a new expression for you: 78 | 79 | ```haskell 80 | let expr = (all of your terms) $ verEx 81 | ``` 82 | 83 | ##API 84 | 85 | ### Terms 86 | * . anything 87 | * . anythingBut - String 88 | * . something 89 | * . somethingBut - String 90 | * . endOfLine' 91 | * . find - String 92 | * . possibly - String 93 | * . startOfLine 94 | 95 | ### Special characters and groups 96 | * .any - String 97 | * .anyOf - String 98 | * .br 99 | * .lineBreak 100 | * .range - [String] 101 | * .tab 102 | * .word 103 | 104 | ### Modifiers 105 | * .withAnyCase 106 | * .searchOneLine 107 | * .searchGlobal 108 | 109 | ### Functions 110 | * .replace - String(source) String(value) 111 | * .test 112 | 113 | ### Other 114 | * .add - String 115 | * .multiple - String 116 | * .alt 117 | -------------------------------------------------------------------------------- /Setup.hs: -------------------------------------------------------------------------------- 1 | import Distribution.Simple 2 | main = defaultMain 3 | -------------------------------------------------------------------------------- /Text/Regex/VerbalExpressions.hs: -------------------------------------------------------------------------------- 1 | {-| A library to make it easier to work with regular expressions. Based on the (original) 2 | Javascript VerbalExpression library by jehna. 3 | 4 | Here's some examples, first a http validator: 5 | 6 | > let expr = endOfLine 7 | > . anythingBut " " 8 | > . possibly "www" 9 | > . find "://" 10 | > . possibly "s" 11 | > . find "http" 12 | > . startOfLine 13 | > . searchGlobal 14 | > $ verEx 15 | 16 | You can use VerEx's test to find if it matches. 17 | 18 | > test "http://www.google.com" expr 19 | > True 20 | 21 | The actual expression is the following in regexp: 22 | 23 | > ^(?:http)(?:s)?(?:://)(?:www.)?(?:[^ ]*)$ 24 | 25 | Replacing a string. 26 | 27 | > let replaceMe = "Replace bird with a duck" 28 | > let expr2 = find "bird" $ verEx; 29 | > foo = replace replaceMe "duck" expr2 30 | 31 | The above can be shortened. 32 | 33 | > bar = replace "We have a red house" "blue" . find "red" $ verEx 34 | 35 | Basic usage of Verbal Expressions is through a singleton, called 36 | verEx, that compiles it to a regexp. 37 | 38 | > let expr = (all of your terms) $ verEx -} 39 | module Text.Regex.VerbalExpressions 40 | ( verEx 41 | , add 42 | , startOfLine 43 | , startOfLine' 44 | , endOfLine 45 | , endOfLine' 46 | , find 47 | , possibly 48 | , anything 49 | , anythingBut 50 | , something 51 | , somethingBut 52 | , replace 53 | , lineBreak 54 | , br 55 | , tab 56 | , word 57 | , anyOf 58 | , range 59 | , withAnyCase 60 | , withAnyCase' 61 | , searchOneLine 62 | , searchOneLine' 63 | , searchGlobal 64 | , searchGlobal' 65 | , multiple 66 | , alt 67 | , test 68 | ) where 69 | 70 | import Text.Regex.PCRE (getAllTextMatches, (=~)) 71 | import Data.Bits((.|.), (.&.), xor ) 72 | import Data.List(intercalate, isPrefixOf) 73 | 74 | type Flag = Int 75 | 76 | ignorecase :: Flag 77 | ignorecase = 1 78 | 79 | multiline :: Flag 80 | multiline = 2 81 | 82 | global :: Flag 83 | global = 4 84 | 85 | data VerStruct = VerStruct { prefix :: String 86 | , pattern :: String 87 | , suffix :: String 88 | , source :: String 89 | , flags :: Flag 90 | } 91 | instance Show VerStruct where 92 | show = pattern 93 | 94 | verEx :: VerStruct 95 | verEx = VerStruct "" "" "" "" 0 96 | 97 | withAnyCase :: VerStruct -> VerStruct 98 | withAnyCase = withAnyCase' True 99 | 100 | withAnyCase' :: Bool -> VerStruct -> VerStruct 101 | withAnyCase' True v = v { flags = flags v .|. ignorecase } 102 | withAnyCase' False v = v { flags = flags v `xor` ignorecase } 103 | 104 | searchOneLine :: VerStruct -> VerStruct 105 | searchOneLine = searchOneLine' True 106 | 107 | searchOneLine' :: Bool -> VerStruct -> VerStruct 108 | searchOneLine' True v = v { flags = flags v `xor` multiline } 109 | searchOneLine' False v = v { flags = flags v .|. multiline } 110 | 111 | searchGlobal :: VerStruct -> VerStruct 112 | searchGlobal = searchGlobal' True 113 | 114 | searchGlobal' :: Bool -> VerStruct -> VerStruct 115 | searchGlobal' True v = v { flags = flags v .|. global } 116 | searchGlobal' False v = v { flags = flags v `xor` global } 117 | 118 | add :: String -> VerStruct -> VerStruct 119 | add val v = v { pattern = foldl (++) "" [prefix v, source v, val, suffix v] 120 | , source = foldl (++) "" [source v, val] } 121 | 122 | find :: String -> VerStruct -> VerStruct 123 | find val = add ("(?:" ++ val ++ ")") 124 | 125 | possibly :: String -> VerStruct -> VerStruct 126 | possibly val = add ("(?:" ++ val ++ ")?") 127 | 128 | anything :: VerStruct -> VerStruct 129 | anything = add "(?:.*)" 130 | 131 | anythingBut :: String -> VerStruct -> VerStruct 132 | anythingBut val = add ("(?:[^" ++ val ++ "]*)") 133 | 134 | something :: VerStruct -> VerStruct 135 | something = add "(?:.+)" 136 | 137 | somethingBut :: String -> VerStruct -> VerStruct 138 | somethingBut val = add ("(?:[^" ++ val ++ "]+)") 139 | 140 | startOfLine :: VerStruct -> VerStruct 141 | startOfLine = startOfLine' True 142 | 143 | startOfLine' :: Bool -> VerStruct -> VerStruct 144 | startOfLine' True v = add "" v { prefix = "^" } 145 | startOfLine' False v = add "" v { prefix = "" } 146 | 147 | endOfLine :: VerStruct -> VerStruct 148 | endOfLine = endOfLine' True 149 | 150 | endOfLine' :: Bool -> VerStruct -> VerStruct 151 | endOfLine' True v = add "" v { suffix = "$" } 152 | endOfLine' False v = add "" v { suffix = "" } 153 | 154 | lineBreak :: VerStruct -> VerStruct 155 | lineBreak = add "(?:(?:\\n)|(?:\\r\\n))" 156 | 157 | br :: VerStruct -> VerStruct 158 | br = lineBreak 159 | 160 | tab :: VerStruct -> VerStruct 161 | tab = add "(\\t)" 162 | 163 | word :: VerStruct -> VerStruct 164 | word = add "(\\w+)" 165 | 166 | anyOf :: String -> VerStruct -> VerStruct 167 | anyOf val = add ("[" ++ val ++ "]") 168 | 169 | range :: [String] -> VerStruct -> VerStruct 170 | range args = add ("[" ++ buildrange args ++ "]") 171 | where 172 | buildrange xs | length xs >= 2 = head xs ++ "+" ++ head (tail xs) ++ buildrange (tail $ tail xs) 173 | | otherwise = "" 174 | 175 | multiple :: String -> VerStruct -> VerStruct 176 | multiple val v | head val == '*' = add val v 177 | | head val == '+' = add val v 178 | | otherwise = add ('+' : val) v 179 | 180 | alt :: String -> VerStruct -> VerStruct 181 | alt val v = find val (add ")|(" v { prefix = checkPrefix, suffix = checkSuffix }) 182 | where 183 | checkPrefix 184 | | elem '(' (prefix v) = prefix v ++ "(" 185 | | otherwise = prefix v 186 | 187 | checkSuffix 188 | | elem ')' (suffix v) = ")" ++ suffix v 189 | | otherwise = suffix v 190 | 191 | replace :: String -> String -> VerStruct -> String 192 | replace s val v = replacewords (getStringMatches s v) val s 193 | 194 | test :: String -> VerStruct -> Bool 195 | test val v | flags v .&. multiline > 0 = foundMatch val 196 | | otherwise = foundMatch $ foldl (++) "" (split "\n" val) 197 | where 198 | foundMatch :: String -> Bool 199 | foundMatch value | flags v .&. global > 0 = resultOf $ globalSearch value 200 | | otherwise = resultOf $ lineSearch value 201 | 202 | searcher :: String -> [String] 203 | searcher value = getStringMatches value v 204 | 205 | resultOf :: [a] -> Bool 206 | resultOf = not . null 207 | 208 | globalSearch :: String -> [String] 209 | globalSearch = searcher 210 | 211 | lineSearch :: String -> [String] 212 | lineSearch = concatMap searcher . lines 213 | 214 | replacewords :: [String] -> String -> String -> String 215 | replacewords [] _ sen = sen 216 | replacewords (x:xs) replacer sen = replacewords xs replacer (replacefirst x sen) 217 | where 218 | replacefirst :: String -> String -> String 219 | replacefirst w s = head (split w s) 220 | ++ replacer 221 | ++ join w (tail $ split w s) 222 | 223 | getStringMatches :: String -> VerStruct -> [String] 224 | getStringMatches val v = getAllTextMatches $ val =~ pattern v :: [String] 225 | 226 | 227 | --these are from Data.List.Utils 228 | split :: Eq a => [a] -> [a] -> [[a]] 229 | split _ [] = [] 230 | split delim str = 231 | let (firstline, remainder) = breakList (isPrefixOf delim) str 232 | in 233 | firstline : case remainder of 234 | [] -> [] 235 | x -> if x == delim 236 | then [[]] 237 | else split delim 238 | (drop (length delim) x) 239 | breakList :: ([a] -> Bool) -> [a] -> ([a], [a]) 240 | breakList func = spanList (not . func) 241 | 242 | spanList :: ([a] -> Bool) -> [a] -> ([a], [a]) 243 | spanList _ [] = ([],[]) 244 | spanList func list@(x:xs) = 245 | if func list 246 | then (x:ys,zs) 247 | else ([],list) 248 | where (ys,zs) = spanList func xs 249 | 250 | join :: [a] -> [[a]] -> [a] 251 | join = intercalate 252 | -------------------------------------------------------------------------------- /examples.hs: -------------------------------------------------------------------------------- 1 | import Text.Regex.VerbalExpressions 2 | import Control.Arrow 3 | 4 | main :: IO() 5 | main = do 6 | --create an example of how to test for correctly formed URLs 7 | let expr = searchGlobal >>> 8 | startOfLine >>> 9 | find "http" >>> 10 | possibly "s" >>> 11 | find "://" >>> 12 | possibly "www" >>> 13 | anythingBut " " >>> 14 | endOfLine 15 | $ verEx 16 | 17 | -- Use VerEx's test function to find if it matches 18 | print $ test "http://www.google.com" expr 19 | 20 | --Ouputs the actual expression used: ^(?:http)(?:s)?(?:://)(?:www.)?(?:[^ ]*)$ 21 | print $ expr 22 | 23 | -- Create a test string 24 | let replaceMe = "Replace bird with a duck" 25 | -- Create an expression that seeks for word "bird" 26 | let expr2 = find "bird" $ verEx; 27 | 28 | -- Execute the expression 29 | print $ replace replaceMe "duck" expr2 30 | 31 | -- Shorthand string replace 32 | print $ replace "We have a red house" "blue" . find "red" $ verEx 33 | -------------------------------------------------------------------------------- /verbalexpressions.cabal: -------------------------------------------------------------------------------- 1 | name: verbalexpressions 2 | version: 1.0.0.0 3 | synopsis: Regular expressions made easy 4 | description: VerbalExpressions is a Haskell library that helps to construct 5 | difficult regular expressions. 6 | homepage: https://github.com/VerbalExpressions/HaskellVerbalExpressions 7 | license: MIT 8 | license-file: LICENSE 9 | author: whackashoe 10 | copyright: (c) 2013, whackashoe 11 | category: Text 12 | build-type: Simple 13 | cabal-version: >=1.8 14 | 15 | library 16 | exposed-modules: Text.Regex.VerbalExpressions 17 | build-depends: base > 4.6 && < 4.9, 18 | regex-pcre 19 | ghc-options: -Wall 20 | --------------------------------------------------------------------------------