Skip to main content

wolfram_expr/
wxf_impls.rs

1//! [`ToWXF`]/[`FromWXF`] impls for the `wolfram-expr` value types.
2//!
3//! The traits, token cursor, and primitive/std impls live in the
4//! dependency-free [`wolfram_serialize`] crate; the impls here build `wolfram-expr`'s
5//! own types (`Expr`, `Symbol`, `Association`, `NumericArray`, `PackedArray`,
6//! `BigInteger`, `BigReal`) on top of the raw reader/writer.
7
8use std::convert::TryFrom;
9
10use wolfram_serialize::Error;
11use wolfram_serialize::{
12    ExpressionEnum, FromWXF, NumericArrayEnum, PackedArrayEnum, Reader, ToWXF, Writer,
13    WxfReader, WxfStruct, WxfWriter,
14};
15
16use crate::{
17    ArrayBuf, Association, BigInteger, BigReal, Expr, ExprKind, NumericArray,
18    PackedArray, RuleEntry, Symbol,
19};
20
21//==============================================================================
22// ToWXF
23//==============================================================================
24
25impl ToWXF for Symbol {
26    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
27        w.write_symbol(self.as_str())
28    }
29}
30
31impl ToWXF for NumericArray {
32    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
33        w.write_numeric_array(
34            ArrayBuf::data_type(self),
35            ArrayBuf::dimensions(self),
36            ArrayBuf::as_bytes(self),
37        )
38    }
39}
40
41impl ToWXF for PackedArray {
42    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
43        w.write_packed_array(
44            ArrayBuf::data_type(self),
45            ArrayBuf::dimensions(self),
46            ArrayBuf::as_bytes(self),
47        )
48    }
49}
50
51// NOTE: `Association` is a `Vec<RuleEntry>` type alias, so the orphan rule
52// forbids `impl ToWXF for Association` here (both `Vec` and `ToWXF` are foreign).
53// Association serialization is inlined into the `Expr` impl via `write_assoc`.
54
55/// Write an Association body: `write_association(n)` then each `(rule, key, value)`.
56fn write_assoc<W: Writer>(a: &Association, w: &mut WxfWriter<W>) -> Result<(), Error> {
57    w.write_association(a.len())?;
58    for e in a.iter() {
59        w.write_rule(e.delayed)?;
60        e.key.to_wxf(w)?;
61        e.value.to_wxf(w)?;
62    }
63    Ok(())
64}
65
66impl ToWXF for BigInteger {
67    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
68        w.write_big_integer(self.as_str())
69    }
70}
71
72impl ToWXF for BigReal {
73    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
74        w.write_big_real(self.as_str())
75    }
76}
77
78impl ToWXF for Expr {
79    fn to_wxf<W: Writer>(&self, w: &mut WxfWriter<W>) -> Result<(), Error> {
80        match self.kind() {
81            ExprKind::Integer(n) => w.write_integer(*n),
82            ExprKind::Real(r) => w.write_real(**r),
83            ExprKind::String(t) => w.write_string(t.as_str()),
84            ExprKind::Symbol(sym) => w.write_symbol(sym.as_str()),
85            ExprKind::Normal(normal) => {
86                w.write_function(normal.elements().len())?;
87                normal.head().to_wxf(w)?;
88                for arg in normal.elements() {
89                    arg.to_wxf(w)?;
90                }
91                Ok(())
92            },
93            ExprKind::ByteArray(b) => w.write_byte_array(b.as_slice()),
94            ExprKind::Association(a) => write_assoc(a, w),
95            ExprKind::NumericArray(arr) => w.write_numeric_array(
96                ArrayBuf::data_type(arr),
97                ArrayBuf::dimensions(arr),
98                ArrayBuf::as_bytes(arr),
99            ),
100            ExprKind::PackedArray(arr) => w.write_packed_array(
101                ArrayBuf::data_type(arr),
102                ArrayBuf::dimensions(arr),
103                ArrayBuf::as_bytes(arr),
104            ),
105            ExprKind::BigInteger(n) => w.write_big_integer(n.as_str()),
106            ExprKind::BigReal(r) => w.write_big_real(r.as_str()),
107        }
108    }
109}
110
111// `Expr` has its own hand-written impl above rather than going through
112// `#[derive(ToWXF)]`, so it never picked up the `WxfStruct` marker that gates
113// the blanket `impl<T: ToWXF + WxfStruct> ToWXF for Vec<T>` (that bound exists
114// purely so the blanket doesn't collide with the primitive-specific
115// `Vec<u8>`/`Vec<f64>`/etc. impls, which live in `wolfram-serialize` and can't
116// see `Expr`). Implementing the marker here is enough — the already-coherent
117// blanket in `wolfram-serialize` then covers `Vec<Expr>` for both directions
118// with no new `Vec` impl (the orphan rules forbid one anyway: `Vec` isn't a
119// fundamental type, so `impl ForeignTrait for Vec<LocalType>` isn't allowed
120// outside the crate that defines `ForeignTrait`).
121impl WxfStruct for Expr {}
122
123//==============================================================================
124// FromWXF
125//==============================================================================
126
127/// Build a [`Symbol`] from a name read off the WXF wire — stored verbatim, no
128/// validation. The kernel produced it; re-parsing it would be pointless work.
129/// The `String` is moved straight into the `Symbol`'s storage (no extra copy).
130fn symbol_from_name(name: String) -> Symbol {
131    // SAFETY: a `Symbol` is just an `Arc<String>`; no syntax invariant is relied
132    // upon, so storing an arbitrary wire name is sound (see `Symbol::new`).
133    unsafe { Symbol::unchecked_new(name) }
134}
135
136fn numeric_array_from_parts(
137    dt: NumericArrayEnum,
138    dims: Vec<usize>,
139    bytes: Vec<u8>,
140) -> NumericArray {
141    NumericArray::new(dt, dims, bytes)
142}
143
144fn packed_array_from_parts(
145    dt: NumericArrayEnum,
146    dims: Vec<usize>,
147    bytes: Vec<u8>,
148) -> Result<PackedArray, Error> {
149    let pdt = PackedArrayEnum::try_from(dt).map_err(|_| {
150        Error::invalid(format!(
151            "PackedArray does not support element type {}",
152            dt.name()
153        ))
154    })?;
155    Ok(PackedArray::new(pdt, dims, bytes))
156}
157
158impl<'de> FromWXF<'de> for Symbol {
159    fn from_wxf_with_tag<R: Reader<'de>>(
160        r: &mut WxfReader<R>,
161        tok: ExpressionEnum,
162    ) -> Result<Self, Error> {
163        if tok != ExpressionEnum::Symbol {
164            return Err(Error::unexpected_token(&["Symbol"], tok));
165        }
166        Ok(symbol_from_name(r.read_symbol_name()?))
167    }
168}
169
170impl<'de> FromWXF<'de> for NumericArray {
171    fn from_wxf_with_tag<R: Reader<'de>>(
172        r: &mut WxfReader<R>,
173        tok: ExpressionEnum,
174    ) -> Result<Self, Error> {
175        if tok != ExpressionEnum::NumericArray {
176            return Err(Error::unexpected_token(&["NumericArray"], tok));
177        }
178        let (dt, dims, bytes) = r.read_numeric_array_parts()?;
179        Ok(numeric_array_from_parts(dt, dims, bytes))
180    }
181}
182
183impl<'de> FromWXF<'de> for PackedArray {
184    fn from_wxf_with_tag<R: Reader<'de>>(
185        r: &mut WxfReader<R>,
186        tok: ExpressionEnum,
187    ) -> Result<Self, Error> {
188        if tok != ExpressionEnum::PackedArray {
189            return Err(Error::unexpected_token(&["PackedArray"], tok));
190        }
191        let (dt, dims, bytes) = r.read_numeric_array_parts()?;
192        packed_array_from_parts(dt, dims, bytes)
193    }
194}
195
196// `Association` (= `Vec<RuleEntry>`) can't impl the foreign `FromWXF` here
197// (orphan rule); use [`read_association`] directly, or deserialize as `Expr`.
198
199impl<'de> FromWXF<'de> for BigInteger {
200    fn from_wxf_with_tag<R: Reader<'de>>(
201        r: &mut WxfReader<R>,
202        tok: ExpressionEnum,
203    ) -> Result<Self, Error> {
204        if tok != ExpressionEnum::BigInteger {
205            return Err(Error::unexpected_token(&["BigInteger"], tok));
206        }
207        Ok(BigInteger(r.read_symbol_name()?))
208    }
209}
210
211impl<'de> FromWXF<'de> for BigReal {
212    fn from_wxf_with_tag<R: Reader<'de>>(
213        r: &mut WxfReader<R>,
214        tok: ExpressionEnum,
215    ) -> Result<Self, Error> {
216        if tok != ExpressionEnum::BigReal {
217            return Err(Error::unexpected_token(&["BigReal"], tok));
218        }
219        Ok(BigReal(r.read_symbol_name()?))
220    }
221}
222
223/// Read an Association body (token already consumed): `count` × (rule, key, value).
224fn read_association<'de, R: Reader<'de>>(
225    r: &mut WxfReader<R>,
226) -> Result<Association, Error> {
227    let n = r.read_varint()?;
228    let mut a = Association::new();
229    for _ in 0..n {
230        let delayed = r.read_rule()?;
231        let key = Expr::from_wxf(r)?;
232        let value = Expr::from_wxf(r)?;
233        a.push(RuleEntry {
234            key,
235            value,
236            delayed,
237        });
238    }
239    Ok(a)
240}
241
242impl<'de> FromWXF<'de> for Expr {
243    fn from_wxf_with_tag<R: Reader<'de>>(
244        r: &mut WxfReader<R>,
245        tok: ExpressionEnum,
246    ) -> Result<Self, Error> {
247        match tok {
248            ExpressionEnum::Integer8 => Ok(Expr::from(i64::from(r.read_i8()?))),
249            ExpressionEnum::Integer16 => Ok(Expr::from(i64::from(r.read_i16()?))),
250            ExpressionEnum::Integer32 => Ok(Expr::from(i64::from(r.read_i32()?))),
251            ExpressionEnum::Integer64 => Ok(Expr::from(r.read_i64()?)),
252            ExpressionEnum::Real64 => {
253                let f = r.read_f64()?;
254                if f.is_nan() {
255                    return Err(Error::invalid("Real64 token contained NaN".into()));
256                }
257                Ok(Expr::real(f))
258            },
259            ExpressionEnum::String => Ok(Expr::string(r.read_str()?.to_owned())),
260            ExpressionEnum::Symbol => {
261                Ok(Expr::symbol(symbol_from_name(r.read_symbol_name()?)))
262            },
263            ExpressionEnum::ByteArray => Ok(Expr::from(r.read_byte_array()?.to_vec())),
264            ExpressionEnum::BigInteger => {
265                Ok(Expr::from(BigInteger(r.read_symbol_name()?)))
266            },
267            ExpressionEnum::BigReal => Ok(Expr::from(BigReal(r.read_symbol_name()?))),
268            ExpressionEnum::NumericArray => {
269                let (dt, dims, bytes) = r.read_numeric_array_parts()?;
270                Ok(Expr::from(numeric_array_from_parts(dt, dims, bytes)))
271            },
272            ExpressionEnum::PackedArray => {
273                let (dt, dims, bytes) = r.read_numeric_array_parts()?;
274                Ok(Expr::from(packed_array_from_parts(dt, dims, bytes)?))
275            },
276            ExpressionEnum::Function => {
277                let n = r.read_varint()?;
278                let head = Expr::from_wxf(r)?;
279                let mut args = Vec::with_capacity(n as usize);
280                for _ in 0..n {
281                    args.push(Expr::from_wxf(r)?);
282                }
283                Ok(Expr::normal(head, args))
284            },
285            ExpressionEnum::Association => Ok(Expr::from(read_association(r)?)),
286            other @ (ExpressionEnum::Rule | ExpressionEnum::RuleDelayed) => {
287                Err(Error::unexpected_token(&[], other))
288            },
289        }
290    }
291}