src/main.rs

changeset 0
548bf3cc032e
child 2
254e1e4bd795
equal deleted inserted replaced
-1:000000000000 0:548bf3cc032e
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 }

mercurial