Skip to content

Instantly share code, notes, and snippets.

@taylorwood
Created October 8, 2017 00:56
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save taylorwood/232129ccd3cb809281fea591d46f1b8a to your computer and use it in GitHub Desktop.
Save taylorwood/232129ccd3cb809281fea591d46f1b8a to your computer and use it in GitHub Desktop.
Recursive clojure.spec for grouped/nested infix expressions http://taylorwood.github.io/2017/10/04/clojure-spec-boolean.html
(ns playground.test
(:require [clojure.spec.alpha :as s]
[clojure.spec.gen.alpha :as sgen]
[clojure.spec.test.alpha :as stest]
[clojure.string :as cs]
[clojure.walk :as walk]))
(def op-keys #{:and :or})
(s/def ::expression
(s/and keyword?
#(not (contains? op-keys %))))
(s/def ::subgroup (s/or :g ::group :e ::expression))
(s/def ::group
(s/cat :head ::subgroup
:tail (s/* (s/cat :op op-keys :clause ::subgroup))))
(s/conform ::group [:x])
(s/conform ::group [:x :y])
(s/conform ::group [:x :and :y])
(s/conform ::group [:x :and :y :or]) ;; hanging operator, no good
(s/conform ::group [:x :and :y :or :z])
(s/conform ::group [:x :and :xx :and :yy :and [:y :or :z :or [:foo :and :bar]]])
(s/conform ::group [[:x :or :y] :and :x])
(s/conform ::group [[:x
:or :y
:or :z
:or [:q :and :z]]
:and :x
:and :y
:and :z
:and [:x
:or :y
:or :z
:or [:q :and :z]]])
(sgen/sample (s/gen ::group))
(defn clause-str [clause]
(walk/postwalk
(fn [elem]
(if (coll? elem)
(format "(%s)" (cs/join " " (map name elem)))
elem))
clause))
(s/fdef clause-str
:args (s/cat :clause (s/spec ::group)) ;; important to wrap regex spec in s/spec here
:ret string?)
(stest/instrument `clause-str)
(clause-str [[:x :or :y] :and :x])
(map clause-str (sgen/sample (s/gen ::group)))
(binding [s/*recursion-limit* 1] ;; limit generative recursion, avoid long run time
(stest/check `clause-str))
(stest/summarize-results *1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment