Thu, 19 Oct 2023 12:08:07 -0500
Fix typo
use std::io; use std::io::BufWriter; use std::io::Write; #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum Element { Added, Deleted, Replaced, Other, Comment, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum Status { Output(Element), Ignore(Element), Scan(Element, bool), } fn main() { let input = io::stdin(); let mut output = BufWriter::new(io::stdout()); let mut status_stack = Vec::new(); use Status::*; use Element::*; let current = |s : &Vec<Status>| s.last().map_or(Output(Other), |s| *s); let mut out = |c : char| { write!(output, "{}", c).unwrap(); }; let mut lineno = 0; for l in input.lines().map(|l| l.unwrap()) { lineno += 1; let mut chars = l.chars(); let started_ignore = if let Ignore(_) = current(&status_stack) { true } else { false }; let mut maybe_next_char = None; 'process_line: loop { let next_char = match maybe_next_char { None => chars.next(), Some(c) => { maybe_next_char = None; Some(c) } }; match(current(&status_stack), next_char) { (_, None) => { break 'process_line; }, (st @ (Output(e) | Ignore(e)), Some('\\')) if e != Comment => { let mut command = String::new(); let mut first = true; maybe_next_char = 'scan_command: loop { match chars.next() { Some(c) if first && (c=='{' || c=='}' || c=='\\') => { command.push(c); break 'scan_command None; }, Some(c) if c.is_alphanumeric() => { command.push(c); }, maybe_c => { break 'scan_command maybe_c; } } first = false; }; let output_guard = if let Ignore(_) = st { false } else { true }; match command.as_str() { "added" => { status_stack.push(Scan(Added, true && output_guard)); }, "replaced" => { status_stack.push(Scan(Replaced, true && output_guard)); }, "deleted" => { status_stack.push(Scan(Deleted, false)); }, _ => { if output_guard { out('\\'); command.chars().for_each(|c| out(c.clone())); } } }; }, (Scan(next, o), Some(c)) => { match c { '{' => { status_stack.pop(); status_stack.push(if o { Output(next) } else { Ignore(next) }); }, ' ' => { }, _ => panic!("Non-whitespace character ({c}) separating arguments on\ line {lineno}"), } }, (Output(e), Some('{')) if e != Comment => { out('{'); status_stack.push(Output(Other)); }, (Ignore(e), Some('{')) if e != Comment => { status_stack.push(Ignore(Other)); }, (Output(Added) | Ignore(Added) | Output(Deleted) | Ignore(Deleted), Some('}')) => { status_stack.pop(); }, (Output(Replaced) | Ignore(Replaced), Some('}')) => { status_stack.pop(); status_stack.push(Scan(Deleted, false)); }, (Output(Other), Some('}')) => { out('}'); status_stack.pop(); }, (Ignore(_), Some('}')) => { status_stack.pop(); }, (Output(e), Some('%')) if e != Comment=> { out('%'); status_stack.push(Output(Comment)); }, (Ignore(e), Some('%')) if e != Comment => { status_stack.push(Ignore(Comment)); }, (Output(_), Some(c)) => { out(c); }, (Ignore(_), Some(_)) => { }, }; } match current(&status_stack) { Ignore(e) => { if !started_ignore { out('\n'); } if e == Comment { status_stack.pop(); } }, Output(e) => { out('\n'); if e == Comment { status_stack.pop(); } }, Scan(_, _) => { }, } } output.flush().unwrap(); }