From: ········@gmail.com
Subject: Using macros inside other macros...
Date: 
Message-ID: <e208fcd7-eeb5-4924-beb5-327d744bd875@s9g2000prg.googlegroups.com>
I'm currently having difficulty understanding how to use macros to
generate code that is used by other macros. I mean this in two
diffrent senses:

#1 - Having macros accept code which is generated by other macros.

#2 - Using other macros within the defmacro of another macro.

I'm primarily concerned with #1. Here's an example that I'm grappling
with:

(defmacro ia-slot (slot &rest options)
  "[Initarg/Accessor-slot] Given an argument 'a' expands to a slot
  definition providing an initarg of ':a' and and
  accessor of 'a'. Additional options can be
  specified in the body of the form."
  (let ((slot-str (string-upcase (string slot)))) ;Converts slot's
symbol to a string so that a ':' can be added.
    `(,slot
    :initarg ,(intern slot-str "KEYWORD")
    :accessor ,slot
    ,@options)))

(defclass test-inter ()
  ((a1
    :initarg :a1
    :initform nil
    :accessor a1)
   (ia-slot a2 :initform nil)))

The basic idea of this code is that when given the name of a CLOS slot
it expands into a definition of the slot which includes the initarg
and accessor options. The problem is then when this code is evaluated
(in CLISP 2.44 running in SLIME) this error occurs:

DEFCLASS TEST-INTER: slot options for slot IA-SLOT must come in pairs
   [Condition of type SYSTEM::SIMPLE-SOURCE-PROGRAM-ERROR]

Now, what seems to be happening is that when defclass is expanded it
assumes that each slot definition contains no macros and uses the
first symbol of the sexp as the name of the slot without evaluating
it. I'm not sure if there exists a way to force the ia-slot macro to
automaticly expand before defclass does, if this can be easily
automated I'd be happy to know how. Otherwise I'd like to know if the
CLOS provides other ways to do what I want (I've read about :accessor-
prefix but can't get it to work).

Any help is appreciated (I'll post about #2 later).

From: Rainer Joswig
Subject: Re: Using macros inside other macros...
Date: 
Message-ID: <joswig-CC2F38.10551718012009@news-europe.giganews.com>
In article 
<····································@s9g2000prg.googlegroups.com>,
 ········@gmail.com wrote:

> I'm currently having difficulty understanding how to use macros to
> generate code that is used by other macros. I mean this in two
> diffrent senses:
> 
> #1 - Having macros accept code which is generated by other macros.
> 
> #2 - Using other macros within the defmacro of another macro.
> 
> I'm primarily concerned with #1. Here's an example that I'm grappling
> with:
> 
> (defmacro ia-slot (slot &rest options)
>   "[Initarg/Accessor-slot] Given an argument 'a' expands to a slot
>   definition providing an initarg of ':a' and and
>   accessor of 'a'. Additional options can be
>   specified in the body of the form."
>   (let ((slot-str (string-upcase (string slot)))) ;Converts slot's
> symbol to a string so that a ':' can be added.
>     `(,slot
>     :initarg ,(intern slot-str "KEYWORD")
>     :accessor ,slot
>     ,@options)))
> 
> (defclass test-inter ()
>   ((a1
>     :initarg :a1
>     :initform nil
>     :accessor a1)
>    (ia-slot a2 :initform nil)))
> 
> The basic idea of this code is that when given the name of a CLOS slot
> it expands into a definition of the slot which includes the initarg
> and accessor options. The problem is then when this code is evaluated
> (in CLISP 2.44 running in SLIME) this error occurs:
> 
> DEFCLASS TEST-INTER: slot options for slot IA-SLOT must come in pairs
>    [Condition of type SYSTEM::SIMPLE-SOURCE-PROGRAM-ERROR]
> 
> Now, what seems to be happening is that when defclass is expanded it
> assumes that each slot definition contains no macros and uses the
> first symbol of the sexp as the name of the slot without evaluating
> it.

Check out the SYNTAX (really!) for DEFCLASS (from the CLHS):

defclass class-name ({superclass-name}*) ({slot-specifier}*) [[class-option]]
=> new-class


slot-specifier::= slot-name | (slot-name [[slot-option]])
slot-name::= symbol
slot-option::= {:reader reader-function-name}* | 
               {:writer writer-function-name}* | 
               {:accessor reader-function-name}* | 
               {:allocation allocation-type} | 
               {:initarg initarg-name}* | 
               {:initform form} | 
               {:type type-specifier} | 
               {:documentation string} 
function-name::= {symbol | (setf symbol)}
class-option::= (:default-initargs . initarg-list) | 
                (:documentation string) | 
                (:metaclass class-name) 


You see that a slot-specifier is either a symbol or a list
starting with a symbol. IT IS NOT A FUNCTION or MACRO CALL.


> I'm not sure if there exists a way to force the ia-slot macro to
> automaticly expand before defclass does, if this can be easily
> automated I'd be happy to know how. Otherwise I'd like to know if the
> CLOS provides other ways to do what I want (I've read about :accessor-
> prefix but can't get it to work).

You might have read about :accessor-prefix, but in ANSI CL
there is no slot-option :accessor-prefix - see above.

There are two typical solutions:

1)  write a MY-DEFCLASS which does what you want and
    expands into DEFCLASS (or even some other code
    using the MOP)

2)  use read-time functionality  like #.
    where you can compute lisp expressions during
    read. Simple example:

   CL-USER 30 > (read-from-string "(1 #.(sin 1.5) 2)")
   (1 0.997495 2)

> 
> Any help is appreciated (I'll post about #2 later).

-- 
http://lispm.dyndns.org/
From: Pascal J. Bourguignon
Subject: Re: Using macros inside other macros...
Date: 
Message-ID: <87ljt8vp7g.fsf@galatea.local>
········@gmail.com writes:

> I'm currently having difficulty understanding how to use macros to
> generate code that is used by other macros. I mean this in two
> diffrent senses:
>
> #1 - Having macros accept code which is generated by other macros.
>
> #2 - Using other macros within the defmacro of another macro.
>
> I'm primarily concerned with #1. 

(defmacro acceptor (data)
   `(car ',data))

(defmacro producer (arg1 arg2)
   `(+ ,arg1 ,arg2))

(macroexpand '(acceptor #.(macroexpand '(producer 1 2)))
--> (CAR '(+ 1 2)) ;
    T
(acceptor #.(macroexpand '(producer 1 2)))
--> +
   
is what your #1 means.  Force feeding code to a macro that takes data
is like trying to fit square pegs in round holes.


It's not up to you to decide what a macro accepts. It accepts what it
has been designed to accept.  If a macro takes as argument some DATA
it won't have evaluated you shouldn't try to feed it some CODE to be
evaluated.


> Here's an example that I'm grappling
> with:
>
> (defmacro ia-slot (slot &rest options)
> [...]
> (defclass test-inter ()
> [...]

> The basic idea of this code is that when given the name of a CLOS slot
> it expands into a definition of the slot which includes the initarg
> and accessor options. The problem is then when this code is evaluated

The problems are that:

- you have defined a macro that doesn't return lisp code.


    C/USER[40]> (macroexpand '(ia-slot my-slot))
    (MY-SLOT :INITARG :MY-SLOT :ACCESSOR MY-SLOT) ;
    T
    C/USER[41]> (ia-slot my-slot)

    *** - EVAL: undefined function MY-SLOT
    The following restarts are available:
    USE-VALUE      :R1      Input a value to be used instead of (FDEFINITION 'MY-SLOT).
    RETRY          :R2      Retry
    STORE-VALUE    :R3      Input a new value for (FDEFINITION 'MY-SLOT).
    ABORT          :R4      Abort main loop
    C/Break 1 USER[42]> :q
    C/USER[43]> 

    Macros must always generate lisp CODE.


- you are trying to give lisp code to be evaluated as an argument to a
  macro that takes lisp data that it won't evaluate.



> (in CLISP 2.44 running in SLIME) this error occurs:
>
> DEFCLASS TEST-INTER: slot options for slot IA-SLOT must come in pairs
>    [Condition of type SYSTEM::SIMPLE-SOURCE-PROGRAM-ERROR]
>
> Now, what seems to be happening is that when defclass is expanded it
> assumes that each slot definition contains no macros and uses the
> first symbol of the sexp as the name of the slot without evaluating
> it. I'm not sure if there exists a way to force the ia-slot macro to
> automaticly expand before defclass does, if this can be easily
> automated I'd be happy to know how. 

There's the read-time #. trick above, but it will cause more problem
than you want.  There's an alternative that is even worse, using
MACROEXPAND and EVAL.  The point is that macros are not to be used
this way.


> Otherwise I'd like to know if the
> CLOS provides other ways to do what I want (I've read about :accessor-
> prefix but can't get it to work).

You can create classes at run-time, with the MOP function
ENSURE-CLASS.  Then of course you will get all the function arguments
evaluated.


Another way, is to define your own defclass macro (possibly by
wrapping over cl:defclass), that would have different evaluation rules
for its arguments.


> Any help is appreciated (I'll post about #2 later).

-- 
__Pascal Bourguignon__
From: Kalle Olavi Niemitalo
Subject: Re: Using macros inside other macros...
Date: 
Message-ID: <87sknggah4.fsf@Astalo.kon.iki.fi>
········@gmail.com writes:

> (defclass test-inter ()
>   ((a1
>     :initarg :a1
>     :initform nil
>     :accessor a1)
>    (ia-slot a2 :initform nil)))

You can do this by wrapping DEFCLASS in your own macro (possibly
even shadowing the symbol):

(defmacro ia-slot (slot &rest options &key &allow-other-keys)
  "[Initarg/Accessor-slot] Given an argument 'a' expands to a slot
  definition providing an initarg of ':a' and and
  accessor of 'a'. Additional options can be
  specified in the body of the form."
  ;;Converts slot's symbol to a string so that a ':' can be added.
  (let ((slot-str (string-upcase (string slot)))) 
    `(,slot
      :initarg ,(intern slot-str "KEYWORD")
      :accessor ,slot
      ,@options)))
(eval-when (:compile-toplevel :load-toplevel :execute)
  (setf (get 'ia-slot 'macroexpand-slot-specifier) t))

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun macroexpand-1-slot-specifier (slot-specifier &optional env)
    "Macroexpand one level of a slot specifier.
Return EXPANSION and EXPANDED-P, like MACROEXPAND-1 does."
    (if (and (consp slot-specifier)
             (symbolp (first slot-specifier))
             (get (first slot-specifier) 'macroexpand-slot-specifier))
      (macroexpand-1 slot-specifier env)
      (values slot-specifier nil)))

  (defun macroexpand-slot-specifier (slot-specifier &optional env)
    "Macroexpand a slot specifier to something suitable for DEFCLASS.
Return EXPANSION and EXPANDED-P, like MACROEXPAND does."
    (loop with expanded-p and ever-expanded-p = nil
          do (setf (values slot-specifier expanded-p)
                   (macroexpand-1-slot-specifier slot-specifier env))
          while expanded-p
          do (setq ever-expanded-p t)
          finally (return (values slot-specifier ever-expanded-p)))))

(defmacro my-defclass (class-name superclass-names slot-specifiers
                       &rest class-options &environment env)
  "Like DEFCLASS but expand some macros as slot specifiers.
The names of those macros must have the MACROEXPAND-SLOT-SPECIFIER
property with T as the value.  (Other values are reserved for
future extensions.)  Such symbols cannot then be used as slot names
in MY-DEFCLASS."
  `(defclass ,class-name ,superclass-names
     ,(loop for slot-specifier in slot-specifiers
            collect (macroexpand-slot-specifier slot-specifier env))
     ,@class-options))

(my-defclass test-inter ()
  ((a1
    :initarg :a1
    :initform nil
    :accessor a1)
   (ia-slot a2 :initform nil)))

Other schemes are also possible; for example, you could use
#(ia-slot a2 :initform nil) or (:macro ia-slot a2 :initform nil)
and adjust MACROEXPAND-1-SLOT-SPECIFIER accordingly.

Alternatively, you could use read-time evaluation:

(eval-when (:compile-toplevel :execute)
  (defun ia-slot (slot &rest options &key &allow-other-keys)
    "[Initarg/Accessor-slot] Given an argument 'a' returns a slot
    definition providing an initarg of ':a' and and
    accessor of 'a'. Additional options can be
    specified in the body of the form."
    ;;Converts slot's symbol to a string so that a ':' can be added.
    (let ((slot-str (string-upcase (string slot))))
      `(,slot
        :initarg ,(intern slot-str "KEYWORD")
        :accessor ,slot
        ,@options))))

(defclass test-inter ()
  ((a1
    :initarg :a1
    :initform nil
    :accessor a1)
   #.(ia-slot 'a2 :initform 'nil)))

BTW, I don't know why you have that STRING-UPCASE call there.