#!../src/tops  -i -s ../sys  -u ../usr
/*
Program Tops - a stack-based computing environment
Copyright (C) 1999-2006  Dale R. Williamson

Author: Dale R. Williamson <dale.williamson@prodigy.net>

File test/parser  May 2005
 Test the infix parser
 Also see file test/infix_equ for analysis using infix.
*/

<< \ This file begins with postfix

nl "Testing the infix parser" . nl 

\-----------------------------------------------------------------------

\ Tokens.

syspath "tok.n" cat "FILE" book

nl "Tokens from file " FILE cat ":" cat . nl 
FILE asciiload vol2str 64 ".out" >stk chop 3 indent . nl

nl "  Getting the tokens:" . nl 
FILE 0 "parse_driver" >stk 3 indent . nl

\-----------------------------------------------------------------------

\ Parsing 1:  Parsing a file.

syspath "exp.n" cat "FILE" book

nl "Parsing infix C expressions on file " FILE cat ":" cat . nl
FILE psource

EXP_ERRORS 
IF " Errors parsing expressions in file exp.n:" . EXP_ERRORS .i nl
THEN

\-----------------------------------------------------------------------
 
\ Parsing 2: Multiple output arguments.

   << nl "Multiple output arguments:" . nl nl >>

   if(missing("symmetric")) source("mmath.v"); 

   ERRORS = EXP_ERRORS;

   seedset(seed0);
   Kgg=symmetric((fac1=2)*identity((g=100))*(fac2=4)+random(g,g));

   nl(dot(" Kgg infix:"));
// main("Kgg 1st 6 items dup submat .m nl nl");
   nl(nl(.m(Kgg[1:6,1:6])));

   main(<< \ Showing brace-quote to gather postfix, and main to run it:
   {"
      seed0 seedset
      fac1 g identity * fac2 * g g random + symmetric "KggPost" book

      " Kgg postfix:" . nl
      KggPost 1st 6 items dup submat .m nl nl
   "} >>);

/* Compare the postfix and infix versions: */
   (check,rmax,cmax)=maxfetch(abs(KggPost-Kgg));
   if(check != 0) (nl(dot(" Error multiple output args")), ERRORS++);
   else nl(dot(" Ok multiple output args"));

   Rao=Sao=dense(sprand(g,1,.8)); /* a-set dofs at the zeroes in Rao */

/* Storing four output items from word partition into named items on
   the left hand side of an equate: */
   (Kaa,Koa,Kao,Koo)=partition(Kgg,Rao,Rao);

/* Merging symmetric partitions into another matrix (the transpose 
   operator ' is used for Kao=Koa'): */
   Kgg1=merge(Kaa,Koa,Koa',Koo,Rao,Rao);

/* Check the max abs difference between original and merged: */
   if(check != 0) (nl(dot(" Error merge")), ERRORS++);
   else nl(dot(" Ok matrix merge"));
/*

------------------------------------------------------------------------

   Parsing 3: bracket tests.

   Brackets are used for constructing arrays.  Inside brackets, comma
   and semicolon are operators.  Semicolon piles two submatrices on top
   of each other, and comma parks them beside each other. 
    
   Precedence of semicolon and comma can be compared to precedence of
   math operators + and *.  In mathematical operations, * has higher
   precedence than + and so multiplcation is performed before addition
   unless there are parentheses to control the operations.
   
   In terms of precedence, but controlled by [ ] instead of ( ), semi-
   colon is to + as comma is to *.  Here are some examples:

      Precedence for numbers:     A=((1+2)*3)   B=(1+2*3)

      Precedence for submatrices: A=[[a;b],c]   B=[a;b,c]

      (Precedence for submatrices also requires the appropriate rows 
      and columns to be compatible)

   Within brackets, nested equates must be enclosed in parentheses.
*/
<< nl "Creating matrices:" . nl nl >>

   R=[1 , 2 ;  3 , 4  ;  5 , 6  ;  7 , 8 ];
   nl(dot(sp("Four-by-2 matrix R input by rows:")));
   nl(dot(mtext(R)));
   R1=[<< 1 8 items 4 matrix >>]; /* postfix makes R1 for checking */
   (check,rmax,cmax)=maxfetch(abs(R-R1));
   if(check != 0) (nl(dot(" Error checking R")), ERRORS++);
   else nl(dot(" Ok checking R"));

// Brackets to force precedence of semicolon over comma:
   C=[[1 ; 2] , [3 ; 4] , [5 ; 6] , [7 ; 8] ];
   nl(dot(sp("Two-by-4 matrix C input by columns:")));
   nl(dot(mtext(C)));
   C1=[<< 1 8 items 2 fold >>]; /* postfix makes C1 for checking */
   (check,rmax,cmax)=maxfetch(abs(C-C1));
   if(check != 0) (nl(dot(" Error checking C1")), ERRORS++);
   else nl(dot(" Ok checking C"));

   B=[(C=[[1;2], (D=[3;4])]), [5;6], [7;8]];
   nl(dot(sp("Two-by-4 matrix B input by columns:")));
   nl(dot(mtext(B)));
   B1=[<< 1 8 items 2 fold >>]; /* postfix makes B1 for checking */
   (check_B,rmax,cmax)=maxfetch(abs(B-B1));
   if(check_B != 0) (nl(dot(" Error creating B")), ERRORS++);
   else nl(dot(" Ok creating B"));

   nl(dot(sp("Matrix C, equated 2-by-2 submatrix of B:")));
   nl(dot(mtext(C)));

   C1=B1[,1:2]; // all rows, columns 1 and 2 of B1

   (check_C,rmax,cmax)=maxfetch(abs(C-C1));
   if(check_C != 0) (nl(dot(" Error creating C")), ERRORS++);
   else nl(dot(" Ok creating C"));

   nl(dot(sp("Matrix D, equated 2-by-1 submatrix of C:")));
   nl(dot(mtext(D)));

   D1=[<< B1 2nd catch >>];

   (check_D,rmax,cmax)=maxfetch(abs(D-D1));
   if(check_D != 0) (nl(dot(" Error creating D")), ERRORS++);
   else nl(dot(" Ok creating D"));

/* Building matrices inside brackets with a function. */
   seedset(seed0);
   A=[(-(B=random(5,2))');-[random(4,2),-(C=random(4,3))]];
<< 
\  Create the same matrices with straight postfix:
   seed0 seedset
   5 2 random dup "B1" book transpose negate (hTop)
   4 2 random 4 3 random "C1" book C negate park negate (hBot)
   (hTop hBot) pile "A1" book
>>
   nl();
/* Check infix against postfix: */
   if(null?(A-A1) && !null?(!(B-B1)) && null?(C-C1))
      dot(" Ok brackets ");
   else
      (dot(" Error brackets"), ERRORS++);
   nl();

/* Multiple bracket uses: making matrices and making indices; 
   functions making indices */

   base_save = xbase;
   main("0based");

   D = [10*reversed(1:100) , 5*reversed(complex(1:100,dup))];

   R = [sort([Re(D[*,1*ndx(1)]) , ndx(1:rows(D))],yes)][*,1*ndx(2)];

   S1 = D[R , ndx(2:cols(D))]; // extract rows in sorted order

   S4 = D[ndx(1:10), ndx(1:2)] - 
        D[ndx(thrulist(1,10)), ndx(thrulist(1,2))];

/* These work too; brackets around sort() are optional, and * for 
   all-row count is optional: */

   R = sort([Re(D[,0+ndx(1)]) , ndx(1:rows(D))],yes)[,0+ndx(2)];

   S2 = D[R , ndx(2:cols(D))]; // extract rows in sorted order

   A=complex(1,2);
   S3 = (!Re(complex(-Re(A),-Im(A))==-A));

   NULL = S1 - reversed(complex(1:100,dup)) +
          S2 - reversed(complex(1:100,dup)) +
          S3; + (!(null?(S4)));

   if(null?(NULL))
      dot(" Ok bracket indices");
      else
         (dot(" Error bracket indices; check S1, S2, S3"), ERRORS++);
      nl();
   indexbase(base_save);

/* Bracket notation for fetching array elements

     >> T = [1,2,3,4 ; 10,20,30,40]'; T .m
       Row 1:        1       10
       Row 2:        2       20
       Row 3:        3       30
       Row 4:        4       40

   Reading right to left in the following expression for V, the 2nd
   term in the 2:3 pair of the 2nd:4th elements of column 2 of T is 
   40:
      >> V = T[*,2][2:4][2:3][2]; V .m
       Row 1:       40

   Each bracked expression operates on the matrix that results from
   operations on its left. */

   T = [1,2,3,4 ; 10,20,30,40]'; V = T[*,2][2:4][2:3][2];

   if(V==40) dot(" Ok fetching array elements");
   else (dot(" Error fetching matrix elemets"), ERRORS++);
   nl();

/* Storing terms into matrices. */
   f1 = [2 ; 1];
   f2 = [3 ; 10];
   buf = @f1[2] : @f2[2];
   S1 = @(buf[1]==1 && buf[10]==10);

   /* Storing into a matrix that is in the library of a word. */

      function Cfunc() {
         { A=random(7,4); }
         X[2,1] = x = urn;
      }
      B=random(4,2);

      Cfunc; /* run Cfunc() to test former bug storing into X */

   /* This forces a new column on matrix Cfunc.A: */
      Cfunc.A[2:=rows(B), 4:=cols(B)] = D = 100*B; 

   ALL_NULL = [ null?(<< D 100 B * - >>)
              ; null?(<< "Cfunc" "A" yank 2 B rows items
                4 B cols items submat B 100 * - >>)
              ; S1
              ; null?(Cfunc.X - [0 ; Cfunc.x])
              ];

   if(totals(ALL_NULL) == rows(ALL_NULL)*true) 
      dot(" Ok storing matrix elements");
   else 
      (dot(" Error storing matrix elements"), ERRORS++);
   nl();

/*----------------------------------------------------------------------

   Parsing 4: math operators.

  Test math operators +=, -=, *=, /+:
*/
   function (p) = Real(r, c) { p = random(r, c); }
   function (p) = Comp(r, c) { p = complex(random(r, c), random(r, c));}

   r = 60; c = 50;
   U = Real(r,c); V = Real(r,c); A = U+V - (R=U+=V);
   U = Real(r,c); V = Real(r,c); B = U-V - (R=U-=V);
   U = Real(r,c); V = Real(r,c); C = U.*V - (R=U*=V);
   U = Real(r,c); V = Real(r,c); D = U./V - (R=U/=V);

   N = null?(A) + null?(B) + null?(C) + null?(D);

   U = Comp(r,c); V = Comp(r,c); A = U+V - (R=U+=V);
   U = Comp(r,c); V = Comp(r,c); B = U-V - (R=U-=V);
   U = Comp(r,c); V = Comp(r,c); C = U.*V - (R=U*=V);
   U = Comp(r,c); V = Comp(r,c); D = U./V - (R=U/=V);

   N += null?(A) + null?(B) + null?(C) + null?(D);

   U = Real(r,c); V = Comp(r,c); A = U+V - (R=U+=V);
   U = Real(r,c); V = Comp(r,c); B = U-V - (R=U-=V);
   U = Real(r,c); V = Comp(r,c); C = U.*V - (R=U*=V);
   U = Real(r,c); V = Comp(r,c); D = U./V - (R=U/=V);

   N += null?(A) + null?(B) + null?(C) + null?(D);

   U = Comp(r,c); V = Real(r,c); A = U+V - (R=U+=V);
   U = Comp(r,c); V = Real(r,c); B = U-V - (R=U-=V);
   U = Comp(r,c); V = Real(r,c); C = U.*V - (R=U*=V);
   U = Comp(r,c); V = Real(r,c); D = U./V - (R=U/=V);

   N += null?(A) + null?(B) + null?(C) + null?(D);

   A = fill(1.0, 5, 2); V = [1:5, 3*(1:5), 9*(1:5)];
   B = (V \* A);
  << 1.0 5 2 fill "A" book 1 5 : dup 3 * over 9 * 3 parkn "V" book >>
  << V A diagpre "B1" book >>

   N += null?(B1 - B);

   A = fill(1.0, 5, 2); V = [1:2, 3*(1:2), 9*(1:2)];
   B = (A *\ V);
   << 1.0 5 2 fill "A" book 1 2 : dup 3 * over 9 * 3 parkn "V" book >>
   << A V diagpost "B1" book >>

   N += null?(B1 - B);

   if(N == 18*true) dot(" Ok operators += -= *= /= .* ./ \* *\");
   else (dot(" Error in operators += -= *= /="), ERRORS++);
   nl();

/*----------------------------------------------------------------------

   Parsing 5: write and read binary files. */

   nl();
   dot(" Write and read binary files")
   nl();

/* The following sources postfix code from file easy_io.v and runs a 
   couple of the words it provides to write and read a binary file: */
   source(catpath(usrpath,"easy_io.v"));

   to_file((A1=random(100,60)),(FILE="/tmp/easy.bin"));

   A=from_file(FILE);

   if(null?(A-A1)) dot(" Ok matrix from binary easy_io file");
   else (dot(" Error matrix from binary easy_io file"), ERRORS++);
   nl();
   delete(FILE);

/* The following uses the op4 file utilities: */
   saveop4(FILE, (A = random(20,10)), 0);
   B = loadop4(FILE, 1, 0)[1:5, 3:6];

   if(null?(A[1:5, 3:6] - B)) dot(" Ok matrix from binary op4 file");
   else (dot(" Error matrix from binary op4 file"), ERRORS++);
   nl();
   delete(FILE);
/*
------------------------------------------------------------------------

   Parsing 6: eigenanalysis using Lapack dsygv().

   Building symmetric matrices and running function dsygv().  This is 
   an example of a function that returns two arguments, in this case a 
   matrix of eigenvalues and a matrix of eigenvectors.

*/ 
<< "dsygv" exists? \ skip to 7 if no eigenanalysis word
IF >>

<< nl "Eigenanalysis of symmetric matrices" . nl nl >>
   seedset(seed0);
   M = identity((size=4));

/* Make K, a positive definite symmetric matrix: */
   K = 10*identity(size) + (random(size,size)+dup')/2;
   (Phi, Omeg) = dsygv(M, K);

   dot("Eigenvalues: " + crowd(mtext(Omeg')));
   nl();
   dot(pile("Eigenvectors: ", mtext(Phi)));
   nl();
<< 
\  Also create M and K and do the problem with straight postfix, no 
\  parsing:
   seed0 seedset
   4 "size" book, size identity (hM)
   10 size identity * size size random dup bend + 2 / + (hK)
   (hM hK) dsygv (hPhi hOmeg) "Omeg1" book "Phi1" book 
>>
/* Compare infix and postfix results: */
   (Z1,rmax,cmax) = maxfetch(abs(Phi-Phi1));
   (Z2,rmax,cmax) = maxfetch(abs(Omeg-Omeg1));

   if(Z1==0 && (!Z2)!=0)
      dot(nl(" Ok postfix and infix eigenanalysis"));
   else
      dot(nl(" Error postfix and infix eigenanalysis"));
   nl();
   nl();
/*
------------------------------------------------------------------------

   Parsing 7: Creating word reig from a parsed function and running it.

   This example tests dot notation in which TITLE and nModes are banked
   into reig before it runs, and ET is extracted after. */

   nl(dot("Parsing reig(), creating word reig and running it"));
   nl();

function (Phi, Omeg) = reig(M, K)
{
   {
      nModes_def = 10;
      nModes = UDEF;
      TITLE = "";
   }
   if(nModes == UDEF) nModes = min(nModes_def, rows(M));

   if(nModes > 100) return(purged, purged);

   MODES = (1:nModes); 
   nModes = UDEF; // reset to undefined 

   dot(TITLE);
   nl();

   push(time);

   (Phi, Omeg) = dsygv(M, K);

   ET = 1E6*(time - pull()); // elapsed microseconds

   return(catch(Phi,MODES), reach(Omeg,MODES));
}

/* Given symbols such as these for function reig():

      function (Phi, Omeg) = reig(M, K)
      {

         ... function body 

      }

   the parser converts the infix text enclosed by braces into postfix 
   text for an inline.  At this point, the parser has created the text
   for inline reig, and from it the program has created word reig.
*/
// Show the initial library of word reig:
// nl(wholib("reig"));

// Set up an eigenvalue problem for word reig:
   seedset(seed0);
   M = identity(size=8);
   K = 10*identity(size) + (random(size,size)+dup')/2;

// Bank some initial data into XXX of reig using dot notation reig.XXX:
   reig.nModes = rows(M)/2;
//   reig.TITLE = cat("Case 1, ",cat("modes = ", intstr(reig.nModes))); 
   reig.TITLE = "Case 1, modes = " + intstr(reig.nModes); 

// Run reig(): 
   (Phi, Omeg) = reig(M, K);

// Display returned results, Omeg and Phi:
// dot(nl(cat("Eigenvalues: ", crowd(mtext(Omeg')))));
// dot(nl(pile("Eigenvectors: ", mtext(Phi))));

// Display ET extracted from reig using dot notation reig.XXX: 
   nl(dot("Elapsed microseconds: " + intstr(reig.ET)));

// Show the current library of reig:
// dot(nl("Compare this with the library shown before running:"));
// nl(wholib(nl("reig")));
/*
------------------------------------------------------------------------

   Parsing 8: Testing the parser symbols for comments.
*/
THEN >>

/*
   {#Sharp style

      /*C style (but nesting is allowed)*/

   #}
*/

#  Sharp single line
// Double slash single line

/*----------------------------------------------------------------------

   Parsing 9: Testing functions.
*/

   function (E) = func(M) {
   /* A function that uses native words for variables that are also
      in PRE__, RET__ and ret__ and does not create E to return. */
      {
         drop = 2;
         dup = 20;
      }
      return(10);
   }
   
/* Stack friendiness test: */
   D=(xx, 99, 100, func(1), depth); /* depth should equal 3 */

/* Yank precedence test: */
   function (E) = test_yank() {
   /* Verify that context is properly handled for string S in function 
      extract() (syn yank). */

      { W = "SERVER_ALLOW"; /* use library of word SERVER_ALLOW */

        clients1 = "clients"; 
        clients = "bad string"; /* causes error if context is wrong */
      }
      ERR = ercnt;

      extract("W", clients1);
      yank("W", "clients1");
      yank(W, clients1);
      yank(W, "clients"); /* error if extract() uses local context */

      W.clients;
      W.clients1;

      return(ercnt - ERR);
   }

   if(func("M")==10 && D==3 && !test_yank)
      dot(nl(" Ok function tests"));
   else
      dot(nl(" Error function tests"));
   nl();
   nl();

/*----------------------------------------------------------------------

   Parsing 10: Testing if() else() branching.

   In C, the general form of if() and else() is:

      if(expression) {
         statements ... 
      }
      else (expression) {
         statements ...
      }

   Braces are optional when there is only one statement.

   Here is an example of a function with C-like if() else() branching 
   envisioned for the parser:

   function (R) = testif(P) { 
   SEE BELOW FOR AN EXAMPLE LIKE THIS ONE THAT WORKS WITH THE PARSER IN 
   ITS CURRENT STAGE OF DEVELOPMENT
      if(P < 10) {
         A = P*[.25; .75];
         R = @totals(A) // operator @ fetches the NUM from MAT A(1,1)
      }
      else {
         A = P*[-.25; -.75];
         R = @totals(A);
      }
   }
/*
   In its present form, the parser only recognizes a single statement
   following if() and else().  It does not recognize braces to enclose
   a number of statements following if() or else(), so the example 
   above will not work.

   But there is a workaround to allow multiple statements, and it will
   continue to work after C-like braces are implemented.

   Restricted to a single statement, it is possible to use a paren list
   to enclose a number of statements for each branch and make it appear
   to the parser that there is a single statement.  Here is a descrip-
   tion of paren lists.

   A paren list encloses a group of statements separated by commas
   instead of semicolons:

      (statement1, statement2, ... statementN)

   The simplest paren list is just an argument list to a function. 

   Inside a paren list, equates must be enclosed within another set of 
   parentheses; and appending a semicolon makes a paren expression:

      (statement1, statement2, (A=[B,C]), ..., statementN);

   The parser works well with paren expressions summarized as: paren-
   theses followed by semicolon that surround a list of statements 
   separated by commas, with equate statements embedded in additional 
   parentheses.

   An important property of paren lists is that postfix operations can 
   be done inside them: see man ( or where("(").

   Below is an example using paren expressions to enclose multiple 
   statements in the if() and else() branches to make the parser in 
   its current form run the example above.  Statements are separated 
   by commas (instead of semicolons) and statements that are equates 
   (all of them in this example) are enclosed by additional parentheses.

   Statements written using paren expressions, as in the example below,
   will still work when C-like statement-enclosing braces are imple-
   mented. */

----------------------------------------------------------------------*/

   catmsg(no);

   function (R) = testif(P) {
      if(P < 10) (
         (A = P*[.25; .75]),
         (R = @totals(A)) // operator @ fetches the NUM from MAT A(1,1)
      );
      else (
         (A = P*[-.25; -.75]),
         (R = @totals(A))
      );
   }

   R1 = testif((p=+100))/p + 1;
   R2 = testif((p=-100))/p - 1;

   if(R1+R2==0) dot(nl(" Ok if() else() test"));
   else dot(nl(" Error if() else() test"));
   nl();

<<
\ Display a summary:
   nl nl ERRORS 0= 
   IF " Ok all infix parser tests" .
   ELSE " Errors infix parser:" . ERRORS .i
   THEN nl

   2 idle "*" 72 cats . nl
>>
/* ---------------------------------------------------------------------

   Appendix - Parser notes and examples

Matrix fetching and storing, 3-29-06.

Test cases:
C="A[*,*]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[*,*]=B"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[,*]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[,*]=B"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[,]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[,]=B"; T=asciify(parse(C)); nl(dot(C+": "+T));

C="A[1:5]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[1:5]=B"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[1:5,*]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[1:5,*]=B"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[*,1:5]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[*,1:5]=B"; T=asciify(parse(C)); nl(dot(C+": "+T));

C="A[thrulist(1,5)]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[thrulist(1,5)]=B"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[*,thrulist(1,5)]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[*,thrulist(1,5)]=B"; T=asciify(parse(C)); nl(dot(C+": "+T));

C="A[r,c]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[r,c]=B"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[items(1,10),items(2,4)]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[items(1,10),items(2,4)]=B"; T=asciify(parse(C)); nl(dot(C+": "+T));

C="A[[5;3;1]]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[[5;3;1]]=B"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[[2,1],[1,2,3]]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[[2,1],[1,2,3]]=B"; T=asciify(parse(C)); nl(dot(C+": "+T));

Running test cases:
>> nn
A[*,*]: "A" fetchif purged purged purged place1 
A[*,*]=B: "A" fetchif purged purged B place1 "A" book A 
A[,*]: "A" fetchif purged purged purged place1 
A[,*]=B: "A" fetchif purged purged B place1 "A" book A 
A[,]: "A" fetchif purged purged purged place1 
A[,]=B: "A" fetchif purged purged B place1 "A" book A 
A[1:5]: "A" fetchif 1 5 : purged purged place1 
A[1:5]=B: "A" fetchif 1 5 : purged B place1 "A" book A 
A[1:5,*]: "A" fetchif 1 5 : purged purged place1 
A[1:5,*]=B: "A" fetchif 1 5 : purged B place1 "A" book A 
A[*,1:5]: "A" fetchif purged 1 5 : purged place1 
A[*,1:5]=B: "A" fetchif purged 1 5 : B place1 "A" book A 
A[thrulist(1,5)]: "A" fetchif 1 5 thrulist purged purged place1 
A[thrulist(1,5)]=B: "A" fetchif 1 5 thrulist purged B place1 "A" book A 
A[*,thrulist(1,5)]: "A" fetchif purged 1 5 thrulist purged place1 
A[*,thrulist(1,5)]=B: "A" fetchif purged 1 5 thrulist B place1 "A" book A 
A[r,c]: "A" fetchif r c purged place1 
A[r,c]=B: "A" fetchif r c B place1 "A" book A 
A[items(1,10),items(2,4)]: "A" fetchif 1 10 items 2 4 items purged place1 
A[items(1,10),items(2,4)]=B: "A" fetchif 1 10 items 2 4 items B place1 "A" book A 
A[[5;3;1]]: "A" fetchif 5 3 pile 1 pile hand purged purged place1 
A[[5;3;1]]=B: "A" fetchif 5 3 pile 1 pile hand purged B place1 "A" book A 
A[[2,1],[1,2,3]]: "A" fetchif 2 1 park hand 1 2 park 3 park hand purged place1 
A[[2,1],[1,2,3]]=B: "A" fetchif 2 1 park hand 1 2 park 3 park hand B place1 "A" book A 


Improved matrix element fetching, 3-11-06.

Eliminate unnecessary fetching of all rows or all columns and put 
column operations before row operations for speed.

Test cases:
C="A[*,*]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[,*]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[,]"; T=asciify(parse(C)); nl(dot(C+": "+T));

C="A[1:5]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[1:5,*]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[*,1:5]"; T=asciify(parse(C)); nl(dot(C+": "+T));

C="A[thrulist(1,5)]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[*,thrulist(1,5)]"; T=asciify(parse(C)); nl(dot(C+": "+T));

C="A[r,c]"; T=asciify(parse(C)); nl(dot(C+": "+T));
C="A[items(1,10),items(2,4)]"; T=asciify(parse(C)); nl(dot(C+": "+T));

C="A[[5;3;1]]"; T=asciify(parse(C)); nl(dot(C+": "+T)); 
C="A[[2,1],[1,2,3]]"; T=asciify(parse(C)); nl(dot(C+": "+T));

Test case results before modifying prs.c:
A[*,*]: A 1st over rows items reach 1st over cols items catch
A[,*]: A 1st over rows items reach 1st over cols items catch
A[,]: A 1st over rows items reach
A[1:5]: A 1 5 : reach
A[1:5,*]: A 1 5 : reach 1st over cols items catch
A[*,1:5]: A 1st over rows items reach 1 5 : catch
A[thrulist(1,5)]: A 1 5 thrulist reach
A[*,thrulist(1,5)]: A 1st over rows items reach 1 5 thrulist catch
A[r,c]: A r reach c catch
A[items(1,10),items(2,4)]: A 1 10 items reach 2 4 items catch
A[[5;3;1]]: wrong
A[[2,1],[1,2,3]]: wrong

Test case results after modifying prs.c:
NOTE: recent changes produce somewhat different results than shown.
A[*,*]: A
A[,*]: A
A[,]: A
A[1:5]: A 1 5 : reach 
A[1:5,*]: A 1 5 : reach 
A[*,1:5]: A 1 5 : catch 
A[thrulist(1,5)]: A 1 5 thrulist reach 
A[*,thrulist(1,5)]: A 1 5 thrulist catch 
A[r,c]: A c catch r reach 
A[items(1,10),items(2,4)]: A 2 4 items catch 1 10 items reach 
A[[5;3;1]]: A 5 3 pile 1 pile hand reach 
A[[2,1],[1,2,3]]: A 1 2 park 3 park hand catch 2 1 park hand reach 

--------------------------------------------------------------------- */
