Skip to main content

wstp_example/
wstp.rs

1//! This example demonstrates how WSTP links can be used in LibraryLink functions to pass
2//! arbitrary expressions as the function arguments and return value.
3
4use wolfram_library_link::{
5    self as wll,
6    expr::{Expr, ExprKind, Symbol},
7    wstp::Link,
8};
9
10// Generates a special "loader" function, which returns an Association containing the
11// loaded forms of all functions exported by this library.
12//
13// The loader can be loaded and used by evaluating:
14//
15// ```
16// loadFunctions = LibraryFunctionLoad[
17//     "libwstp_example",
18//     "load_wstp_functions",
19//     LinkObject,
20//     LinkObject
21// ];
22//
23// $functions = loadFunctions["libwstp_example"];
24// ```
25wll::generate_loader!(load_wstp_functions);
26
27//======================================
28// Using `&mut Link`
29//======================================
30
31//------------------
32// square_wstp()
33//------------------
34
35/// Define a WSTP function that squares a number.
36///
37/// ```wolfram
38/// square = $functions["square_wstp"];
39///
40/// square[4]    (* Returns 16 *)
41/// ```
42#[wll::export(wstp)]
43fn square_wstp(link: &mut Link) {
44    // Get the number of elements in the arguments list.
45    let arg_count: usize = link.test_head("System`List").unwrap();
46
47    if arg_count != 1 {
48        panic!("square_wstp: expected to get a single argument");
49    }
50
51    // Get the argument value.
52    let x = link.get_i64().expect("expected Integer argument");
53
54    // Write the return value.
55    link.put_i64(x * x).unwrap();
56}
57
58//------------------
59// count_args()
60//------------------
61
62/// Define a function that returns an integer count of the number of arguments it was
63/// given.
64///
65/// The exported LibraryLink function can be loaded and used by evaluating:
66///
67/// ```wolfram
68/// countArgs = $functions["count_args"];
69///
70/// countArgs[a]          (* Returns 1)
71/// countArgs[a, b, c]    (* Returns 3 *)
72/// ```
73#[wll::export(wstp)]
74fn count_args(link: &mut Link) {
75    // Get the number of elements in the arguments list.
76    let arg_count: usize = link.test_head("System`List").unwrap();
77
78    // Discard the remaining argument data.
79    link.new_packet().unwrap();
80
81    // Write the return value.
82    link.put_i64(i64::try_from(arg_count).unwrap()).unwrap();
83}
84
85//------------------
86// total_args_i64()
87//------------------
88
89/// Define a function that returns the sum of it's integer arguments.
90///
91/// The exported LibraryLink function can be loaded and used by evaluating:
92///
93/// ```wolfram
94/// totalArgsI64 = $functions["total_args_i64"];
95///
96/// totalArgsI64[1, 1, 2, 3, 5]    (* Returns 12 *)
97/// ```
98#[wll::export(wstp)]
99fn total_args_i64(link: &mut Link) {
100    // Check that we recieved a functions arguments list, and get the number of arguments.
101    let arg_count: usize = link.test_head("System`List").unwrap();
102
103    let mut total: i64 = 0;
104
105    // Get each argument, assuming that they are all integers, and add it to the total.
106    for _ in 0..arg_count {
107        let term = link.get_i64().expect("expected Integer argument");
108        total += term;
109    }
110
111    // Write the return value to the link.
112    link.put_i64(total).unwrap();
113}
114
115//------------------
116// string_join()
117//------------------
118
119/// Define a function that will join its string arguments into a single string.
120///
121/// The exported LibraryLink function can be loaded and used by evaluating:
122///
123/// ```wolfram
124/// stringJoin = $functions["string_join"];
125///
126/// stringJoin["Foo", "Bar"]           (* Returns "FooBar" *)
127/// stringJoin["Foo", "Bar", "Baz"]    (* Returns "FooBarBaz" *)
128/// stringJoin[]                       (* Returns "" *)
129/// ```
130#[wll::export(wstp)]
131fn string_join(link: &mut Link) {
132    use wstp::LinkStr;
133
134    let arg_count = link.test_head("System`List").unwrap();
135
136    let mut buffer = String::new();
137
138    for _ in 0..arg_count {
139        let elem: LinkStr<'_> = link.get_string_ref().expect("expected String argument");
140        buffer.push_str(elem.as_str());
141    }
142
143    // Write the joined string value to the link.
144    link.put_str(buffer.as_str()).unwrap();
145}
146
147//------------------
148// link_expr_identity()
149//------------------
150
151/// Define a function that returns the argument expression that was sent over the link.
152/// That expression will be a list of the arguments passed to this LibraryFunction[..].
153///
154/// ```wolfram
155/// linkExprIdentity = $functions["link_expr_identity"];
156///
157/// linkExprIdentity[5]      (* Returns {5} *)
158/// linkExprIdentity[a, b]   (* Returns {a, b} *)
159/// ```
160#[wll::export(wstp)]
161fn link_expr_identity(link: &mut Link) {
162    let expr = link.get_expr().unwrap();
163    assert!(!link.is_ready());
164    link.put_expr(&expr).unwrap();
165}
166
167//------------------
168// expr_string_join()
169//------------------
170
171/// This example is an alternative to the `string_join()` example.
172///
173/// This example shows using the `Expr` and `ExprKind` types to process expressions on
174/// the WSTP link.
175#[wll::export(wstp)]
176fn expr_string_join(link: &mut Link) {
177    let expr = link.get_expr().unwrap();
178
179    let ExprKind::Normal(list) = expr.kind() else {
180        panic!("expected a List, got: {:?}", expr);
181    };
182    assert!(list.has_head(&Symbol::new("System`List")));
183
184    let mut buffer = String::new();
185    for elem in list.elements() {
186        match elem.kind() {
187            ExprKind::String(str) => buffer.push_str(str),
188            _ => panic!("expected String argument, got: {:?}", elem),
189        }
190    }
191
192    link.put_str(buffer.as_str()).unwrap()
193}
194
195//======================================
196// Using `Vec<Expr>` argument list
197//======================================
198
199//------------------
200// total()
201//------------------
202
203#[wll::export(wstp)]
204fn total(args: Vec<Expr>) -> Expr {
205    let mut total = 0.0f64;
206    for (index, arg) in args.into_iter().enumerate() {
207        total += match arg.kind() {
208            ExprKind::Integer(n) => *n as f64,
209            ExprKind::Real(f) => f64::from(*f),
210            _ => panic!(
211                "expected argument at position {} to be a number, got {}",
212                index + 1,
213                arg
214            ),
215        };
216    }
217    Expr::from(total)
218}