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