(defmacro regex
"Given an expression that returns a string returns a function
that given a string returns nil if pattern did not match
and a map of named groups if pattern matches."
[regex-string-expression]
(let [regex-string (eval regex-string-expression)
re-group-regex #"(?<!\\)(?:\\\\)*\((?:\?<(\w+)>|[^?])"
string-arg (gensym)
matcher-arg (gensym)
pattern (re-pattern regex-string)
group-names (mapv second (re-seq re-group-regex regex-string))
mappings (apply concat (map-indexed (fn [i group-name]
`(~group-name (.group ~matcher-arg ~(inc i))))
group-names))]
`(fn [~string-arg]
(let [~matcher-arg (re-matcher ~pattern ~string-arg)]
(if (.matches ~matcher-arg)
{~
@mappings ~
@mappings}
nil)))))