├── .gitignore ├── README.md ├── doc └── intro.md ├── project.clj ├── src └── clojureverbalexpressions │ └── core.clj └── test └── clojureverbalexpressions └── core_test.clj /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /lib 3 | /classes 4 | /checkouts 5 | pom.xml 6 | pom.xml.asc 7 | *.jar 8 | *.class 9 | *.swp 10 | .lein-deps-sum 11 | .lein-failures 12 | .lein-plugins 13 | .lein-repl-history 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ClojureVerbalExpressions 2 | ======================= 3 | 4 | ## Installation 5 | Add `[clojureverbalexpressions "0.2.1"]` too your project.clj file. 6 | 7 | ## Usage 8 | ```clojure 9 | user=> (require '[clojureverbalexpressions :as verex]) 10 | nil 11 | user=> (def verbal verex/VerEx) 12 | #'user/verbal 13 | ``` 14 | ## Examples 15 | 16 | ### Testing if we have a valid URL 17 | ```clojure 18 | ;; Create an example of how to test for correctly formed URLs 19 | 20 | (def tester (-> VerEx 21 | (start-of-line) 22 | (find "http") 23 | (maybe "s") 24 | (find "://") 25 | (maybe "www.") 26 | (anything-but " ") 27 | (end-of-line))) 28 | 29 | ;; Create an example URL 30 | (def test-url "https://www.google.com") 31 | 32 | ;; Test if the URL is valid 33 | (if (match tester test-url) 34 | (println "Valid URL")) 35 | 36 | ;; Print the generated regex 37 | (println (source tester)) 38 | ;; => ^(http)(s)?(\:\/\/)(www\.)?([^\ ]*)$ 39 | ``` 40 | ### Replacing strings 41 | ```clojure 42 | ;; Create a test string 43 | (def replace-me "Replace bird with a duck") 44 | 45 | ;; Create an expression that looks for the word "bird" 46 | (def expression (find VerEx "bird")) 47 | 48 | ;; Execute the expression in VerEx 49 | (def result-verex (replace expression replace-me "duck")) 50 | (println result-verex) 51 | ``` 52 | ### "Shorthand" for string replace 53 | ```clojure 54 | ;; (def result (clojure.string/replace "We have a red house" #"red" "blue") 55 | (def result (replace (find VerEx "red") "We have a red house" "blue")) 56 | (println result) 57 | ``` 58 | ## Other implementations 59 | You can view all implementations on [VerbalExpressions.github.io](http://VerbalExpressions.github.io) 60 | -------------------------------------------------------------------------------- /doc/intro.md: -------------------------------------------------------------------------------- 1 | # Introduction to clojureverbalexpressions 2 | 3 | TODO: write [great documentation](http://jacobian.org/writing/great-documentation/what-to-write/) 4 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject clojureverbalexpressions "0.2.1" 2 | :description "Port of the Verbal Expressions lib for Clojure" 3 | :url "http://github.com/Foxboron/clojureverbalexpressions" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.5.1"] 7 | [midje "1.5.1"]] 8 | :profiles {:dev {:dependencies [[midje "1.5.1"]]}} 9 | :scm {:name "git" 10 | :url "https://github.com/VerbalExpressions/ClojureVerbalExpressions"}) 11 | -------------------------------------------------------------------------------- /src/clojureverbalexpressions/core.clj: -------------------------------------------------------------------------------- 1 | (ns clojureverbalexpressions.core 2 | (:require [clojure.string :as s]) 3 | (:refer-clojure :exclude [find replace range or])) 4 | 5 | (defrecord VerbalExpression [source modifier prefix suffix pattern]) 6 | 7 | ;; The VE should start matching on all lines. 8 | (def VerEx (VerbalExpression. "" "m" "" "" #"(?m)")) 9 | 10 | (defn replace [{regex :pattern} string replacement] 11 | (s/replace string regex replacement)) 12 | 13 | (defn regex [{regex :pattern}] 14 | regex) 15 | 16 | (defn source [{source :source}] 17 | source) 18 | 19 | (defn match [{regex :pattern} string] 20 | (if (nil? (re-find regex string)) 21 | false 22 | true)) 23 | 24 | (defn sanitize [string] 25 | (s/replace string #"([.$*+?^()\[\]{}\\|])" "\\\\$1")) 26 | 27 | (defn add [{:keys [prefix source suffix modifier] :as v} value] 28 | ;; Debuging proposes 29 | ;;(println (str "(?" modifier ")" prefix source value suffix)) 30 | (assoc v 31 | :pattern (re-pattern (str "(?" modifier ")" prefix source value suffix)) 32 | :source (str source value))) 33 | 34 | (defn anything [verex] 35 | (add verex "(?:.*)")) 36 | 37 | (defn anything-but [verex value] 38 | (add verex (str "(?:[^" (sanitize value) "]*)"))) 39 | 40 | (defn something [v value] 41 | (add v "(?:.+)")) 42 | 43 | (defn something-but [v value] 44 | (add v (str "(?:[^" (sanitize value) "]+)"))) 45 | 46 | (defn end-of-line [{suffix :suffix :as verex}] 47 | (add (assoc-in verex [:suffix] (str suffix "$")) "")) 48 | 49 | (defn maybe [verex value] 50 | (add verex (str "(?:" (sanitize value) ")?"))) 51 | 52 | (defn start-of-line [{prefix :prefix :as verex}] 53 | (add (assoc-in verex [:prefix] (str "^" prefix)) "")) 54 | 55 | (defn find [verex value] 56 | (add verex (str "(?:" (sanitize value) ")"))) 57 | 58 | (def then find) 59 | 60 | (defn any [verex value] 61 | (add verex (str "[" (sanitize value) "]"))) 62 | 63 | (def any-of any) 64 | 65 | (defn line-break [verex] 66 | (add verex "(?:(?:\\n)|(?:\\r\\n))")) 67 | 68 | (def br line-break) 69 | 70 | (defn range [verex & args] 71 | (let [from-tos (partition 2 (for [i args] (sanitize i)))] 72 | (add verex (str "([" (s/join "" (for [i from-tos] (s/join "-" i))) "])")))) 73 | 74 | (defn tab [verex] 75 | (add verex "\t")) 76 | 77 | (defn word [verex] 78 | (add verex "\\w+")) 79 | 80 | (defn or 81 | ([{:keys [prefix suffix] :as v}] 82 | (-> (assoc v :prefix (str prefix "(?:") :suffix (str ")" suffix)) 83 | (add ")|(?:"))) 84 | 85 | ([v value] 86 | (then (or v) value))) 87 | 88 | (defn add-modifier [{modifier :modifier :as v} m] 89 | (-> (assoc v :modifier (str m modifier)) 90 | (add ""))) 91 | 92 | (defn remove-modifier [{modifier :modifier :as v} m] 93 | (-> (assoc v :modifier (s/replace modifier m "")) 94 | (add ""))) 95 | 96 | (defn multiple [v value] 97 | (let [value (sanitize value)] 98 | (add v (case (last value) (\* \+) value (str value "+"))))) 99 | 100 | (defn with-any-case 101 | ([v] 102 | (with-any-case v true)) 103 | ([v b] 104 | (if b (add-modifier v "i") (remove-modifier v "i")))) 105 | 106 | (defn search-one-line 107 | ([v] 108 | (search-one-line v true)) 109 | ([v b] 110 | ;; As the VE does matches on all lines, we need to remove the 111 | ;; modifier when we want to select on one line. 112 | (if b (remove-modifier v "m") (add-modifier v "m")))) 113 | 114 | (defn begin-capture [{suffix :suffix :as v}] 115 | (-> (assoc v :suffix (str suffix ")")) 116 | (add "("))) 117 | 118 | (defn end-capture [{suffix :suffix :as v}] 119 | (-> (assoc v :suffix (subs suffix 0 (dec (count suffix)))) 120 | (add ")"))) 121 | -------------------------------------------------------------------------------- /test/clojureverbalexpressions/core_test.clj: -------------------------------------------------------------------------------- 1 | (ns clojureverbalexpressions.core-test 2 | (:use midje.sweet) 3 | (:require [clojureverbalexpressions.core :as verex])) 4 | 5 | (fact "verex renders as strings" 6 | (def v (-> verex/VerEx (verex/add "^$"))) 7 | (verex/source v) => "^$") 8 | 9 | (fact "matches characters in range" 10 | (def v (-> verex/VerEx (verex/start-of-line) (verex/range "a" "c"))) 11 | (verex/match v "a") => true 12 | (verex/match v "b") => true 13 | (verex/match v "c") => true) 14 | 15 | (fact "does not match characters outside of range" 16 | (def v (-> verex/VerEx (verex/start-of-line) (verex/range "a" "b" "X" "Z"))) 17 | (doseq [i ["a" "b"]] 18 | (verex/match v i) => true) 19 | (doseq [i ["X" "Y" "Z"]] 20 | (verex/match v i) => true)) 21 | 22 | (fact "should not match chars outside of range" 23 | (def v (-> verex/VerEx (verex/start-of-line) (verex/range "a" "b" "X" "Z"))) 24 | (verex/match v "c") => false 25 | (verex/match v "W") => false) 26 | 27 | 28 | (fact "should match start of line" 29 | (def v (-> verex/VerEx (verex/start-of-line))) 30 | (verex/match v "text ") => true) 31 | 32 | (fact "should match end of line" 33 | (def v (-> verex/VerEx (verex/start-of-line) (verex/end-of-line))) 34 | (verex/match v "") => false) 35 | 36 | (fact "matches anything" 37 | (def v (-> verex/VerEx (verex/start-of-line) (verex/anything) (verex/end-of-line))) 38 | (verex/match v "!#¤%&/()=?`-,'*|") => true) 39 | 40 | (fact "should match anything except the specefied element when not found" 41 | (def v (-> verex/VerEx (verex/start-of-line) (verex/anything-but "X") (verex/end-of-line))) 42 | (verex/match v "Y Files") => true) 43 | 44 | (fact "should match anything expect specefied element when found" 45 | (def v (-> verex/VerEx (verex/start-of-line) (verex/anything-but "X") (verex/end-of-line))) 46 | (verex/match v "VerEX") => false) 47 | 48 | (fact "finds element" 49 | (def v (-> verex/VerEx (verex/start-of-line) (verex/find "Wally") (verex/end-of-line))) 50 | (verex/match v "Wally") => true) 51 | 52 | (fact "dosnt find element" 53 | (def v (-> verex/VerEx (verex/start-of-line) (verex/find "Wally") (verex/end-of-line))) 54 | (verex/match v "Wall-e") => false) 55 | 56 | (fact "match when the element i maybe there" 57 | (def v (-> verex/VerEx (verex/start-of-line) (verex/find "Clojure1.") (verex/maybe "5") (verex/end-of-line))) 58 | (verex/match v "Clojure1.5") => true) 59 | 60 | (fact "match when the the possible element is gone" 61 | (def v (-> verex/VerEx (verex/start-of-line) (verex/find "Clojure1.") (verex/maybe "5") (verex/end-of-line))) 62 | (verex/match v "Clojure1.") => true) 63 | 64 | (fact "should match on any when element is found" 65 | (def v (-> verex/VerEx (verex/start-of-line) (verex/any "Q") (verex/anything) (verex/end-of-line))) 66 | (verex/match v "Query") => true) 67 | 68 | (fact "should not match on any when element is not found" 69 | (def v (-> verex/VerEx (verex/start-of-line) (verex/any "Q") (verex/anything) (verex/end-of-line))) 70 | (verex/match v "W") => false) 71 | 72 | (fact "should match when line break is there" 73 | (def v (-> verex/VerEx (verex/start-of-line) (verex/anything) (verex/line-break) (verex/anything) (verex/end-of-line))) 74 | (verex/match v "Marco \r\n Polo") => true) 75 | 76 | (fact "should not match with line breaks are none" 77 | (def v (-> verex/VerEx (verex/start-of-line) (verex/anything) (verex/line-break) (verex/anything) (verex/end-of-line))) 78 | (verex/match v "Marco Polo") => false) 79 | 80 | (fact "Should match when tab is present" 81 | (def v (-> verex/VerEx (verex/start-of-line) (verex/anything) (verex/tab) (verex/end-of-line))) 82 | (verex/match v " \t") => true) 83 | 84 | (fact "should not match when tab is missing" 85 | (def v (-> verex/VerEx (verex/start-of-line) (verex/anything) (verex/tab) (verex/end-of-line))) 86 | (verex/match v "Nope") => false) 87 | 88 | (fact "match one word" 89 | (def v (-> verex/VerEx (verex/start-of-line) (verex/word) (verex/end-of-line))) 90 | (verex/match v "OneWord") => true) 91 | 92 | (fact "not match when two words are present" 93 | (def v (-> verex/VerEx (verex/start-of-line) (verex/word) (verex/end-of-line))) 94 | (verex/match v "Two Words") => false) 95 | 96 | (fact "match when or is fulfilled" 97 | (def v (-> verex/VerEx (verex/start-of-line) (verex/anything) (verex/find "G") (verex/or) (verex/find "h") (verex/end-of-line))) 98 | (verex/match v "Github") => true) 99 | 100 | (fact "should not match on upper case when or condition not fulfilled" 101 | (def v (-> verex/VerEx (verex/start-of-line) (verex/anything) (verex/find "G") (verex/or) (verex/find "h") (verex/end-of-line))) 102 | (verex/match v "Bitbucket") => false) 103 | 104 | (fact "should match on upper case when lower case is given and any case is true" 105 | (def v (-> verex/VerEx (verex/start-of-line) (verex/find "THOR") (verex/end-of-line) (verex/with-any-case true))) 106 | (verex/match v "thor")) 107 | 108 | (fact "should match multiple lines" 109 | (def v (-> verex/VerEx (verex/start-of-line) (verex/anything) (verex/find "Pong") (verex/anything) (verex/end-of-line) (verex/search-one-line false))) 110 | (verex/match v "Ping \n Pong \n Ping") => true) 111 | 112 | (fact "should match email adress" 113 | (def v (-> verex/VerEx (verex/start-of-line) (verex/word) (verex/then "@") (verex/word) (verex/then ".") (verex/word) (verex/end-of-line))) 114 | (verex/match v "mail@mail.com") => true) 115 | 116 | (fact "should match url" 117 | (def v (-> verex/VerEx (verex/start-of-line) (verex/then "http") (verex/maybe "s") (verex/then "://") (verex/maybe "www.") (verex/word) (verex/then ".") (verex/word) (verex/maybe "/") (verex/end-of-line))) 118 | (verex/match v "https://google.com") => true) 119 | 120 | 121 | --------------------------------------------------------------------------------