proclet_utils_macros/
lib.rs1use proc_macro::TokenStream;
4use proclet::{
5 op,
6 pm1::{Error, StringLiteral},
7 prelude::*,
8 proclet, punctuated,
9};
10use std::collections::{hash_map::Entry, HashMap, HashSet};
11
12#[doc(hidden)]
13#[proc_macro]
14pub fn _define_ops(input: TokenStream) -> TokenStream {
15 proclet(input, |input| {
16 let args = punctuated(StringLiteral::parser(), op(",")).parse_all(input)?;
17
18 let mut map = HashMap::<String, (bool, HashSet<char>)>::new();
19
20 for (op, _) in args {
21 let str = op.value();
22 let clen = str.chars().count();
23 if clen == 0 {
24 return Err(Error::with_span(op.span(), "empty operator"));
25 } else if clen > 1 {
26 let mut chars = op.value().chars();
27 let mut ci = chars.next().unwrap().len_utf8();
28 for ch in chars {
29 let s = str[..ci].to_string();
30 ci += ch.len_utf8();
31 map.entry(s).or_insert((false, HashSet::new())).1.insert(ch);
32 }
33 }
34 match map.entry(op.into_value()) {
35 Entry::Occupied(e) => e.into_mut().0 = true,
36 Entry::Vacant(e) => {
37 e.insert((true, HashSet::new()));
38 }
39 }
40 }
41
42 let mut output = String::from(concat!(
43 "const fn __proclet_define_ops",
44 "(str: &::core::primitive::str, next: ::core::option::Option<::core::primitive::char>)",
45 "-> ::proclet::Match<::std::borrow::Cow<'static, ::core::primitive::str>>",
46 "{ use ::proclet::Match; use ::core::option::Option; use ::std::borrow::Cow;",
47 "match (str.as_bytes(), next) {"
48 ));
49 for (str, (valid, follow)) in map {
50 let bss = to_byte_string_string(&str);
51 if !follow.is_empty() {
52 output.push('(');
53 output.push_str(&bss);
54 output.push_str(", Option::Some(");
55 let mut it = follow.into_iter().peekable();
56 while let Some(ch) = it.next() {
57 output.push_str(&format!("\'\\u{{{:x}}}\'", u32::from(ch)));
58 if it.peek().is_some() {
59 output.push('|');
60 }
61 }
62 output.push_str(")) => ");
63 if valid {
64 output.push_str(&format!("Match::Partial(Cow::Borrowed({:?})),", &str));
65 } else {
66 output.push_str("Match::NeedMore,")
67 }
68 }
69 if valid {
70 output.push('(');
71 output.push_str(&bss);
72 output.push_str(&format!(
73 ", _) => Match::Complete(Cow::Borrowed({:?})),",
74 &str
75 ));
76 }
77 }
78 output.push_str("_ => Match::NoMatch }}");
79 let output: TokenStream = output
80 .parse()
81 .expect("internal error: generated invalid code");
82 Ok(output)
83 })
84}
85
86fn to_byte_string_string(str: &str) -> String {
87 let mut output = String::from("b\"");
88 let mut chi = 0;
89 for ch in str.chars() {
90 let clen = ch.len_utf8();
91 for &b in str[chi..chi + clen].as_bytes() {
92 output.push_str(&format!("\\x{:02x}", b));
93 }
94 chi += clen;
95 }
96 output.push('\"');
97 output
98}