From 03eb10aaac651b682840f30557ddfcd558dd7719 Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Fri, 14 Jul 2023 20:27:02 -0400 Subject: [PATCH 1/5] Add serde feature, implement it for relevant types, drive-by `Hash` for `SimpleSpan` --- Cargo.toml | 6 +++++- src/error.rs | 5 +++++ src/extension.rs | 1 + src/lib.rs | 3 +++ src/span.rs | 3 ++- src/util.rs | 38 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 54 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b7449a6a..0e6bdf66 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ std = [] 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 +52,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. @@ -68,6 +71,7 @@ regex-automata = { version = "0.3", optional = true } 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", optional = true, features = ["derive"] } unicode-ident = "1.0.10" [dev-dependencies] 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 82bff40e..fd81f77d 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 5ff7dc38..8d84cee4 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::{Serialize, Deserialize, Serializer, Deserializer, de::Visitor}; #[cfg(feature = "label")] use self::label::{LabelError, Labelled}; diff --git a/src/span.rs b/src/span.rs index 41d6006d..323e1e0b 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 50eb3783..7d67843d 100644 --- a/src/util.rs +++ b/src/util.rs @@ -7,6 +7,7 @@ use core::{ hash::{Hash, Hasher}, ops::{Deref, DerefMut}, }; +use core::fmt::Formatter; pub use sync::MaybeSync; @@ -137,3 +138,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 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)) + } +} From 987a119aebb3229dd4368fca116a3ceb878ff57a Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Fri, 14 Jul 2023 20:27:41 -0400 Subject: [PATCH 2/5] Fix `Formatter` import --- src/util.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/util.rs b/src/util.rs index 7d67843d..c2f5e67d 100644 --- a/src/util.rs +++ b/src/util.rs @@ -7,7 +7,6 @@ use core::{ hash::{Hash, Hasher}, ops::{Deref, DerefMut}, }; -use core::fmt::Formatter; pub use sync::MaybeSync; @@ -160,7 +159,7 @@ impl<'de, T: Deserialize<'de>, R: Deref> Deserialize<'de> for Maybe< impl<'de2, T: Deserialize<'de2>, R: Deref> Visitor<'de2> for MaybeVisitor { type Value = Maybe; - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { write!(formatter, "a Maybe") } From 715ce073279102dc69e1bfcc9dcaa2022aecc659 Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Fri, 14 Jul 2023 20:31:24 -0400 Subject: [PATCH 3/5] Don't automatically enable std features of deps --- Cargo.toml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0e6bdf66..776b62b1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,11 +67,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 } 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", optional = true, features = ["derive"] } +serde = { version = "1.0", default-features = false, optional = true, features = ["derive"] } unicode-ident = "1.0.10" [dev-dependencies] @@ -123,3 +123,7 @@ required-features = ["label"] [[example]] name = "json" required-features = ["std"] + +[[example]] +name = "io" +required-features = ["std"] From 983b9e2856d427f14ae44ec9eee7003f16590788 Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Sat, 15 Jul 2023 11:32:06 -0400 Subject: [PATCH 4/5] Fix build/features --- Cargo.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 776b62b1..e934313f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,10 @@ 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 = [] @@ -67,7 +70,7 @@ rustdoc-args = ["--cfg", "docsrs"] [dependencies] hashbrown = "0.14" stacker = { version = "0.1", optional = true } -regex-automata = { version = "0.3", default-features = false, 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 } From 7df3def4775622dae04ad1367c90d8d638da7cec Mon Sep 17 00:00:00 2001 From: Rune Tynan Date: Sat, 15 Jul 2023 15:39:17 -0400 Subject: [PATCH 5/5] fmt --- src/lib.rs | 2 +- src/util.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8d84cee4..bc12bb8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -119,7 +119,7 @@ use core::{ }; use hashbrown::HashMap; #[cfg(feature = "serde")] -use serde::{Serialize, Deserialize, Serializer, Deserializer, de::Visitor}; +use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "label")] use self::label::{LabelError, Labelled}; diff --git a/src/util.rs b/src/util.rs index c2f5e67d..9b5d27ee 100644 --- a/src/util.rs +++ b/src/util.rs @@ -155,14 +155,14 @@ impl<'de, T: Deserialize<'de>, R: Deref> Deserialize<'de> for Maybe< 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>, @@ -170,7 +170,7 @@ impl<'de, T: Deserialize<'de>, R: Deref> Deserialize<'de> for Maybe< T::deserialize(deserializer).map(Maybe::Val) } } - + deserializer.deserialize_newtype_struct("Maybe", MaybeVisitor(PhantomData)) } }