From c2b8f5ed57ba986d55856409914905853624a338 Mon Sep 17 00:00:00 2001 From: David Bittner Date: Fri, 26 Apr 2019 17:58:27 -0400 Subject: [PATCH] it all works, just need to figure out how to deal with the end of the stream --- Cargo.lock | 54 ++++++++++ Cargo.toml | 2 + src/enums.rs | 49 +++++++++ src/lib.rs | 146 +------------------------- src/parsers.rs | 238 +++++++++++++++++++++++++++++++++++++++++++ src/parsers/tests.rs | 43 ++++++++ 6 files changed, 389 insertions(+), 143 deletions(-) create mode 100644 src/enums.rs create mode 100644 src/parsers.rs create mode 100644 src/parsers/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 885967e..1002107 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,6 +5,8 @@ name = "ansi-parser" version = "0.1.0" dependencies = [ "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -21,6 +23,52 @@ dependencies = [ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num-derive" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.33 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "proc-macro2" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.15.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "version_check" version = "0.1.5" @@ -29,4 +77,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" +"checksum num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2" +"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" +"checksum proc-macro2 0.4.28 (registry+https://github.com/rust-lang/crates.io-index)" = "ba92c84f814b3f9a44c5cfca7d2ad77fa10710867d2bbb1b3d175ab5f47daa12" +"checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" +"checksum syn 0.15.33 (registry+https://github.com/rust-lang/crates.io-index)" = "ec52cd796e5f01d0067225a5392e70084acc4c0013fa71d55166d38a8b307836" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" diff --git a/Cargo.toml b/Cargo.toml index 49338a6..fef0ff8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,5 @@ edition = "2018" [dependencies] nom = "4.2.3" +num-traits = "0.2" +num-derive = "0.2" diff --git a/src/enums.rs b/src/enums.rs new file mode 100644 index 0000000..aaeb6d9 --- /dev/null +++ b/src/enums.rs @@ -0,0 +1,49 @@ +use num_derive::FromPrimitive; + +#[derive(Debug, PartialEq, FromPrimitive)] +pub enum TextAttribute { + Off = 0, + Bold = 1, + Underscore = 4, + Blink = 5, + ReverseVideo = 7, + Concealed = 8 +} + +#[derive(Debug, PartialEq, FromPrimitive)] +pub enum Color { + Black = 30, + Red = 31, + Green = 32, + Yellow = 33, + Blue = 34, + Magenta = 35, + Cyan = 36, + White = 37 +} + +#[derive(Debug, PartialEq)] +pub enum AnsiSequence { + CursorPos(u32, u32), + CursorUp(u32), + CursorDown(u32), + CursorForward(u32), + CursorBackward(u32), + CursorSave, + CursorRestore, + EraseDisplay, + EraseLine, + SetGraphicsMode{ + ta: TextAttribute, + fg: Color, + bg: Color + }, + SetMode(u8), + ResetMode(u8), +} + +#[derive(Debug, PartialEq)] +pub enum Output<'a> { + TextBlock(&'a str), + Escape(AnsiSequence) +} diff --git a/src/lib.rs b/src/lib.rs index 3c6a8a0..9dce930 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,144 +1,4 @@ -use nom::*; +mod enums; +mod parsers; -#[derive(Debug, PartialEq)] -enum TextAttributes { - Off = 0, - Bold = 1, - Underscore = 4, - Blink = 5, - ReverseVideo = 7, - Concealed = 8 -} - -#[derive(Debug, PartialEq)] -enum Color { - Black = 30, - Red = 31, - Green = 32, - Yellow = 33, - Blue = 34, - Magenta = 35, - Cyan = 36, - White = 37 -} - -#[derive(Debug, PartialEq)] -enum AnsiSequence { - CursorPos(u32, u32), - CursorUp(u32), - CursorDown(u32), - CursorForward(u32), - CursorBackward(u32), - SaveCursorPos(u32), - RestoreCursorPos(u32), - EraseDisplay, - EraseLine, - SetGraphicsMode{ - ta: Option, - fg: Option, - bg: Option - }, - SetMode(u8), - Resetmode(u8), -} - -named!( - parse_int, - map_res!( - map_res!( - nom::digit, - std::str::from_utf8 - ), - |s: &str| s.parse::() - ) -); - -named!( - cursor_pos, - do_parse!( - x: parse_int >> - tag!(";") >> - y: parse_int >> - alt!( - tag!("H") | - tag!("f") - ) >> - (AnsiSequence::CursorPos(x, y)) - ) -); - -named!( - cursor_up, - do_parse!( - am: parse_int >> - tag!("A") >> - (AnsiSequence::CursorUp(am)) - ) -); - -named!( - cursor_down, - do_parse!( - am: parse_int >> - tag!("B") >> - (AnsiSequence::CursorDown(am)) - ) -); - -named!( - cursor_forward, - do_parse!( - am: parse_int >> - tag!("C") >> - (AnsiSequence::CursorForward(am)) - ) -); - -named!( - cursor_backward, - do_parse!( - am: parse_int >> - tag!("D") >> - (AnsiSequence::CursorBackward(am)) - ) -); - -named!( - combined, - alt!( - cursor_pos - | cursor_up - | cursor_down - | cursor_forward - | cursor_backward - ) -); - -named!( - parse_escape, - do_parse!( - tag_s!("\\\x27[") >> - seq: combined >> - (seq) - ) -); - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_combined() { - let temp = "\\\x27[15;244H"; - let temp = parse_escape(temp.as_bytes()); - - assert!(temp.is_ok()); - assert_eq!(AnsiSequence::CursorPos(15, 244), temp.unwrap().1); - - let temp = "\\\x27[22D"; - let temp = parse_escape(temp.as_bytes()); - - assert!(temp.is_ok()); - assert_eq!(AnsiSequence::CursorBackward(22), temp.unwrap().1); - } -} +pub use enums::*; diff --git a/src/parsers.rs b/src/parsers.rs new file mode 100644 index 0000000..e9059c5 --- /dev/null +++ b/src/parsers.rs @@ -0,0 +1,238 @@ +#[cfg(test)] +mod tests; + +use crate::{AnsiSequence, Output, Color, TextAttribute}; + +use num_traits::cast::FromPrimitive; +use std::convert::TryInto; +use nom::*; + +named!( + parse_int, + map_res!( + map_res!( + nom::digit, + std::str::from_utf8 + ), + |s: &str| s.parse::() + ) +); + +named!( + parse_fg_color, + do_parse!( + val: parse_int >> + worked: expr_opt!(FromPrimitive::from_u32(val)) >> + (worked) + ) +); + +named!( + parse_bg_color, + do_parse!( + val: parse_int >> + val: expr_opt!(val.checked_sub(10)) >> + worked: expr_opt!(FromPrimitive::from_u32(val)) >> + (worked) + ) +); + +named!( + parse_text_attr, + map!( + map!( + parse_int, + FromPrimitive::from_u32 + ), + Option::unwrap + ) +); + +named!( + cursor_pos, + do_parse!( + x: parse_int >> + tag!(";") >> + y: parse_int >> + alt!( + tag!("H") | + tag!("f") + ) >> + (AnsiSequence::CursorPos(x, y)) + ) +); + +named!( + cursor_up, + do_parse!( + am: parse_int >> + tag!("A") >> + (AnsiSequence::CursorUp(am)) + ) +); + +named!( + cursor_down, + do_parse!( + am: parse_int >> + tag!("B") >> + (AnsiSequence::CursorDown(am)) + ) +); + +named!( + cursor_forward, + do_parse!( + am: parse_int >> + tag!("C") >> + (AnsiSequence::CursorForward(am)) + ) +); + +named!( + cursor_backward, + do_parse!( + am: parse_int >> + tag!("D") >> + (AnsiSequence::CursorBackward(am)) + ) +); + +named!( + cursor_save, + do_parse!( + tag!("s") >> + (AnsiSequence::CursorSave) + ) +); + +named!( + cursor_restore, + do_parse!( + tag!("u") >> + (AnsiSequence::CursorRestore) + ) +); + +named!( + erase_display, + do_parse!( + tag!("2J") >> + (AnsiSequence::EraseDisplay) + ) +); + +named!( + erase_line, + do_parse!( + tag!("K") >> + (AnsiSequence::EraseDisplay) + ) +); + +named!( + graphics_mode, + do_parse!( + ta: parse_text_attr >> + tag!(";") >> + fg: parse_fg_color >> + tag!(";") >> + bg: parse_bg_color >> + tag!("m") >> + (AnsiSequence::SetGraphicsMode{ + ta: ta, + fg: fg, + bg: bg + }) + ) +); + +named!( + set_mode, + do_parse!( + tag!("=") >> + mode: parse_int >> + conv: expr_res!(mode.try_into()) >> + tag!("h") >> + (AnsiSequence::SetMode(conv)) + ) +); + +named!( + reset_mode, + do_parse!( + tag!("=") >> + mode: parse_int >> + conv: expr_res!(mode.try_into()) >> + tag!("l") >> + (AnsiSequence::ResetMode(conv)) + ) +); + +named!( + combined, + alt!( + cursor_pos + | cursor_up + | cursor_down + | cursor_forward + | cursor_backward + | cursor_save + | cursor_restore + | erase_display + | erase_line + | graphics_mode + | set_mode + | reset_mode + ) +); + +named!( + parse_escape, + do_parse!( + tag_s!("\x27[") >> + seq: combined >> + (Output::Escape(seq)) + ) +); + +named!( + parse_str, + do_parse!( + text: map_res!( + take_until!("\x27["), + std::str::from_utf8 + ) >> + (Output::TextBlock(text)) + ) +); + +named!( + parse_output, + do_parse!( + out: alt!(parse_escape | parse_str) >> + (out) + ) +); + +pub struct ParserIterator<'a> { + dat: &'a[u8], +} + +impl<'a> Iterator for ParserIterator<'a> { + type Item = Output<'a>; + + fn next(&mut self) -> Option { + let parse = parse_output(self.dat.as_bytes()) + .unwrap(); + + self.dat = parse.0; + Some(parse.1) + } +} + +pub fn iterate_on<'a>(bytes: &'a[u8]) -> ParserIterator<'a> { + ParserIterator { + dat: bytes + } +} diff --git a/src/parsers/tests.rs b/src/parsers/tests.rs new file mode 100644 index 0000000..3c408a2 --- /dev/null +++ b/src/parsers/tests.rs @@ -0,0 +1,43 @@ +use super::*; + +#[test] +fn test_graphics_mode() { + let parse = "4;31;42m"; + let temp = graphics_mode(parse.as_bytes()); + + assert!(temp.is_ok()); + assert_eq!(AnsiSequence::SetGraphicsMode{ + ta: TextAttribute::Underscore, + fg: Color::Red, + bg: Color::Green + }, + temp.unwrap().1 + ); +} + +#[test] +fn test_set_mode() { + let parse = "=7h"; + let temp = set_mode(parse.as_bytes()); + + assert_eq!(AnsiSequence::SetMode(7), temp.unwrap().1); +} + +#[test] +fn test_reset_mode() { + let parse = "=13l"; + let temp = reset_mode(parse.as_bytes()); + + assert_eq!(AnsiSequence::ResetMode(13), temp.unwrap().1); +} + +#[test] +fn test_parser_iterator() { + let parse_str = "Hello, world? How are \x27[=7lyou? I hope you're doing well."; + + let strings: Vec = iterate_on(parse_str.as_bytes()) + .take(2) + .collect(); + + println!("{:#?}", strings); +}