diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2017-01-17 13:53:50 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2017-01-17 13:53:50 +0000 |
commit | 23eb9e6b6d5315171cc15969bbc755f258004df0 (patch) | |
tree | 70d3a286561a60c67410e7446e293fb13eecfa68 /scripts/qapi2texi.py | |
parent | 02b351d8466a26c93bf35ff8fd5c316e6aee8c0f (diff) | |
parent | 56e8bdd46a8a42d89b0afea9da83ae7679cc0439 (diff) |
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2017-01-16' into staging
QAPI patches for 2017-01-16
# gpg: Signature made Mon 16 Jan 2017 09:26:49 GMT
# gpg: using RSA key 0x3870B400EB918653
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>"
# gpg: aka "Markus Armbruster <armbru@pond.sub.org>"
# Primary key fingerprint: 354B C8B3 D7EB 2A6B 6867 4E5F 3870 B400 EB91 8653
* remotes/armbru/tags/pull-qapi-2017-01-16: (180 commits)
build-sys: add qapi doc generation targets
build-sys: add txt documentation rules
build-sys: use a generic TEXI2MAN rule
build-sys: remove dvi doc generation
build-sys: use --no-split for info
docs: add qemu logo to pdf
qapi: add qapi2texi script
qmp-events: move 'MIGRATION_PASS' doc to schema
qmp-events: move 'DUMP_COMPLETED' doc to schema
qmp-events: move 'MEM_UNPLUG_ERROR' doc to schema
qmp-events: move 'VSERPORT_CHANGE' doc to schema
qmp-events: move 'QUORUM_REPORT_BAD' doc to schema
qmp-events: move 'QUORUM_FAILURE' doc to schema
qmp-events: move 'GUEST_PANICKED' doc to schema
qmp-events: move 'BALLOON_CHANGE' doc to schema
qmp-events: move 'ACPI_DEVICE_OST' doc to schema
qmp-events: move 'MIGRATION' doc to schema
qmp-events: move 'SPICE_MIGRATE_COMPLETED' doc to schema
qmp-events: move 'SPICE_DISCONNECTED' doc to schema
qmp-events: move 'SPICE_INITIALIZED' doc to schema
...
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'scripts/qapi2texi.py')
-rwxr-xr-x | scripts/qapi2texi.py | 271 |
1 files changed, 271 insertions, 0 deletions
diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py new file mode 100755 index 0000000000..83ded95c2d --- /dev/null +++ b/scripts/qapi2texi.py @@ -0,0 +1,271 @@ +#!/usr/bin/env python +# QAPI texi generator +# +# This work is licensed under the terms of the GNU LGPL, version 2+. +# See the COPYING file in the top-level directory. +"""This script produces the documentation of a qapi schema in texinfo format""" +import re +import sys + +import qapi + +COMMAND_FMT = """ +@deftypefn {type} {{}} {name} + +{body} + +@end deftypefn + +""".format + +ENUM_FMT = """ +@deftp Enum {name} + +{body} + +@end deftp + +""".format + +STRUCT_FMT = """ +@deftp {{{type}}} {name} + +{body} + +@end deftp + +""".format + +EXAMPLE_FMT = """@example +{code} +@end example +""".format + + +def subst_strong(doc): + """Replaces *foo* by @strong{foo}""" + return re.sub(r'\*([^*\n]+)\*', r'@emph{\1}', doc) + + +def subst_emph(doc): + """Replaces _foo_ by @emph{foo}""" + return re.sub(r'\b_([^_\n]+)_\b', r' @emph{\1} ', doc) + + +def subst_vars(doc): + """Replaces @var by @code{var}""" + return re.sub(r'@([\w-]+)', r'@code{\1}', doc) + + +def subst_braces(doc): + """Replaces {} with @{ @}""" + return doc.replace("{", "@{").replace("}", "@}") + + +def texi_example(doc): + """Format @example""" + # TODO: Neglects to escape @ characters. + # We should probably escape them in subst_braces(), and rename the + # function to subst_special() or subs_texi_special(). If we do that, we + # need to delay it until after subst_vars() in texi_format(). + doc = subst_braces(doc).strip('\n') + return EXAMPLE_FMT(code=doc) + + +def texi_format(doc): + """ + Format documentation + + Lines starting with: + - |: generates an @example + - =: generates @section + - ==: generates @subsection + - 1. or 1): generates an @enumerate @item + - */-: generates an @itemize list + """ + lines = [] + doc = subst_braces(doc) + doc = subst_vars(doc) + doc = subst_emph(doc) + doc = subst_strong(doc) + inlist = "" + lastempty = False + for line in doc.split('\n'): + empty = line == "" + + # FIXME: Doing this in a single if / elif chain is + # problematic. For instance, a line without markup terminates + # a list if it follows a blank line (reaches the final elif), + # but a line with some *other* markup, such as a = title + # doesn't. + # + # Make sure to update section "Documentation markup" in + # docs/qapi-code-gen.txt when fixing this. + if line.startswith("| "): + line = EXAMPLE_FMT(code=line[2:]) + elif line.startswith("= "): + line = "@section " + line[2:] + elif line.startswith("== "): + line = "@subsection " + line[3:] + elif re.match(r'^([0-9]*\.) ', line): + if not inlist: + lines.append("@enumerate") + inlist = "enumerate" + line = line[line.find(" ")+1:] + lines.append("@item") + elif re.match(r'^[*-] ', line): + if not inlist: + lines.append("@itemize %s" % {'*': "@bullet", + '-': "@minus"}[line[0]]) + inlist = "itemize" + lines.append("@item") + line = line[2:] + elif lastempty and inlist: + lines.append("@end %s\n" % inlist) + inlist = "" + + lastempty = empty + lines.append(line) + + if inlist: + lines.append("@end %s\n" % inlist) + return "\n".join(lines) + + +def texi_body(doc): + """ + Format the body of a symbol documentation: + - main body + - table of arguments + - followed by "Returns/Notes/Since/Example" sections + """ + body = texi_format(str(doc.body)) + "\n" + if doc.args: + body += "@table @asis\n" + for arg, section in doc.args.iteritems(): + desc = str(section) + opt = '' + if "#optional" in desc: + desc = desc.replace("#optional", "") + opt = ' (optional)' + body += "@item @code{'%s'}%s\n%s\n" % (arg, opt, + texi_format(desc)) + body += "@end table\n" + + for section in doc.sections: + name, doc = (section.name, str(section)) + func = texi_format + if name.startswith("Example"): + func = texi_example + + if name: + # FIXME the indentation produced by @quotation in .txt and + # .html output is confusing + body += "\n@quotation %s\n%s\n@end quotation" % \ + (name, func(doc)) + else: + body += func(doc) + + return body + + +def texi_alternate(expr, doc): + """Format an alternate to texi""" + body = texi_body(doc) + return STRUCT_FMT(type="Alternate", + name=doc.symbol, + body=body) + + +def texi_union(expr, doc): + """Format a union to texi""" + discriminator = expr.get("discriminator") + if discriminator: + union = "Flat Union" + else: + union = "Simple Union" + + body = texi_body(doc) + return STRUCT_FMT(type=union, + name=doc.symbol, + body=body) + + +def texi_enum(expr, doc): + """Format an enum to texi""" + for i in expr['data']: + if i not in doc.args: + doc.args[i] = '' + body = texi_body(doc) + return ENUM_FMT(name=doc.symbol, + body=body) + + +def texi_struct(expr, doc): + """Format a struct to texi""" + body = texi_body(doc) + return STRUCT_FMT(type="Struct", + name=doc.symbol, + body=body) + + +def texi_command(expr, doc): + """Format a command to texi""" + body = texi_body(doc) + return COMMAND_FMT(type="Command", + name=doc.symbol, + body=body) + + +def texi_event(expr, doc): + """Format an event to texi""" + body = texi_body(doc) + return COMMAND_FMT(type="Event", + name=doc.symbol, + body=body) + + +def texi_expr(expr, doc): + """Format an expr to texi""" + (kind, _) = expr.items()[0] + + fmt = {"command": texi_command, + "struct": texi_struct, + "enum": texi_enum, + "union": texi_union, + "alternate": texi_alternate, + "event": texi_event}[kind] + + return fmt(expr, doc) + + +def texi(docs): + """Convert QAPI schema expressions to texi documentation""" + res = [] + for doc in docs: + expr = doc.expr + if not expr: + res.append(texi_body(doc)) + continue + try: + doc = texi_expr(expr, doc) + res.append(doc) + except: + print >>sys.stderr, "error at @%s" % doc.info + raise + + return '\n'.join(res) + + +def main(argv): + """Takes schema argument, prints result to stdout""" + if len(argv) != 2: + print >>sys.stderr, "%s: need exactly 1 argument: SCHEMA" % argv[0] + sys.exit(1) + + schema = qapi.QAPISchema(argv[1]) + print texi(schema.docs) + + +if __name__ == "__main__": + main(sys.argv) |