xee_interpreter/atomic/
atomic_core.rs

1use chrono::Offset;
2use ibig::IBig;
3use iri_string::types::{IriReferenceStr, IriReferenceString, IriString};
4use ordered_float::OrderedFloat;
5use rust_decimal::prelude::*;
6use std::cmp::Ordering;
7use std::fmt;
8use std::rc::Rc;
9use xee_xpath_ast::ast::Name;
10
11use xee_schema_type::Xs;
12
13use crate::atomic::types::{BinaryType, IntegerType, StringType};
14use crate::error;
15use crate::string::Collation;
16
17use super::datetime::{
18    Duration, GDay, GMonth, GMonthDay, GYear, GYearMonth, NaiveDateTimeWithOffset,
19    NaiveDateWithOffset, NaiveTimeWithOffset, YearMonthDuration,
20};
21use super::{op_unary, OpEq};
22use super::{AtomicCompare, OpGt};
23
24// We try to maintain this struct as size 16 as it's cloned a lot during normal
25// operation. Anything bigger we stuff in an Rc
26
27// https://www.w3.org/TR/xpath-datamodel-31/#xs-types
28
29/// An atomic value.
30///
31/// These are designated with a `xs:` namespace prefix and are described by the
32/// [XPath data model](https://www.w3.org/TR/xpath-datamodel-31/#xs-types).
33#[derive(Debug, Clone, PartialEq, Eq, Hash)]
34pub enum Atomic {
35    /// xs:untypedAtomic
36    Untyped(Rc<str>),
37    /// a string type such as xs:string, xs:token, etc
38    String(StringType, Rc<str>),
39    /// xs:float
40    ///
41    /// This is an [`ordered_float::OrderedFloat`]
42    Float(OrderedFloat<f32>),
43    /// xs:double
44    ///
45    /// This is an [`ordered_float::OrderedFloat`]
46    Double(OrderedFloat<f64>),
47    /// xs:decimal
48    ///
49    /// This is a [`rust_decimal::Decimal`]
50    Decimal(Rc<Decimal>),
51    /// xs integer types (xs:integer, xs:long, xs:int, etc)
52    ///
53    /// This is an [`ibig::IBig`]
54    Integer(IntegerType, Rc<IBig>),
55    /// xs:duration
56    Duration(Rc<Duration>),
57    /// xs:yearMonthDuration
58    YearMonthDuration(YearMonthDuration),
59    /// xs:dayTimeDuration
60    ///
61    /// This is a [`chrono::Duration`]
62    DayTimeDuration(Rc<chrono::Duration>),
63    /// xs:dateTime
64    DateTime(Rc<NaiveDateTimeWithOffset>),
65    /// xs:dateTimeStamp
66    ///
67    /// This is a [`chrono::DateTime`] with a fixed offset
68    DateTimeStamp(Rc<chrono::DateTime<chrono::FixedOffset>>),
69    /// xs:time
70    Time(Rc<NaiveTimeWithOffset>),
71    /// xs:date
72    Date(Rc<NaiveDateWithOffset>),
73    /// xs:gYearMonth
74    GYearMonth(Rc<GYearMonth>),
75    /// xs:gYear
76    GYear(Rc<GYear>),
77    /// xs:gMonthDay
78    GMonthDay(Rc<GMonthDay>),
79    /// xs:gMonth
80    GDay(Rc<GDay>),
81    /// xs:gDay
82    GMonth(Rc<GMonth>),
83    /// xs:boolean
84    Boolean(bool),
85    /// xs binary types (xs:hexBinary, xs:base64Binary)
86    Binary(BinaryType, Rc<[u8]>),
87    /// xs:QName
88    QName(Rc<Name>),
89}
90
91// This takes 24 bytes to store. Atomic is the largest part of Item. We could
92// try to make it smaller by using Rc<String> and Rc<Vec<u8>> instead of
93// Rc<str> and Rc<[u8]>, but that would pack it so tightly that item, which
94// uses atomic, would need 24 bytes anyway (as it does already), and we'd have
95// more indirections. Since we have no clear evidence that would help, we leave
96// it at 24 for now.
97#[cfg(target_arch = "x86_64")]
98static_assertions::assert_eq_size!(Atomic, [u8; 24]);
99
100impl Atomic {
101    /// The [effective boolean
102    /// value](https://www.w3.org/TR/xpath-functions-31/#func-boolean) of an
103    /// atomic value.
104    ///
105    /// - xs:boolean are taken as is.
106    ///
107    /// - xs:string is false if empty, otherwise true
108    /// - xs:untypedAtomic is false if empty, otherwise true
109    /// - any xs integer values are false if zero, otherwise true
110    /// - xs:decimal is false if zero, otherwise true
111    /// - xs:float is false if zero or NaN, otherwise true
112    /// - xs:double is false if zero or NaN, otherwise true
113    ///
114    /// All other types are not convertible to a boolean.
115    pub(crate) fn effective_boolean_value(&self) -> error::Result<bool> {
116        match self {
117            Atomic::Boolean(b) => Ok(*b),
118            // https://www.w3.org/TR/xpath-31/#id-ebv
119            // point 4
120            Atomic::String(_, s) => Ok(!s.is_empty()),
121            Atomic::Untyped(s) => Ok(!s.is_empty()),
122            // point 5
123            Atomic::Integer(_, i) => Ok(!i.is_zero()),
124            Atomic::Decimal(d) => Ok(!d.is_zero()),
125            // NaN also counts as false
126            Atomic::Float(f) => Ok(!f.is_zero() && !f.is_nan()),
127            Atomic::Double(d) => Ok(!d.is_zero() && !d.is_nan()),
128            // point 6
129            _ => Err(error::Error::FORG0006),
130        }
131    }
132
133    // XXX is this named right? It's consistent with  to_double, to_bool, etc,
134    // but inconsistent with the to_string Rust convention
135    pub(crate) fn to_str(&self) -> error::Result<&str> {
136        match self {
137            Atomic::String(_, s) => Ok(s),
138            _ => Err(error::Error::XPTY0004),
139        }
140    }
141
142    /// Get the string if this atomic value is a xs:string
143    pub fn to_string(&self) -> error::Result<String> {
144        Ok(self.to_str()?.to_string())
145    }
146
147    /// Get the string value of the atomic value.
148    ///
149    /// This is the canonical representation of the atomic value
150    /// according to xs:schema.
151    pub(crate) fn string_value(&self) -> String {
152        self.clone().into_canonical()
153    }
154
155    /// Check whether this value is the NaN value.
156    ///  
157    /// Only xs:float and xs:double can be NaN.
158    pub fn is_nan(&self) -> bool {
159        match self {
160            Atomic::Float(f) => f.is_nan(),
161            Atomic::Double(d) => d.is_nan(),
162            _ => false,
163        }
164    }
165
166    /// Check whether this value is infinite.
167    ///
168    /// Only xs:float and xs:double can be infinite.
169    pub fn is_infinite(&self) -> bool {
170        match self {
171            Atomic::Float(f) => f.is_infinite(),
172            Atomic::Double(d) => d.is_infinite(),
173            _ => false,
174        }
175    }
176
177    /// Check whether this value is zero.
178    ///
179    /// Only numeric types can be zero.
180    pub fn is_zero(&self) -> bool {
181        match self {
182            Atomic::Float(f) => f.is_zero(),
183            Atomic::Double(d) => d.is_zero(),
184            Atomic::Decimal(d) => d.is_zero(),
185            Atomic::Integer(_, i) => i.is_zero(),
186            _ => false,
187        }
188    }
189
190    /// Check whether this is a numeric value.
191    ///
192    /// That is, xs:float, xs:double, xs:decimal, xs:integer and any
193    /// types derived from xs:integer such as xs:int, xs:long, etc.
194    pub fn is_numeric(&self) -> bool {
195        matches!(
196            self,
197            Atomic::Float(_) | Atomic::Double(_) | Atomic::Decimal(_) | Atomic::Integer(_, _)
198        )
199    }
200
201    pub(crate) fn is_addable(&self) -> bool {
202        matches!(
203            self,
204            Atomic::Float(_)
205                | Atomic::Double(_)
206                | Atomic::Decimal(_)
207                | Atomic::Integer(_, _)
208                | Atomic::DayTimeDuration(_)
209                | Atomic::YearMonthDuration(_)
210        )
211    }
212
213    pub(crate) fn is_comparable(&self) -> bool {
214        matches!(
215            self,
216            Atomic::String(_, _)
217                | Atomic::Float(_)
218                | Atomic::Double(_)
219                | Atomic::Decimal(_)
220                | Atomic::Integer(_, _)
221                | Atomic::YearMonthDuration(_)
222                | Atomic::DayTimeDuration(_)
223                | Atomic::DateTime(_)
224                | Atomic::DateTimeStamp(_)
225                | Atomic::Time(_)
226                | Atomic::Date(_)
227                | Atomic::Boolean(_)
228                | Atomic::Binary(_, _)
229        )
230    }
231
232    pub(crate) fn is_true(&self) -> bool {
233        if let Atomic::Boolean(b) = self {
234            *b
235        } else {
236            false
237        }
238    }
239
240    pub(crate) fn is_untyped(&self) -> bool {
241        matches!(self, Atomic::Untyped(_))
242    }
243
244    pub(crate) fn schema_type(&self) -> Xs {
245        match self {
246            Atomic::String(string_type, _) => string_type.schema_type(),
247            Atomic::Untyped(_) => Xs::UntypedAtomic,
248            Atomic::Boolean(_) => Xs::Boolean,
249            Atomic::Decimal(_) => Xs::Decimal,
250            Atomic::Integer(integer_type, _) => integer_type.schema_type(),
251            Atomic::Float(_) => Xs::Float,
252            Atomic::Double(_) => Xs::Double,
253            Atomic::QName(_) => Xs::QName,
254            Atomic::Binary(binary_type, _) => binary_type.schema_type(),
255            Atomic::Duration(_) => Xs::Duration,
256            Atomic::YearMonthDuration(_) => Xs::YearMonthDuration,
257            Atomic::DayTimeDuration(_) => Xs::DayTimeDuration,
258            Atomic::Time(_) => Xs::Time,
259            Atomic::Date(_) => Xs::Date,
260            Atomic::DateTime(_) => Xs::DateTime,
261            Atomic::DateTimeStamp(_) => Xs::DateTimeStamp,
262            Atomic::GYearMonth(_) => Xs::GYearMonth,
263            Atomic::GYear(_) => Xs::GYear,
264            Atomic::GMonthDay(_) => Xs::GMonthDay,
265            Atomic::GMonth(_) => Xs::GMonth,
266            Atomic::GDay(_) => Xs::GDay,
267        }
268    }
269
270    pub(crate) fn ensure_base_schema_type(&self, xs: Xs) -> error::Result<()> {
271        if self.schema_type().derives_from(xs) {
272            Ok(())
273        } else {
274            Err(error::Error::XPTY0004)
275        }
276    }
277
278    pub(crate) fn derives_from(&self, other: &Atomic) -> bool {
279        self.schema_type().derives_from(other.schema_type())
280    }
281
282    pub(crate) fn has_same_schema_type(&self, other: &Atomic) -> bool {
283        self.schema_type() == other.schema_type()
284    }
285
286    pub(crate) fn plus(self) -> error::Result<Atomic> {
287        op_unary::unary_plus(self)
288    }
289
290    pub(crate) fn minus(self) -> error::Result<Atomic> {
291        op_unary::unary_minus(self)
292    }
293
294    /// Compare atoms using XPath rules.
295    ///
296    /// This means for instance that an integer can compare the same as a
297    /// decimal. This is different from the Eq implemented for the atom itself,
298    /// which compares the actual data, and different types are always distinct
299    /// in that case.
300    ///
301    /// Simple equal uses a comparison with the codepoint collation, and UTC as
302    /// the timezone.
303    pub fn simple_equal(&self, other: &Atomic) -> bool {
304        self.equal(other, &Collation::CodePoint, chrono::offset::Utc.fix())
305    }
306
307    /// Compare atoms using XPath rules, with explicit collation and offset.
308    pub fn equal(
309        &self,
310        other: &Atomic,
311        collation: &Collation,
312        default_offset: chrono::FixedOffset,
313    ) -> bool {
314        // TODO: clone is annoying
315        let equal = OpEq::atomic_compare(
316            self.clone(),
317            other.clone(),
318            |a, b| collation.compare(a, b),
319            default_offset,
320        );
321        equal.unwrap_or_default()
322    }
323
324    /// Deep-equal comparison.
325    ///
326    /// This is like equal, but NaN compare equal as well
327    pub(crate) fn deep_equal(
328        &self,
329        other: &Atomic,
330        collation: &Collation,
331        default_offset: chrono::FixedOffset,
332    ) -> bool {
333        if self.is_nan() && other.is_nan() {
334            return true;
335        }
336        self.equal(other, collation, default_offset)
337    }
338
339    pub(crate) fn fallible_compare(
340        &self,
341        other: &Atomic,
342        collation: &Collation,
343        default_offset: chrono::FixedOffset,
344    ) -> error::Result<Ordering> {
345        if !self.is_comparable() || !other.is_comparable() {
346            return Err(error::Error::XPTY0004);
347        }
348        let is_equal = OpEq::atomic_compare(
349            self.clone(),
350            other.clone(),
351            |a, b| collation.compare(a, b),
352            default_offset,
353        )?;
354
355        if is_equal {
356            Ok(Ordering::Equal)
357        } else {
358            let is_greater = OpGt::atomic_compare(
359                self.clone(),
360                other.clone(),
361                |a, b| collation.compare(a, b),
362                default_offset,
363            )?;
364            if is_greater {
365                Ok(Ordering::Greater)
366            } else {
367                Ok(Ordering::Less)
368            }
369        }
370    }
371
372    /// This function is intended to be used by sort_by_key
373    /// Since comparison is fallible, we sort all error cases as
374    /// less than all non-error cases, and then we detect them later.
375    /// This requires an additional pass to determine that for each pair a, b
376    /// comparison doesn't fail.
377    pub(crate) fn compare(
378        &self,
379        other: &Atomic,
380        collation: &Collation,
381        default_offset: chrono::FixedOffset,
382    ) -> Ordering {
383        self.fallible_compare(other, collation, default_offset)
384            .unwrap_or(Ordering::Less)
385    }
386}
387
388impl fmt::Display for Atomic {
389    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390        write!(
391            f,
392            "{:?} {}",
393            self.schema_type(),
394            self.clone().into_canonical()
395        )
396    }
397}
398
399// strings
400
401impl From<String> for Atomic {
402    fn from(s: String) -> Self {
403        Atomic::String(StringType::String, s.into())
404    }
405}
406
407impl From<&str> for Atomic {
408    fn from(s: &str) -> Self {
409        Atomic::String(StringType::String, s.into())
410    }
411}
412
413impl From<&String> for Atomic {
414    fn from(s: &String) -> Self {
415        Atomic::String(StringType::String, s.clone().into())
416    }
417}
418
419impl TryFrom<Atomic> for String {
420    type Error = error::Error;
421
422    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
423        match a {
424            Atomic::String(_, s) => Ok(s.to_string()),
425            _ => Err(error::Error::XPTY0004),
426        }
427    }
428}
429
430// bool
431
432impl From<bool> for Atomic {
433    fn from(b: bool) -> Self {
434        Atomic::Boolean(b)
435    }
436}
437
438impl TryFrom<Atomic> for bool {
439    type Error = error::Error;
440
441    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
442        match a {
443            Atomic::Boolean(b) => Ok(b),
444            _ => Err(error::Error::XPTY0004),
445        }
446    }
447}
448
449// decimal
450
451impl From<Decimal> for Atomic {
452    fn from(d: Decimal) -> Self {
453        Atomic::Decimal(d.into())
454    }
455}
456
457impl TryFrom<Atomic> for Decimal {
458    type Error = error::Error;
459
460    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
461        match a {
462            Atomic::Decimal(d) => Ok(*d.as_ref()),
463            _ => Err(error::Error::XPTY0004),
464        }
465    }
466}
467
468// URL
469
470impl From<IriString> for Atomic {
471    fn from(u: IriString) -> Self {
472        Atomic::String(StringType::AnyURI, u.to_string().into())
473    }
474}
475
476impl From<IriReferenceString> for Atomic {
477    fn from(u: IriReferenceString) -> Self {
478        Atomic::String(StringType::AnyURI, u.to_string().into())
479    }
480}
481
482impl From<&IriReferenceStr> for Atomic {
483    fn from(u: &IriReferenceStr) -> Self {
484        Atomic::String(StringType::AnyURI, u.to_string().into())
485    }
486}
487
488impl TryFrom<Atomic> for IriReferenceString {
489    type Error = error::Error;
490
491    fn try_from(a: Atomic) -> Result<Self, error::Error> {
492        match a {
493            Atomic::String(_, s) => {
494                Ok(s.as_ref().try_into().map_err(|_| error::Error::FORG0002)?)
495            }
496            _ => Err(error::Error::XPTY0004),
497        }
498    }
499}
500
501// integers
502
503impl From<IBig> for Atomic {
504    fn from(i: IBig) -> Self {
505        Atomic::Integer(IntegerType::Integer, i.into())
506    }
507}
508
509impl From<Rc<IBig>> for Atomic {
510    fn from(i: Rc<IBig>) -> Self {
511        Atomic::Integer(IntegerType::Integer, i)
512    }
513}
514
515impl TryFrom<Atomic> for Rc<IBig> {
516    type Error = error::Error;
517
518    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
519        match a {
520            Atomic::Integer(_, i) => Ok(i),
521            _ => Err(error::Error::XPTY0004),
522        }
523    }
524}
525
526impl TryFrom<Atomic> for IBig {
527    type Error = error::Error;
528
529    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
530        match a {
531            Atomic::Integer(_, i) => Ok(i.as_ref().clone()),
532            _ => Err(error::Error::XPTY0004),
533        }
534    }
535}
536
537impl From<i64> for Atomic {
538    fn from(i: i64) -> Self {
539        let i: IBig = i.into();
540        Atomic::Integer(IntegerType::Long, i.into())
541    }
542}
543
544impl TryFrom<Atomic> for i64 {
545    type Error = error::Error;
546
547    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
548        match a {
549            Atomic::Integer(IntegerType::Long, i) => Ok(i.as_ref().clone().try_into()?),
550            _ => Err(error::Error::XPTY0004),
551        }
552    }
553}
554
555impl From<i32> for Atomic {
556    fn from(i: i32) -> Self {
557        let i: IBig = i.into();
558        Atomic::Integer(IntegerType::Int, i.into())
559    }
560}
561
562impl TryFrom<Atomic> for i32 {
563    type Error = error::Error;
564
565    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
566        match a {
567            Atomic::Integer(IntegerType::Int, i) => Ok(i.as_ref().clone().try_into()?),
568            _ => Err(error::Error::XPTY0004),
569        }
570    }
571}
572
573impl From<i16> for Atomic {
574    fn from(i: i16) -> Self {
575        let i: IBig = i.into();
576        Atomic::Integer(IntegerType::Short, i.into())
577    }
578}
579
580impl TryFrom<Atomic> for i16 {
581    type Error = error::Error;
582
583    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
584        match a {
585            Atomic::Integer(IntegerType::Short, i) => Ok(i.as_ref().clone().try_into()?),
586            _ => Err(error::Error::XPTY0004),
587        }
588    }
589}
590
591impl From<i8> for Atomic {
592    fn from(i: i8) -> Self {
593        let i: IBig = i.into();
594        Atomic::Integer(IntegerType::Byte, i.into())
595    }
596}
597
598impl TryFrom<Atomic> for i8 {
599    type Error = error::Error;
600
601    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
602        match a {
603            Atomic::Integer(IntegerType::Byte, i) => Ok(i.as_ref().clone().try_into()?),
604            _ => Err(error::Error::XPTY0004),
605        }
606    }
607}
608
609impl From<u64> for Atomic {
610    fn from(i: u64) -> Self {
611        let i: IBig = i.into();
612        Atomic::Integer(IntegerType::UnsignedLong, i.into())
613    }
614}
615
616impl TryFrom<Atomic> for u64 {
617    type Error = error::Error;
618
619    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
620        match a {
621            Atomic::Integer(IntegerType::UnsignedLong, i) => Ok(i.as_ref().clone().try_into()?),
622            _ => Err(error::Error::XPTY0004),
623        }
624    }
625}
626
627impl From<u32> for Atomic {
628    fn from(i: u32) -> Self {
629        let i: IBig = i.into();
630        Atomic::Integer(IntegerType::UnsignedInt, i.into())
631    }
632}
633
634impl TryFrom<Atomic> for u32 {
635    type Error = error::Error;
636
637    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
638        match a {
639            Atomic::Integer(IntegerType::UnsignedInt, i) => Ok(i.as_ref().clone().try_into()?),
640            _ => Err(error::Error::XPTY0004),
641        }
642    }
643}
644
645impl From<u16> for Atomic {
646    fn from(i: u16) -> Self {
647        let i: IBig = i.into();
648        Atomic::Integer(IntegerType::UnsignedShort, i.into())
649    }
650}
651
652impl TryFrom<Atomic> for u16 {
653    type Error = error::Error;
654
655    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
656        match a {
657            Atomic::Integer(IntegerType::UnsignedShort, i) => Ok(i.as_ref().clone().try_into()?),
658            _ => Err(error::Error::XPTY0004),
659        }
660    }
661}
662
663impl From<u8> for Atomic {
664    fn from(i: u8) -> Self {
665        let i: IBig = i.into();
666        Atomic::Integer(IntegerType::UnsignedByte, i.into())
667    }
668}
669
670impl TryFrom<Atomic> for u8 {
671    type Error = error::Error;
672
673    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
674        match a {
675            Atomic::Integer(IntegerType::UnsignedByte, i) => Ok(i.as_ref().clone().try_into()?),
676            _ => Err(error::Error::XPTY0004),
677        }
678    }
679}
680
681// floats
682
683impl From<f32> for Atomic {
684    fn from(f: f32) -> Self {
685        Atomic::Float(OrderedFloat(f))
686    }
687}
688
689impl From<OrderedFloat<f32>> for Atomic {
690    fn from(f: OrderedFloat<f32>) -> Self {
691        Atomic::Float(f)
692    }
693}
694
695impl TryFrom<Atomic> for f32 {
696    type Error = error::Error;
697
698    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
699        match a {
700            Atomic::Float(f) => Ok(f.into_inner()),
701            // type promotion
702            Atomic::Decimal(_) | Atomic::Integer(_, _) => {
703                let f: f32 = a.cast_to_float()?.try_into()?;
704                Ok(f)
705            }
706            _ => Err(error::Error::XPTY0004),
707        }
708    }
709}
710
711impl From<f64> for Atomic {
712    fn from(f: f64) -> Self {
713        Atomic::Double(OrderedFloat(f))
714    }
715}
716
717impl From<OrderedFloat<f64>> for Atomic {
718    fn from(f: OrderedFloat<f64>) -> Self {
719        Atomic::Double(f)
720    }
721}
722
723impl TryFrom<Atomic> for f64 {
724    type Error = error::Error;
725
726    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
727        match a {
728            Atomic::Double(f) => Ok(f.into_inner()),
729            // type promotion
730            Atomic::Float(f) => Ok(f.into_inner() as f64),
731            Atomic::Decimal(_) | Atomic::Integer(_, _) => {
732                let f: f64 = a.cast_to_double()?.try_into()?;
733                Ok(f)
734            }
735            _ => Err(error::Error::XPTY0004),
736        }
737    }
738}
739
740impl From<Name> for Atomic {
741    fn from(n: Name) -> Self {
742        Atomic::QName(n.into())
743    }
744}
745
746impl TryFrom<Atomic> for Name {
747    type Error = error::Error;
748
749    fn try_from(a: Atomic) -> Result<Self, Self::Error> {
750        match a {
751            Atomic::QName(n) => Ok(n.as_ref().clone()),
752            _ => Err(error::Error::XPTY0004),
753        }
754    }
755}