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