mirror of https://gitee.com/openkylin/qemu.git
qapi: Simplify how QAPIDoc implements its state machine
QAPIDoc uses a state machine to for processing of documentation lines. Its state is encoded as an enum QAPIDoc._state (well, as enum-like class actually, thanks to our infatuation with Python 2). All we ever do with the state is calling the state's function to process a line of documentation. The enum values effectively serve as handles for the functions. Eliminate the rather wordy indirection: store the function to call in QAPIDoc._append_line. Update and improve comments. Signed-off-by: Markus Armbruster <armbru@redhat.com> Message-Id: <20190606153803.5278-8-armbru@redhat.com> Reviewed-by: Kevin Wolf <kwolf@redhat.com> [Commit message typo fixed]
This commit is contained in:
parent
c9d4070991
commit
157dd36395
|
@ -102,6 +102,24 @@ def __init__(self, info, msg):
|
||||||
|
|
||||||
|
|
||||||
class QAPIDoc(object):
|
class QAPIDoc(object):
|
||||||
|
"""
|
||||||
|
A documentation comment block, either expression or free-form
|
||||||
|
|
||||||
|
Expression documentation blocks consist of
|
||||||
|
|
||||||
|
* a body section: one line naming the expression, followed by an
|
||||||
|
overview (any number of lines)
|
||||||
|
|
||||||
|
* argument sections: a description of each argument (for commands
|
||||||
|
and events) or member (for structs, unions and alternates)
|
||||||
|
|
||||||
|
* features sections: a description of each feature flag
|
||||||
|
|
||||||
|
* additional (non-argument) sections, possibly tagged
|
||||||
|
|
||||||
|
Free-form documentation blocks consist only of a body section.
|
||||||
|
"""
|
||||||
|
|
||||||
class Section(object):
|
class Section(object):
|
||||||
def __init__(self, name=None):
|
def __init__(self, name=None):
|
||||||
# optional section name (argument/member or section name)
|
# optional section name (argument/member or section name)
|
||||||
|
@ -120,26 +138,6 @@ def __init__(self, name):
|
||||||
def connect(self, member):
|
def connect(self, member):
|
||||||
self.member = member
|
self.member = member
|
||||||
|
|
||||||
class DocPart:
|
|
||||||
"""
|
|
||||||
Describes which part of the documentation we're parsing right now.
|
|
||||||
|
|
||||||
Expression documentation blocks consist of
|
|
||||||
* a BODY part: first line naming the expression, plus an
|
|
||||||
optional overview
|
|
||||||
* an ARGS part: description of each argument (for commands and
|
|
||||||
events) or member (for structs, unions and alternates),
|
|
||||||
* a FEATURES part: description of each feature,
|
|
||||||
* a VARIOUS part: optional tagged sections.
|
|
||||||
|
|
||||||
Free-form documentation blocks consist only of a BODY part.
|
|
||||||
"""
|
|
||||||
# TODO Make it a subclass of Enum when Python 2 support is removed
|
|
||||||
BODY = 1
|
|
||||||
ARGS = 2
|
|
||||||
FEATURES = 3
|
|
||||||
VARIOUS = 4
|
|
||||||
|
|
||||||
def __init__(self, parser, info):
|
def __init__(self, parser, info):
|
||||||
# self._parser is used to report errors with QAPIParseError. The
|
# self._parser is used to report errors with QAPIParseError. The
|
||||||
# resulting error position depends on the state of the parser.
|
# resulting error position depends on the state of the parser.
|
||||||
|
@ -156,7 +154,7 @@ def __init__(self, parser, info):
|
||||||
self.sections = []
|
self.sections = []
|
||||||
# the current section
|
# the current section
|
||||||
self._section = self.body
|
self._section = self.body
|
||||||
self._part = QAPIDoc.DocPart.BODY
|
self._append_line = self._append_body_line
|
||||||
|
|
||||||
def has_section(self, name):
|
def has_section(self, name):
|
||||||
"""Return True if we have a section with this name."""
|
"""Return True if we have a section with this name."""
|
||||||
|
@ -171,21 +169,10 @@ def append(self, line):
|
||||||
|
|
||||||
The way that the line is dealt with depends on which part of
|
The way that the line is dealt with depends on which part of
|
||||||
the documentation we're parsing right now:
|
the documentation we're parsing right now:
|
||||||
|
* The body section: ._append_line is ._append_body_line
|
||||||
BODY means that we're ready to process free-form text into
|
* An argument section: ._append_line is ._append_args_line
|
||||||
self.body. A symbol name is only allowed if no other text was
|
* A features section: ._append_line is ._append_features_line
|
||||||
parsed yet. It is interpreted as the symbol name that
|
* An additional section: ._append_line is ._append_various_line
|
||||||
describes the currently documented object. On getting the
|
|
||||||
second symbol name, we proceed to ARGS.
|
|
||||||
|
|
||||||
ARGS means that we're parsing the arguments section. Any
|
|
||||||
symbol name is interpreted as an argument and an ArgSection is
|
|
||||||
created for it.
|
|
||||||
|
|
||||||
VARIOUS is the final part where free-form sections may appear.
|
|
||||||
This includes named sections such as "Return:" as well as
|
|
||||||
unnamed paragraphs. Symbols are not allowed any more in this
|
|
||||||
part.
|
|
||||||
"""
|
"""
|
||||||
line = line[1:]
|
line = line[1:]
|
||||||
if not line:
|
if not line:
|
||||||
|
@ -195,17 +182,7 @@ def append(self, line):
|
||||||
if line[0] != ' ':
|
if line[0] != ' ':
|
||||||
raise QAPIParseError(self._parser, "Missing space after #")
|
raise QAPIParseError(self._parser, "Missing space after #")
|
||||||
line = line[1:]
|
line = line[1:]
|
||||||
|
self._append_line(line)
|
||||||
if self._part == QAPIDoc.DocPart.BODY:
|
|
||||||
self._append_body_line(line)
|
|
||||||
elif self._part == QAPIDoc.DocPart.ARGS:
|
|
||||||
self._append_args_line(line)
|
|
||||||
elif self._part == QAPIDoc.DocPart.FEATURES:
|
|
||||||
self._append_features_line(line)
|
|
||||||
elif self._part == QAPIDoc.DocPart.VARIOUS:
|
|
||||||
self._append_various_line(line)
|
|
||||||
else:
|
|
||||||
assert False
|
|
||||||
|
|
||||||
def end_comment(self):
|
def end_comment(self):
|
||||||
self._end_section()
|
self._end_section()
|
||||||
|
@ -219,6 +196,19 @@ def _is_section_tag(name):
|
||||||
'TODO:')
|
'TODO:')
|
||||||
|
|
||||||
def _append_body_line(self, line):
|
def _append_body_line(self, line):
|
||||||
|
"""
|
||||||
|
Process a line of documentation text in the body section.
|
||||||
|
|
||||||
|
If this a symbol line and it is the section's first line, this
|
||||||
|
is an expression documentation block for that symbol.
|
||||||
|
|
||||||
|
If it's an expression documentation block, another symbol line
|
||||||
|
begins the argument section for the argument named by it, and
|
||||||
|
a section tag begins an additional section. Start that
|
||||||
|
section and append the line to it.
|
||||||
|
|
||||||
|
Else, append the line to the current section.
|
||||||
|
"""
|
||||||
name = line.split(' ', 1)[0]
|
name = line.split(' ', 1)[0]
|
||||||
# FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
|
# FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
|
||||||
# recognized, and get silently treated as ordinary text
|
# recognized, and get silently treated as ordinary text
|
||||||
|
@ -230,38 +220,49 @@ def _append_body_line(self, line):
|
||||||
if not self.symbol:
|
if not self.symbol:
|
||||||
raise QAPIParseError(self._parser, "Invalid name")
|
raise QAPIParseError(self._parser, "Invalid name")
|
||||||
elif self.symbol:
|
elif self.symbol:
|
||||||
# We already know that we document some symbol
|
# This is an expression documentation block
|
||||||
if name.startswith('@') and name.endswith(':'):
|
if name.startswith('@') and name.endswith(':'):
|
||||||
self._part = QAPIDoc.DocPart.ARGS
|
self._append_line = self._append_args_line
|
||||||
self._append_args_line(line)
|
self._append_args_line(line)
|
||||||
elif line == 'Features:':
|
elif line == 'Features:':
|
||||||
self._part = QAPIDoc.DocPart.FEATURES
|
self._append_line = self._append_features_line
|
||||||
elif self._is_section_tag(name):
|
elif self._is_section_tag(name):
|
||||||
self._part = QAPIDoc.DocPart.VARIOUS
|
self._append_line = self._append_various_line
|
||||||
self._append_various_line(line)
|
self._append_various_line(line)
|
||||||
else:
|
else:
|
||||||
self._append_freeform(line.strip())
|
self._append_freeform(line.strip())
|
||||||
else:
|
else:
|
||||||
# This is free-form documentation without a symbol
|
# This is a free-form documentation block
|
||||||
self._append_freeform(line.strip())
|
self._append_freeform(line.strip())
|
||||||
|
|
||||||
def _append_args_line(self, line):
|
def _append_args_line(self, line):
|
||||||
|
"""
|
||||||
|
Process a line of documentation text in an argument section.
|
||||||
|
|
||||||
|
A symbol line begins the next argument section, a section tag
|
||||||
|
section or a non-indented line after a blank line begins an
|
||||||
|
additional section. Start that section and append the line to
|
||||||
|
it.
|
||||||
|
|
||||||
|
Else, append the line to the current section.
|
||||||
|
|
||||||
|
"""
|
||||||
name = line.split(' ', 1)[0]
|
name = line.split(' ', 1)[0]
|
||||||
|
|
||||||
if name.startswith('@') and name.endswith(':'):
|
if name.startswith('@') and name.endswith(':'):
|
||||||
line = line[len(name)+1:]
|
line = line[len(name)+1:]
|
||||||
self._start_args_section(name[1:-1])
|
self._start_args_section(name[1:-1])
|
||||||
elif self._is_section_tag(name):
|
elif self._is_section_tag(name):
|
||||||
self._part = QAPIDoc.DocPart.VARIOUS
|
self._append_line = self._append_various_line
|
||||||
self._append_various_line(line)
|
self._append_various_line(line)
|
||||||
return
|
return
|
||||||
elif (self._section.text.endswith('\n\n')
|
elif (self._section.text.endswith('\n\n')
|
||||||
and line and not line[0].isspace()):
|
and line and not line[0].isspace()):
|
||||||
if line == 'Features:':
|
if line == 'Features:':
|
||||||
self._part = QAPIDoc.DocPart.FEATURES
|
self._append_line = self._append_features_line
|
||||||
else:
|
else:
|
||||||
self._start_section()
|
self._start_section()
|
||||||
self._part = QAPIDoc.DocPart.VARIOUS
|
self._append_line = self._append_various_line
|
||||||
self._append_various_line(line)
|
self._append_various_line(line)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -274,19 +275,29 @@ def _append_features_line(self, line):
|
||||||
line = line[len(name)+1:]
|
line = line[len(name)+1:]
|
||||||
self._start_features_section(name[1:-1])
|
self._start_features_section(name[1:-1])
|
||||||
elif self._is_section_tag(name):
|
elif self._is_section_tag(name):
|
||||||
self._part = QAPIDoc.DocPart.VARIOUS
|
self._append_line = self._append_various_line
|
||||||
self._append_various_line(line)
|
self._append_various_line(line)
|
||||||
return
|
return
|
||||||
elif (self._section.text.endswith('\n\n')
|
elif (self._section.text.endswith('\n\n')
|
||||||
and line and not line[0].isspace()):
|
and line and not line[0].isspace()):
|
||||||
self._start_section()
|
self._start_section()
|
||||||
self._part = QAPIDoc.DocPart.VARIOUS
|
self._append_line = self._append_various_line
|
||||||
self._append_various_line(line)
|
self._append_various_line(line)
|
||||||
return
|
return
|
||||||
|
|
||||||
self._append_freeform(line.strip())
|
self._append_freeform(line.strip())
|
||||||
|
|
||||||
def _append_various_line(self, line):
|
def _append_various_line(self, line):
|
||||||
|
"""
|
||||||
|
Process a line of documentation text in an additional section.
|
||||||
|
|
||||||
|
A symbol line is an error.
|
||||||
|
|
||||||
|
A section tag begins an additional section. Start that
|
||||||
|
section and append the line to it.
|
||||||
|
|
||||||
|
Else, append the line to the current section.
|
||||||
|
"""
|
||||||
name = line.split(' ', 1)[0]
|
name = line.split(' ', 1)[0]
|
||||||
|
|
||||||
if name.startswith('@') and name.endswith(':'):
|
if name.startswith('@') and name.endswith(':'):
|
||||||
|
|
Loading…
Reference in New Issue