Created
February 1, 2014 16:40
-
-
Save dreamlayers/8754844 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# decatchify.py : Script for removing exception catching from MESS | |
# *** Configuration *** | |
# List of filenames to not process | |
WHITELIST = ( | |
'machine.c' # Contains running_machine::start_all_devices() | |
) | |
# Try needs to become nothing, so the try block is just a normal block. | |
# A preprocessor define can eliminate try, but replacing allows more | |
# selective removal. Use of a string that can be defined to try or nothing | |
# would allow switching. | |
TRY_REPLACE = '/* try */' | |
# Catch blocks need to be entirely disabled, because they can refer to | |
# the catch parameter. A preprocessor condition is needed here, because | |
# "#endif" is added afterwards. | |
CATCH_COND = '#if 0' | |
# This just ensures that the program is being run in the right place. | |
# It is a safety measure to avoid accidentally processing unintended source. | |
MUSTEXIST = ('src/emu/emu.h', 'src/emu/mame.c', 'docs/mame.txt') | |
# *** Program *** | |
import os | |
import re | |
import sys | |
OKBESIDE = re.compile('[^A-Za-z_0-9]') | |
def validate_keyword(match): | |
b = match.start() | |
if b == 0 or OKBESIDE.match(match.string[b-1]): | |
e = match.end() | |
if e == len(match.string) or OKBESIDE.match(match.string[e]): | |
return True | |
return False | |
# Each parser state has a regular expression, with 1 or more alternatives | |
# that can be found. Each alternative has an associated parser function. | |
# The match object is passed to that function. | |
# | |
# Parser functions return a tuple: (current line string, current column, | |
# parser state). This allows functions to alter the line, including adding | |
# newlines inside to create more lines. | |
TRY_REPLACE_LEN = len(TRY_REPLACE) | |
def remove_try(match): | |
if validate_keyword(match): | |
return (match.string[:match.start()] + TRY_REPLACE | |
+ match.string[match.end():], | |
match.start() + TRY_REPLACE_LEN, 0) | |
else: | |
return (match.string, match.end(), 0) | |
BADC = re.compile(',|\s*\)') | |
INDENT = re.compile('\s*') | |
def begin_catch(match): | |
global bracelevel | |
e = match.string[match.end():] | |
if validate_keyword(match) and not BADC.match(e): | |
before = match.string[:match.start()] | |
if before.strip() == '': | |
# Preserve indent for catch. | |
prefix = before | |
before = '' | |
else: | |
# There is some code before the catch. | |
# Split line and use its indent. | |
before += '\n' | |
m = INDENT.match(before) | |
if m: | |
prefix = m.group() | |
else: | |
prefix = '' | |
before += CATCH_COND + '\n' + prefix + 'catch' | |
bracelevel = 0; | |
return (before + e, len(before), 3) | |
else: | |
return (match.string, match.end(), 0) | |
def brace_in(match): | |
global bracelevel | |
bracelevel += 1 | |
return (match.string, match.end(), 3) | |
def brace_out(match): | |
global bracelevel | |
bracelevel -= 1 | |
if bracelevel > 0: | |
return (match.string, match.end(), 3) | |
else: | |
# End of catch statement | |
e = match.string[match.end():] | |
if e.strip() == '': | |
e = '' | |
return (match.string[:match.end()] + '\n#endif\n' + e, | |
match.end() + 8, 0) | |
def unquote(match): | |
b = match.start() | |
if b == 0 or match.string[b] != '\\': | |
return (match.string, match.end(), 0) | |
else: | |
return (match.string, match.end(), 2) | |
# This defines parser states, their regular expressions and associated | |
# functions. Simple functions are lambdas below. | |
RE = ( | |
# Initial state | |
(re.compile('(/\*)|(//)|(try)|(catch)|(")'), | |
{ | |
1 : lambda match : (match.string, match.end(), 1), | |
2 : lambda match : (match.string, len(match.string), 0), | |
3 : remove_try, | |
4 : begin_catch, | |
5 : lambda match : (match.string, match.end(), 2) | |
}), | |
# Searching for end of comment | |
(re.compile('(\*/)'), | |
{ | |
1 : lambda match : (match.string, match.end(), 0), | |
}), | |
# Searching for end of quote | |
(re.compile('(")'), | |
{ | |
1 : unquote, | |
}), | |
# In catch statement, using braces to find its end | |
(re.compile('({)|(})'), | |
{ | |
1 : brace_in, | |
2 : brace_out | |
}), | |
) | |
# This processes one file. | |
def decatchify(fn): | |
with open(fn, 'r+') as f: | |
lines = f.readlines() | |
f.seek(0) | |
f.truncate() | |
# Exiting the script here would lose file contents. That is assumed | |
# to be okay because git can re-create the file. | |
reidx = 0 # Which regular expression to use now | |
for l in lines: | |
loc = 0 # Current column in l | |
while True: | |
match = RE[reidx][0].search(l, loc) | |
if match: | |
(l, loc, reidx) = RE[reidx][1][match.lastindex](match) | |
else: | |
break | |
f.write(l) | |
if len(sys.argv) != 2: | |
print '''Give top level MESS (mame) directory as argument. C++ source files | |
in it and all subdirectories will be altered to remove exception catching. | |
Do not run this a second time if files have already been altered.''' | |
sys.exit(1) | |
for fn in MUSTEXIST: | |
fntest = os.path.join(sys.argv[1], fn) | |
if not os.path.isfile(fntest): | |
print 'Failed to find', fntest | |
print 'Are you running this in the right place?' | |
sys.exit(1) | |
# This traverses the tree and removes exception catching | |
for subdir, dirs, files in os.walk(sys.argv[1]): | |
for fn in files: | |
if fn.endswith('.c') or fn.endswith('.cpp'): | |
if not fn in WHITELIST: | |
decatchify(os.path.join(subdir, fn)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment