-
Notifications
You must be signed in to change notification settings - Fork 5
/
wstp.rs
231 lines (194 loc) · 6.45 KB
/
wstp.rs
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
//! This example demonstrates how WSTP links can be used in LibraryLink functions to pass
//! arbitrary expressions as the function arguments and return value.
use wolfram_library_link::{
self as wll,
expr::{Expr, ExprKind, Number, Symbol},
wstp::Link,
};
// Generates a special "loader" function, which returns an Association containing the
// loaded forms of all functions exported by this library.
//
// The loader can be loaded and used by evaluating:
//
// ```
// loadFunctions = LibraryFunctionLoad[
// "libwstp_example",
// "load_wstp_functions",
// LinkObject,
// LinkObject
// ];
//
// $functions = loadFunctions["libwstp_example"];
// ```
wll::generate_loader!(load_wstp_functions);
//======================================
// Using `&mut Link`
//======================================
//------------------
// square_wstp()
//------------------
/// Define a WSTP function that squares a number.
///
/// ```wolfram
/// square = $functions["square_wstp"];
///
/// square[4] (* Returns 16 *)
/// ```
#[wll::export(wstp)]
fn square_wstp(link: &mut Link) {
// Get the number of elements in the arguments list.
let arg_count: usize = link.test_head("System`List").unwrap();
if arg_count != 1 {
panic!("square_wstp: expected to get a single argument");
}
// Get the argument value.
let x = link.get_i64().expect("expected Integer argument");
// Write the return value.
link.put_i64(x * x).unwrap();
}
//------------------
// count_args()
//------------------
/// Define a function that returns an integer count of the number of arguments it was
/// given.
///
/// The exported LibraryLink function can be loaded and used by evaluating:
///
/// ```wolfram
/// countArgs = $functions["count_args"];
///
/// countArgs[a] (* Returns 1)
/// countArgs[a, b, c] (* Returns 3 *)
/// ```
#[wll::export(wstp)]
fn count_args(link: &mut Link) {
// Get the number of elements in the arguments list.
let arg_count: usize = link.test_head("System`List").unwrap();
// Discard the remaining argument data.
link.new_packet().unwrap();
// Write the return value.
link.put_i64(i64::try_from(arg_count).unwrap()).unwrap();
}
//------------------
// total_args_i64()
//------------------
/// Define a function that returns the sum of it's integer arguments.
///
/// The exported LibraryLink function can be loaded and used by evaluating:
///
/// ```wolfram
/// totalArgsI64 = $functions["total_args_i64"];
///
/// totalArgsI64[1, 1, 2, 3, 5] (* Returns 12 *)
/// ```
#[wll::export(wstp)]
fn total_args_i64(link: &mut Link) {
// Check that we recieved a functions arguments list, and get the number of arguments.
let arg_count: usize = link.test_head("System`List").unwrap();
let mut total: i64 = 0;
// Get each argument, assuming that they are all integers, and add it to the total.
for _ in 0..arg_count {
let term = link.get_i64().expect("expected Integer argument");
total += term;
}
// Write the return value to the link.
link.put_i64(total).unwrap();
}
//------------------
// string_join()
//------------------
/// Define a function that will join its string arguments into a single string.
///
/// The exported LibraryLink function can be loaded and used by evaluating:
///
/// ```wolfram
/// stringJoin = $functions["string_join"];
///
/// stringJoin["Foo", "Bar"] (* Returns "FooBar" *)
/// stringJoin["Foo", "Bar", "Baz"] (* Returns "FooBarBaz" *)
/// stringJoin[] (* Returns "" *)
/// ```
#[wll::export(wstp)]
fn string_join(link: &mut Link) {
use wstp::LinkStr;
let arg_count = link.test_head("System`List").unwrap();
let mut buffer = String::new();
for _ in 0..arg_count {
let elem: LinkStr<'_> = link.get_string_ref().expect("expected String argument");
buffer.push_str(elem.as_str());
}
// Write the joined string value to the link.
link.put_str(buffer.as_str()).unwrap();
}
//------------------
// link_expr_identity()
//------------------
/// Define a function that returns the argument expression that was sent over the link.
/// That expression will be a list of the arguments passed to this LibraryFunction[..].
///
/// ```wolfram
/// linkExprIdentity = $functions["link_expr_identity"];
///
/// linkExprIdentity[5] (* Returns {5} *)
/// linkExprIdentity[a, b] (* Returns {a, b} *)
/// ```
#[wll::export(wstp)]
fn link_expr_identity(link: &mut Link) {
let expr = link.get_expr().unwrap();
assert!(!link.is_ready());
link.put_expr(&expr).unwrap();
}
//------------------
// expr_string_join()
//------------------
/// This example is an alternative to the `string_join()` example.
///
/// This example shows using the `Expr` and `ExprKind` types to process expressions on
/// the WSTP link.
#[wll::export(wstp)]
fn expr_string_join(link: &mut Link) {
let expr = link.get_expr().unwrap();
let list = expr.try_as_normal().unwrap();
assert!(list.has_head(&Symbol::new("System`List")));
let mut buffer = String::new();
for elem in list.elements() {
match elem.kind() {
ExprKind::String(str) => buffer.push_str(str),
_ => panic!("expected String argument, got: {:?}", elem),
}
}
link.put_str(buffer.as_str()).unwrap()
}
//======================================
// Using `Vec<Expr>` argument list
//======================================
//------------------
// total()
//------------------
#[wll::export(wstp)]
fn total(args: Vec<Expr>) -> Expr {
let mut total = Number::Integer(0);
for (index, arg) in args.into_iter().enumerate() {
let number = match arg.try_as_number() {
Some(number) => number,
None => panic!(
"expected argument at position {} to be a number, got {}",
// Add +1 to display using WL 1-based indexing.
index + 1,
arg
),
};
use Number::{Integer, Real};
total = match (total, number) {
// If the sum and new term are integers, use integers.
(Integer(total), Integer(term)) => Integer(total + term),
// Otherwise, if the either the total or new term are machine real numbers,
// use floating point numbers.
(Integer(int), Real(real)) | (Real(real), Integer(int)) => {
Number::real(int as f64 + *real)
},
(Real(total), Real(term)) => Real(total + term),
}
}
Expr::number(total)
}