home *** CD-ROM | disk | FTP | other *** search
- /* This is file MACROS.CC */
- #include "em.h"
- /*-----*/
- /* int ____; long int bp(){__SR(); ____=_bp; __SR(); return ____;}
- void BP(){int i=0; long*p=(long int*)bp();
- while(p) {i++; p=(long int*)*p;} for(;i>0;i--) fprintf(debug," ");} */
- /*-----*/
- char*CMN="current_macro",keychar,keyseqc[65]; val keyseq(keyseqc,_keyseq);
- macro*Record=0; var*globvars=0; int nglobvars=0; val*refaddr(val L);
- mark err(0,0); val comment("/*",2),tnemmoc("*/",2);
- /*-----*/
- KF(obey){val f; switch(T1.n){
- case 0: if(record) MOAN("can't call macro which is being recorded"); T1.m=Macro;
- case _macro: (*T1.m)(N); return;
- default: MOAN("obey() with bad arg or no arg");
- case _keyseq: f=T1.keyseq(); if(f.n==_bad) MOAN(f.s);
- if(f.n!=_macro) MOAN("error in obey(): this key is not bound to a macro");
- (*f.m)(N);}}
- /*-----*/
- KF(macromenu){macro*M; M=macro_menu(); if(!M) MOAN("aborted"); (*M)();};
- /*-----*/
- KF(namemacro) {int i; macro*M; val nam; if(T2.n!=_macro)
- T2=*getk(T2,"type key sequence bound to the macro (ctrlX E for current macro)");
- T1.getifn(T1t,"name to bind to the macro?");
- for(i=0;i<T1.n;i++) if(!isalnum(T1.s[i])) MOAN("name must be all alphanumeric");
- if(T1.n) {if(!isalpha(T1.s[0])) MOAN("name must start with a letter");
- nam=named(T1); if(nam.n!=_unidfname) {
- pr(CW,"'%s' is already a %t",T1.s,nam.typ()); MOAN(CW);}}
- if(!play) if(basemi.rec) {setrek(T1,T1.copy()); setrek(T2,keyseq.copy());}
- if(T2.n==_subr?T2.f==&obey:0) M=Macro; /* as 'ctrlX E' calls current macro */
- else if(T2.n==_macro) M=T2.m;
- else {pr(CW,"%s is not bound to a macro",&keyseq); MOAN(CW);}
- if(M->name) if(M->name!=CMN) {
- pr(CW,"macro already named '%s': shall I rename it?",M->name);
- if(!yesno(CW)) MOAN("user abort"); delete M->name;}
- M->name=copyof(T1);}
- /*-----*/
- KF(beginmacro) {macro*m; if(play) return; if(record) MOAN("already recording");
- Display="start macro"; if(Macro) if(Macro->name==CMN) Macro->name=0;
- if(!Macro?:Macro->bound.n?:Macro->name!=0)Macro=new macro();else Macro->empty();
- basemi.rec=0; record=Macro; thisstep.f=&_idle; laststep.del(); laststep.clear();
- if(T2.n==_keyseq) bindkeymacro(val(1,0),T2);
- if(T1.n>0) namemacro(val(1,0),T1,val(record));
- else {for(m=macros;m;m=m->next) if(m->name==CMN)
- if(m->bound.n) m->name=0; else delete m; record->name=CMN;}}
- /*-----*/
- KF(endmacro) {if(play) return;
- Display="end macro"; if(!record) MOAN("not recording"); basemi.rec=0;
- record->tidy(); record=0; thisstep.f=&_idle; laststep.del(); laststep.clear();}
- /*-----*/
- KF(repeat) {if(play) {mi->rec=0; (*mi->prevstep)(N.i); return;}
- laststep(N.i); if(record)(*record)+=new macstep(kf(&repeat),N); rept=1;}
- /*-----*/
- KF(prmacro){Macro->print();}
- /*-----*/
- KF(prmacros) {prvars(globvars); macro*M; for(M=macros;M;M=M->next) M->print();}
- /*-----*/
- macro*macros=0;
- /*-----*/
- macstep laststep,thisstep;
- val specialchars("\"\\^`",4); /* chars that need \ before as selves in string */
- char*rsvword[]={0};
- char*Keysort[]={"magicstring","string","unbound","subroutine","macro","char",
- "keyarray","buffer","int","keysequence","reservedword","unidentifiedname",
- "call","bad","uncheckedsubr","function","functionwithinfo","reference","type",
- "float","label",0};
- char**keysort=Keysort+2;
- /*-----*/
- #define Bad(s) ({err=*this; *this=x; return val(s,_bad);})
- #define badq(s) if((s).n==_bad) return (s)
- #define Badq(s,delendum) if((s).n==_bad) {(delendum).del(); return (s);}
- /*-----*//* skip whites and comments */
- void mark::Skip(){int n; char*s;
- A: n=r->n; s=r->s;
- C: if(c<n) {if(s[c]==' '?:s[c]==9) {c++; goto C;}}
- else if(r->next) {r=r->next; c=0; goto A;} else return;
- if(c>n-2 ?: s[c]!='/' ?: s[c+1]!='*') return; c+=2;
- B: if(c<=n-2) if(s[c]=='*') if(s[c+1]=='/') {c+=2; goto A;}
- if(c<n) c++; else if(r->next) {r=r->next;c=0;} else MOAN("missing */"); goto B;}
- /*-----*/
- int mark::thisch(char C){Skip(); int i; if(i=(**this==C)) ++*this; return i;};
- /*-----*//* look for s */
- int mark::string(val s){Skip(); if(c+s.n>r->n) return 0;
- if(!strncmp(&r->s[c],s.s,s.n)) {c+=s.n; return 1;} return 0;}
- /*-----*/
- /* int mark::string(val s){Skip(); mark e=here(*this,s);
- if(!e.r) return 0; *this=e; return 1;} */
- /*-----*//* if n=0, s = 0 or 1 or -> standard empty string, do not delete it */
- void val::del(){int i; call*c; switch(n){
- case _keyseq: case _unidfname: delete s; break;
- case _call: for(i=(c=C)->n-1;i>=0;i--) c->arg[i].del(); delete c; break;
- default: if(n>0 ?: magic()) delete s;} n=0; s=0;}
- /*-----*/
- char*copyof(char*s,int n/*=0*/){if(!n) n=strlen(s); if(!s?:!n) return "\000";
- char*t=new char[n+1]; memcpy(t,s,n); t[n]=0; return t;}
- /*-----*/
- char*copyof(const val&s){if(!s.s?:s.n<=0) return "\000";
- char*t=new char[s.n+1]; memcpy(t,s.s,s.n); t[s.n]=0; return t;}
- /*-----*/
- val named(val name){int i; Subr*s; char*w; macro*m; val f; var*v;
- if(s=namedsubr(name)) return val(s); /* subr name */
- for(m=macros;m;m=m->next) if(m->name) if(name==m->name) return val(m);
- for(i=0;w=rsvword[i];i++) if(name==w) return val(i,_rsvword);/* reserved word */
- for(i=0;i<255;i++) if(name==altnames[i]) return val(256+i,_char); /*****/
- for(i=0;f=keynames[i],f.s;i++) {if(name==f.s) return val(f.n,_char);}
- if(name.n==5) if(!strncmp(name.s,"ctrl",4)) return val(name.s[4]&31,_char);
- for(i=0;w=funcname[i].name;i++) if(name==w) return val(&funcname[i]);
- for(i=-2;w=keysort[i];i++) if(name==w) return val(-i,_type);
- for(v=globvars;v;v=v->next) if(name==v->name) return val(v);
- if(Record) for(v=Record->vars;v;v=v->next) if(name==v->name) return val(v);
- return val(copyof(name),_unidfname);}
- /*-----*/
- val mark::label(){mark x=*this; Skip(); if(!is_alpha()) Bad("not a name");
- int y=c; while(is_alnum()) ++*this; int z=c;
- if(!thisch(':')) Bad("no ':' after label"); return named(val(r->s+y,z-y));}
- /*-----*/
- val mark::name(){mark x=*this; Skip(); if(!is_alpha()) Bad("not a name");
- int y=c; while(is_alnum()) ++*this; return named(val(r->s+y,c-y));}
- /*-----*/
- val mark::number(){int C,i=0; mark x=*this; Skip();
- if((C=**this-'0')<0?:C>9) Bad("not a number");
- do {c++; i=10*i+C;} while((C=**this-'0')<0?0:C<=9); return val(i);}
- /*-----*//* look for text in " " */
- val mark::string(){int i,j,k,m=0,magic=0; char C,W[i=lwand(strsize)+1];
- mark x=*this; for(i--;i>=0;i--) W[i]=0;
- if(thisch('"')) for(j=0;;c++) {
- if(magic) {m=1; magic=0; W[j>>3]|=128>>(j&7);}
- if(j>=strsize-1) Bad("string too long");
- switch(C=**this){
- case '`': magic=1; break; /* next char is magic */
- case '\\': c++; C=**this;
- if(C<'0'?0:C<'8') {k=C-'0'; c++; C=**this; /* \ and 3 octal digits */
- if(C<'0'?0:C<'8') {k=k*8+C-'0'; c++; C=**this;
- if(C<'0'?0:C<'8') {k=k*8+C-'0'; c++; C=**this;}} c--;CW[j++]=k;}
- else {if(!eol()) CW[j++]=**this; /* code char as itself */} break;
- case '^': c++; CW[j++]=**this&31; break; /* control char */
- case '"': CW[j]=0; c++; if(!m) return val(j?copyof(CW,j):"\000",j);
- k=lwand(j); for(i=0;i<k;i++) CW[j+1+i]=W[i];
- return val(copyof(CW,j+1+k),j|0x80000000); /* magic string */
- case LF: Bad("eol in string");
- default: CW[j++]=C;}}
- Bad("not a string");}
- /*-----*/
- val mark::elem(){char C; mark x=*this; Skip();
- val s=number(); if(s.n!=_bad) return s;
- s=name(); if(s.n!=_bad) return s;
- s=string(); if(s.n!=_bad) return s;
- if(eol()) Bad("(part of) expression missing at eol");
- if((C=**this)=='(') {c++; s=expr(); badq(s); if(thisch(')')) return s;
- s.del(); Bad("rubbish or eol found instead of )");}
- Bad(strncmp(s.s,"not a ",6)?s.s:"not an expression element");}
- /*-----*/
- typedef struct{char res,Lr,L,Rr,R; char*op; char pr; func*f; char*com;} op_use;
- int npriosused;
- char*Eq=" =="+1,*Ne=" !="+1,*Ge=" >="+1,*Le=" <="+1,*Gt=" >"+1,*Lt=" <"+1,
- *Alc=" ="+1,*Add=" +"+1,*Sub=" -"+1,*Tim=" *"+1,*Div=" /"+1;
- char*Ops[]={Alc,Add,Sub,Tim,Div,0}; char op_from_right[27]={[14]1};
- op_use *PP[28]={0},ops[]={ /*** keep these entries in priority order ***/
- {_int ,1,_int ,0,_int ,Alc,14,_allocate,0},
- {_string,1,_string,0,_string,Alc,14,_allocate,0},
- {_char ,1,_char ,0,_char ,Alc,14,_allocate,0},
- {_int ,0,_string,0,_string,Eq, 7,_eq, 0},
- {_int ,0,_string,0,_string,Ne, 7,_ne, 0},
- {_int ,0,_int ,0,_int ,Eq, 7,_eq, 0},
- {_int ,0,_int ,0,_int ,Ne, 7,_ne, 0},
- {_int ,0,_int ,0,_int ,Ge, 6,_ge, 0},
- {_int ,0,_int ,0,_int ,Le, 6,_le, 0},
- {_int ,0,_int ,0,_int ,Gt, 6,_gt, 0},
- {_int ,0,_int ,0,_int ,Lt, 6,_lt, 0},
- {_int ,0,_int ,0,_int ,Add, 4,_plus, 0},
- {_int ,0, 0 ,0,_int ,Add, 4,_same, 0},
- {_int ,0,_int ,0,_int ,Sub, 4,_minus, 0},
- {_int ,0, 0 ,0,_int ,Sub, 4,_neg, 0},
- {_int ,0,_int ,0,_int ,Tim, 3,_times, 0},
- {_int ,0,_int ,0,_int ,Div, 3,_divide, "rounds towards -inf; x/0 = 0"},
- {0,0,0,0,0,0,27,0,0}};
- enum{MINUSPR=4};
- /**** and secure all func's & subr's against bad values in args ****/
- /*-----*//* set up info tables re operators */
- void set_up_PP(){op_use*B; int i=0,j=0; if(PP[0]) return;
- for(B=ops;B->op;B++) {if(j!=B->pr) j=(PP[i++]=B)->pr; B->op[-1]=i-1;}
- PP[npriosused=i]=B;}
- /*-----*/
- int display_op_uses(){int i,j=1; op_use*P; set_up_PP();
- display("operator uses allowed in macros (# = must be a variable)",0,0,Green);
- for(i=0;(P=&ops[i])->op;i++) {
- if(P->L) pr(CW,"%s%t%T",P->Lr?"#":" ",P->L,9); else pr(CW,"%T",9);
- pa(CW,"%-2s %s%t%T: returns %t%T: priority %1d %s",P->op,
- P->Rr?"#":" ",P->R,11,P->res,28,npriosused-P->op[-1],P->com?:"");
- if(j==gp_Rows-1) {display("(More)",j,0,Green); get_key(); j=0;}
- display(CW,j++,0,Orange);}
- return j;}
- /*-----*/
- char*mark::op(int p){mark x=*this; Skip();
- char*s, a=**this, b=c+1>=r->n?CR:r->s[c+1]; op_use*B,*C=PP[p+1];
- for(B=PP[p];B<C;B++) if(a==(s=B->op)[0]) if(!s[1]?1:b==s[1]) goto A;
- err=*this; *this=x; return 0; A: c++; if(s[1]) c++; return s;}
- /*-----*/
- val callop(val L,char*op,val R){
- int Lt=L.type(),Rt=R.type(),prio=op[-1]; op_use*B,*C=PP[prio+1]; val t,ar[3];
- for(B=PP[prio];B<C;B++) if(op==B->op) if(Lt==B->L) if(Rt==B->R)
- if(B->Lr?L.n==_ref:1) if(B->Rr?R.n==_ref:1) goto OK;
- pr(CW,"type error in expr:");
- if(L.n) pa(CW," %s %t", L.n==_ref?"ref":"nonref",Lt);
- pa(CW, " %s %s %t",op,R.n==_ref?"ref":"nonref",Rt);
- L.del(); R.del(); return val(CW,_bad);
- OK: ar[0]=ff(B->f); ar[1]=L; ar[2]=R; t=call_n(3,B->res,B->pr,B->op,ar);
- if(L.known_now()) if(R.known_now()) {val u=t(); t.del(); return u;} return t;}
- /*-----*/
- val mark::expr(int p/*=0*/){char *Op; val X,*Y,Z; int fr; mark x=*this;
- set_up_PP(); Skip(); if(eol()) Bad("expression missing at eol");
- if(p>=npriosused) return Call();
- Op=op(p); X=expr(p+1); badq(X); if(Op) {X=callop(val(),Op,X); badq(X);}
- if(fr=op_from_right[PP[p]->pr]) Y=&X.C->arg[2]; else Y=&X;
- while(Op=op(p)) {Z=monexpr_upto(p+1); Badq(Z,X); *Y=callop(*Y,Op,Z);
- if(fr) {Badq(*Y,X); Y=&Y->C->arg[2];}} return X;}
- /*-----*/
- val mark::monexpr_upto(int p){mark x=*this; Skip();
- val X; char*op, a=**this, b=c+1>=r->n?CR:r->s[c+1]; op_use*B,*C=PP[p];
- for(B=PP[0];B<C;B++) if(a==(op=B->op)[0]) if(!op[1]?1:b==op[1]) goto A;
- err=*this; *this=x; return expr(p); A:
- c++; if(op[1]) c++; X=monexpr_upto(p); badq(X); X=callop(val(),op,X); return X;}
- /*-----*/
- static var*declare(char*name,int type){
- if(type==_label) return Record->vars=new var(name,type,0,Record->vars);
- else if(Record)
- return Record->vars=new var(name,type,Record->nvars++,Record->vars);
- else return globvars=new var(name,type,nglobvars++,globvars);}
- /*-----*/
- int val::type(){int i; short*Z; switch(n) {
- default: return magic()?_magic:notstring()?n:_string;
- case _ref: return v->type;
- case _subr: case _Subr: case _macro: return 0;
- case _func: i=(Z=Funcinfo()->args)[0]; return i!=_adinf?i:Z[1];
- case _Func: i=(Z=Fn ->args)[0]; return i!=_adinf?i:Z[1];
- case _call: return C->type;}}
- /*-----*/
- int val::typ(){switch(n) {
- default: return magic()?_magic:notstring()?n:_string;
- case _ref: return v->type;}}
- /*-----*/
- int val::checktype(int typ){int m=type(); if(typ==m ?: typ==666) return 1;
- switch(typ){
- case _magic: return m==_string;
- case _keyseq: return m==_char;
- case _char: return m==_string;
- case _label: if(m==_unidfname) {*this=named(s); /* already thus declared? */
- if(n==_unidfname) {n=_ref; v=declare(copyof(s),_label);} return 1;}}
- return 0;}
- /*-----*/
- val val::convto(int t){int f=typ(); if(t==f) return *this; switch(f){
- case _string: if(t==_magic?:t==_char) return *this;
- case _char: if(t==_keyseq) return *this; break;}
- pr(CW,"can't convert %t to %t",f,t); MOAN(CW); return val();}
- /*-----*/
- int val::known_now(){
- switch(n){case _subr: case _macro: case _call: case _Subr:
- case _func: case _Func: case _ref: return 0;} return 1;};
- #define MoaN(s) ({err=*this; *this=x; Moan=s; goto BAD;})
- /*-----*/
- val mark::Call(){
- val t,u,args[32],N,Arg[4]; int i=0,j,k,l,n,se; mark x=*this;
- char*ac; mark q[33]; static short MA[]={_int,0}; jmp_buf*oldbad,failed;
- short*a; Skip(); q[0]=*this; n=0; if(eol()) MoaN("matter missing at eol");
- t=elem(); badq(t); args[0]=t; q[1]=*this; n=1;
- if(thisch('(')) if(!thisch(')')) {
- do {u=expr(); q[i=n+1]=*this; if(u.bad()) {Moan=u.s; goto BAD;}
- if(n>31) {u.del(); MoaN("> 31 args");}
- args[n++]=u;} while(thisch(','));
- if(!thisch(')')) MoaN("rubbish after arg");}
- switch(t.n) {
- case _Subr: a=t.S->args-1;
- Arg[1]=val(1,0); Arg[2]=val(); Arg[3]=val();
- for(i=j=1;(k=a[j])?i<n:0;j++) if(args[i].checktype(k)) Arg[j]=args[i++];
- if(i!=n) goto BADARG;
- Arg[0]=kf(t.S->f); return val(call_n(4,0,0,t.S->name,Arg));
- case _Func: a=t.Fn->args; if(se=(a[0]==_adinf)) a++;
- for(i=j=1;i<n;i++) {
- if(!a[j]) MoaN("too many args");
- if(!args[i].checktype(a[j])) {a++; goto BADARG;}
- if(a[j+1]!=_adinf) j++;}
- if(a[j]) if(a[j+1]!=_adinf) MoaN("too few args");
- k=a[0]; if(se) goto Z;
- for(i=1;i<n;i++) if(!args[i].known_now()) goto Z; /* can I find value now?*/
- oldbad=bad; bad=&failed; if(setjmp(*bad)) goto BADD;
- t=t.Fn->f(n,args); for(i=0;i<n;i++) args[i].del(); bad=oldbad; return t;
- BADD: err=x; bad=oldbad; return val(Moan,_bad);
- /* case _func: k=Funcinfo()->args[0]; */
- Z: return val(call_n(n,k,0,t.Fn->name,args));
- case _macro: i=1; N=val(1,0); if(i<n) if(args[i].checktype(_int)) N=args[i++];
- if(i!=n) {a=MA; goto BADARG;}
- Arg[0]=kf(&obey); Arg[1]=N; Arg[2]=t; Arg[3]=val();
- return val(call_n(4,0,0,"obey",Arg));
- case _type: if(n!=1) {pr(CW,"'%t' should not have args",t.i); i=1; MoaN(CW);}
- return t;
- default: if(n==1) return t; /* has no args so isn't a call */
- pr(CW,"a %t can't be a call base",t.n); i=0; MoaN(CW);}
- BADARG:
- if(a[1]) {pr(CW,"args %sshould be:",t.n==_Func?"":"(can be omitted) ");
- for(l=1;k=a[l];l++) {if(k==_adinf) {pa(CW,"s"); /* plural */ break;}
- else pa(CW," %t",k);}}
- else pr(CW,"should have no args");
- ac=CW+strlen(CW);
- if(n) {pa(CW," args are:"); for(l=1;l<n;l++) pa(CW," %t",args[l].type());}
- else pa(CW," has no args");
- *ac=0; Display=CW; Moan=ac+1;
- BAD: err=q[i]; for(i=0;i<n;i++) args[i].del(); *this=x; return val(Moan,_bad);;}
- #define Bad(s) ({err=*this; *this=x; return val(s,_bad);})
- /*-----*/
- char*not_a_decl="not a decl";
- val mark::decl(){val args[32]; int i=0,n=0; mark q[33],x=*this; Skip();
- if(eol()) return val(); q[0]=*this; args[0]=name(); q[1]=*this; n=1;
- if(args[0].n!=_type?: (Skip(), !is_alnum())) {Moan=not_a_decl; goto BAD;}
- do {val u=name(); q[i=n+1]=*this;
- if(u.bad()) {Moan="bad name in decl"; goto BAD;}
- if(n>31) {u.del(); Moan="declaration has > 31 args"; goto BAD;}
- if(u.n!=_unidfname) {
- u.print(Moan=CW); pa(CW," is already a %t",u.typ()); u.del(); goto BAD;}
- args[n++]=u;} while(thisch(','));
- return val(call_n(n,0,0,keysort[-args[0].i],args));
- BAD: err=q[i]; for(i=0;i<n;i++) args[i].del(); *this=x; return val(Moan,_bad);;}
- /*-----*/
- call*call_n(int n,int type,int pr,char*name,val*a){
- call*c=(call*)myalloc(sizeof(call)+(n-1)*sizeof(val));
- c->n=n; c->type=type; c->pr=pr; c->name=name;
- int i; val*b=c->arg; for(i=0;i<n;i++) b[i]=a[i]; return c;}
- /*-----*/
- KF(_idle){}
- /*-----*/
- static macstep*jump=0;
- KF(go_to){if(T1.n==_ref) if(T1.v->type==_label) jump=(macstep*)(T1.v->offset);}
- /*-----*/
- KF(If){} /* 'if' is handled elsewhere */
- /*-----*/
- subr*subrcalled(val V){if(V.n==_call) V=V.C->arg[0]; switch(V.n){
- case _subr: return V.f; case _Subr: return V.S->f; default: return 0;}}
- /*----- is val a char or the name of a (char or special key)? */
- int val::charval(){
- if(n==1) return s[0]; /* string with one char */
- if(n==_char) return i;
- if(n<1) return -1; int j;
- if(n<4) for(j=0;j<255;j++) if(*this==altshortnames[j]) return j+256; return -1;}
- /*-----*/
- void val::expandkeyseq(char*K,int plain/*=0*/){int e,j,k,p; char *x,*S=s,a[3];
- if(n==_char) if(S=a,i&~255) {a[0]=3; a[1]=0; a[2]=i&255;} else {a[0]=2; a[1]=i;}
- else if(n!=_keyseq) MOAN("BUG: expandkeyseq bad arg"); p=(byte)S[0];
- if(plain) {for(e=1,K[0]=0;e<p;e++) if(S[e]) {
- strcat(K,keyname((byte)S[e],e>1?!S[e-1]:0)); strcat(K," ");}
- e=strlen(K); K[e-1]=0; return;}
- strcpy(K,"keyseq("); k=6;
- for(e=1;e<p;e++) if(S[e]) {x=keyname((byte)S[e],e>1?!S[e-1]:0);
- if(x[1]) {strcat(K,x); k=strlen(K);}
- else {j=(byte)*x;
- K[k++]='"'; if(j=='"'?:j=='\\') K[k++]='\\'; K[k++]=j; K[k++]='"';}
- K[k++]=','; K[k]=0;}
- K[k-1]=')'; K[k]=0;}
- /*-----*/
- #undef Bad
- /*----- translate buffer to macro */
- void translate(buffer*BB){int i; buffer*BBB=B; macro*m;
- val *arg,*K=0,N,T,T1,T2,U,W; Record=0; char spec; mark par,x,y; subr*Z;
- jmp_buf*oldbad=bad,failed; bad=&failed; if(setjmp(*bad)) goto BAD;
- /* if(record) {Moan="can't read in macros while recording a macro"; goto BAD;}*/
- T=val(); if(B!=BB) BB->go_to();
- for(i=0;i<basemi.nvars;i++) basemi.stack[i].del();
- delete globvars; delete basemi.stack;
- globvars=0; basemi.stack=0; nglobvars=basemi.nvars=0; par=mark(B->text.next,0);
- NEXTINSTR: B->dot=par; B->dotcc=-1; B->display();
- x=par; par.Skip(); err=par; spec=0;
- if(par.eof()) {if(B!=BBB) BBB->go_to(); bad=oldbad; Moan=0; return;} y=par;
- if(Record) if(T=par.label(), T.n!=_bad) {
- if(T.n==_unidfname) {T.n=_ref; T.v=declare(copyof(T.s),_label);}
- if(T.n==_ref) {*Record+=new macstep(T);
- if(T.v->type==_label) {
- if(T.v->offset) {Moan="label defined twice"; goto BAD;}
- T.v->offset=(long int)Record->last; goto NEXTINSTR;}
- else {pr(Moan=CW,"this label is already declared as a %t",T.v->type);
- goto BAD;}}
- else {pr(Moan=CW,"a %t can't be a label",T.n); goto BAD;}}
- T=par.decl(); if(T.n!=_bad ?: T.s!=not_a_decl) goto A; Moan=0; par=y;
- spec=par.thisch('#'); T=par.expr(); if(T.n==_bad) {Moan=T.s; goto BAD;}
- A: if(!par.thisch(';')) {
- err=par; par=x; T.del(); Moan="rubbish after command"; goto BAD;}
- if(spec) switch(T.n) {
- default: pr(Moan=CW,"%t after '#'",T.typ()); goto BAD;
- case _bad: Moan=T.s; goto BAD;
- case _subr: Z=T.f; goto Y;
- case _Subr: Z=T.S->f; Y: N=val(1,0); T1=val(); T2=val(); goto Z;
- case _call: switch(U=(arg=T.C->arg)[0], Z=U.f, U.n) {
- case _Subr: U=kf(Z=U.S->f);
- case _subr: N=arg[1]; T1=arg[2]; T2=arg[3];
- Z: if(Z==&unbindkey) {if(T1.n) if(K=&T1.keyseq()) K->unbind(); K=0;}
- else if(Z==&buffer_) {if(T1.n) buffer_(N,T1,T2); BB->go_to();}
- else if(Z==&beginmacro) { /* new macro named T1 bound to key T2 */
- if(T2.n) {if(Moan=(K=&T2.keyseq())->moanifbound(T2,0)) goto BAD;
- (Record=new macro())->bound=keyseq.copy();
- if(T1.n>0) namemacro(val(1,0),T1,val(Record)); *K=*Record;}
- else {K=0; Record=new macro();
- if(T1.n>0) namemacro(val(1,0),T1,val(Record));
- else {for(m=macros;m;m=m->next) if(m->name==CMN)
- if(m->bound.n) m->name=0;
- else if(Macro!=record) delete m;
- Record->name=CMN; record=0; Macro=Record;}}}
- /* unnamed & unbound macro terminates and replaces current macro */
- else if(Z==&endmacro) {Record=0; K=0; break;}
- else {Moan="wrong subroutine after '#'"; goto BAD;} break;
- default: pr(Moan=CW,"call of a %t after '#'",U.n); goto BAD;}
- break;}
-
- else switch(T.n) {
- case _bad: Moan=T.s; goto BAD;
- case _rsvword: pr(Moan=CW,"'%s' used wrongly",rsvword[T.i]); goto BAD;
- case _unidfname: pr(Moan=CW,"'%s' not known",T.s); goto BAD;
- default: pr(Moan=CW,"this is a %t",T.typ()); goto BAD;
- case _Subr: case _subr: case _Func: case _macro: *Record+=new macstep(T); break;
- case _call: U=(arg=T.C->arg)[0];
- if(U.n!=_type)if(!Record){Moan="instruction not in a macro body"; goto BAD;}
- switch(U.n) {
- case _type: switch(U.i) {
- default: Moan="can't declare this type of variable yet"; goto BAD;
- case _int: case _string: for(i=1;i<T.C->n;i++) {W=T.C->arg[i];
- if(W.n==_unidfname) W=named(val(W.s)); if(W.n!=_unidfname) {
- W.print(Moan=CW); pa(CW," is already a %t",W.typ()); goto BAD;}
- declare(W.s,U.i);}} break;
- case _Subr: case _subr: *Record+=new macstep(U,arg[1],arg[2],arg[3]);
- break;
- case _macro: *Record+=new macstep(U,arg[1]); break;
- /* case _macro: *Record+=new macstep(kf(&obey),arg[1],U); break;*/
- case _Func: case _func: *Record+=new macstep(T); T=val(); break;
- default: pr(Moan=CW,"call base is a %t",U.typ()); goto BAD;}}
- goto NEXTINSTR;
- BAD: bad=oldbad; BB->go_to(); T.del(); B->dotcc=-1; err.c<?=err.r->n;B->dot=err;
- if(Record) {if(K) *K=val(); delete Record; Record=0;} MOAN(Moan);}
- /* unbind & delete incomplete macro */
- /*-----*/
- #define __(name,Name,args) {&name,0,Name,args}
- val __idle(int i,val*v){return val();}
- Func anonfunc=__(__idle,"###",0); Subr anonsubr=__(_idle,"@@@",0);
- /*-----*/
- Subr*val::Subrinfo(){reg int i; reg char*s;
- for(i=0;s=subrname[i].name;i++) if(f==subrname[i].f) return &subrname[i];
- return &anonsubr;}
- /*-----*/
- Func*val::Funcinfo(){reg int i; reg char*s;
- for(i=0;s=funcname[i].name;i++) if(fn==funcname[i].f) return &funcname[i];
- return &anonfunc;}
- /*-----*/
- char*val::Subrname(){return f ?Subrinfo()->name:"<<unknown subroutine>>";}
- char*val::Funcname(){return fn?Funcinfo()->name:"<<unknown function>>";}
- /*-----*/
- char*chname(int c){static char ct[3]="^ ",sc[3]="\\ ",cc[2]=" ",cs[5]="\\000";
- c&=255; if(c>=128) {pr(cs,"\\%03o",c); return cs;}
- if(specialchars>>c) {sc[1]=c; return sc;}
- if(c<32) {ct[1]=c+64; return ct;} cc[0]=c; return cc;}
- /*----- if ')' last before B->dot, replace it with ',', else insert '(' */
- void start_an_arg(){int i=!B->dot.c?0:B->dot.r->s[B->dot.c-1]==')';
- if(i) B->dot.bs(); *B+=i?',':'(';}
- /*-----*/
- val val::copy(){switch(n){
- default: if(magic()) {int i=n&0x7fffffff; return val(copyof(s,i+1+lwand(i)),n);}
- if(n<=0) return *this; return val(copyof(s,n),n);
- case _keyseq: return val(copyof(s,(byte)s[0]),n);
- case _unidfname: return val(copyof(s,0),n);}}
- /*-----*/
- void prvars(var*V){int i,j; var*v; int Nt[_typeend-_typebeg],*nt=Nt-_typebeg;
- for(i=_typebeg;i<_typeend;i++) nt[i]=0;
- for(j=0,v=V;v;v=v->next) {nt[v->type]++; j++;}
- if(j) for(i=_typebeg;i<_typeend;i++) if(nt[i]) if(i!=_label) {
- pb("%t ",i); for(v=V;v;v=v->next) if(v->type==i) pb("%s,",v->name);
- B->dot.bs(); *B+=";\n";}}
- /*-----*/
- void macro::print(){macstep *A; char*N=name?:CMN; *B+="#beginmacro";
- if(bound.n) if(N!=CMN) pb("(%S,%K)",N,&bound); else pb("(%K)",&bound);
- else if(N!=CMN) pb("(%S)",N); *B+=';'; newline(); prvars(vars);
- for(A=text;A;A=A->next) A->print(); *B+="#endmacro;\n";}
- /*-----*/
- void prsubrargs(val f,val N,val T1,val T2){f.print();
- if(N.n ?:(uns int)N.s >=4096) {start_an_arg(); N.print(); *B+=')';}
- if(T1.n?:(uns int)T1.s>=4096) {start_an_arg(); T1.print(); *B+=')';}
- if(T2.n?:(uns int)T2.s>=4096) {start_an_arg(); T2.print(); *B+=')';}}
- /*-----*/
- void macstep::print(){val v; switch(f.n){
- case _Subr: if(f.S->f==&_idle) return; break;
- case _subr: if(f.f==&_idle) return; break;
- case _macro: if(f.m->name) break; v=kf(obey);
- macstep(v,N,f.m->bound,val()).print(); return;
- default: if(!f.magic()) if(f.notstring()) break;
- case _char: case _keyseq: case _int: pb("*** "); break;
- case _rsvword: pb("*** reserved: "); break;
- case _ref: pb("%s: ",f.v->name); return;}
- prsubrargs(f,N,T1,T2); *B+=';'; newline();}
- /*-----*/
- void val::print(char*Z/*=(char*)_buffer*/,int pr/*=1000*/){
- int j,k,p; val*a; char*z;
- if(this) switch(n){
- default: if((j=type())<=0) p_r(Z,"<<<%t>>>",j); else p_r(Z,"%v",this); break;
- case 0: if(!s) p_r(Z,"%s","<<<null>>>"); else p_r(Z,"\"\""); break;
- case _type: p_r(Z,keysort[-i]); break;
- case _keyseq: p_r(Z,"%K",this); break;
- case _Subr: p_r(Z,S->name); break;
- case _subr: p_r(Z,Subrname()); break;
- case _Func: p_r(Z,Fn->name); break;
- case _func: p_r(Z,Funcname()); break;
- case _macro: if(m->name) p_r(Z,m->name);
- else if(m->bound.n) p_r(Z,"%K",&m->bound);
- else p_r(Z,"<<<unnamed unbound macro>>>"); break;
- case _char: if(i<256) p_r(Z,"\"%s\"",chname(i));
- else if(z=altnames[i&255]) p_r(Z,"%s",z);
- else p_r(Z,"<<<char 0 %1d>>>",i&255); break;
- case _buffer: p_r(Z,"<<<buffer \"%s\">>>",b->name); break;
- case _int: z="%1d"; if(i<0) if(pr<=MINUSPR) z="(%1d)"; p_r(Z,z,i); break;
- case _rsvword: p_r(Z,"%s",rsvword[i]); break;
- case _unidfname: p_r(Z,"<<<unknown word: %s>>>",s); break;
- case _bad: p_r(Z,"<<<bad parsing: %s>>>",s); break;
- case _call: if(!C) break; p=C->n; a=C->arg; if(!a) break;
- if(a[0].n==_func) {if(pr) goto EXPR; else p_r(Z,C->name);}
- else if(a[0].n==_subr) {prsubrargs(a[0],a[1],a[2],a[3]); break;}
- else a[0].print();
- if(p<2) break;
- for(j=1;j<p;j++) {start_an_arg(); a[j].print(); p_r(Z,")");} break;
- EXPR: p=C->pr; j=1; k=0;
- if(op_from_right[p]) {j=0; k=1;} if(pr<=p) p_r(Z,"(");
- if(a[1].n?:a[1].i) a[1].print(Z,p+j); p_r(Z,C->name); a[2].print(Z,p+k);
- if(pr<=p) p_r(Z,")"); break;
- case _ref: p_r(Z,v->name); break;}}
- /*-----*/
- void macstep::del(){f.del(); N.del(); T1.del(); T2.del(); clear();}
- /*----- remove pointers to buffers & macros which are being deleted */
- void del_every(val f,keyarray&ka){int i,n=ka.n; val*F=ka.a;
- for(i=0;i<n;i++,F++) if(F->n==_keyarray) {del_every(f,*F->k); return;}
- else if(f==*F) F->n=F->i=0;}
- void del_every(val f,val&m){val*a; int i,n; /* look in macro args */
- if(m.n==_call) {a=m.C->arg; n=m.C->n; for(i=0;i<n;i++) del_every(f,*a++);}
- else if(f==m) m.n=m.i=0;}
- void del_every(val f,macstep*M){for(;M;M=M->next){del_every(f,M->f);
- del_every(f,M->N); del_every(f,M->T1); del_every(f,M->T2);}}
- void del_every(val f){macro*m; for(m=macros;m;m=m->next) del_every(f,m->text);
- del_every(f,&thisstep); del_every(f,&laststep); del_every(f,keys);}
- /*-----*/
- macro::~macro(){macro**I; del_every(val(this));
- for(I=&(macros);*I;I=&((*I)->next)) if(*I==(this)) {*I=(*I)->next; break;}
- empty();};
- /*----- delete all macsteps in macro */
- void macro::empty(){macstep *A,*B; if(bound.n) delete bound.s; delete vars;
- if(name!=CMN) delete name; for(B=text;A=B;B=A->next) delete A; text=last=0;}
- /*----- macro+=macstep : append macstep to macro */
- void macro::operator+=(macstep*m){if(play?:!this) return;
- if(last) last->next=m; else text=m; (last=m)->next=0;}
- /*----- tidy macro */ /* chain up insert/overlay char macsteps */
- void macro::tidy(){int i,n; subr*u; macstep*M,*N,*P; char*s; if(!text) return;
- for(M=text;M;M=M->next) if(M->T1.n==_char) if(M->N.i==1)
- if((u=M->f.f)==&insert?:u==&overlay?:u==&nomove){/* chain chars into string */
- for(n=1,N=(P=M)->next;;P=N,N=N->next,n++)
- if(!N ?: N->T1.n!=_char ?: N->f.f!=u ?: N->N.i!=1) break;
- if(N) if(N->f.f==&repeat) n--; if(n<=1) continue;
- s=new char[n+1]; s[n]=0; s[0]=M->T1.i;
- for(i=1,N=M->next;i<n;) {s[i++]=N->T1.i; P=N->next; delete N; N=P;};
- M->T1=val(s,n); M->next=N;}
- last=0; for(M=text;M;M=M->next) if(M) last=M;}
- /*-----*/
- macro *record=0,*Macro=0; int macdepth; macstep _lazy(kf(&_idle));
- /*-----*/
- static void G(){int i; if(!basemi.stack) {basemi.stack=new val[nglobvars];
- for(i=0;i<nglobvars;i++) basemi.stack[i]=val();}}
- /*-----*/
- void macro::operator()(val N/*=val(1,0)*/,int r/*=0*/){
- int nn=N.n==_int?N.i:1; strings*S,*T;
- val *p; int i,n,b=0; macstep*A; macrinfo MI,*oldmi=mi; mi=&MI; G();
- jmp_buf*oldbad=bad,failed; bad=&failed; if(setjmp(*bad)) {b=1; goto BAD;}
- if(n=nvars) {p=mi->stack=new val[mi->nvars=n]; for(i=0;i<n;i++) p[i]=val();}
- if(macdepth++>16) {Moan="macro calls >16 deep"; goto BAD;}
- for(i=0;jump=0,Breakin()?0:i<nn;i++)
- for(A=text,MI.prevstep=&_lazy;A;A=jump?:A->next){
- MI.rec=1; jump=0; (*A)(); mi=&MI;
- if(MI.rec) MI.prevstep=A; if(Breakin()) goto BAD;}
- BAD: for(i=0;i<mi->nvars;i++) mi->stack[i].del();
- T=MI.delenda; while(S=T){T=S->next; delete S->s; delete S;}
- macdepth--; mi=oldmi; bad=oldbad; if(b) MOAN(Moan);}
- /*-----*//*** be careful in case a loose macstep is a &repeat ***/
- void macstep::operator()(int p/*=1*/){int i; subr*S;
- prevobtype=obtype; obtype=ob_other; G();
- switch(f.n) {default: break;
- case _func: case _Func: case _call: for(i=0;i<p;i++) f(); break;
- case _Subr: S=f.S->f; goto SS; case _subr: S=f.f;
- SS: if(S==If) for(i=0;i<p;i++) N().i?T1():T2();
- else for(i=0;i<p;i++) S(N(),T1(),T2()); break;
- case _macro: for(i=0;Breakin()?0:i<p;i++) (*f.m)(N);} B->dotcc=-1;}
- /*-----*/
- val val::operator()(){val r,*arg,*lhs; subr*sub; func*fun; macro*M; G();
- switch(n){default: return*this;
- case _macro: (*m)(); return val();
- case _subr: (*f)(val(1,0),val(),val()); return val();
- case _Subr: (*S->f)(val(1,0),val(),val()); return val();
- case _func: return fn(1,this);
- case _Func: return Fn->f(1,this);
- case _ref: if(v->type==_label) return*this;
- lhs=refaddr(*this); return lhs?*lhs:val();
- case _call:;}
- switch((arg=C->arg)[0].n){
- default: pr(CW,"tried to call a %t",arg[0].type()); MOAN(CW);
- case _macro: M=arg[0].m; (*M)(arg[1]); break;
- case _subr: sub=arg[0].f; goto F;
- case _Subr: sub=arg[0].S->f;
- F: if(sub==If) arg[1]().i?arg[2]():arg[3]();
- else (*sub)(arg[1](),arg[2](),arg[3]()); break;
- case _func: fun=arg[0].fn; goto H;
- case _Func: fun=arg[0].Fn->f; H: r=(*fun)(C->n,arg);
- if(!r.notstring()) mi->delenda=new strings(r.s,mi->delenda); return r;
- case _type: pr(CW,"illegal call of `%t'",i); MOAN(CW);}
- return val();}
- /*-----*/
- macro*macro_menu() {int i,n; macro*M; for(n=0,M=macros;M;M=M->next) n++;
- if(play) MOAN("BUG: obeyed macromenu from macro"); if(!n) return 0;
- mousestate ms; ms=Jerry; Jerry.mc=1; Jerry.range(n,80); Jerry.move(0,0);
- int j=0,k,m,w=gp_Rows-2,c=0; char*s; macro*macs[n];
- for(i=0,M=macros;M;M=M->next,i++) macs[i]=M;
- E: k=w/2; k=j<w?0:((j-w/4)/k)*k; display("MACROS DEFINED",0,0,Magenta+8);
- for(i=k;i<n;i++) {if(i-k>=w) break; s=macs[i]->name; *CW=0;
- if(macs[i]->bound.n) pr(CW," %K",&macs[i]->bound); if(s) pa(CW," `%s'",s);
- if(!*CW) pr(CW,CMN); CW[gp_Cols]=0; display(CW,i-k+1,0,Magenta+8);}
- for(i=(w<?n)-1;i>=n-k;i--) display(" ",i,0,Magenta+8);
- display("(\030\031 move, RET chooses, alt_end quits)", (w<?n)+1,0,Magenta+8);
- A: i=k; k=w/2; k=j<w?0:((j-w/4)/k)*k; if(i!=k) goto E; m=j-k+1;
- scr(m,0)=sch(2,White); switch(c=getkey()) {
- case -mousemove: j=Jerry.y; break;
- case -downarrow: j=(j+1 )%n; break;
- case -uparrow: j=(j-1+n)%n; break;
- case -alt_end: case -mbutton: case -rbutton: Jerry=ms; return 0;
- case CR: case -lbutton: Jerry=ms; return macs[j];}
- scr(m,0)=sch(' ',White); if(c!=-mousemove) Jerry.move(0,j); goto A;}
- /*-----*/
- /* enum {_unbound=0,_subr=-1,_macro=-2,_char=-3,_keyarray=-4,_buffer=-5,_int=-6,
- _keyseq=-7,_rsvword=-8,_unidfname=-9,_call=-10,_bad=-11,_Subr=-12,_func=-13,
- _Func=-14}; */
- /*-----*/
- val*refaddr(val L){int i; if(L.n!=_ref) return 0;
- return &(((i=L.v->offset)&0x80000000)?basemi:*mi)[i&0x7fffffff];}
- /*-----*/
- FN(_allocate){val*lhs=refaddr(arg[1]),x=arg[2]().copy();
- if(lhs) {lhs->del(); *lhs=x;} return x;}
- /*-----*/
- FN(_andthen){if(macdepth++>16) MOAN("macro calls >16 deep");
- macstep prev(_lazy); int i; macrinfo MI,*oldmi=mi; mi=&MI; mi->prevstep=&prev;
- for(i=1;i<n;i++) {MI.rec=1; arg[i](); mi=&MI; if(MI.rec) prev.f=arg[i];}
- macdepth--; mi=oldmi; return val();}
- /*-----*/
- int byteq(char*a,char*b,int n){reg byte *s=(byte*)a,*t=(byte*)b,*u=s+n;
- while(s<u) if(*s++!=*t++) return 0; return 1;}
- int streq(val a,val b){if(a.n!=b.n?:a.n<0) return 0; return byteq(a.s,b.s,a.n);}
- /*-----*//* treat val() = undefined as 0 */
- #define EA val a=arg[1](),b=arg[2]();
- FN(_eq ){EA; return a.n==_int ? a.i == b.i : streq(a,b);}
- FN(_ne ){EA; return a.n==_int ? a.i != b.i : !streq(a,b);}
- FN(_ge ){return arg[1]().i >= arg[2]().i;}
- FN(_le ){return arg[1]().i <= arg[2]().i;}
- FN(_gt ){return arg[1]().i > arg[2]().i;}
- FN(_lt ){return arg[1]().i < arg[2]().i;}
- FN(_plus ){return arg[1]().i + arg[2]().i;}
- FN(_minus ){return arg[1]().i - arg[2]().i;}
- FN(_times ){return arg[1]().i * arg[2]().i;}
- FN(_divide){val b=arg[2](); return b.i?arg[1]().i/b.i:0;} /* x/0 = 0 here */
- FN(_same ){return arg[2]().i;}
- FN(_neg ){return - arg[2]().i;}
- /*-----*/
- FN(currentbuffer){return val(copyof(B->name?:".no file."));}
- /*-----*/
- FN(keyseq_){int i,j,m; for(i=j=1;i<n;i++) {
- if((m=arg[i]().charval())<0) MOAN("bad keyseq arg");
- if(m>255) keyseqc[j++]=0; keyseqc[j++]=m&255;
- if(j>61) MOAN("keyseq with too many args");}
- keyseqc[0]=j; return keyseq.copy();}
- /*-----*/
- #define FN(name) val name(int n,val*arg)
- FN(_yesno){B->display(); return yesno(arg[1].s);}
- /*-----*/
- FN(lastkill){return killring[nkill].asstring();};
- /*-----*/
- /* In run time, FN's return value, or val() (= void)), or val(<string>,_bad).
- In compile time, ditto, or val(type,_bad) = "delivers that type but I can't
- find its value now". val(_bad,_bad) = "ditto but I can't tell now what type
- it delivers". val(1,_bad) = "ditto but delivers string". */
-