Skip to content

Commit ca97c7d

Browse files
committed
Translate expr scanner to table driven
1 parent 8039cb3 commit ca97c7d

File tree

1 file changed

+254
-66
lines changed

1 file changed

+254
-66
lines changed

src/scan_expr.rs

+254-66
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,264 @@
1-
use proc_macro2::Delimiter::{self, Brace, Bracket, Parenthesis};
1+
use self::{Action::*, Input::*};
2+
use proc_macro2::{Delimiter, Ident, Spacing, TokenTree};
23
use syn::parse::{ParseStream, Result};
3-
use syn::{
4-
token, AngleBracketedGenericArguments, BinOp, ExprPath, Lifetime, Lit, LitFloat, Member, Token,
5-
Type, UnOp,
6-
};
4+
use syn::{AngleBracketedGenericArguments, BinOp, Expr, ExprPath, Lifetime, Lit, Token, Type};
75

8-
pub(crate) fn scan_expr(input: ParseStream) -> Result<()> {
9-
let consume = |delimiter: Delimiter| {
10-
Result::unwrap(input.step(|cursor| match cursor.group(delimiter) {
11-
Some((_inside, _span, rest)) => Ok((true, rest)),
12-
None => Ok((false, *cursor)),
13-
}))
14-
};
15-
16-
macro_rules! consume {
17-
[$token:tt] => {
18-
input.parse::<Option<Token![$token]>>().unwrap().is_some()
19-
};
20-
}
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+
];
21129

22-
let mut initial = true;
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();
23194
let mut depth = 0usize;
24-
loop {
25-
if initial {
26-
if consume![&] {
27-
initial = consume![mut] || !consume![raw] || consume![const] || consume![mut];
28-
} else if consume![if] || consume![match] || consume![while] {
29-
depth += 1;
30-
} else if input.parse::<Option<Lit>>()?.is_some()
31-
|| (consume(Brace) || consume(Bracket) || consume(Parenthesis))
32-
|| (consume![async] || consume![const] || consume![loop] || consume![unsafe])
33-
&& (consume(Brace) || break)
34-
{
35-
initial = false;
36-
} else if consume![let] {
37-
while !consume![=] {
38-
if !((consume![|] || consume![ref] || consume![mut] || consume![@])
39-
|| (consume![!] || input.parse::<Option<Lit>>()?.is_some())
40-
|| (consume![..=] || consume![..] || consume![&] || consume![_])
41-
|| (consume(Brace) || consume(Bracket) || consume(Parenthesis)))
42-
{
43-
input.parse::<ExprPath>()?;
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+
}
44216
}
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+
})?
45227
}
46-
} else if input.parse::<Option<Lifetime>>()?.is_some() && !consume![:] {
47-
break;
48-
} else if input.parse::<UnOp>().is_err() {
49-
input.parse::<ExprPath>()?;
50-
initial = consume![!] || depth == 0 && input.peek(token::Brace);
51-
}
52-
} else if input.is_empty() || input.peek(Token![,]) {
53-
return Ok(());
54-
} else if depth > 0 && consume(Brace) {
55-
if consume![else] && !consume(Brace) {
56-
initial = consume![if] || break;
57-
} else {
58-
depth -= 1;
59-
}
60-
} else if input.parse::<BinOp>().is_ok() || (consume![..] | consume![=]) {
61-
initial = true;
62-
} else if consume![.] {
63-
if input.parse::<Option<LitFloat>>()?.is_none()
64-
&& (matches!(input.parse()?, Member::Named(_)) && input.peek(Token![::]))
65-
{
66-
input.parse::<AngleBracketedGenericArguments>()?;
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;
67260
}
68-
} else if consume![as] {
69-
input.parse::<Type>()?;
70-
} else if !(consume(Brace) || consume(Bracket) || consume(Parenthesis)) {
71-
break;
72261
}
262+
return Err(input.error("unsupported expression"));
73263
}
74-
75-
Err(input.error("unsupported expression"))
76264
}

0 commit comments

Comments
 (0)