summaryrefslogtreecommitdiff
path: root/new
diff options
context:
space:
mode:
Diffstat (limited to 'new')
-rw-r--r--new/big.c643
-rw-r--r--new/data.c1250
-rw-r--r--new/lex.c1213
-rw-r--r--new/reduce.c2376
-rw-r--r--new/rules.y1686
-rw-r--r--new/steer.c2208
-rw-r--r--new/trans.c1026
-rw-r--r--new/types.c1613
8 files changed, 12015 insertions, 0 deletions
diff --git a/new/big.c b/new/big.c
new file mode 100644
index 0000000..7a1cfe1
--- /dev/null
+++ b/new/big.c
@@ -0,0 +1,643 @@
+/* MIRANDA INTEGER PACKAGE */
+/* package for unbounded precision integers */
+
+/**************************************************************************
+ * Copyright (C) Research Software Limited 1985-90. All rights reserved. *
+ * The Miranda system is distributed as free software under the terms in *
+ * the file "COPYING" which is included in the distribution. *
+ *------------------------------------------------------------------------*/
+
+#include "data.h"
+#include "big.h"
+#include <errno.h>
+
+static double logIBASE,log10IBASE;
+word big_one;
+
+void bigsetup()
+{ logIBASE=log((double)IBASE);
+ log10IBASE=log10((double)IBASE);
+ big_one=make(INT,1,0);
+}
+
+word isnat(x)
+word x;
+{ return(tag[x]==INT&&poz(x));
+}
+
+word sto_word(i) /* store C long long as mira bigint */
+long long i;
+{ word s,x;
+ if(i<0)s=SIGNBIT,i= -i; else s=0;
+ x=make(INT,s|i&MAXDIGIT,0);
+ if(i>>=DIGITWIDTH)
+ { word *p = &rest(x);
+ *p=make(INT,i&MAXDIGIT,0),p= &rest(*p);
+ while(i>>=DIGITWIDTH)
+ *p=make(INT,i&MAXDIGIT,0),p= &rest(*p); }
+ return(x);
+} /* change to long long, DT Oct 2019 */
+
+#define maxval (1ll<<60)
+
+long long get_word(x) /* mira bigint to C long long */
+word x;
+{ long long n=digit0(x);
+ word sign=neg(x);
+ if(!(x=rest(x)))return(sign?-n:n);
+{ word w=DIGITWIDTH;
+ while(x&&w<60)n+=(long long)digit(x)<<w,w+=DIGITWIDTH,x=rest(x);
+ if(x)n=maxval; /* overflow, return large value */
+ return(sign?-n:n);
+}} /* change to long long, DT Oct 2019 */
+
+word bignegate(x)
+word x;
+{ if(bigzero(x))return(x);
+ return(make(INT,hd[x]&SIGNBIT?hd[x]&MAXDIGIT:SIGNBIT|hd[x],tl[x]));
+}
+
+word bigplus(x,y)
+word x,y;
+{ if(poz(x))
+ if(poz(y))return(big_plus(x,y,0));
+ else return(big_sub(x,y));
+ else
+ if(poz(y))return(big_sub(y,x));
+ else return(big_plus(x,y,SIGNBIT)); /* both negative */
+}
+
+word big_plus(x,y,signbit) /* ignore input signs, treat x,y as positive */
+word x,y; word signbit;
+{ word d=digit0(x)+digit0(y);
+ word carry = ((d&IBASE)!=0);
+ word r = make(INT,signbit|d&MAXDIGIT,0); /* result */
+ word *z = &rest(r); /* pointer to rest of result */
+ x = rest(x); y = rest(y);
+ while(x&&y) /* this loop has been unwrapped once, see above */
+ { d = carry+digit(x)+digit(y);
+ carry = ((d&IBASE)!=0);
+ *z = make(INT,d&MAXDIGIT,0);
+ x = rest(x); y = rest(y); z = &rest(*z); }
+ if(y)x=y; /* by convention x is the longer one */
+ while(x)
+ { d = carry+digit(x);
+ carry = ((d&IBASE)!=0);
+ *z = make(INT,d&MAXDIGIT,0);
+ x = rest(x); z = &rest(*z); }
+ if(carry)*z=make(INT,1,0);
+ return(r);
+}
+
+word bigsub(x,y)
+word x,y;
+{ if(poz(x))
+ if(poz(y))return(big_sub(x,y));
+ else return(big_plus(x,y,0)); /* poz x, negative y */
+ else
+ if(poz(y))return(big_plus(x,y,SIGNBIT)); /* negative x, poz y */
+ else return(big_sub(y,x)); /* both negative */
+}
+
+word big_sub(x,y) /* ignore input signs, treat x,y as positive */
+word x,y;
+{ word d = digit0(x)-digit0(y);
+ word borrow = (d&IBASE)!=0;
+ word r=make(INT,d&MAXDIGIT,0); /* result */
+ word *z = &rest(r);
+ word *p=NULL; /* pointer to trailing zeros, if any */
+ x = rest(x); y = rest(y);
+ while(x&&y) /* this loop has been unwrapped once, see above */
+ { d = digit(x)-digit(y)-borrow;
+ borrow = (d&IBASE)!=0;
+ d = d&MAXDIGIT;
+ *z = make(INT,d,0);
+ if(d)p=NULL; else if(!p)p=z;
+ x = rest(x); y = rest(y); z = &rest(*z); }
+ while(y) /* at most one of these two loops will be invoked */
+ { d = -digit(y)-borrow;
+ borrow = ((d&IBASE)!=0);
+ d = d&MAXDIGIT;
+ *z = make(INT,d,0);
+ if(d)p=NULL; else if(!p)p=z;
+ y = rest(y); z = &rest(*z); }
+ while(x) /* alternative loop */
+ { d = digit(x)-borrow;
+ borrow = ((d&IBASE)!=0);
+ d = d&MAXDIGIT;
+ *z = make(INT,d,0);
+ if(d)p=NULL; else if(!p)p=z;
+ x = rest(x); z = &rest(*z); }
+ if(borrow) /* result is negative - take complement and add 1 */
+ { p=NULL;
+ d = (digit(r)^MAXDIGIT) + 1;
+ borrow = ((d&IBASE)!=0); /* borrow now means `carry' (sorry) */
+ digit(r) = SIGNBIT|d; /* set sign bit of result */
+ z = &rest(r);
+ while(*z)
+ { d = (digit(*z)^MAXDIGIT)+borrow;
+ borrow = ((d&IBASE)!=0);
+ digit(*z) = d = d&MAXDIGIT;
+ if(d)p=NULL; else if(!p)p=z;
+ z = &rest(*z); }
+ }
+ if(p)*p=0; /* remove redundant (ie trailing) zeros */
+ return(r);
+}
+
+word bigcmp(x,y) /* returns +ve,0,-ve as x greater than, equal, less than y */
+word x,y;
+{ word d,r,s=neg(x);
+ if(neg(y)!=s)return(s?-1:1);
+ r=digit0(x)-digit0(y);
+ for(;;)
+ { x=rest(x); y=rest(y);
+ if(!x)if(y)return(s?1:-1);
+ else return(s?-r:r);
+ if(!y)return(s?-1:1);
+ d=digit(x)-digit(y);
+ if(d)r=d; }
+}
+
+word bigtimes(x,y) /* naive multiply - quadratic */
+word x,y;
+{ if(len(x)<len(y))
+ { word hold=x; x=y; y=hold; } /* important optimisation */
+ word r=make(INT,0,0);
+ word d = digit0(y);
+ word s=neg(y);
+ word n=0;
+ if(bigzero(x))return(r); /* short cut */
+ for(;;)
+ { if(d)r = bigplus(r,shift(n,stimes(x,d)));
+ n++;
+ y = rest(y);
+ if(!y)
+ return(s!=neg(x)?bignegate(r):r);
+ d=digit(y); }
+}
+
+
+word shift(n,x) /* multiply big x by n'th power of IBASE */
+word n,x;
+{ while(n--)x=make(INT,0,x);
+ return(x);
+} /* NB - we assume x non-zero, else unnormalised result */
+
+word stimes(x,n) /* multiply big x (>=0) by digit n (>0) */
+word x,n;
+{ unsigned d= n*digit0(x); /* ignore sign of x */
+ word carry=d>>DIGITWIDTH;
+ word r = make(INT,d&MAXDIGIT,0);
+ word *y = &rest(r);
+ while(x=rest(x))
+ d=n*digit(x)+carry,
+ *y=make(INT,d&MAXDIGIT,0),
+ y = &rest(*y),
+ carry=d>>DIGITWIDTH;
+ if(carry)*y=make(INT,carry,0);
+ return(r);
+}
+
+word b_rem; /* contains remainder from last call to longdiv or shortdiv */
+
+word bigdiv(x,y) /* may assume y~=0 */
+word x,y;
+{ word s1,s2,q;
+ /* make x,y positive and remember signs */
+ if(s1=neg(y))y=make(INT,digit0(y),rest(y));
+ if(neg(x))
+ x=make(INT,digit0(x),rest(x)),s2=!s1;
+ else s2=s1;
+ /* effect: s1 set iff y negative, s2 set iff signs mixed */
+ if(rest(y))q=longdiv(x,y);
+ else q=shortdiv(x,digit(y));
+ if(s2){ if(!bigzero(b_rem))
+ { x=q;
+ while((digit(x)+=1)==IBASE) /* add 1 to q in situ */
+ { digit(x)=0;
+ if(!rest(x)){ rest(x)=make(INT,1,0); break; }
+ else x=rest(x);
+ }
+ }
+ if(!bigzero(q))digit(q)=SIGNBIT|digit(q);
+ }
+ return(q);
+}
+
+word bigmod(x,y) /* may assume y~=0 */
+word x,y;
+{ word s1,s2;
+ /* make x,y positive and remember signs */
+ if(s1=neg(y))y=make(INT,digit0(y),rest(y));
+ if(neg(x))
+ x=make(INT,digit0(x),rest(x)),s2=!s1;
+ else s2=s1;
+ /* effect: s1 set iff y negative, s2 set iff signs mixed */
+ if(rest(y))longdiv(x,y);
+ else shortdiv(x,digit(y));
+ if(s2){ if(!bigzero(b_rem))
+ b_rem = bigsub(y,b_rem);
+ }
+ return(s1?bignegate(b_rem):b_rem);
+}
+
+/* NB - above have entier based handling of signed cases (as Miranda) in
+ which remainder has sign of divisor. To get this:- if signs of
+ divi(sor/dend) mixed negate quotient and if remainder non-zero take
+ complement and add one to magnitude of quotient */
+
+/* for alternative, truncate based handling of signed cases (usual in C):-
+ magnitudes invariant under change of sign, remainder has sign of
+ dividend, quotient negative if signs of divi(sor/dend) mixed */
+
+word shortdiv(x,n) /* divide big x by single digit n returning big quotient
+ and setting external b_rem as side effect */
+ /* may assume - x>=0,n>0 */
+word x,n;
+{ word d=digit(x),s_rem,q=0;
+ while(x=rest(x)) /* reverse rest(x) into q */
+ q=make(INT,d,q),d=digit(x); /* leaving most sig. digit in d */
+ { word tmp;
+ x=q; s_rem=d%n; d=d/n;
+ if(d||!q)q=make(INT,d,0); /* put back first digit (if not leading 0) */
+ else q=0;
+ while(x) /* in situ division of q by n AND destructive reversal */
+ d=s_rem*IBASE+digit(x),digit(x)=d/n,s_rem=d%n,
+ tmp=x,x=rest(x),rest(tmp)=q,q=tmp;
+ }
+ b_rem=make(INT,s_rem,0);
+ return(q);
+}
+
+word longdiv(x,y) /* divide big x by big y returning quotient, leaving
+ remainder in extern variable b_rem */
+ /* may assume - x>=0,y>0 */
+word x,y;
+{ word n,q,ly,y1,scale;
+ if(bigcmp(x,y)<0){ b_rem=x; return(make(INT,0,0)); }
+ y1=msd(y);
+ if((scale=IBASE/(y1+1))>1) /* rescale if necessary */
+ x=stimes(x,scale),y=stimes(y,scale),y1=msd(y);
+ n=q=0;ly=len(y);
+ while(bigcmp(x,y=make(INT,0,y))>=0)n++;
+ y=rest(y); /* want largest y not exceeding x */
+ ly += n;
+ for(;;)
+ { word d,lx=len(x);
+ if(lx<ly)d=0; else
+ if(lx==ly)
+ if(bigcmp(x,y)>=0)x=bigsub(x,y),d=1;
+ else d=0;
+ else{ d=ms2d(x)/y1;
+ if(d>MAXDIGIT)d=MAXDIGIT;
+ if((d -= 2)>0)x=bigsub(x,stimes(y,d));
+ else d=0;
+ if(bigcmp(x,y)>=0)
+ { x=bigsub(x,y),d++;
+ if(bigcmp(x,y)>=0)
+ x=bigsub(x,y),d++; }
+ }
+ q = make(INT,d,q);
+ if(n-- ==0)
+ { b_rem = scale==1?x:shortdiv(x,scale); return(q); }
+ ly-- ; y = rest(y); }
+} /* see Bird & Wadler p82 for explanation */
+
+word len(x) /* no of digits in big x */
+word x;
+{ word n=1;
+ while(x=rest(x))n++;
+ return(n);
+}
+
+word msd(x) /* most significant digit of big x */
+word x;
+{ while(rest(x))x=rest(x);
+ return(digit(x)); /* sign? */
+}
+
+word ms2d(x) /* most significant 2 digits of big x (len>=2) */
+word x;
+{ word d=digit(x);
+ x=rest(x);
+ while(rest(x))d=digit(x),x=rest(x);
+ return(digit(x)*IBASE+d);
+}
+
+word bigpow(x,y) /* assumes y poz */
+word x,y;
+{ word d,r=make(INT,1,0);
+ while(rest(y)) /* this loop has been unwrapped once, see below */
+ { word i=DIGITWIDTH;
+ d=digit(y);
+ while(i--)
+ { if(d&1)r=bigtimes(r,x);
+ x = bigtimes(x,x);
+ d >>= 1; }
+ y=rest(y);
+ }
+ d=digit(y);
+ if(d&1)r=bigtimes(r,x);
+ while(d>>=1)
+ { x = bigtimes(x,x);
+ if(d&1)r=bigtimes(r,x); }
+ return(r);
+}
+
+double bigtodbl(x)
+word x;
+{ word s=neg(x);
+ double b=1.0, r=(double)digit0(x);
+ x = rest(x);
+ while(x)b=b*IBASE,r=r+b*digit(x),x=rest(x);
+ if(s)return(-r);
+ return(r);
+} /* small end first */
+/* note: can return oo, -oo
+ but is used without surrounding sto_/set)dbl() only in compare() */
+
+/* not currently used
+long double bigtoldbl(x)
+word x;
+{ int s=neg(x);
+ long double b=1.0L, r=digit0(x);
+ x = rest(x);
+ while(x)b=b*IBASE,r=r+b*digit(x),x=rest(x);
+/*printf("bigtoldbl returns %Le\n",s?-r:r); /* DEBUG
+ if(s)return(-r);
+ return(r);
+} /* not compatible with std=c90, lib fns eg sqrtl broken */
+
+word dbltobig(x) /* entier */
+double x;
+{ word s= (x<0);
+ word r=make(INT,0,0);
+ word *p = &r;
+ double y= floor(x);
+/*if(fabs(y-x+1.0)<1e-9)y += 1.0; /* trick due to Peter Bartke, see note */
+ for(y=fabs(y);;)
+ { double n = fmod(y,(double)IBASE);
+ digit(*p) = (word)n;
+ y = (y-n)/(double)IBASE;
+ if(y>0.0)rest(*p)=make(INT,0,0),p=&rest(*p);
+ else break;
+ }
+ if(s)digit(r)=SIGNBIT|digit(r);
+ return(r);
+}
+/* produces junk in low order digits if x exceeds range in which integer
+ can be held without error as a double -- NO, see next comment */
+/* hugs, ghci, mira produce same integer for floor/entier hugenum, has 2^971
+ as factor so the low order bits are NOT JUNK -- 9.1.12 */
+
+/* note on suppressed fix:
+ choice of 1e9 arbitrary, chosen to prevent eg entier(100*0.29) = 28
+ but has undesirable effects, causing eg entier 1.9999999999 = 2
+ underlying problem is that computable floor on true Reals is _|_ at
+ the exact integers. There are inherent deficiences in 64 bit fp,
+ no point in trying to mask this */
+
+double biglog(x) /* logarithm of big x */
+word x;
+{ word n=0;
+ double r=digit(x);
+ if(neg(x)||bigzero(x))errno=EDOM,math_error("log");
+ while(x=rest(x))n++,r=digit(x)+r/IBASE;
+ return(log(r)+n*logIBASE);
+}
+
+double biglog10(x) /* logarithm of big x */
+word x;
+{ word n=0;
+ double r=digit(x);
+ if(neg(x)||bigzero(x))errno=EDOM,math_error("log10");
+ while(x=rest(x))n++,r=digit(x)+r/IBASE;
+ return(log10(r)+n*log10IBASE);
+}
+
+word bigscan(p) /* read a big number (in decimal) */
+ /* NB does NOT check for malformed number, assumes already done */
+char *p; /* p is a pointer to a null terminated string of digits */
+{ word s=0,r=make(INT,0,0);
+ if(*p=='-')s=1,p++; /* optional leading `-' (for NUMVAL) */
+ while(*p)
+ { word d= *p-'0',f=10;
+ p++;
+ while(*p&&f<PTEN)d=10*d+*p-'0',f=10*f,p++;
+ /* rest of loop does r=f*r+d; (in situ) */
+ d= f*digit(r)+d;
+ { word carry=d>>DIGITWIDTH;
+ word *x = &rest(r);
+ digit(r)=d&MAXDIGIT;
+ while(*x)
+ d=f*digit(*x)+carry,
+ digit(*x)=d&MAXDIGIT,
+ carry=d>>DIGITWIDTH,
+ x = &rest(*x);
+ if(carry)*x=make(INT,carry,0);
+ }}
+/*if(*p=='e')
+ { int s=bigscan(p+1);
+ r = bigtimes(r,bigpow(make(INT,10,0),s); } */
+ if(s&&!bigzero(r))digit(r)=digit(r)|SIGNBIT;
+ return(r);
+}
+/* code to handle (unsigned) exponent commented out */
+
+word bigxscan(p,q) /* read unsigned hex number in '\0'-terminated string p to q */
+ /* assumes redundant leading zeros removed */
+char *p, *q;
+{ word r; /* will hold result */
+ word *x = &r;
+ if(*p=='0'&&!p[1])return make(INT,0,0);
+ while(q>p)
+ { unsigned long long hold;
+ q = q-p<15 ? p : q-15; /* read upto 15 hex digits from small end */
+ sscanf(q,"%llx",&hold);
+ *q = '\0';
+ word count=4; /* 15 hex digits => 4 bignum digits */
+ while(count-- && !(hold==0 && q==p))
+ *x = make(INT,hold&MAXDIGIT,0),
+ hold >>= DIGITWIDTH,
+ x = &rest(*x);
+ }
+ return r;
+}
+
+word bigoscan(p,q) /* read unsigned octal number in '\0'-terminated string p to q */
+ /* assumes redundant leading zeros removed */
+char *p, *q;
+{ word r; /* will hold result */
+ word *x = &r;
+ while(q>p)
+ { unsigned word hold;
+ q = q-p<5 ? p : q-5; /* read (upto) 5 octal digits from small end */
+ sscanf(q,"%o",&hold);
+ *q = '\0';
+ *x = make(INT,hold,0),
+ x = &rest(*x);
+ }
+ return r;
+}
+
+word digitval(c)
+char c;
+{ return isdigit(c)?c-'0':
+ isupper(c)?10+c-'A':
+ 10+c-'a'; }
+
+word strtobig(z,base) /* numeral (as Miranda string) to big number */
+ /* does NOT check for malformed numeral, assumes
+ done and that z fully evaluated */
+word z; word base;
+{ word s=0,r=make(INT,0,0),PBASE=PTEN;
+ if(base==16)PBASE=PSIXTEEN; else
+ if(base==8)PBASE=PEIGHT;
+ if(z!=NIL&&hd[z]=='-')s=1,z=tl[z]; /* optional leading `-' (for NUMVAL) */
+ if(base!=10)z=tl[tl[z]]; /* remove "0x" or "0o" */
+ while(z!=NIL)
+ { word d=digitval(hd[z]),f=base;
+ z=tl[z];
+ while(z!=NIL&&f<PBASE)d=base*d+digitval(hd[z]),f=base*f,z=tl[z];
+ /* rest of loop does r=f*r+d; (in situ) */
+ d= f*digit(r)+d;
+ { word carry=d>>DIGITWIDTH;
+ word *x = &rest(r);
+ digit(r)=d&MAXDIGIT;
+ while(*x)
+ d=f*digit(*x)+carry,
+ digit(*x)=d&MAXDIGIT,
+ carry=d>>DIGITWIDTH,
+ x = &rest(*x);
+ if(carry)*x=make(INT,carry,0);
+ }}
+ if(s&&!bigzero(r))digit(r)=digit(r)|SIGNBIT;
+ return(r);
+}
+
+extern char *dicp;
+
+word bigtostr(x) /* number to decimal string (as Miranda list) */
+word x;
+{ word x1,sign,s=NIL;
+#ifdef DEBUG
+ extern word debug;
+ if(debug&04) /* print octally */
+ { word d=digit0(x);
+ sign=neg(x);
+ for(;;)
+ { word i=OCTW;
+ while(i--||d)s=cons('0'+(d&07),s),d >>= 3;
+ x=rest(x);
+ if(x)s=cons(' ',s),d=digit(x);
+ else return(sign?cons('-',s):s); }
+ }
+#endif
+ if(rest(x)==0)
+ { extern char *dicp;
+ sprintf(dicp,"%d",getsmallint(x));
+ return(str_conv(dicp)); }
+ sign=neg(x);
+ x1=make(INT,digit0(x),0); /* reverse x into x1 */
+ while(x=rest(x))x1=make(INT,digit(x),x1);
+ x=x1;
+ for(;;)
+ { /* in situ division of (reversed order) x by PTEN */
+ word d=digit(x),rem=d%PTEN;
+ d=d/PTEN; x1=rest(x);
+ if(d)digit(x)=d;
+ else x=x1; /* remove leading zero from result */
+ while(x1)
+ d=rem*IBASE+digit(x1),
+ digit(x1)=d/PTEN,
+ rem=d%PTEN,
+ x1=rest(x1);
+ /* end of in situ division (also uses x1 as temporary) */
+ if(x)
+ { word i=TENW;
+ while(i--)s=cons('0'+rem%10,s),rem=rem/10; }
+ else
+ { while(rem)s=cons('0'+rem%10,s),rem=rem/10;
+ return(sign?cons('-',s):s); }
+ }
+}
+
+word bigtostrx(x) /* integer to hexadecimal string (as Miranda list) */
+word x;
+{ word r=NIL, s=neg(x);
+ while(x)
+ { word count=4; /* 60 bits => 20 octal digits => 4 bignum digits */
+ unsigned long long factor=1;
+ unsigned long long hold=0;
+ while(count-- && x) /* calculate value of (upto) 4 bignum digits */
+ hold=hold+factor*digit0(x),
+ /* printf("::%llx\n",hold), /* DEBUG */
+ factor<<=15,
+ x=rest(x);
+ sprintf(dicp,"%.15llx",hold); /* 15 hex digits = 60 bits */
+ /* printf(":::%s\n",dicp); /* DEBUG */
+ char *q=dicp+15;
+ while(--q>=dicp)r = cons(*q,r);
+ }
+ while(digit(r)=='0'&&rest(r)!=NIL)r=rest(r); /* remove redundant leading 0's */
+ r = cons('0',cons('x',r));
+ if(s)r = cons('-',r);
+ return(r);
+}
+
+word bigtostr8(x) /* integer to octal string (as Miranda list) */
+word x;
+{ word r=NIL, s=neg(x);
+ while(x)
+ { char *q = dicp+5;
+ sprintf(dicp,"%.5o",digit0(x));
+ while(--q>=dicp)r = cons(*q,r);
+ x = rest(x); }
+ while(digit(r)=='0'&&rest(r)!=NIL)r=rest(r); /* remove redundant leading 0's */
+ r = cons('0',cons('o',r));
+ if(s)r = cons('-',r);
+ return(r);
+}
+
+#ifdef DEBUG
+wff(x) /* check for well-formation of integer */
+word x;
+{ word y=x;
+ if(tag[x]!=INT)printf("BAD TAG %d\n",tag[x]);
+ if(neg(x)&&!digit0(x)&&!rest(x))printf("NEGATIVE ZERO!\n");
+ if(digit0(x)&(~MAXDIGIT))printf("OVERSIZED DIGIT!\n");
+ while(x=rest(x))
+ if(tag[x]!=INT)printf("BAD INTERNAL TAG %d\n",tag[x]); else
+ if(digit(x)&(~MAXDIGIT))
+ printf("OVERSIZED DIGIT!\n"); else
+ if(!digit(x)&&!rest(x))
+ printf("TRAILING ZERO!\n");
+ return(y);
+}
+
+normalise(x) /* remove trailing zeros */
+word x;
+{ if(rest(x))rest(x)=norm1(rest(x));
+ return(wff(x));
+}
+
+norm1(x)
+word x;
+{ if(rest(x))rest(x)=norm1(rest(x));
+ return(!digit(x)&&!rest(x)?0:x);
+}
+
+#endif
+
+/* stall(s)
+char *s;
+{ fprintf(stderr,"big integer %s not yet implemented\n",s);
+ exit(0);
+}
+
+#define destrev(x,y,z) while(x)z=x,x=rest(x),rest(z)=y,y=z;
+/* destructively reverse x into y using z as temp */
+
+/* END OF MIRANDA INTEGER PACKAGE */
+
diff --git a/new/data.c b/new/data.c
new file mode 100644
index 0000000..c7463ac
--- /dev/null
+++ b/new/data.c
@@ -0,0 +1,1250 @@
+/* MIRANDA DATA REPRESENTATIONS */
+
+/**************************************************************************
+ * Copyright (C) Research Software Limited 1985-90. All rights reserved. *
+ * The Miranda system is distributed as free software under the terms in *
+ * the file "COPYING" which is included in the distribution. *
+ *------------------------------------------------------------------------*/
+
+#include "data.h"
+#include "big.h"
+#define INITSPACE 250000
+word SPACE=INITSPACE; /* false ceiling in heap to improve paging behaviour
+ during compilation */
+extern word SPACELIMIT; /* see steer.c for default value */
+ /* SPACELIMIT controls the size of the heap (i.e. the number of list
+ cells available) - the minimum survivable number given the need to
+ compile the prelude etc is probably about 6000 */
+ /* Note: the size of a list cell is 2 ints + 1 char */
+#define BIGTOP (SPACELIMIT + ATOMLIMIT)
+word listp=ATOMLIMIT-1;
+word *hdspace,*tlspace;
+long long cellcount=0;
+long claims=0;
+long nogcs=0;
+extern word atgc; /* flag, set in steer.c */
+#define poschar(c) !(negchar((c)-1))
+#define negchar(c) (c&128)
+ /* safest to test for -ve chars this way, since not all m/c's do sign
+ extension - DT Jan 84 */
+
+trueheapsize()
+{ return(nogcs==0?listp-ATOMLIMIT+1:SPACE); }
+
+setupheap()
+{ hdspace=(word *)malloc(SPACELIMIT*sizeof(word));
+ tlspace=(word *)malloc(SPACELIMIT*sizeof(word));
+ hd=hdspace-ATOMLIMIT; tl=tlspace-ATOMLIMIT;
+ if(SPACE>SPACELIMIT)SPACE=SPACELIMIT;
+ tag=(char *)calloc(BIGTOP+1,sizeof(char));
+ /* NB use calloc because it sets contents to zero */
+ /* tag[TOP] must be zero and exists as a sentinel */
+ if(hdspace==NULL||tlspace==NULL||tag==NULL)mallocfail("heap");
+}
+
+resetheap() /* warning - cannot do this dynamically, because both the
+ compiler and the reducer hold onto absolute heap addresses
+ during certain space consuming computations */
+{ if(SPACELIMIT<trueheapsize())
+ fprintf(stderr,"impossible event in resetheap\n"),exit(1);
+ hdspace=(word *)realloc((char *)hdspace,SPACELIMIT*sizeof(word));
+ if(hdspace==NULL)mallocfail("heap");
+ tlspace=(word *)realloc((char *)tlspace,SPACELIMIT*sizeof(word));
+ if(tlspace==NULL)mallocfail("heap");
+ hd=hdspace-ATOMLIMIT; tl=tlspace-ATOMLIMIT;
+ tag=(char *)realloc(tag,BIGTOP+1);
+ if(tag==NULL)mallocfail("heap");
+ tag[BIGTOP]=0;
+ if(SPACE>SPACELIMIT)SPACE=SPACELIMIT;
+ if(SPACE<INITSPACE&&INITSPACE<=SPACELIMIT)SPACE=INITSPACE,tag[TOP]=0;
+ /* tag[TOP] is always zero and exists as a sentinel */
+}
+
+mallocfail(x)
+char *x;
+{ fprintf(stderr,"panic: cannot find enough free space for %s\n",x);
+ exit(1);
+}
+
+resetgcstats()
+{ cellcount= -claims;
+ nogcs = 0;
+ initclock();
+}
+
+make(t,x,y) /* creates a new cell with "tag" t, "hd" x and "tl" y */
+word t,x,y;
+{ while(poschar(tag[++listp]));
+ /* find next cell with zero or negative tag (=unwanted) */
+ if(listp==TOP)
+ { if(SPACE!=SPACELIMIT)
+ if(!compiling)SPACE=SPACELIMIT; else
+ if(claims<=SPACE/4&&nogcs>1)
+ { /* during compilation we raise false ceiling whenever residency
+ reaches 75% on 2 successive gc's */
+ static word wait=0;
+ word sp=SPACE;
+ if(wait)wait--; else
+ SPACE+= SPACE/2,wait=2,
+ SPACE=5000*(1+(SPACE-1)/5000); /* round upwards */
+ if(SPACE>SPACELIMIT)SPACE=SPACELIMIT;
+ if(atgc&&SPACE>sp)
+ printf( "\n<<increase heap from %d to %d>>\n",sp,SPACE);
+ }
+ if(listp==TOP)
+ {
+#if defined ORION105
+ asm("savew6");
+ gc();
+ asm("restw6");
+#elif defined sparc
+ asm("ta 0x03"); /* see /usr/include/sun4/trap.h */
+ /* asm("ta ST_FLUSH_WINDOWS"); */
+ gc();
+#else
+ gc();
+#endif
+ if(t>STRCONS)mark(x);
+ if(t>=INT)mark(y);
+ return(make(t,x,y)); }
+ }
+ claims++;
+ tag[listp]= t;
+ hd[listp]= x;
+ tl[listp]= y;
+ return(listp); }
+
+/* cons ap ap2 ap3 are all #defined in terms of make
+ - see MIRANDA DECLARATIONS */
+
+setwd(x,a,b)
+word x,a,b;
+{ hd[x]= a;
+ tl[x]= b; }
+
+word collecting=0; /* flag for reset(), in case interrupt strikes in gc */
+
+gc() /* the "garbage collector" */
+{ char *p1;
+ extern word making;
+ collecting=1;
+ p1= &(tag[ATOMLIMIT]);
+ if(atgc)
+ printf("\n<<gc after %ld claims>>\n",claims);
+ if(claims<=SPACE/10 && nogcs>1 && SPACE==SPACELIMIT)
+ { /* if heap utilisation exceeds 90% on 2 successive gc's, give up */
+ static word hnogcs=0;
+ if(nogcs==hnogcs)
+ { extern word ideep;
+ extern char *current_script;
+ fprintf(stderr,"<<not enough heap space -- task abandoned>>\n");
+ if(!compiling)outstats();
+ if(compiling&&ideep==0)
+ fprintf(stderr,"not enough heap to compile current script\n"),
+ fprintf(stderr,"script = \"%s\", heap = %d\n",current_script,SPACE);
+ exit(1); } /* if compiling should reset() instead - FIX LATER */
+ else hnogcs=nogcs+1; }
+ nogcs++;
+ while(*p1= -*p1)p1++; /* make all tags -ve (= unwanted) */
+/*if(atgc)
+ { extern int lasthead;
+#define BACKSTOP 020000000000
+ printf("bases() called\n");
+ printf("lasthead= ");
+ if(lasthead==BACKSTOP)printf("BACKSTOP");
+ else out(stdout,lasthead);
+ putchar('\n'); } /* DEBUG */
+ bases();
+/*if(atgc)printf("bases() done\n"); /* DEBUG */
+ listp= ATOMLIMIT - 1;
+ cellcount+= claims;
+ claims= 0;
+ collecting=0;
+}
+/* int Icount; /* DEBUG */
+
+gcpatch() /* called when gc interrupted - see reset in steer.c */
+/* must not allocate any cells between calling this and next gc() */
+{ char *p1;
+ for(p1= &(tag[ATOMLIMIT]);*p1;p1++)if(negchar(*p1))*p1= -*p1;
+ /* otherwise mutator crashes on funny tags */
+}
+
+bases() /* marks everthing that must be saved */
+{ word *p;
+ extern YYSTYPE yyval;
+ extern word *cstack;
+ extern word fileq,primenv;
+ extern word cook_stdin,common_stdin,common_stdinb,rv_expr,rv_script;
+ extern word margstack,vergstack,litstack,linostack,prefixstack;
+ extern word idsused,suppressids,lastname,
+ eprodnts,nonterminals,ntmap,ihlist,ntspecmap,gvars,lexvar;
+ extern word R,TABSTRS,SGC,ND,SBND,NT,current_id,meta_pending;
+ extern word showchain,newtyps,algshfns,errs,speclocs;
+ extern word SUBST[],tvmap,localtvmap;
+ extern word tfnum,tfbool,tfbool2,tfnum2,tfstrstr,
+ tfnumnum,ltchar,bnf_t,tstep,tstepuntil;
+ extern word exec_t,read_t,filestat_t;
+ extern word big_one;
+ extern word nill,standardout;
+ extern word lexstates,lexdefs,oldfiles,includees,embargoes,exportfiles,
+ exports,internals, freeids,tlost,detrop,rfl,bereaved,ld_stuff;
+ extern word CLASHES,ALIASES,SUPPRESSED,TSUPPRESSED,DETROP,MISSING,fnts,FBS;
+ extern word outfilq,waiting;
+ /* Icount=0; /* DEBUG */
+ p= (word *)&p;
+/* we follow everything on the C stack that looks like a pointer into
+list space. This is failsafe in that the worst that can happen,if e.g. a
+stray integer happens to point into list space, is that the garbage
+collector will collect less garbage than it could have done */
+ if(p<cstack) /* which way does stack grow? */
+ while(++p!=cstack)mark(*p);/* for machines with stack growing downwards */
+ else
+ while(--p!=cstack)mark(*p);/* for machines with stack growing upwards */
+ mark(*cstack);
+/* now follow all pointer-containing external variables */
+ mark(outfilq);
+ mark(waiting);
+ if(compiling||rv_expr||rv_script) /* rv flags indicate `readvals' in use */
+ { extern YYSTYPE *yyvs, *yyvsp;
+ extern word namebucket[];
+ extern word *dstack,*stackp; /* undump stack - see load_script(), below */
+ extern word *pnvec,nextpn,loading; /* private name vector */
+ extern word make_status;
+ word i;
+ mark(make_status);
+ mark(primenv);
+ mark(fileq);
+ mark(idsused);
+ mark(eprodnts);
+ mark(nonterminals);
+ mark(ntmap);
+ mark(ihlist);
+ mark(ntspecmap);
+ mark(gvars);
+ mark(lexvar);
+ mark(common_stdin);
+ mark(common_stdinb);
+ mark(cook_stdin);
+ mark(margstack);
+ mark(vergstack);
+ mark(litstack);
+ mark(linostack);
+ mark(prefixstack);
+ mark(files);
+ mark(oldfiles);
+ mark(includees);
+ mark(freeids);
+ mark(exports);
+ mark(internals);
+ mark(CLASHES);
+ mark(ALIASES);
+ mark(SUPPRESSED);
+ mark(TSUPPRESSED);
+ mark(DETROP);
+ mark(MISSING);
+ mark(FBS);
+ mark(lexstates);
+ mark(lexdefs);
+ for(i=0;i<128;i++)
+ if(namebucket[i])mark(namebucket[i]);
+ for(p=dstack;p<stackp;p++)mark(*p);
+ if(loading)
+ { mark(algshfns);
+ mark(speclocs);
+ mark(exportfiles);
+ mark(embargoes);
+ mark(rfl);
+ mark(detrop);
+ mark(bereaved);
+ mark(ld_stuff);
+ mark(tlost);
+ for(i=0;i<nextpn;i++)mark(pnvec[i]); }
+ mark(lastname);
+ mark(suppressids);
+ mark(lastexp);
+ mark(nill);
+ mark(standardout);
+ mark(big_one);
+ mark(yyval);
+/* for(vp= yyvs;vp<=yyvsp;vp++)mark(*vp); */
+ mark(yylval);
+ mark(R);
+ mark(TABSTRS);
+ mark(SGC);
+ mark(ND);
+ mark(SBND);
+ mark(NT);
+ mark(current_id);
+ mark(meta_pending);
+ mark(newtyps);
+ mark(showchain);
+ mark(errs);
+ mark(tfnum);
+ mark(tfbool);
+ mark(tfbool2);
+ mark(tfnum2);
+ mark(tfstrstr);
+ mark(tfnumnum);
+ mark(ltchar);
+ mark(bnf_t);
+ mark(exec_t);
+ mark(read_t);
+ mark(filestat_t);
+ mark(tstep);
+ mark(tstepuntil);
+ mark(tvmap);
+ mark(localtvmap);
+ for(i=0;i<hashsize;i++)mark(SUBST[i]); }
+/* if(atgc)printf("<<%d I-nodes>>\n",Icount); /* DEBUG */
+}
+
+#define tlptrbits 030000000000
+/* see reduce.c */
+
+mark(x) /* a marked cell is distinguished by having a +ve "tag" */
+word x;
+{ x&= ~tlptrbits; /* x may be a `reversed pointer' (see reduce.c) */
+ while(isptr(x)&&negchar(tag[x]))
+ { /*if(hd[x]==I)Icount++; /* DEBUG */
+ if((tag[x]= -tag[x])<INT)return;
+ if(tag[x]>STRCONS)mark(hd[x]);
+ x= tl[x]&~tlptrbits; }
+}
+
+union numparts {double real; struct{word left;word right;} parts;};
+
+double get_dbl(x)
+word x;
+{ union numparts r;
+ r.parts.left= hd[x];
+ r.parts.right= tl[x];
+ return(r.real); }
+
+/* Miranda's arithmetic model requires fp overflow trapped. On sparc this
+ can be done by setting a trap with ieee_handler (see steer.c) otherwise
+ we test for overflow with finite(), see IEEE754-1985 (Appendix) */
+
+sto_dbl(R)
+double R;
+{ union numparts r;
+#if !defined sparc /* */
+ if(!finite(R))fpe_error(); /* see note on arithmetic model above */
+#endif /* */
+ r.real=R;
+ return(make(DOUBLE,r.parts.left,r.parts.right));
+}
+
+setdbl(x,R)
+double R;
+{ union numparts r;
+#if !defined sparc /* */
+ if(!finite(R))fpe_error(); /* see note on arithmetic model above */
+#endif /* */
+ r.real=R;
+ tag[x]=DOUBLE; hd[x]=r.parts.left; tl[x]=r.parts.right;
+}
+
+sto_char(c) /* assumes 0<=c<=UMAX */
+word c;
+{ return c<256?c:make(UNICODE,c,0); }
+
+get_char(x)
+word x;
+{ if(x<256)return x;
+ if(tag[x]==UNICODE)return hd[x];
+ fprintf(stderr,"impossible event in get_char(x), tag[x]==%d\n",tag[x]);
+ exit(1);
+}
+
+is_char(x)
+word x;
+{ return 0<=x && x<256 || tag[x]==UNICODE; }
+
+sto_id(p1)
+char *p1;
+{ return(make(ID,cons(strcons(p1,NIL),undef_t),UNDEF)); }
+ /* the hd of an ID contains cons(strcons(name,who),type) and
+ the tl has the value */
+ /* who is NIL, hereinfo, or cons(aka,hereinfo) where aka
+ is of the form datapair(oldname,0) oldname being a string */
+ /* hereinfo is fileinfo(script,line_no) */
+
+/* hereafter is stuff for dumping and undumping compiled scripts
+
+ (internal heap object) (external file rep - char sequence)
+ ---------------------- -----------------------------------
+ 0..127 self
+ 128..383 CHAR_X (self-128)
+ 384..ATOMLIMIT-1 (self-256)
+ integer (-127..127) SHORT_X <byte>
+ integer INT_X <4n bytes> (-1)
+ double DBL_X <8 bytes>
+ unicode_char UNICODE_X <4 bytes>
+ typevar TVAR_X <byte>
+ ap(x,y) [x] [y] AP_X
+ cons(x,y) [y] [x] CONS_X
+ id (=occurrence) ID_X <string terminated by '\0'>
+ pname (=occurrence) PN_X <2 bytes>
+ PN1_X <4 bytes>
+ datapair(string,0) AKA_X <string...\0>
+ fileinfo(script,line_no) HERE_X <string...\0> <2 bytes> (**)
+ constructor(n,x) [x] CONSTRUCT_X <2 bytes>
+ readvals(h,t) [t] RV_X
+ definition [val] [type] [who] [id] DEF_X
+ [val] [pname] DEF_X
+ definition-list [definition*] DEF_X
+ filename <string terminated by '\0'>
+ mtime <4 bytes>
+
+ complete script XVERSION
+ [ [filename]
+ [mtime]
+ [shareable] (=0 or 1)
+ [definition-list] ]+
+ '\0'
+ [definition-list] (algshfns)
+ [ND] or [True] (see below)
+ DEF_X
+ [SGC]
+ DEF_X
+ [freeids]
+ DEF_X
+ [definition-list] (internals)
+
+ type-error script XVERSION
+ '\1'
+ <4 bytes> (=errline)
+ ... (rest as normal script)
+
+ syntax-error script XVERSION
+ `\0'
+ <4 bytes> (=errline)
+ [ [filename]
+ [mtime] ]+
+
+ Notes
+ -----
+ first filename in dump must be that of `current_script' (ie the
+ main source file). All pathnames in dump are correct wrt the
+ directory of the main source.
+ (**) empty string is abbreviation for current filename in hereinfo
+ True in ND position indicates an otherwise correct dump whose exports
+ include type orphans
+
+ Pending:
+ --------
+ could have abbreviation for iterated ap and cons
+
+ remaining issue - external format should be machine and version
+ independent - not clear how to do this
+*/
+
+#define XBASE ATOMLIMIT-256
+#define CHAR_X (XBASE)
+#define SHORT_X (XBASE+1)
+#define INT_X (XBASE+2)
+#define DBL_X (XBASE+3)
+#define ID_X (XBASE+4)
+#define AKA_X (XBASE+5)
+#define HERE_X (XBASE+6)
+#define CONSTRUCT_X (XBASE+7)
+#define RV_X (XBASE+8)
+#define PN_X (XBASE+9)
+#define PN1_X (XBASE+10)
+#define DEF_X (XBASE+11)
+#define AP_X (XBASE+12)
+#define CONS_X (XBASE+13)
+#define TVAR_X (XBASE+14)
+#define UNICODE_X (XBASE+15)
+#define XLIMIT (XBASE+16)
+#if XLIMIT>512
+SEE ME!!! /* coding scheme breaks down if this occurs */
+#else
+
+static char prefix[pnlim];
+word preflen;
+
+setprefix(p) /* to that of pathname p */
+char *p;
+{ char *g;
+ (void)strcpy(prefix,p);
+ g=rindex(prefix,'/');
+ if(g)g[1]='\0';
+ else *prefix='\0';
+ preflen = strlen(prefix);
+} /* before calling dump_script or load_script must setprefix() to that
+ of current pathname of file being dumped/loaded - to get correct
+ translation between internal pathnames (relative to dump script)
+ and external pathnames */
+
+char *mkrel(p) /* makes pathname p correct relative to prefix */
+char *p; /* must use when writing pathnames to dump */
+{ if(strncmp(prefix,p,preflen)==0)return(p+preflen);
+ if(p[0]=='/')return(p);
+ fprintf(stderr,"impossible event in mkrelative\n"); /* or use getwd */
+ /* not possible because all relative pathnames in files were computed
+ wrt current script */
+ return(p); /* proforma only */
+}
+
+#define bits_15 0177777
+char *CFN;
+
+dump_script(files,f) /* write compiled script files to file f */
+word files;
+FILE *f;
+{ extern word ND,bereaved,errline,algshfns,internals,freeids,SGC;
+ putc(XVERSION,f); /* identifies dump format */
+ if(files==NIL){ /* source contains syntax or metatype error */
+ extern word oldfiles;
+ word x;
+ putc(0,f);
+ putw(errline,f);
+ for(x=oldfiles;x!=NIL;x=tl[x])
+ fprintf(f,"%s",mkrel(get_fil(hd[x]))),putc(0,f),
+ /*filename*/
+ putw(fil_time(hd[x]),f); /* mtime */
+ return; }
+ if(ND!=NIL)putc(1,f),putw(errline,f);
+ for(;files!=NIL;files=tl[files])
+ { fprintf(f,"%s",mkrel(CFN=get_fil(hd[files]))); /* filename */
+ putc(0,f);
+ putw(fil_time(hd[files]),f);
+ putc(fil_share(hd[files]),f);
+ dump_defs(fil_defs(hd[files]),f);
+ }
+ putc(0,f); /* header - not a possible filename */
+ dump_defs(algshfns,f);
+ if(ND==NIL&&bereaved!=NIL)dump_ob(True,f); /* special flag */
+ else dump_ob(ND,f);
+ putc(DEF_X,f);
+ dump_ob(SGC,f);
+ putc(DEF_X,f);
+ dump_ob(freeids,f);
+ putc(DEF_X,f);
+ dump_defs(internals,f);
+}
+
+dump_defs(defs,f) /* write list of defs to file f */
+word defs;
+FILE *f;
+{ while(defs!=NIL)
+ if(tag[hd[defs]]==STRCONS) /* pname */
+ { word v=get_pn(hd[defs]);
+ dump_ob(pn_val(hd[defs]),f);
+ if(v>bits_15)
+ putc(PN1_X,f),
+ putw(v,f);
+ else
+ putc(PN_X,f),
+ putc(v&255,f),
+ putc(v >> 8,f);
+ putc(DEF_X,f);
+ defs=tl[defs]; }
+ else
+ { dump_ob(id_val(hd[defs]),f);
+ dump_ob(id_type(hd[defs]),f);
+ dump_ob(id_who(hd[defs]),f);
+ putc(ID_X,f);
+ fprintf(f,"%s",(char *)get_id(hd[defs]));
+ putc(0,f);
+ putc(DEF_X,f);
+ defs=tl[defs]; }
+ putc(DEF_X,f); /* delimiter */
+}
+
+dump_ob(x,f) /* write combinatory expression x to file f */
+word x;
+FILE *f;
+{ /* printob("dumping: ",x); /* DEBUG */
+ switch(tag[x])
+ { case ATOM: if(x<128)putc(x,f); else
+ if(x>=384)putc(x-256,f); else
+ putc(CHAR_X,f),putc(x-128,f);
+ return;
+ case TVAR: putc(TVAR_X,f), putc(gettvar(x),f);
+ if(gettvar(x)>255)
+ fprintf(stderr,"panic, tvar too large\n");
+ return;
+ case INT: { /* 32 bit version (suppressed)
+ int d=get_int(x);
+ if(abs(d)<=127)
+ { putc(SHORT_X,f); putc(d,f); return; }
+ putc(INT_X,f);
+ putw(d,f);
+ /* variable length version */
+ word d=digit(x);
+ if(rest(x)==0&&(d&MAXDIGIT)<=127)
+ { if(d&SIGNBIT)d= -(d&MAXDIGIT);
+ putc(SHORT_X,f); putc(d,f); return; }
+ putc(INT_X,f);
+ putw(d,f);
+ x=rest(x);
+ while(x)
+ putw(digit(x),f),x=rest(x);
+ putw(-1,f);
+ /* end of variable length version */
+ return; }
+ /* 4 bytes per digit wasteful at current value of IBASE */
+ case DOUBLE: putc(DBL_X,f);
+ putw(hd[x],f);
+ putw(tl[x],f);
+ return;
+ case UNICODE: putc(UNICODE_X,f);
+ putw(hd[x],f);
+ return;
+ case DATAPAIR: fprintf(f,"%c%s",AKA_X,(char *)hd[x]);
+ putc(0,f);
+ return;
+ case FILEINFO: { word line=tl[x];
+ if((char *)hd[x]==CFN)putc(HERE_X,f);
+ else fprintf(f,"%c%s",HERE_X,mkrel(hd[x]));
+ putc(0,f);
+ putc(line&255,f);
+ putc((line >>= 8)&255,f);
+ if(line>255)fprintf(stderr,
+ "impossible line number %d in dump_ob\n",tl[x]);
+ return; }
+ case CONSTRUCTOR: dump_ob(tl[x],f);
+ putc(CONSTRUCT_X,f);
+ putc(hd[x]&255,f);
+ putc(hd[x]>>8,f);
+ return;
+ case STARTREADVALS: dump_ob(tl[x],f);
+ putc(RV_X,f);
+ return;
+ case ID: fprintf(f,"%c%s",ID_X,get_id(x));
+ putc(0,f);
+ return;
+ case STRCONS: { word v=get_pn(x); /* private name */
+ if(v>bits_15)
+ putc(PN1_X,f),
+ putw(v,f);
+ else
+ putc(PN_X,f),
+ putc(v&255,f),
+ putc(v >> 8,f);
+ return; }
+ case AP: dump_ob(hd[x],f);
+ dump_ob(tl[x],f);
+ putc(AP_X,f);
+ return;
+ case CONS: dump_ob(tl[x],f);
+ dump_ob(hd[x],f);
+ putc(CONS_X,f);
+ return;
+ default: fprintf(stderr,"impossible tag %d in dump_ob\n",tag[x]);
+ }
+}
+
+#define ovflocheck if(dicq-dic>DICSPACE)dicovflo()
+extern char *dic; extern word DICSPACE;
+
+word BAD_DUMP=0,CLASHES=NIL,ALIASES=NIL,PNBASE=0,SUPPRESSED=NIL,
+ TSUPPRESSED=NIL,TORPHANS=0;
+
+load_script(f,src,aliases,params,main)
+ /* loads a compiled script from file f for source src */
+ /* main=1 if is being loaded as main script, 0 otherwise */
+FILE *f;
+char *src;
+word aliases,params,main;
+{ extern word nextpn,ND,errline,algshfns,internals,freeids,includees,SGC;
+ extern char *dicp, *dicq;
+ word ch,files=NIL;
+ TORPHANS=BAD_DUMP=0;
+ CLASHES=NIL;
+ dsetup();
+ setprefix(src);
+ if(getc(f)!=XVERSION){ BAD_DUMP= -1; return(NIL); }
+ if(aliases!=NIL)
+ { /* for each `old' install diversion to `new' */
+ /* if alias is of form -old `new' is a pname */
+ word a,hold;
+ ALIASES=aliases;
+ for(a=aliases;a!=NIL;a=tl[a])
+ { word old=tl[hd[a]],new=hd[hd[a]];
+ hold=cons(id_who(old),cons(id_type(old),id_val(old)));
+ id_type(old)=alias_t;
+ id_val(old)=new;
+ if(tag[new]==ID)
+ if((id_type(new)!=undef_t||id_val(new)!=UNDEF)
+ &&id_type(new)!=alias_t)
+ CLASHES=add1(new,CLASHES);
+ hd[hd[a]]=hold;
+ }
+ if(CLASHES!=NIL){ BAD_DUMP= -2; unscramble(aliases); return(NIL); }
+ for(a=aliases;a!=NIL;a=tl[a]) /* FIX1 */
+ if(tag[ch=id_val(tl[hd[a]])]==ID) /* FIX1 */
+ if(id_type(ch)!=alias_t) /* FIX1 */
+ id_type(ch)=new_t; /* FIX1 */
+ }
+ PNBASE=nextpn; /* base for relocation of internal names in dump */
+ SUPPRESSED=NIL; /* list of `-id' aliases successfully obeyed */
+ TSUPPRESSED=NIL; /* list of -typename aliases (illegal just now) */
+ while((ch=getc(f))!=0&&ch!=EOF&&!BAD_DUMP)
+ { word s,holde=0;
+ dicq=dicp;
+ if(files==NIL&&ch==1) /* type error script */
+ { holde=getw(f),ch=getc(f);
+ if(main)errline=holde; }
+ if(ch!='/')(void)strcpy(dicp,prefix),dicq+=preflen;
+ /* locate wrt current posn */
+ *dicq++ = ch;
+ while((*dicq++ =ch=getc(f))&&ch!=EOF); /* filename */
+ ovflocheck;
+ ch=getw(f); /* mtime */
+ s=getc(f); /* share bit */
+ /*printf("loading: %s(%d)\n",dicp,ch); /* DEBUG */
+ if(files==NIL) /* is this the right dump? */
+ if(strcmp(dicp,src))
+ { BAD_DUMP=1;
+ if(aliases!=NIL)unscramble(aliases);
+ return(NIL); }
+ CFN=get_id(name()); /* wasteful way to share filename */
+ files = cons(make_fil(CFN,ch,s,load_defs(f)),
+ files);
+ }
+/* warning: load_defs side effects id's in namebuckets, cannot be undone by
+unload until attached to global `files', so interrupts are disabled during
+load_script - see steer.c */ /* for big dumps this may be too coarse - FIX */
+ if(ch==EOF||BAD_DUMP){ if(!BAD_DUMP)BAD_DUMP=2;
+ if(aliases!=NIL)unscramble(aliases);
+ return(files); }
+ if(files==NIL){ /* dump of syntax error state */
+ extern word oldfiles;
+ ch=getw(f);
+ if(main)errline=ch;
+ while((ch=getc(f))!=EOF)
+ { dicq=dicp;
+ if(ch!='/')(void)strcpy(dicp,prefix),dicq+=preflen;
+ /* locate wrt current posn */
+ *dicq++ = ch;
+ while((*dicq++ =ch=getc(f))&&ch!=EOF); /* filename */
+ ovflocheck;
+ ch=getw(f); /* mtime */
+ if(oldfiles==NIL) /* is this the right dump? */
+ if(strcmp(dicp,src))
+ { BAD_DUMP=1;
+ if(aliases!=NIL)unscramble(aliases);
+ return(NIL); }
+ oldfiles = cons(make_fil(get_id(name()),ch,0,NIL),
+ oldfiles);
+ }
+ if(aliases!=NIL)unscramble(aliases);
+ return(NIL); }
+ algshfns=append1(algshfns,load_defs(f));
+ ND=load_defs(f);
+ if(ND==True)ND=NIL,TORPHANS=1;
+ SGC=append1(SGC,load_defs(f));
+ if(main||includees==NIL)freeids=load_defs(f);
+ else bindparams(load_defs(f),hdsort(params));
+ if(aliases!=NIL)unscramble(aliases);
+ if(main)internals=load_defs(f);
+ return(reverse(files));
+}/* was it necessary to unscramble aliases before error returns?
+ check this later */
+/* actions labelled FIX1 were inserted to deal with the pathological case
+ that the destination of an alias (not part of a cyclic alias) has a direct
+ definition in the file and the aliasee is missing from the file
+ - this is both nameclash and missing aliasee, but without fix the two
+ errors cancel each other out and are unreported */
+
+word DETROP=NIL,MISSING=NIL;
+
+bindparams(formal,actual) /* process bindings of free ids */
+/* formal is list of cons(id,cons(original_name,type)) */
+/* actual is list of cons(name,value) | ap(name,typevalue)) */
+/* both in alpha order of original name */
+word formal,actual;
+{ extern word FBS; word badkind=NIL;
+ DETROP=MISSING=NIL;
+ FBS=cons(formal,FBS);
+ /* FBS is list of list of formals bound in current script */
+ for(;;)
+ { word a; char *f;
+ while(formal!=NIL && (actual==NIL ||
+ strcmp((f=(char *)hd[hd[tl[hd[formal]]]]),get_id(a=hd[hd[actual]]))<0))
+ /* the_val(hd[hd[formal]])=findid((char *)hd[hd[tl[hd[formal]]]]),
+ above line picks up identifier of that name in current scope */
+ MISSING=cons(hd[tl[hd[formal]]],MISSING),
+ formal=tl[formal];
+ if(actual==NIL)break;
+ if(formal==NIL||strcmp(f,get_id(a)))DETROP=cons(a,DETROP);
+ else { word fa=tl[tl[hd[formal]]]==type_t?t_arity(hd[hd[formal]]):-1;
+ word ta=tag[hd[actual]]==AP?t_arity(hd[actual]):-1;
+ if(fa!=ta)
+ badkind=cons(cons(hd[hd[actual]],datapair(fa,ta)),badkind);
+ the_val(hd[hd[formal]])=tl[hd[actual]];
+ formal=tl[formal]; }
+ actual=tl[actual];
+ }
+for(;badkind!=NIL;badkind=tl[badkind])
+ DETROP=cons(hd[badkind],DETROP);
+}
+
+unscramble(aliases) /* remove old to new diversions installed above */
+word aliases;
+{ word a=NIL;
+ for(;aliases!=NIL;aliases=tl[aliases])
+ { word old=tl[hd[aliases]],hold=hd[hd[aliases]];
+ word new=id_val(old);
+ hd[hd[aliases]]=new; /* put back for missing check, see below */
+ id_who(old)=hd[hold]; hold=tl[hold];
+ id_type(old)=hd[hold];
+ id_val(old)=tl[hold]; }
+ for(;ALIASES!=NIL;ALIASES=tl[ALIASES])
+ { word new=hd[hd[ALIASES]];
+ word old=tl[hd[ALIASES]];
+ if(tag[new]!=ID)
+ { if(!member(SUPPRESSED,new))a=cons(old,a);
+ continue; } /* aka stuff irrelevant to pnames */
+ if(id_type(new)==new_t)id_type(new)=undef_t; /* FIX1 */
+ if(id_type(new)==undef_t)a=cons(old,a); else
+ if(!member(CLASHES,new))
+ /* install aka info in new */
+ if(tag[id_who(new)]!=CONS)
+ id_who(new)=cons(datapair(get_id(old),0),id_who(new)); }
+ ALIASES=a; /* transmits info about missing aliasees */
+}
+
+char *getaka(x) /* returns original name of x (as a string) */
+word x;
+{ word y=id_who(x);
+ return(tag[y]!=CONS?get_id(x):(char *)hd[hd[y]]);
+}
+
+get_here(x) /* here info for id x */
+word x;
+{ word y=id_who(x);
+ return(tag[y]==CONS?tl[y]:y);
+}
+
+word *dstack=0,*stackp,*dlim;
+/* stackp=dstack; /* if load_script made interruptible, add to reset */
+
+dsetup()
+{ if(!dstack)
+ { dstack=(word *)malloc(1000*sizeof(word));
+ if(dstack==NULL)mallocfail("dstack");
+ dlim=dstack+1000; }
+ stackp=dstack;
+}
+
+dgrow()
+{ word *hold=dstack;
+ dstack=(word *)realloc(dstack,2*(dlim-dstack)*sizeof(word));
+ if(dstack==NULL)mallocfail("dstack");
+ dlim=dstack+2*(dlim-hold);
+ stackp += dstack-hold;
+ /*printf("dsize=%d\n",dlim-dstack); /* DEBUG */
+}
+
+load_defs(f) /* load a sequence of definitions from file f, terminated
+ by DEF_X, or a single object terminated by DEF_X */
+FILE *f;
+{ extern char *dicp, *dicq;
+ extern word *pnvec,common_stdin,common_stdinb,nextpn,rv_script;
+ word ch,defs=NIL;
+ while((ch=getc(f))!=EOF)
+ { if(stackp==dlim)dgrow();
+ switch(ch)
+ { case CHAR_X: *stackp++ = getc(f)+128;
+ continue;
+ case TVAR_X: *stackp++ = mktvar(getc(f));
+ continue;
+ case SHORT_X: ch = getc(f);
+ if(ch&128)ch= ch|(~127); /*force a sign extension*/
+ *stackp++ = stosmallint(ch);
+ continue;
+ case INT_X: { word *x;
+ ch = getw(f);
+ *stackp++ = make(INT,ch,0);
+ /* for 32 bit version suppress to end of varpart */
+ x = &rest(stackp[-1]);
+ ch = getw(f);
+ while(ch!= -1)
+ *x=make(INT,ch,0),ch=getw(f),x= &rest(*x);
+ /* end of variable length part */
+ continue; }
+ case DBL_X: ch=getw(f);
+ *stackp++ = make(DOUBLE,ch,getw(f));
+ continue;
+ case UNICODE_X: *stackp++ = make(UNICODE,getw(f),0);
+ continue;
+ case PN_X: ch = getc(f);
+ ch = PNBASE+(ch|(getc(f)<<8));
+ *stackp++ = ch<nextpn?pnvec[ch]:sto_pn(ch);
+ /* efficiency hack for *stackp++ = sto_pn(ch); */
+ continue;
+ case PN1_X: ch=PNBASE+getw(f);
+ *stackp++ = ch<nextpn?pnvec[ch]:sto_pn(ch);
+ /* efficiency hack for *stackp++ = sto_pn(ch); */
+ continue;
+ case CONSTRUCT_X: ch = getc(f);
+ ch = ch|(getc(f)<<8);
+ stackp[-1] = constructor(ch,stackp[-1]);
+ continue;
+ case RV_X: stackp[-1] = readvals(0,stackp[-1]);
+ rv_script=1;
+ continue;
+ case ID_X: dicq=dicp;
+ while((*dicq++ =ch=getc(f))&&ch!=EOF);
+ ovflocheck;
+ *stackp++=name(); /* see lex.c */
+ if(id_type(stackp[-1])==new_t) /* FIX1 (& next 2 lines) */
+ CLASHES=add1(stackp[-1],CLASHES),stackp[-1]=NIL;
+ else
+ if(id_type(stackp[-1])==alias_t) /* follow alias */
+ stackp[-1]=id_val(stackp[-1]);
+ continue;
+ case AKA_X: dicq=dicp;
+ while((*dicq++ =ch=getc(f))&&ch!=EOF);
+ ovflocheck;
+ *stackp++=datapair(get_id(name()),0);
+ /* wasteful, to share string */
+ continue;
+ case HERE_X: dicq=dicp;
+ ch=getc(f);
+ if(!ch){ /* coding hack, 0 means current file name */
+ ch = getc(f);
+ ch = ch|getc(f)<<8;
+ *stackp++ = fileinfo(CFN,ch);
+ continue; }
+ /* next line locates wrt current posn */
+ if(ch!='/')(void)strcpy(dicp,prefix),dicq+=preflen;
+ *dicq++ = ch;
+ while((*dicq++ =ch=getc(f))&&ch!=EOF);
+ ovflocheck;
+ ch = getc(f);
+ ch = ch|getc(f)<<8;
+ *stackp++ = fileinfo(get_id(name()),ch); /* wasteful */
+ continue;
+ case DEF_X: switch(stackp-dstack){
+ case 0: /* defs delimiter */
+ { /*printlist("contents: ",defs); /* DEBUG */
+ return(reverse(defs)); }
+ case 1: /* ob delimiter */
+ { return(*--stackp); }
+ case 2: /* pname defn */
+ { ch = *--stackp;
+ pn_val(ch)= *--stackp;
+ defs=cons(ch,defs); /* NB defs now includes pnames */
+ continue; }
+ case 4:
+ if(tag[stackp[-1]]!=ID)
+ if(stackp[-1]==NIL){ stackp -= 4; continue; } /* FIX1 */
+ else { /* id aliased to pname */
+ word akap;
+ ch= *--stackp;
+ SUPPRESSED=cons(ch,SUPPRESSED);
+ stackp--; /* who */
+ akap= tag[*stackp]==CONS?hd[*stackp]:NIL;
+ stackp--; /* lose type */
+ pn_val(ch)= *--stackp;
+ if(stackp[1]==type_t&&t_class(ch)!=synonym_t)
+ /* suppressed typename */
+ { word a=ALIASES; /* reverse assoc in ALIASES */
+ while(a!=NIL&&id_val(tl[hd[a]])!=ch)
+ a=tl[a];
+ if(a!=NIL) /* surely must hold ?? */
+ TSUPPRESSED=cons(tl[hd[a]],TSUPPRESSED);
+ /*if(akap==NIL)
+ akap=datapair(get_id(tl[hd[a]]),0); */
+ /*if(t_class(ch)==algebraic_t)
+ CSUPPRESS=append1(CSUPPRESS,t_info(ch));
+ t_info(ch)= cons(akap,fileinfo(CFN,0));
+ /* assists identifn of dangling typerefs
+ see privatise() in steer.c */ }else
+ if(pn_val(ch)==UNDEF)
+ { /* special kludge for undefined names */
+ /* necessary only if we allow names specified
+ but not defined to be %included */
+ if(akap==NIL) /* reverse assoc in ALIASES */
+ { word a=ALIASES;
+ while(a!=NIL&&id_val(tl[hd[a]])!=ch)
+ a=tl[a];
+ if(a!=NIL)
+ akap=datapair(get_id(tl[hd[a]]),0); }
+ pn_val(ch)= ap(akap,fileinfo(CFN,0));
+ /* this will generate sensible error message
+ see reduction rule for DATAPAIR */
+ }
+ defs=cons(ch,defs);
+ continue; }
+ if(
+ id_type(stackp[-1])!=new_t&& /* FIX1 */
+ (id_type(stackp[-1])!=undef_t||
+ id_val(stackp[-1])!=UNDEF)) /* nameclash */
+ { if(id_type(stackp[-1])==alias_t) /* cyclic aliasing */
+ { word a=ALIASES;
+ while(a!=NIL&&tl[hd[a]]!=stackp[-1])a=tl[a];
+ if(a==NIL)
+ { fprintf(stderr,
+ "impossible event in cyclic alias (%s)\n",
+ get_id(stackp[-1]));
+ stackp-=4;
+ continue; }
+ defs=cons(*--stackp,defs);
+ hd[hd[hd[a]]]= *--stackp; /* who */
+ hd[tl[hd[hd[a]]]]= *--stackp; /* type */
+ tl[tl[hd[hd[a]]]]= *--stackp; /* value */
+ continue; }
+ /*if(strcmp(CFN,hd[get_here(stackp[-1])]))
+ /* EXPT (ignore clash if from same original file) */
+ CLASHES=add1(stackp[-1],CLASHES);
+ stackp-=4; }
+ else
+ defs=cons(*--stackp,defs),
+ /*printf("%s undumped\n",get_id(hd[defs])), /* DEBUG */
+ id_who(hd[defs])= *--stackp,
+ id_type(hd[defs])= *--stackp,
+ id_val(hd[defs])= *--stackp;
+ continue;
+ default:
+ { /* printf("badly formed def in dump\n"); /* DEBUG */
+ BAD_DUMP=3; return(defs); } /* should unsetids */
+ } /* of switch */
+ case AP_X: ch = *--stackp;
+ if(stackp[-1]==READ&&ch==0)stackp[-1] = common_stdin; else
+ if(stackp[-1]==READBIN&&ch==0)stackp[-1] = common_stdinb; else
+ stackp[-1] = ap(stackp[-1],ch);
+ continue;
+ case CONS_X: ch = *--stackp;
+ stackp[-1] = cons(ch,stackp[-1]);
+ continue;
+ default: *stackp++ = ch>127?ch+256:ch;
+ }}
+ BAD_DUMP=4; /* should unsetids */
+ return(defs);
+}
+
+extern char *obsuffix;
+
+okdump(t) /* return 1 if script t has a non-syntax-error dump */
+char *t;
+{ char obf[120];
+ FILE *f;
+ (void)strcpy(obf,t);
+ (void)strcpy(obf+strlen(obf)-1,obsuffix);
+ f=fopen(obf,"r");
+ if(f&&getc(f)==XVERSION&&getc(f)){fclose(f); return(1); }
+ return(0);
+}
+
+geterrlin(t) /* returns errline from dump of t if relevant, 0 otherwise */
+char *t;
+{ char obf[120];
+ extern char *dicp,*dicq;
+ word ch,el;
+ FILE *f;
+ (void)strcpy(obf,t);
+ (void)strcpy(obf+strlen(obf)-1,obsuffix);
+ if(!(f=fopen(obf,"r")))return(0);
+ if(getc(f)!=XVERSION||(ch=getc(f))&&ch!=1){ fclose(f);
+ return(0); }
+ el=getw(f);
+ /* now check this is right dump */
+ setprefix(t);
+ ch=getc(f);
+ dicq=dicp;
+ if(ch!='/')(void)strcpy(dicp,prefix),dicq+=preflen;
+ /* locate wrt current posn */
+ *dicq++ = ch;
+ while((*dicq++ =ch=getc(f))&&ch!=EOF); /* filename */
+ ch=getw(f); /* mtime */
+ if(strcmp(dicp,t)||ch!=fm_time(t))return(0); /* wrong dump */
+ /* this test not foolproof, strictly should extract all files and check
+ their mtimes, as in undump, but this involves reading the whole dump */
+ return(el);
+}
+
+hdsort(x) /* sorts list of name-value pairs on name */
+word x;
+{ word a=NIL,b=NIL,hold=NIL;
+ if(x==NIL)return(NIL);
+ if(tl[x]==NIL)return(x);
+ while(x!=NIL) /* split x */
+ { hold=a,a=cons(hd[x],b),b=hold;
+ x=tl[x]; }
+ a=hdsort(a),b=hdsort(b);
+ /* now merge two halves back together */
+ while(a!=NIL&&b!=NIL)
+ if(strcmp(get_id(hd[hd[a]]),get_id(hd[hd[b]]))<0)x=cons(hd[a],x),a=tl[a];
+ else x=cons(hd[b],x),b=tl[b];
+ if(a==NIL)a=b;
+ while(a!=NIL)x=cons(hd[a],x),a=tl[a];
+ return(reverse(x));
+}
+#endif
+
+append1(x,y) /* rude append */
+word x,y;
+{ word x1=x;
+ if(x1==NIL)return(y);
+ while(tl[x1]!=NIL)x1=tl[x1];
+ tl[x1]=y;
+ return(x);
+}
+
+/* following is stuff for printing heap objects in readable form - used
+ for miscellaneous diagnostics etc - main function is out(FILE *,object) */
+
+/* charname returns the printable name of a character, as a string (using
+ C conventions for control characters */ /* DAT 13/9/83 */
+/* NB we use DECIMAL (not octal) for miscellaneous unprintables */
+
+/* WARNING - you should take a copy of the name if you intend to do anything
+ with it other than print it immediately */
+
+char *charname(c)
+word c;
+{ static char s[5];
+ switch(c)
+ { case '\n': return("\\n");
+ case '\t': return("\\t");
+ case '\b': return("\\b");
+ case '\f': return("\\f"); /* form feed */
+ case '\r': return("\\r"); /* carriage return */
+ case '\\': return("\\\\");
+ case '\'': return("\\'");
+ case '"': return("\\\"");
+ /* we escape all quotes for safety, since the context could be either
+ character or string quotation */
+ default: if(c<32||c>126) /* miscellaneous unprintables -- convert to decimal */
+ sprintf(s,"\\%d",c);
+ else s[0]=c,s[1]='\0';
+ return(s);
+ }
+}
+
+out(f,x)
+/* the routines "out","out1","out2" are for printing compiled expressions */
+FILE *f;
+word x;
+{
+#ifdef DEBUG
+ static pending=NIL; /* cycle trap */
+ word oldpending=pending; /* cycle trap */
+#endif
+ if(x<0||x>TOP){ fprintf(f,"<%d>",x); return; }
+#ifdef DEBUG
+ if(member(pending,x)){ fprintf(f,"..."); return; } /* cycle trap */
+ pending=cons(x,pending); /* cycle trap */
+#endif
+ if(tag[x]==LAMBDA)
+ { fprintf(f,"$(");out(f,hd[x]);putc(')',f);
+ out(f,tl[x]); } else
+ { while(tag[x]==CONS)
+ { out1(f,hd[x]);
+ putc(':',f);
+ x= tl[x];
+#ifdef DEBUG
+ if(member(pending,x))break; /* cycle trap */
+ pending=cons(x,pending); /* cycle trap */
+#endif
+ }
+ out1(f,x); }
+#ifdef DEBUG
+ pending=oldpending; /* cycle trap */
+#endif
+} /* warning - cycle trap not interrupt safe if `out' used in compiling
+ process */
+
+out1(f,x)
+FILE *f;
+word x;
+{ if(x<0||x>TOP){ fprintf(f,"<%d>",x); return; }
+ if(tag[x]==AP)
+ { out1(f,hd[x]);
+ putc(' ',f);
+ out2(f,tl[x]); }
+ else out2(f,x); }
+
+out2(f,x)
+FILE *f;
+word x;
+{ extern char *yysterm[], *cmbnms[];
+ if(x<0||x>TOP){ fprintf(f,"<%d>",x); return; }
+ if(tag[x]==INT)
+ { if(rest(x))
+ { x=bigtostr(x);
+ while(x)putc(hd[x],f),x=tl[x]; }
+ else fprintf(f,"%d",getsmallint(x));
+ return; }
+ if(tag[x]==DOUBLE){ outr(f,get_dbl(x)); return; }
+ if(tag[x]==ID){ fprintf(f,"%s",get_id(x)); return; }
+ if(x<256){ fprintf(f,"\'%s\'",charname(x)); return; }
+ if(tag[x]==UNICODE){ fprintf(f,"'\%x'",hd[x]); return; }
+ if(tag[x]==ATOM)
+ { fprintf(f,"%s",x<CMBASE?yysterm[x-256]:
+ x==True?"True":
+ x==False?"False":
+ x==NIL?"[]":
+ x==NILS?"\"\"":
+ cmbnms[x-CMBASE]);
+ return; }
+ if(tag[x]==TCONS||tag[x]==PAIR)
+ { fprintf(f,"(");
+ while(tag[x]==TCONS)
+ out(f,hd[x]), putc(',',f), x=tl[x];
+ out(f,hd[x]); putc(',',f); out(f,tl[x]);
+ putc(')',f); return; }
+ if(tag[x]==TRIES)
+ { fprintf(f,"TRIES("); out(f,hd[x]); putc(',',f); out(f,tl[x]);
+ putc(')',f); return; }
+ if(tag[x]==LABEL)
+ { fprintf(f,"LABEL("); out(f,hd[x]); putc(',',f); out(f,tl[x]);
+ putc(')',f); return; }
+ if(tag[x]==SHOW)
+ { fprintf(f,"SHOW("); out(f,hd[x]); putc(',',f); out(f,tl[x]);
+ putc(')',f); return; }
+ if(tag[x]==STARTREADVALS)
+ { fprintf(f,"READVALS("); out(f,hd[x]); putc(',',f); out(f,tl[x]);
+ putc(')',f); return; }
+ if(tag[x]==LET)
+ { fprintf(f,"(LET ");
+ out(f,dlhs(hd[x])),fprintf(f,"=");
+ out(f,dval(hd[x])),fprintf(f,";IN ");
+ out(f,tl[x]);
+ fprintf(f,")"); return; }
+ if(tag[x]==LETREC)
+ { word body=tl[x];
+ fprintf(f,"(LETREC ");
+ x=hd[x];
+ while(x!=NIL)out(f,dlhs(hd[x])),fprintf(f,"="),
+ out(f,dval(hd[x])),fprintf(f,";"),x=tl[x];
+ fprintf(f,"IN ");
+ out(f,body);
+ fprintf(f,")"); return; }
+ if(tag[x]==DATAPAIR)
+ { fprintf(f,"DATAPAIR(%s,%d)",(char *)hd[x],tl[x]);
+ return; }
+ if(tag[x]==FILEINFO)
+ { fprintf(f,"FILEINFO(%s,%d)",(char *)hd[x],tl[x]);
+ return; }
+ if(tag[x]==CONSTRUCTOR)
+ { fprintf(f,"CONSTRUCTOR(%d)",hd[x]);
+ return; }
+ if(tag[x]==STRCONS)
+ { fprintf(f,"<$%d>",hd[x]); return; }/* used as private id's, inter alia*/
+ if(tag[x]==SHARE)
+ { fprintf(f,"(SHARE:"); out(f,hd[x]); fprintf(f,")"); return; }
+ if(tag[x]!=CONS&&tag[x]!=AP&&tag[x]!=LAMBDA)
+ /* not a recognised structure */
+ { fprintf(f,"<%d|tag=%d>",x,tag[x]); return; }
+ putc('(',f);
+ out(f,x);
+ putc(')',f); }
+
+outr(f,r) /* prints a number */
+FILE *f;
+double r;
+{ double p;
+ p= r<0?-r: r;
+ if(p>=1000.0||p<=.001)fprintf(f,"%e",r);
+ else fprintf(f,"%f",r); }
+
+/* end of MIRANDA DATA REPRESENTATIONS */
+
diff --git a/new/lex.c b/new/lex.c
new file mode 100644
index 0000000..a4e9d09
--- /dev/null
+++ b/new/lex.c
@@ -0,0 +1,1213 @@
+/* MIRANDA LEX ANALYSER */
+
+/**************************************************************************
+ * Copyright (C) Research Software Limited 1985-90. All rights reserved. *
+ * The Miranda system is distributed as free software under the terms in *
+ * the file "COPYING" which is included in the distribution. *
+ *------------------------------------------------------------------------*/
+
+#include "data.h"
+#include "lex.h"
+#include <errno.h>
+
+extern word DICSPACE; /* see steer.c for default value */
+/* capacity in chars of dictionary space for storing identifiers and file names
+ to get a larger name space just increase this number */
+extern FILE *s_in;
+extern word echoing,listing,verbosity,magic,inbnf,inlex;
+word fileq=NIL; /* list of currently open-for-input files, of form
+ cons(strcons(stream,<ptr to element of 'files'>),...)*/
+word insertdepth= -1,margstack=NIL,col=0,lmargin=0;
+word echostack=NIL;
+word lverge=0,vergstack=NIL;
+char *prefixbase; /* stores prefixes for pathnames, to get static resolution */
+word prefixlimit=1024; /* initial size of space for prefixes */
+word prefix,prefixstack=NIL; /* current prefix, stack of old prefixes */
+word atnl=1,line_no=0;
+word lastline;
+word litstack=NIL,linostack=NIL;
+word c=' ', lastc;
+word commandmode;
+word common_stdin,common_stdinb,cook_stdin;
+word litmain=0,literate=0; /* flags "literate" comment convention */
+char *dic,*dicp,*dicq;
+char *pathname();
+
+setupdic()
+{ dicp=dicq=dic=malloc(DICSPACE);
+ if(dic==NULL)mallocfail("dictionary");
+ /* it is not permissible to realloc dic, because at the moment identifiers
+ etc. contain absolute pointers into the dictionary space - so we must
+ choose fairly large initial value for DICSPACE. Fix this later */
+ prefixbase=malloc(prefixlimit);
+ prefixbase[0]='\0';
+ prefix=0;
+}
+
+/* this allows ~login convention in filenames */
+/* #define okgetpwnam
+/* suppress 26.5.06 getpwnam cases runtime error when statically linked (Linux) */
+
+#ifdef okgetpwnam
+#include <pwd.h>
+struct passwd *getpwnam();
+#endif
+char *getenv();
+
+char *gethome(n) /* for expanding leading `~' in tokens and pathnames */
+char *n;
+{ struct passwd *pw;
+ if(n[0]=='\0')return(getenv("HOME"));
+#ifdef okgetpwnam
+ if(pw=getpwnam(n))return(pw->pw_dir);
+#endif
+ return(NULL);
+}
+
+#define ovflocheck if(dicq-dic>DICSPACE)dicovflo()
+
+dicovflo() /* is this called everywhere it should be? Check later */
+{ fprintf(stderr,"\npanic: dictionary overflow\n"); exit(1); }
+
+char *token() /* lex analyser for command language (very simple) */
+{ extern char *current_script;
+ word ch=getchar();
+ dicq = dicp; /* uses top of dictionary as temporary work space */
+ while(ch==' '||ch=='\t')ch=getchar();
+ if(ch=='~')
+ { char *h;
+ *dicq++ = ch;
+ ch=getchar();
+ while(isalnum(ch)||ch=='-'||ch=='_'||ch=='.')
+ *dicq++ = ch,ch=getchar();
+ /* NB csh does not allow `.' in user ids when expanding `~'
+ but this may be a mistake */
+ *dicq='\0';
+ if(h=gethome(dicp+1))
+ (void)strcpy(dicp,h),dicq=dicp+strlen(dicp);
+ }
+#ifdef SPACEINFILENAMES
+ if(ch!='"'&&ch!='<') /* test added 9.5.06 see else part */
+#endif
+ while(!isspace(ch)&&ch!=EOF)
+ { *dicq++ = ch;
+ if(ch=='%')
+ if(dicq[-2]=='\\')(--dicq)[-1]='%';
+ else dicq--,(void)strcpy(dicq,current_script),dicq+=strlen(dicq);
+ ch=getchar(); }
+#ifdef SPACEINFILENAMES
+ else { word closeq= ch=='<'?'>':'"'; /* this branch added 9.5.06 */
+ *dicq++ = ch; /* to allow spaces in "tok" or <tok> */
+ ch=getchar();
+ while(ch!=closeq&&ch!='\n'&&ch!=EOF)
+ *dicq++ = ch, ch=getchar();
+ if(ch==closeq)*dicq++ = ch, ch=getchar(); }
+#endif
+ *dicq++ = '\0';
+ ovflocheck;
+ while(ch==' '||ch=='\t')ch=getchar();
+ ungetc(ch,stdin);
+ return(*dicp=='\0'?(char *)NULL:dicp);
+} /* NB - if no token returns NULL rather than pointer to empty string */
+
+char *addextn(b,s) /* if(b)force s to end in ".m", and resolve <quotes> */
+word b;
+char *s;
+{ extern char *miralib;
+ extern char linebuf[];
+ word n=strlen(s);
+ /* printf("addextn(%s)\n",s); /* DEBUG */
+ if(s[0]=='<'&&s[n-1]=='>')
+ { static miralen=0; /* code to handle quotes added 21/1/87 */
+ if(!miralen)miralen=strlen(miralib);
+ strcpy(linebuf,miralib);
+ linebuf[miralen]= '/';
+ strcpy(linebuf+miralen+1,s+1);
+ strcpy(dicp,linebuf);
+ s=dicp;
+ n=n+miralen-1;
+ dicq=dicp+n+1;
+ dicq[-1] = '\0'; /* overwrites '>' */
+ ovflocheck; } else
+ if(s[0]=='\"'&&s[n-1]=='\"')
+ { /*strip quotes */
+ dicq=dicp; s++;
+ while(*s)*dicq++ = *s++;
+ dicq[-1]='\0'; /* overwrites '"' */
+ s=dicp; n=n-2;
+ }
+ if(!b||strcmp(s+n-2,".m")==0)return(s);
+ if(s==dicp)dicq--;/*if s in scratch area at top of dic, extend in situ*/
+ else { /* otherwise build new copy at top of dic */
+ dicq=dicp;
+ while(*s)*dicq++ = *s++;
+ *dicq = '\0'; }
+ if(strcmp(dicq-2,".x")==0)dicq -= 2; else
+ if(dicq[-1]=='.')dicq -= 1;
+ (void)strcpy(dicq,".m");
+ dicq += 3;
+ ovflocheck;
+ /* printf("return(%s)\n",dicp); /* DEBUG */
+ return(dicp);
+} /* NB - call keep(dicp) if the result is to be retained */
+
+word brct=0;
+
+spaces(n)
+word n;
+{ while(n-- >0)putchar(' ');
+}
+
+litname(s)
+char *s;
+{ word n=strlen(s);
+ return(n>=6 && strcmp(s+n-6,".lit.m")==0);
+}
+
+word getch() /* keeps track of current position in the variable "col"(column) */
+{ word ch= getc(s_in);
+ if(ch==EOF&&!atnl&&tl[fileq]==NIL) /* badly terminated top level file */
+ { atnl=1; return('\n'); }
+ if(atnl)
+ { if((line_no==0&&!commandmode||magic&&line_no==1)&&litstack==NIL)
+ litmain=literate= (ch=='>')||litname(get_fil(current_file));
+ if(literate)
+ { word i=0;
+ while(ch!=EOF&&ch!='>')
+ { ungetc(ch,s_in);
+ line_no++;
+ (void)fgets(dicp,250,s_in);
+ if(i==0&&line_no>1)chblank(dicp); i++;
+ if(echoing)spaces(lverge),fputs(dicp,stdout);
+ ch=getc(s_in); }
+ if((i>1||line_no==1&&i==1)&&ch!=EOF)chblank(dicp);
+ if(ch=='>')
+ { if(echoing)putchar(ch),spaces(lverge);ch=getc(s_in); }
+ } /* supports alternative `literate' comment convention */
+ atnl=0; col= lverge+literate;
+ if(!commandmode&&ch!=EOF)line_no++; }
+ if(echoing&&ch!=EOF)
+ { putchar(ch);
+ if(ch=='\n'&&!literate)
+ if(litmain)putchar('>'),spaces(lverge);
+ else spaces(lverge);
+ }
+ if(ch=='\t')col= ((col-lverge)/8 + 1)*8+lverge;
+ else col++;
+ if(ch=='\n')atnl= 1;
+ return(ch); }
+
+word blankerr=0;
+
+chblank(s)
+char *s;
+{ while(*s==' '||*s=='\t')s++;
+ if(*s=='\n')return;
+ syntax("formal text not delimited by blank line\n");
+ blankerr=1;
+ reset(); /* easiest way to recover is to pretend it was an interrupt */
+}
+
+/* getlitch gets a character from input like getch, but using C escaping
+ conventions if the char is backslash -- for use in reading character
+ and string constants */
+
+word rawch;
+/* it is often important to know, when certain characters are returned (e.g.
+ quotes and newlines) whether they were escaped or literal */
+
+word errch; /* for reporting unrecognised \escape */
+
+word getlitch()
+{ extern word UTF8;
+ word ch=c;
+ rawch = ch;
+ if(ch=='\n')return(ch); /* always an error */
+ if(UTF8&&ch>127)
+ { /* UTF-8 uses 2 or 3 bytes for unicode points to 0xffff */
+ word ch1=c=getch();
+ if((ch&0xe0)==0xc0) /* 2 bytes */
+ { if((ch1&0xc0)!=0x80)
+ return -5; /* not valid UTF8 */
+ c=getch();
+ return sto_char((ch&0x1f)<<6|ch1&0x3f); }
+ word ch2=c=getch();
+ if((ch&0xf0)==0xe0) /* 3 bytes */
+ { if((ch1&0xc0)!=0x80||(ch2&0xc0)!=0x80)
+ return -5; /* not valid UTF8 */
+ c=getch();
+ return sto_char((ch&0xf)<<12|(ch1&0x3f)<<6|ch2&0x3f); }
+ word ch3=c=getch();
+ if((ch&0xf8)==0xf0) /* 4 bytes, beyond basic multiligual plane */
+ { if((ch1&0xc0)!=0x80||(ch2&0xc0)!=0x80||(ch3&0xc0)!=0x80)
+ return -5; /* not valid UTF8 */
+ c=getch();
+ return((ch&7)<<18|(ch1&0x3f)<<12|(ch2&0x3f)<<6|ch3&0x3f); }
+ return(-5);
+ /* not UTF8 */
+ }
+ if(ch!='\\')
+ { c=getch(); return(ch); }
+ ch = getch();
+ c = getch();
+ switch(ch)
+ { case '\n': return(getlitch()); /* escaped nl was handled in 'getch()' */
+ case 'a': return('\a');
+ case 'b': return('\b');
+ case 'f': return('\f'); /* form feed */
+ case 'n': return('\n'); /* newline, == linefeed */
+ case 'r': return('\r'); /* carriage return */
+ case 't': return('\t');
+ case 'v': return('\v');
+ case 'X': /* omit for Haskell escape rules, see also lines marked H */
+ case 'x': if(isxdigit(c))
+ { word value, N=ch=='x'?4:6; /* N=7 for Haskell escape rules */
+ char hold[8];
+ ch = c;
+ word count=0;
+ /* while(ch=='0'&&isxdigit(peekch()))ch=getch(); /* H-lose leading 0s */
+ while(isxdigit(ch)&&count<N)
+ hold[count++]=ch,ch=getch();
+ /* read upto N hex digits */
+ hold[count] = '\0';
+ sscanf(hold,"%x",&value);
+ c = ch;
+ return value>UMAX?-3 /* \x out of range */
+ :sto_char(value); }
+ else return -2; /* \x with no hex digits */
+ default: if('0'<=ch&&ch<='9')
+ { word n=ch-'0',count=1,N=3; /* N=8 for Haskell escape rules */
+ ch = c;
+ /* while(ch=='0'&&isdigit(peekch()))ch=getch(); /* H-lose leading 0s */
+ while(isdigit(ch)&&count<N)
+ /* read upto N digits */
+ { n = 10*n+ch-'0';
+ count++;
+ ch = getch(); }
+ c = ch;
+ return /* n>UMAX?-4: /* H \decimal out of range */
+ sto_char(n); }
+ if(ch=='\''||ch=='"'||ch=='\\'||ch=='`')return(ch); /* see note */
+ if(ch=='&')return -7; /* Haskell null escape, accept silently */
+ errch=ch<=255?ch:'?';
+ return -6; /* unrecognised \something */
+ }
+} /* note: we accept \` for ` because getlitch() is used by charclass() */
+
+char *rdline() /* used by the "!" command -- see RULES */
+{ extern char *current_script;
+ static char linebuf[BUFSIZE];
+ char *p=linebuf;
+ word ch=getchar(),expansion=0;
+ while(ch==' '||ch=='\t')ch=getchar();
+ if(ch=='\n'||ch=='!'&&!(*linebuf))
+ { /* "!!" or "!" on its own means repeat last !command */
+ if(*linebuf)printf("!%s",linebuf);
+ while(ch!='\n'&&ch!=EOF)ch=getchar();
+ return(linebuf); }
+ if(ch=='!')
+ expansion=1,p=linebuf+strlen(linebuf)-1; /* p now points at old '\n' */
+ else ungetc(ch,stdin);
+ while((*p++ =ch=getchar())!='\n'&&ch!=EOF)
+ if(p-linebuf>=BUFSIZE)
+ { *p='\0';
+ fprintf(stderr,"sorry, !command too long (limit=%d chars): %s...\n",
+ BUFSIZE,linebuf);
+ while((ch=getchar())!='\n'&&ch!=EOF);
+ return(NULL);
+ } else
+ if(p[-1]=='%')
+ if(p>linebuf+1&&p[-2]=='\\')(--p)[-1]='%'; else
+ { (void)strncpy(p-1,current_script,linebuf+BUFSIZE-p);
+ p = linebuf+strlen(linebuf);
+ expansion = 1;
+ }
+ *p = '\0';
+ if(expansion)printf("!%s",linebuf);
+ return(linebuf); }
+
+setlmargin() /* this and the next routine are used to enforce the offside
+ rule ("yylex" refuses to read a symbol if col<lmargin) */
+{ margstack= cons(lmargin,margstack);
+ if(lmargin<col)lmargin= col; } /* inner scope region cannot "protrude" */
+
+unsetlmargin()
+{ if(margstack==NIL)return; /* in case called after `syntax("..")' */
+ lmargin= hd[margstack];
+ margstack= tl[margstack]; }
+
+word okid();
+word okulid();
+word PREL=1;
+
+#define isletter(c) ('a'<=c&&c<='z'||'A'<=c&&c<='Z')
+
+errclass(word val, word string)
+/* diagnose error in charclass, string or char const */
+{ char *s = string==2?"char class":string?"string":"char const";
+ if(val==-2)printf("\\x with no xdigits in %s\n",s); else
+ if(val==-3)printf("\\hexadecimal escape out of range in %s\n",s); else
+ if(val==-4)printf("\\decimal escape out of range in %s\n",s); else
+ if(val==-5)printf("unrecognised character in %s"
+ "(UTF8 error)\n",s); else
+ if(val==-6)printf("unrecognised escape \\%c in %s\n",errch,s); else
+ if(val==-7)printf("illegal use of \\& in char const\n"); else
+ printf("unknown error in %s\n",s);
+ acterror(); }
+
+yylex() /* called by YACC to get the next symbol */
+{ extern word SYNERR,exportfiles,inexplist,sreds;
+ /* SYNERR flags context sensitive syntax error detected in actions */
+ if(SYNERR)return(END); /* tell YACC to go home */
+ layout();
+ if(c=='\n') /* can only occur in command mode */
+/* if(magic){ commandmode=0; /* expression just read, now script */
+/* line_no=2;
+/* return(c); } else /* no longer relevant 26.11.2019 */
+ return(END);
+ if(col<lmargin)
+ if(c=='='&&(margstack==NIL||col>=hd[margstack]))/* && part fixes utah.bug*/
+ { c = getch();
+ return(ELSEQ); /* ELSEQ means "OFFSIDE =" */
+ }
+ else return(OFFSIDE);
+ if(c==';') /* fixes utah2.bug */
+ { c=getch(); layout();
+ if(c=='='&&(margstack==NIL||col>=hd[margstack]))
+ { c = getch();
+ return(ELSEQ); /* ELSEQ means "OFFSIDE =" */
+ }
+ else return(';');
+ }
+ if(
+ /* c=='_'&&okid(peekch()) || /* _id/_ID as lowercase id */
+ isletter(c)){ kollect(okid);
+ if(inlex==1){ layout();
+ yylval=name();
+ return(c=='='?LEXDEF:
+ isconstructor(yylval)?CNAME:
+ NAME); }
+ if(inbnf==1)
+ /* add trailing space to nonterminal to avoid clash
+ with ordinary names */
+ dicq[-1] = ' ',
+ *dicq++ = '\0';
+ return(identifier(0)); }
+ if('0'<=c&&c<='9'||c=='.'&&peekdig())
+ { if(c=='0'&&tolower(peekch())=='x')
+ hexnumeral(); else /* added 21.11.2013 */
+ if(c=='0'&&tolower(peekch())=='o')
+ getch(),c=getch(),octnumeral(); /* added 21.11.2013 */
+ else numeral();
+ return(CONST); }
+ if(c=='%'&&!commandmode)return(directive());
+ if(c=='\'')
+ { c = getch();
+ yylval= getlitch();
+ if(yylval<0){ errclass(yylval,0); return CONST; }
+ if(!is_char(yylval))
+ printf("%simpossible event while reading char const ('\\%u\')\n",
+ echoing?"\n":"",yylval),
+ acterror();
+ if(rawch=='\n'||c!='\'')syntax("improperly terminated char const\n");
+ else c= getch();
+ return(CONST); }
+ if(inexplist&&(c=='\"'||c=='<'))
+ { if(!pathname())syntax("badly formed pathname in %export list\n");
+ else exportfiles=strcons(addextn(1,dicp),exportfiles),
+ keep(dicp);
+ return(PATHNAME); }
+ if(inlex==1&&c=='`')
+ { return(charclass()?ANTICHARCLASS:CHARCLASS); }
+ if(c=='\"')
+ { string();
+ if(yylval==NIL)yylval=NILS; /* to help typechecker! */
+ return(CONST); }
+ if(inbnf==2) /* fiddle to offside rule in grammars */
+ if(c=='[')brct++; else if(c==']')brct--; else
+ if(c=='|'&&brct==0)
+ return(OFFSIDE);
+ if(c==EOF)
+ { if(tl[fileq]==NIL&&margstack!=NIL)return(OFFSIDE); /* to fix dtbug */
+ fclose((FILE *)hd[hd[fileq]]);
+ fileq= tl[fileq]; insertdepth--;
+ if(fileq!=NIL&&hd[echostack])
+ { if(literate)putchar('>'),spaces(lverge);
+ printf("<end of insert>"); }
+ s_in= fileq==NIL?stdin:(FILE *)hd[hd[fileq]];
+ c= ' ';
+ if(fileq==NIL)
+ { lverge=c=col=lmargin=0;
+ /* c=0; necessary because YACC sometimes reads 1 token past END */
+ atnl=1;
+ echoing=verbosity&listing;
+ lastline=line_no;
+ /* hack so errline can be set right if err at end of file */
+ line_no=0;
+ litmain=literate=0;
+ return(END); }
+ else { current_file = tl[hd[fileq]];
+ prefix=hd[prefixstack];
+ prefixstack=tl[prefixstack];
+ echoing=hd[echostack];
+ echostack=tl[echostack];
+ lverge=hd[vergstack];
+ vergstack=tl[vergstack];
+ literate=hd[litstack];
+ litstack=tl[litstack];
+ line_no=hd[linostack];
+ linostack=tl[linostack]; }
+ return(yylex()); }
+ lastc= c;
+ c= getch();
+#define try(x,y) if(c==x){ c=getch(); return(y); }
+ switch(lastc) {
+ case '_': if(c=='') /* underlined something */
+ { c=getch();
+ if(c=='<'){ c=getch(); return(LE); }
+ if(c=='>'){ c=getch(); return(GE); }
+ if(c=='%'&&!commandmode)return(directive());
+ if(isletter(c)) /* underlined reserved word */
+ { kollect(okulid);
+ if(dicp[1]=='_'&&dicp[2]=='')
+ return(identifier(1)); }
+ syntax("illegal use of underlining\n");
+ return('_'); }
+ return(lastc);
+ case '-': try('>',ARROW) try('-',MINUSMINUS) return(lastc);
+ case '<': try('-',LEFTARROW) try('=',LE) return(lastc);
+ case '=': if(c=='>'){ syntax("unexpected symbol =>\n"); return '='; }
+ try('=',EQEQ) return(lastc);
+ case '+': try('+',PLUSPLUS) return(lastc);
+ case '.': if(c=='.')
+ { c=getch();
+ return(DOTDOT);
+ }
+ return(lastc);
+ case '\\': try('/',VEL) return(lastc);
+ case '>': try('=',GE) return(lastc);
+ case '~': try('=',NE) return(lastc);
+ case '&': if(c=='>')
+ { c=getch();
+ if(c=='>')yylval=1;
+ else yylval=0,ungetc(c,s_in);
+ c=' ';
+ return(TO); }
+ return(lastc);
+ case '/': try('/',DIAG) return(lastc);
+ case '*': try('*',collectstars()) return(lastc);
+ case ':': if(c==':')
+ { c=getch();
+ if(c=='='){ c=getch(); return(COLON2EQ); }
+ else return(COLONCOLON);
+ }
+ return(lastc);
+ case '$': if(
+ /* c=='_'&&okid(peekch())|| /* _id/_ID as id */
+ isletter(c))
+ { word t;
+ kollect(okid);
+ t=identifier(0);
+ return(t==NAME?INFIXNAME:t==CNAME?INFIXCNAME:'$'); }
+ /* the last alternative is an error - caveat */
+ if('1'<=c&&c<='9')
+ { word n=0;
+ while(isdigit(c)&&n<1e6)n=10*n+c-'0',c=getch();
+ if(n>sreds)
+ /* sreds==0 everywhere except in semantic redn clause */
+ printf("%ssyntax error: illegal symbol $%d%s\n",
+ echoing?"\n":"",n,n>=1e6?"...":""),
+ acterror();
+ else { yylval=mkgvar(n); return(NAME); }
+ }
+ if(c=='-')
+ { if(!compiling)
+ syntax("unexpected symbol $-\n"); else
+ {c=getch(); yylval=common_stdin; return(CONST); }}
+ /* NB we disallow recursive use of $($/+/-) inside $+ data
+ whence addition of `compiling' to premises */
+ if(c==':')
+ { c=getch();
+ if(c!='-')syntax("unexpected symbol $:\n"); else
+ { if(!compiling)
+ syntax("unexpected symbol $:-\n"); else
+ {c=getch(); yylval=common_stdinb; return(CONST); }}} /* $:- */
+ if(c=='+')
+ { /* if(!(commandmode&&compiling||magic))
+ syntax("unexpected symbol $+\n"); else /* disallow in scripts */
+ if(!compiling)
+ syntax("unexpected symbol $+\n"); else
+ { c=getch();
+ if(commandmode)
+ yylval=cook_stdin;
+ else yylval=ap(readvals(0,0),OFFSIDE);
+ return(CONST); }}
+ if(c=='$')
+ { if(!(inlex==2||commandmode&&compiling))
+ syntax("unexpected symbol $$\n"); else
+ { c=getch();
+ if(inlex) { yylval=mklexvar(0); return(NAME); }
+ else return(DOLLAR2); }}
+ if(c=='#')
+ { if(inlex!=2)syntax("unexpected symbol $#\n"); else
+ { c=getch(); yylval=mklexvar(1); return(NAME); }}
+ if(c=='*')
+ { c=getch(); yylval=ap(GETARGS,0); return(CONST); }
+ if(c=='0')
+ syntax("illegal symbol $0\n");
+ default: return(lastc);
+}}
+
+layout()
+{L:while(c==' '||c=='\n'&&!commandmode||c=='\t') c= getch();
+ if(c==EOF&&commandmode){ c='\n'; return; }
+ if(c=='|'&&peekch()=='|' /* ||comments */
+ || col==1&&line_no==1 /* added 19.11.2013 */
+ &&c=='#'&&peekch()=='!') /* UNIX magic string */
+ { while((c=getch())!='\n'&&c!=EOF);
+ if(c==EOF&&!commandmode)return;
+ c= '\n';
+ goto L; }
+}
+
+collectstars()
+{ word n=2;
+ while(c=='*')c=getch(),n++;
+ yylval= mktvar(n);
+ return(TYPEVAR);
+}
+
+word gvars=NIL; /* list of grammar variables - no need to reset */
+
+mkgvar(i) /* make bound variable (corresponding to $i in bnf rule) */
+word i;
+{ word *p= &gvars;
+ while(--i)
+ { if(*p==NIL)*p=cons(sto_id("gvar"),NIL);
+ p= &tl[*p]; }
+ if(*p==NIL)*p=cons(sto_id("gvar"),NIL);
+ return(hd[*p]);
+} /* all these variables have the same name, and are not in hashbucket */
+
+word lexvar=0;
+
+mklexvar(i) /* similar - corresponds to $$, $# on rhs of %lex rule */
+word i; /* i=0 or 1 */
+{ extern word ltchar;
+ if(!lexvar)
+ lexvar=cons(sto_id("lexvar"),sto_id("lexvar")),
+ id_type(hd[lexvar])=ltchar,
+ id_type(tl[lexvar])=genlstat_t();
+ return(i?tl[lexvar]:hd[lexvar]);
+}
+
+word ARGC;
+char **ARGV; /* initialised in main(), see steer.c */
+
+conv_args() /* used to give access to command line args
+ see case GETARGS in reduce.c */
+{ word i=ARGC,x=NIL;
+ if(i==0)return(NIL); /* possible only if not invoked from a magic script */
+ { while(--i)x=cons(str_conv(ARGV[i]),x);
+ x=cons(str_conv(ARGV[0]),x); }
+ return(x);
+}
+
+str_conv(s) /* convert C string to Miranda form */
+char *s;
+{ word x=NIL,i=strlen(s);
+ while(i--)x=cons(s[i],x);
+ return(x);
+} /* opposite of getstring() - see reduce.c */
+
+okpath(ch)
+word ch;
+{ return(ch!='\"'&&ch!='\n'&&ch!='>'); }
+
+char *pathname() /* returns NULL if not valid pathname (in string quotes) */
+{ layout();
+ if(c=='<') /* alternative quotes <..> for system libraries */
+ { extern char *miralib;
+ char *hold=dicp;
+ c=getch();
+ (void)strcpy(dicp,miralib);
+ dicp+=strlen(miralib);
+ *dicp++ = '/';
+ kollect(okpath);
+ dicp=hold;
+ if(c!='>')return(NULL);
+ c=' ';
+ return(dicp); }
+ if(c!='\"')return(NULL);
+ c=getch();
+ if(c=='~')
+ { char *h,*hold=dicp;
+ extern char linebuf[];
+ *dicp++ = c;
+ c=getch();
+ while(isalnum(c)||c=='-'||c=='_'||c=='.')
+ *dicp++ = c, c=getch();
+ *dicp='\0';
+ if(h=gethome(hold+1))
+ (void)strcpy(hold,h),dicp=hold+strlen(hold);
+ else (void)strcpy(&linebuf[0],hold),
+ (void)strcpy(hold,prefixbase+prefix),
+ dicp=hold+strlen(prefixbase+prefix),
+ (void)strcpy(dicp,&linebuf[0]),
+ dicp+=strlen(dicp);
+ kollect(okpath);
+ dicp=hold;
+ } else
+ if(c=='/') /* absolute pathname */
+ kollect(okpath);
+ else { /* relative pathname */
+ char *hold=dicp;
+ (void)strcpy(dicp,prefixbase+prefix);
+ dicp+=strlen(prefixbase+prefix);
+ kollect(okpath);
+ dicp=hold; }
+ if(c!='\"')return(NULL);
+ c = ' ';
+ return(dicp);
+} /* result is volatile - call keep(dicp) to retain */
+
+adjust_prefix(f) /* called at %insert and at loadfile, to get static pathname
+ resolution */
+char *f;
+{ /* the directory part of the pathname f becomes the new
+ prefix for pathnames, and we stack the current prefix */
+ char *g;
+ prefixstack=strcons(prefix,prefixstack);
+ prefix += strlen(prefixbase+prefix)+1;
+ while(prefix+strlen(f)>=prefixlimit) /* check and fix overflow */
+ prefixlimit += 1024, prefixbase=realloc(prefixbase,prefixlimit);
+ (void)strcpy(prefixbase+prefix,f);
+ g=rindex(prefixbase+prefix,'/');
+ if(g)g[1]='\0';
+ else prefixbase[prefix]='\0';
+}
+
+/* NOTES on how static pathname resolution is achieved:
+(the specification is that pathnames must always be resolved relative to the
+file in which they are encountered)
+Definition -- the 'prefix' of a pathname is the initial segment up to but not
+including the last occurrence of '/' (null if no '/' present).
+Keep the wd constant during compilation. Have a global char* prefix, initially
+null.
+1) Whenever you read a relative pathname(), insert 'prefix' on the front of it.
+2) On entering a new level of insert, stack old prefix and prefix becomes that
+ of new file name. Done by calling adjust_prefix().
+3) On quitting a level of insert, unstack old prefix.
+*/
+
+peekdig()
+{ word ch = getc(s_in);
+ ungetc(ch,s_in);
+ return('0'<=ch&&ch<='9');
+}
+
+peekch()
+{ word ch = getc(s_in);
+ ungetc(ch,s_in);
+ return(ch);
+}
+
+openfile(n) /* returns 0 or 1 as indication of success - puts file on fileq
+ if successful */
+char *n;
+{ FILE *f;
+ f= fopen(n,"r");
+ if(f==NULL)return(0);
+ fileq= cons(strcons(f,NIL),fileq);
+ insertdepth++;
+ return(1);
+}
+
+identifier(s) /* recognises reserved words */
+word s; /* flags looking for ul reserved words only */
+{ extern word lastid,initialising;
+ if(inbnf==1)
+ { /* only reserved nonterminals are `empty', `end', `error', `where' */
+ if(is("empty ")||is("e_m_p_t_y"))return(EMPTYSY); else
+ if(is("end ")||is("e_n_d"))return(ENDSY); else
+ if(is("error ")||is("e_r_r_o_r"))return(ERRORSY); else
+ if(is("where ")||is("w_h_e_r_e"))return(WHERE); }
+ else
+ switch(dicp[0])
+ { case 'a': if(is("abstype")||is("a_b_s_t_y_p_e"))
+ return(ABSTYPE);
+ break;
+ case 'd': if(is("div")||is("d_i_v"))
+ return(DIV);
+ break;
+ case 'F': if(is("False")) /* True, False alleged to be predefined, not
+ reserved (??) */
+ { yylval = False;
+ return(CONST); }
+ break;
+ case 'i': if(is("if")||is("i_f"))
+ return(IF);
+ break;
+ case 'm': if(is("mod")||is("m_o_d"))
+ return(REM);
+ break;
+ case 'o': if(is("otherwise")||is("o_t_h_e_r_w_i_s_e"))
+ return(OTHERWISE);
+ break;
+ case 'r': if(is("readvals")||is("r_e_a_d_v_a_l_s"))
+ return(READVALSY);
+ break;
+ case 's': if(is("show")||is("s_h_o_w"))
+ return(SHOWSYM);
+ break;
+ case 'T': if(is("True"))
+ { yylval = True;
+ return(CONST); }
+ case 't': if(is("type")||is("t_y_p_e"))
+ return(TYPE);
+ break;
+ case 'w': if(is("where")||is("w_h_e_r_e"))
+ return(WHERE);
+ if(is("with")||is("w_i_t_h"))
+ return(WITH);
+ break;
+ }
+ if(s){ syntax("illegal use of underlining\n"); return('_'); }
+ yylval=name(); /* not a reserved word */
+ if(commandmode&&lastid==0&&id_type(yylval)!=undef_t)lastid=yylval;
+ return(isconstructor(yylval)?CNAME:NAME);
+}
+
+word disgusting=0; /* flag to turn off typecheck, temporary hack for jrc */
+
+directive() /* these are of the form "%identifier" */
+{ extern word SYNERR,magic;
+ word holdcol=col-1,holdlin=line_no;
+ c = getch();
+ if(c=='%'){ c=getch(); return(ENDIR); }
+ kollect(okulid);
+ switch(dicp[0]=='_'&&dicp[1]==''?dicp[2]:dicp[0])
+ { case 'b': if(is("begin")||is("_^Hb_^He_^Hg_^Hi_^Hn"))
+ if(inlex)
+ return(LBEGIN);
+ if(is("bnf")||is("_^Hb_^Hn_^Hf"))
+ { setlmargin(); col=holdcol+4;
+ /* `indent' to right hand end of directive */
+ return(BNF); }
+ break;
+ case 'e': if(is("export")||is("_e_x_p_o_r_t"))
+ { if(magic)syntax(
+ "%export directive not permitted in \"-exp\" script\n");
+ return(EXPORT); }
+ break;
+ case 'f': if(is("free")||is("_f_r_e_e"))
+ { if(magic)syntax(
+ "%free directive not permitted in \"-exp\" script\n");
+ return(FREE); }
+ break;
+ case 'i': if(is("include")||is("_i_n_c_l_u_d_e"))
+ { if(!SYNERR){ layout(); setlmargin(); }
+ /* does `indent' for grammar */
+ if(!pathname())
+ syntax("bad pathname after %include\n");
+ else yylval=strcons(addextn(1,dicp),
+ fileinfo(get_fil(current_file),holdlin)),
+ /* (includee,hereinfo) */
+ keep(dicp);
+ return(INCLUDE); }
+ if(is("insert")||is("_i_n_s_e_r_t"))
+ { char *f=pathname();
+ if(!f)syntax("bad pathname after %insert\n"); else
+ if(insertdepth<12&&openfile(f))
+ { adjust_prefix(f);
+ vergstack=cons(lverge,vergstack);
+ echostack=cons(echoing,echostack);
+ litstack=cons(literate,litstack);
+ linostack=strcons(line_no,linostack);
+ line_no=0; atnl=1; /* was line_no=1; */
+ keep(dicp);
+ current_file = make_fil(f,fm_time(f),0,NIL);
+ files = append1(files,cons(current_file,NIL));
+ tl[hd[fileq]] = current_file;
+ s_in = (FILE *)hd[hd[fileq]];
+ literate= peekch()=='>'||litname(f);
+ col=lverge=holdcol;
+ if(echoing)
+ { putchar('\n');
+ if(!literate)
+ if(litmain)putchar('>'),spaces(holdcol);
+ else spaces(holdcol); }
+ c = getch(); } /* used to precede previous cmd when echo
+ was delayed by one char, see getch() */
+ else { word toomany=(insertdepth>=12);
+ printf("%s%%insert error - cannot open \"%s\"\n",
+ echoing?"\n":"",f);
+ keep(dicp);
+ if(toomany)printf(
+ "too many nested %%insert directives (limit=%d)\n",
+ insertdepth);
+ else
+ files = append1(files,cons(make_fil(f,0,0,NIL),NIL));
+ /* line above for benefit of `oldfiles' */
+ acterror(); }
+ return(yylex()); }
+ break;
+ case 'l': if(is("lex")||is("_^Hl_^He_^Hx"))
+ { if(inlex)syntax("nested %lex not permitted\n");
+ /* due to use of global vars inlex, lexdefs */
+ return(LEX); }
+ if(is("list")||is("_l_i_s_t"))
+ { echoing=verbosity; return(yylex()); }
+ break;
+ case 'n': if(is("nolist")||is("_n_o_l_i_s_t"))
+ { echoing=0; return(yylex()); }
+ break;
+ }
+ if(echoing)putchar('\n');
+ printf("syntax error: unknown directive \"%%%s\"\n",dicp),
+ acterror();
+ return(END);
+}
+
+okid(ch)
+word ch;
+{ return('a'<=ch&&ch<='z'||'A'<=ch&&ch<='Z'||'0'<=ch&&ch<='9'
+ ||ch=='_'||ch=='\''); }
+
+okulid(ch)
+word ch;
+{ return('a'<=ch&&ch<='z'||'A'<=ch&&ch<='Z'||'0'<=ch&&ch<='9'
+ ||ch=='_'||ch==''||ch=='\''); }
+
+kollect(f)
+/* note top of dictionary used as work space to collect current token */
+word (*f)();
+{ dicq= dicp;
+ while((*f)(c)){ *dicq++ = c; c= getch(); }
+ *dicq++ = '\0';
+ ovflocheck;
+}
+
+char *keep(p) /* call this to retain volatile string for later use */
+char *p;
+{ if(p==dicp)dicp= dicq;
+ else (void)strcpy(dicp,p),
+ p=dicp,
+ dicp=dicq=dicp+strlen(dicp)+1,
+ dic_check();
+ return(p);
+}
+
+dic_check() /* called from REDUCE */
+{ ovflocheck; }
+
+numeral()
+{ word nflag=1;
+ dicq= dicp;
+ while(isdigit(c))
+ *dicq++ = c, c=getch();
+ if(c=='.'&&peekdig())
+ { *dicq++ = c, c=getch(); nflag=0;
+ while(isdigit(c))
+ *dicq++ = c, c=getch(); }
+ if(c=='e')
+ { word np=0;
+ *dicq++ = c, c=getch(); nflag=0;
+ if(c=='+')c=getch(); else /* ignore + before exponent */
+ if(c=='-')*dicq++ = c, c=getch();
+ if(!isdigit(c)) /* e must be followed by some digits */
+ syntax("badly formed floating point number\n");
+ while(c=='0')
+ *dicq++ = c, c=getch();
+ while(isdigit(c))
+ np++, *dicq++ = c, c=getch();
+ if(!nflag&&np>3) /* scanf falls over with silly exponents */
+ { syntax("floating point number out of range\n");
+ return; }
+ }
+ ovflocheck;
+ if(nflag) /* `.' or `e' makes fractional */
+ *dicq = '\0',
+ yylval= bigscan(dicp); else
+ { double r=0.0;
+ if(dicq-dicp>60) /* this allows 59 chars */
+ /* scanf crashes, on VAX, gives wrong answers, on ORION 1/05 */
+ { syntax("illegal floating point constant (too many digits)\n");
+ return; }
+ errno=0;
+ *dicq = '\n';
+ sscanf(dicp,"%lf",&r);
+ if(errno)fpe_error(); else
+ yylval= sto_dbl((double)r); }
+}
+
+hexnumeral() /* added 21.11.2013 */
+{ extern word errno;
+ word nflag=1;
+ dicq= dicp;
+ *dicq++ = c, c=getch(); /* 0 */
+ *dicq++ = c, c=getch(); /* x */
+ if(!isxdigit(c)&&c!='.')syntax("malformed hex number\n");
+ while(c=='0'&&isxdigit(peekch()))c=getch(); /* skip zeros before first nonzero digit */
+ while(isxdigit(c))
+ *dicq++ = c, c=getch();
+ ovflocheck;
+ if(c=='.'||tolower(c)=='p') /* hex float, added 20.11.19 */
+ { double d;
+ if(c=='.')
+ { *dicq++ = c, c=getch();
+ while(isxdigit(c))
+ *dicq++ = c, c=getch(); }
+ if(c=='p')
+ { *dicq++ = c, c=getch();
+ if(c=='+'||c=='-')*dicq++ = c, c=getch();
+ if(!isdigit(c))syntax("malformed hex float\n");
+ while(isdigit(c))
+ *dicq++ = c, c=getch(); }
+ ovflocheck;
+ *dicq='\0';
+ if(dicq-dicp>60||sscanf(dicp,"%lf",&d)!=1)
+ syntax("malformed hex float\n");
+ else yylval= sto_dbl(d);
+ return; }
+ *dicq = '\0';
+ yylval= bigxscan(dicp+2,dicq);
+}
+
+octnumeral() /* added 21.11.2013 */
+{ extern word errno;
+ word nflag=1;
+ dicq= dicp;
+ if(!isdigit(c))syntax("malformed octal number\n");
+ while(c=='0'&&isdigit(peekch()))c=getch(); /* skip zeros before first nonzero digit */
+ while(isdigit(c)&&c<='7')
+ *dicq++ = c, c=getch();
+ if(isdigit(c))syntax("illegal digit in octal number\n");
+ ovflocheck;
+ *dicq = '\0';
+ yylval= bigoscan(dicp,dicq);
+}
+
+word namebucket[128]; /* each namebucket has a list terminated by 0, not NIL */
+
+hash(s) /* returns a value in {0..127} */
+char *s;
+{ word h = *s;
+ if(h)while(*++s)h ^= *s; /* guard necessary to deal with s empty */
+ return(h&127);
+}
+
+isconstrname(s)
+char *s;
+{ if(s[0]=='$')s++;
+ return isupper(*s); /* formerly !islower */
+}
+
+getfname(x)
+/* nonterminals have an added ' ', getfname returns the corresponding
+ function name */
+word x;
+{ char *p = get_id(x);
+ dicq= dicp;
+ while(*dicq++ = *p++);
+ if(dicq-dicp<3)fprintf(stderr,"impossible event in getfname\n"),exit(1);
+ dicq[-2] = '\0'; /* overwrite last char */
+ ovflocheck;
+ return(name());
+}
+
+isnonterminal(x)
+word x;
+{ char *n;
+ if(tag[x]!=ID)return(0);
+ n = get_id(x);
+ return(n[strlen(n)-1]==' ');
+}
+
+name()
+{ word q,h;
+ q= namebucket[h=hash(dicp)];
+ while(q&&!is(get_id(hd[q])))q= tl[q];
+ if(q==0)
+ { q = sto_id(dicp);
+ namebucket[h] = cons(q,namebucket[h]);
+ keep(dicp); }
+ else q= hd[q];
+ return(q); }
+/* note - keeping buckets sorted didn't seem to help (if anything slightly
+ slower) probably because ordering only relevant if name not present, and
+ outweighed by increased complexity of loop */
+
+static word inprelude=1;
+
+make_id(n) /* used in mira_setup(), primdef(), predef(), all in steer.c */
+char *n;
+{ word x,h;
+ h=hash(n);
+ x = sto_id(inprelude?keep(n):n);
+ namebucket[h] = cons(x,namebucket[h]);
+ return(x); }
+
+findid(n) /* like name() but returns NIL rather than create new id */
+char *n;
+{ word q;
+ q= namebucket[hash(n)];
+ while(q&&!strcmp(n,get_id(hd[q]))==0)q= tl[q];
+ return(q?hd[q]:NIL); }
+
+word *pnvec=0,nextpn,pn_lim=200; /* private name vector */
+
+reset_pns() /* (re)initialise private name space */
+{ nextpn=0;
+ if(!pnvec)
+ { pnvec=(word *)malloc(pn_lim*sizeof(word));
+ if(pnvec==NULL)mallocfail("pnvec"); }
+}
+
+make_pn(val) /* create new private name with value val */
+word val;
+{ if(nextpn==pn_lim)
+ { pn_lim+=400;
+ pnvec=(word *)realloc(pnvec,pn_lim*sizeof(word));
+ if(pnvec==NULL)mallocfail("pnvec"); }
+ pnvec[nextpn]=strcons(nextpn,val);
+ return(pnvec[nextpn++]);
+}
+
+sto_pn(n) /* return n'th private name, extending pnvec if necessary */
+word n;
+{ if(n>=pn_lim)
+ { while(pn_lim<=n)pn_lim+=400;
+ pnvec=(word *)realloc(pnvec,pn_lim*sizeof(word));
+ if(pnvec==NULL)mallocfail("pnvec"); }
+ while(nextpn<=n) /* NB allocates all missing names upto and including nth*/
+ pnvec[nextpn]=strcons(nextpn,UNDEF),nextpn++;
+ return(pnvec[n]);
+}
+
+mkprivate(x) /* disguise identifiers prior to removal from environment */
+word x; /* used in setting up prelude - see main() in steer.c */
+{ while(x!=NIL)
+ { char *s = get_id(hd[x]);
+ get_id(hd[x])[0] += 128; /* hack to make private internal name */
+ x = tl[x]; } /* NB - doesn't change hashbucket */
+ inprelude=0;
+}
+
+word sl=100;
+
+string()
+{ word p;
+ word ch,badch=0;
+ c = getch();
+ ch= getlitch();
+ p= yylval= cons(NIL,NIL);
+ while(ch!=EOF&&rawch!='\"'&&rawch!='\n')
+ if(ch==-7) ch=getlitch(); else /* skip \& */
+ if(ch<0){ badch=ch; break; }
+ else { p= tl[p]= cons(ch,NIL);
+ ch= getlitch(); }
+ yylval= tl[yylval];
+ if(badch)errclass(badch,1);
+ if(rawch=='\n')
+ syntax("non-escaped newline encountered inside string quotes\n"); else
+ if(ch==EOF)
+ { if(echoing)putchar('\n');
+ printf("syntax error: script ends inside unclosed string quotes - \n");
+ printf(" \"");
+ while(yylval!=NIL&& sl-- )
+ { putchar(hd[yylval]);
+ yylval= tl[yylval]; }
+ printf("...\"\n");
+ acterror(); }
+}
+
+charclass()
+{ word p;
+ word ch,badch=0,anti=0;
+ c = getch();
+ if(c=='^')anti=1,c=getch();
+ ch= getlitch();
+ p= yylval= cons(NIL,NIL);
+ while(ch!=EOF&&rawch!='`'&&rawch!='\n')
+ if(ch==-7)ch=getlitch(); else /* skip \& */
+ if(ch<0){ badch=ch; break; }
+ else { if(rawch=='-'&&hd[p]!=NIL&&hd[p]!=DOTDOT)
+ ch=DOTDOT; /* non-initial, non-escaped '-' */
+ p= tl[p]= cons(ch,NIL);
+ ch= getlitch(); }
+ if(hd[p]==DOTDOT)hd[p]='-'; /* naturalise a trailing '-' */
+ for(p=yylval;tl[p]!=NIL;p=tl[p]) /* move each DOTDOT to front of range */
+ if(hd[tl[p]]==DOTDOT)
+ { hd[tl[p]]=hd[p],hd[p]=DOTDOT;
+ if(hd[tl[p]]>=hd[tl[tl[p]]])
+ syntax("illegal use of '-' in [charclass]\n");
+ }
+ yylval= tl[yylval];
+ if(badch)errclass(badch,2);
+ if(rawch=='\n')
+ syntax("non-escaped newline encountered in char class\n"); else
+ if(ch==EOF)
+ { if(echoing)putchar('\n');
+ printf(
+ "syntax error: script ends inside unclosed char class brackets - \n");
+ printf(" [");
+ while(yylval!=NIL&& sl-- )
+ { putchar(hd[yylval]);
+ yylval= tl[yylval]; }
+ printf("...]\n");
+ acterror(); }
+ return(anti);
+}
+
+reset_lex() /* called after an error */
+{ extern word errs,errline;
+ extern char *current_script;
+ /*printf("reset_lex()\n"); /* DEBUG */
+ if(!commandmode)
+ { if(!errs)errs=fileinfo(get_fil(current_file),line_no);
+ /* convention, if errs set contains location of error, otherwise pick up
+ from current_file and line_no */
+ if(tl[errs]==0&&(char *)hd[errs]==current_script)
+ /* at end of file, so line_no has been reset to 0 */
+ printf("error occurs at end of ");
+ else printf("error found near line %d of ",tl[errs]);
+ printf("%sfile \"%s\"\ncompilation abandoned\n",
+ (char *)hd[errs]==current_script?"":"%insert ",
+ (char *)hd[errs]);
+ if((char *)hd[errs]==current_script)
+ errline=tl[errs]==0?lastline:tl[errs],errs=0;
+ else { while(tl[linostack]!=NIL)linostack=tl[linostack];
+ errline=hd[linostack]; }
+ /* tells editor where to find error - errline contains location of 1st
+ error in main script, errs is hereinfo of upto one error in %insert
+ script (each is 0 if not set) - some errors can set both */
+ }
+ reset_state();
+}
+
+reset_state() /* reset all global variables used by compiler */
+{ extern word TABSTRS,SGC,newtyps,algshfns,showchain,inexplist,sreds,
+ rv_script,idsused;
+ /* printf("reset_state()\n"); /* DEBUG */
+ if(commandmode)
+ while(c!='\n'&&c!=EOF)c=getc(s_in); /* no echo */
+ while(fileq!=NIL)fclose((FILE *)hd[hd[fileq]]),fileq=tl[fileq];
+ insertdepth= -1;
+ s_in=stdin;
+ echostack=idsused=prefixstack=litstack=linostack=vergstack
+ =margstack=NIL;
+ prefix=0; prefixbase[0]='\0';
+ echoing=verbosity&listing;
+ brct=inbnf=sreds=inlex=inexplist=commandmode=lverge=col=lmargin=0;
+ atnl=1;
+ rv_script=0;
+ algshfns=newtyps=showchain=SGC=TABSTRS=NIL;
+ c=' ';
+ line_no=0;
+ litmain=literate=0;
+ /* printf("exit reset_state()\n"); /* DEBUG */
+}
+
+/* end of MIRANDA LEX ANALYSER */
+
diff --git a/new/reduce.c b/new/reduce.c
new file mode 100644
index 0000000..04f7267
--- /dev/null
+++ b/new/reduce.c
@@ -0,0 +1,2376 @@
+/* MIRANDA REDUCE */
+/* new SK reduction machine - installed Oct 86 */
+
+/**************************************************************************
+ * Copyright (C) Research Software Limited 1985-90. All rights reserved. *
+ * The Miranda system is distributed as free software under the terms in *
+ * the file "COPYING" which is included in the distribution. *
+ *------------------------------------------------------------------------*/
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+struct stat buf; /* used only by code for FILEMODE, FILESTAT in reduce */
+#include "data.h"
+#include "big.h"
+#include "lex.h"
+extern word debug, UTF8, UTF8OUT;
+#define FST HD
+#define SND TL
+#define BSDCLOCK
+/* POSIX clock wraps around after c. 72 mins */
+#ifdef RYU
+char* d2s(double);
+word d2s_buffered(double, char*);
+#endif
+
+double fa,fb;
+long long cycles=0;
+word stdinuse=0;
+/* int lasthead=0; /* DEBUG */
+
+#define constr_tag(x) hd[x]
+#define idconstr_tag(x) hd[id_val(x)]
+#define constr_name(x) (tag[tl[x]]==ID?get_id(tl[x]):get_id(pn_val(tl[x])))
+#define suppressed(x) (tag[tl[x]]==STRCONS&&tag[pn_val(tl[x])]!=ID)
+ /* suppressed constructor */
+
+#define isodigit(x) ('0'<=(x) && (x)<='7')
+#define sign(x) (x)
+#define fsign(x) ((d=(x))<0?-1:d>0)
+/* ### */ /* functions marked ### contain possibly recursive calls
+ to reduce - fix later */
+
+compare(a,b) /* returns -1, 0, 1 as a is less than equal to or greater than
+ b in the ordering imposed on all data types by the miranda
+ language -- a and b already reduced */
+ /* used by MATCH, EQ, NEQ, GR, GRE */
+word a,b;
+{ double d;
+ L: switch(tag[a])
+ { case DOUBLE:
+ if(tag[b]==DOUBLE)return(fsign(get_dbl(a)-get_dbl(b)));
+ else return(fsign(get_dbl(a)-bigtodbl(b)));
+ case INT:
+ if(tag[b]==INT)return(bigcmp(a,b));
+ else return(fsign(bigtodbl(a)-get_dbl(b)));
+ case UNICODE: return sign(get_char(a)-get_char(b));
+ case ATOM:
+ if(tag[b]==UNICODE) return sign(get_char(a)-get_char(b));
+ if(S<=a&&a<=ERROR||S<=b&&b<=ERROR)
+ fn_error("attempt to compare functions");
+ /* what about constructors - FIX LATER */
+ if(tag[b]==ATOM)return(sign(a-b)); /* order of declaration */
+ else return(-1); /* atomic object always less than non-atomic */
+ case CONSTRUCTOR:
+ if(tag[b]==CONSTRUCTOR)
+ return(sign(constr_tag(a)-constr_tag(b))); /*order of declaration*/
+ else return(-1); /* atom less than non-atom */
+ case CONS: case AP:
+ if(tag[a]==tag[b])
+ { word temp;
+ hd[a]=reduce(hd[a]);
+ hd[b]=reduce(hd[b]);
+ if((temp=compare(hd[a],hd[b]))!=0)return(temp);
+ a=tl[a]=reduce(tl[a]);
+ b=tl[b]=reduce(tl[b]);
+ goto L; }
+ else if(S<=b&&b<=ERROR)fn_error("attempt to compare functions");
+ else return(1); /* non-atom greater than atom */
+ default: fprintf(stderr,"\nghastly error in compare\n");
+ }
+ return(0);
+}
+
+force(x) /* ensures that x is evaluated "all the way" */
+word x; /* x is already reduced */ /* ### */
+{ word h;
+ switch(tag[x])
+ { case AP: h=hd[x];
+ while(tag[h]==AP)h=hd[h];
+ if(S<=h&&h<=ERROR)return; /* don't go inside functions */
+ /* what about unsaturated constructors? fix later */
+ while(tag[x]==AP)
+ { tl[x]=reduce(tl[x]);
+ force(tl[x]);
+ x=hd[x]; }
+ return;
+ case CONS: while(tag[x]==CONS)
+ { hd[x]=reduce(hd[x]);
+ force(hd[x]);
+ x=tl[x]=reduce(tl[x]); }
+ }
+ return;
+}
+
+head(x) /* finds the function part of x */
+word x;
+{ while(tag[x]==AP)x= hd[x];
+ return(x);
+}
+
+extern char linebuf[]; /* used as workspace in various places */
+
+/* ### */ /* opposite is str_conv - see lex.c */
+char *getstring(x,cmd) /* collect Miranda string - x is already reduced */
+word x;
+char *cmd; /* context, for error message */
+{ word x1=x,n=0;
+ char *p=linebuf;
+ while(tag[x]==CONS&&n<BUFSIZE)
+ n++, hd[x] = reduce(hd[x]), x=tl[x]=reduce(tl[x]);
+ x=x1;
+ while(tag[x]==CONS&&n--)
+ *p++ = hd[x], x=tl[x];
+ *p++ ='\0';
+ if(p-linebuf>BUFSIZE)
+ { if(cmd)fprintf(stderr,
+ "\n%s, argument string too long (limit=%d chars): %s...\n",
+ cmd,BUFSIZE,linebuf),
+ outstats(),
+ exit(1);
+ else return(linebuf); /* see G_CLOSE */ }
+ return(linebuf); /* very inefficient to keep doing this for filenames etc.
+ CANNOT WE SUPPORT A PACKED REPRESENTATION OF STRINGS? */
+} /* call keep(linebuf) if you want to save the string */
+
+FILE *s_out=NULL; /* destination of current output message */
+ /* initialised in main() */
+#define Stdout 0
+#define Stderr 1
+#define Tofile 2
+#define Closefile 3
+#define Appendfile 4
+#define System 5
+#define Exit 6
+#define Stdoutb 7
+#define Tofileb 8
+#define Appendfileb 9
+ /* order of declaration of constructors of these names in sys_message */
+
+/* ### */
+output(e) /* "output" is called by YACC (see MIRANDA RULES) to print the
+ value of an expression - output then calls "reduce" - so the
+ whole reduction process is driven by the need to print */
+ /* the value of the whole expression is a list of `messages' */
+word e;
+{
+ extern word *cstack;
+ cstack = &e; /* don't follow C stack below this in gc */
+L:e= reduce(e);
+ while(tag[e]==CONS)
+ { word d;
+ hd[e]= reduce(hd[e]);
+ switch(constr_tag(head(hd[e])))
+ { case Stdout: print(tl[hd[e]]);
+ break;
+ case Stdoutb: UTF8OUT=0;
+ print(tl[hd[e]]);
+ UTF8OUT=UTF8;
+ break;
+ case Stderr: s_out=stderr; print(tl[hd[e]]); s_out=stdout;
+ break;
+ case Tofile: outf(hd[e]);
+ break;
+ case Tofileb: UTF8OUT=0;
+ outf(hd[e]);
+ UTF8OUT=UTF8;
+ break;
+ case Closefile: closefile(tl[hd[e]]=reduce(tl[hd[e]]));
+ break;
+ case Appendfile: apfile(tl[hd[e]]=reduce(tl[hd[e]]));
+ break;
+ case Appendfileb: UTF8OUT=0;
+ apfile(tl[hd[e]]=reduce(tl[hd[e]]));
+ UTF8OUT=UTF8;
+ break;
+ case System: system(getstring(tl[hd[e]]=reduce(tl[hd[e]]),"System"));
+ break;
+ case Exit: { word n=reduce(tl[hd[e]]);
+ if(tag[n]==INT)n=digit0(n);
+ else word_error("Exit");
+ outstats(); exit(n); }
+ default: fprintf(stderr,"\n<impossible event in output list: ");
+ out(stderr,hd[e]);
+ fprintf(stderr,">\n"); }
+ e= tl[e]= reduce(tl[e]);
+ }
+ if(e==NIL)return;
+ fprintf(stderr,"\nimpossible event in output\n"),
+ putc('<',stderr),out(stderr,e),fprintf(stderr,">\n");
+ exit(1);
+}
+
+/* ### */
+print(e) /* evaluate list of chars and send to s_out */
+word e;
+{ e= reduce(e);
+ while(tag[e]==CONS && is_char(hd[e]=reduce(hd[e])))
+ { unsigned word c=get_char(hd[e]);
+ if(UTF8)outUTF8(c,s_out); else
+ if(c<256) putc(c,s_out);
+ else fprintf(stderr,"\n warning: non Latin1 char \%x in print, ignored\n",c);
+ e= tl[e]= reduce(tl[e]); }
+ if(e==NIL)return;
+ fprintf(stderr,"\nimpossible event in print\n"),
+ putc('<',stderr),out(stderr,e),fprintf(stderr,">\n"),
+ exit(1);
+}
+
+word outfilq=NIL; /* list of opened-for-output files */
+/* note that this will be automatically reset to NIL and all files on it
+closed at end of expression evaluation, because of the fork-exit structure */
+
+/* ### */
+outf(e) /* e is of the form (Tofile f x) */
+word e;
+{ word p=outfilq; /* have we already opened this file for output? */
+ char *f=getstring(tl[hd[e]]=reduce(tl[hd[e]]),"Tofile");
+ while(p!=NIL && strcmp((char *)hd[hd[p]],f)!=0)p=tl[p];
+ if(p==NIL) /* new output file */
+ { s_out= fopen(f,"w");
+ if(s_out==NULL)
+ { fprintf(stderr,"\nTofile: cannot write to \"%s\"\n",f);
+ s_out=stdout;
+ return;
+ /* outstats(); exit(1); /* release one policy */
+ }
+ if(isatty(fileno(s_out)))setbuf(s_out,NULL); /*for unbuffered tty output*/
+ outfilq= cons(datapair(keep(f),s_out),outfilq); }
+ else s_out= (FILE *)tl[hd[p]];
+ print(tl[e]);
+ s_out= stdout;
+}
+
+apfile(f) /* open file of name f for appending and add to outfilq */
+word f;
+{ word p=outfilq; /* is it already open? */
+ char *fil=getstring(f,"Appendfile");
+ while(p!=NIL && strcmp((char *)hd[hd[p]],fil)!=0)p=tl[p];
+ if(p==NIL) /* no, so open in append mode */
+ { FILE *s=fopen(fil,"a");
+ if(s==NULL)
+ fprintf(stderr,"\nAppendfile: cannot write to \"%s\"\n",fil);
+ else outfilq= cons(datapair(keep(fil),s),outfilq);
+ }
+ /* if already there do nothing */
+}
+
+closefile(f) /* remove file of name "f" from outfilq and close stream */
+word f;
+{ word *p= &outfilq; /* is this file open for output? */
+ char *fil=getstring(f,"Closefile");
+ while(*p!=NIL && strcmp((char *)hd[hd[*p]],fil)!=0)p= &tl[*p];
+ if(*p!=NIL) /* yes */
+ { fclose((FILE *)tl[hd[*p]]);
+ *p=tl[*p]; /* remove link from outfilq */}
+ /* otherwise ignore closefile request (harmless??) */
+}
+
+static word errtrap=0; /* to prevent error cycles - see ERROR below */
+word waiting=NIL;
+/* list of terminated child processes with exit_status - see Exec/EXEC */
+
+/* pointer-reversing SK reduction machine - based on code written Sep 83 */
+
+#define BACKSTOP 020000000000
+#define READY(x) (x)
+#define RESTORE(x)
+/* in this machine the above two are no-ops, alternate definitions are, eg
+#define READY(x) (x+1)
+#define RESTORE(x) x--
+(if using this method each strict comb needs next opcode unallocated)
+ see comment before "ready" switch */
+#define FIELD word
+#define tlptrbit 020000000000
+#define tlptrbits 030000000000
+ /* warning -- if you change this tell `mark()' in data.c */
+#define mktlptr(x) x |= tlptrbit
+#define mk1tlptr x |= tlptrbits
+#define mknormal(x) x &= ~tlptrbits
+#define abnormal(x) ((x)<0)
+/* covers x is tlptr and x==BACKSTOP */
+
+/* control abstractions */
+
+#define setcell(t,a,b) tag[e]=t,hd[e]=a,tl[e]=b
+#define DOWNLEFT hold=s, s=e, e=hd[e], hd[s]=hold
+#define DOWNRIGHT hold=hd[s], hd[s]=e, e=tl[s], tl[s]=hold, mktlptr(s)
+#define downright if(abnormal(s))goto DONE; DOWNRIGHT
+#define UPLEFT hold=s, s=hd[s], hd[hold]=e, e=hold
+#define upleft if(abnormal(s))goto DONE; UPLEFT
+#define GETARG(a) UPLEFT, a=tl[e]
+#define getarg(a) upleft; a=tl[e]
+#define UPRIGHT mknormal(s), hold=tl[s], tl[s]=e, e=hd[s], hd[s]=hold
+#define lastarg tl[e]
+word reds=0;
+
+/* IMPORTANT WARNING - the macro's
+ `downright;' `upleft;' `getarg;'
+ MUST BE ENCLOSED IN BRACES when they occur as the body of a control
+ structure (if, while etc.) */
+
+#define simpl(r) hd[e]=I, e=tl[e]=r
+
+#ifdef DEBUG
+word maxrdepth=0,rdepth=0;
+#endif
+
+#define fails(x) (x==NIL)
+#define FAILURE NIL
+ /* used by grammar combinators */
+
+/* reduce e to hnf, note that a function in hnf will have head h with
+ S<=h<=ERROR all combinators lie in this range see combs.h */
+FIELD reduce(e)
+FIELD e;
+{ FIELD s=BACKSTOP,hold,arg1,arg2,arg3;
+#ifdef DEBUG
+ if(++rdepth>maxrdepth)maxrdepth=rdepth;
+ if(debug&02)
+ printf("reducing: "),out(stdout,e),putchar('\n');
+#endif
+
+ NEXTREDEX:
+ while(!abnormal(e)&&tag[e]==AP)DOWNLEFT;
+#ifdef HISTO
+ histo(e);
+#endif
+#ifdef DEBUG
+ if(debug&02)
+ { printf("head= ");
+ if(e==BACKSTOP)printf("BACKSTOP");
+ else out(stdout,e);
+ putchar('\n'); }
+#endif
+
+ OPDECODE:
+/*lasthead=e; /* DEBUG */
+ cycles++;
+ switch(e)
+ {
+ case S: /* S f g x => f x(g x) */
+ getarg(arg1);
+ getarg(arg2);
+ upleft;
+ hd[e]=ap(arg1,lastarg); tl[e]=ap(arg2,lastarg);
+ DOWNLEFT;
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ case B: /* B f g x => f(g z) */
+ getarg(arg1);
+ getarg(arg2);
+ upleft;
+ hd[e]=arg1; tl[e]=ap(arg2,lastarg);
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ case CB: /* CB f g x => g(f z) */
+ getarg(arg1);
+ getarg(arg2);
+ upleft;
+ hd[e]=arg2; tl[e]=ap(arg1,lastarg);
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ case C: /* C f g x => f x g */
+ getarg(arg1);
+ getarg(arg2);
+ upleft;
+ hd[e]=ap(arg1,lastarg); tl[e]=arg2;
+ DOWNLEFT;
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ case Y: /* Y h => self where self=(h self) */
+ upleft;
+ hd[e]=tl[e]; tl[e]=e;
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ L_K:
+ case K: /* K x y => x */
+ getarg(arg1);
+ upleft;
+ hd[e]=I; e=tl[e]=arg1;
+ goto NEXTREDEX; /* could make eager in first arg */
+
+ L_KI:
+ case KI: /* KI x y => y */
+ upleft; /* lose first arg */
+ upleft;
+ hd[e]=I; e=lastarg; /* ?? */
+ goto NEXTREDEX; /* could make eager in 2nd arg */
+
+ case S1: /* S1 k f g x => k(f x)(g x) */
+ getarg(arg1);
+ getarg(arg2);
+ getarg(arg3);
+ upleft;
+ hd[e]=ap(arg2,lastarg);
+ hd[e]=ap(arg1,hd[e]);
+ tl[e]=ap(arg3,lastarg);
+ DOWNLEFT;
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ case B1: /* B1 k f g x => k(f(g x)) */
+ getarg(arg1); /* Mark Scheevel's new B1 */
+ getarg(arg2);
+ getarg(arg3);
+ upleft;
+ hd[e]=arg1;
+ tl[e]=ap(arg3,lastarg);
+ tl[e]=ap(arg2,tl[e]);
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ case C1: /* C1 k f g x => k(f x)g */
+ getarg(arg1);
+ getarg(arg2);
+ getarg(arg3);
+ upleft;
+ hd[e]=ap(arg2,lastarg);
+ hd[e]=ap(arg1,hd[e]);
+ tl[e]=arg3;
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ case S_p: /* S_p f g x => (f x) : (g x) */
+ getarg(arg1);
+ getarg(arg2);
+ upleft;
+ setcell(CONS,ap(arg1,lastarg),ap(arg2,lastarg));
+ goto DONE;
+
+ case B_p: /* B_p f g x => f : (g x) */
+ getarg(arg1);
+ getarg(arg2);
+ upleft;
+ setcell(CONS,arg1,ap(arg2,lastarg));
+ goto DONE;
+
+ case C_p: /* C_p f g x => (f x) : g */
+ getarg(arg1);
+ getarg(arg2);
+ upleft;
+ setcell(CONS,ap(arg1,lastarg),arg2);
+ goto DONE;
+
+ case ITERATE: /* ITERATE f x => x:ITERATE f (f x) */
+ getarg(arg1);
+ upleft;
+ hold=ap(hd[e],ap(arg1,lastarg));
+ setcell(CONS,lastarg,hold);
+ goto DONE;
+
+ case ITERATE1: /* ITERATE1 f x => [], x=FAIL
+ => x:ITERATE1 f (f x), otherwise */
+ getarg(arg1);
+ upleft;
+ if((lastarg=reduce(lastarg))==FAIL) /* ### */
+ { hd[e]=I; e=tl[e]=NIL; }
+ else
+ { hold=ap(hd[e],ap(arg1,lastarg));
+ setcell(CONS,lastarg,hold); }
+ goto DONE;
+
+ case G_RULE:
+ case P: /* P x y => x:y */
+ getarg(arg1);
+ upleft;
+ setcell(CONS,arg1,lastarg);
+ goto DONE;
+
+ case U: /* U f x => f (HD x) (TL x)
+ non-strict uncurry */
+ getarg(arg1);
+ upleft;
+ hd[e]=ap(arg1,ap(HD,lastarg));
+ tl[e]=ap(TL,lastarg);
+ DOWNLEFT;
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ case Uf: /* Uf f x => f (BODY x) (LAST x)
+ version of non-strict U for
+ arbitrary constructors */
+ getarg(arg1);
+ upleft;
+ if(tag[head(lastarg)]==CONSTRUCTOR) /* be eager if safe */
+ hd[e]=ap(arg1,hd[lastarg]),
+ tl[e]=tl[lastarg];
+ else
+ hd[e]=ap(arg1,ap(BODY,lastarg)),
+ tl[e]=ap(LAST,lastarg);
+ DOWNLEFT;
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ case ATLEAST: /* ATLEAST k f x => f(x-k), isnat x & x>=k
+ => FAIL, otherwise */
+ /* for matching n+k patterns */
+ getarg(arg1);
+ getarg(arg2);
+ upleft;
+ lastarg= reduce(lastarg); /* ### */
+ if(tag[lastarg]==INT)
+ { hold = bigsub(lastarg,arg1);
+ if(poz(hold))hd[e]=arg2,tl[e]=hold;
+ else hd[e]=I,e=tl[e]=FAIL; }
+ else hd[e]=I,e=tl[e]=FAIL;
+ goto NEXTREDEX;
+
+ case U_: /* U_ f (a:b) => f a b
+ U_ f other => FAIL
+ U_ is a strict version of U(see above) */
+ getarg(arg1);
+ upleft;
+ lastarg= reduce(lastarg); /* ### */
+ if(lastarg==NIL)
+ { hd[e]=I;
+ e=tl[e]=FAIL;
+ goto NEXTREDEX; }
+ hd[e]=ap(arg1,hd[lastarg]);
+ tl[e]=tl[lastarg];
+ goto NEXTREDEX;
+
+ case Ug: /* Ug k f (k x1 ... xn) => f x1 ... xn, n>=0
+ Ug k f other => FAIL
+ Ug is a strict version of U for arbitrary constructor k */
+ getarg(arg1);
+ getarg(arg2);
+ upleft;
+ lastarg= reduce(lastarg); /* ### */
+ if(constr_tag(arg1)!=constr_tag(head(lastarg)))
+ { hd[e]=I;
+ e=tl[e]=FAIL;
+ goto NEXTREDEX; }
+ if(tag[lastarg]==CONSTRUCTOR) /* case n=0 */
+ { hd[e]=I; e=tl[e]=arg2; goto NEXTREDEX; }
+ hd[e]=hd[lastarg];
+ tl[e]=tl[lastarg];
+ while(tag[hd[e]]!=CONSTRUCTOR)
+ /* go back to head of arg3, copying spine */
+ { hd[e]=ap(hd[hd[e]],tl[hd[e]]);
+ DOWNLEFT; }
+ hd[e]=arg2; /* replace k with f */
+ goto NEXTREDEX;
+
+ case MATCH: /* MATCH a f a => f
+ MATCH a f b => FAIL */
+ upleft;
+ arg1=lastarg=reduce(lastarg); /* ### */
+ /* note that MATCH evaluates arg1, usually needless, could have second
+ version - MATCHEQ, say */
+ getarg(arg2);
+ upleft;
+ lastarg=reduce(lastarg); /* ### */
+ hd[e]=I;
+ e=tl[e]=compare(arg1,lastarg)?FAIL:arg2;
+ goto NEXTREDEX;
+
+ case MATCHINT: /* same but 1st arg is integer literal */
+ getarg(arg1);
+ getarg(arg2);
+ upleft;
+ lastarg=reduce(lastarg); /* ### */
+ hd[e]=I;
+ e=tl[e]=(tag[lastarg]!=INT||bigcmp(arg1,lastarg))?FAIL:arg2;
+ /* note no coercion from INT to DOUBLE here */
+ goto NEXTREDEX;
+
+ case GENSEQ: /* GENSEQ (i,NIL) a => a:GENSEQ (i,NIL) (a+i)
+ GENSEQ (i,b) a => [], a>b=sign
+ => a:GENSEQ (i,b) (a+i), otherwise
+ where
+ sign = 1, i>=0
+ = -1, otherwise */
+ GETARG(arg1);
+ UPLEFT;
+ if(tl[arg1]!=NIL&&
+ (tag[arg1]==AP?compare(lastarg,tl[arg1]):compare(tl[arg1],lastarg))>0)
+ hd[e]=I, e=tl[e]=NIL;
+ else hold=ap(hd[e],numplus(lastarg,hd[arg1])),
+ setcell(CONS,lastarg,hold);
+ goto DONE;
+ /* efficiency hack - tag of arg1 encodes sign of step */
+
+ case MAP: /* MAP f [] => []
+ MAP f (a:x) => f a : MAP f x */
+ getarg(arg1);
+ upleft;
+ lastarg=reduce(lastarg); /* ### */
+ if(lastarg==NIL)
+ hd[e]=I, e=tl[e]=NIL;
+ else hold=ap(hd[e],tl[lastarg]),
+ setcell(CONS,ap(arg1,hd[lastarg]),hold);
+ goto DONE;
+
+ case FLATMAP: /* funny version of map for compiling zf exps
+ FLATMAP f [] => []
+ FLATMAP f (a:x) => FLATMAP f x, f a=FAIL
+ => f a ++ FLATMAP f x
+ (FLATMAP was formerly called MAP1) */
+ getarg(arg1);
+ getarg(arg2);
+ L1:arg2=reduce(arg2); /* ### */
+ if(arg2==NIL)
+ { hd[e]=I;
+ e=tl[e]=NIL;
+ goto DONE; }
+ hold=reduce(hold=ap(arg1,hd[arg2]));
+ if(hold==FAIL||hold==NIL){ arg2=tl[arg2]; goto L1; }
+ tl[e]=ap(hd[e],tl[arg2]);
+ hd[e]=ap(APPEND,hold);
+ goto NEXTREDEX;
+
+ case FILTER: /* FILTER f [] => []
+ FILTER f (a:x) => a : FILTER f x, f a
+ => FILTER f x, otherwise */
+ getarg(arg1);
+ upleft;
+ lastarg=reduce(lastarg); /* ### */
+ while(lastarg!=NIL&&reduce(ap(arg1,hd[lastarg]))==False) /* ### */
+ lastarg=reduce(tl[lastarg]); /* ### */
+ if(lastarg==NIL)
+ hd[e]=I, e=tl[e]=NIL;
+ else hold=ap(hd[e],tl[lastarg]),
+ setcell(CONS,hd[lastarg],hold);
+ goto DONE;
+
+ case LIST_LAST: /* LIST_LAST x => x!(#x-1) */
+ upleft;
+ if((lastarg=reduce(lastarg))==NIL)fn_error("last []"); /* ### */
+ while((tl[lastarg]=reduce(tl[lastarg]))!=NIL) /* ### */
+ lastarg=tl[lastarg];
+ hd[e]=I; e=tl[e]=hd[lastarg];
+ goto NEXTREDEX;
+
+ case LENGTH: /* takes length of a list */
+ upleft;
+ { long long n=0; /* problem - may be followed by gc */
+ /* cannot make static because of ### below */
+ while((lastarg=reduce(lastarg))!=NIL) /* ### */
+ lastarg=tl[lastarg],n++;
+ simpl(sto_word(n)); }
+ goto DONE;
+
+ case DROP:
+ getarg(arg1);
+ upleft;
+ arg1=tl[hd[e]]=reduce(tl[hd[e]]); /* ### */
+ if(tag[arg1]!=INT)word_error("drop");
+ { long long n=get_word(arg1);
+ while(n-- >0)
+ if((lastarg=reduce(lastarg))==NIL) /* ### */
+ { simpl(NIL); goto DONE; }
+ else lastarg=tl[lastarg]; }
+ simpl(lastarg);
+ goto NEXTREDEX;
+
+ case SUBSCRIPT: /* SUBSCRIPT i x => x!i */
+ upleft;
+ upleft;
+ arg1=tl[hd[e]]=reduce(tl[hd[e]]); /* ### */
+ lastarg=reduce(lastarg); /* ### */
+ if(lastarg==NIL)subs_error();
+ { long long indx = tag[arg1]==ATOM?arg1:/* small indexes represented directly */
+ tag[arg1]==INT?get_word(arg1):
+ word_error("!");
+ /* problem, indx may be followed by gc
+ - cannot make static, because of ### below */
+ if(indx<0)subs_error();
+ while(indx)
+ { lastarg= tl[lastarg]= reduce(tl[lastarg]); /* ### */
+ if(lastarg==NIL)subs_error();
+ indx--; }
+ hd[e]= I;
+ e=tl[e]=hd[lastarg]; /* could be eager in tl[e] */
+ goto NEXTREDEX; }
+
+ case FOLDL1: /* FOLDL1 op (a:x) => FOLDL op a x */
+ getarg(arg1);
+ upleft;
+ if((lastarg=reduce(lastarg))!=NIL) /* ### */
+ { hd[e]=ap2(FOLDL,arg1,hd[lastarg]);
+ tl[e]=tl[lastarg];
+ goto NEXTREDEX; }
+ else fn_error("foldl1 applied to []");
+
+ case FOLDL: /* FOLDL op r [] => r
+ FOLDL op r (a:x) => FOLDL op (op r a)^ x
+
+ ^ (FOLDL op) is made strict in 1st param */
+ getarg(arg1);
+ getarg(arg2);
+ upleft;
+ while((lastarg=reduce(lastarg))!=NIL) /* ### */
+ arg2=reduce(ap2(arg1,arg2,hd[lastarg])), /* ^ ### */
+ lastarg=tl[lastarg];
+ hd[e]=I, e=tl[e]=arg2;
+ goto NEXTREDEX;
+
+ case FOLDR: /* FOLDR op r [] => r
+ FOLDR op r (a:x) => op a (FOLDR op r x) */
+ getarg(arg1);
+ getarg(arg2);
+ upleft;
+ lastarg=reduce(lastarg); /* ### */
+ if(lastarg==NIL)
+ hd[e]=I, e=tl[e]=arg2;
+ else hold=ap(hd[e],tl[lastarg]),
+ hd[e]=ap(arg1,hd[lastarg]), tl[e]=hold;
+ goto NEXTREDEX;
+
+ L_READBIN:
+ case READBIN: /* READBIN streamptr => nextchar : READBIN streamptr
+ if end of file, READBIN file => NIL
+ READBIN does no UTF-8 conversion */
+ UPLEFT; /* gc insecurity - arg is not a heap object */
+ if(lastarg==0) /* special case created by $:- */
+ { if(stdinuse=='-')stdin_error(':');
+ if(stdinuse)
+ { hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ stdinuse=':';
+ tl[e]=(word)stdin; }
+ hold= getc((FILE *)lastarg);
+ if(hold==EOF)
+ { fclose((FILE *)lastarg);
+ hd[e]=I;
+ e=tl[e]= NIL;
+ goto DONE; }
+ setcell(CONS,hold,ap(READBIN,lastarg));
+ goto DONE;
+
+ L_READ:
+ case READ: /* READ streamptr => nextchar : READ streamptr
+ if end of file, READ file => NIL
+ does UTF-8 conversion where appropriate */
+ UPLEFT; /* gc insecurity - arg is not a heap object */
+ if(lastarg==0) /* special case created by $- */
+ { if(stdinuse==':')stdin_error('-');
+ if(stdinuse)
+ { hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ stdinuse='-';
+ tl[e]=(word)stdin; }
+ hold=UTF8?sto_char(fromUTF8((FILE *)lastarg)):getc((FILE *)lastarg);
+ if(hold==EOF)
+ { fclose((FILE *)lastarg);
+ hd[e]=I;
+ e=tl[e]= NIL;
+ goto DONE; }
+ setcell(CONS,hold,ap(READ,lastarg));
+ goto DONE;
+
+ L_READVALS:
+ case READVALS: /* READVALS (t:fil) f => [], EOF from FILE *f
+ => val : READVALS t f, otherwise
+ where val is obtained by parsing lines of
+ f, and taking next legal expr of type t */
+ GETARG(arg1);
+ upleft;
+ hold=parseline(hd[arg1],(FILE *)lastarg,tl[arg1]);
+ if(hold==EOF)
+ { fclose((FILE *)lastarg);
+ hd[e]=I;
+ e=tl[e]= NIL;
+ goto DONE; }
+ arg2=ap(hd[e],lastarg);
+ setcell(CONS,hold,arg2);
+ goto DONE;
+
+ case BADCASE: /* BADCASE cons(oldn,here_info) => BOTTOM */
+ UPLEFT;
+ { extern word sourcemc;
+ word subject= hd[lastarg];
+ /* either datapair(oldn,0) or 0 */
+ fprintf(stderr,"\nprogram error: missing case in definition");
+ if(subject) /* cannot do patterns - FIX LATER */
+ fprintf(stderr," of %s",(char *)hd[subject]);
+ putc('\n',stderr);
+ out_here(stderr,tl[lastarg],1);
+ /* if(sourcemc&&nargs>1)
+ { int i=2;
+ fprintf(stderr,"arg%s = ",nargs>2?"s":"");
+ while(i<=nargs)out(stderr,tl[stackp[-(i++)]]),putc(' ',stderr);
+ putc('\n',stderr); } /* fix later */
+ }
+ outstats();
+ exit(1);
+
+ case GETARGS: /* GETARGS 0 => argv ||`$*' = command line args */
+ UPLEFT;
+ simpl(conv_args());
+ goto DONE;
+
+ case CONFERROR: /* CONFERROR error_info => BOTTOM */
+ /* if(nargs<1)fprintf(stderr,"\nimpossible event in reduce\n"),
+ exit(1); */
+ UPLEFT;
+ fprintf(stderr,"\nprogram error: lhs of definition doesn't match rhs");
+ /*fprintf(stderr," OF ");
+ out_formal1(stderr,hd[lastarg]); /* omit - names may have been aliased */
+ putc('\n',stderr);
+ out_here(stderr,tl[lastarg],1);
+ outstats();
+ exit(1);
+
+ case ERROR: /* ERROR error_info => BOTTOM */
+ upleft;
+ if(errtrap)fprintf(stderr,"\n(repeated error)\n");
+ else { errtrap=1;
+ fprintf(stderr,"\nprogram error: ");
+ s_out=stderr;
+ print(lastarg); /* ### */
+ putc('\n',stderr); }
+ outstats();
+ exit(1);
+
+ case WAIT: /* WAIT pid => <exit_status of child process pid> */
+ UPLEFT;
+ { word *w= &waiting; /* list of terminated pid's and their exit statuses */
+ while(*w!=NIL&&hd[*w]!=lastarg)w= &tl[tl[*w]];
+ if(*w!=NIL)hold=hd[tl[*w]],
+ *w=tl[tl[*w]]; /* remove entry */
+ else { word status;
+ while((hold=wait(&status))!=lastarg&&hold!= -1)
+ waiting=cons(hold,cons(WEXITSTATUS(status),waiting));
+ if(hold!= -1)hold=WEXITSTATUS(status); }}
+ simpl(stosmallint(hold));
+ goto DONE;
+
+ L_I:
+/* case MONOP: (all strict monadic operators share this code) */
+ case I: /* we treat I as strict to avoid I-chains (MOD1) */
+ case SEQ:
+ case FORCE:
+ case HD:
+ case TL:
+ case BODY:
+ case LAST:
+ case EXEC:
+ case FILEMODE:
+ case FILESTAT:
+ case GETENV:
+ case INTEGER:
+ case NUMVAL:
+ case TAKE:
+ case STARTREAD:
+ case STARTREADBIN:
+ case NB_STARTREAD:
+ case COND:
+ case APPEND:
+ case AND:
+ case OR:
+ case NOT:
+ case NEG:
+ case CODE:
+ case DECODE:
+ case SHOWNUM:
+ case SHOWHEX:
+ case SHOWOCT:
+ case ARCTAN_FN: /* ...FN are strict functions of one numeric arg */
+ case EXP_FN:
+ case ENTIER_FN:
+ case LOG_FN:
+ case LOG10_FN:
+ case SIN_FN:
+ case COS_FN:
+ case SQRT_FN:
+ downright; /* subtask -- reduce arg */
+ goto NEXTREDEX;
+
+ case TRY: /* TRY f g x => TRY(f x)(g x) */
+ getarg(arg1);
+ getarg(arg2);
+ while(!abnormal(s))
+ { UPLEFT;
+ hd[e]=ap(TRY,arg1=ap(arg1,lastarg));
+ arg2=tl[e]=ap(arg2,lastarg); }
+ DOWNLEFT;
+ /* DOWNLEFT; DOWNRIGHT; equivalent to:*/
+ hold=s,s=e,e=tl[e],tl[s]=hold,mktlptr(s); /* now be strict in arg1 */
+ goto NEXTREDEX;
+
+ case FAIL: /* FAIL x => FAIL */
+ while(!abnormal(s))hold=s,s=hd[s],hd[hold]=FAIL,tl[hold]=0;
+ goto DONE;
+
+/* case DIOP: (all strict diadic operators share this code) */
+ case ZIP:
+ case STEP:
+ case EQ:
+ case NEQ:
+ case PLUS:
+ case MINUS:
+ case TIMES:
+ case INTDIV:
+ case FDIV:
+ case MOD:
+ case GRE:
+ case GR:
+ case POWER:
+ case SHOWSCALED:
+ case SHOWFLOAT:
+ case MERGE:
+ upleft;
+ downright; /* first subtask -- reduce arg2 */
+ goto NEXTREDEX;
+
+ case Ush: /* strict in three args */
+ case STEPUNTIL:
+ upleft;
+ upleft;
+ downright;
+ goto NEXTREDEX; /* first subtask -- reduce arg3 */
+
+ case Ush1: /* non-strict version of Ush */
+ /* Ush1 (k f1...fn) p stuff
+ => "k"++' ':f1 x1 ...++' ':fn xn, p='\0'
+ => "(k"++' ':f1 x1 ...++' ':fn xn++")", p='\1'
+ where xi = LAST(BODY^(n-i) stuff) */
+ getarg(arg1);
+ arg1=reduce(arg1); /* ### */
+ getarg(arg2);
+ arg2=reduce(arg2); /* ### */
+ getarg(arg3);
+ if(tag[arg1]==CONSTRUCTOR) /* don't parenthesise atom */
+ { hd[e]=I;
+ if(suppressed(arg1))
+ e=tl[e]=str_conv("<unprintable>");
+ else e=tl[e]=str_conv(constr_name(arg1));
+ goto DONE; }
+ hold=arg2?cons(')',NIL):NIL;
+ while(tag[arg1]!=CONSTRUCTOR)
+ hold=cons(' ',ap2(APPEND,ap(tl[arg1],ap(LAST,arg3)),hold)),
+ arg1=hd[arg1],arg3=ap(BODY,arg3);
+ if(suppressed(arg1))
+ { hd[e]=I; e=tl[e]=str_conv("<unprintable>"); goto DONE; }
+ hold=ap2(APPEND,str_conv(constr_name(arg1)),hold);
+ if(arg2)
+ { setcell(CONS,'(',hold); goto DONE; }
+ else { hd[e]=I; e=tl[e]=hold; goto NEXTREDEX; }
+
+ case MKSTRICT: /* MKSTRICT k f x1 ... xk => f x1 ... xk, xk~=BOT */
+ GETARG(arg1);
+ getarg(arg2);
+ { word i=arg1;
+ while(i--) { upleft; } }
+ lastarg=reduce(lastarg); /* ### */
+ while(--arg1) /* go back towards head, copying spine */
+ { hd[e]=ap(hd[hd[e]],tl[hd[e]]);
+ DOWNLEFT;}
+ hd[e]=arg2; /* overwrite (MKSTRICT k f) with f */
+ goto NEXTREDEX;
+
+ case G_ERROR: /* G_ERROR f g toks = (g residue):[], fails(f toks)
+ = f toks, otherwise */
+ GETARG(arg1);
+ GETARG(arg2);
+ upleft;
+ hold=ap(arg1,lastarg);
+ hold=reduce(hold); /* ### */
+ if(!fails(hold))
+ { hd[e]=I; e=tl[e]=hold; goto DONE; }
+ hold=g_residue(lastarg);
+ setcell(CONS,ap(arg2,hold),NIL);
+ goto DONE;
+
+ case G_ALT: /* G_ALT f g toks = f toks, !fails(f toks)
+ = g toks, otherwise */
+ GETARG(arg1);
+ GETARG(arg2);
+ upleft;
+ hold=ap(arg1,lastarg);
+ hold=reduce(hold); /* ### */
+ if(!fails(hold))
+ { hd[e]=I; e=tl[e]=hold; goto DONE; }
+ hd[e]=arg2;
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ case G_OPT: /* G_OPT f toks = []:toks, fails(f toks)
+ = [a]:toks', otherwise
+ where
+ a:toks' = f toks */
+ GETARG(arg1);
+ upleft;
+ hold=ap(arg1,lastarg);
+ hold=reduce(hold); /* ### */
+ if(fails(hold))
+ setcell(CONS,NIL,lastarg);
+ else setcell(CONS,cons(hd[hold],NIL),tl[hold]);
+ goto DONE;
+
+ case G_STAR: /* G_STAR f toks => []:toks, fails(f toks)
+ => ((a:FST z):SND z)
+ where
+ a:toks' = f toks
+ z = G_STAR f toks'
+ */
+ GETARG(arg1);
+ upleft;
+ hold=ap(arg1,lastarg);
+ hold=reduce(hold); /* ### */
+ if(fails(hold))
+ { setcell(CONS,NIL,lastarg); goto DONE; }
+ arg2=ap(hd[e],tl[hold]); /* called z in above rules */
+ tag[e]=CONS;hd[e]=cons(hd[hold],ap(FST,arg2));tl[e]=ap(SND,arg2);
+ goto DONE;
+
+ /* G_RULE has same action as P */
+
+ case G_FBSTAR: /* G_FBSTAR f toks
+ = I:toks, if fails(f toks)
+ = G_SEQ (G_FBSTAR f) (G_RULE (CB a)) toks', otherwise
+ where a:toks' = f toks
+ */
+ GETARG(arg1);
+ upleft;
+ hold=ap(arg1,lastarg);
+ hold=reduce(hold); /* ### */
+ if(fails(hold))
+ { setcell(CONS,I,lastarg); goto DONE; }
+ hd[e]=ap2(G_SEQ,hd[e],ap(G_RULE,ap(CB,hd[hold]))); tl[e]=tl[hold];
+ goto NEXTREDEX;
+
+ case G_SYMB: /* G_SYMB t ((t,s):toks) = t:toks
+ G_SYMB t toks = FAILURE */
+ GETARG(arg1); /* will be in NF */
+ upleft;
+ lastarg=reduce(lastarg); /* ### */
+ if(lastarg==NIL)
+ { hd[e]=I,e=tl[e]=NIL; goto DONE; }
+ hd[lastarg]=reduce(hd[lastarg]); /* ### */
+ hold=ap(FST,hd[lastarg]);
+ if(compare(arg1,reduce(hold))) /* ### */
+ hd[e]=I,e=tl[e]=FAILURE;
+ else setcell(CONS,arg1,tl[lastarg]);
+ goto DONE;
+
+ case G_ANY: /* G_ANY ((t,s):toks) = t:toks
+ G_ANY [] = FAILURE */
+ upleft;
+ lastarg=reduce(lastarg); /* ### */
+ if(lastarg==NIL)
+ hd[e]=I,e=tl[e]=FAILURE;
+ else setcell(CONS,ap(FST,hd[lastarg]),tl[lastarg]);
+ goto DONE;
+
+ case G_SUCHTHAT: /* G_SUCHTHAT f ((t,s):toks) = t:toks, f t
+ G_SUCHTHAT f toks = FAILURE */
+ GETARG(arg1);
+ upleft;
+ lastarg=reduce(lastarg); /* ### */
+ if(lastarg==NIL)
+ { hd[e]=I,e=tl[e]=FAILURE; goto DONE; }
+ hold=ap(FST,hd[lastarg]);
+ hold=reduce(hold); /* ### */
+ if(reduce(ap(arg1,hold))==True) /* ### */
+ setcell(CONS,hold,tl[lastarg]);
+ else hd[e]=I,e=tl[e]=FAILURE;
+ goto DONE;
+
+
+ case G_END: /* G_END [] = []:[]
+ G_END other = FAILURE */
+ upleft;
+ lastarg=reduce(lastarg);
+ if(lastarg==NIL)
+ setcell(CONS,NIL,NIL);
+ else hd[e]=I,e=tl[e]=FAILURE;
+ goto DONE;
+
+ case G_STATE: /* G_STATE ((t,s):toks) = s:((t,s):toks)
+ G_STATE [] = FAILURE */
+ upleft;
+ lastarg=reduce(lastarg); /* ### */
+ if(lastarg==NIL)
+ hd[e]=I,e=tl[e]=FAILURE;
+ else setcell(CONS,ap(SND,hd[lastarg]),lastarg);
+ goto DONE;
+
+ case G_SEQ: /* G_SEQ f g toks = FAILURE, fails(f toks)
+ = FAILURE, fails(g toks')
+ = b a:toks'', otherwise
+ where
+ a:toks' = f toks
+ b:toks'' = g toks' */
+ GETARG(arg1);
+ GETARG(arg2);
+ upleft;
+ hold=ap(arg1,lastarg);
+ hold=reduce(hold); /* ### */
+ if(fails(hold))
+ { hd[e]=I,e=tl[e]=FAILURE; goto DONE; }
+ arg3=ap(arg2,tl[hold]);
+ arg3=reduce(arg3); /* ### */
+ if(fails(arg3))
+ { hd[e]=I,e=tl[e]=FAILURE; goto DONE; }
+ setcell(CONS,ap(hd[arg3],hd[hold]),tl[arg3]);
+ goto DONE;
+
+ case G_UNIT: /* G_UNIT toks => I:toks */
+ upleft;
+ tag[e]=CONS,hd[e]=I;
+ goto DONE;
+ /* G_UNIT is right multiplicative identity, equivalent (G_RULE I) */
+
+ case G_ZERO: /* G_ZERO toks => FAILURE */
+ upleft;
+ simpl(FAILURE);
+ goto DONE;
+ /* G_ZERO is left additive identity */
+
+ case G_CLOSE: /* G_CLOSE s f toks = <error s>, fails(f toks')
+ = <error s>, toks'' ~= NIL
+ = a, otherwise
+ where
+ toks' = G_COUNT toks
+ a:toks'' = f toks' */
+ GETARG(arg1);
+ GETARG(arg2);
+ upleft;
+ arg3=ap(G_COUNT,lastarg);
+ hold=ap(arg2,arg3);
+ hold=reduce(hold); /* ### */
+ if(fails(hold) /* ||(tl[hold]=reduce(tl[hold]))!=NIL /* ### */
+ ) /* suppress to make parsers lazy by default 13/12/90 */
+ { fprintf(stderr,"\nPARSE OF %sFAILS WITH UNEXPECTED ",
+ getstring(arg1,0));
+ arg3=reduce(tl[g_residue(arg3)]);
+ if(arg3==NIL)
+ fprintf(stderr,"END OF INPUT\n"),
+ outstats(),
+ exit(1);
+ hold=ap(FST,hd[arg3]);
+ hold=reduce(hold);
+ fprintf(stderr,"TOKEN \"");
+ if(hold==OFFSIDE)fprintf(stderr,"offside"); /* not now possible */
+ { char *p=getstring(hold,0);
+ while(*p)fprintf(stderr,"%s",charname(*p++)); }
+ fprintf(stderr,"\"\n");
+ outstats();
+ exit(1); }
+ hd[e]=I,e=tl[e]=hd[hold];
+ goto NEXTREDEX;
+/* NOTE the atom OFFSIDE differs from every string and is used as a
+ pseudotoken when implementing the offside rule - see `indent' in prelude */
+
+ case G_COUNT: /* G_COUNT NIL => NIL
+ G_COUNT (t:toks) => t:G_COUNT toks */
+ /* G_COUNT is an identity operation on lists - its purpose is to mark
+ last token examined, for syntax error location purposes */
+ upleft;
+ if((lastarg=reduce(lastarg))==NIL) /* ### */
+ { hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ setcell(CONS,hd[lastarg],ap(G_COUNT,tl[lastarg]));
+ goto DONE;
+
+/* Explanation of %lex combinators. A lex analyser is of type
+
+ lexer == [char] -> [alpha]
+
+ At top level these are of the form (LEX_RPT f) where f is of type
+
+ lexer1 == startcond -> [char] -> (alpha,startcond,[char])
+
+ A lexer1 is guaranteed to return a triple (if it returns at all...)
+ and is built using LEX_TRY.
+
+ LEX_TRY [(scstuff,(matcher [],rule))*] :: lexer1
+ rule :: [char] -> alpha
+ matcher :: partial_match -> input -> {(alpha,input') | []}
+
+ partial_match and input are both [char] and [] represents failure.
+ The other lex combinators - LEX_SEQ, LEX_OR, LEX_CLASS etc., all
+ create and combine objects of type matcher.
+
+ LEX_RPT1 is a deviant version that labels the input characters
+ with their lexical state (row,col) using LEX_COUNT - goes with
+ LEX_TRY1 which feeds the leading state of input to each rule.
+
+*/
+
+ case LEX_RPT1: /* LEX_RPT1 f s x => LEX_RPT f s (LEX_COUNT0 x)
+ i.e. LEX_RPT1 f s => B (LEX_RPT f s) LEX_COUNT0
+ */
+ GETARG(arg1);
+ UPLEFT;
+ hd[e]=ap(B,ap2(LEX_RPT,arg1,lastarg)); tl[e]=LEX_COUNT0;
+ DOWNLEFT;
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ case LEX_RPT: /* LEX_RPT f s [] => []
+ LEX_RPT f s x => a : LEX_RPT f s' y
+ where
+ (a,s',y) = f s x
+ note that if f returns a result it is
+ guaranteed to be a triple
+ */
+ GETARG(arg1);
+ GETARG(arg2);
+ upleft;
+ if((lastarg=reduce(lastarg))==NIL) /* ### */
+ { hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ hold=ap2(arg1,arg2,lastarg);
+ arg1=hd[hd[e]];
+ hold=reduce(hold);
+ setcell(CONS,hd[hold],ap2(arg1,hd[tl[hold]],tl[tl[hold]]));
+ goto DONE;
+
+ case LEX_TRY:
+ upleft;
+ tl[e]=reduce(tl[e]); /* ### */
+ force(tl[e]);
+ hd[e]=LEX_TRY_;
+ DOWNLEFT;
+ /* falls thru to next case */
+
+ case LEX_TRY_:
+ /* LEX_TRY ((scstuff,(f,rule)):alt) s x => LEX_TRY alt s x, if f x = []
+ => (rule (rev a),s,y), otherwise
+ where
+ (a,y) = f x
+ LEX_TRY [] s x => BOTTOM
+ */
+ GETARG(arg1);
+ GETARG(arg2);
+ upleft;
+L2: if(arg1==NIL)lexfail(lastarg);
+ if(hd[hd[hd[arg1]]]&&!member(hd[hd[hd[arg1]]],arg2))
+ { arg1=tl[arg1]; goto L2; } /* hd[scstuff] is 0 or list of startconds */
+ hold=ap(hd[tl[hd[arg1]]],lastarg);
+ if((hold=reduce(hold))==NIL) /* ### */
+ { arg1=tl[arg1]; goto L2; }
+ setcell(CONS,ap(tl[tl[hd[arg1]]],ap(DESTREV,hd[hold])),
+ cons(tl[hd[hd[arg1]]]?tl[hd[hd[arg1]]]-1:arg2,tl[hold]));
+ /* tl[scstuff] is 1 + next start condition (0 = no change) */
+ goto DONE;
+
+ case LEX_TRY1:
+ upleft;
+ tl[e]=reduce(tl[e]); /* ### */
+ force(tl[e]);
+ hd[e]=LEX_TRY1_;
+ DOWNLEFT;
+ /* falls thru to next case */
+
+ case LEX_TRY1_:
+ /* LEX_TRY1 ((scstuff,(f,rule)):alt) s x => LEX_TRY1 alt s x, if f x = []
+ => (rule n (rev a),s,y), otherwise
+ where
+ (a,y) = f x
+ n = lexstate(x)
+ ||same as LEX_TRY but feeds lexstate to rule
+ */
+ GETARG(arg1);
+ GETARG(arg2);
+ upleft;
+L3: if(arg1==NIL)lexfail(lastarg);
+ if(hd[hd[hd[arg1]]]&&!member(hd[hd[hd[arg1]]],arg2))
+ { arg1=tl[arg1]; goto L3; } /* hd[scstuff] is 0 or list of startconds */
+ hold=ap(hd[tl[hd[arg1]]],lastarg);
+ if((hold=reduce(hold))==NIL) /* ### */
+ { arg1=tl[arg1]; goto L3; }
+ setcell(CONS,ap2(tl[tl[hd[arg1]]],lexstate(lastarg),ap(DESTREV,hd[hold])),
+ cons(tl[hd[hd[arg1]]]?tl[hd[hd[arg1]]]-1:arg2,tl[hold]));
+ /* tl[scstuff] is 1 + next start condition (0 = no change) */
+ goto DONE;
+
+ case DESTREV: /* destructive reverse - used only by LEX_TRY */
+ GETARG(arg1); /* known to be an explicit list */
+ arg2=NIL; /* to hold reversed list */
+ while(arg1!=NIL)
+ { if(tag[hd[arg1]]==STRCONS) /* strip off lex state if present */
+ hd[arg1]=tl[hd[arg1]];
+ hold=tl[arg1],tl[arg1]=arg2,arg2=arg1,arg1=hold; }
+ hd[e]=I; e=tl[e]=arg2;
+ goto DONE;
+
+ case LEX_COUNT0: /* LEX_COUNT0 x => LEX_COUNT (state0,x) */
+ upleft;
+ hd[e]=LEX_COUNT; tl[e]=strcons(0,tl[e]);
+ DOWNLEFT;
+ /* falls thru to next case */
+
+ case LEX_COUNT: /* LEX_COUNT (state,[]) => []
+ LEX_COUNT (state,(a:x)) => (state,a):LEX_COUNT(state',a)
+ where
+ state == (line_no*256+col_no)
+ */
+ GETARG(arg1);
+ if((tl[arg1]=reduce(tl[arg1]))==NIL) /* ### */
+ { hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ hold=hd[tl[arg1]]; /* the char */
+ setcell(CONS,strcons(hd[arg1],hold),ap(LEX_COUNT,arg1));
+ if(hold=='\n')hd[arg1]=(hd[arg1]>>8)+1<<8;
+ else { word col = hd[arg1]&255;
+ col = hold=='\t'?(col/8+1)*8:col+1;
+ hd[arg1] = hd[arg1]&(~255)|col; }
+ tl[arg1]=tl[tl[arg1]];
+ goto DONE;
+
+#define lh(x) (tag[hd[x]]==STRCONS?tl[hd[x]]:hd[x])
+ /* hd char of possibly lex-state-labelled string */
+
+ case LEX_STRING: /* LEX_STRING [] p x => p : x
+ LEX_STRING (c:s) p (c:x) => LEX_STRING s (c:p) x
+ LEX_STRING (c:s) p other => []
+ */
+ GETARG(arg1);
+ GETARG(arg2);
+ upleft;
+ while(arg1!=NIL)
+ { if((lastarg=reduce(lastarg))==NIL||lh(lastarg)!=hd[arg1]) /* ### */
+ { hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ arg1=tl[arg1]; arg2=cons(hd[lastarg],arg2); lastarg=tl[lastarg]; }
+ tag[e]=CONS; hd[e]=arg2;
+ goto DONE;
+
+ case LEX_CLASS: /* LEX_CLASS set p (c:x) => (c:p) : x, if c in set
+ LEX_CLASS set p x => [], otherwise
+ */
+ GETARG(arg1);
+ GETARG(arg2);
+ upleft;
+ if((lastarg=reduce(lastarg))==NIL|| /* ### */
+ (hd[arg1]==ANTICHARCLASS?memclass(lh(lastarg),tl[arg1])
+ :!memclass(lh(lastarg),arg1))
+ )
+ { hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ setcell(CONS,cons(hd[lastarg],arg2),tl[lastarg]);
+ goto DONE;
+
+ case LEX_DOT: /* LEX_DOT p (c:x) => (c:p) : x
+ LEX_DOT p [] => []
+ */
+ GETARG(arg1);
+ upleft;
+ if((lastarg=reduce(lastarg))==NIL) /* ### */
+ { hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ setcell(CONS,cons(hd[lastarg],arg1),tl[lastarg]);
+ goto DONE;
+
+ case LEX_CHAR: /* LEX_CHAR c p (c:x) => (c:p) : x
+ LEX_CHAR c p x => []
+ */
+ GETARG(arg1);
+ GETARG(arg2);
+ upleft;
+ if((lastarg=reduce(lastarg))==NIL||lh(lastarg)!=arg1) /* ### */
+ { hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ setcell(CONS,cons(arg1,arg2),tl[lastarg]);
+ goto DONE;
+
+ case LEX_SEQ: /* LEX_SEQ f g p x => [], if f p x = []
+ => g q y, otherwise
+ where
+ (q,y) = f p x
+ */
+ GETARG(arg1);
+ GETARG(arg2);
+ GETARG(arg3);
+ upleft;
+ hold=ap2(arg1,arg3,lastarg);
+ lastarg=NIL; /* anti-dragging measure */
+ if((hold=reduce(hold))==NIL) /* ### */
+ { hd[e]=I; e=tl[e]; goto DONE; }
+ hd[e]=ap(arg2,hd[hold]); tl[e]=tl[hold];
+ DOWNLEFT;
+ DOWNLEFT;
+ goto NEXTREDEX;
+
+ case LEX_OR: /* LEX_OR f g p x => g p x, if f p x = []
+ => f p x, otherwise
+ */
+ GETARG(arg1);
+ GETARG(arg2);
+ GETARG(arg3);
+ upleft;
+ hold=ap2(arg1,arg3,lastarg);
+ if((hold=reduce(hold))==NIL) /* ### */
+ { hd[e]=ap(arg2,arg3); DOWNLEFT; DOWNLEFT; goto NEXTREDEX; }
+ hd[e]=I; e=tl[e]=hold;
+ goto DONE;
+
+ case LEX_RCONTEXT: /* LEX_RC f g p x => [], if f p x = []
+ => [], if g q y = []
+ => f p x, otherwise <-*
+ where
+ (q,y) = f p x
+
+ (*) special case g=0 means test for y=[]
+ */
+ GETARG(arg1);
+ GETARG(arg2);
+ GETARG(arg3);
+ upleft;
+ hold=ap2(arg1,arg3,lastarg);
+ lastarg=NIL; /* anti-dragging measure */
+ if((hold=reduce(hold))==NIL /* ### */
+ || (arg2?(reduce(ap2(arg2,hd[hold],tl[hold]))==NIL) /* ### */
+ :(tl[hold]=reduce(tl[hold]))!=NIL ))
+ { hd[e]=I; e=tl[e]; goto DONE; }
+ hd[e]=I; e=tl[e]=hold;
+ goto DONE;
+
+ case LEX_STAR: /* LEX_STAR f p x => p : x, if f p x = []
+ => LEX_STAR f q y, otherwise
+ where
+ (q,y) = f p x
+ */
+ GETARG(arg1);
+ GETARG(arg2);
+ upleft;
+ hold=ap2(arg1,arg2,lastarg);
+ while((hold=reduce(hold))!=NIL) /* ### */
+ arg2=hd[hold],lastarg=tl[hold],hold=ap2(arg1,arg2,lastarg);
+ tag[e]=CONS; hd[e]=arg2;
+ goto DONE;
+
+ case LEX_OPT: /* LEX_OPT f p x => p : x, if f p x = []
+ => f p x, otherwise
+ */
+ GETARG(arg1);
+ GETARG(arg2);
+ upleft;
+ hold=ap2(arg1,arg2,lastarg);
+ if((hold=reduce(hold))==NIL) /* ### */
+ { tag[e]=CONS; hd[e]=arg2; goto DONE; }
+ hd[e]=I; e=tl[e]=hold;
+ goto DONE;
+
+/* case NUMBER: /* constructor of arity 1
+ UPLEFT; /* cannot occur free
+ goto DONE; */ /* UNUSED*/
+
+/* case CONSTRUCTOR:
+ for(;;){upleft; } /* reapply to args until DONE */
+
+ default: /* non combinator */
+ cycles--; /* oops! */
+ if(abnormal(e)) /* silly recursion */
+ { fprintf(stderr,"\nBLACK HOLE\n");
+ outstats();
+ exit(1); }
+
+ switch(tag[e])
+ { case STRCONS: e=pn_val(e); /* private name */
+ /*if(e==UNDEF||e==FREE)
+ fprintf(stderr,
+ "\nimpossible event in reduce - undefined pname\n"),
+ exit(1);
+ /* redundant test - remove when sure */
+ goto NEXTREDEX;
+ case DATAPAIR: /* datapair(oldn,0)(fileinfo(filename,0))=>BOTTOM */
+ /* kludge for trapping inherited undefined name without
+ current alias - see code in load_defs */
+ upleft;
+ fprintf(stderr,
+ "\nUNDEFINED NAME (specified as \"%s\" in %s)\n",
+ (char *)hd[hd[e]],(char *)hd[lastarg]);
+ outstats();
+ exit(1);
+ case ID: if(id_val(e)==UNDEF||id_val(e)==FREE)
+ { fprintf(stderr,"\nUNDEFINED NAME - %s\n",get_id(e));
+ outstats();
+ exit(1); }
+ /* setcell(AP,I,id_val(e)); /* overwrites error-info */
+ e=id_val(e); /* could be eager in value */
+ goto NEXTREDEX;
+ default: fprintf(stderr,"\nimpossible tag (%d) in reduce\n",tag[e]);
+ exit(1);
+ case CONSTRUCTOR: for(;;){upleft; } /* reapply to args until DONE */
+ case STARTREADVALS:
+ /* readvals(0,t) file => READVALS (t:file) streamptr */
+ { char *fil;
+ upleft;
+ lastarg=reduce(lastarg); /* ### */
+ if(lastarg==OFFSIDE) /* special case, represents stdin */
+ { if(stdinuse&&stdinuse!='+')
+ { tag[e]=AP; hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ stdinuse='+';
+ hold=cons(tl[hd[e]],0),lastarg=(word)stdin; }
+ else
+ hold=cons(tl[hd[e]],lastarg),
+ lastarg=(word)fopen(fil=getstring(lastarg,"readvals"),"r");
+ if((FILE *)lastarg==NULL) /* cannot open file for reading */
+ /* { hd[e]=I; e=tl[e]=NIL; goto DONE; } */
+ { fprintf(stderr,"\nreadvals, cannot open: \"%s\"\n",fil);
+ outstats(); exit(1); }
+ hd[e]=ap(READVALS,hold); }
+ DOWNLEFT;
+ DOWNLEFT;
+ goto L_READVALS;
+ case ATOM: /* for(;;){upleft; } */
+ /* as above if there are constructors with tag ATOM
+ and +ve arity. Since there are none we could test
+ for missing combinators at this point. Thus
+ /*if(!abnormal(s))
+ fprintf(stderr,"\nreduce: unknown combinator "),
+ out(stderr,e), putc('\n',stderr),exit(1); */
+ case INT:
+ case UNICODE:
+ case DOUBLE:
+ case CONS:; /* all fall thru to DONE */
+ }
+
+ } /* end of decode switch */
+
+ DONE: /* sub task completed -- s is either BACKSTOP or a tailpointer */
+
+ if(s==BACKSTOP)
+ { /* whole expression now in hnf */
+#ifdef DEBUG
+ if(debug&02)printf("result= "),out(stdout,e),putchar('\n');
+ rdepth--;
+#endif
+ return(e); /* end of reduction */
+ /* outchar(hd[e]);
+ e=tl[e];
+ goto NEXTREDEX;
+ /* above shows how to incorporate printing into m/c */
+ }
+
+ /* otherwise deal with return from subtask */
+ UPRIGHT;
+ if(tag[e]==AP)
+ { /* we have just reduced argn of strict operator -- so now
+ we must reduce arg(n-1) */
+ DOWNLEFT;
+ DOWNRIGHT; /* there is a faster way to do this - see TRY */
+ goto NEXTREDEX;
+ }
+
+ /* only possible if mktlptr marks the cell rather than the field */
+/* if(e==BACKSTOP)
+ fprintf(stderr,"\nprogram error: BLACK HOLE2\n"),
+ outstats(),
+ exit(1); */
+
+ /* we are through reducing args of strict operator */
+ /* we can merge the following switch with the main one, if desired,
+ - in this case use the alternate definitions of READY and RESTORE
+ and replace the following switch by
+ /* e=READY(e); goto OPDECODE; */
+
+#ifdef DEBUG
+ if(debug&02){ printf("ready("); out(stdout,e); printf(")\n"); }
+#endif
+ switch(e) /* "ready" switch */
+ {
+/* case READY(MONOP):/* paradigm for execution of strict monadic operator
+ GETARG(arg1);
+ hd[e]=I; e=tl[e]=do_monop(arg1);
+ goto NEXTREDEX; */
+
+ case READY(I): /* I x => x */
+ UPLEFT;
+ e=lastarg;
+ goto NEXTREDEX;
+
+ case READY(SEQ): /* SEQ a b => b, a~=BOTTOM */
+ UPLEFT;
+ upleft;
+ hd[e]=I;e=lastarg;
+ goto NEXTREDEX;
+
+ case READY(FORCE): /* FORCE x => x, total x */
+ UPLEFT;
+ force(lastarg);
+ hd[e]=I;e=lastarg;
+ goto NEXTREDEX;
+
+ case READY(HD):
+ UPLEFT;
+ if(lastarg==NIL)
+ { fprintf(stderr,"\nATTEMPT TO TAKE hd OF []\n");
+ outstats(); exit(1); }
+ hd[e]=I; e=tl[e]=hd[lastarg];
+ goto NEXTREDEX;
+
+ case READY(TL):
+ UPLEFT;
+ if(lastarg==NIL)
+ { fprintf(stderr,"\nATTEMPT TO TAKE tl OF []\n");
+ outstats(); exit(1); }
+ hd[e]=I; e=tl[e]=tl[lastarg];
+ goto NEXTREDEX;
+
+ case READY(BODY):
+ /* BODY(k x1 .. xn) => k x1 ... x(n-1)
+ for arbitrary constructor k */
+ UPLEFT;
+ hd[e]=I; e=tl[e]=hd[lastarg];
+ goto NEXTREDEX;
+
+ case READY(LAST): /* LAST(k x1 .. xn) => xn
+ for arbitrary constructor k */
+ UPLEFT;
+ hd[e]=I; e=tl[e]=tl[lastarg];
+ goto NEXTREDEX;
+
+ case READY(TAKE):
+ GETARG(arg1);
+ upleft;
+ if(tag[arg1]!=INT)word_error("take");
+ { long long n=get_word(arg1);
+ if(n<=0||(lastarg=reduce(lastarg))==NIL) /* ### */
+ { simpl(NIL); goto DONE; }
+ setcell(CONS,hd[lastarg],ap2(TAKE,sto_word(n-1),tl[lastarg])); }
+ goto DONE;
+
+ case READY(FILEMODE): /* FILEMODE string => string'
+ (see filemode in manual) */
+ UPLEFT;
+ if(!stat(getstring(lastarg,"filemode"),&buf))
+ { mode_t mode=buf.st_mode;
+ word d=S_ISDIR(mode)?'d':'-';
+ word perm= buf.st_uid==geteuid()?(mode&0700)>>6:
+ buf.st_gid==getegid()?(mode&070)>>3:
+ mode&07;
+ word r=perm&04?'r':'-',w=perm&02?'w':'-',x=perm&01?'x':'-';
+ setcell(CONS,d,cons(r,cons(w,cons(x,NIL))));
+ }
+ else hd[e]=I,e=tl[e]=NIL;
+ goto DONE;
+
+ case READY(FILESTAT): /* FILESTAT string => ((inode,dev),mtime) */
+ UPLEFT;
+ /* Notes:
+ Non-existent file has conventional ((inode,dev),mtime) of ((0,-1),0)
+ We assume time_t can be stored in int field, this may not port */
+ if(!stat(getstring(lastarg,"filestat"),&buf))
+ setcell(CONS,cons(sto_word(buf.st_ino),
+ sto_word(buf.st_dev) ),
+ sto_word(buf.st_mtime) );
+ else setcell(CONS,cons(stosmallint(0),
+ stosmallint(-1) ),
+ stosmallint(0) );
+ goto DONE;
+
+ case READY(GETENV): /* GETENV string => string'
+ (see man (2) getenv) */
+ UPLEFT;
+ { char *a = getstring(lastarg,"getenv");
+ unsigned char *p = getenv(a);
+ hold = NIL;
+ if(p){ word i;
+ unsigned char *q=p, *r=p;
+ if(UTF8)
+ { while(*r) /* compress to Latin-1 in situ */
+ if(*r>127) /* start of multibyte */
+ if((*r==194||*r==195)&&r[1]>=128&&r[1]<=191) /* Latin-1 */
+ *q= *r==194?r[1]:r[1]+64, q++, r+=2;
+ else getenv_error(a),
+ /* or silently accept errors here? */
+ *q++=*r++;
+ else *q++=*r++;
+ *q='\0';
+ }
+ /* convert p to list */
+ i = strlen(p);
+ while(i--)hold=cons(p[i],hold);
+ }
+ }
+ hd[e]=I; e=tl[e]=hold;
+ goto DONE;
+
+ case READY(EXEC): /* EXEC string
+ fork off a process to execute string as a
+ shell command, returning (via pipes) the
+ triple (stdout,stderr,exit_status)
+ convention: if fork fails, exit status is -1 */
+ UPLEFT;
+ { word pid=(-1),fd[2],fd_a[2];
+ char *cp=getstring(lastarg,"system");
+ /* pipe(fd) should return 0, -1 means fail */
+ /* fd_a is 2nd pipe, for error messages */
+ if(pipe(fd)==(-1)||pipe(fd_a)==(-1)||(pid=fork()))
+ { /* parent (reader) */
+ FILE *fp,*fp_a;
+ if(pid!= -1)
+ close(fd[1]),
+ close(fd_a[1]),
+ fp=(FILE *)fdopen(fd[0],"r"),
+ fp_a=(FILE *)fdopen(fd_a[0],"r");
+ if(pid== -1||!fp||!fp_a)
+ setcell(CONS,NIL,cons(piperrmess(pid),sto_word(-1))); else
+ setcell(CONS,ap(READ,fp),cons(ap(READ,fp_a),ap(WAIT,pid)));
+ }
+ else { /* child (writer) */
+ word in;
+ static char *shell="/bin/sh";
+ dup2(fd[1],1); /* so pipe replaces stdout */
+ dup2(fd_a[1],2); /* 2nd pipe replaces stderr */
+ close(fd[1]);
+ close(fd[0]);
+ close(fd_a[1]);
+ close(fd_a[0]);
+ fclose(stdin); /* anti side-effect measure */
+ execl(shell,shell,"-c",cp,(char *)0);
+ }
+ }
+ goto DONE;
+
+ case READY(NUMVAL): /* NUMVAL numeral => number */
+ UPLEFT;
+ { word x=lastarg;
+ word base=10;
+ while(x!=NIL)
+ hd[x]=reduce(hd[x]), /* ### */
+ x=tl[x]=reduce(tl[x]); /* ### */
+ while(lastarg!=NIL&&isspace(hd[lastarg]))lastarg=tl[lastarg];
+ x=lastarg;
+ if(x!=NIL&&hd[x]=='-')x=tl[x];
+ if(hd[x]=='0'&&tl[x]!=NIL)
+ switch(tolower(hd[tl[x]]))
+ { case 'o':
+ base=8;
+ x=tl[tl[x]];
+ while(x!=NIL&&isodigit(hd[x]))x=tl[x];
+ break;
+ case 'x':
+ base=16;
+ x=tl[tl[x]];
+ while(x!=NIL&&isxdigit(hd[x]))x=tl[x];
+ break;
+ default: goto L;
+ }
+ else L: while(x!=NIL&&isdigit(hd[x]))x=tl[x];
+ if(x==NIL)
+ hd[e]=I,e=tl[e]=strtobig(lastarg,base);
+ else { char *p=linebuf;
+ double d; char junk=0;
+ x=lastarg;
+ while(x!=NIL&&p-linebuf<BUFSIZE-1) *p++ = hd[x], x=tl[x];
+ *p++ ='\0';
+ if(p-linebuf>60||sscanf(linebuf,"%lf%c",&d,&junk)!=1||junk)
+ { fprintf(stderr,"\nbad arg for numval: \"%s\"\n",linebuf);
+ outstats();
+ exit(1); }
+ else hd[e]=I,e=tl[e]=sto_dbl(d); }
+ goto DONE; }
+
+ case READY(STARTREAD): /* STARTREAD filename => READ streamptr */
+ UPLEFT;
+ { char *fil;
+ lastarg = (word)fopen(fil=getstring(lastarg,"read"),"r");
+ if((FILE *)lastarg==NULL) /* cannot open file for reading */
+ /* { hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ /* could just return empty contents */
+ { fprintf(stderr,"\nread, cannot open: \"%s\"\n",fil);
+ outstats(); exit(1); }
+ hd[e]=READ;
+ DOWNLEFT; }
+ goto L_READ;
+
+ case READY(STARTREADBIN): /* STARTREADBIN filename => READBIN streamptr */
+ UPLEFT;
+ { char *fil;
+ lastarg = (word)fopen(fil=getstring(lastarg,"readb"),"r");
+ if((FILE *)lastarg==NULL) /* cannot open file for reading */
+ /* { hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ /* could just return empty contents */
+ { fprintf(stderr,"\nreadb, cannot open: \"%s\"\n",fil);
+ outstats(); exit(1); }
+ hd[e]=READBIN;
+ DOWNLEFT; }
+ goto L_READBIN;
+
+ case READY(TRY): /* TRY FAIL y => y
+ TRY other y => other */
+ GETARG(arg1);
+ UPLEFT;
+ if(arg1==FAIL)
+ { hd[e]=I; e=lastarg; goto NEXTREDEX; }
+ if(S<=(hold=head(arg1))&&hold<=ERROR)
+ /* function - other than unsaturated constructor */
+ goto DONE;/* nb! else may take premature decision(interacts with MOD1)*/
+ hd[e]=I;
+ e=tl[e]=arg1;
+ goto NEXTREDEX;
+
+ case READY(COND): /* COND True => K
+ COND False => KI */
+ UPLEFT;
+ hd[e]=I;
+ if(lastarg==True)
+ { e=tl[e]=K; goto L_K; }
+ else { e=tl[e]=KI; goto L_KI; }
+ /* goto OPDECODE; /* to speed up we have set extra labels */
+
+ /* alternative rules /* COND True x => K x
+ COND False x => I */
+
+ case READY(APPEND): /* APPEND NIL y => y
+ APPEND (a:x) y => a:APPEND x y */
+ GETARG(arg1);
+ upleft;
+ if(arg1==NIL)
+ { hd[e]=I,e=lastarg; goto NEXTREDEX; }
+ setcell(CONS,hd[arg1],ap2(APPEND,tl[arg1],lastarg));
+ goto DONE;
+
+ case READY(AND): /* AND True => I
+ AND False => K False */
+ UPLEFT;
+ if(lastarg==True){ e=I; goto L_I; }
+ else { hd[e]=K,DOWNLEFT; goto L_K; }
+
+ case READY(OR): /* OR True => K True
+ OR False => I */
+ UPLEFT;
+ if(lastarg==True){ hd[e]=K; DOWNLEFT; goto L_K; }
+ else { e=I; goto L_I; }
+
+ /* alternative rules ?? /* AND True y => y
+ AND False y => False
+ OR True y => True
+ OR False y => y */
+
+ case READY(NOT): /* NOT True => False
+ NOT False => True */
+ UPLEFT;
+ hd[e]=I; e=tl[e]=lastarg==True?False:True;
+ goto DONE;
+
+ case READY(NEG): /* NEG x => -x, if x is a number */
+ UPLEFT;
+ if(tag[lastarg]==INT)simpl(bignegate(lastarg));
+ else setdbl(e,-get_dbl(lastarg));
+ goto DONE;
+
+ case READY(CODE): /* miranda char to int type-conversion */
+ UPLEFT;
+ simpl(make(INT,get_char(lastarg),0));
+ goto DONE;
+
+ case READY(DECODE): /* int to char type conversion */
+ UPLEFT;
+ if(tag[lastarg]==DOUBLE)word_error("decode");
+ long long val=get_word(lastarg);
+ if(val<0||val>UMAX)
+ { fprintf(stderr,"\nCHARACTER OUT-OF-RANGE decode(%d)\n",val);
+ outstats();
+ exit(1); }
+ hd[e]=I; e=tl[e]=sto_char(val);
+ goto DONE;
+
+ case READY(INTEGER): /* predicate on numbers */
+ UPLEFT;
+ hd[e]=I; e=tl[e]=tag[lastarg]==INT?True:False;
+ goto NEXTREDEX;
+
+ case READY(SHOWNUM): /* SHOWNUM number => numeral */
+ UPLEFT;
+ if(tag[lastarg]==DOUBLE)
+ { double x=get_dbl(lastarg);
+#ifndef RYU
+ sprintf(linebuf,"%.16g",x);
+ char *p=linebuf;
+ while isdigit(*p)p++; /* add .0 to false integer */
+ if(!*p)*p++='.',*p++='0',*p='\0';
+ hd[e]=I; e=tl[e]=str_conv(linebuf); }
+#else
+ d2s_buffered(x,linebuf);
+ arg1=str_conv(linebuf);
+ if(*linebuf=='.')arg1=cons('0',arg1);
+ if(*linebuf=='-'&&linebuf[1]=='.')arg1=cons('-',cons('0',tl[arg1]));
+ hd[e]=I; e=tl[e]=arg1; }
+#endif
+ else simpl(bigtostr(lastarg));
+ goto DONE;
+
+ case READY(SHOWHEX):
+ UPLEFT;
+ if(tag[lastarg]==DOUBLE)
+ { sprintf(linebuf,"%a",get_dbl(lastarg));
+ hd[e]=I; e=tl[e]=str_conv(linebuf); }
+ else simpl(bigtostrx(lastarg));
+ goto DONE;
+
+ case READY(SHOWOCT):
+ UPLEFT;
+ if(tag[lastarg]==DOUBLE)word_error("showoct");
+ else simpl(bigtostr8(lastarg));
+ goto DONE;
+
+ /* paradigm for strict monadic arithmetic fns */
+ case READY(ARCTAN_FN): /* atan */
+ UPLEFT;
+ errno=0; /* to clear */
+ setdbl(e,atan(force_dbl(lastarg)));
+ if(errno)math_error("atan");
+ goto DONE;
+
+ case READY(EXP_FN): /* exp */
+ UPLEFT;
+ errno=0; /* to clear */
+ setdbl(e,exp(force_dbl(lastarg)));
+ if(errno)math_error("exp");
+ goto DONE;
+
+ case READY(ENTIER_FN): /* floor */
+ UPLEFT;
+ if(tag[lastarg]==INT)simpl(lastarg);
+ else simpl(dbltobig(get_dbl(lastarg)));
+ goto DONE;
+
+ case READY(LOG_FN): /* log */
+ UPLEFT;
+ if(tag[lastarg]==INT)setdbl(e,biglog(lastarg));
+ else { errno=0; /* to clear */
+ fa=force_dbl(lastarg);
+ setdbl(e,log(fa));
+ if(errno)math_error("log"); }
+ goto DONE;
+
+ case READY(LOG10_FN): /* log10 */
+ UPLEFT;
+ if(tag[lastarg]==INT)setdbl(e,biglog10(lastarg));
+ else { errno=0; /* to clear */
+ fa=force_dbl(lastarg);
+ setdbl(e,log10(fa));
+ if(errno)math_error("log10"); }
+ goto DONE;
+
+ case READY(SIN_FN): /* sin */
+ UPLEFT;
+ errno=0; /* to clear */
+ setdbl(e,sin(force_dbl(lastarg)));
+ if(errno)math_error("sin");
+ goto DONE;
+
+ case READY(COS_FN): /* cos */
+ UPLEFT;
+ errno=0; /* to clear */
+ setdbl(e,cos(force_dbl(lastarg)));
+ if(errno)math_error("cos");
+ goto DONE;
+
+ case READY(SQRT_FN): /* sqrt */
+ UPLEFT;
+ fa=force_dbl(lastarg);
+ if(fa<0.0)math_error("sqrt");
+ setdbl(e,sqrt(fa));
+ goto DONE;
+
+/* case READY(DIOP):/* paradigm for execution of strict diadic operator
+ RESTORE(e); /* do not write modified form of operator back into graph
+ GETARG(arg1);
+ GETARG(arg2);
+ hd[e]=I; e=tl[e]=diop(arg1,arg2);
+ goto NEXTREDEX; */
+
+/* case READY(EQUAL): /* UNUSED
+ RESTORE(e);
+ GETARG(arg1);
+ GETARG(arg2);
+ if(isap(arg1)&&hd[arg1]!=NUMBER&&isap(arg2)&&hd[arg2]!=NUMBER)
+ { /* recurse on components
+ hd[e]=ap2(EQUAL,tl[arg1],tl[arg2]);
+ hd[e]=ap3(EQUAL,hd[arg1],hd[arg2],hd[e]);
+ tl[e]=False;
+ }
+ else { hd[e]=I; e=tl[e]= (eqatom(arg1,arg2)?True:False); }
+ goto NEXTREDEX; */
+
+ case READY(ZIP): /* ZIP (a:x) (b:y) => (a,b) : ZIP x y
+ ZIP x y => [] */
+ RESTORE(e);
+ GETARG(arg1);
+ GETARG(arg2);
+ if(arg1==NIL||arg2==NIL)
+ { hd[e]=I; e=tl[e]=NIL; goto DONE; }
+ setcell(CONS,cons(hd[arg1],hd[arg2]),ap2(ZIP,tl[arg1],tl[arg2]));
+ goto DONE;
+
+ case READY(EQ): /* EQ x x => True
+ EQ x y => False
+ see definition of function "compare" above */
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ hd[e]=I; e=tl[e]=compare(arg1,lastarg)?False:True; /* ### */
+ goto DONE;
+
+ case READY(NEQ): /* NEQ x x => False
+ NEQ x y => True
+ see definition of function "compare" above */
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ hd[e]=I; e=tl[e]=compare(arg1,lastarg)?True:False; /* ### */
+ goto DONE;
+
+ case READY(GR):
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ hd[e]=I; e=tl[e]=compare(arg1,lastarg)>0?True:False; /* ### */
+ goto DONE;
+
+ case READY(GRE):
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ hd[e]=I; e=tl[e]=compare(arg1,lastarg)>=0?True:False; /* ### */
+ goto DONE;
+
+ case READY(PLUS):
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ if(tag[arg1]==DOUBLE)
+ setdbl(e,get_dbl(arg1)+force_dbl(lastarg)); else
+ if(tag[lastarg]==DOUBLE)
+ setdbl(e,bigtodbl(arg1)+get_dbl(lastarg));
+ else simpl(bigplus(arg1,lastarg));
+ goto DONE;
+
+ case READY(MINUS):
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ if(tag[arg1]==DOUBLE)
+ setdbl(e,get_dbl(arg1)-force_dbl(lastarg)); else
+ if(tag[lastarg]==DOUBLE)
+ setdbl(e,bigtodbl(arg1)-get_dbl(lastarg));
+ else simpl(bigsub(arg1,lastarg));
+ goto DONE;
+
+ case READY(TIMES):
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ if(tag[arg1]==DOUBLE)
+ setdbl(e,get_dbl(arg1)*force_dbl(lastarg)); else
+ if(tag[lastarg]==DOUBLE)
+ setdbl(e,bigtodbl(arg1)*get_dbl(lastarg));
+ else simpl(bigtimes(arg1,lastarg));
+ goto DONE;
+
+ case READY(INTDIV):
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ if(tag[arg1]==DOUBLE||tag[lastarg]==DOUBLE)word_error("div");
+ if(bigzero(lastarg))div_error(); /* build into bigmod ? */
+ simpl(bigdiv(arg1,lastarg));
+ goto DONE;
+
+ case READY(FDIV):
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ /* experiment, suppressed
+ if(tag[lastarg]==INT&&tag[arg1]==INT&&!bigzero(lastarg))
+ { extern int b_rem;
+ int d = bigdiv(arg1,lastarg);
+ if(bigzero(b_rem)){ simpl(d); goto DONE; }
+ } /* makes a/b integer if a, b integers dividing exactly */
+ fa=force_dbl(arg1);
+ fb=force_dbl(lastarg);
+ if(fb==0.0)div_error();
+ setdbl(e,fa/fb);
+ goto DONE;
+
+ case READY(MOD):
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ if(tag[arg1]==DOUBLE||tag[lastarg]==DOUBLE)word_error("mod");
+ if(bigzero(lastarg))div_error(); /* build into bigmod ? */
+ simpl(bigmod(arg1,lastarg));
+ goto DONE;
+
+ case READY(POWER):
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ if(tag[lastarg]==DOUBLE)
+ { fa=force_dbl(arg1);
+ if(fa<0.0)errno=EDOM,math_error("^");
+ fb=get_dbl(lastarg); }else
+ if(tag[arg1]==DOUBLE)
+ fa=get_dbl(arg1),fb=bigtodbl(lastarg); else
+ if(neg(lastarg))
+ fa=bigtodbl(arg1),fb=bigtodbl(lastarg);
+ else { simpl(bigpow(arg1,lastarg));
+ goto DONE; }
+ errno=0; /* to clear */
+ setdbl(e,pow(fa,fb));
+ if(errno)math_error("power");
+ goto DONE;
+
+ case READY(SHOWSCALED): /* SHOWSCALED precision number => numeral */
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ if(tag[arg1]==DOUBLE)
+ word_error("showscaled");
+ arg1=getsmallint(arg1);
+ (void)sprintf(linebuf,"%.*e",arg1,force_dbl(lastarg));
+ hd[e]=I; e=tl[e]=str_conv(linebuf);
+ goto DONE;
+
+ case READY(SHOWFLOAT): /* SHOWFLOAT precision number => numeral */
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ if(tag[arg1]==DOUBLE)
+ word_error("showfloat");
+ arg1=getsmallint(arg1);
+ (void)sprintf(linebuf,"%.*f",arg1,force_dbl(lastarg));
+ hd[e]=I; e=tl[e]=str_conv(linebuf);
+ goto DONE;
+
+#define coerce_dbl(x) tag[x]==DOUBLE?(x):sto_dbl(bigtodbl(x))
+
+ case READY(STEP): /* STEP i a => GENSEQ (i,NIL) a */
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ hd[e]=ap(GENSEQ,cons(arg1,NIL));
+ goto NEXTREDEX;
+
+ case READY(MERGE): /* MERGE [] y => y
+ MERGE (a:x) [] => a:x
+ MERGE (a:x) (b:y) => a:MERGE x (b:y), if a<=b
+ => b:MERGE (a:x) y, otherwise */
+ RESTORE(e);
+ GETARG(arg1);
+ UPLEFT;
+ if(arg1==NIL)simpl(lastarg); else
+ if(lastarg==NIL)simpl(arg1); else
+ if(compare(hd[arg1]=reduce(hd[arg1]),
+ hd[lastarg]=reduce(hd[lastarg]))<=0) /* ### */
+ setcell(CONS,hd[arg1],ap2(MERGE,tl[arg1],lastarg));
+ else setcell(CONS,hd[lastarg],ap2(MERGE,tl[lastarg],arg1));
+ goto DONE;
+
+ case READY(STEPUNTIL): /* STEPUNTIL i a b => GENSEQ (i,b) a */
+ RESTORE(e);
+ GETARG(arg1);
+ GETARG(arg2);
+ UPLEFT;
+ hd[e]=ap(GENSEQ,cons(arg1,arg2));
+ if(tag[arg1]==INT?poz(arg1):get_dbl(arg1)>=0.0)
+ tag[tl[hd[e]]]=AP; /* hack to record sign of step - see GENSEQ */
+ goto NEXTREDEX;
+
+ case READY(Ush):
+ /* Ush (k f1...fn) p (k x1...xn)
+ => "k"++' ':f1 x1 ...++' ':fn xn, p='\0'
+ => "(k"++' ':f1 x1 ...++' ':fn xn++")", p='\1'
+ Ush (k f1...fn) p other => FAIL */
+ RESTORE(e);
+ GETARG(arg1);
+ GETARG(arg2);
+ GETARG(arg3);
+ if(constr_tag(head(arg1))!=constr_tag(head(arg3)))
+ { hd[e]=I;
+ e=tl[e]=FAIL;
+ goto DONE; } /* result is string, so cannot be more args */
+ if(tag[arg1]==CONSTRUCTOR) /* don't parenthesise atom */
+ { hd[e]=I;
+ if(suppressed(arg1))
+ e=tl[e]=str_conv("<unprintable>");
+ else e=tl[e]=str_conv(constr_name(arg1));
+ goto DONE; }
+ hold=arg2?cons(')',NIL):NIL;
+ while(tag[arg1]!=CONSTRUCTOR)
+ hold=cons(' ',ap2(APPEND,ap(tl[arg1],tl[arg3]),hold)),
+ arg1=hd[arg1],arg3=hd[arg3];
+ if(suppressed(arg1))
+ { hd[e]=I; e=tl[e]=str_conv("<unprintable>"); goto DONE; }
+ hold=ap2(APPEND,str_conv(constr_name(arg1)),hold);
+ if(arg2)
+ { setcell(CONS,'(',hold); goto DONE; }
+ else { hd[e]=I; e=tl[e]=hold; goto NEXTREDEX; }
+
+ default: fprintf(stderr,"\nimpossible event in reduce ("),
+ out(stderr,e),fprintf(stderr,")\n"),
+ exit(1);
+ return(0); /* proforma only - unreachable */
+ } /* end of "ready" switch */
+
+} /* end of reduce */
+
+memclass(c,x) /* is char c in list x (may include ranges) */
+{ while(x!=NIL)
+ { if(hd[x]==DOTDOT)
+ { x=tl[x];
+ if(hd[x]<=c&&c<=hd[tl[x]])return(1);
+ x=tl[x]; }
+ else if(c==hd[x])return(1);
+ x=tl[x]; }
+ return(0);
+}
+
+lexfail(x) /* x is known to be a non-empty string (see LEX_RPT) */
+{ word i=24;
+ fprintf(stderr,"\nLEX FAILS WITH UNRECOGNISED INPUT: \"");
+ while(i--&&x!=NIL&&0<=lh(x)&&lh(x)<=255)
+ fprintf(stderr,"%s",charname(lh(x))),
+ x=tl[x];
+ fprintf(stderr,"%s\"\n",x==NIL?"":"...");
+ outstats();
+ exit(1);
+}
+
+lexstate(x) /* extracts initial state info from list of chars labelled
+ by LEX_COUNT - x is evaluated and known to be non-empty */
+{ x = hd[hd[x]]; /* count field of first char */
+ return(cons(sto_word(x>>8),stosmallint(x&255)));
+}
+
+piperrmess(pid)
+word pid;
+{ return(str_conv(pid== -1?"cannot create process\n":"cannot open pipe\n"));
+}
+
+g_residue(toks2) /* remainder of token stream from last token examined */
+word toks2;
+{ word toks1 = NIL;
+ if(tag[toks2]!=CONS)
+ { if(tag[toks2]==AP&&hd[toks2]==I&&tl[toks2]==NIL)
+ return(cons(NIL,NIL));
+ return(cons(NIL,toks2)); /*no tokens examined, whole grammar is `error'*/
+ /* fprintf(stderr,"\nimpossible event in g_residue\n"),
+ exit(1); /* grammar fn must have examined >=1 tokens */ }
+ while(tag[tl[toks2]]==CONS)toks1=cons(hd[toks2],toks1),toks2=tl[toks2];
+ if(tl[toks2]==NIL||tag[tl[toks2]]==AP&&hd[tl[toks2]]==I&&tl[tl[toks2]]==NIL)
+ { toks1=cons(hd[toks2],toks1);
+ return(cons(ap(DESTREV,toks1),NIL)); }
+ return(cons(ap(DESTREV,toks1),toks2));
+}
+
+numplus(x,y)
+word x,y;
+{ if(tag[x]==DOUBLE)
+ return(sto_dbl(get_dbl(x)+force_dbl(y)));
+ if(tag[y]==DOUBLE)
+ return(sto_dbl(bigtodbl(x)+get_dbl(y)));
+ return(bigplus(x,y));
+}
+
+fn_error(s)
+char *s;
+{ fprintf(stderr,"\nprogram error: %s\n",s);
+ outstats();
+ exit(1); }
+
+getenv_error(char *a)
+{ fprintf(stderr,
+ "program error: getenv(%s): illegal characters in result string\n",a);
+ outstats();
+ exit(1); }
+
+subs_error()
+{ fn_error("subscript out of range");
+}
+
+div_error()
+{ fn_error("attempt to divide by zero");
+}
+/* other arithmetic exceptions signal-trapped by fpe_error - see STEER */
+
+math_error(s)
+char *s;
+{ fprintf(stderr,"\nmath function %serror (%s)\n",
+ errno==EDOM?"domain ":errno==ERANGE?"range ":"",s);
+ outstats();
+ exit(1);
+}
+
+word_error(s)
+char *s;
+{ fprintf(stderr,
+ "\nprogram error: fractional number where integer expected (%s)\n",s);
+ outstats();
+ exit(1);
+}
+
+char *stdname(c)
+word c;
+{ return c==':' ? "$:-" : c=='-' ? "$-" : "$+"; }
+
+stdin_error(c)
+word c;
+{ if(stdinuse==c)
+ fprintf(stderr,"program error: duplicate use of %s\n",stdname(c));
+ else fprintf(stderr,"program error: simultaneous use of %s and %s\n",
+ stdname(c), stdname(stdinuse));
+ outstats();
+ exit(1);
+}
+
+#ifdef BSDCLOCK
+#include <sys/times.h>
+#include <unistd.h>
+#ifndef CLK_TCK
+#define CLK_TCK sysconf(_SC_CLK_TCK)
+#endif
+#else
+/* this is ANSII C, POSIX */
+#include <time.h>
+clock_t start, end;
+#endif
+
+initclock()
+{
+#ifndef BSDCLOCK
+start=clock();
+#endif
+}
+
+out_here(f,h,nl) /* h is fileinfo(scriptname,line_no) */
+FILE *f;
+word h,nl;
+{ extern word errs;
+ if(tag[h]!=FILEINFO)
+ { fprintf(stderr,"(impossible event in outhere)\n"); return; }
+ fprintf(f,"(line %3d of \"%s\")",tl[h],(char *)hd[h]);
+ if(nl)putc('\n',f); else putc(' ',f);
+ if(compiling&&!errs)errs=h; /* relevant only when called from steer.c */
+} /* `soft' error, set errs rather than errline, so not saved in dump */
+
+outstats()
+{ extern long claims,nogcs;
+ extern word atcount;
+ extern long long cellcount;
+#ifdef BSDCLOCK
+ struct tms buffer;
+#endif
+#ifdef HISTO
+ if(sourcemc)printhisto();
+#endif
+ if(!atcount)return;
+#ifdef BSDCLOCK
+ times(&buffer);
+#else
+ end=clock();
+#endif
+ printf("||");
+ printf("reductions = %lld, cells claimed = %lld, ",
+ cycles,cellcount+claims);
+ printf("no of gc's = %ld, cpu = %0.2f",nogcs,
+#ifdef BSDCLOCK
+ buffer.tms_utime/(CLK_TCK*1.0));
+#else
+ ((double) (end - start)) / CLOCKS_PER_SEC);
+#endif
+ putchar('\n');
+#ifdef DEBUG
+ printf("||maxr_depth=%d\n",maxrdepth);
+#endif
+}
+
+/* end of MIRANDA REDUCE */
+
diff --git a/new/rules.y b/new/rules.y
new file mode 100644
index 0000000..e44698b
--- /dev/null
+++ b/new/rules.y
@@ -0,0 +1,1686 @@
+/* Miranda token declarations and syntax rules for "YACC" */
+
+/**************************************************************************
+ * Copyright (C) Research Software Limited 1985-90. All rights reserved. *
+ * The Miranda system is distributed as free software under the terms in *
+ * the file "COPYING" which is included in the distribution. *
+ *------------------------------------------------------------------------*/
+
+/* miranda symbols */
+
+%token VALUE EVAL WHERE IF TO LEFTARROW COLONCOLON COLON2EQ
+ TYPEVAR NAME CNAME CONST DOLLAR2 OFFSIDE ELSEQ
+ ABSTYPE WITH DIAG EQEQ FREE INCLUDE EXPORT TYPE
+ OTHERWISE SHOWSYM PATHNAME BNF LEX ENDIR ERRORSY ENDSY
+ EMPTYSY READVALSY LEXDEF CHARCLASS ANTICHARCLASS LBEGIN
+
+%right ARROW
+%right PLUSPLUS ':' MINUSMINUS
+%nonassoc DOTDOT
+%right VEL
+%right '&'
+%nonassoc '>' GE '=' NE LE '<'
+%left '+' '-'
+%left '*' '/' REM DIV
+%right '^'
+%left '.' /* fiddle to make '#' behave */
+%left '!'
+%right INFIXNAME INFIXCNAME
+%token CMBASE /* placeholder to start combinator values - see combs.h */
+
+%{
+/* the following definition has to be kept in line with the token declarations
+ above */
+char *yysterm[]= {
+ 0,
+ "VALUE",
+ "EVAL",
+ "where",
+ "if",
+ "&>",
+ "<-",
+ "::",
+ "::=",
+ "TYPEVAR",
+ "NAME",
+ "CONSTRUCTOR-NAME",
+ "CONST",
+ "$$",
+ "OFFSIDE",
+ "OFFSIDE =",
+ "abstype",
+ "with",
+ "//",
+ "==",
+ "%free",
+ "%include",
+ "%export",
+ "type",
+ "otherwise",
+ "show",
+ "PATHNAME",
+ "%bnf",
+ "%lex",
+ "%%",
+ "error",
+ "end",
+ "empty",
+ "readvals",
+ "NAME",
+ "`char-class`",
+ "`char-class`",
+ "%%begin",
+ "->",
+ "++",
+ "--",
+ "..",
+ "\\/",
+ ">=",
+ "~=",
+ "<=",
+ "mod",
+ "div",
+ "$NAME",
+ "$CONSTRUCTOR"};
+
+%}
+
+/* Miranda syntax rules */
+/* the associated semantic actions perform the compilation */
+
+%{
+#include "data.h"
+#include "lex.h"
+extern word nill,k_i,Void;
+extern word message,standardout;
+extern word big_one;
+#define isltmess_t(t) (islist_t(t)&&tl[t]==message)
+#define isstring_t(t) (islist_t(t)&&tl[t]==char_t)
+extern word SYNERR,errs,echoing,gvars;
+extern word listdiff_fn,indent_fn,outdent_fn;
+extern word polyshowerror;
+word lastname=0;
+word suppressids=NIL;
+word idsused=NIL;
+word tvarscope=0;
+word includees=NIL,embargoes=NIL,exportfiles=NIL,freeids=NIL,exports=NIL;
+word lexdefs=NIL,lexstates=NIL,inlex=0,inexplist=0;
+word inbnf=0,col_fn=0,fnts=NIL,eprodnts=NIL,nonterminals=NIL,sreds=0;
+word ihlist=0,ntspecmap=NIL,ntmap=NIL,lasth=0;
+word obrct=0;
+
+void evaluate(x)
+word x;
+{ extern word debug;
+ word t;
+ t=type_of(x);
+ if(t==wrong_t)return;
+ lastexp=x;
+ x=codegen(x);
+ if(polyshowerror)return;
+ if(process())
+ /* setup new process for each evaluation */
+ { (void)signal(SIGINT,(sighandler)dieclean);
+ /* if interrupted will flush output etc before going */
+ compiling=0;
+ resetgcstats();
+ output(isltmess_t(t)?x:
+ cons(ap(standardout,isstring_t(t)?x
+ :ap(mkshow(0,0,t),x)),NIL));
+ (void)signal(SIGINT,SIG_IGN);/* otherwise could do outstats() twice */
+ putchar('\n');
+ outstats();
+ exit(0); }
+}
+
+void obey(x) /* like evaluate but no fork, no stats, no extra '\n' */
+word x;
+{ word t=type_of(x);
+ x=codegen(x);
+ if(polyshowerror)return;
+ compiling=0;
+ output(isltmess_t(t)?x:
+ cons(ap(standardout,isstring_t(t)?x:ap(mkshow(0,0,t),x)),NIL));
+}
+
+int isstring(x)
+word x;
+{ return(x==NILS||tag[x]==CONS&&is_char(hd[x]));
+}
+
+word compose(x) /* used in compiling 'cases' */
+word x;
+{ word y=hd[x];
+ if(hd[y]==OTHERWISE)y=tl[y]; /* OTHERWISE was just a marker - lose it */
+ else y=tag[y]==LABEL?label(hd[y],ap(tl[y],FAIL)):
+ ap(y,FAIL); /* if all guards false result is FAIL */
+ x = tl[x];
+ if(x!=NIL)
+ { while(tl[x]!=NIL)y=label(hd[hd[x]],ap(tl[hd[x]],y)), x=tl[x];
+ y=ap(hd[x],y);
+ /* first alternative has no label - label of enclosing rhs applies */
+ }
+ return(y);
+}
+
+word starts(x) /* x is grammar rhs - returns list of nonterminals in start set */
+word x;
+{ L: switch(tag[x])
+ { case ID: return(cons(x,NIL));
+ case LABEL:
+ case LET:
+ case LETREC: x=tl[x]; goto L;
+ case AP: switch(hd[x])
+ { case G_SYMB:
+ case G_SUCHTHAT:
+ case G_RULE: return(NIL);
+ case G_OPT:
+ case G_FBSTAR:
+ case G_STAR: x=tl[x]; goto L;
+ default: if(hd[x]==outdent_fn)
+ { x=tl[x]; goto L; }
+ if(tag[hd[x]]==AP)
+ if(hd[hd[x]]==G_ERROR)
+ { x=tl[hd[x]]; goto L; }
+ if(hd[hd[x]]==G_SEQ)
+ { if(eprod(tl[hd[x]]))
+ return(UNION(starts(tl[hd[x]]),starts(tl[x])));
+ x=tl[hd[x]]; goto L; } else
+ if(hd[hd[x]]==G_ALT)
+ return(UNION(starts(tl[hd[x]]),starts(tl[x])));
+ else
+ if(hd[hd[x]]==indent_fn)
+ { x=tl[x]; goto L; }
+ }
+ default: return(NIL);
+ }
+}
+
+int eprod(x) /* x is grammar rhs - does x admit empty production? */
+word x;
+{ L: switch(tag[x])
+ { case ID: return(member(eprodnts,x));
+ case LABEL:
+ case LET:
+ case LETREC: x=tl[x]; goto L;
+ case AP: switch(hd[x])
+ { case G_SUCHTHAT:
+ case G_ANY:
+ case G_SYMB: return(0);
+ case G_RULE: return(1);
+ case G_OPT:
+ case G_FBSTAR:
+ case G_STAR: return(1);
+ default: if(hd[x]==outdent_fn)
+ { x=tl[x]; goto L; }
+ if(tag[hd[x]]==AP)
+ if(hd[hd[x]]==G_ERROR)
+ { x=tl[hd[x]]; goto L; }
+ if(hd[hd[x]]==G_SEQ)
+ return(eprod(tl[hd[x]])&&eprod(tl[x])); else
+ if(hd[hd[x]]==G_ALT)
+ return(eprod(tl[hd[x]])||eprod(tl[x]));
+ else
+ if(hd[hd[x]]==indent_fn)
+ { x=tl[x]; goto L; }
+ }
+ default: return(x==G_STATE||x==G_UNIT);
+ /* G_END is special case, unclear whether it counts as an e-prodn.
+ decide no for now, sort this out later */
+ }
+}
+
+word add_prod(d,ps,hr)
+word d,ps,hr;
+{ word p,n=dlhs(d);
+ for(p=ps;p!=NIL;p=tl[p])
+ if(dlhs(hd[p])==n)
+ if(dtyp(d)==undef_t&&dval(hd[p])==UNDEF)
+ { dval(hd[p])=dval(d); return(ps); } else
+ if(dtyp(d)!=undef_t&&dtyp(hd[p])==undef_t)
+ { dtyp(hd[p])=dtyp(d); return(ps); }
+ else
+ errs=hr,
+ printf(
+ "%ssyntax error: conflicting %s of nonterminal \"%s\"\n",
+ echoing?"\n":"",
+ dtyp(d)==undef_t?"definitions":"specifications",
+ get_id(n)),
+ acterror();
+ return(cons(d,ps));
+}
+/* clumsy - this algorithm is quadratic in number of prodns - fix later */
+
+word getloc(nt,prods) /* get here info for nonterminal */
+word nt,prods;
+{ while(prods!=NIL&&dlhs(hd[prods])!=nt)prods=tl[prods];
+ if(prods!=NIL)return(hd[dval(hd[prods])]);
+ return(0); /* should not happen, but just in case */
+}
+
+void findnt(nt) /* set errs to here info of undefined nonterminal */
+word nt;
+{ word p=ntmap;
+ while(p!=NIL&&hd[hd[p]]!=nt)p=tl[p];
+ if(p!=NIL)
+ { errs=tl[hd[p]]; return; }
+ p=ntspecmap;
+ while(p!=NIL&&hd[hd[p]]!=nt)p=tl[p];
+ if(p!=NIL)errs=tl[hd[p]];
+}
+
+#define isap2(fn,x) (tag[x]==AP&&tag[hd[x]]==AP&&hd[hd[x]]==(fn))
+#define firstsymb(term) tl[hd[term]]
+
+void binom(rhs,x)
+/* performs the binomial optimisation on rhs of nonterminal x
+ x: x alpha1| ... | x alphaN | rest ||need not be in this order
+ ==>
+ x: rest (alpha1|...|alphaN)*
+*/
+word rhs,x;
+{ word *p= &tl[rhs]; /* rhs is of form label(hereinf, stuff) */
+ word *lastp=0,*holdrhs,suffix,alpha=NIL;
+ if(tag[*p]==LETREC)p = &tl[*p]; /* ignore trailing `where defs' */
+ if(isap2(G_ERROR,*p))p = &tl[hd[*p]];
+ holdrhs=p;
+ while(isap2(G_ALT,*p))
+ if(firstsymb(tl[hd[*p]])==x)
+ alpha=cons(tl[tl[hd[*p]]],alpha),
+ *p=tl[*p],p = &tl[*p];
+ else lastp=p,p = &tl[tl[*p]];
+ /* note each (G_ALT a b) except the outermost is labelled */
+ if(lastp&&firstsymb(*p)==x)
+ alpha=cons(tl[*p],alpha),
+ *lastp=tl[hd[*lastp]];
+ if(alpha==NIL)return;
+ suffix=hd[alpha],alpha=tl[alpha];
+ while(alpha!=NIL)
+ suffix=ap2(G_ALT,hd[alpha],suffix),
+ alpha=tl[alpha];
+ *holdrhs=ap2(G_SEQ,*holdrhs,ap(G_FBSTAR,suffix));
+}
+/* should put some labels on the alpha's - fix later */
+
+word getcol_fn()
+{ extern char *dicp,*dicq;
+ if(!col_fn)
+ strcpy(dicp,"bnftokenindentation"),
+ dicq=dicp+20,
+ col_fn=name();
+ return(col_fn);
+}
+
+void startbnf()
+{ ntspecmap=ntmap=nonterminals=NIL;
+ if(fnts==0)col_fn=0; /* reinitialise, a precaution */
+}
+
+word ih_abstr(x) /* abstract inherited attributes from grammar rule */
+word x;
+{ word ih=ihlist;
+ while(ih!=NIL) /* relies on fact that ihlist is reversed */
+ x=lambda(hd[ih],x),ih=tl[ih];
+ return(x);
+}
+
+int can_elide(x) /* is x of the form $1 applied to ih attributes in order? */
+word x;
+{ word ih;
+ if(ihlist)
+ for(ih=ihlist;ih!=NIL&&tag[x]==AP;ih=tl[ih],x=hd[x])
+ if(hd[ih]!=tl[x])return(0);
+ return(x==mkgvar(1));
+}
+
+int e_re(x) /* does regular expression x match empty string ? */
+{ L: if(tag[x]==AP)
+ { if(hd[x]==LEX_STAR||hd[x]==LEX_OPT)return(1);
+ if(hd[x]==LEX_STRING)return(tl[x]==NIL);
+ if(tag[hd[x]]!=AP)return(0);
+ if(hd[hd[x]]==LEX_OR)
+ { if(e_re(tl[hd[x]]))return(1);
+ x=tl[x]; goto L; } else
+ if(hd[hd[x]]==LEX_SEQ)
+ { if(!e_re(tl[hd[x]]))return(0);
+ x=tl[x]; goto L; } else
+ if(hd[hd[x]]==LEX_RCONTEXT)
+ { x=tl[hd[x]]; goto L; }
+ }
+ return(0);
+}
+
+%}
+
+%%
+
+entity: /* the entity to be parsed is either a definition script or an
+ expression (the latter appearing as a command line) */
+
+ error|
+
+ script
+ = { lastname=0; /* outstats(); */ }|
+ /* statistics not usually wanted after compilation */
+
+/* MAGIC exp '\n' script
+ = { lastexp=$2; }| /* change to magic scripts 19.11.2013 */
+
+ VALUE exp
+ = { lastexp=$2; }| /* next line of `$+' */
+
+ EVAL exp
+ = { if(!SYNERR&&yychar==0)
+ { evaluate($2); }
+ }|
+
+ EVAL exp COLONCOLON
+ /* boring problem - how to make sure no junk chars follow here?
+ likewise TO case -- trick used above doesn't work, yychar is
+ here always -1 Why? Too fiddly to bother with just now */
+ = { word t=type_of($2);
+ if(t!=wrong_t)
+ { lastexp=$2;
+ if(tag[$2]==ID&&id_type($2)==wrong_t)t=wrong_t;
+ out_type(t);
+ putchar('\n'); }
+ }|
+
+ EVAL exp TO
+ = { FILE *fil=NULL,*efil;
+ word t=type_of($2);
+ char *f=token(),*ef;
+ if(f)keep(f); ef=token(); /* wasteful of dic space, FIX LATER */
+ if(f){ fil= fopen(f,$3?"a":"w");
+ if(fil==NULL)
+ printf("cannot open \"%s\" for writing\n",f); }
+ else printf("filename missing after \"&>\"\n");
+ if(ef)
+ { efil= fopen(ef,$3?"a":"w");
+ if(efil==NULL)
+ printf("cannot open \"%s\" for writing\n",ef); }
+ if(t!=wrong_t)$2=codegen(lastexp=$2);
+ if(polyshowerror)return;
+ if(t!=wrong_t&&fil!=NULL&&(!ef||efil))
+ { word pid;/* launch a concurrent process to perform task */
+ sighandler oldsig;
+ oldsig=signal(SIGINT,SIG_IGN); /* ignore interrupts */
+ if(pid=fork())
+ { /* "parent" */
+ if(pid==-1)perror("cannot create process");
+ else printf("process %d\n",pid);
+ fclose(fil);
+ if(ef)fclose(efil);
+ (void)signal(SIGINT,oldsig); }else
+ { /* "child" */
+ (void)signal(SIGQUIT,SIG_IGN); /* and quits */
+#ifndef SYSTEM5
+ (void)signal(SIGTSTP,SIG_IGN); /* and stops */
+#endif
+ close(1); dup(fileno(fil)); /* subvert stdout */
+ close(2); dup(fileno(ef?efil:fil)); /* subvert stderr */
+ /* FUNNY BUG - if redirect stdout stderr to same file by two
+ calls to freopen, their buffers get conflated - whence do
+ by subverting underlying file descriptors, as above
+ (fix due to Martin Guy) */
+ /* formerly used dup2, but not present in system V */
+ fclose(stdin);
+ /* setbuf(stdout,NIL);
+ /* not safe to change buffering of stream already in use */
+ /* freopen would have reset the buffering automatically */
+ lastexp = NIL; /* what else should we set to NIL? */
+ /*atcount= 1; */
+ compiling= 0;
+ resetgcstats();
+ output(isltmess_t(t)?$2:
+ cons(ap(standardout,isstring_t(t)?$2:
+ ap(mkshow(0,0,t),$2)),NIL));
+ putchar('\n');
+ outstats();
+ exit(0); } } };
+
+script:
+ /* empty */|
+ defs;
+
+exp:
+ op | /* will later suppress in favour of (op) in arg */
+ e1;
+
+op:
+ '~'
+ = { $$ = NOT; }|
+ '#'
+ = { $$ = LENGTH; }|
+ diop;
+
+diop:
+ '-'
+ = { $$ = MINUS; }|
+ diop1;
+
+diop1:
+ '+'
+ = { $$ = PLUS; }|
+ PLUSPLUS
+ = { $$ = APPEND; }|
+ ':'
+ = { $$ = P; }|
+ MINUSMINUS
+ = { $$ = listdiff_fn; }|
+ VEL
+ = { $$ = OR; }|
+ '&'
+ = { $$ = AND; }|
+ relop |
+ '*'
+ = { $$ = TIMES; }|
+ '/'
+ = { $$ = FDIV; }|
+ DIV
+ = { $$ = INTDIV; }|
+ REM
+ = { $$ = MOD; }|
+ '^'
+ = { $$ = POWER; }|
+ '.'
+ = { $$ = B; }|
+ '!'
+ = { $$ = ap(C,SUBSCRIPT); }|
+ INFIXNAME|
+ INFIXCNAME;
+
+relop:
+ '>'
+ = { $$ = GR; }|
+ GE
+ = { $$ = GRE; }|
+ eqop
+ = { $$ = EQ; }|
+ NE
+ = { $$ = NEQ; }|
+ LE
+ = { $$ = ap(C,GRE); }|
+ '<'
+ = { $$ = ap(C,GR); };
+
+eqop:
+ EQEQ| /* silently accept for benefit of Haskell users */
+ '=';
+
+rhs:
+ cases WHERE ldefs
+ = { $$ = block($3,compose($1),0); }|
+ exp WHERE ldefs
+ = { $$ = block($3,$1,0); }|
+ exp|
+ cases
+ = { $$ = compose($1); };
+
+cases:
+ exp ',' if exp
+ = { $$ = cons(ap2(COND,$4,$1),NIL); }|
+ exp ',' OTHERWISE
+ = { $$ = cons(ap(OTHERWISE,$1),NIL); }|
+ cases reindent ELSEQ alt
+ = { $$ = cons($4,$1);
+ if(hd[hd[$1]]==OTHERWISE)
+ syntax("\"otherwise\" must be last case\n"); };
+
+alt:
+ here exp
+ = { errs=$1,
+ syntax("obsolete syntax, \", otherwise\" missing\n");
+ $$ = ap(OTHERWISE,label($1,$2)); }|
+ here exp ',' if exp
+ = { $$ = label($1,ap2(COND,$5,$2)); }|
+ here exp ',' OTHERWISE
+ = { $$ = ap(OTHERWISE,label($1,$2)); };
+
+if:
+ /* empty */
+ = { extern word strictif;
+ if(strictif)syntax("\"if\" missing\n"); }|
+ IF;
+
+indent:
+ /* empty */
+ = { if(!SYNERR){layout(); setlmargin();}
+ };
+/* note that because of yacc's one symbol look ahead, indent must usually be
+ invoked one symbol earlier than the non-terminal to which it applies
+ - see `production:' for an exception */
+
+outdent:
+ separator
+ = { unsetlmargin(); };
+
+separator:
+ OFFSIDE | ';' ;
+
+reindent:
+ /* empty */
+ = { if(!SYNERR)
+ { unsetlmargin(); layout(); setlmargin(); }
+ };
+
+liste: /* NB - returns list in reverse order */
+ exp
+ = { $$ = cons($1,NIL); }|
+ liste ',' exp /* left recursive so as not to eat YACC stack */
+ = { $$ = cons($3,$1); };
+
+e1:
+ '~' e1 %prec '='
+ = { $$ = ap(NOT,$2); }|
+ e1 PLUSPLUS e1
+ = { $$ = ap2(APPEND,$1,$3); }|
+ e1 ':' e1
+ = { $$ = cons($1,$3); }|
+ e1 MINUSMINUS e1
+ = { $$ = ap2(listdiff_fn,$1,$3); }|
+ e1 VEL e1
+ = { $$ = ap2(OR,$1,$3); }|
+ e1 '&' e1
+ = { $$ = ap2(AND,$1,$3); }|
+ reln |
+ e2;
+
+es1: /* e1 or presection */
+ '~' e1 %prec '='
+ = { $$ = ap(NOT,$2); }|
+ e1 PLUSPLUS e1
+ = { $$ = ap2(APPEND,$1,$3); }|
+ e1 PLUSPLUS
+ = { $$ = ap(APPEND,$1); }|
+ e1 ':' e1
+ = { $$ = cons($1,$3); }|
+ e1 ':'
+ = { $$ = ap(P,$1); }|
+ e1 MINUSMINUS e1
+ = { $$ = ap2(listdiff_fn,$1,$3); }|
+ e1 MINUSMINUS
+ = { $$ = ap(listdiff_fn,$1); }|
+ e1 VEL e1
+ = { $$ = ap2(OR,$1,$3); }|
+ e1 VEL
+ = { $$ = ap(OR,$1); }|
+ e1 '&' e1
+ = { $$ = ap2(AND,$1,$3); }|
+ e1 '&'
+ = { $$ = ap(AND,$1); }|
+ relsn |
+ es2;
+
+e2:
+ '-' e2 %prec '-'
+ = { $$ = ap(NEG,$2); }|
+ '#' e2 %prec '.'
+ = { $$ = ap(LENGTH,$2); }|
+ e2 '+' e2
+ = { $$ = ap2(PLUS,$1,$3); }|
+ e2 '-' e2
+ = { $$ = ap2(MINUS,$1,$3); }|
+ e2 '*' e2
+ = { $$ = ap2(TIMES,$1,$3); }|
+ e2 '/' e2
+ = { $$ = ap2(FDIV,$1,$3); }|
+ e2 DIV e2
+ = { $$ = ap2(INTDIV,$1,$3); } |
+ e2 REM e2
+ = { $$ = ap2(MOD,$1,$3); }|
+ e2 '^' e2
+ = { $$ = ap2(POWER,$1,$3); } |
+ e2 '.' e2
+ = { $$ = ap2(B,$1,$3); }|
+ e2 '!' e2
+ = { $$ = ap2(SUBSCRIPT,$3,$1); }|
+ e3;
+
+es2: /* e2 or presection */
+ '-' e2 %prec '-'
+ = { $$ = ap(NEG,$2); }|
+ '#' e2 %prec '.'
+ = { $$ = ap(LENGTH,$2); }|
+ e2 '+' e2
+ = { $$ = ap2(PLUS,$1,$3); }|
+ e2 '+'
+ = { $$ = ap(PLUS,$1); }|
+ e2 '-' e2
+ = { $$ = ap2(MINUS,$1,$3); }|
+ e2 '-'
+ = { $$ = ap(MINUS,$1); }|
+ e2 '*' e2
+ = { $$ = ap2(TIMES,$1,$3); }|
+ e2 '*'
+ = { $$ = ap(TIMES,$1); }|
+ e2 '/' e2
+ = { $$ = ap2(FDIV,$1,$3); }|
+ e2 '/'
+ = { $$ = ap(FDIV,$1); }|
+ e2 DIV e2
+ = { $$ = ap2(INTDIV,$1,$3); } |
+ e2 DIV
+ = { $$ = ap(INTDIV,$1); } |
+ e2 REM e2
+ = { $$ = ap2(MOD,$1,$3); }|
+ e2 REM
+ = { $$ = ap(MOD,$1); }|
+ e2 '^' e2
+ = { $$ = ap2(POWER,$1,$3); } |
+ e2 '^'
+ = { $$ = ap(POWER,$1); } |
+ e2 '.' e2
+ = { $$ = ap2(B,$1,$3); }|
+ e2 '.'
+ = { $$ = ap(B,$1); }|
+ e2 '!' e2
+ = { $$ = ap2(SUBSCRIPT,$3,$1); }|
+ e2 '!'
+ = { $$ = ap2(C,SUBSCRIPT,$1); }|
+ es3;
+
+e3:
+ comb INFIXNAME e3
+ = { $$ = ap2($2,$1,$3); }|
+ comb INFIXCNAME e3
+ = { $$ = ap2($2,$1,$3); }|
+ comb;
+
+es3: /* e3 or presection */
+ comb INFIXNAME e3
+ = { $$ = ap2($2,$1,$3); }|
+ comb INFIXNAME
+ = { $$ = ap($2,$1); }|
+ comb INFIXCNAME e3
+ = { $$ = ap2($2,$1,$3); }|
+ comb INFIXCNAME
+ = { $$ = ap($2,$1); }|
+ comb;
+
+comb:
+ comb arg
+ = { $$ = ap($1,$2); }|
+ arg;
+
+reln:
+ e2 relop e2
+ = { $$ = ap2($2,$1,$3); }|
+ reln relop e2
+ = { word subject;
+ subject = hd[hd[$1]]==AND?tl[tl[$1]]:tl[$1];
+ $$ = ap2(AND,$1,ap2($2,subject,$3));
+ }; /* EFFICIENCY PROBLEM - subject gets re-evaluated (and
+ retypechecked) - fix later */
+
+relsn: /* reln or presection */
+ e2 relop e2
+ = { $$ = ap2($2,$1,$3); }|
+ e2 relop
+ = { $$ = ap($2,$1); }|
+ reln relop e2
+ = { word subject;
+ subject = hd[hd[$1]]==AND?tl[tl[$1]]:tl[$1];
+ $$ = ap2(AND,$1,ap2($2,subject,$3));
+ }; /* EFFICIENCY PROBLEM - subject gets re-evaluated (and
+ retypechecked) - fix later */
+
+arg:
+ { if(!SYNERR)lexstates=NIL,inlex=1; }
+ LEX lexrules ENDIR
+ = { inlex=0; lexdefs=NIL;
+ if(lexstates!=NIL)
+ { word echoed=0;
+ for(;lexstates!=NIL;lexstates=tl[lexstates])
+ { if(!echoed)printf(echoing?"\n":""),echoed=1;
+ if(!(tl[hd[lexstates]]&1))
+ printf("warning: lex state %s is never entered\n",
+ get_id(hd[hd[lexstates]])); else
+ if(!(tl[hd[lexstates]]&2))
+ printf("warning: lex state %s has no associated rules\n",
+ get_id(hd[hd[lexstates]])); }
+ }
+ if($3==NIL)syntax("%lex with no rules\n");
+ else tag[$3]=LEXER;
+ /* result is lex-list, in reverse order, of items of the form
+ cons(scstuff,cons(matcher,rhs))
+ where scstuff is of the form
+ cons(0-or-list-of-startconditions,1+newstartcondition)
+ */
+ $$ = $3; }|
+ NAME |
+ CNAME |
+ CONST |
+ READVALSY
+ = { $$ = readvals(0,0); }|
+ SHOWSYM
+ = { $$ = show(0,0); }|
+ DOLLAR2
+ = { $$ = lastexp;
+ if(lastexp==UNDEF)
+ syntax("no previous expression to substitute for $$\n"); }|
+ '[' ']'
+ = { $$ = NIL; }|
+ '[' exp ']'
+ = { $$ = cons($2,NIL); }|
+ '[' exp ',' exp ']'
+ = { $$ = cons($2,cons($4,NIL)); }|
+ '[' exp ',' exp ',' liste ']'
+ = { $$ = cons($2,cons($4,reverse($6))); }|
+ '[' exp DOTDOT exp ']'
+ = { $$ = ap3(STEPUNTIL,big_one,$4,$2); }|
+ '[' exp DOTDOT ']'
+ = { $$ = ap2(STEP,big_one,$2); }|
+ '[' exp ',' exp DOTDOT exp ']'
+ = { $$ = ap3(STEPUNTIL,ap2(MINUS,$4,$2),$6,$2); }|
+ '[' exp ',' exp DOTDOT ']'
+ = { $$ = ap2(STEP,ap2(MINUS,$4,$2),$2); }|
+ '[' exp '|' qualifiers ']'
+ = { $$ = SYNERR?NIL:compzf($2,$4,0); }|
+ '[' exp DIAG qualifiers ']'
+ = { $$ = SYNERR?NIL:compzf($2,$4,1); }|
+ '(' op ')' /* RSB */
+ = { $$ = $2; }|
+ '(' es1 ')' /* presection or parenthesised e1 */
+ = { $$ = $2; }|
+ '(' diop1 e1 ')' /* postsection */
+ = { $$ = (tag[$2]==AP&&hd[$2]==C)?ap(tl[$2],$3): /* optimisation */
+ ap2(C,$2,$3); }|
+ '(' ')'
+ = { $$ = Void; }| /* the void tuple */
+ '(' exp ',' liste ')'
+ = { if(tl[$4]==NIL)$$=pair($2,hd[$4]);
+ else { $$=pair(hd[tl[$4]],hd[$4]);
+ $4=tl[tl[$4]];
+ while($4!=NIL)$$=tcons(hd[$4],$$),$4=tl[$4];
+ $$ = tcons($2,$$); }
+ /* representation of the tuple (a1,...,an) is
+ tcons(a1,tcons(a2,...pair(a(n-1),an))) */
+ };
+
+lexrules:
+ lexrules lstart here re indent { if(!SYNERR)inlex=2; }
+ ARROW exp lpostfix { if(!SYNERR)inlex=1; } outdent
+ = { if($9<0 && e_re($4))
+ errs=$3,
+ syntax("illegal lex rule - lhs matches empty\n");
+ $$ = cons(cons(cons($2,1+$9),cons($4,label($3,$8))),$1); }|
+ lexdefs
+ = { $$ = NIL; };
+
+lstart:
+ /* empty */
+ = { $$ = 0; }|
+ '<' cnames '>'
+ = { word ns=NIL;
+ for(;$2!=NIL;$2=tl[$2])
+ { word *x = &lexstates,i=1;
+ while(*x!=NIL&&hd[hd[*x]]!=hd[$2])i++,x = &tl[*x];
+ if(*x == NIL)*x = cons(cons(hd[$2],2),NIL);
+ else tl[hd[*x]] |= 2;
+ ns = add1(i,ns); }
+ $$ = ns; };
+
+cnames:
+ CNAME
+ = { $$=cons($1,NIL); }|
+ cnames CNAME
+ = { if(member($1,$2))
+ printf("%ssyntax error: repeated name \"%s\" in start conditions\n",
+ echoing?"\n":"",get_id($2)),
+ acterror();
+ $$ = cons($2,$1); };
+
+lpostfix:
+ /* empty */
+ = { $$ = -1; }|
+ LBEGIN CNAME
+ = { word *x = &lexstates,i=1;
+ while(*x!=NIL&&hd[hd[*x]]!=$2)i++,x = &tl[*x];
+ if(*x == NIL)*x = cons(cons($2,1),NIL);
+ else tl[hd[*x]] |= 1;
+ $$ = i;
+ }|
+ LBEGIN CONST
+ = { if(!isnat($2)||get_word($2)!=0)
+ syntax("%begin not followed by IDENTIFIER or 0\n");
+ $$ = 0; };
+
+lexdefs:
+ lexdefs LEXDEF indent '=' re outdent
+ = { lexdefs = cons(cons($2,$5),lexdefs); }|
+ /* empty */
+ = { lexdefs = NIL; };
+
+re: /* regular expression */
+ re1 '|' re
+ { $$ = ap2(LEX_OR,$1,$3); }|
+ re1;
+
+re1:
+ lterm '/' lterm
+ { $$ = ap2(LEX_RCONTEXT,$1,$3); }|
+ lterm '/'
+ { $$ = ap2(LEX_RCONTEXT,$1,0); }|
+ lterm;
+
+lterm:
+ lfac lterm
+ { $$ = ap2(LEX_SEQ,$1,$2); }|
+ lfac;
+
+lfac:
+ lunit '*'
+ { if(e_re($1))
+ syntax("illegal regular expression - arg of * matches empty\n");
+ $$ = ap(LEX_STAR,$1); }|
+ lunit '+'
+ { $$ = ap2(LEX_SEQ,$1,ap(LEX_STAR,$1)); }|
+ lunit '?'
+ { $$ = ap(LEX_OPT,$1); }|
+ lunit;
+
+lunit:
+ '(' re ')'
+ = { $$ = $2; }|
+ CONST
+ = { if(!isstring($1))
+ printf("%ssyntax error - unexpected token \"",
+ echoing?"\n":""),
+ out(stdout,$1),printf("\" in regular expression\n"),
+ acterror();
+ $$ = $1==NILS?ap(LEX_STRING,NIL):
+ tl[$1]==NIL?ap(LEX_CHAR,hd[$1]):
+ ap(LEX_STRING,$1);
+ }|
+ CHARCLASS
+ = { if($1==NIL)
+ syntax("empty character class `` cannot match\n");
+ $$ = tl[$1]==NIL?ap(LEX_CHAR,hd[$1]):ap(LEX_CLASS,$1); }|
+ ANTICHARCLASS
+ = { $$ = ap(LEX_CLASS,cons(ANTICHARCLASS,$1)); }|
+ '.'
+ = { $$ = LEX_DOT; }|
+ name
+ = { word x=lexdefs;
+ while(x!=NIL&&hd[hd[x]]!=$1)x=tl[x];
+ if(x==NIL)
+ printf(
+ "%ssyntax error: undefined lexeme %s in regular expression\n",
+ echoing?"\n":"",
+ get_id($1)),
+ acterror();
+ else $$ = tl[hd[x]]; };
+
+name: NAME|CNAME;
+
+qualifiers:
+ exp
+ = { $$ = cons(cons(GUARD,$1),NIL); }|
+ generator
+ = { $$ = cons($1,NIL); }|
+ qualifiers ';' generator
+ = { $$ = cons($3,$1); }|
+ qualifiers ';' exp
+ = { $$ = cons(cons(GUARD,$3),$1); };
+
+generator:
+ e1 ',' generator
+ = { /* fix syntax to disallow patlist on lhs of iterate generator */
+ if(hd[$3]==GENERATOR)
+ { word e=tl[tl[$3]];
+ if(tag[e]==AP&&tag[hd[e]]==AP&&
+ (hd[hd[e]]==ITERATE||hd[hd[e]]==ITERATE1))
+ syntax("ill-formed generator\n"); }
+ $$ = cons(REPEAT,cons(genlhs($1),$3)); idsused=NIL; }|
+ generator1;
+
+generator1:
+ e1 LEFTARROW exp
+ = { $$ = cons(GENERATOR,cons(genlhs($1),$3)); idsused=NIL; }|
+ e1 LEFTARROW exp ',' exp DOTDOT
+ = { word p = genlhs($1); idsused=NIL;
+ $$ = cons(GENERATOR,
+ cons(p,ap2(irrefutable(p)?ITERATE:ITERATE1,
+ lambda(p,$5),$3)));
+ };
+
+defs:
+ def|
+ defs def;
+
+def:
+ v act2 indent '=' here rhs outdent
+ = { word l = $1, r = $6;
+ word f = head(l);
+ if(tag[f]==ID&&!isconstructor(f)) /* fnform defn */
+ while(tag[l]==AP)r=lambda(tl[l],r),l=hd[l];
+ r = label($5,r); /* to help locate type errors */
+ declare(l,r),lastname=l; }|
+
+ spec
+ = { word h=reverse(hd[$1]),hr=hd[tl[$1]],t=tl[tl[$1]];
+ while(h!=NIL&&!SYNERR)specify(hd[h],t,hr),h=tl[h];
+ $$ = cons(nill,NIL); }|
+
+ ABSTYPE here typeforms indent WITH lspecs outdent
+ = { extern word TABSTRS;
+ extern char *dicp,*dicq;
+ word x=reverse($6),ids=NIL,tids=NIL;
+ while(x!=NIL&&!SYNERR)
+ specify(hd[hd[x]],cons(tl[tl[hd[x]]],NIL),hd[tl[hd[x]]]),
+ ids=cons(hd[hd[x]],ids),x=tl[x];
+ /* each id in specs has its id_type set to const(t,NIL) as a way
+ of flagging that t is an abstract type */
+ x=reverse($3);
+ while(x!=NIL&&!SYNERR)
+ { word shfn;
+ decltype(hd[x],abstract_t,undef_t,$2);
+ tids=cons(head(hd[x]),tids);
+ /* check for presence of showfunction */
+ (void)strcpy(dicp,"show");
+ (void)strcat(dicp,get_id(hd[tids]));
+ dicq = dicp+strlen(dicp)+1;
+ shfn=name();
+ if(member(ids,shfn))
+ t_showfn(hd[tids])=shfn;
+ x=tl[x]; }
+ TABSTRS = cons(cons(tids,ids),TABSTRS);
+ $$ = cons(nill,NIL); }|
+
+ typeform indent act1 here EQEQ type act2 outdent
+ = { word x=redtvars(ap($1,$6));
+ decltype(hd[x],synonym_t,tl[x],$4);
+ $$ = cons(nill,NIL); }|
+
+ typeform indent act1 here COLON2EQ construction act2 outdent
+ = { word rhs = $6, r_ids = $6, n=0;
+ while(r_ids!=NIL)r_ids=tl[r_ids],n++;
+ while(rhs!=NIL&&!SYNERR)
+ { word h=hd[rhs],t=$1,stricts=NIL,i=0;
+ while(tag[h]==AP)
+ { if(tag[tl[h]]==AP&&hd[tl[h]]==strict_t)
+ stricts=cons(i,stricts),tl[h]=tl[tl[h]];
+ t=ap2(arrow_t,tl[h],t),h=hd[h],i++; }
+ if(tag[h]==ID)
+ declconstr(h,--n,t);
+ /* warning - type not yet in reduced form */
+ else { stricts=NIL;
+ if(echoing)putchar('\n');
+ printf("syntax error: illegal construct \"");
+ out_type(hd[rhs]);
+ printf("\" on right of ::=\n");
+ acterror(); } /* can this still happen? check later */
+ if(stricts!=NIL) /* ! operators were present */
+ { word k = id_val(h);
+ while(stricts!=NIL)
+ k=ap2(MKSTRICT,i-hd[stricts],k),
+ stricts=tl[stricts];
+ id_val(h)=k; /* overwrite id_val of original constructor */
+ }
+ r_ids=cons(h,r_ids);
+ rhs = tl[rhs]; }
+ if(!SYNERR)decltype($1,algebraic_t,r_ids,$4);
+ $$ = cons(nill,NIL); }|
+
+ indent setexp EXPORT parts outdent
+ = { inexplist=0;
+ if(exports!=NIL)
+ errs=$2,
+ syntax("multiple %export statements are illegal\n");
+ else { if($4==NIL&&exportfiles==NIL&&embargoes!=NIL)
+ exportfiles=cons(PLUS,NIL);
+ exports=cons($2,$4); } /* cons(hereinfo,identifiers) */
+ $$ = cons(nill,NIL); }|
+
+ FREE here '{' specs '}'
+ = { if(freeids!=NIL)
+ errs=$2,
+ syntax("multiple %free statements are illegal\n"); else
+ { word x=reverse($4);
+ while(x!=NIL&&!SYNERR)
+ { specify(hd[hd[x]],tl[tl[hd[x]]],hd[tl[hd[x]]]);
+ freeids=cons(head(hd[hd[x]]),freeids);
+ if(tl[tl[hd[x]]]==type_t)
+ t_class(hd[freeids])=free_t;
+ else id_val(hd[freeids])=FREE; /* conventional value */
+ x=tl[x]; }
+ fil_share(hd[files])=0; /* parameterised scripts unshareable */
+ freeids=alfasort(freeids);
+ for(x=freeids;x!=NIL;x=tl[x])
+ hd[x]=cons(hd[x],cons(datapair(get_id(hd[x]),0),
+ id_type(hd[x])));
+ /* each element of freeids is of the form
+ cons(id,cons(original_name,type)) */
+ }
+ $$ = cons(nill,NIL); }|
+
+ INCLUDE bindings modifiers outdent
+ /* fiddle - 'indent' done by yylex() on reading fileid */
+ = { extern char *dicp;
+ extern word CLASHES,BAD_DUMP;
+ includees=cons(cons($1,cons($3,$2)),includees);
+ /* $1 contains file+hereinfo */
+ $$ = cons(nill,NIL); }|
+
+ here BNF { startbnf(); inbnf=1;} names outdent productions ENDIR
+ /* fiddle - `indent' done by yylex() while processing directive */
+ = { word lhs=NIL,p=$6,subjects,body,startswith=NIL,leftrecs=NIL;
+ ihlist=inbnf=0;
+ nonterminals=UNION(nonterminals,$4);
+ for(;p!=NIL;p=tl[p])
+ if(dval(hd[p])==UNDEF)nonterminals=add1(dlhs(hd[p]),nonterminals);
+ else lhs=add1(dlhs(hd[p]),lhs);
+ nonterminals=setdiff(nonterminals,lhs);
+ if(nonterminals!=NIL)
+ errs=$1,
+ member($4,hd[nonterminals])||findnt(hd[nonterminals]),
+ printf("%sfatal error in grammar, ",echoing?"\n":""),
+ printf("undefined nonterminal%s: ",
+ tl[nonterminals]==NIL?"":"s"),
+ printlist("",nonterminals),
+ acterror(); else
+ { /* compute list of nonterminals admitting empty prodn */
+ eprodnts=NIL;
+ L:for(p=$6;p!=NIL;p=tl[p])
+ if(!member(eprodnts,dlhs(hd[p]))&&eprod(dval(hd[p])))
+ { eprodnts=cons(dlhs(hd[p]),eprodnts); goto L; }
+ /* now compute startswith reln between nonterminals
+ (performing binomial transformation en route)
+ and use to detect unremoved left recursion */
+ for(p=$6;p!=NIL;p=tl[p])
+ if(member(lhs=starts(dval(hd[p])),dlhs(hd[p])))
+ binom(dval(hd[p]),dlhs(hd[p])),
+ startswith=cons(cons(dlhs(hd[p]),starts(dval(hd[p]))),
+ startswith);
+ else startswith=cons(cons(dlhs(hd[p]),lhs),startswith);
+ startswith=tclos(sortrel(startswith));
+ for(;startswith!=NIL;startswith=tl[startswith])
+ if(member(tl[hd[startswith]],hd[hd[startswith]]))
+ leftrecs=add1(hd[hd[startswith]],leftrecs);
+ if(leftrecs!=NIL)
+ errs=getloc(hd[leftrecs],$6),
+ printf("%sfatal error in grammar, ",echoing?"\n":""),
+ printlist("irremovable left recursion: ",leftrecs),
+ acterror();
+ if($4==NIL) /* implied start symbol */
+ $4=cons(dlhs(hd[lastlink($6)]),NIL);
+ fnts=1; /* fnts is flag indicating %bnf in use */
+ if(tl[$4]==NIL) /* only one start symbol */
+ subjects=getfname(hd[$4]),
+ body=ap2(G_CLOSE,str_conv(get_id(hd[$4])),hd[$4]);
+ else
+ { body=subjects=Void;
+ while($4!=NIL)
+ subjects=pair(getfname(hd[$4]),subjects),
+ body=pair(
+ ap2(G_CLOSE,str_conv(get_id(hd[$4])),hd[$4]),
+ body),
+ $4=tl[$4];
+ }
+ declare(subjects,label($1,block($6,body, 0)));
+ }};
+
+setexp:
+ here
+ = { $$=$1;
+ inexplist=1; }; /* hack to fix lex analyser */
+
+bindings:
+ /* empty */
+ = { $$ = NIL; }|
+ '{' bindingseq '}'
+ = { $$ = $2; };
+
+bindingseq:
+ bindingseq binding
+ = { $$ = cons($2,$1); }|
+ binding
+ = { $$ = cons($1,NIL); };
+
+binding:
+ NAME indent '=' exp outdent
+ = { $$ = cons($1,$4); }|
+ typeform indent act1 EQEQ type act2 outdent
+ = { word x=redtvars(ap($1,$5));
+ word arity=0,h=hd[x];
+ while(tag[h]==AP)arity++,h=hd[h];
+ $$ = ap(h,make_typ(arity,0,synonym_t,tl[x]));
+ };
+
+modifiers:
+ /* empty */
+ = { $$ = NIL; }|
+ negmods
+ = { word a,b,c=0;
+ for(a=$1;a!=NIL;a=tl[a])
+ for(b=tl[a];b!=NIL;b=tl[b])
+ { if(hd[hd[a]]==hd[hd[b]])c=hd[hd[a]];
+ if(tl[hd[a]]==tl[hd[b]])c=tl[hd[a]];
+ if(c)break; }
+ if(c)printf(
+ "%ssyntax error: conflicting aliases (\"%s\")\n",
+ echoing?"\n":"",
+ get_id(c)),
+ acterror();
+ };
+
+negmods:
+ negmods negmod
+ = { $$ = cons($2,$1); }|
+ negmod
+ = { $$ = cons($1,NIL); };
+
+negmod:
+ NAME '/' NAME
+ = { $$ = cons($1,$3); }|
+ CNAME '/' CNAME
+ = { $$ = cons($1,$3); }|
+ '-' NAME
+ = { $$ = cons(make_pn(UNDEF),$2); }/*|
+ '-' CNAME */; /* no - cannot suppress constructors selectively */
+
+here:
+ /* empty */
+ = { extern word line_no;
+ lasth = $$ = fileinfo(get_fil(current_file),line_no);
+ /* (script,line_no) for diagnostics */
+ };
+
+act1:
+ /* empty */
+ = { tvarscope=1; };
+
+act2:
+ /* empty */
+ = { tvarscope=0; idsused= NIL; };
+
+ldefs:
+ ldef
+ = { $$ = cons($1,NIL);
+ dval($1) = tries(dlhs($1),cons(dval($1),NIL));
+ if(!SYNERR&&get_ids(dlhs($1))==NIL)
+ errs=hd[hd[tl[dval($1)]]],
+ syntax("illegal lhs for local definition\n");
+ }|
+ ldefs ldef
+ = { if(dlhs($2)==dlhs(hd[$1]) /*&&dval(hd[$1])!=UNDEF*/)
+ { $$ = $1;
+ if(!fallible(hd[tl[dval(hd[$1])]]))
+ errs=hd[dval($2)],
+ printf("%ssyntax error: \
+unreachable case in defn of \"%s\"\n",echoing?"\n":"",get_id(dlhs($2))),
+ acterror();
+ tl[dval(hd[$1])]=cons(dval($2),tl[dval(hd[$1])]); }
+ else if(!SYNERR)
+ { word ns=get_ids(dlhs($2)),hr=hd[dval($2)];
+ if(ns==NIL)
+ errs=hr,
+ syntax("illegal lhs for local definition\n");
+ $$ = cons($2,$1);
+ dval($2)=tries(dlhs($2),cons(dval($2),NIL));
+ while(ns!=NIL&&!SYNERR) /* local nameclash check */
+ { nclashcheck(hd[ns],$1,hr);
+ ns=tl[ns]; }
+ /* potentially quadratic - fix later */
+ }
+ };
+
+ldef:
+ spec
+ = { errs=hd[tl[$1]];
+ syntax("`::' encountered in local defs\n");
+ $$ = cons(nill,NIL); }|
+ typeform here EQEQ
+ = { errs=$2;
+ syntax("`==' encountered in local defs\n");
+ $$ = cons(nill,NIL); }|
+ typeform here COLON2EQ
+ = { errs=$2;
+ syntax("`::=' encountered in local defs\n");
+ $$ = cons(nill,NIL); }|
+ v act2 indent '=' here rhs outdent
+ = { word l = $1, r = $6;
+ word f = head(l);
+ if(tag[f]==ID&&!isconstructor(f)) /* fnform defn */
+ while(tag[l]==AP)r=lambda(tl[l],r),l=hd[l];
+ r = label($5,r); /* to help locate type errors */
+ $$ = defn(l,undef_t,r); };
+
+vlist:
+ v
+ = { $$ = cons($1,NIL); }|
+ vlist ',' v /* left recursive so as not to eat YACC stack */
+ = { $$ = cons($3,$1); }; /* reverse order, NB */
+
+v:
+ v1 |
+ v1 ':' v
+ = { $$ = cons($1,$3); };
+
+v1:
+ v1 '+' CONST /* n+k pattern */
+ = { if(!isnat($3))
+ syntax("inappropriate use of \"+\" in pattern\n");
+ $$ = ap2(PLUS,$3,$1); }|
+ '-' CONST
+ = { /* if(tag[$2]==DOUBLE)
+ $$ = cons(CONST,sto_dbl(-get_dbl($2))); else */
+ if(tag[$2]==INT)
+ $$ = cons(CONST,bignegate($2)); else
+ syntax("inappropriate use of \"-\" in pattern\n"); }|
+ v2 INFIXNAME v1
+ = { $$ = ap2($2,$1,$3); }|
+ v2 INFIXCNAME v1
+ = { $$ = ap2($2,$1,$3); }|
+ v2;
+
+v2:
+ v3 |
+ v2 v3
+ = { $$ = ap(hd[$1]==CONST&&tag[tl[$1]]==ID?tl[$1]:$1,$2); };
+ /* repeated name apparatus may have wrapped CONST around leading id
+ - not wanted */
+
+v3:
+ NAME
+ = { if(sreds&&member(gvars,$1))syntax("illegal use of $num symbol\n");
+ /* cannot use grammar variable in a binding position */
+ if(memb(idsused,$1))$$ = cons(CONST,$1);
+ /* picks up repeated names in a template */
+ else idsused= cons($1,idsused); } |
+ CNAME |
+ CONST
+ = { if(tag[$1]==DOUBLE)
+ syntax("use of floating point literal in pattern\n");
+ $$ = cons(CONST,$1); }|
+ '[' ']'
+ = { $$ = nill; }|
+ '[' vlist ']'
+ = { word x=$2,y=nill;
+ while(x!=NIL)y = cons(hd[x],y), x = tl[x];
+ $$ = y; }|
+ '(' ')'
+ = { $$ = Void; }|
+ '(' v ')'
+ = { $$ = $2; }|
+ '(' v ',' vlist ')'
+ = { if(tl[$4]==NIL)$$=pair($2,hd[$4]);
+ else { $$=pair(hd[tl[$4]],hd[$4]);
+ $4=tl[tl[$4]];
+ while($4!=NIL)$$=tcons(hd[$4],$$),$4=tl[$4];
+ $$ = tcons($2,$$); }
+ /* representation of the tuple (a1,...,an) is
+ tcons(a1,tcons(a2,...pair(a(n-1),an))) */
+ };
+
+type:
+ type1 |
+ type ARROW type
+ = { $$ = ap2(arrow_t,$1,$3); };
+
+type1:
+ type2 INFIXNAME type1
+ = { $$ = ap2($2,$1,$3); }|
+ type2;
+
+type2:
+ /* type2 argtype /* too permissive - fix later */
+ /* = { $$ = ap($1,$2); }| */
+ tap|
+ argtype;
+
+tap:
+ NAME argtype
+ = { $$ = ap($1,$2); }|
+ tap argtype
+ = { $$ = ap($1,$2); };
+
+argtype:
+ NAME
+ = { $$ = transtypeid($1); }|
+ /* necessary while prelude not meta_tchecked (for prelude)*/
+ typevar
+ = { if(tvarscope&&!memb(idsused,$1))
+ printf("%ssyntax error: unbound type variable ",echoing?"\n":""),
+ out_type($1),putchar('\n'),acterror();
+ $$ = $1; }|
+ '(' typelist ')'
+ = { $$ = $2; }|
+ '[' type ']' /* at release one was `typelist' */
+ = { $$ = ap(list_t,$2); }|
+ '[' type ',' typel ']'
+ = { syntax(
+ "tuple-type with missing parentheses (obsolete syntax)\n"); };
+
+typelist:
+ /* empty */
+ = { $$ = void_t; }| /* voidtype */
+ type |
+ type ',' typel
+ = { word x=$3,y=void_t;
+ while(x!=NIL)y = ap2(comma_t,hd[x],y), x = tl[x];
+ $$ = ap2(comma_t,$1,y); };
+
+typel:
+ type
+ = { $$ = cons($1,NIL); }|
+ typel ',' type /* left recursive so as not to eat YACC stack */
+ = { $$ = cons($3,$1); };
+
+parts: /* returned in reverse order */
+ parts NAME
+ = { $$ = add1($2,$1); }|
+ parts '-' NAME
+ = { $$ = $1; embargoes=add1($3,embargoes); }|
+ parts PATHNAME
+ = { $$ = $1; }| /*the pathnames are placed on exportfiles in yylex*/
+ parts '+'
+ = { $$ = $1;
+ exportfiles=cons(PLUS,exportfiles); }|
+ NAME
+ = { $$ = add1($1,NIL); }|
+ '-' NAME
+ = { $$ = NIL; embargoes=add1($2,embargoes); }|
+ PATHNAME
+ = { $$ = NIL; }|
+ '+'
+ = { $$ = NIL;
+ exportfiles=cons(PLUS,exportfiles); };
+
+specs: /* returns a list of cons(id,cons(here,type))
+ in reverse order of appearance */
+ specs spec
+ = { word x=$1,h=hd[$2],t=tl[$2];
+ while(h!=NIL)x=cons(cons(hd[h],t),x),h=tl[h];
+ $$ = x; }|
+ spec
+ = { word x=NIL,h=hd[$1],t=tl[$1];
+ while(h!=NIL)x=cons(cons(hd[h],t),x),h=tl[h];
+ $$ = x; };
+
+spec:
+ typeforms indent here COLONCOLON ttype outdent
+ = { $$ = cons($1,cons($3,$5)); };
+ /* hack: `typeforms' includes `namelist' */
+
+lspecs: /* returns a list of cons(id,cons(here,type))
+ in reverse order of appearance */
+ lspecs lspec
+ = { word x=$1,h=hd[$2],t=tl[$2];
+ while(h!=NIL)x=cons(cons(hd[h],t),x),h=tl[h];
+ $$ = x; }|
+ lspec
+ = { word x=NIL,h=hd[$1],t=tl[$1];
+ while(h!=NIL)x=cons(cons(hd[h],t),x),h=tl[h];
+ $$ = x; };
+
+lspec:
+ namelist indent here {inbnf=0;} COLONCOLON type outdent
+ = { $$ = cons($1,cons($3,$6)); };
+
+namelist:
+ NAME ',' namelist
+ = { $$ = cons($1,$3); }|
+ NAME
+ = { $$ = cons($1,NIL); };
+
+typeforms:
+ typeforms ',' typeform act2
+ = { $$ = cons($3,$1); }|
+ typeform act2
+ = { $$ = cons($1,NIL); };
+
+typeform:
+ CNAME typevars
+ = { syntax("upper case identifier out of context\n"); }|
+ NAME typevars /* warning if typevar is repeated */
+ = { $$ = $1;
+ idsused=$2;
+ while($2!=NIL)
+ $$ = ap($$,hd[$2]),$2 = tl[$2];
+ }|
+ typevar INFIXNAME typevar
+ = { if(eqtvar($1,$3))
+ syntax("repeated type variable in typeform\n");
+ idsused=cons($1,cons($3,NIL));
+ $$ = ap2($2,$1,$3); }|
+ typevar INFIXCNAME typevar
+ = { syntax("upper case identifier cannot be used as typename\n"); };
+
+ttype:
+ type|
+ TYPE
+ = { $$ = type_t; };
+
+typevar:
+ '*'
+ = { $$ = mktvar(1); }|
+ TYPEVAR;
+
+typevars:
+ /* empty */
+ = { $$ = NIL; }|
+ typevar typevars
+ = { if(memb($2,$1))
+ syntax("repeated type variable on lhs of type def\n");
+ $$ = cons($1,$2); };
+
+construction:
+ constructs
+ = { extern word SGC; /* keeps track of sui-generis constructors */
+ if( tl[$1]==NIL && tag[hd[$1]]!=ID )
+ /* 2nd conjunct excludes singularity types */
+ SGC=cons(head(hd[$1]),SGC);
+ };
+
+constructs:
+ construct
+ = { $$ = cons($1,NIL); }|
+ constructs '|' construct
+ = { $$ = cons($3,$1); };
+
+construct:
+ field here INFIXCNAME field
+ = { $$ = ap2($3,$1,$4);
+ id_who($3)=$2; }|
+ construct1;
+
+construct1:
+ '(' construct ')'
+ = { $$ = $2; }|
+ construct1 field1
+ = { $$ = ap($1,$2); }|
+ here CNAME
+ = { $$ = $2;
+ id_who($2)=$1; };
+
+field:
+ type|
+ argtype '!'
+ = { $$ = ap(strict_t,$1); };
+
+field1:
+ argtype '!'
+ = { $$ = ap(strict_t,$1); }|
+ argtype;
+
+names: /* used twice - for bnf list, and for inherited attr list */
+ /* empty */
+ = { $$ = NIL; }|
+ names NAME
+ = { if(member($1,$2))
+ printf("%ssyntax error: repeated identifier \"%s\" in %s list\n",
+ echoing?"\n":"",get_id($2),inbnf?"bnf":"attribute"),
+ acterror();
+ $$ = inbnf?add1($2,$1):cons($2,$1);
+ };
+
+productions:
+ lspec
+ = { word h=reverse(hd[$1]),hr=hd[tl[$1]],t=tl[tl[$1]];
+ inbnf=1;
+ $$=NIL;
+ while(h!=NIL&&!SYNERR)
+ ntspecmap=cons(cons(hd[h],hr),ntspecmap),
+ $$=add_prod(defn(hd[h],t,UNDEF),$$,hr),
+ h=tl[h];
+ }|
+ production
+ = { $$ = cons($1,NIL); }|
+ productions lspec
+ = { word h=reverse(hd[$2]),hr=hd[tl[$2]],t=tl[tl[$2]];
+ inbnf=1;
+ $$=$1;
+ while(h!=NIL&&!SYNERR)
+ ntspecmap=cons(cons(hd[h],hr),ntspecmap),
+ $$=add_prod(defn(hd[h],t,UNDEF),$$,hr),
+ h=tl[h];
+ }|
+ productions production
+ = { $$ = add_prod($2,$1,hd[dval($2)]); };
+
+production:
+ NAME params ':' indent grhs outdent
+ /* found by experiment that indent must follow ':' here */
+ = { $$ = defn($1,undef_t,$5); };
+
+params: /* places inherited attributes, if any, on ihlist */
+ /* empty */
+ = { ihlist=0; }|
+ { inbnf=0; } '(' names ')'
+ = { inbnf=1;
+ if($3==NIL)syntax("unexpected token ')'\n");
+ ihlist=$3; }
+
+grhs:
+ here phrase
+ = { $$ = label($1,$2); };
+
+phrase:
+ error_term
+ = { $$ = ap2(G_ERROR,G_ZERO,$1); }|
+ phrase1
+ = { $$=hd[$1], $1=tl[$1];
+ while($1!=NIL)
+ $$=label(hd[$1],$$),$1=tl[$1],
+ $$=ap2(G_ALT,hd[$1],$$),$1=tl[$1];
+ }|
+ phrase1 '|' error_term
+ = { $$=hd[$1], $1=tl[$1];
+ while($1!=NIL)
+ $$=label(hd[$1],$$),$1=tl[$1],
+ $$=ap2(G_ALT,hd[$1],$$),$1=tl[$1];
+ $$ = ap2(G_ERROR,$$,$3); };
+ /* we right rotate G_ALT's to facilitate left factoring (see trans) */
+
+phrase1:
+ term
+ = { $$=cons($1,NIL); }|
+ phrase1 '|' here term
+ = { $$ = cons($4,cons($3,$1)); };
+
+term:
+ count_factors
+ = { word n=0,f=$1,rule=Void;
+ /* default value of a production is () */
+ /* rule=mkgvar(sreds); /* formerly last symbol */
+ if(f!=NIL&&hd[f]==G_END)sreds++;
+ if(ihlist)rule=ih_abstr(rule);
+ while(n<sreds)rule=lambda(mkgvar(++n),rule);
+ sreds=0;
+ rule=ap(G_RULE,rule);
+ while(f!=NIL)rule=ap2(G_SEQ,hd[f],rule),f=tl[f];
+ $$ = rule; }|
+ count_factors {inbnf=2;} indent '=' here rhs outdent
+ = { if($1!=NIL&&hd[$1]==G_END)sreds++;
+ if(sreds==1&&can_elide($6))
+ inbnf=1,sreds=0,$$=hd[$1]; /* optimisation */
+ else
+ { word f=$1,rule=label($5,$6),n=0;
+ inbnf=1;
+ if(ihlist)rule=ih_abstr(rule);
+ while(n<sreds)rule=lambda(mkgvar(++n),rule);
+ sreds=0;
+ rule=ap(G_RULE,rule);
+ while(f!=NIL)rule=ap2(G_SEQ,hd[f],rule),f=tl[f];
+ $$ = rule; }
+ };
+
+error_term:
+ ERRORSY
+ = { word rule = ap(K,Void); /* default value of a production is () */
+ if(ihlist)rule=ih_abstr(rule);
+ $$ = rule; }|
+ ERRORSY { inbnf=2,sreds=2; } indent '=' here rhs outdent
+ = { word rule = label($5,$6);
+ if(ihlist)rule=ih_abstr(rule);
+ $$ = lambda(pair(mkgvar(1),mkgvar(2)),rule);
+ inbnf=1,sreds=0; };
+
+count_factors:
+ EMPTYSY
+ = { sreds=0; $$=NIL; }|
+ EMPTYSY factors
+ = { syntax("unexpected token after empty\n");
+ sreds=0; $$=NIL; }|
+ { obrct=0; } factors
+ = { word f=$2;
+ if(obrct)
+ syntax(obrct>0?"unmatched { in grammar rule\n":
+ "unmatched } in grammar rule\n");
+ for(sreds=0;f!=NIL;f=tl[f])sreds++;
+ if(hd[$2]==G_END)sreds--;
+ $$ = $2; };
+
+factors:
+ factor
+ = { $$ = cons($1,NIL); }|
+ factors factor
+ = { if(hd[$1]==G_END)
+ syntax("unexpected token after end\n");
+ $$ = cons($2,$1); };
+
+factor:
+ unit|
+ '{' unit '}'
+ = { $$ = ap(outdent_fn,ap2(indent_fn,getcol_fn(),$2)); }|
+ '{' unit
+ = { obrct++;
+ $$ = ap2(indent_fn,getcol_fn(),$2); }|
+ unit '}'
+ = { if(--obrct<0)syntax("unmatched `}' in grammar rule\n");
+ $$ = ap(outdent_fn,$1); } ;
+
+unit:
+ symbol|
+ symbol '*'
+ = { $$ = ap(G_STAR,$1); }|
+ symbol '+'
+ = { $$ = ap2(G_SEQ,$1,ap2(G_SEQ,ap(G_STAR,$1),ap(G_RULE,ap(C,P)))); }|
+ symbol '?'
+ = { $$ = ap(G_OPT,$1); };
+
+symbol:
+ NAME
+ = { extern word NEW;
+ nonterminals=newadd1($1,nonterminals);
+ if(NEW)ntmap=cons(cons($1,lasth),ntmap); }|
+ ENDSY
+ = { $$ = G_END; }|
+ CONST
+ = { if(!isstring($1))
+ printf("%ssyntax error: illegal terminal ",echoing?"\n":""),
+ out(stdout,$1),printf(" (should be string-const)\n"),
+ acterror();
+ $$ = ap(G_SYMB,$1); }|
+ '^'
+ = { $$=G_STATE; }|
+ {inbnf=0;} '[' exp {inbnf=1;} ']'
+ = { $$ = ap(G_SUCHTHAT,$3); }|
+ '-'
+ = { $$ = G_ANY; };
+
+%%
+/* end of MIRANDA RULES */
+
diff --git a/new/steer.c b/new/steer.c
new file mode 100644
index 0000000..27c238d
--- /dev/null
+++ b/new/steer.c
@@ -0,0 +1,2208 @@
+/* MIRANDA STEER */
+/* initialisation routines and assorted routines for I/O etc */
+
+/**************************************************************************
+ * Copyright (C) Research Software Limited 1985-90. All rights reserved. *
+ * The Miranda system is distributed as free software under the terms in *
+ * the file "COPYING" which is included in the distribution. *
+ *------------------------------------------------------------------------*/
+
+/* this stuff is to get the time-last-modified of files */
+#include <sys/types.h>
+#include <sys/stat.h>
+/* #include <sys/wait.h> /* seems not needed, oct 05 */
+struct stat buf; /* see man(2) stat - gets file status */
+
+#include "data.h"
+#include "lex.h"
+#include <float.h>
+word nill,Void;
+word main_id; /* change to magic scripts 19.11.2013 */
+word message,standardout;
+word diagonalise,concat,indent_fn,outdent_fn,listdiff_fn;
+word shownum1,showbool,showchar,showlist,showstring,showparen,showpair,
+ showvoid,showfunction,showabstract,showwhat;
+
+char PRELUDE[pnlim+10],STDENV[pnlim+9];
+ /* if anyone complains, elasticate these buffers! */
+
+#define DFLTSPACE 2500000
+#define DFLTDICSPACE 100000
+/* default values for size of heap, dictionary */
+word SPACELIMIT=DFLTSPACE,DICSPACE=DFLTDICSPACE;
+
+#ifdef CYGWIN
+#define EDITOR "joe +!"
+#else
+#define EDITOR "vi +!"
+#endif
+/* The name of whatever is locally considered to be the default editor - the
+ user will be able to override this using the `/editor' command.
+ It is also overriden by shell/environment variable EDITOR if present */
+
+extern FILE *s_out;
+word UTF8=0, UTF8OUT=0;
+extern char *vdate, *host;
+extern word version, ND;
+
+char *mkabsolute(char *), *strvers(word);
+void fpe_error(void);
+
+char *editor=NULL;
+word okprel=0; /* set to 1 when prelude loaded */
+word nostdenv=0; /* if set to 1 mira does not load stdenv at startup */
+/* to allow a NOSTDENV directive _in_the_script_ we would need to
+ (i) replace isltmess() test in rules by eg is this a list of thing,
+ where thing is algebraic type originally defined in STDENV
+ (ii) arrange to pick up <stdenv> when current script not loaded
+ not implemented */
+word baded=0; /* see fixeditor() */
+char *miralib=NULL;
+char *mirahdr,*lmirahdr;
+char *promptstr="Miranda ";
+char *obsuffix="x";
+FILE *s_in=NULL;
+word commandmode=0; /* true only when reading command-level expressions */
+word atobject=0,atgc=0,atcount=0,debug=0;
+word magic=0; /* set to 1 means script will start with UNIX magic string */
+word making=0; /* set only for mira -make */
+word mkexports=0; /* set only for mira -exports */
+word mksources=0; /* set only for mira -sources */
+word make_status=0; /* exit status of -make */
+word compiling=1;
+/* there are two types of MIRANDA process - compiling (the main process) and
+subsidiary processes launched for each evaluation - the above flag tells
+us which kind of process we are in */
+word ideep=0; /* depth of %include we are at, see mkincludes() */
+word SYNERR=0;
+word initialising=1;
+word primenv=NIL;
+char *current_script;
+word lastexp=UNDEF; /* value of `$$' */
+word echoing=0,listing=0,verbosity;
+word strictif=1,rechecking=0;
+word errline=0; /* records position of last error, for editor */
+word errs=0; /* secondary error location, in inserted script, if relevant */
+word *cstack;
+extern word c;
+extern char *dicp,*dicq;
+char linebuf[BUFSIZE]; /* used for assorted purposes */
+ /* NB cannot share with linebuf in lex.c, or !! goes wrong */
+static char ebuf[pnlim];
+word col;
+char home_rc[pnlim+8];
+char lib_rc[pnlim+8];
+char *rc_error=NULL;
+#define badval(x) (x<1||x>478000000)
+
+#include <setjmp.h> /* for longjmp() - see man (3) setjmp */
+jmp_buf env;
+
+#ifdef sparc8
+#include <ieeefp.h>
+fp_except commonmask = FP_X_INV|FP_X_OFL|FP_X_DZ; /* invalid|ovflo|divzero */
+#endif
+
+main(argc,argv) /* system initialisation, followed by call to YACC */
+word argc;
+char *argv[];
+{ word manonly=0;
+ char *home, *prs;
+ word okhome_rc; /* flags valid HOME/.mirarc file present */
+ char *argv0=argv[0];
+ char *initscript;
+ word badlib=0;
+ extern word ARGC; extern char **ARGV;
+ extern word newtyps,algshfns;
+ char *progname=rindex(argv[0],'/');
+ cstack= &manonly;
+/* used to indicate the base of the C stack for garbage collection purposes */
+ verbosity=isatty(0);
+/*if(isatty(1))*/ setbuf(stdout,NULL); /* for unbuffered tty output */
+ if(home=getenv("HOME"))
+ { strcpy(home_rc,home);
+ if(strcmp(home_rc,"/")==0)home_rc[0]=0; /* root is special case */
+ strcat(home_rc,"/.mirarc");
+ okhome_rc=rc_read(home_rc); }
+/*setup policy:
+ if valid HOME/.mirarc found look no further, otherwise try
+ <miralib>/.mirarc
+ Complaints - if any .mirarc contained bad data, `announce' complains about
+ the last such looked at. */
+ UTF8OUT=UTF8=utf8test();
+ while(argc>1&&argv[1][0]=='-') /* strip off flags */
+ { if(strcmp(argv[1],"-stdenv")==0)nostdenv=1; else
+ if(strcmp(argv[1],"-count")==0)atcount=1; else
+ if(strcmp(argv[1],"-list")==0)listing=1; else
+ if(strcmp(argv[1],"-nolist")==0)listing=0; else
+ if(strcmp(argv[1],"-nostrictif")==0)strictif=0; else
+ if(strcmp(argv[1],"-gc")==0)atgc=1; else
+ if(strcmp(argv[1],"-object")==0)atobject=1; else
+ if(strcmp(argv[1],"-lib")==0)
+ { argc--,argv++;
+ if(argc==1)missparam("lib"); else miralib=argv[1];
+ } else
+ if(strcmp(argv[1],"-dic")==0)
+ { argc--,argv++;
+ if(argc==1)missparam("dic"); else
+ if(sscanf(argv[1],"%d",&DICSPACE)!=1||badval(DICSPACE))
+ fprintf(stderr,"mira: bad value after flag \"-dic\"\n"),exit(1);
+ } else
+ if(strcmp(argv[1],"-heap")==0)
+ { argc--,argv++;
+ if(argc==1)missparam("heap"); else
+ if(sscanf(argv[1],"%d",&SPACELIMIT)!=1||badval(SPACELIMIT))
+ fprintf(stderr,"mira: bad value after flag \"-heap\"\n"),exit(1);
+ } else
+ if(strcmp(argv[1],"-editor")==0)
+ { argc--,argv++;
+ if(argc==1)missparam("editor");
+ else editor=argv[1],fixeditor();
+ } else
+ if(strcmp(argv[1],"-hush")==0)verbosity=0; else
+ if(strcmp(argv[1],"-nohush")==0)verbosity=1; else
+ if(strcmp(argv[1],"-exp")==0||strcmp(argv[1],"-log")==0)
+ fprintf(stderr,"mira: obsolete flag \"%s\"\n"
+ "use \"-exec\" or \"-exec2\", see manual\n",
+ argv[1]),exit(1); else
+ if(strcmp(argv[1],"-exec")==0) /* replaces -exp 26.11.2019 */
+ ARGC=argc-2,ARGV=argv+2,magic=1,verbosity=0; else
+ if(strcmp(argv[1],"-exec2")==0) /* version of -exec for debugging CGI scripts */
+ { if(argc<=2)fprintf(stderr,"incorrect use of -exec2 flag, missing filename\n"),exit(1);
+ char *logfilname, *p=strrchr(argv[2],'/');
+ FILE *fil=NULL;
+ if(!p)p=argv[2]; /* p now holds last component of prog name */
+ if(logfilname=malloc((strlen(p)+9)))
+ sprintf(logfilname,"miralog/%s",p),
+ fil=fopen(logfilname,"a");
+ else mallocfail("logfile name");
+ /* process requires write permission on local directory "miralog" */
+ if(fil)dup2(fileno(fil),2); /* redirect stderr to log file */
+ else fprintf(stderr,"could not open %s\n",logfilname);
+ ARGC=argc-2,ARGV=argv+2,magic=1,verbosity=0; } else
+ if(strcmp(argv[1],"-man")==0){ manonly=1; break; } else
+ if(strcmp(argv[1],"-version")==0)v_info(0),exit(0); else
+ if(strcmp(argv[1],"-V")==0)v_info(1),exit(0); else
+ if(strcmp(argv[1],"-make")==0) making=1,verbosity=0; else
+ if(strcmp(argv[1],"-exports")==0) making=mkexports=1,verbosity=0; else
+ if(strcmp(argv[1],"-sources")==0) making=mksources=1,verbosity=0; else
+ if(strcmp(argv[1],"-UTF-8")==0) UTF8=1; else
+ if(strcmp(argv[1],"-noUTF-8")==0) UTF8=0; else
+ fprintf(stderr,"mira: unknown flag \"%s\"\n",argv[1]),exit(1);
+ argc--,argv++; }
+ if(argc>2&&!magic&&!making)fprintf(stderr,"mira: too many args\n"),exit(1);
+ if(!miralib) /* no -lib flag */
+ { char *m;
+ /* note search order */
+ if((m=getenv("MIRALIB")))miralib=m; else
+ if(checkversion(m="/usr/lib/miralib"))miralib=m; else
+ if(checkversion(m="/usr/local/lib/miralib"))miralib=m; else
+ if(checkversion(m="miralib"))miralib=m; else
+ badlib=1;
+ }
+ if(badlib)
+ { fprintf(stderr,"fatal error: miralib version %s not found\n",
+ strvers(version));
+ libfails();
+ exit(1);
+ }
+ if(!okhome_rc)
+ { if(rc_error==lib_rc)rc_error=NULL;
+ (void)strcpy(lib_rc,miralib);
+ (void)strcat(lib_rc,"/.mirarc");
+ rc_read(lib_rc); }
+ if(editor==NULL) /* .mirarc was absent or unreadable */
+ { editor=getenv("EDITOR");
+ if(editor==NULL)editor=EDITOR;
+ else strcpy(ebuf,editor),editor=ebuf,fixeditor(); }
+ if(prs=getenv("MIRAPROMPT"))promptstr=prs;
+ if(getenv("RECHECKMIRA")&&!rechecking)rechecking=1;
+ if(getenv("NOSTRICTIF"))strictif=0;
+ setupdic(); /* used by mkabsolute */
+ s_in=stdin;
+ s_out=stdout;
+ miralib=mkabsolute(miralib); /* protection against "/cd" */
+ if(manonly)manaction(),exit(0);
+ (void)strcpy(PRELUDE,miralib); (void)strcat(PRELUDE,"/prelude");
+ /* convention - change spelling of "prelude" at each release */
+ (void)strcpy(STDENV,miralib);
+ (void)strcat(STDENV,"/stdenv.m");
+ mira_setup();
+ if(verbosity)announce();
+ files=NIL;
+ undump(PRELUDE),okprel=1;
+ mkprivate(fil_defs(hd[files]));
+ files=NIL; /* don't wish unload() to unsetids on prelude */
+ if(!nostdenv)
+ { undump(STDENV);
+ while(files!=NIL) /* stdenv may have %include structure */
+ primenv=alfasort(append1(primenv,fil_defs(hd[files]))),
+ files=tl[files];
+ primenv=alfasort(primenv);
+ newtyps=files=NIL; /* don't wish unload() to unsetids */ }
+ if(!magic)rc_write();
+ echoing = verbosity&listing;
+ initialising=0;
+ if(mkexports)
+ { /* making=1, to say if recompiling, also to undump as for %include */
+ word f,argcount=argc-1;
+ extern word exports,freeids;
+ char *s;
+ setjmp(env); /* will return here on blankerr (via reset) */
+ while(--argc) /* where do error messages go?? */
+ { word x=NIL;
+ s=addextn(1,*++argv);
+ if(s==dicp)keep(dicp);
+ undump(s); /* bug, recompile messages goto stdout - FIX LATER */
+ if(files==NIL||ND!=NIL)continue;
+ if(argcount!=1)printf("%s\n",s);
+ if(exports!=NIL)x=exports;
+ /* true (if ever) only if just recompiled */
+ else for(f=files;f!=NIL;f=tl[f])x=append1(fil_defs(hd[f]),x);
+ /* method very clumsy, because exports not saved in dump */
+ if(freeids!=NIL)
+ { word f=freeids;
+ while(f!=NIL)
+ { word n=findid((char *)hd[hd[tl[hd[f]]]]);
+ id_type(n)=tl[tl[hd[f]]];
+ id_val(n)=the_val(hd[hd[f]]);
+ hd[f]=n;
+ f=tl[f]; }
+ f=freeids=typesfirst(freeids);
+ printf("\t%%free {\n");
+ while(f!=NIL)
+ putchar('\t'),
+ report_type(hd[f]),
+ putchar('\n'),
+ f=tl[f];
+ printf("\t}\n"); }
+ for(x=typesfirst(alfasort(x));x!=NIL;x=tl[x])
+ { putchar('\t');
+ report_type(hd[x]);
+ putchar('\n'); } }
+ exit(0); }
+ if(mksources){ extern word oldfiles;
+ char *s;
+ word f,x=NIL;
+ setjmp(env); /* will return here on blankerr (via reset) */
+ while(--argc)
+ if(stat((s=addextn(1,*++argv)),&buf)==0)
+ { if(s==dicp)keep(dicp);
+ undump(s);
+ for(f=files==NIL?oldfiles:files;f!=NIL;f=tl[f])
+ if(!member(x,(word)get_fil(hd[f])))
+ x=cons((word)get_fil(hd[f]),x),
+ printf("%s\n",get_fil(hd[f]));
+ }
+ exit(0); }
+ if(making){ extern word oldfiles;
+ char *s;
+ setjmp(env); /* will return here on blankerr (via reset) */
+ while(--argc) /* where do error messages go?? */
+ { s=addextn(1,*++argv);
+ if(s==dicp)keep(dicp);
+ undump(s);
+ if(ND!=NIL||files==NIL&&oldfiles!=NIL)
+ { if(make_status==1)make_status=0;
+ make_status=strcons(s,make_status); }
+ /* keep list of source files with error-dumps */
+ }
+ if(tag[make_status]==STRCONS)
+ { word h=0,maxw=0,w,n;
+ printf("errors or undefined names found in:-\n");
+ while(make_status) /* reverse to get original order */
+ { h=strcons(hd[make_status],h);
+ w=strlen((char *)hd[h]);
+ if(w>maxw)maxw=w;
+ make_status=tl[make_status]; }
+ maxw++;n=78/maxw;w=0;
+ while(h)
+ printf("%*s%s",maxw,(char *)hd[h],(++w%n)?"":"\n"),
+ h=tl[h];
+ if(w%n)printf("\n");
+ make_status=1; }
+ exit(make_status); }
+ initscript= argc==1?"script.m":magic?argv[1]:addextn(1,argv[1]);
+ if(initscript==dicp)keep(dicp);
+#if sparc8
+ fpsetmask(commonmask);
+#elif defined sparc
+ ieee_handler("set","common",(sighandler)fpe_error);
+#endif
+#if !defined sparc | sparc8
+ (void)signal(SIGFPE,(sighandler)fpe_error); /* catch arithmetic overflow */
+#endif
+ (void)signal(SIGTERM,(sighandler)exit); /* flush buffers if killed */
+ commandloop(initscript);
+ /* parameter is file given as argument */
+}
+
+word vstack[4]; /* record of miralib versions looked at */
+char *mstack[4]; /* and where found */
+word mvp=0;
+
+checkversion(m)
+/* returns 1 iff m is directory with .version containing our version number */
+char *m;
+{ word v1,read=0,r=0;
+ FILE *f=fopen(strcat(strcpy(linebuf,m),"/.version"),"r");
+ if(f&&fscanf(f,"%u",&v1)==1)r= v1==version, read=1;
+ if(f)fclose(f);
+ if(read&&!r)mstack[mvp]=m,vstack[mvp++]=v1;
+ return r;
+}
+
+libfails()
+{ word i=0;
+ fprintf(stderr,"found");
+ for(;i<mvp;i++)fprintf(stderr,"\tversion %s at: %s\n",
+ strvers(vstack[i]),mstack[i]);
+}
+
+char *strvers(v)
+{ static char vbuf[12];
+ if(v<0||v>999999)return "\?\?\?";
+ snprintf(vbuf,12,"%.3f",v/1000.0);
+ return vbuf;
+}
+
+char *mkabsolute(m) /* make sure m is an absolute pathname */
+char *m;
+{ if(m[0]=='/')return(m);
+ if(!getcwd(dicp,pnlim))fprintf(stderr,"panic: cwd too long\n"),exit(1);
+ (void)strcat(dicp,"/");
+ (void)strcat(dicp,m);
+ m=dicp;
+ dicp=dicq+=strlen(dicp)+1;
+ dic_check();
+ return(m);
+}
+
+missparam(s)
+char *s;
+{ fprintf(stderr,"mira: missing param after flag \"-%s\"\n",s);
+ exit(1); }
+
+word oldversion=0;
+#define colmax 400
+#define spaces(s) for(j=s;j>0;j--)putchar(' ')
+
+announce()
+{ extern char *vdate;
+ word w,j;
+/*clrscr(); /* clear screen on start up */
+ w=(twidth()-50)/2;
+ printf("\n\n");
+ spaces(w); printf(" T h e M i r a n d a S y s t e m\n\n");
+ spaces(w+5-strlen(vdate)/2);
+ printf(" version %s last revised %s\n\n",strvers(version),vdate);
+ spaces(w); printf("Copyright Research Software Ltd 1985-2019\n\n");
+ spaces(w); printf(" World Wide Web: http://miranda.org.uk\n\n\n");
+ if(SPACELIMIT!=DFLTSPACE)
+ printf("(%d cells)\n",SPACELIMIT);
+ if(!strictif)printf("(-nostrictif : deprecated!)\n");
+/*printf("\t\t\t\t%dbit platform\n",__WORDSIZE); /* */
+ if(oldversion<1999) /* pre release two */
+ printf("\
+WARNING:\n\
+a new release of Miranda has been installed since you last used\n\
+the system - please read the `CHANGES' section of the /man pages !!!\n\n");
+ else
+ if(version>oldversion)
+ printf("a new version of Miranda has been installed since you last\n"),
+ printf("used the system - see under `CHANGES' in the /man pages\n\n");
+ if(version<oldversion)
+ printf("warning - this is an older version of Miranda than the one\n"),
+ printf("you last used on this machine!!\n\n");
+ if(rc_error)
+ printf("warning: \"%s\" contained bad data (ignored)\n",rc_error);
+}
+
+
+rc_read(rcfile) /* get settings of system parameters from setup file */
+char *rcfile;
+{ FILE *in;
+ char z[20];
+ word h,d,v,s,r=0;
+ oldversion=version; /* default assumption */
+ in=fopen(rcfile,"r");
+ if(in==NULL||fscanf(in,"%19s",z)!=1)
+ return(0); /* file not present, or not readable */
+ if(strncmp(z,"hdve",4)==0 /* current .mirarc format */
+ ||strcmp(z,"lhdve")==0) /* alternative format used at release one */
+ { char *z1 = &z[3];
+ if(z[0]=='l')listing=1,z1++;
+ while(*++z1)if(*z1=='l')listing=1; else
+ if(*z1=='s') /* ignore */; else
+ if(*z1=='r')rechecking=2; else
+ rc_error=rcfile;
+ if(fscanf(in,"%d%d%d%*c",&h,&d,&v)!=3||!getln(in,pnlim-1,ebuf)
+ ||badval(h)||badval(d)||badval(v))rc_error=rcfile;
+ else editor=ebuf,SPACELIMIT=h,DICSPACE=d,r=1,
+ oldversion=v; } else
+ if(strcmp(z,"ehdsv")==0) /* versions before 550 */
+ { if(fscanf(in,"%19s%d%d%d%d",ebuf,&h,&d,&s,&v)!=5
+ ||badval(h)||badval(d)||badval(v))rc_error=rcfile;
+ else editor=ebuf,SPACELIMIT=h,DICSPACE=d,r=1,
+ oldversion=v; } else
+ if(strcmp(z,"ehds")==0) /* versions before 326, "s" was stacklimit (ignore) */
+ { if(fscanf(in,"%s%d%d%d",ebuf,&h,&d,&s)!=4
+ ||badval(h)||badval(d))rc_error=rcfile;
+ else editor=ebuf,SPACELIMIT=h,DICSPACE=d,r=1,
+ oldversion=1; }
+ else rc_error=rcfile; /* unrecognised format */
+ if(editor)fixeditor();
+ fclose(in);
+ return(r);
+}
+
+fixeditor()
+{ if(strcmp(editor,"vi")==0)editor="vi +!"; else
+ if(strcmp(editor,"pico")==0)editor="pico +!"; else
+ if(strcmp(editor,"nano")==0)editor="nano +!"; else
+ if(strcmp(editor,"joe")==0)editor="joe +!"; else
+ if(strcmp(editor,"jpico")==0)editor="jpico +!"; else
+ if(strcmp(editor,"vim")==0)editor="vim +!"; else
+ if(strcmp(editor,"gvim")==0)editor="gvim +! % &"; else
+ if(strcmp(editor,"emacs")==0)editor="emacs +! % &";
+ else { char *p=rindex(editor,'/');
+ if(p==0)p=editor; else p++;
+ if(strcmp(p,"vi")==0)strcat(p," +!");
+ }
+ if(rindex(editor,'&'))rechecking=2;
+ listing=badeditor();
+}
+
+badeditor() /* does editor know how to open file at line? */
+{ char *p=index(editor,'!');
+ while(p&&p[-1]=='\\')p=index(p+1,'!');
+ return (baded = !p);
+}
+
+getln(in,n,s) /* reads line (<=n chars) from in into s - returns 1 if ok */
+FILE *in; /* the newline is discarded, and the result '\0' terminated */
+word n;
+char *s;
+{ while(n--&&(*s=getc(in))!='\n')s++;
+ if(*s!='\n'||n<0)return(0);
+ *s='\0';
+ return(1);
+} /* what a pain that `fgets' doesn't do it right !! */
+
+rc_write()
+{ FILE *out=fopen(home_rc,"w");
+ if(out==NULL)
+ { fprintf(stderr,"warning: cannot write to \"%s\"\n",home_rc);
+ return; }
+ fprintf(out,"hdve");
+ if(listing)fputc('l',out);
+ if(rechecking==2)fputc('r',out);
+ fprintf(out," %d %d %d %s\n",SPACELIMIT,DICSPACE,version,editor);
+ fclose(out);
+}
+
+word lastid=0; /* first inscope identifier of immediately preceding command */
+word rv_expr=0;
+
+commandloop(initscript)
+char* initscript;
+{ word ch;
+ word reset();
+ extern word cook_stdin,polyshowerror;
+ char *lb;
+ if(setjmp(env)==0) /* returns here if interrupted, 0 means first time thru */
+ { if(magic){ undump(initscript); /* was loadfile() changed 26.11.2019
+ to allow dump of magic scripts in ".m"*/
+ if(files==NIL||ND!=NIL||id_val(main_id)==UNDEF)
+ /* files==NIL=>script absent or has syntax errors
+ ND!=NIL=>script has type errors or undefined names
+ all reported by undump() or loadfile() on new compile */
+ { if(files!=NIL&&ND==NIL&&id_val(main_id)==UNDEF)
+ fprintf(stderr,"%s: main not defined\n",initscript);
+ fprintf(stderr,"mira: incorrect use of \"-exec\" flag\n");
+ exit(1); }
+ magic=0; obey(main_id); exit(0); }
+ /* was obey(lastexp), change to magic scripts 19.11.2013 */
+ (void)signal(SIGINT,(sighandler)reset);
+ undump(initscript);
+ if(verbosity)printf("for help type /h\n"); }
+ for(;;)
+ { resetgcstats();
+ if(verbosity)printf("%s",promptstr);
+ ch = getchar();
+ if(rechecking&&src_update())loadfile(current_script);
+ /* modified behaviour for `2-window' mode */
+ while(ch==' '||ch=='\t')ch=getchar();
+ switch(ch)
+ { case '?': ch=getchar();
+ if(ch=='?')
+ { word x; char *aka=NULL;
+ if(!token()&&!lastid)
+ { printf("\7identifier needed after `\?\?'\n");
+ ch=getchar(); /* '\n' */
+ break; }
+ if(getchar()!='\n'){ xschars(); break; }
+ if(baded){ ed_warn(); break; }
+ if(dicp[0])x=findid(dicp);
+ else printf("??%s\n",get_id(lastid)),x=lastid;
+ if(x==NIL||id_type(x)==undef_t)
+ { diagnose(dicp[0]?dicp:get_id(lastid));
+ lastid=0;
+ break; }
+ if(id_who(x)==NIL)
+ { /* nb - primitives have NIL who field */
+ printf("%s -- primitive to Miranda\n",
+ dicp[0]?dicp:get_id(lastid));
+ lastid=0;
+ break; }
+ lastid=x;
+ x=id_who(x); /* get here info */
+ if(tag[x]==CONS)aka=(char *)hd[hd[x]],x=tl[x];
+ if(aka)printf("originally defined as \"%s\"\n",
+ aka);
+ editfile((char *)hd[x],tl[x]);
+ break; }
+ ungetc(ch,stdin);
+ (void)token();
+ lastid=0;
+ if(dicp[0]=='\0')
+ { if(getchar()!='\n')xschars();
+ else allnamescom();
+ break; }
+ while(dicp[0])finger(dicp),(void)token();
+ ch=getchar();
+ break;
+ case ':': /* add (silently) as kindness to Hugs users */
+ case '/': (void)token();
+ lastid=0;
+ command(ch);
+ break;
+ case '!': if(!(lb=rdline()))break; /* rdline returns NULL on failure */
+ lastid=0;
+ if(*lb)
+ { /*system(lb); */ /* always gives /bin/sh */
+ static char *shell=NULL;
+ sighandler oldsig;
+ word pid;
+ if(!shell)
+ { shell=getenv("SHELL");
+ if(!shell)shell="/bin/sh"; }
+ oldsig= signal(SIGINT,SIG_IGN);
+ if(pid=fork())
+ { /* parent */
+ if(pid==-1)
+ perror("UNIX error - cannot create process");
+ while(pid!=wait(0));
+ (void)signal(SIGINT,oldsig); }
+ else execl(shell,shell,"-c",lb,(char *)0);
+ if(src_update())loadfile(current_script); }
+ else printf(
+ "No previous shell command to substitute for \"!\"\n");
+ break;
+ case '|': /* lines beginning "||" are comments */
+ if((ch=getchar())!='|')
+ printf("\7unknown command - type /h for help\n");
+ while(ch!='\n'&&ch!=EOF)ch=getchar();
+ case '\n': break;
+ case EOF: if(verbosity)printf("\nmiranda logout\n");
+ exit(0);
+ default: ungetc(ch,stdin);
+ lastid=0;
+ tl[hd[cook_stdin]]=0; /* unset type of $+ */
+ rv_expr=0;
+ c = EVAL;
+ echoing=0;
+ polyshowerror=0; /* gets set by wrong use of $+, readvals */
+ commandmode=1;
+ yyparse();
+ if(SYNERR)SYNERR=0;
+ else if(c!='\n') /* APPARENTLY NEVER TRUE */
+ { printf("syntax error\n");
+ while(c!='\n'&&c!=EOF)
+ c=getchar(); /* swallow syntax errors */
+ }
+ commandmode=0;
+ echoing=verbosity&listing;
+}}}
+
+parseline(t,f,fil) /* parses next valid line of f at type t, returns EOF
+ if none found. See READVALS in reduce.c */
+word t;
+FILE *f;
+word fil;
+{ word t1,ch;
+ lastexp=UNDEF;
+ for(;;)
+ { ch=getc(f);
+ while(ch==' '||ch=='\t'||ch=='\n')ch=getc(f);
+ if(ch=='|')
+ { ch=getc(f);
+ if(ch=='|') /* leading comment */
+ { while((ch=getc(f))!='\n'&&ch!=EOF);
+ if(ch!=EOF)continue; }
+ else ungetc(ch,f); }
+ if(ch==EOF)return(EOF);
+ ungetc(ch,f);
+ c = VALUE;
+ echoing=0;
+ commandmode=1;
+ s_in=f;
+ yyparse();
+ s_in=stdin;
+ if(SYNERR)SYNERR=0,lastexp=UNDEF; else
+ if((t1=type_of(lastexp))==wrong_t)lastexp=UNDEF; else
+ if(!subsumes(instantiate(t1),t))
+ { printf("data has wrong type :: "), out_type(t1),
+ printf("\nshould be :: "), out_type(t), putc('\n',stdout);
+ lastexp=UNDEF; }
+ if(lastexp!=UNDEF)return(codegen(lastexp));
+ if(isatty(fileno(f)))printf("please re-enter data:\n");
+ else { if(fil)fprintf(stderr,"readvals: bad data in file \"%s\"\n",
+ getstring(fil,0));
+ else fprintf(stderr,"bad data in $+ input\n");
+ outstats(); exit(1); }
+}}
+
+ed_warn()
+{ printf(
+"The currently installed editor command, \"%s\", does not\n\
+include a facility for opening a file at a specified line number. As a\n\
+result the `\?\?' command and certain other features of the Miranda system\n\
+are disabled. See manual section 31/5 on changing the editor for more\n\
+information.\n",editor);
+}
+
+time_t fm_time(f) /* time last modified of file f */
+char *f;
+{ return(stat(f,&buf)==0?buf.st_mtime:0);
+ /* non-existent file has conventional mtime of 0 */
+} /* WARNING - we assume time_t can be stored in an int field
+ - this may not port */
+
+#define same_file(x,y) (hd[fil_inodev(x)]==hd[fil_inodev(y)]&& \
+ tl[fil_inodev(x)]==tl[fil_inodev(y)])
+#define inodev(f) (stat(f,&buf)==0?datapair(buf.st_ino,buf.st_dev):\
+ datapair(0,-1))
+
+word oldfiles=NIL; /* most recent set of sources, in case of interrupted or
+ failed compilation */
+src_update() /* any sources modified ? */
+{ word ft,f=files==NIL?oldfiles:files;
+ while(f!=NIL)
+ { if((ft=fm_time(get_fil(hd[f])))!=fil_time(hd[f]))
+ { if(ft==0)unlinkx(get_fil(hd[f])); /* tidy up after eg `!rm %' */
+ return(1); }
+ f=tl[f]; }
+ return(0);
+}
+
+word loading;
+char *unlinkme; /* if set, is name of partially created obfile */
+
+reset() /* interrupt catcher - see call to signal in commandloop */
+{ extern word lineptr,ATNAMES,current_id;
+ extern word blankerr,collecting/* ,*dstack,*stackp */;
+ /*if(!making) /* see note below
+ (void)signal(SIGINT,SIG_IGN); /* dont interrupt me while I'm tidying up */
+/*if(magic)exit(0); *//* signal now not set to reset in magic scripts */
+ if(collecting)gcpatch();
+ if(loading)
+ { if(!blankerr)
+ printf("\n<<compilation interrupted>>\n");
+ if(unlinkme)unlink(unlinkme);
+ /* stackp=dstack; /* add if undump() made interruptible later*/
+ oldfiles=files,unload(),current_id=ATNAMES=loading=SYNERR=lineptr=0;
+ if(blankerr)blankerr=0,makedump(); }
+ /* magic script cannot be literate so no guard needed on makedump */
+ else printf("<<interrupt>>\n"); /* VAX, SUN, ^C does not cause newline */
+ reset_state(); /* see LEX */
+ if(collecting)collecting=0,gc(); /* to mark stdenv etc as wanted */
+ if(making&&!make_status)make_status=1;
+#ifdef SYSTEM5
+ else (void)signal(SIGINT,(sighandler)reset);/*ready for next interrupt*//*see note*/
+#endif
+ /* during mira -make blankerr is only use of reset */
+ longjmp(env,1);
+}/* under BSD and Linux installed signal remains installed after interrupt
+ and further signals blocked until handler returns */
+
+#define checkeol if(getchar()!='\n')break;
+
+word lose;
+
+normal(f) /* s has ".m" suffix */
+char *f;
+{ word n=strlen(f);
+ return n>=2&&strcmp(f+n-2,".m")==0;
+}
+
+v_info(word full)
+{ printf("%s last revised %s\n",strvers(version),vdate);
+ if(!full)return;
+ printf("%s",host);
+ printf("XVERSION %u\n",XVERSION);
+}
+
+
+command(c)
+word c;
+{ char *t;
+ word ch,ch1;
+ switch(dicp[0])
+ {
+ case 'a': if(is("a")||is("aux"))
+ { checkeol;
+/* if(verbosity)clrscr(); */
+ (void)strcpy(linebuf,miralib);
+ (void)strcat(linebuf,"/auxfile");
+ filecopy(linebuf);
+ return; }
+ case 'c': if(is("count"))
+ { checkeol; atcount=1; return; }
+ if(is("cd"))
+ { char *d=token();
+ if(!d)d=getenv("HOME");
+ else d=addextn(0,d);
+ checkeol;
+ if(chdir(d)==-1)printf("cannot cd to %s\n",d);
+ else if(src_update())undump(current_script);
+ /* alternative: keep old script and recompute pathname
+ wrt new directory - LOOK INTO THIS LATER */
+ return; }
+ case 'd': if(is("dic"))
+ { extern char *dic;
+ if(!token())
+ { lose=getchar(); /* to eat \n */
+ printf("%d chars",DICSPACE);
+ if(DICSPACE!=DFLTDICSPACE)
+ printf(" (default=%d)",DFLTDICSPACE);
+ printf(" %d in use\n",dicq-dic);
+ return; }
+ checkeol;
+ printf(
+ "sorry, cannot change size of dictionary while in use\n");
+ printf(
+ "(/q and reinvoke with flag: mira -dic %s ... )\n",dicp);
+ return; }
+ case 'e': if(is("e")||is("edit"))
+ { char *mf=0;
+ if(t=token())t=addextn(1,t);
+ else t=current_script;
+ checkeol;
+ if(stat(t,&buf)) /* new file */
+ { if(!lmirahdr) /* lazy initialisation */
+ { dicp=dicq;
+ (void)strcpy(dicp,getenv("HOME"));
+ if(strcmp(dicp,"/")==0)
+ dicp[0]=0; /* root is special case */
+ (void)strcat(dicp,"/.mirahdr");
+ lmirahdr=dicp;
+ dicq=dicp=dicp+strlen(dicp)+1; } /* ovflo check? */
+ if(!stat(lmirahdr,&buf))mf=lmirahdr;
+ if(!mf&&!mirahdr) /* lazy initialisation */
+ { dicp=dicq;
+ (void)strcpy(dicp,miralib);
+ (void)strcat(dicp,"/.mirahdr");
+ mirahdr=dicp;
+ dicq=dicp=dicp+strlen(dicp)+1; }
+ if(!mf&&!stat(mirahdr,&buf))mf=mirahdr;
+ /*if(mf)printf("mf=%s\n",mf); /* DEBUG*/
+ if(mf&&t!=current_script)
+ { printf("open new script \"%s\"? [ny]",t);
+ ch1=ch=getchar();
+ while(ch!='\n'&&ch!=EOF)ch=getchar();
+ /*eat rest of line */
+ if(ch1!='y'&&ch1!='Y')return; }
+ if(mf)filecp(mf,t); }
+ editfile(t,strcmp(t,current_script)==0?errline:
+ errs&&strcmp(t,(char *)hd[errs])==0?tl[errs]:
+ geterrlin(t));
+ return; }
+ if(is("editor"))
+ { char *hold=linebuf,*h;
+ if(!getln(stdin,pnlim-1,hold))break; /*reject if too long*/
+ if(!*hold)
+ { /* lose=getchar(); /* to eat newline */
+ printf("%s\n",editor);
+ return; }
+ h=hold+strlen(hold); /* remove trailing white space */
+ while(h[-1]==' '||h[-1]=='\t')*--h='\0';
+ if(*hold=='"'||*hold=='\'')
+ { printf("please type name of editor without quotation marks\n");
+ return; }
+ printf("change editor to: \"%s\"? [ny]",hold);
+ ch1=ch=getchar();
+ while(ch!='\n'&&ch!=EOF)ch=getchar(); /* eat rest of line */
+ if(ch1!='y'&&ch1!='Y')
+ { printf("editor not changed\n");
+ return; }
+ (void)strcpy(ebuf,hold);
+ editor=ebuf;
+ fixeditor(); /* reads "vi" as "vi +!" etc */
+ echoing=verbosity&listing;
+ rc_write();
+ printf("editor = %s\n",editor);
+ return; }
+ case 'f': if(is("f")||is("file"))
+ { char *t=token();
+ checkeol;
+ if(t)t=addextn(1,t),keep(t);
+ /* could get multiple copies of filename in dictionary
+ - FIX LATER */
+ if(t)errs=errline=0; /* moved here from reset() */
+ if(t)if(strcmp(t,current_script)||files==NIL&&okdump(t))
+ { extern word CLASHES;
+ CLASHES=NIL; /* normally done by load_script */
+ undump(t); /* does not always call load_script */
+ if(CLASHES!=NIL)/* pathological case, recompile */
+ loadfile(t); }
+ else loadfile(t); /* force recompilation */
+ else printf("%s%s\n",current_script,
+ files==NIL?" (not loaded)":"");
+ return; }
+ if(is("files")) /* info about internal state, not documented */
+ { word f=files;
+ checkeol;
+ for(;f!=NIL;f=tl[f])
+ printf("(%s,%d,%d)",get_fil(hd[f]),fil_time(hd[f]),
+ fil_share(hd[f])),printlist("",fil_defs(hd[f]));
+ return; } /* DEBUG */
+ if(is("find"))
+ { word i=0;
+ while(token())
+ { word x=findid(dicp),y,f;
+ i++;
+ if(x!=NIL)
+ { char *n=get_id(x);
+ for(y=primenv;y!=NIL;y=tl[y])
+ if(tag[hd[y]]==ID)
+ if(hd[y]==x||getaka(hd[y])==n)
+ finger(get_id(hd[y]));
+ for(f=files;f!=NIL;f=tl[f])
+ for(y=fil_defs(hd[f]);y!=NIL;y=tl[y])
+ if(tag[hd[y]]==ID)
+ if(hd[y]==x||getaka(hd[y])==n)
+ finger(get_id(hd[y])); }
+ }
+ ch=getchar(); /* '\n' */
+ if(i==0)printf("\7identifier needed after `/find'\n");
+ return; }
+ case 'g': if(is("gc"))
+ { checkeol; atgc=1; return; }
+ case 'h': if(is("h")||is("help"))
+ { checkeol;
+/* if(verbosity)clrscr(); */
+ (void)strcpy(linebuf,miralib);
+ (void)strcat(linebuf,"/helpfile");
+ filecopy(linebuf);
+ return; }
+ if(is("heap"))
+ { word x;
+ if(!token())
+ { lose=getchar(); /* to eat \n */
+ printf("%d cells",SPACELIMIT);
+ if(SPACELIMIT!=DFLTSPACE)
+ printf(" (default=%d)",DFLTSPACE);
+ printf("\n");
+ return; }
+ checkeol;
+ if(sscanf(dicp,"%d",&x)!=1||badval(x))
+ { printf("illegal value (heap unchanged)\n"); return; }
+ if(x<trueheapsize())
+ printf("sorry, cannot shrink heap to %d at this time\n",x);
+ else { if(x!=SPACELIMIT)
+ SPACELIMIT=x,resetheap();
+ printf("heaplimit = %d cells\n",SPACELIMIT),
+ rc_write(); }
+ return; }
+ if(is("hush"))
+ { checkeol; echoing=verbosity=0; return; }
+ case 'l': if(is("list"))
+ { checkeol; listing=1; echoing=verbosity&listing;
+ rc_write(); return; }
+ case 'm': if(is("m")||is("man"))
+ { checkeol; manaction(); return; }
+ if(is("miralib"))
+ { checkeol; printf("%s\n",miralib); return; }
+ case 'n': /* if(is("namebuckets"))
+ { int i,x;
+ extern int namebucket[];
+ checkeol;
+ for(i=0;i<128;i++)
+ if(x=namebucket[i])
+ { printf("%d:",i);
+ while(x)
+ putchar(' '),out(stdout,hd[x]),x=tl[x];
+ putchar('\n'); }
+ return; } /* DEBUG */
+ if(is("nocount"))
+ { checkeol; atcount=0; return; }
+ if(is("nogc"))
+ { checkeol; atgc=0; return; }
+ if(is("nohush"))
+ { checkeol; echoing=listing; verbosity=1; return; }
+ if(is("nolist"))
+ { checkeol; echoing=listing=0; rc_write(); return; }
+ if(is("norecheck"))
+ { checkeol; rechecking=0; rc_write(); return; }
+/* case 'o': if(is("object"))
+ { checkeol; atobject=1; return; } /* now done by flag -object */
+ case 'q': if(is("q")||is("quit"))
+ { checkeol; if(verbosity)printf("miranda logout\n"); exit(0); }
+ case 'r': if(is("recheck"))
+ { checkeol; rechecking=2; rc_write(); return; }
+ case 's': if(is("s")||is("settings"))
+ { checkeol;
+ printf("*\theap %d\n",SPACELIMIT);
+ printf("*\tdic %d\n",DICSPACE);
+ printf("*\teditor = %s\n",editor);
+ printf("*\t%slist\n",listing?"":"no");
+ printf("*\t%srecheck\n",rechecking?"":"no");
+ if(!strictif)
+ printf("\t-nostrictif (deprecated!)\n");
+ if(atcount)printf("\tcount\n");
+ if(atgc)printf("\tgc\n");
+ if(UTF8)printf("\tUTF-8 i/o\n");
+ if(!verbosity)printf("\thush\n");
+ if(debug)printf("\tdebug 0%o\n",debug);
+ printf("\n* items remembered between sessions\n");
+ return; }
+ case 'v': if(is("v")||is("version"))
+ { checkeol;
+ v_info(0);
+ return; }
+ case 'V': if(is("V"))
+ { checkeol;
+ v_info(1);
+ return; }
+ default: printf("\7unknown command \"%c%s\"\n",c,dicp);
+ printf("type /h for help\n");
+ while((ch=getchar())!='\n'&&ch!=EOF);
+ return;
+ } /* end of switch statement */
+ xschars();
+}
+
+manaction()
+{ sprintf(linebuf,"\"%s/menudriver\" \"%s/manual\"",miralib,miralib);
+ system(linebuf);
+} /* put quotes around both pathnames to allow for spaces in miralib 8.5.06 */
+
+editfile(t,line)
+char *t;
+word line;
+{ char *ebuf=linebuf;
+ char *p=ebuf,*q=editor;
+ word tdone=0;
+ if(line==0)line=1; /* avoids warnings in some versions of vi */
+ while(*p++ = *q++)
+ if(p[-1]=='\\'&&(q[0]=='!'||q[0]=='%'))p[-1]= *q++; else
+ if(p[-1]=='!')
+ (void)
+ sprintf(p-1,"%d",line),
+ p+=strlen(p); else
+ if(p[-1]=='%')p[-1]='"',*p='\0', /* quote filename 9.5.06 */
+ (void)strncat(p,t,BUFSIZE+ebuf-p),
+ p+=strlen(p),
+ *p++ = '"',*p='\0',
+ tdone=1;
+ if(!tdone)
+ p[-1] = ' ',
+ *p++ = '"',*p='\0', /* quote filename 9.5.06 */
+ (void)strncat(p,t,BUFSIZE+ebuf-p),
+ p+=strlen(p),
+ *p++ = '"',*p='\0';
+ /* printf("%s\n",ebuf); /* DEBUG */
+ system(ebuf);
+ if(src_update())loadfile(current_script);
+ return;
+}
+
+xschars()
+{ word ch;
+ printf("\7extra characters at end of command\n");
+ while((ch=getchar())!='\n'&&ch!=EOF);
+}
+
+reverse(x) /* x is a cons list */
+word x;
+{ word y = NIL;
+ while(x!=NIL)y = cons(hd[x],y), x = tl[x];
+ return(y);
+}
+
+shunt(x,y) /* equivalent to append(reverse(x),y) */
+word x,y;
+{ while(x!=NIL)y = cons(hd[x],y), x = tl[x];
+ return(y);
+}
+
+char *presym[] =
+ {"abstype","div","if","mod","otherwise","readvals","show","type","where",
+ "with", 0};
+word presym_n[] =
+ { 21, 8, 15, 8, 15, 31, 23, 22, 15,
+ 21 };
+
+#include <ctype.h>
+
+filequote(p) /* write p to stdout with <quotes> if appropriate */
+char *p; /* p is a pathname */
+{ static mlen=0;
+ if(!mlen)mlen=(rindex(PRELUDE,'/')-PRELUDE)+1;
+ if(strncmp(p,PRELUDE,mlen)==0)
+ printf("<%s>",p+mlen);
+ else printf("\"%s\"",p);
+} /* PRELUDE is a convenient string with the miralib prefix */
+
+finger(n) /* find info about name stored at dicp */
+char *n;
+{ word x,line;
+ char *s;
+ x=findid(n);
+ if(x!=NIL&&id_type(x)!=undef_t)
+ { if(id_who(x)!=NIL)
+ s=(char *)hd[line=get_here(x)],line=tl[line];
+ if(!lastid)lastid=x;
+ report_type(x);
+ if(id_who(x)==NIL)printf(" ||primitive to Miranda\n");
+ else { char *aka=getaka(x);
+ if(aka==get_id(x))aka=NULL; /* don't report alias to self */
+ if(id_val(x)==UNDEF&&id_type(x)!=wrong_t)
+ printf(" ||(UNDEFINED) specified in "); else
+ if(id_val(x)==FREE)
+ printf(" ||(FREE) specified in "); else
+ if(id_type(x)==type_t&&t_class(x)==free_t)
+ printf(" ||(free type) specified in "); else
+ printf(" ||%sdefined in ",
+ id_type(x)==type_t
+ && t_class(x)==abstract_t?"(abstract type) ":
+ id_type(x)==type_t
+ && t_class(x)==algebraic_t?"(algebraic type) ":
+ id_type(x)==type_t
+ && t_class(x)==placeholder_t?"(placeholder type) ":
+ id_type(x)==type_t
+ && t_class(x)==synonym_t?"(synonym type) ":
+ "");
+ filequote(s);
+ if(baded||rechecking)printf(" line %d",line);
+ if(aka)printf(" (as \"%s\")\n",aka);
+ else putchar('\n');
+ }
+ if(atobject)printf("%s = ",get_id(x)),
+ out(stdout,id_val(x)),putchar('\n');
+ return; }
+ diagnose(n);
+}
+
+diagnose(n)
+char *n;
+{ word i=0;
+ if(isalpha(n[0]))
+ while(n[i]&&okid(n[i]))i++;
+ if(n[i]){ printf("\"%s\" -- not an identifier\n",n); return; }
+ for(i=0;presym[i];i++)
+ if(strcmp(n,presym[i])==0)
+ { printf("%s -- keyword (see manual, section %d)\n",n,presym_n[i]);
+ return; }
+ printf("identifier \"%s\" not in scope\n",n);
+}
+
+static word sorted=0; /* flag to avoid repeatedly sorting fil_defs */
+static word leftist; /* flag to alternate bias of padding in justification */
+word words[colmax]; /* max plausible size of screen */
+
+allnamescom()
+{ word s;
+ word x=ND;
+ word y=x,z=0;
+ leftist=0;
+ namescom(make_fil(nostdenv?0:STDENV,0,0,primenv));
+ if(files==NIL)return; else s=tl[files];
+ while(s!=NIL)namescom(hd[s]),s=tl[s];
+ namescom(hd[files]);
+ sorted=1;
+ /* now print warnings, if any */
+ /*if(ND!=NIL&&id_type(hd[ND])==type_t)
+ { printf("ILLEGAL EXPORT LIST - MISSING TYPENAME%s: ",tl[ND]==NIL?"":"S");
+ printlist("",ND);
+ return; } /* install if incomplete export list is escalated to error */
+ while(x!=NIL&&id_type(hd[x])==undef_t)x=tl[x];
+ while(y!=NIL&&id_type(hd[y])!=undef_t)y=tl[y];
+ if(x!=NIL)
+ { printf("WARNING, SCRIPT CONTAINS TYPE ERRORS: ");
+ for(;x!=NIL;x=tl[x])
+ if(id_type(hd[x])!=undef_t)
+ { if(!z)z=1; else putchar(',');
+ out(stdout,hd[x]); }
+ printf(";\n"); }
+ if(y!=NIL)
+ { printf("%s UNDEFINED NAMES: ",z?"AND":"WARNING, SCRIPT CONTAINS");
+ z=0;
+ for(;y!=NIL;y=tl[y])
+ if(id_type(hd[y])==undef_t)
+ { if(!z)z=1; else putchar(',');
+ out(stdout,hd[y]); }
+ printf(";\n"); }
+}
+/* There are two kinds of entry in ND
+ undefined names: val=UNDEF, type=undef_t
+ type errors: val=UNDEF, type=wrong_t
+*/
+
+#define tolerance 3
+ /* max number of extra spaces we are willing to insert */
+
+namescom(l) /* l is an element of `files' */
+word l;
+{ word n=fil_defs(l),col=0,undefs=NIL,wp=0;
+ word scrwd = twidth();
+ if(!sorted&&n!=primenv) /* primenv already sorted */
+ fil_defs(l)=n=alfasort(n); /* also removes pnames */
+ if(n==NIL)return; /* skip empty files */
+ if(get_fil(l))filequote(get_fil(l));
+ else printf("primitive:");
+ printf("\n");
+ while(n!=NIL)
+ { if(id_type(hd[n])==wrong_t||id_val(hd[n])!=UNDEF)
+ { word w=strlen(get_id(hd[n]));
+ if(col+w<scrwd)col += (col!=0); else
+ if(wp&&col+w>=scrwd)
+ { word i,r,j;
+ if(wp>1)i=(scrwd-col)/(wp-1),r=(scrwd-col)%(wp-1);
+ if(i+(r>0)>tolerance)i=r=0;
+ if(leftist)
+ for(col=0;col<wp;)
+ { printf("%s",get_id(words[col]));
+ if(++col<wp)
+ spaces(1+i+(r-- >0)); }
+ else
+ for(r=wp-1-r,col=0;col<wp;)
+ { printf("%s",get_id(words[col]));
+ if(++col<wp)
+ spaces(1+i+(r-- <=0)); }
+ leftist=!leftist,wp=0,col=0,putchar('\n'); }
+ col+=w;
+ words[wp++]=hd[n]; }
+ else undefs=cons(hd[n],undefs); /* undefined but have good types */
+ n = tl[n]; }
+ if(wp)
+ for(col=0;col<wp;)
+ printf("%s",get_id(words[col])),putc(++col==wp?'\n':' ',stdout);
+ if(undefs==NIL)return;
+ undefs=reverse(undefs);
+ printlist("SPECIFIED BUT NOT DEFINED: ",undefs);
+}
+
+word detrop=NIL; /* list of unused local definitions */
+word rfl=NIL; /* list of include components containing type orphans */
+word bereaved; /* typenames referred to in exports and not exported */
+word ld_stuff=NIL;
+ /* list of list of files, to be unloaded if mkincludes interrupted */
+
+loadfile(t)
+char *t;
+{ extern word fileq;
+ extern word current_id,includees,embargoes,exportfiles,freeids,exports;
+ extern word fnts,FBS,disgusting,nextpn;
+ word h=NIL; /* location of %export directive, if present */
+ loading=1;
+ errs=errline=0;
+ current_script=t;
+ oldfiles=NIL;
+ unload();
+ if(stat(t,&buf))
+ { if(initialising){ fprintf(stderr,"panic: %s not found\n",t); exit(1); }
+ if(verbosity)printf("new file %s\n",t);
+ if(magic)fprintf(stderr,"mira -exec %s%s\n",t,": no such file"),exit(1);
+ if(making&&ideep==0)printf("mira -make %s%s\n",t,": no such file");
+ else oldfiles=cons(make_fil(t,0,0,NIL),NIL);
+ /* for correct record of sources */
+ loading=0;
+ return; }
+ if(!openfile(t))
+ { if(initialising){ fprintf(stderr,"panic: cannot open %s\n",t); exit(1); }
+ printf("cannot open %s\n",t);
+ oldfiles=cons(make_fil(t,0,0,NIL),NIL);
+ loading=0;
+ return; }
+ files = cons(make_fil(t,fm_time(t),1,NIL),NIL);
+ current_file = hd[files],tl[hd[fileq]] = current_file;
+ if(initialising&&strcmp(t,PRELUDE)==0)privlib(); else
+ if(initialising||nostdenv==1)
+ if(strcmp(t,STDENV)==0)stdlib();
+ c = ' ';
+ col = 0;
+ s_in = (FILE *)hd[hd[fileq]];
+ adjust_prefix(t);
+/*if(magic&&!initialising)
+ { if(!(getc(s_in)=='#'&&getc(s_in)=='!'))
+ { files=NIL; return; }
+ while(getc(s_in)!='\n');
+ commandmode=1;
+ c=MAGIC; }
+ else /* change to magic scripts 19.11.2013 */
+ commandmode = 0;
+ if(verbosity||making)printf("compiling %s\n",t);
+ nextpn=0; /* lose pnames */
+ embargoes=detrop=
+ fnts=rfl=bereaved=ld_stuff=exportfiles=freeids=exports=includees=FBS=NIL;
+ yyparse();
+ if(!SYNERR&&exportfiles!=NIL)
+ { /* check pathnames in exportfiles have unique bindings */
+ word s,i,count;
+ for(s=exportfiles;s!=NIL;s=tl[s])
+ if(hd[s]==PLUS) /* add current script (less freeids) to exports */
+ { for(i=fil_defs(hd[files]);i!=NIL;i=tl[i])
+ if(isvariable(hd[i])&&!isfreeid(hd[i]))
+ tl[exports]=add1(hd[i],tl[exports]);
+ } else
+ /* pathnames are expanded to their contents in mkincludes */
+ { for(count=0,i=includees;i!=NIL;i=tl[i])
+ if(!strcmp((char *)hd[hd[hd[i]]],(char *)hd[s]))
+ hd[s]=hd[hd[hd[i]]]/*sharing*/,count++;
+ if(count!=1)
+ SYNERR=1,
+ printf("illegal fileid \"%s\" in export list (%s)\n",
+ (char *)hd[s],
+ count?"ambiguous":"not %included in script");
+ }
+ if(SYNERR)
+ sayhere(hd[exports],1),
+ printf("compilation abandoned\n");
+ }
+ if(!SYNERR&&includees!=NIL)
+ files=append1(files,mkincludes(includees)),includees=NIL;
+ ld_stuff=NIL;
+ if(!SYNERR&!disgusting)
+ { if(verbosity||making&&!mkexports&&!mksources)
+ printf("checking types in %s\n",t);
+ checktypes();
+ /* printf("typecheck complete\n"); /* DEBUG */ }
+ if(!SYNERR&&exports!=NIL)
+ if(ND!=NIL)exports=NIL; else /* skip check, cannot be %included */
+ { /* check exports all present and close under type info */
+ word e,u=NIL,n=NIL,c=NIL;
+ h=hd[exports]; exports=tl[exports];
+ for(e=embargoes;e!=NIL;e=tl[e])
+ { if(id_type(hd[e])==undef_t)u=cons(hd[e],u),ND=add1(hd[e],ND); else
+ if(!member(exports,hd[e]))n=cons(hd[e],n); }
+ if(embargoes!=NIL)
+ exports=setdiff(exports,embargoes);
+ exports=alfasort(exports);
+ for(e=exports;e!=NIL;e=tl[e])
+ if(id_type(hd[e])==undef_t)u=cons(hd[e],u),ND=add1(hd[e],ND); else
+ if(id_type(hd[e])==type_t&&t_class(hd[e])==algebraic_t)
+ c=shunt(t_info(hd[e]),c); /* constructors */
+ if(exports==NIL)printf("warning, export list has void contents\n");
+ else exports=append1(alfasort(c),exports);
+ if(n!=NIL)
+ { printf("redundant entr%s in export list:",tl[n]==NIL?"y":"ies");
+ while(n!=NIL)printf(" -%s",get_id(hd[n])),n=tl[n]; n=1; /* flag */
+ putchar('\n'); }
+ if(u!=NIL)exports=NIL,
+ printlist("undefined names in export list: ",u);
+ if(u!=NIL)sayhere(h,1),h=NIL; else
+ if(exports==NIL||n!=NIL)out_here(stderr,h,1),h=NIL;
+ /* for warnings call out_here not sayhere, so errinfo not saved in dump */
+ }
+ if(!SYNERR&&ND==NIL&&(exports!=NIL||tl[files]!=NIL))
+ { /* find out if script can create type orphans when %included */
+ word e1,t;
+ word r=NIL; /* collect list of referenced typenames */
+ word e=NIL; /* and list of exported typenames */
+ if(exports!=NIL)
+ for(e1=exports;e1!=NIL;e1=tl[e1])
+ { if((t=id_type(hd[e1]))==type_t)
+ if(t_class(hd[e1])==synonym_t)
+ r=UNION(r,deps(t_info(hd[e1])));
+ else e=cons(hd[e1],e);
+ else r=UNION(r,deps(t)); } else
+ for(e1=fil_defs(hd[files]);e1!=NIL;e1=tl[e1])
+ { if((t=id_type(hd[e1]))==type_t)
+ if(t_class(hd[e1])==synonym_t)
+ r=UNION(r,deps(t_info(hd[e1])));
+ else e=cons(hd[e1],e);
+ else r=UNION(r,deps(t)); }
+ for(e1=freeids;e1!=NIL;e1=tl[e1])
+ if((t=id_type(hd[hd[e1]]))==type_t)
+ if(t_class(hd[hd[e1]])==synonym_t)
+ r=UNION(r,deps(t_info(hd[hd[e1]])));
+ else e=cons(hd[hd[e1]],e);
+ else r=UNION(r,deps(t));
+ /*printlist("r: ",r); /* DEBUG */
+ for(;r!=NIL;r=tl[r])
+ if(!member(e,hd[r]))bereaved=cons(hd[r],bereaved);
+ /*printlist("bereaved: ",bereaved); /* DEBUG */
+ }
+ if(exports!=NIL&&bereaved!=NIL)
+ { extern word newtyps;
+ word b=intersection(bereaved,newtyps);
+ /*printlist("newtyps",newtyps); /* DEBUG */
+ if(b!=NIL)
+ /*ND=b; /* to escalate to type error, see also allnamescom */
+ printf("warning, export list is incomplete - missing typename%s: ",
+ tl[b]==NIL?"":"s"),
+ printlist("",b);
+ if(b!=NIL&&h!=NIL)out_here(stdout,h,1); /* sayhere(h,1) for error */
+ }
+ if(!SYNERR&&detrop!=NIL)
+ { word gd=detrop;
+ while(detrop!=NIL&&tag[dval(hd[detrop])]==LABEL)detrop=tl[detrop];
+ if(detrop!=NIL)
+ printf("warning, script contains unused local definitions:-\n");
+ while(detrop!=NIL)
+ { out_here(stdout,hd[hd[tl[dval(hd[detrop])]]],0), putchar('\t');
+ out_pattern(stdout,dlhs(hd[detrop])), putchar('\n');
+ detrop=tl[detrop];
+ while(detrop!=NIL&&tag[dval(hd[detrop])]==LABEL)
+ detrop=tl[detrop]; }
+ while(gd!=NIL&&tag[dval(hd[gd])]!=LABEL)gd=tl[gd];
+ if(gd!=NIL)
+ printf("warning, grammar contains unused nonterminals:-\n");
+ while(gd!=NIL)
+ { out_here(stdout,hd[dval(hd[gd])],0), putchar('\t');
+ out_pattern(stdout,dlhs(hd[gd])), putchar('\n');
+ gd=tl[gd];
+ while(gd!=NIL&&tag[dval(hd[gd])]!=LABEL)gd=tl[gd]; }
+ /* note, usual rhs is tries(pat,list(label(here,exp)))
+ grammar rhs is label(here,...) */
+ }
+ if(!SYNERR)
+ { word x; extern word lfrule,polyshowerror;
+ /* we invoke the code generator */
+ lfrule=0;
+ for(x=fil_defs(hd[files]);x!=NIL;x=tl[x])
+ if(id_type(hd[x])!=type_t)
+ { current_id=hd[x];
+ polyshowerror=0;
+ id_val(hd[x])=codegen(id_val(hd[x]));
+ if(polyshowerror)id_val(hd[x])=UNDEF;
+ /* nb - one remaining class of typerrs trapped in codegen,
+ namely polymorphic show or readvals */
+ }
+ current_id=0;
+ if(lfrule&&(verbosity||making))
+ printf("grammar optimisation: %d common left factors found\n",lfrule);
+ if(initialising&&ND!=NIL)
+ { fprintf(stderr,"panic: %s contains errors\n",okprel?"stdenv":"prelude");
+ exit(1); }
+ if(initialising)makedump(); else
+ if(normal(t)) /* file ends ".m", formerly if(!magic) */
+ fixexports(),makedump(),unfixexports();
+ /* changed 26.11.2019 to allow dump of magic scripts ending ".m" */
+ if(!errline&&errs&&(char *)hd[errs]==current_script)
+ errline=tl[errs]; /* soft error (posn not saved in dump) */
+ ND=alfasort(ND);
+ /* we could sort and remove pnames from each defs component immediately
+ after makedump(), instead of doing this in namescom */
+ loading=0;
+ return; }
+ /* otherwise syntax error found */
+ if(initialising)
+ { fprintf(stderr,"panic: cannot compile %s\n",okprel?"stdenv":"prelude"); exit(1); }
+ oldfiles=files;
+ unload();
+ if(normal(t)&&SYNERR!=2)makedump(); /* make syntax error dump */
+ /* allow dump of magic script in ".m", was if(!magic&&) 26.11.2019 */
+ SYNERR=0;
+ loading=0;
+}
+
+isfreeid(x)
+{ return(id_type(x)==type_t?t_class(x)==free_t:id_val(x)==FREE); }
+
+word internals=NIL; /* used by fix/unfixexports, list of names not exported */
+#define paint(x) id_val(x)=ap(EXPORT,id_val(x))
+#define unpainted(x) (tag[id_val(x)]!=AP||hd[id_val(x)]!=EXPORT)
+#define unpaint(x) id_val(x)=tl[id_val(x)]
+
+fixexports()
+{ extern exports,exportfiles,embargoes,freeids;
+ word e=exports,f;
+ /* printlist("exports: ",e); /* DEBUG */
+ for(;e!=NIL;e=tl[e])paint(hd[e]);
+ internals=NIL;
+ if(exports==NIL&&exportfiles==NIL&&embargoes==NIL) /*no %export in script*/
+ { for(e=freeids;e!=NIL;e=tl[e])
+ internals=cons(privatise(hd[hd[e]]),internals);
+ for(f=tl[files];f!=NIL;f=tl[f])
+ for(e=fil_defs(hd[f]);e!=NIL;e=tl[e])
+ { if(tag[hd[e]]==ID)
+ internals=cons(privatise(hd[e]),internals); }}
+ else for(f=files;f!=NIL;f=tl[f])
+ for(e=fil_defs(hd[f]);e!=NIL;e=tl[e])
+ { if(tag[hd[e]]==ID&&unpainted(hd[e]))
+ internals=cons(privatise(hd[e]),internals); }
+ /* optimisation, need not do this to `silent' components - fix later */
+ /*printlist("internals: ",internals); /* DEBUG */
+ for(e=exports;e!=NIL;e=tl[e])unpaint(hd[e]);
+} /* may not be interrupt safe, re unload() */
+
+unfixexports()
+{ /*printlist("internals: ",internals); /* DEBUG */
+ word i=internals;
+ if(mkexports)return; /* in this case don't want internals restored */
+ while(i!=NIL) /* lose */
+ publicise(hd[i]),i=tl[i];
+ internals=NIL;
+} /* may not be interrupt safe, re unload() */
+
+privatise(x) /* change id to pname, and return new id holding it as value */
+word x;
+{ extern word namebucket[],*pnvec;
+ word n = make_pn(x),h=namebucket[hash(get_id(x))],i;
+ if(id_type(x)==type_t)
+ t_info(x)=cons(datapair(getaka(x),0),get_here(x));
+ /* to assist identification of danging type refs - see typesharing code
+ in mkincludes */
+ /* assumption - nothing looks at the t_info after compilation */
+ if(id_val(x)==UNDEF) /* name specified but not defined */
+ id_val(x)= ap(datapair(getaka(x),0),get_here(x));
+ /* this will generate sensible error message on attempt to use value
+ see reduction rule for DATAPAIR */
+ pnvec[i=hd[n]]=x;
+ tag[n]=ID;hd[n]=hd[x];
+ tag[x]=STRCONS;hd[x]=i;
+ while(hd[h]!=x)h=tl[h];
+ hd[h]=n;
+ return(n);
+} /* WARNING - dependent on internal representation of ids and pnames */
+/* nasty problem - privatisation can screw AKA's */
+
+publicise(x) /* converse of the above, applied to the new id */
+word x;
+{ extern word namebucket[];
+ word i=id_val(x),h=namebucket[hash(get_id(x))];
+ tag[i]=ID,hd[i]=hd[x];
+ /* WARNING - USES FACT THAT tl HOLDS VALUE FOR BOTH ID AND PNAME */
+ if(tag[tl[i]]==AP&&tag[hd[tl[i]]]==DATAPAIR)
+ tl[i]=UNDEF; /* undo kludge, see above */
+ while(hd[h]!=x)h=tl[h];
+ hd[h]=i;
+ return(i);
+}
+
+static sigflag=0;
+
+sigdefer()
+{ /* printf("sigdefer()\n"); /* DEBUG */
+ sigflag=1; } /* delayed signal handler, installed during load_script() */
+
+mkincludes(includees)
+word includees;
+{ extern word FBS,BAD_DUMP,CLASHES,exportfiles,exports,TORPHANS;
+ word pid,result=NIL,tclashes=NIL;
+ includees=reverse(includees); /* process in order of occurrence in script */
+ if(pid=fork())
+ { /* parent */
+ word status;
+ if(pid==-1)
+ { perror("UNIX error - cannot create process"); /* will say why */
+ if(ideep>6) /* perhaps cyclic %include */
+ fprintf(stderr,"error occurs %d deep in %%include files\n",ideep);
+ if(ideep)exit(2);
+ SYNERR=2; /* special code to prevent makedump() */
+ printf("compilation of \"%s\" abandoned\n",current_script);
+ return(NIL); }
+ while(pid!=wait(&status));
+ if((WEXITSTATUS(status))==2) /* child aborted */
+ if(ideep)exit(2); /* recursive abortion of parent process */
+ else { SYNERR=2;
+ printf("compilation of \"%s\" abandoned\n",current_script);
+ return(NIL); }
+ /* if we get to here child completed normally, so carry on */
+ }
+ else { /* child does equivalent of `mira -make' on each includee */
+ extern word oldfiles;
+ (void)signal(SIGINT,SIG_DFL); /* don't trap interrupts */
+ ideep++; making=1; make_status=0; echoing=listing=verbosity=magic=0;
+ setjmp(env); /* will return here on blankerr (via reset) */
+ while(includees!=NIL&&!make_status) /* stop at first bad includee */
+ { undump((char *)hd[hd[hd[includees]]]);
+ if(ND!=NIL||files==NIL&&oldfiles!=NIL)make_status=1;
+ /* any errors in dump? */
+ includees=tl[includees];
+ } /* obscure bug - undump above can reinvoke compiler, which
+ side effects compiler variable `includees' - to fix this
+ had to make sure child is holding local copy of includees*/
+ exit(make_status); }
+ sigflag=0;
+ for(;includees!=NIL;includees=tl[includees])
+ { word x=NIL;
+ sighandler oldsig;
+ FILE *f;
+ char *fn=(char *)hd[hd[hd[includees]]];
+ extern word DETROP,MISSING,ALIASES,TSUPPRESSED,*stackp,*dstack;
+ (void)strcpy(dicp,fn);
+ (void)strcpy(dicp+strlen(dicp)-1,obsuffix);
+ if(!making) /* cannot interrupt load_script() */
+ oldsig=signal(SIGINT,(sighandler)sigdefer);
+ if(f=fopen(dicp,"r"))
+ x=load_script(f,fn,hd[tl[hd[includees]]],tl[tl[hd[includees]]],0),
+ fclose(f);
+ ld_stuff=cons(x,ld_stuff);
+ if(!making)(void)signal(SIGINT,oldsig);
+ if(sigflag)sigflag=0,(* oldsig)(); /* take deferred interrupt */
+ if(f&&!BAD_DUMP&&x!=NIL&&ND==NIL&&CLASHES==NIL&&ALIASES==NIL&&
+ TSUPPRESSED==NIL&&DETROP==NIL&&MISSING==NIL)
+ /* i.e. if load_script worked ok */
+ { /* stuff here is to share repeated file components
+ issues:
+ Consider only includees (fil_share=1), not insertees.
+ Effect of sharing is to replace value fields in later copies
+ by (pointers to) corresponding ids in first copy - so sharing
+ transmitted thru dumps. It is illegal to have more than one
+ copy of a (non-synonym) type in the same scope, even under
+ different names. */
+ word y,z;
+ /* printf("start share analysis\n"); /* DEBUG */
+ if(TORPHANS)rfl=shunt(x,rfl); /* file has type orphans */
+ for(y=x;y!=NIL;y=tl[y])fil_inodev(hd[y])=inodev(get_fil(hd[y]));
+ for(y=x;y!=NIL;y=tl[y])
+ if(fil_share(hd[y]))
+ for(z=result;z!=NIL;z=tl[z])
+ if(fil_share(hd[z])&&same_file(hd[y],hd[z])
+ &&fil_time(hd[y])==fil_time(hd[z]))
+ { word p=fil_defs(hd[y]),q=fil_defs(hd[z]);
+ for(;p!=NIL&&q!=NIL;p=tl[p],q=tl[q])
+ if(tag[hd[p]]==ID)
+ if(id_type(hd[p])==type_t&&
+ (tag[hd[q]]==ID||tag[pn_val(hd[q])]==ID))
+ { /* typeclash - record in tclashes */
+ word w=tclashes;
+ word orig=tag[hd[q]]==ID?hd[q]:pn_val(hd[q]);
+ if(t_class(hd[p])==synonym_t)continue;
+ while(w!=NIL&&((char *)hd[hd[w]]!=get_fil(hd[z])
+ ||hd[tl[hd[w]]]!=orig))
+ w=tl[w];
+ if(w==NIL)
+ w=tclashes=cons(strcons(get_fil(hd[z]),
+ cons(orig,NIL)),tclashes);
+ tl[tl[hd[w]]]=cons(hd[p],tl[tl[hd[w]]]);
+ }
+ else the_val(hd[q])=hd[p];
+ else the_val(hd[p])=hd[q];
+ /*following test redundant - remove when sure is ok*/
+ if(p!=NIL||q!=NIL)
+ fprintf(stderr,"impossible event in mkincludes\n");
+ /*break; /* z loop -- NO! (see liftbug) */
+ }
+ if(member(exportfiles,(word)fn))
+ { /* move ids of x onto exports */
+ for(y=x;y!=NIL;y=tl[y])
+ for(z=fil_defs(hd[y]);z!=NIL;z=tl[z])
+ if(isvariable(hd[z]))
+ tl[exports]=add1(hd[z],tl[exports]);
+ /* skip pnames, constructors (expanded later) */
+ }
+ result=append1(result,x);
+ /* keep `result' in front-first order */
+ if(hd[FBS]==NIL)FBS=tl[FBS];
+ else hd[FBS]=cons(tl[hd[hd[includees]]],hd[FBS]); /* hereinfo */
+ /* printf("share analysis finished\n"); /* DEBUG */
+ continue; }
+ /* something wrong - find out what */
+ if(!f)result=cons(make_fil(hd[hd[hd[includees]]],
+ fm_time(fn),0,NIL),result); else
+ if(x==NIL&&BAD_DUMP!= -2)result=append1(result,oldfiles),oldfiles=NIL;
+ else result=append1(result,x);
+ /* above for benefit of `oldfiles' */
+ /* BAD_DUMP -2 is nameclashes due to aliasing */
+ SYNERR=1;
+ printf("unsuccessful %%include directive ");
+ sayhere(tl[hd[hd[includees]]],1);
+/* if(!f)printf("\"%s\" non-existent or unreadable\n",fn), */
+ if(!f)printf("\"%s\" cannot be loaded\n",fn),
+ CLASHES=DETROP=MISSING=NIL;
+ /* just in case not cleared from a previous load_script() */
+ else
+ if(BAD_DUMP== -2)
+ printlist("aliasing causes nameclashes: ",CLASHES),
+ CLASHES=NIL; else
+ if(ALIASES!=NIL||TSUPPRESSED!=NIL)
+ { if(ALIASES!=NIL)
+ printf("alias fails (name%s not found in file",
+ tl[ALIASES]==NIL?"":"s"),
+ printlist("): ",ALIASES),ALIASES=NIL;
+ if(TSUPPRESSED!=NIL)
+ { printf("illegal alias (cannot suppress typename%s):",
+ tl[TSUPPRESSED]==NIL?"":"s");
+ while(TSUPPRESSED!=NIL)
+ printf(" -%s",get_id(hd[TSUPPRESSED])),
+ TSUPPRESSED=tl[TSUPPRESSED];
+ putchar('\n'); }
+ /* if -typename allowed, remember to look for type orphans */
+ }else
+ if(BAD_DUMP)printf("\"%s\" has bad data in dump file\n",fn); else
+ if(x==NIL)printf("\"%s\" contains syntax error\n",fn); else
+ if(ND!=NIL)
+ printf("\"%s\" contains undefined names or type errors\n",fn);
+ if(ND==NIL&&CLASHES!=NIL) /* can have this and failed aliasing */
+ printf("\"%s\" ",fn),printlist("causes nameclashes: ",CLASHES);
+ while(DETROP!=NIL&&tag[hd[DETROP]]==CONS)
+ { word fa=hd[tl[hd[DETROP]]],ta=tl[tl[hd[DETROP]]];
+ char *pn=get_id(hd[hd[DETROP]]);
+ if(fa== -1||ta== -1)
+ printf("`%s' has binding of wrong kind ",pn),
+ printf(fa== -1?"(should be \"= value\" not \"== type\")\n"
+ :"(should be \"== type\" not \"= value\")\n");
+ else
+ printf("`%s' has == binding of wrong arity ",pn),
+ printf("(formal has arity %d, actual has arity %d)\n",fa,ta);
+ DETROP=tl[DETROP]; }
+ if(DETROP!=NIL)
+ printf("illegal parameter binding (name%s not %%free in file",
+ tl[DETROP]==NIL?"":"s"),
+ printlist("): ",DETROP),DETROP=NIL;
+ if(MISSING!=NIL)
+ printf("missing parameter binding%s: ",tl[MISSING]==NIL?"":"s");
+ while(MISSING!=NIL)
+ printf("%s%s",(char *)hd[hd[MISSING]],tl[MISSING]==NIL?";\n":","),
+ MISSING=tl[MISSING];
+ printf("compilation abandoned\n");
+ stackp=dstack; /* in case of BAD_DUMP */
+ return(result); } /* for unload() */
+ if(tclashes!=NIL)
+ { printf("TYPECLASH - the following type%s multiply named:\n",
+ tl[tclashes]==NIL?" is":"s are");
+ /* structure of tclashes is list of strcons(filname,list-of-ids) */
+ for(;tclashes!=NIL;tclashes=tl[tclashes])
+ { printf("\'%s\' of file \"%s\", as: ",
+ getaka(hd[tl[hd[tclashes]]]),
+ (char *)hd[hd[tclashes]]);
+ printlist("",alfasort(tl[hd[tclashes]])); }
+ printf("typecheck cannot proceed - compilation abandoned\n");
+ SYNERR=1;
+ return(result); } /* for unload */
+ return(result);
+}
+
+word tlost=NIL;
+word pfrts=NIL; /* list of private free types bound in this script */
+
+readoption() /* readopt type orphans */
+{ word f,t;
+ extern word TYPERRS,FBS;
+ pfrts=tlost=NIL;
+ /* exclude anonymous free types, these dealt with later by mcheckfbs() */
+ if(FBS!=NIL)
+ for(f=FBS;f!=NIL;f=tl[f])
+ for(t=tl[hd[f]];t!=NIL;t=tl[t])
+ if(tag[hd[hd[t]]]==STRCONS&&tl[tl[hd[t]]]==type_t)
+ pfrts=cons(hd[hd[t]],pfrts);
+ /* this may needlessly scan `silent' files - fix later */
+ for(;rfl!=NIL;rfl=tl[rfl])
+ for(f=fil_defs(hd[rfl]);f!=NIL;f=tl[f])
+ if(tag[hd[f]]==ID)
+ if((t=id_type(hd[f]))==type_t)
+ { if(t_class(hd[f])==synonym_t)
+ t_info(hd[f])=fixtype(t_info(hd[f]),hd[f]); }
+ else id_type(hd[f])=fixtype(t,hd[f]);
+ if(tlost==NIL)return;
+ TYPERRS++;
+ printf("MISSING TYPENAME%s\n",tl[tlost]==NIL?"":"S");
+ printf("the following type%s no name in this scope:\n",
+ tl[tlost]==NIL?" is needed but has":"s are needed but have");
+ /* structure of tlost is list of cons(losttype,list-of-ids) */
+ for(;tlost!=NIL;tlost=tl[tlost])
+ { /* printf("tinfo_tlost=");out(stdout,t_info(hd[hd[tlost]]));
+ putchar(';'); /*DEBUG */
+ printf("\'%s\' of file \"%s\", needed by: ",
+ (char *)hd[hd[t_info(hd[hd[tlost]])]],
+ (char *)hd[tl[t_info(hd[hd[tlost]])]]);
+ printlist("",alfasort(tl[hd[tlost]])); }
+}
+
+/*fixtype(t,x)
+int t,x;
+{ int t1;
+ t1=fixtype1(t,x);
+ printf("fixing type of %s\n",get_id(x));
+ out_type(t); printf(" := ");
+ out_type(t1); putchar('\n');
+ return(t1);
+} /* DEBUG */
+
+fixtype(t,x) /* substitute out any indirected typenames in t */
+word t,x;
+{ switch(tag[t])
+ { case AP:
+ case CONS: tl[t]=fixtype(tl[t],x);
+ hd[t]=fixtype(hd[t],x);
+ default: return(t);
+ case STRCONS: if(member(pfrts,t))return(t); /* see jrcfree.bug */
+ while(tag[pn_val(t)]!=CONS)t=pn_val(t);/*at most twice*/
+ if(tag[t]!=ID)
+ { /* lost type - record in tlost */
+ word w=tlost;
+ while(w!=NIL&&hd[hd[w]]!=t)w=tl[w];
+ if(w==NIL)
+ w=tlost=cons(cons(t,cons(x,NIL)),tlost);
+ tl[hd[w]]=add1(x,tl[hd[w]]);
+ }
+ return(t);
+ }
+}
+
+#define mask(c) (c&0xDF)
+/* masks out lower case bit, which is 0x20 */
+alfa_ls(a,b) /* 'DICTIONARY ORDER' - not currently used */
+char *a,*b;
+{ while(*a&&mask(*a)==mask(*b))a++,b++;
+ if(mask(*a)==mask(*b))return(strcmp(a,b)<0); /* lower case before upper */
+ return(mask(*a)<mask(*b));
+}
+
+alfasort(x) /* also removes non_IDs from result */
+word x;
+{ word a=NIL,b=NIL,hold=NIL;
+ if(x==NIL)return(NIL);
+ if(tl[x]==NIL)return(tag[hd[x]]!=ID?NIL:x);
+ while(x!=NIL) /* split x */
+ { if(tag[hd[x]]==ID)hold=a,a=cons(hd[x],b),b=hold;
+ x=tl[x]; }
+ a=alfasort(a),b=alfasort(b);
+ /* now merge two halves back together */
+ while(a!=NIL&&b!=NIL)
+ if(strcmp(get_id(hd[a]),get_id(hd[b]))<0)x=cons(hd[a],x),a=tl[a];
+ else x=cons(hd[b],x),b=tl[b];
+ if(a==NIL)a=b;
+ while(a!=NIL)x=cons(hd[a],x),a=tl[a];
+ return(reverse(x));
+}
+
+unsetids(d) /* d is a list of identifiers */
+word d;
+{ while(d!=NIL)
+ { if(tag[hd[d]]==ID)id_val(hd[d])=UNDEF,
+ id_who(hd[d])=NIL,
+ id_type(hd[d])=undef_t;
+ d=tl[d]; } /* should we remove from namebucket ? */
+}
+
+unload() /* clear out current script in preparation for reloading */
+{ extern word TABSTRS,SGC,speclocs,newtyps,rv_script,algshfns,nextpn,nolib,
+ includees,freeids;
+ word x;
+ sorted=0;
+ speclocs=NIL;
+ nextpn=0; /* lose pnames */
+ rv_script=0;
+ algshfns=NIL;
+ unsetids(newtyps);
+ newtyps=NIL;
+ unsetids(freeids);
+ freeids=includees=SGC=freeids=TABSTRS=ND=NIL;
+ unsetids(internals);
+ internals=NIL;
+ while(files!=NIL)
+ { unsetids(fil_defs(hd[files]));
+ fil_defs(hd[files])=NIL;
+ files = tl[files]; }
+ for(;ld_stuff!=NIL;ld_stuff=tl[ld_stuff])
+ for(x=hd[ld_stuff];x!=NIL;x=tl[x])unsetids(fil_defs(hd[x]));
+}
+
+yyerror(s) /* called by YACC in the event of a syntax error */
+char *s;
+{ extern word yychar;
+ if(SYNERR)return; /* error already reported, so shut up */
+ if(echoing)printf("\n");
+ printf("%s - unexpected ",s);
+ if(yychar==OFFSIDE&&(c==EOF||c=='|'))
+ { if(c==EOF) /* special case introduced by fix for dtbug */
+ printf("end of file"); else
+ printf("token '|'");
+ /* special case introduced by sreds fix to offside rule */
+ } else
+ { printf(yychar==0?commandmode?"newline":"end of file":"token ");
+ if(yychar>=256)putchar('\"');
+ if(yychar!=0)out2(stdout,yychar);
+ if(yychar>=256)putchar('\"'); }
+ printf("\n");
+ SYNERR=1;
+ reset_lex();
+}
+
+syntax(s) /* called by actions after discovering a (context sensitive) syntax
+ error */
+char *s;
+{ if(SYNERR)return;
+ if(echoing)printf("\n");
+ printf("syntax error: %s",s);
+ SYNERR=1; /* this will stop YACC at its next call to yylex() */
+ reset_lex();
+}
+
+acterror() /* likewise, but assumes error message output by caller */
+{ if(SYNERR)return;
+ SYNERR=1; /* to stop YACC at next symbol */
+ reset_lex();
+}
+
+mira_setup()
+{ extern word common_stdin,common_stdinb,cook_stdin;
+ setupheap();
+ tsetup();
+ reset_pns();
+ bigsetup();
+ common_stdin= ap(READ,0);
+ common_stdinb= ap(READBIN,0);
+ cook_stdin=ap(readvals(0,0),OFFSIDE);
+ nill= cons(CONST,NIL);
+ Void=make_id("()");
+ id_type(Void)=void_t;
+ id_val(Void)=constructor(0,Void);
+ message=make_id("sys_message");
+ main_id=make_id("main"); /* change to magic scripts 19.11.2013 */
+ concat=make_id("concat");
+ diagonalise=make_id("diagonalise");
+ standardout=constructor(0,"Stdout");
+ indent_fn=make_id("indent");
+ outdent_fn=make_id("outdent");
+ listdiff_fn=make_id("listdiff");
+ shownum1=make_id("shownum1");
+ showbool=make_id("showbool");
+ showchar=make_id("showchar");
+ showlist=make_id("showlist");
+ showstring=make_id("showstring");
+ showparen=make_id("showparen");
+ showpair=make_id("showpair");
+ showvoid=make_id("showvoid");
+ showfunction=make_id("showfunction");
+ showabstract=make_id("showabstract");
+ showwhat=make_id("showwhat");
+ primlib(); } /* sets up predefined ids, not referred to by RULES */
+
+void dieclean() /* called if evaluation is interrupted - see RULES */
+{ printf("<<...interrupt>>\n");
+#ifndef NOSTATSONINT
+ outstats(); /* suppress in presence of segfault on ^C with /count */
+#endif
+ exit(0);
+}
+
+/* the function process() creates a process and waits for it to die -
+ returning 1 in the child and 0 in the parent - it is used in the
+ evaluation command (see MIRANDA RULES) */
+process()
+{ word pid;
+ sighandler oldsig;
+ oldsig = signal(SIGINT,SIG_IGN);
+ /* do not let parent receive interrupts intended for child */
+ if(pid=fork())
+ { /* parent */
+ word status; /* see man 2 exit, wait, signal */
+ if(pid== -1)
+ { perror("UNIX error - cannot create process");
+ return(0);
+ }
+ while(pid!=wait(&status));
+ /* low byte of status is termination state of child, next byte is the
+ (low order byte of the) exit status */
+ if(WIFSIGNALED(status)) /* abnormal termination status */
+ { char *cd=status&0200?" (core dumped)":"";
+ char *pc=""; /* "probably caused by stack overflow\n";*/
+ switch(WTERMSIG(status))
+ { case SIGBUS: printf("\n<<...bus error%s>>\n%s",cd,pc); break;
+ case SIGSEGV: printf("\n<<...segmentation fault%s>>\n%s",cd,pc); break;
+ default: printf("\n<<...uncaught signal %d>>\n",WTERMSIG(status));
+ } }
+ /*if(status >>= 8)printf("\n(exit status %d)\n",status); */
+ (void)signal(SIGINT,oldsig); /* restore interrupt status */
+ return(0); }
+ else return(1); /* child */
+}
+
+/* Notice that the MIRANDA system has a two-level interrupt structure.
+ 1) Each evaluation (see RULES) is an interruptible process.
+ 2) If the command loop is interrupted outside an evaluation or during
+ compilation it reverts to the top level prompt - see set_jmp and
+ signal(reset) in commandloop() */
+
+primdef(n,v,t) /* used by "primlib", see below */
+char *n;
+word v,t;
+{ word x;
+ x= make_id(n);
+ primenv=cons(x,primenv);
+ id_val(x)= v;
+ id_type(x)=t; }
+
+predef(n,v,t) /* used by "privlib" and "stdlib", see below */
+char *n;
+word v,t;
+{ word x;
+ x= make_id(n);
+ addtoenv(x);
+ id_val(x)= isconstructor(x)?constructor(v,x):v;
+ id_type(x)=t;
+}
+
+primlib() /* called by "mira_setup", this routine enters
+ the primitive identifiers into the primitive environment */
+{ primdef("num",make_typ(0,0,synonym_t,num_t),type_t);
+ primdef("char",make_typ(0,0,synonym_t,char_t),type_t);
+ primdef("bool",make_typ(0,0,synonym_t,bool_t),type_t);
+ primdef("True",1,bool_t); /* accessible only to 'finger' */
+ primdef("False",0,bool_t); /* likewise - FIX LATER */
+}
+
+privlib() /* called when compiling <prelude>, adds some
+ internally defined identifiers to the environment */
+{ extern word ltchar;
+ predef("offside",OFFSIDE,ltchar); /* used by `indent' in prelude */
+ predef("changetype",I,wrong_t); /* wrong_t to prevent being typechecked */
+ predef("first",HD,wrong_t);
+ predef("rest",TL,wrong_t);
+/* the following added to make prelude compilable without stdenv */
+ predef("code",CODE,undef_t);
+ predef("concat",ap2(FOLDR,APPEND,NIL),undef_t);
+ predef("decode",DECODE,undef_t);
+ predef("drop",DROP,undef_t);
+ predef("error",ERROR,undef_t);
+ predef("filter",FILTER,undef_t);
+ predef("foldr",FOLDR,undef_t);
+ predef("hd",HD,undef_t);
+ predef("map",MAP,undef_t);
+ predef("shownum",SHOWNUM,undef_t);
+ predef("take",TAKE,undef_t);
+ predef("tl",TL,undef_t);
+}
+
+stdlib() /* called when compiling <stdenv>, adds some
+ internally defined identifiers to the environment */
+{ predef("arctan",ARCTAN_FN,undef_t);
+ predef("code",CODE,undef_t);
+ predef("cos",COS_FN,undef_t);
+ predef("decode",DECODE,undef_t);
+ predef("drop",DROP,undef_t);
+ predef("entier",ENTIER_FN,undef_t);
+ predef("error",ERROR,undef_t);
+ predef("exp",EXP_FN,undef_t);
+ predef("filemode",FILEMODE,undef_t);
+ predef("filestat",FILESTAT,undef_t); /* added Feb 91 */
+ predef("foldl",FOLDL,undef_t);
+ predef("foldl1",FOLDL1,undef_t); /* new at release 2 */
+ predef("hugenum",sto_dbl(DBL_MAX),undef_t);
+ /* max_normal() if present returns same value (see <math.h>) */
+ predef("last",LIST_LAST,undef_t);
+ predef("foldr",FOLDR,undef_t);
+ predef("force",FORCE,undef_t);
+ predef("getenv",GETENV,undef_t);
+ predef("integer",INTEGER,undef_t);
+ predef("log",LOG_FN,undef_t);
+ predef("log10",LOG10_FN,undef_t); /* new at release 2 */
+ predef("merge",MERGE,undef_t); /* new at release 2 */
+ predef("numval",NUMVAL,undef_t);
+ predef("read",STARTREAD,undef_t);
+ predef("readb",STARTREADBIN,undef_t);
+ predef("seq",SEQ,undef_t);
+ predef("shownum",SHOWNUM,undef_t);
+ predef("showhex",SHOWHEX,undef_t);
+ predef("showoct",SHOWOCT,undef_t);
+ predef("showfloat",SHOWFLOAT,undef_t); /* new at release 2 */
+ predef("showscaled",SHOWSCALED,undef_t); /* new at release 2 */
+ predef("sin",SIN_FN,undef_t);
+ predef("sqrt",SQRT_FN,undef_t);
+ predef("system",EXEC,undef_t); /* new at release 2 */
+ predef("take",TAKE,undef_t);
+ predef("tinynum",mktiny(),undef_t); /* new at release 2 */
+ predef("zip2",ZIP,undef_t); /* new at release 2 */
+}
+
+mktiny()
+{ volatile
+ double x=1.0,x1=x/2.0;
+ while(x1>0.0)x=x1,x1/=2.0;
+ return(sto_dbl(x));
+}
+/* min_subnormal() if present returns same value (see <math.h>) */
+
+size(x) /* measures the size of a compiled expression */
+word x;
+{ word s;
+ s= 0;
+ while(tag[x]==CONS||tag[x]==AP)
+ { s= s+1+size(hd[x]);
+ x= tl[x]; }
+ return(s); }
+
+makedump()
+{ char *obf=linebuf;
+ FILE *f;
+ (void)strcpy(obf,current_script);
+ (void)strcpy(obf+strlen(obf)-1,obsuffix);
+ f=fopen(obf,"w");
+ if(!f){ printf("WARNING: CANNOT WRITE TO %s\n",obf);
+ if(strcmp(current_script,PRELUDE)==0||
+ strcmp(current_script,STDENV)==0)
+ printf(
+ "TO FIX THIS PROBLEM PLEASE GET SUPER-USER TO EXECUTE `mira'\n");
+ if(making&&!make_status)make_status=1;
+ return; }
+ /* printf("dumping to %s\n",obf); /* DEBUG */
+ unlinkme=obf;
+ /* fchmod(fileno(f),0666); /* to make dumps writeable by all */ /* no! */
+ setprefix(current_script);
+ dump_script(files,f);
+ unlinkme=NULL;
+ fclose(f);
+}
+
+undump(t) /* restore t from dump, or recompile if necessary */
+char *t;
+{ extern word BAD_DUMP,CLASHES;
+ if(!normal(t)&&!initialising)return loadfile(t);
+ /* except for prelude, only .m files have dumps */
+ char obf[pnlim];
+ FILE *f;
+ sighandler oldsig;
+ word flen=strlen(t);
+ time_t t1=fm_time(t),t2;
+ if(flen>pnlim)
+ { printf("sorry, pathname too long (limit=%d): %s\n",pnlim,t);
+ return; } /* if anyone complains, should remove this limit */
+ (void)strcpy(obf,t);
+ (void)strcpy(obf+flen-1,obsuffix);
+ t2=fm_time(obf);
+ if(t2&&!t1)t2=0,unlink(obf); /* dump is orphan - remove */
+ if(!t2||t2<t1) /* dump is nonexistent or older than source - ignore */
+ { loadfile(t); return; }
+ f=fopen(obf,"r");
+ if(!f){ printf("cannot open %s\n",obf); loadfile(t); return; }
+ current_script=t;
+ loading=1;
+ oldfiles=NIL;
+ unload();
+/*if(!initialising)printf("undumping from %s\n",obf); /* DEBUG */
+ if(!initialising&&!making) /* ie this is the main script */
+ sigflag=0,
+ oldsig=signal(SIGINT,(sighandler)sigdefer);
+ /* can't take interrupt during load_script */
+ files=load_script(f,t,NIL,NIL,!making&!initialising);
+ fclose(f);
+ if(BAD_DUMP)
+ { extern word *stackp,*dstack;
+ unlink(obf); unload(); CLASHES=NIL; stackp=dstack;
+ printf("warning: %s contains incorrect data (file removed)\n",obf);
+ if(BAD_DUMP== -1)printf("(obsolete dump format)\n"); else
+ if(BAD_DUMP==1)printf("(wrong source file)\n"); else
+ printf("(error %d)\n",BAD_DUMP); }
+ if(!initialising&&!making) /* restore interrupt handler */
+ (void)signal(SIGINT,oldsig);
+ if(sigflag)sigflag=0,(*oldsig)(); /* take deferred interrupt */
+ /*if(!initialising)printf("%s undumped\n",obf); /* DEBUG */
+ if(CLASHES!=NIL)
+ { if(ideep==0)printf("cannot load %s ",obf),
+ printlist("due to name clashes: ",alfasort(CLASHES));
+ unload();
+ loading=0;
+ return; }
+ if(BAD_DUMP||src_update())loadfile(t);/* any sources modified since dump? */
+ else
+ if(initialising)
+ { if(ND!=NIL||files==NIL) /* error in dump of PRELUDE */
+ fprintf(stderr,"panic: %s contains errors\n",obf),
+ exit(1); } /* beware of dangling else ! (whence {}) */
+ else
+ if(verbosity||magic||mkexports) /* for less silent making s/mkexports/making/ */
+ if(files==NIL)printf("%s contains syntax error\n",t); else
+ if(ND!=NIL)printf("%s contains undefined names or type errors\n",t); else
+ if(!making&&!magic)printf("%s\n",t); /* added &&!magic 26.11.2019 */
+ if(!files==NIL&&!making&!initialising)unfixexports();
+ loading=0;
+}
+
+unlinkx(t) /* remove orphaned .x file */
+char *t;
+{ char *obf=linebuf;
+ (void)strcpy(obf,t);
+ (void)strcpy(obf+strlen(t)-1,obsuffix);
+ if(!stat(obf,&buf))unlink(obf);
+}
+
+void fpe_error()
+{ if(compiling)
+ { (void)signal(SIGFPE,(sighandler)fpe_error); /* reset SIGFPE trap */
+#ifdef sparc8
+ fpsetmask(commonmask); /* to clear sticky bits */
+#endif
+ syntax("floating point number out of range\n");
+ SYNERR=0; longjmp(env,1);
+ /* go straight back to commandloop - necessary because decoding very
+ large numbers can cause huge no. of repeated SIGFPE exceptions */
+ }
+ else printf("\nFLOATING POINT OVERFLOW\n"),exit(1);
+}
+
+char fbuf[512];
+
+filecopy(fil) /* copy the file "fil" to standard out */
+char *fil;
+{ word in=open(fil,0),n;
+ if(in== -1)return;
+ while((n=read(in,fbuf,512))>0)write(1,fbuf,n);
+ close(in);
+}
+
+filecp(fil1,fil2) /* copy file "fil1" to "fil2" (like `cp') */
+char *fil1,*fil2;
+{ word in=open(fil1,0),n;
+ word out=creat(fil2,0644);
+ if(in== -1||out== -1)return;
+ while((n=read(in,fbuf,512))>0)write(out,fbuf,n);
+ close(in);
+ close(out);
+}
+
+/* to define winsize and TIOCGWINSZ for twidth() */
+#include <termios.h>
+#include <sys/ioctl.h>
+
+twidth() /* returns width (in columns) of current window, less 2 */
+{
+#ifdef TIOCGWINSZ
+ static struct winsize tsize;
+ ioctl(fileno(stdout),TIOCGWINSZ,&tsize);
+ return (tsize.ws_col==0)?78:tsize.ws_col-2;
+#else
+#error TIOCGWINSZ undefined
+/* porting note: if you cannot find how to enable use of TIOCGWINSZ
+ comment out the above #error line */
+ return 78; /* give up, we will assume screen width to be 80 */
+#endif
+}
+
+/* was called when Miranda starts up and before /help, /aux
+ to clear screen - suppressed Oct 2019 */
+/* clrscr()
+{ printf("\x1b[2J\x1b[H"); fflush(stdout);
+} */
+
+/* the following code tests if we are in a UTF-8 locale */
+
+#ifdef CYGWIN
+#include <windows.h>
+
+utf8test()
+{ return GetACP()==65001; }
+/* codepage 1252 is Windows version of Latin-1; 65001 is UTF-8 */
+
+#else
+
+utf8test()
+{ char *lang;
+ if(!(lang=getenv("LC_CTYPE")))
+ lang=getenv("LANG");
+ if(lang&&
+ (strstr(lang,"UTF-8")||strstr(lang,"UTF8")||
+ strstr(lang,"utf-8")||strstr(lang,"utf8")))
+ return 1;
+ return 0;
+}
+#endif
+
+/* end of MIRANDA STEER */
+
diff --git a/new/trans.c b/new/trans.c
new file mode 100644
index 0000000..e50eb8a
--- /dev/null
+++ b/new/trans.c
@@ -0,0 +1,1026 @@
+/* MIRANDA TRANS */
+/* performs translation to combinatory logic */
+
+/**************************************************************************
+ * Copyright (C) Research Software Limited 1985-90. All rights reserved. *
+ * The Miranda system is distributed as free software under the terms in *
+ * the file "COPYING" which is included in the distribution. *
+ *------------------------------------------------------------------------*/
+
+#include "data.h"
+
+ /* miscellaneous declarations */
+extern word nill,Void;
+extern word listdiff_fn,count_fn,from_fn;
+extern word diagonalise,concat;
+extern word lastname,initialising;
+extern word current_id,echoing;
+extern word errs;
+word newtyps=NIL; /* list of typenames declared in current script */
+word SGC=NIL; /* list of user defined sui-generis constructors */
+#define sui_generis(k) (/* k==Void|| */ member(SGC,k))
+ /* 3/10/88 decision to treat `()' as lifted */
+
+abstract(x,e) /* abstraction of template x from compiled expression e */
+word x,e;
+{ switch(tag[x])
+ { case ID:
+ if(isconstructor(x))
+ return(sui_generis(x)?ap(K,e):
+ ap2(Ug,primconstr(x),e));
+ else return(abstr(x,e));
+ case CONS:
+ if(hd[x]==CONST)
+ if(tag[tl[x]]==INT)return(ap2(MATCHINT,tl[x],e));
+ else return(ap2(MATCH,tl[x]==NILS?NIL:tl[x],e));
+ else return(ap(U_,abstract(hd[x],abstract(tl[x],e))));
+ case TCONS:
+ case PAIR: /* tuples */
+ return(ap(U,abstract(hd[x],abstract(tl[x],e))));
+ case AP:
+ if(sui_generis(head(x)))
+ return(ap(Uf,abstract(hd[x],abstract(tl[x],e))));
+ if(tag[hd[x]]==AP&&hd[hd[x]]==PLUS) /* n+k pattern */
+ return(ap2(ATLEAST,tl[hd[x]],abstract(tl[x],e)));
+ while(tag[x]==AP)
+ { e= abstract(tl[x],e);
+ x= hd[x]; }
+ /* now x must be a constructor */
+ default: ; }
+ if(isconstructor(x))
+ return(ap2(Ug,primconstr(x),e));
+ printf("error in declaration of \"%s\", undeclared constructor in pattern: ",
+ get_id(current_id)); /* something funny here - fix later */
+ out(stdout,x);
+ printf("\n");
+ return(NIL);
+}
+
+primconstr(x)
+word x;
+{ x=id_val(x);
+ while(tag[x]!=CONSTRUCTOR)x=tl[x];
+ return(x);
+ /* => constructor values are of the form TRY f k where k is the
+ original constructor value, and ! constructors are of the form
+ MKSTRICT i k */
+}
+
+memb(l,x) /* tests if x is a member of list "l" - used in testing for
+ repeated names - see rule for "v2" in MIRANDA RULES */
+word l,x;
+{ if(tag[x]==TVAR) /* type variable! */
+ while(l!=NIL&&!eqtvar(hd[l],x))l= tl[l];
+ else while(l!=NIL&&hd[l]!=x)l= tl[l];
+ return(l!=NIL); }
+
+abstr(x,e) /* "bracket abstraction" of variable x from code e */
+word x,e;
+{ switch(tag[e])
+ { case TCONS:
+ case PAIR:
+ case CONS: return(liscomb(abstr(x,hd[e]),abstr(x,tl[e])));
+ case AP: if(hd[e]==BADCASE||hd[e]==CONFERROR)
+ return(ap(K,e)); /* don't go inside error info */
+ return(combine(abstr(x,hd[e]),abstr(x,tl[e])));
+ case LAMBDA:
+ case LET:
+ case LETREC:
+ case TRIES:
+ case LABEL:
+ case SHOW:
+ case LEXER:
+ case SHARE: fprintf(stderr,"impossible event in abstr (tag=%d)\n",tag[e]),
+ exit(1);
+ default: if(x==e||isvar_t(x)&&isvar_t(e)&&eqtvar(x,e))
+ return(I); /* see note */
+ return(ap(K,e));
+}} /* note - we allow abstraction wrt tvars - see genshfns() */
+
+#define mkindex(i) ((i)<256?(i):make(INT,i,0))
+ /* will fall over if i >= IBASE */
+
+abstrlist(x,e) /* abstraction of list of variables x from code e */
+word x,e;
+{ switch(tag[e])
+ { case TCONS:
+ case PAIR:
+ case CONS: return(liscomb(abstrlist(x,hd[e]),abstrlist(x,tl[e])));
+ case AP: if(hd[e]==BADCASE||hd[e]==CONFERROR)
+ return(ap(K,e)); /* don't go inside error info */
+ else return(combine(abstrlist(x,hd[e]),abstrlist(x,tl[e])));
+ case LAMBDA: case LET: case LETREC: case TRIES: case LABEL: case SHOW:
+ case LEXER:
+ case SHARE: fprintf(stderr,
+ "impossible event in abstrlist (tag=%d)\n",tag[e]),
+ exit(1);
+ default: { word i=0;
+ while(x!=NIL&&hd[x]!=e)i++,x=tl[x];
+ if(x==NIL)return(ap(K,e));
+ return(ap(SUBSCRIPT,mkindex(i))); }
+}}
+
+word rv_script=0; /* flags readvals in use (for garbage collector) */
+
+codegen(x) /* returns expression x with abstractions performed */
+word x;
+{ extern word debug,commandmode,cook_stdin,common_stdin,common_stdinb,rv_expr;
+ switch(tag[x])
+ { case AP: if(commandmode /* beware of corrupting lastexp */
+ &&x!=cook_stdin&&x!=common_stdin&&x!=common_stdinb) /* but share $+ $- */
+ return(make(AP,codegen(hd[x]),codegen(tl[x])));
+ if(tag[hd[x]]==AP&&hd[hd[x]]==APPEND&&tl[hd[x]]==NIL)
+ return(codegen(tl[x])); /* post typecheck reversal of HR bug fix */
+ hd[x]=codegen(hd[x]); tl[x]=codegen(tl[x]);
+ /* otherwise do in situ */
+ return(tag[hd[x]]==AP&&hd[hd[x]]==G_ALT?leftfactor(x):x);
+ case TCONS:
+ case PAIR: return(make(CONS,codegen(hd[x]),codegen(tl[x])));
+ case CONS: if(commandmode)
+ return(make(CONS,codegen(hd[x]),codegen(tl[x])));
+ /* otherwise do in situ (see declare) */
+ hd[x]=codegen(hd[x]); tl[x]=codegen(tl[x]);
+ return(x);
+ case LAMBDA: return(abstract(hd[x],codegen(tl[x])));
+ case LET: return(translet(hd[x],tl[x]));
+ case LETREC: return(transletrec(hd[x],tl[x]));
+ case TRIES: return(transtries(hd[x],tl[x]));
+ case LABEL: return(codegen(tl[x]));
+ case SHOW: return(makeshow(hd[x],tl[x]));
+ case LEXER:
+ { word r=NIL,uses_state=0;;
+ while(x!=NIL)
+ { word rule=abstr(mklexvar(0),codegen(tl[tl[hd[x]]]));
+ rule=abstr(mklexvar(1),rule);
+ if(!(tag[rule]==AP&&hd[rule]==K))uses_state=1;
+ r=cons(cons(hd[hd[x]], /* start condition stuff */
+ cons(ap(hd[tl[hd[x]]],NIL), /* matcher [] */
+ rule)),
+ r);
+ x=tl[x]; }
+ if(!uses_state) /* strip off (K -) from each rule */
+ { for(x=r;x!=NIL;x=tl[x])tl[tl[hd[x]]]=tl[tl[tl[hd[x]]]];
+ r = ap(LEX_RPT,ap(LEX_TRY,r)); }
+ else r = ap(LEX_RPT1,ap(LEX_TRY1,r));
+ return(ap(r,0)); } /* 0 startcond */
+ case STARTREADVALS:
+ if(ispoly(tl[x]))
+ { extern word cook_stdin,polyshowerror,ND;
+ printf("type error - %s used at polymorphic type :: [",
+ cook_stdin&&x==hd[cook_stdin]?"$+":"readvals or $+");
+ out_type(redtvars(tl[x])),printf("]\n");
+ polyshowerror=1;
+ if(current_id)
+ ND=add1(current_id,ND),
+ id_type(current_id)=wrong_t,
+ id_val(current_id)=UNDEF;
+ if(hd[x])sayhere(hd[x],1); }
+ if(commandmode)rv_expr=1; else rv_script=1;
+ return(x);
+ case SHARE: if(tl[x]!= -1) /* arbitrary flag for already visited */
+ hd[x]=codegen(hd[x]),tl[x]= -1;
+ return(hd[x]);
+ default: if(x==NILS)return(NIL);
+ return(x); /* identifier, private name, or constant */
+}}
+
+word lfrule=0;
+
+leftfactor(x)
+
+/* grammar optimisations - x is of the form ap2(G_ALT,...)
+ G_ALT(G_SEQ a b) a => G_SEQ a (G_ALT b G_UNIT)
+ G_ALT(G_SEQ a b)(G_SEQ a c) => G_SEQ a (G_ALT b c)
+ G_ALT(G_SEQ a b)(G_ALT a d) => G_ALT(G_SEQ a (G_ALT b G_UNIT)) d
+ G_ALT(G_SEQ a b)(G_ALT(G_SEQ a c) d) => G_ALT(G_SEQ a (G_ALT b c)) d
+*/
+word x;
+{ word a,b,c,d;
+ if(tag[c=tl[hd[x]]]==AP&&tag[hd[c]]==AP&&hd[hd[c]]==G_SEQ)
+ a=tl[hd[c]],b=tl[c]; else return(x);
+ if(same(a,d=tl[x]))
+ { hd[x]=ap(G_SEQ,a), tl[x]=ap2(G_ALT,b,G_UNIT); lfrule++;
+ /* printob("rule1: ",x); */
+ return(x); }
+ if(tag[d]==AP&&tag[hd[d]]==AP)
+ c=hd[hd[d]]; else return(x);
+ if(c==G_SEQ&&same(a,tl[hd[d]]))
+ { c=tl[d],
+ hd[x]=ap(G_SEQ,a), tl[x]=leftfactor(ap2(G_ALT,b,c)); lfrule++;
+ /* printob("rule2: ",x); */
+ return(x); }
+ if(c!=G_ALT)return(x);
+ if(same(a,c=tl[hd[d]]))
+ { d=tl[d];
+ hd[x]=ap(G_ALT,ap2(G_SEQ,a,ap2(G_ALT,b,G_UNIT)));
+ tl[x]=d; lfrule++;
+ /* printob("rule3: ",x); */
+ return(leftfactor(x)); }
+ if(tag[c]==AP&&tag[hd[c]]==AP&&hd[hd[c]]==G_SEQ
+ &&same(a,tl[hd[c]]))
+ { c=tl[c],d=tl[d],
+ hd[x]=ap(G_ALT,ap2(G_SEQ,a,leftfactor(ap2(G_ALT,b,c))));
+ tl[x]=d; lfrule++;
+ /* printob("rule4: ",x); */
+ return(leftfactor(x)); }
+ return(x);
+}
+
+same(x,y) /* structural equality */
+word x,y;
+{ if(x==y)return(1);
+ if(tag[x]==ATOM||tag[y]==ATOM||tag[x]!=tag[y])return(0);
+ if(tag[x]<INT)return(hd[x]==hd[y]&&tl[x]==tl[y]);
+ if(tag[x]>STRCONS)return(same(hd[x],hd[y])&&same(tl[x],tl[y]));
+ return(hd[x]==hd[y]&&same(tl[x],tl[y])); /* INT..STRCONS */
+}
+
+static word was_poly;
+word polyshowerror;
+
+makeshow(here,type)
+word here,type;
+{ word f;
+ extern word ND;
+ was_poly=0; f=mkshow(0,0,type);
+ /* printob("showfn=",f); /* DEBUG */
+ if(here&&was_poly)
+ { extern char *current_script;
+ printf("type error in definition of %s\n",get_id(current_id));
+ sayhere(here,0);
+ printf(" use of \"show\" at polymorphic type ");
+ out_type(redtvars(type));
+ putchar('\n');
+ id_type(current_id)=wrong_t;
+ id_val(current_id)=UNDEF;
+ polyshowerror=1;
+ ND=add1(current_id,ND);
+ was_poly=0; }
+ return(f);
+}
+
+mkshow(s,p,t) /* build a show function appropriate to type t */
+word s,p,t; /* p is precedence - 0 for top level, 1 for internal */
+ /* s flags special case invoked from genshfns */
+{ extern word shownum1,showbool,showchar,showlist,showstring,showparen,
+ showvoid,showpair,showfunction,showabstract,showwhat;
+ word a=NIL;
+ while(tag[t]==AP)a=cons(tl[t],a),t=hd[t];
+ switch(t)
+ { case num_t: return(p?shownum1:SHOWNUM);
+ case bool_t: return(showbool);
+ case char_t: return(showchar);
+ case list_t: if(hd[a]==char_t)return(showstring);
+ return(ap(showlist,mkshow(s,0,hd[a])));
+ case comma_t: return(ap(showparen,ap2(showpair,mkshow(s,0,hd[a]),
+ mkshowt(s,hd[tl[a]]))));
+ case void_t: return(showvoid);
+ case arrow_t:return(showfunction);
+ default: if(tag[t]==ID)
+ { word r=t_showfn(t);
+ if(r==0) /* abstype without show function */
+ return(showabstract);
+ if(r==showwhat) /* dont apply to parameter showfns */
+ return(r);
+ while(a!=NIL)r=ap(r,mkshow(s,1,hd[a])),a=tl[a];
+ if(t_class(t)==algebraic_t)r=ap(r,p);
+ return(r);
+ /* note that abstype-showfns have only one precedence
+ and show their components (if any) at precedence 1
+ - if the latter is a problem could do parenthesis
+ stripping */
+ }
+ if(isvar_t(t)){ if(s)return(t); /* see genshfns */
+ was_poly=1;
+ return(showwhat); }
+ /* arbitrary - could be any strict function */
+ if(tag[t]==STRCONS) /* pname */ /* DEBUG */
+ { printf("warning - mkshow applied to suppressed type\n");
+ return(showwhat); }
+ else { printf("impossible event in mkshow ("),
+ out_type(t), printf(")\n");
+ return(showwhat); }
+ }
+}
+
+mkshowt(s,t) /* t is a (possibly singleton) tuple type */
+word s,t; /* flags special call from genshfns */
+{ extern word showpair;
+ if(tl[t]==void_t)return(mkshow(s,0,tl[hd[t]]));
+ return(ap2(showpair,mkshow(s,0,tl[hd[t]]),mkshowt(s,tl[t])));
+}
+
+word algshfns=NIL; /* list of showfunctions for all algebraic types in scope
+ (list of pnames) - needed to make dumps */
+
+genshfns() /* called after meta type check - create show functions for
+ algebraic types */
+{ word s;
+ for(s=newtyps;s!=NIL;s=tl[s])
+ if(t_class(hd[s])==algebraic_t)
+ { word f=0,r=t_info(hd[s]); /* r is list of constructors */
+ word ush= tl[r]==NIL&&member(SGC,hd[r])?Ush1:Ush;
+ for(;r!=NIL;r=tl[r])
+ { word t=id_type(hd[r]),k=id_val(hd[r]);
+ while(tag[k]!=CONSTRUCTOR)k=tl[k];/* lawful and !'d constructors*/
+ /* k now holds constructor(i,hd[r]) */
+ /* k=constructor(hd[k],datapair(get_id(tl[k]),0));
+ /* this `freezes' the name of the constructor */
+ /* incorrect, makes showfns immune to aliasing, should be
+ done at mkshow time, not genshfn time - FIX LATER */
+ while(isarrow_t(t))
+ k=ap(k,mkshow(1,1,tl[hd[t]])),t=tl[t]; /* NB 2nd arg */
+ k=ap(ush,k);
+ while(iscompound_t(t))k=abstr(tl[t],k),t=hd[t];
+ /* see kahrs.bug.m (this is the fix) */
+ if(f)f=ap2(TRY,k,f);
+ else f=k;
+ }
+ /* f~=0, placeholder types dealt with in specify() */
+ pn_val(t_showfn(hd[s]))=f;
+ algshfns=cons(t_showfn(hd[s]),algshfns);
+ }
+ else
+ if(t_class(hd[s])==abstract_t) /* if showfn present check type is ok */
+ if(t_showfn(hd[s]))
+ if(!abshfnck(hd[s],id_type(t_showfn(hd[s]))))
+ printf("warning - \"%s\" has type inappropriate for a show-function\n",
+ get_id(t_showfn(hd[s]))),t_showfn(hd[s])=0;
+}
+
+abshfnck(t,f) /* t is an abstype, is f right type for its showfn? */
+word t,f;
+{ word n=t_arity(t),i=1;
+ while(i<=n)
+ if(isarrow_t(f))
+ { word h=tl[hd[f]];
+ if(!(isarrow_t(h)&&isvar_t(tl[hd[h]])&&gettvar(tl[hd[h]])==i
+ &&islist_t(tl[h])&&tl[tl[h]]==char_t))return(0);
+ i++,f=tl[f];
+ } else return(0);
+ if(!(isarrow_t(f)&&islist_t(tl[f])&&tl[tl[f]]==char_t))return(0);
+ f=tl[hd[f]];
+ while(iscompound_t(f)&&isvar_t(tl[f])&&gettvar(tl[f])==n--)f=hd[f];
+ return(f==t);
+}
+
+transtries(id,x)
+word id,x; /* x is a list of alternative values, in reverse order */
+{ word r,h=0,earliest;
+ if(fallible(hd[x])) /* add default last case */
+ { word oldn=tag[id]==ID?datapair(get_id(id),0):0;
+ r=ap(BADCASE,h=cons(oldn,0));
+ /* 0 is placeholder for here-info */
+ /* oldn omitted if id is pattern - FIX LATER */ }
+ else r=codegen(earliest=hd[x]), x = tl[x];
+ while(x!=NIL)r=ap2(TRY,codegen(earliest=hd[x]),r), x=tl[x];
+ if(h)tl[h]=hd[earliest]; /* first line-no is the best marker */
+ return(r);
+}
+
+translet(d,e) /* compile block with body e and def d */
+word d,e;
+{ word x=mklazy(d);
+ return(ap(abstract(dlhs(x),codegen(e)),codegen(dval(x))));
+}
+/* nasty bug, codegen(dval(x)) was interfering with abstract(dlhs(x)...
+ to fix made codegen on tuples be NOT in situ 20/11/88 */
+
+transletrec(dd,e) /* better method, using list indexing - Jan 88 */
+word e,dd;
+{ word lhs=NIL,rhs=NIL,pn=1;
+ /* list of defs (x=e) is combined to listwise def `xs=es' */
+ for(;dd!=NIL;dd=tl[dd])
+ { word x=hd[dd];
+ if(tag[dlhs(x)]==ID) /* couldn't be constructor, by grammar */
+ lhs=cons(dlhs(x),lhs),
+ rhs=cons(codegen(dval(x)),rhs);
+ else { word i=0,ids,p=mkgvar(pn++); /* see note 1 */
+ x=new_mklazy(x); ids=dlhs(x);
+ lhs=cons(p,lhs),rhs=cons(codegen(dval(x)),rhs);
+ for(;ids!=NIL;ids=tl[ids],i++)
+ lhs=cons(hd[ids],lhs),
+ rhs=cons(ap2(SUBSCRIPT,mkindex(i),p),rhs);
+ }
+ }
+ if(tl[lhs]==NIL) /* singleton */
+ return(ap(abstr(hd[lhs],codegen(e)),ap(Y,abstr(hd[lhs],hd[rhs]))));
+ return(ap(abstrlist(lhs,codegen(e)),ap(Y,abstrlist(lhs,rhs))));
+}
+/* note 1: we here use the alternative `mklazy' transformation
+ pat = e => x1=p!0;...;xn=p!(n-1);p=(lambda(pat)[xs])e|conferror;
+ where p is a private name (need be unique only within a given letrec)
+*/
+
+mklazy(d) /* transforms local p=e to ids=($p.ids)e|conferror */
+word d;
+{ if(irrefutable(dlhs(d)))return(d);
+{ word ids=mktuple(dlhs(d));
+ if(ids==NIL){ printf("impossible event in mklazy\n"); return(d); }
+ dval(d)=ap2(TRY,ap(lambda(dlhs(d),ids),dval(d)),
+ ap(CONFERROR,cons(dlhs(d),here_inf(dval(d)))));
+ dlhs(d)=ids;
+ return(d);
+}}
+
+new_mklazy(d) /* transforms local p=e to ids=($p.ids)e|conferror
+ with ids a LIST (not tuple as formerly) */
+word d;
+{ word ids=get_ids(dlhs(d));
+ if(ids==NIL){ printf("impossible event in new_mklazy\n"); return(d); }
+ dval(d)=ap2(TRY,ap(lambda(dlhs(d),ids),dval(d)),
+ ap(CONFERROR,cons(dlhs(d),here_inf(dval(d)))));
+ dlhs(d)=ids;
+ return(d);
+}
+
+here_inf(rhs) /* rhs is of form tries(id,val_list) */
+word rhs;
+{ word x=tl[rhs];
+ while(tl[x]!=NIL)x=tl[x]; /* find earliest alternative */
+ return(hd[hd[x]]); /* hd[x] is of form label(here_info,value) */
+}
+
+irrefutable(x) /* x built from suigeneris constr's and (unrepeated) names */
+word x;
+{ if(tag[x]==CONS)return(0); /* includes constants */
+ if(isconstructor(x))return(sui_generis(x));
+ if(tag[x]==ID)return(1);
+ if(tag[x]==AP&&tag[hd[x]]==AP&&hd[hd[x]]==PLUS) /* n+k pattern */
+ return(0);
+ return(irrefutable(hd[x])&&irrefutable(tl[x]));
+}
+
+combine(x,y)
+word x,y;
+{ word a,b,a1,b1;
+ a= tag[x]==AP&&hd[x]==K;
+ b= tag[y]==AP&&hd[y]==K;
+ if(a&&b)return(ap(K,ap(tl[x],tl[y])));
+ /* rule of K propagation */
+ if(a&&y==I)return(tl[x]);
+ /* rule 'eta */
+ b1= tag[y]==AP&&tag[hd[y]]==AP&&hd[hd[y]]==B;
+ if(a)if(b1)return(ap3(B1,tl[x],tl[hd[y]],tl[y])); else
+ /* Mark Scheevel's new B1 introduction rule -- adopted Aug 83 */
+ if(tag[tl[x]]==AP&&tag[hd[tl[x]]]==AP&&hd[hd[tl[x]]]==COND)
+ return(ap3(COND,tl[hd[tl[x]]],ap(K,tl[tl[x]]),y));
+ else return(ap2(B,tl[x],y));
+ a1= tag[x]==AP&&tag[hd[x]]==AP&&hd[hd[x]]==B;
+ if(b)if(a1)if(tag[tl[hd[x]]]==AP&&hd[tl[hd[x]]]==COND)
+ return(ap3(COND,tl[tl[hd[x]]],tl[x],y));
+ else return(ap3(C1,tl[hd[x]],tl[x],tl[y]));
+ else return(ap2(C,x,tl[y]));
+ if(a1)if(tag[tl[hd[x]]]==AP&&hd[tl[hd[x]]]==COND)
+ return(ap3(COND,tl[tl[hd[x]]],tl[x],y));
+ else return(ap3(S1,tl[hd[x]],tl[x],y));
+ else return(ap2(S,x,y)); }
+
+liscomb(x,y) /* the CONSy analogue of "combine" */
+word x,y;
+{ word a,b;
+ a= tag[x]==AP&&hd[x]==K;
+ b= tag[y]==AP&&hd[y]==K;
+ if(a&&b)return(ap(K,cons(tl[x],tl[y])));
+ /* K propagation again */
+ if(a)if(y==I)return(ap(P,tl[x])); /* eta P - new rule added 20/11/88 */
+ else return(ap2(B_p,tl[x],y));
+ if(b)return(ap2(C_p,x,tl[y]));
+ return(ap2(S_p,x,y)); }
+/* B_p,C_p,S_p are the CONSy analogues of B,C,S
+ see MIRANDA REDUCE for their definitions */
+
+compzf(e,qq,diag) /* compile a zf expression with body e and qualifiers qq
+ (listed in reverse order); diag is 0 for sequential
+ and 1 for diagonalising zf expressions */
+word e,qq,diag;
+{ word hold=NIL,r=0,g1= -1; /* r is number of generators */
+ while(qq!=NIL) /* unreverse qualifier list */
+ { if(hd[hd[qq]]==REPEAT)qq=fixrepeats(qq);
+ hold=cons(hd[qq],hold);
+ if(hd[hd[qq]]==GUARD)r++; /* count filters */
+ qq = tl[qq]; }
+ for(qq=hold;qq!=NIL&&hd[hd[qq]]==GUARD;qq=tl[qq])r--; /* less leading filters */
+ if(hd[hd[hold]]==GENERATOR)g1=tl[tl[hd[hold]]]; /* rhs of 1st generator */
+ e=transzf(e,hold,diag?diagonalise:concat);
+ /* diagonalise [ // ] comprehensions, but not [ | ] ones */
+ if(diag)
+ while(r--)e=ap(concat,e); /* see funny version of rule 3 below */
+ return(e==g1?ap2(APPEND,NIL,e):e); /* test in g1 is to fix HR bug */
+}
+/* HR bug - if Rule 1 applied at outermost level, type info is lost
+ eg [p|p<-3] ==> 3 (reported by Ham Richards, Nov 89)
+*/
+
+transzf(e,qq,conc) /* Bird and Wadler page 63 */
+word e,qq,conc;
+{ word q,q2;
+ if(qq==NIL)return(cons(e,NIL));
+ q=hd[qq];
+ if(hd[q]==GUARD)
+ return(ap3(COND,tl[q],transzf(e,tl[qq],conc),NIL));
+ if(tl[qq]==NIL)
+ if(hd[tl[q]]==e&&isvariable(e))return(tl[tl[q]]); /* Rule 1 */
+ else if(irrefutable(hd[tl[q]]))
+ return(ap2(MAP,lambda(hd[tl[q]],e),tl[tl[q]])); /* Rule 2 */
+ else /* Rule 2 warped for refutable patterns */
+ return(ap2(FLATMAP,lambda(hd[tl[q]],cons(e,NIL)),tl[tl[q]]));
+ q2=hd[tl[qq]];
+ if(hd[q2]==GUARD)
+ if(conc==concat) /* Rule 3 */
+ { tl[tl[q]]=ap2(FILTER,lambda(hd[tl[q]],tl[q2]),tl[tl[q]]);
+ tl[qq]=tl[tl[qq]];
+ return(transzf(e,qq,conc)); }
+ else /* funny [//] version of Rule 3 to avoid creating weak lists */
+ { e=ap3(COND,tl[q2],cons(e,NIL),NIL);
+ tl[qq]=tl[tl[qq]];
+ return(transzf(e,qq,conc)); } /* plus wrap result with concat */
+ return(ap(conc,transzf(transzf(e,tl[qq],conc),cons(q,NIL),conc)));
+ /* Rule 4 */
+}
+
+fixrepeats(qq) /* expands multi-lhs generators in zf expressions */
+word qq;
+{ word q = hd[qq];
+ word rhs = q;
+ qq = tl[qq];
+ while(hd[rhs]==REPEAT)rhs = tl[tl[rhs]];
+ rhs = tl[tl[rhs]]; /* rhs now contains the common right hand side */
+ while(hd[q]==REPEAT)
+ { qq = cons(cons(GENERATOR,cons(hd[tl[q]],rhs)),qq);
+ q = tl[tl[q]];
+ }
+ return(cons(q,qq));
+} /* EFFICIENCY PROBLEM - rhs gets re-evaluated for each lhs, fix later */
+ /* likewise re-typechecked, although this probably doesn't matter */
+
+lastlink(x) /* finds last link of a list -- needed with zf body elision */
+word x;
+{ while(tl[x]!=NIL)x=tl[x];
+ return(x);
+}
+
+#define ischar(x) ((x)>=0&&(x)<=255)
+
+genlhs(x) /* x is an expression found on the lhs of <- and genlhs returns
+ the corresponding pattern */
+word x;
+{ word hold;
+ switch(tag[x])
+ { case AP:
+ if(tag[hd[x]]==AP&&hd[hd[x]]==PLUS&&isnat(tl[x]))
+ return(ap2(PLUS,tl[x],genlhs(tl[hd[x]]))); /* n+k pattern */
+ case CONS:
+ case TCONS:
+ case PAIR:
+ hold=genlhs(hd[x]); return(make(tag[x],hold,genlhs(tl[x])));
+ case ID:
+ if(member(idsused,x))return(cons(CONST,x));
+ if(!isconstructor(x))idsused=cons(x,idsused); return(x);
+ case INT: return(cons(CONST,x));
+ case DOUBLE: syntax("floating point literal in pattern\n");
+ return(nill);
+ case ATOM: if(x==True||x==False||x==NILS||x==NIL||ischar(x))
+ return(cons(CONST,x));
+ default: syntax("illegal form on left of <-\n");
+ return(nill);
+}}
+
+#ifdef OBSOLETE
+genexp(x) /* undoes effect of genlhs - sorry about that! (see qualifiers1)*/
+word x;
+{ switch(tag[x])
+ { case AP: return(ap(genexp(hd[x]),genexp(tl[x])));
+ case TCONS: return(tcons(genexp(hd[x]),genexp(tl[x])));
+ case PAIR: return(pair(genexp(hd[x]),genexp(tl[x])));
+ case CONS: return(hd[x]==CONST?tl[x]
+ :cons(genexp(hd[x]),genexp(tl[x])));
+ default: return(x); /* must be ID or constant */
+}}
+#endif
+
+word speclocs=NIL; /* list of cons(id,hereinfo) giving location of spec for
+ ids both defined and specified - needed to locate errs
+ in meta_tcheck, abstr_mcheck */
+getspecloc(x)
+word x;
+{ word s=speclocs;
+ while(s!=NIL&&hd[hd[s]]!=x)s=tl[s];
+ return(s==NIL?id_who(x):tl[hd[s]]); }
+
+declare(x,e) /* translates <pattern> = <exp> at top level */
+word x,e;
+{ if(tag[x]==ID&&!isconstructor(x))decl1(x,e);else
+ { word bindings=scanpattern(x,x,share(tries(x,cons(e,NIL)),undef_t),
+ ap(CONFERROR,cons(x,hd[e])));
+ /* hd[e] is here-info */
+ /* note creation of share node to force sharing on code generation
+ and typechecking */
+ if(bindings==NIL){ errs=hd[e];
+ syntax("illegal lhs for definition\n");
+ return; }
+ lastname=0;
+ while(bindings!=NIL)
+ { word h;
+ if(id_val(h=hd[hd[bindings]])!=UNDEF)
+ { errs=hd[e]; nameclash(h); return; }
+ id_val(h)=tl[hd[bindings]];
+ if(id_who(h)!=NIL)speclocs=cons(cons(h,id_who(h)),speclocs);
+ id_who(h)=hd[e]; /* here-info */
+ if(id_type(h)==undef_t)addtoenv(h);
+ bindings = tl[bindings];
+ }
+}}
+
+scanpattern(p,x,e,fail) /* declare ids in x as components of `p=e', each as
+ n = ($p.n)e, result is list of bindings */
+word p,x,e,fail;
+{ if(hd[x]==CONST||isconstructor(x))return(NIL);
+ if(tag[x]==ID){ word binding=
+ cons(x,ap2(TRY,ap(lambda(p,x),e),fail));
+ return(cons(binding,NIL)); }
+ if(tag[x]==AP&&tag[hd[x]]==AP&&hd[hd[x]]==PLUS) /* n+k pattern */
+ return(scanpattern(p,tl[x],e,fail));
+ return(shunt(scanpattern(p,hd[x],e,fail),scanpattern(p,tl[x],e,fail)));
+}
+
+get_ids(x) /* return list of names in pattern x (without repetitions) */
+word x;
+{ if(hd[x]==CONST||isconstructor(x))return(NIL);
+ if(tag[x]==ID)return(cons(x,NIL));
+ if(tag[x]==AP&&tag[hd[x]]==AP&&hd[hd[x]]==PLUS) /* n+k pattern */
+ return(get_ids(tl[x]));
+ return(UNION(get_ids(hd[x]),get_ids(tl[x])));
+}
+
+mktuple(x) /* extract tuple-structure of names from pattern x */
+word x;
+{ if(hd[x]==CONST||isconstructor(x))return(NIL);
+ if(tag[x]==ID)return(x);
+ if(tag[x]==AP&&tag[hd[x]]==AP&&hd[hd[x]]==PLUS) /* n+k pattern */
+ return(mktuple(tl[x]));
+{ word y=mktuple(tl[x]); x=mktuple(hd[x]);
+ return(x==NIL?y:y==NIL?x:pair(x,y));
+}}
+
+decl1(x,e) /* declare name x to have the value denoted by e */
+word x,e;
+{ if(id_val(x)!=UNDEF&&lastname!=x)
+ { errs=hd[e]; nameclash(x); return; }
+ if(id_val(x)==UNDEF)
+ { id_val(x)= tries(x,cons(e,NIL));
+ if(id_who(x)!=NIL)speclocs=cons(cons(x,id_who(x)),speclocs);
+ id_who(x)= hd[e]; /* here-info */
+ if(id_type(x)==undef_t)addtoenv(x);
+ } else
+ if(!fallible(hd[tl[id_val(x)]]))
+ errs=hd[e],
+ printf("%ssyntax error: unreachable case in defn of \"%s\"\n",
+ echoing?"\n":"",get_id(x)),
+ acterror();
+ else tl[id_val(x)]= cons(e,tl[id_val(x)]);
+/* multi-clause definitions are composed as tries(id,rhs_list)
+ where id is included purely for diagnostic purposes
+ note that rhs_list is reversed - put right by code generation */
+}
+
+fallible(e) /* e is "fallible" rhs - if not sure, says yes */
+word e;
+{ for(;;)
+ { if(tag[e]==LABEL)e=tl[e];
+ if(tag[e]==LETREC||tag[e]==LET)e=tl[e]; else
+ if(tag[e]==LAMBDA)
+ if(irrefutable(hd[e]))e=tl[e];
+ else return(1); else
+ if(tag[e]==AP&&tag[hd[e]]==AP&&tag[hd[hd[e]]]==AP&&hd[hd[hd[e]]]==COND)
+ e=tl[e]; else
+ return(e==FAIL); /* test for nested (COND a b FAIL) */
+ }
+} /* NOTE
+ When an rhs contains FAIL as a result of compiling an elseless guard set
+ it is of the form
+ XX ::= ap3(COND,a,b,FAIL) | let[rec](def[s],XX) | lambda(pat,XX)
+ an rhs is fallible if
+ 1) it is an XX, as above, or
+ 2) it is of the form lambda(pat1,...,lambda(patn,e)...)
+ where at least one of the patterns pati is refutable.
+ */
+
+/* combinator to select i'th out of n args *//*
+k(i,n)
+int i,n;
+{ if(i==1)return(n==1?I:n==2?K:ap2(B,K,k(1,n-1)));
+ if(i==2&&n==2)return(KI); /* redundant but saves space *//*
+ return(ap(K,k(i-1,n-1)));
+} */
+
+#define arity_check if(t_arity(tf)!=arity)\
+ printf("%ssyntax error: \
+wrong number of parameters for typename \"%s\" (%d expected)\n",\
+ echoing?"\n":"",get_id(tf),t_arity(tf)),errs=here,acterror()
+
+decltype(tf,class,info,here) /* declare a user defined type */
+word tf,class,info,here;
+{ word arity=0;
+ extern word errs;
+ while(tag[tf]==AP)arity++,tf=hd[tf];
+ if(class==synonym_t&&id_type(tf)==type_t&&t_class(tf)==abstract_t
+ &&t_info(tf)==undef_t)
+ { /* this is binding for declared but not yet bound abstract typename */
+ arity_check;
+ id_who(tf)=here;
+ t_info(tf)=info;
+ return; }
+ if(class==abstract_t&&id_type(tf)==type_t&&t_class(tf)==synonym_t)
+ { /* this is abstype declaration of already bound typename */
+ arity_check;
+ t_class(tf)=abstract_t;
+ return; }
+ if(id_val(tf)!=UNDEF)
+ { errs=here; nameclash(tf); return; }
+ if(class!=synonym_t)newtyps=add1(tf,newtyps);
+ id_val(tf)=make_typ(arity,class==algebraic_t?make_pn(UNDEF):0,class,info);
+ if(id_type(tf)!=undef_t){ errs=here; respec_error(tf); return; }
+ else addtoenv(tf);
+ id_who(tf)=here;
+ id_type(tf)=type_t;
+}
+
+declconstr(x,n,t) /* declare x to be constructor number n of type t */
+word x,n,t; /* x must be an identifier */
+{ id_val(x)=constructor(n,x);
+ if(n>>16)
+ { syntax("algebraic type has too many constructors\n"); return; }
+ if(id_type(x)!=undef_t){ errs=id_who(x); respec_error(x); return; }
+ else addtoenv(x);
+ id_type(x) = t;
+} /* the value of a constructor x is constructor(constr_tag,x)
+ where constr_tag is a small natural number */
+
+/* #define DEPSDEBUG .
+ /* switches on debugging printouts for dependency analysis in block() */
+#ifdef DEPSDEBUG
+pd(def)
+word def;
+{ out1(stdout,dlhs(def)); }
+
+pdlist(defs)
+word defs;
+{ putchar('(');
+ for(;defs!=NIL;defs=tl[defs])
+ pd(hd[defs]),printf(tl[defs]==NIL?"":",");
+ putchar(')');
+}
+#endif
+
+block(defs,e,keep) /* semantics of "where" - performs dependency analysis */
+/* defs has form list(defn(pat,typ,val)), e is body of block */
+/* if `keep' hold together as single letrec */
+word defs,e,keep;
+{ word ids=NIL,deftoids=NIL,g=NIL,d;
+ extern word SYNERR,detrop;
+ /* return(letrec(defs,e)); /* release one semantics was just this */
+ if(SYNERR)return(NIL); /* analysis falls over on empty patterns */
+ for(d=defs;d!=NIL;d=tl[d]) /* first collect all ids defined in block */
+ { word x = get_ids(dlhs(hd[d]));
+ ids=UNION(ids,x);
+ deftoids=cons(cons(hd[d],x),deftoids);
+ }
+ defs=sort(defs);
+ for(d=defs;d!=NIL;d=tl[d]) /* now build dependency relation g */
+ { word x=intersection(deps(dval(hd[d])),ids),y=NIL;
+ for(;x!=NIL;x=tl[x]) /* replace each id by corresponding def */
+ y=add1(invgetrel(deftoids,hd[x]),y);
+ g=cons(cons(hd[d],add1(hd[d],y)),g);
+ /* treat all defs as recursive for now */
+ }
+ g=reverse(g); /* keep in address order of first components */
+#ifdef DEPSDEBUG
+ { word g1=g;
+ printf("g=");
+ for(;g1!=NIL;g1=tl[g1])
+ pd(hd[hd[g1]]),putchar(':'),pdlist(tl[hd[g1]]),putchar(';');
+ printf("\n"); }
+#endif
+/* g is list(cons(def,defs))
+ where defs are all on which def immediately depends, plus self */
+ g = tclos(g); /* now g is list(cons(def,ultdefs)) */
+#ifdef DEPSDEBUG
+ { word g1=g;
+ printf("tclos(g)=");
+ for(;g1!=NIL;g1=tl[g1])
+ pd(hd[hd[g1]]),putchar(':'),pdlist(tl[hd[g1]]),putchar(';');
+ printf("\n"); }
+#endif
+ { /* check for unused definitions */
+ word x=intersection(deps(e),ids),y=NIL,*g1= &g;
+ for(;x!=NIL;x=tl[x])
+ { word d=invgetrel(deftoids,hd[x]);
+ if(!member(y,d))y=UNION(y,getrel(g,d)); }
+ defs=setdiff(defs,y); /* these are de trop */
+ if(defs!=NIL)detrop=append1(detrop,defs);
+ if(keep) /* if local polymorphism not required */
+ return(letrec(y,e)); /* analysis was solely to find unwanted defs */
+ /* remove redundant entries from g */
+ /* no, leave in for typecheck - could remove afterwards
+ while(*g1!=NIL&&defs!=NIL)
+ if(hd[hd[*g1]]==hd[defs])*g1=tl[*g1]; else
+ if(hd[hd[*g1]]<hd[defs])g1= &tl[*g1];
+ else defs=tl[defs]; */
+ }
+ g = msc(g); /* g is list(defgroup,ultdefs) */
+#ifdef DEPSDEBUG
+ { word g1=g;
+ printf("msc(g)=");
+ for(;g1!=NIL;g1=tl[g1])
+ pdlist(hd[hd[g1]]),putchar(':'),pdlist(tl[hd[g1]]),putchar(';');
+ printf("\n"); }
+#endif
+ g = tsort(g); /* g is list(defgroup) in dependency order */
+#ifdef DEPSDEBUG
+ { word g1=g;
+ printf("tsort(g)=");
+ for(;g1!=NIL;g1=tl[g1])
+ pdlist(hd[g1]),putchar(';');
+ printf("\n"); }
+#endif
+ g = reverse(g); /* reconstruct block inside-first */
+ while(g!=NIL)
+ { if(tl[hd[g]]==NIL &&
+ intersection(get_ids(dlhs(hd[hd[g]])),deps(dval(hd[hd[g]])))==NIL
+ )e=let(hd[hd[g]],e); /* single non-recursive def */
+ else e=letrec(hd[g],e);
+ g=tl[g]; }
+ return(e);
+}
+/* Implementation note:
+ tsort will fall over if there is a non-list strong component because it
+ was originally written on assumption that relation is over identifiers.
+ Whence need to pretend all defs recursive until after tsort.
+ Could do better - some defs may be subsidiary to others */
+
+tclos(r) /* fast transitive closure - destructive in r */
+word r; /* r is of form list(cons(x,xs)) */
+{ word r1;
+ for(r1=r;r1!=NIL;r1=tl[r1])
+ { word x= less1(tl[hd[r1]],hd[hd[r1]]);
+ /* invariant x intersect tl[hd[r1]] = NIL */
+ while(x!=NIL)
+ { x=imageless(r,x,tl[hd[r1]]);
+ tl[hd[r1]]=UNION(tl[hd[r1]],x); }
+ }
+ return(r);
+}
+
+getrel(r,x) /* r is list(cons(x,xs)) - return appropriate xs, else NIL */
+word r,x;
+{ while(r!=NIL&&hd[hd[r]]!=x)r=tl[r];
+ return(r==NIL?NIL:tl[hd[r]]);
+}
+
+invgetrel(r,x) /* return first x1 such that `x1 r x' error if none found */
+word r,x;
+{ while(r!=NIL&&!member(tl[hd[r]],x))r=tl[r];
+ if(r==NIL)fprintf(stderr,"impossible event in invgetrel\n"),exit(1);
+ return(hd[hd[r]]);
+}
+
+
+imageless(r,y,z) /* image of set y in reln r, less set z */
+word r,y,z;
+{ word i=NIL;
+ while(r!=NIL&&y!=NIL)
+ if(hd[hd[r]]==hd[y])
+ i=UNION(i,less(tl[hd[r]],z)),r=tl[r],y=tl[y]; else
+ if(hd[hd[r]]<hd[y])r=tl[r];
+ else y=tl[y];
+ return(i);
+}
+
+less(x,y) /* non-destructive set difference x-y */
+word x,y;
+{ word r=NIL;
+ while(x!=NIL&&y!=NIL)
+ if(hd[x]==hd[y])x=tl[x],y=tl[y]; else
+ if(hd[x]<hd[y])r=cons(hd[x],r),x=tl[x];
+ else y=tl[y];
+ return(shunt(r,x));
+}
+
+less1(x,a) /* non-destructive set difference x- {a} */
+word x,a;
+{ word r=NIL;
+ while(x!=NIL&&hd[x]!=a)r=cons(hd[x],r),x=tl[x];
+ return(shunt(r,x==NIL?NIL:tl[x]));
+}
+
+sort(x) /* into address order */
+word x;
+{ word a=NIL,b=NIL,hold=NIL;
+ if(x==NIL||tl[x]==NIL)return(x);
+ while(x!=NIL) /* split x */
+ { hold=a,a=cons(hd[x],b),b=hold;
+ x=tl[x]; }
+ a=sort(a),b=sort(b);
+ /* now merge two halves back together */
+ while(a!=NIL&&b!=NIL)
+ if(hd[a]<hd[b])x=cons(hd[a],x),a=tl[a];
+ else x=cons(hd[b],x),b=tl[b];
+ if(a==NIL)a=b;
+ while(a!=NIL)x=cons(hd[a],x),a=tl[a];
+ return(reverse(x));
+}
+
+sortrel(x) /* sort relation into address order of first components */
+word x; /* x is a list of cons(y,ys) */
+{ word a=NIL,b=NIL,hold=NIL;
+ if(x==NIL||tl[x]==NIL)return(x);
+ while(x!=NIL) /* split x */
+ { hold=a,a=cons(hd[x],b),b=hold;
+ x=tl[x]; }
+ a=sortrel(a),b=sortrel(b);
+ /* now merge two halves back together */
+ while(a!=NIL&&b!=NIL)
+ if(hd[hd[a]]<hd[hd[b]])x=cons(hd[a],x),a=tl[a];
+ else x=cons(hd[b],x),b=tl[b];
+ if(a==NIL)a=b;
+ while(a!=NIL)x=cons(hd[a],x),a=tl[a];
+ return(reverse(x));
+}
+
+specify(x,t,h) /* semantics of a "::" statement */
+word x,t,h; /* N.B. t not yet in reduced form */
+{ extern word showwhat;
+ if(tag[x]!=ID&&t!=type_t){ errs=h;
+ syntax("incorrect use of ::\n");
+ return; }
+ if(t==type_t)
+ { word a=0;
+ while(tag[x]==AP)a++,x=hd[x];
+ if(!(id_val(x)==UNDEF&&id_type(x)==undef_t))
+ { errs=h; nameclash(x); return; }
+ id_type(x)=type_t;
+ if(id_who(x)==NIL)id_who(x)=h; /* premise always true, see above */
+ /* if specified and defined, locate by definition */
+ id_val(x)=make_typ(a,showwhat,placeholder_t,NIL);/* placeholder type */
+ addtoenv(x);
+ newtyps=add1(x,newtyps);
+ return; }
+ if(id_type(x)!=undef_t){ errs=h; respec_error(x); return; }
+ id_type(x)=t;
+ if(id_who(x)==NIL)id_who(x)=h; /* as above */
+ else speclocs=cons(cons(x,h),speclocs);
+ if(id_val(x)==UNDEF)addtoenv(x);
+}
+
+respec_error(x) /* only one type spec per name allowed - IS THIS RIGHT? */
+word x;
+{ extern word primenv;
+ if(echoing)putchar('\n');
+ printf("syntax error: type of \"%s\" already declared%s\n",get_id(x),
+ member(primenv,x)?" (in standard environment)":"");
+ acterror();
+}
+
+nameclash(x) /* only one top level binding per name allowed */
+word x;
+{ extern word primenv;
+ if(echoing)putchar('\n');
+ printf("syntax error: nameclash, \"%s\" already defined%s\n",get_id(x),
+ member(primenv,x)?" (in standard environment)":"");
+ acterror();
+}
+
+nclashcheck(n,dd,hr) /* is n already bound in list of definitions dd */
+word n,dd,hr;
+{ while(dd!=NIL&&!nclchk(n,dlhs(hd[dd]),hr))dd=tl[dd];
+}
+
+nclchk(n,p,hr) /* is n already bound in pattern p */
+word n,p,hr;
+{ if(hd[p]==CONST)return(0);
+ if(tag[p]==ID)
+ { if(n!=p)return(0);
+ if(echoing)putchar('\n');
+ errs=hr,
+ printf(
+"syntax error: conflicting definitions of \"%s\" in where clause\n",
+ get_id(n)),
+ acterror();
+ return(1); }
+ if(tag[p]==AP&&hd[p]==PLUS) /* hd of n+k pattern */
+ return(0);
+ return(nclchk(n,hd[p],hr)||nclchk(n,tl[p],hr));
+}
+
+transtypeid(x) /* recognises literal type constants - see RULES */
+word x;
+{ char *n=get_id(x);
+ return(strcmp(n,"bool")==0?bool_t:
+ strcmp(n,"num")==0?num_t:
+ strcmp(n,"char")==0?char_t:
+ x);
+}
+
+/* end of MIRANDA TRANS */
+
diff --git a/new/types.c b/new/types.c
new file mode 100644
index 0000000..f20627f
--- /dev/null
+++ b/new/types.c
@@ -0,0 +1,1613 @@
+/* MIRANDA TYPECHECKER */
+
+/**************************************************************************
+ * Copyright (C) Research Software Limited 1985-90. All rights reserved. *
+ * The Miranda system is distributed as free software under the terms in *
+ * the file "COPYING" which is included in the distribution. *
+ *------------------------------------------------------------------------*/
+
+#include "data.h"
+#include "big.h"
+word R=NIL; /* direct-and-indirect dependency graph */
+word TABSTRS=NIL; /* list of abstype declarations */
+word ND; /* undefined names used in script */
+word SBND; /* names specified but not defined (handled separately) */
+word FBS; /* list of bindings caused by parameterised %include's */
+word ATNAMES; /* global var set by abstr_check */
+word NT=NIL; /* undefined typenames used in script */
+word TYPERRS;
+word bnf_t=0;
+word current_id=0,lastloc=0,lineptr=0; /* used to locate type errors */
+word showchain=NIL; /* links together all occurrences of special forms (show)
+ encountered during typecheck */
+extern word rfl;
+
+#include <setjmp.h>
+jmp_buf env1; /* for longjmp - see man (3) setjmp */
+
+checktypes() /* outcome indicated by setting of flags SYNERR, TYPERRS, ND */
+{ word s;
+ extern word freeids,SYNERR,fnts;
+ ATNAMES=TYPERRS=0;
+ NT=R=SBND=ND=NIL; /* NT=R= added 4/6/88 */
+ if(setjmp(env1)==1)goto L;
+ if(rfl!=NIL)readoption();
+ for(s=reverse(fil_defs(hd[files]));s!=NIL;s=tl[s])
+ comp_deps(hd[s]); /* for each identifier in current script, compute
+ dependencies to form R */
+ R=tclos(sortrel(R));
+ if(FBS!=NIL)mcheckfbs();
+ abstr_mcheck(TABSTRS);
+L:if(TYPERRS)
+ { /* badly formed types, so give up */
+ TABSTRS=NT=R=NIL;
+ printf("typecheck cannot proceed - compilation abandoned\n");
+ SYNERR=1;
+ return; }
+ if(freeids!=NIL)redtfr(freeids);
+ /* printgraph("dependency analysis:",R); /* for debugging */
+ genshfns();
+ if(fnts!=NIL)genbnft();
+ R=msc(R);
+ /* printgraph("strong components:",R); /* for debugging */
+ s=tsort(R);
+ /* printlist("topological sort:",s); /* for debugging */
+ NT=R=NIL; /* must be invariant across the call */
+ while(s!=NIL)infer_type(hd[s]),s=tl[s];
+ checkfbs();
+ while(TABSTRS!=NIL)
+ abstr_check(hd[TABSTRS]),TABSTRS=tl[TABSTRS];
+ if(SBND!=NIL)
+ printlist("SPECIFIED BUT NOT DEFINED: ",alfasort(SBND)),SBND=NIL;
+ fixshows();
+ lastloc=0;
+ return;
+}
+
+/* NOTES
+ let element ::= id | list(id)
+ let graph1 ::= list(cons(id,list(id)))
+ let graph2 ::= list(cons(element,list(id)))
+ we define:
+ comp_deps(id)->builds R::graph1, direct dependencies
+ R=tclos(sortrel(R))::graph1, direct and indirect dependencies
+ msc(R)->collects maximal strong components in R, now R::graph2
+ tsort(graph2)->list(element), topologically sorted
+ infer_type(element)->fills in the id_type field(s) of element
+*/
+/* R occupies quadratic worst-case space - does anyone know a better way? */
+
+comp_deps(n) /* adds to R an entry of the form cons(n,RHS) where n is an
+ identifier and RHS is a list of all the identifiers in the
+ current script upon which n directly depends */
+/* it also meta-typechecks type specifications, and puts them in reduced
+ form, as it goes */
+word n;
+{ word rhs=NIL,r;
+ /* printf("comp_deps(%s)\n",get_id(n)); /* DEBUG */
+ if(id_type(n)==type_t)
+ { if(t_class(n)==algebraic_t)
+ { r=t_info(n);
+ while(r!=NIL) /* meta type check constructors */
+ { current_id=hd[r];
+ id_type(hd[r])=redtvars(meta_tcheck(id_type(hd[r])));
+ r=tl[r]; }
+ }
+ else if(t_class(n)==synonym_t)
+ current_id=n,t_info(n)=meta_tcheck(t_info(n));
+ else if(t_class(n)==abstract_t)
+ if(t_info(n)==undef_t)
+ printf("error: script contains no binding for abstract typename\
+ \"%s\"\n",get_id(n)),sayhere(id_who(n),1),TYPERRS++;
+ else current_id=n,t_info(n)=meta_tcheck(t_info(n));
+ /* placeholder types - no action */
+ current_id=0;
+ return; }
+ if(tag[id_val(n)]==CONSTRUCTOR)return;
+ /* primitive constructors require no type analysis */
+ if(id_type(n)!=undef_t) /* meta typecheck spec, if present */
+ { current_id=n;
+ if(tag[id_type(n)]==CONS)
+ { /* signature identifier */
+ if(id_val(n)==UNDEF)SBND=add1(n,SBND);
+ id_type(n)=redtvars(meta_tcheck(hd[id_type(n)]));
+ current_id=0;
+ return; } /* typechecked separately, under TABSTRS */
+ id_type(n)=redtvars(meta_tcheck(id_type(n)));
+ current_id=0; }
+ if(id_val(n)==FREE)return; /* no further analysis required */
+ if(id_val(n)==UNDEF) /* name specified but not defined */
+ { SBND=add1(n,SBND); /* change of policy (as for undefined sigid, above) */
+ return; } /* now not added to ND, so script can be %included */
+ r=deps(id_val(n));
+ while(r!=NIL)
+ { if(id_val(hd[r])!=UNDEF&&id_type(hd[r])==undef_t)
+ /* only defined names without explicitly assigned types
+ cause dependency */
+ rhs=add1(hd[r],rhs);
+ r=tl[r]; }
+ R=cons(cons(n,rhs),R);
+}
+
+tsort(g) /* topological sort - returns a list of the elements in the domain
+ of relation g, in an order such that each element is preceded by everything
+ it depends on */
+word g; /* the structure of g is "graph2" see NOTES above */
+{ word NP=NIL; /* NP is set of elements with no predecessor */
+ word g1=g, r=NIL; /* r is result */
+ g=NIL;
+ while(g1!=NIL)
+ { if(tl[hd[g1]]==NIL)NP=cons(hd[hd[g1]],NP);
+ else g=cons(hd[g1],g);
+ g1=tl[g1]; }
+ while(NP!=NIL)
+ { word D=NIL; /* ids to be removed from range of g */
+ while(NP!=NIL)
+ { r=cons(hd[NP],r);
+ if(tag[hd[NP]]==ID)D=add1(hd[NP],D);
+ else D=UNION(D,hd[NP]);
+ NP=tl[NP]; }
+ g1=g;g=NIL;
+ while(g1!=NIL)
+ { word rhs=setdiff(tl[hd[g1]],D);
+ if(rhs==NIL)NP=cons(hd[hd[g1]],NP);
+ else tl[hd[g1]]=rhs,g=cons(hd[g1],g);
+ g1=tl[g1]; }
+ }
+ if(g!=NIL)fprintf(stderr,"error: impossible event in tsort\n");
+ return(reverse(r));
+}
+
+msc(R) /* collects maximal strong components in R, converting it from "graph1"
+ to "graph2" form - destructive in R */
+word R;
+{ word R1=R;
+ while(R1!=NIL)
+ { word *r= &tl[hd[R1]],l=hd[hd[R1]];
+ if(remove1(l,r))
+ { hd[hd[R1]]=cons(l,NIL);
+ while(*r!=NIL)
+ { word n=hd[*r],*R2= &tl[R1];
+ while(*R2!=NIL&&hd[hd[*R2]]!=n)R2= &tl[*R2]; /* find n-entry in R */
+ if(*R2!=NIL&&member(tl[hd[*R2]],l))
+ { *r=tl[*r]; /* remove n from r */
+ *R2=tl[*R2]; /* remove n's entry from R */
+ hd[hd[R1]]=add1(n,hd[hd[R1]]);
+ }
+ else r= &tl[*r];
+ }
+ }
+ R1=tl[R1];
+ }
+ return(R);
+}
+
+word meta_pending=NIL;
+
+meta_tcheck(t) /* returns type t with synonyms substituted out and checks that
+ the result is well formed */
+word t;
+{ word tn=t,i=0;
+ /* TO DO -- TIDY UP ERROR MESSAGES AND SET ERRLINE (ERRS) IF POSS */
+ while(iscompound_t(tn))
+ tl[tn]=meta_tcheck(tl[tn]),i++,tn=hd[tn];
+ if(tag[tn]==STRCONS)goto L; /* patch to handle free type bindings */
+ if(tag[tn]!=ID)
+ { if(i>0&&(isvar_t(tn)||tn==bool_t||tn==num_t||tn==char_t))
+ { TYPERRS++;
+ if(tag[current_id]==DATAPAIR)
+ locate_inc(),
+ printf("badly formed type \""),out_type(t),
+ printf("\" in binding for \"%s\"\n",(char *)hd[current_id]),
+ printf("("),out_type(tn),printf(" has zero arity)\n");
+ else
+ printf("badly formed type \""),out_type(t),
+ printf("\" in %s for \"%s\"\n",
+ id_type(current_id)==type_t?"== binding":"specification",
+ get_id(current_id)),
+ printf("("),out_type(tn),printf(" has zero arity)\n"),
+ sayhere(getspecloc(current_id),1);
+ sterilise(t); }
+ return(t); }
+ if(id_type(tn)==undef_t&&id_val(tn)==UNDEF)
+ { TYPERRS++;
+ if(!member(NT,tn))
+ { if(tag[current_id]==DATAPAIR)locate_inc();
+ printf("undeclared typename \"%s\" ",get_id(tn));
+ if(tag[current_id]==DATAPAIR)
+ printf("in binding for %s\n",(char *)hd[current_id]);
+ else sayhere(getspecloc(current_id),1);
+ NT=add1(tn,NT); }
+ return(t); }else
+ if(id_type(tn)!=type_t||t_arity(tn)!=i)
+ { TYPERRS++;
+ if(tag[current_id]==DATAPAIR)
+ locate_inc(),
+ printf("badly formed type \""),out_type(t),
+ printf("\" in binding for \"%s\"\n",(char *)hd[current_id]);
+ else
+ printf("badly formed type \""),out_type(t),
+ printf("\" in %s for \"%s\"\n",
+ id_type(current_id)==type_t?"== binding":"specification",
+ get_id(current_id));
+ if(id_type(tn)!=type_t)
+ printf("(%s not defined as typename)\n",get_id(tn));
+ else printf("(typename %s has arity %d)\n",get_id(tn),t_arity(tn));
+ if(tag[current_id]!=DATAPAIR)
+ sayhere(getspecloc(current_id),1);
+ sterilise(t);
+ return(t); }
+L:if(t_class(tn)!=synonym_t)return(t);
+ if(member(meta_pending,tn))
+ { TYPERRS++;/* report cycle */
+ if(tag[current_id]==DATAPAIR)locate_inc();
+ printf("error: cycle in type \"==\" definition%s ",
+ meta_pending==NIL?"":"s");
+ printelement(meta_pending); putchar('\n');
+ if(tag[current_id]!=DATAPAIR)
+ sayhere(id_who(tn),1);
+ longjmp(env1,1); /* fatal error - give up */
+/* t_class(tn)=algebraic_t;t_info(tn)=NIL;
+ /* to make sure we dont fall in here again! */
+ return(t); }
+ meta_pending=cons(tn,meta_pending);
+ tn=NIL;
+ while(iscompound_t(t))
+ tn=cons(tl[t],tn),t=hd[t];
+ t=meta_tcheck(ap_subst(t_info(t),tn));
+ meta_pending=tl[meta_pending];
+ return(t);
+}
+/* needless inefficiency - we recheck the rhs of a synonym every time we
+ use it */
+
+sterilise(t) /* to prevent multiple reporting of metatype errors from
+ namelist :: type */
+word t;
+{ if(tag[t]==AP)hd[t]=list_t,tl[t]=num_t;
+}
+
+word tvcount=1;
+#define NTV mktvar(tvcount++)
+ /* brand new type variable */
+#define reset_SUBST (current_id=tvcount>=hashsize?clear_SUBST():0)
+
+infer_type(x) /* deduces the types of the identifiers in x - no result,
+ works by filling in id_type fields */
+word x; /* x is an "element" */
+{ if(tag[x]==ID)
+ { word t,oldte=TYPERRS;
+ current_id=x;
+ t = subst(etype(id_val(x),NIL,NIL));
+ if(id_type(x)==undef_t)id_type(x)=redtvars(t);
+ else /* x already has assigned type */
+ if(!subsumes(t,instantiate(id_type(x))))
+ { TYPERRS++;
+ printf("incorrect declaration ");
+ sayhere(getspecloc(x),1); /* or: id_who(x) to locate defn */
+ printf("specified, "); report_type(x); putchar('\n');
+ printf("inferred, %s :: ",get_id(x)); out_type(redtvars(t));
+ putchar('\n'); }
+ if(TYPERRS>oldte)id_type(x)=wrong_t,
+ id_val(x)=UNDEF,
+ ND=add1(x,ND);
+ reset_SUBST; }
+ else{ /* recursive group of names */
+ word x1,oldte,ngt=NIL;
+ for(x1=x;x1!=NIL;x1=tl[x1])
+ ngt=cons(NTV,ngt),
+ id_type(hd[x1])=ap(bind_t,hd[ngt]);
+ for(x1=x;x1!=NIL;x1=tl[x1])
+ { oldte=TYPERRS,
+ current_id=hd[x1],
+ unify(tl[id_type(hd[x1])],etype(id_val(hd[x1]),NIL,ngt));
+ if(TYPERRS>oldte)
+ id_type(hd[x1])=wrong_t,
+ id_val(hd[x1])=UNDEF,ND=add1(hd[x1],ND); }
+ for(x1=x;x1!=NIL;x1=tl[x1])
+ if(id_type(hd[x1])!=wrong_t)
+ id_type(hd[x1])=redtvars(ult(tl[id_type(hd[x1])]));
+ reset_SUBST;
+ }
+}
+
+word hereinc; /* location of currently-being-processed %include */
+word lasthereinc;
+
+mcheckfbs()
+{ word ff,formals,n;
+ lasthereinc=0;
+ for(ff=FBS;ff!=NIL;ff=tl[ff])
+ { hereinc=hd[hd[FBS]];
+ for(formals=tl[hd[ff]];formals!=NIL;formals=tl[formals])
+ { word t=tl[tl[hd[formals]]];
+ if(t!=type_t)continue;
+ current_id=hd[tl[hd[formals]]]; /* nb datapair(orig,0) not id */
+ t_info(hd[hd[formals]])=meta_tcheck(t_info(hd[hd[formals]]));
+ /*ATNAMES=cons(hd[hd[formals]],ATNAMES?ATNAMES:NIL); */
+ current_id=0;
+ }
+ if(TYPERRS)return; /* to avoid misleading error messages */
+ for(formals=tl[hd[ff]];formals!=NIL;formals=tl[formals])
+ { word t=tl[tl[hd[formals]]];
+ if(t==type_t)continue;
+ current_id=hd[tl[hd[formals]]]; /* nb datapair(orig,0) not id */
+ tl[tl[hd[formals]]]=redtvars(meta_tcheck(t));
+ current_id=0;
+ }
+ /* above double traverse is very inefficient way of doing types first
+ would be better to have bindings sorted in this order beforehand */
+ }
+ /* all imported names must now have their types reduced to
+ canonical form wrt the parameter bindings */
+ /* alternative method - put info in ATNAMES, see above and in abstr_check */
+ /* a problem with this is that types do not print in canonical form */
+ if(TYPERRS)return;
+ for(ff=tl[files];ff!=NIL;ff=tl[ff])
+ for(formals=fil_defs(hd[ff]);formals!=NIL;formals=tl[formals])
+ if(tag[n=hd[formals]]==ID)
+ if(id_type(n)==type_t)
+ { if(t_class(n)==synonym_t)t_info(n)=meta_tcheck(t_info(n)); }
+ else id_type(n)=redtvars(meta_tcheck(id_type(n)));
+} /* wasteful if many includes */
+
+redtfr(x) /* ensure types of freeids are in reduced form */
+word x;
+{ for(;x!=NIL;x=tl[x])
+ tl[tl[hd[x]]] = id_type(hd[hd[x]]);
+}
+
+checkfbs()
+/* FBS is list of entries of form cons(hereinfo,formals) where formals
+ has elements of form cons(id,cons(datapair(orig,0),type)) */
+{ word oldte=TYPERRS,formals;
+ lasthereinc=0;
+ for(;FBS!=NIL;FBS=tl[FBS])
+ for(hereinc=hd[hd[FBS]],formals=tl[hd[FBS]];
+ formals!=NIL;formals=tl[formals])
+ { word t,t1=fix_type(tl[tl[hd[formals]]]);
+ if(t1==type_t)continue;
+ current_id=hd[tl[hd[formals]]]; /* nb datapair(orig,0) not id */
+ t = subst(etype(the_val(hd[hd[formals]]),NIL,NIL));
+ if(!subsumes(t,instantiate(t1)))
+ { TYPERRS++;
+ locate_inc();
+ printf("binding for parameter `%s' has wrong type\n",
+ (char *)hd[current_id]);
+ printf( "required :: "); out_type(tl[tl[hd[formals]]]);
+ printf("\n actual :: "); out_type(redtvars(t));
+ putchar('\n'); }
+ the_val(hd[hd[formals]])=codegen(the_val(hd[hd[formals]])); }
+ if(TYPERRS>oldte)
+ { /* badly typed parameter bindings, so give up */
+ extern word SYNERR;
+ TABSTRS=NT=R=NIL;
+ printf("compilation abandoned\n");
+ SYNERR=1; }
+ reset_SUBST;
+}
+
+fix_type(t,x) /* substitute out any indirected typenames in t */
+word t,x;
+{ switch(tag[t])
+ { case AP:
+ case CONS: tl[t]=fixtype(tl[t],x);
+ hd[t]=fixtype(hd[t],x);
+ default: return(t);
+ case STRCONS: while(tag[pn_val(t)]!=CONS)t=pn_val(t);/*at most twice*/
+ return(t);
+ }
+}
+
+locate_inc()
+{ if(lasthereinc==hereinc)return;
+ printf("incorrect %%include directive ");
+ sayhere(lasthereinc=hereinc,1);
+}
+
+abstr_mcheck(tabstrs) /* meta-typecheck abstract type declarations */
+word tabstrs;
+{ while(tabstrs!=NIL)
+ { word atnames=hd[hd[tabstrs]],sigids=tl[hd[tabstrs]],rtypes=NIL;
+ if(cyclic_abstr(atnames))return;
+ while(sigids!=NIL) /* compute representation types */
+ { word rt=rep_t(id_type(hd[sigids]),atnames);
+ /*if(rt==id_type(hd[sigids]))
+ printf("abstype declaration error: \"%s\" has a type unrelated to \
+the abstraction\n",get_id(hd[sigids])),
+ sayhere(getspecloc(hd[sigids]),1),
+ TYPERRS++; /* suppressed June 89, see karen.m, secret.m */
+ rtypes=cons(rt,rtypes);
+ sigids=tl[sigids]; }
+ rtypes=reverse(rtypes);
+ hd[hd[tabstrs]]=cons(hd[hd[tabstrs]],rtypes);
+ tabstrs=tl[tabstrs];
+ }
+}
+
+abstr_check(x) /* typecheck the implementation equations of a type abstraction
+ with the given signature */
+word x;
+{ word rtypes=tl[hd[x]],sigids=tl[x];
+/*int holdat=ATNAMES;
+ ATNAMES=shunt(hd[hd[x]],ATNAMES); */
+ ATNAMES=hd[hd[x]];
+ txchange(sigids,rtypes); /* install representation types */
+ /* report_types("concrete signature:\n",sigids); /* DEBUG */
+ for(x=sigids;x!=NIL;x=tl[x])
+ { word t,oldte=TYPERRS;
+ current_id=hd[x];
+ t=subst(etype(id_val(hd[x]),NIL,NIL));
+ if(!subsumes(t,instantiate(id_type(hd[x]))))
+ { TYPERRS++;
+ printf("abstype implementation error\n");
+ printf("\"%s\" is bound to value of type: ",get_id(hd[x]));
+ out_type(redtvars(t));
+ printf("\ntype expected: ");
+ out_type(id_type(hd[x]));
+ putchar('\n');
+ sayhere(id_who(hd[x]),1); }
+ if(TYPERRS>oldte)
+ id_type(hd[x])=wrong_t,id_val(hd[x])=UNDEF,ND=add1(hd[x],ND);
+ reset_SUBST; }
+ /* restore the abstract types - for "finger" */
+ for(x=sigids;x!=NIL;x=tl[x],rtypes=tl[rtypes])
+ if(id_type(hd[x])!=wrong_t)id_type(hd[x])=hd[rtypes];
+ ATNAMES= /* holdat */ 0;
+}
+
+cyclic_abstr(atnames) /* immediately-cyclic acts of dta are illegal */
+word atnames;
+{ word x,y=NIL;
+ for(x=atnames;x!=NIL;x=tl[x])y=ap(y,t_info(hd[x]));
+ for(x=atnames;x!=NIL;x=tl[x])
+ if(occurs(hd[x],y))
+ { printf("illegal type abstraction: cycle in \"==\" binding%s ",
+ tl[atnames]==NIL?"":"s");
+ printelement(atnames); putchar('\n');
+ sayhere(id_who(hd[x]),1);
+ TYPERRS++; return(1); }
+ return(0);
+}
+
+txchange(ids,x) /* swap the id_type of each id with the corresponding type
+ in the list x */
+word ids,x;
+{ while(ids!=NIL)
+ { word t=id_type(hd[ids]);
+ id_type(hd[ids])=hd[x],hd[x]=t;
+ ids=tl[ids],x=tl[x]; }
+}
+
+report_type(x)
+word x;
+{ printf("%s",get_id(x));
+ if(id_type(x)==type_t)
+ if(t_arity(x)>5)printf("(arity %d)",t_arity(x));
+ else { word i,j;
+ for(i=1;i<=t_arity(x);i++)
+ { putchar(' ');
+ for(j=0;j<i;j++)putchar('*'); }
+ }
+ printf(" :: ");
+ out_type(id_type(x));
+}
+
+report_types(header,x)
+char *header;
+word x;
+{ printf("%s",header);
+ while(x!=NIL)
+ report_type(hd[x]),putchar(';'),x=tl[x];
+ putchar('\n');
+}
+
+typesfirst(x) /* rearrange list of ids to put types first */
+word x;
+{ word *y= &x,z=NIL;
+ while(*y!=NIL)
+ if(id_type(hd[*y])==type_t)
+ z=cons(hd[*y],z),*y=tl[*y];
+ else y= &tl[*y];
+ return(shunt(z,x));
+}
+
+rep_t1(T,L) /* computes the representation type corresponding to T, wrt the
+ abstract typenames in L */
+ /* will need to apply redtvars to result, see below */
+ /* if no substitutions found, result is identically T */
+word T,L;
+{ word args=NIL,t1,new=0;
+ for(t1=T;iscompound_t(t1);t1=hd[t1])
+ { word a=rep_t1(tl[t1],L);
+ if(a!=tl[t1])new=1;
+ args=cons(a,args); }
+ if(member(L,t1))return(ap_subst(t_info(t1),args));
+ /* call to redtvars removed 26/11/85
+ leads to premature normalisation of subterms */
+ if(!new)return(T);
+ while(args!=NIL)
+ t1=ap(t1,hd[args]),args=tl[args];
+ return(t1);
+}
+
+rep_t(T,L) /* see above */
+word T,L;
+{ word t=rep_t1(T,L);
+ return(t==T?t:redtvars(t));
+}
+
+type_of(x) /* returns the type of expression x, in reduced form */
+word x;
+{ word t;
+ TYPERRS=0;
+ t=redtvars(subst(etype(x,NIL,NIL)));
+ fixshows();
+ if(TYPERRS>0)t=wrong_t;
+ return(t);
+}
+
+checktype(x) /* is expression x well-typed ? */
+ /* not currently used */
+word x;
+{ TYPERRS=0;
+ etype(x,NIL,NIL);
+ reset_SUBST;
+ return(!TYPERRS);
+}
+
+#define bound_t(t) (iscompound_t(t)&&hd[t]==bind_t)
+#define tf(a,b) ap2(arrow_t,a,b)
+#define tf2(a,b,c) tf(a,tf(b,c))
+#define tf3(a,b,c,d) tf(a,tf2(b,c,d))
+#define tf4(a,b,c,d,e) tf(a,tf3(b,c,d,e))
+#define lt(a) ap(list_t,a)
+#define pair_t(x,y) ap2(comma_t,x,ap2(comma_t,y,void_t))
+
+word tfnum,tfbool,tfbool2,tfnum2,tfstrstr,tfnumnum,ltchar,
+ tstep,tstepuntil;
+
+tsetup()
+{ tfnum=tf(num_t,num_t);
+ tfbool=tf(bool_t,bool_t);
+ tfnum2=tf(num_t,tfnum);
+ tfbool2=tf(bool_t,tfbool);
+ ltchar=lt(char_t);
+ tfstrstr=tf(ltchar,ltchar);
+ tfnumnum=tf(num_t,num_t);
+ tstep=tf2(num_t,num_t,lt(num_t));
+ tstepuntil=tf(num_t,tstep);
+}
+
+word exec_t=0,read_t=0,filestat_t=0; /* set lazily, when used */
+
+genlstat_t() /* type of %lex state */
+{ return(pair_t(num_t,num_t)); }
+
+genbnft() /* if %bnf used, find out input type of parsing fns */
+{ word bnftokenstate=findid("bnftokenstate");
+ if(bnftokenstate!=NIL&&id_type(bnftokenstate)==type_t)
+ if(t_arity(bnftokenstate)==0)
+ bnf_t=t_class(bnftokenstate)==synonym_t?
+ t_info(bnftokenstate):bnftokenstate;
+ else printf("warning - bnftokenstate has arity>0 (ignored by parser)\n"),
+ bnf_t=void_t;
+ else bnf_t=void_t; /* now bnf_t holds the state type */
+ bnf_t=ap2(comma_t,ltchar,ap2(comma_t,bnf_t,void_t));
+} /* the input type for parsers is lt(bnf_t)
+ note that tl[hd[tl[bnf_t]]] holds the state type */
+
+extern word col_fn;
+
+checkcolfn() /* if offside rule used, check col_fn has right type */
+{ word t=id_type(col_fn),f=tf(tl[hd[tl[bnf_t]]],num_t);
+ if(t==undef_t||t==wrong_t
+ /* will be already reported - do not generate further typerrs
+ in both cases col_fn will behave as undefined name */
+ ||subsumes(instantiate(t),f))
+ { col_fn=0; return; } /* no further action required */
+ printf("`bnftokenindentation' has wrong type for use in offside rule\n");
+ printf("type required :: "); out_type(f); putchar('\n');
+ printf(" actual type :: "); out_type(t); putchar('\n');
+ sayhere(getspecloc(col_fn),1);
+ TYPERRS++;
+ col_fn= -1; /* error flag */
+} /* note that all parsing fns get type wrong_t if offside rule used
+ anywhere and col_fn has wrong type - strictly this is overkill */
+
+etype(x,env,ngt) /* infer a type for an expression, by using unification */
+word x,env; /* env is list of local bindings of variables to types */
+word ngt; /* ngt is list of non-generic type variables */
+{ word a,b,c,d; /* initialise to 0 ? */
+ switch(tag[x])
+ { case AP: if(hd[x]==BADCASE||hd[x]==CONFERROR)return(NTV);
+ /* don't type check insides of error messages */
+ { word ft=etype(hd[x],env,ngt),at=etype(tl[x],env,ngt),rt=NTV;
+ if(!unify1(ft,ap2(arrow_t,at,rt)))
+ { ft=subst(ft);
+ if(isarrow_t(ft))
+ if(tag[hd[x]]==AP&&hd[hd[x]]==G_ERROR)
+ type_error8(at,tl[hd[ft]]);
+ else
+ type_error("unify","with",at,tl[hd[ft]]);
+ else type_error("apply","to",ft,at);
+ return(NTV); }
+ return(rt); }
+ case CONS: { word ht=etype(hd[x],env,ngt),rt=etype(tl[x],env,ngt);
+ if(!unify1(ap(list_t,ht),rt))
+ { type_error("cons","to",ht,rt);
+ return(NTV); }
+ return(rt); }
+ case LEXER: { word hold=lineptr;
+ lineptr=hd[tl[tl[hd[x]]]];
+ tl[tl[hd[x]]]=tl[tl[tl[hd[x]]]];/*discard label(hereinf,-)*/
+ a=etype(tl[tl[hd[x]]],env,ngt);
+ while((x=tl[x])!=NIL)
+ { lineptr=hd[tl[tl[hd[x]]]];
+ tl[tl[hd[x]]]=tl[tl[tl[hd[x]]]];/*discard label... */
+ if(!unify1(a,b=etype(tl[tl[hd[x]]],env,ngt)))
+ { type_error7(a,b);
+ lineptr=hold;
+ return(NTV); }
+ }
+ lineptr=hold;
+ return(tf(ltchar,lt(a))); }
+ case TCONS: return(ap2(comma_t,etype(hd[x],env,ngt),
+ etype(tl[x],env,ngt)));
+ case PAIR: return(ap2(comma_t,etype(hd[x],env,ngt),
+ ap2(comma_t,etype(tl[x],env,ngt),void_t)));
+ case DOUBLE:
+ case INT: return(num_t);
+ case ID: a=env;
+ while(a!=NIL) /* take local binding, if present */
+ if(hd[hd[a]]==x)
+ return(linst(tl[hd[a]]=subst(tl[hd[a]]),ngt));
+ else a=tl[a];
+ a=id_type(x); /* otherwise pick up global binding */
+ if(bound_t(a))return(tl[a]);
+ if(a==type_t)type_error1(x);
+ if(a==undef_t)
+ { extern word commandmode;
+ if(commandmode)type_error2(x);
+ else
+ if(!member(ND,x)) /* report first occurrence only */
+ { if(lineptr)sayhere(lineptr,0);
+ else if(tag[current_id]==DATAPAIR) /* see checkfbs */
+ locate_inc();
+ printf("undefined name \"%s\"\n",get_id(x));
+ ND=add1(x,ND); }
+ return(NTV); }
+ if(a==wrong_t)return(NTV);
+ return(instantiate(ATNAMES?rep_t(a,ATNAMES):a));
+ case LAMBDA: a=NTV; b=NTV;
+ d=cons(a,ngt);
+ c=conforms(hd[x],a,env,d);
+ if(c==-1||!unify(b,etype(tl[x],c,d)))return(NTV);
+ return(tf(a,b));
+ case LET: { word e,def=hd[x];
+ a=NTV,e=conforms(dlhs(def),a,env,cons(a,ngt));
+ current_id=cons(dlhs(def),current_id);
+ c=lineptr; lineptr=dval(def);
+ b = unify(a,etype(dval(def),env,ngt));
+ lineptr=c;
+ current_id=tl[current_id];
+ if(e==-1||!b)return(NTV);
+ return(etype(tl[x],e,ngt)); }
+ case LETREC: { word e=env,s=NIL;
+ a=NIL; c=ngt;
+ for(d=hd[x];d!=NIL;d=tl[d])
+ if(dtyp(hd[d])==undef_t)
+ a=cons(hd[d],a), /* unspecified defs */
+ dtyp(hd[d])=(b=NTV),
+ c=cons(b,c), /* collect non-generic tvars */
+ e=conforms(dlhs(hd[d]),b,e,c);
+ else dtyp(hd[d])=meta_tcheck(dtyp(hd[d])),
+ /* should do earlier, and locate errs properly*/
+ s=cons(hd[d],s), /* specified defs */
+ e=cons(cons(dlhs(hd[d]),dtyp(hd[d])),e);
+ if(e==-1)return(NTV);
+ b=1;
+ for(;a!=NIL;a=tl[a])
+ { current_id=cons(dlhs(hd[a]),current_id);
+ d=lineptr; lineptr=dval(hd[a]);
+ b &= unify(dtyp(hd[a]),etype(dval(hd[a]),e,c));
+ lineptr=d; current_id=tl[current_id]; }
+ for(;s!=NIL;s=tl[s])
+ { current_id=cons(dlhs(hd[s]),current_id);
+ d=lineptr; lineptr=dval(hd[s]);
+ if(!subsumes(a=etype(dval(hd[s]),e,ngt),
+ linst(dtyp(hd[s]),ngt)))
+ /* would be better to set lineptr to spec here */
+ b=0,type_error6(dlhs(hd[s]),dtyp(hd[s]),a);
+ lineptr=d; current_id=tl[current_id]; }
+ if(!b)return(NTV);
+ return(etype(tl[x],e,ngt)); }
+ case TRIES: { word hold=lineptr;
+ a=NTV;
+ x=tl[x];
+ while(x!=NIL&&(lineptr=hd[hd[x]],
+ unify(a,etype(tl[hd[x]],env,ngt)))
+ )x=tl[x];
+ lineptr=hold;
+ if(x!=NIL)return(NTV);
+ return(a); }
+ case LABEL: { word hold=lineptr,t;
+ lineptr=hd[x];
+ t=etype(tl[x],env,ngt);
+ lineptr=hold;
+ return(t); }
+ case STARTREADVALS: if(tl[x]==0)
+ hd[x]=lineptr, /* insert here-info */
+ tl[x]=NTV,
+ showchain=cons(x,showchain);
+ return(tf(ltchar,lt(tl[x])));
+ case SHOW: hd[x]=lineptr; /* insert here-info */
+ showchain=cons(x,showchain);
+ return(tf(tl[x]=NTV,ltchar));
+ case SHARE: if(tl[x]==undef_t)
+ { word h=TYPERRS;
+ tl[x]=subst(etype(hd[x],env,ngt));
+ if(TYPERRS>h)hd[x]=UNDEF,tl[x]=wrong_t; }
+ if(tl[x]==wrong_t)
+ { TYPERRS++; return(NTV); }
+ return(tl[x]);
+ case CONSTRUCTOR: a=id_type(tl[x]);
+ return(instantiate(ATNAMES?rep_t(a,ATNAMES):a));
+ case UNICODE: return(char_t);
+ case ATOM: if(x<256)return(char_t);
+ switch(x)
+ {
+ case S:a=NTV,b=NTV,c=NTV;
+ d=tf3(tf2(a,b,c),tf(a,b),a,c);
+ return(d);
+ case K:a=NTV,b=NTV;
+ return(tf2(a,b,a));
+ case Y:a=NTV;
+ return(tf(tf(a,a),a));
+ case C:a=NTV,b=NTV,c=NTV;
+ return(tf3(tf2(a,b,c),b,a,c));
+ case B:a=NTV,b=NTV,c=NTV;
+ return(tf3(tf(a,b),tf(c,a),c,b));
+ case FORCE:
+ case G_UNIT:
+ case G_RULE:
+ case I:a=NTV;
+ return(tf(a,a));
+ case G_ZERO:return(NTV);
+ case HD:a=NTV;
+ return(tf(lt(a),a));
+ case TL:a=lt(NTV);
+ return(tf(a,a));
+ case BODY:a=NTV,b=NTV;
+ return(tf(ap(a,b),a));
+ case LAST:a=NTV,b=NTV;
+ return(tf(ap(a,b),b));
+ case S_p:a=NTV,b=NTV;
+ c=lt(b);
+ return(tf3(tf(a,b),tf(a,c),a,c));
+ case U:
+ case U_: a=NTV,b=NTV;
+ c=lt(a);
+ return(tf2(tf2(a,c,b),c,b));
+ case Uf: a=NTV,b=NTV,c=NTV;
+ return(tf2(tf2(tf(a,b),a,c),b,c));
+ case COND: a=NTV;
+ return(tf3(bool_t,a,a,a));
+ case EQ:case GR:case GRE:
+ case NEQ: a=NTV;
+ return(tf2(a,a,bool_t));
+ case NEG: return(tfnum);
+ case AND:
+ case OR: return(tfbool2);
+ case NOT: return(tfbool);
+ case MERGE:
+ case APPEND: a=lt(NTV);
+ return(tf2(a,a,a));
+ case STEP: return(tstep);
+ case STEPUNTIL: return(tstepuntil);
+ case MAP: a=NTV; b=NTV;
+ return(tf2(tf(a,b),lt(a),lt(b)));
+ case FLATMAP: a=NTV,b=lt(NTV);
+ return(tf2(tf(a,b),lt(a),b));
+ case FILTER: a=NTV; b=lt(a);
+ return(tf2(tf(a,bool_t),b,b));
+ case ZIP: a=NTV; b=NTV;
+ return(tf2(lt(a),lt(b),lt(pair_t(a,b))));
+ case FOLDL: a=NTV; b=NTV;
+ return(tf3(tf2(a,b,a),a,lt(b),a));
+ case FOLDL1: a=NTV;
+ return(tf2(tf2(a,a,a),lt(a),a));
+ case LIST_LAST: a=NTV;
+ return(tf(lt(a),a));
+ case FOLDR: a=NTV; b=NTV;
+ return(tf3(tf2(a,b,b),b,lt(a),b));
+ case MATCHINT:
+ case MATCH: a=NTV,b=NTV;
+ return(tf3(a,b,a,b));
+ case TRY: a=NTV;
+ return(tf2(a,a,a));
+ case DROP:
+ case TAKE: a=lt(NTV);
+ return(tf2(num_t,a,a));
+ case SUBSCRIPT:a=NTV;
+ return(tf2(num_t,lt(a),a));
+ case P: a=NTV;
+ b=lt(a);
+ return(tf2(a,b,b));
+ case B_p: a=NTV,b=NTV;
+ c=lt(a);
+ return(tf3(a,tf(b,c),b,c));
+ case C_p: a=NTV,b=NTV;
+ c=lt(b);
+ return(tf3(tf(a,b),c,a,c));
+ case S1: a=NTV,b=NTV,c=NTV,d=NTV;
+ return(tf4(tf2(a,b,c),tf(d,a),tf(d,b),d,c));
+ case B1: a=NTV,b=NTV,c=NTV,d=NTV;
+ return(tf4(tf(a,b),tf(c,a),tf(d,c),d,b));
+ case C1: a=NTV,b=NTV,c=NTV,d=NTV;
+ return(tf4(tf2(a,b,c),tf(d,a),b,d,c));
+ case SEQ: a=NTV,b=NTV;
+ return(tf2(a,b,b));
+ case ITERATE1:
+ case ITERATE: a=NTV;
+ return(tf2(tf(a,a),a,lt(a)));
+ case EXEC: { if(!exec_t)
+ a=ap2(comma_t,ltchar,ap2(comma_t,num_t,void_t)),
+ exec_t=tf(ltchar,ap2(comma_t,ltchar,a));
+ return(exec_t); }
+ case READBIN:
+ case READ: { if(!read_t)
+ read_t=tf(char_t,ltchar);
+ /* $- is ap(READ,0) */
+ return(read_t); }
+ case FILESTAT: { if(!filestat_t)
+ filestat_t=tf(ltchar,pair_t(pair_t(num_t,num_t),num_t));
+ return(filestat_t); }
+ case FILEMODE:
+ case GETENV:
+ case NB_STARTREAD:
+ case STARTREADBIN:
+ case STARTREAD: return(tfstrstr);
+ case GETARGS: return(tf(char_t,lt(ltchar)));
+ case SHOWHEX:
+ case SHOWOCT:
+ case SHOWNUM: return(tf(num_t,ltchar));
+ case SHOWFLOAT:
+ case SHOWSCALED: return(tf2(num_t,num_t,ltchar));
+ case NUMVAL: return(tf(ltchar,num_t));
+ case INTEGER: return(tf(num_t,bool_t));
+ case CODE: return(tf(char_t,num_t));
+ case DECODE: return(tf(num_t,char_t));
+ case LENGTH: return(tf(lt(NTV),num_t));
+ case ENTIER_FN: case ARCTAN_FN: case EXP_FN: case SIN_FN:
+ case COS_FN: case SQRT_FN: case LOG_FN: case LOG10_FN:
+ return(tfnumnum);
+ case MINUS:case PLUS:case TIMES:case INTDIV:case FDIV:
+ case MOD:case POWER: return(tfnum2);
+ case True: case False: return(bool_t);
+ case NIL: a=lt(NTV);
+ return(a);
+ case NILS: return(ltchar);
+ case MKSTRICT: a=NTV;
+ return(tf(char_t,tf(a,a)));
+/* the following are not the true types of the G_fns, which have the action
+ Ai->lt(bnf_t)->(B:lt(bnf_t))
+ here represented by the type Ai->B. G_CLOSE interfaces the parser fns to
+ the outside world */
+ case G_ALT: a=NTV;
+ return(tf2(a,a,a));
+ case G_ERROR: a=NTV;
+ return(tf2(a,tf(lt(bnf_t),a),a));
+ case G_OPT:
+ case G_STAR: a=NTV;
+ return(tf(a,lt(a)));
+ case G_FBSTAR: a=NTV; b=tf(a,a);
+ return(tf(b,b));
+ case G_SYMB: return(tfstrstr);
+ case G_ANY: return(ltchar);
+ case G_SUCHTHAT: return(tf(tf(ltchar,bool_t),ltchar));
+ case G_END: return(lt(bnf_t));
+ case G_STATE: return(tl[hd[tl[bnf_t]]]);
+ case G_SEQ: a=NTV; b=NTV;
+ return(tf2(a,tf(a,b),b));
+ /* G_RULE has same type as I */
+ case G_CLOSE: a=NTV;
+ if(col_fn) /* offside rule used */
+ if(col_fn== -1) /* arbitrary flag */
+ TYPERRS++; /*overkill, see note on checkcolfn*/
+ else checkcolfn();
+ return(tf3(ltchar,a,lt(bnf_t),a));
+ case OFFSIDE: return(ltchar);
+ /* pretend, used by indent, see prelude */
+ case FAIL: /* compiled from last guard on rhs */
+ case CONFERROR:
+ case BADCASE:
+ case UNDEF: return(NTV);
+ case ERROR: return(tf(ltchar,NTV));
+ default: printf("do not know type of ");
+ out(stdout,x);
+ putchar('\n');
+ return(wrong_t);
+ }
+ default: printf("unexpected tag in etype ");
+ out(stdout,tag[x]);
+ putchar('\n');
+ return(wrong_t);
+ }
+}
+
+rhs_here(r)
+word r;
+{ if(tag[r]==LABEL)return(hd[r]);
+ if(tag[r]==TRIES)return(hd[hd[lastlink(tl[r])]]);
+ return(0); /* something wrong */
+} /* efficiency hack, sometimes we set lineptr to rhs, can extract here_info
+ as above when needed */
+
+conforms(p,t,e,ngt) /* returns new environment of local type bindings obtained
+ by conforming pattern p to type t; -1 means failure */
+word p,t,e,ngt;
+{ if(e==-1)return(-1);
+ if(tag[p]==ID&&!isconstructor(p))return(cons(cons(p,t),e));
+ if(hd[p]==CONST)
+ { unify(etype(tl[p],e,ngt),t); return(e); }
+ if(tag[p]==CONS)
+ { word at=NTV;
+ if(!unify(lt(at),t))return(-1);
+ return(conforms(tl[p],t,conforms(hd[p],at,e,ngt),ngt)); }
+ if(tag[p]==TCONS)
+ { word at=NTV,bt=NTV;
+ if(!unify(ap2(comma_t,at,bt),t))return(-1);
+ return(conforms(tl[p],bt,conforms(hd[p],at,e,ngt),ngt)); }
+ if(tag[p]==PAIR)
+ { word at=NTV,bt=NTV;
+ if(!unify(ap2(comma_t,at,ap2(comma_t,bt,void_t)),t))return(-1);
+ return(conforms(tl[p],bt,conforms(hd[p],at,e,ngt),ngt)); }
+ if(tag[p]==AP&&tag[hd[p]]==AP&&hd[hd[p]]==PLUS) /* n+k pattern */
+ { if(!unify(num_t,t))return(1);
+ return(conforms(tl[p],num_t,e,ngt)); }
+{ word p_args=NIL,pt;
+ while(tag[p]==AP)p_args=cons(tl[p],p_args),p=hd[p];
+ if(!isconstructor(p))
+ { type_error4(p); return(-1); }
+ if(id_type(p)==undef_t)
+ { type_error5(p); return(-1); }
+ pt= /*instantiate(id_type(p)); */
+ instantiate(ATNAMES?rep_t(id_type(p),ATNAMES):id_type(p));
+ while(p_args!=NIL&&isarrow_t(pt))
+ { e=conforms(hd[p_args],tl[hd[pt]],e,ngt),pt=tl[pt],p_args=tl[p_args];
+ if(e==-1)return(-1); }
+ if(p_args!=NIL||isarrow_t(pt)){ type_error3(p); return(-1); }
+ if(!unify(pt,t))return(-1);
+ return(e);
+}}
+
+locate(s) /* for locating type errors */
+char *s;
+{ TYPERRS++;
+ if(TYPERRS==1||lastloc!=current_id) /* avoid tedious repetition */
+ if(current_id)
+ if(tag[current_id]==DATAPAIR) /* see checkfbs */
+ { locate_inc();
+ printf("%s in binding for %s\n",s,(char *)hd[current_id]);
+ return; }
+ else
+ { extern word fnts;
+ word x=current_id;
+ printf("%s in definition of ",s);
+ while(tag[x]==CONS)
+ if(tag[tl[x]]==ID&&member(fnts,tl[x]))
+ printf("nonterminal "),x=hd[x]; else /*note1*/
+ out_formal1(stdout,hd[x]),printf(", subdef of "),
+ x=tl[x];
+ printf("%s",get_id(x));
+ putchar('\n'); }
+ else printf("%s in expression\n",s);
+ if(lineptr)sayhere(lineptr,0); else
+ if(current_id&&id_who(current_id)!=NIL)sayhere(id_who(current_id),0);
+ lastloc=current_id;
+}
+/* note1: this is hack to suppress extra `subdef of <fst start symb>' when
+ reporting error in defn of non-terminal in %bnf stuff */
+
+sayhere(h,nl) /* h is hereinfo - reports location (in parens, newline if nl)
+ and sets errline/errs if not already set */
+word h,nl;
+{ extern word errs,errline;
+ extern char *current_script;
+ if(tag[h]!=FILEINFO)
+ { h=rhs_here(h);
+ if(tag[h]!=FILEINFO)
+ { fprintf(stderr,"(impossible event in sayhere)\n"); return; }}
+ printf("(line %3d of %s\"%s\")",tl[h],
+ (char *)hd[h]==current_script?"":"%insert file ",(char *)hd[h]);
+ if(nl)putchar('\n'); else putchar(' ');
+ if((char *)hd[h]==current_script)
+ { if(!errline) /* tells editor where first error is */
+ errline=tl[h]; }
+ else { if(!errs)errs=h; }
+}
+
+type_error(a,b,t1,t2)
+char *a,*b;
+word t1,t2;
+{ t1=redtvars(ap(subst(t1),subst(t2)));
+ t2=tl[t1];t1=hd[t1];
+ locate("type error");
+ printf("cannot %s ",a);out_type(t1);
+ printf(" %s ",b);out_type(t2);putchar('\n');
+}
+
+type_error1(x) /* typename in expression */
+word x;
+{ locate("type error");
+ printf("typename used as identifier (%s)\n",get_id(x));
+}
+
+type_error2(x) /* undefined name in expression */
+word x;
+{ if(compiling)return; /* treat as type error only in $+ data */
+ TYPERRS++;
+ printf("undefined name - %s\n",get_id(x));
+}
+
+type_error3(x) /* constructor used at wrong arity in formal */
+word x;
+{ locate("error");
+ printf("constructor \"%s\" used at wrong arity in formal\n", get_id(x));
+}
+
+type_error4(x) /* non-constructor as head of formal */
+word x;
+{ locate("error");
+ printf("illegal object \""); out_pattern(stdout,x);
+ printf("\" as head of formal\n");
+}
+
+type_error5(x) /* undeclared constructor in formal */
+word x;
+{ locate("error");
+ printf("undeclared constructor \""); out_pattern(stdout,x);
+ printf("\" in formal\n");
+ ND=add1(x,ND);
+}
+
+type_error6(x,f,a)
+word x,f,a;
+{ TYPERRS++;
+ printf("incorrect declaration "); sayhere(lineptr,1);
+ printf("specified, %s :: ",get_id(x)); out_type(f); putchar('\n');
+ printf("inferred, %s :: ",get_id(x)); out_type(redtvars(subst(a)));
+ putchar('\n');
+}
+
+type_error7(t,args)
+word t,args;
+{ word i=1;
+ while((args=tl[args])!=NIL)i++;
+ locate("type error");
+ printf(i==1?"1st":i==2?"2nd":i==3?"3rd":"%dth",i);
+ printf(" arg of zip has type :: ");
+ out_type(redtvars(subst(t)));
+ printf("\n - should be list\n");
+}
+
+type_error8(t1,t2)
+word t1,t2;
+{ word big;
+ t1=subst(t1); t2=subst(t2);
+ if(same(hd[t1],hd[t2]))
+ t1=tl[t1],t2=tl[t2]; /* discard `[bnf_t]->' */
+ t1=redtvars(ap(t1,t2));
+ t2=tl[t1];t1=hd[t1];
+ big = size(t1)>=10 || size(t2)>=10;
+ locate("type error");
+ printf("cannot unify%s ",big?"\n ":"");out_type(t1);
+ printf(big?"\nwith\n ":" with ");out_type(t2);putchar('\n');
+}
+
+unify(t1,t2) /* works by side-effecting SUBST, returns 1,0 as it succeeds
+ or fails */
+word t1,t2;
+{ t1=subst(t1),t2=subst(t2);
+ if(t1==t2)return(1);
+ if(isvar_t(t1)&&!occurs(t1,t2))
+ { addsubst(t1,t2); return(1); }
+ if(isvar_t(t2)&&!occurs(t2,t1))
+ { addsubst(t2,t1); return(1); }
+ if(iscompound_t(t1)&&iscompound_t(t2)&&
+ unify1(hd[t1],hd[t2])&&unify1(tl[t1],tl[t2]))return(1);
+ type_error("unify","with",t1,t2);
+ return(0);
+}
+
+unify1(t1,t2) /* inner call - exactly like unify, except error reporting is
+ done only by top level, see above */
+ /* we do this to avoid printing inner parts of types */
+word t1,t2;
+{ t1=subst(t1),t2=subst(t2);
+ if(t1==t2)return(1);
+ if(isvar_t(t1)&&!occurs(t1,t2))
+ { addsubst(t1,t2); return(1); }
+ if(isvar_t(t2)&&!occurs(t2,t1))
+ { addsubst(t2,t1); return(1); }
+ if(iscompound_t(t1)&&iscompound_t(t2))
+ return(unify1(hd[t1],hd[t2])&&unify1(tl[t1],tl[t2]));
+ return(0);
+}
+
+subsumes(t1,t2) /* like unify but lop-sided; returns 1,0 as t2 falls, doesnt
+ fall under t1 */
+word t1,t2;
+{ if(t2==wrong_t)return(1);
+ /* special case, shows up only when compiling prelude (changetype etc) */
+ return(subsu1(t1,t2,t2)); }
+
+subsu1(t1,t2,T2)
+word t1,t2,T2;
+{ t1=subst(t1);
+ if(t1==t2)return(1);
+ if(isvar_t(t1)&&!occurs(t1,T2))
+ { addsubst(t1,t2); return(1); }
+ if(iscompound_t(t1)&&iscompound_t(t2))
+ return(subsu1(hd[t1],hd[t2],T2)&&subsu1(tl[t1],tl[t2],T2));
+ return(0);
+}
+
+walktype(t,f) /* make a copy of t with f applied to its variables */
+word t;
+word (*f)();
+{ if(isvar_t(t))return((*f)(t));
+ if(iscompound_t(t))
+ { word h1=walktype(hd[t],f);
+ word t1=walktype(tl[t],f);
+ return(h1==hd[t]&&t1==tl[t]?t:ap(h1,t1)); }
+ return(t);
+}
+
+occurs(tv,t) /* does tv occur in type t? */
+word tv,t;
+{ while(iscompound_t(t))
+ { if(occurs(tv,tl[t]))return(1);
+ t=hd[t]; }
+ return(tv==t);
+}
+
+ispoly(t) /* does t contain tvars? (should call subst first) */
+word t;
+{ while(iscompound_t(t))
+ { if(ispoly(tl[t]))return(1);
+ t=hd[t]; }
+ return(isvar_t(t));
+}
+
+word SUBST[hashsize]; /* hash table of substitutions */
+
+clear_SUBST()
+/* To save time and space we call this after a type inference to clear out
+ substitutions in extinct variables. Calling this too often can slow you
+ down - whence #define reset_SUBST, see above */
+{ word i;
+ fixshows();
+ for(i=0;i<hashsize;i++)SUBST[i]=0;
+ /*printf("tvcount=%d\n",tvcount); /* probe */
+ tvcount=1;
+ return(0); /* see defn of reset_SUBST */
+}
+/* doubling hashsize from 512 to 1024 speeded typecheck by only 3% on
+ parser.m (=350 line block, used c. 5000 tvars) - may be worth increasing
+ for very large programs however. Guesstimate - further increase from
+ 512 would be worthwhile on blocks>2000 lines */
+
+fixshows()
+{ while(showchain!=NIL)
+ { tl[hd[showchain]]=subst(tl[hd[showchain]]);
+ showchain=tl[showchain]; }
+}
+
+lookup(tv) /* find current substitution for type variable */
+word tv;
+{ word h=SUBST[hashval(tv)];
+ while(h)
+ { if(eqtvar(hd[hd[h]],tv))return(tl[hd[h]]);
+ h=tl[h]; }
+ return(tv); /* no substitution found, so answer is self */
+}
+
+addsubst(tv,t) /* add new substitution to SUBST */
+word tv,t;
+{ word h=hashval(tv);
+ SUBST[h]=cons(cons(tv,t),SUBST[h]);
+}
+
+ult(tv) /* fully substituted out value of a type var */
+word tv;
+{ word s=lookup(tv);
+ return(s==tv?tv:subst(s));
+}
+
+subst(t) /* returns fully substituted out value of type expression */
+word t;
+{ return(walktype(t,ult));
+}
+
+word localtvmap=NIL;
+word NGT=0;
+
+lmap(tv)
+word tv;
+{ word l;
+ if(non_generic(tv))return(tv);
+ for(l=localtvmap;l!=NIL;l=tl[l])
+ if(hd[hd[l]]==tv)return(tl[hd[l]]);
+ localtvmap=cons(cons(tv,l=NTV),localtvmap);
+ return(l);
+}
+
+linst(t,ngt) /* local instantiate */
+word t; /* relevant tvars are those not in ngt */
+{ localtvmap=NIL; NGT=ngt;
+ return(walktype(t,lmap));
+}
+
+non_generic(tv)
+word tv;
+{ word x;
+ for(x=NGT;x!=NIL;x=tl[x])
+ if(occurs(tv,subst(hd[x])))return(1);
+ return(0);
+} /* note that when a non-generic tvar is unified against a texp, all tvars
+ in texp become non-generic; this is catered for by call to subst above
+ (obviating the need for unify to directly side-effect NGT) */
+
+word tvmap=NIL;
+
+mapup(tv)
+word tv;
+{ word *m= &tvmap;
+ tv=gettvar(tv);
+ while(--tv)m= &tl[*m];
+ if(*m==NIL)*m=cons(NTV,NIL);
+ return(hd[*m]);
+}
+
+instantiate(t) /* make a copy of t with a new set of type variables */
+word t; /* t MUST be in reduced form - see redtvars */
+{ tvmap=NIL;
+ return(walktype(t,mapup));
+}
+
+ap_subst(t,args) /* similar, but with a list of substitions for the type
+ variables provided (args). Again, t must be in reduced form */
+word t,args;
+{ word r;
+ tvmap=args;
+ r=walktype(t,mapup);
+ tvmap=NIL; /* ready for next use */
+ return(r);
+}
+
+
+mapdown(tv)
+word tv;
+{ word *m= &tvmap;
+ word i=1;
+ while(*m!=NIL&&!eqtvar(hd[*m],tv))m= &tl[*m],i++;
+ if(*m==NIL)*m=cons(tv,NIL);
+ return(mktvar(i));
+}
+
+redtvars(t) /* renames the variables in t, in order of appearance to walktype,
+ using the numbers 1,2,3... */
+word t;
+{ tvmap=NIL;
+ return(walktype(t,mapdown));
+}
+
+
+remove1(e,ss) /* destructively remove e from set with address ss, returning
+ 1 if e was present, 0 otherwise */
+word e,*ss;
+{ while(*ss!=NIL&&hd[*ss]<e)ss= &tl[*ss]; /* we assume set in address order */
+ if(*ss==NIL||hd[*ss]!=e)return(0);
+ *ss=tl[*ss];
+ return(1);
+}
+
+setdiff(s1,s2) /* destructive on s1, returns set difference */
+word s1,s2; /* both are in ascending address order */
+{ word *ss1= &s1;
+ while(*ss1!=NIL&&s2!=NIL)
+ if(hd[*ss1]==hd[s2])*ss1=tl[*ss1]; else /* removes element */
+ if(hd[*ss1]<hd[s2])ss1= &tl[*ss1];
+ else s2=tl[s2];
+ return(s1);
+}
+
+add1(e,s) /* inserts e destructively into set s, kept in ascending address
+ order */
+word e,s;
+{ word s1=s;
+ if(s==NIL||e<hd[s])return(cons(e,s));
+ if(e==hd[s])return(s); /* no duplicates! */
+ while(tl[s1]!=NIL&&e>hd[tl[s1]])s1=tl[s1];
+ if(tl[s1]==NIL)tl[s1]=cons(e,NIL);else
+ if(e!=hd[tl[s1]])tl[s1]=cons(e,tl[s1]);
+ return(s);
+}
+
+word NEW; /* nasty hack, see rules */
+
+newadd1(e,s) /* as above, but with side-effect on NEW */
+word e,s;
+{ word s1=s;
+ NEW=1;
+ if(s==NIL||e<hd[s])return(cons(e,s));
+ if(e==hd[s]){ NEW=0; return(s); } /* no duplicates! */
+ while(tl[s1]!=NIL&&e>hd[tl[s1]])s1=tl[s1];
+ if(tl[s1]==NIL)tl[s1]=cons(e,NIL);else
+ if(e!=hd[tl[s1]])tl[s1]=cons(e,tl[s1]);
+ else NEW=0;
+ return(s);
+}
+
+UNION(s1,s2) /* destructive on s1; s1, s2 both in address order */
+word s1,s2;
+{ word *ss= &s1;
+ while(*ss!=NIL&&s2!=NIL)
+ if(hd[*ss]==hd[s2])ss= &tl[*ss],s2=tl[s2]; else
+ if(hd[*ss]<hd[s2])ss= &tl[*ss];
+ else *ss=cons(hd[s2],*ss),ss= &tl[*ss],s2=tl[s2];
+ if(*ss==NIL)
+ while(s2!=NIL)*ss=cons(hd[s2],*ss),ss= &tl[*ss],s2=tl[s2];
+ /* must copy tail of s2, in case of later destructive operations on s1 */
+ return(s1);
+}
+
+intersection(s1,s2) /* s1, s2 and result all in address order */
+word s1,s2;
+{ word r=NIL;
+ while(s1!=NIL&&s2!=NIL)
+ if(hd[s1]==hd[s2])r=cons(hd[s1],r),s1=tl[s1],s2=tl[s2]; else
+ if(hd[s1]<hd[s2])s1=tl[s1];
+ else s2=tl[s2];
+ return(reverse(r));
+}
+
+deps(x) /* returns list of the free identifiers in expression x */
+word x;
+{ word d=NIL;
+L:switch(tag[x])
+{ case AP:
+ case TCONS:
+ case PAIR:
+ case CONS: d=UNION(d,deps(hd[x]));
+ x=tl[x];
+ goto L;
+ case ID: return(isconstructor(x)?d:add1(x,d));
+ case LAMBDA: /* d=UNION(d,patdeps(hd[x]));
+ /* should add this - see sahbug3.m */
+ return(rembvars(UNION(d,deps(tl[x])),hd[x]));
+ case LET: d=rembvars(UNION(d,deps(tl[x])),dlhs(hd[x]));
+ return(UNION(d,deps(dval(hd[x]))));
+ case LETREC: { word y;
+ d=UNION(d,deps(tl[x]));
+ for(y=hd[x];y!=NIL;y=tl[y])
+ d=UNION(d,deps(dval(hd[y])));
+ for(y=hd[x];y!=NIL;y=tl[y])
+ d=rembvars(d,dlhs(hd[y]));
+ return(d); }
+ case LEXER: while(x!=NIL)
+ d=UNION(d,deps(tl[tl[hd[x]]])),
+ x=tl[x];
+ return(d);
+ case TRIES:
+ case LABEL: x=tl[x]; goto L;
+ case SHARE: x=hd[x]; goto L; /* repeated analysis - fix later */
+ default: return(d);
+}}
+
+rembvars(x,p) /* x is list of ids in address order, remove bv's of pattern p
+ (destructive on x) */
+word x,p;
+{ L:
+ switch(tag[p])
+ { case ID: return(remove1(p,&x),x);
+ case CONS: if(hd[p]==CONST)return(x);
+ x=rembvars(x,hd[p]);p=tl[p];goto L;
+ case AP: if(tag[hd[p]]==AP&&hd[hd[p]]==PLUS)
+ p=tl[p]; /* for n+k patterns */
+ else { x=rembvars(x,hd[p]);p=tl[p]; }
+ goto L;
+ case PAIR:
+ case TCONS: x=rembvars(x,hd[p]);p=tl[p];goto L;
+ default: fprintf(stderr, "impossible event in rembvars\n");
+ return(x);
+}}
+
+member(s,x)
+word s,x;
+{ while(s!=NIL&&x!=hd[s])s=tl[s];
+ return(s!=NIL);
+}
+
+printgraph(title,g) /* for debugging info */
+char *title;
+word g;
+{ printf("%s\n",title);
+ while(g!=NIL)
+ { printelement(hd[hd[g]]); putchar(':');
+ printelement(tl[hd[g]]); printf(";\n");
+ g=tl[g]; }
+}
+
+printelement(x)
+word x;
+{ if(tag[x]!=CONS){ out(stdout,x); return; }
+ putchar('(');
+ while(x!=NIL)
+ { out(stdout,hd[x]);
+ x=tl[x];
+ if(x!=NIL)putchar(' '); }
+ putchar(')');
+}
+
+printlist(title,l) /* for debugging */
+char *title;
+word l;
+{ printf("%s",title);
+ while(l!=NIL)
+ { printelement(hd[l]);
+ l=tl[l];
+ if(l!=NIL)putchar(','); }
+ printf(";\n");
+}
+
+printob(title,x) /* for debugging */
+char *title;
+word x;
+{ printf("%s",title); out(stdout,x); putchar('\n');
+ return(x); }
+
+print2obs(title,title2,x,y) /* for debugging */
+char *title,*title2;
+word x,y;
+{ printf("%s",title); out(stdout,x); printf("%s",title2); out(stdout,y); putchar('\n');
+}
+
+word allchars=0; /* flag used by tail */
+
+out_formal1(f,x)
+FILE *f;
+word x;
+{ extern word nill;
+ if(hd[x]==CONST)x=tl[x];
+ if(x==NIL)fprintf(f,"[]"); else
+ if(tag[x]==CONS&&tail(x)==NIL)
+ if(allchars)
+ { fprintf(f,"\"");while(x!=NIL)fprintf(f,"%s",charname(hd[x])),x=tl[x];
+ fprintf(f,"\""); } else
+ { fprintf(f,"[");
+ while(x!=nill&&x!=NIL)
+ { out_pattern(f,hd[x]);
+ x=tl[x];
+ if(x!=nill&&x!=NIL)fprintf(f,","); }
+ fprintf(f,"]"); } else
+ if(tag[x]==AP||tag[x]==CONS)
+ { fprintf(f,"("); out_pattern(f,x);
+ fprintf(f,")"); } else
+ if(tag[x]==TCONS||tag[x]==PAIR)
+ { fprintf(f,"(");
+ while(tag[x]==TCONS)
+ { out_pattern(f,hd[x]);
+ x=tl[x]; fprintf(f,","); }
+ out_pattern(f,hd[x]); fprintf(f,","); out_pattern(f,tl[x]);
+ fprintf(f,")"); } else
+ if(tag[x]==INT&&neg(x)||tag[x]==DOUBLE&&get_dbl(x)<0)
+ { fprintf(f,"("); out(f,x); fprintf(f,")"); } /* -ve numbers */
+ else
+ out(f,x); /* all other cases */
+}
+
+out_pattern(f,x)
+FILE *f;
+word x;
+{ if(tag[x]==CONS)
+ if(hd[x]==CONST&&(tag[tl[x]]==INT||tag[tl[x]]==DOUBLE))out(f,tl[x]); else
+ if(hd[x]!=CONST&&tail(x)!=NIL)
+ { out_formal(f,hd[x]); fprintf(f,":"); out_pattern(f,tl[x]); }
+ else out_formal(f,x);
+ else out_formal(f,x);
+}
+
+out_formal(f,x)
+FILE *f;
+word x;
+{ if(tag[x]!=AP)
+ out_formal1(f,x); else
+ if(tag[hd[x]]==AP&&hd[hd[x]]==PLUS) /* n+k pattern */
+ { out_formal(f,tl[x]); fprintf(f,"+"); out(f,tl[hd[x]]); }
+ else
+ { out_formal(f,hd[x]); fprintf(f," "); out_formal1(f,tl[x]); }
+}
+
+tail(x)
+word x;
+{ allchars=1;
+ while(tag[x]==CONS)allchars&=(is_char(hd[x])),x=tl[x];
+ return(x);
+}
+
+out_type(t) /* for printing external representation of types */
+word t;
+{ while(isarrow_t(t))
+ { out_type1(tl[hd[t]]);
+ printf("->");
+ t=tl[t]; }
+ out_type1(t);
+}
+
+out_type1(t)
+word t;
+{ if(iscompound_t(t)&&!iscomma_t(t)&&!islist_t(t)&&!isarrow_t(t))
+ { out_type1(hd[t]);
+ putchar(' ');
+ t=tl[t]; }
+ out_type2(t);
+}
+
+out_type2(t)
+word t;
+{ if(islist_t(t))
+ { putchar('[');
+ out_type(tl[t]); /* could be out_typel, but absence of parentheses
+ might be confusing */
+ putchar(']'); }else
+ if(iscompound_t(t))
+ { putchar('(');
+ out_typel(t);
+ if(iscomma_t(t)&&tl[t]==void_t)putchar(',');
+ /* type of a one-tuple -- an anomaly that should never occur */
+ putchar(')'); }else
+ switch(t)
+ {
+ case bool_t: printf("bool"); return;
+ case num_t: printf("num"); return;
+ case char_t: printf("char"); return;
+ case wrong_t: printf("WRONG"); return;
+ case undef_t: printf("UNKNOWN"); return;
+ case void_t: printf("()"); return;
+ case type_t: printf("type"); return;
+ default: if(tag[t]==ID)printf("%s",get_id(t));else
+ if(isvar_t(t))
+ { word n=gettvar(t);
+ /*if(1)printf("t%d",n-1); else /* experiment, suppressed */
+ /*if(n<=26)putchar('a'+n-1); else /* experiment */
+ if(n>0&&n<7)while(n--)putchar('*'); /* 6 stars max */
+ else printf("%d",n); }else
+ if(tag[t]==STRCONS) /* pname - see hack in privatise */
+ { extern char *current_script;
+ if(tag[pn_val(t)]==ID)printf("%s",get_id(pn_val(t))); else
+ /* ?? one level of indirection sometimes present */
+ if(strcmp((char *)hd[tl[t_info(t)]],current_script)==0)
+ printf("%s",(char *)hd[hd[t_info(t)]]); else /* elision */
+ printf("`%s@%s'",
+ (char *)hd[hd[t_info(t)]], /* original typename */
+ (char *)hd[tl[t_info(t)]]); /* sourcefile */ }
+ else printf("<BADLY FORMED TYPE:%d,%d,%d>",tag[t],hd[t],tl[t]);
+ }
+}
+
+out_typel(t)
+word t;
+{ while(iscomma_t(t))
+ { out_type(tl[hd[t]]);
+ t=tl[t];
+ if(iscomma_t(t))putchar(',');
+ else if(t!=void_t)printf("<>"); } /* "tuple-cons", shouldn't occur free */
+ if(t==void_t)return;
+ out_type(t);
+}
+
+/* end of MIRANDA TYPECHECKER */
+