Skip to content

Instantly share code, notes, and snippets.

@mbrezu
Created November 5, 2009 06:58
Show Gist options
  • Save mbrezu/226834 to your computer and use it in GitHub Desktop.
Save mbrezu/226834 to your computer and use it in GitHub Desktop.
quasiquote with unquote-splicing
(ns quasiquote
(:use clojure.test))
;; **************************************
;; Quasiquote from ClojureQL
;; http://gitorious.org/clojureql/clojureql/blobs/master/src/dk/bestinclass/clojureql/util.clj
;; modified to work with unquote-splicing
;; **************************************
;; Original copyright notice from the util.clj file
;; (current file is a modified fragment of that file):
;; Copyright (c) 2008,2009 Lau B. Jensen <lau.jensen {at} bestinclass.dk
;; Meikel Brandmeyer <mb {at} kotka.de>
;; All rights reserved.
;;
;; The use and distribution terms for this software are covered by the
;; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
;; which can be found in the file LICENSE.txt at the root of this distribution.
;; By using this software in any fashion, you are agreeing to be bound by the
;; terms of this license. You must not remove this notice, or any other, from
;; this software.
(defn self-eval?
"Check whether the given form is self-evaluating."
[obj]
(or (keyword? obj)
(number? obj)
(instance? Character obj)
(string? obj)
(nil? obj)))
(defn flatten-map
"Flatten the keys and values of a map into a list."
[the-map]
(reduce (fn [result entry] (-> result
(conj (key entry))
(conj (val entry))))
[] the-map))
(defn unquote?
"Tests whether the given form is of the form (unquote ...)."
[form]
(and (seq? form) (= (first form) `unquote)))
(defn unquote-splicing?
"Tests whether the given form is of the form (unquote ...)."
[form]
(and (seq? form) (= (first form) `unquote-splicing)))
(declare quasiquote*)
(defn quasiquote-for-splicing [form]
(let [need-splice (unquote-splicing? form)
quasiquoted (quasiquote* form)]
(if need-splice
quasiquoted
`(list ~quasiquoted))))
(defn concat-code-for-splicing [form]
(cons `concat (map quasiquote-for-splicing form)))
(defn quasiquote*
"Worker for quasiquote macro. See docstring there. For use in macros."
[form]
(cond
(self-eval? form) form
(unquote? form) (second form)
(unquote-splicing? form) (second form)
(symbol? form) (list 'quote form)
(vector? form) (cons `vec (list (concat-code-for-splicing form)))
(map? form) (apply hash-map (map quasiquote* (flatten-map form)))
(set? form) (list* `apply `hash-set (list (concat-code-for-splicing form)))
(seq? form) (concat-code-for-splicing form)
:else (list 'quote form)))
(defmacro quasiquote
"Quote the supplied form as quote does, but evaluate unquoted parts.
Example: (let [x 5] (quasiquote (+ ~x 6))) => (+ 5 6)"
[form]
(quasiquote* form))
(deftest test-quasiquote-unquote-splicing
(is (= '[+ a b d e o u]
(let [x '(d e)] (quasiquote [+ a b ~@x o u]))))
(is (= '(+ a b d e o u)
(let [x '(d e)] (quasiquote (+ a b ~@x o u)))))
(is (= '#{+ a b d e o u}
(let [x '(d e)] (quasiquote #{+ a b ~@x o u}))))
(is (= '[1 2 #{a b}]
(let [x (quasiquote (a b))] (quasiquote [1 2 #{~@x}]))))
(is (= '[1 2 #{(a b)}]
(let [x (quasiquote (a b))] (quasiquote [1 2 #{~x}])))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment