aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Wilm <joe@jwilm.com>2016-09-12 10:51:13 -0700
committerJoe Wilm <joe@jwilm.com>2016-09-12 10:51:13 -0700
commitdca1c5c633981aa96932e0d50d3e27fe7de7a2b2 (patch)
tree4b160c79627a31cd6e9639d4f3e4dc780a928f19
downloadr-alacritty-vte-dca1c5c633981aa96932e0d50d3e27fe7de7a2b2.tar.gz
r-alacritty-vte-dca1c5c633981aa96932e0d50d3e27fe7de7a2b2.tar.bz2
r-alacritty-vte-dca1c5c633981aa96932e0d50d3e27fe7de7a2b2.zip
WIP
-rw-r--r--.gitignore2
-rw-r--r--Cargo.toml6
-rw-r--r--codegen/Cargo.toml8
-rw-r--r--codegen/README.md11
-rw-r--r--codegen/src/ext.rs242
-rw-r--r--codegen/src/main.rs18
-rw-r--r--src/definitions.rs74
-rw-r--r--src/lib.rs53
-rw-r--r--src/table.rs.in214
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__
+];