Ilya Zakharevich on Tue, 2 Jul 2002 11:48:51 -0400


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

Re: [PATCH 2.2.2] mnemonics for flags [NEW PATCH]


On Thu, Jun 27, 2002 at 09:30:26AM -0400, I wrote:
> Three intertwined questions remain:
> 
>   a) Should the "pattern" for the flags be exposed via the struct entree;
> 
>   b) Should the corresponding C function take a long or a GEN;
> 
>   c) Should the pattern be embedded into the argument signature
>      string, or be available elsewhere?

The following patch answers these question "yes", "long", "embedded".

The differences with the previous implementation:

 1) to avoid ambiguity, the flags should be enclosed in quotes (or
    otherwise made into t_STR);

 2) to avoid any slowdown, the flag descriptor is moved into the end
    of the entree->code.  It is separated by '\n' from the body of the
    signature; in particular, this part is entered only if a
    processing of a mneumonic flag is requested.

Enjoy,
Ilya

*** ././src/gp/highlvl.c.orig	Fri Apr 19 22:12:31 2002
--- ././src/gp/highlvl.c	Tue Jul  2 17:10:20 2002
***************
*** 244,250 ****
  {"plotcursor",11,(void*)rectcursor,10,"L"},
  {"plotdraw",99,(void*)rectdraw_flag,10,"vGD0,L,"},
  {"plotfile",16,(void*)plot_outfile_set,10,"ls"},
! {"ploth",99,(void*)ploth,10,"V=GGIpD0,L,D0,L,"},
  {"plothraw",25,(void*)plothraw,10,"GGD0,L,"},
  {"plothsizes",0,(void*)plothsizes_flag,10,"D0,L,"},
  {"plotinit",99,(void*)initrect_gen,10,"vLD0,G,D0,G,D0,L,"},
--- 244,250 ----
  {"plotcursor",11,(void*)rectcursor,10,"L"},
  {"plotdraw",99,(void*)rectdraw_flag,10,"vGD0,L,"},
  {"plotfile",16,(void*)plot_outfile_set,10,"ls"},
! {"ploth",99,(void*)ploth,10,"V=GGIpD0,M,D0,L,\nParametric|1; Recursive|2; no-X-axis|8; no-Y-axis|16; no-Frame|32; no-Lines|64; Points-too|128; Splines|256; no-X-ticks|512; no-Y-ticks|1024; Same-ticks|2048"},
  {"plothraw",25,(void*)plothraw,10,"GGD0,L,"},
  {"plothsizes",0,(void*)plothsizes_flag,10,"D0,L,"},
  {"plotinit",99,(void*)initrect_gen,10,"vLD0,G,D0,G,D0,L,"},
***************
*** 284,290 ****
    "plotcursor(w): current position of cursor in rectwindow w",
    "plotdraw(list, {flag=0}): draw vector of rectwindows list at indicated x,y positions; list is a vector w1,x1,y1,w2,x2,y2,etc. . If flag!=0, x1, y1 etc. express fractions of the size of the current output device",
    "plotfile(filename): set the output file for plotting output. \"-\" redirects to the same place as PARI output",
!   "ploth(X=a,b,expr,{flags=0},{n=0}): plot of expression expr, X goes from a to b in high resolution. Both flags and n are optional. Binary digits of flags mean : 1 parametric plot, 2 recursive plot, 8 omit x-axis, 16 omit y-axis, 32 omit frame, 64 do not join points, 128 plot both lines and points, 256 use cubic splines, 512/1024 no x/y ticks, 2048 plot all ticks with the same length. n specifies number of reference points on the graph (0=use default value). Returns a vector for the bounding box",
    "plothraw(listx,listy,{flag=0}): plot in high resolution points  whose x (resp. y) coordinates are in listx (resp. listy). If flag is 1, join points, other non-0 flags should be combinations of bits 8,16,32,64,128,256 meaning the same as for ploth()",
    "plothsizes({flag=0}): returns array of 6 elements: terminal width and height, sizes for ticks in horizontal and vertical directions, width and height of characters.  If flag=0, sizes of ticks and characters are in pixels, otherwise are fractions of the screen size",
    "plotinit(w,{x=0},{y=0},{flag=0}): initialize rectwindow w to size x,y. If flag!=0, x and y express fractions of the size of the current output device. x=0 or y=0 means use the full size of the device",
--- 284,290 ----
    "plotcursor(w): current position of cursor in rectwindow w",
    "plotdraw(list, {flag=0}): draw vector of rectwindows list at indicated x,y positions; list is a vector w1,x1,y1,w2,x2,y2,etc. . If flag!=0, x1, y1 etc. express fractions of the size of the current output device",
    "plotfile(filename): set the output file for plotting output. \"-\" redirects to the same place as PARI output",
!   "ploth(X=a,b,expr,{flags=0},{n=0}): plot of expression expr, X goes from a to b in high resolution. Both flags and n are optional. Binary digits of flags mean (and can be replaced by punctyation-separated mneumonics): 1=Parametric, 2=Recursive, 8=no-X-axis, 16=no-Y-axis, 32=no-Frame, 64=no-Lines (do not join points), 128=Points-too (plot both lines and points), 256=Splines (use cubic splines), 512=no-X-ticks, 1024= no-Y-ticks, 2048=Same-ticks (plot all ticks with the same length). n specifies number of reference points on the graph (0=use default value). Returns a vector for the bounding box",
    "plothraw(listx,listy,{flag=0}): plot in high resolution points  whose x (resp. y) coordinates are in listx (resp. listy). If flag is 1, join points, other non-0 flags should be combinations of bits 8,16,32,64,128,256 meaning the same as for ploth()",
    "plothsizes({flag=0}): returns array of 6 elements: terminal width and height, sizes for ticks in horizontal and vertical directions, width and height of characters.  If flag=0, sizes of ticks and characters are in pixels, otherwise are fractions of the screen size",
    "plotinit(w,{x=0},{y=0},{flag=0}): initialize rectwindow w to size x,y. If flag!=0, x and y express fractions of the size of the current output device. x=0 or y=0 means use the full size of the device",
*** ././src/language/init.c.orig	Mon Jun 10 00:45:47 2002
--- ././src/language/init.c	Tue Jul  2 17:03:44 2002
***************
*** 1888,1893 ****
--- 1888,1895 ----
   *     The unquoted components can be of any pari type (converted according to
   *     the current output format)
   *  s* any number of strings (see s)
+  *  M  Mneumonic or a flag (converted to a long); description follows
+  *	 after \n at the end of the argument description
   *  D  Has a default value. Format is "Dvalue,type," (the ending comma is
   *     mandatory). Ex: D0,L, (arg is long, 0 by default).
   *     Special syntax:
*** ././src/language/anal.c.orig	Sat Jun  8 16:37:45 2002
--- ././src/language/anal.c	Tue Jul  2 17:38:33 2002
***************
*** 82,87 ****
--- 82,276 ----
  static long br_status, br_count;
  static GEN br_res = NULL;
  
+ /* TEMPLATE is assumed to be ";"-separated list of items.  Each item
+    may have one of the following forms: id=value id==value id|value id&~value.
+    Each id consists of alphanum characters, dashes and underscores.
+    IDs are case-sensitive.
+ 
+    ARG consists of several IDs separated by punctuation (and optional
+    whitespace).  Each modifies the return value in a "natural" way: an
+    ID from id=value should be the first in the sequence and sets RETVAL to
+    VALUE (and cannot be negated), ID from id|value bit-ORs RETVAL with
+    VALUE (and bit-ANDs RETVAL with ~VALUE if negated), ID from
+    id&~value behaves as if it were noid|value, ID from
+    id==value behaves the same as id=value, but should come alone.
+ 
+    For items of the form id|value and id&~value negated forms are
+    allowed: either when arg looks like no[-_]id, or when id looks like
+    this, and arg is not-negated.
+  */
+ 
+ #define A_ACTION_ASSIGN		1
+ #define A_ACTION_SET		2
+ #define A_ACTION_UNSET		3
+ 
+ #define PARSEMNU_TEMPL_TERM_NL	1
+ #define PARSEMNU_ARG_WHITESP	2
+ 
+ #define IS_ID(c)	(isalnum((int)c) || ((c) == '_') || ((c) == '-'))
+ #define STMT_START	do
+ #define STMT_END	while (0)
+ #define ERR(reason)	STMT_START {	\
+     if (failure && first) {		\
+ 	*failure = reason; *failure_arg = NULL; return 0;		\
+     } else err(talker,reason); } STMT_END
+ #define ERR2(reason,s)	STMT_START {	\
+     if (failure && first) {		\
+ 	*failure = reason; *failure_arg = s; return 0;		\
+     } else err(talker,reason,s); } STMT_END
+ 
+ unsigned long
+ parse_option_string(char *arg, char *template, long flag, char **failure, char **failure_arg)
+ {
+     unsigned long retval = 0;
+     char *etemplate = NULL;
+ 
+     if (flag & PARSEMNU_TEMPL_TERM_NL)
+ 	etemplate = strchr(template, '\n');
+     if (!etemplate)
+ 	etemplate = template + strlen(template);
+ 
+     if (failure)
+ 	*failure = NULL;
+     while (1) {
+ 	long numarg;
+ 	char *e, *id;
+ 	char *negated;			/* action found with 'no'-ID */
+ 	int negate;			/* Arg has 'no' prefix removed */
+ 	int l, action = 0, first = 1, singleton = 0;
+ 	char b[80], *buf, *inibuf;
+ 
+ 	if (flag & PARSEMNU_ARG_WHITESP)
+ 	    while (isspace((int)*arg)) arg++;
+ 	if (!*arg)
+ 	    break;
+ 	e = arg;
+ 	while (IS_ID(*e))
+ 	    e++;
+ 	/* Now the ID is whatever is between arg and e. */
+ 	l = e - arg;
+ 	if (l >= sizeof(b))
+ 	    ERR("id too long in a stringified flag");
+ 	if (!l)				/* Garbage after whitespace? */
+ 	    ERR("a stringified flag does not start with an id");
+ 	strncpy(b, arg, l);
+ 	b[l] = 0;
+ 	arg = e;
+ 	e = inibuf = buf = b;
+ 	while (('0' <= *e) && (*e <= '9'))
+ 	    e++;
+ 	if (*e == 0)
+ 	    ERR("numeric id in a stringified flag");	
+ 	negate = 0;
+ 	negated = NULL;
+       find:
+ 	id = template;
+ 	while ((id = strstr(id, buf)) && id < etemplate) {
+ 	    if (IS_ID(id[l])) {		/* We do not allow abbreviations yet */
+ 		id = id + l;		/* False positive */
+ 		continue;
+ 	    }
+ 	    if ((id >= template + 2) && (IS_ID(id[-1]))) {
+ 		char *s = id;
+ 
+ 		if ( !negate && s >= template+3
+ 		     && ((id[-1] == '_') || (id[-1] == '-')) )
+ 		    s--;
+ 		/* Check whether we are preceeded by "no" */
+ 		if ( negate		/* buf initially started with "no" */
+ 		     || (s < template+2) || (s[-1] != 'o') || (s[-2] != 'n')
+ 		     || (s >= template+3 && IS_ID(s[-3]))) {
+ 		    id = id + l;		/* False positive */
+ 		    continue;
+ 		}
+ 		/* Found noID in the template! */
+ 		negated = id + l;
+ 		id = id + l;
+ 		continue;		/* Try to find without 'no'. */
+ 	    }
+ 	    /* Found as is */
+ 	    id = id + l;
+ 	    break;
+ 	}
+ 	if ( !id && !negated && !negate 
+ 	     && (l > 2) && buf[0] == 'n' && buf[1] == 'o' ) {
+ 	    /* Try to find the flag without the prefix "no". */
+ 	    buf += 2;
+ 	    if ((buf[0] == '_') || (buf[0] == '-'))
+ 		buf++;
+ 	    negate = 1;
+ 	    if (buf[0])
+ 		goto find;
+ 	}
+ 	if (!id && negated) {	/* Negated and AS_IS forms, prefer AS_IS */
+ 	    id = negated;	/* Otherwise, use negated form */
+ 	    negate = 1;
+ 	}
+ 	if (!id)
+ 	    ERR2("Unrecognized id '%s' in a stringified flag", inibuf);
+ 	if (singleton && !first)
+ 	    ERR("Singleton id non-single in a stringified flag");
+ 	if (id[0] == '=') {
+ 	    if (negate)
+ 		ERR("Cannot negate id=value in a stringified flag");
+ 	    if (!first)
+ 		ERR("Assign action should be first in a stringified flag");
+ 	    action = A_ACTION_ASSIGN;
+ 	    id++;
+ 	    if (id[0] == '=') {
+ 		singleton = 1;
+ 		id++;
+ 	    }
+ 	} else if (id[0] == '^') {
+ 	    if (id[1] != '~')
+ 		err(talker, "Unrecognized action in a template");
+ 	    id += 2;
+ 	    if (negate)
+ 		action = A_ACTION_SET;
+ 	    else
+ 		action = A_ACTION_UNSET;
+ 	} else if (id[0] == '|') {
+ 	    id++;
+ 	    if (negate)
+ 		action = A_ACTION_UNSET;
+ 	    else
+ 		action = A_ACTION_SET;
+ 	}
+ 
+ 	e = id;
+ 
+ 	while ((*e >= '0' && *e <= '9')) e++;
+ 	while (isspace((int)*e))
+ 	    e++;
+ 	if (*e && (*e != ';') && (*e != ','))
+ 	    err(talker, "Non-numeric argument of an action in a template");
+ 	numarg = atol(id);		/* Now it is safe to get it... */
+ 	switch (action) {
+ 	case A_ACTION_SET:
+ 	    retval |= numarg;
+ 	    break;
+ 	case A_ACTION_UNSET:
+ 	    retval &= ~numarg;
+ 	    break;
+ 	case A_ACTION_ASSIGN:
+ 	    retval = numarg;
+ 	    break;
+ 	default:
+ 	    ERR("error in parse_option_string");
+ 	}
+ 	first = 0;
+ 	if (flag & PARSEMNU_ARG_WHITESP)
+ 	    while (isspace((int)*arg))
+ 		arg++;
+ 	if (*arg && !(ispunct((int)*arg) && *arg != '-'))
+ 	    ERR("Junk after an id in a stringified flag");
+ 	/* Skip punctuation */
+ 	if (*arg)
+ 	    arg++;
+     }
+     return retval;
+ }
+ 
  /*  Special characters:
   *     ' ', '\t', '\n', '\\' are forbidden internally (suppressed by filtre).
   *     { } are forbidden everywhere and will be used to denote optional
***************
*** 1485,1490 ****
--- 1674,1680 ----
      void *call = ep->value;
      GEN argvec[9];
      matcomp *init[9];
+     char *flags = NULL;
  
      deriv = (*analyseur == '\'' && analyseur[1] == '(') && analyseur++;
      if (*analyseur == '(')
***************
*** 1514,1520 ****
      }
      if (*s == 'p') { argvec[i++] = (GEN) prec; s++; }
  
!     while (*s)
        switch (*s++)
        {
  	case 'G': /* GEN */
--- 1704,1710 ----
      }
      if (*s == 'p') { argvec[i++] = (GEN) prec; s++; }
  
!     while (*s && *s != '\n')
        switch (*s++)
        {
  	case 'G': /* GEN */
***************
*** 1583,1588 ****
--- 1773,1796 ----
  	  *bp++ = 0; argvec[i++] = (GEN) buf;
  	  break;
  
+ 	case 'M': /* Mneumonic flag */
+ 	  match_comma(); argvec[i] = expr();
+           if (br_status) err(breaker,"here (argument reading)");
+ 	  if (typ(argvec[i]) == t_STR) {
+ 	      if (!flags)
+ 		  flags = ep->code;
+ 	      flags = strchr(flags, '\n'); /* Skip to the following '\n' */
+ 	      if (!flags)
+ 		  err(talker, "not enough flags in string function signature");
+ 	      flags++;
+ 	      argvec[i] = (GEN) parse_option_string((char*)(argvec[i] + 1),
+ 			  flags, PARSEMNU_ARG_WHITESP | PARSEMNU_TEMPL_TERM_NL,
+ 			  NULL, NULL);
+ 	  } else
+ 	      argvec[i] = (GEN)itos(argvec[i]);
+ 	  i++;
+           break;
+ 
  	case 's': /* expanded string; empty arg yields "" */
  	  match_comma();
  	  if (*s == '*') /* any number of string objects */
***************
*** 2523,2531 ****
      /* Optimized for G and p. */
      while (*s == 'G') { match_comma(); skipexpr(); s++; }
      if (*s == 'p') s++;
!     while (*s) switch (*s++)
      {
!       case 'G': case 'n': case 'L':
          match_comma();
          if (*analyseur == ',' || *analyseur == ')') break;
          skipexpr(); break;
--- 2731,2739 ----
      /* Optimized for G and p. */
      while (*s == 'G') { match_comma(); skipexpr(); s++; }
      if (*s == 'p') s++;
!     while (*s && *s != '\n') switch (*s++)
      {
!       case 'G': case 'n': case 'L': case 'M':
          match_comma();
          if (*analyseur == ',' || *analyseur == ')') break;
          skipexpr(); break;
***************
*** 2583,2588 ****
--- 2791,2798 ----
          match('='); matchcomma = 0; break;
        case ',':
          matchcomma=1; break;
+       case '\n':			/* Before the mneumonic */
+ 	break;
        default:
          err(bugparier,"skipidentifier (unknown code)");
      }