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:arity
the number of operandsprec
the precedence of the operator (lower numbers equal higher precedence)assoc
the associativity of the operator, either"LTR"
,"RTL"
, or anything else for no associativitystr
aglue::glue()
format string with{A[1]}
,{A[2]}
, etc., standing in for the first, second, etc. operands.nopar
a 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:::op
can help to assemble such lists.paren
aglue::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 ofmean
matches tona.rm
.Division: An R expression like
1/3
gets 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/3L
will get translated into1/3
in C/C++, which evaluates to 0 (as it is integer division).Modulo: R uses "Knuth's modulo", where
a %% b
has the same sign asb
. Lua also uses Knuth's modulo, but C/C++ use "truncated modulo", wherea % b
has the same sign asa
. (see Wikipedia for details). So when converting a modulo expression from R to C/C++, a call to the functionkmod
is 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; }
(R) ora % b
(Lua)Types: In R, the type of
a %% b
and ofa %/% b
depends on the type ofa
andb
(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.