From patchwork Wed Jun 12 16:52:29 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Markus Armbruster X-Patchwork-Id: 1114713 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=nongnu.org (client-ip=209.51.188.17; helo=lists.gnu.org; envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=redhat.com Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 45PD1v59k8z9sNT for ; Thu, 13 Jun 2019 03:12:03 +1000 (AEST) Received: from localhost ([::1]:33966 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hb6n3-0005mT-Q0 for incoming@patchwork.ozlabs.org; Wed, 12 Jun 2019 13:12:01 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]:57905) by lists.gnu.org with esmtp (Exim 4.86_2) (envelope-from ) id 1hb6Ub-0007SE-RY for qemu-devel@nongnu.org; Wed, 12 Jun 2019 12:52:59 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hb6UU-0006SB-IS for qemu-devel@nongnu.org; Wed, 12 Jun 2019 12:52:52 -0400 Received: from mx1.redhat.com ([209.132.183.28]:34658) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hb6UP-0006Ef-SF for qemu-devel@nongnu.org; Wed, 12 Jun 2019 12:52:47 -0400 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 181FBC18B2E9 for ; Wed, 12 Jun 2019 16:52:33 +0000 (UTC) Received: from blackfin.pond.sub.org (ovpn-116-148.ams2.redhat.com [10.36.116.148]) by smtp.corp.redhat.com (Postfix) with ESMTPS id B71FF60BF1; Wed, 12 Jun 2019 16:52:32 +0000 (UTC) Received: by blackfin.pond.sub.org (Postfix, from userid 1000) id C7FE41136423; Wed, 12 Jun 2019 18:52:29 +0200 (CEST) From: Markus Armbruster To: qemu-devel@nongnu.org Date: Wed, 12 Jun 2019 18:52:29 +0200 Message-Id: <20190612165229.26976-12-armbru@redhat.com> In-Reply-To: <20190612165229.26976-1-armbru@redhat.com> References: <20190612165229.26976-1-armbru@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.12 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.31]); Wed, 12 Jun 2019 16:52:33 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [PULL 11/11] qapi: Simplify how QAPIDoc implements its state machine X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Kevin Wolf Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" 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 Message-Id: <20190606153803.5278-8-armbru@redhat.com> Reviewed-by: Kevin Wolf [Commit message typo fixed] --- scripts/qapi/common.py | 125 ++++++++++++++++++++++------------------- 1 file changed, 68 insertions(+), 57 deletions(-) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 1164301edf..d61bfdc526 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -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(':'):