Thu, 19 Oct 2023 12:08:07 -0500
Fix typo
| 0 | 1 | use std::io; |
| 2 | use std::io::BufWriter; | |
| 3 | use std::io::Write; | |
| 4 | ||
| 5 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | |
| 6 | enum Element { | |
| 7 | Added, | |
| 8 | Deleted, | |
| 9 | Replaced, | |
| 10 | Other, | |
| 11 | Comment, | |
| 12 | } | |
| 13 | ||
| 14 | #[derive(Clone, Copy, Debug, PartialEq, Eq)] | |
| 15 | enum Status { | |
| 16 | Output(Element), | |
| 17 | Ignore(Element), | |
| 18 | Scan(Element, bool), | |
| 19 | } | |
| 20 | ||
| 21 | fn main() { | |
| 22 | let input = io::stdin(); | |
| 23 | let mut output = BufWriter::new(io::stdout()); | |
| 24 | let mut status_stack = Vec::new(); | |
| 25 | ||
| 26 | use Status::*; | |
| 27 | use Element::*; | |
| 28 | ||
| 29 | let current = |s : &Vec<Status>| s.last().map_or(Output(Other), |s| *s); | |
| 30 | let mut out = |c : char| { write!(output, "{}", c).unwrap(); }; | |
| 31 | let mut lineno = 0; | |
| 32 | ||
| 33 | for l in input.lines().map(|l| l.unwrap()) { | |
| 34 | lineno += 1; | |
| 35 | let mut chars = l.chars(); | |
| 36 | let started_ignore = if let Ignore(_) = current(&status_stack) { true } else { false }; | |
| 37 | let mut maybe_next_char = None; | |
| 38 | 'process_line: loop { | |
| 39 | let next_char = match maybe_next_char { | |
| 40 | None => chars.next(), | |
| 41 | Some(c) => { | |
| 42 | maybe_next_char = None; | |
| 43 | Some(c) | |
| 44 | } | |
| 45 | }; | |
| 46 | match(current(&status_stack), next_char) { | |
| 47 | (_, None) => { | |
| 48 | break 'process_line; | |
| 49 | }, | |
| 50 | (st @ (Output(e) | Ignore(e)), Some('\\')) if e != Comment => { | |
| 51 | let mut command = String::new(); | |
| 52 | let mut first = true; | |
| 53 | maybe_next_char = 'scan_command: loop { | |
| 54 | match chars.next() { | |
| 55 | Some(c) if first && (c=='{' || c=='}' || c=='\\') => { | |
| 56 | command.push(c); | |
| 57 | break 'scan_command None; | |
| 58 | }, | |
| 59 | Some(c) if c.is_alphanumeric() => { | |
| 60 | command.push(c); | |
| 61 | }, | |
| 62 | maybe_c => { | |
| 63 | break 'scan_command maybe_c; | |
| 64 | } | |
| 65 | } | |
| 66 | first = false; | |
| 67 | }; | |
| 68 | let output_guard = if let Ignore(_) = st { false } else { true }; | |
| 69 | match command.as_str() { | |
| 70 | "added" => { | |
| 71 | status_stack.push(Scan(Added, true && output_guard)); | |
| 72 | }, | |
| 73 | "replaced" => { | |
| 74 | status_stack.push(Scan(Replaced, true && output_guard)); | |
| 75 | }, | |
| 76 | "deleted" => { | |
| 77 | status_stack.push(Scan(Deleted, false)); | |
| 78 | }, | |
| 79 | _ => { | |
| 80 | if output_guard { | |
| 81 | out('\\'); | |
| 82 | command.chars().for_each(|c| out(c.clone())); | |
| 83 | } | |
| 84 | } | |
| 85 | }; | |
| 86 | }, | |
| 87 | (Scan(next, o), Some(c)) => { | |
| 88 | match c { | |
| 89 | '{' => { | |
| 90 | status_stack.pop(); | |
| 91 | status_stack.push(if o { Output(next) } else { Ignore(next) }); | |
| 92 | }, | |
| 93 | ' ' => { | |
| 94 | }, | |
| 95 | _ => panic!("Non-whitespace character ({c}) separating arguments on\ | |
| 96 | line {lineno}"), | |
| 97 | } | |
| 98 | }, | |
| 99 | (Output(e), Some('{')) if e != Comment => { | |
| 100 | out('{'); | |
| 101 | status_stack.push(Output(Other)); | |
| 102 | }, | |
| 103 | (Ignore(e), Some('{')) if e != Comment => { | |
| 104 | status_stack.push(Ignore(Other)); | |
| 105 | }, | |
| 106 | (Output(Added) | Ignore(Added) | Output(Deleted) | Ignore(Deleted), Some('}')) => { | |
| 107 | status_stack.pop(); | |
| 108 | }, | |
| 109 | (Output(Replaced) | Ignore(Replaced), Some('}')) => { | |
| 110 | status_stack.pop(); | |
| 111 | status_stack.push(Scan(Deleted, false)); | |
| 112 | }, | |
| 113 | (Output(Other), Some('}')) => { | |
| 114 | out('}'); | |
| 115 | status_stack.pop(); | |
| 116 | }, | |
| 117 | (Ignore(_), Some('}')) => { | |
| 118 | status_stack.pop(); | |
| 119 | }, | |
| 120 | (Output(e), Some('%')) if e != Comment=> { | |
| 121 | out('%'); | |
| 122 | status_stack.push(Output(Comment)); | |
| 123 | }, | |
| 124 | (Ignore(e), Some('%')) if e != Comment => { | |
| 125 | status_stack.push(Ignore(Comment)); | |
| 126 | }, | |
| 127 | (Output(_), Some(c)) => { | |
| 128 | out(c); | |
| 129 | }, | |
| 130 | (Ignore(_), Some(_)) => { | |
| 131 | }, | |
| 132 | }; | |
| 133 | } | |
| 134 | match current(&status_stack) { | |
| 135 | Ignore(e) => { | |
| 136 | if !started_ignore { | |
| 137 | out('\n'); | |
| 138 | } | |
| 139 | if e == Comment { | |
| 140 | status_stack.pop(); | |
| 141 | } | |
| 142 | }, | |
| 143 | Output(e) => { | |
| 144 | out('\n'); | |
| 145 | if e == Comment { | |
| 146 | status_stack.pop(); | |
| 147 | } | |
| 148 | }, | |
| 149 | Scan(_, _) => { | |
| 150 | }, | |
| 151 | } | |
| 152 | } | |
| 153 | ||
| 154 | output.flush().unwrap(); | |
| 155 | } |