[R] Extracting Comments from Functions/Packages

Leonard Mada |eo@m@d@ @end|ng |rom @yon|c@eu
Thu Oct 7 18:30:24 CEST 2021


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);
}



More information about the R-help mailing list