diff options
Diffstat (limited to 'new/reduce.c')
-rw-r--r-- | new/reduce.c | 2376 |
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 */ + |