extern crate utf8parse as utf8;
mod table;
mod definitions;
use definitions::{Action, State, unpack};
use table::{EXIT_ACTIONS, ENTRY_ACTIONS, STATE_CHANGE};
impl State {
#[inline(always)]
pub fn exit_action(&self) -> Action {
unsafe {
*EXIT_ACTIONS.get_unchecked(*self as usize)
}
}
#[inline(always)]
pub fn entry_action(&self) -> Action {
unsafe {
*ENTRY_ACTIONS.get_unchecked(*self as usize)
}
}
}
const MAX_INTERMEDIATES: usize = 2;
const MAX_PARAMS: usize = 16;
struct VtUtf8Receiver<'a, P: Perform + 'a>(&'a mut P, &'a mut State);
impl<'a, P: Perform> utf8::Receiver for VtUtf8Receiver<'a, P> {
fn codepoint(&mut self, c: char) {
self.0.print(c);
*self.1 = State::Ground;
}
fn invalid_sequence(&mut self) {
self.0.print('�');
*self.1 = State::Ground;
}
}
pub struct Parser {
state: State,
intermediates: [u8; MAX_INTERMEDIATES],
intermediate_idx: usize,
params: [i64; MAX_PARAMS],
num_params: usize,
ignoring: bool,
utf8_parser: utf8::Parser,
}
impl Parser {
pub fn new() -> Parser {
Parser {
state: State::Ground,
intermediates: [0u8; MAX_INTERMEDIATES],
intermediate_idx: 0,
params: [0i64; MAX_PARAMS],
num_params: 0,
ignoring: false,
utf8_parser: utf8::Parser::new(),
}
}
fn params(&self) -> &[i64] {
&self.params[..self.num_params]
}
fn intermediates(&self) -> &[u8] {
&self.intermediates[..self.intermediate_idx]
}
pub fn advance<P: Perform>(&mut self, performer: &mut P, byte: u8) {
if let State::Utf8 = self.state {
self.process_utf8(performer, byte);
return;
}
let mut change = STATE_CHANGE[State::Anywhere as usize][byte as usize];
if change == 0 {
change = STATE_CHANGE[self.state as usize][byte as usize];
}
let (state, action) = unpack(change);
self.perform_state_change(performer, state, action, byte);
}
#[inline]
fn process_utf8<P>(&mut self, performer: &mut P, byte: u8)
where P: Perform
{
let mut receiver = VtUtf8Receiver(performer, &mut self.state);
let utf8_parser = &mut self.utf8_parser;
utf8_parser.advance(&mut receiver, byte);
}
fn perform_state_change<P>(&mut self, performer: &mut P, state: State, action: Action, byte: u8)
where P: Perform
{
macro_rules! maybe_action {
($action:expr, $arg:expr) => {
match $action {
Action::None => (),
action => {
self.perform_action(performer, action, $arg);
},
}
}
}
match state {
State::Anywhere => {
self.perform_action(performer, action, byte);
},
state => {
let exit_action = self.state.exit_action();
maybe_action!(exit_action, 0);
maybe_action!(action, byte);
maybe_action!(state.entry_action(), 0);
self.state = state;
}
}
}
fn perform_action<P: Perform>(&mut self, performer: &mut P, action: Action, byte: u8) {
match action {
Action::Print => performer.print(byte as char),
Action::Execute => performer.execute(byte),
Action::Hook => {
performer.hook(
self.params(),
self.intermediates(),
self.ignoring,
byte
);
},
Action::Put => performer.put(byte),
Action::OscStart => performer.osc_start(),
Action::OscPut => performer.osc_put(byte),
Action::OscEnd => performer.osc_end(byte),
Action::Unhook => performer.unhook(byte),
Action::CsiDispatch => {
performer.csi_dispatch(
self.params(),
self.intermediates(),
self.ignoring,
byte as char
);
}
Action::EscDispatch => {
performer.esc_dispatch(
self.params(),
self.intermediates(),
self.ignoring,
byte
);
},
Action::Ignore | Action::None => (),
Action::Collect => {
if self.intermediate_idx == MAX_INTERMEDIATES {
self.ignoring = true;
} else {
self.intermediates[self.intermediate_idx] = byte;
self.intermediate_idx += 1;
}
},
Action::Param => {
if byte == 0x3b {
self.num_params += 1;
let idx = self.num_params - 1; self.params[idx] = 0;
} else {
if self.num_params == 0 {
self.num_params = 1;
self.params[0] = 0;
}
let idx = self.num_params - 1;
self.params[idx] *= 10;
self.params[idx] += (byte - ('0' as u8)) as i64;
}
},
Action::Clear => {
self.intermediate_idx = 0;
self.num_params = 0;
self.ignoring = false;
},
Action::BeginUtf8 => {
self.process_utf8(performer, byte);
},
}
}
}
pub trait Perform {
fn print(&mut self, char);
fn execute(&mut self, byte: u8);
fn hook(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, byte: u8);
fn put(&mut self, byte: u8);
fn unhook(&mut self, byte: u8);
fn osc_start(&mut self);
fn osc_put(&mut self, byte: u8);
fn osc_end(&mut self, byte: u8);
fn csi_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, char);
fn esc_dispatch(&mut self, params: &[i64], intermediates: &[u8], ignore: bool, byte: u8);
}