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 @@ class QAPISemError(QAPIError):
|
||||
|
||||
|
||||
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):
|
||||
def __init__(self, name=None):
|
||||
# optional section name (argument/member or section name)
|
||||
@ -120,26 +138,6 @@ class QAPIDoc(object):
|
||||
def connect(self, 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):
|
||||
# self._parser is used to report errors with QAPIParseError. The
|
||||
# resulting error position depends on the state of the parser.
|
||||
@ -156,7 +154,7 @@ class QAPIDoc(object):
|
||||
self.sections = []
|
||||
# the current section
|
||||
self._section = self.body
|
||||
self._part = QAPIDoc.DocPart.BODY
|
||||
self._append_line = self._append_body_line
|
||||
|
||||
def has_section(self, name):
|
||||
"""Return True if we have a section with this name."""
|
||||
@ -171,21 +169,10 @@ class QAPIDoc(object):
|
||||
|
||||
The way that the line is dealt with depends on which part of
|
||||
the documentation we're parsing right now:
|
||||
|
||||
BODY means that we're ready to process free-form text into
|
||||
self.body. A symbol name is only allowed if no other text was
|
||||
parsed yet. It is interpreted as the symbol name that
|
||||
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.
|
||||
* The body section: ._append_line is ._append_body_line
|
||||
* An argument section: ._append_line is ._append_args_line
|
||||
* A features section: ._append_line is ._append_features_line
|
||||
* An additional section: ._append_line is ._append_various_line
|
||||
"""
|
||||
line = line[1:]
|
||||
if not line:
|
||||
@ -195,17 +182,7 @@ class QAPIDoc(object):
|
||||
if line[0] != ' ':
|
||||
raise QAPIParseError(self._parser, "Missing space after #")
|
||||
line = line[1:]
|
||||
|
||||
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
|
||||
self._append_line(line)
|
||||
|
||||
def end_comment(self):
|
||||
self._end_section()
|
||||
@ -219,6 +196,19 @@ class QAPIDoc(object):
|
||||
'TODO:')
|
||||
|
||||
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]
|
||||
# FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
|
||||
# recognized, and get silently treated as ordinary text
|
||||
@ -230,38 +220,49 @@ class QAPIDoc(object):
|
||||
if not self.symbol:
|
||||
raise QAPIParseError(self._parser, "Invalid name")
|
||||
elif self.symbol:
|
||||
# We already know that we document some symbol
|
||||
# This is an expression documentation block
|
||||
if name.startswith('@') and name.endswith(':'):
|
||||
self._part = QAPIDoc.DocPart.ARGS
|
||||
self._append_line = self._append_args_line
|
||||
self._append_args_line(line)
|
||||
elif line == 'Features:':
|
||||
self._part = QAPIDoc.DocPart.FEATURES
|
||||
self._append_line = self._append_features_line
|
||||
elif self._is_section_tag(name):
|
||||
self._part = QAPIDoc.DocPart.VARIOUS
|
||||
self._append_line = self._append_various_line
|
||||
self._append_various_line(line)
|
||||
else:
|
||||
self._append_freeform(line.strip())
|
||||
else:
|
||||
# This is free-form documentation without a symbol
|
||||
# This is a free-form documentation block
|
||||
self._append_freeform(line.strip())
|
||||
|
||||
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]
|
||||
|
||||
if name.startswith('@') and name.endswith(':'):
|
||||
line = line[len(name)+1:]
|
||||
self._start_args_section(name[1:-1])
|
||||
elif self._is_section_tag(name):
|
||||
self._part = QAPIDoc.DocPart.VARIOUS
|
||||
self._append_line = self._append_various_line
|
||||
self._append_various_line(line)
|
||||
return
|
||||
elif (self._section.text.endswith('\n\n')
|
||||
and line and not line[0].isspace()):
|
||||
if line == 'Features:':
|
||||
self._part = QAPIDoc.DocPart.FEATURES
|
||||
self._append_line = self._append_features_line
|
||||
else:
|
||||
self._start_section()
|
||||
self._part = QAPIDoc.DocPart.VARIOUS
|
||||
self._append_line = self._append_various_line
|
||||
self._append_various_line(line)
|
||||
return
|
||||
|
||||
@ -274,19 +275,29 @@ class QAPIDoc(object):
|
||||
line = line[len(name)+1:]
|
||||
self._start_features_section(name[1:-1])
|
||||
elif self._is_section_tag(name):
|
||||
self._part = QAPIDoc.DocPart.VARIOUS
|
||||
self._append_line = self._append_various_line
|
||||
self._append_various_line(line)
|
||||
return
|
||||
elif (self._section.text.endswith('\n\n')
|
||||
and line and not line[0].isspace()):
|
||||
self._start_section()
|
||||
self._part = QAPIDoc.DocPart.VARIOUS
|
||||
self._append_line = self._append_various_line
|
||||
self._append_various_line(line)
|
||||
return
|
||||
|
||||
self._append_freeform(line.strip())
|
||||
|
||||
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]
|
||||
|
||||
if name.startswith('@') and name.endswith(':'):
|
||||
|
Loading…
Reference in New Issue
Block a user