Simple example of pattern matching and rewriting of expressions in R with a bquote()-style syntax.
N.B. doesn't support splicing-style syntax (..()), but shouldn't be too hard to add.
match_exprs = function(expr, ...) {
patterns = list(...)
for (pattern in patterns) {
tryCatch({
match = match_expr(expr, pattern[[2]])
return(eval(bquote(bquote(.(pattern[[3]]))), match))
},
no_match = function(e) NULL
)
}
stop(no_match("No pattern matches ", deparse1(expr)))
}
match_expr = function(expr, pattern) {
if (is_placeholder(pattern)){
return(setNames(list(expr), placeholder_name(pattern)))
} else if (
is.name(expr) &&
is.name(pattern) &&
expr == pattern
) {
return(list())
} else if (
is.atomic(expr) &&
is.atomic(pattern) &&
expr == pattern
) {
return(list())
} else if (
is.call(expr) &&
is.call(pattern) &&
length(expr) == length(pattern)
) {
matches = .mapply(match_expr, list(as.list(expr), as.list(pattern)), NULL)
return(do.call(c, matches))
} else {
stop(no_match("No pattern matches ", deparse1(expr)))
}
}
no_match = function(...) {
errorCondition(paste0(...), class = "no_match")
}
is_placeholder = function(expr) {
is.call(expr) && expr[[1]] == "." && length(expr) == 2 && is.name(expr[[2]])
}
placeholder_name = function(expr) {
deparse1(expr[[2]])
}
match_exprs(quote(f(1, z + 7)),
g(x, y) ~ foo,
f(1, y) ~ bar,
f(.(x), .(y)) ~ .(x) + .(y)
)## 1 + (z + 7)
This is a great idea. Not sure I have completely wrapped my head around it yet. First I thought we were just going to match something, but this function alters the expression pretty much in the same way we replace strings with
str_replaceso we could also call itreplace_exprs.