Created
November 5, 2009 06:58
-
-
Save mbrezu/226834 to your computer and use it in GitHub Desktop.
quasiquote with unquote-splicing
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 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