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