In my case, I'm trying to use a the expression '[x] that is available to
the macro in the resulting product.  So I don't want the value of x at all,
just a vector with the symbol x.

That vector is in a variable I have in the macro named
'positional-parameters', so I substitute '~positional-parameters to with
the hope of getting '[x] in the resulting definition.

Here's a fragment of the overall defs.  As you can see I'm just messing
around, I was trying to write a defun with 'supplied-p' lambda list
semantics, and my clojure/macrology is awful.  It's the macro near the
bottom.  You can append this to
https://github.com/dtenny/clojdt/blob/master/src/jdt/cl.clj if you want to
git clone and compile it.

(def lambda-list-keyword-symbols
  "Symbols without namespace specifications"
  (into #{} (map symbol ["&allow-other-keys" "&key" "&rest" "&aux"
"&optional"])))

(defn lambda-list-keyword?
  "If x is a lambda list keyword, in any case, return the canonicalized
form as a symbol.
   If it starts with an ampersand but is not a valid lambda list keyword,
throw an exception."
  [x]
  (let [sym-name (.toLowerCase (name x))]
    (if (= (first sym-name) \&)
      (if-let [lambda-sym (lambda-list-keyword-symbols (symbol sym-name))]
        lambda-sym
        (throw (Exception. (str "'" x "' is not a valid lambda list
keyword."))))
      nil)))

(defn lambda-list-segment
  "Return three values in a vector.

   The first value is a vector of non-lambda-list-keywords
   in l before the next lambda list keyword in the list.  Order is
preserved.

   The second value is the canonicalized lambda list keyword (as a clojure
symbol, e.g. '&aux)
   that terminated accumulation, or nil if we reached the end of the list.

   The third value is the rest of the list following the lambda list
keyword, or nil if there aren't
   any more values.

   E.g. (lambda-list-segment '(a b &optional c))
        => [[a b] &optional (c)]"
  [l]
  (loop [result [] l l]
    (if (empty? l)
      [result nil nil]
      (let [head (first l)
            tail (rest l)
            lambda-keyword (lambda-list-keyword? head)]
        (if lambda-keyword
          [result lambda-keyword tail]
          (recur (conj result head) tail))))))

(defn partition-lambda-list
  "Return a map keyed by lambda list keyword (or the pseudo keyword
'&positional'
   for positional args), and valued by a vector lambda vars associated with
the
   type of lambda list element, in the order specified by the caller.
   E.g. (partition-lambda-list [a b c &optional (d nil d-supplied-p) &key
e])
        => {&positional [a b c] &optional [(d nil d-supplied-p)] &key [e]}
   Ignore case in things appearing to be lambda list keywords.
   Complain if any symbol begins with an ampersand and is not a valid
lambda list keyword.
   Preserve order of parameters."
  [lambda-list]
  (let [first-chunk (lambda-list-segment lambda-list)
        positionals (first first-chunk)]
    (loop [result {'&positional positionals}
           next-lambda-key (second first-chunk)
           lambda-list (nth first-chunk 2)]
      (if (empty? lambda-list)
        (if next-lambda-key
          (merge result {next-lambda-key []}) ; in case it's
&allow-other-keys
          result)
        (let [chunk (lambda-list-segment lambda-list)]
          (recur (merge result {next-lambda-key (first chunk)})
                 (second chunk)
                 (nth chunk 2)))))))

(defn parse-defun-decls
  "Parse defun elements following the lambda list.
   If there are declarations, strip them from the sequence.
   Return a vector of two things.
   1) the docstring, or nil if there isn't one.
   2) forms following the optional declarations and docstring, nil if there
aren't any."
  [s]
  (let [declare? (fn [l]
                   (and (list? l)
                        (.equalsIgnoreCase (name (first l)) "declare")))]
    (loop [s s]
      (if (empty? s)
        [nil nil]
        (let [head (first s)]
          (cond (string? head) [head (rest s)] ; **BUG** won't work if
declare is after docstring
                (declare? head) (recur (rest s))
                :else [nil s]))))))

(defn positional-values
  "Given a sequence of formal parameter names for positional values
   and a sequence of actual arguments to a defun'ed function call,
   return a vector of values from actual args to be bound to parameters.
   Return [] if there aren't any."
  [parameters actual-args]
  (if parameters
    (if (< (count actual-args) (count parameters))
      (throw (Exception. (str "Insufficient arguments in " actual-args
                              " to match positional parameters in "
parameters)))
      (loop [values [] args actual-args]
        (if (empty? args)
          values
          (recur (conj values (first args)) (rest args)))))
    []))

;; *FINISH*: define/support RETURN-FROM

(defmacro defun
  "defun function-name lambda-list [[declaration* | documentation]] form*
   => function-name
   Arguments and Values:
    function-name---a function name.
    lambda-list---an ordinary lambda list.
    declaration---a declare expression that is discarded.
    documentation---a string; not evaluated.
    forms---an implicit progn.

   Notes:
     This is meant to be as common-lisp oriented as possible in a clojure
environment.
     Thus a function definition is (defun foo (&optional bar) ...)
     and NOT                       (defun foo [clojure-y args] ...)"
  [function-name lambda-list & decls-doc-forms]
  (let [param-map (partition-lambda-list lambda-list)
        [docstring forms] (parse-defun-decls decls-doc-forms)
        args-var (gensym "args")
        positional-parameters (get param-map '&positional)]
    (if docstring
      (comment ; *FINISH* docstring variant
;;      `(defn ~function-name ~docstring [& ~args-var]
;;         (let [~@(lambda-list-bindings param-map args-var)]
;;           ~@forms))
        )
      `(defn ~function-name [& ~args-var]
         ;; *FINISH*: other param types once we get positional values
working
         (let [~positional-parameters (#_positional-values print
'~positional-parameters ~args-var)]
           ~@forms)))))

(println (macroexpand-1 '(defun foo (x) (println x))))
;(println (macroexpand '(defun foo (x) (println  x))))
(defun foo (x) (println x))
(foo x)  ; <<<---- generates the error





On Sun, Jul 20, 2014 at 5:46 PM, Matthew DeVore <matv...@gmail.com> wrote:

> I don't have a Clojure REPL handy at the moment but it looks like the x
> symbol is being resolved in the macro context rather than the expansion
> context. In the macro source, where you have a plain x, try to replace it
> with ~'x ... This blog post may be relevant:
> http://amalloy.hubpages.com/hub/Unhygenic-anaphoric-Clojure-macros-for-fun-and-profit
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to a topic in the
> Google Groups "Clojure" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/clojure/grxYLrjnz60/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> clojure+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to