diff options
Diffstat (limited to 'crates/tt/src/lib.rs')
-rw-r--r-- | crates/tt/src/lib.rs | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs new file mode 100644 index 000000000..20c3f5eab --- /dev/null +++ b/crates/tt/src/lib.rs | |||
@@ -0,0 +1,246 @@ | |||
1 | //! `tt` crate defines a `TokenTree` data structure: this is the interface (both | ||
2 | //! input and output) of macros. It closely mirrors `proc_macro` crate's | ||
3 | //! `TokenTree`. | ||
4 | use std::{ | ||
5 | fmt::{self, Debug}, | ||
6 | panic::RefUnwindSafe, | ||
7 | }; | ||
8 | |||
9 | use stdx::impl_from; | ||
10 | |||
11 | pub use smol_str::SmolStr; | ||
12 | |||
13 | /// Represents identity of the token. | ||
14 | /// | ||
15 | /// For hygiene purposes, we need to track which expanded tokens originated from | ||
16 | /// which source tokens. We do it by assigning an distinct identity to each | ||
17 | /// source token and making sure that identities are preserved during macro | ||
18 | /// expansion. | ||
19 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
20 | pub struct TokenId(pub u32); | ||
21 | |||
22 | impl TokenId { | ||
23 | pub const fn unspecified() -> TokenId { | ||
24 | TokenId(!0) | ||
25 | } | ||
26 | } | ||
27 | |||
28 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
29 | pub enum TokenTree { | ||
30 | Leaf(Leaf), | ||
31 | Subtree(Subtree), | ||
32 | } | ||
33 | impl_from!(Leaf, Subtree for TokenTree); | ||
34 | |||
35 | impl TokenTree { | ||
36 | pub fn empty() -> Self { | ||
37 | TokenTree::Subtree(Subtree::default()) | ||
38 | } | ||
39 | } | ||
40 | |||
41 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
42 | pub enum Leaf { | ||
43 | Literal(Literal), | ||
44 | Punct(Punct), | ||
45 | Ident(Ident), | ||
46 | } | ||
47 | impl_from!(Literal, Punct, Ident for Leaf); | ||
48 | |||
49 | #[derive(Clone, PartialEq, Eq, Hash, Default)] | ||
50 | pub struct Subtree { | ||
51 | pub delimiter: Option<Delimiter>, | ||
52 | pub token_trees: Vec<TokenTree>, | ||
53 | } | ||
54 | |||
55 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
56 | pub struct Delimiter { | ||
57 | pub id: TokenId, | ||
58 | pub kind: DelimiterKind, | ||
59 | } | ||
60 | |||
61 | #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] | ||
62 | pub enum DelimiterKind { | ||
63 | Parenthesis, | ||
64 | Brace, | ||
65 | Bracket, | ||
66 | } | ||
67 | |||
68 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
69 | pub struct Literal { | ||
70 | pub text: SmolStr, | ||
71 | pub id: TokenId, | ||
72 | } | ||
73 | |||
74 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
75 | pub struct Punct { | ||
76 | pub char: char, | ||
77 | pub spacing: Spacing, | ||
78 | pub id: TokenId, | ||
79 | } | ||
80 | |||
81 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||
82 | pub enum Spacing { | ||
83 | Alone, | ||
84 | Joint, | ||
85 | } | ||
86 | |||
87 | #[derive(Debug, Clone, PartialEq, Eq, Hash)] | ||
88 | pub struct Ident { | ||
89 | pub text: SmolStr, | ||
90 | pub id: TokenId, | ||
91 | } | ||
92 | |||
93 | fn print_debug_subtree(f: &mut fmt::Formatter<'_>, subtree: &Subtree, level: usize) -> fmt::Result { | ||
94 | let align = std::iter::repeat(" ").take(level).collect::<String>(); | ||
95 | |||
96 | let aux = match subtree.delimiter.map(|it| (it.kind, it.id.0)) { | ||
97 | None => "$".to_string(), | ||
98 | Some((DelimiterKind::Parenthesis, id)) => format!("() {}", id), | ||
99 | Some((DelimiterKind::Brace, id)) => format!("{{}} {}", id), | ||
100 | Some((DelimiterKind::Bracket, id)) => format!("[] {}", id), | ||
101 | }; | ||
102 | |||
103 | if subtree.token_trees.is_empty() { | ||
104 | write!(f, "{}SUBTREE {}", align, aux)?; | ||
105 | } else { | ||
106 | writeln!(f, "{}SUBTREE {}", align, aux)?; | ||
107 | for (idx, child) in subtree.token_trees.iter().enumerate() { | ||
108 | print_debug_token(f, child, level + 1)?; | ||
109 | if idx != subtree.token_trees.len() - 1 { | ||
110 | writeln!(f)?; | ||
111 | } | ||
112 | } | ||
113 | } | ||
114 | |||
115 | Ok(()) | ||
116 | } | ||
117 | |||
118 | fn print_debug_token(f: &mut fmt::Formatter<'_>, tkn: &TokenTree, level: usize) -> fmt::Result { | ||
119 | let align = std::iter::repeat(" ").take(level).collect::<String>(); | ||
120 | |||
121 | match tkn { | ||
122 | TokenTree::Leaf(leaf) => match leaf { | ||
123 | Leaf::Literal(lit) => write!(f, "{}LITERAL {} {}", align, lit.text, lit.id.0)?, | ||
124 | Leaf::Punct(punct) => write!( | ||
125 | f, | ||
126 | "{}PUNCH {} [{}] {}", | ||
127 | align, | ||
128 | punct.char, | ||
129 | if punct.spacing == Spacing::Alone { "alone" } else { "joint" }, | ||
130 | punct.id.0 | ||
131 | )?, | ||
132 | Leaf::Ident(ident) => write!(f, "{}IDENT {} {}", align, ident.text, ident.id.0)?, | ||
133 | }, | ||
134 | TokenTree::Subtree(subtree) => { | ||
135 | print_debug_subtree(f, subtree, level)?; | ||
136 | } | ||
137 | } | ||
138 | |||
139 | Ok(()) | ||
140 | } | ||
141 | |||
142 | impl Debug for Subtree { | ||
143 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
144 | print_debug_subtree(f, self, 0) | ||
145 | } | ||
146 | } | ||
147 | |||
148 | impl fmt::Display for TokenTree { | ||
149 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
150 | match self { | ||
151 | TokenTree::Leaf(it) => fmt::Display::fmt(it, f), | ||
152 | TokenTree::Subtree(it) => fmt::Display::fmt(it, f), | ||
153 | } | ||
154 | } | ||
155 | } | ||
156 | |||
157 | impl fmt::Display for Subtree { | ||
158 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
159 | let (l, r) = match self.delimiter_kind() { | ||
160 | Some(DelimiterKind::Parenthesis) => ("(", ")"), | ||
161 | Some(DelimiterKind::Brace) => ("{", "}"), | ||
162 | Some(DelimiterKind::Bracket) => ("[", "]"), | ||
163 | None => ("", ""), | ||
164 | }; | ||
165 | f.write_str(l)?; | ||
166 | let mut needs_space = false; | ||
167 | for tt in self.token_trees.iter() { | ||
168 | if needs_space { | ||
169 | f.write_str(" ")?; | ||
170 | } | ||
171 | needs_space = true; | ||
172 | match tt { | ||
173 | TokenTree::Leaf(Leaf::Punct(p)) => { | ||
174 | needs_space = p.spacing == Spacing::Alone; | ||
175 | fmt::Display::fmt(p, f)? | ||
176 | } | ||
177 | tt => fmt::Display::fmt(tt, f)?, | ||
178 | } | ||
179 | } | ||
180 | f.write_str(r)?; | ||
181 | Ok(()) | ||
182 | } | ||
183 | } | ||
184 | |||
185 | impl fmt::Display for Leaf { | ||
186 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
187 | match self { | ||
188 | Leaf::Ident(it) => fmt::Display::fmt(it, f), | ||
189 | Leaf::Literal(it) => fmt::Display::fmt(it, f), | ||
190 | Leaf::Punct(it) => fmt::Display::fmt(it, f), | ||
191 | } | ||
192 | } | ||
193 | } | ||
194 | |||
195 | impl fmt::Display for Ident { | ||
196 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
197 | fmt::Display::fmt(&self.text, f) | ||
198 | } | ||
199 | } | ||
200 | |||
201 | impl fmt::Display for Literal { | ||
202 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
203 | fmt::Display::fmt(&self.text, f) | ||
204 | } | ||
205 | } | ||
206 | |||
207 | impl fmt::Display for Punct { | ||
208 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
209 | fmt::Display::fmt(&self.char, f) | ||
210 | } | ||
211 | } | ||
212 | |||
213 | impl Subtree { | ||
214 | /// Count the number of tokens recursively | ||
215 | pub fn count(&self) -> usize { | ||
216 | let children_count = self | ||
217 | .token_trees | ||
218 | .iter() | ||
219 | .map(|c| match c { | ||
220 | TokenTree::Subtree(c) => c.count(), | ||
221 | _ => 0, | ||
222 | }) | ||
223 | .sum::<usize>(); | ||
224 | |||
225 | self.token_trees.len() + children_count | ||
226 | } | ||
227 | |||
228 | pub fn delimiter_kind(&self) -> Option<DelimiterKind> { | ||
229 | self.delimiter.map(|it| it.kind) | ||
230 | } | ||
231 | } | ||
232 | |||
233 | pub mod buffer; | ||
234 | |||
235 | #[derive(Debug, PartialEq, Eq, Clone)] | ||
236 | pub enum ExpansionError { | ||
237 | IOError(String), | ||
238 | JsonError(String), | ||
239 | Unknown(String), | ||
240 | ExpansionError(String), | ||
241 | } | ||
242 | |||
243 | pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe { | ||
244 | fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>) | ||
245 | -> Result<Subtree, ExpansionError>; | ||
246 | } | ||