diff options
Diffstat (limited to 'crates/mbe')
-rw-r--r-- | crates/mbe/src/lib.rs | 58 | ||||
-rw-r--r-- | crates/mbe/src/mbe_expander/matcher.rs | 34 | ||||
-rw-r--r-- | crates/mbe/src/mbe_expander/transcriber.rs | 47 | ||||
-rw-r--r-- | crates/mbe/src/parser.rs | 84 | ||||
-rw-r--r-- | crates/mbe/src/syntax_bridge.rs | 3 | ||||
-rw-r--r-- | crates/mbe/src/tests.rs | 48 |
6 files changed, 171 insertions, 103 deletions
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 3ad609a00..b3472879d 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs | |||
@@ -14,10 +14,10 @@ mod tests; | |||
14 | 14 | ||
15 | use std::fmt; | 15 | use std::fmt; |
16 | 16 | ||
17 | pub use tt::{Delimiter, Punct}; | 17 | pub use tt::{Delimiter, DelimiterKind, Punct}; |
18 | 18 | ||
19 | use crate::{ | 19 | use crate::{ |
20 | parser::{parse_pattern, Op}, | 20 | parser::{parse_pattern, parse_template, Op}, |
21 | tt_iter::TtIter, | 21 | tt_iter::TtIter, |
22 | }; | 22 | }; |
23 | 23 | ||
@@ -78,8 +78,24 @@ pub struct MacroRules { | |||
78 | 78 | ||
79 | #[derive(Clone, Debug, PartialEq, Eq)] | 79 | #[derive(Clone, Debug, PartialEq, Eq)] |
80 | struct Rule { | 80 | struct Rule { |
81 | lhs: tt::Subtree, | 81 | lhs: MetaTemplate, |
82 | rhs: tt::Subtree, | 82 | rhs: MetaTemplate, |
83 | } | ||
84 | |||
85 | #[derive(Clone, Debug, PartialEq, Eq)] | ||
86 | struct MetaTemplate { | ||
87 | delimiter: Option<Delimiter>, | ||
88 | tokens: Vec<Result<Op, ExpandError>>, | ||
89 | } | ||
90 | |||
91 | impl<'a> MetaTemplate { | ||
92 | fn iter(&self) -> impl Iterator<Item = &Result<Op, ExpandError>> { | ||
93 | self.tokens.iter() | ||
94 | } | ||
95 | |||
96 | fn delimiter_kind(&self) -> Option<DelimiterKind> { | ||
97 | self.delimiter.map(|it| it.kind) | ||
98 | } | ||
83 | } | 99 | } |
84 | 100 | ||
85 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | 101 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
@@ -167,7 +183,7 @@ impl MacroRules { | |||
167 | rules.push(rule); | 183 | rules.push(rule); |
168 | if let Err(()) = src.expect_char(';') { | 184 | if let Err(()) = src.expect_char(';') { |
169 | if src.len() > 0 { | 185 | if src.len() > 0 { |
170 | return Err(ParseError::Expected("expected `:`".to_string())); | 186 | return Err(ParseError::Expected("expected `;`".to_string())); |
171 | } | 187 | } |
172 | break; | 188 | break; |
173 | } | 189 | } |
@@ -201,23 +217,23 @@ impl MacroRules { | |||
201 | 217 | ||
202 | impl Rule { | 218 | impl Rule { |
203 | fn parse(src: &mut TtIter) -> Result<Rule, ParseError> { | 219 | fn parse(src: &mut TtIter) -> Result<Rule, ParseError> { |
204 | let mut lhs = src | 220 | let lhs = src |
205 | .expect_subtree() | 221 | .expect_subtree() |
206 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))? | 222 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; |
207 | .clone(); | ||
208 | lhs.delimiter = None; | ||
209 | src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?; | 223 | src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?; |
210 | src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?; | 224 | src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?; |
211 | let mut rhs = src | 225 | let rhs = src |
212 | .expect_subtree() | 226 | .expect_subtree() |
213 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))? | 227 | .map_err(|()| ParseError::Expected("expected subtree".to_string()))?; |
214 | .clone(); | 228 | |
215 | rhs.delimiter = None; | 229 | let lhs = MetaTemplate { tokens: parse_pattern(&lhs), delimiter: None }; |
230 | let rhs = MetaTemplate { tokens: parse_template(&rhs), delimiter: None }; | ||
231 | |||
216 | Ok(crate::Rule { lhs, rhs }) | 232 | Ok(crate::Rule { lhs, rhs }) |
217 | } | 233 | } |
218 | } | 234 | } |
219 | 235 | ||
220 | fn to_parse_error(e: ExpandError) -> ParseError { | 236 | fn to_parse_error(e: &ExpandError) -> ParseError { |
221 | let msg = match e { | 237 | let msg = match e { |
222 | ExpandError::InvalidRepeat => "invalid repeat".to_string(), | 238 | ExpandError::InvalidRepeat => "invalid repeat".to_string(), |
223 | _ => "invalid macro definition".to_string(), | 239 | _ => "invalid macro definition".to_string(), |
@@ -225,22 +241,22 @@ fn to_parse_error(e: ExpandError) -> ParseError { | |||
225 | ParseError::Expected(msg) | 241 | ParseError::Expected(msg) |
226 | } | 242 | } |
227 | 243 | ||
228 | fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> { | 244 | fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> { |
229 | for op in parse_pattern(pattern) { | 245 | for op in pattern.iter() { |
230 | let op = op.map_err(to_parse_error)?; | 246 | let op = op.as_ref().map_err(|e| to_parse_error(&e))?; |
231 | 247 | ||
232 | match op { | 248 | match op { |
233 | Op::TokenTree(tt::TokenTree::Subtree(subtree)) => validate(subtree)?, | 249 | Op::Subtree(subtree) => validate(&subtree)?, |
234 | Op::Repeat { subtree, separator, .. } => { | 250 | Op::Repeat { subtree, separator, .. } => { |
235 | // Checks that no repetition which could match an empty token | 251 | // Checks that no repetition which could match an empty token |
236 | // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 | 252 | // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 |
237 | 253 | ||
238 | if separator.is_none() { | 254 | if separator.is_none() { |
239 | if parse_pattern(subtree).all(|child_op| { | 255 | if subtree.iter().all(|child_op| { |
240 | match child_op.map_err(to_parse_error) { | 256 | match child_op.as_ref().map_err(to_parse_error) { |
241 | Ok(Op::Var { kind, .. }) => { | 257 | Ok(Op::Var { kind, .. }) => { |
242 | // vis is optional | 258 | // vis is optional |
243 | if kind.map_or(false, |it| it == "vis") { | 259 | if kind.as_ref().map_or(false, |it| it == "vis") { |
244 | return true; | 260 | return true; |
245 | } | 261 | } |
246 | } | 262 | } |
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs index 44722c0f1..385b46601 100644 --- a/crates/mbe/src/mbe_expander/matcher.rs +++ b/crates/mbe/src/mbe_expander/matcher.rs | |||
@@ -2,10 +2,10 @@ | |||
2 | 2 | ||
3 | use crate::{ | 3 | use crate::{ |
4 | mbe_expander::{Binding, Bindings, Fragment}, | 4 | mbe_expander::{Binding, Bindings, Fragment}, |
5 | parser::{parse_pattern, Op, RepeatKind, Separator}, | 5 | parser::{Op, RepeatKind, Separator}, |
6 | subtree_source::SubtreeTokenSource, | 6 | subtree_source::SubtreeTokenSource, |
7 | tt_iter::TtIter, | 7 | tt_iter::TtIter, |
8 | ExpandError, | 8 | ExpandError, MetaTemplate, |
9 | }; | 9 | }; |
10 | 10 | ||
11 | use super::ExpandResult; | 11 | use super::ExpandResult; |
@@ -83,7 +83,7 @@ impl Match { | |||
83 | // sense to try using it. Matching errors are added to the `Match`. It might | 83 | // sense to try using it. Matching errors are added to the `Match`. It might |
84 | // make sense to make pattern parsing a separate step? | 84 | // make sense to make pattern parsing a separate step? |
85 | 85 | ||
86 | pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> Result<Match, ExpandError> { | 86 | pub(super) fn match_(pattern: &MetaTemplate, src: &tt::Subtree) -> Result<Match, ExpandError> { |
87 | assert!(pattern.delimiter == None); | 87 | assert!(pattern.delimiter == None); |
88 | 88 | ||
89 | let mut res = Match::default(); | 89 | let mut res = Match::default(); |
@@ -101,12 +101,12 @@ pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> Result<Match, | |||
101 | 101 | ||
102 | fn match_subtree( | 102 | fn match_subtree( |
103 | res: &mut Match, | 103 | res: &mut Match, |
104 | pattern: &tt::Subtree, | 104 | pattern: &MetaTemplate, |
105 | src: &mut TtIter, | 105 | src: &mut TtIter, |
106 | ) -> Result<(), ExpandError> { | 106 | ) -> Result<(), ExpandError> { |
107 | for op in parse_pattern(pattern) { | 107 | for op in pattern.iter() { |
108 | match op? { | 108 | match op.as_ref().map_err(|err| err.clone())? { |
109 | Op::TokenTree(tt::TokenTree::Leaf(lhs)) => { | 109 | Op::Leaf(lhs) => { |
110 | let rhs = match src.expect_leaf() { | 110 | let rhs = match src.expect_leaf() { |
111 | Ok(l) => l, | 111 | Ok(l) => l, |
112 | Err(()) => { | 112 | Err(()) => { |
@@ -132,7 +132,7 @@ fn match_subtree( | |||
132 | } | 132 | } |
133 | } | 133 | } |
134 | } | 134 | } |
135 | Op::TokenTree(tt::TokenTree::Subtree(lhs)) => { | 135 | Op::Subtree(lhs) => { |
136 | let rhs = match src.expect_subtree() { | 136 | let rhs = match src.expect_subtree() { |
137 | Ok(s) => s, | 137 | Ok(s) => s, |
138 | Err(()) => { | 138 | Err(()) => { |
@@ -150,7 +150,7 @@ fn match_subtree( | |||
150 | res.add_err(err!("leftover tokens")); | 150 | res.add_err(err!("leftover tokens")); |
151 | } | 151 | } |
152 | } | 152 | } |
153 | Op::Var { name, kind } => { | 153 | Op::Var { name, kind, .. } => { |
154 | let kind = match kind { | 154 | let kind = match kind { |
155 | Some(k) => k, | 155 | Some(k) => k, |
156 | None => { | 156 | None => { |
@@ -172,7 +172,7 @@ fn match_subtree( | |||
172 | } | 172 | } |
173 | } | 173 | } |
174 | Op::Repeat { subtree, kind, separator } => { | 174 | Op::Repeat { subtree, kind, separator } => { |
175 | match_repeat(res, subtree, kind, separator, src)?; | 175 | match_repeat(res, subtree, *kind, separator, src)?; |
176 | } | 176 | } |
177 | } | 177 | } |
178 | } | 178 | } |
@@ -372,9 +372,9 @@ impl<'a> TtIter<'a> { | |||
372 | 372 | ||
373 | pub(super) fn match_repeat( | 373 | pub(super) fn match_repeat( |
374 | res: &mut Match, | 374 | res: &mut Match, |
375 | pattern: &tt::Subtree, | 375 | pattern: &MetaTemplate, |
376 | kind: RepeatKind, | 376 | kind: RepeatKind, |
377 | separator: Option<Separator>, | 377 | separator: &Option<Separator>, |
378 | src: &mut TtIter, | 378 | src: &mut TtIter, |
379 | ) -> Result<(), ExpandError> { | 379 | ) -> Result<(), ExpandError> { |
380 | // Dirty hack to make macro-expansion terminate. | 380 | // Dirty hack to make macro-expansion terminate. |
@@ -489,12 +489,12 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragmen | |||
489 | result.map(|tt| if kind == "expr" { tt.map(Fragment::Ast) } else { tt.map(Fragment::Tokens) }) | 489 | result.map(|tt| if kind == "expr" { tt.map(Fragment::Ast) } else { tt.map(Fragment::Tokens) }) |
490 | } | 490 | } |
491 | 491 | ||
492 | fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &tt::Subtree) -> Result<(), ExpandError> { | 492 | fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &MetaTemplate) -> Result<(), ExpandError> { |
493 | for op in parse_pattern(pattern) { | 493 | for op in pattern.iter() { |
494 | match op? { | 494 | match op.as_ref().map_err(|e| e.clone())? { |
495 | Op::Var { name, .. } => buf.push(name.clone()), | 495 | Op::Var { name, .. } => buf.push(name.clone()), |
496 | Op::TokenTree(tt::TokenTree::Leaf(_)) => (), | 496 | Op::Leaf(_) => (), |
497 | Op::TokenTree(tt::TokenTree::Subtree(subtree)) => collect_vars(buf, subtree)?, | 497 | Op::Subtree(subtree) => collect_vars(buf, subtree)?, |
498 | Op::Repeat { subtree, .. } => collect_vars(buf, subtree)?, | 498 | Op::Repeat { subtree, .. } => collect_vars(buf, subtree)?, |
499 | } | 499 | } |
500 | } | 500 | } |
diff --git a/crates/mbe/src/mbe_expander/transcriber.rs b/crates/mbe/src/mbe_expander/transcriber.rs index 57592dc92..57f3f104d 100644 --- a/crates/mbe/src/mbe_expander/transcriber.rs +++ b/crates/mbe/src/mbe_expander/transcriber.rs | |||
@@ -6,8 +6,8 @@ use syntax::SmolStr; | |||
6 | use super::ExpandResult; | 6 | use super::ExpandResult; |
7 | use crate::{ | 7 | use crate::{ |
8 | mbe_expander::{Binding, Bindings, Fragment}, | 8 | mbe_expander::{Binding, Bindings, Fragment}, |
9 | parser::{parse_template, Op, RepeatKind, Separator}, | 9 | parser::{Op, RepeatKind, Separator}, |
10 | ExpandError, | 10 | ExpandError, MetaTemplate, |
11 | }; | 11 | }; |
12 | 12 | ||
13 | impl Bindings { | 13 | impl Bindings { |
@@ -50,7 +50,10 @@ impl Bindings { | |||
50 | } | 50 | } |
51 | } | 51 | } |
52 | 52 | ||
53 | pub(super) fn transcribe(template: &tt::Subtree, bindings: &Bindings) -> ExpandResult<tt::Subtree> { | 53 | pub(super) fn transcribe( |
54 | template: &MetaTemplate, | ||
55 | bindings: &Bindings, | ||
56 | ) -> ExpandResult<tt::Subtree> { | ||
54 | assert!(template.delimiter == None); | 57 | assert!(template.delimiter == None); |
55 | let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; | 58 | let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; |
56 | let mut arena: Vec<tt::TokenTree> = Vec::new(); | 59 | let mut arena: Vec<tt::TokenTree> = Vec::new(); |
@@ -76,35 +79,35 @@ struct ExpandCtx<'a> { | |||
76 | 79 | ||
77 | fn expand_subtree( | 80 | fn expand_subtree( |
78 | ctx: &mut ExpandCtx, | 81 | ctx: &mut ExpandCtx, |
79 | template: &tt::Subtree, | 82 | template: &MetaTemplate, |
80 | arena: &mut Vec<tt::TokenTree>, | 83 | arena: &mut Vec<tt::TokenTree>, |
81 | ) -> ExpandResult<tt::Subtree> { | 84 | ) -> ExpandResult<tt::Subtree> { |
82 | // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation | 85 | // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation |
83 | let start_elements = arena.len(); | 86 | let start_elements = arena.len(); |
84 | let mut err = None; | 87 | let mut err = None; |
85 | for op in parse_template(template) { | 88 | for op in template.iter() { |
86 | let op = match op { | 89 | let op = match op { |
87 | Ok(op) => op, | 90 | Ok(op) => op, |
88 | Err(e) => { | 91 | Err(e) => { |
89 | err = Some(e); | 92 | err = Some(e.clone()); |
90 | break; | 93 | break; |
91 | } | 94 | } |
92 | }; | 95 | }; |
93 | match op { | 96 | match op { |
94 | Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => arena.push(tt.clone()), | 97 | Op::Leaf(tt) => arena.push(tt.clone().into()), |
95 | Op::TokenTree(tt::TokenTree::Subtree(tt)) => { | 98 | Op::Subtree(tt) => { |
96 | let ExpandResult { value: tt, err: e } = expand_subtree(ctx, tt, arena); | 99 | let ExpandResult { value: tt, err: e } = expand_subtree(ctx, &tt, arena); |
97 | err = err.or(e); | 100 | err = err.or(e); |
98 | arena.push(tt.into()); | 101 | arena.push(tt.into()); |
99 | } | 102 | } |
100 | Op::Var { name, .. } => { | 103 | Op::Var { name, id, .. } => { |
101 | let ExpandResult { value: fragment, err: e } = expand_var(ctx, name); | 104 | let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name, *id); |
102 | err = err.or(e); | 105 | err = err.or(e); |
103 | push_fragment(arena, fragment); | 106 | push_fragment(arena, fragment); |
104 | } | 107 | } |
105 | Op::Repeat { subtree, kind, separator } => { | 108 | Op::Repeat { subtree, kind, separator } => { |
106 | let ExpandResult { value: fragment, err: e } = | 109 | let ExpandResult { value: fragment, err: e } = |
107 | expand_repeat(ctx, subtree, kind, separator, arena); | 110 | expand_repeat(ctx, subtree, *kind, separator, arena); |
108 | err = err.or(e); | 111 | err = err.or(e); |
109 | push_fragment(arena, fragment) | 112 | push_fragment(arena, fragment) |
110 | } | 113 | } |
@@ -115,12 +118,10 @@ fn expand_subtree( | |||
115 | ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err } | 118 | ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err } |
116 | } | 119 | } |
117 | 120 | ||
118 | fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { | 121 | fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult<Fragment> { |
119 | if v == "crate" { | 122 | if v == "crate" { |
120 | // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. | 123 | // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. |
121 | let tt = | 124 | let tt = tt::Leaf::from(tt::Ident { text: "$crate".into(), id }).into(); |
122 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() }) | ||
123 | .into(); | ||
124 | ExpandResult::ok(Fragment::Tokens(tt)) | 125 | ExpandResult::ok(Fragment::Tokens(tt)) |
125 | } else if !ctx.bindings.contains(v) { | 126 | } else if !ctx.bindings.contains(v) { |
126 | // Note that it is possible to have a `$var` inside a macro which is not bound. | 127 | // Note that it is possible to have a `$var` inside a macro which is not bound. |
@@ -139,14 +140,8 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { | |||
139 | let tt = tt::Subtree { | 140 | let tt = tt::Subtree { |
140 | delimiter: None, | 141 | delimiter: None, |
141 | token_trees: vec![ | 142 | token_trees: vec![ |
142 | tt::Leaf::from(tt::Punct { | 143 | tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, id }).into(), |
143 | char: '$', | 144 | tt::Leaf::from(tt::Ident { text: v.clone(), id }).into(), |
144 | spacing: tt::Spacing::Alone, | ||
145 | id: tt::TokenId::unspecified(), | ||
146 | }) | ||
147 | .into(), | ||
148 | tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() }) | ||
149 | .into(), | ||
150 | ], | 145 | ], |
151 | } | 146 | } |
152 | .into(); | 147 | .into(); |
@@ -161,9 +156,9 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { | |||
161 | 156 | ||
162 | fn expand_repeat( | 157 | fn expand_repeat( |
163 | ctx: &mut ExpandCtx, | 158 | ctx: &mut ExpandCtx, |
164 | template: &tt::Subtree, | 159 | template: &MetaTemplate, |
165 | kind: RepeatKind, | 160 | kind: RepeatKind, |
166 | separator: Option<Separator>, | 161 | separator: &Option<Separator>, |
167 | arena: &mut Vec<tt::TokenTree>, | 162 | arena: &mut Vec<tt::TokenTree>, |
168 | ) -> ExpandResult<Fragment> { | 163 | ) -> ExpandResult<Fragment> { |
169 | let mut buf: Vec<tt::TokenTree> = Vec::new(); | 164 | let mut buf: Vec<tt::TokenTree> = Vec::new(); |
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index c3fdd4040..77cc739b6 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs | |||
@@ -4,16 +4,17 @@ | |||
4 | use smallvec::SmallVec; | 4 | use smallvec::SmallVec; |
5 | use syntax::SmolStr; | 5 | use syntax::SmolStr; |
6 | 6 | ||
7 | use crate::{tt_iter::TtIter, ExpandError}; | 7 | use crate::{tt_iter::TtIter, ExpandError, MetaTemplate}; |
8 | 8 | ||
9 | #[derive(Debug)] | 9 | #[derive(Clone, Debug, PartialEq, Eq)] |
10 | pub(crate) enum Op<'a> { | 10 | pub(crate) enum Op { |
11 | Var { name: &'a SmolStr, kind: Option<&'a SmolStr> }, | 11 | Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId }, |
12 | Repeat { subtree: &'a tt::Subtree, kind: RepeatKind, separator: Option<Separator> }, | 12 | Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> }, |
13 | TokenTree(&'a tt::TokenTree), | 13 | Leaf(tt::Leaf), |
14 | Subtree(MetaTemplate), | ||
14 | } | 15 | } |
15 | 16 | ||
16 | #[derive(Clone, Debug, PartialEq, Eq)] | 17 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] |
17 | pub(crate) enum RepeatKind { | 18 | pub(crate) enum RepeatKind { |
18 | ZeroOrMore, | 19 | ZeroOrMore, |
19 | OneOrMore, | 20 | OneOrMore, |
@@ -45,16 +46,12 @@ impl PartialEq for Separator { | |||
45 | } | 46 | } |
46 | } | 47 | } |
47 | 48 | ||
48 | pub(crate) fn parse_template( | 49 | pub(crate) fn parse_template(template: &tt::Subtree) -> Vec<Result<Op, ExpandError>> { |
49 | template: &tt::Subtree, | 50 | parse_inner(&template, Mode::Template) |
50 | ) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> { | ||
51 | parse_inner(template, Mode::Template) | ||
52 | } | 51 | } |
53 | 52 | ||
54 | pub(crate) fn parse_pattern( | 53 | pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Vec<Result<Op, ExpandError>> { |
55 | pattern: &tt::Subtree, | 54 | parse_inner(&pattern, Mode::Pattern) |
56 | ) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> { | ||
57 | parse_inner(pattern, Mode::Pattern) | ||
58 | } | 55 | } |
59 | 56 | ||
60 | #[derive(Clone, Copy)] | 57 | #[derive(Clone, Copy)] |
@@ -63,12 +60,13 @@ enum Mode { | |||
63 | Template, | 60 | Template, |
64 | } | 61 | } |
65 | 62 | ||
66 | fn parse_inner(src: &tt::Subtree, mode: Mode) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> { | 63 | fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ExpandError>> { |
67 | let mut src = TtIter::new(src); | 64 | let mut src = TtIter::new(&tt); |
68 | std::iter::from_fn(move || { | 65 | std::iter::from_fn(move || { |
69 | let first = src.next()?; | 66 | let first = src.next()?; |
70 | Some(next_op(first, &mut src, mode)) | 67 | Some(next_op(first, &mut src, mode)) |
71 | }) | 68 | }) |
69 | .collect() | ||
72 | } | 70 | } |
73 | 71 | ||
74 | macro_rules! err { | 72 | macro_rules! err { |
@@ -83,37 +81,46 @@ macro_rules! bail { | |||
83 | }; | 81 | }; |
84 | } | 82 | } |
85 | 83 | ||
86 | fn next_op<'a>( | 84 | fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Result<Op, ExpandError> { |
87 | first: &'a tt::TokenTree, | ||
88 | src: &mut TtIter<'a>, | ||
89 | mode: Mode, | ||
90 | ) -> Result<Op<'a>, ExpandError> { | ||
91 | let res = match first { | 85 | let res = match first { |
92 | tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. })) => { | 86 | tt::TokenTree::Leaf(leaf @ tt::Leaf::Punct(tt::Punct { char: '$', .. })) => { |
93 | // Note that the '$' itself is a valid token inside macro_rules. | 87 | // Note that the '$' itself is a valid token inside macro_rules. |
94 | let second = match src.next() { | 88 | let second = match src.next() { |
95 | None => return Ok(Op::TokenTree(first)), | 89 | None => return Ok(Op::Leaf(leaf.clone())), |
96 | Some(it) => it, | 90 | Some(it) => it, |
97 | }; | 91 | }; |
98 | match second { | 92 | match second { |
99 | tt::TokenTree::Subtree(subtree) => { | 93 | tt::TokenTree::Subtree(subtree) => { |
100 | let (separator, kind) = parse_repeat(src)?; | 94 | let (separator, kind) = parse_repeat(src)?; |
95 | let delimiter = subtree.delimiter; | ||
96 | let tokens = parse_inner(&subtree, mode); | ||
97 | let subtree = MetaTemplate { tokens, delimiter }; | ||
101 | Op::Repeat { subtree, separator, kind } | 98 | Op::Repeat { subtree, separator, kind } |
102 | } | 99 | } |
103 | tt::TokenTree::Leaf(leaf) => match leaf { | 100 | tt::TokenTree::Leaf(leaf) => match leaf { |
104 | tt::Leaf::Punct(_) => { | 101 | tt::Leaf::Punct(punct) => { |
105 | return Err(ExpandError::UnexpectedToken); | 102 | static UNDERSCORE: SmolStr = SmolStr::new_inline("_"); |
103 | |||
104 | if punct.char != '_' { | ||
105 | return Err(ExpandError::UnexpectedToken); | ||
106 | } | ||
107 | let name = UNDERSCORE.clone(); | ||
108 | let kind = eat_fragment_kind(src, mode)?; | ||
109 | let id = punct.id; | ||
110 | Op::Var { name, kind, id } | ||
106 | } | 111 | } |
107 | tt::Leaf::Ident(ident) => { | 112 | tt::Leaf::Ident(ident) => { |
108 | let name = &ident.text; | 113 | let name = ident.text.clone(); |
109 | let kind = eat_fragment_kind(src, mode)?; | 114 | let kind = eat_fragment_kind(src, mode)?; |
110 | Op::Var { name, kind } | 115 | let id = ident.id; |
116 | Op::Var { name, kind, id } | ||
111 | } | 117 | } |
112 | tt::Leaf::Literal(lit) => { | 118 | tt::Leaf::Literal(lit) => { |
113 | if is_boolean_literal(lit) { | 119 | if is_boolean_literal(&lit) { |
114 | let name = &lit.text; | 120 | let name = lit.text.clone(); |
115 | let kind = eat_fragment_kind(src, mode)?; | 121 | let kind = eat_fragment_kind(src, mode)?; |
116 | Op::Var { name, kind } | 122 | let id = lit.id; |
123 | Op::Var { name, kind, id } | ||
117 | } else { | 124 | } else { |
118 | bail!("bad var 2"); | 125 | bail!("bad var 2"); |
119 | } | 126 | } |
@@ -121,19 +128,22 @@ fn next_op<'a>( | |||
121 | }, | 128 | }, |
122 | } | 129 | } |
123 | } | 130 | } |
124 | tt => Op::TokenTree(tt), | 131 | tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()), |
132 | tt::TokenTree::Subtree(subtree) => { | ||
133 | let delimiter = subtree.delimiter; | ||
134 | let tokens = parse_inner(&subtree, mode); | ||
135 | let subtree = MetaTemplate { tokens, delimiter }; | ||
136 | Op::Subtree(subtree) | ||
137 | } | ||
125 | }; | 138 | }; |
126 | Ok(res) | 139 | Ok(res) |
127 | } | 140 | } |
128 | 141 | ||
129 | fn eat_fragment_kind<'a>( | 142 | fn eat_fragment_kind<'a>(src: &mut TtIter<'a>, mode: Mode) -> Result<Option<SmolStr>, ExpandError> { |
130 | src: &mut TtIter<'a>, | ||
131 | mode: Mode, | ||
132 | ) -> Result<Option<&'a SmolStr>, ExpandError> { | ||
133 | if let Mode::Pattern = mode { | 143 | if let Mode::Pattern = mode { |
134 | src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?; | 144 | src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?; |
135 | let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?; | 145 | let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?; |
136 | return Ok(Some(&ident.text)); | 146 | return Ok(Some(ident.text.clone())); |
137 | }; | 147 | }; |
138 | Ok(None) | 148 | Ok(None) |
139 | } | 149 | } |
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index 2bec7fd49..265c0d63d 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs | |||
@@ -313,7 +313,7 @@ trait TokenConvertor { | |||
313 | return; | 313 | return; |
314 | } | 314 | } |
315 | 315 | ||
316 | result.push(if k.is_punct() && k != UNDERSCORE { | 316 | result.push(if k.is_punct() { |
317 | assert_eq!(range.len(), TextSize::of('.')); | 317 | assert_eq!(range.len(), TextSize::of('.')); |
318 | let delim = match k { | 318 | let delim = match k { |
319 | T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])), | 319 | T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])), |
@@ -378,7 +378,6 @@ trait TokenConvertor { | |||
378 | let leaf: tt::Leaf = match k { | 378 | let leaf: tt::Leaf = match k { |
379 | T![true] | T![false] => make_leaf!(Ident), | 379 | T![true] | T![false] => make_leaf!(Ident), |
380 | IDENT => make_leaf!(Ident), | 380 | IDENT => make_leaf!(Ident), |
381 | UNDERSCORE => make_leaf!(Ident), | ||
382 | k if k.is_keyword() => make_leaf!(Ident), | 381 | k if k.is_keyword() => make_leaf!(Ident), |
383 | k if k.is_literal() => make_leaf!(Literal), | 382 | k if k.is_literal() => make_leaf!(Literal), |
384 | LIFETIME_IDENT => { | 383 | LIFETIME_IDENT => { |
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 6cd0ed205..1d9afb4fb 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs | |||
@@ -761,6 +761,18 @@ fn test_last_expr() { | |||
761 | } | 761 | } |
762 | 762 | ||
763 | #[test] | 763 | #[test] |
764 | fn test_expr_with_attr() { | ||
765 | parse_macro( | ||
766 | r#" | ||
767 | macro_rules! m { | ||
768 | ($a:expr) => {0} | ||
769 | } | ||
770 | "#, | ||
771 | ) | ||
772 | .assert_expand_items("m!(#[allow(a)]())", "0"); | ||
773 | } | ||
774 | |||
775 | #[test] | ||
764 | fn test_ty() { | 776 | fn test_ty() { |
765 | parse_macro( | 777 | parse_macro( |
766 | r#" | 778 | r#" |
@@ -1020,6 +1032,42 @@ fn test_underscore() { | |||
1020 | } | 1032 | } |
1021 | 1033 | ||
1022 | #[test] | 1034 | #[test] |
1035 | fn test_underscore_not_greedily() { | ||
1036 | parse_macro( | ||
1037 | r#" | ||
1038 | macro_rules! q { | ||
1039 | ($($a:ident)* _) => {0}; | ||
1040 | } | ||
1041 | "#, | ||
1042 | ) | ||
1043 | // `_` overlaps with `$a:ident` but rustc matches it under the `_` token | ||
1044 | .assert_expand_items(r#"q![a b c d _]"#, r#"0"#); | ||
1045 | |||
1046 | parse_macro( | ||
1047 | r#" | ||
1048 | macro_rules! q { | ||
1049 | ($($a:expr => $b:ident)* _ => $c:expr) => {0}; | ||
1050 | } | ||
1051 | "#, | ||
1052 | ) | ||
1053 | // `_ => ou` overlaps with `$a:expr => $b:ident` but rustc matches it under `_ => $c:expr` | ||
1054 | .assert_expand_items(r#"q![a => b c => d _ => ou]"#, r#"0"#); | ||
1055 | } | ||
1056 | |||
1057 | #[test] | ||
1058 | fn test_underscore_as_type() { | ||
1059 | parse_macro( | ||
1060 | r#" | ||
1061 | macro_rules! q { | ||
1062 | ($a:ty) => {0}; | ||
1063 | } | ||
1064 | "#, | ||
1065 | ) | ||
1066 | // Underscore is a type | ||
1067 | .assert_expand_items(r#"q![_]"#, r#"0"#); | ||
1068 | } | ||
1069 | |||
1070 | #[test] | ||
1023 | fn test_vertical_bar_with_pat() { | 1071 | fn test_vertical_bar_with_pat() { |
1024 | parse_macro( | 1072 | parse_macro( |
1025 | r#" | 1073 | r#" |