99# ' @param code (`character`, `language` or `expression`) code to evaluate.
1010# ' It is possible to preserve original formatting of the `code` by providing a `character` or an
1111# ' `expression` being a result of `parse(keep.source = TRUE)`.
12+ # ' @param ... ([`dots`]) additional arguments passed to future methods.
1213# '
1314# ' @return
1415# ' `qenv` environment with `code/expr` evaluated or `qenv.error` if evaluation fails.
2122# ' q <- eval_code(q, quote(library(checkmate)))
2223# ' q <- eval_code(q, expression(assert_number(a)))
2324# '
24- # ' @aliases eval_code,qenv,character-method
25- # ' @aliases eval_code,qenv,language-method
26- # ' @aliases eval_code,qenv,expression-method
27- # ' @aliases eval_code,qenv.error,ANY-method
28- # '
25+ # ' @aliases eval_code,qenv-method
26+ # ' @aliases eval_code,qenv.error-method
27+ # ' @seealso [within.qenv]
2928# ' @export
30- setGeneric ("eval_code ", function(object, code) standardGeneric("eval_code"))
29+ setGeneric ("eval_code ", function(object, code, ...) standardGeneric("eval_code"))
30+
31+ setMethod ("eval_code ", signature = c(object = "qenv"), function(object, code, ...) {
32+ if (! is.language(code ) && ! is.character(code )) {
33+ stop(" eval_code accepts code being language or character" )
34+ }
35+ code <- .preprocess_code(code )
36+ # preprocess code to ensure it is a character vector
37+ .eval_code(object = object , code = code , ... )
38+ })
39+
40+ setMethod ("eval_code ", signature = c(object = "qenv.error"), function(object, code, ...) object)
3141
32- setMethod ("eval_code ", signature = c("qenv", "character"), function(object, code) {
42+ # ' @keywords internal
43+ .eval_code <- function (object , code , ... ) {
44+ if (identical(trimws(code ), " " ) || length(code ) == 0 ) {
45+ return (object )
46+ }
3347 parsed_code <- parse(text = code , keep.source = TRUE )
3448 object @ .xData <- rlang :: env_clone(object @ .xData , parent = parent.env(.GlobalEnv ))
3549 if (length(parsed_code ) == 0 ) {
@@ -42,7 +56,6 @@ setMethod("eval_code", signature = c("qenv", "character"), function(object, code
4256 for (i in seq_along(code_split )) {
4357 current_code <- code_split [[i ]]
4458 current_call <- parse(text = current_code , keep.source = TRUE )
45-
4659 # Using withCallingHandlers to capture warnings and messages.
4760 # Using tryCatch to capture the error and abort further evaluation.
4861 x <- withCallingHandlers(
@@ -60,7 +73,7 @@ setMethod("eval_code", signature = c("qenv", "character"), function(object, code
6073 errorCondition(
6174 message = sprintf(
6275 " %s \n when evaluating qenv code:\n %s" ,
63- . ansi_strip(conditionMessage(e )),
76+ cli :: ansi_strip(conditionMessage(e )),
6477 current_code
6578 ),
6679 class = c(" qenv.error" , " try-error" , " simpleError" ),
@@ -69,11 +82,11 @@ setMethod("eval_code", signature = c("qenv", "character"), function(object, code
6982 }
7083 ),
7184 warning = function (w ) {
72- attr(current_code , " warning" ) <<- . ansi_strip(sprintf(" > %s\n " , conditionMessage(w )))
85+ attr(current_code , " warning" ) <<- cli :: ansi_strip(sprintf(" > %s\n " , conditionMessage(w )))
7386 invokeRestart(" muffleWarning" )
7487 },
7588 message = function (m ) {
76- attr(current_code , " message" ) <<- . ansi_strip(sprintf(" > %s" , conditionMessage(m )))
89+ attr(current_code , " message" ) <<- cli :: ansi_strip(sprintf(" > %s" , conditionMessage(m )))
7790 invokeRestart(" muffleMessage" )
7891 }
7992 )
@@ -87,42 +100,17 @@ setMethod("eval_code", signature = c("qenv", "character"), function(object, code
87100
88101 lockEnvironment(object @ .xData , bindings = TRUE )
89102 object
90- })
91-
92- setMethod ("eval_code ", signature = c("qenv", "language"), function(object, code) {
93- eval_code(object , code = paste(vapply(lang2calls(code ), deparse1 , collapse = " \n " , character (1L )), collapse = " \n " ))
94- })
103+ }
95104
96- setMethod ("eval_code ", signature = c("qenv", "expression"), function(object, code) {
97- srcref <- attr(code , " wholeSrcref" )
98- if (length(srcref )) {
99- eval_code(object , code = paste(attr(code , " wholeSrcref" ), collapse = " \n " ))
105+ setGeneric (".preprocess_code ", function(code) standardGeneric(".preprocess_code"))
106+ setMethod (".preprocess_code ", signature = c("character"), function(code) paste(code, collapse = "\n"))
107+ setMethod (".preprocess_code ", signature = c("ANY"), function(code) {
108+ if (is.expression(code ) && length(attr(code , " wholeSrcref" ))) {
109+ paste(attr(code , " wholeSrcref" ), collapse = " \n " )
100110 } else {
101- Reduce(function (u , v ) {
102- if (inherits(v , " =" ) && identical(typeof(v ), " language" )) {
103- # typeof(`=`) is language, but it doesn't dispatch on it, so we need to
104- # explicitly pass it as first class of the object
105- class(v ) <- unique(c(" language" , class(v )))
106- }
107- eval_code(u , v )
108- }, init = object , x = code )
111+ paste(
112+ vapply(lang2calls(code ), deparse1 , collapse = " \n " , character (1L )),
113+ collapse = " \n "
114+ )
109115 }
110116})
111-
112- setMethod ("eval_code ", signature = c("qenv.error", "ANY"), function(object, code) {
113- object
114- })
115-
116- # if cli is installed rlang adds terminal printing characters
117- # which need to be removed
118- .ansi_strip <- function (chr ) {
119- if (requireNamespace(" cli" , quietly = TRUE )) {
120- cli :: ansi_strip(chr )
121- } else {
122- chr
123- }
124- }
125-
126- get_code_attr <- function (qenv , attr ) {
127- unlist(lapply(qenv @ code , function (x ) attr(x , attr )))
128- }
0 commit comments