import sys import re import mdp.docparser as dparser DEBUG = False def split_camel_case_name(name): comps = [] cur = '' for c in name: if c.islower() or not cur: cur += c else: comps.append(cur) cur = c if cur: comps.append(cur) return comps def get_class_method_c_name_prefix(cls): comps = split_camel_case_name(cls) return '_'.join([c.lower() for c in comps]) + '_' def strip_class_prefix(name, cls): prefix = get_class_method_c_name_prefix(cls) if name.startswith(prefix): return name[len(prefix):] else: return name def strip_module_prefix(name, mod): prefix = get_class_method_c_name_prefix(mod) if name.startswith(prefix): return name[len(prefix):] else: return name def strip_module_prefix_from_class(name, mod): mod = mod.lower() mod = mod[0].upper() + mod[1:] if name.startswith(mod): return name[len(mod):] else: return name def make_gtype_id(cls): comps = split_camel_case_name(cls) comps = [comps[0]] + ['TYPE'] + comps[1:] return '_'.join([c.upper() for c in comps]) class Type(object): def __init__(self, name): object.__init__(self) self.name = name class BasicType(Type): def __init__(self, name): Type.__init__(self, name) class _GTypedType(Type): def __init__(self, name, short_name, gtype_id, docs): Type.__init__(self, name) self.docs = docs self.methods = [] self.static_methods = [] self.gtype_id = gtype_id self.short_name = short_name self.annotations = {} class EnumBase(_GTypedType): def __init__(self, name, short_name, gtype_id, docs): super(EnumBase, self).__init__(name, short_name, gtype_id, docs) self.values = [] class EnumValue(object): def __init__(self, name, attributes, docs): super(EnumValue, self).__init__() self.name = name self.attributes = attributes self.docs = docs class Enum(EnumBase): def __init__(self, name, short_name, gtype_id, docs): super(Enum, self).__init__(name, short_name, gtype_id, docs) class Flags(EnumBase): def __init__(self, name, short_name, gtype_id, docs): super(Flags, self).__init__(name, short_name, gtype_id, docs) class _InstanceType(_GTypedType): def __init__(self, name, short_name, gtype_id, docs): _GTypedType.__init__(self, name, short_name, gtype_id, docs) self.constructor = None class Class(_InstanceType): def __init__(self, name, short_name, parent, gtype_id, docs): _InstanceType.__init__(self, name, short_name, gtype_id, docs) self.parent = parent self.vmethods = [] self.signals = [] class Boxed(_InstanceType): def __init__(self, name, short_name, gtype_id, docs): _InstanceType.__init__(self, name, short_name, gtype_id, docs) class Pointer(_InstanceType): def __init__(self, name, short_name, gtype_id, docs): _InstanceType.__init__(self, name, short_name, gtype_id, docs) class Symbol(object): def __init__(self, name, c_name, docs): object.__init__(self) self.name = name self.c_name = c_name self.docs = docs self.summary = None self.annotations = {} class FunctionBase(Symbol): def __init__(self, name, c_name, params, retval, docs): Symbol.__init__(self, name, c_name, docs) self.params = params self.retval = retval class Function(FunctionBase): def __init__(self, name, c_name, params, retval, docs): FunctionBase.__init__(self, name, c_name, params, retval, docs) class Method(FunctionBase): def __init__(self, name, c_name, cls, params, retval, docs): FunctionBase.__init__(self, name, c_name, params, retval, docs) self.cls = cls class StaticMethod(FunctionBase): def __init__(self, name, c_name, cls, params, retval, docs): FunctionBase.__init__(self, name, c_name, params, retval, docs) self.cls = cls class Constructor(FunctionBase): def __init__(self, name, c_name, cls, params, retval, docs): FunctionBase.__init__(self, name, c_name, params, retval, docs) self.cls = cls class VMethod(FunctionBase): def __init__(self, name, cls, params, retval, docs): FunctionBase.__init__(self, name, name, params, retval, docs) self.cls = cls class Signal(FunctionBase): def __init__(self, name, cls, params, retval, docs): FunctionBase.__init__(self, name, name, params, retval, docs) self.cls = cls class ParamBase(object): def __init__(self, typ, docs): object.__init__(self) self.type = typ self.docs = docs self.attributes = {} self.transfer_mode = None self.element_type = None self.array = False self.array_fixed_len = None self.array_len_param = None self.array_zero_terminated = None class Param(ParamBase): def __init__(self, name, typ, docs): ParamBase.__init__(self, typ, docs) self.name = name self.out = False self.caller_allocates = False self.callee_allocates = False self.in_ = False self.inout = False self.allow_none = False self.default_value = None self.scope = 'call' class Retval(ParamBase): def __init__(self, typ, docs): ParamBase.__init__(self, typ, docs) class Module(object): def __init__(self, name): object.__init__(self) self.name = name self.classes = [] self.boxed = [] self.__class_dict = {} self.functions = [] self.__methods = {} self.__constructors = {} self.__vmethods = {} self.__signals = {} self.types = {} self.enums = [] def __add_class(self, pcls): name = pcls.name short_name = getattr(pcls, 'short_name', strip_module_prefix_from_class(pcls.name, self.name)) gtype_id = getattr(pcls, 'gtype_id', make_gtype_id(pcls.name)) docs = pcls.docs parent = None constructable = False annotations = {} for a in pcls.annotations: pieces = a.split() prefix = pieces[0] if prefix == 'parent': assert len(pieces) == 2 parent = pieces[1] elif prefix == 'constructable': assert len(pieces) == 1 constructable = True elif prefix.find('.') >= 0: annotations[prefix] = ' '.join(pieces[1:]) else: raise RuntimeError("unknown annotation '%s' in class %s" % (a, name)) cls = Class(name, short_name, parent, gtype_id, docs) cls.summary = pcls.summary cls.annotations = annotations cls.constructable = constructable self.classes.append(cls) self.__class_dict[name] = cls def __add_boxed_or_pointer(self, pcls, What): name = pcls.name short_name = getattr(pcls, 'short_name', strip_module_prefix_from_class(pcls.name, self.name)) gtype_id = getattr(pcls, 'gtype_id', make_gtype_id(pcls.name)) docs = pcls.docs annotations = {} for a in pcls.annotations: pieces = a.split() prefix = pieces[0] if prefix.find('.') >= 0: annotations[prefix] = ' '.join(pieces[1:]) else: raise RuntimeError("unknown annotation '%s' in class %s" % (a, name)) cls = What(name, short_name, gtype_id, docs) cls.summary = pcls.summary cls.annotations = annotations self.boxed.append(cls) self.__class_dict[name] = cls def __add_boxed(self, pcls): self.__add_boxed_or_pointer(pcls, Boxed) def __add_pointer(self, pcls): self.__add_boxed_or_pointer(pcls, Pointer) def __add_enum(self, ptyp): if DEBUG: print 'enum', ptyp.name name = ptyp.name short_name = getattr(ptyp, 'short_name', strip_module_prefix_from_class(ptyp.name, self.name)) gtype_id = getattr(ptyp, 'gtype_id', make_gtype_id(ptyp.name)) docs = ptyp.docs annotations = {} for a in ptyp.annotations: pieces = a.split() prefix = pieces[0] if prefix.find('.') >= 0: annotations[prefix] = ' '.join(pieces[1:]) else: raise RuntimeError("unknown annotation '%s' in class %s" % (a, name)) if isinstance(ptyp, dparser.Enum): enum = Enum(name, short_name, gtype_id, docs) else: enum = Flags(name, short_name, gtype_id, docs) for value in ptyp.values: attributes = self.__parse_enum_value_annotations(value.annotations) enum.values.append(EnumValue(value.name, attributes, value.docs)) enum.summary = ptyp.summary enum.annotations = annotations self.enums.append(enum) def __parse_enum_value_annotations(self, annotations): attributes = {} for a in annotations: pieces = a.split() prefix = pieces[0] if '.' in prefix[1:-1] and len(pieces) == 2: attributes[prefix] = pieces[1] if attributes: return attributes else: return None def __parse_param_or_retval_annotation(self, annotation, param): pieces = annotation.split() prefix = pieces[0] if prefix == 'transfer': if len(pieces) > 2: raise RuntimeError("invalid annotation '%s'" % (a,)) if not pieces[1] in ('none', 'container', 'full'): raise RuntimeError("invalid annotation '%s'" % (a,)) param.transfer_mode = pieces[1] return True if prefix == 'element-type': if len(pieces) > 3: raise RuntimeError("invalid annotation '%s'" % (a,)) if len(pieces) == 2: param.element_type = pieces[1] else: param.element_type = pieces[1:] return True if prefix == 'array': if len(pieces) == 1: param.array = True return True if len(pieces) > 2: raise RuntimeError("invalid annotation '%s'" % (a,)) m = re.match(r'fixed-size\s*=\s*(\d+)$', pieces[1]) if m: param.array_fixed_size = int(m.group(1)) return True m = re.match(r'length\s*=\s*(\S+)$', pieces[1]) if m: param.array_len_param = m.group(1) return True m = re.match(r'zero-terminated\s*=\s*(\d+)$', pieces[1]) if m: param.array_zero_terminated = bool(int(m.group(1))) return True raise RuntimeError("invalid annotation '%s'" % (a,)) if prefix == 'type': if len(pieces) > 2: raise RuntimeError("invalid annotation '%s'" % (a,)) param.type = pieces[1] return True if '.' in prefix[1:-1] and len(pieces) == 2: param.attributes[prefix] = pieces[1] return False def __parse_param_annotation(self, annotation, param): pieces = annotation.split() prefix = pieces[0] if prefix == 'out': if len(pieces) > 2: raise RuntimeError("invalid annotation '%s'" % (a,)) if len(pieces) == 1: param.out = True return True if pieces[1] == 'caller-allocates': param.out = True param.caller_allocates = True return True if pieces[1] == 'callee-allocates': param.out = True param.callee_allocates = True return True raise RuntimeError("invalid annotation '%s'" % (a,)) if prefix == 'in': if len(pieces) > 1: raise RuntimeError("invalid annotation '%s'" % (a,)) param.in_ = True return True if prefix == 'inout': if len(pieces) > 1: raise RuntimeError("invalid annotation '%s'" % (a,)) param.inout = True return True if prefix == 'allow-none': if len(pieces) > 1: raise RuntimeError("invalid annotation '%s'" % (a,)) param.allow_none = True return True if prefix == 'default': if len(pieces) != 2: raise RuntimeError("invalid annotation '%s'" % (a,)) param.default_value = pieces[1] return True if prefix == 'scope': if len(pieces) != 2: raise RuntimeError("invalid annotation '%s'" % (a,)) if not pieces[1] in ('call', 'async', 'notified'): raise RuntimeError("invalid annotation '%s'" % (a,)) param.scope = pieces[1] return True if '.' in prefix[1:-1] and len(pieces) == 2: param.attributes[prefix] = pieces[1] return False def __parse_retval(self, pretval): retval = Retval(pretval.type, pretval.docs) if pretval.annotations: for a in pretval.annotations: if not self.__parse_param_or_retval_annotation(a, retval): raise RuntimeError("invalid annotation '%s'" % (a,)) if not retval.type: raise RuntimeError('return type missing') return retval def __parse_param(self, pp, pfunc): if DEBUG: print pp.name, pp.type, pp.docs param = Param(pp.name, pp.type, pp.docs) if pp.annotations: for a in pp.annotations: if self.__parse_param_or_retval_annotation(a, param): pass elif self.__parse_param_annotation(a, param): pass else: raise RuntimeError("in %s: invalid annotation '%s'" % (pfunc.name, a,)) if param.type is None: raise RuntimeError('in %s: type of param "%s" is missing' % (pfunc.name, param.name)) return param def __add_vmethod(self, pfunc): _, cls, name = pfunc.name.split(':') assert _ == 'vfunc' params = [] retval = None docs = pfunc.docs if pfunc.annotations: for a in pfunc.annotations: raise RuntimeError("unknown annotation '%s' in function %s" % (a, name)) if pfunc.params: for p in pfunc.params: params.append(self.__parse_param(p, pfunc)) if pfunc.retval: retval = self.__parse_retval(pfunc.retval) meth = VMethod(name, cls, params[1:], retval, docs) this_class_methods = self.__vmethods.get(cls) if not this_class_methods: this_class_methods = [] self.__vmethods[cls] = this_class_methods this_class_methods.append(meth) def __add_signal(self, pfunc): _, cls, name = pfunc.name.split(':') assert _ == 'signal' params = [] retval = None docs = pfunc.docs if pfunc.annotations: for a in pfunc.annotations: raise RuntimeError("unknown annotation '%s' in function %s" % (a, name)) if pfunc.params: for p in pfunc.params: params.append(self.__parse_param(p, pfunc)) if pfunc.retval: retval = self.__parse_retval(pfunc.retval) meth = Signal(name, cls, params[1:], retval, docs) this_class_signals = self.__signals.get(cls) if not this_class_signals: this_class_signals = [] self.__signals[cls] = this_class_signals this_class_signals.append(meth) def __add_function(self, pfunc): name = pfunc.name c_name = pfunc.name params = [] retval = None docs = pfunc.docs cls = None constructor_of = None static_method_of = None kwargs = False annotations = {} if pfunc.annotations: for a in pfunc.annotations: pieces = a.split() prefix = pieces[0] if prefix == 'constructor-of': assert len(pieces) == 2 constructor_of = pieces[1] elif prefix == 'static-method-of': assert len(pieces) == 2 static_method_of = pieces[1] elif prefix == 'moo-kwargs': assert len(pieces) == 1 kwargs = True elif prefix.find('.') >= 0: annotations[prefix] = ' '.join(pieces[1:]) else: raise RuntimeError("unknown annotation '%s' in function %s" % (a, name)) if pfunc.params: for p in pfunc.params: params.append(self.__parse_param(p, pfunc)) if pfunc.retval: retval = self.__parse_retval(pfunc.retval) if hasattr(pfunc, 'method_of'): cls = pfunc.method_of elif static_method_of: cls = static_method_of elif constructor_of: cls = constructor_of elif params: m = re.match(r'(const-)?([\w\d_]+)\*', params[0].type) if m: cls = m.group(2) if not self.__class_dict.has_key(cls): cls = None if cls: name = strip_class_prefix(name, cls) else: name = strip_module_prefix(name, self.name) if constructor_of: func = Constructor(name, c_name, cls, params, retval, docs) if constructor_of in self.__constructors: raise RuntimeError('duplicated constructor of class %s' % constructor_of) self.__constructors[constructor_of] = func elif cls: if static_method_of: func = StaticMethod(name, c_name, cls, params, retval, docs) else: func = Method(name, c_name, cls, params[1:], retval, docs) this_class_methods = self.__methods.get(cls) if not this_class_methods: this_class_methods = [] self.__methods[cls] = this_class_methods this_class_methods.append(func) else: func = Function(name, c_name, params, retval, docs) self.functions.append(func) func.summary = pfunc.summary func.annotations = annotations func.kwargs = kwargs def init_from_dox(self, blocks): for b in blocks: if isinstance(b, dparser.Class): self.__add_class(b) elif isinstance(b, dparser.Boxed): self.__add_boxed(b) elif isinstance(b, dparser.Pointer): self.__add_pointer(b) elif isinstance(b, dparser.Enum) or isinstance(b, dparser.Flags): self.__add_enum(b) elif isinstance(b, dparser.Flags): self.__add_flags(b) elif isinstance(b, dparser.VMethod): self.__add_vmethod(b) elif isinstance(b, dparser.Signal): self.__add_signal(b) elif isinstance(b, dparser.Function): self.__add_function(b) else: raise RuntimeError('oops') instance_types = {} for cls in self.classes + self.boxed: if cls.name in instance_types: raise RuntimeError('duplicated class %s' % (cls.name,)) instance_types[cls.name] = cls for cls in self.__constructors: func = self.__constructors[cls] if not cls in instance_types: raise RuntimeError('Constructor of unknown class %s' % cls) else: cls = instance_types[cls] if cls.constructor is not None: raise RuntimeError('duplicated constructor in class %s' % cls) cls.constructor = func for cls in self.__methods: methods = self.__methods[cls] if not cls in instance_types: raise RuntimeError('Methods of unknown class %s' % cls) else: cls = instance_types[cls] for m in methods: m.cls = cls if isinstance(m, Method): cls.methods.append(m) elif isinstance(m, StaticMethod): cls.static_methods.append(m) else: oops() for cls in self.__vmethods: methods = self.__vmethods[cls] if not cls in instance_types: raise RuntimeError('Virtual methods of unknown class %s' % cls) else: cls = instance_types[cls] for m in methods: m.cls = cls cls.vmethods += methods for cls in self.__signals: signals = self.__signals[cls] if not cls in instance_types: raise RuntimeError('Signals of unknown class %s' % cls) else: cls = instance_types[cls] for s in signals: s.cls = cls cls.signals += signals def format_func(func): if func.retval and func.retval.type: s = func.retval.type + ' ' else: s = 'void ' s += func.name s += ' (' for i in range(len(func.params)): if i != 0: s += ', ' p = func.params[i] s += '%s %s' % (p.type, p.name) s += ')' return s if DEBUG: for cls in self.classes: print 'class %s' % (cls.name,) for meth in cls.methods: print ' %s' % (format_func(meth),) for func in self.functions: print format_func(func)