### Checking parse(* deparse()) "inversion property" ---------------------------- ## EPD := eval-parse-deparse : eval(text = parse(deparse(*))) ## Hopefully typically the identity(): pd0 <- function(expr, backtick = TRUE, ...) parse(text = deparse(expr, backtick=backtick, ...)) id_epd <- function(expr, control = "all", ...) eval(pd0(expr, control=control, ...)) ## "digits17" ("all") causes different results on x86_64 and ## Windows/aarch64 dPut <- function(x, control = c("quoteExpression", "showAttributes", "niceNames", "keepInteger")) dput(x, control=control) ##' Does 'x' contain "real" numbers ##' with > 3 digits after "." where deparse may be platform dependent? hasReal <- function(x) { if(is.double(x) || is.complex(x)) !all((x == round(x, 3)) | is.na(x)) else if(is.logical(x) || is.integer(x) || is.symbol(x) || is.call(x) || is.environment(x) || is.character(x)) FALSE else if(is.recursive(x)) # recurse : any(vapply(x, hasReal, NA)) else if(isS4(x)) { if(length(sn <- slotNames(x))) any(vapply(sn, function(s) hasReal(slot(x, s)), NA)) else # no slots FALSE # ? } else FALSE } isMissObj <- function(obj) identical(obj, alist(a=)[[1]]) ##' Does 'obj' contain "the missing object" ? ##' @note defined recursively! hasMissObj <- function(obj) { if(is.recursive(obj)) { if(is.function(obj) || is.language(obj)) FALSE else # incl pairlist()s any(vapply(obj, hasMissObj, NA)) } else isMissObj(obj) } check_EPD <- function(obj, show = !hasReal(obj), oNam = deparse(substitute(obj)), ## FIXME: add "niceNames" here: ?!? control = c("keepInteger","showAttributes","keepNA"), not.identical.ldouble = if(!interactive()) c("t1", "t2", "ydata"), eq.tol = if(noLdbl) 2*.Machine$double.eps else 0) { stopifnot(is.character(oNam)) if(show) dPut(obj) if(is.environment(obj) || hasMissObj(obj)) { cat("__ not parse()able __:", if(is.environment(obj)) "environment" else "hasMissObj(.) is true", "\n") return(invisible(obj)) # cannot parse it } ob2 <- id_epd(obj) po <- tryCatch(pd0(obj, control=control),# the default deparse() *should* typically parse error = function(e) { cat("default parse(*, deparse(obj)) failed:\n ", conditionMessage(e), "\n but deparse(*, control='all') should work.\n") pd0(obj, control = "all") }) noLdbl <- (.Machine$sizeof.longdouble <= 8) ## TRUE typically from --disable-long-double if(!identical(obj, ob2, ignore.environment=TRUE, ignore.bytecode=TRUE, ignore.srcref=TRUE)) { ae <- all.equal(obj, ob2, tolerance = eq.tol, # in case of functions: check.environment=FALSE) if(is.na(match(oNam, not.identical.ldouble))) { ae.txt <- "all.equal(*,*, tol = ..)" ## differs for "no-ldouble": sprintf("all.equal(*,*, tol = %.3g)", eq.tol) cat("not identical(*, ignore.env=T),", if(isTRUE(ae)) paste("but", ae.txt), "\n") } if(!isTRUE(ae)) stop("Not equal: ", ae.txt, paste(c(" giving", head(ae, 2), if(length(ae) > 2) "...."), collapse = "\n ")) } if(!is.language(obj)) { ob2. <- eval(obj) ## almost always *NOT* identical to obj, but eval()ed } if(show || !is.list(obj)) { ## check it works when wrapped (but do not recurse inf.!) cat(" --> checking list(*): ") check_EPD(list(.chk = obj), show = FALSE, oNam=oNam, eq.tol=eq.tol) cat("Ok\n") } invisible(obj) } ##' Check deparse <--> parse consistency for *all* objects: runEPD_checks <- function(env = .GlobalEnv) { stopifnot(is.environment(env)) for(nm in ls(envir=env)) { cat(nm,": ", sep="") x <- env[[nm]] ## if(!any(nm == "mf")) ## 'mf' [bug in deparse(mf, control="all") now fixed] check_EPD(x, oNam=nm) if(is.function(x) && !inherits(x, "classGeneratorFunction")) { ## FIXME? classGeneratorFunction, e.g., mForm don't "work" yet cat("checking body(.):\n") check_EPD(if(is.language(bx <- body(x))) removeSource(bx) else bx) cat("checking formals(.):\n"); check_EPD(formals(x)) } cat("--=--=--=--=--\n") } }