This is a bug in the re-seq implementation in ClojureScript:
(defn re-seq
"Returns a lazy sequence of successive matches of re in s."
[re s]
(let [match-data (re-find re s)
match-idx (.search s re)
match-str (if (coll? match-data) (first match-data) match-data)
post-idx (+ match-idx (max 1 (count match-str)))
post-match (subs s post-idx)]
(when match-data (lazy-seq (cons match-data (when (<= post-idx (count s)) (re-seq re post-match)))))))
The issue comes in where it recurses into re-seq
with the remainder of the string, doing this means that ^[a-f]
will match again against this new, shorter, string.
One solution is to make your regex sticky:
(js/RegExp. #"^." "y")
This makes subsequent uses of your regex aware of previous matches, do note that you will need to make sure you place this code carefully as it will need to be created at the correct location, it can't be global! If it were global you would run into weird state issues like this one:
(let [re (js/RegExp. #"^." "y")]
[(re-seq re "cccc")
(re-seq re "abbb")])
;; => [("c" "c") nil]
(which I cannot explain at all!)
An alternative implementation of re-seq
might make this initial clone for you:
(defn re-seq2
"Returns a lazy sequence of successive matches of re in s."
[re s]
(let [re-seq* (fn re-seq* [re s]
(let [match-data (re-find re s)
match-idx (.search s re)
match-str (if (coll? match-data) (first match-data) match-data)
post-idx (+ match-idx (max 1 (count match-str)))
post-match (subs s post-idx)]
(when match-data (lazy-seq (cons match-data (when (<= post-idx (count s)) (re-seq* re post-match)))))))]
(re-seq* (js/RegExp. re "y") s)))
(let [re #"^."]
[(re-seq2 re "cccc")
(re-seq2 re "abbb")])
;; => [("c") ("a")]