3 # the AGS script compiler basically doesn't optimize at all,
4 # and the code it emits is super-naive and redundant.
5 # the register bx is most often used only as a temporary
6 # storage and discarded immediately after doing one basic op.
7 # therefore, doing these transformations should be safe.
8 # running these transformations on .s files reduces the
9 # amount of code by about 15-25%, but it also removes debugging
10 # information (sourceline directives).
11 # this speeds up game execution and makes the game smaller.
12 # currently, the optimizer takes input only from stdin and
13 # apply a single transformation at a time.
14 # a future optimization could be to cache code-chunks between
15 # jump labels and apply multiple transformation on the in-memory
20 class MultiLineMatcher():
21 def __init__(self, regexes, matchfn, nomatchfn, fout=sys.stdout):
22 self.matchfn = matchfn
23 self.nomatchfn = nomatchfn
24 self.regexes = regexes
26 self.saved_lines = None
31 line = line.rstrip('\n')
32 m = self.regexes[self.line_matches].match(line)
34 if self.matches is None: self.matches = []
35 if self.saved_lines is None: self.saved_lines = []
36 self.matches.append(m)
37 self.saved_lines.append(line)
38 self.line_matches += 1
39 if self.line_matches == len(self.regexes):
40 self.matchfn(self, self.matches, self.saved_lines)
43 self.saved_lines = None
48 for ln in self.saved_lines:
49 self.nomatchfn(self, ln)
50 self.saved_lines = None
51 self.nomatchfn(self, line)
56 def push_pop_matchfn(matcher, matches, lines):
57 ws, reg1 = matches[0].groups(0)
58 ws2, reg2 = matches[1].groups(0)
60 if reg1 == reg2: removed += 1
61 else: matcher.fout.write("%smr %s, %s\n"%(ws, reg2, reg1))
64 def output_fn(matcher, line):
65 matcher.fout.write( "%s\n" % line)
67 def sourceline_matchfn(matcher, matches, lines):
71 def thisaddr_sourceline_matchfn(matcher, matches, lines):
73 ws, addr = matches[0].groups(0)
74 matcher.fout.write( "%s\n" % lines[0])
75 matcher.fout.write( "%ssourceline %d\n" %(ws, int(addr)))
78 def cmp_mr_matchfn(matcher, matches, lines):
80 ws, op = matches[0].groups(0)
81 matcher.fout.write( "%s%s ax, bx\n"%(ws, op))
84 def cmp2_matchfn(matcher, matches, lines):
86 if op == 'gt': return 'lte'
87 elif op == 'gte': return 'lt'
88 elif op == 'lt': return 'gte'
89 elif op == 'lte': return 'gt'
91 ws, val = matches[1].groups(0)
92 ws2, op = matches[2].groups(0)
93 matcher.fout.write("%sli bx, %s\n"%(ws, val))
94 matcher.fout.write("%s%s ax, bx\n"%(ws, op)) # since we already switched registers, we don't need to switch the op too
97 def load_negative_literal_matchfn(matcher, matches, lines):
99 ws, val = matches[1].groups(0)
100 matcher.fout.write( "%smr bx, ax\n"%(ws))
101 matcher.fout.write( "%sli ax, -%s\n"%(ws, val))
104 def load_negative_literal2_matchfn(matcher, matches, lines):
106 ws, val = matches[0].groups(0)
107 matcher.fout.write( "%sli ax, -%s\n"%(ws, val))
108 matcher.fout.write( "%sli bx, -%s\n"%(ws, val))
111 def load_literal_matchfn(matcher, matches, lines):
113 ws, val = matches[1].groups(0)
114 matcher.fout.write( "%smr bx, ax\n"%(ws))
115 matcher.fout.write( "%sli ax, %s\n"%(ws, val))
118 def axmar_matchfn(matcher, matches, lines):
120 ws, val = matches[0].groups(0)
121 matcher.fout.write( "%sli bx, %s\n"%(ws, val))
122 matcher.fout.write( "%s\n" % lines[2])
123 matcher.fout.write( "%s\n" % lines[3])
126 def mr_swap_matchfn(matcher, matches, lines):
128 matcher.fout.write( "%s\n" % lines[0])
131 def memread4_swap_matchfn(matcher, matches, lines):
133 # # memread4 ax; mr bx, ax; li ax, 1
134 ws, reg1 = matches[0].groups(0)
135 ws, reg2a, reg2b = matches[1].groups(0)
136 ws, reg3, val = matches[2].groups(0)
137 if reg1 == reg2b and reg1 == reg3:
138 matcher.fout.write("%smemread4 %s\n"%(ws, reg2a))
139 matcher.fout.write("%sli %s, %s\n"%(ws, reg1, val))
142 for line in lines: matcher.fout.write("%s\n"%line)
144 def ptrstack2x_matchfn(matcher, matches, lines):
146 ws, val1 = matches[0].groups(0)
147 ws, val2 = matches[2].groups(0)
149 matcher.fout.write( "%s\n" % lines[0])
150 matcher.fout.write( "%s\n" % lines[1])
153 for line in lines: matcher.fout.write("%s\n"%line)
155 def regload_arith_matchfn(matcher, matches, lines):
156 # li ax, 1; add bx, ax, ax; mr ax, bx
158 ws, reg1, val = matches[0].groups(0)
159 ws, op, reg2a, reg2b = matches[1].groups(0)
160 ws, reg3a, reg3b = matches[2].groups(0)
161 if reg1 == reg2b and reg3a == reg1 and reg2a == reg3b:
162 matcher.fout.write( "%s%si %s, %s\n" % (ws, op, reg2a, val))
163 matcher.fout.write( "%s\n" % lines[2])
166 for line in lines: matcher.fout.write("%s\n"%line)
168 def load0_matchfn(matcher, matches, lines):
170 ws, reg1 = matches[0].groups(0)
171 matcher.fout.write( "%sxor %s, %s\n"%(ws, reg1, reg1))
172 # actually we don't remove here, but we wanna see the count of replacements
175 # ========= macro replacement ==========
176 def objcall0_matchfn(matcher, matches, lines):
177 ws, obj, tail = matches[0].groups(0)
178 ws, func, tail = matches[5].groups(0)
180 matcher.fout.write("%sOBJCALL0(%s, %s)%s\n"%(ws, func, obj, tail))
183 def objcall1_matchfn(matcher, matches, lines):
184 ws, arg, tail = matches[0].groups(0)
185 ws, obj, tail = matches[1].groups(0)
186 ws, func, tail = matches[7].groups(0)
188 matcher.fout.write("%sOBJCALL1(%s, %s, %s)%s\n"%(ws, func, obj, arg, tail))
191 def objcall1dynstr_matchfn(matcher, matches, lines):
192 ws, arg, tail = matches[0].groups(0)
193 ws, obj, tail = matches[3].groups(0)
194 ws, func, tail = matches[9].groups(0)
196 matcher.fout.write("%sOBJCALL1_DYNSTR(%s, %s, %s)%s\n"%(ws, func, obj, arg, tail))
199 def farcall0_matchfn(matcher, matches, lines):
200 ws, fun, tail = matches[1].groups(0)
203 matcher.fout.write("%sFARCALL0(%s)%s\n"%(ws, fun, tail))
205 def farcall1_matchfn(matcher, matches, lines):
206 ws, arg, tail = matches[0].groups(0)
207 ws, fun, tail = matches[3].groups(0)
210 matcher.fout.write("%sFARCALL1(%s, %s)%s\n"%(ws, fun, arg, tail))
212 def varislit_matchfn(matcher, matches, lines):
213 ws, var, tail = matches[0].groups(0)
214 ws, val, tail = matches[3].groups(0)
215 ws, op, tail_ = matches[4].groups(0)
218 m = "VAR_EQ" if op == 'cmpeq' else "VAR_NE"
219 matcher.fout.write("%s%s(%s, %s)%s\n"%(ws, m, var, val, tail))
221 def varislit2_matchfn(matcher, matches, lines):
222 ws, var, tail = matches[0].groups(0)
223 ws, val, tail = matches[2].groups(0)
224 ws, op, tail_ = matches[3].groups(0)
227 m = "VAR_EQ" if op == 'cmpeq' else "VAR_NE"
228 matcher.fout.write("%s%s(%s, %s)%s\n"%(ws, m, var, val, tail))
230 def incvar_matchfn(matcher, matches, lines):
231 ws, val, tail = matches[0].groups(0)
232 ws, var1, tail = matches[2].groups(0)
233 ws, op, tail = matches[5].groups(0)
234 ws, var2, tail = matches[6].groups(0)
237 for line in lines: matcher.fout.write("%s\n"%line)
243 matcher.fout.write("%sINC_VAR(%s, %d)%s\n"%(ws, var1, vali, tail))
245 def incvar2_matchfn(matcher, matches, lines):
246 ws, var, tail = matches[0].groups(0)
247 ws, op, val, tail = matches[2].groups(0)
253 matcher.fout.write("%sINC_VAR(%s, %d)%s\n"%(ws, var, vali, tail))
255 def setvar_matchfn(matcher, matches, lines):
256 ws, val, tail = matches[0].groups(0)
257 ws, var, tail = matches[1].groups(0)
260 matcher.fout.write("%sSET_VAR(%s, %s)%s\n"%(ws, var, val, tail))
263 def eprint(text): sys.stderr.write("%s\n"%text)
266 eprint("usage: %s [options] infile outfile"%sys.argv[0])
268 eprint("options: -cmp -pushpop -sourceline -lnl -ll -cmp2 -axmar -mrswap -m4s ...")
269 eprint("at least one option required.\n")
270 for i in commandline_args_matcher_map.keys():
271 eprint("%s: %s"%(i, help_text[i]))
275 def get_matcher_name(matcher):
276 return matcher_names[matcher]
278 removed_per_matcher = {}
279 def optimize(matcher, fin):
288 mn = get_matcher_name(matcher)
289 global removed_per_matcher
290 removed_per_matcher[mn] = removed
292 def seek_text(fin, fout):
299 if s.startswith('.text'): break
305 re_digits = r"([0-9]+)"
306 re_number__ = r"-{0,1}[0-9]+"
307 re_number = r"(" + re_number__ + ")"
308 re_string__ = r'\"[^"]*\"'
309 re_string = r'(' + re_string__ + ')'
310 # variable prefixed with @ means: variable exported by curr script
311 re_var__ = r"@{0,1}[_9A-Za-z]+[0-9A-Za-z_]*"
312 re_var = r"(" + re_var__ + ")"
313 re_var_str_or_num = "(" + "(?:" + re_var__ + ")|(?:" + re_string__ + ")|(?:" + re_number__ +")" + ")"
314 re_iden_or_num = r"([0-9A-Za-z_]+)"
315 re_leading_ws = r"(\s*)"
317 re_extfunc = r"([_9A-Za-z]+[0-9A-Za-z_:]*)"
322 all_matchers['pushpop_matcher'] = MultiLineMatcher([
323 re.compile('(\s+)push ([a-z]+)'),
324 re.compile('(\s+)pop ([a-z]+)'),
325 ], push_pop_matchfn, output_fn)
327 all_matchers['sourceline_matcher'] = MultiLineMatcher([
328 re.compile('(\s+)sourceline ([0-9]+)'),
329 ], sourceline_matchfn, output_fn)
331 # this matcher is only for debug purposes.
332 # it adds a sourceline statement after every thisaddr,
333 # which is right at the beginning of each func.
334 # so one can set e.g. a breakpoint in the ags interpreter.
335 all_matchers['thisaddr_sourceline_matcher'] = MultiLineMatcher([
336 re.compile('(\s+)thisaddr ([0-9]+)$'),
337 ], thisaddr_sourceline_matchfn, output_fn)
339 all_matchers['cmp_mr_matcher'] = MultiLineMatcher([
340 re.compile('(\s+)(cmpeq|cmpne|lor|land) bx, ax'),
341 re.compile('(\s+)mr ax, bx'),
342 ], cmp_mr_matchfn, output_fn)
344 all_matchers['load_negative_literal_matcher'] = MultiLineMatcher([
345 re.compile('(\s+)push ax$'),
346 re.compile('(\s+)li ax, ([0-9]+)$'),
347 re.compile('(\s+)li bx, 0$'),
348 re.compile('(\s+)sub bx, ax$'),
349 re.compile('(\s+)mr ax, bx$'),
350 re.compile('(\s+)pop bx$'),
351 ], load_negative_literal_matchfn, output_fn)
353 all_matchers['load_negative_literal2_matcher'] = MultiLineMatcher([
354 re.compile('(\s+)li ax, ([0-9]+)$'),
355 re.compile('(\s+)li bx, 0$'),
356 re.compile('(\s+)sub bx, ax$'),
357 re.compile('(\s+)mr ax, bx$'),
358 ], load_negative_literal2_matchfn, output_fn)
360 all_matchers['load_literal_matcher'] = MultiLineMatcher([
361 re.compile('(\s+)push ax$'),
362 re.compile('(\s+)li ax, ([0-9]+)$'),
363 re.compile('(\s+)pop bx$'),
364 ], load_literal_matchfn, output_fn)
366 all_matchers['cmp2_matcher'] = MultiLineMatcher([
367 re.compile('(\s+)mr bx, ax'),
368 re.compile('(\s+)li ax, ([0-9]+)'),
369 re.compile('(\s+)(gt|gte|lt|lte) bx, ax'),
370 re.compile('(\s+)mr ax, bx'),
371 ], cmp2_matchfn, output_fn)
373 all_matchers['axmar_matcher'] = MultiLineMatcher([
374 re.compile('(\s+)li ax, ([0-9]+)$'),
375 re.compile('(\s+)mr bx, ax$'),
376 re.compile('(\s+)li mar, (.+)$'),
377 re.compile('(\s+)mr ax, mar$'),
378 ], axmar_matchfn, output_fn)
380 all_matchers['mr_swap_matcher'] = MultiLineMatcher([
381 re.compile('(\s+)mr ax, bx'),
382 re.compile('(\s+)mr bx, ax'),
383 ], mr_swap_matchfn, output_fn)
385 all_matchers['memread4_swap_matcher'] = MultiLineMatcher([
386 # memread4 ax; mr bx, ax; li ax, 1
387 re.compile('(\s+)memread4 ([a-d]x)'),
388 re.compile('(\s+)mr ([a-d]x), ([a-d]x)'),
389 re.compile('(\s+)li ([a-d]x), ([0-9]+)$'),
390 ], memread4_swap_matchfn, output_fn)
392 all_matchers['ptrstack2x_matcher'] = MultiLineMatcher([
393 re.compile('(\s+)ptrstack ([0-9]+)$'),
394 re.compile('(\s+)mem.*$'),
395 re.compile('(\s+)ptrstack ([0-9]+)$'),
396 ], ptrstack2x_matchfn, output_fn)
398 all_matchers['regload_arith_matcher'] = MultiLineMatcher([
399 # li ax, 1; add bx, ax, ax; mr ax, bx
400 re.compile('(\s+)li ([a-d]x), ([0-9]+)$'),
401 re.compile('(\s+)(add|sub) ([a-d]x), ([a-d]x)$'),
402 re.compile('(\s+)mr ([a-d]x), ([a-d]x)$'),
403 ], regload_arith_matchfn, output_fn)
405 all_matchers['assertlte_matcher'] = MultiLineMatcher([
406 re.compile('(\s+)assertlte\s.*'),
407 ], sourceline_matchfn, output_fn)
409 all_matchers['load0_matcher'] = MultiLineMatcher([
410 re.compile('(\s+)li ([a-d]x), 0$'),
411 ], load0_matchfn, output_fn)
413 # macros. we replace specific snippets with macros, to make the code
416 all_matchers['farcall0_matcher'] = MultiLineMatcher([
417 re.compile(re_leading_ws + r'setfuncargs 0' + re_tail),
418 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
419 re.compile(re_leading_ws + r'farcall ax' + re_tail),
420 ], farcall0_matchfn, output_fn)
422 all_matchers['farcall1_matcher'] = MultiLineMatcher([
423 re.compile(re_leading_ws + r'li ax, ' + re_var_str_or_num + re_tail),
424 re.compile(re_leading_ws + r'farpush ax' + re_tail),
425 re.compile(re_leading_ws + r'setfuncargs 1' + re_tail),
426 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
427 re.compile(re_leading_ws + r'farcall ax' + re_tail),
428 re.compile(re_leading_ws + r'farsubsp 1' + re_tail),
429 ], farcall1_matchfn, output_fn)
431 all_matchers['objcall0_matcher'] = MultiLineMatcher([
432 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
433 re.compile(re_leading_ws + r'mr ax, mar' + re_tail),
434 re.compile(re_leading_ws + r'push op' + re_tail),
435 re.compile(re_leading_ws + r'callobj ax' + re_tail),
436 re.compile(re_leading_ws + r'setfuncargs 0' + re_tail),
437 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
438 re.compile(re_leading_ws + r'farcall ax' + re_tail),
439 re.compile(re_leading_ws + r'pop op' + re_tail),
440 ], objcall0_matchfn, output_fn)
442 all_matchers['objcall1_matcher'] = MultiLineMatcher([
443 re.compile(re_leading_ws + r'li bx, ' + re_iden_or_num + re_tail),
444 re.compile(re_leading_ws + r'li mar, ' + re_iden_or_num + re_tail),
445 re.compile(re_leading_ws + r'mr ax, mar' + re_tail),
446 re.compile(re_leading_ws + r'push op' + re_tail),
447 re.compile(re_leading_ws + r'callobj ax' + re_tail),
448 re.compile(re_leading_ws + r'farpush bx' + re_tail),
449 re.compile(re_leading_ws + r'setfuncargs 1' + re_tail),
450 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
451 re.compile(re_leading_ws + r'farcall ax' + re_tail),
452 re.compile(re_leading_ws + r'farsubsp 1' + re_tail),
453 re.compile(re_leading_ws + r'pop op' + re_tail),
454 ], objcall1_matchfn, output_fn)
456 all_matchers['objcall1dynstr_matcher'] = MultiLineMatcher([
457 re.compile(re_leading_ws + r'li ax, ' + re_string + re_tail),
458 re.compile(re_leading_ws + r'newstr ax' + re_tail),
459 re.compile(re_leading_ws + r'mr bx, ax' + re_tail),
460 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
461 re.compile(re_leading_ws + r'mr ax, mar' + re_tail),
462 re.compile(re_leading_ws + r'push op' + re_tail),
463 re.compile(re_leading_ws + r'callobj ax' + re_tail),
464 re.compile(re_leading_ws + r'farpush bx' + re_tail),
465 re.compile(re_leading_ws + r'setfuncargs 1' + re_tail),
466 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
467 re.compile(re_leading_ws + r'farcall ax' + re_tail),
468 re.compile(re_leading_ws + r'farsubsp 1' + re_tail),
469 re.compile(re_leading_ws + r'pop op' + re_tail),
470 ], objcall1dynstr_matchfn, output_fn)
472 all_matchers['varislit_matcher'] = MultiLineMatcher([
473 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
474 re.compile(re_leading_ws + r'memread4 ax' + re_tail),
475 re.compile(re_leading_ws + r'mr bx, ax' + re_tail),
476 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
477 re.compile(re_leading_ws + r'(cmpeq|cmpne) ax, bx' + re_tail),
478 ], varislit_matchfn, output_fn)
480 all_matchers['varislit2_matcher'] = MultiLineMatcher([
481 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
482 re.compile(re_leading_ws + r'memread4 bx' + re_tail),
483 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
484 re.compile(re_leading_ws + r'(cmpeq|cmpne) ax, bx' + re_tail),
485 ], varislit2_matchfn, output_fn)
487 all_matchers['incvar_matcher'] = MultiLineMatcher([
488 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
489 re.compile(re_leading_ws + r'push ax' + re_tail),
490 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
491 re.compile(re_leading_ws + r'memread4 ax' + re_tail),
492 re.compile(re_leading_ws + r'pop bx' + re_tail),
493 re.compile(re_leading_ws + r'(sub|add) ax, bx' + re_tail),
494 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
495 re.compile(re_leading_ws + r'memwrite4 ax' + re_tail),
496 ], incvar_matchfn, output_fn)
498 all_matchers['incvar2_matcher'] = MultiLineMatcher([
499 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
500 re.compile(re_leading_ws + r'memread4 ax' + re_tail),
501 re.compile(re_leading_ws + r'(subi|addi) ax, ' + re_number + re_tail),
502 re.compile(re_leading_ws + r'memwrite4 ax' + re_tail),
503 ], incvar2_matchfn, output_fn)
505 all_matchers['setvar_matcher'] = MultiLineMatcher([
506 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
507 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
508 re.compile(re_leading_ws + r'memwrite4 ax' + re_tail),
509 ], setvar_matchfn, output_fn)
511 for i in all_matchers.keys():
512 matcher_names[all_matchers[i]] = i
514 commandline_args_matcher_map = {
515 "-cmp" : 'cmp_mr_matcher',
516 "-pushpop": 'pushpop_matcher',
517 "-sourceline": 'sourceline_matcher',
518 "-lnl": 'load_negative_literal_matcher',
519 "-lnl2": 'load_negative_literal2_matcher',
520 "-ll": 'load_literal_matcher',
521 "-cmp2": 'cmp2_matcher',
522 "-axmar": 'axmar_matcher',
523 "-mrswap": 'mr_swap_matcher',
524 "-m4s": 'memread4_swap_matcher',
525 "-ptrstack2x": 'ptrstack2x_matcher',
526 "-rlarith": 'regload_arith_matcher',
527 "-assertlte": "assertlte_matcher",
528 "-load0": "load0_matcher",
529 "-fcdebug": "thisaddr_sourceline_matcher",
530 "-objcall0": "objcall0_matcher",
531 "-objcall1": "objcall1_matcher",
532 "-objcall1dynstr": "objcall1dynstr_matcher",
533 "-farcall0": "farcall0_matcher",
534 "-farcall1": "farcall1_matcher",
535 "-varislit": "varislit_matcher",
536 "-varislit2": "varislit2_matcher",
537 "-incvar": "incvar_matcher",
538 "-incvar2": "incvar2_matcher",
539 "-setvar": "setvar_matcher",
543 '-cmp': "optimize cmp/mr",
544 '-cmp2': "optimize gt/gte/lt/lte (requires prev -ll pass)",
545 "-pushpop": "optimize push/pop",
546 "-sourceline": "remove sourceline statements",
547 "-lnl": "optimize negative literal loads",
548 "-lnl2": "optimize negative literal loads variant2",
549 "-ll" : "optimize literal loads",
551 "-mrswap": "remove gratuitous reverse register copy",
552 "-m4s": "optimize register swap after memread",
553 "-ptrstack2x": "optimize duplicate ptrstack statements",
554 "-rlarith": "remove temporary register loads for add/sub",
555 "-assertlte": "remove assertlte statements",
556 "-load0": "replace load of 0 with xor reg, reg",
557 "-fcdebug": "insert sourceline directives on the start of each func (4 debugging)",
558 "-objcall0": "replace objcall0 with macro",
559 "-objcall1": "replace objcall1 with macro",
560 "-objcall1dynstr": "replace objcall1 with macro",
561 "-farcall0": "replace farcall0 with macro",
562 "-farcall1": "replace farcall1 with macro",
563 "-varislit": "varislit macro (neg)",
564 "-varislit2": "varislit macro",
565 "-incvar": "incvar macro",
566 "-incvar2": "incvar macro",
567 "-setvar": "setvar macro",
572 if len(sys.argv) < 4: return usage()
573 # -cmp -pushpop -sourceline -lnl -ll -cmp2 -axmar -mrswap
574 for i in xrange(1, len(sys.argv)-2):
575 if not sys.argv[i] in commandline_args_matcher_map: return usage()
576 else: matcher_names.append(commandline_args_matcher_map[sys.argv[i]])
578 fn = sys.argv[len(sys.argv)-2]
580 fout = open(sys.argv[len(sys.argv)-1], "w")
584 for mn in matcher_names:
585 tmp = tempfile.TemporaryFile()
588 seek_text(fin, m.fout)
593 chunk = fin.read(4096)
594 if chunk == '': break
599 for i in removed_per_matcher.keys():
600 if removed_per_matcher[i] == 0: continue
601 total_removed += removed_per_matcher[i]
602 s += "[%s:%d] " %(i[:len(i)-len("_matcher")], removed_per_matcher[i])
604 sys.stdout.write( "%s: removed %d lines %s\n"%(fn, total_removed, s))
608 if __name__ == "__main__": sys.exit(main())