Last active
August 25, 2016 19:12
-
-
Save loganmhb/bc4af0371ffb763d91356f804f3e5094 to your computer and use it in GitHub Desktop.
spec-based for macro
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns spec-test.core | |
(:require [clojure.spec :as s]) | |
(:refer-clojure :exclude [for])) | |
(s/def ::for-bindings | |
(s/* (s/cat :lval any? | |
:rval any? | |
:modifiers (s/* (s/cat :type #{:when :let :while} | |
:modifier-body any?))))) | |
(defn for* | |
([body {:keys [lval rval modifiers]}] | |
`(let [iter# (fn iter# [s#] | |
(lazy-seq | |
(when-let [~lval (first s#)] | |
(when ~(or (:while modifiers) | |
true) | |
(if ~(or (:when modifiers) | |
true) | |
(let [~@(:let modifiers)] | |
(cons ~body | |
(iter# (rest s#)))) | |
(iter# (rest s#)))))))] | |
(iter# ~rval))) | |
([body {:keys [lval rval modifiers]} & bindings] | |
`(let [iter# (fn iter# [s#] | |
(lazy-seq | |
(when-let [~lval (first s#)] | |
(when ~(or (:while modifiers) | |
true) | |
(if ~(or (:when modifiers) true) | |
(let [~@(:let modifiers)] | |
(concat ~(apply for* body bindings) | |
(iter# (rest s#)))) | |
(iter# (rest s#)))))))] | |
(iter# ~rval)))) | |
(defmacro for [bindings body] | |
(let [parse-tree (map (fn [binding] | |
(update binding | |
:modifiers | |
#(->> % | |
(map (juxt :type :modifier-body)) | |
(into {})))) | |
(s/conform ::for-bindings bindings))] | |
(apply for* body parse-tree))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment