1#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum Token {
14 Text(String),
18 Control(u8),
21 Csi {
25 private: Option<char>,
26 params: Vec<u16>,
27 intermediates: Vec<u8>,
28 final_byte: char,
29 },
30 Esc {
33 intermediates: Vec<u8>,
34 final_byte: u8,
35 },
36 Osc(String),
39 Other { intro: u8, data: Vec<u8> },
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45enum State {
46 Ground,
47 Escape,
48 EscapeIntermediate,
49 Csi,
50 Osc,
51 Str, }
53
54#[derive(Debug, Default)]
57pub struct Parser {
58 state: Option<State>, text: String,
60 utf8: Vec<u8>,
61 intermediates: Vec<u8>,
62 param_bytes: Vec<u8>,
63 private: Option<char>,
64 str_intro: u8,
65 str_data: Vec<u8>,
66 str_esc: bool,
67}
68
69const MAX_SEQ: usize = 4096;
72
73impl Parser {
74 pub fn new() -> Self {
75 Self::default()
76 }
77
78 fn state(&self) -> State {
79 self.state.unwrap_or(State::Ground)
80 }
81
82 pub fn feed(&mut self, input: &[u8]) -> Vec<Token> {
86 let mut out = Vec::new();
87 for &b in input {
88 self.byte(b, &mut out);
89 }
90 self.flush_text(&mut out);
91 out
92 }
93
94 fn flush_text(&mut self, out: &mut Vec<Token>) {
95 if !self.text.is_empty() {
96 out.push(Token::Text(std::mem::take(&mut self.text)));
97 }
98 }
99
100 fn start(&mut self, state: State) {
101 self.state = Some(state);
102 self.intermediates.clear();
103 self.param_bytes.clear();
104 self.private = None;
105 self.str_data.clear();
106 self.str_esc = false;
107 }
108
109 fn byte(&mut self, b: u8, out: &mut Vec<Token>) {
110 match self.state() {
111 State::Ground => self.ground(b, out),
112 State::Escape => self.escape(b, out),
113 State::EscapeIntermediate => self.escape_intermediate(b, out),
114 State::Csi => self.csi(b, out),
115 State::Osc => self.string_byte(b, true, out),
116 State::Str => self.string_byte(b, false, out),
117 }
118 }
119
120 fn ground(&mut self, b: u8, out: &mut Vec<Token>) {
121 if !self.utf8.is_empty() {
122 if (0x80..0xC0).contains(&b) {
123 self.utf8.push(b);
124 let want = utf8_len(self.utf8[0]);
125 if self.utf8.len() == want {
126 match std::str::from_utf8(&self.utf8) {
127 Ok(s) => self.text.push_str(s),
128 Err(_) => self.text.push('\u{FFFD}'),
129 }
130 self.utf8.clear();
131 }
132 return;
133 }
134 self.text.push('\u{FFFD}');
136 self.utf8.clear();
137 }
139 match b {
140 0x1B => {
141 self.flush_text(out);
142 self.start(State::Escape);
143 }
144 0x00..=0x1F | 0x7F => {
145 self.flush_text(out);
146 out.push(Token::Control(b));
147 }
148 0x20..=0x7E => self.text.push(b as char),
149 0x80..=0xBF => self.text.push('\u{FFFD}'), _ => {
151 if utf8_len(b) == 1 {
152 self.text.push('\u{FFFD}'); } else {
154 self.utf8.push(b);
155 }
156 }
157 }
158 }
159
160 fn escape(&mut self, b: u8, out: &mut Vec<Token>) {
161 match b {
162 b'[' => self.start(State::Csi),
163 b']' => self.start(State::Osc),
164 b'P' | b'X' | b'^' | b'_' => {
165 self.start(State::Str);
166 self.str_intro = b;
167 }
168 0x20..=0x2F => {
169 self.intermediates.push(b);
170 self.state = Some(State::EscapeIntermediate);
171 }
172 0x30..=0x7E => {
173 out.push(Token::Esc {
174 intermediates: Vec::new(),
175 final_byte: b,
176 });
177 self.state = None;
178 }
179 0x1B => self.start(State::Escape),
180 0x18 | 0x1A => self.state = None, _ => out.push(Token::Control(b)), }
183 }
184
185 fn escape_intermediate(&mut self, b: u8, out: &mut Vec<Token>) {
186 match b {
187 0x20..=0x2F => {
188 if self.intermediates.len() < 8 {
189 self.intermediates.push(b);
190 }
191 }
192 0x30..=0x7E => {
193 out.push(Token::Esc {
194 intermediates: std::mem::take(&mut self.intermediates),
195 final_byte: b,
196 });
197 self.state = None;
198 }
199 0x1B => self.start(State::Escape),
200 0x18 | 0x1A => self.state = None,
201 _ => out.push(Token::Control(b)),
202 }
203 }
204
205 fn csi(&mut self, b: u8, out: &mut Vec<Token>) {
206 match b {
207 b'0'..=b'9' | b';' | b':' => {
208 if self.param_bytes.len() < MAX_SEQ {
209 self.param_bytes.push(b);
210 } else {
211 self.state = None; }
213 }
214 0x3C..=0x3F => {
215 if self.private.is_none() && self.param_bytes.is_empty() {
216 self.private = Some(b as char);
217 } }
219 0x20..=0x2F => {
220 if self.intermediates.len() < 8 {
221 self.intermediates.push(b);
222 }
223 }
224 0x40..=0x7E => {
225 out.push(Token::Csi {
226 private: self.private.take(),
227 params: parse_params(&self.param_bytes),
228 intermediates: std::mem::take(&mut self.intermediates),
229 final_byte: b as char,
230 });
231 self.state = None;
232 }
233 0x1B => self.start(State::Escape),
234 0x18 | 0x1A => self.state = None,
235 _ => out.push(Token::Control(b)),
236 }
237 }
238
239 fn string_byte(&mut self, b: u8, is_osc: bool, out: &mut Vec<Token>) {
240 if self.str_esc {
241 self.str_esc = false;
242 if b == b'\\' {
243 self.dispatch_string(is_osc, out);
244 return;
245 }
246 if self.str_data.len() < MAX_SEQ {
247 self.str_data.push(0x1B);
248 }
249 }
251 match b {
252 0x07 => self.dispatch_string(is_osc, out), 0x1B => self.str_esc = true,
254 _ => {
255 if self.str_data.len() < MAX_SEQ {
256 self.str_data.push(b);
257 }
258 }
259 }
260 }
261
262 fn dispatch_string(&mut self, is_osc: bool, out: &mut Vec<Token>) {
263 let data = std::mem::take(&mut self.str_data);
264 if is_osc {
265 out.push(Token::Osc(String::from_utf8_lossy(&data).into_owned()));
266 } else {
267 out.push(Token::Other {
268 intro: self.str_intro,
269 data,
270 });
271 }
272 self.state = None;
273 }
274}
275
276fn utf8_len(lead: u8) -> usize {
279 match lead {
280 0xC0..=0xDF => 2,
281 0xE0..=0xEF => 3,
282 0xF0..=0xF7 => 4,
283 _ => 1,
284 }
285}
286
287fn parse_params(bytes: &[u8]) -> Vec<u16> {
290 if bytes.is_empty() {
291 return Vec::new();
292 }
293 bytes
294 .split(|&b| b == b';')
295 .take(32)
296 .map(|seg| {
297 let mut v: u32 = 0;
298 for &b in seg {
299 if !b.is_ascii_digit() {
300 break;
301 }
302 v = (v * 10 + (b - b'0') as u32).min(u16::MAX as u32);
303 }
304 v as u16
305 })
306 .collect()
307}