325 lines
10 KiB
Rust
325 lines
10 KiB
Rust
#[cfg(test)]
|
|
mod tests;
|
|
|
|
use crate::AnsiSequence;
|
|
|
|
use heapless::Vec;
|
|
use nom::branch::alt;
|
|
use nom::bytes::complete::tag;
|
|
use nom::character::complete::{digit0, digit1};
|
|
use nom::combinator::{map, map_res, opt, value};
|
|
use nom::sequence::{delimited, preceded};
|
|
use nom::IResult;
|
|
use nom::Parser;
|
|
|
|
macro_rules! tag_parser {
|
|
($sig:ident, $tag:expr, $ret:expr) => {
|
|
fn $sig(input: &str) -> IResult<&str, AnsiSequence> {
|
|
value($ret, tag($tag)).parse(input)
|
|
}
|
|
};
|
|
}
|
|
|
|
fn parse_u32(input: &str) -> IResult<&str, u32> {
|
|
map_res(digit1, |s: &str| s.parse::<u32>()).parse(input)
|
|
}
|
|
|
|
fn parse_u8(input: &str) -> IResult<&str, u8> {
|
|
map_res(digit1, |s: &str| s.parse::<u8>()).parse(input)
|
|
}
|
|
|
|
// TODO kind of ugly, would prefer to pass in the default so we could use it for
|
|
// all escapes with defaults (not just those that default to 1).
|
|
fn parse_def_cursor_int(input: &str) -> IResult<&str, u32> {
|
|
map(digit0, |s: &str| s.parse::<u32>().unwrap_or(1)).parse(input)
|
|
}
|
|
|
|
fn cursor_pos(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(
|
|
(
|
|
tag("["),
|
|
parse_def_cursor_int,
|
|
opt(tag(";")),
|
|
parse_def_cursor_int,
|
|
alt((tag("H"), tag("f"))),
|
|
),
|
|
|(_, x, _, y, _)| AnsiSequence::CursorPos(x, y),
|
|
)
|
|
.parse(input)
|
|
}
|
|
|
|
fn escape(input: &str) -> IResult<&str, AnsiSequence> {
|
|
value(AnsiSequence::Escape, tag("\u{1b}")).parse(input)
|
|
}
|
|
|
|
fn cursor_up(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(delimited(tag("["), parse_def_cursor_int, tag("A")), |am| {
|
|
AnsiSequence::CursorUp(am)
|
|
})
|
|
.parse(input)
|
|
}
|
|
|
|
fn cursor_down(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(delimited(tag("["), parse_def_cursor_int, tag("B")), |am| {
|
|
AnsiSequence::CursorDown(am)
|
|
})
|
|
.parse(input)
|
|
}
|
|
|
|
fn cursor_right(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(delimited(tag("["), parse_def_cursor_int, tag("C")), |am| {
|
|
AnsiSequence::CursorRight(am)
|
|
})
|
|
.parse(input)
|
|
}
|
|
|
|
fn cursor_left(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(delimited(tag("["), parse_def_cursor_int, tag("D")), |am| {
|
|
AnsiSequence::CursorLeft(am)
|
|
})
|
|
.parse(input)
|
|
}
|
|
|
|
fn graphics_mode1(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(delimited(tag("["), parse_u8, tag("m")), |val| {
|
|
let mode =
|
|
Vec::from_slice(&[val]).expect("Vec::from_slice should allocate sufficient size");
|
|
AnsiSequence::SetGraphicsMode(mode)
|
|
})
|
|
.parse(input)
|
|
}
|
|
|
|
fn graphics_mode2(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(
|
|
(tag("["), parse_u8, tag(";"), parse_u8, tag("m")),
|
|
|(_, val1, _, val2, _)| {
|
|
let mode = Vec::from_slice(&[val1, val2])
|
|
.expect("Vec::from_slice should allocate sufficient size");
|
|
AnsiSequence::SetGraphicsMode(mode)
|
|
},
|
|
)
|
|
.parse(input)
|
|
}
|
|
|
|
fn graphics_mode3(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(
|
|
(
|
|
tag("["),
|
|
parse_u8,
|
|
tag(";"),
|
|
parse_u8,
|
|
tag(";"),
|
|
parse_u8,
|
|
tag("m"),
|
|
),
|
|
|(_, val1, _, val2, _, val3, _)| {
|
|
let mode = Vec::from_slice(&[val1, val2, val3])
|
|
.expect("Vec::from_slice should allocate sufficient size");
|
|
AnsiSequence::SetGraphicsMode(mode)
|
|
},
|
|
)
|
|
.parse(input)
|
|
}
|
|
|
|
fn graphics_mode4(input: &str) -> IResult<&str, AnsiSequence> {
|
|
value(AnsiSequence::SetGraphicsMode(Vec::new()), tag("[m")).parse(input)
|
|
}
|
|
|
|
fn graphics_mode5(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(
|
|
(
|
|
tag("["),
|
|
parse_u8,
|
|
tag(";"),
|
|
parse_u8,
|
|
tag(";"),
|
|
parse_u8,
|
|
tag(";"),
|
|
parse_u8,
|
|
tag(";"),
|
|
parse_u8,
|
|
tag("m"),
|
|
),
|
|
|(_, val1, _, val2, _, val3, _, val4, _, val5, _)| {
|
|
let mode = Vec::from_slice(&[val1, val2, val3, val4, val5])
|
|
.expect("Vec::from_slice should allocate sufficient size");
|
|
AnsiSequence::SetGraphicsMode(mode)
|
|
},
|
|
)
|
|
.parse(input)
|
|
}
|
|
|
|
fn graphics_mode(input: &str) -> IResult<&str, AnsiSequence> {
|
|
alt((
|
|
graphics_mode1,
|
|
graphics_mode2,
|
|
graphics_mode3,
|
|
graphics_mode4,
|
|
graphics_mode5,
|
|
))
|
|
.parse(input)
|
|
}
|
|
|
|
fn set_mode(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(delimited(tag("[="), parse_u8, tag("h")), |val| {
|
|
AnsiSequence::SetMode(val)
|
|
})
|
|
.parse(input)
|
|
}
|
|
|
|
fn reset_mode(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(delimited(tag("[="), parse_u8, tag("l")), |val| {
|
|
AnsiSequence::ResetMode(val)
|
|
})
|
|
.parse(input)
|
|
}
|
|
|
|
fn set_top_and_bottom(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(
|
|
(tag("["), parse_u32, tag(";"), parse_u32, tag("r")),
|
|
|(_, x, _, y, _)| AnsiSequence::SetTopAndBottom(x, y),
|
|
)
|
|
.parse(input)
|
|
}
|
|
|
|
fn erase_line_to_end(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(alt((tag("[K"), tag("[0K"))), |_| {
|
|
AnsiSequence::EraseLineToEnd
|
|
})
|
|
.parse(input)
|
|
}
|
|
|
|
fn erase_screen_to_end(input: &str) -> IResult<&str, AnsiSequence> {
|
|
map(alt((tag("[J"), tag("[0J"))), |_| {
|
|
AnsiSequence::EraseScreenToEnd
|
|
})
|
|
.parse(input)
|
|
}
|
|
|
|
tag_parser!(
|
|
erase_screen_from_start,
|
|
"[1J",
|
|
AnsiSequence::EraseScreenFromStart
|
|
);
|
|
tag_parser!(
|
|
erase_line_from_start,
|
|
"[1K",
|
|
AnsiSequence::EraseLineFromStart
|
|
);
|
|
tag_parser!(cursor_up_scroll, "[ M", AnsiSequence::CursorUpScroll);
|
|
tag_parser!(erase_line, "[2K", AnsiSequence::EraseLine);
|
|
tag_parser!(cursor_home, "[H", AnsiSequence::CursorHome);
|
|
tag_parser!(request_cursor_pos, "[6n", AnsiSequence::RequestCursorPos);
|
|
tag_parser!(cursor_save, "[s", AnsiSequence::CursorSave);
|
|
tag_parser!(cursor_restore, "[u", AnsiSequence::CursorRestore);
|
|
tag_parser!(erase_screen, "[2J", AnsiSequence::EraseScreen);
|
|
tag_parser!(hide_cursor, "[?25l", AnsiSequence::HideCursor);
|
|
tag_parser!(show_cursor, "[?25h", AnsiSequence::ShowCursor);
|
|
tag_parser!(cursor_to_app, "[?1h", AnsiSequence::CursorToApp);
|
|
tag_parser!(set_new_line_mode, "[20h", AnsiSequence::SetNewLineMode);
|
|
tag_parser!(set_col_132, "[?3h", AnsiSequence::SetCol132);
|
|
tag_parser!(set_smooth_scroll, "[?4h", AnsiSequence::SetSmoothScroll);
|
|
tag_parser!(set_reverse_video, "[?5h", AnsiSequence::SetReverseVideo);
|
|
tag_parser!(set_origin_rel, "[?6h", AnsiSequence::SetOriginRelative);
|
|
tag_parser!(set_auto_wrap, "[?7h", AnsiSequence::SetAutoWrap);
|
|
tag_parser!(set_auto_repeat, "[?8h", AnsiSequence::SetAutoRepeat);
|
|
tag_parser!(set_interlacing, "[?9h", AnsiSequence::SetInterlacing);
|
|
tag_parser!(set_linefeed, "[20l", AnsiSequence::SetLineFeedMode);
|
|
tag_parser!(set_cursorkey, "[?1l", AnsiSequence::SetCursorKeyToCursor);
|
|
tag_parser!(set_vt52, "[?2l", AnsiSequence::SetVT52);
|
|
tag_parser!(set_col80, "[?3l", AnsiSequence::SetCol80);
|
|
tag_parser!(set_jump_scroll, "[?4l", AnsiSequence::SetJumpScrolling);
|
|
tag_parser!(set_normal_video, "[?5l", AnsiSequence::SetNormalVideo);
|
|
tag_parser!(set_origin_abs, "[?6l", AnsiSequence::SetOriginAbsolute);
|
|
tag_parser!(reset_auto_wrap, "[?7l", AnsiSequence::ResetAutoWrap);
|
|
tag_parser!(reset_auto_repeat, "[?8l", AnsiSequence::ResetAutoRepeat);
|
|
tag_parser!(reset_interlacing, "[?9l", AnsiSequence::ResetInterlacing);
|
|
|
|
tag_parser!(set_alternate_keypad, "=", AnsiSequence::SetAlternateKeypad);
|
|
tag_parser!(set_numeric_keypad, ">", AnsiSequence::SetNumericKeypad);
|
|
tag_parser!(set_uk_g0, "(A", AnsiSequence::SetUKG0);
|
|
tag_parser!(set_uk_g1, ")A", AnsiSequence::SetUKG1);
|
|
tag_parser!(set_us_g0, "(B", AnsiSequence::SetUSG0);
|
|
tag_parser!(set_us_g1, ")B", AnsiSequence::SetUSG1);
|
|
tag_parser!(set_g0_special, "(0", AnsiSequence::SetG0SpecialChars);
|
|
tag_parser!(set_g1_special, ")0", AnsiSequence::SetG1SpecialChars);
|
|
tag_parser!(set_g0_alternate, "(1", AnsiSequence::SetG0AlternateChar);
|
|
tag_parser!(set_g1_alternate, ")1", AnsiSequence::SetG1AlternateChar);
|
|
tag_parser!(set_g0_graph, "(2", AnsiSequence::SetG0AltAndSpecialGraph);
|
|
tag_parser!(set_g1_graph, ")2", AnsiSequence::SetG1AltAndSpecialGraph);
|
|
tag_parser!(set_single_shift2, "N", AnsiSequence::SetSingleShift2);
|
|
tag_parser!(set_single_shift3, "O", AnsiSequence::SetSingleShift3);
|
|
|
|
fn combined(input: &str) -> IResult<&str, AnsiSequence> {
|
|
// `alt` only supports up to 21 parsers, and nom doesn't seem to
|
|
// have an alternative with higher variability.
|
|
// So we simply nest them.
|
|
alt((
|
|
alt((
|
|
escape,
|
|
cursor_pos,
|
|
cursor_up,
|
|
cursor_down,
|
|
cursor_right,
|
|
cursor_left,
|
|
cursor_save,
|
|
cursor_restore,
|
|
erase_screen,
|
|
erase_line,
|
|
graphics_mode,
|
|
set_mode,
|
|
reset_mode,
|
|
hide_cursor,
|
|
show_cursor,
|
|
cursor_to_app,
|
|
set_new_line_mode,
|
|
set_col_132,
|
|
set_smooth_scroll,
|
|
set_reverse_video,
|
|
set_origin_rel,
|
|
)),
|
|
alt((
|
|
set_auto_wrap,
|
|
set_auto_repeat,
|
|
set_interlacing,
|
|
set_linefeed,
|
|
set_cursorkey,
|
|
set_vt52,
|
|
set_col80,
|
|
set_jump_scroll,
|
|
set_normal_video,
|
|
set_origin_abs,
|
|
reset_auto_wrap,
|
|
reset_auto_repeat,
|
|
reset_interlacing,
|
|
set_top_and_bottom,
|
|
set_alternate_keypad,
|
|
set_numeric_keypad,
|
|
set_uk_g0,
|
|
set_uk_g1,
|
|
set_us_g0,
|
|
set_us_g1,
|
|
set_g0_special,
|
|
)),
|
|
set_g1_special,
|
|
set_g0_alternate,
|
|
set_g1_alternate,
|
|
set_g0_graph,
|
|
set_g1_graph,
|
|
set_single_shift2,
|
|
set_single_shift3,
|
|
request_cursor_pos,
|
|
cursor_home,
|
|
erase_line_from_start,
|
|
erase_screen_from_start,
|
|
erase_screen_to_end,
|
|
erase_line_to_end,
|
|
cursor_up_scroll,
|
|
))
|
|
.parse(input)
|
|
}
|
|
|
|
pub fn parse_escape(input: &str) -> IResult<&str, AnsiSequence> {
|
|
preceded(tag("\u{1b}"), combined).parse(input)
|
|
}
|