diff options
author | Joe Wilm <joe@jwilm.com> | 2016-09-12 10:51:13 -0700 |
---|---|---|
committer | Joe Wilm <joe@jwilm.com> | 2016-09-12 10:51:13 -0700 |
commit | dca1c5c633981aa96932e0d50d3e27fe7de7a2b2 (patch) | |
tree | 4b160c79627a31cd6e9639d4f3e4dc780a928f19 | |
download | r-alacritty-vte-dca1c5c633981aa96932e0d50d3e27fe7de7a2b2.tar.gz r-alacritty-vte-dca1c5c633981aa96932e0d50d3e27fe7de7a2b2.tar.bz2 r-alacritty-vte-dca1c5c633981aa96932e0d50d3e27fe7de7a2b2.zip |
WIP
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Cargo.toml | 6 | ||||
-rw-r--r-- | codegen/Cargo.toml | 8 | ||||
-rw-r--r-- | codegen/README.md | 11 | ||||
-rw-r--r-- | codegen/src/ext.rs | 242 | ||||
-rw-r--r-- | codegen/src/main.rs | 18 | ||||
-rw-r--r-- | src/definitions.rs | 74 | ||||
-rw-r--r-- | src/lib.rs | 53 | ||||
-rw-r--r-- | src/table.rs.in | 214 |
9 files changed, 628 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a9d37c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +target +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..cf36ebb --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "fun-table" +version = "0.1.0" +authors = ["Joe Wilm <joe@jwilm.com>"] + +[dependencies] diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml new file mode 100644 index 0000000..a48de05 --- /dev/null +++ b/codegen/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "codegen" +version = "0.1.0" +authors = ["Joe Wilm <joe@jwilm.com>"] + +[dependencies] +syntex = "0.43.0" +syntex_syntax = "0.43.0" diff --git a/codegen/README.md b/codegen/README.md new file mode 100644 index 0000000..d13aa5d --- /dev/null +++ b/codegen/README.md @@ -0,0 +1,11 @@ +codegen +======= + +Depends on libsyntex and generates table.rs from table.rs.in. This code is +separate from the main vtparse crate since compiling libsyntex takes ~1 +eternity. + +## Usage + +`cargo run` in the codegen folder will process `table.rs.in` and output +`table.rs`. The latter file should be committed back into the repo. diff --git a/codegen/src/ext.rs b/codegen/src/ext.rs new file mode 100644 index 0000000..3a08935 --- /dev/null +++ b/codegen/src/ext.rs @@ -0,0 +1,242 @@ +use syntex::Registry; + +use syntex_syntax::ast::{ExprKind, MetaItem, Arm, Expr, PatKind, LitKind, Pat}; +use syntex_syntax::codemap::Span; +use syntex_syntax::ext::base::{Annotatable, ExtCtxt, MacEager, MacResult, DummyResult}; +use syntex_syntax::ext::build::AstBuilder; +use syntex_syntax::parse::token::{Token, InternedString, DelimToken}; +use syntex_syntax::parse::parser::Parser; +use syntex_syntax::parse::PResult; +use syntex_syntax::ptr::P; +use syntex_syntax::tokenstream::TokenTree; + +use definitions::{State, Action}; + +pub fn register(registry: &mut Registry) { + registry.add_macro("state_table", expand_state_table); +} + +fn state_from_str<S>(s: &S) -> Result<State, ()> + where S: AsRef<str> +{ + Ok(match s.as_ref() { + "State::Anywhere" => State::Anywhere, + "State::CsiEntry" => State::CsiEntry, + "State::CsiIgnore" => State::CsiIgnore, + "State::CsiIntermediate" => State::CsiIntermediate, + "State::CsiParam" => State::CsiParam, + "State::DcsEntry" => State::DcsEntry, + "State::DcsIgnore" => State::DcsIgnore, + "State::DcsIntermediate" => State::DcsIntermediate, + "State::DcsParam" => State::DcsParam, + "State::DcsPassthrough" => State::DcsPassthrough, + "State::Escape" => State::Escape, + "State::EscapeIntermediate" => State::EscapeIntermediate, + "State::Ground" => State::Ground, + "State::OscString" => State::OscString, + "State::SosPmApcString" => State::SosPmApcString, + _ => return Err(()) + }) +} + +fn parse_table_input_mappings<'a>(parser: &mut Parser<'a>) -> PResult<'a, Vec<Arm>> { + // Must start on open brace + try!(parser.expect(&Token::OpenDelim(DelimToken::Brace))); + + let mut arms: Vec<Arm> = Vec::new(); + while parser.token != Token::CloseDelim(DelimToken::Brace) { + match parser.parse_arm() { + Ok(arm) => arms.push(arm), + Err(mut e) => { + // Recover by skipping to the end of the block. + return Err(e); + } + } + } + + // Consume the closing brace + parser.bump(); + Ok(arms) +} + +/// Expressions describing state transitions and actions +#[derive(Debug)] +struct TableDefinitionExprs { + state_expr: P<Expr>, + mapping_arms: Vec<Arm>, +} + +fn state_from_expr(expr: P<Expr>, cx: &mut ExtCtxt) -> Result<State, ()> { + let s = match expr.node { + ExprKind::Path(ref _qself, ref path) => { + path.to_string() + }, + _ => { + cx.span_err(expr.span, "expected State"); + return Err(()) + } + }; + + state_from_str(&s).map_err(|_| { + cx.span_err(expr.span, "expected State"); + () + }) +} + +fn u8_lit_from_expr(expr: &Expr, cx: &mut ExtCtxt) -> Result<u8, ()> { + static MSG: &'static str = "expected u8 int literal"; + + match expr.node { + ExprKind::Lit(ref lit) => { + match lit.node { + LitKind::Int(val, _) => { + Ok(val as u8) + }, + _ => { + cx.span_err(lit.span, MSG); + return Err(()); + } + } + }, + _ => { + cx.span_err(expr.span, MSG); + return Err(()); + } + } +} + +fn input_mapping_from_arm(arm: Arm, cx: &mut ExtCtxt) -> Result<InputMapping, ()> { + let Arm { pats, body, .. } = arm; + + let input = try!(InputDefinition::from_pat(&pats[0], cx)); + let transition = try!(Transition::from_expr(&body, cx)); + + Ok(InputMapping { + input: input, + transition: transition, + }) +} + +/// What happens when certain input is received +enum Transition { + State(State), + Action(Action), + StateAction(State, Action), +} + +impl Transition { + fn from_expr(expr: &Expr, cx: &mut ExtCtxt) -> Result<Transition, ()> { + unimplemented!(); + } +} + +enum InputDefinition { + Specific(u8), + Range { start: u8, end: u8 } +} + +impl InputDefinition { + fn from_pat(pat: &Pat, cx: &mut ExtCtxt) -> Result<InputDefinition, ()> { + Ok(match pat.node { + PatKind::Lit(ref lit_expr) => { + InputDefinition::Specific(try!(u8_lit_from_expr(&lit_expr, cx))) + }, + PatKind::Range(ref start_expr, ref end_expr) => { + InputDefinition::Range { + start: try!(u8_lit_from_expr(start_expr, cx)), + end: try!(u8_lit_from_expr(end_expr, cx)), + } + }, + _ => { + cx.span_err(pat.span, "expected literal or range expression"); + return Err(()) + } + }) + } +} + +struct InputMapping { + input: InputDefinition, + transition: Transition, +} + +struct TableDefinition { + state: State, + mappings: Vec<InputMapping>, +} + +fn parse_raw_definitions( + definitions: Vec<TableDefinitionExprs>, + cx: &mut ExtCtxt +) -> Result<Vec<TableDefinition>, ()> { + let mut out = Vec::new(); + + for raw in definitions { + let TableDefinitionExprs { state_expr, mapping_arms } = raw; + let state = try!(state_from_expr(state_expr, cx)); + + let mut mappings = Vec::new(); + for arm in mapping_arms { + mappings.push(try!(input_mapping_from_arm(arm, cx))); + } + + out.push(TableDefinition { + state: state, + mappings: mappings, + }) + } + + Ok(out) +} + +fn parse_table_definition<'a>(parser: &mut Parser<'a>) -> PResult<'a, TableDefinitionExprs> { + let state_expr = try!(parser.parse_expr()); + try!(parser.expect(&Token::FatArrow)); + let mappings = try!(parse_table_input_mappings(parser)); + + Ok(TableDefinitionExprs { + state_expr: state_expr, + mapping_arms: mappings + }) +} + +fn parse_table_definition_list<'a>(parser: &mut Parser<'a>) + -> PResult<'a, Vec<TableDefinitionExprs>> +{ + let mut definitions = Vec::new(); + while parser.token != Token::Eof { + definitions.push(try!(parse_table_definition(parser))); + parser.eat(&Token::Comma); + } + + Ok(definitions) +} + +fn expand_state_table<'cx>( + cx: &'cx mut ExtCtxt, + sp: Span, + args: &[TokenTree]) + -> Box<MacResult + 'cx> +{ + macro_rules! ptry { + ($pres:expr) => { + match $pres { + Ok(val) => val, + Err(mut err) => { + err.emit(); + return DummyResult::any(sp); + } + } + } + } + + // Parse the lookup spec + let mut parser: Parser = cx.new_parser_from_tts(args); + let definitions = ptry!(parse_table_definition_list(&mut parser)); + let definitions = match parse_raw_definitions(definitions, cx) { + Ok(definitions) => definitions, + Err(_) => return DummyResult::any(sp), + }; + + panic!("End of current implementation, go write some more :)"); +} diff --git a/codegen/src/main.rs b/codegen/src/main.rs new file mode 100644 index 0000000..d1994bb --- /dev/null +++ b/codegen/src/main.rs @@ -0,0 +1,18 @@ +extern crate syntex; +extern crate syntex_syntax; + +mod ext; + +#[path="../../src/definitions.rs"] +pub mod definitions; + +use std::path::Path; + +fn main() { + let src = &Path::new("../src/table.rs.in"); + let dst = &Path::new("../src/table.rs"); + + let mut registry = syntex::Registry::new(); + ext::register(&mut registry); + registry.expand("state_table", src, dst); +} 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<P: Parser> { +// 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__ +]; |