|
Packit |
1c1d7e |
"""Script to generate reports on translator classes from Doxygen sources.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
The main purpose of the script is to extract the information from sources
|
|
Packit |
1c1d7e |
related to internationalization (the translator classes). It uses the
|
|
Packit |
1c1d7e |
information to generate documentation (language.doc,
|
|
Packit |
1c1d7e |
translator_report.txt) from templates (language.tpl, maintainers.txt).
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
Simply run the script without parameters to get the reports and
|
|
Packit |
1c1d7e |
documentation for all supported languages. If you want to generate the
|
|
Packit |
1c1d7e |
translator report only for some languages, pass their codes as arguments
|
|
Packit |
1c1d7e |
to the script. In that case, the language.doc will not be generated.
|
|
Packit |
1c1d7e |
Example:
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
python translator.py en nl cz
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
Originally, the script was written in Perl and was known as translator.pl.
|
|
Packit |
1c1d7e |
The last Perl version was dated 2002/05/21 (plus some later corrections)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
Petr Prikryl (prikryl at atlas dot cz)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
History:
|
|
Packit |
1c1d7e |
--------
|
|
Packit |
1c1d7e |
2002/05/21 - This was the last Perl version.
|
|
Packit |
1c1d7e |
2003/05/16 - List of language marks can be passed as arguments.
|
|
Packit |
1c1d7e |
2004/01/24 - Total reimplementation started: classes TrManager, and Transl.
|
|
Packit |
1c1d7e |
2004/02/05 - First version that produces translator report. No language.doc yet.
|
|
Packit |
1c1d7e |
2004/02/10 - First fully functional version that generates both the translator
|
|
Packit |
1c1d7e |
report and the documentation. It is a bit slower than the
|
|
Packit |
1c1d7e |
Perl version, but is much less tricky and much more flexible.
|
|
Packit |
1c1d7e |
It also solves some problems that were not solved by the Perl
|
|
Packit |
1c1d7e |
version. The translator report content should be more useful
|
|
Packit |
1c1d7e |
for developers.
|
|
Packit |
1c1d7e |
2004/02/11 - Some tuning-up to provide more useful information.
|
|
Packit |
1c1d7e |
2004/04/16 - Added new tokens to the tokenizer (to remove some warnings).
|
|
Packit |
1c1d7e |
2004/05/25 - Added from __future__ import generators not to force Python 2.3.
|
|
Packit |
1c1d7e |
2004/06/03 - Removed dependency on textwrap module.
|
|
Packit |
1c1d7e |
2004/07/07 - Fixed the bug in the fill() function.
|
|
Packit |
1c1d7e |
2004/07/21 - Better e-mail mangling for HTML part of language.doc.
|
|
Packit |
1c1d7e |
- Plural not used for reporting a single missing method.
|
|
Packit |
1c1d7e |
- Removal of not used translator adapters is suggested only
|
|
Packit |
1c1d7e |
when the report is not restricted to selected languages
|
|
Packit |
1c1d7e |
explicitly via script arguments.
|
|
Packit |
1c1d7e |
2004/07/26 - Better reporting of not-needed adapters.
|
|
Packit |
1c1d7e |
2004/10/04 - Reporting of not called translator methods added.
|
|
Packit |
1c1d7e |
2004/10/05 - Modified to check only doxygen/src sources for the previous report.
|
|
Packit |
1c1d7e |
2005/02/28 - Slight modification to generate "mailto.txt" auxiliary file.
|
|
Packit |
1c1d7e |
2005/08/15 - Doxygen's root directory determined primarily from DOXYGEN
|
|
Packit |
1c1d7e |
environment variable. When not found, then relatively to the script.
|
|
Packit |
1c1d7e |
2007/03/20 - The "translate me!" searched in comments and reported if found.
|
|
Packit |
1c1d7e |
2008/06/09 - Warning when the MAX_DOT_GRAPH_HEIGHT is still part of trLegendDocs().
|
|
Packit |
1c1d7e |
2009/05/09 - Changed HTML output to fit it with XHTML DTD
|
|
Packit |
1c1d7e |
2009/09/02 - Added percentage info to the report (implemented / to be implemented).
|
|
Packit |
1c1d7e |
2010/02/09 - Added checking/suggestion 'Reimplementation using UTF-8 suggested.
|
|
Packit |
1c1d7e |
2010/03/03 - Added [unreachable] prefix used in maintainers.txt.
|
|
Packit |
1c1d7e |
2010/05/28 - BOM skipped; minor code cleaning.
|
|
Packit |
1c1d7e |
2010/05/31 - e-mail mangled already in maintainers.txt
|
|
Packit |
1c1d7e |
2010/08/20 - maintainers.txt to UTF-8, related processing of unicode strings
|
|
Packit |
1c1d7e |
- [any mark] introduced instead of [unreachable] only
|
|
Packit |
1c1d7e |
- marks highlighted in HTML
|
|
Packit |
1c1d7e |
2010/08/30 - Highlighting in what will be the table in langhowto.html modified.
|
|
Packit |
1c1d7e |
2010/09/27 - The underscore in \latexonly part of the generated language.doc
|
|
Packit |
1c1d7e |
was prefixed by backslash (was LaTeX related error).
|
|
Packit |
1c1d7e |
2013/02/19 - Better diagnostics when translator_xx.h is too crippled.
|
|
Packit |
1c1d7e |
2013/06/25 - TranslatorDecoder checks removed after removing the class.
|
|
Packit |
1c1d7e |
2013/09/04 - Coloured status in langhowto. *ALMOST up-to-date* category
|
|
Packit |
1c1d7e |
of translators introduced.
|
|
Packit |
1c1d7e |
2014/06/16 - unified for Python 2.6+ and 3.0+
|
|
Packit |
1c1d7e |
"""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
from __future__ import print_function
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
import os
|
|
Packit |
1c1d7e |
import platform
|
|
Packit |
1c1d7e |
import re
|
|
Packit |
1c1d7e |
import sys
|
|
Packit |
1c1d7e |
import textwrap
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def xopen(fname, mode='r', encoding='utf-8-sig'):
|
|
Packit |
1c1d7e |
'''Unified file opening for Python 2 an Python 3.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
Python 2 does not have the encoding argument. Python 3 has one, and
|
|
Packit |
1c1d7e |
the default 'utf-8-sig' is used (skips the BOM automatically).
|
|
Packit |
1c1d7e |
'''
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
if sys.version_info[0] == 2:
|
|
Packit |
1c1d7e |
return open(fname, mode=mode) # Python 2 without encoding
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
return open(fname, mode=mode, encoding=encoding) # Python 3 with encoding
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def fill(s):
|
|
Packit |
1c1d7e |
"""Returns string formatted to the wrapped paragraph multiline string.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
Replaces whitespaces by one space and then uses he textwrap.fill()."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Replace all whitespace by spaces, remove whitespaces that are not
|
|
Packit |
1c1d7e |
# necessary, strip the left and right whitespaces, and break the string
|
|
Packit |
1c1d7e |
# to list of words.
|
|
Packit |
1c1d7e |
rexWS = re.compile(r'\s+')
|
|
Packit |
1c1d7e |
lst = rexWS.sub(' ', s).strip().split()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# If the list is not empty, put the words together and form the lines
|
|
Packit |
1c1d7e |
# of maximum 70 characters. Build the list of lines.
|
|
Packit |
1c1d7e |
lines = []
|
|
Packit |
1c1d7e |
if lst:
|
|
Packit |
1c1d7e |
line = lst.pop(0) # no separation space in front of the first word
|
|
Packit |
1c1d7e |
for word in lst:
|
|
Packit |
1c1d7e |
if len(line) + len(word) < 70:
|
|
Packit |
1c1d7e |
line += ' ' + word
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
lines.append(line) # another full line formed
|
|
Packit |
1c1d7e |
line = word # next line started
|
|
Packit |
1c1d7e |
lines.append(line) # the last line
|
|
Packit |
1c1d7e |
return '\n'.join(lines)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
class Transl:
|
|
Packit |
1c1d7e |
"""One instance is build for each translator.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
The abbreviation of the source file--part after 'translator_'--is used as
|
|
Packit |
1c1d7e |
the identification of the object. The empty string is used for the
|
|
Packit |
1c1d7e |
abstract Translator class from translator.h. The other information is
|
|
Packit |
1c1d7e |
extracted from inside the source file."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __init__(self, fname, manager):
|
|
Packit |
1c1d7e |
"""Bind to the manager and initialize."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Store the filename and the reference to the manager object.
|
|
Packit |
1c1d7e |
self.fname = fname
|
|
Packit |
1c1d7e |
self.manager = manager
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The instance is responsible for loading the source file, so it checks
|
|
Packit |
1c1d7e |
# for its existence and quits if something goes wrong.
|
|
Packit |
1c1d7e |
if not os.path.isfile(fname):
|
|
Packit |
1c1d7e |
sys.stderr.write("\a\nFile '%s' not found!\n" % fname)
|
|
Packit |
1c1d7e |
sys.exit(1)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Initialize the other collected information.
|
|
Packit |
1c1d7e |
self.classId = None
|
|
Packit |
1c1d7e |
self.baseClassId = None
|
|
Packit |
1c1d7e |
self.readableStatus = None # 'up-to-date', '1.2.3', '1.3', etc.
|
|
Packit |
1c1d7e |
self.status = None # '', '1.2.03', '1.3.00', etc.
|
|
Packit |
1c1d7e |
self.lang = None # like 'Brasilian'
|
|
Packit |
1c1d7e |
self.langReadable = None # like 'Brasilian Portuguese'
|
|
Packit |
1c1d7e |
self.note = None # like 'should be cleaned up'
|
|
Packit |
1c1d7e |
self.prototypeDic = {} # uniPrototype -> prototype
|
|
Packit |
1c1d7e |
self.translateMeText = 'translate me!'
|
|
Packit |
1c1d7e |
self.translateMeFlag = False # comments with "translate me!" found
|
|
Packit |
1c1d7e |
self.txtMAX_DOT_GRAPH_HEIGHT_flag = False # found in string in trLegendDocs()
|
|
Packit |
1c1d7e |
self.obsoleteMethods = None # list of prototypes to be removed
|
|
Packit |
1c1d7e |
self.missingMethods = None # list of prototypes to be implemented
|
|
Packit |
1c1d7e |
self.implementedMethods = None # list of implemented required methods
|
|
Packit |
1c1d7e |
self.adaptMinClass = None # The newest adapter class that can be used
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __tokenGenerator(self):
|
|
Packit |
1c1d7e |
"""Generator that reads the file and yields tokens as 4-tuples.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
The tokens have the form (tokenId, tokenString, lineNo). The
|
|
Packit |
1c1d7e |
last returned token has the form ('eof', None, None). When trying
|
|
Packit |
1c1d7e |
to access next token after that, the exception would be raised."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Set the dictionary for recognizing tokenId for keywords, separators
|
|
Packit |
1c1d7e |
# and the similar categories. The key is the string to be recognized,
|
|
Packit |
1c1d7e |
# the value says its token identification.
|
|
Packit |
1c1d7e |
tokenDic = { 'class': 'class',
|
|
Packit |
1c1d7e |
'const': 'const',
|
|
Packit |
1c1d7e |
'public': 'public',
|
|
Packit |
1c1d7e |
'protected': 'protected',
|
|
Packit |
1c1d7e |
'private': 'private',
|
|
Packit |
1c1d7e |
'static': 'static',
|
|
Packit |
1c1d7e |
'virtual': 'virtual',
|
|
Packit |
1c1d7e |
':': 'colon',
|
|
Packit |
1c1d7e |
';': 'semic',
|
|
Packit |
1c1d7e |
',': 'comma',
|
|
Packit |
1c1d7e |
'[': 'lsqbra',
|
|
Packit |
1c1d7e |
']': 'rsqbra',
|
|
Packit |
1c1d7e |
'(': 'lpar',
|
|
Packit |
1c1d7e |
')': 'rpar',
|
|
Packit |
1c1d7e |
'{': 'lcurly',
|
|
Packit |
1c1d7e |
'}': 'rcurly',
|
|
Packit |
1c1d7e |
'=': 'assign',
|
|
Packit |
1c1d7e |
'*': 'star',
|
|
Packit |
1c1d7e |
'&': 'amp',
|
|
Packit |
1c1d7e |
'+': 'plus',
|
|
Packit |
1c1d7e |
'-': 'minus',
|
|
Packit |
1c1d7e |
'!': 'excl',
|
|
Packit |
1c1d7e |
'?': 'qmark',
|
|
Packit |
1c1d7e |
'<': 'lt',
|
|
Packit |
1c1d7e |
'>': 'gt',
|
|
Packit |
1c1d7e |
"'": 'quot',
|
|
Packit |
1c1d7e |
'"': 'dquot',
|
|
Packit |
1c1d7e |
'.': 'dot',
|
|
Packit |
1c1d7e |
'%': 'perc',
|
|
Packit |
1c1d7e |
'~': 'tilde',
|
|
Packit |
1c1d7e |
'^': 'caret',
|
|
Packit |
1c1d7e |
}
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Regular expression for recognizing identifiers.
|
|
Packit |
1c1d7e |
rexId = re.compile(r'^[a-zA-Z]\w*$')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Open the file for reading and extracting tokens until the eof.
|
|
Packit |
1c1d7e |
# Initialize the finite automaton.
|
|
Packit |
1c1d7e |
f = xopen(self.fname)
|
|
Packit |
1c1d7e |
lineNo = 0
|
|
Packit |
1c1d7e |
line = '' # init -- see the pos initialization below
|
|
Packit |
1c1d7e |
linelen = 0 # init
|
|
Packit |
1c1d7e |
pos = 100 # init -- pos after the end of line
|
|
Packit |
1c1d7e |
status = 0
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
tokenId = None # init
|
|
Packit |
1c1d7e |
tokenStr = '' # init -- the characters will be appended.
|
|
Packit |
1c1d7e |
tokenLineNo = 0
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
while status != 777:
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Get the next character. Read next line first, if necessary.
|
|
Packit |
1c1d7e |
if pos < linelen:
|
|
Packit |
1c1d7e |
c = line[pos]
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
lineNo += 1
|
|
Packit |
1c1d7e |
line = f.readline()
|
|
Packit |
1c1d7e |
linelen = len(line)
|
|
Packit |
1c1d7e |
pos = 0
|
|
Packit |
1c1d7e |
if line == '': # eof
|
|
Packit |
1c1d7e |
status = 777
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
c = line[pos]
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Consume the character based on the status
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
if status == 0: # basic status
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# This is the initial status. If tokenId is set, yield the
|
|
Packit |
1c1d7e |
# token here and only here (except when eof is found).
|
|
Packit |
1c1d7e |
# Initialize the token variables after the yield.
|
|
Packit |
1c1d7e |
if tokenId:
|
|
Packit |
1c1d7e |
# If it is an unknown item, it can still be recognized
|
|
Packit |
1c1d7e |
# here. Keywords and separators are the example.
|
|
Packit |
1c1d7e |
if tokenId == 'unknown':
|
|
Packit |
1c1d7e |
if tokenStr in tokenDic:
|
|
Packit |
1c1d7e |
tokenId = tokenDic[tokenStr]
|
|
Packit |
1c1d7e |
elif tokenStr.isdigit():
|
|
Packit |
1c1d7e |
tokenId = 'num'
|
|
Packit |
1c1d7e |
elif rexId.match(tokenStr):
|
|
Packit |
1c1d7e |
tokenId = 'id'
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
msg = '\aWarning: unknown token "' + tokenStr + '"'
|
|
Packit |
1c1d7e |
msg += '\tfound on line %d' % tokenLineNo
|
|
Packit |
1c1d7e |
msg += ' in "' + self.fname + '".\n'
|
|
Packit |
1c1d7e |
sys.stderr.write(msg)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
yield (tokenId, tokenStr, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# If it is a comment that contains the self.translateMeText
|
|
Packit |
1c1d7e |
# string, set the flag -- the situation will be reported.
|
|
Packit |
1c1d7e |
if tokenId == 'comment' and tokenStr.find(self.translateMeText) >= 0:
|
|
Packit |
1c1d7e |
self.translateMeFlag = True
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
tokenId = None
|
|
Packit |
1c1d7e |
tokenStr = ''
|
|
Packit |
1c1d7e |
tokenLineNo = 0
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Now process the character. When we just skip it (spaces),
|
|
Packit |
1c1d7e |
# stay in this status. All characters that will be part of
|
|
Packit |
1c1d7e |
# some token cause moving to the specific status. And only
|
|
Packit |
1c1d7e |
# when moving to the status == 0 (or the final state 777),
|
|
Packit |
1c1d7e |
# the token is yielded. With respect to that the automaton
|
|
Packit |
1c1d7e |
# behaves as Moore's one (output bound to status). When
|
|
Packit |
1c1d7e |
# collecting tokens, the automaton is the Mealy's one
|
|
Packit |
1c1d7e |
# (actions bound to transitions).
|
|
Packit |
1c1d7e |
if c.isspace():
|
|
Packit |
1c1d7e |
pass # just skip whitespace characters
|
|
Packit |
1c1d7e |
elif c == '/': # Possibly comment starts here, but
|
|
Packit |
1c1d7e |
tokenId = 'unknown' # it could be only a slash in code.
|
|
Packit |
1c1d7e |
tokenStr = c
|
|
Packit |
1c1d7e |
tokenLineNo = lineNo
|
|
Packit |
1c1d7e |
status = 1
|
|
Packit |
1c1d7e |
elif c == '#':
|
|
Packit |
1c1d7e |
tokenId = 'preproc' # preprocessor directive
|
|
Packit |
1c1d7e |
tokenStr = c
|
|
Packit |
1c1d7e |
tokenLineNo = lineNo
|
|
Packit |
1c1d7e |
status = 5
|
|
Packit |
1c1d7e |
elif c == '"': # string starts here
|
|
Packit |
1c1d7e |
tokenId = 'string'
|
|
Packit |
1c1d7e |
tokenStr = c
|
|
Packit |
1c1d7e |
tokenLineNo = lineNo
|
|
Packit |
1c1d7e |
status = 6
|
|
Packit |
1c1d7e |
elif c == "'": # char literal starts here
|
|
Packit |
1c1d7e |
tokenId = 'charlit'
|
|
Packit |
1c1d7e |
tokenStr = c
|
|
Packit |
1c1d7e |
tokenLineNo = lineNo
|
|
Packit |
1c1d7e |
status = 8
|
|
Packit |
1c1d7e |
elif c in tokenDic: # known one-char token
|
|
Packit |
1c1d7e |
tokenId = tokenDic[c]
|
|
Packit |
1c1d7e |
tokenStr = c
|
|
Packit |
1c1d7e |
tokenLineNo = lineNo
|
|
Packit |
1c1d7e |
# stay in this state to yield token immediately
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
tokenId = 'unknown' # totally unknown
|
|
Packit |
1c1d7e |
tokenStr = c
|
|
Packit |
1c1d7e |
tokenLineNo = lineNo
|
|
Packit |
1c1d7e |
status = 333
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
pos += 1 # move position in any case
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 1: # possibly a comment
|
|
Packit |
1c1d7e |
if c == '/': # ... definitely the C++ comment
|
|
Packit |
1c1d7e |
tokenId = 'comment'
|
|
Packit |
1c1d7e |
tokenStr += c
|
|
Packit |
1c1d7e |
pos += 1
|
|
Packit |
1c1d7e |
status = 2
|
|
Packit |
1c1d7e |
elif c == '*': # ... definitely the C comment
|
|
Packit |
1c1d7e |
tokenId = 'comment'
|
|
Packit |
1c1d7e |
tokenStr += c
|
|
Packit |
1c1d7e |
pos += 1
|
|
Packit |
1c1d7e |
status = 3
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
status = 0 # unrecognized, don't move pos
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 2: # inside the C++ comment
|
|
Packit |
1c1d7e |
if c == '\n': # the end of C++ comment
|
|
Packit |
1c1d7e |
status = 0 # yield the token
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
tokenStr += c # collect the C++ comment
|
|
Packit |
1c1d7e |
pos += 1
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 3: # inside the C comment
|
|
Packit |
1c1d7e |
if c == '*': # possibly the end of the C comment
|
|
Packit |
1c1d7e |
tokenStr += c
|
|
Packit |
1c1d7e |
status = 4
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
tokenStr += c # collect the C comment
|
|
Packit |
1c1d7e |
pos += 1
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 4: # possibly the end of the C comment
|
|
Packit |
1c1d7e |
if c == '/': # definitely the end of the C comment
|
|
Packit |
1c1d7e |
tokenStr += c
|
|
Packit |
1c1d7e |
status = 0 # yield the token
|
|
Packit |
1c1d7e |
elif c == '*': # more stars inside the comment
|
|
Packit |
1c1d7e |
tokenStr += c
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
tokenStr += c # this cannot be the end of comment
|
|
Packit |
1c1d7e |
status = 3
|
|
Packit |
1c1d7e |
pos += 1
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 5: # inside the preprocessor directive
|
|
Packit |
1c1d7e |
if c == '\n': # the end of the preproc. command
|
|
Packit |
1c1d7e |
status = 0 # yield the token
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
tokenStr += c # collect the preproc
|
|
Packit |
1c1d7e |
pos += 1
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 6: # inside the string
|
|
Packit |
1c1d7e |
if c == '\\': # escaped char inside the string
|
|
Packit |
1c1d7e |
tokenStr += c
|
|
Packit |
1c1d7e |
status = 7
|
|
Packit |
1c1d7e |
elif c == '"': # end of the string
|
|
Packit |
1c1d7e |
tokenStr += c
|
|
Packit |
1c1d7e |
status = 0
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
tokenStr += c # collect the chars of the string
|
|
Packit |
1c1d7e |
pos += 1
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 7: # escaped char inside the string
|
|
Packit |
1c1d7e |
tokenStr += c # collect the char of the string
|
|
Packit |
1c1d7e |
status = 6
|
|
Packit |
1c1d7e |
pos += 1
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 8: # inside the char literal
|
|
Packit |
1c1d7e |
tokenStr += c # collect the char of the literal
|
|
Packit |
1c1d7e |
status = 9
|
|
Packit |
1c1d7e |
pos += 1
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 9: # end of char literal expected
|
|
Packit |
1c1d7e |
if c == "'": # ... and found
|
|
Packit |
1c1d7e |
tokenStr += c
|
|
Packit |
1c1d7e |
status = 0
|
|
Packit |
1c1d7e |
pos += 1
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
tokenId = 'error' # end of literal was expected
|
|
Packit |
1c1d7e |
tokenStr += c
|
|
Packit |
1c1d7e |
status = 0
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 333: # start of the unknown token
|
|
Packit |
1c1d7e |
if c.isspace():
|
|
Packit |
1c1d7e |
pos += 1
|
|
Packit |
1c1d7e |
status = 0 # tokenId may be determined later
|
|
Packit |
1c1d7e |
elif c in tokenDic: # separator, don't move pos
|
|
Packit |
1c1d7e |
status = 0
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
tokenStr += c # collect
|
|
Packit |
1c1d7e |
pos += 1
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# We should have finished in the final status. If some token
|
|
Packit |
1c1d7e |
# have been extracted, yield it first.
|
|
Packit |
1c1d7e |
assert(status == 777)
|
|
Packit |
1c1d7e |
if tokenId:
|
|
Packit |
1c1d7e |
yield (tokenId, tokenStr, tokenLineNo)
|
|
Packit |
1c1d7e |
tokenId = None
|
|
Packit |
1c1d7e |
tokenStr = ''
|
|
Packit |
1c1d7e |
tokenLineNo = 0
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The file content is processed. Close the file. Then always yield
|
|
Packit |
1c1d7e |
# the eof token.
|
|
Packit |
1c1d7e |
f.close()
|
|
Packit |
1c1d7e |
yield ('eof', None, None)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __collectClassInfo(self, tokenIterator):
|
|
Packit |
1c1d7e |
"""Collect the information about the class and base class.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
The tokens including the opening left curly brace of the class are
|
|
Packit |
1c1d7e |
consumed."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
status = 0 # initial state
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
while status != 777: # final state
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Always assume that the previous tokens were processed. Get
|
|
Packit |
1c1d7e |
# the next one.
|
|
Packit |
1c1d7e |
tokenId, tokenStr, tokenLineNo = next(tokenIterator)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Process the token and never return back.
|
|
Packit |
1c1d7e |
if status == 0: # waiting for the 'class' keyword.
|
|
Packit |
1c1d7e |
if tokenId == 'class':
|
|
Packit |
1c1d7e |
status = 1
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 1: # expecting the class identification
|
|
Packit |
1c1d7e |
if tokenId == 'id':
|
|
Packit |
1c1d7e |
self.classId = tokenStr
|
|
Packit |
1c1d7e |
status = 2
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 2: # expecting the curly brace or base class info
|
|
Packit |
1c1d7e |
if tokenId == 'lcurly':
|
|
Packit |
1c1d7e |
status = 777 # correctly finished
|
|
Packit |
1c1d7e |
elif tokenId == 'colon':
|
|
Packit |
1c1d7e |
status = 3
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 3: # expecting the 'public' in front of base class id
|
|
Packit |
1c1d7e |
if tokenId == 'public':
|
|
Packit |
1c1d7e |
status = 4
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 4: # expecting the base class id
|
|
Packit |
1c1d7e |
if tokenId == 'id':
|
|
Packit |
1c1d7e |
self.baseClassId = tokenStr
|
|
Packit |
1c1d7e |
status = 5
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 5: # expecting the curly brace and quitting
|
|
Packit |
1c1d7e |
if tokenId == 'lcurly':
|
|
Packit |
1c1d7e |
status = 777 # correctly finished
|
|
Packit |
1c1d7e |
elif tokenId == 'comment':
|
|
Packit |
1c1d7e |
pass
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Extract the status of the TranslatorXxxx class. The readable form
|
|
Packit |
1c1d7e |
# will be used in reports the status form is a string that can be
|
|
Packit |
1c1d7e |
# compared lexically (unified length, padding with zeros, etc.).
|
|
Packit |
1c1d7e |
if self.baseClassId:
|
|
Packit |
1c1d7e |
lst = self.baseClassId.split('_')
|
|
Packit |
1c1d7e |
if lst[0] == 'Translator':
|
|
Packit |
1c1d7e |
self.readableStatus = 'up-to-date'
|
|
Packit |
1c1d7e |
self.status = ''
|
|
Packit |
1c1d7e |
elif lst[0] == 'TranslatorAdapter':
|
|
Packit |
1c1d7e |
self.status = lst[1] + '.' + lst[2]
|
|
Packit |
1c1d7e |
self.readableStatus = self.status
|
|
Packit |
1c1d7e |
if len(lst) > 3: # add the last part of the number
|
|
Packit |
1c1d7e |
self.status += '.' + ('%02d' % int(lst[3]))
|
|
Packit |
1c1d7e |
self.readableStatus += '.' + lst[3]
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.status += '.00'
|
|
Packit |
1c1d7e |
elif lst[0] == 'TranslatorEnglish':
|
|
Packit |
1c1d7e |
# Obsolete or Based on English.
|
|
Packit |
1c1d7e |
if self.classId[-2:] == 'En':
|
|
Packit |
1c1d7e |
self.readableStatus = 'English based'
|
|
Packit |
1c1d7e |
self.status = 'En'
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.readableStatus = 'obsolete'
|
|
Packit |
1c1d7e |
self.status = '0.0.00'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Check whether status was set, or set 'strange'.
|
|
Packit |
1c1d7e |
if self.status == None:
|
|
Packit |
1c1d7e |
self.status = 'strange'
|
|
Packit |
1c1d7e |
if not self.readableStatus:
|
|
Packit |
1c1d7e |
self.readableStatus = 'strange'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Extract the name of the language and the readable form.
|
|
Packit |
1c1d7e |
self.lang = self.classId[10:] # without 'Translator'
|
|
Packit |
1c1d7e |
if self.lang == 'Brazilian':
|
|
Packit |
1c1d7e |
self.langReadable = 'Brazilian Portuguese'
|
|
Packit |
1c1d7e |
elif self.lang == 'Chinesetraditional':
|
|
Packit |
1c1d7e |
self.langReadable = 'Chinese Traditional'
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.langReadable = self.lang
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __unexpectedToken(self, status, tokenId, tokenLineNo):
|
|
Packit |
1c1d7e |
"""Reports unexpected token and quits with exit code 1."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
import inspect
|
|
Packit |
1c1d7e |
calledFrom = inspect.stack()[1][3]
|
|
Packit |
1c1d7e |
msg = "\a\nUnexpected token '%s' on the line %d in '%s'.\n"
|
|
Packit |
1c1d7e |
msg = msg % (tokenId, tokenLineNo, self.fname)
|
|
Packit |
1c1d7e |
msg += 'status = %d in %s()\n' % (status, calledFrom)
|
|
Packit |
1c1d7e |
sys.stderr.write(msg)
|
|
Packit |
1c1d7e |
sys.exit(1)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def collectPureVirtualPrototypes(self):
|
|
Packit |
1c1d7e |
"""Returns dictionary 'unified prototype' -> 'full prototype'.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
The method is expected to be called only for the translator.h. It
|
|
Packit |
1c1d7e |
extracts only the pure virtual method and build the dictionary where
|
|
Packit |
1c1d7e |
key is the unified prototype without argument identifiers."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Prepare empty dictionary that will be returned.
|
|
Packit |
1c1d7e |
resultDic = {}
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Start the token generator which parses the class source file.
|
|
Packit |
1c1d7e |
tokenIterator = self.__tokenGenerator()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Collect the class and the base class identifiers.
|
|
Packit |
1c1d7e |
self.__collectClassInfo(tokenIterator)
|
|
Packit |
1c1d7e |
assert(self.classId == 'Translator')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Let's collect readable form of the public virtual pure method
|
|
Packit |
1c1d7e |
# prototypes in the readable form -- as defined in translator.h.
|
|
Packit |
1c1d7e |
# Let's collect also unified form of the same prototype that omits
|
|
Packit |
1c1d7e |
# everything that can be omitted, namely 'virtual' and argument
|
|
Packit |
1c1d7e |
# identifiers.
|
|
Packit |
1c1d7e |
prototype = '' # readable prototype (with everything)
|
|
Packit |
1c1d7e |
uniPrototype = '' # unified prototype (without arg. identifiers)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Collect the pure virtual method prototypes. Stop on the closing
|
|
Packit |
1c1d7e |
# curly brace followed by the semicolon (end of class).
|
|
Packit |
1c1d7e |
status = 0
|
|
Packit |
1c1d7e |
curlyCnt = 0 # counter for the level of curly braces
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Loop until the final state 777 is reached. The errors are processed
|
|
Packit |
1c1d7e |
# immediately. In this implementation, it always quits the application.
|
|
Packit |
1c1d7e |
while status != 777:
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Get the next token.
|
|
Packit |
1c1d7e |
tokenId, tokenStr, tokenLineNo = next(tokenIterator)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
if status == 0: # waiting for 'public:'
|
|
Packit |
1c1d7e |
if tokenId == 'public':
|
|
Packit |
1c1d7e |
status = 1
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 1: # colon after the 'public'
|
|
Packit |
1c1d7e |
if tokenId == 'colon':
|
|
Packit |
1c1d7e |
status = 2
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 2: # waiting for 'virtual'
|
|
Packit |
1c1d7e |
if tokenId == 'virtual':
|
|
Packit |
1c1d7e |
prototype = tokenStr # but not to unified prototype
|
|
Packit |
1c1d7e |
status = 3
|
|
Packit |
1c1d7e |
elif tokenId == 'comment':
|
|
Packit |
1c1d7e |
pass
|
|
Packit |
1c1d7e |
elif tokenId == 'rcurly':
|
|
Packit |
1c1d7e |
status = 11 # expected end of class
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 3: # return type of the method expected
|
|
Packit |
1c1d7e |
if tokenId == 'id':
|
|
Packit |
1c1d7e |
prototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
uniPrototype = tokenStr # start collecting the unified prototype
|
|
Packit |
1c1d7e |
status = 4
|
|
Packit |
1c1d7e |
elif tokenId == 'tilde':
|
|
Packit |
1c1d7e |
status = 4
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 4: # method identifier expected
|
|
Packit |
1c1d7e |
if tokenId == 'id':
|
|
Packit |
1c1d7e |
prototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
status = 5
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 5: # left bracket of the argument list expected
|
|
Packit |
1c1d7e |
if tokenId == 'lpar':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 6
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 6: # collecting arguments of the method
|
|
Packit |
1c1d7e |
if tokenId == 'rpar':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 7
|
|
Packit |
1c1d7e |
elif tokenId == 'const':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 12
|
|
Packit |
1c1d7e |
elif tokenId == 'id': # type identifier
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 13
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 7: # assignment expected or left curly brace
|
|
Packit |
1c1d7e |
if tokenId == 'assign':
|
|
Packit |
1c1d7e |
status = 8
|
|
Packit |
1c1d7e |
elif tokenId == 'lcurly':
|
|
Packit |
1c1d7e |
curlyCnt = 1 # method body entered
|
|
Packit |
1c1d7e |
status = 10
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 8: # zero expected
|
|
Packit |
1c1d7e |
if tokenId == 'num' and tokenStr == '0':
|
|
Packit |
1c1d7e |
status = 9
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 9: # after semicolon, produce the dic item
|
|
Packit |
1c1d7e |
if tokenId == 'semic':
|
|
Packit |
1c1d7e |
assert(uniPrototype not in resultDic)
|
|
Packit |
1c1d7e |
resultDic[uniPrototype] = prototype
|
|
Packit |
1c1d7e |
status = 2
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 10: # consuming the body of the method
|
|
Packit |
1c1d7e |
if tokenId == 'rcurly':
|
|
Packit |
1c1d7e |
curlyCnt -= 1
|
|
Packit |
1c1d7e |
if curlyCnt == 0:
|
|
Packit |
1c1d7e |
status = 2 # body consumed
|
|
Packit |
1c1d7e |
elif tokenId == 'lcurly':
|
|
Packit |
1c1d7e |
curlyCnt += 1
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 11: # probably the end of class
|
|
Packit |
1c1d7e |
if tokenId == 'semic':
|
|
Packit |
1c1d7e |
status = 777
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 12: # type id for argument expected
|
|
Packit |
1c1d7e |
if tokenId == 'id':
|
|
Packit |
1c1d7e |
prototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
status = 13
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 13: # namespace qualification or * or & expected
|
|
Packit |
1c1d7e |
if tokenId == 'colon': # was namespace id
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 14
|
|
Packit |
1c1d7e |
elif tokenId == 'star' or tokenId == 'amp': # pointer or reference
|
|
Packit |
1c1d7e |
prototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
status = 16
|
|
Packit |
1c1d7e |
elif tokenId == 'id': # argument identifier
|
|
Packit |
1c1d7e |
prototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
# don't put this into unified prototype
|
|
Packit |
1c1d7e |
status = 17
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 14: # second colon for namespace:: expected
|
|
Packit |
1c1d7e |
if tokenId == 'colon':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 15
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 15: # type after namespace:: expected
|
|
Packit |
1c1d7e |
if tokenId == 'id':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 13
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 16: # argument identifier expected
|
|
Packit |
1c1d7e |
if tokenId == 'id':
|
|
Packit |
1c1d7e |
prototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
# don't put this into unified prototype
|
|
Packit |
1c1d7e |
status = 17
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 17: # comma or ')' after argument identifier expected
|
|
Packit |
1c1d7e |
if tokenId == 'comma':
|
|
Packit |
1c1d7e |
prototype += ', '
|
|
Packit |
1c1d7e |
uniPrototype += ', '
|
|
Packit |
1c1d7e |
status = 6
|
|
Packit |
1c1d7e |
elif tokenId == 'rpar':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 7
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Eat the rest of the source to cause closing the file.
|
|
Packit |
1c1d7e |
while tokenId != 'eof':
|
|
Packit |
1c1d7e |
tokenId, tokenStr, tokenLineNo = next(tokenIterator)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Return the resulting dictionary with 'uniPrototype -> prototype'.
|
|
Packit |
1c1d7e |
return resultDic
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __collectPublicMethodPrototypes(self, tokenIterator):
|
|
Packit |
1c1d7e |
"""Collects prototypes of public methods and fills self.prototypeDic.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
The dictionary is filled by items: uniPrototype -> prototype.
|
|
Packit |
1c1d7e |
The method is expected to be called only for TranslatorXxxx classes,
|
|
Packit |
1c1d7e |
i.e. for the classes that implement translation to some language.
|
|
Packit |
1c1d7e |
It assumes that the opening curly brace of the class was already
|
|
Packit |
1c1d7e |
consumed. The source is consumed until the end of the class.
|
|
Packit |
1c1d7e |
The caller should consume the source until the eof to cause closing
|
|
Packit |
1c1d7e |
the source file."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
assert(self.classId != 'Translator')
|
|
Packit |
1c1d7e |
assert(self.baseClassId != None)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The following finite automaton slightly differs from the one
|
|
Packit |
1c1d7e |
# inside self.collectPureVirtualPrototypes(). It produces the
|
|
Packit |
1c1d7e |
# dictionary item just after consuming the body of the method
|
|
Packit |
1c1d7e |
# (transition from state 10 to state 2). It also does not allow
|
|
Packit |
1c1d7e |
# definitions of public pure virtual methods, except for
|
|
Packit |
1c1d7e |
# TranslatorAdapterBase (states 8 and 9). Argument identifier inside
|
|
Packit |
1c1d7e |
# method argument lists can be omitted or commented.
|
|
Packit |
1c1d7e |
#
|
|
Packit |
1c1d7e |
# Let's collect readable form of all public method prototypes in
|
|
Packit |
1c1d7e |
# the readable form -- as defined in the source file.
|
|
Packit |
1c1d7e |
# Let's collect also unified form of the same prototype that omits
|
|
Packit |
1c1d7e |
# everything that can be omitted, namely 'virtual' and argument
|
|
Packit |
1c1d7e |
# identifiers.
|
|
Packit |
1c1d7e |
prototype = '' # readable prototype (with everything)
|
|
Packit |
1c1d7e |
uniPrototype = '' # unified prototype (without arg. identifiers)
|
|
Packit |
1c1d7e |
warning = '' # warning message -- if something special detected
|
|
Packit |
1c1d7e |
methodId = None # processed method id
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Collect the method prototypes. Stop on the closing
|
|
Packit |
1c1d7e |
# curly brace followed by the semicolon (end of class).
|
|
Packit |
1c1d7e |
status = 0
|
|
Packit |
1c1d7e |
curlyCnt = 0 # counter for the level of curly braces
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Loop until the final state 777 is reached. The errors are processed
|
|
Packit |
1c1d7e |
# immediately. In this implementation, it always quits the application.
|
|
Packit |
1c1d7e |
while status != 777:
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Get the next token.
|
|
Packit |
1c1d7e |
tokenId, tokenStr, tokenLineNo = next(tokenIterator)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
if status == 0: # waiting for 'public:'
|
|
Packit |
1c1d7e |
if tokenId == 'public':
|
|
Packit |
1c1d7e |
status = 1
|
|
Packit |
1c1d7e |
elif tokenId == 'eof': # non-public things until the eof
|
|
Packit |
1c1d7e |
status = 777
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 1: # colon after the 'public'
|
|
Packit |
1c1d7e |
if tokenId == 'colon':
|
|
Packit |
1c1d7e |
status = 2
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 2: # waiting for 'virtual' (can be omitted)
|
|
Packit |
1c1d7e |
if tokenId == 'virtual':
|
|
Packit |
1c1d7e |
prototype = tokenStr # but not to unified prototype
|
|
Packit |
1c1d7e |
status = 3
|
|
Packit |
1c1d7e |
elif tokenId == 'id': # 'virtual' was omitted
|
|
Packit |
1c1d7e |
prototype = tokenStr
|
|
Packit |
1c1d7e |
uniPrototype = tokenStr # start collecting the unified prototype
|
|
Packit |
1c1d7e |
status = 4
|
|
Packit |
1c1d7e |
elif tokenId == 'comment':
|
|
Packit |
1c1d7e |
pass
|
|
Packit |
1c1d7e |
elif tokenId == 'protected' or tokenId == 'private':
|
|
Packit |
1c1d7e |
status = 0
|
|
Packit |
1c1d7e |
elif tokenId == 'rcurly':
|
|
Packit |
1c1d7e |
status = 11 # expected end of class
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 3: # return type of the method expected
|
|
Packit |
1c1d7e |
if tokenId == 'id':
|
|
Packit |
1c1d7e |
prototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
uniPrototype = tokenStr # start collecting the unified prototype
|
|
Packit |
1c1d7e |
status = 4
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 4: # method identifier expected
|
|
Packit |
1c1d7e |
if tokenId == 'id':
|
|
Packit |
1c1d7e |
prototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
methodId = tokenStr # for reporting
|
|
Packit |
1c1d7e |
status = 5
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 5: # left bracket of the argument list expected
|
|
Packit |
1c1d7e |
if tokenId == 'lpar':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 6
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 6: # collecting arguments of the method
|
|
Packit |
1c1d7e |
if tokenId == 'rpar':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 7
|
|
Packit |
1c1d7e |
elif tokenId == 'const':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 12
|
|
Packit |
1c1d7e |
elif tokenId == 'id': # type identifier
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 13
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 7: # left curly brace expected
|
|
Packit |
1c1d7e |
if tokenId == 'lcurly':
|
|
Packit |
1c1d7e |
curlyCnt = 1 # method body entered
|
|
Packit |
1c1d7e |
status = 10
|
|
Packit |
1c1d7e |
elif tokenId == 'comment':
|
|
Packit |
1c1d7e |
pass
|
|
Packit |
1c1d7e |
elif tokenId == 'assign': # allowed only for TranslatorAdapterBase
|
|
Packit |
1c1d7e |
assert(self.classId == 'TranslatorAdapterBase')
|
|
Packit |
1c1d7e |
status = 8
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 8: # zero expected (TranslatorAdapterBase)
|
|
Packit |
1c1d7e |
assert(self.classId == 'TranslatorAdapterBase')
|
|
Packit |
1c1d7e |
if tokenId == 'num' and tokenStr == '0':
|
|
Packit |
1c1d7e |
status = 9
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 9: # after semicolon (TranslatorAdapterBase)
|
|
Packit |
1c1d7e |
assert(self.classId == 'TranslatorAdapterBase')
|
|
Packit |
1c1d7e |
if tokenId == 'semic':
|
|
Packit |
1c1d7e |
status = 2
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 10: # consuming the body of the method, then dic item
|
|
Packit |
1c1d7e |
if tokenId == 'rcurly':
|
|
Packit |
1c1d7e |
curlyCnt -= 1
|
|
Packit |
1c1d7e |
if curlyCnt == 0:
|
|
Packit |
1c1d7e |
# Check for possible copy/paste error when name
|
|
Packit |
1c1d7e |
# of the method was not corrected (i.e. the same
|
|
Packit |
1c1d7e |
# name already exists).
|
|
Packit |
1c1d7e |
if uniPrototype in self.prototypeDic:
|
|
Packit |
1c1d7e |
msg = "'%s' prototype found again (duplicity)\n"
|
|
Packit |
1c1d7e |
msg += "in '%s'.\n" % self.fname
|
|
Packit |
1c1d7e |
msg = msg % uniPrototype
|
|
Packit |
1c1d7e |
sys.stderr.write(msg)
|
|
Packit |
1c1d7e |
assert False
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
assert(uniPrototype not in self.prototypeDic)
|
|
Packit |
1c1d7e |
# Insert new dictionary item.
|
|
Packit |
1c1d7e |
self.prototypeDic[uniPrototype] = prototype
|
|
Packit |
1c1d7e |
status = 2 # body consumed
|
|
Packit |
1c1d7e |
methodId = None # outside of any method
|
|
Packit |
1c1d7e |
elif tokenId == 'lcurly':
|
|
Packit |
1c1d7e |
curlyCnt += 1
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Warn in special case.
|
|
Packit |
1c1d7e |
elif methodId == 'trLegendDocs' and tokenId == 'string' \
|
|
Packit |
1c1d7e |
and tokenStr.find('MAX_DOT_GRAPH_HEIGHT') >= 0:
|
|
Packit |
1c1d7e |
self.txtMAX_DOT_GRAPH_HEIGHT_flag = True
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 11: # probably the end of class
|
|
Packit |
1c1d7e |
if tokenId == 'semic':
|
|
Packit |
1c1d7e |
status = 777
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 12: # type id for argument expected
|
|
Packit |
1c1d7e |
if tokenId == 'id':
|
|
Packit |
1c1d7e |
prototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
status = 13
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 13: # :: or * or & or id or ) expected
|
|
Packit |
1c1d7e |
if tokenId == 'colon': # was namespace id
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 14
|
|
Packit |
1c1d7e |
elif tokenId == 'star' or tokenId == 'amp': # pointer or reference
|
|
Packit |
1c1d7e |
prototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
status = 16
|
|
Packit |
1c1d7e |
elif tokenId == 'id': # argument identifier
|
|
Packit |
1c1d7e |
prototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
# don't put this into unified prototype
|
|
Packit |
1c1d7e |
status = 17
|
|
Packit |
1c1d7e |
elif tokenId == 'comment': # probably commented-out identifier
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
elif tokenId == 'rpar':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 7
|
|
Packit |
1c1d7e |
elif tokenId == 'comma':
|
|
Packit |
1c1d7e |
prototype += ', '
|
|
Packit |
1c1d7e |
uniPrototype += ', '
|
|
Packit |
1c1d7e |
status = 6
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 14: # second colon for namespace:: expected
|
|
Packit |
1c1d7e |
if tokenId == 'colon':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 15
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 15: # type after namespace:: expected
|
|
Packit |
1c1d7e |
if tokenId == 'id':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 13
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 16: # argument identifier or ) expected
|
|
Packit |
1c1d7e |
if tokenId == 'id':
|
|
Packit |
1c1d7e |
prototype += ' ' + tokenStr
|
|
Packit |
1c1d7e |
# don't put this into unified prototype
|
|
Packit |
1c1d7e |
status = 17
|
|
Packit |
1c1d7e |
elif tokenId == 'rpar':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 7
|
|
Packit |
1c1d7e |
elif tokenId == 'comment':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
elif status == 17: # comma or ')' after argument identifier expected
|
|
Packit |
1c1d7e |
if tokenId == 'comma':
|
|
Packit |
1c1d7e |
prototype += ', '
|
|
Packit |
1c1d7e |
uniPrototype += ', '
|
|
Packit |
1c1d7e |
status = 6
|
|
Packit |
1c1d7e |
elif tokenId == 'rpar':
|
|
Packit |
1c1d7e |
prototype += tokenStr
|
|
Packit |
1c1d7e |
uniPrototype += tokenStr
|
|
Packit |
1c1d7e |
status = 7
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.__unexpectedToken(status, tokenId, tokenLineNo)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def collectAdapterPrototypes(self):
|
|
Packit |
1c1d7e |
"""Returns the dictionary of prototypes implemented by adapters.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
It is created to process the translator_adapter.h. The returned
|
|
Packit |
1c1d7e |
dictionary has the form: unifiedPrototype -> (version, classId)
|
|
Packit |
1c1d7e |
thus by looking for the prototype, we get the information what is
|
|
Packit |
1c1d7e |
the newest (least adapting) adapter that is sufficient for
|
|
Packit |
1c1d7e |
implementing the method."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Start the token generator which parses the class source file.
|
|
Packit |
1c1d7e |
assert(os.path.split(self.fname)[1] == 'translator_adapter.h')
|
|
Packit |
1c1d7e |
tokenIterator = self.__tokenGenerator()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Get the references to the involved dictionaries.
|
|
Packit |
1c1d7e |
reqDic = self.manager.requiredMethodsDic
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Create the empty dictionary that will be returned.
|
|
Packit |
1c1d7e |
adaptDic = {}
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Loop through the source of the adapter file until no other adapter
|
|
Packit |
1c1d7e |
# class is found.
|
|
Packit |
1c1d7e |
while True:
|
|
Packit |
1c1d7e |
try:
|
|
Packit |
1c1d7e |
# Collect the class and the base class identifiers.
|
|
Packit |
1c1d7e |
self.__collectClassInfo(tokenIterator)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Extract the comparable version of the adapter class.
|
|
Packit |
1c1d7e |
# Note: The self.status as set by self.__collectClassInfo()
|
|
Packit |
1c1d7e |
# contains similar version, but is related to the base class,
|
|
Packit |
1c1d7e |
# not to the class itself.
|
|
Packit |
1c1d7e |
lst = self.classId.split('_')
|
|
Packit |
1c1d7e |
version = ''
|
|
Packit |
1c1d7e |
if lst[0] == 'TranslatorAdapter': # TranslatorAdapterBase otherwise
|
|
Packit |
1c1d7e |
version = lst[1] + '.' + lst[2]
|
|
Packit |
1c1d7e |
if len(lst) > 3: # add the last part of the number
|
|
Packit |
1c1d7e |
version += '.' + ('%02d' % int(lst[3]))
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
version += '.00'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Collect the prototypes of implemented public methods.
|
|
Packit |
1c1d7e |
self.__collectPublicMethodPrototypes(tokenIterator)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# For the required methods, update the dictionary of methods
|
|
Packit |
1c1d7e |
# implemented by the adapter.
|
|
Packit |
1c1d7e |
for protoUni in self.prototypeDic:
|
|
Packit |
1c1d7e |
if protoUni in reqDic:
|
|
Packit |
1c1d7e |
# This required method will be marked as implemented
|
|
Packit |
1c1d7e |
# by this adapter class. This implementation assumes
|
|
Packit |
1c1d7e |
# that newer adapters do not reimplement any required
|
|
Packit |
1c1d7e |
# methods already implemented by older adapters.
|
|
Packit |
1c1d7e |
assert(protoUni not in adaptDic)
|
|
Packit |
1c1d7e |
adaptDic[protoUni] = (version, self.classId)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Clear the dictionary object and the information related
|
|
Packit |
1c1d7e |
# to the class as the next adapter class is to be processed.
|
|
Packit |
1c1d7e |
self.prototypeDic.clear()
|
|
Packit |
1c1d7e |
self.classId = None
|
|
Packit |
1c1d7e |
self.baseClassId = None
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
except StopIteration:
|
|
Packit |
1c1d7e |
break
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Return the result dictionary.
|
|
Packit |
1c1d7e |
return adaptDic
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def processing(self):
|
|
Packit |
1c1d7e |
"""Processing of the source file -- only for TranslatorXxxx classes."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Start the token generator which parses the class source file.
|
|
Packit |
1c1d7e |
tokenIterator = self.__tokenGenerator()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Collect the class and the base class identifiers.
|
|
Packit |
1c1d7e |
self.__collectClassInfo(tokenIterator)
|
|
Packit |
1c1d7e |
assert(self.classId != 'Translator')
|
|
Packit |
1c1d7e |
assert(self.classId[:17] != 'TranslatorAdapter')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Collect the prototypes of implemented public methods.
|
|
Packit |
1c1d7e |
self.__collectPublicMethodPrototypes(tokenIterator)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Eat the rest of the source to cause closing the file.
|
|
Packit |
1c1d7e |
while True:
|
|
Packit |
1c1d7e |
try:
|
|
Packit |
1c1d7e |
t = next(tokenIterator)
|
|
Packit |
1c1d7e |
except StopIteration:
|
|
Packit |
1c1d7e |
break
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Shorthands for the used dictionaries.
|
|
Packit |
1c1d7e |
reqDic = self.manager.requiredMethodsDic
|
|
Packit |
1c1d7e |
adaptDic = self.manager.adaptMethodsDic
|
|
Packit |
1c1d7e |
myDic = self.prototypeDic
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Build the list of obsolete methods.
|
|
Packit |
1c1d7e |
self.obsoleteMethods = []
|
|
Packit |
1c1d7e |
for p in myDic:
|
|
Packit |
1c1d7e |
if p not in reqDic:
|
|
Packit |
1c1d7e |
self.obsoleteMethods.append(p)
|
|
Packit |
1c1d7e |
self.obsoleteMethods.sort()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Build the list of missing methods and the list of implemented
|
|
Packit |
1c1d7e |
# required methods.
|
|
Packit |
1c1d7e |
self.missingMethods = []
|
|
Packit |
1c1d7e |
self.implementedMethods = []
|
|
Packit |
1c1d7e |
for p in reqDic:
|
|
Packit |
1c1d7e |
if p in myDic:
|
|
Packit |
1c1d7e |
self.implementedMethods.append(p)
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
self.missingMethods.append(p)
|
|
Packit |
1c1d7e |
self.missingMethods.sort()
|
|
Packit |
1c1d7e |
self.implementedMethods.sort()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Check whether adapter must be used or suggest the newest one.
|
|
Packit |
1c1d7e |
# Change the status and set the note accordingly.
|
|
Packit |
1c1d7e |
if self.baseClassId != 'Translator':
|
|
Packit |
1c1d7e |
if not self.missingMethods:
|
|
Packit |
1c1d7e |
self.note = 'Change the base class to Translator.'
|
|
Packit |
1c1d7e |
self.status = ''
|
|
Packit |
1c1d7e |
self.readableStatus = 'almost up-to-date'
|
|
Packit |
1c1d7e |
elif self.baseClassId != 'TranslatorEnglish':
|
|
Packit |
1c1d7e |
# The translator uses some of the adapters.
|
|
Packit |
1c1d7e |
# Look at the missing methods and check what adapter
|
|
Packit |
1c1d7e |
# implements them. Remember the one with the lowest version.
|
|
Packit |
1c1d7e |
adaptMinVersion = '9.9.99'
|
|
Packit |
1c1d7e |
adaptMinClass = 'TranslatorAdapter_9_9_99'
|
|
Packit |
1c1d7e |
for uniProto in self.missingMethods:
|
|
Packit |
1c1d7e |
if uniProto in adaptDic:
|
|
Packit |
1c1d7e |
version, cls = adaptDic[uniProto]
|
|
Packit |
1c1d7e |
if version < adaptMinVersion:
|
|
Packit |
1c1d7e |
adaptMinVersion = version
|
|
Packit |
1c1d7e |
adaptMinClass = cls
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Test against the current status -- preserve the self.status.
|
|
Packit |
1c1d7e |
# Possibly, the translator implements enough methods to
|
|
Packit |
1c1d7e |
# use some newer adapter.
|
|
Packit |
1c1d7e |
status = self.status
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# If the version of the used adapter is smaller than
|
|
Packit |
1c1d7e |
# the required, set the note and update the status as if
|
|
Packit |
1c1d7e |
# the newer adapter was used.
|
|
Packit |
1c1d7e |
if adaptMinVersion > status:
|
|
Packit |
1c1d7e |
self.note = 'Change the base class to %s.' % adaptMinClass
|
|
Packit |
1c1d7e |
self.status = adaptMinVersion
|
|
Packit |
1c1d7e |
self.adaptMinClass = adaptMinClass
|
|
Packit |
1c1d7e |
self.readableStatus = adaptMinVersion # simplified
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# If everything seems OK, some explicit warning flags still could
|
|
Packit |
1c1d7e |
# be set.
|
|
Packit |
1c1d7e |
if not self.note and self.status == '' and \
|
|
Packit |
1c1d7e |
(self.translateMeFlag or self.txtMAX_DOT_GRAPH_HEIGHT_flag):
|
|
Packit |
1c1d7e |
self.note = ''
|
|
Packit |
1c1d7e |
if self.translateMeFlag:
|
|
Packit |
1c1d7e |
self.note += 'The "%s" found in a comment.' % self.translateMeText
|
|
Packit |
1c1d7e |
if self.note != '':
|
|
Packit |
1c1d7e |
self.note += '\n\t\t'
|
|
Packit |
1c1d7e |
if self.txtMAX_DOT_GRAPH_HEIGHT_flag:
|
|
Packit |
1c1d7e |
self.note += 'The MAX_DOT_GRAPH_HEIGHT found in trLegendDocs()'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# If everything seems OK, but there are obsolete methods, set
|
|
Packit |
1c1d7e |
# the note to clean-up source. This note will be used only when
|
|
Packit |
1c1d7e |
# the previous code did not set another note (priority).
|
|
Packit |
1c1d7e |
if not self.note and self.status == '' and self.obsoleteMethods:
|
|
Packit |
1c1d7e |
self.note = 'Remove the obsolete methods (never used).'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# If there is at least some note but the status suggests it is
|
|
Packit |
1c1d7e |
# otherwise up-to-date, mark is as ALMOST up-to-date.
|
|
Packit |
1c1d7e |
if self.note and self.status == '':
|
|
Packit |
1c1d7e |
self.readableStatus = 'almost up-to-date'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def report(self, fout):
|
|
Packit |
1c1d7e |
"""Returns the report part for the source as a multiline string.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
No output for up-to-date translators without problem."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# If there is nothing to report, return immediately.
|
|
Packit |
1c1d7e |
if self.status == '' and not self.note:
|
|
Packit |
1c1d7e |
return
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Report the number of not implemented methods.
|
|
Packit |
1c1d7e |
fout.write('\n\n\n')
|
|
Packit |
1c1d7e |
fout.write(self.classId + ' (' + self.baseClassId + ')')
|
|
Packit |
1c1d7e |
percentImplemented = 100 # init
|
|
Packit |
1c1d7e |
allNum = len(self.manager.requiredMethodsDic)
|
|
Packit |
1c1d7e |
if self.missingMethods:
|
|
Packit |
1c1d7e |
num = len(self.missingMethods)
|
|
Packit |
1c1d7e |
percentImplemented = 100 * (allNum - num) / allNum
|
|
Packit |
1c1d7e |
fout.write(' %d' % num)
|
|
Packit |
1c1d7e |
fout.write(' method')
|
|
Packit |
1c1d7e |
if num > 1:
|
|
Packit |
1c1d7e |
fout.write('s')
|
|
Packit |
1c1d7e |
fout.write(' to implement (%d %%)' % (100 * num / allNum))
|
|
Packit |
1c1d7e |
fout.write('\n' + '-' * len(self.classId))
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Write the info about the implemented required methods.
|
|
Packit |
1c1d7e |
fout.write('\n\n Implements %d' % len(self.implementedMethods))
|
|
Packit |
1c1d7e |
fout.write(' of the required methods (%d %%).' % percentImplemented)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Report the missing method, but only when it is not English-based
|
|
Packit |
1c1d7e |
# translator.
|
|
Packit |
1c1d7e |
if self.missingMethods and self.status != 'En':
|
|
Packit |
1c1d7e |
fout.write('\n\n Missing methods (should be implemented):\n')
|
|
Packit |
1c1d7e |
reqDic = self.manager.requiredMethodsDic
|
|
Packit |
1c1d7e |
for p in self.missingMethods:
|
|
Packit |
1c1d7e |
fout.write('\n ' + reqDic[p])
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Always report obsolete methods.
|
|
Packit |
1c1d7e |
if self.obsoleteMethods:
|
|
Packit |
1c1d7e |
fout.write('\n\n Obsolete methods (should be removed, never used):\n')
|
|
Packit |
1c1d7e |
myDic = self.prototypeDic
|
|
Packit |
1c1d7e |
for p in self.obsoleteMethods:
|
|
Packit |
1c1d7e |
fout.write('\n ' + myDic[p])
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# For English-based translator, report the implemented methods.
|
|
Packit |
1c1d7e |
if self.status == 'En' and self.implementedMethods:
|
|
Packit |
1c1d7e |
fout.write('\n\n This English-based translator implements ')
|
|
Packit |
1c1d7e |
fout.write('the following methods:\n')
|
|
Packit |
1c1d7e |
reqDic = self.manager.requiredMethodsDic
|
|
Packit |
1c1d7e |
for p in self.implementedMethods:
|
|
Packit |
1c1d7e |
fout.write('\n ' + reqDic[p])
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def getmtime(self):
|
|
Packit |
1c1d7e |
"""Returns the last modification time of the source file."""
|
|
Packit |
1c1d7e |
assert(os.path.isfile(self.fname))
|
|
Packit |
1c1d7e |
return os.path.getmtime(self.fname)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
class TrManager:
|
|
Packit |
1c1d7e |
"""Collects basic info and builds subordinate Transl objects."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __init__(self):
|
|
Packit |
1c1d7e |
"""Determines paths, creates and initializes structures.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
The arguments of the script may explicitly say what languages should
|
|
Packit |
1c1d7e |
be processed. Write the two letter identifications that are used
|
|
Packit |
1c1d7e |
for composing the source filenames, so...
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
python translator.py cz
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
this will process only translator_cz.h source.
|
|
Packit |
1c1d7e |
"""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Determine the path to the script and its name.
|
|
Packit |
1c1d7e |
self.script = os.path.abspath(sys.argv[0])
|
|
Packit |
1c1d7e |
self.script_path, self.script_name = os.path.split(self.script)
|
|
Packit |
1c1d7e |
self.script_path = os.path.abspath(self.script_path)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Determine the absolute path to the Doxygen's root subdirectory.
|
|
Packit |
1c1d7e |
# If DOXYGEN environment variable is not found, the directory is
|
|
Packit |
1c1d7e |
# determined from the path of the script.
|
|
Packit |
1c1d7e |
doxy_default = os.path.join(self.script_path, '..')
|
|
Packit |
1c1d7e |
self.doxy_path = os.path.abspath(os.getenv('DOXYGEN', doxy_default))
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Get the explicit arguments of the script.
|
|
Packit |
1c1d7e |
self.script_argLst = sys.argv[1:]
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Build the path names based on the Doxygen's root knowledge.
|
|
Packit |
1c1d7e |
self.doc_path = os.path.join(self.doxy_path, 'doc')
|
|
Packit |
1c1d7e |
self.src_path = os.path.join(self.doxy_path, 'src')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Create the empty dictionary for Transl object identified by the
|
|
Packit |
1c1d7e |
# class identifier of the translator.
|
|
Packit |
1c1d7e |
self.__translDic = {}
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Create the None dictionary of required methods. The key is the
|
|
Packit |
1c1d7e |
# unified prototype, the value is the full prototype. Set inside
|
|
Packit |
1c1d7e |
# the self.__build().
|
|
Packit |
1c1d7e |
self.requiredMethodsDic = None
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Create the empty dictionary that says what method is implemented
|
|
Packit |
1c1d7e |
# by what adapter.
|
|
Packit |
1c1d7e |
self.adaptMethodsDic = {}
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The last modification time will capture the modification of this
|
|
Packit |
1c1d7e |
# script, of the translator.h, of the translator_adapter.h (see the
|
|
Packit |
1c1d7e |
# self.__build() for the last two) of all the translator_xx.h files
|
|
Packit |
1c1d7e |
# and of the template for generating the documentation. So, this
|
|
Packit |
1c1d7e |
# time can be compared with modification time of the generated
|
|
Packit |
1c1d7e |
# documentation to decide, whether the doc should be re-generated.
|
|
Packit |
1c1d7e |
self.lastModificationTime = os.path.getmtime(self.script)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Set the names of the translator report text file, of the template
|
|
Packit |
1c1d7e |
# for generating "Internationalization" document, for the generated
|
|
Packit |
1c1d7e |
# file itself, and for the maintainers list.
|
|
Packit |
1c1d7e |
self.translatorReportFileName = 'translator_report.txt'
|
|
Packit |
1c1d7e |
self.maintainersFileName = 'maintainers.txt'
|
|
Packit |
1c1d7e |
self.languageTplFileName = 'language.tpl'
|
|
Packit |
1c1d7e |
self.languageDocFileName = 'language.doc'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The information about the maintainers will be stored
|
|
Packit |
1c1d7e |
# in the dictionary with the following name.
|
|
Packit |
1c1d7e |
self.__maintainersDic = None
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Define the other used structures and variables for information.
|
|
Packit |
1c1d7e |
self.langLst = None # including English based
|
|
Packit |
1c1d7e |
self.supportedLangReadableStr = None # coupled En-based as a note
|
|
Packit |
1c1d7e |
self.numLang = None # excluding coupled En-based
|
|
Packit |
1c1d7e |
self.doxVersion = None # Doxygen version
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Build objects where each one is responsible for one translator.
|
|
Packit |
1c1d7e |
self.__build()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __build(self):
|
|
Packit |
1c1d7e |
"""Find the translator files and build the objects for translators."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The translator.h must exist (the Transl object will check it),
|
|
Packit |
1c1d7e |
# create the object for it and let it build the dictionary of
|
|
Packit |
1c1d7e |
# required methods.
|
|
Packit |
1c1d7e |
tr = Transl(os.path.join(self.src_path, 'translator.h'), self)
|
|
Packit |
1c1d7e |
self.requiredMethodsDic = tr.collectPureVirtualPrototypes()
|
|
Packit |
1c1d7e |
tim = tr.getmtime()
|
|
Packit |
1c1d7e |
if tim > self.lastModificationTime:
|
|
Packit |
1c1d7e |
self.lastModificationTime = tim
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The translator_adapter.h must exist (the Transl object will check it),
|
|
Packit |
1c1d7e |
# create the object for it and store the reference in the dictionary.
|
|
Packit |
1c1d7e |
tr = Transl(os.path.join(self.src_path, 'translator_adapter.h'), self)
|
|
Packit |
1c1d7e |
self.adaptMethodsDic = tr.collectAdapterPrototypes()
|
|
Packit |
1c1d7e |
tim = tr.getmtime()
|
|
Packit |
1c1d7e |
if tim > self.lastModificationTime:
|
|
Packit |
1c1d7e |
self.lastModificationTime = tim
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Create the list of the filenames with language translator sources.
|
|
Packit |
1c1d7e |
# If the explicit arguments of the script were typed, process only
|
|
Packit |
1c1d7e |
# those files.
|
|
Packit |
1c1d7e |
if self.script_argLst:
|
|
Packit |
1c1d7e |
lst = ['translator_' + x + '.h' for x in self.script_argLst]
|
|
Packit |
1c1d7e |
for fname in lst:
|
|
Packit |
1c1d7e |
if not os.path.isfile(os.path.join(self.src_path, fname)):
|
|
Packit |
1c1d7e |
sys.stderr.write("\a\nFile '%s' not found!\n" % fname)
|
|
Packit |
1c1d7e |
sys.exit(1)
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
lst = os.listdir(self.src_path)
|
|
Packit |
1c1d7e |
lst = [x for x in lst if x[:11] == 'translator_'
|
|
Packit |
1c1d7e |
and x[-2:] == '.h'
|
|
Packit |
1c1d7e |
and x != 'translator_adapter.h']
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Build the object for the translator_xx.h files, and process the
|
|
Packit |
1c1d7e |
# content of the file. Then insert the object to the dictionary
|
|
Packit |
1c1d7e |
# accessed via classId.
|
|
Packit |
1c1d7e |
for fname in lst:
|
|
Packit |
1c1d7e |
fullname = os.path.join(self.src_path, fname)
|
|
Packit |
1c1d7e |
tr = Transl(fullname, self)
|
|
Packit |
1c1d7e |
tr.processing()
|
|
Packit |
1c1d7e |
assert(tr.classId != 'Translator')
|
|
Packit |
1c1d7e |
self.__translDic[tr.classId] = tr
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Extract the global information of the processed info.
|
|
Packit |
1c1d7e |
self.__extractProcessedInfo()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __extractProcessedInfo(self):
|
|
Packit |
1c1d7e |
"""Build lists and strings of the processed info."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Build the auxiliary list with strings compound of the status,
|
|
Packit |
1c1d7e |
# readable form of the language, and classId.
|
|
Packit |
1c1d7e |
statLst = []
|
|
Packit |
1c1d7e |
for obj in list(self.__translDic.values()):
|
|
Packit |
1c1d7e |
assert(obj.classId != 'Translator')
|
|
Packit |
1c1d7e |
s = obj.status + '|' + obj.langReadable + '|' + obj.classId
|
|
Packit |
1c1d7e |
statLst.append(s)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Sort the list and extract the object identifiers (classId's) for
|
|
Packit |
1c1d7e |
# the up-to-date translators and English-based translators.
|
|
Packit |
1c1d7e |
statLst.sort()
|
|
Packit |
1c1d7e |
self.upToDateIdLst = [x.split('|')[2] for x in statLst if x[0] == '|']
|
|
Packit |
1c1d7e |
self.EnBasedIdLst = [x.split('|')[2] for x in statLst if x[:2] == 'En']
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Reverse the list and extract the TranslatorAdapter based translators.
|
|
Packit |
1c1d7e |
statLst.reverse()
|
|
Packit |
1c1d7e |
self.adaptIdLst = [x.split('|')[2] for x in statLst if x[0].isdigit()]
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Build the list of tuples that contain (langReadable, obj).
|
|
Packit |
1c1d7e |
# Sort it by readable name.
|
|
Packit |
1c1d7e |
self.langLst = []
|
|
Packit |
1c1d7e |
for obj in list(self.__translDic.values()):
|
|
Packit |
1c1d7e |
self.langLst.append((obj.langReadable, obj))
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
self.langLst.sort(key=lambda x: x[0])
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Create the list with readable language names. If the language has
|
|
Packit |
1c1d7e |
# also the English-based version, modify the item by appending
|
|
Packit |
1c1d7e |
# the note. Number of the supported languages is equal to the length
|
|
Packit |
1c1d7e |
# of the list.
|
|
Packit |
1c1d7e |
langReadableLst = []
|
|
Packit |
1c1d7e |
for name, obj in self.langLst:
|
|
Packit |
1c1d7e |
if obj.status == 'En': continue
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Append the 'En' to the classId to possibly obtain the classId
|
|
Packit |
1c1d7e |
# of the English-based object. If the object exists, modify the
|
|
Packit |
1c1d7e |
# name for the readable list of supported languages.
|
|
Packit |
1c1d7e |
classIdEn = obj.classId + 'En'
|
|
Packit |
1c1d7e |
if classIdEn in self.__translDic:
|
|
Packit |
1c1d7e |
name += ' (+En)'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Append the result name of the language, possibly with note.
|
|
Packit |
1c1d7e |
langReadableLst.append(name)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Create the multiline string of readable language names,
|
|
Packit |
1c1d7e |
# with punctuation, wrapped to paragraph.
|
|
Packit |
1c1d7e |
if len(langReadableLst) == 1:
|
|
Packit |
1c1d7e |
s = langReadableLst[0]
|
|
Packit |
1c1d7e |
elif len(langReadableLst) == 2:
|
|
Packit |
1c1d7e |
s = ' and '.join(langReadableLst)
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
s = ', '.join(langReadableLst[:-1]) + ', and '
|
|
Packit |
1c1d7e |
s += langReadableLst[-1]
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
self.supportedLangReadableStr = fill(s + '.')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Find the number of the supported languages. The English based
|
|
Packit |
1c1d7e |
# languages are not counted if the non-English based also exists.
|
|
Packit |
1c1d7e |
self.numLang = len(self.langLst)
|
|
Packit |
1c1d7e |
for name, obj in self.langLst:
|
|
Packit |
1c1d7e |
if obj.status == 'En':
|
|
Packit |
1c1d7e |
classId = obj.classId[:-2]
|
|
Packit |
1c1d7e |
if classId in self.__translDic:
|
|
Packit |
1c1d7e |
self.numLang -= 1 # the couple will be counted as one
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Extract the version of Doxygen.
|
|
Packit |
1c1d7e |
f = xopen(os.path.join(self.doxy_path, 'VERSION'))
|
|
Packit |
1c1d7e |
self.doxVersion = f.readline().strip()
|
|
Packit |
1c1d7e |
f.close()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Update the last modification time.
|
|
Packit |
1c1d7e |
for tr in list(self.__translDic.values()):
|
|
Packit |
1c1d7e |
tim = tr.getmtime()
|
|
Packit |
1c1d7e |
if tim > self.lastModificationTime:
|
|
Packit |
1c1d7e |
self.lastModificationTime = tim
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __getNoTrSourceFilesLst(self):
|
|
Packit |
1c1d7e |
"""Returns the list of sources to be checked.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
All .cpp files and also .h files that do not declare or define
|
|
Packit |
1c1d7e |
the translator methods are included in the list. The file names
|
|
Packit |
1c1d7e |
are searched in doxygen/src directory.
|
|
Packit |
1c1d7e |
"""
|
|
Packit |
1c1d7e |
files = []
|
|
Packit |
1c1d7e |
for item in os.listdir(self.src_path):
|
|
Packit |
1c1d7e |
# Split the bare name to get the extension.
|
|
Packit |
1c1d7e |
name, ext = os.path.splitext(item)
|
|
Packit |
1c1d7e |
ext = ext.lower()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Include only .cpp and .h files (case independent) and exclude
|
|
Packit |
1c1d7e |
# the files where the checked identifiers are defined.
|
|
Packit |
1c1d7e |
if ext == '.cpp' or (ext == '.h' and name.find('translator') == -1):
|
|
Packit |
1c1d7e |
fname = os.path.join(self.src_path, item)
|
|
Packit |
1c1d7e |
assert os.path.isfile(fname) # assumes no directory with the ext
|
|
Packit |
1c1d7e |
files.append(fname) # full name
|
|
Packit |
1c1d7e |
return files
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __removeUsedInFiles(self, fname, dic):
|
|
Packit |
1c1d7e |
"""Removes items for method identifiers that are found in fname.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
The method reads the content of the file as one string and searches
|
|
Packit |
1c1d7e |
for all identifiers from dic. The identifiers that were found in
|
|
Packit |
1c1d7e |
the file are removed from the dictionary.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
Note: If more files is to be checked, the files where most items are
|
|
Packit |
1c1d7e |
probably used should be checked first and the resulting reduced
|
|
Packit |
1c1d7e |
dictionary should be used for checking the next files (speed up).
|
|
Packit |
1c1d7e |
"""
|
|
Packit |
1c1d7e |
lst_in = list(dic.keys()) # identifiers to be searched for
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Read content of the file as one string.
|
|
Packit |
1c1d7e |
assert os.path.isfile(fname)
|
|
Packit |
1c1d7e |
f = xopen(fname)
|
|
Packit |
1c1d7e |
cont = f.read()
|
|
Packit |
1c1d7e |
f.close()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Remove the items for identifiers that were found in the file.
|
|
Packit |
1c1d7e |
while lst_in:
|
|
Packit |
1c1d7e |
item = lst_in.pop(0)
|
|
Packit |
1c1d7e |
if cont.find(item) != -1:
|
|
Packit |
1c1d7e |
del dic[item]
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __checkForNotUsedTrMethods(self):
|
|
Packit |
1c1d7e |
"""Returns the dictionary of not used translator methods.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
The method can be called only after self.requiredMethodsDic has been
|
|
Packit |
1c1d7e |
built. The stripped prototypes are the values, the method identifiers
|
|
Packit |
1c1d7e |
are the keys.
|
|
Packit |
1c1d7e |
"""
|
|
Packit |
1c1d7e |
# Build the dictionary of the required method prototypes with
|
|
Packit |
1c1d7e |
# method identifiers used as keys.
|
|
Packit |
1c1d7e |
trdic = {}
|
|
Packit |
1c1d7e |
for prototype in list(self.requiredMethodsDic.keys()):
|
|
Packit |
1c1d7e |
ri = prototype.split('(')[0]
|
|
Packit |
1c1d7e |
identifier = ri.split()[1].strip()
|
|
Packit |
1c1d7e |
trdic[identifier] = prototype
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Build the list of source files where translator method identifiers
|
|
Packit |
1c1d7e |
# can be used.
|
|
Packit |
1c1d7e |
files = self.__getNoTrSourceFilesLst()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Loop through the files and reduce the dictionary of id -> proto.
|
|
Packit |
1c1d7e |
for fname in files:
|
|
Packit |
1c1d7e |
self.__removeUsedInFiles(fname, trdic)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Return the dictionary of not used translator methods.
|
|
Packit |
1c1d7e |
return trdic
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __emails(self, classId):
|
|
Packit |
1c1d7e |
"""Returns the list of maintainer emails.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
The method returns the list of e-mail addresses for the translator
|
|
Packit |
1c1d7e |
class, but only the addresses that were not marked as [xxx]."""
|
|
Packit |
1c1d7e |
lst = []
|
|
Packit |
1c1d7e |
for m in self.__maintainersDic[classId]:
|
|
Packit |
1c1d7e |
if not m[1].startswith('['):
|
|
Packit |
1c1d7e |
email = m[1]
|
|
Packit |
1c1d7e |
email = email.replace(' at ', '@') # Unmangle the mangled e-mail
|
|
Packit |
1c1d7e |
email = email.replace(' dot ', '.')
|
|
Packit |
1c1d7e |
lst.append(email)
|
|
Packit |
1c1d7e |
return lst
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def getBgcolorByReadableStatus(self, readableStatus):
|
|
Packit |
1c1d7e |
if readableStatus == 'up-to-date':
|
|
Packit |
1c1d7e |
color = '#ccffcc' # green
|
|
Packit |
1c1d7e |
elif readableStatus.startswith('almost'):
|
|
Packit |
1c1d7e |
color = '#ffffff' # white
|
|
Packit |
1c1d7e |
elif readableStatus.startswith('English'):
|
|
Packit |
1c1d7e |
color = '#ccffcc' # green
|
|
Packit |
1c1d7e |
elif readableStatus.startswith('1.8'):
|
|
Packit |
1c1d7e |
color = '#ffffcc' # yellow
|
|
Packit |
1c1d7e |
elif readableStatus.startswith('1.7'):
|
|
Packit |
1c1d7e |
color = '#ffcccc' # pink
|
|
Packit |
1c1d7e |
elif readableStatus.startswith('1.6'):
|
|
Packit |
1c1d7e |
color = '#ffcccc' # pink
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
color = '#ff5555' # red
|
|
Packit |
1c1d7e |
return color
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def generateTranslatorReport(self):
|
|
Packit |
1c1d7e |
"""Generates the translator report."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
output = os.path.join(self.doc_path, self.translatorReportFileName)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Open the textual report file for the output.
|
|
Packit |
1c1d7e |
f = xopen(output, 'w')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Output the information about the version.
|
|
Packit |
1c1d7e |
f.write('(' + self.doxVersion + ')\n\n')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Output the information about the number of the supported languages
|
|
Packit |
1c1d7e |
# and the list of the languages, or only the note about the explicitly
|
|
Packit |
1c1d7e |
# given languages to process.
|
|
Packit |
1c1d7e |
if self.script_argLst:
|
|
Packit |
1c1d7e |
f.write('The report was generated for the following, explicitly')
|
|
Packit |
1c1d7e |
f.write(' identified languages:\n\n')
|
|
Packit |
1c1d7e |
f.write(self.supportedLangReadableStr + '\n\n')
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
f.write('Doxygen supports the following ')
|
|
Packit |
1c1d7e |
f.write(str(self.numLang))
|
|
Packit |
1c1d7e |
f.write(' languages (sorted alphabetically):\n\n')
|
|
Packit |
1c1d7e |
f.write(self.supportedLangReadableStr + '\n\n')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Write the summary about the status of language translators (how
|
|
Packit |
1c1d7e |
# many translators) are up-to-date, etc.
|
|
Packit |
1c1d7e |
s = 'Of them, %d translators are up-to-date, ' % len(self.upToDateIdLst)
|
|
Packit |
1c1d7e |
s += '%d translators are based on some adapter class, ' % len(self.adaptIdLst)
|
|
Packit |
1c1d7e |
s += 'and %d are English based.' % len(self.EnBasedIdLst)
|
|
Packit |
1c1d7e |
f.write(fill(s) + '\n\n')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The e-mail addresses of the maintainers will be collected to
|
|
Packit |
1c1d7e |
# the auxiliary file in the order of translator classes listed
|
|
Packit |
1c1d7e |
# in the translator report.
|
|
Packit |
1c1d7e |
fmail = xopen('mailto.txt', 'w')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Write the list of "up-to-date" translator classes.
|
|
Packit |
1c1d7e |
if self.upToDateIdLst:
|
|
Packit |
1c1d7e |
s = '''The following translator classes are up-to-date (sorted
|
|
Packit |
1c1d7e |
alphabetically). This means that they derive from the
|
|
Packit |
1c1d7e |
Translator class, they implement all %d of the required
|
|
Packit |
1c1d7e |
methods, and even minor problems were not spotted by the script:'''
|
|
Packit |
1c1d7e |
s = s % len(self.requiredMethodsDic)
|
|
Packit |
1c1d7e |
f.write('-' * 70 + '\n')
|
|
Packit |
1c1d7e |
f.write(fill(s) + '\n\n')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
mailtoLst = []
|
|
Packit |
1c1d7e |
for x in self.upToDateIdLst:
|
|
Packit |
1c1d7e |
obj = self.__translDic[x]
|
|
Packit |
1c1d7e |
if obj.note is None:
|
|
Packit |
1c1d7e |
f.write(' ' + obj.classId + '\n')
|
|
Packit |
1c1d7e |
mailtoLst.extend(self.__emails(obj.classId))
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
fmail.write('up-to-date\n')
|
|
Packit |
1c1d7e |
fmail.write('; '.join(mailtoLst))
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Write separately the list of "ALMOST up-to-date" translator classes.
|
|
Packit |
1c1d7e |
s = '''The following translator classes are ALMOST up-to-date (sorted
|
|
Packit |
1c1d7e |
alphabetically). This means that they derive from the
|
|
Packit |
1c1d7e |
Translator class, but there still may be some minor problems
|
|
Packit |
1c1d7e |
listed for them:'''
|
|
Packit |
1c1d7e |
f.write('\n' + ('-' * 70) + '\n')
|
|
Packit |
1c1d7e |
f.write(fill(s) + '\n\n')
|
|
Packit |
1c1d7e |
mailtoLst = []
|
|
Packit |
1c1d7e |
for x in self.upToDateIdLst:
|
|
Packit |
1c1d7e |
obj = self.__translDic[x]
|
|
Packit |
1c1d7e |
if obj.note is not None:
|
|
Packit |
1c1d7e |
f.write(' ' + obj.classId + '\t-- ' + obj.note + '\n')
|
|
Packit |
1c1d7e |
mailtoLst.extend(self.__emails(obj.classId))
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
fmail.write('\n\nalmost up-to-date\n')
|
|
Packit |
1c1d7e |
fmail.write('; '.join(mailtoLst))
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Write the list of the adapter based classes. The very obsolete
|
|
Packit |
1c1d7e |
# translators that derive from TranslatorEnglish are included.
|
|
Packit |
1c1d7e |
if self.adaptIdLst:
|
|
Packit |
1c1d7e |
s = '''The following translator classes need maintenance
|
|
Packit |
1c1d7e |
(the most obsolete at the end). The other info shows the
|
|
Packit |
1c1d7e |
estimation of Doxygen version when the class was last
|
|
Packit |
1c1d7e |
updated and number of methods that must be implemented to
|
|
Packit |
1c1d7e |
become up-to-date:'''
|
|
Packit |
1c1d7e |
f.write('\n' + '-' * 70 + '\n')
|
|
Packit |
1c1d7e |
f.write(fill(s) + '\n\n')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Find also whether some adapter classes may be removed.
|
|
Packit |
1c1d7e |
adaptMinVersion = '9.9.99'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
mailtoLst = []
|
|
Packit |
1c1d7e |
numRequired = len(self.requiredMethodsDic)
|
|
Packit |
1c1d7e |
for x in self.adaptIdLst:
|
|
Packit |
1c1d7e |
obj = self.__translDic[x]
|
|
Packit |
1c1d7e |
f.write(' %-30s' % obj.classId)
|
|
Packit |
1c1d7e |
f.write(' %-6s' % obj.readableStatus)
|
|
Packit |
1c1d7e |
numimpl = len(obj.missingMethods)
|
|
Packit |
1c1d7e |
pluralS = ''
|
|
Packit |
1c1d7e |
if numimpl > 1: pluralS = 's'
|
|
Packit |
1c1d7e |
percent = 100 * numimpl / numRequired
|
|
Packit |
1c1d7e |
f.write('\t%2d method%s to implement (%d %%)' % (
|
|
Packit |
1c1d7e |
numimpl, pluralS, percent))
|
|
Packit |
1c1d7e |
if obj.note:
|
|
Packit |
1c1d7e |
f.write('\n\tNote: ' + obj.note + '\n')
|
|
Packit |
1c1d7e |
f.write('\n')
|
|
Packit |
1c1d7e |
mailtoLst.extend(self.__emails(obj.classId)) # to maintainer
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Check the level of required adapter classes.
|
|
Packit |
1c1d7e |
if obj.status != '0.0.00' and obj.status < adaptMinVersion:
|
|
Packit |
1c1d7e |
adaptMinVersion = obj.status
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
fmail.write('\n\ntranslator based\n')
|
|
Packit |
1c1d7e |
fmail.write('; '.join(mailtoLst))
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Set the note if some old translator adapters are not needed
|
|
Packit |
1c1d7e |
# any more. Do it only when the script is called without arguments,
|
|
Packit |
1c1d7e |
# i.e. all languages were checked against the needed translator
|
|
Packit |
1c1d7e |
# adapters.
|
|
Packit |
1c1d7e |
if not self.script_argLst:
|
|
Packit |
1c1d7e |
to_remove = {}
|
|
Packit |
1c1d7e |
for version, adaptClassId in list(self.adaptMethodsDic.values()):
|
|
Packit |
1c1d7e |
if version < adaptMinVersion:
|
|
Packit |
1c1d7e |
to_remove[adaptClassId] = True
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
if to_remove:
|
|
Packit |
1c1d7e |
lst = list(to_remove.keys())
|
|
Packit |
1c1d7e |
lst.sort()
|
|
Packit |
1c1d7e |
plural = len(lst) > 1
|
|
Packit |
1c1d7e |
note = 'Note: The adapter class'
|
|
Packit |
1c1d7e |
if plural: note += 'es'
|
|
Packit |
1c1d7e |
note += ' ' + ', '.join(lst)
|
|
Packit |
1c1d7e |
if not plural:
|
|
Packit |
1c1d7e |
note += ' is'
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
note += ' are'
|
|
Packit |
1c1d7e |
note += ' not used and can be removed.'
|
|
Packit |
1c1d7e |
f.write('\n' + fill(note) + '\n')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Write the list of the English-based classes.
|
|
Packit |
1c1d7e |
if self.EnBasedIdLst:
|
|
Packit |
1c1d7e |
s = '''The following translator classes derive directly from the
|
|
Packit |
1c1d7e |
TranslatorEnglish. The class identifier has the suffix 'En'
|
|
Packit |
1c1d7e |
that says that this is intentional. Usually, there is also
|
|
Packit |
1c1d7e |
a non-English based version of the translator for
|
|
Packit |
1c1d7e |
the language:'''
|
|
Packit |
1c1d7e |
f.write('\n' + '-' * 70 + '\n')
|
|
Packit |
1c1d7e |
f.write(fill(s) + '\n\n')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
for x in self.EnBasedIdLst:
|
|
Packit |
1c1d7e |
obj = self.__translDic[x]
|
|
Packit |
1c1d7e |
f.write(' ' + obj.classId)
|
|
Packit |
1c1d7e |
f.write('\timplements %d methods' % len(obj.implementedMethods))
|
|
Packit |
1c1d7e |
if obj.note:
|
|
Packit |
1c1d7e |
f.write(' -- ' + obj.note)
|
|
Packit |
1c1d7e |
f.write('\n')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Check for not used translator methods and generate warning if found.
|
|
Packit |
1c1d7e |
# The check is rather time consuming, so it is not done when report
|
|
Packit |
1c1d7e |
# is restricted to explicitly given language identifiers.
|
|
Packit |
1c1d7e |
if not self.script_argLst:
|
|
Packit |
1c1d7e |
dic = self.__checkForNotUsedTrMethods()
|
|
Packit |
1c1d7e |
if dic:
|
|
Packit |
1c1d7e |
s = '''WARNING: The following translator methods are declared
|
|
Packit |
1c1d7e |
in the Translator class but their identifiers do not appear
|
|
Packit |
1c1d7e |
in source files. The situation should be checked. The .cpp
|
|
Packit |
1c1d7e |
files and .h files excluding the '*translator*' files
|
|
Packit |
1c1d7e |
in doxygen/src directory were simply searched for occurrence
|
|
Packit |
1c1d7e |
of the method identifiers:'''
|
|
Packit |
1c1d7e |
f.write('\n' + '=' * 70 + '\n')
|
|
Packit |
1c1d7e |
f.write(fill(s) + '\n\n')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
keys = list(dic.keys())
|
|
Packit |
1c1d7e |
keys.sort()
|
|
Packit |
1c1d7e |
for key in keys:
|
|
Packit |
1c1d7e |
f.write(' ' + dic[key] + '\n')
|
|
Packit |
1c1d7e |
f.write('\n')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Write the details for the translators.
|
|
Packit |
1c1d7e |
f.write('\n' + '=' * 70)
|
|
Packit |
1c1d7e |
f.write('\nDetails for translators (classes sorted alphabetically):\n')
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
cls = list(self.__translDic.keys())
|
|
Packit |
1c1d7e |
cls.sort()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
for c in cls:
|
|
Packit |
1c1d7e |
obj = self.__translDic[c]
|
|
Packit |
1c1d7e |
assert(obj.classId != 'Translator')
|
|
Packit |
1c1d7e |
obj.report(f)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Close the report file and the auxiliary file with e-mails.
|
|
Packit |
1c1d7e |
f.close()
|
|
Packit |
1c1d7e |
fmail.close()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def __loadMaintainers(self):
|
|
Packit |
1c1d7e |
"""Load and process the file with the maintainers.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
Fills the dictionary classId -> [(name, e-mail), ...]."""
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
fname = os.path.join(self.doc_path, self.maintainersFileName)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Include the maintainers file to the group of files checked with
|
|
Packit |
1c1d7e |
# respect to the modification time.
|
|
Packit |
1c1d7e |
tim = os.path.getmtime(fname)
|
|
Packit |
1c1d7e |
if tim > self.lastModificationTime:
|
|
Packit |
1c1d7e |
self.lastModificationTime = tim
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Process the content of the maintainers file.
|
|
Packit |
1c1d7e |
f = xopen(fname)
|
|
Packit |
1c1d7e |
inside = False # inside the record for the language
|
|
Packit |
1c1d7e |
lineReady = True
|
|
Packit |
1c1d7e |
classId = None
|
|
Packit |
1c1d7e |
maintainersLst = None
|
|
Packit |
1c1d7e |
self.__maintainersDic = {}
|
|
Packit |
1c1d7e |
while lineReady:
|
|
Packit |
1c1d7e |
line = f.readline() # next line
|
|
Packit |
1c1d7e |
lineReady = line != '' # when eof, then line == ''
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
line = line.strip() # eof should also behave as separator
|
|
Packit |
1c1d7e |
if line != '' and line[0] == '%': # skip the comment line
|
|
Packit |
1c1d7e |
continue
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
if not inside: # if outside of the record
|
|
Packit |
1c1d7e |
if line != '': # should be language identifier
|
|
Packit |
1c1d7e |
classId = line
|
|
Packit |
1c1d7e |
maintainersLst = []
|
|
Packit |
1c1d7e |
inside = True
|
|
Packit |
1c1d7e |
# Otherwise skip empty line that do not act as separator.
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
else: # if inside the record
|
|
Packit |
1c1d7e |
if line == '': # separator found
|
|
Packit |
1c1d7e |
inside = False
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
# If it is the first maintainer, create the empty list.
|
|
Packit |
1c1d7e |
if classId not in self.__maintainersDic:
|
|
Packit |
1c1d7e |
self.__maintainersDic[classId] = []
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Split the information about the maintainer and append
|
|
Packit |
1c1d7e |
# the tuple. The address may be prefixed '[unreachable]'
|
|
Packit |
1c1d7e |
# or whatever '[xxx]'. This will be processed later.
|
|
Packit |
1c1d7e |
lst = line.split(':', 1)
|
|
Packit |
1c1d7e |
assert(len(lst) == 2)
|
|
Packit |
1c1d7e |
t = (lst[0].strip(), lst[1].strip())
|
|
Packit |
1c1d7e |
self.__maintainersDic[classId].append(t)
|
|
Packit |
1c1d7e |
f.close()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
def generateLanguageDoc(self):
|
|
Packit |
1c1d7e |
"""Checks the modtime of files and generates language.doc."""
|
|
Packit |
1c1d7e |
self.__loadMaintainers()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Check the last modification time of the template file. It is the
|
|
Packit |
1c1d7e |
# last file from the group that decide whether the documentation
|
|
Packit |
1c1d7e |
# should or should not be generated.
|
|
Packit |
1c1d7e |
fTplName = os.path.join(self.doc_path, self.languageTplFileName)
|
|
Packit |
1c1d7e |
tim = os.path.getmtime(fTplName)
|
|
Packit |
1c1d7e |
if tim > self.lastModificationTime:
|
|
Packit |
1c1d7e |
self.lastModificationTime = tim
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# If the generated documentation exists and is newer than any of
|
|
Packit |
1c1d7e |
# the source files from the group, do not generate it and quit
|
|
Packit |
1c1d7e |
# quietly.
|
|
Packit |
1c1d7e |
fDocName = os.path.join(self.doc_path, self.languageDocFileName)
|
|
Packit |
1c1d7e |
if os.path.isfile(fDocName):
|
|
Packit |
1c1d7e |
if os.path.getmtime(fDocName) > self.lastModificationTime:
|
|
Packit |
1c1d7e |
return
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The document or does not exist or is older than some of the
|
|
Packit |
1c1d7e |
# sources. It must be generated again.
|
|
Packit |
1c1d7e |
#
|
|
Packit |
1c1d7e |
# Read the template of the documentation, and remove the first
|
|
Packit |
1c1d7e |
# attention lines.
|
|
Packit |
1c1d7e |
f = xopen(fTplName)
|
|
Packit |
1c1d7e |
doctpl = f.read()
|
|
Packit |
1c1d7e |
f.close()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
pos = doctpl.find('/***')
|
|
Packit |
1c1d7e |
assert pos != -1
|
|
Packit |
1c1d7e |
doctpl = doctpl[pos:]
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Fill the tplDic by symbols that will be inserted into the
|
|
Packit |
1c1d7e |
# document template.
|
|
Packit |
1c1d7e |
tplDic = {}
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
s = ('Do not edit this file. It was generated by the %s script.\n' +\
|
|
Packit |
1c1d7e |
' * Edit the %s and %s files instead.') % (
|
|
Packit |
1c1d7e |
self.script_name, self.languageTplFileName, self.maintainersFileName)
|
|
Packit |
1c1d7e |
tplDic['editnote'] = s
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
tplDic['doxVersion'] = self.doxVersion
|
|
Packit |
1c1d7e |
tplDic['supportedLangReadableStr'] = self.supportedLangReadableStr
|
|
Packit |
1c1d7e |
tplDic['translatorReportFileName'] = self.translatorReportFileName
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
ahref = '
|
|
Packit |
1c1d7e |
ahref += '"\n>doxygen/doc/' + self.translatorReportFileName
|
|
Packit |
1c1d7e |
ahref += ''
|
|
Packit |
1c1d7e |
tplDic['translatorReportLink'] = ahref
|
|
Packit |
1c1d7e |
tplDic['numLangStr'] = str(self.numLang)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Define templates for HTML table parts of the documentation.
|
|
Packit |
1c1d7e |
htmlTableTpl = '''\
|
|
Packit |
1c1d7e |
\\htmlonly
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
<font size="+1" color="#ffffff"> Language </font>
|
|
Packit |
1c1d7e |
<font size="+1" color="#ffffff"> Maintainer </font>
|
|
Packit |
1c1d7e |
<font size="+1" color="#ffffff"> Contact address </font>
|
|
Packit |
1c1d7e |
<font size="-2" color="#ffffff">(replace the at and dot)</font>
|
|
Packit |
1c1d7e |
<font size="+1" color="#ffffff"> Status </font>
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
%s
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
\\endhtmlonly
|
|
Packit |
1c1d7e |
'''
|
|
Packit |
1c1d7e |
htmlTableTpl = textwrap.dedent(htmlTableTpl)
|
|
Packit |
1c1d7e |
htmlTrTpl = '\n %s\n '
|
|
Packit |
1c1d7e |
htmlTdTpl = '\n %s'
|
|
Packit |
1c1d7e |
htmlTdStatusColorTpl = '\n %s'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Loop through transl objects in the order of sorted readable names
|
|
Packit |
1c1d7e |
# and add generate the content of the HTML table.
|
|
Packit |
1c1d7e |
trlst = []
|
|
Packit |
1c1d7e |
for name, obj in self.langLst:
|
|
Packit |
1c1d7e |
# Fill the table data elements for one row. The first element
|
|
Packit |
1c1d7e |
# contains the readable name of the language. Only the oldest
|
|
Packit |
1c1d7e |
# translator are colour marked in the language column. Less
|
|
Packit |
1c1d7e |
# "heavy" color is used (when compared with the Status column).
|
|
Packit |
1c1d7e |
if obj.readableStatus.startswith('1.4'):
|
|
Packit |
1c1d7e |
bkcolor = self.getBgcolorByReadableStatus('1.4')
|
|
Packit |
1c1d7e |
else:
|
|
Packit |
1c1d7e |
bkcolor = '#ffffff'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
lst = [ htmlTdStatusColorTpl % (bkcolor, obj.langReadable) ]
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The next two elements contain the list of maintainers
|
|
Packit |
1c1d7e |
# and the list of their mangled e-mails. For English-based
|
|
Packit |
1c1d7e |
# translators that are coupled with the non-English based,
|
|
Packit |
1c1d7e |
# insert the 'see' note.
|
|
Packit |
1c1d7e |
mm = None # init -- maintainer
|
|
Packit |
1c1d7e |
ee = None # init -- e-mail address
|
|
Packit |
1c1d7e |
if obj.status == 'En':
|
|
Packit |
1c1d7e |
# Check whether there is the coupled non-English.
|
|
Packit |
1c1d7e |
classId = obj.classId[:-2]
|
|
Packit |
1c1d7e |
if classId in self.__translDic:
|
|
Packit |
1c1d7e |
lang = self.__translDic[classId].langReadable
|
|
Packit |
1c1d7e |
mm = 'see the %s language' % lang
|
|
Packit |
1c1d7e |
ee = ' '
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
if not mm and obj.classId in self.__maintainersDic:
|
|
Packit |
1c1d7e |
# Build a string of names separated by the HTML break element.
|
|
Packit |
1c1d7e |
# Special notes used instead of names are highlighted.
|
|
Packit |
1c1d7e |
lm = []
|
|
Packit |
1c1d7e |
for maintainer in self.__maintainersDic[obj.classId]:
|
|
Packit |
1c1d7e |
name = maintainer[0]
|
|
Packit |
1c1d7e |
if name.startswith('--'):
|
|
Packit |
1c1d7e |
name = ''\
|
|
Packit |
1c1d7e |
+ name + ''
|
|
Packit |
1c1d7e |
lm.append(name)
|
|
Packit |
1c1d7e |
mm = ' '.join(lm)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The marked addresses (they start with the mark '[unreachable]',
|
|
Packit |
1c1d7e |
# '[resigned]', whatever '[xxx]') will not be displayed at all.
|
|
Packit |
1c1d7e |
# Only the mark will be used instead.
|
|
Packit |
1c1d7e |
rexMark = re.compile('(?P<mark>\\[.*?\\])')
|
|
Packit |
1c1d7e |
le = []
|
|
Packit |
1c1d7e |
for maintainer in self.__maintainersDic[obj.classId]:
|
|
Packit |
1c1d7e |
address = maintainer[1]
|
|
Packit |
1c1d7e |
m = rexMark.search(address)
|
|
Packit |
1c1d7e |
if m is not None:
|
|
Packit |
1c1d7e |
address = ''\
|
|
Packit |
1c1d7e |
+ m.group('mark') + ''
|
|
Packit |
1c1d7e |
le.append(address)
|
|
Packit |
1c1d7e |
ee = ' '.join(le)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Append the maintainer and e-mail elements.
|
|
Packit |
1c1d7e |
lst.append(htmlTdTpl % mm)
|
|
Packit |
1c1d7e |
lst.append(htmlTdTpl % ee)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The last element contains the readable form of the status.
|
|
Packit |
1c1d7e |
bgcolor = self.getBgcolorByReadableStatus(obj.readableStatus)
|
|
Packit |
1c1d7e |
lst.append(htmlTdStatusColorTpl % (bgcolor, obj.readableStatus))
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Join the table data to one table row.
|
|
Packit |
1c1d7e |
trlst.append(htmlTrTpl % (''.join(lst)))
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Join the table rows and insert into the template.
|
|
Packit |
1c1d7e |
htmlTable = htmlTableTpl % (''.join(trlst))
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Define templates for LaTeX table parts of the documentation.
|
|
Packit |
1c1d7e |
latexTableTpl = r'''
|
|
Packit |
1c1d7e |
\latexonly
|
|
Packit |
1c1d7e |
\footnotesize
|
|
Packit |
1c1d7e |
\begin{longtable}{|l|l|l|l|}
|
|
Packit |
1c1d7e |
\hline
|
|
Packit |
1c1d7e |
{\bf Language} & {\bf Maintainer} & {\bf Contact address} & {\bf Status} \\
|
|
Packit |
1c1d7e |
\hline
|
|
Packit |
1c1d7e |
%s
|
|
Packit |
1c1d7e |
\hline
|
|
Packit |
1c1d7e |
\end{longtable}
|
|
Packit |
1c1d7e |
\normalsize
|
|
Packit |
1c1d7e |
\endlatexonly
|
|
Packit |
1c1d7e |
'''
|
|
Packit |
1c1d7e |
latexTableTpl = textwrap.dedent(latexTableTpl)
|
|
Packit |
1c1d7e |
latexLineTpl = '\n' + r' %s & %s & {\tt\tiny %s} & %s \\'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Loop through transl objects in the order of sorted readable names
|
|
Packit |
1c1d7e |
# and add generate the content of the LaTeX table.
|
|
Packit |
1c1d7e |
trlst = []
|
|
Packit |
1c1d7e |
for name, obj in self.langLst:
|
|
Packit |
1c1d7e |
# For LaTeX, more maintainers for the same language are
|
|
Packit |
1c1d7e |
# placed on separate rows in the table. The line separator
|
|
Packit |
1c1d7e |
# in the table is placed explicitly above the first
|
|
Packit |
1c1d7e |
# maintainer. Prepare the arguments for the LaTeX row template.
|
|
Packit |
1c1d7e |
maintainers = []
|
|
Packit |
1c1d7e |
if obj.classId in self.__maintainersDic:
|
|
Packit |
1c1d7e |
maintainers = self.__maintainersDic[obj.classId]
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
lang = obj.langReadable
|
|
Packit |
1c1d7e |
maintainer = None # init
|
|
Packit |
1c1d7e |
email = None # init
|
|
Packit |
1c1d7e |
if obj.status == 'En':
|
|
Packit |
1c1d7e |
# Check whether there is the coupled non-English.
|
|
Packit |
1c1d7e |
classId = obj.classId[:-2]
|
|
Packit |
1c1d7e |
if classId in self.__translDic:
|
|
Packit |
1c1d7e |
langNE = self.__translDic[classId].langReadable
|
|
Packit |
1c1d7e |
maintainer = 'see the %s language' % langNE
|
|
Packit |
1c1d7e |
email = '~'
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
if not maintainer and (obj.classId in self.__maintainersDic):
|
|
Packit |
1c1d7e |
lm = [ m[0] for m in self.__maintainersDic[obj.classId] ]
|
|
Packit |
1c1d7e |
maintainer = maintainers[0][0]
|
|
Packit |
1c1d7e |
email = maintainers[0][1]
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
status = obj.readableStatus
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Use the template to produce the line of the table and insert
|
|
Packit |
1c1d7e |
# the hline plus the constructed line into the table content.
|
|
Packit |
1c1d7e |
# The underscore character must be escaped.
|
|
Packit |
1c1d7e |
trlst.append('\n \\hline')
|
|
Packit |
1c1d7e |
s = latexLineTpl % (lang, maintainer, email, status)
|
|
Packit |
1c1d7e |
s = s.replace('_', '\\_')
|
|
Packit |
1c1d7e |
trlst.append(s)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# List the other maintainers for the language. Do not set
|
|
Packit |
1c1d7e |
# lang and status for them.
|
|
Packit |
1c1d7e |
lang = '~'
|
|
Packit |
1c1d7e |
status = '~'
|
|
Packit |
1c1d7e |
for m in maintainers[1:]:
|
|
Packit |
1c1d7e |
maintainer = m[0]
|
|
Packit |
1c1d7e |
email = m[1]
|
|
Packit |
1c1d7e |
s = latexLineTpl % (lang, maintainer, email, status)
|
|
Packit |
1c1d7e |
s = s.replace('_', '\\_')
|
|
Packit |
1c1d7e |
trlst.append(s)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Join the table lines and insert into the template.
|
|
Packit |
1c1d7e |
latexTable = latexTableTpl % (''.join(trlst))
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Put the HTML and LaTeX parts together and define the dic item.
|
|
Packit |
1c1d7e |
tplDic['informationTable'] = htmlTable + '\n' + latexTable
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Insert the symbols into the document template and write it down.
|
|
Packit |
1c1d7e |
f = xopen(fDocName, 'w')
|
|
Packit |
1c1d7e |
f.write(doctpl % tplDic)
|
|
Packit |
1c1d7e |
f.close()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
if __name__ == '__main__':
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The Python 2.6+ or 3.3+ is required.
|
|
Packit |
1c1d7e |
major = sys.version_info[0]
|
|
Packit |
1c1d7e |
minor = sys.version_info[1]
|
|
Packit |
1c1d7e |
if (major == 2 and minor < 6) or (major == 3 and minor < 0):
|
|
Packit |
1c1d7e |
print('Python 2.6+ or Python 3.0+ are required for the script')
|
|
Packit |
1c1d7e |
sys.exit(1)
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# The translator manager builds the Transl objects, parses the related
|
|
Packit |
1c1d7e |
# sources, and keeps them in memory.
|
|
Packit |
1c1d7e |
trMan = TrManager()
|
|
Packit |
1c1d7e |
|
|
Packit |
1c1d7e |
# Process the Transl objects and generate the output files.
|
|
Packit |
1c1d7e |
trMan.generateLanguageDoc()
|
|
Packit |
1c1d7e |
trMan.generateTranslatorReport()
|