wolfram_expr/lib.rs
1//! Efficient and ergonomic representation of Wolfram expressions in Rust.
2
3#![allow(clippy::let_and_return)]
4#![warn(missing_docs)]
5
6mod array_buf;
7mod association;
8mod bignum;
9mod byte_array;
10mod complex;
11mod conversion;
12mod macros;
13mod numeric_array;
14mod packed_array;
15mod ptr_cmp;
16mod wl;
17mod wxf;
18mod wxf_impls;
19
20pub mod symbol;
21
22#[cfg(test)]
23mod tests;
24
25mod test_readme {
26 //! Ensure that doc tests in the README.md file get run.
27 #![doc(hidden)]
28 #![doc = include_str!("../README.md")]
29}
30
31use std::fmt;
32use std::mem;
33use std::sync::Arc;
34
35#[doc(inline)]
36pub use self::symbol::Symbol;
37
38pub use self::array_buf::{ArrayBuf, ArrayElement, NumericArrayRead};
39pub use self::association::{Association, RuleEntry};
40pub use self::bignum::{BigInteger, BigReal};
41pub use self::byte_array::ByteArray;
42pub use self::complex::{Complex32, Complex64};
43pub use self::numeric_array::NumericArray;
44pub use self::packed_array::PackedArray;
45pub use self::wxf::{ExpressionEnum, HeaderEnum, NumericArrayEnum, PackedArrayEnum};
46
47
48#[cfg(feature = "unstable_parse")]
49pub use self::ptr_cmp::ExprRefCmp;
50
51/// Wolfram Language expression.
52///
53/// # Example
54///
55/// Construct the expression `{1, 2, 3}`:
56///
57/// ```
58/// use wolfram_expr::{Expr, Symbol};
59///
60/// let expr = Expr::normal(Symbol::new("System`List"), vec![
61/// Expr::from(1),
62/// Expr::from(2),
63/// Expr::from(3)
64/// ]);
65/// ```
66///
67/// # Reference counting
68///
69/// Internally, `Expr` is an atomically reference-counted [`ExprKind`]. This makes cloning
70/// an expression computationally inexpensive.
71#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
72pub struct Expr {
73 inner: Arc<ExprKind>,
74}
75
76// Assert that Expr has the same size and alignment as a usize / pointer.
77const _: () = assert!(mem::size_of::<Expr>() == mem::size_of::<usize>());
78const _: () = assert!(mem::size_of::<Expr>() == mem::size_of::<*const ()>());
79const _: () = assert!(mem::align_of::<Expr>() == mem::align_of::<usize>());
80const _: () = assert!(mem::align_of::<Expr>() == mem::align_of::<*const ()>());
81
82impl Expr {
83 /// Construct a new expression from an [`ExprKind`].
84 pub fn new(kind: ExprKind) -> Expr {
85 Expr {
86 inner: Arc::new(kind),
87 }
88 }
89
90 /// Consume `self` and return an owned [`ExprKind`].
91 ///
92 /// If the reference count of `self` is equal to 1 this function will *not* perform
93 /// a clone of the stored `ExprKind`, making this operation very cheap in that case.
94 // Silence the clippy warning about this method. While this method technically doesn't
95 // follow the Rust style convention of using `into` to prefix methods which take
96 // `self` by move, I think using `to` is more appropriate given the expected
97 // performance characteristics of this method. `into` implies that the method is
98 // always returning data already owned by this type, and as such should be a very
99 // cheap operation. This method can make no such guarantee; if the reference count is
100 // 1, then performance is very good, but if the reference count is >1, a deeper clone
101 // must be done.
102 #[allow(clippy::wrong_self_convention)]
103 pub fn to_kind(self) -> ExprKind {
104 match Arc::try_unwrap(self.inner) {
105 Ok(kind) => kind,
106 Err(self_) => (*self_).clone(),
107 }
108 }
109
110 /// Get the [`ExprKind`] representing this expression.
111 pub fn kind(&self) -> &ExprKind {
112 &self.inner
113 }
114
115 /// Get mutable access to the [`ExprKind`] that represents this expression.
116 ///
117 /// If the reference count of the underlying shared pointer is not equal to 1, this
118 /// will clone the [`ExprKind`] to make it unique.
119 pub fn kind_mut(&mut self) -> &mut ExprKind {
120 Arc::make_mut(&mut self.inner)
121 }
122
123 /// Retrieve the reference count of this expression.
124 pub fn ref_count(&self) -> usize {
125 Arc::strong_count(&self.inner)
126 }
127
128 /// Construct a new normal expression from the head and elements.
129 pub fn normal<H: Into<Expr>>(head: H, contents: Vec<Expr>) -> Expr {
130 let head = head.into();
131 // let contents = contents.into();
132 Expr {
133 inner: Arc::new(ExprKind::Normal(Normal { head, contents })),
134 }
135 }
136
137 // TODO: Should Expr's be cached? Especially Symbol exprs? Would certainly save
138 // a lot of allocations.
139 /// Construct a new expression from a [`Symbol`].
140 pub fn symbol<S: Into<Symbol>>(s: S) -> Expr {
141 let s = s.into();
142 Expr {
143 inner: Arc::new(ExprKind::Symbol(s)),
144 }
145 }
146
147 /// Construct a new expression from a [`Number`].
148 ///
149 /// # Migration
150 ///
151 /// ```
152 /// # use wolfram_expr::{Expr, ExprKind, F64};
153 /// // Expr::number(Number::Integer(42))
154 /// let _int = Expr::from(42_i64);
155 ///
156 /// // Expr::number(Number::real(3.14))
157 /// let _real = Expr::from(3.14_f64); // or Expr::real(3.14)
158 ///
159 /// // Expr::number(Number::Real(f)) — when you already have an F64
160 /// let f = F64::new(3.14).unwrap();
161 /// let _real = Expr::new(ExprKind::Real(f));
162 /// ```
163 #[deprecated(since = "0.6.0-alpha.3", note = "use `Expr::from(i64)` or `Expr::from(f64)` instead")]
164 #[allow(deprecated)]
165 pub fn number(num: Number) -> Expr {
166 match num {
167 Number::Integer(i) => Expr::from(i),
168 Number::Real(r) => Expr::new(ExprKind::Real(r)),
169 }
170 }
171
172 /// Construct a new expression from a [`String`].
173 pub fn string<S: Into<String>>(s: S) -> Expr {
174 Expr {
175 inner: Arc::new(ExprKind::String(s.into())),
176 }
177 }
178
179 /// Construct an expression from a floating-point number.
180 ///
181 /// ```
182 /// # use wolfram_expr::Expr;
183 /// let expr = Expr::real(3.14159);
184 /// ```
185 ///
186 /// # Panics
187 ///
188 /// This function will panic if `real` is NaN.
189 pub fn real(real: f64) -> Expr {
190 let r = ordered_float::NotNan::new(real)
191 .unwrap_or_else(|_| panic!("Expr::real: got NaN"));
192 Expr::new(ExprKind::Real(r))
193 }
194
195 /// Returns the outer-most symbol "tag" used in this expression.
196 ///
197 /// To illustrate:
198 ///
199 /// Expression | Tag
200 /// -------------|----
201 /// `5` | `None`
202 /// `"hello"` | `None`
203 /// `foo` | `foo`
204 /// `f[1, 2, 3]` | `f`
205 /// `g[x][y]` | `g`
206 //
207 // TODO: _[x] probably should return None, even though technically
208 // Blank[][x] has the tag Blank.
209 // TODO: The above TODO is probably wrong -- tag() shouldn't have any language
210 // semantics built in to it.
211 pub fn tag(&self) -> Option<Symbol> {
212 match *self.inner {
213 ExprKind::Normal(ref normal) => normal.head.tag(),
214 ExprKind::Symbol(ref sym) => Some(sym.clone()),
215 // Atomic variants (no symbolic head): Integer, Real, String, ByteArray,
216 // Association, NumericArray, PackedArray, BigInteger, BigReal.
217 _ => None,
218 }
219 }
220
221 /// If this represents a [`Normal`] expression, return its head. Otherwise, return
222 /// `None`.
223 pub fn normal_head(&self) -> Option<Expr> {
224 match *self.inner {
225 ExprKind::Normal(ref normal) => Some(normal.head.clone()),
226 _ => None,
227 }
228 }
229
230 /// Attempt to get the element at `index` of a `Normal` expression.
231 ///
232 /// Return `None` if this is not a `Normal` expression, or the given index is out of
233 /// bounds.
234 ///
235 /// `index` is 0-based. The 0th index is the first element, not the head.
236 ///
237 /// This function does not panic.
238 pub fn normal_part(&self, index_0: usize) -> Option<&Expr> {
239 match self.kind() {
240 ExprKind::Normal(ref normal) => normal.contents.get(index_0),
241 _ => None,
242 }
243 }
244
245 /// Returns `true` if `self` is a `Normal` expr with the head `sym`.
246 pub fn has_normal_head(&self, sym: &Symbol) -> bool {
247 match *self.kind() {
248 ExprKind::Normal(ref normal) => normal.has_head(sym),
249 _ => false,
250 }
251 }
252
253 //==================================
254 // Common values
255 //==================================
256
257 /// [`Null`](https://reference.wolfram.com/language/ref/Null.html) <sub>WL</sub>.
258 pub fn null() -> Expr {
259 crate::expr!(System::Null)
260 }
261
262 //==================================
263 // Convenience creation functions
264 //==================================
265
266 /// Construct a new `Rule[_, _]` expression from the left-hand side and right-hand
267 /// side.
268 ///
269 /// # Example
270 ///
271 /// Construct the expression `FontSize -> 16`:
272 ///
273 /// ```
274 /// use wolfram_expr::{Expr, Symbol};
275 ///
276 /// let option = Expr::rule(Symbol::new("System`FontSize"), Expr::from(16));
277 /// ```
278 pub fn rule<LHS: Into<Expr>>(lhs: LHS, rhs: Expr) -> Expr {
279 let lhs = lhs.into();
280 crate::expr!(System::Rule[lhs, rhs])
281 }
282 /// Construct a new `RuleDelayed[_, _]` expression from the left-hand side and right-hand
283 /// side.
284 ///
285 /// # Example
286 ///
287 /// Construct the expression `x :> RandomReal[]`:
288 ///
289 /// ```
290 /// use wolfram_expr::{Expr, Symbol};
291 ///
292 /// let delayed = Expr::rule_delayed(
293 /// Symbol::new("Global`x"),
294 /// Expr::normal(Symbol::new("System`RandomReal"), vec![])
295 /// );
296 /// ```
297 pub fn rule_delayed<LHS: Into<Expr>>(lhs: LHS, rhs: Expr) -> Expr {
298 let lhs = lhs.into();
299 crate::expr!(System::RuleDelayed[lhs, rhs])
300 }
301
302 /// Construct a new `List[...]`(`{...}`) expression from it's elements.
303 ///
304 /// # Example
305 ///
306 /// Construct the expression `{1, 2, 3}`:
307 ///
308 /// ```
309 /// use wolfram_expr::Expr;
310 ///
311 /// let list = Expr::list(vec![Expr::from(1), Expr::from(2), Expr::from(3)]);
312 /// ```
313 pub fn list(elements: Vec<Expr>) -> Expr {
314 // `..elements` splices the Vec; the in-place `collect` reuses its
315 // allocation (no realloc), so this is as cheap as the direct form.
316 crate::expr!(System::List[..elements])
317 }
318}
319
320/// Wolfram Language expression variants.
321///
322/// Marked `#[non_exhaustive]` so that future variant additions (for new WXF wire types,
323/// etc.) are non-breaking. Downstream `match` expressions over `ExprKind` from outside
324/// this crate must include a `_ => …` arm.
325#[non_exhaustive]
326#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
327pub enum ExprKind<E = Expr> {
328 /// A machine (64-bit) integer.
329 Integer(i64),
330 /// A machine (64-bit) real, guaranteed non-NaN.
331 Real(F64),
332 /// A string.
333 String(String),
334 /// A symbol such as `` System`Plus ``.
335 Symbol(Symbol),
336 /// A normal expression `head[args…]` — see [`Normal`].
337 Normal(Normal<E>),
338 // WXF-derived variants:
339 /// A `ByteArray` — a flat buffer of bytes.
340 ByteArray(ByteArray),
341 /// An `Association` of key/value rules.
342 Association(Association),
343 /// A `NumericArray` — a packed array of fixed-width numbers.
344 NumericArray(NumericArray),
345 /// A `PackedArray` — a packed rectangular array of machine numbers.
346 PackedArray(PackedArray),
347 /// An arbitrary-precision integer.
348 BigInteger(BigInteger),
349 /// An arbitrary-precision real.
350 BigReal(BigReal),
351}
352
353/// Wolfram Language "normal" expression: `f[...]`.
354///
355/// A *normal* expression is any expression that consists of a head and zero or
356/// more arguments.
357#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
358pub struct Normal<E = Expr> {
359 /// The head of this normal expression.
360 head: E,
361
362 /// The elements of this normal expression.
363 ///
364 /// If `head` conceptually represents a function, these are the arguments that are
365 /// being applied to `head`.
366 contents: Vec<E>,
367}
368
369/// Subset of [`ExprKind`] that covers number-type expression values.
370///
371/// # Migration
372///
373/// Match on [`ExprKind`] directly — no intermediate `Number` needed:
374///
375/// ```
376/// # use wolfram_expr::{Expr, ExprKind, expr};
377/// let expr = expr!(42);
378///
379/// // Before:
380/// // if let Some(n) = expr.try_as_number() {
381/// // match n { Number::Integer(i) => …, Number::Real(r) => … }
382/// // }
383///
384/// // After:
385/// match expr.kind() {
386/// ExprKind::Integer(i) => println!("integer: {i}"),
387/// ExprKind::Real(r) => println!("real: {}", r.into_inner()),
388/// _ => {} // non-numeric expression
389/// }
390/// ```
391///
392/// To *construct* a number expression use [`Expr::from`] or [`Expr::real`] — see
393/// [`Expr::number`] for the full mapping.
394#[deprecated(since = "0.6.0-alpha.3", note = "match on `ExprKind::Integer` / `ExprKind::Real` directly")]
395#[allow(missing_docs)]
396#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
397pub enum Number {
398 Integer(i64),
399 Real(F64),
400}
401
402/// 64-bit floating-point real number. Not NaN.
403pub type F64 = ordered_float::NotNan<f64>;
404/// 32-bit floating-point real number. Not NaN.
405pub type F32 = ordered_float::NotNan<f32>;
406
407//=======================================
408// Type Impl's
409//=======================================
410
411impl Normal {
412 /// Construct a new normal expression from the head and elements.
413 pub fn new<E: Into<Expr>>(head: E, contents: Vec<Expr>) -> Self {
414 Normal {
415 head: head.into(),
416 contents,
417 }
418 }
419
420 /// The head of this normal expression.
421 pub fn head(&self) -> &Expr {
422 &self.head
423 }
424
425 /// The elements of this normal expression.
426 ///
427 /// If `head` conceptually represents a function, these are the arguments that are
428 /// being applied to `head`.
429 pub fn elements(&self) -> &[Expr] {
430 &self.contents
431 }
432
433 /// The elements of this normal expression.
434 ///
435 /// Use [`Normal::elements()`] to get a reference to this value.
436 pub fn into_elements(self) -> Vec<Expr> {
437 self.contents
438 }
439
440 /// Returns `true` if the head of this expression is `sym`.
441 pub fn has_head(&self, sym: &Symbol) -> bool {
442 self.head == *sym
443 }
444}
445
446#[allow(deprecated)]
447impl Number {
448 /// Construct a `Number::Real` from an `f64`.
449 ///
450 /// # Migration
451 ///
452 /// ```
453 /// # use wolfram_expr::Expr;
454 /// // Before: Expr::number(Number::real(3.14))
455 /// let _real = Expr::from(3.14_f64); // or Expr::real(3.14)
456 /// ```
457 ///
458 /// # Panics
459 ///
460 /// Panics if `r` is NaN.
461 #[deprecated(since = "0.6.0-alpha.3", note = "use `Expr::from(f64)` or `Expr::real(f64)` instead")]
462 pub fn real(r: f64) -> Self {
463 let ExprKind::Real(f) = Expr::real(r).to_kind() else { unreachable!() };
464 Number::Real(f)
465 }
466}
467
468//=======================================
469// Display & Debug impl/s
470//=======================================
471
472impl fmt::Debug for Expr {
473 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
474 let Expr { inner } = self;
475 write!(f, "{:?}", inner)
476 }
477}
478
479
480//======================================
481// Comparision trait impls
482//======================================
483
484impl PartialEq<Symbol> for Expr {
485 fn eq(&self, other: &Symbol) -> bool {
486 match self.kind() {
487 ExprKind::Symbol(self_sym) => self_sym == other,
488 _ => false,
489 }
490 }
491}