From: gray@antaire.COM (Gray Watson)
Subject: cccp patch for multi-arg defines...
Reply-To: Gray.Watson@antaire.com
Organization: Antaire Corporation, Pittsburgh, PA, USA
Distribution: gnu
Date: Mon, 13 Apr 1992 02:07:39 GMT

Not a bug.  A feature upgrade on-top of gcc version 2.1.

>From my own message on Fri, 27 Mar 92 12:09:23 EST:

> I think a vote should be taken on an extension to cpp to handle
> variable argument macros.

Well??

>	#define vmacro (...)		(void)printf(...)
>	/* '...' will absorb as many arguments as possible and regurgite them
>	   to the '...' in the destination code, comma seperated. */
>
>	Example:
>		main() { vmacro("hello %s\n", "world"); }
>
>	would become:
>		main() { (void)printf("hello %s\n", "world"); }

Below I have the patches to cccp.c for the above feature.  RMS mentioned that
since ... is already defined (for case ranges) that another token should be
chosen as the magical multiple-argument-token.  He's correct, but ... is the
perfect one.  Oh well.

After a couple of hours of deep discussion here, we came up with __REST__ as
magical token.  It is quite arbitrary and is #defined on in the patches so it
can be changed easily.

In any case.  It seems to work fine here.  Can anyone see any problems with
this being integrated into ccp?

gray

-------------------------------------------------------------------------------
$Id: absorb_rest,v 1.2 1992/03/28 16:15:31 gray Exp $
-------------------------------------------------------------------------------

*** cccp.c.ORG	Fri Mar 27 15:21:45 1992
--- cccp.c	Sat Mar 28 11:04:52 1992
***************
*** 572,577 ****
--- 572,578 ----
    U_CHAR *expansion;
    int line;			/* Line number of definition */
    char *file;			/* File of definition */
+   char rest_args;		/* Nonzero if last arg. absorbs the rest */
    struct reflist {
      struct reflist *next;
      char stringify;		/* nonzero if this arg was preceded by a
***************
*** 600,605 ****
--- 601,609 ----
    KEYDEF *keydef;
  };
  
+ /* special macro argument that will absorb the rest of the arguments */
+ static char rest_args_string[] = "__REST__";
+ #define REST_ARGS_LENGTH	(sizeof (rest_args_string) - 1)
  
  /* The structure of a node in the hash table.  The hash table
     has entries for all tokens defined by #define commands (type T_MACRO),
***************
*** 4524,4529 ****
--- 4528,4534 ----
    int sym_length;		/* and how long it is */
    int line = instack[indepth].lineno;
    char *file = instack[indepth].nominal_fname;
+   int rest_args = 0;
  
    DEFINITION *defn;
    int arglengths = 0;		/* Accumulate lengths of arg names
***************
*** 4560,4571 ****
        temp->argno = argno++;
        arg_ptrs = temp;
  
!       if (!is_idstart[*bp])
! 	pedwarn ("parameter name starts with a digit in `#define'");
  
!       /* Find the end of the arg name.  */
!       while (is_idchar[*bp]) {
! 	bp++;
        }
        temp->length = bp - temp->name;
        arglengths += temp->length + 2;
--- 4565,4588 ----
        temp->argno = argno++;
        arg_ptrs = temp;
  
!       if (rest_args)
! 	pedwarn ("parameter is specified after '%s' rest-args token",
! 		 rest_args_string);
! 
!       /* do we have a "special" rest-args token here? */
!       if (limit - bp > REST_ARGS_LENGTH &&
! 	  strncmp (rest_args_string, bp, REST_ARGS_LENGTH) == 0) {
! 	bp += REST_ARGS_LENGTH;
! 	rest_args = 1;
!       }
!       else {
! 	if (!is_idstart[*bp])
! 	  pedwarn ("parameter name starts with an illegal character in `#define'");
  
! 	/* Find the end of the arg name.  */
! 	while (is_idchar[*bp]) {
! 	  bp++;
! 	}
        }
        temp->length = bp - temp->name;
        arglengths += temp->length + 2;
***************
*** 4604,4609 ****
--- 4621,4627 ----
      if (bp < limit && (*bp == ' ' || *bp == '\t')) ++bp;
      /* now everything from bp before limit is the definition. */
      defn = collect_expansion (bp, limit, argno, arg_ptrs);
+     defn->rest_args = rest_args;
  
      /* Now set defn->args.argnames to the result of concatenating
         the argument names in reverse order
***************
*** 4860,4865 ****
--- 4878,4884 ----
    U_CHAR *stringify = 0;
    int maxsize;
    int expected_delimiter = '\0';
+   int rest_args;
  
    /* Scan thru the replacement list, ignoring comments and quoted
       strings, picking up on the macro calls.  It does a linear search
***************
*** 5010,5025 ****
        }
      }
  
      /* Handle the start of a symbol.  */
!     if (is_idchar[c] && nargs > 0) {
        U_CHAR *id_beg = p - 1;
        int id_len;
  
        --exp_p;
!       while (p != limit && is_idchar[*p]) p++;
        id_len = p - id_beg;
  
!       if (is_idstart[c]) {
  	register struct arglist *arg;
  
  	for (arg = arglist; arg != NULL; arg = arg->next) {
--- 5029,5051 ----
        }
      }
  
+     /* do we have a rest_args_string symbol here? */
+     rest_args = (limit - (p - 1) >= REST_ARGS_LENGTH &&
+ 		 strncmp (rest_args_string, p - 1, REST_ARGS_LENGTH) == 0);
+ 
      /* Handle the start of a symbol.  */
!     if ((is_idchar[c] || rest_args) && nargs > 0) {
        U_CHAR *id_beg = p - 1;
        int id_len;
  
        --exp_p;
!       if (rest_args)
! 	p += REST_ARGS_LENGTH - 1;
!       else
! 	while (p != limit && is_idchar[*p]) p++;
        id_len = p - id_beg;
  
!       if (is_idstart[c] || rest_args) {
  	register struct arglist *arg;
  
  	for (arg = arglist; arg != NULL; arg = arg->next) {
***************
*** 6720,6725 ****
--- 6746,6752 ----
    register U_CHAR *xbuf;
    int xbuf_len;
    int start_line = instack[indepth].lineno;
+   int rest_args;
  
    CHECK_DEPTH (return;);
  
***************
*** 6753,6765 ****
  
      /* Parse all the macro args that are supplied.  I counts them.
         The first NARGS args are stored in ARGS.
!        The rest are discarded.  */
      i = 0;
      do {
        /* Discard the open-parenthesis or comma before the next arg.  */
        ++instack[indepth].bufp;
!       parse_error
! 	= macarg ((i < nargs || (nargs == 0 && i == 0)) ? &args[i] : 0);
        if (parse_error) {
  	error_with_line (line_for_error (start_line), parse_error);
  	break;
--- 6780,6803 ----
  
      /* Parse all the macro args that are supplied.  I counts them.
         The first NARGS args are stored in ARGS.
!        The rest are discarded.
!        If rest_args is set then we assume macarg absorbed the rest of the args.
!        */
      i = 0;
+     rest_args = 0;
      do {
        /* Discard the open-parenthesis or comma before the next arg.  */
        ++instack[indepth].bufp;
!       if (rest_args)
! 	continue;
!       if (i < nargs || (nargs == 0 && i == 0)) {
! 	/* if we are working on last arg which absorbes rest of args... */
! 	if (i == nargs - 1 && defn->rest_args)
! 	  rest_args = 1;
! 	parse_error = macarg (&args[i], rest_args);
!       }
!       else
! 	parse_error = macarg (0, 0);
        if (parse_error) {
  	error_with_line (line_for_error (start_line), parse_error);
  	break;
***************
*** 7007,7018 ****
  
  /*
   * Parse a macro argument and store the info on it into *ARGPTR.
   * Return nonzero to indicate a syntax error.
   */
  
  static char *
! macarg (argptr)
       register struct argdata *argptr;
  {
    FILE_BUF *ip = &instack[indepth];
    int paren = 0;
--- 7045,7058 ----
  
  /*
   * Parse a macro argument and store the info on it into *ARGPTR.
+  * REST_ARGS is passed to macarg1 to make it absorb the rest of the args.
   * Return nonzero to indicate a syntax error.
   */
  
  static char *
! macarg (argptr, rest_args)
       register struct argdata *argptr;
+      int rest_args;
  {
    FILE_BUF *ip = &instack[indepth];
    int paren = 0;
***************
*** 7022,7028 ****
    /* Try to parse as much of the argument as exists at this
       input stack level.  */
    U_CHAR *bp = macarg1 (ip->bufp, ip->buf + ip->length,
! 			&paren, &newlines, &comments);
  
    /* If we find the end of the argument at this level,
       set up *ARGPTR to point at it in the input stack.  */
--- 7062,7068 ----
    /* Try to parse as much of the argument as exists at this
       input stack level.  */
    U_CHAR *bp = macarg1 (ip->bufp, ip->buf + ip->length,
! 			&paren, &newlines, &comments, rest_args);
  
    /* If we find the end of the argument at this level,
       set up *ARGPTR to point at it in the input stack.  */
***************
*** 7060,7066 ****
        newlines = 0;
        comments = 0;
        bp = macarg1 (ip->bufp, ip->buf + ip->length, &paren,
! 		    &newlines, &comments);
        final_start = bufsize;
        bufsize += bp - ip->bufp;
        extra += newlines;
--- 7100,7106 ----
        newlines = 0;
        comments = 0;
        bp = macarg1 (ip->bufp, ip->buf + ip->length, &paren,
! 		    &newlines, &comments, rest_args);
        final_start = bufsize;
        bufsize += bp - ip->bufp;
        extra += newlines;
***************
*** 7149,7161 ****
     Value returned is pointer to stopping place.
  
     Increment *NEWLINES each time a newline is passed.
!    Set *COMMENTS to 1 if a comment is seen.  */
  
  static U_CHAR *
! macarg1 (start, limit, depthptr, newlines, comments)
       U_CHAR *start;
       register U_CHAR *limit;
       int *depthptr, *newlines, *comments;
  {
    register U_CHAR *bp = start;
  
--- 7189,7203 ----
     Value returned is pointer to stopping place.
  
     Increment *NEWLINES each time a newline is passed.
!    Set *COMMENTS to 1 if a comment is seen.
!    REST_ARGS notifies macarg1 that it should absorb the rest of the args. */
  
  static U_CHAR *
! macarg1 (start, limit, depthptr, newlines, comments, rest_args)
       U_CHAR *start;
       register U_CHAR *limit;
       int *depthptr, *newlines, *comments;
+      int rest_args;
  {
    register U_CHAR *bp = start;
  
***************
*** 7226,7232 ****
        }
        break;
      case ',':
!       if ((*depthptr) == 0)
  	return bp;
        break;
      }
--- 7268,7275 ----
        }
        break;
      case ',':
!       /* if we've returned to lowest level and we aren't absorbing all args */
!       if ((*depthptr) == 0 && rest_args == 0)
  	return bp;
        break;
      }

Newsgroups: gnu.gcc.bug