1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
extern crate proc_macro;
use proc_macro::{Ident, TokenStream, TokenTree};
use std::iter::Peekable;
fn next_group(source: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Option<proc_macro::Group> {
if let Some(TokenTree::Group(_)) = source.peek() {
let group = match source.next().unwrap() {
TokenTree::Group(group) => group,
_ => unreachable!("just checked with peek()!"),
};
Some(group)
} else {
None
}
}
fn next_literal(source: &mut Peekable<impl Iterator<Item = TokenTree>>) -> Option<String> {
if let Some(TokenTree::Literal(lit)) = source.peek() {
let mut literal = lit.to_string();
if literal.starts_with("\"") {
literal.remove(0);
literal.remove(literal.len() - 1);
}
source.next();
return Some(literal);
}
return None;
}
#[proc_macro_attribute]
pub fn main(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut modified = TokenStream::new();
let mut source = item.into_iter().peekable();
let mut crate_rename = None;
while let Some(TokenTree::Punct(punct)) = source.peek() {
assert_eq!(format!("{}", punct), "#");
let _ = source.next().unwrap();
let group = next_group(&mut source);
let mut group = group.unwrap().stream().into_iter().peekable();
let attribute_name = format!("{}", group.next().unwrap());
if attribute_name == "rwasm" {
let group = next_group(&mut group);
let mut group = group.unwrap().stream().into_iter().peekable();
let config_name = format!("{}", group.next().unwrap());
if group.peek().is_some() {
let _ = group.next();
let config_value = Some(next_literal(&mut group).unwrap());
if config_name == "crate_rename" {
crate_rename = config_value;
}
}
}
}
if let TokenTree::Ident(ident) = source.next().unwrap() {
assert_eq!(format!("{}", ident), "async");
modified.extend(std::iter::once(TokenTree::Ident(ident)));
} else {
panic!("[rwasm::main] is allowed only for async functions");
}
if let TokenTree::Ident(ident) = source.next().unwrap() {
assert_eq!(format!("{}", ident), "fn");
modified.extend(std::iter::once(TokenTree::Ident(ident)));
} else {
panic!("[rwasm::main] is allowed only for functions");
}
if let TokenTree::Ident(ident) = source.next().unwrap() {
assert_eq!(format!("{}", ident), "main");
modified.extend(std::iter::once(TokenTree::Ident(Ident::new(
"amain",
ident.span(),
))));
} else {
panic!("[rwasm::main] expecting main function");
}
modified.extend(source);
let mut prelude: TokenStream = format!(
"
fn main() {{
{crate_name}::wasync::Executor::spawn(amain());
}}
",
crate_name = crate_rename.unwrap_or_else(|| "rwasm".to_string()),
)
.parse()
.unwrap();
prelude.extend(modified);
prelude
}