use self::{Action::*, Input::*}; use proc_macro2::{Delimiter, Ident, Spacing, TokenTree}; use syn::parse::{ParseStream, Result}; use syn::{AngleBracketedGenericArguments, BinOp, Expr, ExprPath, Lifetime, Lit, Token, Type}; enum Input { Keyword(&'static str), Punct(&'static str), ConsumeAny, ConsumeBinOp, ConsumeBrace, ConsumeDelimiter, ConsumeIdent, ConsumeLifetime, ConsumeLiteral, ConsumeNestedBrace, ExpectPath, ExpectTurbofish, ExpectType, CanBeginExpr, Otherwise, Empty, } enum Action { SetState(&'static [(Input, Action)]), IncDepth, DecDepth, Finish, } static INIT: [(Input, Action); 28] = [ (ConsumeDelimiter, SetState(&POSTFIX)), (Keyword("async"), SetState(&ASYNC)), (Keyword("break"), SetState(&BREAK_LABEL)), (Keyword("const"), SetState(&CONST)), (Keyword("continue"), SetState(&CONTINUE)), (Keyword("for"), SetState(&FOR)), (Keyword("if"), IncDepth), (Keyword("let"), SetState(&PATTERN)), (Keyword("loop"), SetState(&BLOCK)), (Keyword("match"), IncDepth), (Keyword("move"), SetState(&CLOSURE)), (Keyword("return"), SetState(&RETURN)), (Keyword("static"), SetState(&CLOSURE)), (Keyword("unsafe"), SetState(&BLOCK)), (Keyword("while"), IncDepth), (Keyword("yield"), SetState(&RETURN)), (Keyword("_"), SetState(&POSTFIX)), (Punct("!"), SetState(&INIT)), (Punct("#"), SetState(&[(ConsumeDelimiter, SetState(&INIT))])), (Punct("&"), SetState(&REFERENCE)), (Punct("*"), SetState(&INIT)), (Punct("-"), SetState(&INIT)), (Punct("..="), SetState(&INIT)), (Punct(".."), SetState(&RANGE)), (Punct("|"), SetState(&CLOSURE_ARGS)), (ConsumeLifetime, SetState(&[(Punct(":"), SetState(&INIT))])), (ConsumeLiteral, SetState(&POSTFIX)), (ExpectPath, SetState(&PATH)), ]; static POSTFIX: [(Input, Action); 10] = [ (Keyword("as"), SetState(&[(ExpectType, SetState(&POSTFIX))])), (Punct("..="), SetState(&INIT)), (Punct(".."), SetState(&RANGE)), (Punct("."), SetState(&DOT)), (Punct("?"), SetState(&POSTFIX)), (ConsumeBinOp, SetState(&INIT)), (Punct("="), SetState(&INIT)), (ConsumeNestedBrace, SetState(&IF_THEN)), (ConsumeDelimiter, SetState(&POSTFIX)), (Empty, Finish), ]; static ASYNC: [(Input, Action); 3] = [ (Keyword("move"), SetState(&ASYNC)), (Punct("|"), SetState(&CLOSURE_ARGS)), (ConsumeBrace, SetState(&POSTFIX)), ]; static BLOCK: [(Input, Action); 1] = [(ConsumeBrace, SetState(&POSTFIX))]; static BREAK_LABEL: [(Input, Action); 2] = [ (ConsumeLifetime, SetState(&BREAK_VALUE)), (Otherwise, SetState(&BREAK_VALUE)), ]; static BREAK_VALUE: [(Input, Action); 3] = [ (ConsumeNestedBrace, SetState(&IF_THEN)), (CanBeginExpr, SetState(&INIT)), (Otherwise, SetState(&POSTFIX)), ]; static CLOSURE: [(Input, Action); 6] = [ (Keyword("async"), SetState(&CLOSURE)), (Keyword("move"), SetState(&CLOSURE)), (Punct(","), SetState(&CLOSURE)), (Punct(">"), SetState(&CLOSURE)), (Punct("|"), SetState(&CLOSURE_ARGS)), (ConsumeLifetime, SetState(&CLOSURE)), ]; static CLOSURE_ARGS: [(Input, Action); 2] = [ (Punct("|"), SetState(&CLOSURE_RET)), (ConsumeAny, SetState(&CLOSURE_ARGS)), ]; static CLOSURE_RET: [(Input, Action); 2] = [ (Punct("->"), SetState(&[(ExpectType, SetState(&BLOCK))])), (Otherwise, SetState(&INIT)), ]; static CONST: [(Input, Action); 2] = [ (Punct("|"), SetState(&CLOSURE_ARGS)), (ConsumeBrace, SetState(&POSTFIX)), ]; static CONTINUE: [(Input, Action); 2] = [ (ConsumeLifetime, SetState(&POSTFIX)), (Otherwise, SetState(&POSTFIX)), ]; static DOT: [(Input, Action); 3] = [ (Keyword("await"), SetState(&POSTFIX)), (ConsumeIdent, SetState(&METHOD)), (ConsumeLiteral, SetState(&POSTFIX)), ]; static FOR: [(Input, Action); 2] = [ (Punct("<"), SetState(&CLOSURE)), (Otherwise, SetState(&PATTERN)), ]; static IF_ELSE: [(Input, Action); 2] = [(Keyword("if"), SetState(&INIT)), (ConsumeBrace, DecDepth)]; static IF_THEN: [(Input, Action); 2] = [(Keyword("else"), SetState(&IF_ELSE)), (Otherwise, DecDepth)]; static METHOD: [(Input, Action); 1] = [(ExpectTurbofish, SetState(&POSTFIX))]; static PATH: [(Input, Action); 4] = [ (Punct("!="), SetState(&INIT)), (Punct("!"), SetState(&INIT)), (ConsumeNestedBrace, SetState(&IF_THEN)), (Otherwise, SetState(&POSTFIX)), ]; static PATTERN: [(Input, Action); 15] = [ (ConsumeDelimiter, SetState(&PATTERN)), (Keyword("box"), SetState(&PATTERN)), (Keyword("in"), IncDepth), (Keyword("mut"), SetState(&PATTERN)), (Keyword("ref"), SetState(&PATTERN)), (Keyword("_"), SetState(&PATTERN)), (Punct("!"), SetState(&PATTERN)), (Punct("&"), SetState(&PATTERN)), (Punct("..="), SetState(&PATTERN)), (Punct(".."), SetState(&PATTERN)), (Punct("="), SetState(&INIT)), (Punct("@"), SetState(&PATTERN)), (Punct("|"), SetState(&PATTERN)), (ConsumeLiteral, SetState(&PATTERN)), (ExpectPath, SetState(&PATTERN)), ]; static RANGE: [(Input, Action); 6] = [ (Punct("..="), SetState(&INIT)), (Punct(".."), SetState(&RANGE)), (Punct("."), SetState(&DOT)), (ConsumeNestedBrace, SetState(&IF_THEN)), (Empty, Finish), (Otherwise, SetState(&INIT)), ]; static RAW: [(Input, Action); 3] = [ (Keyword("const"), SetState(&INIT)), (Keyword("mut"), SetState(&INIT)), (Otherwise, SetState(&POSTFIX)), ]; static REFERENCE: [(Input, Action); 3] = [ (Keyword("mut"), SetState(&INIT)), (Keyword("raw"), SetState(&RAW)), (Otherwise, SetState(&INIT)), ]; static RETURN: [(Input, Action); 2] = [ (CanBeginExpr, SetState(&INIT)), (Otherwise, SetState(&POSTFIX)), ]; pub(crate) fn scan_expr(input: ParseStream) -> Result<()> { let mut state = INIT.as_slice(); let mut depth = 0usize; 'table: loop { for rule in state { if match rule.0 { Input::Keyword(expected) => input.step(|cursor| match cursor.ident() { Some((ident, rest)) if ident == expected => Ok((true, rest)), _ => Ok((false, *cursor)), })?, Input::Punct(expected) => input.step(|cursor| { let begin = *cursor; let mut cursor = begin; for (i, ch) in expected.chars().enumerate() { match cursor.punct() { Some((punct, _)) if punct.as_char() != ch => break, Some((_, rest)) if i == expected.len() - 1 => { return Ok((true, rest)); } Some((punct, rest)) if punct.spacing() == Spacing::Joint => { cursor = rest; } _ => break, } } Ok((false, begin)) })?, Input::ConsumeAny => input.parse::>()?.is_some(), Input::ConsumeBinOp => input.parse::().is_ok(), Input::ConsumeBrace | Input::ConsumeNestedBrace => { (matches!(rule.0, Input::ConsumeBrace) || depth > 0) && input.step(|cursor| match cursor.group(Delimiter::Brace) { Some((_inside, _span, rest)) => Ok((true, rest)), None => Ok((false, *cursor)), })? } Input::ConsumeDelimiter => input.step(|cursor| match cursor.any_group() { Some((_inside, _delimiter, _span, rest)) => Ok((true, rest)), None => Ok((false, *cursor)), })?, Input::ConsumeIdent => input.parse::>()?.is_some(), Input::ConsumeLifetime => input.parse::>()?.is_some(), Input::ConsumeLiteral => input.parse::>()?.is_some(), Input::ExpectPath => { input.parse::()?; true } Input::ExpectTurbofish => { if input.peek(Token![::]) { input.parse::()?; } true } Input::ExpectType => { Type::without_plus(input)?; true } Input::CanBeginExpr => Expr::peek(input), Input::Otherwise => true, Input::Empty => input.is_empty() || input.peek(Token![,]), } { state = match rule.1 { Action::SetState(next) => next, Action::IncDepth => (depth += 1, &INIT).1, Action::DecDepth => (depth -= 1, &POSTFIX).1, Action::Finish => return if depth == 0 { Ok(()) } else { break }, }; continue 'table; } } return Err(input.error("unsupported expression")); } }