From dca1c5c633981aa96932e0d50d3e27fe7de7a2b2 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Mon, 12 Sep 2016 10:51:13 -0700 Subject: WIP --- src/definitions.rs | 74 ++++++++++++++++++ src/lib.rs | 53 +++++++++++++ src/table.rs.in | 214 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 341 insertions(+) create mode 100644 src/definitions.rs create mode 100644 src/lib.rs create mode 100644 src/table.rs.in (limited to 'src') diff --git a/src/definitions.rs b/src/definitions.rs new file mode 100644 index 0000000..f887559 --- /dev/null +++ b/src/definitions.rs @@ -0,0 +1,74 @@ +#[derive(Debug)] +pub enum State { + Anywhere = 0, + CsiEntry = 1, + CsiIgnore = 2, + CsiIntermediate = 3, + CsiParam = 4, + DcsEntry = 5, + DcsIgnore = 6, + DcsIntermediate = 7, + DcsParam = 8, + DcsPassthrough = 9, + Escape = 10, + EscapeIntermediate = 11, + Ground = 12, + OscString = 13, + SosPmApcString = 14, + Unused__ = 15, +} + +#[derive(Debug)] +pub enum Action { + None = 0, + Clear = 1, + Collect = 2, + CsiDispatch = 3, + EscDispatch = 4, + Execute = 5, + Hook = 6, + Ignore = 7, + OscEnd = 8, + OscPut = 9, + OscStart = 10, + Param = 11, + Print = 12, + Put = 13, + Unhook = 14, + Unused__ = 15, +} + +/// 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)] +fn unpack(delta: u8) -> (State, Action) { + ( + // Action is stored in bottom 4 bits + unsafe { ::std::mem::transmute(delta & 0x0f) }, + + // State is stored in top 4 bits + unsafe { ::std::mem::transmute(delta >> 4) }, + ) +} + +#[cfg(test)] +mod tests { + use super::{State, Action, unpack}; + #[test] + fn unpack_state_action() { + match unpack(0xee) { + (State::SosPmApcString, Action::Unhook) => (), + _ => panic!("unpack failed"), + } + + match unpack(0xff) { + (State::Unused__, Action::Unused__) => (), + _ => panic!("unpack failed"), + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..d7dadad --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,53 @@ +mod table; +mod definitions; + +pub use definitions::{Action, State, unpack}; + +use table::{EXIT_ACTIONS, ENTRY_ACTIONS, STATE_CHANGE}; + +impl State { + /// Get exit action for this state + #[inline(always)] + pub fn exit_action(&self) -> Action { + unsafe { + ::table::EXIT_ACTIONS.get_unchecked(*self as usize) + } + } + + /// Get entry action for this state + #[inline(always)] + pub fn entry_action(&self) -> Action { + unsafe { + ::table::ENTRY_ACTIONS.get_unchecked(*self as usize) + } + } +} + + +// struct StateMachine { +// state: State, +// } +// +// trait Parser { +// fn csi_entry(&mut self, c: char); +// fn csi_param(&mut self, c: char); +// } +// +// struct Foo; +// +// impl Parser for Foo { +// fn csi_entry(&mut self, c: char) { +// println!("csi_entry char={:?}", c); +// } +// fn csi_param(&mut self, c: char) { +// println!("csi_param char={:?}", c); +// } +// } +// +// #[test] +// fn it_works() { +// let table: u8 = &[Parser::csi_entry, Parser::csi_param]; +// let mut foo = Foo; +// table[0](&mut foo, 'b'); +// } + diff --git a/src/table.rs.in b/src/table.rs.in new file mode 100644 index 0000000..9c39f5e --- /dev/null +++ b/src/table.rs.in @@ -0,0 +1,214 @@ +/// This is the state change table. It's indexed first by current state and then by the next +/// character in the pty stream. +/// +/// TODO implement a syntax extension that runs via syntex in build.rs to actually turn this into a +/// table. +static STATE_CHANGE: [[u8; 256]; 16] = state_table! { + State::Anywhere => { + 0x18 => (Action::Execute, State::Ground), + 0x1a => (Action::Execute, State::Ground), + 0x80...0x8f => (Action::Execute, State::Ground), + 0x91...0x97 => (Action::Execute, State::Ground), + 0x99 => (Action::Execute, State::Ground), + 0x9a => (Action::Execute, State::Ground), + 0x9c => (Action::Execute, State::Ground), + 0x1b => State::Escape, + 0x98 => State::SosPmApcString, + 0x9e => State::SosPmApcString, + 0x9f => State::SosPmApcString, + 0x90 => State::DcsEntry, + 0x9d => State::OscString, + 0x9b => State::CsiEntry, + }, + + State::Ground => { + 0x00...0x17 => Action::Execute, + 0x19 => Action::Execute, + 0x1c...0x1f => Action::Execute, + 0x20...0x7f => Action::Print, + 0x80...0x8f => Action::Execute, + 0x91...0x9a => Action::Execute, + 0x9c => Action::Execute + }, + + State::Escape => { + 0x00...0x17 => Action::Execute, + 0x19 => Action::Execute, + 0x1c...0x1f => Action::Execute, + 0x7f => Action::Ignore, + 0x20...0x2f => (Action::Collect, State::EscapeIntermediate), + 0x30...0x4f => (Action::EscDispatch, State::Ground), + 0x51...0x57 => (Action::EscDispatch, State::Ground), + 0x59 => (Action::EscDispatch, State::Ground), + 0x5a => (Action::EscDispatch, State::Ground), + 0x5c => (Action::EscDispatch, State::Ground), + 0x60...0x7e => (Action::EscDispatch, State::Ground), + 0x5b => State::CsiEntry, + 0x5d => State::OscString, + 0x50 => State::DcsEntry, + 0x58 => State::SosPmApcString, + 0x5e => State::SosPmApcString, + 0x5f => State::SosPmApcString, + }, + + State::EscapeIntermediate => { + 0x00...0x17 => Action::Execute, + 0x19 => Action::Execute, + 0x1c...0x1f => Action::Execute, + 0x20...0x2f => Action::Collect, + 0x7f => Action::Ignore, + 0x30...0x7e => (Action::EscDispatch, State::Ground) + }, + + State::CsiEntry => { + 0x00...0x17 => Action::Execute, + 0x19 => Action::Execute, + 0x1c...0x1f => Action::Execute, + 0x7f => Action::Ignore, + 0x20...0x2f => (Action::Collect, State::CsiIntermediate), + 0x3a => State::CsiIgnore, + 0x30...0x39 => (Action::Param, State::CsiParam), + 0x3b => (Action::Param, State::CsiParam), + 0x3c...0x3f => (Action::Collect, State::CsiParam), + 0x40...0x7e => (Action::CsiDispatch, State::Ground) + }, + + State::CsiIgnore => { + 0x00...0x17 => Action::Execute, + 0x19 => Action::Execute, + 0x1c...0x1f => Action::Execute, + 0x20...0x3f => Action::Ignore, + 0x7f => Action::Ignore, + 0x40...0x7e => State::Ground, + }, + + State::CsiParam => { + 0x00...0x17 => Action::Execute, + 0x19 => Action::Execute, + 0x1c...0x1f => Action::Execute, + 0x30...0x39 => Action::Param, + 0x3b => Action::Param, + 0x7f => Action::Ignore, + 0x3a => State::CsiIgnore, + 0x3c...0x3f => State::CsiIgnore, + 0x20...0x2f => (Action::Collect, State::CsiIntermediate), + 0x40...0x7e => (Action::CsiDispatch, State::Ground) + }, + + State::CsiIntermediate => { + 0x00...0x17 => Action::Execute, + 0x19 => Action::Execute, + 0x1c...0x1f => Action::Execute, + 0x20...0x2f => Action::Collect, + 0x7f => Action::Ignore, + 0x30...0x3f => State::CsiIgnore, + 0x40...0x7e => (Action::CsiDispatch, State::Ground), + }, + + State::DcsEntry => { + 0x00...0x17 => Action::Ignore, + 0x19 => Action::Ignore, + 0x1c...0x1f => Action::Ignore, + 0x7f => Action::Ignore, + 0x3a => State::DcsIgnore, + 0x20...0x2f => (Action::Collect, State::DcsIntermediate), + 0x30...0x39 => (Action::Param, State::DcsParam), + 0x3b => (Action::Param, State::DcsParam), + 0x3c...0x3f => (Action::Collect, State::DcsParam), + 0x40...0x7e => State::DcsPassthrough + }, + + State::DcsIntermediate => { + 0x00...0x17 => Action::Ignore, + 0x19 => Action::Ignore, + 0x1c...0x1f => Action::Ignore, + 0x20...0x2f => Action::Collect, + 0x7f => Action::Ignore, + 0x30...0x3f => State::DcsIgnore, + 0x40...0x7e => State::DcsPassthrough + }, + + State::DcsIgnore => { + 0x00...0x17 => Action::Ignore, + 0x19 => Action::Ignore, + 0x1c...0x1f => Action::Ignore, + 0x20...0x7f => Action::Ignore, + 0x9c => State::Ground + }, + + State::DcsParam => { + 0x00...0x17 => Action::Ignore, + 0x19 => Action::Ignore, + 0x1c...0x1f => Action::Ignore, + 0x30...0x39 => Action::Param, + 0x3b => Action::Param, + 0x7f => Action::Ignore, + 0x3a => State::DcsIgnore, + 0x3c...0x3f => State::DcsIgnore, + 0x20...0x2f => (Action::Collect, State::DcsIntermediate), + 0x40...0x7e => State::DcsPassthrough + }, + + State::DcsPassthrough => { + 0x00...0x17 => Action::Put, + 0x19 => Action::Put, + 0x1c...0x1f => Action::Put, + 0x20...0x7e => Action::Put, + 0x7f => Action::Ignore, + 0x9c => State::Ground, + }, + + State::SosPmApcString => { + 0x00...0x17 => Action::Ignore, + 0x19 => Action::Ignore, + 0x1c...0x1f => Action::Ignore, + 0x20...0x7f => Action::Ignore, + 0x9c => State::Ground + }, + + State::OscString => { + 0x00...0x17 => Action::Ignore, + 0x19 => Action::Ignore, + 0x1c...0x1f => Action::Ignore, + 0x20...0x7f => Action::OscPut, + 0x9c => State::Ground, + } +}; + +static ENTRY_ACTIONS: &'static [Action] = &[ + Action::None, // State::Anywhere + Action::Clear, // State::CsiEntry + Action::None, // State::CsiIgnore + Action::None, // State::CsiIntermediate + Action::None, // State::CsiParam + Action::Clear, // State::DcsEntry + Action::None, // State::DcsIgnore + Action::None, // State::DcsIntermediate + Action::None, // State::DcsParam + Action::Hook, // State::DcsPassthrough + Action::Clear, // State::Escape + Action::None, // State::EscapeIntermediate + Action::None, // State::Ground + Action::OscStart, // State::OscString + Action::None, // State::SosPmApcString + Action::None, // State::Unused__ +]; + +static EXIT_ACTIONS: &'static [Action] = &[ + Action::None, // State::Anywhere + Action::None, // State::CsiEntry + Action::None, // State::CsiIgnore + Action::None, // State::CsiIntermediate + Action::None, // State::CsiParam + Action::None, // State::DcsEntry + Action::None, // State::DcsIgnore + Action::None, // State::DcsIntermediate + Action::None, // State::DcsParam + Action::Unhook, // State::DcsPassthrough + Action::None, // State::Escape + Action::None, // State::EscapeIntermediate + Action::None, // State::Ground + Action::OscEnd, // State::OscString + Action::None, // State::SosPmApcString + Action::None, // State::Unused__ +]; -- cgit