1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#![deny(missing_docs)]
#[macro_use]
extern crate darling;
use darling::FromDeriveInput;
use quote::{format_ident, quote};
use syn::{parse_macro_input, DeriveInput};
mod content_eq;
use content_eq::node_content_eq;
mod content_display;
use content_display::node_content_display;
#[derive(Default, FromMeta)]
#[darling(default)]
struct NodeDisplay {
leaf: bool,
}
#[derive(FromDeriveInput)]
#[darling(attributes(lang_util), forward_attrs(allow, doc, cfg))]
struct NodeContentOpts {
ident: syn::Ident,
generics: syn::Generics,
#[darling(default)]
display: NodeDisplay,
}
#[proc_macro_derive(NodeContent, attributes(lang_util))]
pub fn node_content(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let opts = NodeContentOpts::from_derive_input(&input).expect("failed to parse options");
let lifetimes: Vec<_> = opts.generics.lifetimes().map(|_| quote! { '_ }).collect();
let content_eq_body = node_content_eq(&input);
let base_ident = &opts.ident;
let struct_name = if lifetimes.is_empty() {
quote! { #base_ident }
} else {
quote! { #base_ident<#(#lifetimes),*> }
};
let mut expanded = quote! {
#[automatically_derived]
impl ::lang_util::node::NodeContent for #struct_name {}
#[automatically_derived]
impl ::lang_util::node::NodeContentEq for #struct_name {
fn content_eq(&self, other: &Self) -> bool {
#content_eq_body
}
}
};
let raw_name = base_ident
.to_string()
.strip_suffix("Data")
.map(|id| format_ident!("{}", id));
if let Some(raw_name) = &raw_name {
let lifetimes: Vec<_> = input.generics.lifetimes().collect();
let type_name = if lifetimes.is_empty() {
quote! { #raw_name }
} else {
quote! { #raw_name<#(#lifetimes),*> }
};
let doc = format!("Type alias for `Node<{}>`", struct_name);
let quoted = quote! {
#[doc = #doc]
pub type #type_name = ::lang_util::node::Node<#struct_name>;
};
expanded.extend(quoted);
};
let node_name = raw_name.unwrap_or_else(|| base_ident.clone()).to_string();
let display_quoted = node_content_display(&input, &opts, &struct_name, &node_name);
expanded.extend(display_quoted);
proc_macro::TokenStream::from(expanded)
}
#[proc_macro_derive(Token, attributes(lang_util))]
pub fn token(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let lifetimes: Vec<_> = input.generics.lifetimes().map(|_| quote! { '_ }).collect();
let base_ident = &input.ident;
let enum_name = if lifetimes.is_empty() {
quote! { #base_ident }
} else {
quote! { #base_ident<#(#lifetimes),*> }
};
let quoted = quote! {
impl ::lang_util::error::Token for #enum_name {
}
};
proc_macro::TokenStream::from(quoted)
}