Skip to main content

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;
17pub mod 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// WXF serialization — the traits, derives, and entry points live in the
48// dependency-free `wolfram-serialize` crate; re-exported here for ergonomics.
49pub use wolfram_serialize::{
50    from_wxf, from_wxf_ref, read_wxf, to_wxf, CompressionLevel, Failure, FromWXF, Reader,
51    ToWXF,
52};
53
54#[cfg(feature = "unstable_parse")]
55pub use self::ptr_cmp::ExprRefCmp;
56
57/// Wolfram Language expression.
58///
59/// # Example
60///
61/// Construct the expression `{1, 2, 3}`:
62///
63/// ```
64/// use wolfram_expr::{Expr, Symbol};
65///
66/// let expr = Expr::normal(Symbol::new("System`List"), vec![
67///     Expr::from(1),
68///     Expr::from(2),
69///     Expr::from(3)
70/// ]);
71/// ```
72///
73/// # Reference counting
74///
75/// Internally, `Expr` is an atomically reference-counted [`ExprKind`]. This makes cloning
76/// an expression computationally inexpensive.
77#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
78pub struct Expr {
79    inner: Arc<ExprKind>,
80}
81
82// Assert that Expr has the same size and alignment as a usize / pointer.
83const _: () = assert!(mem::size_of::<Expr>() == mem::size_of::<usize>());
84const _: () = assert!(mem::size_of::<Expr>() == mem::size_of::<*const ()>());
85const _: () = assert!(mem::align_of::<Expr>() == mem::align_of::<usize>());
86const _: () = assert!(mem::align_of::<Expr>() == mem::align_of::<*const ()>());
87
88impl Expr {
89    /// Construct a new expression from an [`ExprKind`].
90    pub fn new(kind: ExprKind) -> Expr {
91        Expr {
92            inner: Arc::new(kind),
93        }
94    }
95
96    /// Consume `self` and return an owned [`ExprKind`].
97    ///
98    /// If the reference count of `self` is equal to 1 this function will *not* perform
99    /// a clone of the stored `ExprKind`, making this operation very cheap in that case.
100    // Silence the clippy warning about this method. While this method technically doesn't
101    // follow the Rust style convention of using `into` to prefix methods which take
102    // `self` by move, I think using `to` is more appropriate given the expected
103    // performance characteristics of this method. `into` implies that the method is
104    // always returning data already owned by this type, and as such should be a very
105    // cheap operation. This method can make no such guarantee; if the reference count is
106    // 1, then performance is very good, but if the reference count is >1, a deeper clone
107    // must be done.
108    #[allow(clippy::wrong_self_convention)]
109    pub fn to_kind(self) -> ExprKind {
110        match Arc::try_unwrap(self.inner) {
111            Ok(kind) => kind,
112            Err(self_) => (*self_).clone(),
113        }
114    }
115
116    /// Get the [`ExprKind`] representing this expression.
117    pub fn kind(&self) -> &ExprKind {
118        &self.inner
119    }
120
121    /// Get mutable access to the [`ExprKind`] that represents this expression.
122    ///
123    /// If the reference count of the underlying shared pointer is not equal to 1, this
124    /// will clone the [`ExprKind`] to make it unique.
125    pub fn kind_mut(&mut self) -> &mut ExprKind {
126        Arc::make_mut(&mut self.inner)
127    }
128
129    /// Retrieve the reference count of this expression.
130    pub fn ref_count(&self) -> usize {
131        Arc::strong_count(&self.inner)
132    }
133
134    /// Construct a new normal expression from the head and elements.
135    pub fn normal<H: Into<Expr>>(head: H, contents: Vec<Expr>) -> Expr {
136        let head = head.into();
137        // let contents = contents.into();
138        Expr {
139            inner: Arc::new(ExprKind::Normal(Normal { head, contents })),
140        }
141    }
142
143    // TODO: Should Expr's be cached? Especially Symbol exprs? Would certainly save
144    //       a lot of allocations.
145    /// Construct a new expression from a [`Symbol`].
146    pub fn symbol<S: Into<Symbol>>(s: S) -> Expr {
147        let s = s.into();
148        Expr {
149            inner: Arc::new(ExprKind::Symbol(s)),
150        }
151    }
152
153    /// Construct a new expression from a [`Number`].
154    pub fn number(num: Number) -> Expr {
155        Expr {
156            inner: Arc::new(ExprKind::from(num)),
157        }
158    }
159
160    /// Construct a new expression from a [`String`].
161    pub fn string<S: Into<String>>(s: S) -> Expr {
162        Expr {
163            inner: Arc::new(ExprKind::String(s.into())),
164        }
165    }
166
167    /// Construct an expression from a floating-point number.
168    ///
169    /// ```
170    /// # use wolfram_expr::Expr;
171    /// let expr = Expr::real(3.14159);
172    /// ```
173    ///
174    /// # Panics
175    ///
176    /// This function will panic if `real` is NaN.
177    pub fn real(real: f64) -> Expr {
178        Expr::number(Number::real(real))
179    }
180
181    /// Returns the outer-most symbol "tag" used in this expression.
182    ///
183    /// To illustrate:
184    ///
185    /// Expression   | Tag
186    /// -------------|----
187    /// `5`          | `None`
188    /// `"hello"`    | `None`
189    /// `foo`        | `foo`
190    /// `f[1, 2, 3]` | `f`
191    /// `g[x][y]`    | `g`
192    //
193    // TODO: _[x] probably should return None, even though technically
194    //       Blank[][x] has the tag Blank.
195    // TODO: The above TODO is probably wrong -- tag() shouldn't have any language
196    //       semantics built in to it.
197    pub fn tag(&self) -> Option<Symbol> {
198        match *self.inner {
199            ExprKind::Normal(ref normal) => normal.head.tag(),
200            ExprKind::Symbol(ref sym) => Some(sym.clone()),
201            // Atomic variants (no symbolic head): Integer, Real, String, ByteArray,
202            // Association, NumericArray, PackedArray, BigInteger, BigReal.
203            _ => None,
204        }
205    }
206
207    /// If this represents a [`Normal`] expression, return its head. Otherwise, return
208    /// `None`.
209    pub fn normal_head(&self) -> Option<Expr> {
210        match *self.inner {
211            ExprKind::Normal(ref normal) => Some(normal.head.clone()),
212            _ => None,
213        }
214    }
215
216    /// Attempt to get the element at `index` of a `Normal` expression.
217    ///
218    /// Return `None` if this is not a `Normal` expression, or the given index is out of
219    /// bounds.
220    ///
221    /// `index` is 0-based. The 0th index is the first element, not the head.
222    ///
223    /// This function does not panic.
224    pub fn normal_part(&self, index_0: usize) -> Option<&Expr> {
225        match self.kind() {
226            ExprKind::Normal(ref normal) => normal.contents.get(index_0),
227            _ => None,
228        }
229    }
230
231    /// Returns `true` if `self` is a `Normal` expr with the head `sym`.
232    pub fn has_normal_head(&self, sym: &Symbol) -> bool {
233        match *self.kind() {
234            ExprKind::Normal(ref normal) => normal.has_head(sym),
235            _ => false,
236        }
237    }
238
239    //==================================
240    // Common values
241    //==================================
242
243    /// [`Null`](https://reference.wolfram.com/language/ref/Null.html) <sub>WL</sub>.
244    pub fn null() -> Expr {
245        crate::expr!(System::Null)
246    }
247
248    //==================================
249    // Convenience creation functions
250    //==================================
251
252    /// Construct a new `Rule[_, _]` expression from the left-hand side and right-hand
253    /// side.
254    ///
255    /// # Example
256    ///
257    /// Construct the expression `FontSize -> 16`:
258    ///
259    /// ```
260    /// use wolfram_expr::{Expr, Symbol};
261    ///
262    /// let option = Expr::rule(Symbol::new("System`FontSize"), Expr::from(16));
263    /// ```
264    pub fn rule<LHS: Into<Expr>>(lhs: LHS, rhs: Expr) -> Expr {
265        let lhs = lhs.into();
266        crate::expr!(System::Rule[lhs, rhs])
267    }
268    /// Construct a new `RuleDelayed[_, _]` expression from the left-hand side and right-hand
269    /// side.
270    ///
271    /// # Example
272    ///
273    /// Construct the expression `x :> RandomReal[]`:
274    ///
275    /// ```
276    /// use wolfram_expr::{Expr, Symbol};
277    ///
278    /// let delayed = Expr::rule_delayed(
279    ///     Symbol::new("Global`x"),
280    ///     Expr::normal(Symbol::new("System`RandomReal"), vec![])
281    /// );
282    /// ```
283    pub fn rule_delayed<LHS: Into<Expr>>(lhs: LHS, rhs: Expr) -> Expr {
284        let lhs = lhs.into();
285        crate::expr!(System::RuleDelayed[lhs, rhs])
286    }
287
288    /// Construct a new `List[...]`(`{...}`) expression from it's elements.
289    ///
290    /// # Example
291    ///
292    /// Construct the expression `{1, 2, 3}`:
293    ///
294    /// ```
295    /// use wolfram_expr::Expr;
296    ///
297    /// let list = Expr::list(vec![Expr::from(1), Expr::from(2), Expr::from(3)]);
298    /// ```
299    pub fn list(elements: Vec<Expr>) -> Expr {
300        // `..elements` splices the Vec; the in-place `collect` reuses its
301        // allocation (no realloc), so this is as cheap as the direct form.
302        crate::expr!(System::List[..elements])
303    }
304}
305
306/// Wolfram Language expression variants.
307///
308/// Marked `#[non_exhaustive]` so that future variant additions (for new WXF wire types,
309/// etc.) are non-breaking. Downstream `match` expressions over `ExprKind` from outside
310/// this crate must include a `_ => …` arm.
311#[allow(missing_docs)]
312#[non_exhaustive]
313#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
314pub enum ExprKind<E = Expr> {
315    Integer(i64),
316    Real(F64),
317    String(String),
318    Symbol(Symbol),
319    Normal(Normal<E>),
320    // WXF-derived variants:
321    ByteArray(ByteArray),
322    Association(Association),
323    NumericArray(NumericArray),
324    PackedArray(PackedArray),
325    BigInteger(BigInteger),
326    BigReal(BigReal),
327}
328
329/// Wolfram Language "normal" expression: `f[...]`.
330///
331/// A *normal* expression is any expression that consists of a head and zero or
332/// more arguments.
333#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
334pub struct Normal<E = Expr> {
335    /// The head of this normal expression.
336    head: E,
337
338    /// The elements of this normal expression.
339    ///
340    /// If `head` conceptually represents a function, these are the arguments that are
341    /// being applied to `head`.
342    contents: Vec<E>,
343}
344
345/// Subset of [`ExprKind`] that covers number-type expression values.
346#[allow(missing_docs)]
347#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
348pub enum Number {
349    // TODO: Rename this to MachineInteger
350    Integer(i64),
351    // TODO: Make an explicit MachineReal type which hides the inner f64, so that other
352    //       code can make use of WL machine reals with a guaranteed type. In
353    //       particular, change wl_compile::mir::Constant to use that type.
354    Real(F64),
355}
356
357/// 64-bit floating-point real number. Not NaN.
358pub type F64 = ordered_float::NotNan<f64>;
359/// 32-bit floating-point real number. Not NaN.
360pub type F32 = ordered_float::NotNan<f32>;
361
362//=======================================
363// Type Impl's
364//=======================================
365
366impl Normal {
367    /// Construct a new normal expression from the head and elements.
368    pub fn new<E: Into<Expr>>(head: E, contents: Vec<Expr>) -> Self {
369        Normal {
370            head: head.into(),
371            contents,
372        }
373    }
374
375    /// The head of this normal expression.
376    pub fn head(&self) -> &Expr {
377        &self.head
378    }
379
380    /// The elements of this normal expression.
381    ///
382    /// If `head` conceptually represents a function, these are the arguments that are
383    /// being applied to `head`.
384    pub fn elements(&self) -> &[Expr] {
385        &self.contents
386    }
387
388    /// The elements of this normal expression.
389    ///
390    /// Use [`Normal::elements()`] to get a reference to this value.
391    pub fn into_elements(self) -> Vec<Expr> {
392        self.contents
393    }
394
395    /// Returns `true` if the head of this expression is `sym`.
396    pub fn has_head(&self, sym: &Symbol) -> bool {
397        self.head == *sym
398    }
399}
400
401impl Number {
402    /// # Panics
403    ///
404    /// This function will panic if `r` is NaN.
405    ///
406    /// TODO: Change this function to take `NotNan` instead, so the caller doesn't have to
407    ///       worry about panics.
408    pub fn real(r: f64) -> Self {
409        let r = match ordered_float::NotNan::new(r) {
410            Ok(r) => r,
411            Err(_) => panic!("Number::real: got NaN"),
412        };
413        Number::Real(r)
414    }
415}
416
417//=======================================
418// Display & Debug impl/s
419//=======================================
420
421impl fmt::Debug for Expr {
422    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
423        let Expr { inner } = self;
424        write!(f, "{:?}", inner)
425    }
426}
427
428
429//======================================
430// Comparision trait impls
431//======================================
432
433impl PartialEq<Symbol> for Expr {
434    fn eq(&self, other: &Symbol) -> bool {
435        match self.kind() {
436            ExprKind::Symbol(self_sym) => self_sym == other,
437            _ => false,
438        }
439    }
440}