diff options
-rw-r--r-- | Cargo.toml | 4 | ||||
-rw-r--r-- | src/definitions.rs | 99 | ||||
-rw-r--r-- | src/lib.rs | 540 | ||||
-rw-r--r-- | src/table.rs | 188 | ||||
-rw-r--r-- | vte_generate_state_changes/Cargo.toml | 15 | ||||
l--------- | vte_generate_state_changes/LICENSE-APACHE | 1 | ||||
l--------- | vte_generate_state_changes/LICENSE-MIT | 1 | ||||
-rw-r--r-- | vte_generate_state_changes/src/lib.rs | 176 |
8 files changed, 392 insertions, 632 deletions
@@ -13,9 +13,6 @@ name = "vte" edition = "2021" rust-version = "1.62.1" -[workspace] -members = ["vte_generate_state_changes"] - [features] ansi = ["log", "cursor-icon", "bitflags"] default = ["no_std"] @@ -29,4 +26,3 @@ cursor-icon = { version = "1.0.0", default-features = false, optional = true } log = { version = "0.4.17", optional = true } memchr = "2.7.4" serde = { version = "1.0.160", features = ["derive"], optional = true } -vte_generate_state_changes = { version = "0.2.0", path = "vte_generate_state_changes" } diff --git a/src/definitions.rs b/src/definitions.rs deleted file mode 100644 index 694c783..0000000 --- a/src/definitions.rs +++ /dev/null @@ -1,99 +0,0 @@ -use core::mem; - -#[allow(dead_code)] -#[repr(u8)] -#[derive(PartialEq, Eq, Debug, Default, Copy, Clone)] -pub enum State { - CsiEntry, - CsiIgnore, - CsiIntermediate, - CsiParam, - DcsEntry, - DcsIgnore, - DcsIntermediate, - DcsParam, - DcsPassthrough, - Escape, - EscapeIntermediate, - OscString, - SosPmApcString, - Anywhere, - #[default] - Ground, -} - -// NOTE: Removing the unused actions prefixed with `_` will reduce performance. -#[allow(dead_code)] -#[repr(u8)] -#[derive(PartialEq, Eq, Debug, Clone, Copy)] -pub enum Action { - None, - _Clear, - Collect, - CsiDispatch, - EscDispatch, - Execute, - _Hook, - _Ignore, - _OscEnd, - OscPut, - _OscStart, - Param, - _Print, - Put, - _Unhook, -} - -/// Unpack a u8 into a State and Action -/// -/// The implementation of this assumes that there are *precisely* 16 variants -/// for both Action and State. Furthermore, it assumes that the enums are -/// tag-only; that is, there is no data in any variant. -/// -/// Bad things will happen if those invariants are violated. -#[inline(always)] -pub fn unpack(delta: u8) -> (State, Action) { - unsafe { - ( - // State is stored in bottom 4 bits - mem::transmute::<u8, State>(delta & 0x0F), - // Action is stored in top 4 bits - mem::transmute::<u8, Action>(delta >> 4), - ) - } -} - -#[inline(always)] -pub const fn pack(state: State, action: Action) -> u8 { - (action as u8) << 4 | state as u8 -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn unpack_state_action() { - match unpack(0xEE) { - (State::Ground, Action::_Unhook) => (), - _ => panic!("unpack failed"), - } - - match unpack(0x0E) { - (State::Ground, Action::None) => (), - _ => panic!("unpack failed"), - } - - match unpack(0xE0) { - (State::CsiEntry, Action::_Unhook) => (), - _ => panic!("unpack failed"), - } - } - - #[test] - fn pack_state_action() { - assert_eq!(pack(State::Ground, Action::_Unhook), 0xEE); - assert_eq!(pack(State::Ground, Action::None), 0x0E); - assert_eq!(pack(State::CsiEntry, Action::_Unhook), 0xE0); - } -} @@ -35,13 +35,10 @@ use core::str; #[cfg(feature = "no_std")] use arrayvec::ArrayVec; -mod definitions; mod params; -mod table; #[cfg(feature = "ansi")] pub mod ansi; -use definitions::{unpack, Action, State}; pub use params::{Params, ParamsIter}; const MAX_INTERMEDIATES: usize = 2; @@ -113,7 +110,7 @@ impl<const OSC_RAW_BUF_SIZE: usize> Parser<OSC_RAW_BUF_SIZE> { let mut i = 0; // Handle partial codepoints from previous calls to `advance`. - if self.partial_utf8_len > 0 { + if self.partial_utf8_len != 0 { i += self.advance_partial_utf8(performer, bytes); } @@ -121,12 +118,9 @@ impl<const OSC_RAW_BUF_SIZE: usize> Parser<OSC_RAW_BUF_SIZE> { match self.state { State::Ground => i += self.advance_ground(performer, &bytes[i..]), _ => { + // Inlining it results in worse codegen. let byte = bytes[i]; - let change = table::STATE_CHANGES[self.state as usize][byte as usize]; - let (state, action) = unpack(change); - - self.perform_state_change(performer, state, action, byte); - + self.change_state(performer, byte); i += 1; }, } @@ -159,12 +153,9 @@ impl<const OSC_RAW_BUF_SIZE: usize> Parser<OSC_RAW_BUF_SIZE> { match self.state { State::Ground => i += self.advance_ground(performer, &bytes[i..]), _ => { + // Inlining it results in worse codegen. let byte = bytes[i]; - let change = table::STATE_CHANGES[self.state as usize][byte as usize]; - let (state, action) = unpack(change); - - self.perform_state_change(performer, state, action, byte); - + self.change_state(performer, byte); i += 1; }, } @@ -173,164 +164,398 @@ impl<const OSC_RAW_BUF_SIZE: usize> Parser<OSC_RAW_BUF_SIZE> { i } - #[inline] - fn perform_state_change<P>(&mut self, performer: &mut P, state: State, action: Action, byte: u8) - where - P: Perform, - { - if state == State::Anywhere { - self.perform_action(performer, action, byte); - return; + #[inline(always)] + fn change_state<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match self.state { + State::CsiEntry => self.advance_csi_entry(performer, byte), + State::CsiIgnore => self.advance_csi_ignore(performer, byte), + State::CsiIntermediate => self.advance_csi_intermediate(performer, byte), + State::CsiParam => self.advance_csi_param(performer, byte), + State::DcsEntry => self.advance_dcs_entry(performer, byte), + State::DcsIgnore => self.anywhere(performer, byte), + State::DcsIntermediate => self.advance_dcs_intermediate(performer, byte), + State::DcsParam => self.advance_dcs_param(performer, byte), + State::DcsPassthrough => self.advance_dcs_passthrough(performer, byte), + State::Escape => self.advance_esc(performer, byte), + State::EscapeIntermediate => self.advance_esc_intermediate(performer, byte), + State::OscString => self.advance_osc_string(performer, byte), + State::SosPmApcString => self.anywhere(performer, byte), + State::Ground => unreachable!(), } + } - match self.state { - State::DcsPassthrough => performer.unhook(), - State::OscString => { - let param_idx = self.osc_num_params; - let idx = self.osc_raw.len(); - - match param_idx { - // Finish last parameter if not already maxed - MAX_OSC_PARAMS => (), - - // First param is special - 0 to current byte index - 0 => { - self.osc_params[param_idx] = (0, idx); - self.osc_num_params += 1; - }, + #[inline(always)] + fn advance_csi_entry<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match byte { + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => performer.execute(byte), + 0x20..=0x2F => { + self.action_collect(byte); + self.state = State::CsiIntermediate + }, + 0x30..=0x39 => { + self.action_paramnext(byte); + self.state = State::CsiParam + }, + 0x3A => { + self.action_subparam(); + self.state = State::CsiParam + }, + 0x3B => { + self.action_param(); + self.state = State::CsiParam + }, + 0x3C..=0x3F => { + self.action_collect(byte); + self.state = State::CsiParam + }, + 0x40..=0x7E => self.action_csi_dispatch(performer, byte), + _ => self.anywhere(performer, byte), + } + } - // All other params depend on previous indexing - _ => { - let prev = self.osc_params[param_idx - 1]; - let begin = prev.1; - self.osc_params[param_idx] = (begin, idx); - self.osc_num_params += 1; - }, - } - self.osc_dispatch(performer, byte); + #[inline(always)] + fn advance_csi_ignore<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match byte { + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => performer.execute(byte), + 0x20..=0x3F => (), + 0x40..=0x7E => self.state = State::Ground, + 0x7F => (), + _ => self.anywhere(performer, byte), + } + } + + #[inline(always)] + fn advance_csi_intermediate<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match byte { + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => performer.execute(byte), + 0x20..=0x2F => self.action_collect(byte), + 0x30..=0x3F => self.state = State::CsiIgnore, + 0x40..=0x7E => self.action_csi_dispatch(performer, byte), + _ => self.anywhere(performer, byte), + } + } + + #[inline(always)] + fn advance_csi_param<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match byte { + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => performer.execute(byte), + 0x20..=0x2F => { + self.action_collect(byte); + self.state = State::CsiIntermediate }, - _ => (), + 0x30..=0x39 => self.action_paramnext(byte), + 0x3A => self.action_subparam(), + 0x3B => self.action_param(), + 0x3C..=0x3F => self.state = State::CsiIgnore, + 0x40..=0x7E => self.action_csi_dispatch(performer, byte), + 0x7F => (), + _ => self.anywhere(performer, byte), } + } - if action == Action::None { - match state { - State::CsiEntry | State::DcsEntry | State::Escape => self.reset_params(), - State::DcsPassthrough => { - if self.params.is_full() { - self.ignoring = true; - } else { - self.params.push(self.param); - } + #[inline(always)] + fn advance_dcs_entry<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match byte { + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => (), + 0x20..=0x2F => { + self.action_collect(byte); + self.state = State::DcsIntermediate + }, + 0x30..=0x39 => { + self.action_paramnext(byte); + self.state = State::DcsParam + }, + 0x3A => { + self.action_subparam(); + self.state = State::DcsParam + }, + 0x3B => { + self.action_param(); + self.state = State::DcsParam + }, + 0x3C..=0x3F => { + self.action_collect(byte); + self.state = State::DcsParam + }, + 0x40..=0x7E => self.action_hook(performer, byte), + 0x7F => (), + _ => self.anywhere(performer, byte), + } + } - performer.hook( - self.params(), - self.intermediates(), - self.ignoring, - byte as char, - ); - }, - State::OscString => { - self.osc_raw.clear(); - self.osc_num_params = 0; - }, - _ => (), - } - } else { - self.perform_action(performer, action, byte); + #[inline(always)] + fn advance_dcs_intermediate<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match byte { + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => (), + 0x20..=0x2F => self.action_collect(byte), + 0x30..=0x3F => self.state = State::DcsIgnore, + 0x40..=0x7E => self.action_hook(performer, byte), + 0x7F => (), + _ => self.anywhere(performer, byte), } + } - self.state = state; + #[inline(always)] + fn advance_dcs_param<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match byte { + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => (), + 0x20..=0x2F => { + self.action_collect(byte); + self.state = State::DcsIntermediate + }, + 0x30..=0x39 => self.action_paramnext(byte), + 0x3A => self.action_subparam(), + 0x3B => self.action_param(), + 0x3C..=0x3F => self.state = State::DcsIgnore, + 0x40..=0x7E => self.action_hook(performer, byte), + 0x7F => (), + _ => self.anywhere(performer, byte), + } } - #[inline] - fn perform_action<P: Perform>(&mut self, performer: &mut P, action: Action, byte: u8) { - match action { - Action::Execute => performer.execute(byte), - Action::Put => performer.put(byte), - Action::OscPut => { + #[inline(always)] + fn advance_dcs_passthrough<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match byte { + 0x00..=0x17 | 0x19 | 0x1C..=0x7E => performer.put(byte), + 0x18 | 0x1A => { + performer.unhook(); + performer.execute(byte); + self.state = State::Ground + }, + 0x1B => { + performer.unhook(); + self.reset_params(); + self.state = State::Escape + }, + 0x7F => (), + 0x9C => { + performer.unhook(); + self.state = State::Ground + }, + _ => (), + } + } + + #[inline(always)] + fn advance_esc<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match byte { + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => performer.execute(byte), + 0x20..=0x2F => { + self.action_collect(byte); + self.state = State::EscapeIntermediate + }, + 0x30..=0x4F => { + performer.esc_dispatch(self.intermediates(), self.ignoring, byte); + self.state = State::Ground + }, + 0x50 => { + self.reset_params(); + self.state = State::DcsEntry + }, + 0x51..=0x57 => { + performer.esc_dispatch(self.intermediates(), self.ignoring, byte); + self.state = State::Ground + }, + 0x58 => self.state = State::SosPmApcString, + 0x59..=0x5A => { + performer.esc_dispatch(self.intermediates(), self.ignoring, byte); + self.state = State::Ground + }, + 0x5B => { + self.reset_params(); + self.state = State::CsiEntry + }, + 0x5C => { + performer.esc_dispatch(self.intermediates(), self.ignoring, byte); + self.state = State::Ground + }, + 0x5D => { + self.osc_raw.clear(); + self.osc_num_params = 0; + self.state = State::OscString + }, + 0x5E..=0x5F => self.state = State::SosPmApcString, + 0x60..=0x7E => { + performer.esc_dispatch(self.intermediates(), self.ignoring, byte); + self.state = State::Ground + }, + // Anywhere. + 0x18 | 0x1A => { + performer.execute(byte); + self.state = State::Ground + }, + 0x1B => (), + _ => (), + } + } + + #[inline(always)] + fn advance_esc_intermediate<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match byte { + 0x00..=0x17 | 0x19 | 0x1C..=0x1F => performer.execute(byte), + 0x20..=0x2F => self.action_collect(byte), + 0x30..=0x7E => { + performer.esc_dispatch(self.intermediates(), self.ignoring, byte); + self.state = State::Ground + }, + 0x7F => (), + _ => self.anywhere(performer, byte), + } + } + + #[inline(always)] + fn advance_osc_string<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match byte { + 0x00..=0x06 | 0x08..=0x17 | 0x19 | 0x1C..=0x1F => (), + 0x07 => { + self.osc_end(performer, byte); + self.state = State::Ground + }, + 0x18 | 0x1A => { + self.osc_end(performer, byte); + performer.execute(byte); + self.state = State::Ground + }, + 0x1B => { + self.osc_end(performer, byte); + self.reset_params(); + self.state = State::Escape + }, + 0x3B => { #[cfg(feature = "no_std")] { if self.osc_raw.is_full() { return; } } - - let idx = self.osc_raw.len(); - - // Param separator - if byte == b';' { - let param_idx = self.osc_num_params; - match param_idx { - // Only process up to MAX_OSC_PARAMS - MAX_OSC_PARAMS => return, - - // First param is special - 0 to current byte index - 0 => { - self.osc_params[param_idx] = (0, idx); - }, - - // All other params depend on previous indexing - _ => { - let prev = self.osc_params[param_idx - 1]; - let begin = prev.1; - self.osc_params[param_idx] = (begin, idx); - }, - } - - self.osc_num_params += 1; - } else { - self.osc_raw.push(byte); - } + self.action_osc_put_param() }, - Action::CsiDispatch => { - if self.params.is_full() { - self.ignoring = true; - } else { - self.params.push(self.param); - } + _ => self.action_osc_put(byte), + } + } - performer.csi_dispatch( - self.params(), - self.intermediates(), - self.ignoring, - byte as char, - ); - }, - Action::EscDispatch => { - performer.esc_dispatch(self.intermediates(), self.ignoring, byte); + #[inline(always)] + fn anywhere<P: Perform>(&mut self, performer: &mut P, byte: u8) { + match byte { + 0x18 | 0x1A => { + performer.execute(byte); + self.state = State::Ground }, - Action::Collect => { - if self.intermediate_idx == MAX_INTERMEDIATES { - self.ignoring = true; - } else { - self.intermediates[self.intermediate_idx] = byte; - self.intermediate_idx += 1; - } + 0x1B => { + self.reset_params(); + self.state = State::Escape }, - Action::Param => { - if self.params.is_full() { - self.ignoring = true; - return; - } + _ => (), + } + } - match byte { - b';' => { - self.params.push(self.param); - self.param = 0; - }, - b':' => { - self.params.extend(self.param); - self.param = 0; - }, - _ => { - // Continue collecting bytes into param - self.param = self.param.saturating_mul(10); - self.param = self.param.saturating_add((byte - b'0') as u16); - }, - } + #[inline] + fn action_csi_dispatch<P: Perform>(&mut self, performer: &mut P, byte: u8) { + if self.params.is_full() { + self.ignoring = true; + } else { + self.params.push(self.param); + } + performer.csi_dispatch(self.params(), self.intermediates(), self.ignoring, byte as char); + + self.state = State::Ground + } + + #[inline] + fn action_hook<P: Perform>(&mut self, performer: &mut P, byte: u8) { + if self.params.is_full() { + self.ignoring = true; + } else { + self.params.push(self.param); + } + performer.hook(self.params(), self.intermediates(), self.ignoring, byte as char); + self.state = State::DcsPassthrough; + } + + #[inline] + fn action_collect(&mut self, byte: u8) { + if self.intermediate_idx == MAX_INTERMEDIATES { + self.ignoring = true; + } else { + self.intermediates[self.intermediate_idx] = byte; + self.intermediate_idx += 1; + } + } + + /// Advance to the next subparameter. + #[inline] + fn action_subparam(&mut self) { + if self.params.is_full() { + self.ignoring = true; + } else { + self.params.extend(self.param); + self.param = 0; + } + } + + /// Advance to the next parameter. + #[inline] + fn action_param(&mut self) { + if self.params.is_full() { + self.ignoring = true; + } else { + self.params.push(self.param); + self.param = 0; + } + } + + /// Advance inside the parameter without terminating it. + #[inline] + fn action_paramnext(&mut self, byte: u8) { + if self.params.is_full() { + self.ignoring = true; + } else { + // Continue collecting bytes into param. + self.param = self.param.saturating_mul(10); + self.param = self.param.saturating_add((byte - b'0') as u16); + } + } + + /// Add OSC param separator. + #[inline] + fn action_osc_put_param(&mut self) { + let idx = self.osc_raw.len(); + + let param_idx = self.osc_num_params; + match param_idx { + // First param is special - 0 to current byte index. + 0 => self.osc_params[param_idx] = (0, idx), + + // Only process up to MAX_OSC_PARAMS. + MAX_OSC_PARAMS => return, + + // All other params depend on previous indexing. + _ => { + let prev = self.osc_params[param_idx - 1]; + let begin = prev.1; + self.osc_params[param_idx] = (begin, idx); }, - _ => (), } + + self.osc_num_params += 1; + } + + #[inline(always)] + fn action_osc_put(&mut self, byte: u8) { + #[cfg(feature = "no_std")] + { + if self.osc_raw.is_full() { + return; + } + } + self.osc_raw.push(byte); + } + + fn osc_end<P: Perform>(&mut self, performer: &mut P, byte: u8) { + self.action_osc_put_param(); + self.osc_dispatch(performer, byte); + self.osc_raw.clear(); + self.osc_num_params = 0; } /// Reset escape sequence parameters and intermediates. @@ -504,6 +729,25 @@ impl<const OSC_RAW_BUF_SIZE: usize> Parser<OSC_RAW_BUF_SIZE> { } } +#[derive(PartialEq, Eq, Debug, Default, Copy, Clone)] +enum State { + CsiEntry, + CsiIgnore, + CsiIntermediate, + CsiParam, + DcsEntry, + DcsIgnore, + DcsIntermediate, + DcsParam, + DcsPassthrough, + Escape, + EscapeIntermediate, + OscString, + SosPmApcString, + #[default] + Ground, +} + /// Performs actions requested by the Parser /// /// Actions in this case mean, for example, handling a CSI escape sequence diff --git a/src/table.rs b/src/table.rs deleted file mode 100644 index ac288e7..0000000 --- a/src/table.rs +++ /dev/null @@ -1,188 +0,0 @@ -use vte_generate_state_changes::generate_state_changes; - -/// This is the state change table. It's indexed first by current state and then -/// by the next character in the pty stream. -use crate::definitions::{pack, Action, State}; - -// Generate state changes at compile-time -pub const STATE_CHANGES: [[u8; 256]; 13] = state_changes(); -generate_state_changes!(state_changes, { - Escape { - 0x00..=0x17 => (Anywhere, Execute), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, Execute), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, Execute), - 0x7f => (Anywhere, None), - 0x20..=0x2f => (EscapeIntermediate, Collect), - 0x30..=0x4f => (Ground, EscDispatch), - 0x51..=0x57 => (Ground, EscDispatch), - 0x59 => (Ground, EscDispatch), - 0x5a => (Ground, EscDispatch), - 0x5c => (Ground, EscDispatch), - 0x60..=0x7e => (Ground, EscDispatch), - 0x5b => (CsiEntry, None), - 0x5d => (OscString, None), - 0x50 => (DcsEntry, None), - 0x58 => (SosPmApcString, None), - 0x5e => (SosPmApcString, None), - 0x5f => (SosPmApcString, None), - }, - - EscapeIntermediate { - 0x00..=0x17 => (Anywhere, Execute), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, Execute), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, Execute), - 0x20..=0x2f => (Anywhere, Collect), - 0x7f => (Anywhere, None), - 0x30..=0x7e => (Ground, EscDispatch), - }, - - CsiEntry { - 0x00..=0x17 => (Anywhere, Execute), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, Execute), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, Execute), - 0x7f => (Anywhere, None), - 0x20..=0x2f => (CsiIntermediate, Collect), - 0x30..=0x39 => (CsiParam, Param), - 0x3a..=0x3b => (CsiParam, Param), - 0x3c..=0x3f => (CsiParam, Collect), - 0x40..=0x7e => (Ground, CsiDispatch), - }, - - CsiIgnore { - 0x00..=0x17 => (Anywhere, Execute), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, Execute), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, Execute), - 0x20..=0x3f => (Anywhere, None), - 0x7f => (Anywhere, None), - 0x40..=0x7e => (Ground, None), - }, - - CsiParam { - 0x00..=0x17 => (Anywhere, Execute), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, Execute), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, Execute), - 0x30..=0x39 => (Anywhere, Param), - 0x3a..=0x3b => (Anywhere, Param), - 0x7f => (Anywhere, None), - 0x3c..=0x3f => (CsiIgnore, None), - 0x20..=0x2f => (CsiIntermediate, Collect), - 0x40..=0x7e => (Ground, CsiDispatch), - }, - - CsiIntermediate { - 0x00..=0x17 => (Anywhere, Execute), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, Execute), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, Execute), - 0x20..=0x2f => (Anywhere, Collect), - 0x7f => (Anywhere, None), - 0x30..=0x3f => (CsiIgnore, None), - 0x40..=0x7e => (Ground, CsiDispatch), - }, - - DcsEntry { - 0x00..=0x17 => (Anywhere, None), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, None), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, None), - 0x7f => (Anywhere, None), - 0x20..=0x2f => (DcsIntermediate, Collect), - 0x30..=0x39 => (DcsParam, Param), - 0x3a..=0x3b => (DcsParam, Param), - 0x3c..=0x3f => (DcsParam, Collect), - 0x40..=0x7e => (DcsPassthrough, None), - }, - - DcsIntermediate { - 0x00..=0x17 => (Anywhere, None), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, None), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, None), - 0x20..=0x2f => (Anywhere, Collect), - 0x7f => (Anywhere, None), - 0x30..=0x3f => (DcsIgnore, None), - 0x40..=0x7e => (DcsPassthrough, None), - }, - - DcsIgnore { - 0x00..=0x17 => (Anywhere, None), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, None), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, None), - 0x20..=0x7f => (Anywhere, None), - 0x9c => (Ground, None), - }, - - DcsParam { - 0x00..=0x17 => (Anywhere, None), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, None), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, None), - 0x30..=0x39 => (Anywhere, Param), - 0x3a..=0x3b => (Anywhere, Param), - 0x7f => (Anywhere, None), - 0x3c..=0x3f => (DcsIgnore, None), - 0x20..=0x2f => (DcsIntermediate, Collect), - 0x40..=0x7e => (DcsPassthrough, None), - }, - - DcsPassthrough { - 0x00..=0x17 => (Anywhere, Put), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, Put), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, Put), - 0x20..=0x7e => (Anywhere, Put), - 0x7f => (Anywhere, None), - 0x9c => (Ground, None), - }, - - SosPmApcString { - 0x00..=0x17 => (Anywhere, None), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, None), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, None), - 0x20..=0x7f => (Anywhere, None), - 0x9c => (Ground, None), - }, - - OscString { - 0x00..=0x06 => (Anywhere, None), - 0x07 => (Ground, None), - 0x08..=0x17 => (Anywhere, None), - 0x18 => (Ground, Execute), - 0x19 => (Anywhere, None), - 0x1a => (Ground, Execute), - 0x1b => (Escape, None), - 0x1c..=0x1f => (Anywhere, None), - 0x20..=0xff => (Anywhere, OscPut), - } -}); diff --git a/vte_generate_state_changes/Cargo.toml b/vte_generate_state_changes/Cargo.toml deleted file mode 100644 index dd0e714..0000000 --- a/vte_generate_state_changes/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -authors = ["Christian Duerr <contact@christianduerr.com>"] -description = "Proc macro for generating VTE state changes" -repository = "https://github.com/alacritty/vte" -name = "vte_generate_state_changes" -license = "Apache-2.0 OR MIT" -version = "0.2.0" -edition = "2018" - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = "1.0.6" -quote = "1.0.2" diff --git a/vte_generate_state_changes/LICENSE-APACHE b/vte_generate_state_changes/LICENSE-APACHE deleted file mode 120000 index 965b606..0000000 --- a/vte_generate_state_changes/LICENSE-APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-APACHE
\ No newline at end of file diff --git a/vte_generate_state_changes/LICENSE-MIT b/vte_generate_state_changes/LICENSE-MIT deleted file mode 120000 index 76219eb..0000000 --- a/vte_generate_state_changes/LICENSE-MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE-MIT
\ No newline at end of file diff --git a/vte_generate_state_changes/src/lib.rs b/vte_generate_state_changes/src/lib.rs deleted file mode 100644 index ff8ea49..0000000 --- a/vte_generate_state_changes/src/lib.rs +++ /dev/null @@ -1,176 +0,0 @@ -#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use)] - -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; - -/// Create a `const fn` which will return an array with all state changes. -#[proc_macro] -pub fn generate_state_changes(item: proc_macro::TokenStream) -> proc_macro::TokenStream { - // Convert from proc_macro -> proc_macro2 - let item: TokenStream = item.into(); - let mut iter = item.into_iter().peekable(); - - // Determine output function name - let fn_name = iter.next().unwrap(); - - // Separator between name and body with state changes - expect_punct(&mut iter, ','); - - // Create token stream to assign each state change to the array - let assignments_stream = states_stream(&mut iter); - - quote!( - const fn #fn_name() -> [[u8; 256]; 13] { - let mut state_changes = [[0; 256]; 13]; - - #assignments_stream - - state_changes - } - ) - .into() -} - -/// Generate the array assignment statements for all origin states. -fn states_stream(iter: &mut impl Iterator<Item = TokenTree>) -> TokenStream { - let mut states_stream = next_group(iter).into_iter().peekable(); - - // Loop over all origin state entries - let mut tokens = quote!(); - while states_stream.peek().is_some() { - // Add all mappings for this state - tokens.extend(state_entry_stream(&mut states_stream)); - - // Allow trailing comma - optional_punct(&mut states_stream, ','); - } - tokens -} - -/// Generate the array assignment statements for one origin state. -fn state_entry_stream(iter: &mut Peekable<token_stream::IntoIter>) -> TokenStream { - // Origin state name - let state = iter.next().unwrap(); - - // Token stream with all the byte->target mappings - let mut changes_stream = next_group(iter).into_iter().peekable(); - - let mut tokens = quote!(); - while changes_stream.peek().is_some() { - // Add next mapping for this state - tokens.extend(change_stream(&mut changes_stream, &state)); - - // Allow trailing comma - optional_punct(&mut changes_stream, ','); - } - tokens -} - -/// Generate the array assignment statement for a single byte->target mapping -/// for one state. -fn change_stream(iter: &mut Peekable<token_stream::IntoIter>, state: &TokenTree) -> TokenStream { - // Start of input byte range - let start = next_usize(iter); - - // End of input byte range - let end = if optional_punct(iter, '.') { - // Read inclusive end of range - expect_punct(iter, '.'); - expect_punct(iter, '='); - next_usize(iter) - } else { - // Without range, end is equal to start - start - }; - - // Separator between byte input range and output state - expect_punct(iter, '='); - expect_punct(iter, '>'); - - // Token stream with target state and action - let mut target_change_stream = next_group(iter).into_iter().peekable(); - - let mut tokens = quote!(); - while target_change_stream.peek().is_some() { - // Target state/action for all bytes in the range - let (target_state, target_action) = target_change(&mut target_change_stream); - - // Create a new entry for every byte in the range - for byte in start..=end { - tokens.extend(quote!( - state_changes[State::#state as usize][#byte] = - pack(State::#target_state, Action::#target_action); - )); - } - } - tokens -} - -/// Get next target state and action. -fn target_change(iter: &mut Peekable<token_stream::IntoIter>) -> (TokenTree, TokenTree) { - let target_state = iter.next().unwrap(); - - // Separator between state and action - expect_punct(iter, ','); - - let target_action = iter.next().unwrap(); - - (target_state, target_action) -} - -/// Check if next token matches specific punctuation. -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, - } -} - -/// Ensure next token matches specific punctuation. -/// -/// # Panics -/// -/// Panics if the punctuation does not match. -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), - } -} - -/// Get next token as [`usize`]. -/// -/// # Panics -/// -/// Panics if the next token is not a [`usize`] in hex or decimal literal -/// format. -fn next_usize(iter: &mut impl Iterator<Item = TokenTree>) -> usize { - match iter.next() { - Some(Literal(literal)) => { - let literal = literal.to_string(); - if let Some(prefix) = literal.strip_prefix("0x") { - usize::from_str_radix(prefix, 16).unwrap() - } else { - literal.parse::<usize>().unwrap() - } - }, - token => panic!("Expected literal, but got {:?}", token), - } -} - -/// Get next token as [`Group`]. -/// -/// # Panics -/// -/// Panics if the next token is not a [`Group`]. -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), - } -} |