Files
x2/sub/_macros/src/patterns.rs
2025-12-27 19:54:39 -05:00

83 lines
2.4 KiB
Rust

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::<Vec<_>>();
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);
}
}