Skip to main content

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}