#!/usr/bin/env python
# -*- coding: utf-8 -*-
import argparse # parse arguments
import os.path # getting extension from file
import sys # output and stuff
import re # for regular expressions
import urllib #
## extract doxygen-tag namespace
RE_MODULE = re.compile( r'^\s*([A-Z][\w-]*)\s*({.*?})?\s*DEFINITIONS.*?::=\s*?BEGIN(.*)END', re.VERBOSE | re.MULTILINE | re.DOTALL)
RE_SPACES = re.compile(r'\s+')
RE_COMMENTS = re.compile(r'^\s*--.*\n', re.MULTILINE)
RE_BASIC_TYPES = re.compile(r'^OCTET\s+STRING|BIT\s+STRING|BOOLEAN|INTEGER|FLOAT')
RE_FIELDS = re.compile(r'^\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)
RE_IMPORTS = re.compile(r'^\s*IMPORTS\s*(.*?);', re.DOTALL | re.MULTILINE)
RE_IMPORT_ELEMENTS = re.compile(r'^([,\s\w-]*?)FROM\s*([\w-]+)\s*({[^}]*}(?:\s+WITH\s+SUCCESSORS)?)?', re.MULTILINE)
RE_IMPORT_ELEMENT_TYPE = re.compile(r'[^,\s]+')
RE_DOXY_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_DOXY_REF = re.compile(r'@ref\s+([\w-]+)')
RE_DOXY_CLASS = re.compile(r'@class\s+([\w-]+)')
RE_DOXY_BRIEF = re.compile(r'@brief\s+')
RE_TYPE = re.compile(r'(([A-Z][\w-]*)\s*::=[\w \t]+(?:{(.*?)})?.*?)\n\s*\n', re.MULTILINE | re.DOTALL)
extTypes = {}
def parseText(content, indent=None):
# we need to keep only documenting text under the /** */ and --!
# convert --! comments to C-style ones
c = RE_DOXY_COMMENTS.sub(r'/** \g<1>*/', content)
if c is not None:
content = c
ret = ''
for m in RE_DOXY_C_COMMENTS.finditer(content):
lines = m.group(1).splitlines()
for l in lines:
l = RE_DOXY_C_COMMENTS_I.sub('', l, 1).rstrip()
ret += ''.ljust(indent or 0) + l + '\n'
def repl_ref(m):
return '[**{0}**]({1}#{0})'.format(m.group(1), extTypes.get(m.group(1),''))
c = RE_DOXY_REF.sub(repl_ref, ret)
if c is not None:
ret = c
c = RE_DOXY_CLASS.sub('', ret)
if c is not None:
ret = c
c = RE_DOXY_BRIEF.sub('', ret)
if c is not None:
ret = c
def repl_category(m):
ret = '\n Categories: '
for l in m.group(1).split(','):
ret += '[{0}](#{1}) '.format(l.strip(), urllib.quote_plus(l.strip()))
return ret + '\n\n'
c = re.sub(r'@category:\s+(.+)', repl_category, ret, re.MULTILINE)
if c is not None:
ret = c
return ret
cpos=0
def parseModule(mname, content):
global cpos
cpos = 0
ret = ''
m = RE_IMPORTS.search(content)
if m is not None:
pos = 0
if m.group(1) is not None:
ret += '## Imports:\n'
s = m.group(1)
for fm in RE_IMPORT_ELEMENTS.finditer(s):
imName = fm.group(2)
for im in RE_IMPORT_ELEMENT_TYPE.finditer(fm.group(1)):
extTypes[im.group(0)] = imName+'.md'
ret += ' * **{}** *{}*
\n'.format(imName, RE_SPACES.sub(' ', fm.group(3) or ''))
ret += parseText(s[pos:fm.start()], 3)+'\n'
pos = fm.end()
ret += parseText(s[pos:])
cpos = m.end()
m = RE_EXPORTS.search(content)
if m is not None:
if cpos < m.end():
cpos = m.end()
# parse types
def repl_type (m, doc):
ret = '## {0}\n\n'.format(m.group(2))
if doc is not None:
ret += parseText(doc) + '\n\n'
# parse fields and get out fields descriptions
if m.group(3) is not None:
ret += 'Fields:\n'
pos = 0
for fm in RE_FIELDS.finditer(m.group(3)):
if fm.group(2) is not None:
f = fm.group(1).strip()
t = fm.group(2).strip()
if RE_BASIC_TYPES.match(t) is not None:
ret += '* {0} **{1}** {2}
\n'.format(f, t, fm.group(3) or '')
else:
ret += '* {0} [**{1}**]({2}#{1}) {3}
\n'.format(f, t, extTypes.get(t,''), fm.group(3) or '')
ret += parseText(fm.string[pos:fm.start()], 3)
pos = fm.end()
return ret + '```asn1\n' + RE_COMMENTS.sub('', m.group(1).strip()) +'\n```\n\n'
pos = 0
for m in RE_TYPE.finditer(content[cpos:]):
ss = repl_type (m, m.string[pos:m.start()])
ret += ss
pos = m.end()
return ret
def parseAsn(outDir, content) :
# iterate modules in the file
pos= 0
cnt = 0
for m in RE_MODULE.finditer(content):
ret = '# ASN.1 module {}\n OID: _{}_\n'.format(m.group(1), RE_SPACES.sub(' ', m.group(2)))
ret += parseText(content[pos:m.start()]) + '\n'
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").write(ret)
pos = m.end()
cnt += 1
return cnt
def main():
argc = len(sys.argv)
if argc < 3:
sys.stderr.write("Usage: {} [ASN.1 files]\n".format(sys.argv[0]))
exit(1)
outDir = sys.argv[1]
cnt = 0
for a in sys.argv[2:]:
try:
content = open(a).read()
cnt += parseAsn(outDir, content)
except IOError as e:
sys.stderr.write(e[1]+"\n")
print("{} modules porcessed\n".format(cnt))
if __name__ == '__main__':
main()