1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
//! Procedural macros for `#[export]` and `#[init]`.
//!
//! Emitted paths are resolved dynamically at expansion time via
//! `proc-macro-crate`: if the caller's `Cargo.toml` has `wolfram-export` the
//! macro emits `::wolfram_export::*`; if it has `wolfram-library-link` (legacy)
//! it emits `::wolfram_library_link::*`. Both crates expose the same hidden
//! runtime surface so generated code resolves correctly in both cases.
use TokenStream;
use TokenStream as TokenStream2;
use quote;
use ;
//======================================
// #[init]
//======================================
/// Designate an initialization function to run once, when this library is
/// loaded via Wolfram LibraryLink — distinct from [`export`], which wraps a
/// function called on *every* invocation from Wolfram.
///
/// The annotated function must take no arguments and return `()`. `#[init]`
/// can be applied to at most one function in a library, and a library isn't
/// required to define one at all.
///
/// Behind the scenes, the macro generates a `WolframLibrary_initialize()` C
/// symbol — [the well-known entry point][lib-init] the Wolfram Kernel calls
/// automatically when the library is loaded, before any exported function
/// runs.
///
/// # Panics
///
/// Panics inside the `#[init]` function are caught and reported to the Kernel
/// as an error code. If initialization panics, the Kernel will not load any
/// of this library's other exported functions.
///
/// # Example
///
/// ```
/// use wolfram_export::init;
///
/// #[init]
/// fn init_my_library() {
/// println!("library is now initialized");
/// }
/// ```
///
/// [lib-init]: https://reference.wolfram.com/language/LibraryLink/tutorial/LibraryStructure.html#280210622
//======================================
// #[export] — one macro, three wire formats, picked by a keyword argument.
//======================================
/// Export a function as a Wolfram LibraryLink function, in one of four wire
/// formats picked by a keyword argument. All four need
/// `LibraryFunctionLoad` on the Wolfram side and none of them need the
/// `automate-function-loading-boilerplate` feature to *work* — that feature
/// only affects whether `cargo wl build` can discover the load call for you
/// (`#[export(margs)]` needs an `args =`/`ret =` annotation for that
/// discovered call to be correct — see below).
///
/// | Attribute | Wire format | Cargo feature (on `wolfram-export`) |
/// |-------------------|--------------------------|--------------------------------------|
/// | `#[export]` | native `MArgument` ABI | `native` (in the default feature set) |
/// | `#[export(margs)]`| raw `MArgument` ABI | `native` |
/// | `#[export(wstp)]` | WSTP `LinkObject` | `wstp` |
/// | `#[export(wxf)]` | typed WXF `ByteArray` | `wxf` |
///
/// ```toml
/// # Cargo.toml
/// wolfram-export = { version = "0.6", features = ["wstp", "wxf"] } # native is on by default
/// ```
///
/// # `#[export]` — native mode (default)
///
/// Parameters and the return type must implement `FromArg`/`IntoArg`:
/// `bool`, `i64`, `f64`, `String`, `NumericArray`, and references thereof.
/// This is the fastest mode — arguments cross the LibraryLink ABI with no
/// intermediate encoding.
///
/// ```
/// # mod scope {
/// use wolfram_export::export;
///
/// #[export]
/// fn add(a: i64, b: i64) -> i64 { a + b }
///
/// #[export]
/// fn greet(name: String) -> String { format!("Hello, {name}!") }
/// # }
/// ```
///
/// # `#[export(margs)]` — raw native mode
///
/// The function works directly on LibraryLink's C argument type: it receives
/// the raw `&[MArgument]` slice and the `MArgument` return slot, exactly as
/// the kernel passed them. `MArgument` is a union of typed pointers
/// (`integer`, `real`, `tensor`, `sparse`, `numeric`, `utf8string`, …), so
/// this is the escape hatch for anything plain `#[export]` can't express —
/// types with no `FromArg`/`IntoArg` impl, like `SparseArray`, or full
/// control over how each argument is read.
///
/// Reading a union field is `unsafe`: nothing checks that the field you read
/// matches what the kernel actually sent — that's up to the signature the
/// function is loaded with.
///
/// ```
/// # mod scope {
/// use wolfram_export::{export, sys::MArgument};
///
/// #[export(margs, args = (::Real, ::Real), ret = ::Real)]
/// fn raw_add(args: &[MArgument], ret: MArgument) {
/// unsafe { *ret.real = *args[0].real + *args[1].real; }
/// }
/// # }
/// ```
///
/// The `FromArg`/`IntoArg` conversions that plain `#[export]` applies
/// automatically are still there to call yourself, so only the argument that
/// actually needs raw handling has to be done by hand:
///
/// ```rust,ignore
/// use wolfram_export::{export, sys::MArgument};
/// use wolfram_library_link::{FromArg, IntoArg, NumericArray};
///
/// #[export(margs,
/// args = (::List[::LibraryDataType["NumericArray", "Real64"], "Constant"], ::Real),
/// ret = ::LibraryDataType["NumericArray", "Real64"]
/// )]
/// fn scale(args: &[MArgument], ret: MArgument) {
/// let arr = unsafe { <&NumericArray<f64>>::from_arg(&args[0]) };
/// let factor = unsafe { f64::from_arg(&args[1]) };
/// let scaled: Vec<f64> = arr.as_slice().iter().map(|v| v * factor).collect();
/// unsafe { NumericArray::from_slice(&scaled).into_arg(ret) };
/// }
/// ```
///
/// For a type with no `FromArg`/`IntoArg` impl at all — reading the raw
/// `MArgument.sparse` pointer and driving the `MSparseArray_*` C API in `rtl`
/// directly — see `margs_sparse_array_merge` in
/// [wolfram-examples-internal](https://github.com/WolframResearch/wolfram-rust-library/blob/master/wolfram-examples-internal/src/margs.rs).
///
/// The `args = (..)`/`ret = ..` annotation declares the function's
/// `LibraryFunctionLoad` type specs — the same `{Real, Real}, Real` you would
/// write on the Wolfram side, as `expr!` fragments (each is spliced verbatim
/// into a `wolfram_expr::expr!` call, so `wolfram-expr` must be a direct
/// dependency of your crate). It is not required for the function to work —
/// you can always call `LibraryFunctionLoad` yourself with the right types —
/// but it is what lets `cargo wl build` put a correct load call in the
/// generated `Functions.wl`. Without it the generated entry defaults to the
/// same fixed `LinkObject`/`LinkObject` placeholder `#[export(wstp)]` uses,
/// which a raw `MArgument` function does *not* actually accept, and the macro
/// emits a compile-time warning telling you to annotate it.
///
/// # `#[export(wstp)]` — WSTP mode
///
/// The function receives a `Vec<Expr>` (all arguments as a list) and returns
/// an `Expr`, or takes a `&mut Link` for low-level control over the wire.
/// Use this mode when the function's arguments or return value don't fit a
/// fixed native/WXF shape — e.g. variadic arguments, or streaming a result
/// incrementally.
///
/// ```
/// # mod scope {
/// use wolfram_export::export;
/// use wolfram_expr::{expr, Expr, ExprKind};
///
/// // High-level: Vec<Expr> in, Expr out.
/// #[export(wstp)]
/// fn reverse(args: Vec<Expr>) -> Expr {
/// let list = args.into_iter().next().expect("expected 1 arg");
/// if let ExprKind::Normal(n) = list.kind() {
/// let head = n.head().clone();
/// expr!(head[..n.elements().iter().rev().cloned()])
/// } else {
/// list
/// }
/// }
/// # }
/// ```
///
/// # `#[export(wxf)]` — typed WXF mode
///
/// The generated wrapper reads a WXF-encoded `ByteArray` MArgument,
/// deserializes all arguments via `FromWXF`, calls your function, and
/// serializes the return value via `ToWXF` back into a `ByteArray`. Panics
/// are caught and returned as structured `Failure["RustPanic", …]`
/// expressions. Use this mode for structured arguments/return values
/// (structs, enums, `Option`/`Result`) without hand-writing WSTP code.
///
/// ```
/// # mod scope {
/// use wolfram_export::export;
/// use wolfram_expr::{expr, Expr};
/// use wolfram_serialize::{ToWXF, FromWXF};
///
/// // Primitives and Vec<T> work out of the box.
/// #[export(wxf)]
/// fn scale(values: Vec<f64>, factor: f64) -> Vec<f64> {
/// values.into_iter().map(|v| v * factor).collect()
/// }
///
/// // `Expr` implements `ToWXF`/`FromWXF` directly (in `wolfram-expr`), so a
/// // function can take and return untyped Wolfram Language expressions —
/// // useful when the shape isn't known ahead of time, or a derive isn't worth
/// // it. Build the result with the `expr!` macro.
/// #[export(wxf)]
/// fn add_hold(e: Expr) -> Expr {
/// expr!(System::Hold[e])
/// }
///
/// // Structs need #[derive(ToWXF, FromWXF)].
/// #[derive(ToWXF, FromWXF)]
/// struct Point { x: f64, y: f64 }
///
/// #[export(wxf)]
/// fn midpoint(a: Point, b: Point) -> Point {
/// Point { x: (a.x + b.x) / 2.0, y: (a.y + b.y) / 2.0 }
/// }
///
/// // Option<T> and Result<T,E> are supported too.
/// #[export(wxf)]
/// fn safe_div(a: f64, b: f64) -> Option<f64> {
/// if b == 0.0 { None } else { Some(a / b) }
/// }
/// # }
/// ```
///
/// On the Wolfram side a struct maps to an `Association`:
///
/// ```wolfram
/// midpoint[<|"x" -> 0.0, "y" -> 0.0|>, <|"x" -> 2.0, "y" -> 4.0|>]
/// (* Returns <|"x" -> 1.0, "y" -> 2.0|> *)
/// ```