File: printcall.R

package info (click to toggle)
r-cran-plotmo 3.7.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 3,400 kB
  • sloc: sh: 13; makefile: 2
file content (131 lines) | stat: -rw-r--r-- 5,791 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# printcall.R: functions for printing call information

# If call is specified, print it (where call is from match.call or similar).
# Else use the call stack to determine the call. The n arg tells us how
# far to go back in the call stack.
#
# Examples: printcall()     describe the call to the current function
#           printcall(n=2)  describe the call to the caller of the current function
#           printcall(call) describe call where call is from match.call or similar

printcall <- function(prefix="", call=NULL, all=FALSE, n=1)
{
    # check prefix and n here, other args checked in call.as.char
    stopifnot.string(prefix, allow.empty=TRUE)
    stopifnot(is.numeric(n))
    call <- call.as.char(call, all, n+1)
    printf.wrap("%s%s\n", prefix, call)
}
# returns args and concise description of their values, dots are included
# all=TRUE to include all formal args (not always avail e.g. for primitives)
#
# TODO Does not expand the dots (just prints "..."), need fixed version of match.call
#      to expand the dots see e.g. higher.call.to.deprefix (but that would only work
#      here if dots for caller at n where the same as the dots to printcall).

call.as.char <- function(call=NULL, all=FALSE, n=1)
{
    stopifnot(is.numeric(all) || is.logical(all), length(all) == 1)
    stopifnot(is.numeric(n), length(n) == 1, n > 0)
    if(is.null(call))
        call <- match.call2(all=all, n=n+1) # +1 to skip call to call.as.char
    else if(all) # we have the call but not the func itself, so can't get formals
        stop("all=TRUE is not allowed when the call argument is used")
    fname <- fname.from.call(call)
    if(all) {
        formals <- formals(attr(call, "sys.function"))
        call[[1]] <- NULL               # delete func name from call, leave args
        formals[["..."]] <- NULL        # delete ... in formal args if any
        call <- merge.list(formals, call)
    } else
        call[[1]] <- NULL               # delete func name from call, leave args
    ret <- paste(fname, "(", list.as.char(call, maxlen=50), ")", sep="")
    attr(ret, "fname") <- fname # needed for alignment with nchar in printcall
    ret
}
# Similar to match.call but with args "all" and "n".
# Also, this always returns a call, even if it is merely "unknown()".
# So you can safely call it with any n (although n must be a positive int).

match.call2 <- function(all=FALSE, n=1)
{
    stopifnot(is.numeric(all) || is.logical(all), length(all) == 1)
    stopifnot(is.numeric(n), length(n) == 1, n > 0)
    # get sys.function and sys.call for the given n, needed for match.call
    sys.function <- try(sys.function(-n), silent=TRUE)
    if(is.try.err(sys.function) || is.null(sys.function)) # typically "not that many frames"
        return(call("unknown"))
    sys.call <- try(sys.call(-n), silent=TRUE)
    if(is.try.err(sys.call) || is.null(sys.call))
        return(call("unknown"))
    # TODO following can cause incorrect "... used in a situation where it does not exist"
    #      R version 3.1.4 will fix that issue in match.call (I hope)
    # envir <- parent.frame(n+1) # use when new version of match.call is ready
    call <- try(match.call(definition=sys.function, call=sys.call, expand.dots=TRUE),
                silent=TRUE)
    if(is.try.err(call)) {
        # match.call failed, fallback to a weaker description of call
        # no expansion of dots and no arg values :(
        call <- sys.call
    }
    attr(call, "sys.function") <- sys.function
    call
}
callers.name <- function(n=1)
{
    stopifnot(is.numeric(n), length(n) == 1, floor(n) == n, n >= 0)
    call <- try(sys.call(-(n+1)), silent=TRUE)
    fname.from.call(call) # will also check if try error
}
fname.from.call <- function(call) # call was obtained using sys.call() or similar
{
    if(is.try.err(call))
        return("unknown") # most likely n was misspecified (too big)
    if(is.null(call)) # e.g. NULL->source->withVisible->eval->eval->print->test->callers.name
        return("NULL")
    caller <- as.list(call)[[1]]
    if(is.name(caller))   # e.g. foo3(x=1)
        caller <- as.character(caller)
    else {                # class(caller) is "call" e.g. plotmo::localfunc(x=1)
        stopifnot(is.call(call))
        caller <- format(caller)
    }
    if(grepl("function (", substr(caller[1], 1, 10), fixed=TRUE))
        paste0("function(", paste.trunc(strip.space.collapse(substring(caller, 11))), ")")
    else
        paste.trunc(strip.space.collapse(caller))
}
# if EVAL is FALSE this will print something like xlim=..1, ylim=..2
# TODO add n arg when match.call is fixed (R version 3.2.1)
# TODO also then make this callable as printdots() instead of printdots(...)

printdots <- function(..., EVAL=TRUE, PREFIX=sprint("%s dots: ", callers.name))
{
    sys.call <- as.list(sys.call())
    ensure.dots.present(sys.call)
    callers.name <- callers.name()
    printf.wrap("%s%s\n", PREFIX, dots.as.char(..., EVAL=EVAL))
}
dots.as.char <- function(..., EVAL=TRUE)
{
    sys.call <- as.list(sys.call())
    ensure.dots.present(sys.call)
    dots <- match.call(expand.dots=FALSE)$...
    if(is.null(dots))
        return("no dots")
    if(EVAL) {
        stopifnot(is.numeric(EVAL) || is.logical(EVAL), length(EVAL) == 1)
        dots <- eval.dotlist(dots)
    }
    list.as.char(dots)
}
# issue error message if ... wasn't used in the call to dots.as.char
ensure.dots.present <- function(sys.call)
{
    dots.present <- FALSE
    for(i in seq_len(length(sys.call)))
        if(sys.call[i] == "...")
            dots.present <- TRUE
    if(!dots.present)
        stop0("dots.as.char should be invoked with dots, for example dots.as.char(...)")
}