;; Various useful macros
;;
;; Everybody is invited to add their own little macros here!
;;
;; 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 epl-v10.html 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.

(ns
  #^{:author "Konrad Hinsen"
     :doc "Various small macros"}
  clojure.contrib.macros)

;; By Konrad Hinsen
(defmacro const
  "Evaluate the constant expression expr at compile time."
  [expr]
  (eval expr))

;; By Konrad Hinsen
; This macro is made obsolete by Clojure's built-in letfn. I renamed it to
; letfn- (to avoid a name clash) but leave it in for a while, since its
; syntax is not quite the same as Clojure's. Expect this to disappear
; in the long run!
(defmacro letfn-
  "OBSOLETE: use clojure.core/letfn
   A variant of let for local function definitions. fn-bindings consists
   of name/args/body triples, with (letfn [name args body] ...)
   being equivalent to (let [name (fn name args body)] ...)."
  [fn-bindings & exprs]
  (let [makefn (fn [[name args body]] (list name (list 'fn name args body)))
	fns (vec (apply concat (map makefn (partition 3 fn-bindings))))]
  `(let ~fns ~@exprs)))

 ;; By Konrad Hinsen

 (defn- unqualified-symbol
  [s]
  (let [s-str (str s)]
    (symbol (subs s-str (inc (.indexOf s-str (int \/)))))))
 
(defn- bound-var?
  [var]
  (try
    (do (deref var) true)
    (catch java.lang.IllegalStateException e false)))

(defn- fns-from-ns
  [ns ns-symbol]
  (apply concat
    (for [[k v] (ns-publics ns)
          :when (and (bound-var? v)
                     (fn? @v)
                     (not (:macro (meta v))))]
       [k (symbol (str ns-symbol) (str k))])))

(defn- expand-symbol
  [ns-or-var-sym]
  (if (= ns-or-var-sym '*ns*)
    (fns-from-ns *ns* (ns-name *ns*))
    (if-let [ns (find-ns ns-or-var-sym)]
      (fns-from-ns ns ns-or-var-sym)
      (list (unqualified-symbol ns-or-var-sym) ns-or-var-sym))))

(defmacro with-direct-linking
  "EXPERIMENTAL!
   Compiles the functions in body with direct links to the functions
   named in symbols, i.e. without a var lookup for each invocation.
   Symbols is a vector of symbols that name either vars or namespaces.
   A namespace reference is replaced by the list of all symbols in the
   namespace that are bound to functions. If symbols is not provided,
   the default value ['clojure.core] is used. The symbol *ns* can be
   used to refer to the current namespace."
  {:arglists '([symbols? & body])}
  [& body]
  (let [[symbols body] (if (vector? (first body))
                         [(first body) (rest body)]
                         [['clojure.core] body])
  			bindings (vec (mapcat expand-symbol symbols))]
    `(let ~bindings ~@body)))
 