mirror of
https://github.com/RGBCube/cstree
synced 2025-07-28 09:37:44 +00:00
Serializable trees (#11)
Clean up #4. Now everything is moved to the new design.
This commit is contained in:
parent
91b68c9a23
commit
3dbfe22bed
7 changed files with 711 additions and 93 deletions
91
Cargo.lock
generated
91
Cargo.lock
generated
|
@ -36,6 +36,8 @@ dependencies = [
|
||||||
"m_lexer",
|
"m_lexer",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"serde",
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_test",
|
||||||
"servo_arc",
|
"servo_arc",
|
||||||
"text-size",
|
"text-size",
|
||||||
]
|
]
|
||||||
|
@ -67,6 +69,12 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "0.4.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lasso"
|
name = "lasso"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
|
@ -143,6 +151,24 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "991431c3519a3f36861882da93630ce66b52918dcf1b8e2fd66b397fc96f28df"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.1.57"
|
version = "0.1.57"
|
||||||
|
@ -167,6 +193,12 @@ version = "0.6.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
|
checksum = "b5eb417147ba9860a96cfe72a0b93bf88fee1744b5636ec99ab20c1aa9376581"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -175,15 +207,50 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.119"
|
version = "1.0.120"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9bdd36f49e35b61d49efd8aa7fc068fd295961fd2286d0b2ee9a4c7a14e99cc3"
|
checksum = "166b2349061381baf54a58e4b13c89369feb0ef2eaa57198899e2312aac30aab"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.120"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ca2a8cb5805ce9e3b95435e3765b7b553cecc762d938d409434338386cb5775"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.61"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fceb2595057b6891a4ee808f70054bd2d12f0e97f1cbb78689b59f676df325a"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_test"
|
||||||
|
version = "1.0.120"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3dd7d96489b14fa2f4a89be299ac117c8023d1ead9aaee963a2dde72dad4d14b"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servo_arc"
|
name = "servo_arc"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nodrop",
|
"nodrop",
|
||||||
|
"serde",
|
||||||
"stable_deref_trait",
|
"stable_deref_trait",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -199,14 +266,22 @@ version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.58"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cc60a3d73ea6594cd712d830cc1f0390fd71542d8c8cd24e70cc54cdfd5e05d5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "text-size"
|
name = "text-size"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a"
|
checksum = "288cb548dbe72b652243ea797201f3d481a0609a967980fcc5b2315ea811560a"
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thread_local"
|
name = "thread_local"
|
||||||
|
@ -217,6 +292,12 @@ dependencies = [
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
version = "0.3.9"
|
version = "0.3.9"
|
||||||
|
|
15
Cargo.toml
15
Cargo.toml
|
@ -8,15 +8,26 @@ license = "MIT OR Apache-2.0"
|
||||||
repository = "https://github.com/domenicquirl/cstree"
|
repository = "https://github.com/domenicquirl/cstree"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version = "1.0.89", optional = true, default-features = false }
|
|
||||||
lasso = "0.4.1"
|
lasso = "0.4.1"
|
||||||
text-size = "1.0.0"
|
text-size = "1.0.0"
|
||||||
fxhash= "0.2.1"
|
fxhash= "0.2.1"
|
||||||
servo_arc = { path = "vendor/servo_arc" }
|
servo_arc = { path = "vendor/servo_arc" }
|
||||||
parking_lot= "0.11.1"
|
parking_lot= "0.11.1"
|
||||||
|
|
||||||
|
[dependencies.serde]
|
||||||
|
version = "1.0"
|
||||||
|
optional = true
|
||||||
|
default-features = false
|
||||||
|
features = ["derive"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
m_lexer = "0.0.4"
|
m_lexer = "0.0.4"
|
||||||
|
serde_json = "1.0.61"
|
||||||
|
serde_test = "1.0.119"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
serde1 = ["serde", "text-size/serde"]
|
default = []
|
||||||
|
serde1 = ["serde", "servo_arc/servo"]
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
features = ["serde1"]
|
||||||
|
|
|
@ -1,63 +1,255 @@
|
||||||
use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
|
//! Serialization and Deserialization for syntax trees.
|
||||||
use std::fmt;
|
|
||||||
|
|
||||||
use crate::{Language, NodeOrToken, SyntaxNode, SyntaxToken};
|
use crate::{interning::Resolver, GreenNodeBuilder, Language, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent};
|
||||||
|
use serde::{
|
||||||
|
de::{Error, SeqAccess, Visitor},
|
||||||
|
ser::SerializeTuple,
|
||||||
|
Deserialize, Serialize,
|
||||||
|
};
|
||||||
|
use std::{collections::VecDeque, fmt, marker::PhantomData};
|
||||||
|
|
||||||
struct SerDisplay<T>(T);
|
type Rodeo = lasso::Rodeo<lasso::Spur, fxhash::FxBuildHasher>;
|
||||||
impl<T: fmt::Display> Serialize for SerDisplay<T> {
|
type RodeoResolver = lasso::RodeoResolver<lasso::Spur>;
|
||||||
|
|
||||||
|
/// Expands to the first expression, if there's
|
||||||
|
/// no expression following, otherwise return the second expression.
|
||||||
|
///
|
||||||
|
/// Required for having two different values if the argument is `$(...)?`.
|
||||||
|
macro_rules! data_list {
|
||||||
|
($_:expr, $list:expr) => {
|
||||||
|
$list
|
||||||
|
};
|
||||||
|
|
||||||
|
($list:expr,) => {
|
||||||
|
$list
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generate the code that should be put inside the [`Serialize`] implementation
|
||||||
|
/// of a [`SyntaxNode`]-like type.
|
||||||
|
///
|
||||||
|
/// It serializes a [`SyntaxNode`] into a tuple with 2 elements.
|
||||||
|
/// The first element is the serialized event stream that was generated
|
||||||
|
/// by [`SyntaxNode::preorder_with_tokens()`].
|
||||||
|
/// The second element is a list of `D`s, where `D` is the data of the nodes.
|
||||||
|
/// The data may only be serialized if it's `Some(data)`. Each `EnterNode` event
|
||||||
|
/// contains a boolean which indicates if this node has a data. If it has one,
|
||||||
|
/// the deserializer should pop the first element from the data list and continue.
|
||||||
|
///
|
||||||
|
/// Takes the `Language` (`$l`), `SyntaxNode` (`$node`), `Resolver` (`$resolver`),
|
||||||
|
/// `Serializer` (`$serializer`), and an optional `data_list` which must be a `mut Vec<D>`.
|
||||||
|
macro_rules! gen_serialize {
|
||||||
|
($l:ident, $node:expr, $resolver:expr, $ser:ident, $($data_list:ident)?) => {{
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
let events = $node.preorder_with_tokens().filter_map(|event| match event {
|
||||||
|
WalkEvent::Enter(NodeOrToken::Node(node)) => {
|
||||||
|
let has_data = false;
|
||||||
|
$(let has_data = node
|
||||||
|
.get_data()
|
||||||
|
.map(|data| {
|
||||||
|
$data_list.push(data);
|
||||||
|
true
|
||||||
|
})
|
||||||
|
.unwrap_or(false);)?
|
||||||
|
|
||||||
|
Some(Event::EnterNode($l::kind_to_raw(node.kind()), has_data))
|
||||||
|
}
|
||||||
|
WalkEvent::Enter(NodeOrToken::Token(tok)) => Some(Event::Token($l::kind_to_raw(tok.kind()), tok.resolve_text($resolver))),
|
||||||
|
|
||||||
|
WalkEvent::Leave(NodeOrToken::Node(_)) => Some(Event::LeaveNode),
|
||||||
|
WalkEvent::Leave(NodeOrToken::Token(_)) => None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut tuple = $ser.serialize_tuple(2)?;
|
||||||
|
|
||||||
|
// TODO(Stupremee): We can easily avoid this allocation but it would
|
||||||
|
// require more weird and annoying-to-write code, so I'll skip it for now.
|
||||||
|
tuple.serialize_element(&events.collect::<Vec<_>>())?;
|
||||||
|
tuple.serialize_element(&data_list!(Vec::<()>::new(), $($data_list)?))?;
|
||||||
|
|
||||||
|
tuple.end()
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize)]
|
||||||
|
#[serde(tag = "t", content = "c")]
|
||||||
|
enum Event<'text> {
|
||||||
|
/// The second parameter indicates if this node needs data.
|
||||||
|
/// If the boolean is true, the next element inside the data list
|
||||||
|
/// must be attached to this node.
|
||||||
|
EnterNode(SyntaxKind, bool),
|
||||||
|
Token(SyntaxKind, &'text str),
|
||||||
|
LeaveNode,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make a `SyntaxNode` serializable but without serializing the data.
|
||||||
|
pub(crate) struct SerializeWithResolver<'node, 'resolver, L: Language, D: 'static, RN: 'static, R> {
|
||||||
|
pub(crate) node: &'node SyntaxNode<L, D, RN>,
|
||||||
|
pub(crate) resolver: &'resolver R,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make a `SyntaxNode` serializable which will include the data for serialization.
|
||||||
|
pub(crate) struct SerializeWithData<'node, 'resolver, L: Language, D: 'static, RN: 'static, R> {
|
||||||
|
pub(crate) node: &'node SyntaxNode<L, D, RN>,
|
||||||
|
pub(crate) resolver: &'resolver R,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, D, RN, R> Serialize for SerializeWithData<'_, '_, L, D, RN, R>
|
||||||
|
where
|
||||||
|
L: Language,
|
||||||
|
R: Resolver,
|
||||||
|
D: Serialize,
|
||||||
|
{
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
serializer.collect_str(&self.0)
|
let mut data_list = Vec::new();
|
||||||
|
gen_serialize!(L, self.node, self.resolver, serializer, data_list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct DisplayDebug<T>(T);
|
impl<L, D, RN, R> Serialize for SerializeWithResolver<'_, '_, L, D, RN, R>
|
||||||
impl<T: fmt::Debug> fmt::Display for DisplayDebug<T> {
|
where
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
L: Language,
|
||||||
fmt::Debug::fmt(&self.0, f)
|
R: Resolver,
|
||||||
|
{
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
gen_serialize!(L, self.node, self.resolver, serializer,)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language> Serialize for SyntaxNode<L> {
|
impl<L, D, R> Serialize for SyntaxNode<L, D, R>
|
||||||
|
where
|
||||||
|
L: Language,
|
||||||
|
D: Serialize,
|
||||||
|
R: Resolver,
|
||||||
|
{
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
let mut state = serializer.serialize_map(Some(3))?;
|
let node = SerializeWithResolver {
|
||||||
state.serialize_entry("kind", &SerDisplay(DisplayDebug(self.kind())))?;
|
node: self,
|
||||||
state.serialize_entry("text_range", &self.text_range())?;
|
resolver: self.resolver().as_ref(),
|
||||||
state.serialize_entry("children", &Children(self))?;
|
};
|
||||||
state.end()
|
node.serialize(serializer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<L: Language> Serialize for SyntaxToken<L> {
|
impl<'de, L, D> Deserialize<'de> for SyntaxNode<L, D, RodeoResolver>
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
where
|
||||||
|
L: Language,
|
||||||
|
D: Deserialize<'de>,
|
||||||
|
{
|
||||||
|
// Deserialization is done by walking down the deserialized event stream,
|
||||||
|
// which is the first element inside the tuple. The events
|
||||||
|
// are then passed to a `GreenNodeBuilder` which will do all
|
||||||
|
// the hard work for use.
|
||||||
|
//
|
||||||
|
// While walking the event stream, we also store a list of booleans,
|
||||||
|
// which indicate which node needs to set data. After creating the tree,
|
||||||
|
// we walk down the nodes, check if the bool at `data_list[idx]` is true,
|
||||||
|
// and if so, pop the first element of the data list and attach the data
|
||||||
|
// to the current node.
|
||||||
|
fn deserialize<DE>(deserializer: DE) -> Result<Self, DE::Error>
|
||||||
where
|
where
|
||||||
S: Serializer,
|
DE: serde::Deserializer<'de>,
|
||||||
{
|
{
|
||||||
let mut state = serializer.serialize_map(Some(3))?;
|
struct EventVisitor<L: Language, D: 'static> {
|
||||||
state.serialize_entry("kind", &SerDisplay(DisplayDebug(self.kind())))?;
|
_marker: PhantomData<SyntaxNode<L, D, Rodeo>>,
|
||||||
state.serialize_entry("text_range", &self.text_range())?;
|
}
|
||||||
state.serialize_entry("text", &self.text().as_str())?;
|
|
||||||
state.end()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Children<T>(T);
|
impl<'de, L, D> Visitor<'de> for EventVisitor<L, D>
|
||||||
|
where
|
||||||
|
L: Language,
|
||||||
|
D: Deserialize<'de>,
|
||||||
|
{
|
||||||
|
type Value = (SyntaxNode<L, D, RodeoResolver>, VecDeque<bool>);
|
||||||
|
|
||||||
impl<L: Language> Serialize for Children<&'_ SyntaxNode<L>> {
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
formatter.write_str("a list of tree events")
|
||||||
where
|
}
|
||||||
S: Serializer,
|
|
||||||
{
|
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
let mut state = serializer.serialize_seq(None)?;
|
where
|
||||||
self.0.children_with_tokens().try_for_each(|element| match element {
|
A: SeqAccess<'de>,
|
||||||
NodeOrToken::Node(it) => state.serialize_element(&it),
|
{
|
||||||
NodeOrToken::Token(it) => state.serialize_element(&it),
|
let mut builder = GreenNodeBuilder::new();
|
||||||
|
let mut data_indices = VecDeque::new();
|
||||||
|
|
||||||
|
while let Some(next) = seq.next_element::<Event<'_>>()? {
|
||||||
|
match next {
|
||||||
|
Event::EnterNode(kind, has_data) => {
|
||||||
|
builder.start_node(kind);
|
||||||
|
data_indices.push_back(has_data);
|
||||||
|
}
|
||||||
|
Event::Token(kind, text) => builder.token(kind, text),
|
||||||
|
Event::LeaveNode => builder.finish_node(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (tree, resolver) = builder.finish();
|
||||||
|
let tree = SyntaxNode::new_root_with_resolver(tree, resolver.unwrap().into_resolver());
|
||||||
|
Ok((tree, data_indices))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ProcessedEvents<L: Language, D: 'static>(SyntaxNode<L, D, RodeoResolver>, VecDeque<bool>);
|
||||||
|
impl<'de, L, D> Deserialize<'de> for ProcessedEvents<L, D>
|
||||||
|
where
|
||||||
|
L: Language,
|
||||||
|
D: Deserialize<'de>,
|
||||||
|
{
|
||||||
|
fn deserialize<DE>(deserializer: DE) -> Result<Self, DE::Error>
|
||||||
|
where
|
||||||
|
DE: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let (tree, ids) = deserializer.deserialize_seq(EventVisitor { _marker: PhantomData })?;
|
||||||
|
Ok(Self(tree, ids))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (ProcessedEvents(tree, data_indices), mut data) =
|
||||||
|
<(ProcessedEvents<L, D>, VecDeque<D>)>::deserialize(deserializer)?;
|
||||||
|
|
||||||
|
tree.descendants().zip(data_indices).try_for_each(|(node, has_data)| {
|
||||||
|
if has_data {
|
||||||
|
let data = data
|
||||||
|
.pop_front()
|
||||||
|
.ok_or_else(|| DE::Error::custom("invalid serialized tree"))?;
|
||||||
|
node.set_data(data);
|
||||||
|
}
|
||||||
|
<Result<(), DE::Error>>::Ok(())
|
||||||
})?;
|
})?;
|
||||||
state.end()
|
|
||||||
|
if !data.is_empty() {
|
||||||
|
Err(DE::Error::custom(
|
||||||
|
"serialized SyntaxNode contained too many data elements",
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Ok(tree)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for SyntaxKind {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
serializer.serialize_u16(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for SyntaxKind {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Ok(Self(u16::deserialize(deserializer)?))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ use std::{
|
||||||
sync::atomic::{AtomicU32, Ordering},
|
sync::atomic::{AtomicU32, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[cfg(feature = "serde1")]
|
||||||
|
use crate::serde_impls::{SerializeWithData, SerializeWithResolver};
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use servo_arc::Arc;
|
use servo_arc::Arc;
|
||||||
|
|
||||||
|
@ -872,6 +874,46 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde1")]
|
||||||
|
impl<L, D, R> SyntaxNode<L, D, R>
|
||||||
|
where
|
||||||
|
L: Language,
|
||||||
|
{
|
||||||
|
/// Return an anonymous object that can be used to serialize this node,
|
||||||
|
/// including the data for each node.
|
||||||
|
pub fn as_serialize_with_data(&self) -> impl serde::Serialize + '_
|
||||||
|
where
|
||||||
|
R: Resolver,
|
||||||
|
D: serde::Serialize,
|
||||||
|
{
|
||||||
|
SerializeWithData {
|
||||||
|
node: self,
|
||||||
|
resolver: self.resolver().as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return an anonymous object that can be used to serialize this node,
|
||||||
|
/// including the data and by using an external resolver.
|
||||||
|
pub fn as_serialize_with_data_with_resolver<'node>(
|
||||||
|
&'node self,
|
||||||
|
resolver: &'node impl Resolver,
|
||||||
|
) -> impl serde::Serialize + 'node
|
||||||
|
where
|
||||||
|
D: serde::Serialize,
|
||||||
|
{
|
||||||
|
SerializeWithData { node: self, resolver }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return an anonymous object that can be used to serialize this node,
|
||||||
|
/// which uses the given resolver instead of the resolver inside the tree.
|
||||||
|
pub fn as_serialize_with_resolver<'node>(
|
||||||
|
&'node self,
|
||||||
|
resolver: &'node impl Resolver,
|
||||||
|
) -> impl serde::Serialize + 'node {
|
||||||
|
SerializeWithResolver { node: self, resolver }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<L: Language, D, R> fmt::Debug for SyntaxNode<L, D, R>
|
impl<L: Language, D, R> fmt::Debug for SyntaxNode<L, D, R>
|
||||||
where
|
where
|
||||||
R: Resolver,
|
R: Resolver,
|
||||||
|
|
|
@ -1,15 +1,14 @@
|
||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
use common::TestLang;
|
use common::{build_recursive, build_tree_with_cache, Element, SyntaxNode};
|
||||||
use cstree::{GreenNode, GreenNodeBuilder, NodeCache, SyntaxKind, TextRange};
|
use cstree::{GreenNodeBuilder, NodeCache, SyntaxKind, TextRange};
|
||||||
use lasso::{Interner, Resolver, Rodeo};
|
use lasso::{Resolver, Rodeo};
|
||||||
|
|
||||||
type SyntaxNode<D = (), R = ()> = cstree::SyntaxNode<TestLang, D, R>;
|
fn build_tree<D>(root: &Element<'_>) -> (SyntaxNode<D>, impl Resolver) {
|
||||||
|
let mut builder = GreenNodeBuilder::new();
|
||||||
#[derive(Debug)]
|
build_recursive(root, &mut builder, 0);
|
||||||
enum Element<'s> {
|
let (node, interner) = builder.finish();
|
||||||
Node(Vec<Element<'s>>),
|
(SyntaxNode::new_root(node), interner.unwrap())
|
||||||
Token(&'s str),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn two_level_tree() -> Element<'static> {
|
fn two_level_tree() -> Element<'static> {
|
||||||
|
@ -21,43 +20,6 @@ fn two_level_tree() -> Element<'static> {
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_tree<D>(root: &Element<'_>) -> (SyntaxNode<D>, impl Resolver) {
|
|
||||||
let mut builder = GreenNodeBuilder::new();
|
|
||||||
build_recursive(root, &mut builder, 0);
|
|
||||||
let (node, interner) = builder.finish();
|
|
||||||
(SyntaxNode::new_root(node), interner.unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_tree_with_cache<'c, 'i, I>(root: &Element<'_>, cache: &'c mut NodeCache<'i, I>) -> GreenNode
|
|
||||||
where
|
|
||||||
I: Interner,
|
|
||||||
{
|
|
||||||
let mut builder = GreenNodeBuilder::with_cache(cache);
|
|
||||||
build_recursive(root, &mut builder, 0);
|
|
||||||
let (node, interner) = builder.finish();
|
|
||||||
assert!(interner.is_none());
|
|
||||||
node
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_recursive<'c, 'i, I>(root: &Element<'_>, builder: &mut GreenNodeBuilder<'c, 'i, I>, mut from: u16) -> u16
|
|
||||||
where
|
|
||||||
I: Interner,
|
|
||||||
{
|
|
||||||
match root {
|
|
||||||
Element::Node(children) => {
|
|
||||||
builder.start_node(SyntaxKind(from));
|
|
||||||
for child in children {
|
|
||||||
from = build_recursive(child, builder, from + 1);
|
|
||||||
}
|
|
||||||
builder.finish_node();
|
|
||||||
}
|
|
||||||
Element::Token(text) => {
|
|
||||||
builder.token(SyntaxKind(from), *text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
from
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn create() {
|
fn create() {
|
||||||
let tree = two_level_tree();
|
let tree = two_level_tree();
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
use cstree::{Language, SyntaxKind};
|
use cstree::{GreenNode, GreenNodeBuilder, Language, NodeCache, SyntaxKind};
|
||||||
|
use lasso::Interner;
|
||||||
|
|
||||||
|
pub type SyntaxNode<D = (), R = ()> = cstree::SyntaxNode<TestLang, D, R>;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Element<'s> {
|
||||||
|
Node(Vec<Element<'s>>),
|
||||||
|
Token(&'s str),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||||
pub enum TestLang {}
|
pub enum TestLang {}
|
||||||
|
@ -13,3 +22,33 @@ impl Language for TestLang {
|
||||||
kind
|
kind
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn build_tree_with_cache<'c, 'i, I>(root: &Element<'_>, cache: &'c mut NodeCache<'i, I>) -> GreenNode
|
||||||
|
where
|
||||||
|
I: Interner,
|
||||||
|
{
|
||||||
|
let mut builder = GreenNodeBuilder::with_cache(cache);
|
||||||
|
build_recursive(root, &mut builder, 0);
|
||||||
|
let (node, interner) = builder.finish();
|
||||||
|
assert!(interner.is_none());
|
||||||
|
node
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build_recursive<'c, 'i, I>(root: &Element<'_>, builder: &mut GreenNodeBuilder<'c, 'i, I>, mut from: u16) -> u16
|
||||||
|
where
|
||||||
|
I: Interner,
|
||||||
|
{
|
||||||
|
match root {
|
||||||
|
Element::Node(children) => {
|
||||||
|
builder.start_node(SyntaxKind(from));
|
||||||
|
for child in children {
|
||||||
|
from = build_recursive(child, builder, from + 1);
|
||||||
|
}
|
||||||
|
builder.finish_node();
|
||||||
|
}
|
||||||
|
Element::Token(text) => {
|
||||||
|
builder.token(SyntaxKind(from), *text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
from
|
||||||
|
}
|
||||||
|
|
291
tests/serde.rs
Normal file
291
tests/serde.rs
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
#![cfg(feature = "serde1")]
|
||||||
|
|
||||||
|
mod common;
|
||||||
|
|
||||||
|
use common::{Element, SyntaxNode};
|
||||||
|
use cstree::{GreenNodeBuilder, NodeCache, NodeOrToken};
|
||||||
|
use lasso::Resolver;
|
||||||
|
use serde_test::Token;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
type Rodeo = lasso::Rodeo<lasso::Spur, fxhash::FxBuildHasher>;
|
||||||
|
type RodeoResolver = lasso::RodeoResolver<lasso::Spur>;
|
||||||
|
|
||||||
|
/// Macro for generating a list of `serde_test::Token`s using a simpler DSL.
|
||||||
|
macro_rules! event_tokens {
|
||||||
|
($($name:ident($($token:tt)*)),*) => {
|
||||||
|
[
|
||||||
|
$(
|
||||||
|
event_tokens!(@token, $name($($token)*))
|
||||||
|
),*
|
||||||
|
].concat()
|
||||||
|
};
|
||||||
|
|
||||||
|
(@token, token($kind:expr, $str:expr)) => {
|
||||||
|
[
|
||||||
|
Token::Struct { name: "Event", len: 2 },
|
||||||
|
Token::BorrowedStr("t"),
|
||||||
|
Token::BorrowedStr("Token"),
|
||||||
|
Token::BorrowedStr("c"),
|
||||||
|
Token::Tuple { len: 2 },
|
||||||
|
Token::U16($kind),
|
||||||
|
Token::BorrowedStr($str),
|
||||||
|
Token::TupleEnd,
|
||||||
|
Token::StructEnd,
|
||||||
|
].as_ref()
|
||||||
|
};
|
||||||
|
|
||||||
|
(@token, node($kind:expr, $data:expr)) => {
|
||||||
|
[
|
||||||
|
Token::Struct { name: "Event", len: 2 },
|
||||||
|
Token::BorrowedStr("t"),
|
||||||
|
Token::BorrowedStr("EnterNode"),
|
||||||
|
Token::BorrowedStr("c"),
|
||||||
|
Token::Tuple { len: 2 },
|
||||||
|
Token::U16($kind),
|
||||||
|
Token::Bool($data),
|
||||||
|
Token::TupleEnd,
|
||||||
|
Token::StructEnd,
|
||||||
|
].as_ref()
|
||||||
|
};
|
||||||
|
|
||||||
|
(@token, leave_node()) => {
|
||||||
|
[
|
||||||
|
Token::Struct { name: "Event", len: 1 },
|
||||||
|
Token::BorrowedStr("t"),
|
||||||
|
Token::BorrowedStr("LeaveNode"),
|
||||||
|
Token::StructEnd,
|
||||||
|
].as_ref()
|
||||||
|
};
|
||||||
|
|
||||||
|
(@token, data($data:expr)) => {
|
||||||
|
[Token::Str($data)].as_ref()
|
||||||
|
};
|
||||||
|
|
||||||
|
(@token, seq($len:expr)) => {
|
||||||
|
[Token::Seq { len: Option::Some($len) }].as_ref()
|
||||||
|
};
|
||||||
|
|
||||||
|
(@token, seq_end()) => {
|
||||||
|
[Token::SeqEnd].as_ref()
|
||||||
|
};
|
||||||
|
|
||||||
|
(@token, tuple($len:expr)) => {
|
||||||
|
[Token::Tuple { len: $len }].as_ref()
|
||||||
|
};
|
||||||
|
|
||||||
|
(@token, tuple_end()) => {
|
||||||
|
[Token::TupleEnd].as_ref()
|
||||||
|
};
|
||||||
|
|
||||||
|
(@token,) => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn three_level_tree_with_data_tokens() -> Vec<Token> {
|
||||||
|
event_tokens!(
|
||||||
|
tuple(2),
|
||||||
|
seq(14),
|
||||||
|
node(0, true),
|
||||||
|
node(1, true),
|
||||||
|
node(2, true),
|
||||||
|
token(3, "foo"),
|
||||||
|
token(4, "bar"),
|
||||||
|
leave_node(),
|
||||||
|
token(5, "baz"),
|
||||||
|
leave_node(),
|
||||||
|
node(6, true),
|
||||||
|
token(7, "pub"),
|
||||||
|
token(8, "fn"),
|
||||||
|
token(9, "tree"),
|
||||||
|
leave_node(),
|
||||||
|
leave_node(),
|
||||||
|
seq_end(),
|
||||||
|
seq(4),
|
||||||
|
data("1"),
|
||||||
|
data("2"),
|
||||||
|
data("3"),
|
||||||
|
data("4"),
|
||||||
|
seq_end(),
|
||||||
|
tuple_end()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn three_level_tree_tokens() -> Vec<Token> {
|
||||||
|
event_tokens!(
|
||||||
|
tuple(2),
|
||||||
|
seq(14),
|
||||||
|
node(0, false),
|
||||||
|
node(1, false),
|
||||||
|
node(2, false),
|
||||||
|
token(3, "foo"),
|
||||||
|
token(4, "bar"),
|
||||||
|
leave_node(),
|
||||||
|
token(5, "baz"),
|
||||||
|
leave_node(),
|
||||||
|
node(6, false),
|
||||||
|
token(7, "pub"),
|
||||||
|
token(8, "fn"),
|
||||||
|
token(9, "tree"),
|
||||||
|
leave_node(),
|
||||||
|
leave_node(),
|
||||||
|
seq_end(),
|
||||||
|
seq(0),
|
||||||
|
seq_end(),
|
||||||
|
tuple_end()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct NonSerializable;
|
||||||
|
|
||||||
|
/// Serializable SyntaxNode that doesn't have a identity `PartialEq` implementation,
|
||||||
|
/// but checks if both trees have equal nodes and tokens.
|
||||||
|
struct TestNode<R: 'static> {
|
||||||
|
node: SyntaxNode<String, R>,
|
||||||
|
with_data: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R> TestNode<R> {
|
||||||
|
fn new(node: SyntaxNode<String, R>) -> Self {
|
||||||
|
Self { node, with_data: false }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_data(node: SyntaxNode<String, R>) -> Self {
|
||||||
|
Self { node, with_data: true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Resolver> fmt::Debug for TestNode<R> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Debug::fmt(&self.node, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: Resolver> serde::Serialize for TestNode<R> {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: serde::Serializer,
|
||||||
|
{
|
||||||
|
if self.with_data {
|
||||||
|
self.node.as_serialize_with_data().serialize(serializer)
|
||||||
|
} else {
|
||||||
|
self.node.serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> serde::Deserialize<'de> for TestNode<RodeoResolver> {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: serde::Deserializer<'de>,
|
||||||
|
{
|
||||||
|
Ok(Self {
|
||||||
|
node: SyntaxNode::deserialize(deserializer)?,
|
||||||
|
with_data: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R1: Resolver, R2: Resolver> PartialEq<TestNode<R2>> for TestNode<R1> {
|
||||||
|
fn eq(&self, other: &TestNode<R2>) -> bool {
|
||||||
|
self.node.kind() == other.node.kind()
|
||||||
|
&& self.node.get_data() == other.node.get_data()
|
||||||
|
&& self.node.text_range() == other.node.text_range()
|
||||||
|
&& self
|
||||||
|
.node
|
||||||
|
.children_with_tokens()
|
||||||
|
.zip(other.node.children_with_tokens())
|
||||||
|
.all(|(this, other)| match (this, other) {
|
||||||
|
(NodeOrToken::Node(this), NodeOrToken::Node(other)) => {
|
||||||
|
TestNode::new(this.clone()) == TestNode::new(other.clone())
|
||||||
|
}
|
||||||
|
(NodeOrToken::Token(this), NodeOrToken::Token(other)) => {
|
||||||
|
this.kind() == other.kind() && this.text_range() == other.text_range()
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustfmt::skip]
|
||||||
|
fn three_level_tree() -> Element<'static> {
|
||||||
|
use Element::*;
|
||||||
|
|
||||||
|
Node(vec![
|
||||||
|
Node(vec![
|
||||||
|
Node(vec![
|
||||||
|
Token("foo"),
|
||||||
|
Token("bar")
|
||||||
|
]),
|
||||||
|
Token("baz")
|
||||||
|
]),
|
||||||
|
Node(vec![
|
||||||
|
Token("pub"),
|
||||||
|
Token("fn"),
|
||||||
|
Token("tree")
|
||||||
|
]),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_tree(root: Element<'_>) -> SyntaxNode<String, RodeoResolver> {
|
||||||
|
let mut builder = GreenNodeBuilder::new();
|
||||||
|
common::build_recursive(&root, &mut builder, 0);
|
||||||
|
let (node, interner) = builder.finish();
|
||||||
|
SyntaxNode::new_root_with_resolver(node, interner.unwrap().into_resolver())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn attach_data<R>(node: &SyntaxNode<String, R>) {
|
||||||
|
node.descendants().enumerate().for_each(|(idx, node)| {
|
||||||
|
node.set_data(format!("{}", idx + 1));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_tree_with_data_with_resolver() {
|
||||||
|
let mut interner = Rodeo::with_hasher(Default::default());
|
||||||
|
let mut cache = NodeCache::with_interner(&mut interner);
|
||||||
|
|
||||||
|
let root = three_level_tree();
|
||||||
|
let root = common::build_tree_with_cache(&root, &mut cache);
|
||||||
|
let tree = SyntaxNode::<String, ()>::new_root(root.clone());
|
||||||
|
attach_data(&tree);
|
||||||
|
|
||||||
|
let serialized = serde_json::to_string(&tree.as_serialize_with_data_with_resolver(&interner)).unwrap();
|
||||||
|
let deserialized: TestNode<_> = serde_json::from_str(&serialized).unwrap();
|
||||||
|
|
||||||
|
let expected = SyntaxNode::new_root_with_resolver(root, interner);
|
||||||
|
attach_data(&expected);
|
||||||
|
assert_eq!(TestNode::new(expected), deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_tree_with_resolver() {
|
||||||
|
let mut interner = Rodeo::with_hasher(Default::default());
|
||||||
|
let mut cache = NodeCache::with_interner(&mut interner);
|
||||||
|
|
||||||
|
let root = three_level_tree();
|
||||||
|
let root = common::build_tree_with_cache(&root, &mut cache);
|
||||||
|
let tree = SyntaxNode::<String, NonSerializable>::new_root(root.clone());
|
||||||
|
|
||||||
|
let serialized = serde_json::to_string(&tree.as_serialize_with_resolver(&interner)).unwrap();
|
||||||
|
let deserialized: TestNode<_> = serde_json::from_str(&serialized).unwrap();
|
||||||
|
|
||||||
|
let expected = SyntaxNode::new_root_with_resolver(root, interner);
|
||||||
|
assert_eq!(TestNode::new(expected), deserialized);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_tree_with_data() {
|
||||||
|
let tree = build_tree(three_level_tree());
|
||||||
|
let tree = TestNode::with_data(tree);
|
||||||
|
attach_data(&tree.node);
|
||||||
|
|
||||||
|
serde_test::assert_tokens(&tree, three_level_tree_with_data_tokens().as_slice());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_tree_without_data() {
|
||||||
|
let tree = build_tree(three_level_tree());
|
||||||
|
let tree = TestNode::new(tree);
|
||||||
|
|
||||||
|
serde_test::assert_tokens(&tree, three_level_tree_tokens().as_slice());
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue