diff mbox

[v2,03/13] autodoc: extract doc from the C files

Message ID 20180519130602.90096-4-luc.vanoostenryck@gmail.com (mailing list archive)
State Mainlined, archived
Headers show

Commit Message

Luc Van Oostenryck May 19, 2018, 1:05 p.m. UTC
Add a tool which parse comment from source files and
extract kerneldoc-like documentation from them.

Note: this is rather quite crude, support only generic 'blocs'
      and doc for functions; it has no support for anything else.

Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
---
 .gitignore                   |   1 +
 Documentation/sphinx/cdoc.py | 200 +++++++++++++++++++++++++++++++++++
 2 files changed, 201 insertions(+)
 create mode 100755 Documentation/sphinx/cdoc.py

Comments

Randy Dunlap May 19, 2018, 10:30 p.m. UTC | #1
On 05/19/18 06:05, Luc Van Oostenryck wrote:
> Add a tool which parse comment from source files and
> extract kerneldoc-like documentation from them.
> 
> Note: this is rather quite crude, support only generic 'blocs'
>       and doc for functions; it has no support for anything else.
> 
> Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
> ---
>  .gitignore                   |   1 +
>  Documentation/sphinx/cdoc.py | 200 +++++++++++++++++++++++++++++++++++
>  2 files changed, 201 insertions(+)
>  create mode 100755 Documentation/sphinx/cdoc.py
> 
> diff --git a/Documentation/sphinx/cdoc.py b/Documentation/sphinx/cdoc.py
> new file mode 100755
> index 000000000..4eae75e89
> --- /dev/null
> +++ b/Documentation/sphinx/cdoc.py
> @@ -0,0 +1,200 @@
> +#!/usr/bin/env python
> +# SPDX_License-Identifier: MIT
> +#
> +# Copyright (C) 2018 Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
> +#
> +
> +"""
> +///
> +// Sparse source files may contain documentation inside block-comments
> +// specifically formatted::
> +//
> +// 	///
> +// 	// Here is some doc
> +// 	// and here is some more.
> +//
> +// More precisely, a doc-block begins with a line containting only ``///``

                                                     containing

> +// and continues with lines beginning by ``//`` followed by either a space,
> +// a tab or nothing, the first space after ``//`` is ignored.
> +//
> +// For functions, some additional syntax must be respected inside the
> +// block-comment::
> +//
> +// 	///
> +// 	// <mandatory short one-line description>
> +// 	// <optional blanck line>

                     blank

> +// 	// @<1st paramater's name>: <description>

                 parameter's

> +// 	// @<2nd parameter's name>: ...
> +// 	// @return: <description> (absent for void functions)
> +// 	// <optional blank line>
> +// 	// <optional long multi-line description>
> +// 	int somefunction(void *ptr, int count);
> +//
> +// Inside the description fields, parameter's names can be referenced
> +// by using ``@<parameter name>``. A function doc-block must directly

                                                           must directly precede

> +// the function it documents. This function can span multiple lines and
> +// can either be a function prototype (ending with ``;``) or a
> +// function definition.
> +//
> +// Some future versions will also allow to document structures, unions,
> +// enums, typedefs and variables.
> +//
diff mbox

Patch

diff --git a/.gitignore b/.gitignore
index bc00736d7..5569f98b0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ 
 *.a
 *.so
 .*.swp
+*.pyc
 
 # generated
 version.h
diff --git a/Documentation/sphinx/cdoc.py b/Documentation/sphinx/cdoc.py
new file mode 100755
index 000000000..4eae75e89
--- /dev/null
+++ b/Documentation/sphinx/cdoc.py
@@ -0,0 +1,200 @@ 
+#!/usr/bin/env python
+# SPDX_License-Identifier: MIT
+#
+# Copyright (C) 2018 Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
+#
+
+"""
+///
+// Sparse source files may contain documentation inside block-comments
+// specifically formatted::
+//
+// 	///
+// 	// Here is some doc
+// 	// and here is some more.
+//
+// More precisely, a doc-block begins with a line containting only ``///``
+// and continues with lines beginning by ``//`` followed by either a space,
+// a tab or nothing, the first space after ``//`` is ignored.
+//
+// For functions, some additional syntax must be respected inside the
+// block-comment::
+//
+// 	///
+// 	// <mandatory short one-line description>
+// 	// <optional blanck line>
+// 	// @<1st paramater's name>: <description>
+// 	// @<2nd parameter's name>: ...
+// 	// @return: <description> (absent for void functions)
+// 	// <optional blank line>
+// 	// <optional long multi-line description>
+// 	int somefunction(void *ptr, int count);
+//
+// Inside the description fields, parameter's names can be referenced
+// by using ``@<parameter name>``. A function doc-block must directly
+// the function it documents. This function can span multiple lines and
+// can either be a function prototype (ending with ``;``) or a
+// function definition.
+//
+// Some future versions will also allow to document structures, unions,
+// enums, typedefs and variables.
+//
+
+"""
+
+import re
+
+__version__  = '0.0'
+
+
+class Lines:
+	def __init__(self, lines):
+		# type: (Iterable[str]) -> None
+		self.index = 0
+		self.lines = lines
+		self.last = None
+		self.back = False
+
+	def __iter__(self):
+		# type: () -> Lines
+		return self
+
+	def memo(self):
+		# type: () -> Tuple[int, str]
+		return (self.index, self.last)
+
+	def __next__(self):
+		# type: () -> Tuple[int, str]
+		if not self.back:
+			self.last = next(self.lines).rstrip()
+			self.index += 1
+		else:
+			self.back = False
+		return self.memo()
+	def next(self):
+		return self.__next__()
+
+	def undo(self):
+		# type: () -> None
+		self.back = True
+
+def readline_delim(lines, delim):
+	# type: (Lines, Tuple[str, str]) -> Tuple[int, str]
+	try:
+		(lineno, line) = next(lines)
+		if line == '':
+			raise StopIteration
+		while line[-1] not in delim:
+			(n, l) = next(lines)
+			line += ' ' + l.lstrip()
+	except:
+		line = ''
+	return (lineno, line)
+
+
+def process_block(lines):
+	# type: (Lines) -> Dict[str, Any]
+	info = { }
+	tags = []
+	desc = []
+	state = 'START'
+
+	(n, l) = lines.memo()
+	#print('processing line ' + str(n) + ': ' + l)
+
+	## is it a single line comment ?
+	m = re.match(r"^///\s+(.+)$", l)	# /// ...
+	if m:
+		info['type'] = 'single'
+		info['desc'] = (n, m.group(1).rstrip())
+		return info
+
+	## read the multi line comment
+	for (n, l) in lines:
+		#print('state %d: %4d: %s' % (state, n, l))
+		if l.startswith('// '):
+			l = l[3:]					## strip leading '// '
+		elif l.startswith('//\t') or l == '//':
+			l = l[2:]					## strip leading '//'
+		else:
+			lines.undo()				## end of doc-block
+			break
+
+		if state == 'START':			## one-line short description
+			info['short'] = (n ,l)
+			state = 'PRE-TAGS'
+		elif state == 'PRE-TAGS':		## ignore empty line
+			if l != '':
+				lines.undo()
+				state = 'TAGS'
+		elif state == 'TAGS':			## match the '@tagnames'
+			m = re.match(r"^@([\w-]*)(:?\s*)(.*)", l)
+			if m:
+				tag = m.group(1)
+				sep = m.group(2)
+				## FIXME/ warn if sep != ': '
+				l = m.group(3)
+				## FIXME: try multi-line ???
+				tags.append((n, tag, l))
+			else:
+				lines.undo()
+				state = 'PRE-DESC'
+		elif state == 'PRE-DESC':		## ignore the first empty lines
+			if l != '':					## or first line of description
+				desc = [n, l]
+				state = 'DESC'
+		elif state == 'DESC':			## remaining lines -> description
+			desc.append(l)
+		else:
+			pass
+
+	## fill the info
+	if len(tags):
+		info['tags'] = tags
+	if len(desc):
+		info['desc'] = desc
+
+	## read the item (function only for now)
+	(n, line) = readline_delim(lines, (')', ';'))
+	if len(line):
+		line = line.rstrip(';')
+		#print('function: %4d: %s' % (n, line))
+		info['type'] = 'func'
+		info['func'] = (n, line)
+	else:
+		info['type'] = 'bloc'
+
+	return info
+
+def process_file(f):
+	# type: (TextIOWrapper) -> List[Dict[str, Any]]
+	docs = []
+	lines = Lines(f)
+	for (n, l) in lines:
+		#print("%4d: %s" % (n, l))
+		if l.startswith('///'):
+			info = process_block(lines)
+			docs.append(info)
+
+	return docs
+
+
+if __name__ == '__main__':
+	""" extract the doc from stdin """
+	import sys
+
+	res = process_file(sys.stdin)
+	for info in res:
+		print('###');
+		print('type: %s' % (info.get('type', '???')))
+		val = info.get('short', None)
+		if val:
+			print('short:%4d: %s' % val)
+		for val in info.get('tags', []):
+			print('tags: %4d: @%s: %s' % val)
+		val = info.get('desc', None)
+		if val:
+			n = val[0]
+			print('desc: %4d:\n\t%s' % (n, '\n\t'.join(val[1:])))
+
+# vim: tabstop=4