reader macro communicating with a compile-time macro

Discussion of Common Lisp

reader macro communicating with a compile-time macro

Postby Harnon » Wed Jul 30, 2008 10:52 am

So, i'm trying to make a certain macro that functions like this
Example:
Code: Select all
  (with-program-cases ((listp x) t)
               (loop for y = x then #?((cdr y) (1+ y)) until #?((null y) (> y 5))
                       do(print y)))

This should expand into something like this:
Code: Select all
    (cond ((listp x) (loop for y=x then (cdr y) until (null y) do(print y)))
                (t (loop for y=x then (1+ y) until (> y 5) do(print y))))


In other words, each 'case' in the argument to the with-program-cases macro has a slight different
body, which is given by a list of different choices which is prefixed by the #? reader macro.
Preferably, i would also want to be able to define the macro so i could have something like this
#?([for y = x] [for y = 1])
where the brackets would indicate that for y=x and for y=1 would be pasted into the appropriate place without surrounding parentheses. But that's for another time!

I figured i would implement this by making the reader macro #? append the appropriate data to two global variables
, *cases* and *case-vars*. The macro with-program-cases would then use this data to expand into an intermediate state given by the following (using the first example above):

Code: Select all
(COND ((LISTP X)
            (SYMBOL-MACROLET ((#:G1757 (CDR X)) (#:G1761 (NULL Y)))
                        (LOOP FOR Y = X THEN #:G1757 UNTIL #:G1761)))
           (T (SYMBOL-MACROLET ((#:G1757 (1+ Y)) (#:G1761 (> Y 5)))
                         (LOOP FOR Y = X THEN #:G1757 UNTIL #:G1761))))


The problem occurs when there are multiple with-program-codes expanded at the same time.
First, all the reader macros are expanded. When the with-program-cases is macroexpanded,
it now sees all the data pertaining to all the cases within each with-program-cases body. In other words, it has
no clue which data belongs to itself. So, what i need, i think, is either a totally different way to do this whole thing :? ,
or some way to communicate between the reader macros and the with-program-cases macro that surrounds them.
Thix! :D
Harnon
 
Posts: 78
Joined: Wed Jul 30, 2008 9:59 am

Re: reader macro communicating with a compile-time macro

Postby Ramarren » Wed Jul 30, 2008 1:09 pm

Side effecting read macros are a bad idea. There is no guarantee that they will be executed once when expanding, and in general run into weird issues like the one you have here. What I would do if trying something like this is to make the read macro expand to some unexported symbol, and then walk the source tree in with-program-cases rebuilding the tree.

I think that in this case you can get away without a full code walker even, as this is just a source tree transformation, rather than syntax tree. This would make the #?([for y = x] [for y = 1]) mostly trivial as well, as you could just make it expand into a different marker symbol.
Ramarren
 
Posts: 613
Joined: Sun Jun 29, 2008 4:02 am
Location: Warsaw, Poland

Re: reader macro communicating with a compile-time macro

Postby danb » Thu Jul 31, 2008 12:30 am

Harnon wrote:So, i'm trying to make a certain macro

each 'case' in the argument to the with-program-cases macro has a slight different
body, which is given by a list of different choices which is prefixed by the #? reader macro.

I figured i would implement this by making the reader macro #? append the appropriate data to two global variables

The problem occurs when there are multiple with-program-codes expanded at the same time.


Try to avoid using globals.
This seems to work:

Code: Select all
(set-dispatch-macro-character
   #\# #\?
   (lambda (stream char n)
     (declare (ignore char n))
     `(program-alternatives ,@(read stream t :eof t))))

(defun copy-nth-alternative-tree (tree n)
  (cond ((atom tree) tree)
        ((eq (car tree) 'program-alternatives)
         (nth n (cdr tree)))
        (t (mapcar (lambda (tree) (copy-nth-alternative-tree tree n)) tree))))

(defmacro with-program-cases (cases &body body)
  `(cond
    ,@(loop for n below (length cases)
            collect `(,(nth n cases) ,@(copy-nth-alternative-tree body n)))))
danb
 
Posts: 35
Joined: Sat Jun 28, 2008 1:05 pm
Location: Urbana, Illinois, US

Re: reader macro communicating with a compile-time macro

Postby Harnon » Thu Jul 31, 2008 7:59 am

Wow! Thx, guys. I'm boggled by the simplicity (lol) compared to how hard i tried to make it.
In this case, maybe one constant variable would be good, such as

Code: Select all
(let ((program-alternatives (gensym)))
(set-dispatch-macro-character
   #\# #\?
   (lambda (stream char n)
     (declare (ignore char n))
     `(,program-alternatives ,@(read stream t :eof t))))

(defun copy-nth-alternative-tree (tree n)
  (cond ((atom tree) tree)
        ((equal (car tree) program-alternatives)
         (nth n (cdr tree)))
        (t (mapcar (lambda (tree) (copy-nth-alternative-tree tree n)) tree))))

(defmacro with-program-cases (cases &body body)
  `(cond
    ,@(loop for n below (length cases)
            collect `(,(nth n cases) ,@(copy-nth-alternative-tree body n))))))


Do you think this lexical variable would be fine?
Harnon
 
Posts: 78
Joined: Wed Jul 30, 2008 9:59 am

Re: reader macro communicating with a compile-time macro

Postby danb » Thu Jul 31, 2008 12:23 pm

Harnon wrote:
Code: Select all
(let ((program-alternatives (gensym)))
  ...

Do you think this lexical variable would be fine?

I think so, as long as you make sure the function and the read macro are always (re)defined together.
danb
 
Posts: 35
Joined: Sat Jun 28, 2008 1:05 pm
Location: Urbana, Illinois, US


Return to Common Lisp

Who is online

Users browsing this forum: No registered users and 3 guests