medfall

A super great game engine
Log | Files | Refs

parse.py (10381B)


      1 try:
      2     from lxml import etree
      3     from lxml.etree import ETCompatXMLParser as parser
      4 
      5     def xml_fromstring(argument):
      6         return etree.fromstring(argument, parser=parser())
      7 
      8     def xml_frompath(path):
      9         return etree.parse(path, parser=parser()).getroot()
     10 except ImportError:
     11     try:
     12         import xml.etree.cElementTree as etree
     13     except ImportError:
     14         import xml.etree.ElementTree as etree
     15 
     16     def xml_fromstring(argument):
     17         return etree.fromstring(argument)
     18 
     19     def xml_frompath(path):
     20         return etree.parse(path).getroot()
     21 
     22 
     23 from collections import defaultdict, OrderedDict
     24 from contextlib import closing
     25 from itertools import chain
     26 import re
     27 
     28 from glad.opener import URLOpener
     29 
     30 
     31 _ARRAY_RE = re.compile(r'\[\d*\]')
     32 
     33 
     34 class Spec(object):
     35     API = 'https://cvs.khronos.org/svn/repos/ogl/trunk/doc/registry/public/api/'
     36     NAME = ''
     37 
     38     def __init__(self, root):
     39         self.root = root
     40 
     41         self._types = None
     42         self._groups = None
     43         self._enums = None
     44         self._commands = None
     45         self._features = None
     46         self._extensions = None
     47 
     48     @classmethod
     49     def from_url(cls, url, opener=None):
     50         if opener is None:
     51             opener = URLOpener.default()
     52 
     53         with closing(opener.urlopen(url)) as f:
     54             raw = f.read()
     55 
     56         return cls(xml_fromstring(raw))
     57 
     58     @classmethod
     59     def from_svn(cls, opener=None):
     60         return cls.from_url(cls.API + cls.NAME + '.xml', opener=opener)
     61 
     62     @classmethod
     63     def fromstring(cls, string):
     64         return cls(xml_fromstring(string))
     65 
     66     @classmethod
     67     def from_file(cls, path):
     68         return cls(xml_frompath(path))
     69 
     70     @property
     71     def comment(self):
     72         return self.root.find('comment').text
     73 
     74     @property
     75     def types(self):
     76         if self._types is None:
     77             self._types = [Type(element) for element in
     78                            self.root.find('types').iter('type')]
     79         return self._types
     80 
     81     @property
     82     def groups(self):
     83         if self._groups is None:
     84             self._groups = dict([(element.attrib['name'], Group(element))
     85                                  for element in self.root.find('groups')])
     86         return self._groups
     87 
     88     @property
     89     def commands(self):
     90         if self._commands is None:
     91             self._commands = dict([(element.find('proto').find('name').text,
     92                                     Command(element, self))
     93                                    for element in self.root.find('commands')])
     94         return self._commands
     95 
     96     @property
     97     def enums(self):
     98         if self._enums is not None:
     99             return self._enums
    100 
    101         self._enums = dict()
    102         for element in self.root.iter('enums'):
    103             namespace = element.attrib['namespace']
    104             type_ = element.get('type')
    105             group = element.get('group')
    106             vendor = element.get('vendor')
    107             comment = element.get('comment', '')
    108 
    109             for enum in element:
    110                 if enum.tag == 'unused':
    111                     continue
    112                 assert enum.tag == 'enum'
    113 
    114                 name = enum.attrib['name']
    115                 self._enums[name] = Enum(name, enum.attrib['value'], namespace,
    116                                          type_, group, vendor, comment)
    117 
    118         return self._enums
    119 
    120     @property
    121     def features(self):
    122         if self._features is not None:
    123             return self._features
    124 
    125         self._features = defaultdict(OrderedDict)
    126         for element in self.root.iter('feature'):
    127             num = tuple(map(int, element.attrib['number'].split('.')))
    128             self._features[element.attrib['api']][num] = Feature(element, self)
    129 
    130         return self._features
    131 
    132     @property
    133     def extensions(self):
    134         if self._extensions is not None:
    135             return self._extensions
    136 
    137         self._extensions = defaultdict(dict)
    138         for element in self.root.find('extensions'):
    139             for api in element.attrib['supported'].split('|'):
    140                 self._extensions[api][element.attrib['name']] = Extension(element, self)
    141 
    142         return self._extensions
    143 
    144 
    145 class Type(object):
    146     def __init__(self, element):
    147         apientry = element.find('apientry')
    148         if apientry is not None:
    149             apientry.text = 'APIENTRY'
    150         self.raw = ''.join(element.itertext())
    151         self.api = element.get('api')
    152         self.name = element.get('name')
    153 
    154     @property
    155     def is_preprocessor(self):
    156         return '#' in self.raw
    157 
    158 
    159 class Group(object):
    160     def __init__(self, element):
    161         self.name = element.attrib['name']
    162         self.enums = [enum.attrib['name'] for enum in element]
    163 
    164 
    165 class Enum(object):
    166     def __init__(self, name, value, namespace, type_=None,
    167                  group=None, vendor=None, comment=''):
    168         self.name = name
    169         self.value = value
    170         self.namespace = namespace
    171         self.type = type_
    172         self.group = group
    173         self.vendor = vendor
    174         self.comment = comment
    175 
    176     def __hash__(self):
    177         return hash(self.name)
    178 
    179     def __str__(self):
    180         return self.name
    181 
    182     __repr__ = __str__
    183 
    184 
    185 class Command(object):
    186     def __init__(self, element, spec):
    187         self.proto = Proto(element.find('proto'))
    188         self.params = [Param(ele, spec) for ele in element.iter('param')]
    189 
    190     def __hash__(self):
    191         return hash(self.proto.name)
    192 
    193     def __str__(self):
    194         return '{self.proto.name}'.format(self=self)
    195 
    196     __repr__ = __str__
    197 
    198 
    199 class Proto(object):
    200     def __init__(self, element):
    201         self.name = element.find('name').text
    202         self.ret = OGLType(element)
    203 
    204     def __str__(self):
    205         return '{self.ret} {self.name}'.format(self=self)
    206 
    207 
    208 class Param(object):
    209     def __init__(self, element, spec):
    210         self.group = element.get('group')
    211         self.type = OGLType(element)
    212         self.name = element.find('name').text.strip('*')
    213 
    214     def __str__(self):
    215         return '{0!r} {1}'.format(self.type, self.name)
    216 
    217 
    218 class OGLType(object):
    219     def __init__(self, element):
    220         self.element = element
    221         self.raw = ''.join(element.itertext()).strip()
    222 
    223         self.name = element.find('name').text
    224 
    225         self.type = (self.raw.replace('const', '').replace('unsigned', '')
    226                      .replace('struct', '').strip().split(None, 1)[0]
    227                      if element.find('ptype') is None else element.find('ptype').text)
    228         # 0 if no pointer, 1 if *, 2 if **
    229         self.is_pointer = 0 if self.raw is None else self.raw.count('*')
    230         # it can be a pointer to an array, or just an array
    231         self.is_pointer += len(_ARRAY_RE.findall(self.raw))
    232         self.is_const = False if self.raw is None else 'const' in self.raw
    233         self.is_unsigned = False if self.raw is None else 'unsigned' in self.raw
    234 
    235         if 'struct' in self.raw and 'struct' not in self.type:
    236             self.type = 'struct {}'.format(self.type)
    237 
    238         ptype = element.find('ptype')
    239         self.ptype = ptype.text if ptype is not None else None
    240 
    241     def to_d(self):
    242         if self.is_pointer > 1 and self.is_const:
    243             s = 'const({}{}*)'.format('u' if self.is_unsigned else '', self.type)
    244             s += '*' * (self.is_pointer - 1)
    245         else:
    246             t = '{}{}'.format('u' if self.is_unsigned else '', self.type)
    247             s = 'const({})'.format(t) if self.is_const else t
    248             s += '*' * self.is_pointer
    249         return s.replace('struct ', '')
    250 
    251     to_volt = to_d
    252 
    253     def to_c(self):
    254         result = ''
    255         for text in self.element.itertext():
    256             if text == self.name:
    257                 # yup * is sometimes part of the name
    258                 result += '*' * text.count('*')
    259             else:
    260                 result += text
    261         result = _ARRAY_RE.sub('*', result)
    262         return result.strip()
    263 
    264     NIM_POINTER_MAP = {
    265       'void': 'pointer',
    266       'GLchar': 'cstring',
    267       'struct _cl_context': 'ClContext',
    268       'struct _cl_event': 'ClEvent'
    269     }
    270 
    271     def to_nim(self):
    272         if self.is_pointer == 2:
    273           s = 'cstringArray' if self.type == 'GLchar' else 'ptr pointer'
    274         else:
    275           s = self.type
    276           if self.is_pointer == 1:
    277             default  = 'ptr ' + s
    278             s = self.NIM_POINTER_MAP.get(s, default)
    279         return s
    280 
    281     __str__ = to_d
    282     __repr__ = __str__
    283 
    284 
    285 class Extension(object):
    286     def __init__(self, element, spec):
    287         self.name = element.attrib['name']
    288 
    289         self.require = []
    290         for required in chain.from_iterable(element.findall('require')):
    291             if required.tag == 'type':
    292                 continue
    293 
    294             data = {'enum': spec.enums, 'command': spec.commands}[required.tag]
    295             try:
    296                 self.require.append(data[required.attrib['name']])
    297             except KeyError:
    298                 pass  # TODO
    299 
    300     @property
    301     def enums(self):
    302         for r in self.require:
    303             if isinstance(r, Enum):
    304                 yield r
    305 
    306     @property
    307     def functions(self):
    308         for r in self.require:
    309             if isinstance(r, Command):
    310                 yield r
    311 
    312     def __hash__(self):
    313         return hash(self.name)
    314 
    315     def __str__(self):
    316         return self.name
    317 
    318     __repr__ = __str__
    319 
    320 
    321 class Feature(Extension):
    322     def __init__(self, element, spec):
    323         Extension.__init__(self, element, spec)
    324         self.spec = spec
    325 
    326         # not every spec has a ._remove member, but there shouldn't be a remove
    327         # tag without that member, if there is, blame me!
    328         for removed in chain.from_iterable(element.findall('remove')):
    329             if removed.tag == 'type':
    330                 continue
    331 
    332             data = {'enum': spec.enums, 'command': spec.commands}[removed.tag]
    333             try:
    334                 spec._remove.add(data[removed.attrib['name']])
    335             except KeyError:
    336                 pass  # TODO
    337 
    338         self.number = tuple(map(int, element.attrib['number'].split('.')))
    339         self.api = element.attrib['api']
    340 
    341     def __str__(self):
    342         return '{self.name}@{self.number!r}'.format(self=self)
    343 
    344     @property
    345     def enums(self):
    346         for enum in super(Feature, self).enums:
    347             if enum not in getattr(self.spec, 'removed', []):
    348                 yield enum
    349 
    350     @property
    351     def functions(self):
    352         for func in super(Feature, self).functions:
    353             if func not in getattr(self.spec, 'removed', []):
    354                 yield func
    355 
    356     __repr__ = __str__