Regex-related utility functions | (ns metabase.util.regex (:require [clojure.string :as str])) |
Wrap regex | (defn non-capturing-group [pattern] (re-pattern (format "(?:%s)" pattern))) |
Combine regex | (defn re-or [patterns] (non-capturing-group (str/join "|" (map non-capturing-group patterns)))) |
Make regex | (defn re-optional [pattern] (str (non-capturing-group pattern) "?")) |
Make regex | (defn re-negate [pattern] (str "(?!" pattern ")")) |
(defmulti ^:private rx-dispatch {:arglists '([listt])} first) | |
(declare rx*) | |
(defmethod rx-dispatch :default [x] x) | |
(defmethod rx-dispatch :? [[_ & args]] (re-optional (rx* (into [:and] args)))) | |
(defmethod rx-dispatch :or [[_ & args]] (re-or (map rx* args))) | |
(defmethod rx-dispatch :and [[_ & args]] (apply str (map rx* args))) | |
(defmethod rx-dispatch :not [[_ arg]] (re-negate (rx* arg))) | |
(defn- rx* [x] (if (seqable? x) (rx-dispatch x) x)) | |
A quick-and-dirty port of the Emacs Lisp
TODO -- instead of memoizing this, why not just do this as a macro and do it at macroexpansion time? Weird. | (def ^{:doc :arglists '([x] [x & more])} rx (memoize (fn rx ;; (rx [:and [:or "Cam" "can"] [:? #"\s+"] #"\d+"]) ;; -> #\"(?:(?:Cam)|(?:can))(?:\s+)?\d+\" ([x] (re-pattern (rx* x))) ([x & more] (rx (into [:and x] more)))))) |