qapi: Proper intermediate representation for modules

Modules are represented only by their names so far.  Introduce class
QAPISchemaModule.  So far, it merely wraps the name.  The next patch
will put it to more interesting use.

Once again, arrays spice up the patch a bit.  For any other type,
@info points to the definition, which lets us map from @info to
module.  For arrays, there is no definition, and @info points to the
first use instead.  We have to use the element type's module instead,
which is only available after .check().

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20191120182551.23795-5-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
master
Markus Armbruster 2019-11-20 19:25:49 +01:00
parent 00ca24ff9e
commit a9f1dd7ee0
1 changed files with 43 additions and 20 deletions

View File

@ -50,9 +50,6 @@ class QAPISchemaEntity(object):
def check(self, schema): def check(self, schema):
assert not self._checked assert not self._checked
if self.info:
self._module = os.path.relpath(self.info.fname,
os.path.dirname(schema.fname))
seen = {} seen = {}
for f in self.features: for f in self.features:
f.check_clash(self.info, seen) f.check_clash(self.info, seen)
@ -68,6 +65,13 @@ class QAPISchemaEntity(object):
if self.doc: if self.doc:
self.doc.check() self.doc.check()
def _set_module(self, schema, info):
assert self._checked
self._module = schema.module_by_fname(info and info.fname)
def set_module(self, schema):
self._set_module(schema, self.info)
@property @property
def ifcond(self): def ifcond(self):
assert self._checked assert self._checked
@ -75,7 +79,7 @@ class QAPISchemaEntity(object):
@property @property
def module(self): def module(self):
assert self._checked assert self._module or not self.info
return self._module return self._module
def is_implicit(self): def is_implicit(self):
@ -135,15 +139,19 @@ class QAPISchemaVisitor(object):
pass pass
class QAPISchemaInclude(QAPISchemaEntity): class QAPISchemaModule(object):
def __init__(self, name):
self.name = name
def __init__(self, fname, info):
class QAPISchemaInclude(QAPISchemaEntity):
def __init__(self, sub_module, info):
QAPISchemaEntity.__init__(self, None, info, None) QAPISchemaEntity.__init__(self, None, info, None)
self.fname = fname self._sub_module = sub_module
def visit(self, visitor): def visit(self, visitor):
QAPISchemaEntity.visit(self, visitor) QAPISchemaEntity.visit(self, visitor)
visitor.visit_include(self.fname, self.info) visitor.visit_include(self._sub_module.name, self.info)
class QAPISchemaType(QAPISchemaEntity): class QAPISchemaType(QAPISchemaEntity):
@ -276,16 +284,14 @@ class QAPISchemaArrayType(QAPISchemaType):
self.info and self.info.defn_meta) self.info and self.info.defn_meta)
assert not isinstance(self.element_type, QAPISchemaArrayType) assert not isinstance(self.element_type, QAPISchemaArrayType)
def set_module(self, schema):
self._set_module(schema, self.element_type.info)
@property @property
def ifcond(self): def ifcond(self):
assert self._checked assert self._checked
return self.element_type.ifcond return self.element_type.ifcond
@property
def module(self):
assert self._checked
return self.element_type.module
def is_implicit(self): def is_implicit(self):
return True return True
@ -783,6 +789,10 @@ class QAPISchema(object):
self.docs = parser.docs self.docs = parser.docs
self._entity_list = [] self._entity_list = []
self._entity_dict = {} self._entity_dict = {}
self._module_dict = {}
self._schema_dir = os.path.dirname(fname)
self._make_module(None) # built-ins
self._make_module(fname)
self._predefining = True self._predefining = True
self._def_predefineds() self._def_predefineds()
self._predefining = False self._predefining = False
@ -826,14 +836,26 @@ class QAPISchema(object):
info, "%s uses unknown type '%s'" % (what, name)) info, "%s uses unknown type '%s'" % (what, name))
return typ return typ
def _module_name(self, fname):
if fname is None:
return None
return os.path.relpath(fname, self._schema_dir)
def _make_module(self, fname):
name = self._module_name(fname)
if not name in self._module_dict:
self._module_dict[name] = QAPISchemaModule(name)
return self._module_dict[name]
def module_by_fname(self, fname):
name = self._module_name(fname)
assert name in self._module_dict
return self._module_dict[name]
def _def_include(self, expr, info, doc): def _def_include(self, expr, info, doc):
include = expr['include'] include = expr['include']
assert doc is None assert doc is None
main_info = info self._def_entity(QAPISchemaInclude(self._make_module(include), info))
while main_info.parent:
main_info = main_info.parent
fname = os.path.relpath(include, os.path.dirname(main_info.fname))
self._def_entity(QAPISchemaInclude(fname, info))
def _def_builtin_type(self, name, json_type, c_type): def _def_builtin_type(self, name, json_type, c_type):
self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type)) self._def_entity(QAPISchemaBuiltinType(name, json_type, c_type))
@ -1065,15 +1087,16 @@ class QAPISchema(object):
ent.check(self) ent.check(self)
ent.connect_doc() ent.connect_doc()
ent.check_doc() ent.check_doc()
for ent in self._entity_list:
ent.set_module(self)
def visit(self, visitor): def visit(self, visitor):
visitor.visit_begin(self) visitor.visit_begin(self)
module = None module = None
visitor.visit_module(module)
for entity in self._entity_list: for entity in self._entity_list:
if visitor.visit_needed(entity): if visitor.visit_needed(entity):
if entity.module != module: if entity.module != module:
module = entity.module module = entity.module
visitor.visit_module(module) visitor.visit_module(module.name)
entity.visit(visitor) entity.visit(visitor)
visitor.visit_end() visitor.visit_end()