Commits (3)
......@@ -2,7 +2,7 @@
-- Date: 13.06.2022
ITS-Container {itu-t (0) identified-organization (4) etsi (0) itsDomain (5) wg1 (1) ts (102894) cdd (2) major-version-3 (3) minor-version-1 (1)}
ETSI-ITS-CDD {itu-t (0) identified-organization (4) etsi (0) itsDomain (5) wg1 (1) ts (102894) cdd (2) major-version-3 (3) minor-version-1 (1)}
DEFINITIONS AUTOMATIC TAGS ::=
......
......@@ -16,15 +16,12 @@ RE_MODULE = re.compile( r'^\s*([A-Z][\w-]*)\s*({.*?})?\s*DEFINITIONS.*?::=\s*?
RE_SPACES = re.compile(r'\s+')
RE_COMMENTS = re.compile(r'^\s*--.*?\n|--.*?(?:--|$)|/\*.*?\*/[\t ]*\n?', re.MULTILINE|re.DOTALL)
RE_COMMENTS = re.compile(r'^\s*--.*?\n|(?:^\s*)?--.*?(?:--[\t ]*|$)|(?:^\s*)?/\*.*?\*/[\t ]*\n?', re.MULTILINE|re.DOTALL)
RE_BASIC_TYPES = re.compile(r'^OCTET\s+STRING|BIT\s+STRING|BOOLEAN|INTEGER|FLOAT|SEQUENCE|SET|NULL')
#RE_TYPE_BODY_1 = re.compile(r'.*?{(.*)}\s*WITH', re.MULTILINE|re.DOTALL)
#RE_TYPE_BODY_2 = re.compile(r'.*?{(.*)}\s*(?:WITH.*|\(.*?\)|\s*$)', re.MULTILINE|re.DOTALL)
RE_TYPE_BODY = re.compile(r'.*?{(.*)}\s*(?:WITH.*|\(.*?\)|\s*$)', re.MULTILINE|re.DOTALL)
#RE_FIELDS = re.compile(r'^\s*(?:/\*\*.*?\*/)|^\s*([\w-]+?)\s+(OCTET\s+STRING|BIT\s+STRING|[A-Z][.\w-]+)?(.*?)(?:,((?:\s*--!?<.*?\n)*)|((?:--!?<.*?\n)*)$)', re.MULTILINE | re.DOTALL| re.VERBOSE)
RE_FIELDS = re.compile(r'^\s*/\*.*?\*/|^\s*--\!.*?\n|^[\s&]*([\w-]+)\s+(OCTET\s+STRING|BIT\s+STRING|[A-Z][\w-]+)?((?:{[^}]*}|\([^)]*\)|.)*?)(?:,|(--)|$)', re.MULTILINE | re.DOTALL)
RE_EXPORTS = re.compile(r'^\s*EXPORTS.*?;', re.DOTALL | re.MULTILINE)
......@@ -40,8 +37,8 @@ RE_DOXY_ASN_COMMENTS = re.compile(r'^\s*--[-!#](:?$|\s(.*))', re.MULTILINE)
RE_DOXY_C_COMMENTS = re.compile(r'^\s*/\*\*\s(.*?)\*/', re.MULTILINE | re.DOTALL)
RE_DOXY_C_COMMENTS_I = re.compile(r'\s*\*+')
RE_STRIPSTAR = re.compile(r'^\s*\*', re.MULTILINE)
RE_STRIPSTAR = re.compile(r'^\s*\*?', re.MULTILINE)
RE_DOXY_REF = re.compile(r'@ref\s+([\w-]+)')
RE_DOXY_CLASS = re.compile(r'@(?:class|struct|details):?\s+([\w-]+)')
......@@ -59,6 +56,7 @@ RE_DOXY_SECTION = re.compile(r"^\s*@(brief|note|(class|struct|param|field|detail
# RE_TYPE = re.compile(r'(([A-Z][\w-]*)\s*::=[\w \t]+(?:{+(.*?)}+)?.*?)\n\s*\n', re.MULTILINE | re.DOTALL)
RE_TYPE = re.compile(r'^\s*([A-Z][\w-]*)?\s*([{} \t:\w-]*?)?::=([\w \t]+.*?)\n\s*\n', re.MULTILINE | re.DOTALL)
RE_OPTIONS = re.compile(r'^\s*@options[\s:]+(.+)', re.MULTILINE)
RE_DOXY_OTHER = re.compile(r'\^(\w+)', re.MULTILINE)
extTypes = {}
cpos = 0
......@@ -87,6 +85,8 @@ def parseText(content, indent=None):
content = RE_DOXY_STRIP_SINGLE_TAG.sub('', content)
content = re.sub('\^(\w+)', '<sup>\\1</sup>', content)
return indentLines(content, indent)
def parseInlineComments(content:str, indent=None):
......@@ -139,14 +139,16 @@ def parseModule(mname, content):
# parse types
def repl_type (m, doc):
title = t = m.group(1) # type name
title = typeName = m.group(1) # type name
f_params = {}
s_unit = ''
s_category = ''
s_note = ''
s_revision = ''
options = copy.copy(o_args)
if doc : # doc is the prepending comment. Check if not None and not Empty
# doc is the prepending comment. Check if not None and not Empty
if doc :
doc = parseDoxyComments(doc)
# parse options
......@@ -154,42 +156,52 @@ def parseModule(mname, content):
nonlocal options
if m.group(1) is not None:
for o in m.group(1).split(','):
setattr(options, o.strip(), True)
setattr(options, o.strip().replace('-', '_'), True)
return ''
doc=RE_OPTIONS.sub(repl_options, doc)
# parse @param, @details, @brief, etc...
def repl_section (m):
nonlocal title
nonlocal t
nonlocal typeName
nonlocal f_params
nonlocal s_note
nonlocal options
ret = ''
l = m.group(4).lstrip(":, \t").lstrip('\n')
if m.group(2) is not None:
# this can be class|struct|details|param|field
if m.group(3) == t:
if m.group(3) == typeName:
ret = parseText(l)
else:
if len(l):
f_params[m.group(3)] = parseText(l, 2)
elif m.group(1) == 'brief':
if o_args.brief_as_title:
if options.brief_as_title:
title = parseText(l)
else:
ret = parseText(l)
elif m.group(1) == 'note':
s_note = '\n>>>\n' + 'NOTE: ' + parseText(l).rstrip() + '\n>>>\n'
else:
ret = m.string[m.start():m.end()]
return ret
doc = RE_DOXY_SECTION.sub(repl_section, doc)
# parse @category XXX [, YYY, ZZZ]
def repl_category(m):
nonlocal s_category
s_category = '\n&nbsp;&nbsp;&nbsp;&nbsp;**Categories**: '
for l in m.group(1).split(','):
# s_category += '[{0}](#{1}) '.format(l.strip(), urlquote(l.strip()))
s_category += l.strip() + ' '
if options.category_links:
for l in m.group(1).split(','):
s_category += '[{0}](#{1}) '.format(l.strip(), urlquote(l.strip()))
else:
for l in m.group(1).split(','):
s_category += l.strip() + ' '
s_category += '\n'
return ''
doc = RE_DOXY_CATEGORY.sub(repl_category, doc)
......@@ -209,9 +221,9 @@ def parseModule(mname, content):
doc = ''
ret = ''
if t is not None:
if typeName is not None:
fields = ''
ret = '\n### <a name="{0}"></a>{1}\n'.format(t, title) + parseText(doc)
ret = '\n### <a name="{0}"></a>{1}\n'.format(typeName, title) + parseText(doc)
# parse fields and get out fields descriptions
if m.group(3) is not None:
......@@ -228,35 +240,36 @@ def parseModule(mname, content):
# add description to the previous type
if len(field):
fields += parseInlineComments(fm.string[pos:fm.start()], 3)
field = ''
field = ''
f = fm.group(1).strip()
ext = fm.group(3) or ''
if f in f_params:
field = f_params.pop(f) + '\n\n'
if fm.group(2) is not None:
fTitle = 'Fields:\n'
if len(field) or not o_args.no_auto_fields:
t = fm.group(2).strip()
if RE_BASIC_TYPES.match(t) is not None:
field = '* {0} **{1}** {2}<br>\n'.format(f, t, ext) + field
if len(field) or not options.no_auto_fields:
fTitle = 'Fields:\n'
fTypeName = fm.group(2).strip()
if RE_BASIC_TYPES.match(fTypeName) is not None:
field = '* {0} **{1}** {2}<br>\n'.format(f, fTypeName, ext) + field
else:
field = '* {0} [**{1}**]({2}#{1}) {3}<br>\n'.format(f, t, extTypes.get(t,''), ext) + field
field = '* {0} [**{1}**]({2}#{1}) {3}<br>\n'.format(f, fTypeName, extTypes.get(fTypeName,''), ext) + field
else:
fTitle = 'Values:\n'
if len(field) or not o_args.no_auto_values:
if len(field) or not options.no_auto_values:
field = '* **{0}** {1}<br>\n'.format(f, ext) + field
if len(field):
field += parseText(fm.string[pos:fm.start()], 3)
field += parseText(parseDoxyComments(fm.string[pos:fm.start()]).strip(), 3)
pos = fm.end()
if fm.group(4) is not None:
# keep '--' for the next round
pos -= 2
if len(field):
fields += field
field = ''
if len(field):
fields += parseInlineComments(typeBody[pos:], 3)
# add all other fields defined as @params
if 'force-all-fields' in options or 'force-all-fields' in o_args:
if 'force_all_fields' in options or 'force_all_fields' in o_args:
for f in f_params:
fields += '* {}<br>\n{}\n\n'.format(f, f_params[f])
if len(fields):
......@@ -290,7 +303,15 @@ def parseAsn(outDir, content) :
if m.group(3) is not None:
ret += parseModule(m.group(1), m.group(3))
ret += '\n\n'
open(outDir + '/' + m.group(1) + '.md', "w",encoding='utf-8').write(ret)
try:
f = open(outDir + '/' + m.group(1) + '.md', mode='w',encoding='utf-8')
f.write(ret)
except OSError as err:
print("OS error: {0}".format(err))
except BaseException as err:
print(f"Unexpected {err=}, {type(err)=}")
finally:
f.close()
pos = m.end()
cnt += 1
return cnt
......@@ -303,6 +324,7 @@ def main():
ap.add_argument('--force-all-fields', '-f', default=False,action='store_true', help='Add all fields in the list even if empty')
ap.add_argument('--no-auto-fields', '-F', default=False,action='store_true', help='Add fields only if @param or @field is defined')
ap.add_argument('--no-auto-values', '-V', default=False,action='store_true', help='Do not add named values or enums')
ap.add_argument('--category-links', '-c', default=False,action='store_true', help='Create links for categories')
ap.add_argument('modules', action='store', nargs='+', help='ASN.1 files')
o_args = ap.parse_args()
......