Rev 2 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
/** R : A Computer Langage for Statistical Data Analysis* Copyright (C) 1995, 1996 Robert Gentleman and Ross Ihaka** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU General Public License as published by* the Free Software Foundation; either version 2 of the License, or* (at your option) any later version.** This program is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the* GNU General Public License for more details.** You should have received a copy of the GNU General Public License* along with this program; if not, write to the Free Software* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.*/#define ARGUSED(x) LEVELS(x)#include "Defn.h"#ifdef Macintosh/* The universe will end if the Stack on the Mac grows* until it hits the heap. This code places a limit* on the depth to which eval can recurse. */#define EVAL_LIMIT 100extern void isintrpt();#endif/* NEEDED: A fixup is needed in browser, because it *//* can trap errors, and currently does not reset the *//* limit to the right value. *//* not ifdef'ed to keep main etc happy */extern int R_EvalDepth;extern int R_EvalCount;/* eval - return value of e evaluated in rho */SEXP eval(SEXP e, SEXP rho){SEXP op, tmp, val;/** The use of depthsave below is necessary because of the possibility* of non-local returns from evaluation. Without this an "expression* too complex error" is quite likely.*/#ifdef EVAL_LIMITint depthsave = R_EvalDepth++;if (R_EvalDepth > EVAL_LIMIT)error("expression too complex for evaluator\n");#endif#ifdef Macintosh/* check for a user abort */if ((R_EvalCount++ % 100) == 0) {isintrpt();}#endifR_Visible = 1;switch (TYPEOF(e)) {case NILSXP:case LISTSXP:case LGLSXP:case FACTSXP:case ORDSXP:case INTSXP:case REALSXP:case STRSXP:case CPLXSXP:case SPECIALSXP:case BUILTINSXP:case ENVSXP:case CLOSXP:case VECSXP:tmp = e;break;case SYMSXP:R_Visible = 1;if (e == R_DotsSymbol)error("... used in an incorrect context\n");tmp = findVar(e, rho);if (tmp == R_UnboundValue)error("Object \"%s\" not found\n", CHAR(PRINTNAME(e)));else if (tmp == R_MissingArg) {char *n = CHAR(PRINTNAME(e));if(*n) error("Argument \"%s\" is missing, with no default\n", CHAR(PRINTNAME(e)));else error("Argument is missing, with no default\n");}else if (TYPEOF(tmp) == PROMSXP) {PROTECT(tmp);tmp = eval(tmp, rho);#ifdef oldif (NAMED(tmp) == 1) NAMED(tmp) = 2;else NAMED(tmp) = 1;#elseNAMED(tmp) = 2;#endifUNPROTECT(1);}else if (!isNull(tmp))NAMED(tmp) = 1;break;case PROMSXP:if (PRVALUE(e) == R_UnboundValue) {if(PRSEEN(e))errorcall(R_GlobalContext->call,"recursive default argument reference\n");PRSEEN(e) = 1;val = eval(PREXPR(e), PRENV(e));PRSEEN(e) = 0;PRVALUE(e) = val;}tmp = PRVALUE(e);break;case EXPRSXP:{int i, n;n = LENGTH(e);for(i=0 ; i<n ; i++)tmp = eval(VECTOR(e)[i], rho);}break;case LANGSXP:if (TYPEOF(CAR(e)) == SYMSXP)PROTECT(op = findFun(CAR(e), rho));elsePROTECT(op = eval(CAR(e), rho));if(TRACE(op)) {Rprintf("trace: ");PrintValue(e);}if (TYPEOF(op) == SPECIALSXP) {int save = R_PPStackTop;PROTECT(CDR(e));R_Visible = 1 - PRIMPRINT(op);tmp = PRIMFUN(op) (e, op, CDR(e), rho);UNPROTECT(1);if(save != R_PPStackTop) {Rprintf("stack imbalance in %s, %d then %d\n",PRIMNAME(op), save, R_PPStackTop);}}else if (TYPEOF(op) == BUILTINSXP) {int save = R_PPStackTop;PROTECT(tmp = evalList(CDR(e), rho));R_Visible = 1 - PRIMPRINT(op);tmp = PRIMFUN(op) (e, op, tmp, rho);UNPROTECT(1);if(save != R_PPStackTop) {Rprintf("stack imbalance in %s, %d then %d\n",PRIMNAME(op), save, R_PPStackTop);}}else if (TYPEOF(op) == CLOSXP) {PROTECT(tmp = promiseArgs(CDR(e), rho));tmp = applyClosure(e, op, tmp, rho, R_NilValue);UNPROTECT(1);}elseerror("attempt to apply non-function\n");UNPROTECT(1);break;case DOTSXP:error("... used in an incorrect context\n");default:UNIMPLEMENTED("eval");}#ifdef EVAL_LIMITR_EvalDepth = depthsave;#endifreturn (tmp);}/* applyClosure - apply SEXP op of type CLOSXP to actuals */SEXP applyClosure(SEXP call, SEXP op, SEXP arglist, SEXP rho, SEXP suppliedenv){int nargs;SEXP body, formals, actuals, savedrho;volatile SEXP newrho;SEXP f, a, tmp;RCNTXT cntxt;/* formals = list of formal parameters *//* actuals = values to be bound to formals *//* arglist = the tagged list of arguments */formals = FORMALS(op);body = BODY(op);savedrho = CLOENV(op);nargs = length(arglist);/*set up a context with the call in it so error has access to it */begincontext(&cntxt, CTXT_RETURN, call, newrho, rho, arglist);actuals = matchArgs(formals,arglist);PROTECT(newrho = extendEnv(savedrho, formals, actuals));/* Use default code for unbound formals */f = formals;a = actuals;while (f != R_NilValue) {if (CAR(a) == R_MissingArg && CAR(f) != R_MissingArg) {CAR(a) = mkPROMISE(CAR(f), newrho);MISSING(a) = 2;}f = CDR(f);a = CDR(a);}/* fix up any extras that were supplied by usemethod */if( suppliedenv != R_NilValue ) {for( tmp=FRAME(suppliedenv); tmp!=R_NilValue ; tmp=CDR(tmp) ){for( a=actuals; a!=R_NilValue; a=CDR(a) )if( TAG(a) == TAG(tmp) )break;if(a == R_NilValue) {FRAME(newrho) = CONS(CAR(tmp), FRAME(newrho));TAG(FRAME(newrho)) = TAG(tmp);}}}NARGS(newrho) = nargs;/*end the previous context and start up a new one with the correct env*/endcontext(&cntxt);/*if we have a generic function we need to use the sysparent of thegeneric as the sysparent of the method because the method is astraight substitution of the generic*/if( R_GlobalContext->callflag == CTXT_GENERIC )begincontext(&cntxt, CTXT_RETURN, call, newrho, R_GlobalContext->sysparent, arglist);elsebegincontext(&cntxt, CTXT_RETURN, call, newrho, rho, arglist);/* Default value */tmp = R_NilValue;/* debugging */DEBUG(newrho) = DEBUG(op);if( DEBUG(op) ) {Rprintf("debugging in: ");PrintValueRec(call);}if (setjmp(cntxt.cjmpbuf)) {tmp = R_ReturnedValue;}else {tmp = eval(body, newrho);}endcontext(&cntxt);if( DEBUG(op) ) {Rprintf("exiting from: ");PrintValueRec(call);}UNPROTECT(1);return (tmp);}static SEXP EnsureLocal(SEXP symbol, SEXP rho){SEXP vl;if ((vl = findVarInFrame(FRAME(rho), symbol)) != R_UnboundValue) {vl = eval(symbol, rho); /* for promises */if(NAMED(vl) == 2) {PROTECT(vl = duplicate(vl));defineVar(symbol, vl, rho);UNPROTECT(1);}return vl;}vl = eval(symbol, ENCLOS(rho));if (vl == R_UnboundValue)error("Object \"%s\" not found\n", CHAR(PRINTNAME(symbol)));PROTECT(vl = duplicate(vl));defineVar(symbol, vl, rho);UNPROTECT(1);NAMED(vl) = 1;return vl;}/* Note: If val is a language object it must be protected *//* to prevent evaluation. As an example consider *//* e <- quote(f(x=1,y=2); names(e) <- c("","a","b") */static SEXP replaceCall(SEXP fun, SEXP val, SEXP args, SEXP rhs){SEXP tmp, ptmp;PROTECT(fun);PROTECT(args);PROTECT(rhs);PROTECT(val);ptmp = tmp = allocList(length(args)+3);UNPROTECT(4);CAR(ptmp) = fun; ptmp = CDR(ptmp);CAR(ptmp) = val; ptmp = CDR(ptmp);while(args != R_NilValue) {CAR(ptmp) = CAR(args);ptmp = CDR(ptmp);args = CDR(args);}CAR(ptmp) = rhs;TYPEOF(tmp) = LANGSXP;return tmp;}static SEXP assignCall(SEXP op, SEXP symbol, SEXP fun, SEXP val, SEXP args, SEXP rhs){PROTECT(op);PROTECT(symbol);val = replaceCall(fun, val, args, rhs);UNPROTECT(2);return lang3(op, symbol, val);}SEXP do_if(SEXP call, SEXP op, SEXP args, SEXP rho){int cond = asLogical(eval(CAR(args), rho));if (cond == NA_LOGICAL)errorcall(call, "missing value where logical needed\n");else if (cond)return (eval(CAR(CDR(args)), rho));else if (length(args) > 2)return (eval(CAR(CDR(CDR(args))), rho));R_Visible = 0;return R_NilValue;}SEXP do_for(SEXP call, SEXP op, SEXP args, SEXP rho){int tmp, dbg;volatile int i, n, bgn;SEXP sym, body;volatile SEXP ans, v, val;RCNTXT cntxt;sym = CAR(args);val = CADR(args);body = CADDR(args);PROTECT(args);PROTECT(rho);PROTECT(val = eval(val, rho));defineVar(sym, R_NilValue, rho);if (isList(val) || isNull(val)) {n = length(val);PROTECT(v = R_NilValue);}else {n = LENGTH(val);PROTECT(v = allocVector(TYPEOF(val), 1));}ans = R_NilValue;dbg = DEBUG(rho);if(isLanguage(body) && isSymbol(CAR(body)) && strcmp(CHAR(PRINTNAME(CAR(body))),"{") )bgn = 1;elsebgn = 0;for (i = 0; i < n; i++) {if( DEBUG(rho) && bgn ) {Rprintf("debug: ");PrintValue(body);do_browser(call,op,args,rho);}begincontext(&cntxt, CTXT_LOOP, R_NilValue, R_NilValue, R_NilValue, R_NilValue);if ((tmp = setjmp(cntxt.cjmpbuf)))if (tmp == CTXT_BREAK) break; /* break */else continue; /* next */else {if (isVector(v)) {UNPROTECT(1);PROTECT(v = allocVector(TYPEOF(val), 1));}switch (TYPEOF(val)) {case LGLSXP:LOGICAL(v)[0] = LOGICAL(val)[i];setVar(sym, v, rho);ans = eval(body, rho);break;case INTSXP:INTEGER(v)[0] = INTEGER(val)[i];setVar(sym, v, rho);ans = eval(body, rho);break;case REALSXP:REAL(v)[0] = REAL(val)[i];setVar(sym, v, rho);ans = eval(body, rho);break;case CPLXSXP:COMPLEX(v)[0] = COMPLEX(val)[i];setVar(sym, v, rho);ans = eval(body, rho);break;case STRSXP:STRING(v)[0] = STRING(val)[i];setVar(sym, v, rho);ans = eval(body, rho);break;case EXPRSXP:VECTOR(v)[0] = VECTOR(val)[i];setVar(sym, v, rho);ans = eval(body, rho);break;case LISTSXP:setVar(sym, CAR(val), rho);ans = eval(body, rho);val = CDR(val);}endcontext(&cntxt);}}UNPROTECT(4);R_Visible = 0;DEBUG(rho) = dbg;return ans;}SEXP do_while(SEXP call, SEXP op, SEXP args, SEXP rho){int cond, dbg;volatile int bgn;volatile SEXP s, t;RCNTXT cntxt;checkArity(op, args);s = eval(CAR(args), rho);dbg = DEBUG(rho);t=CAR(CADR(args));if( isSymbol(t) && strcmp(CHAR(PRINTNAME(t)),"{") )bgn=1;t = R_NilValue;for (;;) {if ((cond = asLogical(s)) == NA_LOGICAL)errorcall(call, "missing value where logical needed\n");else if (!cond)break;if( bgn && DEBUG(rho) ) {Rprintf("debug: ");PrintValue(CAR(args));do_browser(call,op,args,rho);}begincontext(&cntxt, CTXT_LOOP, R_NilValue, R_NilValue, R_NilValue, R_NilValue);if ((cond = setjmp(cntxt.cjmpbuf)))if (cond == CTXT_BREAK) break; /* break */else continue; /* next */else {PROTECT(t = eval(CAR(CDR(args)), rho));s = eval(CAR(args), rho);UNPROTECT(1);endcontext(&cntxt);}}R_Visible = 0;DEBUG(rho) = dbg;return t;}SEXP do_repeat(SEXP call, SEXP op, SEXP args, SEXP rho){int cond, dbg;volatile int bgn;volatile SEXP t;RCNTXT cntxt;checkArity(op, args);dbg = DEBUG(rho);if( isSymbol(CAR(args)) && strcmp(CHAR(PRINTNAME(CAR(args))),"{") )bgn=1;for (;;) {if( DEBUG(rho) && bgn ) {Rprintf("debug: ");PrintValue(CAR(args));do_browser(call,op,args,rho);}begincontext(&cntxt, CTXT_LOOP, R_NilValue, R_NilValue, R_NilValue,R_NilValue);if ((cond = setjmp(cntxt.cjmpbuf)))if (cond == CTXT_BREAK) break; /*break */else continue; /* next */else {t = eval(CAR(args), rho);endcontext(&cntxt);}}R_Visible = 0;DEBUG(rho) = dbg;return t;}SEXP do_break(SEXP call, SEXP op, SEXP args, SEXP rho){findcontext(PRIMVAL(op), R_NilValue);/*NOTREACHED*/}SEXP do_paren(SEXP call, SEXP op, SEXP args, SEXP rho){checkArity(op, args);return CAR(args);}SEXP do_begin(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP s;if (args == R_NilValue) {s = R_NilValue;}else {while (args != R_NilValue) {if( DEBUG(rho) ) {Rprintf("debug: ");PrintValue(CAR(args));do_browser(call,op,args,rho);}s = eval(CAR(args), rho);args = CDR(args);}}return s;}SEXP do_return(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP a = args;int na = 0;while(!isNull(a)) {na += 1;if(NAMED(CAR(a)) > 1)CAR(a) = duplicate(CAR(a));a = CDR(a);}switch(na) {case 0:a = R_NilValue;break;case 1:a = CAR(args);break;default:a = args;break;}if( R_BrowseLevel )findcontext(CTXT_BROWSER, a);elsefindcontext(CTXT_RETURN, a);}SEXP do_function(SEXP call, SEXP op, SEXP args, SEXP rho){if (length(args) < 2)WrongArgCount("lambda");CheckFormals(CAR(args));return mkCLOSXP(CAR(args), CADR(args), rho);}/* Assignments for complex LVAL specifications *//* This is the stuff that nightmares are made of ... *//* evalseq - preprocess the LHS of an assignment. *//* Given an expression, evalseq builds a list of *//* partial values for the exression. For example, *//* the assignment x$a[3] <- 10 with LHS x$a[3] yields *//* the (improper) list: *//* *//* (eval(x$a[3]) eval(x$a) eval(x) . x) *//* *//* Note the terminating symbol. *//* The partial evaluations are carried out efficiently *//* using previously computed components. */static SEXP evalseq(SEXP expr, SEXP rho, int forcelocal, SEXP tmploc){SEXP val, nval, nexpr;if (isNull(expr))error("invalid (NULL) left side of assignment\n");if (isSymbol(expr)) {PROTECT(expr);if(forcelocal) {nval = EnsureLocal(expr, rho);}else {nval = eval(expr, rho);}UNPROTECT(1);return CONS(nval, expr);}else if (isLanguage(expr)) {PROTECT(expr);PROTECT(val = evalseq(CADR(expr), rho, forcelocal, tmploc));CAR(tmploc) = CAR(val);PROTECT(nexpr = LCONS(TAG(tmploc), CDDR(expr)));PROTECT(nexpr = LCONS(CAR(expr), nexpr));nval = eval(nexpr, rho);UNPROTECT(4);return CONS(nval, val);}else error("invalid (Non-language) left side of assignment\n");/*NOTREACHED*/}/* Main entry point for complex assignments *//* 1. We have checked to see that CAR(args) is a LANGSXP */static char *asym[] = {":=", "<-", "<<-"};SEXP applydefine(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP expr, lhs, rhs, saverhs, tmp, tmploc, tmpsym;char buf[32];expr = CAR(args);/* it's important that the rhs get evaluated first *//* because assignment is right associative i.e. *//* a <- b <- c is parsed as a <- (b <- c) */PROTECT(saverhs = rhs = eval(CADR(args), rho));tmpsym = install("*tmp*");defineVar(tmpsym, R_NilValue, rho);tmploc = FRAME(rho);while(tmploc != R_NilValue && TAG(tmploc) != tmpsym)tmploc = CDR(tmploc);/* do a partial evaluation down through the lhs */lhs = evalseq(CADR(expr), rho, PRIMVAL(op)==1, tmploc);PROTECT(lhs);PROTECT(rhs); /*just to get the loop right */while (isLanguage(CADR(expr))) {sprintf(buf, "%s<-", CHAR(PRINTNAME(CAR(expr))));tmp = install(buf);UNPROTECT(1);CAR(tmploc) = CAR(lhs);PROTECT(rhs = replaceCall(tmp, TAG(tmploc), CDDR(expr), rhs));rhs = eval(rhs, rho);UNPROTECT(1);PROTECT(rhs);lhs = CDR(lhs);expr = CADR(expr);}sprintf(buf, "%s<-", CHAR(PRINTNAME(CAR(expr))));CAR(tmploc) = CAR(lhs);PROTECT(expr = assignCall(install(asym[PRIMVAL(op)]), CDR(lhs),install(buf), TAG(tmploc), CDDR(expr), rhs));expr = eval(expr, rho);UNPROTECT(4);unbindVar(tmpsym, rho);return duplicate(saverhs);}/* do_set - assignment in its various forms */SEXP do_set(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP s;if (length(args) != 2) WrongArgCount(asym[PRIMVAL(op)]);if (isString(CAR(args)))CAR(args) = install(CHAR(STRING(CAR(args))[0]));switch (PRIMVAL(op)) {case 1: /* <- */if (isSymbol(CAR(args))) {s = eval(CADR(args), rho);if (NAMED(s))s = duplicate(s);PROTECT(s);R_Visible = 0;defineVar(CAR(args), s, rho);UNPROTECT(1);NAMED(s) = 1;return (s);}else if (isLanguage(CAR(args))) {R_Visible = 0;return applydefine(call, op, args, rho);}else errorcall(call,"invalid (do_set) left-hand side to assignment\n");case 2: /* <<- */if (isSymbol(CAR(args))) {s = eval(CADR(args), rho);if (NAMED(s))s = duplicate(s);PROTECT(s);R_Visible = 0;setVar(CAR(args), s, rho);UNPROTECT(1);NAMED(s) = 1;return s;}else if (isLanguage(CAR(args)))return applydefine(call, op, args, rho);else error("invalid assignment lhs\n");default:UNIMPLEMENTED("do_set");}}/* evalList - evaluate each expression in el *//* This is a naturally recursive algorithm, but we* use the iterative form below because it is does* not cause growth of the pointer protection stack,* and because it is a little more efficient. */SEXP evalList(SEXP el, SEXP rho){SEXP ans, h, tail;PROTECT(ans = tail = CONS(R_NilValue, R_NilValue));while(el != R_NilValue) {/* If we have a ... symbol, we look to see what it *//* is bound to. If its binding is Null (i.e. zero length) *//* we just ignore it and return the cdr with all its *//* expressions evaluated; if it is bound to a ... list *//* of promises, we force all the promises and then splice *//* the list of resulting values into the return value. *//* Anything else bound to a ... symbol is an error */if (CAR(el) == R_DotsSymbol) {h = findVar(CAR(el), rho);if (TYPEOF(h) == DOTSXP || h == R_NilValue) {while(h != R_NilValue) {CDR(tail) = CONS(eval(CAR(h), rho), R_NilValue);TAG(CDR(tail)) = CreateTag(TAG(h));tail = CDR(tail);h = CDR(h);}}else if(h != R_MissingArg)error("... used in an incorrect context\n");}else if (CAR(el) != R_MissingArg) {CDR(tail) = CONS(eval(CAR(el), rho), R_NilValue);tail = CDR(tail);TAG(tail) = CreateTag(TAG(el));}el = CDR(el);}UNPROTECT(1);return CDR(ans);}/* evalListKeepMissing - evaluate each expression in el *//* This is a naturally recursive algorithm, but we* use the iterative form below because it is does* not cause growth of the pointer protection stack,* and because it is a little more efficient. */SEXP evalListKeepMissing(SEXP el, SEXP rho){SEXP ans, h, tail;PROTECT(ans = tail = CONS(R_NilValue, R_NilValue));while(el != R_NilValue) {/* If we have a ... symbol, we look to see what it *//* is bound to. If its binding is Null (i.e. zero length) *//* we just ignore it and return the cdr with all its *//* expressions evaluated; if it is bound to a ... list *//* of promises, we force all the promises and then splice *//* the list of resulting values into the return value. *//* Anything else bound to a ... symbol is an error */if (CAR(el) == R_DotsSymbol) {h = findVar(CAR(el), rho);if (TYPEOF(h) == DOTSXP || h == R_NilValue) {while(h != R_NilValue) {if(CAR(h) == R_MissingArg)CDR(tail) = CONS(R_MissingArg, R_NilValue);elseCDR(tail) = CONS(eval(CAR(h), rho), R_NilValue);TAG(CDR(tail)) = CreateTag(TAG(h));tail = CDR(tail);h = CDR(h);}}else if(h != R_MissingArg)error("... used in an incorrect context\n");}else if (CAR(el) == R_MissingArg) {CDR(tail) = CONS(R_MissingArg, R_NilValue);tail = CDR(tail);TAG(tail) = CreateTag(TAG(el));}else {CDR(tail) = CONS(eval(CAR(el), rho), R_NilValue);tail = CDR(tail);TAG(tail) = CreateTag(TAG(el));}el = CDR(el);}UNPROTECT(1);return CDR(ans);}/* promiseArgs - create a promise to evaluate each argument *//* This is a naturally recursive algorithm, but we* use the iterative form below because it is does* not cause growth of the pointer protection stack,* and because it is a little more efficient. */SEXP promiseArgs(SEXP el, SEXP rho){SEXP ans, h, tail;PROTECT(ans = tail = CONS(R_NilValue, R_NilValue));while(el != R_NilValue) {/* If we have a ... symbol, we look to see what it *//* is bound to. If its binding is Null (i.e. zero length) *//* we just ignore it and return the cdr with all its *//* expressions promised; if it is bound to a ... list *//* of promises, we repromise all the promises and then splice *//* the list of resulting values into the return value. *//* Anything else bound to a ... symbol is an error *//* Is this double promise mechanism really needed? */if (CAR(el) == R_DotsSymbol) {h = findVar(CAR(el), rho);if (TYPEOF(h) == DOTSXP || h == R_NilValue) {while(h != R_NilValue) {CDR(tail) = CONS(mkPROMISE(CAR(h), rho), R_NilValue);TAG(CDR(tail)) = CreateTag(TAG(h));tail = CDR(tail);h = CDR(h);}}else if(h != R_MissingArg)error("... used in an incorrect context\n");}else if (CAR(el) == R_MissingArg) {CDR(tail) = CONS(R_MissingArg, R_NilValue);tail = CDR(tail);TAG(tail) = CreateTag(TAG(el));}else {CDR(tail) = CONS(mkPROMISE(CAR(el), rho), R_NilValue);tail = CDR(tail);TAG(tail) = CreateTag(TAG(el));}el = CDR(el);}UNPROTECT(1);return CDR(ans);}/* CheckFormals - check that each formal is a symbol */void CheckFormals(SEXP ls){if (isList(ls))for (; ls != R_NilValue; ls = CDR(ls))if (TYPEOF(TAG(ls)) != SYMSXP)error("invalid formal argument list for \"function\"\n");}/* WrongArgCount - error recovery for incorrect *//* argument count error. */void WrongArgCount(char *s){error("incorrect number of arguments to \"%s\"\n", s);}/* do_eval - evaluate the first argument *//* in the environment specified *//* by the second argument. */SEXP do_eval(SEXP call, SEXP op, SEXP args, SEXP rho){SEXP expr, env;int nback;checkArity(op, args);expr = CAR(args);env = CADR(args);switch(TYPEOF(env)) {case NILSXP:case ENVSXP:PROTECT(env); /* so we can unprotect 2 at the end */break;case LISTSXP:PROTECT(env = allocSExp(ENVSXP));FRAME(env) = duplicate(CADR(args));ENCLOS(env) = R_GlobalEnv;break;case INTSXP:case REALSXP:nback = asInteger(env);if (nback==NA_INTEGER)errorcall(call,"invalid environment\n");if (nback > 0 )nback -= framedepth(R_GlobalContext);nback = -nback;PROTECT(env = sysframe(nback,R_GlobalContext));break;default:errorcall(call, "invalid second argument\n");}if(isLanguage(expr) || isExpression(expr) || isSymbol(expr)) {PROTECT(expr);expr = eval(expr, env);UNPROTECT(1);}UNPROTECT(1);return expr;}SEXP do_recall(SEXP call, SEXP op, SEXP args, SEXP rho){RCNTXT *cptr;SEXP t, s, ans ;cptr=R_GlobalContext;/* get the args supplied */while(cptr != NULL) {if(cptr->callflag == CTXT_RETURN && cptr->cloenv == rho)break;cptr = cptr->nextcontext;}args=cptr->promargs;/* get the env recall was called from */s = R_GlobalContext->sysparent;while(cptr != NULL) {if(cptr->callflag == CTXT_RETURN && cptr->cloenv == s)break;cptr = cptr->nextcontext;}if(cptr == NULL)error("Recall called from outside a closure\n");t=CAR(cptr->call);s = findFun(t, cptr->sysparent);PROTECT(t=LCONS(t,args));ans = applyClosure(t, s, args, rho, R_NilValue);UNPROTECT(1);return ans;}SEXP EvalArgs(SEXP el, SEXP rho, int dropmissing){if(dropmissing) return evalList(el, rho);else return evalListKeepMissing(el, rho);}/* DispatchOrEval - This code is used in internal functions* which dispatch to object methods (e.g. "[" or "[[").* The code either builds promises and dispatches to the* appropriate method, or it evauates the (unevaluated)* arguments it comes in with and returns them so that* the generic built-in C code can continue.** To call this an ugly hack would be to insult all* existing ugly hacks at large in the world. */int DispatchOrEval(SEXP call, SEXP op, SEXP args, SEXP rho, SEXP *ans, int dropmissing){SEXP x;char *pt, buf[128];RCNTXT cntxt;PROTECT(x = eval(CAR(args),rho));pt = strrchr(CHAR(PRINTNAME(CAR(call))), '.');if( isObject(x) && (pt == NULL || strcmp(pt,".default")) ) {PROTECT(args = promiseArgs(args, rho));PRVALUE(CAR(args)) = x;sprintf(buf, "%s",CHAR(PRINTNAME(CAR(call))));begincontext(&cntxt, CTXT_RETURN, call, rho, rho, args);if(usemethod(buf, x, call, args, rho, ans)) {endcontext(&cntxt);UNPROTECT(2);return 1;}endcontext(&cntxt);}elsePROTECT(args);*ans = CONS(x, EvalArgs(CDR(args), rho, dropmissing));TAG(*ans) = CreateTag(TAG(args));UNPROTECT(2);return 0;}/* dominates - compare two class attributes and *//* decide whether one dominates the *//* other; for now a pretty simple *//* version but it could get more *//* complicated comparing factors *//* (ordered vs not) etc. */static SEXP dominates(SEXP cclass, SEXP newobj){SEXP t;t = getAttrib(newobj, R_ClassSymbol);if( cclass == R_NilValue )return t;return cclass;}int DispatchGroup(char* group, SEXP call, SEXP op, SEXP args, SEXP rho, SEXP *ans){int i, j, nargs, whichclass, set;SEXP class, s, t, m, meth, sxp, gr, newrho;char buf[512], generic[128], *pt;/* check whether we are processing the default method */sprintf(buf, "%s", CHAR(PRINTNAME(CAR(call))));pt = strtok(buf, ".");pt = strtok(NULL, ".");if( pt != NULL && !strcmp(pt, "default") )return 0;if( !strcmp(group, "Ops") )nargs = length(args);elsenargs = 1;if( nargs == 1 && !isObject(CAR(args)) )return 0;if(!isObject(CAR(args)) && !isObject(CADR(args)))return 0;class = getAttrib(CAR(args), R_ClassSymbol);if( nargs == 2 )class = dominates(class, CADR(args));sprintf(generic, "%s", CHAR(PRINTNAME(CAR(call))));j = length(class);sxp = R_NilValue;for(whichclass=0 ; whichclass<j ; whichclass++) {sprintf(buf, "%s.%s", generic, CHAR(STRING(class)[whichclass]));meth = install(buf);sxp = findVar(meth, rho);if(isFunction(sxp)) {PROTECT(gr = mkString(""));break;}}if ( !isFunction(sxp) ) /* no specific method look for group */for( whichclass=0 ; whichclass<j ; whichclass++ ) {sprintf(buf, "%s.%s", group, CHAR(STRING(class)[whichclass]));meth = install(buf);sxp = findVar(meth, rho);if( isFunction(sxp) ) {PROTECT(gr = mkString(group));break;}}if( !isFunction(sxp) ) /* no generic or group method so use default*/return 0;/* we either have a group method or a class method */PROTECT(newrho = allocSExp(ENVSXP));PROTECT(m = allocVector(STRSXP,nargs));s=args;for(i=0 ; i<nargs ; i++ ) {t=getAttrib(CAR(s),R_ClassSymbol);set = 0;if( isString(t) ) {for( j=0 ; j<length(t) ; j++ ) {if (!strcmp(CHAR(STRING(t)[j]), CHAR(STRING(class)[whichclass]))) {STRING(m)[i] = mkChar(buf);set = 1;break;}}}if( !set )STRING(m)[i] = mkChar("");s = CDR(s);}defineVar(install(".Method"), m, newrho);UNPROTECT(1);PROTECT(t=mkString(generic));defineVar(install(".Generic"), t, newrho);UNPROTECT(1);defineVar(install(".Group"), gr, newrho);set=length(class)-whichclass;PROTECT(t = allocVector(STRSXP, set));for(j=0 ; j<set ; j++ )STRING(t)[j] = duplicate(STRING(class)[whichclass++]);defineVar(install(".Class"), t, newrho);UNPROTECT(1);PROTECT(t = LCONS(meth,CDR(call)));/* the arguments have been evaluated; since we are passing them* out to a closure we need to wrap them in promises so that* they get duplicated and things like missing/substitute work* */PROTECT(s = promiseArgs(CDR(call), rho));if( length(s) != length(args) )errorcall(call,"dispatch error\n");for(m=s ; m!= R_NilValue ; m = CDR(m), args = CDR(args) )PRVALUE(CAR(m)) = CAR(args);*ans = applyClosure(t, sxp, s, rho, newrho);UNPROTECT(4);return 1;}