Skip to main content

goblin_sigscan_macros/
lib.rs

1use goblin_sigscan_pattern::Atom;
2use proc_macro::{Delimiter, Literal, TokenStream, TokenTree};
3use proc_macro_crate::{FoundCrate, crate_name};
4
5/// Compile-time pattern parser.
6///
7/// ```no_run
8/// use goblin_sigscan_macros::pattern;
9///
10/// let _macro_name = stringify!(pattern);
11/// ```
12#[proc_macro]
13pub fn pattern(input: TokenStream) -> TokenStream {
14    let mut input = input.into_iter().collect::<Vec<_>>();
15
16    if let [TokenTree::Group(group)] = &input[..]
17        && group.delimiter() == Delimiter::None
18    {
19        input = group.stream().into_iter().collect::<Vec<_>>();
20    }
21
22    let literal = match &input[..] {
23        [TokenTree::Literal(lit)] => lit,
24        _ => panic!("expected a single string literal to parse"),
25    };
26
27    let source = parse_str_literal(literal);
28    let atoms = goblin_sigscan_pattern::parse(&source)
29        .unwrap_or_else(|err| panic!("invalid pattern syntax: {err}"));
30    let crate_root = goblin_sigscan_crate_root();
31
32    let body = atoms
33        .into_iter()
34        .map(atom_to_tokens)
35        .map(|atom| format!("{crate_root}::pattern::Atom::{atom}"))
36        .collect::<Vec<_>>()
37        .join(", ");
38
39    format!("&[{body}]")
40        .parse()
41        .expect("token generation failed")
42}
43
44fn goblin_sigscan_crate_root() -> String {
45    match crate_name("goblin-sigscan") {
46        Ok(FoundCrate::Itself) => "goblin_sigscan".to_owned(),
47        Ok(FoundCrate::Name(name)) => name.replace('-', "_"),
48        Err(err) => panic!("unable to resolve goblin-sigscan crate for macro expansion: {err}"),
49    }
50}
51
52fn atom_to_tokens(atom: Atom) -> String {
53    match atom {
54        Atom::Byte(value) => format!("Byte({value})"),
55        Atom::Fuzzy(mask) => format!("Fuzzy({mask})"),
56        Atom::Save(slot) => format!("Save({slot})"),
57        Atom::Skip(skip) => format!("Skip({skip})"),
58        Atom::SkipRange(min, max) => format!("SkipRange({min}, {max})"),
59        Atom::Push(skip) => format!("Push({skip})"),
60        Atom::Pop => "Pop".to_owned(),
61        Atom::Jump1 => "Jump1".to_owned(),
62        Atom::Jump4 => "Jump4".to_owned(),
63        Atom::Ptr => "Ptr".to_owned(),
64        Atom::Pir(slot) => format!("Pir({slot})"),
65        Atom::ReadI8(slot) => format!("ReadI8({slot})"),
66        Atom::ReadU8(slot) => format!("ReadU8({slot})"),
67        Atom::ReadI16(slot) => format!("ReadI16({slot})"),
68        Atom::ReadU16(slot) => format!("ReadU16({slot})"),
69        Atom::ReadI32(slot) => format!("ReadI32({slot})"),
70        Atom::ReadU32(slot) => format!("ReadU32({slot})"),
71        Atom::Zero(slot) => format!("Zero({slot})"),
72        Atom::Back(n) => format!("Back({n})"),
73        Atom::Aligned(align) => format!("Aligned({align})"),
74        Atom::Check(slot) => format!("Check({slot})"),
75        Atom::Case(skip) => format!("Case({skip})"),
76        Atom::Break(skip) => format!("Break({skip})"),
77        Atom::Nop => "Nop".to_owned(),
78    }
79}
80
81fn parse_str_literal(input: &Literal) -> String {
82    let source = input.to_string();
83    let mut chars = source.chars();
84    let mut result = String::new();
85
86    assert_eq!(
87        chars.next(),
88        Some('"'),
89        "expected string literal starting with a quote"
90    );
91
92    loop {
93        let ch = match chars.next() {
94            Some('\\') => match chars.next() {
95                Some('\\') => '\\',
96                Some('"') => '"',
97                Some('\'') => '\'',
98                Some('n') => '\n',
99                Some('r') => '\r',
100                Some('t') => '\t',
101                Some(other) => panic!("unknown escape sequence: {other}"),
102                None => panic!("unexpected end of string literal"),
103            },
104            Some('"') => break,
105            Some(ch) => ch,
106            None => panic!("unexpected end of string literal"),
107        };
108        result.push(ch);
109    }
110
111    result
112}