|
| 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 | +} |
0 commit comments