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, 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//==============================================================================
112// FromWXF
113//==============================================================================
114
115/// Build a [`Symbol`] from a name read off the WXF wire — stored verbatim, no
116/// validation. The kernel produced it; re-parsing it would be pointless work.
117/// The `String` is moved straight into the `Symbol`'s storage (no extra copy).
118fn symbol_from_name(name: String) -> Symbol {
119    // SAFETY: a `Symbol` is just an `Arc<String>`; no syntax invariant is relied
120    // upon, so storing an arbitrary wire name is sound (see `Symbol::new`).
121    unsafe { Symbol::unchecked_new(name) }
122}
123
124fn numeric_array_from_parts(
125    dt: NumericArrayEnum,
126    dims: Vec<usize>,
127    bytes: Vec<u8>,
128) -> NumericArray {
129    NumericArray::new(dt, dims, bytes)
130}
131
132fn packed_array_from_parts(
133    dt: NumericArrayEnum,
134    dims: Vec<usize>,
135    bytes: Vec<u8>,
136) -> Result<PackedArray, Error> {
137    let pdt = PackedArrayEnum::try_from(dt).map_err(|_| {
138        Error::invalid(format!(
139            "PackedArray does not support element type {}",
140            dt.name()
141        ))
142    })?;
143    Ok(PackedArray::new(pdt, dims, bytes))
144}
145
146impl<'de> FromWXF<'de> for Symbol {
147    fn from_wxf_with_tag<R: Reader<'de>>(
148        r: &mut WxfReader<R>,
149        tok: ExpressionEnum,
150    ) -> Result<Self, Error> {
151        if tok != ExpressionEnum::Symbol {
152            return Err(Error::unexpected_token(&["Symbol"], tok));
153        }
154        Ok(symbol_from_name(r.read_symbol_name()?))
155    }
156}
157
158impl<'de> FromWXF<'de> for NumericArray {
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::NumericArray {
164            return Err(Error::unexpected_token(&["NumericArray"], tok));
165        }
166        let (dt, dims, bytes) = r.read_numeric_array_parts()?;
167        Ok(numeric_array_from_parts(dt, dims, bytes))
168    }
169}
170
171impl<'de> FromWXF<'de> for PackedArray {
172    fn from_wxf_with_tag<R: Reader<'de>>(
173        r: &mut WxfReader<R>,
174        tok: ExpressionEnum,
175    ) -> Result<Self, Error> {
176        if tok != ExpressionEnum::PackedArray {
177            return Err(Error::unexpected_token(&["PackedArray"], tok));
178        }
179        let (dt, dims, bytes) = r.read_numeric_array_parts()?;
180        packed_array_from_parts(dt, dims, bytes)
181    }
182}
183
184// `Association` (= `Vec<RuleEntry>`) can't impl the foreign `FromWXF` here
185// (orphan rule); use [`read_association`] directly, or deserialize as `Expr`.
186
187impl<'de> FromWXF<'de> for BigInteger {
188    fn from_wxf_with_tag<R: Reader<'de>>(
189        r: &mut WxfReader<R>,
190        tok: ExpressionEnum,
191    ) -> Result<Self, Error> {
192        if tok != ExpressionEnum::BigInteger {
193            return Err(Error::unexpected_token(&["BigInteger"], tok));
194        }
195        Ok(BigInteger(r.read_symbol_name()?))
196    }
197}
198
199impl<'de> FromWXF<'de> for BigReal {
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::BigReal {
205            return Err(Error::unexpected_token(&["BigReal"], tok));
206        }
207        Ok(BigReal(r.read_symbol_name()?))
208    }
209}
210
211/// Read an Association body (token already consumed): `count` × (rule, key, value).
212fn read_association<'de, R: Reader<'de>>(
213    r: &mut WxfReader<R>,
214) -> Result<Association, Error> {
215    let n = r.read_varint()?;
216    let mut a = Association::new();
217    for _ in 0..n {
218        let delayed = r.read_rule()?;
219        let key = Expr::from_wxf(r)?;
220        let value = Expr::from_wxf(r)?;
221        a.push(RuleEntry {
222            key,
223            value,
224            delayed,
225        });
226    }
227    Ok(a)
228}
229
230impl<'de> FromWXF<'de> for Expr {
231    fn from_wxf_with_tag<R: Reader<'de>>(
232        r: &mut WxfReader<R>,
233        tok: ExpressionEnum,
234    ) -> Result<Self, Error> {
235        match tok {
236            ExpressionEnum::Integer8 => Ok(Expr::from(i64::from(r.read_i8()?))),
237            ExpressionEnum::Integer16 => Ok(Expr::from(i64::from(r.read_i16()?))),
238            ExpressionEnum::Integer32 => Ok(Expr::from(i64::from(r.read_i32()?))),
239            ExpressionEnum::Integer64 => Ok(Expr::from(r.read_i64()?)),
240            ExpressionEnum::Real64 => {
241                let f = r.read_f64()?;
242                if f.is_nan() {
243                    return Err(Error::invalid("Real64 token contained NaN".into()));
244                }
245                Ok(Expr::real(f))
246            },
247            ExpressionEnum::String => Ok(Expr::string(r.read_str()?.to_owned())),
248            ExpressionEnum::Symbol => {
249                Ok(Expr::symbol(symbol_from_name(r.read_symbol_name()?)))
250            },
251            ExpressionEnum::ByteArray => Ok(Expr::from(r.read_byte_array()?.to_vec())),
252            ExpressionEnum::BigInteger => {
253                Ok(Expr::from(BigInteger(r.read_symbol_name()?)))
254            },
255            ExpressionEnum::BigReal => {
256                Ok(Expr::from(BigReal(r.read_symbol_name()?)))
257            },
258            ExpressionEnum::NumericArray => {
259                let (dt, dims, bytes) = r.read_numeric_array_parts()?;
260                Ok(Expr::from(numeric_array_from_parts(dt, dims, bytes)))
261            },
262            ExpressionEnum::PackedArray => {
263                let (dt, dims, bytes) = r.read_numeric_array_parts()?;
264                Ok(Expr::from(packed_array_from_parts(dt, dims, bytes)?))
265            },
266            ExpressionEnum::Function => {
267                let n = r.read_varint()?;
268                let head = Expr::from_wxf(r)?;
269                let mut args = Vec::with_capacity(n as usize);
270                for _ in 0..n {
271                    args.push(Expr::from_wxf(r)?);
272                }
273                Ok(Expr::normal(head, args))
274            },
275            ExpressionEnum::Association => Ok(Expr::from(read_association(r)?)),
276            other @ (ExpressionEnum::Rule | ExpressionEnum::RuleDelayed) => {
277                Err(Error::unexpected_token(&[], other))
278            },
279        }
280    }
281}