In general we avoid this sort of parsing because it is unstructured. If you're taking inputs from e.g. a Shiny app, then it might be better to split the components of the comparison into three parts: LHS, RHS, and operation.
You can provide different semantics for each part. For instance you might take variable names as strings for the LHS and a value from the environment for the RHS with the .data and .env pronouns:
filter_function <- function(data, var, value, op = `==`) {
data %>% filter(op(.data[[var]], .env$value))
}
mtcars %>% filter_function("cyl", 6)
# Pass a different comparison function
mtcars %>% filter_function("cyl", 6, op = `<`)
You could also allow arbitrary expressions for the variable names with {{:
filter_function <- function(data, var, value, op = `==`) {
data %>% filter(op({{ var }}, .env$value))
}
In this case your function takes expressions that can refer to the columns:
mtcars %>% filter_function(cyl, 6)
Your users can still pass strings by using the .data pronoun:
var <- "cyl"
mtcars %>% filter_function(.data[[var]], 6)
And it is fully flexible, e.g. you can pass an expression that combines multiple columns:
mtcars %>% filter_function(cyl + am, 6)