macro_rules! expr {
(true) => { ... };
(false) => { ... };
($k:tt -> $v:tt) => { ... };
(:: $name:ident [ $($args:tt)* ]) => { ... };
(:: $d:tt $name:ident [ $($args:tt)* ]) => { ... };
(:: $name:ident) => { ... };
(:: $d:tt $name:ident) => { ... };
($head:ident $(:: $seg:ident)+ [ $($args:tt)* ]) => { ... };
($head:ident [ $($args:tt)* ]) => { ... };
({ $($assoc:tt)* }) => { ... };
($head:ident $(:: $seg:ident)+) => { ... };
($e:expr) => { ... };
}Expand description
Build a Wolfram Language Expr with WL-like syntax.
§Syntax
| Pattern | Result |
|---|---|
expr!(System::Head[a, b]) | Normal with the symbol System`Head |
expr!(A::B::C[a, b]) | Normal with A`B`C (each :: → a context backtick) |
expr!(var[a, b]) | Normal with the variable var as head (call over var) |
expr!(A::B::C) | the symbol A`B`C |
expr!(::Name) / expr!(::Name[…]) | the context-less symbol Name (works nested in arg, splice, and association-value positions too) |
expr!(::$Name) / expr!(::$Name[…]) | the context-less $-symbol $Name (e.g. $Context, $InputFileName) |
expr!(k -> v) | Rule[k, v] — usable inline inside …[...] |
expr!({k -> v, ...}) | Association — every element must be a -> Rule; bare values are a compile error |
expr!(true) / expr!(false) | True / False symbols |
expr!("str"), expr!(42), expr!(3.14) | string / integer / real |
expr!(rust_var), expr!((rust_expr)) | Expr::from(…) |
expr!(f[..iter]) | splice a sequence of Into<Expr> items as args |
§Conventions
- Symbols are always fully qualified: a bare ident is always a Rust
variable (in head and arg position) — there is no implicit
System`` prefix. WriteSystem::Times[…],Global::x`, etc. for symbols. - Arg position: string literals become WL strings;
k -> vbecomesRule[k, v]inline;{k -> v}an Association;(rust_expr)an arbitrary Rust expression;..itersplices a sequence. - Nesting:
Head[a, b]in arg position recurses to all depths.
§Examples
let msg = "something went wrong";
// Failure["RustPanic", <|"MessageTemplate" -> "something went wrong"|>]
// {…} always produces an Association; non-Rule elements are a compile error.
let e = expr!(System::Failure["RustPanic", {"MessageTemplate" -> msg}]);
// {1, 2, 3}
let list = expr!(System::List[1, 2, 3]);
// Tabular`Arrow`ToTabular[{1, 2, 3}]
// Each `::` becomes a context backtick; `list` is a Rust variable spliced as-is.
let table = expr!(Tabular::Arrow::ToTabular[list]);
let head = Symbol::new("Global`f");
// f[1, 2] — bare ident in head position is a Rust variable, not a symbol.
// To call a symbol by name write expr!(Global::f[1, 2]) instead.
let call = expr!(head[1, 2]);§Symbols
// Fully qualified: each `::` becomes a context backtick.
assert_eq!(expr!(System::Plus), Expr::symbol(Symbol::new("System`Plus")));
assert_eq!(expr!(My::Custom::Symbol), Expr::symbol(Symbol::new("My`Custom`Symbol")));
// Context-less `::Name` produces the bare symbol `Name` (no context prefix)
// — useful for symbols WL resolves relative to the current context, like
// `$Context` or a name meant to land in `Global\``.
assert_eq!(expr!(::Name), Expr::symbol(Symbol::new("Name")));
// `::$Name` covers `$`-prefixed system symbols (the `$` is pasted back on).
assert_eq!(expr!(::$Context), Expr::symbol(Symbol::new("$Context")));
// A Rust `Symbol`/`Expr` value used bare (no `::`) is spliced in as-is —
// not reinterpreted as WL syntax.
let sym = Symbol::new("Global`counter");
assert_eq!(expr!(sym.clone()), Expr::symbol(sym));§Function application
// Literal qualified head. `List` gets WL surface syntax (`{…}`); most
// other heads render as `head[…]`, keeping their full qualified name.
assert_eq!(expr!(System::List[1, 2, 3]).to_string(), "{1, 2, 3}");
assert_eq!(expr!(System::Point[1, 2]).to_string(), "System`Point[1, 2]");
// Nested heads recurse to any depth.
assert_eq!(
expr!(System::Point[System::List[1, 2]]).to_string(),
"System`Point[{1, 2}]"
);
// A runtime `Symbol` as the head calls that symbol, same as writing it
// out literally — handy when the head name is only known at runtime
// (e.g. built with `format!` or looked up from user input).
let h = Symbol::new("Global`f");
assert_eq!(expr!(h[1, 2]), expr!(Global::f[1, 2]));
// Curried application `f[1, 2][3, 4]`: the head is itself a `Normal`, built
// as an ordinary Rust variable and spliced in as the head.
let inner = expr!(Global::f[1, 2]);
let curried = expr!(inner[3, 4]);
assert_eq!(curried.to_string(), "Global`f[1, 2][3, 4]");§Associations
// `{k -> v, ...}` always builds an Association; every element must be a
// `->` Rule — a bare value in `{...}` is a compile error (write
// `System::List[..]` for a WL list instead).
assert_eq!(expr!({"a" -> 1, "b" -> 2}).to_string(), r#"<|"a" -> 1, "b" -> 2|>"#);
// Keys and values can be any `expr!` form, including nested function
// calls and symbols — this is the shape `#[derive(ToWXF)]` emits for a
// per-variant `Failure["OutOfRange", <|"Min" -> 0, "Max" -> 255|>]`.
let e = expr!(System::Failure["OutOfRange", {"Min" -> 0, "Max" -> 255}]);
assert_eq!(e.to_string(), r#"System`Failure["OutOfRange", <|"Min" -> 0, "Max" -> 255|>]"#);§Injecting Rust values: variables, vectors, iterators
// A bare Rust variable in arg position converts via `Expr::from`.
let count = 3i64;
assert_eq!(expr!(System::List[count]), expr!(System::List[3]));
// Parenthesize any other Rust expression — method calls, arithmetic, …
let n = 5i64;
assert_eq!(expr!(System::F[(n * 2)]), expr!(System::F[10]));
// `..iter` splices a sequence of `Into<Expr>` items as args: a `Vec<Expr>`,
// a `Vec<T>` for `T: Into<Expr>`, or any iterator — no intermediate `Vec`
// needed, and it can mix with literal args and other splices.
let values: Vec<i64> = vec![1, 2, 3];
assert_eq!(expr!(System::List[..values]), expr!(System::List[1, 2, 3]));
let middle = vec![1i64, 2];
assert_eq!(
expr!(System::f[0, ..middle, 9]),
expr!(System::f[0, 1, 2, 9])
);
// Splice an iterator adaptor directly, with no collect().
let items = vec![1i64, 2, 3];
assert_eq!(
expr!(System::List[..items.into_iter().rev()]),
expr!(System::List[3, 2, 1])
);