use core::{cmp, fmt, mem, str}; use proc_macro::{Delimiter, Literal, TokenStream, TokenTree}; mod atoms { include!("../../xpat/src/atoms.rs"); } mod parser { include!("../../xpat/src/parser.rs"); } /// Compile time pattern parser. /// /// ```ignore /// const PATTERN: &[pelite::pattern::Atom] = pattern!("pattern string"); /// ``` pub fn proc_pattern(input: TokenStream) -> TokenStream { let mut string = String::new(); parse_tokens(input, &mut string); let pattern = match parser::parse(&string) { Ok(pattern) => pattern, Err(err) => panic!("invalid pattern syntax: {}", err), }; format!("{{ use x::xpat::Atom::*; &{:?} as x::Pattern }}", pattern).parse().unwrap() } pub fn parse_tokens(input: TokenStream, out: &mut String) { let input = input.into_iter().collect::>(); for token in input { match token { TokenTree::Group(g) if g.delimiter() == Delimiter::None => { parse_tokens(g.stream(), out); } TokenTree::Punct(p) if p.as_char() == ',' => {} TokenTree::Literal(lit) => { parse_str_literal(&lit, out); } TokenTree::Group(g) => { panic!("Unexpected Delimiter group") } TokenTree::Ident(_) => { panic!("Unexpected identifier") } TokenTree::Punct(p) => { panic!("Unexpected punctuation") } } } } fn parse_str_literal(input: &Literal, out: &mut String) { let input = input.to_string(); let mut chars = input.chars(); if chars.next() != Some('"') { panic!("expected string literal starting with a `\"` and no extraneous whitespace"); } loop { let chr = match chars.next() { Some('\\') => { match chars.next() { Some('\\') => '\\', Some('\'') => '\'', Some('\"') => '\"', Some('t') => '\t', Some('r') => '\r', Some('n') => '\n', Some('u') => panic!("unicode escape sequence not supported"), Some(chr) => panic!("unknown escape sequence: {}", chr), None => panic!(""), } }, Some('"') => break, Some(chr) => chr, None => panic!("unexpected end of string literal, missing `\"` terminator?"), }; out.push(chr); } }