1use self::{Action::*, Input::*};
2use proc_macro2::{Delimiter, Ident, Spacing, TokenTree};
3use syn::parse::{ParseStream, Result};
4use syn::{AngleBracketedGenericArguments, BinOp, Expr, ExprPath, Lifetime, Lit, Token, Type};
5
6enum 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
25enum Action {
26 SetState(&'static [(Input, Action)]),
27 IncDepth,
28 DecDepth,
29 Finish,
30}
31
32static 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
63static 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
76static ASYNC: [(Input, Action); 3] = [
77 (Keyword("move"), SetState(&ASYNC)),
78 (Punct("|"), SetState(&CLOSURE_ARGS)),
79 (ConsumeBrace, SetState(&POSTFIX)),
80];
81
82static BLOCK: [(Input, Action); 1] = [(ConsumeBrace, SetState(&POSTFIX))];
83
84static BREAK_LABEL: [(Input, Action); 2] = [
85 (ConsumeLifetime, SetState(&BREAK_VALUE)),
86 (Otherwise, SetState(&BREAK_VALUE)),
87];
88
89static BREAK_VALUE: [(Input, Action); 3] = [
90 (ConsumeNestedBrace, SetState(&IF_THEN)),
91 (CanBeginExpr, SetState(&INIT)),
92 (Otherwise, SetState(&POSTFIX)),
93];
94
95static 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
104static CLOSURE_ARGS: [(Input, Action); 2] = [
105 (Punct("|"), SetState(&CLOSURE_RET)),
106 (ConsumeAny, SetState(&CLOSURE_ARGS)),
107];
108
109static CLOSURE_RET: [(Input, Action); 2] = [
110 (Punct("->"), SetState(&[(ExpectType, SetState(&BLOCK))])),
111 (Otherwise, SetState(&INIT)),
112];
113
114static CONST: [(Input, Action); 2] = [
115 (Punct("|"), SetState(&CLOSURE_ARGS)),
116 (ConsumeBrace, SetState(&POSTFIX)),
117];
118
119static CONTINUE: [(Input, Action); 2] = [
120 (ConsumeLifetime, SetState(&POSTFIX)),
121 (Otherwise, SetState(&POSTFIX)),
122];
123
124static DOT: [(Input, Action); 3] = [
125 (Keyword("await"), SetState(&POSTFIX)),
126 (ConsumeIdent, SetState(&METHOD)),
127 (ConsumeLiteral, SetState(&POSTFIX)),
128];
129
130static FOR: [(Input, Action); 2] = [
131 (Punct("<"), SetState(&CLOSURE)),
132 (Otherwise, SetState(&PATTERN)),
133];
134
135static IF_ELSE: [(Input, Action); 2] = [(Keyword("if"), SetState(&INIT)), (ConsumeBrace, DecDepth)];
136static IF_THEN: [(Input, Action); 2] =
137 [(Keyword("else"), SetState(&IF_ELSE)), (Otherwise, DecDepth)];
138
139static METHOD: [(Input, Action); 1] = [(ExpectTurbofish, SetState(&POSTFIX))];
140
141static PATH: [(Input, Action); 4] = [
142 (Punct("!="), SetState(&INIT)),
143 (Punct("!"), SetState(&INIT)),
144 (ConsumeNestedBrace, SetState(&IF_THEN)),
145 (Otherwise, SetState(&POSTFIX)),
146];
147
148static 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
166static 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
175static RAW: [(Input, Action); 3] = [
176 (Keyword("const"), SetState(&INIT)),
177 (Keyword("mut"), SetState(&INIT)),
178 (Otherwise, SetState(&POSTFIX)),
179];
180
181static REFERENCE: [(Input, Action); 3] = [
182 (Keyword("mut"), SetState(&INIT)),
183 (Keyword("raw"), SetState(&RAW)),
184 (Otherwise, SetState(&INIT)),
185];
186
187static RETURN: [(Input, Action); 2] = [
188 (CanBeginExpr, SetState(&INIT)),
189 (Otherwise, SetState(&POSTFIX)),
190];
191
192pub(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}