4
# Hacket is a character from Beckett's Watt. It is only briefly intorduced once at the beginning of Watt,
5
# (IIRC) shortly before Watt hears choir singing about big fat bun for everyone.
9
# This script was used (once) to convert deprecated (bastardized) version of wm3 in yade to the official version
10
# Arguments to provide are explained below.
11
# Many things are hardcoded, don't ever try to use it without reading the source throughly.
12
# You have been warned.
16
# since the tags files must contain prototypes: I ran
17
# ctags-exuberant --extra=+q --c++-kinds=+pf --language-force=c++ *
18
# in wm3's Foundation/Math directory as well as in yade-lib-wm3-math
20
# To perform the actual conversion, I did
21
# ./hackett.py tags-ok tags-bastard ~/yade/trunk/yade-libs/yade-lib-wm3-math/src/yade-lib-wm3-math
22
# where tags-ok and tags-bastard are symlinked from their respective directories
34
# renames that are not obvious to be guess by the first-letter-case-changing algorithm
35
# (they must include class name)
36
renamedTags[('Math::cosinus')]=('Math::Cos')
37
renamedTags[('Math::tangent')]=('Math::Tan')
38
renamedTags[('Math::sinus')]=('Math::Sin')
39
renamedTags[('Math::sqRoot')]=('Math::Sqrt')
40
renamedTags[('Math::power')]=('Math::Pow')
41
renamedTags[('Math::roundDown')]=('Math::Floor')
42
renamedTags[('Math::roundUp')]=('Math::Ceil')
43
renamedTags[('Math::invSqRoot')]=('Math::InvSqrt')
44
renamedTags[('Math::eExp')]=('Math::Exp')
45
renamedTags[('Math::logarithm')]=('Math::Log')
47
logging.basicConfig(level=logging.DEBUG)
49
def tagsParse(tagsFile):
50
logging.info("Parsing tags file `%s'."%tagsFile)
52
tagsFile=open(tagsFile)
56
if l[0]=='!': continue
58
if match('^\S*operator\s.*$',l): continue # discard operators
59
if match('^.*\s[dntc]$',l): continue # discard #defines, namespaces, typedefs and classes
60
#since the text field can contain \t which is otherwise field separator, pattern is clumsy but should work
61
m=match('^(\S+|\S+operator \S+)\s+(\S+)\s+(/\^.*\$/;")\s+(\S+)\s+(\S+)?.*$',l)
63
logging.warn("Line `%s' not matched"%l)
65
name,file,text,type,extra=m.groups()[0:5]
66
#print name,file,text,type,extra
68
### this is probably not needed anymore
69
# these are typedefs, like this:
70
# Wm3::Matrix2d Wm3Matrix2.h /^typedef Matrix2<double> Matrix2d;$/;" t namespace:Wm3
71
# we don't need these, since they weren't bastardized
72
if extra[0:10]=='namespace:': continue
73
# some two bizzare cases (namespace opening and something different...)
74
if extra[0:5]=='file:': continue
75
#if extra[0:6]!='class:':
76
# logging.warning('Skipping weird tag: %s, %s, %s, %s, %s',name,file,text,type,extra)
79
## we want only prototypes and function definitions
80
# perhaps they wouldn't pass through the regexp filter above anyway
81
if not type in 'pf': continue
82
# this would discard classes (have no '::'), but that should have been caught by the above as well...
83
if name.find('::')<0: continue
86
# prepend class name to the symbol name, unless it is already contained
87
if extra[0:6]=='class':
89
if name.find(clss)!=0:
91
if name[0:5]=='Wm3::': name=name[5:] # chop Wm3:: off the symbol name
93
# ctags escape some metacharacters, we don't nee that
94
text=text.replace('\\','')
97
if tags.has_key(name):
99
if t[0]==type and t[1]==text and t[2]==file:
100
# exact match found, skip
102
if isAlready: continue
104
if not tags.has_key(name): tags[name]=[]
105
tags[name].append([type,text,file,False])
109
good=tagsParse(sys.argv[1])
110
bad=tagsParse(sys.argv[2])
113
#pprint(bad); pprint(good)
114
#for c,n in good.keys(): print c
115
#for c,n in bad.keys(): print c
117
#tags that are in bad but not in good
119
# tags that are only renamed in bad and otherwise exist in good
120
# keys are (badClss,badName), values are keys in good: (goodClss,goodName)
121
# list of (class,name) tags, that have been kept intact
125
for badTag in bad.keys():
126
# probably not needed
127
if match('::operator.*',badTag): continue
129
# if the method is lowercase and uppercased appears in the good set, it was renamed
130
cls,meth=badTag.split('::')
131
goodTag=cls+'::'+meth[0].upper()+meth[1:]
132
if renamedTags.has_key(badTag): continue
133
elif good.has_key(badTag): keptTags.append(badTag)
134
elif meth[0].lower()==meth[0] and good.has_key(goodTag):
135
renamedTags[badTag]=goodTag
136
# otherwise, it is an added method that has no corresponding good class
138
addedTags[badTag]=bb[0]
140
######################################################################
141
### uncomment this to get symbols map for hackett-warn-replace.py
145
for t in renamedTags.keys(): r[t.split('::')[-1]]=renamedTags[t].split('::')[-1]
146
for t in sorted(r.keys()): print "\t'%s':'%s',"%(t,r[t])
149
# now accumulate changes for particular files, so that we can iterate over files conveniently
151
for t in renamedTags:
154
if not f in fileReplace: fileReplace.append(f)
159
##############################################
160
######## here begins the actual work
161
##############################################
164
from os.path import join
166
for fName in fileReplace:
167
origFile=join(badDir,fName)
168
bcupFile=origFile+'.~bastardized.0~'
169
logging.info("Processing `%s'."%origFile)
170
if 1: # if 1, files will be copied and written over; the next branch writes to "backup" files
171
shutil.move(origFile,bcupFile)
172
fout=open(origFile,'w')
176
fout=open(bcupFile,'w')
177
# buffer for line that has not been syntactically terminated
180
for l in fin.xreadlines():
181
if len(leftOver)>0: l=leftOver+l
183
for tt in renamedTags:
185
#if t[0]!='p': continue # for now, only do prototypes...
187
pat=t[1][2:-4] # line hardcopy - discard leading /^ and trailing $;/"
189
if f!=fName: continue
190
if not l.find(pat)<0:
191
if t[3]: # there are two different tags in the same file, but their text signature is the same - presumaly argument list that differs continues on the next line etc. Since it will cause erroneous output, shout to the user
192
logging.info("The method `%s' had the same tag `%s' twice (overloaded?). Please fix by hand!!!"%(tt,pat))
194
# classes may be discarded from names
195
badMethName=tt.split('::')[-1]
196
goodMethName=renamedTags[tt].split('::')[-1]
197
# badMethName is the old method inline declarationand definition that is marked as deprecated and merely wraps the new method
198
# goodMethName is the new method
199
# badMeth and goodMeth are corresponding source lines that we will create
201
# this pattern matches both declaration and definition header. It must return return type in \1, method argument list in \2.
202
methodPattern=r'^(.*?)\b'+badMethName+r'\b\s?(\s?\(.*\)\s*(const)?)\s*(;|{).*$';
203
badMeth,count=subn(methodPattern,r'\1'+badMethName+r'\2',l)
204
goodMeth,count2=subn(methodPattern,r'\1'+goodMethName+r'\2 \4',l)
205
# match the same times for both methods (always, since pattern is the same)
206
# match at most once; otherwise output might be syntactically incorrect
207
assert(count==count2); assert(count<=1)
208
# we will append function body (wrapper) to badMeth, get rid of newline
209
badMeth=badMeth.replace('\n',''); #goodMeth=goodMeth.replace('\n','')
210
badMeth="\t__attribute__((deprecated)) inline "+badMeth
212
if count==0: leftOver=l[:-1] # the declaration is not terminated at this lines, process with the next line
215
t[3]=True # this particular tag has been processed by us - this allows us to list unprocessed tags at the end, if there are any, since taht would probably indicate some error
216
if t[0]=='p': # we are dealing with prototype, hence we define wrapper for goodMeth
217
args=search(r'\((.*)\)',goodMeth); # extract arguments
220
args=args.group(1).split(',') # process args one by one
221
try: args.remove('') # in case of void methods, remove empty args
222
except ValueError: pass
224
# begin building the wrapper
225
badMeth+='{return '+goodMethName+'('
226
# pass it all arguments
227
for i in range(0,len(args)):
228
var=args[i].split()[-1] # for every arg, only the last word (variable name), not type
229
var=sub('\[[0-9]+\]$','',var) # remove array brackets after variable
231
if i<len(args)-1: badMeth+=',' # append , except after the last arg
233
fout.write(badMeth) # wrapper is on the line preceeding the actual method
234
fout.write(goodMeth) # write goodMeth
236
if not written and leftOver=='': fout.write(l) # write lines that were not interesting
239
# list tags that have not been processed. There shouldn't be any.
240
for tt in renamedTags:
242
if t[3]==False: logging.warning("Tag `%s' was not processed (%s)!"%(tt,t))