| 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 | |