Can a function know what other function called it?

classic Classic list List threaded Threaded
9 messages Options
Reply | Threaded
Open this post in threaded view
|

Can a function know what other function called it?

kynn
Suppose function foo calls function bar.  Is there any way in which
bar can find out the name of the function that called it, "foo"?

There are two generalization to this question that interest me.
First, can this query go farther up the call stack?  I.e. if bar now
calls baz, can baz find out the name of the function that called the
function that called it, i.e. "foo"?  Second, what other information,
beside its name, can bar find about the environment where it was
called?  E.g. can it find out the file name and line number of the
function call?

Thanks!

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Can a function know what other function called it?

Robert Gentleman
Hi Kynn,


Kynn Jones wrote:
> Suppose function foo calls function bar.  Is there any way in which
> bar can find out the name of the function that called it, "foo"?

 essentially yes. You can find out about the call stack by using sys.calls and
sys.parents etc. The man page plus additional manuals should be sufficient, but
let us know if there are things that are not clear.

>
> There are two generalization to this question that interest me.
> First, can this query go farther up the call stack?  I.e. if bar now
> calls baz, can baz find out the name of the function that called the
> function that called it, i.e. "foo"?  Second, what other information,

 yes - you can (at least currently) get access to the entire calling stack and
some manipulations can be performed.


> beside its name, can bar find about the environment where it was
> called?  E.g. can it find out the file name and line number of the

 there is no real concept of file and line number associated with a function
definition (nor need their even be a name - functions can be anonymous).

 If you want to map back to source files then I think that currently we do not
keep quite enough information when a function is sourced. Others may be able to
elaborate more (or correct my mistakes).  I think we currently store the actual
text for the body of the function so that it can be used for printing, but we
don't store a file name/location/line number or anything of that sort. It could
probably be added, but would be a lot of work, so it would need someone who
really wanted it to do that.

 However, you can find out lots of other things if you want.  Do note that while
 it is possible to determine which function initiated the call, it is not
necessarily possible to figure out which of the calls (if there is more than one
in the body of the function) is active.  R does not keep track of things in that
way. To be clear if foo looks like:

  foo <- function(x) {
    bar(x)
    x = sqrt(x)
    bar(x)
  }
  and you have a breakpoint in bar, you could not (easily) distinguish which of
the two calls to bar was active. There is no line counter or anything of that
sort available.

 best wishes
   Robert

> function call?
>
> Thanks!
>
> ______________________________________________
> [hidden email] mailing list
> https://stat.ethz.ch/mailman/listinfo/r-devel
>

--
Robert Gentleman, PhD
Program in Computational Biology
Division of Public Health Sciences
Fred Hutchinson Cancer Research Center
1100 Fairview Ave. N, M1-B514
PO Box 19024
Seattle, Washington 98109-1024
206-667-7700
[hidden email]

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Can a function know what other function called it?

kynn
On Sat, May 23, 2009 at 4:55 PM, Robert Gentleman <[hidden email]> wrote:

> Hi Kynn,
>
>
> Kynn Jones wrote:
>> Suppose function foo calls function bar.  Is there any way in which
>> bar can find out the name of the function that called it, "foo"?
>
>  essentially yes. You can find out about the call stack by using sys.calls and
> sys.parents etc. The man page plus additional manuals should be sufficient, but
> let us know if there are things that are not clear.

Thanks a lot!  That was very helpful.

This is the best I was able to come up with:

bar <- function() {
  caller <- sub("\\(.*", "", rev(sys.calls())[[2]])
  cat(sprintf("bar: i was called by \"%s\"\n", caller))
}

foo <- function() bar()

> foo()
bar: i was called by "foo"
>

So it works, but I'm a n00b with R.  If there's a less labored way to
achieve this, please let me know.

And thanks again for your response.  It was very instructive.

KJ

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Can a function know what other function called it?

Duncan Murdoch
In reply to this post by Robert Gentleman
On 23/05/2009 4:55 PM, Robert Gentleman wrote:

> Hi Kynn,
>
>
> Kynn Jones wrote:
>> Suppose function foo calls function bar.  Is there any way in which
>> bar can find out the name of the function that called it, "foo"?
>
>  essentially yes. You can find out about the call stack by using sys.calls and
> sys.parents etc. The man page plus additional manuals should be sufficient, but
> let us know if there are things that are not clear.
>
>> There are two generalization to this question that interest me.
>> First, can this query go farther up the call stack?  I.e. if bar now
>> calls baz, can baz find out the name of the function that called the
>> function that called it, i.e. "foo"?  Second, what other information,
>
>  yes - you can (at least currently) get access to the entire calling stack and
> some manipulations can be performed.
>
>
>> beside its name, can bar find about the environment where it was
>> called?  E.g. can it find out the file name and line number of the
>
>  there is no real concept of file and line number associated with a function
> definition (nor need their even be a name - functions can be anonymous).
>
>  If you want to map back to source files then I think that currently we do not
> keep quite enough information when a function is sourced. Others may be able to
> elaborate more (or correct my mistakes).  I think we currently store the actual
> text for the body of the function so that it can be used for printing, but we
> don't store a file name/location/line number or anything of that sort. It could
> probably be added, but would be a lot of work, so it would need someone who
> really wanted it to do that.

By default we don't store either a copy of the source or the references
to the source file for functions in a package, but those options can be
enabled, and are enabled by default for users working at the console.
For example, with this source in c:/temp/test.R:

g <- function(x) {
   x <- 1
   y <- 4
}

You can do the following:

 > getOption("keep.source")
[1] TRUE
 > source("c:/temp/test.R")
 > as.list(g)[[2]]
{
     x <- 1
     y <- 4
}
attr(,"srcfile")
c:/temp/test.R

You can also find out which part of the file corresponds to each
statement of the function definition.  For example,

 > attributes(as.list(g)[[2]])
$srcref
$srcref[[1]]
{

$srcref[[2]]
x <- 1

$srcref[[3]]
y <- 4


$srcfile
c:/temp/test.R

 > print(attr(as.list(g)[[2]], "srcref")[[2]])
x <- 1
 > print(attr(as.list(g)[[2]], "srcref")[[2]], useSource=FALSE)
<srcref: file "c:/temp/test.R" chars 2:3 to 2:8>


>
>  However, you can find out lots of other things if you want.  Do note that while
>  it is possible to determine which function initiated the call, it is not
> necessarily possible to figure out which of the calls (if there is more than one
> in the body of the function) is active.  R does not keep track of things in that
> way. To be clear if foo looks like:
>
>   foo <- function(x) {
>     bar(x)
>     x = sqrt(x)
>     bar(x)
>   }
>   and you have a breakpoint in bar, you could not (easily) distinguish which of
> the two calls to bar was active. There is no line counter or anything of that
> sort available.

The evaluator doesn't pay any attention to srcref records, so this is
still true, but it would be possible to keep the srcref on the stack as
well as all the other info there.

I've written code (and I think I sent it to you last year) that can do
things like replacing the statement coming from a particular line of a
file with whatever code you like; this could be used in writing a nice
source-level debugger.

Duncan Murdoch

>
>  best wishes
>    Robert
>
>> function call?
>>
>> Thanks!
>>
>> ______________________________________________
>> [hidden email] mailing list
>> https://stat.ethz.ch/mailman/listinfo/r-devel
>>
>

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Can a function know what other function called it?

Romain Francois
Duncan Murdoch wrote:
> On 23/05/2009 4:55 PM, Robert Gentleman wrote:
>> Kynn Jones wrote:
>>
[snip]

>>
>>   and you have a breakpoint in bar, you could not (easily)
>> distinguish which of
>> the two calls to bar was active. There is no line counter or anything
>> of that
>> sort available.
>
> The evaluator doesn't pay any attention to srcref records, so this is
> still true, but it would be possible to keep the srcref on the stack
> as well as all the other info there.

Please

>
> I've written code (and I think I sent it to you last year) that can do
> things like replacing the statement coming from a particular line of a
> file with whatever code you like; this could be used in writing a nice
> source-level debugger.

yes

>
> Duncan Murdoch
>
>>
>>  best wishes
>>    Robert
>>
>


--
Romain Francois
Independent R Consultant
+33(0) 6 28 91 30 30
http://romainfrancois.blog.free.fr

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Can a function know what other function called it?

Duncan Murdoch
On 5/24/2009 10:23 AM, Romain Francois wrote:

> Duncan Murdoch wrote:
>> On 23/05/2009 4:55 PM, Robert Gentleman wrote:
>>> Kynn Jones wrote:
>>>
> [snip]
>>>
>>>   and you have a breakpoint in bar, you could not (easily)
>>> distinguish which of
>>> the two calls to bar was active. There is no line counter or anything
>>> of that
>>> sort available.
>>
>> The evaluator doesn't pay any attention to srcref records, so this is
>> still true, but it would be possible to keep the srcref on the stack
>> as well as all the other info there.
>
> Please
Here's a patch file that does this.  (Will it make it through to the
mailing list?  We'll see.)  It's still in progress, so I'm not even
ready to put it into R-devel, but you're welcome to try it out.

The basic idea is that it attaches srcref attributes to the values
returned from sys.calls (which won't be displayed, but if you want to
play with them you can) and to .Traceback (which traceback() will
display).  debug() will also show them.

Not sure what bad side effects (e.g. on execution time) this has.

Duncan Murdoch

>
>>
>> I've written code (and I think I sent it to you last year) that can do
>> things like replacing the statement coming from a particular line of a
>> file with whatever code you like; this could be used in writing a nice
>> source-level debugger.
>
> yes
>
>>
>> Duncan Murdoch
>>
>>>
>>>  best wishes
>>>    Robert
>>>
>>
>
>

Index: src/include/Defn.h
===================================================================
--- src/include/Defn.h (revision 48618)
+++ src/include/Defn.h (working copy)
@@ -493,6 +493,7 @@
     IStackval *intstack;
 # endif
 #endif
+    SEXP srcref;        /* The source line in effect */
 } RCNTXT, *context;
 
 /* The Various Context Types.
@@ -678,6 +679,7 @@
 extern0 Rboolean R_ShowWarnCalls INI_as(FALSE);
 extern0 Rboolean R_ShowErrorCalls INI_as(FALSE);
 extern0 int R_NShowCalls INI_as(50);
+extern0 SEXP R_Srcref;
 
 LibExtern Rboolean utf8locale  INI_as(FALSE);  /* is this a UTF-8 locale? */
 LibExtern Rboolean mbcslocale  INI_as(FALSE);  /* is this a MBCS locale? */
Index: src/library/base/R/traceback.R
===================================================================
--- src/library/base/R/traceback.R (revision 48618)
+++ src/library/base/R/traceback.R (working copy)
@@ -25,7 +25,12 @@
     else {
         for(i in 1L:n) {
             label <- paste(n-i+1L, ": ", sep="")
-            m <- length(x[[i]])
+            m <- length(x[[i]])            
+            if (!is.null(srcref <- attr(x[[i]], "srcref"))) {
+             srcfile <- attr(srcref, "srcfile")
+             x[[i]][m] <- paste(x[[i]][m], " at ", # as.character(srcref, useSource=FALSE), sep="")
+                        basename(srcfile$filename), "#", srcref[1L]+1, sep="")
+            }
             if(m > 1)
                 label <- c(label, rep(substr("          ", 1L,
                                              nchar(label, type="w")),
@@ -35,7 +40,7 @@
                     sep = "\n")
                 cat(label[max.lines+1L], " ...\n")
             } else
-            cat(paste(label, x[[i]], sep=""), sep="\n")
+             cat(paste(label, x[[i]], sep=""), sep="\n")
         }
     }
     invisible()
Index: src/main/context.c
===================================================================
--- src/main/context.c (revision 48618)
+++ src/main/context.c (working copy)
@@ -50,6 +50,7 @@
  * non-local return (i.e. an error)
  * cenddata a void pointer to data for cend to use
  * vmax the current setting of the R_alloc stack
+ * srcref the srcref at the time of the call
  *
  *  Context types can be one of:
  *
@@ -182,6 +183,7 @@
     R_BCIntStackTop = cptr->intstack;
 # endif
 #endif
+    R_Srcref = cptr->srcref;
 }
 
 
@@ -242,6 +244,7 @@
     cptr->intstack = R_BCIntStackTop;
 # endif
 #endif
+    cptr->srcref = R_Srcref;
     R_GlobalContext = cptr;
 }
 
@@ -394,6 +397,8 @@
 {
     /* negative n counts back from the current frame */
     /* positive n counts up from the globalEnv */
+    SEXP result;
+    
     if (n > 0)
  n = framedepth(cptr) - n;
     else
@@ -403,15 +408,24 @@
   _("not that many frames on the stack"));
     while (cptr->nextcontext != NULL) {
  if (cptr->callflag & CTXT_FUNCTION ) {
-    if (n == 0)
- return (duplicate(cptr->call));
-    else
+    if (n == 0) {
+     PROTECT(result = duplicate(cptr->call));
+     if (cptr->srcref && !isNull(cptr->srcref))
+        setAttrib(result, R_SrcrefSymbol, duplicate(cptr->srcref));
+     UNPROTECT(1);
+     return result;
+    } else
  n--;
  }
  cptr = cptr->nextcontext;
     }
-    if (n == 0 && cptr->nextcontext == NULL)
- return (duplicate(cptr->call));
+    if (n == 0 && cptr->nextcontext == NULL) {
+ PROTECT(result = duplicate(cptr->call));
+ if (!isNull(cptr->srcref))
+    setAttrib(result, R_SrcrefSymbol, duplicate(cptr->srcref));
+ UNPROTECT(1);
+ return result;
+    }
     errorcall(R_GlobalContext->call, _("not that many frames on the stack"));
     return R_NilValue; /* just for -Wall */
 }
Index: src/main/errors.c
===================================================================
--- src/main/errors.c (revision 48618)
+++ src/main/errors.c (working copy)
@@ -1294,6 +1294,8 @@
  skip--;
     else {
  SETCAR(t, deparse1(c->call, 0, DEFAULTDEPARSE));
+ if (c->srcref && !isNull(c->srcref))
+    setAttrib(CAR(t), R_SrcrefSymbol, duplicate(c->srcref));
  t = CDR(t);
     }
  }
Index: src/main/eval.c
===================================================================
--- src/main/eval.c (revision 48618)
+++ src/main/eval.c (working copy)
@@ -349,6 +349,10 @@
 {
     SEXP op, tmp;
     static int evalcount = 0;
+    
+    /* Save the current srcref context. */
+    
+    SEXP srcrefsave = R_Srcref;
 
     /* The use of depthsave below is necessary because of the
        possibility of non-local returns from evaluation.  Without this
@@ -515,6 +519,7 @@
  UNIMPLEMENTED_TYPE("eval", e);
     }
     R_EvalDepth = depthsave;
+    R_Srcref = srcrefsave;
     return (tmp);
 }
 
@@ -1191,20 +1196,29 @@
 
 SEXP attribute_hidden do_begin(SEXP call, SEXP op, SEXP args, SEXP rho)
 {
-    SEXP s;
-    if (args == R_NilValue) {
- s = R_NilValue;
-    }
-    else {
- while (args != R_NilValue) {
+    SEXP s = R_NilValue;
+    if (args != R_NilValue) {
+     SEXP srcrefs = getAttrib(call, R_SrcrefSymbol);
+     Rboolean usesrcrefs = !isNull(srcrefs);
+     int i = 1;
+     R_Srcref = R_NilValue;
+ while (args != R_NilValue) {    
+    if (usesrcrefs) PROTECT(R_Srcref = VECTOR_ELT(srcrefs, i++));
     if (DEBUG(rho)) {
- Rprintf("debug: ");
+        if (usesrcrefs) {
+        SEXP srcfile = getAttrib(R_Srcref, R_SrcfileSymbol);
+        SEXP filename = findVar(install("filename"), srcfile);        
+            Rprintf("debug at %s#%d: ", CHAR(STRING_ELT(filename, 0)), INTEGER(R_Srcref)[0]);
+        } else
+    Rprintf("debug: ");
  PrintValue(CAR(args));
  do_browser(call, op, R_NilValue, rho);
     }
     s = eval(CAR(args), rho);
+    if (usesrcrefs) UNPROTECT(1);
     args = CDR(args);
  }
+ R_Srcref = R_NilValue;
     }
     return s;
 }
Index: src/main/main.c
===================================================================
--- src/main/main.c (revision 48618)
+++ src/main/main.c (working copy)
@@ -980,11 +980,18 @@
 {
   RCNTXT *cptr;
   int lct = 1;
+  SEXP srcref;
 
   for (cptr = R_GlobalContext; cptr; cptr = cptr->nextcontext) {
     if ((cptr->callflag & (CTXT_FUNCTION | CTXT_BUILTIN)) &&
  (TYPEOF(cptr->call) == LANGSXP)) {
- Rprintf("where %d: ", lct++);
+ Rprintf("where %d", lct++);
+ if (cptr->srcref && !isNull(srcref = cptr->srcref)) {
+    SEXP srcfile = getAttrib(srcref, R_SrcfileSymbol);
+    SEXP filename = findVar(install("filename"), srcfile);
+    Rprintf(" at %s#%d", CHAR(STRING_ELT(filename, 0)), INTEGER(srcref)[0]);
+ }
+ Rprintf(": ");
  PrintValue(cptr->call);
     }
   }
Index: src/main/memory.c
===================================================================
--- src/main/memory.c (revision 48618)
+++ src/main/memory.c (working copy)
@@ -1653,6 +1653,9 @@
 
     /*  Unbound values which are to be preserved through GCs */
     R_PreciousList = R_NilValue;
+    
+    /*  The current source line */
+    R_Srcref = R_NilValue;
 }
 
 /* Since memory allocated from the heap is non-moving, R_alloc just

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Can a function know what other function called it?

Romain Francois
Duncan Murdoch wrote:

> On 5/24/2009 10:23 AM, Romain Francois wrote:
>> Duncan Murdoch wrote:
>>> On 23/05/2009 4:55 PM, Robert Gentleman wrote:
>>>> Kynn Jones wrote:
>>>>
>> [snip]
>>>>
>>>>   and you have a breakpoint in bar, you could not (easily)
>>>> distinguish which of
>>>> the two calls to bar was active. There is no line counter or
>>>> anything of that
>>>> sort available.
>>>
>>> The evaluator doesn't pay any attention to srcref records, so this
>>> is still true, but it would be possible to keep the srcref on the
>>> stack as well as all the other info there.
>>
>> Please
>
> Here's a patch file that does this.  (Will it make it through to the
> mailing list?  We'll see.)  It's still in progress, so I'm not even
> ready to put it into R-devel, but you're welcome to try it out.
>
> The basic idea is that it attaches srcref attributes to the values
> returned from sys.calls (which won't be displayed, but if you want to
> play with them you can) and to .Traceback (which traceback() will
> display).  debug() will also show them.
>
> Not sure what bad side effects (e.g. on execution time) this has.
>
> Duncan Murdoch

Many thanks. I'll play with this right now. My guess is that since these
"srcref" records are calculated anyway by the parser, this won't affect
too much the execution time.

>
>>
>>>
>>> I've written code (and I think I sent it to you last year) that can
>>> do things like replacing the statement coming from a particular line
>>> of a file with whatever code you like; this could be used in writing
>>> a nice source-level debugger.
>>
>> yes
>>
>>>
>>> Duncan Murdoch
>>>
>>>>
>>>>  best wishes
>>>>    Robert
>>>>
>>>
>>
>>
>


--
Romain Francois
Independent R Consultant
+33(0) 6 28 91 30 30
http://romainfrancois.blog.free.fr

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Can a function know what other function called it?

Duncan Murdoch
On 5/26/2009 12:57 PM, Romain Francois wrote:

> Duncan Murdoch wrote:
>> On 5/24/2009 10:23 AM, Romain Francois wrote:
>>> Duncan Murdoch wrote:
>>>> On 23/05/2009 4:55 PM, Robert Gentleman wrote:
>>>>> Kynn Jones wrote:
>>>>>
>>> [snip]
>>>>>
>>>>>   and you have a breakpoint in bar, you could not (easily)
>>>>> distinguish which of
>>>>> the two calls to bar was active. There is no line counter or
>>>>> anything of that
>>>>> sort available.
>>>>
>>>> The evaluator doesn't pay any attention to srcref records, so this
>>>> is still true, but it would be possible to keep the srcref on the
>>>> stack as well as all the other info there.
>>>
>>> Please
>>
>> Here's a patch file that does this.  (Will it make it through to the
>> mailing list?  We'll see.)  It's still in progress, so I'm not even
>> ready to put it into R-devel, but you're welcome to try it out.
>>
>> The basic idea is that it attaches srcref attributes to the values
>> returned from sys.calls (which won't be displayed, but if you want to
>> play with them you can) and to .Traceback (which traceback() will
>> display).  debug() will also show them.
>>
>> Not sure what bad side effects (e.g. on execution time) this has.
>>
>> Duncan Murdoch
>
> Many thanks. I'll play with this right now. My guess is that since these
> "srcref" records are calculated anyway by the parser, this won't affect
> too much the execution time.

It does add a little extra execution time to every statement, and more
to lines that have source references.  Whether enough to matter, I don't
know.

Duncan Murdoch


>
>>
>>>
>>>>
>>>> I've written code (and I think I sent it to you last year) that can
>>>> do things like replacing the statement coming from a particular line
>>>> of a file with whatever code you like; this could be used in writing
>>>> a nice source-level debugger.
>>>
>>> yes
>>>
>>>>
>>>> Duncan Murdoch
>>>>
>>>>>
>>>>>  best wishes
>>>>>    Robert
>>>>>
>>>>
>>>
>>>
>>
>
>

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel
Reply | Threaded
Open this post in threaded view
|

Re: Can a function know what other function called it?

Duncan Murdoch
In reply to this post by Romain Francois
On 5/26/2009 12:57 PM, Romain Francois wrote:

> Duncan Murdoch wrote:
>> On 5/24/2009 10:23 AM, Romain Francois wrote:
>>> Duncan Murdoch wrote:
>>>> On 23/05/2009 4:55 PM, Robert Gentleman wrote:
>>>>> Kynn Jones wrote:
>>>>>
>>> [snip]
>>>>>
>>>>>   and you have a breakpoint in bar, you could not (easily)
>>>>> distinguish which of
>>>>> the two calls to bar was active. There is no line counter or
>>>>> anything of that
>>>>> sort available.
>>>>
>>>> The evaluator doesn't pay any attention to srcref records, so this
>>>> is still true, but it would be possible to keep the srcref on the
>>>> stack as well as all the other info there.
>>>
>>> Please
>>
>> Here's a patch file that does this.  (Will it make it through to the
>> mailing list?  We'll see.)  It's still in progress, so I'm not even
>> ready to put it into R-devel, but you're welcome to try it out.
>>
>> The basic idea is that it attaches srcref attributes to the values
>> returned from sys.calls (which won't be displayed, but if you want to
>> play with them you can) and to .Traceback (which traceback() will
>> display).  debug() will also show them.
>>
>> Not sure what bad side effects (e.g. on execution time) this has.
>>
>> Duncan Murdoch
>
> Many thanks. I'll play with this right now. My guess is that since these
> "srcref" records are calculated anyway by the parser, this won't affect
> too much the execution time.

I have now committed a more complete version of the code to R-devel.
It's still "experimental"; formats of the displays are likely to change,
and the internal structures might.

This is likely to disrupt front-ends that expect a particular format
from tracebacks or the debugger; hopefully the added information will
make up for the inconvenience.

Duncan Murdoch

>
>>
>>>
>>>>
>>>> I've written code (and I think I sent it to you last year) that can
>>>> do things like replacing the statement coming from a particular line
>>>> of a file with whatever code you like; this could be used in writing
>>>> a nice source-level debugger.
>>>
>>> yes
>>>
>>>>
>>>> Duncan Murdoch
>>>>
>>>>>
>>>>>  best wishes
>>>>>    Robert
>>>>>
>>>>
>>>
>>>
>>
>
>

______________________________________________
[hidden email] mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel