aboutsummaryrefslogtreecommitdiff
path: root/crates/tt/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/tt/src/lib.rs')
-rw-r--r--crates/tt/src/lib.rs246
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`.
4use std::{
5 fmt::{self, Debug},
6 panic::RefUnwindSafe,
7};
8
9use stdx::impl_from;
10
11pub 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)]
20pub struct TokenId(pub u32);
21
22impl TokenId {
23 pub const fn unspecified() -> TokenId {
24 TokenId(!0)
25 }
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29pub enum TokenTree {
30 Leaf(Leaf),
31 Subtree(Subtree),
32}
33impl_from!(Leaf, Subtree for TokenTree);
34
35impl TokenTree {
36 pub fn empty() -> Self {
37 TokenTree::Subtree(Subtree::default())
38 }
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Hash)]
42pub enum Leaf {
43 Literal(Literal),
44 Punct(Punct),
45 Ident(Ident),
46}
47impl_from!(Literal, Punct, Ident for Leaf);
48
49#[derive(Clone, PartialEq, Eq, Hash, Default)]
50pub struct Subtree {
51 pub delimiter: Option<Delimiter>,
52 pub token_trees: Vec<TokenTree>,
53}
54
55#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
56pub struct Delimiter {
57 pub id: TokenId,
58 pub kind: DelimiterKind,
59}
60
61#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
62pub enum DelimiterKind {
63 Parenthesis,
64 Brace,
65 Bracket,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Hash)]
69pub struct Literal {
70 pub text: SmolStr,
71 pub id: TokenId,
72}
73
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75pub struct Punct {
76 pub char: char,
77 pub spacing: Spacing,
78 pub id: TokenId,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
82pub enum Spacing {
83 Alone,
84 Joint,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq, Hash)]
88pub struct Ident {
89 pub text: SmolStr,
90 pub id: TokenId,
91}
92
93fn 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
118fn 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
142impl Debug for Subtree {
143 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
144 print_debug_subtree(f, self, 0)
145 }
146}
147
148impl 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
157impl 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
185impl 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
195impl fmt::Display for Ident {
196 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
197 fmt::Display::fmt(&self.text, f)
198 }
199}
200
201impl fmt::Display for Literal {
202 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
203 fmt::Display::fmt(&self.text, f)
204 }
205}
206
207impl fmt::Display for Punct {
208 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
209 fmt::Display::fmt(&self.char, f)
210 }
211}
212
213impl 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
233pub mod buffer;
234
235#[derive(Debug, PartialEq, Eq, Clone)]
236pub enum ExpansionError {
237 IOError(String),
238 JsonError(String),
239 Unknown(String),
240 ExpansionError(String),
241}
242
243pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe {
244 fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>)
245 -> Result<Subtree, ExpansionError>;
246}