vte_generate_state_changes/
lib.rs1extern crate proc_macro;
2
3use std::iter::Peekable;
4
5use proc_macro2::TokenTree::{Group, Literal, Punct};
6use proc_macro2::{token_stream, TokenStream, TokenTree};
7use quote::quote;
8
9#[proc_macro]
11pub fn generate_state_changes(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
12 let item: TokenStream = item.into();
14 let mut iter = item.into_iter().peekable();
15
16 let fn_name = iter.next().unwrap();
18
19 expect_punct(&mut iter, ',');
21
22 let assignments_stream = states_stream(&mut iter);
24
25 quote!(
26 const fn #fn_name() -> [[u8; 256]; 16] {
27 let mut state_changes = [[0; 256]; 16];
28
29 #assignments_stream
30
31 state_changes
32 }
33 )
34 .into()
35}
36
37fn states_stream(iter: &mut impl Iterator<Item = TokenTree>) -> TokenStream {
39 let mut states_stream = next_group(iter).into_iter().peekable();
40
41 let mut tokens = quote!();
43 while states_stream.peek().is_some() {
44 tokens.extend(state_entry_stream(&mut states_stream));
46
47 optional_punct(&mut states_stream, ',');
49 }
50 tokens
51}
52
53fn state_entry_stream(iter: &mut Peekable<token_stream::IntoIter>) -> TokenStream {
55 let state = iter.next().unwrap().into();
57
58 let mut changes_stream = next_group(iter).into_iter().peekable();
60
61 let mut tokens = quote!();
62 while changes_stream.peek().is_some() {
63 tokens.extend(change_stream(&mut changes_stream, &state));
65
66 optional_punct(&mut changes_stream, ',');
68 }
69 tokens
70}
71
72fn change_stream(iter: &mut Peekable<token_stream::IntoIter>, state: &TokenTree) -> TokenStream {
74 let start = next_usize(iter);
76
77 let end = if optional_punct(iter, '.') {
79 expect_punct(iter, '.');
81 expect_punct(iter, '=');
82 next_usize(iter)
83 } else {
84 start
86 };
87
88 expect_punct(iter, '=');
90 expect_punct(iter, '>');
91
92 let mut target_change_stream = next_group(iter).into_iter().peekable();
94
95 let mut tokens = quote!();
96 while target_change_stream.peek().is_some() {
97 let (target_state, target_action) = target_change(&mut target_change_stream);
99
100 for byte in start..=end {
102 tokens.extend(quote!(
105 state_changes[State::#state as usize][#byte] =
106 pack(State::#target_state, Action::#target_action);
107 ));
108 }
109 }
110 tokens
111}
112
113fn target_change(iter: &mut Peekable<token_stream::IntoIter>) -> (TokenTree, TokenTree) {
115 let target_state = iter.next().unwrap();
116
117 expect_punct(iter, ',');
119
120 let target_action = iter.next().unwrap();
121
122 (target_state, target_action)
123}
124
125fn optional_punct(iter: &mut Peekable<token_stream::IntoIter>, c: char) -> bool {
127 match iter.peek() {
128 Some(Punct(punct)) if punct.as_char() == c => iter.next().is_some(),
129 _ => false,
130 }
131}
132
133fn expect_punct(iter: &mut impl Iterator<Item = TokenTree>, c: char) {
139 match iter.next() {
140 Some(Punct(ref punct)) if punct.as_char() == c => (),
141 token => panic!("Expected punctuation '{}', but got {:?}", c, token),
142 }
143}
144
145fn next_usize(iter: &mut impl Iterator<Item = TokenTree>) -> usize {
151 match iter.next() {
152 Some(Literal(literal)) => {
153 let literal = literal.to_string();
154 if literal.starts_with("0x") {
155 usize::from_str_radix(&literal[2..], 16).unwrap()
156 } else {
157 usize::from_str_radix(&literal, 10).unwrap()
158 }
159 },
160 token => panic!("Expected literal, but got {:?}", token),
161 }
162}
163
164fn next_group(iter: &mut impl Iterator<Item = TokenTree>) -> TokenStream {
170 match iter.next() {
171 Some(Group(group)) => group.stream(),
172 token => panic!("Expected group, but got {:?}", token),
173 }
174}