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 | } |