hugr_model/v0/
mod.rs

1//! Version 0 (unstable).
2//!
3//! **Warning**: This module is still under development and is expected to change.
4//! It is included in the library to allow for early experimentation, and for
5//! the core and model to converge incrementally.
6//!
7//! This module defines representations of the hugr IR as plain data, designed
8//! to be as independent of implementation details as feasible. It can be used
9//! by the core compiler, alternative implementations or tooling that does not
10//! need the power/complexity of the full compiler. We provide the following
11//! in-memory representations:
12//!
13//! - [Table]: Efficient intermediate data structure to facilitate conversions.
14//! - [AST]: Abstract syntax tree that uses direct references rather than table indices.
15//!
16//! The table and AST format are interconvertible and can be serialised to
17//! a binary and text format, respectively:
18//!
19//! - [Binary]: Binary serialisation format optimised for performance and size.
20//! - [Text]: Human readable s-expression based text format.
21//!
22//! # Logical Format
23//!
24//! The hugr IR is a hierarchical graph data structure. __Nodes__ represent both
25//! __instructions__ that manipulate runtime values and __symbols__ which
26//! represent named language objects. Instructions have __input__ and __output__ ports
27//! and runtime values flow between ports when they are connected by a __link__.
28//!
29//! Nodes are organised into __regions__ and do not have any explicit ordering
30//! between them; any schedule that respects the data dependencies between nodes
31//! is valid. Regions come in three different kinds. __Module regions__ form the
32//! top level of a module and can only contain symbols. __Dataflow regions__
33//! describe how data flows from the region's __source__ ports to the region's
34//! __target__ ports. __Controlflow regions__ are control flow graphs containing
35//! dataflow __blocks__, with control flow originating from the region's source
36//! ports and ending in the region's target ports.
37//!
38//! __Terms__ form a meta language that is used to describe types, parameters and metadata that
39//! are known statically. To allow types to be parameterized by values, types and values
40//! are treated uniformly as terms, enabling a restricted form of dependent typing.
41//! Terms are extensible declaratively via __constructors__.
42//! __Constraints__ can be used to express more complex validation rules.
43//!
44//! # Remaining Mismatch with `hugr-core`
45//!
46//! This data model was designed to encode as much of `hugr-core` as possible while also
47//! filling in conceptual gaps and providing a forward-compatible foundation for future
48//! development. However, there are still some mismatches with `hugr-core` that are not
49//! addressed by conversions in import/export:
50//!
51//! - Some static types can not yet be represented in `hugr-core` although they should be.
52//! - The model does not have types with a copy bound as `hugr-core` does, and instead uses
53//!   a more general form of type constraints ([#1556]). Similarly, the model does not have
54//!   bounded naturals. We perform a conversion for compatibility where possible, but this does
55//!   not fully cover all potential cases of bounds.
56//! - `hugr-model` allows to declare term constructors that serve as blueprints for constructing
57//!   runtime values. This allows constants to have potentially multiple representations,
58//!   which can be essential in case of very large constants that require efficient encodings.
59//!   `hugr-core` is more restricted, requiring a canonical representation for constant values.
60//! - `hugr-model` has support for passing closed regions as static parameters to operations,
61//!   which allows for higher-order operations that require their function arguments to be
62//!   statically known. We currently do not yet support converting this to `hugr-core`.
63//! - In a model module, ports are connected when they share the same link. This differs from
64//!   the type of port connectivity in the graph data structure used by `hugr-core`. However,
65//!   `hugr-core` restricts connectivity so that in any group of connected ports there is at
66//!   most one output port (for dataflow) or at most one input port (for control flow). In
67//!   these cases, there is no mismatch.
68//! - `hugr-core` only allows to define type aliases, but not aliases for other terms.
69//! - `hugr-model` has no concept of order edges, encoding a strong preference that ordering
70//!   requirements be encoded within the dataflow paradigm.
71//! - Both `hugr-model` and `hugr-core` support metadata, but they use different encodings.
72//!   `hugr-core` encodes metadata as JSON objects, while `hugr-model` uses terms. Using
73//!   terms has the advantage that metadata can be validated with the same type checking
74//!   mechanism as the rest of the model ([#1553]).
75//! - `hugr-model` have a root region that corresponds to a root `Module` in `hugr-core`.
76//!   `hugr-core` however can have nodes with different operations as their root ([#1554]).
77//!
78//! [#1556]: https://github.com/CQCL/hugr/discussions/1556
79//! [#1553]: https://github.com/CQCL/hugr/issues/1553
80//! [#1554]: https://github.com/CQCL/hugr/issues/1554
81//! [Text]: crate::v0::ast
82//! [Binary]: crate::v0::binary
83//! [Table]: crate::v0::table
84//! [AST]: crate::v0::ast
85use ordered_float::OrderedFloat;
86#[cfg(feature = "pyo3")]
87use pyo3::PyTypeInfo as _;
88#[cfg(feature = "pyo3")]
89use pyo3::types::PyAnyMethods as _;
90use smol_str::SmolStr;
91use std::sync::Arc;
92use table::LinkIndex;
93
94/// Describes how a function or symbol should be acted upon by a linker
95#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
96pub enum Visibility {
97    /// The linker should ignore this function or symbol
98    Private,
99    /// The linker should act upon this function or symbol
100    Public,
101}
102
103/// Core function types.
104///
105/// - **Parameter:** `?inputs : (core.list core.type)`
106/// - **Parameter:** `?outputs : (core.list core.type)`
107/// - **Result:** `core.type`
108pub const CORE_FN: &str = "core.fn";
109
110/// The type of runtime types.
111///
112/// Runtime types are the types of values that can flow between nodes at runtime.
113///
114/// - **Result:** `?type : core.static`
115pub const CORE_TYPE: &str = "core.type";
116
117/// The type of static types.
118///
119/// Static types are the types of statically known parameters.
120///
121/// This is the only term that is its own type.
122///
123/// - **Result:** `?type : core.static`
124pub const CORE_STATIC: &str = "core.static";
125
126/// The type of constraints.
127///
128/// - **Result:** `?type : core.static`
129pub const CORE_CONSTRAINT: &str = "core.constraint";
130
131/// The constraint for non-linear runtime data.
132///
133/// Runtime values are copied implicitly by connecting an output port to more
134/// than one input port. Similarly runtime values can be deleted implicitly when
135/// an output port is not connected to any input port. In either of these cases
136/// the type of the runtime value must satisfy this constraint.
137///
138/// - **Parameter:** `?type : core.type`
139/// - **Result:** `core.constraint`
140pub const CORE_NON_LINEAR: &str = "core.nonlinear";
141
142/// The type of metadata.
143///
144/// - **Result:** `?type : core.static`
145pub const CORE_META: &str = "core.meta";
146
147/// Runtime algebraic data types.
148///
149/// Algebraic data types are sums of products of other runtime types.
150///
151/// - **Parameter:** `?variants : (core.list (core.list core.type))`
152/// - **Result:** `core.type`
153pub const CORE_ADT: &str = "core.adt";
154
155/// Type of string literals.
156///
157/// - **Result:** `core.static`
158pub const CORE_STR_TYPE: &str = "core.str";
159
160/// Type of natural number literals.
161///
162/// - **Result:** `core.static`
163pub const CORE_NAT_TYPE: &str = "core.nat";
164
165/// Type of bytes literals.
166///
167/// - **Result:** `core.static`
168pub const CORE_BYTES_TYPE: &str = "core.bytes";
169
170/// Type of float literals.
171///
172/// - **Result:** `core.static`
173pub const CORE_FLOAT_TYPE: &str = "core.float";
174
175/// Type of control flow regions.
176///
177/// - **Parameter:** `?inputs : (core.list (core.list core.type))`
178/// - **Parameter:** `?outputs : (core.list (core.list core.type))`
179/// - **Result:** `core.type`
180pub const CORE_CTRL: &str = "core.ctrl";
181
182/// The type for runtime constants.
183///
184/// - **Parameter:** `?type : core.type`
185/// - **Result:** `core.static`
186pub const CORE_CONST: &str = "core.const";
187
188/// Constants for runtime algebraic data types.
189///
190/// - **Parameter:** `?variants : (core.list core.type)`
191/// - **Parameter:** `?types : (core.list core.static)`
192/// - **Parameter:** `?tag : core.nat`
193/// - **Parameter:** `?values : (core.tuple ?types)`
194/// - **Result:** `(core.const (core.adt ?variants))`
195pub const CORE_CONST_ADT: &str = "core.const.adt";
196
197/// The type for lists of static data.
198///
199/// Lists are finite sequences such that all elements have the same type.
200/// For heterogeneous sequences, see [`CORE_TUPLE_TYPE`].
201///
202/// - **Parameter:** `?type : core.static`
203/// - **Result:** `core.static`
204pub const CORE_LIST_TYPE: &str = "core.list";
205
206/// The type for tuples of static data.
207///
208/// Tuples are finite sequences that allow elements to have different types.
209/// For homogeneous sequences, see [`CORE_LIST_TYPE`].
210///
211/// - **Parameter:** `?types : (core.list core.static)`
212/// - **Result:** `core.static`
213pub const CORE_TUPLE_TYPE: &str = "core.tuple";
214
215/// Operation to call a statically known function.
216///
217/// - **Parameter:** `?inputs : (core.list core.type)`
218/// - **Parameter:** `?outputs : (core.list core.type)`
219/// - **Parameter:** `?func : (core.const (core.fn ?inputs ?outputs))`
220/// - **Result:** `(core.fn ?inputs ?outputs ?ext)`
221pub const CORE_CALL: &str = "core.call";
222
223/// Operation to call a functiion known at runtime.
224///
225/// - **Parameter:** `?inputs : (core.list core.type)`
226/// - **Parameter:** `?outputs : (core.list core.type)`
227/// - **Result:** `(core.fn [(core.fn ?inputs ?outputs) ?inputs ...] ?outputs)`
228pub const CORE_CALL_INDIRECT: &str = "core.call_indirect";
229
230/// Operation to load a constant value.
231///
232/// - **Parameter:** `?type : core.type`
233/// - **Parameter:** `?value : (core.const ?type)`
234/// - **Result:** `(core.fn [] [?type])`
235pub const CORE_LOAD_CONST: &str = "core.load_const";
236
237/// Operation to create a value of an algebraic data type.
238///
239/// - **Parameter:** `?variants : (core.list (core.list core.type))`
240/// - **Parameter:** `?types : (core.list core.type)`
241/// - **Parameter:** `?tag : core.nat`
242/// - **Result:** `(core.fn ?types [(core.adt ?variants)])`
243pub const CORE_MAKE_ADT: &str = "core.make_adt";
244
245/// Constructor for documentation metadata.
246///
247/// - **Parameter:** `?description : core.str`
248/// - **Result:** `core.meta`
249pub const CORE_META_DESCRIPTION: &str = "core.meta.description";
250
251/// Metadata to tag a node or region as the entrypoint of a module.
252///
253/// - **Result:** `core.meta`
254pub const CORE_ENTRYPOINT: &str = "core.entrypoint";
255
256/// Constructor for JSON encoded metadata.
257///
258/// This is included in the model to allow for compatibility with `hugr-core`.
259/// The intention is to deprecate this in the future in favor of metadata
260/// expressed with custom constructors.
261///
262/// - **Parameter:** `?name : core.str`
263/// - **Parameter:** `?json : core.str`
264/// - **Result:** `core.meta`
265pub const COMPAT_META_JSON: &str = "compat.meta_json";
266
267/// Constructor for JSON encoded constants.
268///
269/// This is included in the model to allow for compatibility with `hugr-core`.
270/// The intention is to deprecate this in the future in favor of constants
271/// expressed with custom constructors.
272///
273/// - **Parameter:** `?type : core.type`
274/// - **Parameter:** `?json : core.str`
275/// - **Result:** `(core.const ?type)`
276pub const COMPAT_CONST_JSON: &str = "compat.const_json";
277
278/// Metadata constructor for order hint keys.
279///
280/// Nodes in a dataflow region can be annotated with a key. Each node may have
281/// at most one key and the key must be unique among all nodes in the same
282/// dataflow region. The parent dataflow graph can then use the
283/// `order_hint.order` metadata to imply a desired ordering relation, referring
284/// to the nodes by their key.
285///
286/// - **Parameter:** `?key : core.nat`
287/// - **Result:** `core.meta`
288pub const ORDER_HINT_KEY: &str = "core.order_hint.key";
289
290/// Metadata constructor for order hint keys on input nodes.
291///
292/// When the sources of a dataflow region are represented by an input operation
293/// within the region, this metadata can be attached the region to give the
294/// input node an order hint key.
295///
296/// - **Parameter:** `?key : core.nat`
297/// - **Result:** `core.meta`
298pub const ORDER_HINT_INPUT_KEY: &str = "core.order_hint.input_key";
299
300/// Metadata constructor for order hint keys on output nodes.
301///
302/// When the targets of a dataflow region are represented by an output operation
303/// within the region, this metadata can be attached the region to give the
304/// output node an order hint key.
305///
306/// - **Parameter:** `?key : core.nat`
307/// - **Result:** `core.meta`
308pub const ORDER_HINT_OUTPUT_KEY: &str = "core.order_hint.output_key";
309
310/// Metadata constructor for order hints.
311///
312/// When this metadata is attached to a dataflow region, it can indicate a
313/// preferred ordering relation between child nodes. Code generation must take
314/// this into account when deciding on an execution order. The child nodes are
315/// identified by a key, using the `order_hint.key` metadata.
316///
317/// The graph consisting of both value dependencies between nodes and order
318/// hints must be directed acyclic.
319///
320/// - **Parameter:** `?before : core.nat`
321/// - **Parameter:** `?after : core.nat`
322/// - **Result:** `core.meta`
323pub const ORDER_HINT_ORDER: &str = "core.order_hint.order";
324
325pub mod ast;
326pub mod binary;
327pub mod scope;
328pub mod table;
329
330pub use bumpalo;
331
332/// Type to indicate whether scopes are open or closed.
333#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
334pub enum ScopeClosure {
335    /// A scope that is open and therefore not isolated from its parent scope.
336    #[default]
337    Open,
338    /// A scope that is closed and therefore isolated from its parent scope.
339    Closed,
340}
341
342#[cfg(feature = "pyo3")]
343impl<'py> pyo3::FromPyObject<'py> for ScopeClosure {
344    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
345        let value: usize = ob.getattr("value")?.extract()?;
346        match value {
347            0 => Ok(Self::Open),
348            1 => Ok(Self::Closed),
349            _ => Err(pyo3::exceptions::PyTypeError::new_err(
350                "Invalid ScopeClosure.",
351            )),
352        }
353    }
354}
355
356#[cfg(feature = "pyo3")]
357impl<'py> pyo3::IntoPyObject<'py> for ScopeClosure {
358    type Target = pyo3::PyAny;
359    type Output = pyo3::Bound<'py, Self::Target>;
360    type Error = pyo3::PyErr;
361
362    fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
363        let py_module = py.import("hugr.model")?;
364        let py_class = py_module.getattr("ScopeClosure")?;
365
366        match self {
367            ScopeClosure::Open => py_class.getattr("OPEN"),
368            ScopeClosure::Closed => py_class.getattr("CLOSED"),
369        }
370    }
371}
372
373/// The kind of a region.
374#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
375pub enum RegionKind {
376    /// Data flow region.
377    #[default]
378    DataFlow = 0,
379    /// Control flow region.
380    ControlFlow = 1,
381    /// Module region.
382    Module = 2,
383}
384
385#[cfg(feature = "pyo3")]
386impl<'py> pyo3::FromPyObject<'py> for RegionKind {
387    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
388        let value: usize = ob.getattr("value")?.extract()?;
389        match value {
390            0 => Ok(Self::DataFlow),
391            1 => Ok(Self::ControlFlow),
392            2 => Ok(Self::Module),
393            _ => Err(pyo3::exceptions::PyTypeError::new_err(
394                "Invalid RegionKind.",
395            )),
396        }
397    }
398}
399
400#[cfg(feature = "pyo3")]
401impl<'py> pyo3::IntoPyObject<'py> for RegionKind {
402    type Target = pyo3::PyAny;
403    type Output = pyo3::Bound<'py, Self::Target>;
404    type Error = pyo3::PyErr;
405
406    fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
407        let py_module = py.import("hugr.model")?;
408        let py_class = py_module.getattr("RegionKind")?;
409
410        match self {
411            RegionKind::DataFlow => py_class.getattr("DATA_FLOW"),
412            RegionKind::ControlFlow => py_class.getattr("CONTROL_FLOW"),
413            RegionKind::Module => py_class.getattr("MODULE"),
414        }
415    }
416}
417
418/// The name of a variable.
419#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
420pub struct VarName(SmolStr);
421
422impl VarName {
423    /// Create a new variable name.
424    pub fn new(name: impl Into<SmolStr>) -> Self {
425        Self(name.into())
426    }
427}
428
429impl AsRef<str> for VarName {
430    fn as_ref(&self) -> &str {
431        self.0.as_ref()
432    }
433}
434
435#[cfg(feature = "pyo3")]
436impl<'py> pyo3::FromPyObject<'py> for VarName {
437    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
438        let name: String = ob.extract()?;
439        Ok(Self::new(name))
440    }
441}
442
443#[cfg(feature = "pyo3")]
444impl<'py> pyo3::IntoPyObject<'py> for &VarName {
445    type Target = pyo3::types::PyString;
446    type Output = pyo3::Bound<'py, Self::Target>;
447    type Error = pyo3::PyErr;
448
449    fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
450        Ok(self.as_ref().into_pyobject(py)?)
451    }
452}
453
454/// The name of a symbol.
455#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
456pub struct SymbolName(SmolStr);
457
458impl SymbolName {
459    /// Create a new symbol name.
460    pub fn new(name: impl Into<SmolStr>) -> Self {
461        Self(name.into())
462    }
463}
464
465impl AsRef<str> for SymbolName {
466    fn as_ref(&self) -> &str {
467        self.0.as_ref()
468    }
469}
470
471#[cfg(feature = "pyo3")]
472impl<'py> pyo3::FromPyObject<'py> for SymbolName {
473    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
474        let name: String = ob.extract()?;
475        Ok(Self::new(name))
476    }
477}
478
479/// The name of a link.
480#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
481pub struct LinkName(SmolStr);
482
483impl LinkName {
484    /// Create a new link name.
485    pub fn new(name: impl Into<SmolStr>) -> Self {
486        Self(name.into())
487    }
488
489    /// Create a new link name from a link index.
490    #[must_use]
491    pub fn new_index(index: LinkIndex) -> Self {
492        // TODO: Should named and numbered links have different namespaces?
493        Self(format!("{index}").into())
494    }
495}
496
497impl AsRef<str> for LinkName {
498    fn as_ref(&self) -> &str {
499        self.0.as_ref()
500    }
501}
502
503#[cfg(feature = "pyo3")]
504impl<'py> pyo3::FromPyObject<'py> for LinkName {
505    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
506        let name: String = ob.extract()?;
507        Ok(Self::new(name))
508    }
509}
510
511#[cfg(feature = "pyo3")]
512impl<'py> pyo3::IntoPyObject<'py> for &LinkName {
513    type Target = pyo3::types::PyString;
514    type Output = pyo3::Bound<'py, Self::Target>;
515    type Error = pyo3::PyErr;
516
517    fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
518        Ok(self.as_ref().into_pyobject(py)?)
519    }
520}
521
522/// A static literal value.
523///
524/// Literal values may be large since they can include strings and byte
525/// sequences of arbitrary length. To enable cheap cloning and sharing,
526/// strings and byte sequences use reference counting.
527#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
528pub enum Literal {
529    /// String literal.
530    Str(SmolStr),
531    /// Natural number literal (unsigned 64 bit).
532    Nat(u64),
533    /// Byte sequence literal.
534    Bytes(Arc<[u8]>),
535    /// Floating point literal
536    Float(OrderedFloat<f64>),
537}
538
539#[cfg(feature = "pyo3")]
540impl<'py> pyo3::FromPyObject<'py> for Literal {
541    fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
542        if pyo3::types::PyString::is_type_of(ob) {
543            let value: String = ob.extract()?;
544            Ok(Literal::Str(value.into()))
545        } else if pyo3::types::PyInt::is_type_of(ob) {
546            let value: u64 = ob.extract()?;
547            Ok(Literal::Nat(value))
548        } else if pyo3::types::PyFloat::is_type_of(ob) {
549            let value: f64 = ob.extract()?;
550            Ok(Literal::Float(value.into()))
551        } else if pyo3::types::PyBytes::is_type_of(ob) {
552            let value: Vec<u8> = ob.extract()?;
553            Ok(Literal::Bytes(value.into()))
554        } else {
555            Err(pyo3::exceptions::PyTypeError::new_err(
556                "Invalid literal value.",
557            ))
558        }
559    }
560}
561
562#[cfg(feature = "pyo3")]
563impl<'py> pyo3::IntoPyObject<'py> for &Literal {
564    type Target = pyo3::PyAny;
565    type Output = pyo3::Bound<'py, Self::Target>;
566    type Error = pyo3::PyErr;
567
568    fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
569        Ok(match self {
570            Literal::Str(s) => s.as_str().into_pyobject(py)?.into_any(),
571            Literal::Nat(n) => n.into_pyobject(py)?.into_any(),
572            Literal::Bytes(b) => pyo3::types::PyBytes::new(py, b)
573                .into_pyobject(py)?
574                .into_any(),
575            Literal::Float(f) => f.0.into_pyobject(py)?.into_any(),
576        })
577    }
578}
579
580#[cfg(test)]
581mod test {
582    use super::*;
583    use proptest::{prelude::*, string::string_regex};
584
585    impl Arbitrary for Literal {
586        type Parameters = ();
587        type Strategy = BoxedStrategy<Self>;
588
589        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
590            prop_oneof![
591                any::<String>().prop_map(|s| Literal::Str(s.into())),
592                any::<u64>().prop_map(Literal::Nat),
593                prop::collection::vec(any::<u8>(), 0..100).prop_map(|v| Literal::Bytes(v.into())),
594                any::<f64>().prop_map(|f| Literal::Float(OrderedFloat(f)))
595            ]
596            .boxed()
597        }
598    }
599
600    impl Arbitrary for SymbolName {
601        type Parameters = ();
602        type Strategy = BoxedStrategy<Self>;
603
604        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
605            string_regex(r"[a-zA-Z\-_][0-9a-zA-Z\-_](\.[a-zA-Z\-_][0-9a-zA-Z\-_])*")
606                .unwrap()
607                .prop_map(Self::new)
608                .boxed()
609        }
610    }
611
612    impl Arbitrary for VarName {
613        type Parameters = ();
614        type Strategy = BoxedStrategy<Self>;
615
616        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
617            string_regex(r"[a-zA-Z\-_][0-9a-zA-Z\-_]")
618                .unwrap()
619                .prop_map(Self::new)
620                .boxed()
621        }
622    }
623
624    proptest! {
625        #[test]
626        fn test_literal_text(lit: Literal) {
627            let text = lit.to_string();
628            let parsed: Literal = text.parse().unwrap();
629            assert_eq!(lit, parsed);
630        }
631    }
632}