Вы можете использовать codetools::walkCode
для обхода дерева кодов. К сожалению, документация codetools довольно скудна. Вот объяснение и пример, чтобы вы начали.
walkCode
принимает выражение и обходчик кода. Обходчик кода - это созданный вами список, который должен содержать три функции обратного вызова: handler
, call
и leaf
. (Вы можете использовать вспомогательную функцию makeCodeWalker
для обеспечения разумной реализации каждого из них по умолчанию.) walkCode
обходит дерево кода и по ходу делает вызовы в обходчик кода.
call(e, w)
вызывается, когда встречается составное выражение. e
является выражением, а w
является самим обходчиком кода. Реализация по умолчанию просто возвращается в дочерние узлы выражения (for (ee in as.list(e)) if (!missing(ee)) walkCode(ee, w)
).
leaf(e, w)
вызывается, когда встречается листовой узел в дереве. Опять же, e
- это выражение конечного узла, а w
- это обходчик кода. Реализация по умолчанию просто print(e)
.
handler(v, w)
вызывается для каждого составного выражения и может использоваться для простого обеспечения альтернативного поведения call
для определенных типов выражений. v
- символьное строковое представление родителя составного выражения (немного сложно объяснить - но в основном <-
, если это выражение присваивания, {
, если это начало блока, if
, если это оператор if и т. д.). Если обработчик возвращает NULL
, то call
вызывается как обычно; если вместо этого вы возвращаете функцию, то она вызывается вместо функции.
Вот чрезвычайно упрощенный пример, в котором подсчитываются случаи появления if
и ifelse
функции. Надеюсь, это поможет вам начать!
library(codetools)
countBranches <- function(func) {
count <- 0
walkCode(body(func),
makeCodeWalker(
handler=function(v, w) {
if (v == 'if' || v == 'ifelse')
count <<- count + 1
NULL # allow normal recursion
},
leaf=function(e, w) NULL))
count
}