wolfram_expr/macros.rs
1/// Build a Wolfram Language [`Expr`][crate::Expr] with WL-like syntax.
2///
3/// # Syntax
4///
5/// | Pattern | Result |
6/// |---------|--------|
7/// | `expr!(System::Head[a, b])` | `Normal` with the symbol `` System`Head `` |
8/// | `expr!(A::B::C[a, b])` | `Normal` with `` A`B`C `` (each `::` → a context backtick) |
9/// | `expr!(var[a, b])` | `Normal` with the variable `var` as head (call over `var`) |
10/// | `expr!(A::B::C)` | the symbol `` A`B`C `` |
11/// | `expr!(::Name)` / `expr!(::Name[…])` | the **context-less** symbol `Name` (works nested in arg, splice, and association-value positions too) |
12/// | `expr!(::$Name)` / `expr!(::$Name[…])` | the context-less `$`-symbol `$Name` (e.g. `` $Context ``, `` $InputFileName ``) |
13/// | `expr!(k -> v)` | `Rule[k, v]` — usable inline inside `…[...]` |
14/// | `expr!({k -> v, ...})` | `Association` — every element must be a `->` Rule; bare values are a compile error |
15/// | `expr!(true)` / `expr!(false)` | `True` / `False` symbols |
16/// | `expr!("str")`, `expr!(42)`, `expr!(3.14)` | string / integer / real |
17/// | `expr!(rust_var)`, `expr!((rust_expr))` | `Expr::from(…)` |
18/// | `expr!(f[..iter])` | splice a sequence of `Into<Expr>` items as args |
19///
20/// # Conventions
21///
22/// - **Symbols are always fully qualified**: a bare ident is *always* a Rust
23/// variable (in head *and* arg position) — there is no implicit `System``
24/// prefix. Write `System::Times[…]`, `Global::x`, etc. for symbols.
25/// - **Arg position**: string literals become WL strings; `k -> v` becomes
26/// `Rule[k, v]` inline; `{k -> v}` an Association; `(rust_expr)` an arbitrary
27/// Rust expression; `..iter` splices a sequence.
28/// - **Nesting**: `Head[a, b]` in arg position recurses to all depths.
29///
30/// # Examples
31///
32/// ```
33/// # use wolfram_expr::{Expr, Symbol, expr};
34/// let msg = "something went wrong";
35///
36/// // Failure["RustPanic", <|"MessageTemplate" -> "something went wrong"|>]
37/// // {…} always produces an Association; non-Rule elements are a compile error.
38/// let e = expr!(System::Failure["RustPanic", {"MessageTemplate" -> msg}]);
39///
40/// // {1, 2, 3}
41/// let list = expr!(System::List[1, 2, 3]);
42///
43/// // Tabular`Arrow`ToTabular[{1, 2, 3}]
44/// // Each `::` becomes a context backtick; `list` is a Rust variable spliced as-is.
45/// let table = expr!(Tabular::Arrow::ToTabular[list]);
46///
47/// let head = Symbol::new("Global`f");
48/// // f[1, 2] — bare ident in head position is a Rust variable, not a symbol.
49/// // To call a symbol by name write expr!(Global::f[1, 2]) instead.
50/// let call = expr!(head[1, 2]);
51/// ```
52///
53/// ## Symbols
54///
55/// ```
56/// # use wolfram_expr::{expr, Expr, Symbol};
57/// // Fully qualified: each `::` becomes a context backtick.
58/// assert_eq!(expr!(System::Plus), Expr::symbol(Symbol::new("System`Plus")));
59/// assert_eq!(expr!(My::Custom::Symbol), Expr::symbol(Symbol::new("My`Custom`Symbol")));
60///
61/// // Context-less `::Name` produces the bare symbol `Name` (no context prefix)
62/// // — useful for symbols WL resolves relative to the current context, like
63/// // `$Context` or a name meant to land in `Global\``.
64/// assert_eq!(expr!(::Name), Expr::symbol(Symbol::new("Name")));
65///
66/// // `::$Name` covers `$`-prefixed system symbols (the `$` is pasted back on).
67/// assert_eq!(expr!(::$Context), Expr::symbol(Symbol::new("$Context")));
68///
69/// // A Rust `Symbol`/`Expr` value used bare (no `::`) is spliced in as-is —
70/// // not reinterpreted as WL syntax.
71/// let sym = Symbol::new("Global`counter");
72/// assert_eq!(expr!(sym.clone()), Expr::symbol(sym));
73/// ```
74///
75/// ## Function application
76///
77/// ```
78/// # use wolfram_expr::{expr, Expr, Symbol};
79/// // Literal qualified head. `List` gets WL surface syntax (`{…}`); most
80/// // other heads render as `head[…]`, keeping their full qualified name.
81/// assert_eq!(expr!(System::List[1, 2, 3]).to_string(), "{1, 2, 3}");
82/// assert_eq!(expr!(System::Point[1, 2]).to_string(), "System`Point[1, 2]");
83///
84/// // Nested heads recurse to any depth.
85/// assert_eq!(
86/// expr!(System::Point[System::List[1, 2]]).to_string(),
87/// "System`Point[{1, 2}]"
88/// );
89///
90/// // A runtime `Symbol` as the head calls that symbol, same as writing it
91/// // out literally — handy when the head name is only known at runtime
92/// // (e.g. built with `format!` or looked up from user input).
93/// let h = Symbol::new("Global`f");
94/// assert_eq!(expr!(h[1, 2]), expr!(Global::f[1, 2]));
95///
96/// // Curried application `f[1, 2][3, 4]`: the head is itself a `Normal`, built
97/// // as an ordinary Rust variable and spliced in as the head.
98/// let inner = expr!(Global::f[1, 2]);
99/// let curried = expr!(inner[3, 4]);
100/// assert_eq!(curried.to_string(), "Global`f[1, 2][3, 4]");
101/// ```
102///
103/// ## Associations
104///
105/// ```
106/// # use wolfram_expr::expr;
107/// // `{k -> v, ...}` always builds an Association; every element must be a
108/// // `->` Rule — a bare value in `{...}` is a compile error (write
109/// // `System::List[..]` for a WL list instead).
110/// assert_eq!(expr!({"a" -> 1, "b" -> 2}).to_string(), r#"<|"a" -> 1, "b" -> 2|>"#);
111///
112/// // Keys and values can be any `expr!` form, including nested function
113/// // calls and symbols — this is the shape `#[derive(ToWXF)]` emits for a
114/// // per-variant `Failure["OutOfRange", <|"Min" -> 0, "Max" -> 255|>]`.
115/// let e = expr!(System::Failure["OutOfRange", {"Min" -> 0, "Max" -> 255}]);
116/// assert_eq!(e.to_string(), r#"System`Failure["OutOfRange", <|"Min" -> 0, "Max" -> 255|>]"#);
117/// ```
118///
119/// ## Injecting Rust values: variables, vectors, iterators
120///
121/// ```
122/// # use wolfram_expr::{expr, Expr};
123/// // A bare Rust variable in arg position converts via `Expr::from`.
124/// let count = 3i64;
125/// assert_eq!(expr!(System::List[count]), expr!(System::List[3]));
126///
127/// // Parenthesize any other Rust expression — method calls, arithmetic, …
128/// let n = 5i64;
129/// assert_eq!(expr!(System::F[(n * 2)]), expr!(System::F[10]));
130///
131/// // `..iter` splices a sequence of `Into<Expr>` items as args: a `Vec<Expr>`,
132/// // a `Vec<T>` for `T: Into<Expr>`, or any iterator — no intermediate `Vec`
133/// // needed, and it can mix with literal args and other splices.
134/// let values: Vec<i64> = vec![1, 2, 3];
135/// assert_eq!(expr!(System::List[..values]), expr!(System::List[1, 2, 3]));
136///
137/// let middle = vec![1i64, 2];
138/// assert_eq!(
139/// expr!(System::f[0, ..middle, 9]),
140/// expr!(System::f[0, 1, 2, 9])
141/// );
142///
143/// // Splice an iterator adaptor directly, with no collect().
144/// let items = vec![1i64, 2, 3];
145/// assert_eq!(
146/// expr!(System::List[..items.into_iter().rev()]),
147/// expr!(System::List[3, 2, 1])
148/// );
149/// ```
150#[macro_export]
151macro_rules! expr {
152 // Booleans (must be before $e:expr to avoid ambiguity)
153 (true) => { $crate::Expr::from($crate::Symbol::new("System`True")) };
154 (false) => { $crate::Expr::from($crate::Symbol::new("System`False")) };
155
156 // Rule: k -> v
157 ($k:tt -> $v:tt) => {
158 $crate::Expr::normal(
159 $crate::Symbol::new("System`Rule"),
160 vec![$crate::expr!($k), $crate::expr!($v)],
161 )
162 };
163
164 // Context-less call: ::Name[args] applies the bare-name symbol `Name` (no
165 // context — the leading `::` means "no context prefix").
166 (:: $name:ident [ $($args:tt)* ]) => {
167 $crate::Expr::normal(
168 $crate::Symbol::new(stringify!($name)),
169 $crate::__expr_args![$($args)*],
170 )
171 };
172
173 // Context-less `$`-symbol call: ::$Name[args]. `$` is its own token (not an
174 // ident char), so it's captured as a `tt` and pasted back onto the name —
175 // letting WL system symbols like `::$Context[…]` be written directly.
176 (:: $d:tt $name:ident [ $($args:tt)* ]) => {
177 $crate::Expr::normal(
178 $crate::Symbol::new(concat!(stringify!($d), stringify!($name))),
179 $crate::__expr_args![$($args)*],
180 )
181 };
182
183 // Context-less symbol value: ::Name -> the bare-name symbol `Name`.
184 (:: $name:ident) => {
185 $crate::Expr::symbol($crate::Symbol::new(stringify!($name)))
186 };
187
188 // Context-less `$`-symbol value: ::$Name -> the bare-name symbol `$Name`.
189 (:: $d:tt $name:ident) => {
190 $crate::Expr::symbol(
191 $crate::Symbol::new(concat!(stringify!($d), stringify!($name)))
192 )
193 };
194
195 // Context-qualified call: A::B::C[args] applies the symbol `A`B`C`.
196 // Each `::` becomes a context backtick and the path is taken verbatim (no
197 // `System`` prefix) — the escape hatch for any non-`System`` context.
198 ($head:ident $(:: $seg:ident)+ [ $($args:tt)* ]) => {
199 $crate::Expr::normal(
200 $crate::Symbol::new(concat!(stringify!($head) $(, "`", stringify!($seg))+)),
201 $crate::__expr_args![$($args)*],
202 )
203 };
204
205 // Function call over a variable: var[args] applies the (`Into<Expr>`) value
206 // `var` to args. A bare ident is *always* a Rust variable — symbols must be
207 // `::`-qualified (`System::Times[…]`); there is no implicit `System`` prefix.
208 // (A non-ident head expression has no inline form — bind it to a variable.)
209 // Args are parsed by the __expr_args! tt-muncher, which handles:
210 // - single-tt args (literals, variables, {..} associations)
211 // - (rust_expr) parenthesized Rust expressions, ..iter splices
212 // - k -> v Rule args (three tokens)
213 ($head:ident [ $($args:tt)* ]) => {
214 $crate::Expr::normal($head, $crate::__expr_args![$($args)*])
215 };
216
217 // Association: {key -> value, ...}
218 // Values are parsed by __expr_assoc! so they can be Head[...] expressions.
219 ({ $($assoc:tt)* }) => {
220 $crate::Expr::new($crate::ExprKind::Association(
221 $crate::__expr_assoc![$($assoc)*]
222 ))
223 };
224
225 // Context-qualified symbol value: A::B::C -> the bare symbol `A`B`C`.
226 ($head:ident $(:: $seg:ident)+) => {
227 $crate::Expr::symbol($crate::Symbol::new(
228 concat!(stringify!($head) $(, "`", stringify!($seg))+)
229 ))
230 };
231
232 // Fallthrough: numbers, string literals, Rust variables, Vec<Expr>, etc.
233 // A bare ident (no `::`) is a Rust *variable*; a `::`-path is a symbol (above).
234 ($e:expr) => { $crate::Expr::from($e) };
235}
236
237/// Internal tt-muncher that parses a comma-separated arg list where each arg
238/// is a single token tree, a `Head[...]` function call, or a `k -> v` Rule.
239#[doc(hidden)]
240#[macro_export]
241macro_rules! __expr_args {
242 // Base: empty
243 () => { vec![] };
244 // Trailing comma only
245 (,) => { vec![] };
246 // ::Name[...] context-less call arg, followed by more
247 (:: $name:ident [ $($inner:tt)* ], $($rest:tt)*) => {{
248 let mut __args = vec![$crate::expr!(:: $name [ $($inner)* ])];
249 __args.extend($crate::__expr_args![$($rest)*]);
250 __args
251 }};
252 // ::Name[...] context-less call arg, last
253 (:: $name:ident [ $($inner:tt)* ]) => {
254 vec![$crate::expr!(:: $name [ $($inner)* ])]
255 };
256 // ::Name context-less symbol arg, followed by more
257 (:: $name:ident, $($rest:tt)*) => {{
258 let mut __args = vec![$crate::expr!(:: $name)];
259 __args.extend($crate::__expr_args![$($rest)*]);
260 __args
261 }};
262 // ::Name context-less symbol arg, last
263 (:: $name:ident) => {
264 vec![$crate::expr!(:: $name)]
265 };
266 // ::$Name[...] context-less `$`-symbol call arg, followed by more
267 (:: $d:tt $name:ident [ $($inner:tt)* ], $($rest:tt)*) => {{
268 let mut __args = vec![$crate::expr!(:: $d $name [ $($inner)* ])];
269 __args.extend($crate::__expr_args![$($rest)*]);
270 __args
271 }};
272 // ::$Name[...] context-less `$`-symbol call arg, last
273 (:: $d:tt $name:ident [ $($inner:tt)* ]) => {
274 vec![$crate::expr!(:: $d $name [ $($inner)* ])]
275 };
276 // ::$Name context-less `$`-symbol arg, followed by more
277 (:: $d:tt $name:ident, $($rest:tt)*) => {{
278 let mut __args = vec![$crate::expr!(:: $d $name)];
279 __args.extend($crate::__expr_args![$($rest)*]);
280 __args
281 }};
282 // ::$Name context-less `$`-symbol arg, last
283 (:: $d:tt $name:ident) => {
284 vec![$crate::expr!(:: $d $name)]
285 };
286 // Rule arg: k -> v, rest...
287 ($k:tt -> $v:tt, $($rest:tt)*) => {{
288 let mut __args = vec![$crate::expr!($k -> $v)];
289 __args.extend($crate::__expr_args![$($rest)*]);
290 __args
291 }};
292 // Rule arg, last: k -> v
293 ($k:tt -> $v:tt) => { vec![$crate::expr!($k -> $v)] };
294 // A::B::C[...] context-qualified call arg, followed by more
295 ($head:ident $(:: $seg:ident)+ [ $($inner:tt)* ], $($rest:tt)*) => {{
296 let mut __args = vec![$crate::expr!($head $(:: $seg)+ [ $($inner)* ])];
297 __args.extend($crate::__expr_args![$($rest)*]);
298 __args
299 }};
300 // A::B::C[...] context-qualified call arg, last
301 ($head:ident $(:: $seg:ident)+ [ $($inner:tt)* ]) => {
302 vec![$crate::expr!($head $(:: $seg)+ [ $($inner)* ])]
303 };
304 // A::B::C symbol arg, followed by more
305 ($head:ident $(:: $seg:ident)+, $($rest:tt)*) => {{
306 let mut __args = vec![$crate::expr!($head $(:: $seg)+)];
307 __args.extend($crate::__expr_args![$($rest)*]);
308 __args
309 }};
310 // A::B::C symbol arg, last
311 ($head:ident $(:: $seg:ident)+) => {
312 vec![$crate::expr!($head $(:: $seg)+)]
313 };
314 // ..iter splice arg (each item `Into<Expr>`), followed by more
315 ( .. $e:expr, $($rest:tt)* ) => {{
316 let mut __args: ::std::vec::Vec<$crate::Expr> =
317 ::core::iter::IntoIterator::into_iter($e)
318 .map(|__x| $crate::Expr::from(__x))
319 .collect();
320 __args.extend($crate::__expr_args![$($rest)*]);
321 __args
322 }};
323 // ..iter splice arg, last
324 ( .. $e:expr ) => {
325 ::core::iter::IntoIterator::into_iter($e)
326 .map(|__x| $crate::Expr::from(__x))
327 .collect::<::std::vec::Vec<$crate::Expr>>()
328 };
329 // Head[...] arg, followed by more args
330 ($head:ident [ $($inner:tt)* ], $($rest:tt)*) => {{
331 let mut __args = vec![$crate::expr!($head [ $($inner)* ])];
332 __args.extend($crate::__expr_args![$($rest)*]);
333 __args
334 }};
335 // Head[...] arg, last
336 ($head:ident [ $($inner:tt)* ]) => {
337 vec![$crate::expr!($head [ $($inner)* ])]
338 };
339 // Single-tt arg followed by more args
340 ($arg:tt, $($rest:tt)*) => {{
341 let mut __args = vec![$crate::expr!($arg)];
342 __args.extend($crate::__expr_args![$($rest)*]);
343 __args
344 }};
345 // Single-tt arg, last
346 ($arg:tt) => { vec![$crate::expr!($arg)] };
347}
348
349/// Internal tt-muncher that parses Association entries `k -> v, ...` where
350/// values can be single token trees or `Head[...]` expressions.
351#[doc(hidden)]
352#[macro_export]
353macro_rules! __expr_assoc {
354 () => { vec![] };
355 (,) => { vec![] };
356 // k -> ::Name[...] context-less call value, rest
357 ($k:tt -> :: $vname:ident [ $($vinner:tt)* ], $($rest:tt)*) => {{
358 let mut __v = vec![$crate::RuleEntry::rule(
359 $crate::expr!($k),
360 $crate::expr!(:: $vname [ $($vinner)* ]),
361 )];
362 __v.extend($crate::__expr_assoc![$($rest)*]);
363 __v
364 }};
365 // k -> ::Name[...] context-less call value, last
366 ($k:tt -> :: $vname:ident [ $($vinner:tt)* ]) => {
367 vec![$crate::RuleEntry::rule(
368 $crate::expr!($k),
369 $crate::expr!(:: $vname [ $($vinner)* ]),
370 )]
371 };
372 // k -> ::Name context-less symbol value, rest
373 ($k:tt -> :: $vname:ident, $($rest:tt)*) => {{
374 let mut __v = vec![$crate::RuleEntry::rule(
375 $crate::expr!($k),
376 $crate::expr!(:: $vname),
377 )];
378 __v.extend($crate::__expr_assoc![$($rest)*]);
379 __v
380 }};
381 // k -> ::Name context-less symbol value, last
382 ($k:tt -> :: $vname:ident) => {
383 vec![$crate::RuleEntry::rule($crate::expr!($k), $crate::expr!(:: $vname))]
384 };
385 // k -> A::B::C[...] qualified-head value, rest
386 ($k:tt -> $vh:ident $(:: $vseg:ident)+ [ $($vinner:tt)* ], $($rest:tt)*) => {{
387 let mut __v = vec![$crate::RuleEntry::rule(
388 $crate::expr!($k),
389 $crate::expr!($vh $(:: $vseg)+ [ $($vinner)* ]),
390 )];
391 __v.extend($crate::__expr_assoc![$($rest)*]);
392 __v
393 }};
394 // k -> A::B::C[...] qualified-head value, last
395 ($k:tt -> $vh:ident $(:: $vseg:ident)+ [ $($vinner:tt)* ]) => {
396 vec![$crate::RuleEntry::rule(
397 $crate::expr!($k),
398 $crate::expr!($vh $(:: $vseg)+ [ $($vinner)* ]),
399 )]
400 };
401 // k -> A::B::C qualified-symbol value, rest
402 ($k:tt -> $vh:ident $(:: $vseg:ident)+, $($rest:tt)*) => {{
403 let mut __v = vec![$crate::RuleEntry::rule(
404 $crate::expr!($k),
405 $crate::expr!($vh $(:: $vseg)+),
406 )];
407 __v.extend($crate::__expr_assoc![$($rest)*]);
408 __v
409 }};
410 // k -> A::B::C qualified-symbol value, last
411 ($k:tt -> $vh:ident $(:: $vseg:ident)+) => {
412 vec![$crate::RuleEntry::rule($crate::expr!($k), $crate::expr!($vh $(:: $vseg)+))]
413 };
414 // k -> Head[...], rest
415 ($k:tt -> $vhead:ident [ $($vinner:tt)* ], $($rest:tt)*) => {{
416 let mut __v = vec![$crate::RuleEntry::rule(
417 $crate::expr!($k),
418 $crate::expr!($vhead [ $($vinner)* ]),
419 )];
420 __v.extend($crate::__expr_assoc![$($rest)*]);
421 __v
422 }};
423 // k -> Head[...], last
424 ($k:tt -> $vhead:ident [ $($vinner:tt)* ]) => {
425 vec![$crate::RuleEntry::rule(
426 $crate::expr!($k),
427 $crate::expr!($vhead [ $($vinner)* ]),
428 )]
429 };
430 // k -> v, rest (single-tt value)
431 ($k:tt -> $v:tt, $($rest:tt)*) => {{
432 let mut __v = vec![$crate::RuleEntry::rule($crate::expr!($k), $crate::expr!($v))];
433 __v.extend($crate::__expr_assoc![$($rest)*]);
434 __v
435 }};
436 // k -> v, last
437 ($k:tt -> $v:tt) => {
438 vec![$crate::RuleEntry::rule($crate::expr!($k), $crate::expr!($v))]
439 };
440}