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}