/* xlcont - xlisp control built-in functions */ /* Copyright (c) 1985, by David Michael Betz All Rights Reserved Permission is granted for unrestricted non-commercial use */ #include "xlisp.h" /* external variables */ extern NODE ***xlstack,*xlenv,*xlvalue; extern NODE *s_unbound; extern NODE *s_evalhook,*s_applyhook; extern NODE *true; /* external routines */ extern NODE *xlxeval(); /* forward declarations */ FORWARD NODE *let(); FORWARD NODE *prog(); FORWARD NODE *progx(); FORWARD NODE *doloop(); /* xcond - built-in function 'cond' */ NODE *xcond(args) NODE *args; { NODE ***oldstk,*arg,*list,*val; /* create a new stack frame */ oldstk = xlsave(&arg,&list,NULL); /* initialize */ arg = args; /* initialize the return value */ val = NIL; /* find a predicate that is true */ while (arg) { /* get the next conditional */ list = xlmatch(LIST,&arg); /* evaluate the predicate part */ if (val = xlevarg(&list)) { /* evaluate each expression */ while (list) val = xlevarg(&list); /* exit the loop */ break; } } /* restore the previous stack frame */ xlstack = oldstk; /* return the value */ return (val); } /* xcase - built-in function 'case' */ NODE *xcase(args) NODE *args; { NODE ***oldstk,*key,*arg,*clause,*list,*val; /* create a new stack frame */ oldstk = xlsave(&key,&arg,&clause,NULL); /* initialize */ arg = args; /* get the key expression */ key = xlevarg(&arg); /* initialize the return value */ val = NIL; /* find a case that matches */ while (arg) { /* get the next case clause */ clause = xlmatch(LIST,&arg); /* compare the key list against the key */ if ((list = xlarg(&clause)) == true || (listp(list) && keypresent(key,list)) || eql(key,list)) { /* evaluate each expression */ while (clause) val = xlevarg(&clause); /* exit the loop */ break; } } /* restore the previous stack frame */ xlstack = oldstk; /* return the value */ return (val); } /* keypresent - check for the presence of a key in a list */ LOCAL int keypresent(key,list) NODE *key,*list; { for (; consp(list); list = cdr(list)) if (eql(car(list),key)) return (TRUE); return (FALSE); } /* xand - built-in function 'and' */ NODE *xand(args) NODE *args; { NODE ***oldstk,*arg,*val; /* create a new stack frame */ oldstk = xlsave(&arg,NULL); /* initialize */ arg = args; val = true; /* evaluate each argument */ while (arg) /* get the next argument */ if ((val = xlevarg(&arg)) == NIL) break; /* restore the previous stack frame */ xlstack = oldstk; /* return the result value */ return (val); } /* xor - built-in function 'or' */ NODE *xor(args) NODE *args; { NODE ***oldstk,*arg,*val; /* create a new stack frame */ oldstk = xlsave(&arg,NULL); /* initialize */ arg = args; val = NIL; /* evaluate each argument */ while (arg) if ((val = xlevarg(&arg))) break; /* restore the previous stack frame */ xlstack = oldstk; /* return the result value */ return (val); } /* xif - built-in function 'if' */ NODE *xif(args) NODE *args; { NODE ***oldstk,*testexpr,*thenexpr,*elseexpr,*val; /* create a new stack frame */ oldstk = xlsave(&testexpr,&thenexpr,&elseexpr,NULL); /* get the test expression, then clause and else clause */ testexpr = xlarg(&args); thenexpr = xlarg(&args); elseexpr = (args ? xlarg(&args) : NIL); xllastarg(args); /* evaluate the appropriate clause */ val = xleval(xleval(testexpr) ? thenexpr : elseexpr); /* restore the previous stack frame */ xlstack = oldstk; /* return the last value */ return (val); } /* xlet - built-in function 'let' */ NODE *xlet(args) NODE *args; { return (let(args,TRUE)); } /* xletstar - built-in function 'let*' */ NODE *xletstar(args) NODE *args; { return (let(args,FALSE)); } /* let - common let routine */ LOCAL NODE *let(args,pflag) NODE *args; int pflag; { NODE ***oldstk,*newenv,*arg,*val; /* create a new stack frame */ oldstk = xlsave(&newenv,&arg,NULL); /* initialize */ arg = args; /* create a new environment frame */ newenv = xlframe(xlenv); /* get the list of bindings and bind the symbols */ if (!pflag) xlenv = newenv; dobindings(xlmatch(LIST,&arg),newenv); if (pflag) xlenv = newenv; /* execute the code */ for (val = NIL; arg; ) val = xlevarg(&arg); /* unbind the arguments */ xlenv = cdr(xlenv); /* restore the previous stack frame */ xlstack = oldstk; /* return the result */ return (val); } /* xprog - built-in function 'prog' */ NODE *xprog(args) NODE *args; { return (prog(args,TRUE)); } /* xprogstar - built-in function 'prog*' */ NODE *xprogstar(args) NODE *args; { return (prog(args,FALSE)); } /* prog - common prog routine */ LOCAL NODE *prog(args,pflag) NODE *args; int pflag; { NODE ***oldstk,*newenv,*arg,*val; /* create a new stack frame */ oldstk = xlsave(&newenv,&arg,NULL); /* initialize */ arg = args; /* create a new environment frame */ newenv = xlframe(xlenv); /* get the list of bindings and bind the symbols */ if (!pflag) xlenv = newenv; dobindings(xlmatch(LIST,&arg),newenv); if (pflag) xlenv = newenv; /* execute the code */ tagblock(arg,&val); /* unbind the arguments */ xlenv = cdr(xlenv); /* restore the previous stack frame */ xlstack = oldstk; /* return the result */ return (val); } /* xgo - built-in function 'go' */ NODE *xgo(args) NODE *args; { NODE *label; /* get the target label */ label = xlarg(&args); xllastarg(args); /* transfer to the label */ xlgo(label); } /* xreturn - built-in function 'return' */ NODE *xreturn(args) NODE *args; { NODE *val; /* get the return value */ val = (args ? xlarg(&args) : NIL); xllastarg(args); /* return from the inner most block */ xlreturn(val); } /* xprog1 - built-in function 'prog1' */ NODE *xprog1(args) NODE *args; { return (progx(args,1)); } /* xprog2 - built-in function 'prog2' */ NODE *xprog2(args) NODE *args; { return (progx(args,2)); } /* progx - common progx code */ LOCAL NODE *progx(args,n) NODE *args; int n; { NODE ***oldstk,*arg,*val; /* create a new stack frame */ oldstk = xlsave(&arg,&val,NULL); /* initialize */ arg = args; /* evaluate the first n expressions */ while (n--) val = xlevarg(&arg); /* evaluate each remaining argument */ while (arg) xlevarg(&arg); /* restore the previous stack frame */ xlstack = oldstk; /* return the last test expression value */ return (val); } /* xprogn - built-in function 'progn' */ NODE *xprogn(args) NODE *args; { NODE ***oldstk,*arg,*val; /* create a new stack frame */ oldstk = xlsave(&arg,NULL); /* initialize */ arg = args; /* evaluate each remaining argument */ for (val = NIL; arg; ) val = xlevarg(&arg); /* restore the previous stack frame */ xlstack = oldstk; /* return the last test expression value */ return (val); } /* xdo - built-in function 'do' */ NODE *xdo(args) NODE *args; { return (doloop(args,TRUE)); } /* xdostar - built-in function 'do*' */ NODE *xdostar(args) NODE *args; { return (doloop(args,FALSE)); } /* doloop - common do routine */ LOCAL NODE *doloop(args,pflag) NODE *args; int pflag; { NODE ***oldstk,*newenv,*arg,*blist,*clist,*test,*rval; int rbreak; /* create a new stack frame */ oldstk = xlsave(&newenv,&arg,&blist,&clist,&test,NULL); /* initialize */ arg = args; /* get the list of bindings */ blist = xlmatch(LIST,&arg); /* create a new environment frame */ newenv = xlframe(xlenv); /* bind the symbols */ if (!pflag) xlenv = newenv; dobindings(blist,newenv); if (pflag) xlenv = newenv; /* get the exit test and result forms */ clist = xlmatch(LIST,&arg); test = xlarg(&clist); /* execute the loop as long as the test is false */ rbreak = FALSE; while (xleval(test) == NIL) { /* execute the body of the loop */ if (tagblock(arg,&rval)) { rbreak = TRUE; break; } /* update the looping variables */ doupdates(blist,pflag); } /* evaluate the result expression */ if (!rbreak) for (rval = NIL; consp(clist); ) rval = xlevarg(&clist); /* unbind the arguments */ xlenv = cdr(xlenv); /* restore the previous stack frame */ xlstack = oldstk; /* return the result */ return (rval); } /* xdolist - built-in function 'dolist' */ NODE *xdolist(args) NODE *args; { NODE ***oldstk,*arg,*clist,*sym,*list,*val,*rval; int rbreak; /* create a new stack frame */ oldstk = xlsave(&arg,&clist,&sym,&list,&val,NULL); /* initialize */ arg = args; /* get the control list (sym list result-expr) */ clist = xlmatch(LIST,&arg); sym = xlmatch(SYM,&clist); list = xlevmatch(LIST,&clist); val = (clist ? xlarg(&clist) : NIL); /* initialize the local environment */ xlenv = xlframe(xlenv); xlbind(sym,NIL,xlenv); /* loop through the list */ rbreak = FALSE; for (; consp(list); list = cdr(list)) { /* bind the symbol to the next list element */ xlsetvalue(sym,car(list)); /* execute the loop body */ if (tagblock(arg,&rval)) { rbreak = TRUE; break; } } /* evaluate the result expression */ if (!rbreak) { xlsetvalue(sym,NIL); rval = xleval(val); } /* unbind the arguments */ xlenv = cdr(xlenv); /* restore the previous stack frame */ xlstack = oldstk; /* return the result */ return (rval); } /* xdotimes - built-in function 'dotimes' */ NODE *xdotimes(args) NODE *args; { NODE ***oldstk,*arg,*clist,*sym,*val,*rval; int rbreak,cnt,i; /* create a new stack frame */ oldstk = xlsave(&arg,&clist,&sym,&val,NULL); /* initialize */ arg = args; /* get the control list (sym list result-expr) */ clist = xlmatch(LIST,&arg); sym = xlmatch(SYM,&clist); cnt = getfixnum(xlevmatch(INT,&clist)); val = (clist ? xlarg(&clist) : NIL); /* initialize the local environment */ xlenv = xlframe(xlenv); xlbind(sym,NIL,xlenv); /* loop through for each value from zero to cnt-1 */ rbreak = FALSE; for (i = 0; i < cnt; i++) { /* bind the symbol to the next list element */ xlsetvalue(sym,cvfixnum((FIXNUM)i)); /* execute the loop body */ if (tagblock(arg,&rval)) { rbreak = TRUE; break; } } /* evaluate the result expression */ if (!rbreak) { xlsetvalue(sym,cvfixnum((FIXNUM)cnt)); rval = xleval(val); } /* unbind the arguments */ xlenv = cdr(xlenv); /* restore the previous stack frame */ xlstack = oldstk; /* return the result */ return (rval); } /* xcatch - built-in function 'catch' */ NODE *xcatch(args) NODE *args; { NODE ***oldstk,*tag,*arg,*val; CONTEXT cntxt; /* create a new stack frame */ oldstk = xlsave(&tag,&arg,NULL); /* initialize */ tag = xlevarg(&args); arg = args; val = NIL; /* establish an execution context */ xlbegin(&cntxt,CF_THROW,tag); /* check for 'throw' */ if (setjmp(cntxt.c_jmpbuf)) val = xlvalue; /* otherwise, evaluate the remainder of the arguments */ else { while (arg) val = xlevarg(&arg); } xlend(&cntxt); /* restore the previous stack frame */ xlstack = oldstk; /* return the result */ return (val); } /* xthrow - built-in function 'throw' */ NODE *xthrow(args) NODE *args; { NODE *tag,*val; /* get the tag and value */ tag = xlarg(&args); val = (args ? xlarg(&args) : NIL); xllastarg(args); /* throw the tag */ xlthrow(tag,val); } /* xerror - built-in function 'error' */ NODE *xerror(args) NODE *args; { char *emsg; NODE *arg; /* get the error message and the argument */ emsg = getstring(xlmatch(STR,&args)); arg = (args ? xlarg(&args) : s_unbound); xllastarg(args); /* signal the error */ xlerror(emsg,arg); } /* xcerror - built-in function 'cerror' */ NODE *xcerror(args) NODE *args; { char *cmsg,*emsg; NODE *arg; /* get the correction message, the error message, and the argument */ cmsg = getstring(xlmatch(STR,&args)); emsg = getstring(xlmatch(STR,&args)); arg = (args ? xlarg(&args) : s_unbound); xllastarg(args); /* signal the error */ xlcerror(cmsg,emsg,arg); /* return nil */ return (NIL); } /* xbreak - built-in function 'break' */ NODE *xbreak(args) NODE *args; { char *emsg; NODE *arg; /* get the error message */ emsg = (args ? getstring(xlmatch(STR,&args)) : "**BREAK**"); arg = (args ? xlarg(&args) : s_unbound); xllastarg(args); /* enter the break loop */ xlbreak(emsg,arg); /* return nil */ return (NIL); } /* xcleanup - built-in function 'clean-up' */ NODE *xcleanup(args) NODE *args; { xllastarg(args); xlcleanup(); } /* xcontinue - built-in function 'continue' */ NODE *xcontinue(args) NODE *args; { xllastarg(args); xlcontinue(); } /* xerrset - built-in function 'errset' */ NODE *xerrset(args) NODE *args; { NODE ***oldstk,*expr,*flag,*val; CONTEXT cntxt; /* create a new stack frame */ oldstk = xlsave(&expr,&flag,NULL); /* get the expression and the print flag */ expr = xlarg(&args); flag = (args ? xlarg(&args) : true); xllastarg(args); /* establish an execution context */ xlbegin(&cntxt,CF_ERROR,flag); /* check for error */ if (setjmp(cntxt.c_jmpbuf)) val = NIL; /* otherwise, evaluate the expression */ else { expr = xleval(expr); val = consa(expr); } xlend(&cntxt); /* restore the previous stack frame */ xlstack = oldstk; /* return the result */ return (val); } /* xevalhook - eval hook function */ NODE *xevalhook(args) NODE *args; { NODE ***oldstk,*expr,*ehook,*ahook,*env,*newehook,*newahook,*newenv,*val; /* create a new stack frame */ oldstk = xlsave(&expr,&ehook,&ahook,&env,&newehook,&newahook,&newenv,NULL); /* get the expression, the new hook functions and the environment */ expr = xlarg(&args); newehook = xlarg(&args); newahook = xlarg(&args); newenv = (args ? xlarg(&args) : xlenv); xllastarg(args); /* bind *evalhook* and *applyhook* to the hook functions */ ehook = getvalue(s_evalhook); setvalue(s_evalhook,newehook); ahook = getvalue(s_applyhook); setvalue(s_applyhook,newahook); env = xlenv; xlenv = newenv; /* evaluate the expression (bypassing *evalhook*) */ val = xlxeval(expr); /* unbind the hook variables */ setvalue(s_evalhook,ehook); setvalue(s_applyhook,ahook); xlenv = env; /* restore the previous stack frame */ xlstack = oldstk; /* return the result */ return (val); } /* dobindings - handle bindings for let/let*, prog/prog*, do/do* */ LOCAL dobindings(blist,env) NODE *blist,*env; { NODE ***oldstk,*list,*bnd,*sym,*val; /* create a new stack frame */ oldstk = xlsave(&list,&bnd,&sym,&val,NULL); /* bind each symbol in the list of bindings */ for (list = blist; consp(list); list = cdr(list)) { /* get the next binding */ bnd = car(list); /* handle a symbol */ if (symbolp(bnd)) { sym = bnd; val = NIL; } /* handle a list of the form (symbol expr) */ else if (consp(bnd)) { sym = xlmatch(SYM,&bnd); val = xlevarg(&bnd); } else xlfail("bad binding"); /* bind the value to the symbol */ xlbind(sym,val,env); } /* restore the previous stack frame */ xlstack = oldstk; } /* doupdates - handle updates for do/do* */ doupdates(blist,pflag) NODE *blist; int pflag; { NODE ***oldstk,*plist,*list,*bnd,*sym,*val; /* create a new stack frame */ oldstk = xlsave(&plist,&list,&bnd,&sym,&val,NULL); /* bind each symbol in the list of bindings */ for (list = blist; consp(list); list = cdr(list)) { /* get the next binding */ bnd = car(list); /* handle a list of the form (symbol expr) */ if (consp(bnd)) { sym = xlmatch(SYM,&bnd); bnd = cdr(bnd); if (bnd) { val = xlevarg(&bnd); if (pflag) { plist = consd(plist); rplaca(plist,cons(sym,val)); } else xlsetvalue(sym,val); } } } /* set the values for parallel updates */ for (; plist; plist = cdr(plist)) xlsetvalue(car(car(plist)),cdr(car(plist))); /* restore the previous stack frame */ xlstack = oldstk; } /* tagblock - execute code within a block and tagbody */ int tagblock(code,pval) NODE *code,**pval; { NODE ***oldstk,*arg; CONTEXT cntxt; int type,sts; /* create a new stack frame */ oldstk = xlsave(&arg,NULL); /* initialize */ arg = code; /* establish an execution context */ xlbegin(&cntxt,CF_GO|CF_RETURN,arg); /* check for a 'return' */ if ((type = setjmp(cntxt.c_jmpbuf)) == CF_RETURN) { *pval = xlvalue; sts = TRUE; } /* otherwise, enter the body */ else { /* check for a 'go' */ if (type == CF_GO) arg = xlvalue; /* evaluate each expression in the body */ while (consp(arg)) if (consp(car(arg))) xlevarg(&arg); else arg = cdr(arg); /* fell out the bottom of the loop */ *pval = NIL; sts = FALSE; } xlend(&cntxt); /* restore the previous stack frame */ xlstack = oldstk; /* return status */ return (sts); }