[R] Extracting Comments from Functions/Packages
Enrico Schumann
e@ @end|ng |rom enr|co@chum@nn@net
Thu Oct 7 21:26:47 CEST 2021
On Thu, 07 Oct 2021, Leonard Mada via R-help writes:
> Dear R Users,
>
>
> I wrote a minimal parser to extract strings and
> comments from the function definitions.
>
>
> The string extraction works fine. But there are no comments:
>
> a.) Are the comments stripped from the compiled packages?
>
> b.) Alternatively: Is the deparse() not suited for this task?
>
> b.2.) Is deparse() parsing the function/expression itself?
>
> [see code for extract.str.fun() function below]
>
>
> ### All strings in "base"
> extract.str.pkg("base")
> # type = 2 for Comments:
> extract.str.pkg("base", type=2)
> extract.str.pkg("sp", type=2)
> extract.str.pkg("NetLogoR", type=2)
>
> The code for the 2 functions (extract.str.pkg &
> extract.str.fun) and the code for the parse.simple()
> parser are below.
>
>
> Sincerely,
>
>
> Leonard
>
> =======
>
> The latest code is on GitHub:
>
> https://github.com/discoleo/R/blob/master/Stat/Tools.Formulas.R
>
>
> ### Code to process functions in packages:
> extract.str.fun = function(fn, pkg, type=1, strip=TRUE) {
> fn = as.symbol(fn); pkg = as.symbol(pkg);
> fn = list(substitute(pkg ::: fn));
> # deparse
> s = paste0(do.call(deparse, fn), collapse="");
> npos = parse.simple(s);
> extract.str(s, npos[[type]], strip=strip)
> }
> extract.str.pkg = function(pkg, type=1, exclude.z = TRUE, strip=TRUE) {
> nms = ls(getNamespace(pkg));
> l = lapply(nms, function(fn) extract.str.fun(fn,
> pkg, type=type, strip=strip));
> if(exclude.z) {
> hasStr = sapply(l, function(s) length(s) >= 1);
> nms = nms[hasStr];
> l = l[hasStr];
> }
> names(l) = nms;
> return(l);
> }
>
> ### minimal Parser:
> # - proof of concept;
> # - may be useful to process non-conformant R "code", e.g.:
> # "{\"abc\" + \"bcd\"} {FUN}"; (still TODO)
> # Warning:
> # - not thoroughly checked &
> # may be a little buggy!
>
> parse.simple = function(x, eol="\n") {
> len = nchar(x);
> n.comm = list(integer(0), integer(0));
> n.str = list(integer(0), integer(0));
> is.hex = function(ch) {
> # Note: only for 1 character!
> return((ch >= "0" && ch <= "9") ||
> (ch >= "A" && ch <= "F") ||
> (ch >= "a" && ch <= "f"));
> }
> npos = 1;
> while(npos <= len) {
> s = substr(x, npos, npos);
> # State: COMMENT
> if(s == "#") {
> n.comm[[1]] = c(n.comm[[1]], npos);
> while(npos < len) {
> npos = npos + 1;
> if(substr(x, npos, npos) == eol) break;
> }
> n.comm[[2]] = c(n.comm[[2]], npos);
> npos = npos + 1; next;
> }
> # State: STRING
> if(s == "\"" || s == "'") {
> n.str[[1]] = c(n.str[[1]], npos);
> while(npos < len) {
> npos = npos + 1;
> se = substr(x, npos, npos);
> if(se == "\\") {
> npos = npos + 1;
> # simple escape vs Unicode:
> if(substr(x, npos, npos) != "u") next;
> len.end = min(len, npos + 4);
> npos = npos + 1;
> isAllHex = TRUE;
> while(npos <= len.end) {
> se = substr(x, npos, npos);
> if( ! is.hex(se)) { isAllHex = FALSE; break; }
> npos = npos + 1;
> }
> if(isAllHex) next;
> }
> if(se == s) break;
> }
> n.str[[2]] = c(n.str[[2]], npos);
> npos = npos + 1; next;
> }
> npos = npos + 1;
> }
> return(list(str = n.str, comm = n.comm));
> }
>
>
> extract.str = function(s, npos, strip=FALSE) {
> if(length(npos[[1]]) == 0) return(character(0));
> strip.FUN = if(strip) {
> function(id) {
> if(npos[[1]][[id]] + 1 < npos[[2]][[id]]) {
> nStart = npos[[1]][[id]] + 1;
> nEnd = npos[[2]][[id]] - 1; # TODO:
> Error with malformed string
> return(substr(s, nStart, nEnd));
> } else {
> return("");
> }
> }
> } else function(id) substr(s, npos[[1]][[id]], npos[[2]][[id]]);
> sapply(seq(length(npos[[1]])), strip.FUN);
> }
>
On a.) There is an option "keep.source" that controls
this behaviour. When you install a package via
R CMD INSTALL, you can specify the option; see
R CMD INSTALL --help .
There is also the "remindR" package on CRAN which
(I think) does something similar.
--
Enrico Schumann
Lucerne, Switzerland
http://enricoschumann.net
More information about the R-help
mailing list