diff options
Diffstat (limited to 'syn/json')
-rw-r--r-- | syn/json/Cargo.toml | 18 | ||||
-rw-r--r-- | syn/json/src/lib.rs | 214 |
2 files changed, 232 insertions, 0 deletions
diff --git a/syn/json/Cargo.toml b/syn/json/Cargo.toml new file mode 100644 index 0000000..77104dc --- /dev/null +++ b/syn/json/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "syn-codegen" +version = "0.1.0" +authors = ["David Tolnay <dtolnay@gmail.com>"] +edition = "2018" +license = "MIT OR Apache-2.0" +description = "Syntax tree describing Syn's syntax tree" +repository = "https://github.com/dtolnay/syn" +documentation = "https://docs.rs/syn-codegen" +categories = ["development-tools::procedural-macro-helpers"] + +[dependencies] +indexmap = { version = "1.0", features = ["serde-1"] } +semver = { version = "0.9", features = ["serde"] } +serde = { version = "1.0.88", features = ["derive"] } + +[dev-dependencies] +serde_json = "1.0" diff --git a/syn/json/src/lib.rs b/syn/json/src/lib.rs new file mode 100644 index 0000000..e3546d9 --- /dev/null +++ b/syn/json/src/lib.rs @@ -0,0 +1,214 @@ +//! # Data structures that describe Syn's syntax tree. +//! +//! The Syn syntax tree is made up of more than 200 types. Occasionally it can +//! come up that you need to implement some behavior across them all. +//! +//! - For example [the Rust integration for AST Explorer][astexplorer] wants to +//! turn a syntax tree from Syn into a JavaScript value understood by the +//! platform's existing cross-language syntax tree visualization code. +//! +//! [astexplorer]: https://astexplorer.net/#/gist/388150a52f74d45a355d2b5e865ded96/0c6d563f28d900472f699c21a1845ad20ae9927f +//! +//! - As another example from within Syn itself, the traits and implementations +//! of the [`visit`], [`visit_mut`], and [`fold`] modules can be generated +//! programmatically from a description of the syntax tree. +//! +//! [`visit`]: https://docs.rs/syn/1.0/syn/visit/index.html +//! [`visit_mut`]: https://docs.rs/syn/1.0/syn/visit_mut/index.html +//! [`fold`]: https://docs.rs/syn/1.0/syn/fold/index.html +//! +//! To make this type of code as easy as possible to implement in any language, +//! every Syn release comes with a machine-readable description of that version +//! of the syntax tree as a JSON file [syn.json]. This `syn-codegen` crate +//! provides the canonical data structures for parsing and making use of the +//! representation in syn.json from Rust code. +//! +//! [syn.json]: https://raw.githubusercontent.com/dtolnay/syn/master/syn.json +//! +//! ## Example +//! +//! ``` +//! use syn_codegen::Definitions; +//! +//! # const IGNORE: &str = stringify! { +//! const SYN: &str = include_str!("syn.json"); +//! # }; +//! # const SYN: &str = include_str!("../../syn.json"); +//! +//! fn main() { +//! let defs: Definitions = serde_json::from_str(SYN).unwrap(); +//! +//! for node in &defs.types { +//! println!("syn::{}", node.ident); +//! } +//! } +//! ``` + +use indexmap::IndexMap; +use semver::Version; +use serde::{Deserialize, Deserializer, Serialize}; + +use std::collections::{BTreeMap, BTreeSet}; + +/// Top-level content of the syntax tree description. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Definitions { + /// The Syn version whose syntax tree is described by this data. + pub version: Version, + + /// Syntax tree types defined by Syn. + pub types: Vec<Node>, + + /// Token types defined by Syn (keywords as well as punctuation). + /// + /// The keys in the map are the Rust type name for the token. The values in + /// the map are the printed token representation. + /// + /// These tokens are accessible in the Syn public API as `syn::token::#name` + /// or alternatively `syn::Token![#repr]`. + pub tokens: BTreeMap<String, String>, +} + +/// Syntax tree type defined by Syn. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Node { + /// Name of the type. + /// + /// This type is accessible in the Syn public API as `syn::#name`. + pub ident: String, + + /// Features behind which this type is cfg gated. + pub features: Features, + + /// Content of the data structure. + #[serde( + flatten, + skip_serializing_if = "is_private", + deserialize_with = "private_if_absent" + )] + pub data: Data, + + #[serde(skip_serializing_if = "is_true", default = "bool_true")] + pub exhaustive: bool, +} + +/// Content of a syntax tree data structure. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub enum Data { + /// This is an opaque type with no publicy accessible structure. + Private, + + /// This type is a braced struct with named fields. + #[serde(rename = "fields")] + Struct(Fields), + + /// This type is an enum. + #[serde(rename = "variants")] + Enum(Variants), +} + +/// Fields of a braced struct syntax tree node with named fields. +/// +/// The keys in the map are the field names. +pub type Fields = IndexMap<String, Type>; + +/// Variants of an enum syntax tree node. +/// +/// The keys in the map are the variant names. +/// +/// Variants are unit variants if they hold no data and tuple variants +/// otherwise. The Syn syntax tree does not make use of braced variants. +pub type Variants = IndexMap<String, Vec<Type>>; + +/// Type of a struct field or tuple variant field in the syntax tree. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum Type { + /// Syntax tree type defined by Syn. + /// + /// This name will match the ident of some `Node`. + Syn(String), + + /// Type defined by the Rust language or standard library. + /// + /// All such types used by Syn are accessible in the Rust prelude and can be + /// used without a qualifying path in most Rust code. + Std(String), + + /// Type defined by proc-macro2. + /// + /// The type is accessible in the proc-macro2 public API as + /// `proc_macro2::#name`. + #[serde(rename = "proc_macro2")] + Ext(String), + + /// Keyword or punctuation token type defined by Syn. + /// + /// This name will match one of the keys in the `tokens` map. + Token(String), + + /// Grouping token defined by Syn. + /// + /// The type is accessible in the Syn public API as `syn::token::#name`. + Group(String), + + /// Punctuated list. + /// + /// This refers to `syn::punctuated::Punctuated<T, P>` with the specified + /// element type and punctuation. + Punctuated(Punctuated), + + /// `std::option::Option` + Option(Box<Type>), + + /// `std::boxed::Box` + Box(Box<Type>), + + /// `std::vec::Vec` + Vec(Box<Type>), + + /// Rust tuple with two or more fields. + Tuple(Vec<Type>), +} + +/// Type of a punctuated list. +/// +/// This refers to `syn::punctuated::Punctuated<#element, #punct>`. +/// +/// The punct string will match one of the keys in the `tokens` map. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct Punctuated { + pub element: Box<Type>, + pub punct: String, +} + +/// Features behind which a syntax tree type is cfg gated. +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct Features { + /// Type is accessible if at least one of these features is enabled against + /// the Syn dependency. + pub any: BTreeSet<String>, +} + +fn is_private(data: &Data) -> bool { + match data { + Data::Private => true, + Data::Struct(_) | Data::Enum(_) => false, + } +} + +fn private_if_absent<'de, D>(deserializer: D) -> Result<Data, D::Error> +where + D: Deserializer<'de>, +{ + let option = Option::deserialize(deserializer)?; + Ok(option.unwrap_or(Data::Private)) +} + +fn is_true(b: &bool) -> bool { + *b +} + +fn bool_true() -> bool { + true +} |