summaryrefslogtreecommitdiff
path: root/new/reduce.c
diff options
context:
space:
mode:
authorJakob Kaivo <jkk@ung.org>2022-03-04 12:32:20 -0500
committerJakob Kaivo <jkk@ung.org>2022-03-04 12:32:20 -0500
commit55f277e77428d7423ae906a8e1f1324d35b07a7d (patch)
tree5c1c04703dff89c46b349025d2d3ec88ea9b3819 /new/reduce.c
import Miranda 2.066 from upstream
Diffstat (limited to 'new/reduce.c')
-rw-r--r--new/reduce.c2376
1 files changed, 2376 insertions, 0 deletions
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 */
+