您可以硬拉spec 这样做。否则,您可能需要查看解析库,例如 Instaparse。
Spec 并不特定于文本解析,并且缺少一些regice 的便利性,但是您可以使用您在spec/def 中定义的组件来构建您的解析器:
(require '[clojure.spec.alpha :as spec])
(defn flatten-seqs [dst src]
(if (sequential? src)
(reduce flatten-seqs dst src)
(conj dst src)))
(defmacro cat-seq [& nested-seqs]
(let [flat (flatten-seqs [] (eval `[~@nested-seqs]))]
`(spec/cat ~@(interleave
(->> flat count range (map #(keyword (str "k" %))))
flat))))
(spec/def :demo/dec-digit (set (vec "0123456789")))
(spec/def :demo/hex-digit (set (vec "0123456789abcdef")))
(spec/def :demo/uuid (cat-seq (repeat 8 :demo/hex-digit)
#{\-}
(repeat 4 :demo/dec-digit)
#{\-}
(repeat 4 :demo/dec-digit)
#{\-}
(repeat 4 :demo/dec-digit)
#{\-}
(repeat 12 :demo/hex-digit)))
(spec/def :demo/ssn (cat-seq (repeat 3 :demo/dec-digit)
#{\-}
(repeat 2 :demo/dec-digit)
#{\-}
(repeat 4 :demo/dec-digit)))
(spec/def :demo/phone (cat-seq (repeat 3 :demo/dec-digit)
#{\-}
(repeat 3 :demo/dec-digit)
#{\-}
(repeat 4 :demo/dec-digit)))
(spec/def :demo/input (spec/alt :uuid :demo/uuid
:ssn :demo/ssn
:phone :demo/phone))
(spec/def :demo/inputs (spec/* :demo/input))
使用方法如下:
(def uuid "cafebabe-0867-5309-0666-0123456789ff")
(def ssn "333-22-5464")
(def phone "123-555-6789")
(spec/valid? :demo/inputs (vec (str ssn phone)))
;; => true
(spec/valid? :demo/inputs (vec (str ssn phone "mjao")))
;; => false
请注意,如果有许多不同的解析替代方案,它们会被标记为它们的类型,例如:ssn,使用conform时:
(spec/conform :demo/inputs (vec (str ssn ssn)))
;; => [[:ssn {:k8 \4, :k5 \2, :k7 \5, :k9 \6, :k6 \-, :k0 \3, :k10 \4, :k3 \-, :k4 \2, :k1 \3, :k2 \3}] [:ssn {:k8 \4, :k5 \2, :k7 \5, :k9 \6, :k6 \-, :k0 \3, :k10 \4, :k3 \-, :k4 \2, :k1 \3, :k2 \3}]]