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` |
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/// let e = expr!(System::Failure["RustPanic", {"MessageTemplate" -> msg}]);
36/// let list = expr!(System::List[1, 2, 3]);
37/// let table = expr!(Tabular::Arrow::ToTabular[list]);
38/// let head = Symbol::new("Global`f");
39/// let call = expr!(head[1, 2]); // a variable head — a call over `head`
40/// ```
41#[macro_export]
42macro_rules! expr {
43 // Booleans (must be before $e:expr to avoid ambiguity)
44 (true) => { $crate::Expr::from($crate::Symbol::new("System`True")) };
45 (false) => { $crate::Expr::from($crate::Symbol::new("System`False")) };
46
47 // Rule: k -> v
48 ($k:tt -> $v:tt) => {
49 $crate::Expr::normal(
50 $crate::Symbol::new("System`Rule"),
51 vec![$crate::expr!($k), $crate::expr!($v)],
52 )
53 };
54
55 // Context-less call: ::Name[args] applies the bare-name symbol `Name` (no
56 // context — the leading `::` means "no context prefix").
57 (:: $name:ident [ $($args:tt)* ]) => {
58 $crate::Expr::normal(
59 $crate::Symbol::new(stringify!($name)),
60 $crate::__expr_args![$($args)*],
61 )
62 };
63
64 // Context-less `$`-symbol call: ::$Name[args]. `$` is its own token (not an
65 // ident char), so it's captured as a `tt` and pasted back onto the name —
66 // letting WL system symbols like `::$Context[…]` be written directly.
67 (:: $d:tt $name:ident [ $($args:tt)* ]) => {
68 $crate::Expr::normal(
69 $crate::Symbol::new(concat!(stringify!($d), stringify!($name))),
70 $crate::__expr_args![$($args)*],
71 )
72 };
73
74 // Context-less symbol value: ::Name -> the bare-name symbol `Name`.
75 (:: $name:ident) => {
76 $crate::Expr::symbol($crate::Symbol::new(stringify!($name)))
77 };
78
79 // Context-less `$`-symbol value: ::$Name -> the bare-name symbol `$Name`.
80 (:: $d:tt $name:ident) => {
81 $crate::Expr::symbol(
82 $crate::Symbol::new(concat!(stringify!($d), stringify!($name)))
83 )
84 };
85
86 // Context-qualified call: A::B::C[args] applies the symbol `A`B`C`.
87 // Each `::` becomes a context backtick and the path is taken verbatim (no
88 // `System`` prefix) — the escape hatch for any non-`System`` context.
89 ($head:ident $(:: $seg:ident)+ [ $($args:tt)* ]) => {
90 $crate::Expr::normal(
91 $crate::Symbol::new(concat!(stringify!($head) $(, "`", stringify!($seg))+)),
92 $crate::__expr_args![$($args)*],
93 )
94 };
95
96 // Function call over a variable: var[args] applies the (`Into<Expr>`) value
97 // `var` to args. A bare ident is *always* a Rust variable — symbols must be
98 // `::`-qualified (`System::Times[…]`); there is no implicit `System`` prefix.
99 // (A non-ident head expression has no inline form — bind it to a variable.)
100 // Args are parsed by the __expr_args! tt-muncher, which handles:
101 // - single-tt args (literals, variables, {..} associations)
102 // - (rust_expr) parenthesized Rust expressions, ..iter splices
103 // - k -> v Rule args (three tokens)
104 ($head:ident [ $($args:tt)* ]) => {
105 $crate::Expr::normal($head, $crate::__expr_args![$($args)*])
106 };
107
108 // Association: {key -> value, ...}
109 // Values are parsed by __expr_assoc! so they can be Head[...] expressions.
110 ({ $($assoc:tt)* }) => {
111 $crate::Expr::new($crate::ExprKind::Association(
112 $crate::__expr_assoc![$($assoc)*]
113 ))
114 };
115
116 // Context-qualified symbol value: A::B::C -> the bare symbol `A`B`C`.
117 ($head:ident $(:: $seg:ident)+) => {
118 $crate::Expr::symbol($crate::Symbol::new(
119 concat!(stringify!($head) $(, "`", stringify!($seg))+)
120 ))
121 };
122
123 // Fallthrough: numbers, string literals, Rust variables, Vec<Expr>, etc.
124 // A bare ident (no `::`) is a Rust *variable*; a `::`-path is a symbol (above).
125 ($e:expr) => { $crate::Expr::from($e) };
126}
127
128/// Internal tt-muncher that parses a comma-separated arg list where each arg
129/// is a single token tree, a `Head[...]` function call, or a `k -> v` Rule.
130#[doc(hidden)]
131#[macro_export]
132macro_rules! __expr_args {
133 // Base: empty
134 () => { vec![] };
135 // Trailing comma only
136 (,) => { vec![] };
137 // ::Name[...] context-less call arg, followed by more
138 (:: $name:ident [ $($inner:tt)* ], $($rest:tt)*) => {{
139 let mut __args = vec![$crate::expr!(:: $name [ $($inner)* ])];
140 __args.extend($crate::__expr_args![$($rest)*]);
141 __args
142 }};
143 // ::Name[...] context-less call arg, last
144 (:: $name:ident [ $($inner:tt)* ]) => {
145 vec![$crate::expr!(:: $name [ $($inner)* ])]
146 };
147 // ::Name context-less symbol arg, followed by more
148 (:: $name:ident, $($rest:tt)*) => {{
149 let mut __args = vec![$crate::expr!(:: $name)];
150 __args.extend($crate::__expr_args![$($rest)*]);
151 __args
152 }};
153 // ::Name context-less symbol arg, last
154 (:: $name:ident) => {
155 vec![$crate::expr!(:: $name)]
156 };
157 // ::$Name[...] context-less `$`-symbol call arg, followed by more
158 (:: $d:tt $name:ident [ $($inner:tt)* ], $($rest:tt)*) => {{
159 let mut __args = vec![$crate::expr!(:: $d $name [ $($inner)* ])];
160 __args.extend($crate::__expr_args![$($rest)*]);
161 __args
162 }};
163 // ::$Name[...] context-less `$`-symbol call arg, last
164 (:: $d:tt $name:ident [ $($inner:tt)* ]) => {
165 vec![$crate::expr!(:: $d $name [ $($inner)* ])]
166 };
167 // ::$Name context-less `$`-symbol arg, followed by more
168 (:: $d:tt $name:ident, $($rest:tt)*) => {{
169 let mut __args = vec![$crate::expr!(:: $d $name)];
170 __args.extend($crate::__expr_args![$($rest)*]);
171 __args
172 }};
173 // ::$Name context-less `$`-symbol arg, last
174 (:: $d:tt $name:ident) => {
175 vec![$crate::expr!(:: $d $name)]
176 };
177 // Rule arg: k -> v, rest...
178 ($k:tt -> $v:tt, $($rest:tt)*) => {{
179 let mut __args = vec![$crate::expr!($k -> $v)];
180 __args.extend($crate::__expr_args![$($rest)*]);
181 __args
182 }};
183 // Rule arg, last: k -> v
184 ($k:tt -> $v:tt) => { vec![$crate::expr!($k -> $v)] };
185 // A::B::C[...] context-qualified call arg, followed by more
186 ($head:ident $(:: $seg:ident)+ [ $($inner:tt)* ], $($rest:tt)*) => {{
187 let mut __args = vec![$crate::expr!($head $(:: $seg)+ [ $($inner)* ])];
188 __args.extend($crate::__expr_args![$($rest)*]);
189 __args
190 }};
191 // A::B::C[...] context-qualified call arg, last
192 ($head:ident $(:: $seg:ident)+ [ $($inner:tt)* ]) => {
193 vec![$crate::expr!($head $(:: $seg)+ [ $($inner)* ])]
194 };
195 // A::B::C symbol arg, followed by more
196 ($head:ident $(:: $seg:ident)+, $($rest:tt)*) => {{
197 let mut __args = vec![$crate::expr!($head $(:: $seg)+)];
198 __args.extend($crate::__expr_args![$($rest)*]);
199 __args
200 }};
201 // A::B::C symbol arg, last
202 ($head:ident $(:: $seg:ident)+) => {
203 vec![$crate::expr!($head $(:: $seg)+)]
204 };
205 // ..iter splice arg (each item `Into<Expr>`), followed by more
206 ( .. $e:expr, $($rest:tt)* ) => {{
207 let mut __args: ::std::vec::Vec<$crate::Expr> =
208 ::core::iter::IntoIterator::into_iter($e)
209 .map(|__x| $crate::Expr::from(__x))
210 .collect();
211 __args.extend($crate::__expr_args![$($rest)*]);
212 __args
213 }};
214 // ..iter splice arg, last
215 ( .. $e:expr ) => {
216 ::core::iter::IntoIterator::into_iter($e)
217 .map(|__x| $crate::Expr::from(__x))
218 .collect::<::std::vec::Vec<$crate::Expr>>()
219 };
220 // Head[...] arg, followed by more args
221 ($head:ident [ $($inner:tt)* ], $($rest:tt)*) => {{
222 let mut __args = vec![$crate::expr!($head [ $($inner)* ])];
223 __args.extend($crate::__expr_args![$($rest)*]);
224 __args
225 }};
226 // Head[...] arg, last
227 ($head:ident [ $($inner:tt)* ]) => {
228 vec![$crate::expr!($head [ $($inner)* ])]
229 };
230 // Single-tt arg followed by more args
231 ($arg:tt, $($rest:tt)*) => {{
232 let mut __args = vec![$crate::expr!($arg)];
233 __args.extend($crate::__expr_args![$($rest)*]);
234 __args
235 }};
236 // Single-tt arg, last
237 ($arg:tt) => { vec![$crate::expr!($arg)] };
238}
239
240/// Internal tt-muncher that parses Association entries `k -> v, ...` where
241/// values can be single token trees or `Head[...]` expressions.
242#[doc(hidden)]
243#[macro_export]
244macro_rules! __expr_assoc {
245 () => { vec![] };
246 (,) => { vec![] };
247 // k -> ::Name[...] context-less call value, rest
248 ($k:tt -> :: $vname:ident [ $($vinner:tt)* ], $($rest:tt)*) => {{
249 let mut __v = vec![$crate::RuleEntry::rule(
250 $crate::expr!($k),
251 $crate::expr!(:: $vname [ $($vinner)* ]),
252 )];
253 __v.extend($crate::__expr_assoc![$($rest)*]);
254 __v
255 }};
256 // k -> ::Name[...] context-less call value, last
257 ($k:tt -> :: $vname:ident [ $($vinner:tt)* ]) => {
258 vec![$crate::RuleEntry::rule(
259 $crate::expr!($k),
260 $crate::expr!(:: $vname [ $($vinner)* ]),
261 )]
262 };
263 // k -> ::Name context-less symbol value, rest
264 ($k:tt -> :: $vname:ident, $($rest:tt)*) => {{
265 let mut __v = vec![$crate::RuleEntry::rule(
266 $crate::expr!($k),
267 $crate::expr!(:: $vname),
268 )];
269 __v.extend($crate::__expr_assoc![$($rest)*]);
270 __v
271 }};
272 // k -> ::Name context-less symbol value, last
273 ($k:tt -> :: $vname:ident) => {
274 vec![$crate::RuleEntry::rule($crate::expr!($k), $crate::expr!(:: $vname))]
275 };
276 // k -> A::B::C[...] qualified-head value, rest
277 ($k:tt -> $vh:ident $(:: $vseg:ident)+ [ $($vinner:tt)* ], $($rest:tt)*) => {{
278 let mut __v = vec![$crate::RuleEntry::rule(
279 $crate::expr!($k),
280 $crate::expr!($vh $(:: $vseg)+ [ $($vinner)* ]),
281 )];
282 __v.extend($crate::__expr_assoc![$($rest)*]);
283 __v
284 }};
285 // k -> A::B::C[...] qualified-head value, last
286 ($k:tt -> $vh:ident $(:: $vseg:ident)+ [ $($vinner:tt)* ]) => {
287 vec![$crate::RuleEntry::rule(
288 $crate::expr!($k),
289 $crate::expr!($vh $(:: $vseg)+ [ $($vinner)* ]),
290 )]
291 };
292 // k -> A::B::C qualified-symbol value, rest
293 ($k:tt -> $vh:ident $(:: $vseg:ident)+, $($rest:tt)*) => {{
294 let mut __v = vec![$crate::RuleEntry::rule(
295 $crate::expr!($k),
296 $crate::expr!($vh $(:: $vseg)+),
297 )];
298 __v.extend($crate::__expr_assoc![$($rest)*]);
299 __v
300 }};
301 // k -> A::B::C qualified-symbol value, last
302 ($k:tt -> $vh:ident $(:: $vseg:ident)+) => {
303 vec![$crate::RuleEntry::rule($crate::expr!($k), $crate::expr!($vh $(:: $vseg)+))]
304 };
305 // k -> Head[...], rest
306 ($k:tt -> $vhead:ident [ $($vinner:tt)* ], $($rest:tt)*) => {{
307 let mut __v = vec![$crate::RuleEntry::rule(
308 $crate::expr!($k),
309 $crate::expr!($vhead [ $($vinner)* ]),
310 )];
311 __v.extend($crate::__expr_assoc![$($rest)*]);
312 __v
313 }};
314 // k -> Head[...], last
315 ($k:tt -> $vhead:ident [ $($vinner:tt)* ]) => {
316 vec![$crate::RuleEntry::rule(
317 $crate::expr!($k),
318 $crate::expr!($vhead [ $($vinner)* ]),
319 )]
320 };
321 // k -> v, rest (single-tt value)
322 ($k:tt -> $v:tt, $($rest:tt)*) => {{
323 let mut __v = vec![$crate::RuleEntry::rule($crate::expr!($k), $crate::expr!($v))];
324 __v.extend($crate::__expr_assoc![$($rest)*]);
325 __v
326 }};
327 // k -> v, last
328 ($k:tt -> $v:tt) => {
329 vec![$crate::RuleEntry::rule($crate::expr!($k), $crate::expr!($v))]
330 };
331}