Skip to content

Instantly share code, notes, and snippets.

@boxxxie
Created February 28, 2020 18:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save boxxxie/fb4aa4c2b4a427175ae599e3a5d97768 to your computer and use it in GitHub Desktop.
Save boxxxie/fb4aa4c2b4a427175ae599e3a5d97768 to your computer and use it in GitHub Desktop.
clojure.core.match+ (adding elixir style guards)
(ns loco.match
(:require [meander.epsilon :as m :refer [match]]
[clojure.walk :as walk]))
;;TODO: add this syntax to defun, and improve arglists
(defn guards-to-map
"transforms a single matching clause (with new :guard syntax) into supported syntax
e.g.: [sym _] :guard [sym [pred1 pred2]] return"
([matcher return] (list matcher return))
([matcher _ guards return]
(let [guards-map (->> guards
(partition 2)
(mapcat (fn [[term guard]]
(if (sequential? term)
(map (fn [term] [term (list term :guard guard)]) term)
[[term (list term :guard guard)]])))
(into {}))
matcher-update (walk/postwalk-replace guards-map matcher)]
(list
(if (empty matcher) ;;handle single-expressions
(into (empty matcher) matcher-update)
matcher-update)
return))))
(defn guard-syntax
"allow for support of 4 arity match syntax
e.g.: [sym _] :guard [sym [pred1 pred2]] return"
[match-rows]
(let [transformed-guards (->> match-rows
(partition 2)
(reduce
(fn [acc matcher-pair]
(let [previous (peek acc)]
(if (and (= 2 (count previous)) (= :guard (second previous)))
(-> acc
pop
(conj (concat previous matcher-pair)))
(conj acc matcher-pair))))
[])
(mapcat (fn [match-tuple]
(apply guards-to-map match-tuple)))
)]
transformed-guards))
(defmacro match+ [match-on & match-rows]
`(match ~match-on
~@(guard-syntax match-rows)))
(ns loco.match-test
(:use loco.match
clojure.test)
(:require [meander.epsilon :as m :refer [match]]))
(deftest match+test
(is (=
(match 1
a 2)
(match+ 1
a 2)))
(is (= (match 1
a a)
(match+ 1
a a)))
(is (= (match 1
(b :guard integer?) :return)
(match+ 1
b :guard [b integer?] :return)))
(is (= (match {:d 1}
{:d (d :guard integer?)} d)
(match+ {:d 1}
{:d d} :guard [d [integer?]] d)))
(is (= (match {:d {:a 1}}
{:d {:a (d :guard integer?)}} d)
(match+ {:d {:a 1}}
{:d {:a d}} :guard [d integer?] d)))
(is (= (match [1 [1]]
[(a :guard integer?) [(b :guard integer?)]] a)
(match+ [1 [1]]
[a [b]] :guard [a integer?
b integer?] a)))
(is (= (match [1 1 2 2 ]
[(a :guard integer?) (b :guard integer?)
(c :guard [integer? even?]) (d :guard [integer? even?])] a)
(match+ [1 1 2 2]
[a b c d] :guard [[a b] integer?
[c d] [integer? even?]] a))
"should be able to express shared guards")
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment