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