r!() { /* proc-macro */ }Expand description
Evaluate R code written as Rust tokens, validated at compile time.
r! takes a single R expression as a token stream, stringify!s it into a
static R source string at build time, and evaluates it via
[miniextendr_api::expression::r_eval_str] (the same protect-safe parse + eval
path as r_str!).
§What you get today
Because the argument is a Rust token tree, the Rust front-end already
rejects unbalanced delimiters (r!(f(1, 2) won’t compile) and
lexically invalid tokens before R ever sees the string — a cheap
compile-time guard over the pure-runtime r_str!. The source is lowered to
a &'static str (stringify!), so there is no format! allocation at the
call site.
This proc-macro additionally validates a conservative subset of known-bad
R syntax constructs (trailing binary operators, consecutive non-unary
binary operators, bare if/while/for without a body, etc.) and emits
a precise compile error pointing at the offending token. Empty (missing)
call arguments — f(, x), matrix(, 2, 2) — are valid R and pass.
§What is deferred
Direct Rf_lang* call-tree lowering (skipping the runtime parser entirely)
is tracked as a follow-up in issue #938 (item 2). Until then r! parses
its static string at first evaluation, exactly like r_str!.
§Non-goals
A complete R grammar validator is not achievable over Rust tokens:
- Single-quoted strings (
'hello') and backtick-quoted names (`foo`) already die at the Rust lexer — nothing to validate. %op%tokenises as%, ident,%and is accepted without analysis.- Anything the validator cannot confidently classify as wrong passes through unvalidated (conservative reject-only-known-bad design).
§Forms
r!(R tokens…)— evaluate inR_GlobalEnv.r!(env: e; R tokens…)— evaluate in the environment SEXPe. The leadingenv: <expr> ;is consumed as Rust, the rest is R source.
Both evaluate to Result<SEXP, String>; the SEXP is unprotected.
§Safety
Expands to an unsafe block; the underlying FFI is #[r_ffi_checked], so
calls from a worker thread are serialized onto the R thread.
§Example
let three = r!(1L + 2L)?;
let rows = r!(getFromNamespace(".theoph_rows", "dataframeflows")())?;
let in_env = r!(env: my_env; x + 1)?;