aboutsummaryrefslogtreecommitdiff
path: root/crates/mbe/src/lib.rs
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-08-13 09:08:11 +0100
committerAleksey Kladov <[email protected]>2020-08-13 09:36:04 +0100
commit2f45cfc415626cfae5cba89c88a25fb3225486f7 (patch)
treee04c21d108582a89efd2d3e5a680a0ed35156912 /crates/mbe/src/lib.rs
parentd42ba6397668fe28bd9cd92db829755905469a69 (diff)
Rename ra_mbe -> mbe
Diffstat (limited to 'crates/mbe/src/lib.rs')
-rw-r--r--crates/mbe/src/lib.rs278
1 files changed, 278 insertions, 0 deletions
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
new file mode 100644
index 000000000..f854ca09a
--- /dev/null
+++ b/crates/mbe/src/lib.rs
@@ -0,0 +1,278 @@
1//! `mbe` (short for Macro By Example) crate contains code for handling
2//! `macro_rules` macros. It uses `TokenTree` (from `tt` package) as the
3//! interface, although it contains some code to bridge `SyntaxNode`s and
4//! `TokenTree`s as well!
5
6mod parser;
7mod mbe_expander;
8mod syntax_bridge;
9mod tt_iter;
10mod subtree_source;
11
12#[cfg(test)]
13mod tests;
14
15pub use tt::{Delimiter, Punct};
16
17use crate::{
18 parser::{parse_pattern, Op},
19 tt_iter::TtIter,
20};
21
22#[derive(Debug, PartialEq, Eq)]
23pub enum ParseError {
24 Expected(String),
25 RepetitionEmtpyTokenTree,
26}
27
28#[derive(Debug, PartialEq, Eq, Clone)]
29pub enum ExpandError {
30 NoMatchingRule,
31 UnexpectedToken,
32 BindingError(String),
33 ConversionError,
34 InvalidRepeat,
35 ProcMacroError(tt::ExpansionError),
36}
37
38impl From<tt::ExpansionError> for ExpandError {
39 fn from(it: tt::ExpansionError) -> Self {
40 ExpandError::ProcMacroError(it)
41 }
42}
43
44pub use crate::syntax_bridge::{
45 ast_to_token_tree, parse_to_token_tree, syntax_node_to_token_tree, token_tree_to_syntax_node,
46 TokenMap,
47};
48
49/// This struct contains AST for a single `macro_rules` definition. What might
50/// be very confusing is that AST has almost exactly the same shape as
51/// `tt::TokenTree`, but there's a crucial difference: in macro rules, `$ident`
52/// and `$()*` have special meaning (see `Var` and `Repeat` data structures)
53#[derive(Clone, Debug, PartialEq, Eq)]
54pub struct MacroRules {
55 rules: Vec<Rule>,
56 /// Highest id of the token we have in TokenMap
57 shift: Shift,
58}
59
60#[derive(Clone, Debug, PartialEq, Eq)]
61struct Rule {
62 lhs: tt::Subtree,
63 rhs: tt::Subtree,
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Eq)]
67struct Shift(u32);
68
69impl Shift {
70 fn new(tt: &tt::Subtree) -> Shift {
71 // Note that TokenId is started from zero,
72 // We have to add 1 to prevent duplication.
73 let value = max_id(tt).map_or(0, |it| it + 1);
74 return Shift(value);
75
76 // Find the max token id inside a subtree
77 fn max_id(subtree: &tt::Subtree) -> Option<u32> {
78 subtree
79 .token_trees
80 .iter()
81 .filter_map(|tt| match tt {
82 tt::TokenTree::Subtree(subtree) => {
83 let tree_id = max_id(subtree);
84 match subtree.delimiter {
85 Some(it) if it.id != tt::TokenId::unspecified() => {
86 Some(tree_id.map_or(it.id.0, |t| t.max(it.id.0)))
87 }
88 _ => tree_id,
89 }
90 }
91 tt::TokenTree::Leaf(tt::Leaf::Ident(ident))
92 if ident.id != tt::TokenId::unspecified() =>
93 {
94 Some(ident.id.0)
95 }
96 _ => None,
97 })
98 .max()
99 }
100 }
101
102 /// Shift given TokenTree token id
103 fn shift_all(self, tt: &mut tt::Subtree) {
104 for t in tt.token_trees.iter_mut() {
105 match t {
106 tt::TokenTree::Leaf(leaf) => match leaf {
107 tt::Leaf::Ident(ident) => ident.id = self.shift(ident.id),
108 tt::Leaf::Punct(punct) => punct.id = self.shift(punct.id),
109 tt::Leaf::Literal(lit) => lit.id = self.shift(lit.id),
110 },
111 tt::TokenTree::Subtree(tt) => {
112 if let Some(it) = tt.delimiter.as_mut() {
113 it.id = self.shift(it.id);
114 };
115 self.shift_all(tt)
116 }
117 }
118 }
119 }
120
121 fn shift(self, id: tt::TokenId) -> tt::TokenId {
122 if id == tt::TokenId::unspecified() {
123 return id;
124 }
125 tt::TokenId(id.0 + self.0)
126 }
127
128 fn unshift(self, id: tt::TokenId) -> Option<tt::TokenId> {
129 id.0.checked_sub(self.0).map(tt::TokenId)
130 }
131}
132
133#[derive(Debug, Eq, PartialEq)]
134pub enum Origin {
135 Def,
136 Call,
137}
138
139impl MacroRules {
140 pub fn parse(tt: &tt::Subtree) -> Result<MacroRules, ParseError> {
141 // Note: this parsing can be implemented using mbe machinery itself, by
142 // matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing
143 // manually seems easier.
144 let mut src = TtIter::new(tt);
145 let mut rules = Vec::new();
146 while src.len() > 0 {
147 let rule = Rule::parse(&mut src)?;
148 rules.push(rule);
149 if let Err(()) = src.expect_char(';') {
150 if src.len() > 0 {
151 return Err(ParseError::Expected("expected `:`".to_string()));
152 }
153 break;
154 }
155 }
156
157 for rule in rules.iter() {
158 validate(&rule.lhs)?;
159 }
160
161 Ok(MacroRules { rules, shift: Shift::new(tt) })
162 }
163
164 pub fn expand(&self, tt: &tt::Subtree) -> ExpandResult<tt::Subtree> {
165 // apply shift
166 let mut tt = tt.clone();
167 self.shift.shift_all(&mut tt);
168 mbe_expander::expand(self, &tt)
169 }
170
171 pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId {
172 self.shift.shift(id)
173 }
174
175 pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, Origin) {
176 match self.shift.unshift(id) {
177 Some(id) => (id, Origin::Call),
178 None => (id, Origin::Def),
179 }
180 }
181}
182
183impl Rule {
184 fn parse(src: &mut TtIter) -> Result<Rule, ParseError> {
185 let mut lhs = src
186 .expect_subtree()
187 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?
188 .clone();
189 lhs.delimiter = None;
190 src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?;
191 src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?;
192 let mut rhs = src
193 .expect_subtree()
194 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?
195 .clone();
196 rhs.delimiter = None;
197 Ok(crate::Rule { lhs, rhs })
198 }
199}
200
201fn to_parse_error(e: ExpandError) -> ParseError {
202 let msg = match e {
203 ExpandError::InvalidRepeat => "invalid repeat".to_string(),
204 _ => "invalid macro definition".to_string(),
205 };
206 ParseError::Expected(msg)
207}
208
209fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> {
210 for op in parse_pattern(pattern) {
211 let op = op.map_err(to_parse_error)?;
212
213 match op {
214 Op::TokenTree(tt::TokenTree::Subtree(subtree)) => validate(subtree)?,
215 Op::Repeat { subtree, separator, .. } => {
216 // Checks that no repetition which could match an empty token
217 // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558
218
219 if separator.is_none() {
220 if parse_pattern(subtree).all(|child_op| {
221 match child_op.map_err(to_parse_error) {
222 Ok(Op::Var { kind, .. }) => {
223 // vis is optional
224 if kind.map_or(false, |it| it == "vis") {
225 return true;
226 }
227 }
228 Ok(Op::Repeat { kind, .. }) => {
229 return matches!(
230 kind,
231 parser::RepeatKind::ZeroOrMore | parser::RepeatKind::ZeroOrOne
232 )
233 }
234 _ => {}
235 }
236 false
237 }) {
238 return Err(ParseError::RepetitionEmtpyTokenTree);
239 }
240 }
241 validate(subtree)?
242 }
243 _ => (),
244 }
245 }
246 Ok(())
247}
248
249#[derive(Debug)]
250pub struct ExpandResult<T>(pub T, pub Option<ExpandError>);
251
252impl<T> ExpandResult<T> {
253 pub fn ok(t: T) -> ExpandResult<T> {
254 ExpandResult(t, None)
255 }
256
257 pub fn only_err(err: ExpandError) -> ExpandResult<T>
258 where
259 T: Default,
260 {
261 ExpandResult(Default::default(), Some(err))
262 }
263
264 pub fn map<U>(self, f: impl FnOnce(T) -> U) -> ExpandResult<U> {
265 ExpandResult(f(self.0), self.1)
266 }
267
268 pub fn result(self) -> Result<T, ExpandError> {
269 self.1.map(Err).unwrap_or(Ok(self.0))
270 }
271}
272
273impl<T: Default> From<Result<T, ExpandError>> for ExpandResult<T> {
274 fn from(result: Result<T, ExpandError>) -> ExpandResult<T> {
275 result
276 .map_or_else(|e| ExpandResult(Default::default(), Some(e)), |it| ExpandResult(it, None))
277 }
278}