src/main.rs

changeset 7
68538da191c7
parent 6
de1cf8032322
child 8
945a396340d2
equal deleted inserted replaced
6:de1cf8032322 7:68538da191c7
3 3
4 #![feature(trait_upcasting)] 4 #![feature(trait_upcasting)]
5 5
6 use std::io; 6 use std::io;
7 use std::fs::File; 7 use std::fs::File;
8 use std::io::{BufWriter, BufRead, BufReader}; 8 use std::io::{BufWriter, BufRead, BufReader, Write, Read};
9 use std::io::Write; 9 use std::fmt::Debug;
10 use clap::Parser; 10 use clap::Parser;
11 11
12 /// Command line parameters 12 /// Command line parameters
13 #[derive(Parser, Debug)] 13 #[derive(Parser, Debug, Clone)]
14 #[clap( 14 #[clap(
15 about = env!("CARGO_PKG_DESCRIPTION"), 15 about = env!("CARGO_PKG_DESCRIPTION"),
16 author = env!("CARGO_PKG_AUTHORS"), 16 author = env!("CARGO_PKG_AUTHORS"),
17 version = env!("CARGO_PKG_VERSION"), 17 version = env!("CARGO_PKG_VERSION"),
18 )] 18 )]
26 26
27 #[clap(flatten)] 27 #[clap(flatten)]
28 config : Config 28 config : Config
29 } 29 }
30 30
31 #[derive(Parser, Debug)] 31 #[derive(Parser, Debug, Clone)]
32 struct Config { 32 struct Config {
33 #[arg(long, short = 'c')] 33 #[arg(long, short = 'c')]
34 /// Strip comments 34 /// Strip comments
35 strip_comments : bool, 35 strip_comments : bool,
36 36
43 lineno : usize, 43 lineno : usize,
44 input_only_ws : bool, 44 input_only_ws : bool,
45 cli : Config 45 cli : Config
46 } 46 }
47 47
48 type AnyChainRule = Box<dyn ChainRule>; 48 type AnyChainRule<W> = Box<dyn ChainRule<W>>;
49 type AnyNestedRule = Box<dyn NestedRule>; 49 type AnyNestedRule<W> = Box<dyn NestedRule<W>>;
50 50
51 trait ChainRule { 51 trait ChainRule<W> {
52 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule; 52 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W>;
53 fn flush(self : Box<Self>, ctx : &Context); 53 fn flush(self : Box<Self>, ctx : &Context) -> W;
54 } 54 }
55 55
56 trait NestedRule : ChainRule { 56 trait NestedRule<W : Write> : ChainRule<W> {
57 fn produce(&mut self, c : char, ctx : &Context); 57 fn produce(&mut self, c : char, ctx : &Context);
58 fn next(self : Box<Self>) -> AnyChainRule; 58 fn next(self : Box<Self>) -> AnyChainRule<W>;
59 fn produce_string(&mut self, s : String, ctx : &Context) { 59 fn produce_string(&mut self, s : String, ctx : &Context) {
60 s.chars().for_each(|c| self.produce(c, ctx)); 60 s.chars().for_each(|c| self.produce(c, ctx));
61 } 61 }
62 fn start_ignored_comment(&mut self, c : char); 62 fn start_ignored_comment(&mut self, c : char);
63 } 63 }
64 64
65 impl<W : Write + 'static> ChainRule for Out<W> { 65 impl<W : Write + 'static> ChainRule<W> for Out<W> {
66 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule { 66 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
67 basic_consume(self, c, ctx, true) 67 basic_consume(self, c, ctx, true)
68 } 68 }
69 fn flush(mut self : Box<Self>, _ctx : &Context) { 69 fn flush(mut self : Box<Self>, _ctx : &Context) -> W {
70 self.output.flush().unwrap(); 70 self.output.flush().unwrap();
71 } 71 self.output
72 } 72 }
73 73 }
74 impl<W : Write + 'static> NestedRule for Out<W> { 74
75 impl<W : Write + 'static> NestedRule<W> for Out<W> {
75 fn produce(&mut self, c : char, ctx : &Context) { 76 fn produce(&mut self, c : char, ctx : &Context) {
76 if c == '\n' { 77 if c == '\n' {
77 self.line_end(ctx.cli.strip_whitespace, ctx.input_only_ws) 78 self.line_end(ctx.cli.strip_whitespace, ctx.input_only_ws)
78 } else if c.is_whitespace() { 79 } else if c.is_whitespace() {
79 self.stored_whitespace.push(c); 80 self.stored_whitespace.push(c);
84 self.whitespace_satisfied = false; 85 self.whitespace_satisfied = false;
85 self.par_satisfied = false; 86 self.par_satisfied = false;
86 } 87 }
87 } 88 }
88 89
89 fn next(self : Box<Self>) -> AnyChainRule { 90 fn next(self : Box<Self>) -> AnyChainRule<W> {
90 self 91 self
91 } 92 }
92 93
93 fn start_ignored_comment(&mut self, c : char) { 94 fn start_ignored_comment(&mut self, c : char) {
94 if self.stored_whitespace.is_empty() && !self.only_whitespace { 95 if self.stored_whitespace.is_empty() && !self.only_whitespace {
101 self.ignored_comment_only_line = true 102 self.ignored_comment_only_line = true
102 } 103 }
103 } 104 }
104 } 105 }
105 106
106 fn basic_consume(mut s : AnyNestedRule, c : char, ctx : &Context, print_end : bool) 107 fn basic_consume<W : Write + 'static>(
107 -> AnyChainRule { 108 mut s : AnyNestedRule<W>,
109 c : char,
110 ctx : &Context,
111 print_end : bool
112 ) -> AnyChainRule<W> {
108 match c { 113 match c {
109 '{' => { 114 '{' => {
110 s.produce(c, ctx); 115 s.produce(c, ctx);
111 Box::new(Group(s)) 116 Box::new(Group(s))
112 }, 117 },
133 s 138 s
134 } 139 }
135 } 140 }
136 } 141 }
137 142
138 struct CommandName { 143 struct CommandName<W : Write> {
139 parent : AnyNestedRule, 144 parent : AnyNestedRule<W>,
140 command : String 145 command : String
141 } 146 }
142 147
143 impl ChainRule for CommandName { 148 impl<W : Write + 'static> ChainRule<W> for CommandName<W> {
144 fn consume(mut self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule { 149 fn consume(mut self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
145 match c { 150 match c {
146 '}' | '{' | '\\' if self.command.len() <= 1 => { 151 '}' | '{' | '\\' if self.command.len() <= 1 => {
147 self.command.push(c); 152 self.command.push(c);
148 self.handle(ctx) 153 self.handle(ctx)
149 }, 154 },
156 .consume(c, ctx) 161 .consume(c, ctx)
157 } 162 }
158 } 163 }
159 } 164 }
160 165
161 fn flush(self : Box<Self>, ctx : &Context) { 166 fn flush(self : Box<Self>, ctx : &Context) -> W {
162 self.handle(ctx) 167 self.handle(ctx)
163 .flush(ctx) 168 .flush(ctx)
164 } 169 }
165 } 170 }
166 171
167 impl CommandName { 172 impl<W : Write + 'static> CommandName<W> {
168 fn handle(mut self, ctx : &Context) -> AnyChainRule { 173 fn handle(mut self, ctx : &Context) -> AnyChainRule<W> {
169 match self.command.as_str() { 174 match self.command.as_str() {
170 "\\added" => { 175 "\\added" => {
171 Scan::new(Added(self.parent)) 176 Scan::new(Added(self.parent))
172 }, 177 },
173 "\\replaced" => { 178 "\\replaced" => {
182 } 187 }
183 } 188 }
184 } 189 }
185 } 190 }
186 191
187 struct Comment(AnyNestedRule); 192 struct Comment<W : Write>(AnyNestedRule<W>);
188 193
189 impl ChainRule for Comment { 194 impl<W : Write + 'static> ChainRule<W> for Comment<W> {
190 fn consume(mut self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule { 195 fn consume(mut self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
191 if c == '\n' { 196 if c == '\n' {
192 self.0.consume(c, ctx) 197 self.0.consume(c, ctx)
193 } else { 198 } else {
194 self.0.produce(c, ctx); 199 self.0.produce(c, ctx);
195 self 200 self
196 } 201 }
197 } 202 }
198 fn flush(self : Box<Self>, ctx : &Context) { 203 fn flush(self : Box<Self>, ctx : &Context) -> W {
199 self.0.flush(ctx) 204 self.0.flush(ctx)
200 } 205 }
201 } 206 }
202 207
203 struct IgnoreComment(AnyChainRule); 208 struct IgnoreComment<W : Write>(AnyChainRule<W>);
204 209
205 impl ChainRule for IgnoreComment { 210 impl<W : Write +'static> ChainRule<W> for IgnoreComment<W> {
206 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule { 211 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
207 if c == '\n' { 212 if c == '\n' {
208 self.0.consume(c, ctx) 213 self.0.consume(c, ctx)
209 } else { 214 } else {
210 self 215 self
211 } 216 }
212 } 217 }
213 fn flush(self : Box<Self>, ctx : &Context) { 218 fn flush(self : Box<Self>, ctx : &Context) -> W {
214 self.0.flush(ctx) 219 self.0.flush(ctx)
215 } 220 }
216 } 221 }
217 222
218 struct Group(AnyNestedRule); 223 struct Group<W : Write>(AnyNestedRule<W>);
219 224
220 impl ChainRule for Group { 225 impl<W : Write + 'static> ChainRule<W> for Group<W> {
221 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule { 226 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
222 basic_consume(self, c, ctx, true) 227 basic_consume(self, c, ctx, true)
223 } 228 }
224 fn flush(self : Box<Self>, ctx : &Context) { 229 fn flush(self : Box<Self>, ctx : &Context) -> W {
225 self.0.flush(ctx) 230 self.0.flush(ctx)
226 } 231 }
227 } 232 }
228 233
229 impl NestedRule for Group { 234 impl<W : Write + 'static> NestedRule<W> for Group<W> {
230 fn produce(&mut self, c : char, ctx : &Context) { 235 fn produce(&mut self, c : char, ctx : &Context) {
231 self.0.produce(c, ctx) 236 self.0.produce(c, ctx)
232 } 237 }
233 fn next(self : Box<Self>) -> AnyChainRule { 238 fn next(self : Box<Self>) -> AnyChainRule<W> {
234 self.0 239 self.0
235 } 240 }
236 fn start_ignored_comment(&mut self, c : char) { 241 fn start_ignored_comment(&mut self, c : char) {
237 self.0.start_ignored_comment(c) 242 self.0.start_ignored_comment(c)
238 } 243 }
239 } 244 }
240 245
241 struct Added(AnyNestedRule); 246 struct Added<W : Write>(AnyNestedRule<W>);
242 247
243 impl ChainRule for Added { 248 impl<W : Write + 'static> ChainRule<W> for Added<W> {
244 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule { 249 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
245 basic_consume(self, c, ctx, false) 250 basic_consume(self, c, ctx, false)
246 } 251 }
247 fn flush(self : Box<Self>, ctx : &Context) { 252 fn flush(self : Box<Self>, ctx : &Context) -> W {
248 self.0.flush(ctx) 253 self.0.flush(ctx)
249 } 254 }
250 } 255 }
251 256
252 impl NestedRule for Added { 257 impl<W : Write + 'static> NestedRule<W> for Added<W> {
253 fn produce(&mut self, c : char, ctx : &Context) { 258 fn produce(&mut self, c : char, ctx : &Context) {
254 self.0.produce(c, ctx) 259 self.0.produce(c, ctx)
255 } 260 }
256 fn next(self : Box<Self>) -> AnyChainRule { 261 fn next(self : Box<Self>) -> AnyChainRule<W> {
257 self.0 262 self.0
258 } 263 }
259 fn start_ignored_comment(&mut self, c : char) { 264 fn start_ignored_comment(&mut self, c : char) {
260 self.0.start_ignored_comment(c) 265 self.0.start_ignored_comment(c)
261 } 266 }
262 } 267 }
263 struct Deleted(AnyNestedRule); 268 struct Deleted<W : Write>(AnyNestedRule<W>);
264 269
265 impl ChainRule for Deleted { 270 impl<W : Write + 'static> ChainRule<W> for Deleted<W> {
266 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule { 271 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
267 basic_consume(self, c, ctx, false) 272 basic_consume(self, c, ctx, false)
268 } 273 }
269 fn flush(self : Box<Self>, ctx : &Context) { 274 fn flush(self : Box<Self>, ctx : &Context) -> W {
270 self.0.flush(ctx) 275 self.0.flush(ctx)
271 } 276 }
272 } 277 }
273 278
274 impl NestedRule for Deleted { 279 impl<W : Write + 'static> NestedRule<W> for Deleted<W> {
275 fn produce(&mut self, _c : char, _ctx : &Context) { 280 fn produce(&mut self, _c : char, _ctx : &Context) {
276 } 281 }
277 fn next(self : Box<Self>) -> AnyChainRule { 282 fn next(self : Box<Self>) -> AnyChainRule<W> {
278 self.0 283 self.0
279 } 284 }
280 fn start_ignored_comment(&mut self, c : char) { 285 fn start_ignored_comment(&mut self, c : char) {
281 self.0.start_ignored_comment(c) 286 self.0.start_ignored_comment(c)
282 } 287 }
283 } 288 }
284 289
285 struct Replaced(AnyNestedRule); 290 struct Replaced<W : Write>(AnyNestedRule<W>);
286 291
287 impl ChainRule for Replaced { 292 impl<W : Write + 'static> ChainRule<W> for Replaced<W> {
288 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule { 293 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
289 basic_consume(self, c, ctx, false) 294 basic_consume(self, c, ctx, false)
290 } 295 }
291 fn flush(self : Box<Self>, ctx : &Context) { 296 fn flush(self : Box<Self>, ctx : &Context) -> W {
292 self.0.flush(ctx) 297 self.0.flush(ctx)
293 } 298 }
294 } 299 }
295 300
296 impl NestedRule for Replaced { 301 impl<W : Write + 'static> NestedRule<W> for Replaced<W> {
297 fn produce(&mut self, c : char, ctx : &Context) { 302 fn produce(&mut self, c : char, ctx : &Context) {
298 self.0.produce(c, ctx) 303 self.0.produce(c, ctx)
299 } 304 }
300 fn next(self : Box<Self>) -> AnyChainRule { 305 fn next(self : Box<Self>) -> AnyChainRule<W> {
301 Scan::new(Deleted(self.0)) 306 Scan::new(Deleted(self.0))
302 } 307 }
303 fn start_ignored_comment(&mut self, c : char) { 308 fn start_ignored_comment(&mut self, c : char) {
304 self.0.start_ignored_comment(c) 309 self.0.start_ignored_comment(c)
305 } 310 }
306 } 311 }
307 312
308 struct Scan(AnyNestedRule); 313 struct Scan<W : Write>(AnyNestedRule<W>);
309 314
310 impl ChainRule for Scan { 315 impl<W : Write + 'static> ChainRule<W> for Scan<W> {
311 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule { 316 fn consume(self : Box<Self>, c : char, ctx : &Context) -> AnyChainRule<W> {
312 if c.is_whitespace() || c == '\n' { 317 if c.is_whitespace() || c == '\n' {
313 self 318 self
314 } else if c == '{' { 319 } else if c == '{' {
315 self.0 320 self.0
316 } else if c == '%' { 321 } else if c == '%' {
318 } else { 323 } else {
319 panic!("Non-whitespace character ({c}) separating arguments on \ 324 panic!("Non-whitespace character ({c}) separating arguments on \
320 line {lineno}", lineno = ctx.lineno) 325 line {lineno}", lineno = ctx.lineno)
321 } 326 }
322 } 327 }
323 fn flush(self : Box<Self>, ctx : &Context) { 328 fn flush(self : Box<Self>, ctx : &Context) -> W {
324 self.0.flush(ctx) 329 self.0.flush(ctx)
325 } 330 }
326 } 331 }
327 332
328 impl Scan { 333 impl<W : Write + 'static> Scan<W> {
329 fn new<R : NestedRule + 'static>(r : R) -> Box<dyn ChainRule> { 334 fn new<R : NestedRule<W> + 'static>(r : R) -> Box<dyn ChainRule<W>> {
330 Box::new(Scan(Box::new(r))) 335 Box::new(Scan(Box::new(r)))
331 } 336 }
332 } 337 }
333 338
334 339
368 } 373 }
369 } 374 }
370 375
371 fn main() { 376 fn main() {
372 let cli = CommandLineArgs::parse(); 377 let cli = CommandLineArgs::parse();
373 let input = cli.input.map_or_else( 378
374 || Box::new(BufReader::new(io::stdin())) as Box<dyn BufRead>, 379 match (cli.input, cli.output) {
375 |f| Box::new(BufReader::new(File::open(f).unwrap())) as Box<dyn BufRead> 380 (None, None) => {
376 ); 381 process_buffered(cli.config, io::stdin(), io::stdout());
377 let output = cli.output.map_or_else( 382 },
378 || Box::new(BufWriter::new(io::stdout())) as Box<dyn Write>, 383 (None, Some(o)) => {
379 |f| Box::new(BufWriter::new(File::create(f).unwrap())) as Box<dyn Write> 384 process_buffered(cli.config, io::stdin(), File::create(o).unwrap());
380 ); 385 }
386 (Some(i), None) => {
387 process_buffered(cli.config, File::open(i).unwrap(), io::stdout());
388 }
389 (Some(i), Some(o)) => {
390 process_buffered(cli.config, File::open(i).unwrap(), File::create(o).unwrap());
391 }
392 }
393 }
394
395 fn process_buffered<I : Read, O : Debug + Write + 'static>(
396 config : Config,
397 input : I,
398 output : O
399 ) -> O {
400 process(config, BufReader::new(input), BufWriter::new(output)).into_inner().unwrap()
401 }
402
403 fn process<I : BufRead, O : Write + 'static>(config : Config, input : I, output : O) -> O {
381 404
382 let mut rule : Box<dyn ChainRule> = Box::new(Out { 405 let mut rule : Box<dyn ChainRule<O>> = Box::new(Out {
383 only_whitespace : true, 406 only_whitespace : true,
384 stored_whitespace : String::new(), 407 stored_whitespace : String::new(),
385 output, 408 output,
386 whitespace_satisfied : true, 409 whitespace_satisfied : true,
387 par_satisfied : true, 410 par_satisfied : true,
388 ignored_comment_only_line : false 411 ignored_comment_only_line : false
389 }); 412 });
390 413
391 let mut ctx = Context{ lineno : 0, cli : cli.config, input_only_ws : true}; 414 let mut ctx = Context{ lineno : 0, cli : config, input_only_ws : true};
392 415
393 for l in input.lines().map(|l| l.unwrap()) { 416 for l in input.lines().map(|l| l.unwrap()) {
394 ctx.lineno += 1; 417 ctx.lineno += 1;
395 ctx.input_only_ws = true; 418 ctx.input_only_ws = true;
396 for c in l.chars() { 419 for c in l.chars() {
398 rule = rule.consume(c, &ctx); 421 rule = rule.consume(c, &ctx);
399 } 422 }
400 rule = rule.consume('\n', &ctx); 423 rule = rule.consume('\n', &ctx);
401 } 424 }
402 425
403 rule.flush(&ctx); 426 rule.flush(&ctx)
404 } 427 }

mercurial