vte_generate_state_changes/
lib.rs1#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use)]
2
3extern crate proc_macro;
4
5use std::iter::Peekable;
6
7use proc_macro2::TokenTree::{Group, Literal, Punct};
8use proc_macro2::{token_stream, TokenStream, TokenTree};
9use quote::quote;
10
11#[proc_macro]
13pub fn generate_state_changes(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
14 let item: TokenStream = item.into();
16 let mut iter = item.into_iter().peekable();
17
18 let fn_name = iter.next().unwrap();
20
21 expect_punct(&mut iter, ',');
23
24 let assignments_stream = states_stream(&mut iter);
26
27 quote!(
28 const fn #fn_name() -> [[u8; 256]; 16] {
29 let mut state_changes = [[0; 256]; 16];
30
31 #assignments_stream
32
33 state_changes
34 }
35 )
36 .into()
37}
38
39fn states_stream(iter: &mut impl Iterator<Item = TokenTree>) -> TokenStream {
41 let mut states_stream = next_group(iter).into_iter().peekable();
42
43 let mut tokens = quote!();
45 while states_stream.peek().is_some() {
46 tokens.extend(state_entry_stream(&mut states_stream));
48
49 optional_punct(&mut states_stream, ',');
51 }
52 tokens
53}
54
55fn state_entry_stream(iter: &mut Peekable<token_stream::IntoIter>) -> TokenStream {
57 let state = iter.next().unwrap();
59
60 let mut changes_stream = next_group(iter).into_iter().peekable();
62
63 let mut tokens = quote!();
64 while changes_stream.peek().is_some() {
65 tokens.extend(change_stream(&mut changes_stream, &state));
67
68 optional_punct(&mut changes_stream, ',');
70 }
71 tokens
72}
73
74fn change_stream(iter: &mut Peekable<token_stream::IntoIter>, state: &TokenTree) -> TokenStream {
76 let start = next_usize(iter);
78
79 let end = if optional_punct(iter, '.') {
81 expect_punct(iter, '.');
83 expect_punct(iter, '=');
84 next_usize(iter)
85 } else {
86 start
88 };
89
90 expect_punct(iter, '=');
92 expect_punct(iter, '>');
93
94 let mut target_change_stream = next_group(iter).into_iter().peekable();
96
97 let mut tokens = quote!();
98 while target_change_stream.peek().is_some() {
99 let (target_state, target_action) = target_change(&mut target_change_stream);
101
102 for byte in start..=end {
104 tokens.extend(quote!(
107 state_changes[State::#state as usize][#byte] =
108 pack(State::#target_state, Action::#target_action);
109 ));
110 }
111 }
112 tokens
113}
114
115fn target_change(iter: &mut Peekable<token_stream::IntoIter>) -> (TokenTree, TokenTree) {
117 let target_state = iter.next().unwrap();
118
119 expect_punct(iter, ',');
121
122 let target_action = iter.next().unwrap();
123
124 (target_state, target_action)
125}
126
127fn optional_punct(iter: &mut Peekable<token_stream::IntoIter>, c: char) -> bool {
129 match iter.peek() {
130 Some(Punct(punct)) if punct.as_char() == c => iter.next().is_some(),
131 _ => false,
132 }
133}
134
135fn expect_punct(iter: &mut impl Iterator<Item = TokenTree>, c: char) {
141 match iter.next() {
142 Some(Punct(ref punct)) if punct.as_char() == c => (),
143 token => panic!("Expected punctuation '{}', but got {:?}", c, token),
144 }
145}
146
147fn next_usize(iter: &mut impl Iterator<Item = TokenTree>) -> usize {
153 match iter.next() {
154 Some(Literal(literal)) => {
155 let literal = literal.to_string();
156 if let Some(prefix) = literal.strip_prefix("0x") {
157 usize::from_str_radix(prefix, 16).unwrap()
158 } else {
159 literal.parse::<usize>().unwrap()
160 }
161 },
162 token => panic!("Expected literal, but got {:?}", token),
163 }
164}
165
166fn next_group(iter: &mut impl Iterator<Item = TokenTree>) -> TokenStream {
172 match iter.next() {
173 Some(Group(group)) => group.stream(),
174 token => panic!("Expected group, but got {:?}", token),
175 }
176}