Skip to content

Instantly share code, notes, and snippets.

@jeroenvandijk
Created June 16, 2026 17:07
Show Gist options
  • Select an option

  • Save jeroenvandijk/d0cbc94552025a189a1ae9fc8916223a to your computer and use it in GitHub Desktop.

Select an option

Save jeroenvandijk/d0cbc94552025a189a1ae9fc8916223a to your computer and use it in GitHub Desktop.
Sandboxed re-matches in Babashka Sci on the JVM
;; Try with deps-try org.babashka/sci 0.13.52 (see https://github.com/eval/deps-try)
;; The following takes 6 seconds on my machine
(time
(re-matches #"^(.*a){20}$" (str (apply str (repeat 28 \a)) "!")))
;; With Sci's new :interrupt-fn feature we can make regexp's safer:
(require '[sci.ctx-store :as store]
'[sci.core :as sci]
'[sci.interrupt :as interrupt])
(deftype InterruptibleCharSequence [^CharSequence s interrupt]
CharSequence
(length [_]
(.length s))
(charAt [_ i]
(interrupt)
(.charAt s i))
(subSequence [_ start end]
(InterruptibleCharSequence. (.subSequence s start end) interrupt))
Object
(toString [_]
(.toString s)))
(defn sci-re-matches [re s]
(re-matches re
(if-let [interrupt (interrupt/get-interrupt-fn (store/get-ctx))]
(InterruptibleCharSequence. s interrupt)
s)))
(defn limit [n]
(let [counter (atom 0)]
(fn [] (when (> (swap! counter inc) n)
(throw (ex-info "interrupted" {:type :interrupt}))))))
;; This will return quickly after reach 500 iterations
(let [ctx (sci/init
{:namespaces {'clojure.core (assoc interrupt/clojure-core 're-matches sci-re-matches)}
:interrupt-fn (limit 500)})]
(time (sci/eval-form ctx '(re-matches #"^(.*a){20}$" (str (apply str (repeat 28 \a)) "!")))))
;; Whereas the following will not be interrupted and run for around 25 seconds
(let [ctx (sci/init
{:namespaces {'clojure.core (assoc interrupt/clojure-core 're-matches sci-re-matches)}
:interrupt-fn (limit Long/MAX_VALUE)})]
(time (sci/eval-form ctx '(re-matches #"^(.*a){20}$" (str (apply str (repeat 28 \a)) "!")))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment