pub struct Expr { /* private fields */ }Expand description
Wolfram Language expression.
§Example
Construct the expression {1, 2, 3}:
use wolfram_expr::{Expr, Symbol};
let expr = Expr::normal(Symbol::new("System`List"), vec![
Expr::from(1),
Expr::from(2),
Expr::from(3)
]);§Reference counting
Internally, Expr is an atomically reference-counted ExprKind. This makes cloning
an expression computationally inexpensive.
Implementations§
Source§impl Expr
impl Expr
Sourcepub fn try_as_normal(&self) -> Option<&Normal>
pub fn try_as_normal(&self) -> Option<&Normal>
If this is a Normal expression, return that. Otherwise return None.
Examples found in repository?
176fn expr_string_join(link: &mut Link) {
177 let expr = link.get_expr().unwrap();
178
179 let list = expr.try_as_normal().unwrap();
180 assert!(list.has_head(&Symbol::new("System`List")));
181
182 let mut buffer = String::new();
183 for elem in list.elements() {
184 match elem.kind() {
185 ExprKind::String(str) => buffer.push_str(str),
186 _ => panic!("expected String argument, got: {:?}", elem),
187 }
188 }
189
190 link.put_str(buffer.as_str()).unwrap()
191}Sourcepub fn try_as_bool(&self) -> Option<bool>
pub fn try_as_bool(&self) -> Option<bool>
Sourcepub fn try_as_str(&self) -> Option<&str>
pub fn try_as_str(&self) -> Option<&str>
If this is a ExprKind::String expression, return that. Otherwise return None.
Sourcepub fn try_as_symbol(&self) -> Option<&Symbol>
pub fn try_as_symbol(&self) -> Option<&Symbol>
If this is a Symbol expression, return that. Otherwise return None.
Sourcepub fn try_as_number(&self) -> Option<Number>
pub fn try_as_number(&self) -> Option<Number>
If this is a Number expression, return that. Otherwise return None.
Examples found in repository?
202fn total(args: Vec<Expr>) -> Expr {
203 let mut total = Number::Integer(0);
204
205 for (index, arg) in args.into_iter().enumerate() {
206 let number = match arg.try_as_number() {
207 Some(number) => number,
208 None => panic!(
209 "expected argument at position {} to be a number, got {}",
210 // Add +1 to display using WL 1-based indexing.
211 index + 1,
212 arg
213 ),
214 };
215
216 use Number::{Integer, Real};
217
218 total = match (total, number) {
219 // If the sum and new term are integers, use integers.
220 (Integer(total), Integer(term)) => Integer(total + term),
221 // Otherwise, if the either the total or new term are machine real numbers,
222 // use floating point numbers.
223 (Integer(int), Real(real)) | (Real(real), Integer(int)) => {
224 Number::real(int as f64 + *real)
225 },
226 (Real(total), Real(term)) => Real(total + term),
227 }
228 }
229
230 Expr::number(total)
231}pub fn try_normal(&self) -> Option<&Normal>
pub fn try_symbol(&self) -> Option<&Symbol>
pub fn try_number(&self) -> Option<Number>
Source§impl Expr
impl Expr
Sourcepub fn to_kind(self) -> ExprKind
pub fn to_kind(self) -> ExprKind
Consume self and return an owned ExprKind.
If the reference count of self is equal to 1 this function will not perform
a clone of the stored ExprKind, making this operation very cheap in that case.
Sourcepub fn kind(&self) -> &ExprKind
pub fn kind(&self) -> &ExprKind
Get the ExprKind representing this expression.
Examples found in repository?
176fn expr_string_join(link: &mut Link) {
177 let expr = link.get_expr().unwrap();
178
179 let list = expr.try_as_normal().unwrap();
180 assert!(list.has_head(&Symbol::new("System`List")));
181
182 let mut buffer = String::new();
183 for elem in list.elements() {
184 match elem.kind() {
185 ExprKind::String(str) => buffer.push_str(str),
186 _ => panic!("expected String argument, got: {:?}", elem),
187 }
188 }
189
190 link.put_str(buffer.as_str()).unwrap()
191}More examples
51fn set_instance_value(args: Vec<Expr>) {
52 assert!(args.len() == 2, "set_instance_value: expected 2 arguments");
53
54 let id: u32 = unwrap_id_arg(&args[0]);
55 let value: String = match args[1].kind() {
56 ExprKind::String(str) => str.clone(),
57 _ => panic!("expected 2nd argument to be a String, got: {}", args[1]),
58 };
59
60 let mut instances = INSTANCES.lock().unwrap();
61
62 let instance: &mut MyObject =
63 instances.get_mut(&id).expect("instance does not exist");
64
65 instance.value = value;
66}
67
68/// Get the fields of the `MyObject` instance for the specified instance ID.
69#[wll::export(wstp)]
70fn get_instance_data(args: Vec<Expr>) -> Expr {
71 assert!(args.len() == 1, "get_instance_data: expected 1 argument");
72
73 let id: u32 = unwrap_id_arg(&args[0]);
74
75 let MyObject { value } = {
76 let instances = INSTANCES.lock().unwrap();
77
78 instances
79 .get(&id)
80 .cloned()
81 .expect("instance does not exist")
82 };
83
84 Expr::normal(Symbol::new("System`Association"), vec![Expr::normal(
85 Symbol::new("System`Rule"),
86 vec![Expr::string("Value"), Expr::string(value)],
87 )])
88}
89
90fn unwrap_id_arg(arg: &Expr) -> u32 {
91 match arg.kind() {
92 ExprKind::Integer(int) => u32::try_from(*int).expect("id overflows u32"),
93 _ => panic!("expected Integer instance ID argument, got: {}", arg),
94 }
95}Sourcepub fn normal<H>(head: H, contents: Vec<Expr>) -> Expr
pub fn normal<H>(head: H, contents: Vec<Expr>) -> Expr
Construct a new normal expression from the head and elements.
Examples found in repository?
More examples
9fn test_runtime_function_from_main_thread() -> bool {
10 let expr = Expr::normal(Symbol::new("System`Plus"), vec![
11 Expr::from(2),
12 Expr::from(2),
13 ]);
14
15 wll::evaluate(&expr) == Expr::from(4)
16}
17
18#[wll::export]
19fn test_runtime_function_from_non_main_thread() -> String {
20 let child = std::thread::spawn(|| {
21 panic::set_hook(Box::new(|_| {
22 // Do nothing, just to avoid printing panic message to stderr.
23 }));
24
25 let result = panic::catch_unwind(|| {
26 wll::evaluate(&Expr::normal(Symbol::new("System`Plus"), vec![
27 Expr::from(2),
28 Expr::from(2),
29 ]))
30 });
31
32 // Restore the previous (default) hook.
33 let _ = panic::take_hook();
34
35 result
36 });
37
38 let result = child.join().unwrap();
39
40 match result {
41 Ok(_) => "didn't panic".to_owned(),
42 // We expect the thread to panic
43 Err(panic) => {
44 if let Some(str) = panic.downcast_ref::<&str>() {
45 format!("PANIC: {}", str)
46 } else if let Some(string) = panic.downcast_ref::<String>() {
47 format!("PANIC: {}", string)
48 } else {
49 "PANIC".to_owned()
50 }
51 },
52 }
53}7fn generate_message(_: Vec<Expr>) {
8 // Construct the expression `Message[MySymbol::msg, "..."]`.
9 let message = Expr::normal(Symbol::new("System`Message"), vec![
10 // MySymbol::msg is MessageName[MySymbol, "msg"]
11 Expr::normal(Symbol::new("System`MessageName"), vec![
12 Expr::from(Symbol::new("Global`MySymbol")),
13 Expr::string("msg"),
14 ]),
15 Expr::string("a Rust LibraryLink function"),
16 ]);
17
18 // Evaluate the message expression.
19 let _: Expr = wll::evaluate(&message);
20}70fn get_instance_data(args: Vec<Expr>) -> Expr {
71 assert!(args.len() == 1, "get_instance_data: expected 1 argument");
72
73 let id: u32 = unwrap_id_arg(&args[0]);
74
75 let MyObject { value } = {
76 let instances = INSTANCES.lock().unwrap();
77
78 instances
79 .get(&id)
80 .cloned()
81 .expect("instance does not exist")
82 };
83
84 Expr::normal(Symbol::new("System`Association"), vec![Expr::normal(
85 Symbol::new("System`Rule"),
86 vec![Expr::string("Value"), Expr::string(value)],
87 )])
88}81pub extern "C" fn demo_wstp_function_callback(
82 lib: WolframLibraryData,
83 mut link: WSLINK,
84) -> c_uint {
85 // Create a safe Link wrapper around the raw `WSLINK`. This is a borrowed rather than
86 // owned Link because the caller (the Kernel) owns the link.
87 let link: &mut Link = unsafe { Link::unchecked_ref_cast_mut(&mut link) };
88
89 // Skip reading the argument list packet.
90 if link.raw_get_next().and_then(|_| link.new_packet()).is_err() {
91 return LIBRARY_FUNCTION_ERROR;
92 }
93
94 let callback_link = unsafe { (*lib).getWSLINK.unwrap()(lib) };
95 let mut callback_link = callback_link as wstp::sys::WSLINK;
96
97 {
98 let safe_callback_link =
99 unsafe { Link::unchecked_ref_cast_mut(&mut callback_link) };
100
101 safe_callback_link
102 // EvaluatePacket[Print["Hello, World! --- WSTP"]]
103 .put_expr(&Expr::normal(Symbol::new("System`EvaluatePacket"), vec![
104 Expr::normal(Symbol::new("System`Print"), vec![Expr::string(
105 "Hello, World! --- WSTP",
106 )]),
107 ]))
108 .unwrap();
109
110 unsafe {
111 (*lib).processWSLINK.unwrap()(
112 safe_callback_link.raw_link() as wll_sys::WSLINK
113 );
114 }
115
116 // Skip the return value packet. This is necessary, otherwise the link has
117 // unread data and the return value of this function cannot be processed properly.
118 if safe_callback_link
119 .raw_get_next()
120 .and_then(|_| safe_callback_link.new_packet())
121 .is_err()
122 {
123 return LIBRARY_FUNCTION_ERROR;
124 }
125 }
126
127 link.put_expr(&Expr::string("returned normally")).unwrap();
128
129 return LIBRARY_NO_ERROR;
130}
131
132/// This example makes use of the [`wstp`][wstp] crate to provide a safe wrapper around
133/// around the WSTP link object, which can be used to read the argument expression and
134/// write out the return expression.
135///
136/// ```wolfram
137/// function = LibraryFunctionLoad[
138/// "raw_wstp_function",
139/// "wstp_expr_function",
140/// LinkObject,
141/// LinkObject
142/// ];
143/// ```
144#[no_mangle]
145pub extern "C" fn wstp_expr_function(
146 _lib: WolframLibraryData,
147 mut unsafe_link: WSLINK,
148) -> c_uint {
149 let link: &mut Link = unsafe { Link::unchecked_ref_cast_mut(&mut unsafe_link) };
150
151 let expr = match link.get_expr() {
152 Ok(expr) => expr,
153 Err(err) => {
154 // Skip reading the argument list packet.
155 if link.raw_get_next().and_then(|_| link.new_packet()).is_err() {
156 return LIBRARY_FUNCTION_ERROR;
157 }
158
159 let err = Expr::string(err.to_string());
160 let err = Expr::normal(Symbol::new("System`Failure"), vec![
161 Expr::string("WSTP Error"),
162 Expr::normal(Symbol::new("System`Association"), vec![Expr::normal(
163 Symbol::new("System`Rule"),
164 vec![Expr::string("Message"), err],
165 )]),
166 ]);
167 match link.put_expr(&err) {
168 Ok(()) => return LIBRARY_NO_ERROR,
169 Err(_) => return LIBRARY_FUNCTION_ERROR,
170 }
171 },
172 };
173
174 let expr_string = format!("Input: {}", expr.to_string());
175
176 match link.put_expr(&Expr::string(expr_string)) {
177 Ok(()) => LIBRARY_NO_ERROR,
178 Err(_) => LIBRARY_FUNCTION_ERROR,
179 }
180}Sourcepub fn number(num: Number) -> Expr
pub fn number(num: Number) -> Expr
Construct a new expression from a Number.
Examples found in repository?
202fn total(args: Vec<Expr>) -> Expr {
203 let mut total = Number::Integer(0);
204
205 for (index, arg) in args.into_iter().enumerate() {
206 let number = match arg.try_as_number() {
207 Some(number) => number,
208 None => panic!(
209 "expected argument at position {} to be a number, got {}",
210 // Add +1 to display using WL 1-based indexing.
211 index + 1,
212 arg
213 ),
214 };
215
216 use Number::{Integer, Real};
217
218 total = match (total, number) {
219 // If the sum and new term are integers, use integers.
220 (Integer(total), Integer(term)) => Integer(total + term),
221 // Otherwise, if the either the total or new term are machine real numbers,
222 // use floating point numbers.
223 (Integer(int), Real(real)) | (Real(real), Integer(int)) => {
224 Number::real(int as f64 + *real)
225 },
226 (Real(total), Real(term)) => Real(total + term),
227 }
228 }
229
230 Expr::number(total)
231}Sourcepub fn string<S>(s: S) -> Expr
pub fn string<S>(s: S) -> Expr
Construct a new expression from a String.
Examples found in repository?
More examples
7fn generate_message(_: Vec<Expr>) {
8 // Construct the expression `Message[MySymbol::msg, "..."]`.
9 let message = Expr::normal(Symbol::new("System`Message"), vec![
10 // MySymbol::msg is MessageName[MySymbol, "msg"]
11 Expr::normal(Symbol::new("System`MessageName"), vec![
12 Expr::from(Symbol::new("Global`MySymbol")),
13 Expr::string("msg"),
14 ]),
15 Expr::string("a Rust LibraryLink function"),
16 ]);
17
18 // Evaluate the message expression.
19 let _: Expr = wll::evaluate(&message);
20}70fn get_instance_data(args: Vec<Expr>) -> Expr {
71 assert!(args.len() == 1, "get_instance_data: expected 1 argument");
72
73 let id: u32 = unwrap_id_arg(&args[0]);
74
75 let MyObject { value } = {
76 let instances = INSTANCES.lock().unwrap();
77
78 instances
79 .get(&id)
80 .cloned()
81 .expect("instance does not exist")
82 };
83
84 Expr::normal(Symbol::new("System`Association"), vec![Expr::normal(
85 Symbol::new("System`Rule"),
86 vec![Expr::string("Value"), Expr::string(value)],
87 )])
88}81pub extern "C" fn demo_wstp_function_callback(
82 lib: WolframLibraryData,
83 mut link: WSLINK,
84) -> c_uint {
85 // Create a safe Link wrapper around the raw `WSLINK`. This is a borrowed rather than
86 // owned Link because the caller (the Kernel) owns the link.
87 let link: &mut Link = unsafe { Link::unchecked_ref_cast_mut(&mut link) };
88
89 // Skip reading the argument list packet.
90 if link.raw_get_next().and_then(|_| link.new_packet()).is_err() {
91 return LIBRARY_FUNCTION_ERROR;
92 }
93
94 let callback_link = unsafe { (*lib).getWSLINK.unwrap()(lib) };
95 let mut callback_link = callback_link as wstp::sys::WSLINK;
96
97 {
98 let safe_callback_link =
99 unsafe { Link::unchecked_ref_cast_mut(&mut callback_link) };
100
101 safe_callback_link
102 // EvaluatePacket[Print["Hello, World! --- WSTP"]]
103 .put_expr(&Expr::normal(Symbol::new("System`EvaluatePacket"), vec![
104 Expr::normal(Symbol::new("System`Print"), vec![Expr::string(
105 "Hello, World! --- WSTP",
106 )]),
107 ]))
108 .unwrap();
109
110 unsafe {
111 (*lib).processWSLINK.unwrap()(
112 safe_callback_link.raw_link() as wll_sys::WSLINK
113 );
114 }
115
116 // Skip the return value packet. This is necessary, otherwise the link has
117 // unread data and the return value of this function cannot be processed properly.
118 if safe_callback_link
119 .raw_get_next()
120 .and_then(|_| safe_callback_link.new_packet())
121 .is_err()
122 {
123 return LIBRARY_FUNCTION_ERROR;
124 }
125 }
126
127 link.put_expr(&Expr::string("returned normally")).unwrap();
128
129 return LIBRARY_NO_ERROR;
130}
131
132/// This example makes use of the [`wstp`][wstp] crate to provide a safe wrapper around
133/// around the WSTP link object, which can be used to read the argument expression and
134/// write out the return expression.
135///
136/// ```wolfram
137/// function = LibraryFunctionLoad[
138/// "raw_wstp_function",
139/// "wstp_expr_function",
140/// LinkObject,
141/// LinkObject
142/// ];
143/// ```
144#[no_mangle]
145pub extern "C" fn wstp_expr_function(
146 _lib: WolframLibraryData,
147 mut unsafe_link: WSLINK,
148) -> c_uint {
149 let link: &mut Link = unsafe { Link::unchecked_ref_cast_mut(&mut unsafe_link) };
150
151 let expr = match link.get_expr() {
152 Ok(expr) => expr,
153 Err(err) => {
154 // Skip reading the argument list packet.
155 if link.raw_get_next().and_then(|_| link.new_packet()).is_err() {
156 return LIBRARY_FUNCTION_ERROR;
157 }
158
159 let err = Expr::string(err.to_string());
160 let err = Expr::normal(Symbol::new("System`Failure"), vec![
161 Expr::string("WSTP Error"),
162 Expr::normal(Symbol::new("System`Association"), vec![Expr::normal(
163 Symbol::new("System`Rule"),
164 vec![Expr::string("Message"), err],
165 )]),
166 ]);
167 match link.put_expr(&err) {
168 Ok(()) => return LIBRARY_NO_ERROR,
169 Err(_) => return LIBRARY_FUNCTION_ERROR,
170 }
171 },
172 };
173
174 let expr_string = format!("Input: {}", expr.to_string());
175
176 match link.put_expr(&Expr::string(expr_string)) {
177 Ok(()) => LIBRARY_NO_ERROR,
178 Err(_) => LIBRARY_FUNCTION_ERROR,
179 }
180}Sourcepub fn real(real: f64) -> Expr
pub fn real(real: f64) -> Expr
Construct an expression from a floating-point number.
let expr = Expr::real(3.14159);§Panics
This function will panic if real is NaN.
Sourcepub fn tag(&self) -> Option<Symbol>
pub fn tag(&self) -> Option<Symbol>
Returns the outer-most symbol “tag” used in this expression.
To illustrate:
| Expression | Tag |
|---|---|
5 | None |
"hello" | None |
foo | foo |
f[1, 2, 3] | f |
g[x][y] | g |
Sourcepub fn normal_head(&self) -> Option<Expr>
pub fn normal_head(&self) -> Option<Expr>
If this represents a Normal expression, return its head. Otherwise, return
None.
Sourcepub fn normal_part(&self, index_0: usize) -> Option<&Expr>
pub fn normal_part(&self, index_0: usize) -> Option<&Expr>
Attempt to get the element at index of a Normal expression.
Return None if this is not a Normal expression, or the given index is out of
bounds.
index is 0-based. The 0th index is the first element, not the head.
This function does not panic.
Sourcepub fn has_normal_head(&self, sym: &Symbol) -> bool
pub fn has_normal_head(&self, sym: &Symbol) -> bool
Returns true if self is a Normal expr with the head sym.
Sourcepub fn rule<LHS>(lhs: LHS, rhs: Expr) -> Expr
pub fn rule<LHS>(lhs: LHS, rhs: Expr) -> Expr
Construct a new Rule[_, _] expression from the left-hand side and right-hand
side.
§Example
Construct the expression FontSize -> 16:
use wolfram_expr::{Expr, Symbol};
let option = Expr::rule(Symbol::new("System`FontSize"), Expr::from(16));Sourcepub fn rule_delayed<LHS>(lhs: LHS, rhs: Expr) -> Expr
pub fn rule_delayed<LHS>(lhs: LHS, rhs: Expr) -> Expr
Construct a new RuleDelayed[_, _] expression from the left-hand side and right-hand
side.
§Example
Construct the expression x :> RandomReal[]:
use wolfram_expr::{Expr, Symbol};
let delayed = Expr::rule_delayed(
Symbol::new("Global`x"),
Expr::normal(Symbol::new("System`RandomReal"), vec![])
);Trait Implementations§
Source§impl Display for Expr
By default, this should generate a string which can be unambiguously parsed to
reconstruct the Expr being displayed. This means symbols will always include their
contexts, special characters in String’s will always be properly escaped, and numeric
literals needing precision and accuracy marks will have them.
impl Display for Expr
By default, this should generate a string which can be unambiguously parsed to
reconstruct the Expr being displayed. This means symbols will always include their
contexts, special characters in String’s will always be properly escaped, and numeric
literals needing precision and accuracy marks will have them.