syn/
scan_expr.rs

1use self::{Action::*, Input::*};
2use proc_macro2::{Delimiter, Ident, Spacing, TokenTree};
3use syn::parse::{ParseStream, Result};
4#[allow(unused_imports)]
5//#[cfg_attr(not(test), expect(unused_imports))] // Rust 1.81+
6use 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}