From: ....What Is?....
Subject: Macro trouble
Date: 
Message-ID: <16789@life.ai.mit.edu>
I am having trouble with this macro:

(defmacro yyfail (lhs in-rule err-stream)
	(let*
		(
			(err-string
				(concatenate
					'string
					(if
						in-rule
						"Parse error in the middle of "
						"Error trying to parse "
					)
					(write-to-string lhs)
					"~%"
				)
			)
			(retval
				`(prog1
					(values nil nil)
					(format ,err-stream ,err-string)
				)
			)
		)
		`(quote ,retval)
	)
)

It's supposed to take the name of the rule it's parsing (lhs) and
two other parameters and return a form that prints an error message
and then returns (values nil nil).  When I call it interactively,
such as

>(yyfail '|If statement| t t)
(prog1
	(values nil nil)
	(format t "Parse error in the middle of |If statement|"))

>(yyfail '|If statement| nil t)
(prog1
	(values nil nil)
	(format t "Error trying to parse |If statement|"))

This is what I wanted.

However, when I compile it, it invariably fails to evaluate lhs and
instead I get something like
(prog1
	(values nil nil)
	(format t "Error trying to parse LHS"))

If I wrap an eval around lhs in (write-to-string lhs) it says lhs
is an unbound variable.  But lhs is in the argument list!

Any ideas?  I'm using AKCL 1.586 on an HP-300 running BSD 4.3.

--
Steve Boswell         | This opinion is distributed in the hopes that it
······@ucsd.edu       | will be useful, but WITHOUT ANY WARRANTY...
······@gnu.ai.mit.edu |

From: David F. Skoll
Subject: Re: Macro trouble
Date: 
Message-ID: <dfs.678658514@pulaski>
Hi,

You probably have a bug in Lisp, because I copied your macro into
Allegro Common Lisp on a Sun 4, and it worked whether it was compiled
or not.  Sorry I can't help any further!

--
David F. Skoll
From: Barry Margolin
Subject: Re: Macro trouble
Date: 
Message-ID: <1991Jul5.172722.11870@Think.COM>
In article <·····@life.ai.mit.edu> ······@wookumz.gnu.ai.mit.edu (....What Is?....) writes:
>I am having trouble with this macro:

[I reformatted it so that it would fit on the screen (and so that I could
understand it)...]

(defmacro yyfail (lhs in-rule err-stream)
>  (let* ((err-string
>	  (concatenate 'string
>		       (if in-rule
>			   "Parse error in the middle of "
>			   "Error trying to parse ")
>		       (write-to-string lhs) "~%"))
>	 (retval
>	  `(prog1
>	     (values nil nil)
>	     (format ,err-stream ,err-string))))
>    `(quote ,retval)))
>
>However, when I compile it, it invariably fails to evaluate lhs and
>instead I get something like
>(prog1
>	(values nil nil)
>	(format t "Error trying to parse LHS"))

I suspect that your program is invoking the macro as

(yyfail lhs ...)

The problem is that macros are expanded at compile time, and they get their
arguments unevaluated.  So, the first argument to the macro is the symbol
LHS.

>If I wrap an eval around lhs in (write-to-string lhs) it says lhs
>is an unbound variable.  But lhs is in the argument list!

You're confused about several things here.

First of all, EVAL generally can't access arguments.  Arguments are
normally lexical variables, and EVAL evaluates its argument in the null
lexical environment.

Second, the EVAL is occurring at the time the macro is being expanded in
the invoker, which is at compile time, not when any of the invoker's
variables are bound.

Basically, there's no way to insert the runtime value of a parameter into
the expansion of a macro.  Instead, you must include the reference to the
value of the parameter in the expansion itself.  Here's my rewrite of your
macro:

(defmacro yyfail (lhs in-rule err-stream)
  (let* ((err-string
	  (format nil "~:[Error trying to parse~;Parse error in the middle of~] ~~S~~%" in-rule)))
	 (retval
	  `(prog1
	     (values nil nil)
	     (format ,err-stream ,err-string ,lhs))))
    `(quote ,retval)))

One other thing that concerns me about this macro is the QUOTE at the end.
Since it returns a quoted form, the forms in RETVAL will never be executed
when they are expanded in the invoker.  Do you do this just so that you
could see the expansion?  If you want to see the expansion of a macro
(rather than execute the expansion) you should use MACROEXPAND, e.g.:

(macroexpand '(yyfail lhs t t))

The other possibility is that it returns a quoted form because the invoker
EVALs the result.  This is probably the wrong thing to do [rule of thumb:
if you think you need to use EVAL, you're probably wrong]; it will also not
work properly with my above fix, because LHS won't be bound in the null
lexical environment that EVAL uses (unless you declare LHS special).

-- 
Barry Margolin, Thinking Machines Corp.

······@think.com
{uunet,harvard}!think!barmar
From: Eliot Handelman
Subject: Re: Macro trouble
Date: 
Message-ID: <11632@idunno.Princeton.EDU>
In article <·····················@Think.COM> ······@think.com writes:
;
;(defmacro yyfail (lhs in-rule err-stream)
;  (let* ((err-string
;	  (format nil "~:[Error trying to parse~;Parse error in the middle of~] ~~S~~%" in-rule)))
;	 (retval
;	  `(prog1
;	     (values nil nil)
;	     (format ,err-stream ,err-string ,lhs))))
;    `(quote ,retval)))
;

While we're at it, there's no point writing 

(prog1 (values nil nil) ...)

since prog1 doesn't return multiple values. If that's what you really
meant to do, then there's good ole MULTIPLE-VALUE-PROG1. Of course
there's no distinction between passing too few values to something
tring to read multiple values and passing NIL, so it doesn't really
matter in this particular case.
From: Ping Luo
Subject: Re: Macro trouble
Date: 
Message-ID: <18523@venera.isi.edu>
the problem is that you tried to eval "lhs" at the compile time and it
surly won't work.

in case like this, i would put (write-to-string lhs) inside the
returned s-expression. 

something like this will do.

(defmacro yyfail (lhs in-rule err-stream)
  `(prog1 (values nil nil)  
    (format ,err-stream 
     (concatenate 'string 
      (if ,in-rule "Parse error in the middle of " "Error trying to parse ") 
      (write-to-string ,lhs) "~%"))))

and the following is the test result:

DSK> ;;;Compile /auto/quark/isd2/ping/test.test
 
;;; Reading source file "test.test"
;;; Writing binary file "test.sbin"
#P"/auto/quark/isd2/ping/test.sbin"
DSK> ;;; Loading binary file "test.sbin"
#P"/auto/quark/isd2/ping/test.sbin"
DSK> (yyfail '|If statement| t t)
Parse error in the middle of |If statement|
NIL
DSK> (yyfail '|If statement| nil t)
Error trying to parse |If statement|
NIL
DSK> 
--
      Ping Luo                           | Phone: (213) 822-1511 ext 709    
      USC/Information Sciences Institute |        (213) 822-1510--1212--709
      4676 Admiralty Way		 | E-mail: ····@isi.edu
      Marina del Rey, CA 90292		 | Fax: (213) 823-6714
                        
                        
From: Ping Luo
Subject: Re: Macro trouble
Date: 
Message-ID: <18525@venera.isi.edu>
the problem is that you tried to eval "lhs" at the compile time and it
surly won't work.

in case like this, i would put (write-to-string lhs) inside the
returned s-expression. 

something like this will do.

(defmacro yyfail (lhs in-rule err-stream)
  `(prog1 (values nil nil)  
    (format ,err-stream 
     (concatenate 'string 
      (if ,in-rule "Parse error in the middle of " "Error trying to parse ") 
      (write-to-string ,lhs) "~%"))))

and the following is the test result:

DSK> ;;;Compile /auto/quark/isd2/ping/test.test
 
;;; Reading source file "test.test"
;;; Writing binary file "test.sbin"
#P"/auto/quark/isd2/ping/test.sbin"
DSK> ;;; Loading binary file "test.sbin"
#P"/auto/quark/isd2/ping/test.sbin"
DSK> (yyfail '|If statement| t t)
Parse error in the middle of |If statement|
NIL
DSK> (yyfail '|If statement| nil t)
Error trying to parse |If statement|
NIL
DSK> (assert (oddp 3))

--
      Ping Luo                           | Phone: (213) 822-1511 ext 709    
      USC/Information Sciences Institute |        (213) 822-1510--1212--709
      4676 Admiralty Way		 | E-mail: ····@isi.edu
      Marina del Rey, CA 90292		 | Fax: (213) 823-6714