ansi_parser/
parsers.rs

1#[cfg(test)]
2mod tests;
3
4use crate::AnsiSequence;
5
6use heapless::Vec;
7use nom::branch::alt;
8use nom::bytes::complete::tag;
9use nom::character::complete::{digit0, digit1};
10use nom::combinator::{map, map_res, opt, value};
11use nom::sequence::{delimited, preceded, tuple};
12use nom::IResult;
13
14macro_rules! tag_parser {
15    ($sig:ident, $tag:expr, $ret:expr) => {
16        fn $sig(input: &str) -> IResult<&str, AnsiSequence> {
17            value($ret, tag($tag))(input)
18        }
19    };
20}
21
22fn parse_u32(input: &str) -> IResult<&str, u32> {
23    map_res(digit1, |s: &str| s.parse::<u32>())(input)
24}
25
26fn parse_u8(input: &str) -> IResult<&str, u8> {
27    map_res(digit1, |s: &str| s.parse::<u8>())(input)
28}
29
30// TODO kind of ugly, would prefer to pass in the default so we could use it for
31// all escapes with defaults (not just those that default to 1).
32fn parse_def_cursor_int(input: &str) -> IResult<&str, u32> {
33    map(digit0, |s: &str| s.parse::<u32>().unwrap_or(1))(input)
34}
35
36fn cursor_pos(input: &str) -> IResult<&str, AnsiSequence> {
37    map(
38        tuple((
39            tag("["),
40            parse_def_cursor_int,
41            opt(tag(";")),
42            parse_def_cursor_int,
43            alt((tag("H"), tag("f"))),
44        )),
45        |(_, x, _, y, _)| AnsiSequence::CursorPos(x, y),
46    )(input)
47}
48
49fn escape(input: &str) -> IResult<&str, AnsiSequence> {
50    value(AnsiSequence::Escape, tag("\u{1b}"))(input)
51}
52
53fn cursor_up(input: &str) -> IResult<&str, AnsiSequence> {
54    map(delimited(tag("["), parse_def_cursor_int, tag("A")), |am| {
55        AnsiSequence::CursorUp(am)
56    })(input)
57}
58
59fn cursor_down(input: &str) -> IResult<&str, AnsiSequence> {
60    map(delimited(tag("["), parse_def_cursor_int, tag("B")), |am| {
61        AnsiSequence::CursorDown(am)
62    })(input)
63}
64
65fn cursor_forward(input: &str) -> IResult<&str, AnsiSequence> {
66    map(delimited(tag("["), parse_def_cursor_int, tag("C")), |am| {
67        AnsiSequence::CursorForward(am)
68    })(input)
69}
70
71fn cursor_backward(input: &str) -> IResult<&str, AnsiSequence> {
72    map(delimited(tag("["), parse_def_cursor_int, tag("D")), |am| {
73        AnsiSequence::CursorBackward(am)
74    })(input)
75}
76
77fn graphics_mode1(input: &str) -> IResult<&str, AnsiSequence> {
78    map(delimited(tag("["), parse_u8, tag("m")), |val| {
79        let mode =
80            Vec::from_slice(&[val]).expect("Vec::from_slice should allocate sufficient size");
81        AnsiSequence::SetGraphicsMode(mode)
82    })(input)
83}
84
85fn graphics_mode2(input: &str) -> IResult<&str, AnsiSequence> {
86    map(
87        tuple((tag("["), parse_u8, tag(";"), parse_u8, tag("m"))),
88        |(_, val1, _, val2, _)| {
89            let mode = Vec::from_slice(&[val1, val2])
90                .expect("Vec::from_slice should allocate sufficient size");
91            AnsiSequence::SetGraphicsMode(mode)
92        },
93    )(input)
94}
95
96fn graphics_mode3(input: &str) -> IResult<&str, AnsiSequence> {
97    map(
98        tuple((
99            tag("["),
100            parse_u8,
101            tag(";"),
102            parse_u8,
103            tag(";"),
104            parse_u8,
105            tag("m"),
106        )),
107        |(_, val1, _, val2, _, val3, _)| {
108            let mode = Vec::from_slice(&[val1, val2, val3])
109                .expect("Vec::from_slice should allocate sufficient size");
110            AnsiSequence::SetGraphicsMode(mode)
111        },
112    )(input)
113}
114
115fn graphics_mode4(input: &str) -> IResult<&str, AnsiSequence> {
116    value(AnsiSequence::SetGraphicsMode(Vec::new()), tag("[m"))(input)
117}
118
119fn graphics_mode5(input: &str) -> IResult<&str, AnsiSequence> {
120    map(
121        tuple((
122            tag("["),
123            parse_u8,
124            tag(";"),
125            parse_u8,
126            tag(";"),
127            parse_u8,
128            tag(";"),
129            parse_u8,
130            tag(";"),
131            parse_u8,
132            tag("m"),
133        )),
134        |(_, val1, _, val2, _, val3, _, val4, _, val5, _)| {
135            let mode = Vec::from_slice(&[val1, val2, val3, val4, val5])
136                .expect("Vec::from_slice should allocate sufficient size");
137            AnsiSequence::SetGraphicsMode(mode)
138        },
139    )(input)
140}
141
142fn graphics_mode(input: &str) -> IResult<&str, AnsiSequence> {
143    alt((
144        graphics_mode1,
145        graphics_mode2,
146        graphics_mode3,
147        graphics_mode4,
148        graphics_mode5,
149    ))(input)
150}
151
152fn set_mode(input: &str) -> IResult<&str, AnsiSequence> {
153    map(delimited(tag("[="), parse_u8, tag("h")), |val| {
154        AnsiSequence::SetMode(val)
155    })(input)
156}
157
158fn reset_mode(input: &str) -> IResult<&str, AnsiSequence> {
159    map(delimited(tag("[="), parse_u8, tag("l")), |val| {
160        AnsiSequence::ResetMode(val)
161    })(input)
162}
163
164fn set_top_and_bottom(input: &str) -> IResult<&str, AnsiSequence> {
165    map(
166        tuple((tag("["), parse_u32, tag(";"), parse_u32, tag("r"))),
167        |(_, x, _, y, _)| AnsiSequence::SetTopAndBottom(x, y),
168    )(input)
169}
170
171tag_parser!(cursor_save, "[s", AnsiSequence::CursorSave);
172tag_parser!(cursor_restore, "[u", AnsiSequence::CursorRestore);
173tag_parser!(erase_display, "[2J", AnsiSequence::EraseDisplay);
174tag_parser!(erase_line, "[K", AnsiSequence::EraseLine);
175tag_parser!(hide_cursor, "[?25l", AnsiSequence::HideCursor);
176tag_parser!(show_cursor, "[?25h", AnsiSequence::ShowCursor);
177tag_parser!(cursor_to_app, "[?1h", AnsiSequence::CursorToApp);
178tag_parser!(set_new_line_mode, "[20h", AnsiSequence::SetNewLineMode);
179tag_parser!(set_col_132, "[?3h", AnsiSequence::SetCol132);
180tag_parser!(set_smooth_scroll, "[?4h", AnsiSequence::SetSmoothScroll);
181tag_parser!(set_reverse_video, "[?5h", AnsiSequence::SetReverseVideo);
182tag_parser!(set_origin_rel, "[?6h", AnsiSequence::SetOriginRelative);
183tag_parser!(set_auto_wrap, "[?7h", AnsiSequence::SetAutoWrap);
184tag_parser!(set_auto_repeat, "[?8h", AnsiSequence::SetAutoRepeat);
185tag_parser!(set_interlacing, "[?9h", AnsiSequence::SetInterlacing);
186tag_parser!(set_linefeed, "[20l", AnsiSequence::SetLineFeedMode);
187tag_parser!(set_cursorkey, "[?1l", AnsiSequence::SetCursorKeyToCursor);
188tag_parser!(set_vt52, "[?2l", AnsiSequence::SetVT52);
189tag_parser!(set_col80, "[?3l", AnsiSequence::SetCol80);
190tag_parser!(set_jump_scroll, "[?4l", AnsiSequence::SetJumpScrolling);
191tag_parser!(set_normal_video, "[?5l", AnsiSequence::SetNormalVideo);
192tag_parser!(set_origin_abs, "[?6l", AnsiSequence::SetOriginAbsolute);
193tag_parser!(reset_auto_wrap, "[?7l", AnsiSequence::ResetAutoWrap);
194tag_parser!(reset_auto_repeat, "[?8l", AnsiSequence::ResetAutoRepeat);
195tag_parser!(reset_interlacing, "[?9l", AnsiSequence::ResetInterlacing);
196
197tag_parser!(set_alternate_keypad, "=", AnsiSequence::SetAlternateKeypad);
198tag_parser!(set_numeric_keypad, ">", AnsiSequence::SetNumericKeypad);
199tag_parser!(set_uk_g0, "(A", AnsiSequence::SetUKG0);
200tag_parser!(set_uk_g1, ")A", AnsiSequence::SetUKG1);
201tag_parser!(set_us_g0, "(B", AnsiSequence::SetUSG0);
202tag_parser!(set_us_g1, ")B", AnsiSequence::SetUSG1);
203tag_parser!(set_g0_special, "(0", AnsiSequence::SetG0SpecialChars);
204tag_parser!(set_g1_special, ")0", AnsiSequence::SetG1SpecialChars);
205tag_parser!(set_g0_alternate, "(1", AnsiSequence::SetG0AlternateChar);
206tag_parser!(set_g1_alternate, ")1", AnsiSequence::SetG1AlternateChar);
207tag_parser!(set_g0_graph, "(2", AnsiSequence::SetG0AltAndSpecialGraph);
208tag_parser!(set_g1_graph, ")2", AnsiSequence::SetG1AltAndSpecialGraph);
209tag_parser!(set_single_shift2, "N", AnsiSequence::SetSingleShift2);
210tag_parser!(set_single_shift3, "O", AnsiSequence::SetSingleShift3);
211
212fn combined(input: &str) -> IResult<&str, AnsiSequence> {
213    // `alt` only supports up to 21 parsers, and nom doesn't seem to
214    // have an alternative with higher variability.
215    // So we simply nest them.
216    alt((
217        alt((
218            escape,
219            cursor_pos,
220            cursor_up,
221            cursor_down,
222            cursor_forward,
223            cursor_backward,
224            cursor_save,
225            cursor_restore,
226            erase_display,
227            erase_line,
228            graphics_mode,
229            set_mode,
230            reset_mode,
231            hide_cursor,
232            show_cursor,
233            cursor_to_app,
234            set_new_line_mode,
235            set_col_132,
236            set_smooth_scroll,
237            set_reverse_video,
238            set_origin_rel,
239        )),
240        alt((
241            set_auto_wrap,
242            set_auto_repeat,
243            set_interlacing,
244            set_linefeed,
245            set_cursorkey,
246            set_vt52,
247            set_col80,
248            set_jump_scroll,
249            set_normal_video,
250            set_origin_abs,
251            reset_auto_wrap,
252            reset_auto_repeat,
253            reset_interlacing,
254            set_top_and_bottom,
255            set_alternate_keypad,
256            set_numeric_keypad,
257            set_uk_g0,
258            set_uk_g1,
259            set_us_g0,
260            set_us_g1,
261            set_g0_special,
262        )),
263        set_g1_special,
264        set_g0_alternate,
265        set_g1_alternate,
266        set_g0_graph,
267        set_g1_graph,
268        set_single_shift2,
269        set_single_shift3,
270    ))(input)
271}
272
273pub fn parse_escape(input: &str) -> IResult<&str, AnsiSequence> {
274    preceded(tag("\u{1b}"), combined)(input)
275}