Skip to content

Instantly share code, notes, and snippets.

@Mikhail-Borisov
Last active November 8, 2020 10:51
Show Gist options
  • Save Mikhail-Borisov/137b100aef83ed502f40bc99f22d6d00 to your computer and use it in GitHub Desktop.
Save Mikhail-Borisov/137b100aef83ed502f40bc99f22d6d00 to your computer and use it in GitHub Desktop.
Clojure macro for regex that returns named capture groups as a map
(defmacro mk-named-regex-fn
"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.
Usage: ((regex \"(?<name>[a-zA-Z]+)\") \"John\") => {\"name\" \"John\"}"
[regex-string-expression]
(let [regex-string (eval regex-string-expression)
re-group-regex #"(?<!\\)(?:\\\\)*\((?:\?<(\w+)>|[^?])"
pattern (re-pattern regex-string)
group-names (mapv second (re-seq re-group-regex regex-string))
group-length (count group-names)]
`(fn [s#]
(let [matcher# (re-matcher ~pattern s#)]
(if (.matches matcher#)
(loop [groups# (transient {})
n# 0]
(if (< n# ~group-length)
(let [inc-n# (inc n#)
group-name# (get ~group-names n#)]
(recur (assoc! groups# group-name# (.group matcher# inc-n#))
inc-n#))
(persistent! groups#)))
nil)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment