Rewrite the AST to be a bit more user-friendly
This commit is a relatively large rewrite of the AST that `syn` exposes. The
main change is to expose enums-of-structs rather than
enums-with-huge-tuple-variants. The best example of this is `ItemKind::Fn` which
changed from:
enum ItemKind {
Fn(Box<FnDecl>, Unsafety, Constness, Option<Abi>, Generics, Box<Block>),
...
}
to
enum ItemKind {
Fn(ItemFn),
...
}
struct ItemFn {
decl: Box<FnDecl>,
unsafety: Unsafety,
constness: Constness,
abi: Option<Abi>,
generics: Generics,
block: Box<Block>,
}
This change serves a few purposes:
* It's now much easier to add fields to each variant of the ast, ast struct
fields tend to be "by default ignored" in most contexts.
* It's much easier to document what each field is, as each field can have
dedicated documentation.
* There's now canonicalized names for each field (the name of the field) which
can help match `match` statements more consistent across a codebase.
A downside of this representation is that it can be a little more verbose to
work with in `match` statements and during constructions. Overall though I'd
feel at least that the readability improved significantly despite the extra
words required to do various operations.
Closes #136
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000..656fe90
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,101 @@
+macro_rules! ast_struct {
+ (
+ $(#[$attr:meta])*
+ pub struct $name:ident {
+ $(
+ $(#[$field_attr:meta])*
+ pub $field:ident: $ty:ty,
+ )*
+ }
+ ) => {
+ $(#[$attr])*
+ #[derive(Debug, Clone, Eq, PartialEq, Hash)]
+ pub struct $name {
+ $(
+ $(#[$field_attr])*
+ pub $field: $ty,
+ )*
+ }
+ }
+}
+
+macro_rules! ast_enum {
+ (
+ $(#[$enum_attr:meta])*
+ pub enum $name:ident { $($variants:tt)* }
+ ) => (
+ $(#[$enum_attr])*
+ #[derive(Debug, Clone, Eq, PartialEq, Hash)]
+ pub enum $name {
+ $($variants)*
+ }
+ )
+}
+
+macro_rules! ast_enum_of_structs {
+ (
+ $(#[$enum_attr:meta])*
+ pub enum $name:ident {
+ $(
+ $(#[$variant_attr:meta])*
+ pub $variant:ident($member:ident $($rest:tt)*),
+ )*
+ }
+
+ $($remaining:tt)*
+ ) => (
+ ast_enum! {
+ $(#[$enum_attr])*
+ pub enum $name {
+ $(
+ $(#[$variant_attr])*
+ $variant($member),
+ )*
+ }
+ }
+
+ $(
+ maybe_ast_struct! {
+ $(#[$variant_attr])*
+ pub struct $member $($rest)*
+ }
+
+ impl From<$member> for $name {
+ fn from(e: $member) -> $name {
+ $name::$variant(e)
+ }
+ }
+ )*
+
+ generate_to_tokens! {
+ $($remaining)*
+ enum $name { $($variant,)* }
+ }
+ )
+}
+
+macro_rules! generate_to_tokens {
+ (do_not_generate_to_tokens $($foo:tt)*) => ();
+
+ (enum $name:ident { $($variant:ident,)* }) => (
+ #[cfg(feature = "printing")]
+ impl ::quote::ToTokens for $name {
+ fn to_tokens(&self, tokens: &mut ::quote::Tokens) {
+ match *self {
+ $(
+ $name::$variant(ref e) => e.to_tokens(tokens),
+ )*
+ }
+ }
+ }
+ )
+}
+
+macro_rules! maybe_ast_struct {
+ (
+ $(#[$attr:meta])*
+ pub struct $name:ident
+ ) => ();
+
+ ($($rest:tt)*) => (ast_struct! { $($rest)* });
+}