Skip to content

Instantly share code, notes, and snippets.

@joekarma
Last active December 16, 2015 11:58
Show Gist options
  • Save joekarma/5430913 to your computer and use it in GitHub Desktop.
Save joekarma/5430913 to your computer and use it in GitHub Desktop.
regex-replace-all-to-list can be used to replace parts of a string with arbitrary objects. An example use-case (shown below) is to linkify parts of a string while still escaping the rest for HTML.
(defun scan-to-ranges (regex target-string)
(let ((matches (ppcre:all-matches regex target-string))
(end-pos (length target-string))
(starts-with-match nil)
(all-ranges nil))
(when (not matches)
(return-from scan-to-ranges
(list (cons 0 end-pos))))
(if (zerop (first matches))
(setf starts-with-match t)
(push (cons 0 (first matches)) all-ranges))
(loop :for (range-start range-end next-range-start) :on matches :by #'cddr
:do (push (cons range-start range-end)
all-ranges)
(when (and (not (= range-end end-pos))
next-range-start)
(push (cons range-end next-range-start) all-ranges)))
(when (/= (cdr (first all-ranges))
end-pos)
(push (cons (cdr (first all-ranges)) end-pos)
all-ranges))
(values (nreverse all-ranges)
starts-with-match)))
(defun regex-replace-all-to-list (regex target-string replacement &optional non-match-replacement)
(multiple-value-bind (ranges starts-with-match)
(scan-to-ranges regex target-string)
(let ((currently-replacing starts-with-match))
(mapcar (lambda (range)
(let ((text (subseq target-string
(car range)
(cdr range))))
(cond (currently-replacing
(setf text (etypecase replacement
(string replacement)
(function (funcall replacement text)))))
(non-match-replacement
(setf text (etypecase non-match-replacement
(string non-match-replacement)
(function (funcall non-match-replacement text))))))
(setf currently-replacing (not currently-replacing))
text))
ranges))))
;; Example usage:
(regex-replace-all-to-list "https?:\\/\\/[^\\s>]*"
"This is a test. Let's see if linkifying this will work:
http://www.google.com
Look okay? Okay, how about this? <http://www.something.com>. Great. All done here."
(lambda (v) `(<:a :href ,v (<:ah ,v)))
(lambda (v) `(<:ah ,v)))
#| RESULT
------
((IT.BESE.YACLML.TAGS:AH
"This is a test. Let's see if linkifying this will work:
")
(IT.BESE.YACLML.TAGS:A
:HREF
"http://www.google.com"
(IT.BESE.YACLML.TAGS:AH "http://www.google.com"))
(IT.BESE.YACLML.TAGS:AH "
Look okay? Okay, how about this? <")
(IT.BESE.YACLML.TAGS:A
:HREF
"http://www.something.com"
(IT.BESE.YACLML.TAGS:AH "http://www.something.com"))
(IT.BESE.YACLML.TAGS:AH ">. Great. All done here.")) |#
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment