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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
extern crate proc_macro;
use std::iter::Peekable;
use proc_macro2::TokenTree::{Group, Literal, Punct};
use proc_macro2::{token_stream, TokenStream, TokenTree};
use quote::quote;
#[proc_macro]
pub fn generate_state_changes(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
let item: TokenStream = item.into();
let mut iter = item.into_iter().peekable();
let fn_name = iter.next().unwrap();
expect_punct(&mut iter, ',');
let assignments_stream = states_stream(&mut iter);
quote!(
const fn #fn_name() -> [[u8; 256]; 16] {
let mut state_changes = [[0; 256]; 16];
#assignments_stream
state_changes
}
)
.into()
}
fn states_stream(iter: &mut impl Iterator<Item = TokenTree>) -> TokenStream {
let mut states_stream = next_group(iter).into_iter().peekable();
let mut tokens = quote!();
while states_stream.peek().is_some() {
tokens.extend(state_entry_stream(&mut states_stream));
optional_punct(&mut states_stream, ',');
}
tokens
}
fn state_entry_stream(iter: &mut Peekable<token_stream::IntoIter>) -> TokenStream {
let state = iter.next().unwrap().into();
let mut changes_stream = next_group(iter).into_iter().peekable();
let mut tokens = quote!();
while changes_stream.peek().is_some() {
tokens.extend(change_stream(&mut changes_stream, &state));
optional_punct(&mut changes_stream, ',');
}
tokens
}
fn change_stream(iter: &mut Peekable<token_stream::IntoIter>, state: &TokenTree) -> TokenStream {
let start = next_usize(iter);
let end = if optional_punct(iter, '.') {
expect_punct(iter, '.');
expect_punct(iter, '=');
next_usize(iter)
} else {
start
};
expect_punct(iter, '=');
expect_punct(iter, '>');
let mut target_change_stream = next_group(iter).into_iter().peekable();
let mut tokens = quote!();
while target_change_stream.peek().is_some() {
let (target_state, target_action) = target_change(&mut target_change_stream);
for byte in start..=end {
tokens.extend(quote!(
state_changes[State::#state as usize][#byte] =
pack(State::#target_state, Action::#target_action);
));
}
}
tokens
}
fn target_change(iter: &mut Peekable<token_stream::IntoIter>) -> (TokenTree, TokenTree) {
let target_state = iter.next().unwrap();
expect_punct(iter, ',');
let target_action = iter.next().unwrap();
(target_state, target_action)
}
fn optional_punct(iter: &mut Peekable<token_stream::IntoIter>, c: char) -> bool {
match iter.peek() {
Some(Punct(punct)) if punct.as_char() == c => iter.next().is_some(),
_ => false,
}
}
fn expect_punct(iter: &mut impl Iterator<Item = TokenTree>, c: char) {
match iter.next() {
Some(Punct(ref punct)) if punct.as_char() == c => (),
token => panic!("Expected punctuation '{}', but got {:?}", c, token),
}
}
fn next_usize(iter: &mut impl Iterator<Item = TokenTree>) -> usize {
match iter.next() {
Some(Literal(literal)) => {
let literal = literal.to_string();
if literal.starts_with("0x") {
usize::from_str_radix(&literal[2..], 16).unwrap()
} else {
usize::from_str_radix(&literal, 10).unwrap()
}
},
token => panic!("Expected literal, but got {:?}", token),
}
}
fn next_group(iter: &mut impl Iterator<Item = TokenTree>) -> TokenStream {
match iter.next() {
Some(Group(group)) => group.stream(),
token => panic!("Expected group, but got {:?}", token),
}
}