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]))
274 def get_matcher_name(matcher):
275 return matcher_names[matcher]
277 removed_per_matcher = {}
278 def optimize(matcher, fin):
287 mn = get_matcher_name(matcher)
288 global removed_per_matcher
289 removed_per_matcher[mn] = removed
291 def seek_text(fin, fout):
298 if s.startswith('.text'): break
304 re_digits = r"([0-9]+)"
305 re_number__ = r"-{0,1}[0-9]+"
306 re_number = r"(" + re_number__ + ")"
307 re_string__ = r'\"[^"]*\"'
308 re_string = r'(' + re_string__ + ')'
309 # variable prefixed with @ means: variable exported by curr script
310 re_var__ = r"@{0,1}[_9A-Za-z]+[0-9A-Za-z_]*"
311 re_var = r"(" + re_var__ + ")"
312 re_var_str_or_num = "(" + "(?:" + re_var__ + ")|(?:" + re_string__ + ")|(?:" + re_number__ +")" + ")"
313 re_iden_or_num = r"([0-9A-Za-z_]+)"
314 re_leading_ws = r"(\s*)"
316 re_extfunc = r"([_9A-Za-z]+[0-9A-Za-z_:]*)"
321 all_matchers['pushpop_matcher'] = MultiLineMatcher([
322 re.compile('(\s+)push ([a-z]+)'),
323 re.compile('(\s+)pop ([a-z]+)'),
324 ], push_pop_matchfn, output_fn)
326 all_matchers['sourceline_matcher'] = MultiLineMatcher([
327 re.compile('(\s+)sourceline ([0-9]+)'),
328 ], sourceline_matchfn, output_fn)
330 # this matcher is only for debug purposes.
331 # it adds a sourceline statement after every thisaddr,
332 # which is right at the beginning of each func.
333 # so one can set e.g. a breakpoint in the ags interpreter.
334 all_matchers['thisaddr_sourceline_matcher'] = MultiLineMatcher([
335 re.compile('(\s+)thisaddr ([0-9]+)$'),
336 ], thisaddr_sourceline_matchfn, output_fn)
338 all_matchers['cmp_mr_matcher'] = MultiLineMatcher([
339 re.compile('(\s+)(cmpeq|cmpne|lor|land) bx, ax'),
340 re.compile('(\s+)mr ax, bx'),
341 ], cmp_mr_matchfn, output_fn)
343 all_matchers['load_negative_literal_matcher'] = MultiLineMatcher([
344 re.compile('(\s+)push ax$'),
345 re.compile('(\s+)li ax, ([0-9]+)$'),
346 re.compile('(\s+)li bx, 0$'),
347 re.compile('(\s+)sub bx, ax$'),
348 re.compile('(\s+)mr ax, bx$'),
349 re.compile('(\s+)pop bx$'),
350 ], load_negative_literal_matchfn, output_fn)
352 all_matchers['load_negative_literal2_matcher'] = MultiLineMatcher([
353 re.compile('(\s+)li ax, ([0-9]+)$'),
354 re.compile('(\s+)li bx, 0$'),
355 re.compile('(\s+)sub bx, ax$'),
356 re.compile('(\s+)mr ax, bx$'),
357 ], load_negative_literal2_matchfn, output_fn)
359 all_matchers['load_literal_matcher'] = MultiLineMatcher([
360 re.compile('(\s+)push ax$'),
361 re.compile('(\s+)li ax, ([0-9]+)$'),
362 re.compile('(\s+)pop bx$'),
363 ], load_literal_matchfn, output_fn)
365 all_matchers['cmp2_matcher'] = MultiLineMatcher([
366 re.compile('(\s+)mr bx, ax'),
367 re.compile('(\s+)li ax, ([0-9]+)'),
368 re.compile('(\s+)(gt|gte|lt|lte) bx, ax'),
369 re.compile('(\s+)mr ax, bx'),
370 ], cmp2_matchfn, output_fn)
372 all_matchers['axmar_matcher'] = MultiLineMatcher([
373 re.compile('(\s+)li ax, ([0-9]+)$'),
374 re.compile('(\s+)mr bx, ax$'),
375 re.compile('(\s+)li mar, (.+)$'),
376 re.compile('(\s+)mr ax, mar$'),
377 ], axmar_matchfn, output_fn)
379 all_matchers['mr_swap_matcher'] = MultiLineMatcher([
380 re.compile('(\s+)mr ax, bx'),
381 re.compile('(\s+)mr bx, ax'),
382 ], mr_swap_matchfn, output_fn)
384 all_matchers['memread4_swap_matcher'] = MultiLineMatcher([
385 # memread4 ax; mr bx, ax; li ax, 1
386 re.compile('(\s+)memread4 ([a-d]x)'),
387 re.compile('(\s+)mr ([a-d]x), ([a-d]x)'),
388 re.compile('(\s+)li ([a-d]x), ([0-9]+)$'),
389 ], memread4_swap_matchfn, output_fn)
391 all_matchers['ptrstack2x_matcher'] = MultiLineMatcher([
392 re.compile('(\s+)ptrstack ([0-9]+)$'),
393 re.compile('(\s+)mem.*$'),
394 re.compile('(\s+)ptrstack ([0-9]+)$'),
395 ], ptrstack2x_matchfn, output_fn)
397 all_matchers['regload_arith_matcher'] = MultiLineMatcher([
398 # li ax, 1; add bx, ax, ax; mr ax, bx
399 re.compile('(\s+)li ([a-d]x), ([0-9]+)$'),
400 re.compile('(\s+)(add|sub) ([a-d]x), ([a-d]x)$'),
401 re.compile('(\s+)mr ([a-d]x), ([a-d]x)$'),
402 ], regload_arith_matchfn, output_fn)
404 all_matchers['assertlte_matcher'] = MultiLineMatcher([
405 re.compile('(\s+)assertlte\s.*'),
406 ], sourceline_matchfn, output_fn)
408 all_matchers['load0_matcher'] = MultiLineMatcher([
409 re.compile('(\s+)li ([a-d]x), 0$'),
410 ], load0_matchfn, output_fn)
412 # macros. we replace specific snippets with macros, to make the code
415 all_matchers['farcall0_matcher'] = MultiLineMatcher([
416 re.compile(re_leading_ws + r'setfuncargs 0' + re_tail),
417 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
418 re.compile(re_leading_ws + r'farcall ax' + re_tail),
419 ], farcall0_matchfn, output_fn)
421 all_matchers['farcall1_matcher'] = MultiLineMatcher([
422 re.compile(re_leading_ws + r'li ax, ' + re_var_str_or_num + re_tail),
423 re.compile(re_leading_ws + r'farpush ax' + re_tail),
424 re.compile(re_leading_ws + r'setfuncargs 1' + re_tail),
425 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
426 re.compile(re_leading_ws + r'farcall ax' + re_tail),
427 re.compile(re_leading_ws + r'farsubsp 1' + re_tail),
428 ], farcall1_matchfn, output_fn)
430 all_matchers['objcall0_matcher'] = MultiLineMatcher([
431 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
432 re.compile(re_leading_ws + r'mr ax, mar' + re_tail),
433 re.compile(re_leading_ws + r'push op' + re_tail),
434 re.compile(re_leading_ws + r'callobj ax' + re_tail),
435 re.compile(re_leading_ws + r'setfuncargs 0' + re_tail),
436 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
437 re.compile(re_leading_ws + r'farcall ax' + re_tail),
438 re.compile(re_leading_ws + r'pop op' + re_tail),
439 ], objcall0_matchfn, output_fn)
441 all_matchers['objcall1_matcher'] = MultiLineMatcher([
442 re.compile(re_leading_ws + r'li bx, ' + re_iden_or_num + re_tail),
443 re.compile(re_leading_ws + r'li mar, ' + re_iden_or_num + re_tail),
444 re.compile(re_leading_ws + r'mr ax, mar' + re_tail),
445 re.compile(re_leading_ws + r'push op' + re_tail),
446 re.compile(re_leading_ws + r'callobj ax' + re_tail),
447 re.compile(re_leading_ws + r'farpush bx' + re_tail),
448 re.compile(re_leading_ws + r'setfuncargs 1' + re_tail),
449 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
450 re.compile(re_leading_ws + r'farcall ax' + re_tail),
451 re.compile(re_leading_ws + r'farsubsp 1' + re_tail),
452 re.compile(re_leading_ws + r'pop op' + re_tail),
453 ], objcall1_matchfn, output_fn)
455 all_matchers['objcall1dynstr_matcher'] = MultiLineMatcher([
456 re.compile(re_leading_ws + r'li ax, ' + re_string + re_tail),
457 re.compile(re_leading_ws + r'newstr ax' + re_tail),
458 re.compile(re_leading_ws + r'mr bx, ax' + re_tail),
459 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
460 re.compile(re_leading_ws + r'mr ax, mar' + re_tail),
461 re.compile(re_leading_ws + r'push op' + re_tail),
462 re.compile(re_leading_ws + r'callobj ax' + re_tail),
463 re.compile(re_leading_ws + r'farpush bx' + re_tail),
464 re.compile(re_leading_ws + r'setfuncargs 1' + re_tail),
465 re.compile(re_leading_ws + r'li ax, ' + re_extfunc + re_tail),
466 re.compile(re_leading_ws + r'farcall ax' + re_tail),
467 re.compile(re_leading_ws + r'farsubsp 1' + re_tail),
468 re.compile(re_leading_ws + r'pop op' + re_tail),
469 ], objcall1dynstr_matchfn, output_fn)
471 all_matchers['varislit_matcher'] = MultiLineMatcher([
472 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
473 re.compile(re_leading_ws + r'memread4 ax' + re_tail),
474 re.compile(re_leading_ws + r'mr bx, ax' + re_tail),
475 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
476 re.compile(re_leading_ws + r'(cmpeq|cmpne) ax, bx' + re_tail),
477 ], varislit_matchfn, output_fn)
479 all_matchers['varislit2_matcher'] = MultiLineMatcher([
480 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
481 re.compile(re_leading_ws + r'memread4 bx' + re_tail),
482 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
483 re.compile(re_leading_ws + r'(cmpeq|cmpne) ax, bx' + re_tail),
484 ], varislit2_matchfn, output_fn)
486 all_matchers['incvar_matcher'] = MultiLineMatcher([
487 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
488 re.compile(re_leading_ws + r'push ax' + re_tail),
489 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
490 re.compile(re_leading_ws + r'memread4 ax' + re_tail),
491 re.compile(re_leading_ws + r'pop bx' + re_tail),
492 re.compile(re_leading_ws + r'(sub|add) ax, bx' + re_tail),
493 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
494 re.compile(re_leading_ws + r'memwrite4 ax' + re_tail),
495 ], incvar_matchfn, output_fn)
497 all_matchers['incvar2_matcher'] = MultiLineMatcher([
498 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
499 re.compile(re_leading_ws + r'memread4 ax' + re_tail),
500 re.compile(re_leading_ws + r'(subi|addi) ax, ' + re_number + re_tail),
501 re.compile(re_leading_ws + r'memwrite4 ax' + re_tail),
502 ], incvar2_matchfn, output_fn)
504 all_matchers['setvar_matcher'] = MultiLineMatcher([
505 re.compile(re_leading_ws + r'li ax, ' + re_number + re_tail),
506 re.compile(re_leading_ws + r'li mar, ' + re_var + re_tail),
507 re.compile(re_leading_ws + r'memwrite4 ax' + re_tail),
508 ], setvar_matchfn, output_fn)
510 for i in all_matchers.keys():
511 matcher_names[all_matchers[i]] = i
513 commandline_args_matcher_map = {
514 "-cmp" : 'cmp_mr_matcher',
515 "-pushpop": 'pushpop_matcher',
516 "-sourceline": 'sourceline_matcher',
517 "-lnl": 'load_negative_literal_matcher',
518 "-lnl2": 'load_negative_literal2_matcher',
519 "-ll": 'load_literal_matcher',
520 "-cmp2": 'cmp2_matcher',
521 "-axmar": 'axmar_matcher',
522 "-mrswap": 'mr_swap_matcher',
523 "-m4s": 'memread4_swap_matcher',
524 "-ptrstack2x": 'ptrstack2x_matcher',
525 "-rlarith": 'regload_arith_matcher',
526 "-assertlte": "assertlte_matcher",
527 "-load0": "load0_matcher",
528 "-fcdebug": "thisaddr_sourceline_matcher",
529 "-objcall0": "objcall0_matcher",
530 "-objcall1": "objcall1_matcher",
531 "-objcall1dynstr": "objcall1dynstr_matcher",
532 "-farcall0": "farcall0_matcher",
533 "-farcall1": "farcall1_matcher",
534 "-varislit": "varislit_matcher",
535 "-varislit2": "varislit2_matcher",
536 "-incvar": "incvar_matcher",
537 "-incvar2": "incvar2_matcher",
538 "-setvar": "setvar_matcher",
542 '-cmp': "optimize cmp/mr",
543 '-cmp2': "optimize gt/gte/lt/lte (requires prev -ll pass)",
544 "-pushpop": "optimize push/pop",
545 "-sourceline": "remove sourceline statements",
546 "-lnl": "optimize negative literal loads",
547 "-lnl2": "optimize negative literal loads variant2",
548 "-ll" : "optimize literal loads",
550 "-mrswap": "remove gratuitous reverse register copy",
551 "-m4s": "optimize register swap after memread",
552 "-ptrstack2x": "optimize duplicate ptrstack statements",
553 "-rlarith": "remove temporary register loads for add/sub",
554 "-assertlte": "remove assertlte statements",
555 "-load0": "replace load of 0 with xor reg, reg",
556 "-fcdebug": "insert sourceline directives on the start of each func (4 debugging)",
557 "-objcall0": "replace objcall0 with macro",
558 "-objcall1": "replace objcall1 with macro",
559 "-objcall1dynstr": "replace objcall1 with macro",
560 "-farcall0": "replace farcall0 with macro",
561 "-farcall1": "replace farcall1 with macro",
562 "-varislit": "varislit macro (neg)",
563 "-varislit2": "varislit macro",
564 "-incvar": "incvar macro",
565 "-incvar2": "incvar macro",
566 "-setvar": "setvar macro",
571 if len(sys.argv) < 4: return usage()
572 # -cmp -pushpop -sourceline -lnl -ll -cmp2 -axmar -mrswap
573 for i in xrange(1, len(sys.argv)-2):
574 if not sys.argv[i] in commandline_args_matcher_map: return usage()
575 else: matcher_names.append(commandline_args_matcher_map[sys.argv[i]])
577 fn = sys.argv[len(sys.argv)-2]
579 fout = open(sys.argv[len(sys.argv)-1], "w")
583 for mn in matcher_names:
584 tmp = tempfile.TemporaryFile()
587 seek_text(fin, m.fout)
592 chunk = fin.read(4096)
593 if chunk == '': break
598 for i in removed_per_matcher.keys():
599 if removed_per_matcher[i] == 0: continue
600 total_removed += removed_per_matcher[i]
601 s += "[%s:%d] " %(i[:len(i)-len("_matcher")], removed_per_matcher[i])
603 sys.stdout.write( "%s: removed %d lines %s\n"%(fn, total_removed, s))
606 if __name__ == "__main__": main()