Ilya Zakharevich on Fri, 11 Jan 2002 21:21:20 -0500


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

[PATCH 2.2.2] mneumonics for flags


This patch adds a possibility to use mneumonic flags instead of
numberic flags.  E.g., instead of 

  6+1024

one can use

  Runge-Kutta/Known-Prime

(for a silly example ;-), if the correponding function descriptor uses
"MRunge-Kutta=6;Known-Prime|1024" instead of "L".

There are 3 allowed actions after "M":

  id==value		    # id should appear alone in the argument
  id=value		    # id should appear first, and may be followed
			    # by flags of two remaining forms
  id|value		    # bit-or the flag with value
  id&~value		    # bit-and the flag with ~value

The options of the last two types may be "negated" by prepending one
of "no", "no_" or "no-".  Either the descriptor in the "M"-string may
be negated, or the mneumonic.  In othere words, the descriptors

  id|value
  no-id&~value
  id|value;no-id&~value

behave identically: you can use either "id", or "no-id" in your mneumonic.

[So far I only instrumented ploth() - see the first chunk, and did
 only rudimentary testing.]

Note that "front-ends" to PARI which read the descriptors need to be
modifed to support "Mjunk," the same way as "L," (e.g., Math::Pari).
Note also that there are some shortcomings:

 a) For backward compatibility, if the first word of the argument is
    not a known flag, we read the argument as a PARI expression.  Thus
    one does not get meaningful error messages for the first word;

 b) Allowing dashes in mneumonics is not 100% intuitive (taking into
    account that the argument may be an expression too);

 c) This is a (minor) CPU burner even if you do not use mneumonic
    flags (due to the need to check the first word for being a
    mneumonic, and skipping over the chars between "M" and ",");

 d) If falling back to expression-mode, we use readlong(), which
    behaves as skipexpr() - while we do skipseq() in the other modes;
    so an expression with ";" will give a non-intuitive result.  [This
    is easily fixable.]

 e) mneumonics may be separated by an arbitrary punctuation, except
    "," and "-".  Again, this may lead to wrong expectations about '*'
    being an opposite of "/" etc.

Enjoy,
Ilya

--- ./src/gp/highlvl.c-pre	Thu Dec  6 09:53:14 2001
+++ ./src/gp/highlvl.c	Fri Jan 11 15:07:02 2002
@@ -238,7 +238,7 @@ entree functions_highlevel[]={
 {"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,"},
+{"ploth",99,(void*)ploth,10,"V=GGIpD0,MParametric|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,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,"},
@@ -278,7 +278,7 @@ char *helpmessages_highlevel[]={
   "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",
+  "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-pre	Thu Jan 10 03:39:06 2002
+++ ./src/language/init.c	Fri Jan 11 15:05:20 2002
@@ -1913,6 +1913,7 @@ geni(void) { return gi; }
  *     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 up to ','
  *  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-pre	Wed Jan  9 07:30:02 2002
+++ ./src/language/anal.c	Fri Jan 11 17:45:44 2002
@@ -83,6 +83,186 @@ static entree *check_new_fun;
 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 IS_ID(c)	(isalnum((int)c) || ((c) == '_') || ((c) == '-'))
+#define STMT_START	do
+#define STMT_END	while (0)
+#define ERR2(a1,a2)	STMT_START {	\
+    if (flag && first) {		\
+	*failure = 1; return 0;		\
+    } else err(a1,a2); } STMT_END
+#define ERR3(a1,a2,a3)	STMT_START {	\
+    if (flag && first) {		\
+	*failure = 1; return 0;		\
+    } else err(a1,a2,a3); } STMT_END
+
+unsigned long
+parse_option_string(char *arg, char *template, long flag, long *failure)
+{
+    unsigned long retval = 0;
+
+    if (flag)
+	*failure = 0;
+    while (1) {
+	long numarg;
+	char *e, *id;
+	char *negated;			/* action found with 'no'-ID */
+	int negate;			/* Arg has 'no' prefix removed */
+	int l, action, first = 1, singleton = 0;
+	char b[80], *buf, *inibuf;
+
+#ifdef WHITESPACE_INPUT_ALLOWED
+	while (isspace((int)*arg)) arg++;
+#endif
+	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))
+	    ERR2(talker, "id too long in a stringified flag");
+	if (!l)				/* Garbage after whitespace? */
+	    ERR2(talker, "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)
+	    ERR2(talker, "numeric id in a stringified flag");	
+	negate = 0;
+	negated = NULL;
+      find:
+	id = template;
+	while ((id = strstr(id, buf))) {
+	    if (IS_ID(id[l])) {
+		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 ASIS forms, prefer ASIS */
+	    id = negated;	/* Otherwise, use negated form */
+	    negate = 1;
+	}
+	if (!id)
+	    ERR3(talker, "Unrecognized id '%s' in a stringified flag", inibuf);
+	if (singleton && !first)
+	    ERR2(talker, "Singleton id non-single in a stringified flag");
+	if (id[0] == '=') {
+	    if (negate)
+		ERR2(talker, "Cannot negate id=value in a stringified flag");
+	    if (!first)
+		ERR2(talker, "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;	    
+	}
+	first = 0;
+#ifdef WHITESPACE_INPUT_ALLOWED
+	while (isspace((int)*arg))
+	    arg++;
+#endif
+	if (*arg && !(ispunct((int)*arg) && *arg != '-'))
+	    ERR2(talker, "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
@@ -1472,7 +1652,7 @@ identifier(void)
   {
     char *s = ep->code, *oldanalyseur = NULL, *buf, *limit, *bp;
     unsigned int ret, noparen, has_pointer=0;
-    long fake;
+    long fake, result;
     void *call = ep->value;
     GEN argvec[9];
     matcomp *init[9];
@@ -1574,6 +1754,24 @@ identifier(void)
 	  *bp++ = 0; argvec[i++] = (GEN) buf;
 	  break;
 
+	case 'M': /* Mneumonics or flag */
+	  match_comma(); mark.raw = analyseur;
+	  skipseq();
+          bp = init_buf(analyseur - mark.raw, &buf,&limit);
+	  strncpy(buf, mark.raw, analyseur - mark.raw);
+	  buf[analyseur - mark.raw] = 0;
+	  /* Try processing as a flag */
+	  result = parse_option_string(buf, s, 1, &fake);
+	  while (*s && *s != ',')
+	      s++;
+	  if (fake) {			/* Error, retry as an expression */
+	      analyseur = mark.raw;
+	      argvec[i++] = (GEN) readlong();
+	      break;
+	  }
+	  argvec[i++] = (GEN) result;
+	  break;
+
 	case 's': /* expanded string; empty arg yields "" */
 	  match_comma();
 	  if (*s == '*') /* any number of string objects */
@@ -2525,6 +2723,12 @@ skipidentifier(void)
           if (*analyseur == ',' || *analyseur == ')') break;
           analyseur++;
         }
+        break;
+      case 'M':
+        match_comma();
+	skipseq();	
+	while (*s && *s != ',')
+	    s++;
         break;
       case 's':
         match_comma();