Skip to content

Commit 1f103d4

Browse files
authored
Merge pull request #1779 from dtolnay/scan
Translate expr scanner to table driven
2 parents ceaf4d6 + 0986a66 commit 1f103d4

File tree

4 files changed

+289
-81
lines changed

4 files changed

+289
-81
lines changed

src/data.rs

Lines changed: 3 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,8 @@ pub(crate) mod parsing {
248248
use crate::parse::discouraged::Speculative as _;
249249
use crate::parse::{Parse, ParseStream};
250250
use crate::restriction::{FieldMutability, Visibility};
251+
#[cfg(not(feature = "full"))]
252+
use crate::scan_expr::scan_expr;
251253
use crate::token;
252254
use crate::ty::Type;
253255
use crate::verbatim;
@@ -276,7 +278,7 @@ pub(crate) mod parsing {
276278
let mut discriminant: Result<Expr> = ahead.parse();
277279
if discriminant.is_ok() {
278280
input.advance_to(&ahead);
279-
} else if scan_lenient_discriminant(input).is_ok() {
281+
} else if scan_expr(input).is_ok() {
280282
discriminant = Ok(Expr::Verbatim(verbatim::between(&begin, input)));
281283
}
282284
discriminant?
@@ -294,85 +296,6 @@ pub(crate) mod parsing {
294296
}
295297
}
296298

297-
#[cfg(not(feature = "full"))]
298-
pub(crate) fn scan_lenient_discriminant(input: ParseStream) -> Result<()> {
299-
use crate::expr::Member;
300-
use crate::lifetime::Lifetime;
301-
use crate::lit::Lit;
302-
use crate::lit::LitFloat;
303-
use crate::op::{BinOp, UnOp};
304-
use crate::path::{self, AngleBracketedGenericArguments};
305-
use proc_macro2::Delimiter::{self, Brace, Bracket, Parenthesis};
306-
307-
let consume = |delimiter: Delimiter| {
308-
Result::unwrap(input.step(|cursor| match cursor.group(delimiter) {
309-
Some((_inside, _span, rest)) => Ok((true, rest)),
310-
None => Ok((false, *cursor)),
311-
}))
312-
};
313-
314-
macro_rules! consume {
315-
[$token:tt] => {
316-
input.parse::<Option<Token![$token]>>().unwrap().is_some()
317-
};
318-
}
319-
320-
let mut initial = true;
321-
let mut depth = 0usize;
322-
loop {
323-
if initial {
324-
if consume![&] {
325-
initial = consume![mut] || !consume![raw] || consume![const] || consume![mut];
326-
} else if consume![if] || consume![match] || consume![while] {
327-
depth += 1;
328-
} else if input.parse::<Option<Lit>>()?.is_some()
329-
|| (consume(Brace) || consume(Bracket) || consume(Parenthesis))
330-
|| (consume![async] || consume![const] || consume![loop] || consume![unsafe])
331-
&& (consume(Brace) || break)
332-
{
333-
initial = false;
334-
} else if consume![let] {
335-
while !consume![=] {
336-
if !((consume![|] || consume![ref] || consume![mut] || consume![@])
337-
|| (consume![!] || input.parse::<Option<Lit>>()?.is_some())
338-
|| (consume![..=] || consume![..] || consume![&] || consume![_])
339-
|| (consume(Brace) || consume(Bracket) || consume(Parenthesis)))
340-
{
341-
path::parsing::qpath(input, true)?;
342-
}
343-
}
344-
} else if input.parse::<Option<Lifetime>>()?.is_some() && !consume![:] {
345-
break;
346-
} else if input.parse::<UnOp>().is_err() {
347-
path::parsing::qpath(input, true)?;
348-
initial = consume![!] || depth == 0 && input.peek(token::Brace);
349-
}
350-
} else if input.is_empty() || input.peek(Token![,]) {
351-
return Ok(());
352-
} else if depth > 0 && consume(Brace) {
353-
if consume![else] && !consume(Brace) {
354-
initial = consume![if] || break;
355-
} else {
356-
depth -= 1;
357-
}
358-
} else if input.parse::<BinOp>().is_ok() || (consume![..] | consume![=]) {
359-
initial = true;
360-
} else if consume![.] {
361-
if input.parse::<Option<LitFloat>>()?.is_none()
362-
&& (input.parse::<Member>()?.is_named() && consume![::])
363-
{
364-
AngleBracketedGenericArguments::do_parse(None, input)?;
365-
}
366-
} else if consume![as] {
367-
input.parse::<Type>()?;
368-
} else if !(consume(Brace) || consume(Bracket) || consume(Parenthesis)) {
369-
break;
370-
}
371-
}
372-
373-
Err(input.error("unsupported expression"))
374-
}
375-
376299
#[cfg_attr(docsrs, doc(cfg(feature = "parsing")))]
377300
impl Parse for FieldsNamed {
378301
fn parse(input: ParseStream) -> Result<Self> {

src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@
264264
clippy::derivable_impls,
265265
clippy::diverging_sub_expression,
266266
clippy::doc_markdown,
267+
clippy::enum_glob_use,
267268
clippy::expl_impl_clone_on_copy,
268269
clippy::explicit_auto_deref,
269270
clippy::if_not_else,
@@ -307,6 +308,8 @@
307308
clippy::wildcard_imports,
308309
)]
309310

311+
extern crate self as syn;
312+
310313
#[cfg(feature = "proc-macro")]
311314
extern crate proc_macro;
312315

@@ -509,6 +512,9 @@ pub use crate::restriction::{FieldMutability, VisRestricted, Visibility};
509512

510513
mod sealed;
511514

515+
#[cfg(all(feature = "parsing", feature = "derive", not(feature = "full")))]
516+
mod scan_expr;
517+
512518
mod span;
513519

514520
#[cfg(all(feature = "parsing", feature = "printing"))]

src/scan_expr.rs

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
use self::{Action::*, Input::*};
2+
use proc_macro2::{Delimiter, Ident, Spacing, TokenTree};
3+
use syn::parse::{ParseStream, Result};
4+
use syn::{AngleBracketedGenericArguments, BinOp, Expr, ExprPath, Lifetime, Lit, Token, Type};
5+
6+
enum Input {
7+
Keyword(&'static str),
8+
Punct(&'static str),
9+
ConsumeAny,
10+
ConsumeBinOp,
11+
ConsumeBrace,
12+
ConsumeDelimiter,
13+
ConsumeIdent,
14+
ConsumeLifetime,
15+
ConsumeLiteral,
16+
ConsumeNestedBrace,
17+
ExpectPath,
18+
ExpectTurbofish,
19+
ExpectType,
20+
CanBeginExpr,
21+
Otherwise,
22+
Empty,
23+
}
24+
25+
enum Action {
26+
SetState(&'static [(Input, Action)]),
27+
IncDepth,
28+
DecDepth,
29+
Finish,
30+
}
31+
32+
static INIT: [(Input, Action); 28] = [
33+
(ConsumeDelimiter, SetState(&POSTFIX)),
34+
(Keyword("async"), SetState(&ASYNC)),
35+
(Keyword("break"), SetState(&BREAK_LABEL)),
36+
(Keyword("const"), SetState(&CONST)),
37+
(Keyword("continue"), SetState(&CONTINUE)),
38+
(Keyword("for"), SetState(&FOR)),
39+
(Keyword("if"), IncDepth),
40+
(Keyword("let"), SetState(&PATTERN)),
41+
(Keyword("loop"), SetState(&BLOCK)),
42+
(Keyword("match"), IncDepth),
43+
(Keyword("move"), SetState(&CLOSURE)),
44+
(Keyword("return"), SetState(&RETURN)),
45+
(Keyword("static"), SetState(&CLOSURE)),
46+
(Keyword("unsafe"), SetState(&BLOCK)),
47+
(Keyword("while"), IncDepth),
48+
(Keyword("yield"), SetState(&RETURN)),
49+
(Keyword("_"), SetState(&POSTFIX)),
50+
(Punct("!"), SetState(&INIT)),
51+
(Punct("#"), SetState(&[(ConsumeDelimiter, SetState(&INIT))])),
52+
(Punct("&"), SetState(&REFERENCE)),
53+
(Punct("*"), SetState(&INIT)),
54+
(Punct("-"), SetState(&INIT)),
55+
(Punct("..="), SetState(&INIT)),
56+
(Punct(".."), SetState(&RANGE)),
57+
(Punct("|"), SetState(&CLOSURE_ARGS)),
58+
(ConsumeLifetime, SetState(&[(Punct(":"), SetState(&INIT))])),
59+
(ConsumeLiteral, SetState(&POSTFIX)),
60+
(ExpectPath, SetState(&PATH)),
61+
];
62+
63+
static POSTFIX: [(Input, Action); 10] = [
64+
(Keyword("as"), SetState(&[(ExpectType, SetState(&POSTFIX))])),
65+
(Punct("..="), SetState(&INIT)),
66+
(Punct(".."), SetState(&RANGE)),
67+
(Punct("."), SetState(&DOT)),
68+
(Punct("?"), SetState(&POSTFIX)),
69+
(ConsumeBinOp, SetState(&INIT)),
70+
(Punct("="), SetState(&INIT)),
71+
(ConsumeNestedBrace, SetState(&IF_THEN)),
72+
(ConsumeDelimiter, SetState(&POSTFIX)),
73+
(Empty, Finish),
74+
];
75+
76+
static ASYNC: [(Input, Action); 3] = [
77+
(Keyword("move"), SetState(&ASYNC)),
78+
(Punct("|"), SetState(&CLOSURE_ARGS)),
79+
(ConsumeBrace, SetState(&POSTFIX)),
80+
];
81+
82+
static BLOCK: [(Input, Action); 1] = [(ConsumeBrace, SetState(&POSTFIX))];
83+
84+
static BREAK_LABEL: [(Input, Action); 2] = [
85+
(ConsumeLifetime, SetState(&BREAK_VALUE)),
86+
(Otherwise, SetState(&BREAK_VALUE)),
87+
];
88+
89+
static BREAK_VALUE: [(Input, Action); 3] = [
90+
(ConsumeNestedBrace, SetState(&IF_THEN)),
91+
(CanBeginExpr, SetState(&INIT)),
92+
(Otherwise, SetState(&POSTFIX)),
93+
];
94+
95+
static CLOSURE: [(Input, Action); 6] = [
96+
(Keyword("async"), SetState(&CLOSURE)),
97+
(Keyword("move"), SetState(&CLOSURE)),
98+
(Punct(","), SetState(&CLOSURE)),
99+
(Punct(">"), SetState(&CLOSURE)),
100+
(Punct("|"), SetState(&CLOSURE_ARGS)),
101+
(ConsumeLifetime, SetState(&CLOSURE)),
102+
];
103+
104+
static CLOSURE_ARGS: [(Input, Action); 2] = [
105+
(Punct("|"), SetState(&CLOSURE_RET)),
106+
(ConsumeAny, SetState(&CLOSURE_ARGS)),
107+
];
108+
109+
static CLOSURE_RET: [(Input, Action); 2] = [
110+
(Punct("->"), SetState(&[(ExpectType, SetState(&BLOCK))])),
111+
(Otherwise, SetState(&INIT)),
112+
];
113+
114+
static CONST: [(Input, Action); 2] = [
115+
(Punct("|"), SetState(&CLOSURE_ARGS)),
116+
(ConsumeBrace, SetState(&POSTFIX)),
117+
];
118+
119+
static CONTINUE: [(Input, Action); 2] = [
120+
(ConsumeLifetime, SetState(&POSTFIX)),
121+
(Otherwise, SetState(&POSTFIX)),
122+
];
123+
124+
static DOT: [(Input, Action); 3] = [
125+
(Keyword("await"), SetState(&POSTFIX)),
126+
(ConsumeIdent, SetState(&METHOD)),
127+
(ConsumeLiteral, SetState(&POSTFIX)),
128+
];
129+
130+
static FOR: [(Input, Action); 2] = [
131+
(Punct("<"), SetState(&CLOSURE)),
132+
(Otherwise, SetState(&PATTERN)),
133+
];
134+
135+
static IF_ELSE: [(Input, Action); 2] = [(Keyword("if"), SetState(&INIT)), (ConsumeBrace, DecDepth)];
136+
static IF_THEN: [(Input, Action); 2] =
137+
[(Keyword("else"), SetState(&IF_ELSE)), (Otherwise, DecDepth)];
138+
139+
static METHOD: [(Input, Action); 1] = [(ExpectTurbofish, SetState(&POSTFIX))];
140+
141+
static PATH: [(Input, Action); 4] = [
142+
(Punct("!="), SetState(&INIT)),
143+
(Punct("!"), SetState(&INIT)),
144+
(ConsumeNestedBrace, SetState(&IF_THEN)),
145+
(Otherwise, SetState(&POSTFIX)),
146+
];
147+
148+
static PATTERN: [(Input, Action); 15] = [
149+
(ConsumeDelimiter, SetState(&PATTERN)),
150+
(Keyword("box"), SetState(&PATTERN)),
151+
(Keyword("in"), IncDepth),
152+
(Keyword("mut"), SetState(&PATTERN)),
153+
(Keyword("ref"), SetState(&PATTERN)),
154+
(Keyword("_"), SetState(&PATTERN)),
155+
(Punct("!"), SetState(&PATTERN)),
156+
(Punct("&"), SetState(&PATTERN)),
157+
(Punct("..="), SetState(&PATTERN)),
158+
(Punct(".."), SetState(&PATTERN)),
159+
(Punct("="), SetState(&INIT)),
160+
(Punct("@"), SetState(&PATTERN)),
161+
(Punct("|"), SetState(&PATTERN)),
162+
(ConsumeLiteral, SetState(&PATTERN)),
163+
(ExpectPath, SetState(&PATTERN)),
164+
];
165+
166+
static RANGE: [(Input, Action); 6] = [
167+
(Punct("..="), SetState(&INIT)),
168+
(Punct(".."), SetState(&RANGE)),
169+
(Punct("."), SetState(&DOT)),
170+
(ConsumeNestedBrace, SetState(&IF_THEN)),
171+
(Empty, Finish),
172+
(Otherwise, SetState(&INIT)),
173+
];
174+
175+
static RAW: [(Input, Action); 3] = [
176+
(Keyword("const"), SetState(&INIT)),
177+
(Keyword("mut"), SetState(&INIT)),
178+
(Otherwise, SetState(&POSTFIX)),
179+
];
180+
181+
static REFERENCE: [(Input, Action); 3] = [
182+
(Keyword("mut"), SetState(&INIT)),
183+
(Keyword("raw"), SetState(&RAW)),
184+
(Otherwise, SetState(&INIT)),
185+
];
186+
187+
static RETURN: [(Input, Action); 2] = [
188+
(CanBeginExpr, SetState(&INIT)),
189+
(Otherwise, SetState(&POSTFIX)),
190+
];
191+
192+
pub(crate) fn scan_expr(input: ParseStream) -> Result<()> {
193+
let mut state = INIT.as_slice();
194+
let mut depth = 0usize;
195+
'table: loop {
196+
for rule in state {
197+
if match rule.0 {
198+
Input::Keyword(expected) => input.step(|cursor| match cursor.ident() {
199+
Some((ident, rest)) if ident == expected => Ok((true, rest)),
200+
_ => Ok((false, *cursor)),
201+
})?,
202+
Input::Punct(expected) => input.step(|cursor| {
203+
let begin = *cursor;
204+
let mut cursor = begin;
205+
for (i, ch) in expected.chars().enumerate() {
206+
match cursor.punct() {
207+
Some((punct, _)) if punct.as_char() != ch => break,
208+
Some((_, rest)) if i == expected.len() - 1 => {
209+
return Ok((true, rest));
210+
}
211+
Some((punct, rest)) if punct.spacing() == Spacing::Joint => {
212+
cursor = rest;
213+
}
214+
_ => break,
215+
}
216+
}
217+
Ok((false, begin))
218+
})?,
219+
Input::ConsumeAny => input.parse::<Option<TokenTree>>()?.is_some(),
220+
Input::ConsumeBinOp => input.parse::<BinOp>().is_ok(),
221+
Input::ConsumeBrace | Input::ConsumeNestedBrace => {
222+
(matches!(rule.0, Input::ConsumeBrace) || depth > 0)
223+
&& input.step(|cursor| match cursor.group(Delimiter::Brace) {
224+
Some((_inside, _span, rest)) => Ok((true, rest)),
225+
None => Ok((false, *cursor)),
226+
})?
227+
}
228+
Input::ConsumeDelimiter => input.step(|cursor| match cursor.any_group() {
229+
Some((_inside, _delimiter, _span, rest)) => Ok((true, rest)),
230+
None => Ok((false, *cursor)),
231+
})?,
232+
Input::ConsumeIdent => input.parse::<Option<Ident>>()?.is_some(),
233+
Input::ConsumeLifetime => input.parse::<Option<Lifetime>>()?.is_some(),
234+
Input::ConsumeLiteral => input.parse::<Option<Lit>>()?.is_some(),
235+
Input::ExpectPath => {
236+
input.parse::<ExprPath>()?;
237+
true
238+
}
239+
Input::ExpectTurbofish => {
240+
if input.peek(Token![::]) {
241+
input.parse::<AngleBracketedGenericArguments>()?;
242+
}
243+
true
244+
}
245+
Input::ExpectType => {
246+
Type::without_plus(input)?;
247+
true
248+
}
249+
Input::CanBeginExpr => Expr::peek(input),
250+
Input::Otherwise => true,
251+
Input::Empty => input.is_empty() || input.peek(Token![,]),
252+
} {
253+
state = match rule.1 {
254+
Action::SetState(next) => next,
255+
Action::IncDepth => (depth += 1, &INIT).1,
256+
Action::DecDepth => (depth -= 1, &POSTFIX).1,
257+
Action::Finish => return if depth == 0 { Ok(()) } else { break },
258+
};
259+
continue 'table;
260+
}
261+
}
262+
return Err(input.error("unsupported expression"));
263+
}
264+
}

0 commit comments

Comments
 (0)