user51462
user51462

Reputation: 2022

R - Creating a function from a string

I came across the function below in the Parlist section of the Expressions chapter in Advanced R:

make_function <- function(args, body, env = parent.frame()) {
  args <- as.pairlist(args)

  eval(call("function", args, body), env)
}

The function allows the user to construct a function from its component pieces: a list of formal arguments, a body, and an environment. E.g.

add <- make_function(alist(a = 1, b = 2), quote(a + b))
add
function (a = 1, b = 2) 
a + b

class(add)
[1] "function"

I was wondering if the function could be modified so as to allow the user to input a string for the body argument? I have tried parse(text = 'a + b') but this returns an expression rather than a call as in quote(a + b):

class(quote(a + b))
[1] "call"
class(parse(text = 'x^m'))
[1] "expression"

Is there a way to construct a call object from a string?

Upvotes: 2

Views: 398

Answers (1)

Roland
Roland

Reputation: 132676

You should generally avoid parsing arbitrary string input from users. Usually, this can be avoided by appropriate software design.

Anyway, just extract the language from the expression:

make_function(alist(a = 1, b = 2), parse(text = 'a^b')[[1]])

Edit:

Just to show how you can check against a whitelist (no regex involved):

whitelist <- c("+", "*", "-", "/", "^", "**", "%%", "%/%", "sin", "cos", "tan", "abs") #etc.
expr <- parse(text = "cos(x)^sin(x)*abs(x)")
foo <- function(e) if (length(e) > 1) lapply(as.list(e), foo) else return(e)
funs <- unlist(foo(expr[[1]]))
funs <- funs[vapply(funs, function(x) {x <- eval(x); is.function(x) | is.primitive(x)}, FUN.VALUE = TRUE)]
all(vapply(funs, function(x) as.character(x) %in% whitelist, FUN.VALUE = TRUE))

Parsing and evaluating arbitrary code in a public shiny app is a security risk. This check ensures that only a predefined set of functions can be used. If you don't shy away from smaller risks, you could just use a blacklist instead (prohibiting functions like system, system2, shell etc.).

Upvotes: 4

Related Questions