554 lines
17 KiB
Python
554 lines
17 KiB
Python
import re
|
|
import sys
|
|
import xml.etree.ElementTree as _etree
|
|
|
|
class Doc(object):
|
|
def __init__(self, text):
|
|
object.__init__(self)
|
|
self.text = text
|
|
|
|
@staticmethod
|
|
def from_xml(elm):
|
|
return Doc(elm.text)
|
|
|
|
def _set_unique_attribute(obj, attr, value):
|
|
if getattr(obj, attr) is not None:
|
|
raise RuntimeError("duplicated attribute '%s'" % (attr,))
|
|
setattr(obj, attr, value)
|
|
|
|
def _set_unique_attribute_bool(obj, attr, value):
|
|
if value.lower() in ('0', 'false', 'no'):
|
|
value = False
|
|
elif value.lower() in ('1', 'true', 'yes'):
|
|
value = True
|
|
else:
|
|
raise RuntimeError("bad value '%s' for boolean attribute '%s'" % (value, attr))
|
|
_set_unique_attribute(obj, attr, value)
|
|
|
|
class _XmlObject(object):
|
|
def __init__(self):
|
|
object.__init__(self)
|
|
self.doc = None
|
|
self.summary = None
|
|
self.annotations = {}
|
|
self.module = None
|
|
|
|
@classmethod
|
|
def from_xml(cls, module, elm, *args):
|
|
obj = cls()
|
|
obj.module = module
|
|
obj._parse_xml(module, elm, *args)
|
|
return obj
|
|
|
|
def _parse_xml_element(self, module, elm):
|
|
if elm.tag in ('doc', 'summary'):
|
|
_set_unique_attribute(self, elm.tag, Doc.from_xml(elm))
|
|
else:
|
|
raise RuntimeError('unknown element %s' % (elm.tag,))
|
|
|
|
def _parse_attribute(self, attr, value):
|
|
if attr.find('.') >= 0:
|
|
self.annotations[attr] = value
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def _parse_xml(self, module, elm, *args):
|
|
for attr, value in elm.items():
|
|
if not self._parse_attribute(attr, value):
|
|
raise RuntimeError('unknown attribute %s' % (attr,))
|
|
for child in elm.getchildren():
|
|
self._parse_xml_element(module, child)
|
|
|
|
class _ParamBase(_XmlObject):
|
|
def __init__(self):
|
|
_XmlObject.__init__(self)
|
|
self.type = None
|
|
self.transfer_mode = None
|
|
|
|
def _parse_attribute(self, attr, value):
|
|
if attr in ('type', 'transfer_mode'):
|
|
_set_unique_attribute(self, attr, value)
|
|
else:
|
|
return _XmlObject._parse_attribute(self, attr, value)
|
|
return True
|
|
|
|
class Param(_ParamBase):
|
|
def __init__(self):
|
|
_ParamBase.__init__(self)
|
|
self.name = None
|
|
self.default_value = None
|
|
self.allow_none = None
|
|
|
|
def _parse_attribute(self, attr, value):
|
|
if attr in ('default_value', 'name'):
|
|
_set_unique_attribute(self, attr, value)
|
|
elif attr in ('allow_none',):
|
|
_set_unique_attribute_bool(self, attr, value)
|
|
else:
|
|
return _ParamBase._parse_attribute(self, attr, value)
|
|
return True
|
|
|
|
class Retval(_ParamBase):
|
|
def __init__(self):
|
|
_ParamBase.__init__(self)
|
|
|
|
class FunctionBase(_XmlObject):
|
|
def __init__(self):
|
|
_XmlObject.__init__(self)
|
|
self.name = None
|
|
self.c_name = None
|
|
self.retval = None
|
|
self.params = []
|
|
self.has_gerror_return = False
|
|
self.kwargs = None
|
|
|
|
def _parse_attribute(self, attr, value):
|
|
if attr in ('c_name', 'name'):
|
|
_set_unique_attribute(self, attr, value)
|
|
elif attr in ('kwargs',):
|
|
_set_unique_attribute_bool(self, attr, value)
|
|
else:
|
|
return _XmlObject._parse_attribute(self, attr, value)
|
|
return True
|
|
|
|
def _parse_xml_element(self, module, elm):
|
|
if elm.tag == 'retval':
|
|
_set_unique_attribute(self, 'retval', Retval.from_xml(module, elm))
|
|
elif elm.tag == 'param':
|
|
self.params.append(Param.from_xml(module, elm))
|
|
else:
|
|
_XmlObject._parse_xml_element(self, module, elm)
|
|
|
|
def _parse_xml(self, module, elm, *args):
|
|
_XmlObject._parse_xml(self, module, elm, *args)
|
|
if not self.name:
|
|
raise RuntimeError('function name missing')
|
|
if not self.c_name:
|
|
raise RuntimeError('function c_name missing')
|
|
|
|
class Function(FunctionBase):
|
|
def __init__(self):
|
|
FunctionBase.__init__(self)
|
|
|
|
def symbol_id(self):
|
|
return self.c_name
|
|
|
|
class MethodBase(FunctionBase):
|
|
def __init__(self):
|
|
super(MethodBase, self).__init__()
|
|
|
|
def _parse_xml(self, module, elm, cls):
|
|
super(MethodBase, self)._parse_xml(module, elm, cls)
|
|
self.cls = cls
|
|
|
|
class Constructor(MethodBase):
|
|
def __init__(self):
|
|
super(Constructor, self).__init__()
|
|
|
|
def symbol_id(self):
|
|
return self.c_name
|
|
|
|
class StaticMethod(MethodBase):
|
|
def __init__(self):
|
|
super(StaticMethod, self).__init__()
|
|
|
|
def symbol_id(self):
|
|
return self.c_name
|
|
|
|
class Method(MethodBase):
|
|
def __init__(self):
|
|
super(Method, self).__init__()
|
|
|
|
def symbol_id(self):
|
|
return self.c_name
|
|
|
|
class VMethod(MethodBase):
|
|
def __init__(self):
|
|
super(VMethod, self).__init__()
|
|
self.c_name = "fake"
|
|
|
|
def symbol_id(self):
|
|
return '%s::%s' % (self.cls.name, self.name)
|
|
|
|
class Signal(MethodBase):
|
|
def __init__(self):
|
|
super(Signal, self).__init__()
|
|
self.c_name = "fake"
|
|
|
|
def symbol_id(self):
|
|
return 'signal:%s:%s' % (self.cls.name, self.name)
|
|
|
|
class Type(_XmlObject):
|
|
def __init__(self):
|
|
_XmlObject.__init__(self)
|
|
self.name = None
|
|
self.c_name = None
|
|
self.gtype_id = None
|
|
|
|
def symbol_id(self):
|
|
return self.name
|
|
|
|
class BasicType(Type):
|
|
def __init__(self, name):
|
|
Type.__init__(self)
|
|
self.name = name
|
|
|
|
class ArrayType(Type):
|
|
def __init__(self, elm_type):
|
|
Type.__init__(self)
|
|
self.elm_type = elm_type
|
|
self.name = '%sArray*' % elm_type.name
|
|
self.c_name = '%sArray*' % elm_type.name
|
|
|
|
class GErrorReturnType(Type):
|
|
def __init__(self):
|
|
Type.__init__(self)
|
|
self.name = 'GError**'
|
|
|
|
class GTypedType(Type):
|
|
def __init__(self):
|
|
Type.__init__(self)
|
|
self.short_name = None
|
|
|
|
def _parse_attribute(self, attr, value):
|
|
if attr in ('short_name', 'name', 'gtype_id'):
|
|
_set_unique_attribute(self, attr, value)
|
|
else:
|
|
return Type._parse_attribute(self, attr, value)
|
|
return True
|
|
|
|
def _parse_xml(self, module, elm, *args):
|
|
Type._parse_xml(self, module, elm, *args)
|
|
if self.name is None:
|
|
raise RuntimeError('class name missing')
|
|
if self.short_name is None:
|
|
raise RuntimeError('class short name missing')
|
|
if self.gtype_id is None:
|
|
raise RuntimeError('class gtype missing')
|
|
|
|
class EnumValue(_XmlObject):
|
|
def __init__(self):
|
|
super(EnumValue, self).__init__()
|
|
self.name = None
|
|
self.short_name = None
|
|
self.enum = None
|
|
|
|
def symbol_id(self):
|
|
return self.name
|
|
|
|
def _parse_attribute(self, attr, value):
|
|
if attr in ('name'):
|
|
_set_unique_attribute(self, attr, value)
|
|
else:
|
|
return super(EnumValue, self)._parse_attribute(attr, value)
|
|
return True
|
|
|
|
def _parse_xml(self, module, elm, *args):
|
|
super(EnumValue, self)._parse_xml(module, elm, *args)
|
|
if self.name is None:
|
|
raise RuntimeError('enum value name missing')
|
|
if not self.short_name:
|
|
self.short_name = self.name
|
|
prefix = module.name.upper() + '_'
|
|
if self.short_name.startswith(prefix):
|
|
self.short_name = self.short_name[len(prefix):]
|
|
|
|
class EnumBase(GTypedType):
|
|
def __init__(self):
|
|
super(EnumBase, self).__init__()
|
|
self.values = []
|
|
self.__value_hash = {}
|
|
|
|
def _parse_xml_element(self, module, elm):
|
|
if elm.tag == 'value':
|
|
value = EnumValue.from_xml(module, elm)
|
|
assert not value.name in self.__value_hash
|
|
self.__value_hash[value.name] = value
|
|
value.enum = self
|
|
self.values.append(value)
|
|
else:
|
|
super(EnumBase, self)._parse_xml_element(module, elm)
|
|
|
|
class Enum(EnumBase):
|
|
def __init__(self):
|
|
super(Enum, self).__init__()
|
|
|
|
class Flags(EnumBase):
|
|
def __init__(self):
|
|
super(Flags, self).__init__()
|
|
|
|
class InstanceType(GTypedType):
|
|
def __init__(self):
|
|
GTypedType.__init__(self)
|
|
self.constructor = None
|
|
self.methods = []
|
|
self.static_methods = []
|
|
self.__method_hash = {}
|
|
|
|
def _parse_xml_element(self, module, elm):
|
|
if elm.tag == 'method':
|
|
meth = Method.from_xml(module, elm, self)
|
|
assert not meth.name in self.__method_hash
|
|
self.__method_hash[meth.name] = meth
|
|
self.methods.append(meth)
|
|
elif elm.tag == 'static-method':
|
|
meth = StaticMethod.from_xml(module, elm, self)
|
|
assert not meth.name in self.__method_hash
|
|
self.__method_hash[meth.name] = meth
|
|
self.static_methods.append(meth)
|
|
elif elm.tag == 'constructor':
|
|
assert not self.constructor
|
|
self.constructor = Constructor.from_xml(module, elm, self)
|
|
else:
|
|
GTypedType._parse_xml_element(self, module, elm)
|
|
|
|
class Pointer(InstanceType):
|
|
def __init__(self):
|
|
InstanceType.__init__(self)
|
|
|
|
class Boxed(InstanceType):
|
|
def __init__(self):
|
|
InstanceType.__init__(self)
|
|
|
|
class Class(InstanceType):
|
|
def __init__(self):
|
|
InstanceType.__init__(self)
|
|
self.parent = None
|
|
self.vmethods = []
|
|
self.signals = []
|
|
self.constructable = None
|
|
self.__vmethod_hash = {}
|
|
self.__signal_hash = {}
|
|
|
|
def _parse_attribute(self, attr, value):
|
|
if attr in ('parent'):
|
|
_set_unique_attribute(self, attr, value)
|
|
elif attr in ('constructable'):
|
|
_set_unique_attribute_bool(self, attr, value)
|
|
else:
|
|
return InstanceType._parse_attribute(self, attr, value)
|
|
return True
|
|
|
|
def _parse_xml_element(self, module, elm):
|
|
if elm.tag == 'virtual':
|
|
meth = VMethod.from_xml(module, elm, self)
|
|
assert not meth.name in self.__vmethod_hash
|
|
self.__vmethod_hash[meth.name] = meth
|
|
self.vmethods.append(meth)
|
|
elif elm.tag == 'signal':
|
|
meth = Signal.from_xml(module, elm, self)
|
|
assert not meth.name in self.__signal_hash
|
|
self.__signal_hash[meth.name] = meth
|
|
self.signals.append(meth)
|
|
else:
|
|
InstanceType._parse_xml_element(self, module, elm)
|
|
|
|
def _parse_xml(self, module, elm, *args):
|
|
InstanceType._parse_xml(self, module, elm, *args)
|
|
if self.parent is None:
|
|
raise RuntimeError('class parent name missing')
|
|
if self.constructable and self.constructor:
|
|
raise RuntimeError('both constructor and constructable attributes present')
|
|
|
|
class Module(object):
|
|
def __init__(self):
|
|
object.__init__(self)
|
|
self.name = None
|
|
self.__classes = []
|
|
self.__boxed = []
|
|
self.__pointers = []
|
|
self.__enums = []
|
|
self.__class_hash = {}
|
|
self.__functions = []
|
|
self.__function_hash = {}
|
|
self.__import_modules = []
|
|
self.__parsing_done = False
|
|
self.__types = {}
|
|
self.__symbols = {}
|
|
|
|
def __add_type(self, typ):
|
|
if typ.name in self.__class_hash:
|
|
raise RuntimeError('duplicated type %s' % typ.name)
|
|
self.__class_hash[typ.name] = typ
|
|
|
|
def __finish_type(self, typ):
|
|
if isinstance(typ, Type):
|
|
return typ
|
|
if typ in self.__types:
|
|
return self.__types[typ]
|
|
if typ == 'GError**':
|
|
return GErrorReturnType()
|
|
m = re.match(r'(const-)?([\w\d_]+)\*', typ)
|
|
if m:
|
|
name = m.group(2)
|
|
if name in self.__types:
|
|
obj_type = self.__types[name]
|
|
if isinstance(obj_type, InstanceType):
|
|
return obj_type
|
|
m = re.match(r'Array<([\w\d_]+)>\*', typ)
|
|
if m:
|
|
elm_type = self.__finish_type(m.group(1))
|
|
return ArrayType(elm_type)
|
|
m = re.match(r'([\w\d_]+)Array\*', typ)
|
|
if m:
|
|
elm_type = self.__finish_type(m.group(1))
|
|
return ArrayType(elm_type)
|
|
return BasicType(typ)
|
|
|
|
def __finish_parsing_method(self, meth, typ):
|
|
sym_id = meth.symbol_id()
|
|
if self.__symbols.get(sym_id):
|
|
raise RuntimeError('duplicate symbol %s' % sym_id)
|
|
self.__symbols[sym_id] = meth
|
|
|
|
for p in meth.params:
|
|
p.type = self.__finish_type(p.type)
|
|
if meth.retval:
|
|
meth.retval.type = self.__finish_type(meth.retval.type)
|
|
|
|
meth.has_gerror_return = False
|
|
if meth.params and isinstance(meth.params[-1].type, GErrorReturnType):
|
|
meth.has_gerror_return = True
|
|
|
|
if meth.kwargs:
|
|
params = list(meth.params)
|
|
pos_args = []
|
|
kw_args = []
|
|
if meth.has_gerror_return:
|
|
params = params[:-1]
|
|
seen_kwarg = False
|
|
for p in params:
|
|
if p.default_value is not None:
|
|
seen_kwarg = True
|
|
elif seen_kwarg:
|
|
raise RuntimeError('in %s: parameter without a default value follows a kwarg one' % sym_id)
|
|
if p.default_value is not None:
|
|
kw_args.append(p)
|
|
else:
|
|
pos_args.append(p)
|
|
|
|
def __finish_parsing_type(self, typ):
|
|
if hasattr(typ, 'constructor') and typ.constructor is not None:
|
|
self.__finish_parsing_method(typ.constructor, typ)
|
|
for meth in typ.static_methods:
|
|
self.__finish_parsing_method(meth, typ)
|
|
for meth in typ.methods:
|
|
self.__finish_parsing_method(meth, typ)
|
|
if hasattr(typ, 'vmethods'):
|
|
for meth in typ.vmethods:
|
|
self.__finish_parsing_method(meth, typ)
|
|
if hasattr(typ, 'signals'):
|
|
for meth in typ.signals:
|
|
self.__finish_parsing_method(meth, typ)
|
|
|
|
def __finish_parsing_enum(self, typ):
|
|
for v in typ.values:
|
|
sym_id = v.symbol_id()
|
|
if self.__symbols.get(sym_id):
|
|
raise RuntimeError('duplicate symbol %s' % sym_id)
|
|
self.__symbols[sym_id] = v
|
|
|
|
def __add_type_symbol(self, typ):
|
|
sym_id = typ.symbol_id()
|
|
if self.__symbols.get(sym_id):
|
|
raise RuntimeError('duplicate symbol %s' % sym_id)
|
|
self.__symbols[sym_id] = typ
|
|
|
|
def __finish_parsing(self):
|
|
if self.__parsing_done:
|
|
return
|
|
|
|
for typ in self.__classes + self.__boxed + self.__pointers + self.__enums:
|
|
self.__add_type_symbol(typ)
|
|
self.__types[typ.name] = typ
|
|
|
|
for module in self.__import_modules:
|
|
for typ in module.get_classes() + module.get_boxed() + \
|
|
module.get_pointers() + module.get_enums():
|
|
self.__types[typ.name] = typ
|
|
for sym in module.__symbols:
|
|
if self.__symbols.get(sym):
|
|
raise RuntimeError('duplicate symbol %s' % sym)
|
|
self.__symbols[sym] = module.__symbols[sym]
|
|
|
|
for typ in self.__classes + self.__boxed + self.__pointers:
|
|
self.__finish_parsing_type(typ)
|
|
|
|
for typ in self.__enums:
|
|
self.__finish_parsing_enum(typ)
|
|
|
|
for func in self.__functions:
|
|
self.__finish_parsing_method(func, None)
|
|
|
|
self.__parsing_done = True
|
|
|
|
def get_classes(self):
|
|
self.__finish_parsing()
|
|
return list(self.__classes)
|
|
|
|
def get_boxed(self):
|
|
self.__finish_parsing()
|
|
return list(self.__boxed)
|
|
|
|
def get_pointers(self):
|
|
self.__finish_parsing()
|
|
return list(self.__pointers)
|
|
|
|
def get_enums(self):
|
|
self.__finish_parsing()
|
|
return list(self.__enums)
|
|
|
|
def get_functions(self):
|
|
self.__finish_parsing()
|
|
return list(self.__functions)
|
|
|
|
def get_symbols(self):
|
|
self.__finish_parsing()
|
|
return dict(self.__symbols)
|
|
|
|
def __parse_module_entry(self, elm):
|
|
if elm.tag == 'class':
|
|
cls = Class.from_xml(self, elm)
|
|
self.__add_type(cls)
|
|
self.__classes.append(cls)
|
|
elif elm.tag == 'boxed':
|
|
cls = Boxed.from_xml(self, elm)
|
|
self.__add_type(cls)
|
|
self.__boxed.append(cls)
|
|
elif elm.tag == 'pointer':
|
|
cls = Pointer.from_xml(self, elm)
|
|
self.__add_type(cls)
|
|
self.__pointers.append(cls)
|
|
elif elm.tag == 'enum':
|
|
enum = Enum.from_xml(self, elm)
|
|
self.__add_type(enum)
|
|
self.__enums.append(enum)
|
|
elif elm.tag == 'flags':
|
|
enum = Flags.from_xml(self, elm)
|
|
self.__add_type(enum)
|
|
self.__enums.append(enum)
|
|
elif elm.tag == 'function':
|
|
func = Function.from_xml(self, elm)
|
|
assert not func.name in self.__function_hash
|
|
self.__function_hash[func.name] = func
|
|
self.__functions.append(func)
|
|
else:
|
|
raise RuntimeError('unknown element %s' % (elm.tag,))
|
|
|
|
def import_module(self, mod):
|
|
self.__import_modules.append(mod)
|
|
|
|
@staticmethod
|
|
def from_xml(filename):
|
|
mod = Module()
|
|
xml = _etree.ElementTree()
|
|
xml.parse(filename)
|
|
root = xml.getroot()
|
|
assert root.tag == 'module'
|
|
mod.name = root.get('name')
|
|
assert mod.name is not None
|
|
for elm in root.getchildren():
|
|
mod.__parse_module_entry(elm)
|
|
return mod
|