medfall

A super great game engine
Log | Files | Refs

generator.py (7837B)


      1 from collections import defaultdict
      2 from datetime import datetime
      3 from itertools import chain
      4 import os.path
      5 import sys
      6 
      7 from glad.lang.common.loader import NullLoader
      8 from glad.opener import URLOpener
      9 from glad.util import api_name
     10 import glad
     11 
     12 
     13 if sys.version_info >= (3, 0):
     14     from urllib.parse import urlencode
     15 else:
     16     from urllib import urlencode
     17 
     18 
     19 HEADER_TEMPLATE = '''
     20     {apis_named} loader generated by glad {version} on {date}.
     21 
     22     Language/Generator: {language}
     23     Specification: {specification}
     24     APIs: {apis}
     25     Profile: {profile}
     26     Extensions:
     27         {extensions}
     28     Loader: {loader}
     29     Local files: {local_files}
     30     Omit khrplatform: {omit_khrplatform}
     31 
     32     Commandline:
     33         {commandline}
     34     Online:
     35         {online}
     36 '''
     37 
     38 
     39 class Generator(object):
     40     NAME = None
     41     NAME_LONG = None
     42     URL = 'http://glad.dav1d.de'
     43 
     44     def __init__(self, path, spec, api, extension_names=None, loader=None,
     45                  opener=None, local_files=False, omit_khrplatform=False,
     46                  header_template=HEADER_TEMPLATE):
     47         self.path = os.path.abspath(path)
     48 
     49         self.spec = spec
     50         for a in api:
     51             if a not in self.spec.features:
     52                 raise ValueError(
     53                     'Unknown API "{0}" for specification "{1}"'
     54                     .format(a, self.spec.NAME)
     55                 )
     56         self.api = api
     57         self.extension_names = extension_names
     58 
     59         self.has_loader = not loader.disabled
     60         self.loader = loader
     61         if self.loader is None:
     62             self.loader = NullLoader
     63 
     64         self.opener = opener
     65         if self.opener is None:
     66             self.opener = URLOpener.default()
     67 
     68         self.local_files = local_files
     69         self.omit_khrplatform = omit_khrplatform
     70 
     71         self._header_template = header_template
     72 
     73     def open(self):
     74         raise NotImplementedError
     75 
     76     def close(self):
     77         raise NotImplementedError
     78 
     79     def __enter__(self):
     80         self.open()
     81         return self
     82 
     83     def __exit__(self, exc_type, exc_value, traceback):
     84         self.close()
     85 
     86     def generate(self):
     87         features = list()
     88         for api, version in self.api.items():
     89             features.extend(self.spec.features[api])
     90 
     91             if version is None:
     92                 version = list(self.spec.features[api].keys())[-1]
     93                 self.api[api] = version
     94 
     95             if version not in self.spec.features[api]:
     96                 raise ValueError(
     97                     'Unknown version "{0}" for specification "{1}"'
     98                     .format(version, self.spec.NAME)
     99                 )
    100 
    101         if self.extension_names is None:
    102            self.extension_names = list(chain.from_iterable(self.spec.extensions[a]
    103                                                            for a in self.api))
    104 
    105         # sort and eliminate duplicates
    106         self.extension_names = list(sorted(set(self.extension_names)))
    107 
    108         e = list(chain.from_iterable(self.spec.extensions[a] for a in self.api))
    109         for ext in self.extension_names:
    110             if ext not in e:
    111                 raise ValueError(
    112                     'Invalid extension "{0}" for specification "{1}"'
    113                     .format(ext, self.spec.NAME)
    114                 )
    115 
    116         self.generate_header()
    117 
    118         types = [t for t in self.spec.types if t.api is None or t.api in self.api]
    119         self.generate_types(types)
    120 
    121         f = list()
    122         for api, version in self.api.items():
    123             f.extend([value for key, value in self.spec.features[api].items()
    124                         if key <= version])
    125         enums, functions = merge(f)
    126         self.generate_features(f)
    127 
    128         extensions = list()
    129         for api in self.api:
    130             extensions.extend(self.spec.extensions[api][ext]
    131                               for ext in self.extension_names if ext
    132                               in self.spec.extensions[api])
    133         self.generate_extensions(extensions, enums, functions)
    134 
    135         fs = defaultdict(list)
    136         es = defaultdict(list)
    137         for api, version in self.api.items():
    138             fs[api].extend(
    139                 [value for key, value in
    140                  self.spec.features[api].items() if key <= version]
    141             )
    142             es[api].extend(self.spec.extensions[api][ext]
    143                            for ext in self.extension_names if ext
    144                            in self.spec.extensions[api])
    145         self.generate_loader(fs, es)
    146 
    147     @property
    148     def header(self):
    149         apis_named = ', '.join(sorted(set(api_name(api) for api in self.api)))
    150         date = datetime.now().strftime('%c')
    151         language = self.NAME_LONG
    152         specification = self.spec.NAME
    153         apis = ', '.join('{}={}'.format(api, '.'.join(map(str, version))) for api, version in self.api.items())
    154         profile = getattr(self.spec, 'profile', '-')
    155         extensions = ',\n        '.join(self.extension_names)
    156         online = self.online
    157         if len(online) > 2000:
    158             online = 'Too many extensions'
    159 
    160         return self._header_template.format(
    161             apis_named=apis_named,
    162             version=glad.__version__,
    163             date=date,
    164             language=language,
    165             specification=specification,
    166             apis=apis,
    167             profile=profile,
    168             extensions=extensions,
    169             loader=self.has_loader,
    170             local_files=self.local_files,
    171             omit_khrplatform=self.omit_khrplatform,
    172             commandline=self.commandline,
    173             online=online
    174         )
    175 
    176     @property
    177     def commandline(self):
    178         profile = getattr(self.spec, 'profile', None)
    179         if profile is not None:
    180             profile = '--profile="{}"'.format(profile)
    181 
    182         api = '--api="{}"'.format(','.join(
    183             '{}={}'.format(api, '.'.join(map(str, version))) for api, version in self.api.items())
    184         )
    185         generator = '--generator="{}"'.format(self.NAME)
    186         specification = '--spec="{}"'.format(self.spec.NAME)
    187         loader = '' if self.has_loader else '--no-loader'
    188         extensions = '--extensions="{}"'.format(','.join(self.extension_names))
    189         local_files = '--local-files' if self.local_files else ''
    190         omit_khrplatform = '--omit-khrplatform' if self.omit_khrplatform else ''
    191 
    192         return ' '.join(filter(None, [
    193             profile, api, generator, specification,
    194             loader, local_files, omit_khrplatform, extensions
    195         ]))
    196 
    197     @property
    198     def online(self):
    199         profile = getattr(self.spec, 'profile', None)
    200         if profile is not None:
    201             profile = ('profile', profile)
    202 
    203         api = [('api', s) for s in ('{}={}'.format(api, '.'.join(map(str, version))) for api, version in self.api.items())]
    204         generator = ('language', self.NAME)
    205         specification = ('specification', self.spec.NAME)
    206         loader = ('loader', 'on') if self.has_loader else None
    207         extensions = [('extensions', ext) for ext in self.extension_names]
    208 
    209         data = [profile, generator, specification, loader]
    210         data.extend(api)
    211         data.extend(extensions)
    212         data = list(filter(None, data))
    213         serialized = urlencode(data)
    214 
    215         # TODO: --local-files, --omit-khrplatform
    216         return '{}/#{}'.format(self.URL, serialized)
    217 
    218     def generate_header(self):
    219         raise NotImplementedError
    220 
    221     def generate_loader(self, features, extensions):
    222         raise NotImplementedError
    223 
    224     def generate_types(self, types):
    225         raise NotImplementedError
    226 
    227     def generate_features(self, features):
    228         raise NotImplementedError
    229 
    230     def generate_extensions(self, extensions, enums, functions):
    231         raise NotImplementedError
    232 
    233 
    234 def merge(features):
    235     enums = set()
    236     functions = set()
    237 
    238     for feature in features:
    239         enums |= set(feature.enums)
    240         functions |= set(feature.functions)
    241 
    242     return enums, functions