qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 3/5] qapi: add qapi2texi script


From: Marc-André Lureau
Subject: [Qemu-devel] [PATCH 3/5] qapi: add qapi2texi script
Date: Fri, 3 Jul 2015 11:51:02 +0200

From: Marc-André Lureau <address@hidden>

As the name suggests, the qapi2texi script converts JSON QAPI
description into a standalone texi file suitable for different target
formats.

As the documentation format doesn't seem to be specified, it parses the
following blocks before each declaration with some variations:

  ##
  # @symbol
  #
  # body
  #
  # @arg: foo
  # @arg: #optional foo
  #
  # Returns: returns
  # Since: version
  # Notes: notes
  ##

Using the json declaration, it's able to give extra information about
the type of arguments and return value expected.

Signed-off-by: Marc-André Lureau <address@hidden>
---
 scripts/qapi.py      |  78 ++++++++++++++++++-
 scripts/qapi2texi.py | 212 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 286 insertions(+), 4 deletions(-)
 create mode 100644 scripts/qapi2texi.py

diff --git a/scripts/qapi.py b/scripts/qapi.py
index 06d7fc2..70208e8 100644
--- a/scripts/qapi.py
+++ b/scripts/qapi.py
@@ -103,6 +103,53 @@ class QAPIExprError(Exception):
         return error_path(self.info['parent']) + \
             "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
 
+class QAPIDoc:
+    def __init__(self, comment):
+        self.symbol = None
+        self.comment = ""
+        self.args = OrderedDict()
+        self.meta = OrderedDict()
+        self.section = None
+
+        for line in comment.split('\n'):
+            line = ' '.join(line.split())
+            split = line.split(' ', 1)
+            key = split[0].rstrip(':')
+
+            if key.startswith("@"):
+                key = key[1:]
+                line = split[1] if len(split) > 1 else ""
+                if self.symbol == None:
+                    self.symbol = key
+                else:
+                    self.start_section(self.args, key)
+            elif key in ("Since", "Returns", "Notes"):
+                line = split[1] if len(split) > 1 else ""
+                self.start_section(self.meta, key)
+            self.append_comment(line)
+
+        self.end_section()
+
+    def append_comment(self, line):
+        if len(line) == 0:
+            return
+        if self.section != None:
+           self.section.append(line)
+        elif self.comment == "":
+            self.comment = line
+        else:
+            self.comment += line if line[0] == ' ' else " " + line
+
+    def end_section(self):
+        if self.section != None:
+            dic = self.section[0]
+            key = self.section[1]
+            dic[key] = " ".join(self.section[2:]).strip()
+
+    def start_section(self, dic, key):
+        self.end_section()
+        self.section = [dic, key, ""]
+
 class QAPISchema:
 
     def __init__(self, fp, previously_included = [], incl_info = None):
@@ -118,11 +165,14 @@ class QAPISchema:
         self.line = 1
         self.line_pos = 0
         self.exprs = []
+        self.comment = ""
+        self.apidoc = []
         self.accept()
 
         while self.tok != None:
             expr_info = {'file': fname, 'line': self.line,
-                         'parent': self.incl_info}
+                         'parent': self.incl_info, 'doc': self.apidoc}
+            self.apidoc = []
             expr = self.get_expr(False)
             if isinstance(expr, dict) and "include" in expr:
                 if len(expr) != 1:
@@ -154,7 +204,7 @@ class QAPISchema:
                 self.exprs.extend(exprs_include.exprs)
             else:
                 expr_elem = {'expr': expr,
-                             'info': expr_info}
+                             'info': expr_info }
                 self.exprs.append(expr_elem)
 
     def accept(self):
@@ -165,8 +215,19 @@ class QAPISchema:
             self.val = None
 
             if self.tok == '#':
-                self.cursor = self.src.find('\n', self.cursor)
-            elif self.tok in ['{', '}', ':', ',', '[', ']']:
+                end = self.src.find('\n', self.cursor)
+                if self.src[self.cursor] != "#":
+                    self.comment += self.src[self.cursor:end+1]
+                self.cursor = end+1
+                continue
+            else:
+                apidoc = QAPIDoc(self.comment)
+                if apidoc.symbol != None or \
+                   not apidoc.comment.find("*-*"):
+                    self.apidoc.append(apidoc)
+                self.comment = ""
+
+            if self.tok in ['{', '}', ':', ',', '[', ']']:
                 return
             elif self.tok == "'":
                 string = ''
@@ -762,6 +823,15 @@ def parse_schema(fname):
         print >>sys.stderr, e
         exit(1)
 
+def parse_schema_full(fname):
+    try:
+        schema = QAPISchema(open(fname, "r"))
+        check_exprs(schema.exprs)
+        return schema.exprs
+    except (QAPISchemaError, QAPIExprError), e:
+        print >>sys.stderr, e
+        exit(1)
+
 #
 # Code generation helpers
 #
diff --git a/scripts/qapi2texi.py b/scripts/qapi2texi.py
new file mode 100644
index 0000000..a3879a4
--- /dev/null
+++ b/scripts/qapi2texi.py
@@ -0,0 +1,212 @@
+# QAPI texi generator
+#
+# This work is licensed under the terms of the GNU GPL, version 2.
+# See the COPYING file in the top-level directory.
+
+from qapi import *
+
+command_fmt = """
address@hidden {type} {{{ret}}} {name} @
+{{{args}}}
+
+{body}
+
address@hidden deftypefn
+
+""".format
+
+enum_fmt = """
address@hidden Enum {name}
+
+{body}
+
address@hidden deftp
+
+""".format
+
+struct_fmt = """
address@hidden {type} {name} @
+{{{attrs}}}
+
+{body}
+
address@hidden deftp
+
+""".format
+
+
+def subst_vars(doc):
+    return re.sub(r'@(\w*)', r'@var{\1}', doc)
+
+
+def texi_args(expr):
+    data = expr["data"] if "data" in expr else {}
+    if type(data) == str:
+        args = data
+    else:
+        args = []
+        for v, t in data.iteritems():
+            if type(t) == str and t.startswith("**"):
+                args.append("address@hidden" % v)
+            elif v.startswith("*"):
+                v = v[1:]
+                args.append("['%s': @var{%s}]" % (v, t))
+            else:
+                args.append("'%s': @var{%s}" % (v, t))
+        args = ", ".join(args)
+    return args
+
+
+def texi_body(doc, arg="@var"):
+    body = "@table %s\n" % arg
+    for arg, c in doc.args.iteritems():
+        if c.startswith("#optional"):
+            c = c[10:]
+            arg += "*"
+        body += "@item %s\n%s\n" % (arg, subst_vars(c))
+    body += "@end table\n"
+    body += subst_vars(doc.comment)
+
+    for k in ("Returns", "Notes", "Since"):
+        if k not in doc.meta:
+            continue
+        body += "address@hidden address@hidden quotation" % \
+                (k, subst_vars(doc.meta[k]))
+    return body
+
+
+def texi_alternate(expr, doc):
+    args = texi_args(expr)
+    body = texi_body(doc)
+    print struct_fmt(type="Alternate",
+                     name=doc.symbol,
+                     attrs="[ " + args + " ]",
+                     body=body)
+
+
+def texi_union(expr, doc):
+    args = texi_args(expr)
+    body = texi_body(doc)
+    print struct_fmt(type="Union",
+                     name=doc.symbol,
+                     attrs="[ " + args + " ]",
+                     body=body)
+
+
+def texi_enum(expr, doc):
+    body = texi_body(doc, "@samp")
+    print enum_fmt(name=doc.symbol,
+                   body=body)
+
+
+def texi_struct(expr, doc):
+    args = texi_args(expr)
+    body = texi_body(doc)
+    print struct_fmt(type="Struct",
+                     name=doc.symbol,
+                     attrs="@{ " + args + " @}",
+                     body=body)
+
+
+def texi_command(expr, doc):
+    args = texi_args(expr)
+    ret = expr["returns"] if "returns" in expr else ""
+    body = texi_body(doc)
+    print command_fmt(type="Command",
+                      name=doc.symbol,
+                      ret=ret,
+                      args="(" + args + ")",
+                      body=body)
+
+
+def texi_event(expr, doc):
+    args = texi_args(expr)
+    body = texi_body(doc)
+    print command_fmt(type="Event",
+                      name=doc.symbol,
+                      ret="",
+                      args="(" + args + ")",
+                      body=body)
+
+
+if len(sys.argv) != 5:
+    print >>sys.stderr, "%s: need exactly 4 arguments" % sys.argv[0]
+    sys.exit(1)
+
+exprs = parse_schema_full(sys.argv[4])
+
+print """
+\input texinfo
address@hidden {filename}
address@hidden en
address@hidden 0
address@hidden 0
+
address@hidden {title}
+
address@hidden
address@hidden
+* QEMU: (qemu-doc).    {title}
address@hidden direntry
address@hidden ifinfo
+
address@hidden
address@hidden {title} {version}
address@hidden titlepage
+
address@hidden
address@hidden Top
address@hidden
+
+This is the API reference for QEMU {version}.
+
address@hidden
+* API Reference::
+* Commands and Events Index::
+* Data Types Index::
address@hidden menu
+
address@hidden ifnottex
+
address@hidden
+
address@hidden API Reference
address@hidden API Reference
+
address@hidden man begin DESCRIPTION
+""".format(title=sys.argv[1], version=sys.argv[2], filename=sys.argv[3])
+
+for cmd in exprs:
+    expr = cmd['expr']
+    docs = cmd['info']['doc']
+    (kind, name) = expr.items()[0]
+
+    for d in docs[0:-1]:
+        print d.comment
+
+    texi = {"command": texi_command,
+            "struct": texi_struct,
+            "enum": texi_enum,
+            "union": texi_union,
+            "alternate": texi_alternate,
+            "event": texi_event}
+    try:
+        texi[kind](expr, docs[-1])
+    except KeyError:
+        raise ValueError("Unknown expression kind '%s'" % kind)
+
+print """
address@hidden man end
+
address@hidden man begin SEEALSO
+The HTML documentation of QEMU for more precise information.
address@hidden man end
+
address@hidden Commands and Events Index
address@hidden Commands and Events Index
address@hidden fn
address@hidden Data Types Index
address@hidden Data Types Index
address@hidden tp
address@hidden
+"""
-- 
2.4.3




reply via email to

[Prev in Thread] Current Thread [Next in Thread]