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)
I see the analogy, but I think it's a bit different from replace --- replace takes a string, finds n matches in it, and replaces each with the replacement; whereas
match_exprstakes a single expression and finds a single pattern (from a set) that matches the entire expression, then returns an entirely new (single) expression. So I'm not sure it's a replace operation per se.The inspiration here is pattern matching from languages like OCaml (see the OCaml example under Tree Patterns here), hence the name. Though since R already has a
match()function, another name might be more appropriate.I am thinking the implementation also might not return an expression necessarily, but rather an arbitrary result.