Adds more tests to uses.py (and updates tests results),
[dwarf-doc.git] / dwarf5 / tools / uses.py
1 # Copyright 2012 DWARF Debugging Information Format Committee
2 #
3 # Handles the testing and update for all DW_* prefixes.
4 # Called by taglink and other convenience apps to do their work.
5 #
6 # Run as an app itself, the options are
7 #    python anylink [-t prefix] ... [-all] [file] ...
8 #    Use either -all or one or more -t, as in examples:
9 #    python anylink -t DW_ACCESS_ -t DW_OP_   test.in test2.in
10 #    python anylink -all    test.in test2.in 
11
12 import sys
13 import fileio
14
15 global linkdefinitionsdict
16 global linkusesdict
17 global labeldefinitionsdict
18 global labelusesdict
19 global ignorethesedict
20 global indexsetdict
21 global dupdefcount
22 global unresolveddwdict
23 # Links meaning \livelink \livetarg \livetargi macros
24 linkdefinitionsdict = {}
25 linkusesdict  = {}
26 # labels meaning \refersec (a ref) and \label  (a def)
27 labeldefinitionsdict = {}
28 labelusesdict =  {}
29 # The dict of indexed things.
30 indexsetdict ={}
31 # DW sorts of names not sensibly resolved.
32 unresolveddwdict = {}
33 dupdefcount = 0
34
35
36
37 # a list of words to ignore: silly stuff.
38 ignorethesedict = {"of":0, "a":0, "the":0, "and":0, "but":0,"DWARF":0,
39 "Standards":0,"Committee":0,"Version":0 }
40
41 class tokmention:
42   def __init__(self):
43     self._token = '' 
44     self._file = ""
45     self._line = 0
46     # Class is "id", "ind","other","none"
47   def __init__(self,tok,filename,line):
48     self._token = tok
49     self._file = filename
50     self._line = line
51
52
53
54
55 def ischar(tok,c):
56    if tok._class != "ind":
57       return "n"
58    if len(tok._tex) != 1:
59        return "n"
60    if tok._tex[0] != c:
61        return "n"
62    return "y"
63
64 def dwspace(tok):
65   if ischar(tok," ") == "y":
66     return "y"
67   if ischar(tok,"\t") == "y":
68     return "y"
69   return "n"
70   
71   
72 def isbrace(tok,brace):
73   if tok._class != "ind":
74      return "n"
75   if len(tok._tex) != 1:
76      return "n"
77   if brace == tok._tex[0]:
78      return "y"
79   return "n"
80
81 def toknamestring(t):
82   """ Turn a token into its string as a string """
83   return ''.join(t._tex)
84
85
86 def pickup(linetoks,tnumin,pattern,myfile,linenum):
87   """ The token pattern characters are
88   i meaning identifier
89   [space] meaning whitespace
90   { meaning left brace
91   } meaning right brace
92   * meaning any token except } and end-line
93   
94   Precondition:  linetoks[tnumin] is identifier (meaning a command)
95   Returns: a token list, one per non-space in the pattern.
96      For the *, the token is itself a list of whatever it contains.
97   """
98   outtoks = []
99   numabsorbed = 1
100   inlen = len(linetoks) 
101   curnum = tnumin
102   curtok = linetoks[curnum]
103   patterncharnum = -1
104   for c in pattern:
105     patterncharnum = patterncharnum + 1
106     if curnum >= inlen:
107       print "ERROR line ended surprisingly, pattern ", pattern,"  line ",linenum," file ",myfile._name
108       return outtoks,numabsorbed
109     curtok = linetoks[curnum]
110     if c == " ":
111       while dwspace(curtok) == "y":
112         curnum = curnum + 1
113         if curnum >= inlen:
114           print "ERROR line ended surprisingly in space, pattern ", pattern, " line ",linenum," file ",myfile._name
115           return outtoks,numabsorbed
116         numabsorbed = numabsorbed + 1
117         curtok = linetoks[curnum]
118       continue
119     elif c == "i":
120       if curtok._class != "id":
121         print "ERROR line  expected identifier got ",curtok._tex, "pattern" , pattern, " line " ,linenum," file ",myfile._name
122         return outtoks,numabsorbed
123       numabsorbed = numabsorbed + 1
124       outtoks += [curtok]
125       curnum = curnum + 1
126       continue
127     elif c == "{":
128       if isbrace(curtok,"{")  == "y":
129         outtoks += [curtok]
130         curnum = curnum + 1
131         numabsorbed = numabsorbed + 1
132       else:
133         print "ERROR line  expected {  got ",curtok._tex," pattern ",pattern," line " ,linenum," file ",myfile._name
134         return outtoks,numabsorbed
135     elif c == "}":
136       if isbrace(curtok,"}")  == "y":
137         outtoks += [curtok]
138         curnum = curnum + 1
139         numabsorbed = numabsorbed + 1
140       else:
141         print "ERROR line  expected }  got ",curtok._tex,"pattern",pattern," line " ,linenum," file ",myfile._name
142         return outtoks,numabsorbed
143     elif c == "*":
144       outlist = []
145       curtok = linetoks[curnum]
146       while isbrace(curtok,"}") == "n":
147         if dwspace(curtok) == "n":
148            outlist += [curtok]
149         curnum = curnum + 1
150         if curnum >= inlen:
151           outtoks += [outlist]
152           if patterncharnum < (len(pattern) -1): 
153             print "ERROR insufficient tokens on line for pattern ", pattern," line " ,linenum," file ",myfile._name
154           return outtoks,numabsorbed
155         numabsorbed = numabsorbed + 1
156         curtok = linetoks[curnum]
157       # Found a right brace, so done here.
158       outtoks += [outlist]
159     else:
160         print "ERROR pattern had unexpected character ",pattern
161         sys.exit(1)
162   return outtoks,numabsorbed
163
164 def reftodict(d,k,v):
165   keystring = toknamestring(k._token)
166   if d.has_key(keystring) == 0:
167      d[keystring] =  [v]
168   else:
169      existing = d.get(keystring)
170      existing += [v]
171      d[keystring] =  existing
172
173 def deftodict(d,k,v):
174   global dupdefcount
175   keystring = toknamestring(k._token)
176   if d.has_key(keystring) == 0:
177      d[keystring] =  [v]
178   else:
179      # This is a duplication, we just record it here,
180      # we will report on it shortly.
181      dupdefcount = dupdefcount + 1
182      existing = d.get(keystring)
183      existing += [v]
184      d[keystring] =  existing
185
186 def livetargprocess(linetoks,tnumin,myfile,linenum):
187   """ \livetarg{chap:DWTAGtemplatevalueparameter}{DW\-\_TAG\-\_template\-\_value\-\_parameter} """
188   global linkdefinitionsdict
189   global linkusesdict
190   global indexsetdict
191   t = linetoks[tnumin]
192   ourtoks,inlen = pickup(linetoks,tnumin,"i { i } { i }",myfile,linenum)
193   if len(ourtoks) > 5:
194     targlink= tokmention(ourtoks[2],myfile,linenum)
195     targname= tokmention(ourtoks[5],myfile,linenum)
196     deftodict(linkdefinitionsdict,targlink,targname)
197     reftodict(indexsetdict,targname,targname)
198   return inlen
199 def livetargiprocess(linetoks,tnumin,myfile,linenum):
200   """ \livetargi{chap:DWTAGtemplatevalueparameter}{DW\-\_TAG\-\_template\-\_value\-\_parameter}{name of targ} """
201   global linkdefinitionsdict
202   global linkusesdict
203   global indexsetdict
204   t = linetoks[tnumin]
205   ourtoks,inlen = pickup(linetoks,tnumin,"i { i } { i } { * }",myfile,linenum)
206   if len(ourtoks) > 5:
207     targlink= tokmention(ourtoks[2],myfile,linenum)
208     targname= tokmention(ourtoks[5],myfile,linenum)
209     deftodict(linkdefinitionsdict,targlink,targname)
210     reftodict(indexsetdict,targname,targname)
211   return inlen
212 def livelinkprocess(linetoks,tnumin,myfile,linenum):
213   """ \livelink{chap:DWTAGtemplatevalueparameter}{DW\-\_TAG\-\_template\-\_value\-\_parameter} """
214   global linkdefinitionsdict
215   global linkusesdict
216   global indexsetdict
217   t = linetoks[tnumin]
218   ourtoks,inlen = pickup(linetoks,tnumin,"i { i } { i }",myfile,linenum)
219   if len(ourtoks) > 5:
220     targlink= tokmention(ourtoks[2],myfile,linenum)
221     targname= tokmention(ourtoks[5],myfile,linenum)
222     reftodict(linkusesdict,targlink,targname)
223     reftodict(indexsetdict,targname,targname)
224   return inlen
225 def labelprocess(linetoks,tnumin,myfile,linenum):
226   """ \label{alabel} """
227   global labeldefinitionsdict
228   t = linetoks[tnumin]
229   ourtoks,inlen = pickup(linetoks,tnumin,"i { i }",myfile,linenum)
230   if len(ourtoks) > 2:
231     label = tokmention(ourtoks[2],myfile,linenum)
232     deftodict(labeldefinitionsdict,label,label)
233   return inlen
234 def addtoindexprocess(linetoks,tnumin,myfile,linenum):
235   """ \addtoindex{alabel} """
236   t = linetoks[tnumin]
237   ourtoks,inlen = pickup(linetoks,tnumin,"i { i }",myfile,linenum)
238   if len(ourtoks) > 2:
239     index = tokmention(ourtoks[2],myfile,linenum)
240     reftodict(indexsetdict,index,index)
241   return inlen
242 def indexprocess(linetoks,tnumin,myfile,linenum):
243   """ \index{indexentryname} """
244   global labelusesdict
245   t = linetoks[tnumin]
246   ourtoks,inlen = pickup(linetoks,tnumin,"i { i }",myfile,linenum)
247   if len(ourtoks) > 2:
248     myentry = tokmention(ourtoks[2],myfile,linenum)
249     reftodict(indexsetdict,myentry,myentry)
250   return inlen
251 def refersecprocess(linetoks,tnumin,myfile,linenum):
252   """ \refersec{label} """
253   global labelusesdict
254   t = linetoks[tnumin]
255   ourtoks,inlen = pickup(linetoks,tnumin,"i { i }",myfile,linenum)
256   if len(ourtoks) > 2:
257     label = tokmention(ourtoks[2],myfile,linenum)
258     reftodict(labelusesdict,label,label)
259   return inlen
260
261 def transfunc(linetoks,myfile,linenum):
262   if len(linetoks) < 1:
263     return linetoks
264   initialtok = linetoks[0]
265   if ''.join(initialtok._tex) == "\\newcommand":
266     # We ignore newcommand lines, they are not stuff
267     # we want to look at, they are new macros, not macro uses.
268     # We don't want to transform or touch them, nor report on them.
269     return linetoks
270   tnumin = 0
271   lasttoknum = len(linetoks)
272   while tnumin < lasttoknum:
273     t = linetoks[tnumin]
274     tnumcount = 1
275     rawtok = ''.join(t._tex)
276     stdname= ''.join(t._std)
277     if rawtok == "\\livetarg":
278       tnumcount = livetargprocess(linetoks,tnumin,myfile,linenum)
279     elif rawtok == "\\livetargi":
280       tnumcount = livetargiprocess(linetoks,tnumin,myfile,linenum)
281     elif rawtok == "\\livelink":
282       tnumcount = livelinkprocess(linetoks,tnumin,myfile,linenum)
283     elif rawtok == "\\label":
284       tnumcount = labelprocess(linetoks,tnumin,myfile,linenum)
285     elif rawtok == "\\refersec":
286       tnumcount = refersecprocess(linetoks,tnumin,myfile,linenum)
287     elif rawtok == "\\addtoindex":
288       tnumcount = addtoindexprocess(linetoks,tnumin,myfile,linenum)
289     elif rawtok == "\\index":
290       tnumcount = indexprocess(linetoks,tnumin,myfile,linenum)
291     else:  
292       if t._class == "id":
293         namemention = tokmention(t,myfile,linenum)
294         namestring = ''.join(t._std)
295         if namestring.startswith("DW"):
296           global unresolveddwdict
297           reftodict(unresolveddwdict,namemention,namemention)
298         # Else we might want to build a dict of all other words 
299         # while leaving out all \latex commands we don't know?
300     tnumin = tnumin + tnumcount
301     # End of for loop.
302   return linetoks
303
304 def process_files(filelist):
305   dwf = fileio.readFilelist(filelist)
306   # We will really just report, not transform
307   # anything, but this works.
308   dwf.dwtransformline(transfunc)
309
310   # Here we report on our discoveries.
311
312 def read_file_args(targlist):
313   cur = 1
314   filelist = []
315   while  len(sys.argv) > cur:
316     v = sys.argv[cur]
317     filelist += [v]
318     cur = int(cur) + 1
319   process_files(filelist)
320
321 def sort_tokmlist(mylist):
322    aux = [ (''.join(x._token._tex),x) for x in mylist ]
323    aux.sort()
324    return[ (x[1]) for x in aux]
325   
326 def printdups(d,name):
327   lablist = d.keys()
328   if len(lablist) < 1:
329     return
330   lablist.sort()
331   for k in lablist:
332      tokmlist = d[k]
333      stokmlist = sort_tokmlist(tokmlist)
334      if len(stokmlist) > 1:
335        print name,k,
336        for i in range (len(stokmlist)):
337          t = stokmlist[i]
338          if i == 0:
339            print t._file._name, t._line,
340          else:
341            print ", ",t._file._name, t._line,
342        print ""
343
344 def printoddDWentries(d,title):
345   """ An odd entry is one where the label text
346       does not match the condensed DW name properly.
347       \livelink{chap:DWTAGfoo}{DW_TAG_fx}
348       for example. 
349   """
350   names = d.keys()
351   for n in names:
352     if n.startswith("chap:DW"):
353       tn = d[n][0]
354       keytok = tn._token
355       toklab = "chap:" + ''.join(keytok._label)
356       if n != toklab:
357         print title, toknamestring(keytok),"mismatch", n ,tn._file._name,tn._line
358
359 def print_stats():
360   global linkdefinitionsdict
361   global linkusesdict
362   global labeldefinitionsdict
363   global labelusesdict
364   global ignorethesedict
365   global indexsetdict
366   global dupdefcount
367   global unresolveddwdict
368  
369   if dupdefcount > 0:
370     print "Duplicate definitions count: ",dupdefcount
371
372   printdups(labeldefinitionsdict,"Duplicated Labels")
373   printdups(linkdefinitionsdict,"Duplicated Links")
374
375   lablist = unresolveddwdict.keys()
376   if len(lablist) >0:
377     lablist.sort()
378     for k in lablist:
379       tokm = unresolveddwdict[k][0]
380       print "Unresolved DW string:", toknamestring(tokm._token)," at ",tokm._file._name,tokm._line
381
382   targlist = linkdefinitionsdict.keys()
383   targlist.sort()
384   for t in targlist:
385      u = linkusesdict.get(t)
386      if u == None:
387        tm = linkdefinitionsdict.get(t)
388        print  "Unused:",t, tm[0]._file._name,tm[0]._line
389     
390   printoddDWentries(linkdefinitionsdict,"link definitions");
391   printoddDWentries(linkusesdict,"link uses");
392   printoddDWentries(labeldefinitionsdict,"label definitions");
393   printoddDWentries(labelusesdict,"label uses");
394   
395
396
397   #FIXME More reporting needed.
398
399 def read_all_args():
400   filelist = []
401   fileio.setkeepordeletecomments("d")
402   cur = 1
403   while  len(sys.argv) > cur:
404     v = sys.argv[cur]
405     filelist += [v]
406     cur = int(cur) + 1
407   if len(filelist) < 1:
408     print >> sys.stderr , "No files specified."
409     printlegals()
410     sys.exit(1)
411   process_files(filelist)
412   print_stats()
413
414 #  anylink [-t <class>] ... [file] ...
415
416 if __name__ == '__main__':
417   read_all_args()
418