xref: /linux/rust/syn/scan_expr.rs (revision 784faa8eca8270671e0ed6d9d21f04bbb80fc5f7)
1*69942c0aSMiguel Ojeda // SPDX-License-Identifier: Apache-2.0 OR MIT
2*69942c0aSMiguel Ojeda 
3808c999fSMiguel Ojeda use self::{Action::*, Input::*};
4808c999fSMiguel Ojeda use proc_macro2::{Delimiter, Ident, Spacing, TokenTree};
5808c999fSMiguel Ojeda use syn::parse::{ParseStream, Result};
6808c999fSMiguel Ojeda use syn::{AngleBracketedGenericArguments, BinOp, Expr, ExprPath, Lifetime, Lit, Token, Type};
7808c999fSMiguel Ojeda 
8808c999fSMiguel Ojeda enum Input {
9808c999fSMiguel Ojeda     Keyword(&'static str),
10808c999fSMiguel Ojeda     Punct(&'static str),
11808c999fSMiguel Ojeda     ConsumeAny,
12808c999fSMiguel Ojeda     ConsumeBinOp,
13808c999fSMiguel Ojeda     ConsumeBrace,
14808c999fSMiguel Ojeda     ConsumeDelimiter,
15808c999fSMiguel Ojeda     ConsumeIdent,
16808c999fSMiguel Ojeda     ConsumeLifetime,
17808c999fSMiguel Ojeda     ConsumeLiteral,
18808c999fSMiguel Ojeda     ConsumeNestedBrace,
19808c999fSMiguel Ojeda     ExpectPath,
20808c999fSMiguel Ojeda     ExpectTurbofish,
21808c999fSMiguel Ojeda     ExpectType,
22808c999fSMiguel Ojeda     CanBeginExpr,
23808c999fSMiguel Ojeda     Otherwise,
24808c999fSMiguel Ojeda     Empty,
25808c999fSMiguel Ojeda }
26808c999fSMiguel Ojeda 
27808c999fSMiguel Ojeda enum Action {
28808c999fSMiguel Ojeda     SetState(&'static [(Input, Action)]),
29808c999fSMiguel Ojeda     IncDepth,
30808c999fSMiguel Ojeda     DecDepth,
31808c999fSMiguel Ojeda     Finish,
32808c999fSMiguel Ojeda }
33808c999fSMiguel Ojeda 
34808c999fSMiguel Ojeda static INIT: [(Input, Action); 28] = [
35808c999fSMiguel Ojeda     (ConsumeDelimiter, SetState(&POSTFIX)),
36808c999fSMiguel Ojeda     (Keyword("async"), SetState(&ASYNC)),
37808c999fSMiguel Ojeda     (Keyword("break"), SetState(&BREAK_LABEL)),
38808c999fSMiguel Ojeda     (Keyword("const"), SetState(&CONST)),
39808c999fSMiguel Ojeda     (Keyword("continue"), SetState(&CONTINUE)),
40808c999fSMiguel Ojeda     (Keyword("for"), SetState(&FOR)),
41808c999fSMiguel Ojeda     (Keyword("if"), IncDepth),
42808c999fSMiguel Ojeda     (Keyword("let"), SetState(&PATTERN)),
43808c999fSMiguel Ojeda     (Keyword("loop"), SetState(&BLOCK)),
44808c999fSMiguel Ojeda     (Keyword("match"), IncDepth),
45808c999fSMiguel Ojeda     (Keyword("move"), SetState(&CLOSURE)),
46808c999fSMiguel Ojeda     (Keyword("return"), SetState(&RETURN)),
47808c999fSMiguel Ojeda     (Keyword("static"), SetState(&CLOSURE)),
48808c999fSMiguel Ojeda     (Keyword("unsafe"), SetState(&BLOCK)),
49808c999fSMiguel Ojeda     (Keyword("while"), IncDepth),
50808c999fSMiguel Ojeda     (Keyword("yield"), SetState(&RETURN)),
51808c999fSMiguel Ojeda     (Keyword("_"), SetState(&POSTFIX)),
52808c999fSMiguel Ojeda     (Punct("!"), SetState(&INIT)),
53808c999fSMiguel Ojeda     (Punct("#"), SetState(&[(ConsumeDelimiter, SetState(&INIT))])),
54808c999fSMiguel Ojeda     (Punct("&"), SetState(&REFERENCE)),
55808c999fSMiguel Ojeda     (Punct("*"), SetState(&INIT)),
56808c999fSMiguel Ojeda     (Punct("-"), SetState(&INIT)),
57808c999fSMiguel Ojeda     (Punct("..="), SetState(&INIT)),
58808c999fSMiguel Ojeda     (Punct(".."), SetState(&RANGE)),
59808c999fSMiguel Ojeda     (Punct("|"), SetState(&CLOSURE_ARGS)),
60808c999fSMiguel Ojeda     (ConsumeLifetime, SetState(&[(Punct(":"), SetState(&INIT))])),
61808c999fSMiguel Ojeda     (ConsumeLiteral, SetState(&POSTFIX)),
62808c999fSMiguel Ojeda     (ExpectPath, SetState(&PATH)),
63808c999fSMiguel Ojeda ];
64808c999fSMiguel Ojeda 
65808c999fSMiguel Ojeda static POSTFIX: [(Input, Action); 10] = [
66808c999fSMiguel Ojeda     (Keyword("as"), SetState(&[(ExpectType, SetState(&POSTFIX))])),
67808c999fSMiguel Ojeda     (Punct("..="), SetState(&INIT)),
68808c999fSMiguel Ojeda     (Punct(".."), SetState(&RANGE)),
69808c999fSMiguel Ojeda     (Punct("."), SetState(&DOT)),
70808c999fSMiguel Ojeda     (Punct("?"), SetState(&POSTFIX)),
71808c999fSMiguel Ojeda     (ConsumeBinOp, SetState(&INIT)),
72808c999fSMiguel Ojeda     (Punct("="), SetState(&INIT)),
73808c999fSMiguel Ojeda     (ConsumeNestedBrace, SetState(&IF_THEN)),
74808c999fSMiguel Ojeda     (ConsumeDelimiter, SetState(&POSTFIX)),
75808c999fSMiguel Ojeda     (Empty, Finish),
76808c999fSMiguel Ojeda ];
77808c999fSMiguel Ojeda 
78808c999fSMiguel Ojeda static ASYNC: [(Input, Action); 3] = [
79808c999fSMiguel Ojeda     (Keyword("move"), SetState(&ASYNC)),
80808c999fSMiguel Ojeda     (Punct("|"), SetState(&CLOSURE_ARGS)),
81808c999fSMiguel Ojeda     (ConsumeBrace, SetState(&POSTFIX)),
82808c999fSMiguel Ojeda ];
83808c999fSMiguel Ojeda 
84808c999fSMiguel Ojeda static BLOCK: [(Input, Action); 1] = [(ConsumeBrace, SetState(&POSTFIX))];
85808c999fSMiguel Ojeda 
86808c999fSMiguel Ojeda static BREAK_LABEL: [(Input, Action); 2] = [
87808c999fSMiguel Ojeda     (ConsumeLifetime, SetState(&BREAK_VALUE)),
88808c999fSMiguel Ojeda     (Otherwise, SetState(&BREAK_VALUE)),
89808c999fSMiguel Ojeda ];
90808c999fSMiguel Ojeda 
91808c999fSMiguel Ojeda static BREAK_VALUE: [(Input, Action); 3] = [
92808c999fSMiguel Ojeda     (ConsumeNestedBrace, SetState(&IF_THEN)),
93808c999fSMiguel Ojeda     (CanBeginExpr, SetState(&INIT)),
94808c999fSMiguel Ojeda     (Otherwise, SetState(&POSTFIX)),
95808c999fSMiguel Ojeda ];
96808c999fSMiguel Ojeda 
97808c999fSMiguel Ojeda static CLOSURE: [(Input, Action); 7] = [
98808c999fSMiguel Ojeda     (Keyword("async"), SetState(&CLOSURE)),
99808c999fSMiguel Ojeda     (Keyword("move"), SetState(&CLOSURE)),
100808c999fSMiguel Ojeda     (Punct(","), SetState(&CLOSURE)),
101808c999fSMiguel Ojeda     (Punct(">"), SetState(&CLOSURE)),
102808c999fSMiguel Ojeda     (Punct("|"), SetState(&CLOSURE_ARGS)),
103808c999fSMiguel Ojeda     (ConsumeLifetime, SetState(&CLOSURE)),
104808c999fSMiguel Ojeda     (ConsumeIdent, SetState(&CLOSURE)),
105808c999fSMiguel Ojeda ];
106808c999fSMiguel Ojeda 
107808c999fSMiguel Ojeda static CLOSURE_ARGS: [(Input, Action); 2] = [
108808c999fSMiguel Ojeda     (Punct("|"), SetState(&CLOSURE_RET)),
109808c999fSMiguel Ojeda     (ConsumeAny, SetState(&CLOSURE_ARGS)),
110808c999fSMiguel Ojeda ];
111808c999fSMiguel Ojeda 
112808c999fSMiguel Ojeda static CLOSURE_RET: [(Input, Action); 2] = [
113808c999fSMiguel Ojeda     (Punct("->"), SetState(&[(ExpectType, SetState(&BLOCK))])),
114808c999fSMiguel Ojeda     (Otherwise, SetState(&INIT)),
115808c999fSMiguel Ojeda ];
116808c999fSMiguel Ojeda 
117808c999fSMiguel Ojeda static CONST: [(Input, Action); 2] = [
118808c999fSMiguel Ojeda     (Punct("|"), SetState(&CLOSURE_ARGS)),
119808c999fSMiguel Ojeda     (ConsumeBrace, SetState(&POSTFIX)),
120808c999fSMiguel Ojeda ];
121808c999fSMiguel Ojeda 
122808c999fSMiguel Ojeda static CONTINUE: [(Input, Action); 2] = [
123808c999fSMiguel Ojeda     (ConsumeLifetime, SetState(&POSTFIX)),
124808c999fSMiguel Ojeda     (Otherwise, SetState(&POSTFIX)),
125808c999fSMiguel Ojeda ];
126808c999fSMiguel Ojeda 
127808c999fSMiguel Ojeda static DOT: [(Input, Action); 3] = [
128808c999fSMiguel Ojeda     (Keyword("await"), SetState(&POSTFIX)),
129808c999fSMiguel Ojeda     (ConsumeIdent, SetState(&METHOD)),
130808c999fSMiguel Ojeda     (ConsumeLiteral, SetState(&POSTFIX)),
131808c999fSMiguel Ojeda ];
132808c999fSMiguel Ojeda 
133808c999fSMiguel Ojeda static FOR: [(Input, Action); 2] = [
134808c999fSMiguel Ojeda     (Punct("<"), SetState(&CLOSURE)),
135808c999fSMiguel Ojeda     (Otherwise, SetState(&PATTERN)),
136808c999fSMiguel Ojeda ];
137808c999fSMiguel Ojeda 
138808c999fSMiguel Ojeda static IF_ELSE: [(Input, Action); 2] = [(Keyword("if"), SetState(&INIT)), (ConsumeBrace, DecDepth)];
139808c999fSMiguel Ojeda static IF_THEN: [(Input, Action); 2] =
140808c999fSMiguel Ojeda     [(Keyword("else"), SetState(&IF_ELSE)), (Otherwise, DecDepth)];
141808c999fSMiguel Ojeda 
142808c999fSMiguel Ojeda static METHOD: [(Input, Action); 1] = [(ExpectTurbofish, SetState(&POSTFIX))];
143808c999fSMiguel Ojeda 
144808c999fSMiguel Ojeda static PATH: [(Input, Action); 4] = [
145808c999fSMiguel Ojeda     (Punct("!="), SetState(&INIT)),
146808c999fSMiguel Ojeda     (Punct("!"), SetState(&INIT)),
147808c999fSMiguel Ojeda     (ConsumeNestedBrace, SetState(&IF_THEN)),
148808c999fSMiguel Ojeda     (Otherwise, SetState(&POSTFIX)),
149808c999fSMiguel Ojeda ];
150808c999fSMiguel Ojeda 
151808c999fSMiguel Ojeda static PATTERN: [(Input, Action); 15] = [
152808c999fSMiguel Ojeda     (ConsumeDelimiter, SetState(&PATTERN)),
153808c999fSMiguel Ojeda     (Keyword("box"), SetState(&PATTERN)),
154808c999fSMiguel Ojeda     (Keyword("in"), IncDepth),
155808c999fSMiguel Ojeda     (Keyword("mut"), SetState(&PATTERN)),
156808c999fSMiguel Ojeda     (Keyword("ref"), SetState(&PATTERN)),
157808c999fSMiguel Ojeda     (Keyword("_"), SetState(&PATTERN)),
158808c999fSMiguel Ojeda     (Punct("!"), SetState(&PATTERN)),
159808c999fSMiguel Ojeda     (Punct("&"), SetState(&PATTERN)),
160808c999fSMiguel Ojeda     (Punct("..="), SetState(&PATTERN)),
161808c999fSMiguel Ojeda     (Punct(".."), SetState(&PATTERN)),
162808c999fSMiguel Ojeda     (Punct("="), SetState(&INIT)),
163808c999fSMiguel Ojeda     (Punct("@"), SetState(&PATTERN)),
164808c999fSMiguel Ojeda     (Punct("|"), SetState(&PATTERN)),
165808c999fSMiguel Ojeda     (ConsumeLiteral, SetState(&PATTERN)),
166808c999fSMiguel Ojeda     (ExpectPath, SetState(&PATTERN)),
167808c999fSMiguel Ojeda ];
168808c999fSMiguel Ojeda 
169808c999fSMiguel Ojeda static RANGE: [(Input, Action); 6] = [
170808c999fSMiguel Ojeda     (Punct("..="), SetState(&INIT)),
171808c999fSMiguel Ojeda     (Punct(".."), SetState(&RANGE)),
172808c999fSMiguel Ojeda     (Punct("."), SetState(&DOT)),
173808c999fSMiguel Ojeda     (ConsumeNestedBrace, SetState(&IF_THEN)),
174808c999fSMiguel Ojeda     (Empty, Finish),
175808c999fSMiguel Ojeda     (Otherwise, SetState(&INIT)),
176808c999fSMiguel Ojeda ];
177808c999fSMiguel Ojeda 
178808c999fSMiguel Ojeda static RAW: [(Input, Action); 3] = [
179808c999fSMiguel Ojeda     (Keyword("const"), SetState(&INIT)),
180808c999fSMiguel Ojeda     (Keyword("mut"), SetState(&INIT)),
181808c999fSMiguel Ojeda     (Otherwise, SetState(&POSTFIX)),
182808c999fSMiguel Ojeda ];
183808c999fSMiguel Ojeda 
184808c999fSMiguel Ojeda static REFERENCE: [(Input, Action); 3] = [
185808c999fSMiguel Ojeda     (Keyword("mut"), SetState(&INIT)),
186808c999fSMiguel Ojeda     (Keyword("raw"), SetState(&RAW)),
187808c999fSMiguel Ojeda     (Otherwise, SetState(&INIT)),
188808c999fSMiguel Ojeda ];
189808c999fSMiguel Ojeda 
190808c999fSMiguel Ojeda static RETURN: [(Input, Action); 2] = [
191808c999fSMiguel Ojeda     (CanBeginExpr, SetState(&INIT)),
192808c999fSMiguel Ojeda     (Otherwise, SetState(&POSTFIX)),
193808c999fSMiguel Ojeda ];
194808c999fSMiguel Ojeda 
scan_expr(input: ParseStream) -> Result<()>195808c999fSMiguel Ojeda pub(crate) fn scan_expr(input: ParseStream) -> Result<()> {
196808c999fSMiguel Ojeda     let mut state = INIT.as_slice();
197808c999fSMiguel Ojeda     let mut depth = 0usize;
198808c999fSMiguel Ojeda     'table: loop {
199808c999fSMiguel Ojeda         for rule in state {
200808c999fSMiguel Ojeda             if match rule.0 {
201808c999fSMiguel Ojeda                 Input::Keyword(expected) => input.step(|cursor| match cursor.ident() {
202808c999fSMiguel Ojeda                     Some((ident, rest)) if ident == expected => Ok((true, rest)),
203808c999fSMiguel Ojeda                     _ => Ok((false, *cursor)),
204808c999fSMiguel Ojeda                 })?,
205808c999fSMiguel Ojeda                 Input::Punct(expected) => input.step(|cursor| {
206808c999fSMiguel Ojeda                     let begin = *cursor;
207808c999fSMiguel Ojeda                     let mut cursor = begin;
208808c999fSMiguel Ojeda                     for (i, ch) in expected.chars().enumerate() {
209808c999fSMiguel Ojeda                         match cursor.punct() {
210808c999fSMiguel Ojeda                             Some((punct, _)) if punct.as_char() != ch => break,
211808c999fSMiguel Ojeda                             Some((_, rest)) if i == expected.len() - 1 => {
212808c999fSMiguel Ojeda                                 return Ok((true, rest));
213808c999fSMiguel Ojeda                             }
214808c999fSMiguel Ojeda                             Some((punct, rest)) if punct.spacing() == Spacing::Joint => {
215808c999fSMiguel Ojeda                                 cursor = rest;
216808c999fSMiguel Ojeda                             }
217808c999fSMiguel Ojeda                             _ => break,
218808c999fSMiguel Ojeda                         }
219808c999fSMiguel Ojeda                     }
220808c999fSMiguel Ojeda                     Ok((false, begin))
221808c999fSMiguel Ojeda                 })?,
222808c999fSMiguel Ojeda                 Input::ConsumeAny => input.parse::<Option<TokenTree>>()?.is_some(),
223808c999fSMiguel Ojeda                 Input::ConsumeBinOp => input.parse::<BinOp>().is_ok(),
224808c999fSMiguel Ojeda                 Input::ConsumeBrace | Input::ConsumeNestedBrace => {
225808c999fSMiguel Ojeda                     (matches!(rule.0, Input::ConsumeBrace) || depth > 0)
226808c999fSMiguel Ojeda                         && input.step(|cursor| match cursor.group(Delimiter::Brace) {
227808c999fSMiguel Ojeda                             Some((_inside, _span, rest)) => Ok((true, rest)),
228808c999fSMiguel Ojeda                             None => Ok((false, *cursor)),
229808c999fSMiguel Ojeda                         })?
230808c999fSMiguel Ojeda                 }
231808c999fSMiguel Ojeda                 Input::ConsumeDelimiter => input.step(|cursor| match cursor.any_group() {
232808c999fSMiguel Ojeda                     Some((_inside, _delimiter, _span, rest)) => Ok((true, rest)),
233808c999fSMiguel Ojeda                     None => Ok((false, *cursor)),
234808c999fSMiguel Ojeda                 })?,
235808c999fSMiguel Ojeda                 Input::ConsumeIdent => input.parse::<Option<Ident>>()?.is_some(),
236808c999fSMiguel Ojeda                 Input::ConsumeLifetime => input.parse::<Option<Lifetime>>()?.is_some(),
237808c999fSMiguel Ojeda                 Input::ConsumeLiteral => input.parse::<Option<Lit>>()?.is_some(),
238808c999fSMiguel Ojeda                 Input::ExpectPath => {
239808c999fSMiguel Ojeda                     input.parse::<ExprPath>()?;
240808c999fSMiguel Ojeda                     true
241808c999fSMiguel Ojeda                 }
242808c999fSMiguel Ojeda                 Input::ExpectTurbofish => {
243808c999fSMiguel Ojeda                     if input.peek(Token![::]) {
244808c999fSMiguel Ojeda                         input.parse::<AngleBracketedGenericArguments>()?;
245808c999fSMiguel Ojeda                     }
246808c999fSMiguel Ojeda                     true
247808c999fSMiguel Ojeda                 }
248808c999fSMiguel Ojeda                 Input::ExpectType => {
249808c999fSMiguel Ojeda                     Type::without_plus(input)?;
250808c999fSMiguel Ojeda                     true
251808c999fSMiguel Ojeda                 }
252808c999fSMiguel Ojeda                 Input::CanBeginExpr => Expr::peek(input),
253808c999fSMiguel Ojeda                 Input::Otherwise => true,
254808c999fSMiguel Ojeda                 Input::Empty => input.is_empty() || input.peek(Token![,]),
255808c999fSMiguel Ojeda             } {
256808c999fSMiguel Ojeda                 state = match rule.1 {
257808c999fSMiguel Ojeda                     Action::SetState(next) => next,
258808c999fSMiguel Ojeda                     Action::IncDepth => (depth += 1, &INIT).1,
259808c999fSMiguel Ojeda                     Action::DecDepth => (depth -= 1, &POSTFIX).1,
260808c999fSMiguel Ojeda                     Action::Finish => return if depth == 0 { Ok(()) } else { break },
261808c999fSMiguel Ojeda                 };
262808c999fSMiguel Ojeda                 continue 'table;
263808c999fSMiguel Ojeda             }
264808c999fSMiguel Ojeda         }
265808c999fSMiguel Ojeda         return Err(input.error("unsupported expression"));
266808c999fSMiguel Ojeda     }
267808c999fSMiguel Ojeda }
268