Эффективная консолидация исходного кода из нескольких файлов в один файл, включая комментарии (Roxygen)

Вопрос

Как эффективно объединить исходный код из нескольких исходных файлов в один исходный файл, включая комментарии к документации Roxygen и, возможно, также другие комментарии?

Я помню, что в каком-то пакете был метод синтаксического анализа, который учитывал бы комментарии (помещая его в некоторый атрибут "поле"), но я больше не могу его найти.


Справочная информация

В основном по двум причинам мне нравится гибкость объединения исходного кода из заданного количества исходных файлов в один исходный файл:

  1. Чтобы не терять голову, я придерживаюсь парадигмы "одно определение на файл", где каждый исходный файл содержит ровно одно определение (функция, метод S4, эталонный класс S4 и т. Д.). Кроме того, эти исходные файлы могут храниться в различных подкаталогах моего "исходного каталога" и, следовательно, могут даже иметь одно и то же имя файла. Тем не менее, чтобы собрать настоящий пакет R, иногда лучше сгруппировать несколько определений в один исходный файл. В случаях дублирования имен файлов мне даже нужно.
  2. При распараллеливании удобно иметь возможность сгруппировать весь необходимый исходный код в один файл и передать его рабочим процессам, чтобы они могли получить исходный код

Домашнее задание

Вот мое текущее решение; он чувствует себя "хорошо", но

  1. Я чувствую, что могло бы быть лучше, это более эффективно, способы сделать это
  2. Это кажется немного хрупким в отношении обнаружения кода Roxygen

Создание примеров исходных файлов

foo1 <- function(x) {message("I'm foo #1"); return(TRUE)}
roxy.1 <- c(
    "#' Title foo1()",
    "#'", 
    "#' Description foo1().",
    "##' This line is commented out",
    "#'", 
    "#' @param x Some R object that doesn't matter.",
    "#' @return \\code{TRUE}.",
    "#' @references \\url{http://www.something.com/}",
    "#' @author Janko Thyson \\email{john.doe@@something.com}",
    "#' @seealso \\code{\\link{foo2}}",
    "#' @example inst/examples/foo1.R"
)

foo2 <- function(y) {message("I'm foo #2"); return(FALSE)}
roxy.2 <- c(
    "#' Title foo2()",
    "#'", 
    "#' Description foo2().",
    "##' This line is commented out",
    "#'", 
    "#' @param y Some R object that doesn't matter.",
    "#' @return \\code{FALSE}.",
    "#' @references \\url{http://www.something.com/}",
    "#' @author Janko Thyson \\email{john.doe@@something.com}",
    "#' @seealso \\code{\\link{foo1}}",
    "#' @example inst/examples/foo2.R"
)

dir.create("src/functions", recursive=TRUE, showWarnings=FALSE)
dir.create("src/conso", recursive=TRUE, showWarnings=FALSE)

write(roxy.1, file="src/functions/foo1.R")
write(deparse(foo1), file="src/functions/foo1.R", append=TRUE)
write(roxy.2, file="src/functions/foo2.R")
write(deparse(foo2), file="src/functions/foo2.R", append=TRUE)

Функция консолидации

consolidateThis <- function(
    path="src/functions",
    path.conso="src/conso/src_functions.R",
    rgx.roxy="^(#' ?|##' ?)(\\w*|@|$)",
    do.overwrite=TRUE,
    do.roxygen=TRUE,
    ...
) {
    if (!file.exists(path)) {
        stop("Check your 'path' argument")
    }
    files <- list.files(path, full.names=TRUE)
    if (do.overwrite) {
        file.create(path.conso)
    }
    sapply(files, function(ii) {
        this <- readLines(con=ii, warn=FALSE)
        code <- base::parse(text=this)
        if (do.roxygen) {     
            idx.roxy <- grep(rgx.roxy, this)
            if (length(idx.roxy)) {
                if (length(idx.roxy) == 1) {
                    stop("Weird roxygen code (just a one-liner)") 
                }
                bench <- seq(from=idx.roxy[1], max(idx.roxy))
                if (!all(bench %in% idx.roxy)) {
                    stop("Breaks in your roxygen code. Possibly detected comments that aren't roxygen code")
                }
                code.roxy <- this[idx.roxy]
                write(code.roxy, file=path.conso, append=TRUE)
            }
        }
        write(c(deparse(code[[1]]), ""), file=path.conso, append=TRUE)
    })
    return(path.conso)
}

Применение функции

path <- consolidateThis()
> path
[1] "src/conso/src_functions.R"

Итак, теперь есть исходный файл 'src/conso/src_functions.R', содержащий консолидированный код

1 ответ

Решение

Есть ли у вас особая необходимость анализировать (а затем разбирать) источник функций? Если нет, вы можете немного упростить код.

Следующее дает точно такой же результат, как ConsolidateThis(),

ConsolidateThis2 <-
function(path="src/functions",
         path.conso="src/conso/src_functions.R",
         overwrite = TRUE) {
    if(overwrite) cat("", file = path.conso) # Blank out the file's contents

    ## A function to append infile's contents to outfile and add 2 <RET>          
    prettyappend <- function(infile, outfile) {
        file.append(outfile, infile)
        cat("\n\n", file = outfile, append = TRUE)
    }

    ## Append all files in 'path.conso' to file 'path'
    sapply(dir(path, full.names=TRUE), prettyappend, path.conso)
}

ConsolidateThis2()
Другие вопросы по тегам