goblin_sigscan_macros/
lib.rs1use goblin_sigscan_pattern::Atom;
2use proc_macro::{Delimiter, Literal, TokenStream, TokenTree};
3use proc_macro_crate::{FoundCrate, crate_name};
4
5#[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}