vacro-parser-macro 0.1.16

Internal macro implementation for vacro-parser.
Documentation
use proc_macro2::{Delimiter, Group, Punct, Spacing, TokenStream, TokenTree};
use quote::TokenStreamExt;
use syn::{
    braced, bracketed,
    ext::IdentExt,
    parenthesized,
    parse::{ParseStream, Parser},
    spanned::Spanned,
    token, Ident, Result, Token,
};

use crate::{
    ast::{
        capture::Capture,
        keyword::Keyword,
        node::{Pattern, PatternKind},
    },
    syntax::{context::ParseContext, keyword::parse_keyword},
};

fn is_rust_punctuation(content: &str) -> bool {
    matches!(
        content,
        "&" | "&&"
            | "&="
            | "@"
            | "^"
            | "^="
            | ":"
            | ","
            | "$"
            | "."
            | ".."
            | "..."
            | "..="
            | "="
            | "=="
            | "=>"
            | ">="
            | ">"
            | "<-"
            | "<="
            | "<"
            | "-"
            | "-="
            | "!="
            | "!"
            | "|"
            | "|="
            | "||"
            | "::"
            | "%"
            | "%="
            | "+"
            | "+="
            | "#"
            | "?"
            | "->"
            | ";"
            | "<<"
            | "<<="
            | ">>"
            | ">>="
            | "/"
            | "/="
            | "*"
            | "*="
            | "~"
            | "_"
    )
}

impl Pattern {
    pub fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let mut ctx = ParseContext::default();
        let mut pattern_list = vec![];
        let start_span = input.span();
        while !input.is_empty() {
            let lookahead = input.lookahead1();
            if lookahead.peek(Token![#]) {
                if input.peek2(token::Brace) {
                    // #{...} 字面量捕获:将大括号内容作为字面量模式
                    let _hash: Token![#] = input.parse()?;
                    let content;
                    let _brace = braced!(content in input);
                    let inner = Pattern::parse_raw(&content, &mut ctx)?;
                    if let PatternKind::Group { children, .. } = inner.kind {
                        pattern_list.extend(children);
                    }
                    continue;
                }
                if !input.peek2(token::Paren) {
                    let _hash_tag: Keyword = Keyword::parse(input, &mut ctx)?;
                    let start_span = _hash_tag.span();
                    let pattern = Pattern {
                        kind: PatternKind::Literal(_hash_tag),
                        span: start_span,
                        meta: None,
                    };
                    pattern_list.push(pattern);
                    continue;
                }
                let _hash_tag = input.parse::<Token![#]>()?;
                let content;
                let _paren = parenthesized!(content in input);
                let inner: TokenStream = content.parse()?;

                let mut content = TokenStream::new();
                let mut hash_punct = Punct::new('#', Spacing::Alone);
                hash_punct.set_span(_hash_tag.span);

                let mut group = Group::new(Delimiter::Parenthesis, inner);
                group.set_span(_paren.span.span());

                content.extend([TokenTree::Punct(hash_punct), TokenTree::Group(group)]);

                let parser =
                    |content: ParseStream| -> Result<Capture> { Capture::parse(content, &mut ctx) };
                let capture = parser.parse2(content)?;
                let span = capture.span;
                let pattern = Pattern {
                    kind: PatternKind::Capture(Box::new(capture)),
                    span,
                    meta: None,
                };
                pattern_list.push(pattern);
            } else if lookahead.peek(Ident::peek_any) {
                let id = Ident::parse_any(input)?;
                let keyword = parse_keyword(id, &mut ctx);
                let span = keyword.span();
                pattern_list.push(Pattern {
                    kind: PatternKind::Literal(keyword),
                    span,
                    meta: None,
                });
            } else if lookahead.peek(token::Brace)
                || lookahead.peek(token::Bracket)
                || lookahead.peek(token::Paren)
            {
                let content;
                let delimiter;
                if lookahead.peek(token::Brace) {
                    let _brace = braced!(content in input);
                    delimiter = Delimiter::Brace;
                } else if lookahead.peek(token::Bracket) {
                    let _bracket = bracketed!(content in input);
                    delimiter = Delimiter::Bracket;
                } else if lookahead.peek(token::Paren) {
                    let _paren = parenthesized!(content in input);
                    delimiter = Delimiter::Parenthesis;
                } else {
                    return Err(syn::Error::new(input.span(), "Unexpected token"));
                }
                let inner: Pattern = Pattern::parse(&content)?;
                let span = inner.span;
                let pattern = Pattern {
                    kind: PatternKind::Group {
                        delimiter,
                        children: vec![inner],
                    },
                    span,
                    meta: None,
                };
                pattern_list.push(pattern);
            } else {
                let mut collect = TokenStream::new();
                let mut punct: Punct = input.parse()?;
                let start_span = punct.span();
                let mut content = String::new();
                while punct.spacing() == Spacing::Joint && !input.is_empty() {
                    if input.peek(Token![#]) {
                        break;
                    }
                    let fork = input.fork();
                    if let Ok(next) = fork.parse::<Punct>() {
                        let mut candidate = content.clone();
                        candidate.push(punct.as_char());
                        candidate.push(next.as_char());
                        if fork.peek(Token![#]) && !is_rust_punctuation(&candidate) {
                            break;
                        }
                    }
                    content.push(punct.as_char());
                    collect.append(punct);
                    punct = input.parse()?;
                }
                let end_span = punct.span();
                collect.append(punct);
                let keyword = parse_keyword(collect, &mut ctx);
                let pattern = Pattern {
                    kind: PatternKind::Literal(keyword),
                    span: start_span.join(end_span).unwrap_or(end_span),
                    meta: None,
                };
                pattern_list.push(pattern);
            }
        }
        let end_span = if let Some(pattern) = pattern_list.last() {
            pattern.span
        } else {
            start_span
        };
        Ok(Pattern {
            kind: PatternKind::Group {
                delimiter: Delimiter::None,
                children: pattern_list,
            },
            span: start_span.join(end_span).unwrap_or(start_span),
            meta: None,
        })
    }

    /// 原始模式解析:所有 token 都视为字面量,不解析 #() 捕获
    pub(crate) fn parse_raw(input: ParseStream, ctx: &mut ParseContext) -> syn::Result<Self> {
        let mut pattern_list = vec![];
        let start_span = input.span();
        while !input.is_empty() {
            let lookahead = input.lookahead1();
            if lookahead.peek(Ident::peek_any) {
                let id = Ident::parse_any(input)?;
                let keyword = parse_keyword(id, ctx);
                let span = keyword.span();
                pattern_list.push(Pattern {
                    kind: PatternKind::Literal(keyword),
                    span,
                    meta: None,
                });
            } else if lookahead.peek(token::Brace)
                || lookahead.peek(token::Bracket)
                || lookahead.peek(token::Paren)
            {
                let content;
                let delimiter;
                if lookahead.peek(token::Brace) {
                    let _brace = braced!(content in input);
                    delimiter = Delimiter::Brace;
                } else if lookahead.peek(token::Bracket) {
                    let _bracket = bracketed!(content in input);
                    delimiter = Delimiter::Bracket;
                } else {
                    let _paren = parenthesized!(content in input);
                    delimiter = Delimiter::Parenthesis;
                }
                let inner = Pattern::parse_raw(&content, ctx)?;
                let span = inner.span;
                pattern_list.push(Pattern {
                    kind: PatternKind::Group {
                        delimiter,
                        children: vec![inner],
                    },
                    span,
                    meta: None,
                });
            } else {
                // 标点符号
                let mut collect = TokenStream::new();
                let mut punct: Punct = input.parse()?;
                let start_span = punct.span();
                while punct.spacing() == Spacing::Joint && !input.is_empty() {
                    collect.append(punct);
                    punct = input.parse()?;
                }
                let end_span = punct.span();
                collect.append(punct);
                let keyword = parse_keyword(collect, ctx);
                pattern_list.push(Pattern {
                    kind: PatternKind::Literal(keyword),
                    span: start_span.join(end_span).unwrap_or(end_span),
                    meta: None,
                });
            }
        }
        let end_span = if let Some(pattern) = pattern_list.last() {
            pattern.span
        } else {
            start_span
        };
        Ok(Pattern {
            kind: PatternKind::Group {
                delimiter: Delimiter::None,
                children: pattern_list,
            },
            span: start_span.join(end_span).unwrap_or(start_span),
            meta: None,
        })
    }
}