diff --git a/Cargo.toml b/Cargo.toml index b7449a6a..e934313f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,13 +17,16 @@ exclude = [ default = ["std", "spill-stack"] # Integrate with the standard library. -std = [] +std = [ + "regex-automata?/std", + "serde?/std" +] # Enable nightly-only features like better compiler diagnostics and a Parser impl for ! (the never type). nightly = [] # Allows deeper recursion by dynamically spilling stack state on to the heap. -spill-stack = ["stacker", "std"] +spill-stack = ["dep:stacker", "std"] # Allows parser memoization, speeding up heavily back-tracking parsers and allowing left recursion. memoization = [] @@ -52,6 +55,9 @@ either = ["dep:either"] # Enables regex combinators regex = ["dep:regex-automata"] +# Enable serde serialization support +serde = ["dep:serde"] + # An alias of all features that work with the stable compiler. # Do not use this feature, its removal is not considered a breaking change and its behaviour may change. # If you're working on chumsky and you're adding a feature that does not require nightly support, please add it to this list. @@ -64,10 +70,11 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] hashbrown = "0.14" stacker = { version = "0.1", optional = true } -regex-automata = { version = "0.3", optional = true } +regex-automata = { version = "0.3", default-features = false, optional = true, features = ["alloc", "meta", "perf", "unicode", "nfa", "dfa", "hybrid"] } spin = { version = "0.9", features = ["once"], default-features = false, optional = true } lexical = { version = "6.1.1", default-features = false, features = ["parse-integers", "parse-floats", "format"], optional = true } either = { version = "1.8.1", optional = true } +serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } unicode-ident = "1.0.10" [dev-dependencies] @@ -119,3 +126,7 @@ required-features = ["label"] [[example]] name = "json" required-features = ["std"] + +[[example]] +name = "io" +required-features = ["std"] diff --git a/src/error.rs b/src/error.rs index 170cac9d..b08584c3 100644 --- a/src/error.rs +++ b/src/error.rs @@ -110,6 +110,7 @@ pub trait Error<'a, I: Input<'a>>: Sized { /// A ZST error type that tracks only whether a parse error occurred at all. This type is for when /// you want maximum parse speed, at the cost of all error reporting. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone, Default)] pub struct EmptyErr(()); @@ -132,6 +133,7 @@ impl fmt::Display for EmptyErr { /// A very cheap error type that tracks only the error span. This type is most useful when you want fast parsing but do /// not particularly care about the quality of error messages. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Cheap> { span: S, @@ -169,6 +171,7 @@ where /// A simple error type that tracks the error span and found token. This type is most useful when you want fast parsing /// but do not particularly care about the quality of error messages. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Simple<'a, T, S = SimpleSpan> { span: S, @@ -226,6 +229,7 @@ where } /// An expected pattern for a [`Rich`] error. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum RichPattern<'a, T, L = &'static str> { /// A specific token was expected. @@ -312,6 +316,7 @@ where // TODO: Maybe should make ExpectedFound encapsulated a bit more /// The reason for a [`Rich`] error. +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub enum RichReason<'a, T, L = &'static str> { /// An unexpected input was found diff --git a/src/extension.rs b/src/extension.rs index f6fed2be..07e82677 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -140,6 +140,7 @@ mod current { /// /// If you're writing an extension crate for chumsky, you can make things less confusing for your users by putting your /// parser behind a type alias. + #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct Ext(pub T); diff --git a/src/lib.rs b/src/lib.rs index 8e11f3d0..08ce42fd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ // TODO: Talk about `.map` and purity assumptions extern crate alloc; +extern crate core; macro_rules! go_extra { ( $O :ty ) => { @@ -117,6 +118,8 @@ use core::{ str::FromStr, }; use hashbrown::HashMap; +#[cfg(feature = "serde")] +use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "label")] use self::label::{LabelError, Labelled}; diff --git a/src/span.rs b/src/span.rs index 59e05a13..dfa6cdb1 100644 --- a/src/span.rs +++ b/src/span.rs @@ -47,7 +47,8 @@ pub trait Span { /// The most basic implementor of `Span` - akin to `Range`, but `Copy` since it's not also /// an iterator. Also has a `Display` implementation -#[derive(Copy, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct SimpleSpan { /// The start offset of the span. pub start: T, diff --git a/src/util.rs b/src/util.rs index 5c106f80..c5f74abd 100644 --- a/src/util.rs +++ b/src/util.rs @@ -137,3 +137,40 @@ impl<'a, T> From<&'a mut T> for Maybe { Self::Ref(x) } } + +#[cfg(feature = "serde")] +impl> Serialize for Maybe { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_newtype_struct("Maybe", &**self) + } +} + +#[cfg(feature = "serde")] +impl<'de, T: Deserialize<'de>, R: Deref> Deserialize<'de> for Maybe { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct MaybeVisitor(PhantomData<(T, R)>); + + impl<'de2, T: Deserialize<'de2>, R: Deref> Visitor<'de2> for MaybeVisitor { + type Value = Maybe; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a Maybe") + } + + fn visit_newtype_struct(self, deserializer: D) -> Result + where + D: Deserializer<'de2>, + { + T::deserialize(deserializer).map(Maybe::Val) + } + } + + deserializer.deserialize_newtype_struct("Maybe", MaybeVisitor(PhantomData)) + } +}