Last active
December 16, 2015 11:58
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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