politeness_macro_impl/
lib.rs

1#![feature(proc_macro_hygiene)]
2#![feature(proc_macro_span)]
3#![feature(extend_one)]
4
5use proc_macro::*;
6use proc_macro::TokenTree::Ident;
7
8enum PolitenessResult {
9    Correct,
10    TooPolite,
11    TooRude
12}
13
14fn count_tokens(strm: TokenStream) -> (i32, i32) {
15    let mut total = 0;
16    let mut polite = 0;
17    let mut input = strm.clone().into_iter();
18    while let Some(t) = input.next() {
19        match t {
20            TokenTree::Group(x) => {
21                let l = count_tokens(x.stream());
22                total += l.0;
23                polite += l.1;
24            },
25            Ident(x) if x.to_string() == "please" => {
26                polite += 1;
27                total += 1;
28            },
29            _ => {
30                total += 1;
31            }
32        }
33    }
34    (total, polite)
35}
36
37fn polite_enough(total: i32, polite: i32) -> (PolitenessResult, f32) {
38    let ratio = if polite == 0 {
39        0.0
40    } else {
41        polite as f32 / total as f32
42    };
43
44    // We can't match on this because floats are dumb
45    if ratio >= 0.0 && ratio <= 0.15 {
46        (PolitenessResult::TooRude, ratio)
47    } else if ratio >= 0.15 && ratio <= 0.4 {
48        (PolitenessResult::Correct, ratio)
49    } else if ratio >= 0.4 {
50        (PolitenessResult::TooPolite, ratio)
51    } else {
52        panic!("what")
53    }
54}
55
56fn remove_please_from_group(grp: Group) -> Group {
57    let strm = remove_please(grp.stream());
58    Group::new(grp.delimiter(), strm)
59}
60
61fn remove_please(tk: TokenStream) -> TokenStream {
62    let mut k = tk.clone().into_iter();
63    let mut new = TokenStream::new();
64    while let Some(f) = k.next() {
65        match f {
66            TokenTree::Group(x) => {
67                new.extend_one(TokenTree::Group(remove_please_from_group(x)));
68            },
69            Ident(x) if x.to_string() == "please" => {
70                // Do nothing, remove it
71            },
72            x @ _ => {
73                new.extend_one(x);
74            }
75        }
76    };
77    new
78}
79
80#[proc_macro]
81pub fn polite(tk: TokenStream) -> TokenStream {
82    let howpolite = count_tokens(tk.clone());
83    match polite_enough(howpolite.0, howpolite.1) {
84        (PolitenessResult::Correct, _) => {},
85        (PolitenessResult::TooRude, x) => {
86            panic!("code was too rude (value {x})")
87        },
88        (PolitenessResult::TooPolite, x) => {
89            panic!("code was too polite (value {x})")
90        }
91    }
92
93    let new = remove_please(tk);
94    new
95}