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