Commit cf40a0a5 authored by Markus Armbruster's avatar Markus Armbruster Committed by Eric Blake
Browse files

qapi: Record 'include' directives in intermediate representation



The include directive permits modular QAPI schemata, but the generated
code is monolithic all the same.  To permit generating modular code,
the front end needs to pass more information on inclusions to the back
ends.  The commit before last added the necessary information to the
parse tree.  This commit adds it to the intermediate representation
and its QAPISchemaVisitor.  A later commit will use this to to
generate modular code.

New entity QAPISchemaInclude represents inclusions.  Call new visitor
method visit_include() for it, so visitors can see the sub-modules a
module includes.

Note that unlike other entities, QAPISchemaInclude has no name, and is
therefore not added to entity_dict.

New QAPISchemaEntity attribute @module names the entity's source file.
Call new visitor method visit_module() when it changes during a visit,
so visitors can keep track of the module being visited.

Signed-off-by: default avatarMarkus Armbruster <armbru@redhat.com>
Reviewed-by: default avatarMarc-André Lureau <marcandre.lureau@redhat.com>
Message-Id: <20180211093607.27351-18-armbru@redhat.com>
[eblake: avoid accidental deletion of self._predefining]
Signed-off-by: default avatarEric Blake <eblake@redhat.com>
parent 8a84767c
Loading
Loading
Loading
Loading
+39 −4
Original line number Diff line number Diff line
@@ -984,8 +984,9 @@ def check_exprs(exprs):

class QAPISchemaEntity(object):
    def __init__(self, name, info, doc):
        assert isinstance(name, str)
        assert name is None or isinstance(name, str)
        self.name = name
        self.module = None
        # For explicitly defined entities, info points to the (explicit)
        # definition.  For builtins (and their arrays), info is None.
        # For implicitly defined entities, info points to a place that
@@ -1014,10 +1015,16 @@ class QAPISchemaVisitor(object):
    def visit_end(self):
        pass

    def visit_module(self, fname):
        pass

    def visit_needed(self, entity):
        # Default to visiting everything
        return True

    def visit_include(self, fname, info):
        pass

    def visit_builtin_type(self, name, info, json_type):
        pass

@@ -1044,6 +1051,16 @@ class QAPISchemaVisitor(object):
        pass


class QAPISchemaInclude(QAPISchemaEntity):

    def __init__(self, fname, info):
        QAPISchemaEntity.__init__(self, None, info, None)
        self.fname = fname

    def visit(self, visitor):
        visitor.visit_include(self.fname, self.info)


class QAPISchemaType(QAPISchemaEntity):
    # Return the C type for common use.
    # For the types we commonly box, this is a pointer type.
@@ -1471,6 +1488,7 @@ class QAPISchemaEvent(QAPISchemaEntity):

class QAPISchema(object):
    def __init__(self, fname):
        self._fname = fname
        parser = QAPISchemaParser(open(fname, 'r'))
        exprs = check_exprs(parser.exprs)
        self.docs = parser.docs
@@ -1485,9 +1503,13 @@ class QAPISchema(object):
    def _def_entity(self, ent):
        # Only the predefined types are allowed to not have info
        assert ent.info or self._predefining
        assert ent.name not in self._entity_dict
        assert ent.name is None or ent.name not in self._entity_dict
        self._entity_list.append(ent)
        if ent.name is not None:
            self._entity_dict[ent.name] = ent
        if ent.info:
            ent.module = os.path.relpath(ent.info['file'],
                                         os.path.dirname(self._fname))

    def lookup_entity(self, name, typ=None):
        ent = self._entity_dict.get(name)
@@ -1498,6 +1520,15 @@ class QAPISchema(object):
    def lookup_type(self, name):
        return self.lookup_entity(name, QAPISchemaType)

    def _def_include(self, expr, info, doc):
        include = expr['include']
        assert doc is None
        main_info = info
        while main_info['parent']:
            main_info = main_info['parent']
        fname = os.path.relpath(include, os.path.dirname(main_info['file']))
        self._def_entity(QAPISchemaInclude(fname, info))

    def _def_builtin_type(self, name, json_type, c_type):
        self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
        # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
@@ -1680,7 +1711,7 @@ class QAPISchema(object):
            elif 'event' in expr:
                self._def_event(expr, info, doc)
            elif 'include' in expr:
                pass
                self._def_include(expr, info, doc)
            else:
                assert False

@@ -1690,8 +1721,12 @@ class QAPISchema(object):

    def visit(self, visitor):
        visitor.visit_begin(self)
        module = None
        for entity in self._entity_list:
            if visitor.visit_needed(entity):
                if entity.module != module:
                    module = entity.module
                    visitor.visit_module(module)
                entity.visit(visitor)
        visitor.visit_end()

+1 −0
Original line number Diff line number Diff line
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
    prefix QTYPE
module comments.json
enum Status ['good', 'bad', 'ugly']
+1 −0
Original line number Diff line number Diff line
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
    prefix QTYPE
module doc-bad-section.json
enum Enum ['one', 'two']
doc symbol=Enum
    body=
+1 −0
Original line number Diff line number Diff line
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
    prefix QTYPE
module doc-good.json
enum Enum ['one', 'two']
object Base
    member base1: Enum optional=False
+1 −0
Original line number Diff line number Diff line
object q_empty
enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool']
    prefix QTYPE
module event-case.json
event oops None
   boxed=False
Loading