vacro-parser-macro 0.1.14

Internal macro implementation for vacro-parser.
Documentation
use proc_macro2::{Delimiter, TokenStream, TokenTree};
use quote::TokenStreamExt;
use syn::{
    braced, bracketed, parenthesized,
    parse::{discouraged::Speculative, Parse, ParseStream, Parser},
    parse_quote,
    spanned::Spanned,
    token, Ident, Path, Token, Type,
};

use crate::{
    ast::{
        capture::{Binder, Capture, EnumVariant, Matcher, MatcherKind, Quantity},
        keyword::Keyword,
        node::{Pattern, PatternKind},
    },
    scope_context::next_inline_index,
    syntax::context::ParseContext,
};

/// 捕获 #(...)
impl Capture {
    pub fn parse(input: syn::parse::ParseStream, ctx: &mut ParseContext) -> syn::Result<Self> {
        let _hash_tag: Token![#] = input.parse()?;
        let start_span = _hash_tag.span;
        let content;
        let _paren = parenthesized!(content in input);

        let lookahead = content.lookahead1();
        let fork = content.fork();
        if fork.parse::<Type>().is_ok() && fork.is_empty() {
            // 匿名捕获 <Capture> 类型
            let ty: Type = content.parse()?;
            let end_span = ty.span();
            let matcher = Matcher {
                kind: MatcherKind::SynType(ty),
                span: start_span.join(end_span).unwrap_or(start_span),
            };
            let quantity = Quantity::One;
            let binder = Binder::Anonymous;
            Ok(Capture {
                _hash_tag,
                _paren,
                matcher,
                quantity,
                binder,
                edge: None,

                span: start_span.join(end_span).unwrap_or(start_span),
            })
        } else if lookahead.peek(Ident) || lookahead.peek(Token![@]) {
            // 具名捕获 <name: Capture> 与 行内捕获 <@: Capture> 及变体

            let binder = if lookahead.peek(Ident) {
                let ident: Ident = content.parse()?;
                Binder::Named(ident)
            } else {
                let _at = content.parse::<Token![@]>()?;
                let i = next_inline_index();
                Binder::Inline(i)
            };
            let mut quantity = Quantity::One;
            if content.peek(Token![?]) {
                quantity = Quantity::Optional;
                content.parse::<Token![?]>()?;
            } else if content.peek(Token![*]) {
                quantity = parse_many_quantity(&content, ctx)?;
            }
            if content.peek(Token![:]) {
                let _colon = content.parse::<Token![:]>()?;
                let matcher = Matcher::parse(&content, ctx)?;
                let end_span = matcher.span;
                Ok(Capture {
                    _hash_tag,
                    _paren,
                    binder,
                    matcher,
                    quantity,
                    edge: None,
                    span: start_span.join(end_span).unwrap_or(start_span),
                })
            } else {
                Err(content.error("expected ':' after capture name"))
            }
        } else {
            let mut quantity = Quantity::One;
            if content.peek(Token![?]) {
                quantity = Quantity::Optional;
                content.parse::<Token![?]>()?;
            } else if content.peek(Token![*]) {
                quantity = parse_many_quantity(&content, ctx)?;
            }
            let _colon = content.parse::<Token![:]>()?;
            let matcher = Matcher::parse(&content, ctx)?;
            let end_span = matcher.span;
            Ok(Capture {
                _hash_tag,
                _paren,
                quantity,
                matcher,
                binder: Binder::Anonymous,
                edge: None,
                span: start_span.join(end_span).unwrap_or(start_span),
            })
        }
    }
}

fn parse_many_quantity(input: ParseStream, ctx: &mut ParseContext) -> syn::Result<Quantity> {
    input.parse::<Token![*]>()?;
    if input.peek(token::Bracket) {
        let separator_tokens;
        let _br = bracketed!(separator_tokens in input);
        if separator_tokens.is_empty() {
            Ok(Quantity::Many(None))
        } else {
            let separator = Keyword::parse(&separator_tokens, ctx)?;
            Ok(Quantity::Many(Some(separator)))
        }
    } else {
        Ok(Quantity::Many(None))
    }
}

impl Matcher {
    pub fn parse(input: syn::parse::ParseStream, ctx: &mut ParseContext) -> syn::Result<Self> {
        let cap = if input.peek(Token![#]) {
            if input.peek2(token::Brace) {
                // #{...} 字面量捕获:将大括号内容作为字面量模式
                let _hash: Token![#] = input.parse()?;
                let start_span = _hash.span;
                let content;
                let _brace = braced!(content in input);
                let inner = Pattern::parse_raw(&content, ctx)?;
                let end_span = inner.span;
                let children = if let PatternKind::Group { children, .. } = inner.kind {
                    children
                } else {
                    vec![inner]
                };
                Matcher {
                    kind: MatcherKind::Nested(children),
                    span: start_span.join(end_span).unwrap_or(start_span),
                }
            } else if !input.peek2(token::Paren) {
                // 仅是一个 #,作为符号
                let _hash_tag = input.parse::<Token![#]>()?;
                let start_span = _hash_tag.span;
                let pattern: Pattern = Pattern::parse(input)?;
                let end_span = pattern.span;
                let hash_tag_pattern = Pattern {
                    kind: PatternKind::Literal(Keyword::Rust(String::from("#"))),
                    span: _hash_tag.span,
                    meta: None,
                };
                Matcher {
                    kind: MatcherKind::Nested(vec![hash_tag_pattern, pattern]),
                    span: start_span.join(end_span).unwrap_or(start_span),
                }
            } else {
                // 如果是 #(...),则解析为 Capture
                let capture = Capture::parse(input, ctx)?;
                let span = capture.span;
                if let MatcherKind::Enum { .. } = &capture.matcher.kind {
                    Matcher {
                        kind: capture.matcher.kind,
                        span,
                    }
                } else {
                    let pattern = Pattern {
                        kind: PatternKind::Capture(Box::new(capture)),
                        span,
                        meta: None,
                    };
                    Matcher {
                        kind: MatcherKind::Nested(vec![pattern]),
                        span,
                    }
                }
            }
        } else if input.peek(Ident) {
            if input.peek2(token::Brace) {
                let enum_name: Type = input.parse()?;
                let start_span = enum_name.span();

                let inner;
                let _brace = braced!(inner in input);
                let variants = inner.parse_terminated(EnumVariant::parse, Token![,])?;

                let span = if let Some(v) = variants.last() {
                    start_span.join(v.span()).unwrap_or(start_span)
                } else {
                    start_span
                };
                let variants = variants
                    .iter()
                    .map(|v| {
                        (
                            v.clone(),
                            Matcher {
                                span: v.span(),
                                kind: match &v {
                                    EnumVariant::Type { ty, .. } => {
                                        MatcherKind::SynType(ty.clone())
                                    }
                                    EnumVariant::Capture { pattern, .. } => {
                                        MatcherKind::Nested(vec![*pattern.clone()])
                                    }
                                },
                            },
                        )
                    })
                    .collect();
                return Ok(Matcher {
                    kind: MatcherKind::Enum {
                        enum_name,
                        variants,
                    },
                    span,
                });
            }
            let ty: Type = input.parse()?;
            let span = ty.span();
            Matcher {
                kind: MatcherKind::SynType(ty),
                span,
            }
        } else {
            let pattern: Pattern = Pattern::parse(input)?;
            let span = pattern.span;
            // 对于单一的空组,进行拆包,减少一层嵌套
            if let PatternKind::Group {
                delimiter: Delimiter::None,
                children,
            } = pattern.kind
            {
                Matcher {
                    kind: MatcherKind::Nested(children),
                    span,
                }
            } else {
                Matcher {
                    kind: MatcherKind::Nested(vec![pattern]),
                    span,
                }
            }
        };
        if !input.is_empty() {
            let start_span = cap.span;
            match cap.kind {
                MatcherKind::SynType(_) | MatcherKind::Enum { .. } => Err(syn::Error::new(
                    input.span(),
                    format!("Unexpected '{}'", input),
                )),
                MatcherKind::Nested(mut pattern_list) => {
                    let pattern: Pattern = Pattern::parse(input)?;
                    let end_span = pattern.span;
                    pattern_list.push(pattern);
                    let matcher = Matcher {
                        kind: MatcherKind::Nested(pattern_list),
                        span: start_span.join(end_span).unwrap_or(start_span),
                    };
                    Ok(matcher)
                }
            }
        } else {
            Ok(cap)
        }
    }
}

impl Parse for EnumVariant {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        // 需要支持 Type | TypeName: Type | TypeName: Pattern

        // 可能是Type或TypeName
        let fork = input.fork();
        // 尝试解析为Type,如果解析后是','或空,则结束
        if let Ok(ty) = fork.parse::<Type>() {
            if fork.is_empty() || fork.peek(Token![,]) {
                input.advance_to(&fork);
                // 如果是Type,那么必须是可简写的模式,可解析为Path
                let path: Path = parse_quote!(#ty);
                let ident = path.segments.last().unwrap();
                let ident = parse_quote!(#ident);
                return Ok(EnumVariant::Type { ident, ty });
            }
        }

        let ident: Ident = input.parse()?;
        let ident: Type = parse_quote!(#ident);

        // 否则需要是 ':'
        let _colon: Token![:] = input.parse()?;
        let fork = input.fork();
        // 可能是Type或Pattern
        if let Ok(ty) = fork.parse::<Type>() {
            // 必须检查边界:Type 后应该是 ',' 或结束
            if fork.is_empty() || fork.peek(Token![,]) {
                input.advance_to(&fork);
                return Ok(EnumVariant::Type { ident, ty });
            }
        }
        {
            let fork = input.fork();
            // 如果是Pattern,那需要确认边界,即找到最近的 ','
            // 否则Pattern会贪婪匹配,将其他分支吞掉

            // 如果Pattern中有逗号,可能会导致边界出错
            // 这要求Pattern中的','必须包裹在Group中
            let mut tokens = TokenStream::new();
            while !fork.peek(Token![,]) && !fork.is_empty() {
                tokens.append(fork.parse::<TokenTree>()?);
            }
            let parser = |input: ParseStream| -> syn::Result<Pattern> { Pattern::parse(input) };
            let pattern = parser.parse2(tokens)?;
            input.advance_to(&fork);
            let captures = pattern.collect_captures();
            let named = if let Some(cap) = captures.first() {
                !cap.is_inline
            } else {
                false
            };
            Ok(EnumVariant::Capture {
                ident,
                named,
                fields: captures,
                pattern: Box::new(pattern),
            })
        }
    }
}

impl EnumVariant {
    fn span(&self) -> proc_macro2::Span {
        match self {
            EnumVariant::Capture { ident, pattern, .. } => {
                ident.span().join(pattern.span).unwrap_or(ident.span())
            }
            EnumVariant::Type { ident, ty } => ident.span().join(ty.span()).unwrap_or(ident.span()),
        }
    }
}