Takes an R expression (in the sense of rlang::is_expression()) and
translates it into a character string giving the equivalent expression in
another programming language, according to the supplied
rules.
This function is experimental.
Usage
translate(expr, rules, env = parent.frame())Arguments
- expr
Expression or list of expressions to be translated.
- rules
Which rules to follow. You can pass a string from among
"C","C++","Lua", or"R", or a list with translation rules (see Details).- env
Environment for injections in
expr(see expression).
Details
The parameter rules can be a character string naming a "built-in"
ruleset. Otherwise, rules should be a list with the
following elements:
ops: an unnamed list of operator definitions, each of which should be a list with four elements:aritythe number of operandsprecthe precedence of the operator (lower numbers equal higher precedence)assocthe associativity of the operator, either"LTR","RTL", or anything else for no associativitystraglue::glue()format string with{A[1]},{A[2]}, etc., standing in for the first, second, etc. operands.nopara numeric vector with indices of arguments to the operator which should never be enclosed in parentheses. The default and usual value is integer(0), but (for example) it can be 2 for the[operator, as parentheses within the second argument (the content of the brackets) are redundant.
The function
elixir:::opcan help to assemble such lists.parenaglue::glue()format string with{x}standing in for the enclosed expression. Describes how parentheses are expressed in the target language. Example:"({x})"is correct for virtually all programming languages.symbol: a function which takes a symbol and returns a character string, representing the name of that symbol in the target language. This could just be equal to base::as.character, but it can be changed to something else in case you want name mangling, or e.g. some processing to replace.in symbols with some other character (as.are often not allowed as part of symbols in popular languages).literal: a named list in which the name refers to the class of the operand to translate, and the value should be a function of a single argument (the operand) returning a character string.
It may be helpful to inspect elixir:::ruleset to clarify the above
format.
There are some important shortcomings to translate(). Here are some
potential pitfalls:
Named arguments are not supported, because we cannot translate an R function call like
mean(x, na.rm = TRUE)without knowing which parameter ofmeanmatches tona.rm.Division: An R expression like
1/3gets translated into1./3.in C/C++, as numeric literals are coerced to typedouble. So both of these evaluate to 0.333. However, the R expression1L/3Lwill get translated into1/3in C/C++, which evaluates to 0 (as it is integer division).Modulo: R uses "Knuth's modulo", where
a %% bhas the same sign asb. Lua also uses Knuth's modulo, but C/C++ use "truncated modulo", wherea % bhas the same sign asa. (see Wikipedia for details). So when converting a modulo expression from R to C/C++, a call to the functionkmodis generated in the C/C++ expression. This is not a standard library function, so you will have to provide a definition yourself. A workable definition is:double kmod(double x, double y) { double r = fmod(x, y); return r && r < 0 != y < 0 ? r + y : r; }Types: In R, the type of
a %% band ofa %/% bdepends on the type ofaandb(if both are integers, the result is an integer; if at least one is numeric, the result is numeric).Chained assignment does not work in Lua.