• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3 -i
2#
3# Copyright (c) 2013-2020 The Khronos Group Inc.
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17# Base class for working-group-specific style conventions,
18# used in generation.
19
20from enum import Enum
21
22# Type categories that respond "False" to isStructAlwaysValid
23# basetype is home to typedefs like ..Bool32
24CATEGORIES_REQUIRING_VALIDATION = set(('handle',
25                                       'enum',
26                                       'bitmask',
27                                       'basetype',
28                                       None))
29
30# These are basic C types pulled in via openxr_platform_defines.h
31TYPES_KNOWN_ALWAYS_VALID = set(('char',
32                                'float',
33                                'int8_t', 'uint8_t',
34                                'int32_t', 'uint32_t',
35                                'int64_t', 'uint64_t',
36                                'size_t',
37                                'uintptr_t',
38                                'int',
39                                ))
40
41
42class ProseListFormats(Enum):
43    """A connective, possibly with a quantifier."""
44    AND = 0
45    EACH_AND = 1
46    OR = 2
47    ANY_OR = 3
48
49    @classmethod
50    def from_string(cls, s):
51        if s == 'or':
52            return cls.OR
53        if s == 'and':
54            return cls.AND
55        return None
56
57    @property
58    def connective(self):
59        if self in (ProseListFormats.OR, ProseListFormats.ANY_OR):
60            return 'or'
61        return 'and'
62
63    def quantifier(self, n):
64        """Return the desired quantifier for a list of a given length."""
65        if self == ProseListFormats.ANY_OR:
66            if n > 1:
67                return 'any of '
68        elif self == ProseListFormats.EACH_AND:
69            if n > 2:
70                return 'each of '
71            if n == 2:
72                return 'both of '
73        return ''
74
75
76class ConventionsBase:
77    """WG-specific conventions."""
78
79    def __init__(self):
80        self._command_prefix = None
81        self._type_prefix = None
82
83    def formatExtension(self, name):
84        """Mark up an extension name as a link the spec."""
85        return '`apiext:{}`'.format(name)
86
87    @property
88    def null(self):
89        """Preferred spelling of NULL."""
90        raise NotImplementedError
91
92    def makeProseList(self, elements, fmt=ProseListFormats.AND, with_verb=False, *args, **kwargs):
93        """Make a (comma-separated) list for use in prose.
94
95        Adds a connective (by default, 'and')
96        before the last element if there are more than 1.
97
98        Adds the right one of "is" or "are" to the end if with_verb is true.
99
100        Optionally adds a quantifier (like 'any') before a list of 2 or more,
101        if specified by fmt.
102
103        Override with a different method or different call to
104        _implMakeProseList if you want to add a comma for two elements,
105        or not use a serial comma.
106        """
107        return self._implMakeProseList(elements, fmt, with_verb, *args, **kwargs)
108
109    @property
110    def struct_macro(self):
111        """Get the appropriate format macro for a structure.
112
113        May override.
114        """
115        return 'slink:'
116
117    @property
118    def external_macro(self):
119        """Get the appropriate format macro for an external type like uint32_t.
120
121        May override.
122        """
123        return 'code:'
124
125    def makeStructName(self, name):
126        """Prepend the appropriate format macro for a structure to a structure type name.
127
128        Uses struct_macro, so just override that if you want to change behavior.
129        """
130        return self.struct_macro + name
131
132    def makeExternalTypeName(self, name):
133        """Prepend the appropriate format macro for an external type like uint32_t to a type name.
134
135        Uses external_macro, so just override that if you want to change behavior.
136        """
137        return self.external_macro + name
138
139    def _implMakeProseList(self, elements, fmt, with_verb, comma_for_two_elts=False, serial_comma=True):
140        """Internal-use implementation to make a (comma-separated) list for use in prose.
141
142        Adds a connective (by default, 'and')
143        before the last element if there are more than 1,
144        and only includes commas if there are more than 2
145        (if comma_for_two_elts is False).
146
147        Adds the right one of "is" or "are" to the end if with_verb is true.
148
149        Optionally adds a quantifier (like 'any') before a list of 2 or more,
150        if specified by fmt.
151
152        Don't edit these defaults, override self.makeProseList().
153        """
154        assert(serial_comma)  # didn't implement what we didn't need
155        if isinstance(fmt, str):
156            fmt = ProseListFormats.from_string(fmt)
157
158        my_elts = list(elements)
159        if len(my_elts) > 1:
160            my_elts[-1] = '{} {}'.format(fmt.connective, my_elts[-1])
161
162        if not comma_for_two_elts and len(my_elts) <= 2:
163            prose = ' '.join(my_elts)
164        else:
165            prose = ', '.join(my_elts)
166
167        quantifier = fmt.quantifier(len(my_elts))
168
169        parts = [quantifier, prose]
170
171        if with_verb:
172            if len(my_elts) > 1:
173                parts.append(' are')
174            else:
175                parts.append(' is')
176        return ''.join(parts)
177
178    @property
179    def file_suffix(self):
180        """Return suffix of generated Asciidoctor files"""
181        raise NotImplementedError
182
183    def api_name(self, spectype=None):
184        """Return API or specification name for citations in ref pages.
185
186        spectype is the spec this refpage is for.
187        'api' (the default value) is the main API Specification.
188        If an unrecognized spectype is given, returns None.
189
190        Must implement."""
191        raise NotImplementedError
192
193    def should_insert_may_alias_macro(self, genOpts):
194        """Return true if we should insert a "may alias" macro in this file.
195
196        Only used by OpenXR right now."""
197        return False
198
199    @property
200    def command_prefix(self):
201        """Return the expected prefix of commands/functions.
202
203        Implemented in terms of api_prefix."""
204        if not self._command_prefix:
205            self._command_prefix = self.api_prefix[:].replace('_', '').lower()
206        return self._command_prefix
207
208    @property
209    def type_prefix(self):
210        """Return the expected prefix of type names.
211
212        Implemented in terms of command_prefix (and in turn, api_prefix)."""
213        if not self._type_prefix:
214            self._type_prefix = ''.join(
215                (self.command_prefix[0:1].upper(), self.command_prefix[1:]))
216        return self._type_prefix
217
218    @property
219    def api_prefix(self):
220        """Return API token prefix.
221
222        Typically two uppercase letters followed by an underscore.
223
224        Must implement."""
225        raise NotImplementedError
226
227    @property
228    def api_version_prefix(self):
229        """Return API core version token prefix.
230
231        Implemented in terms of api_prefix.
232
233        May override."""
234        return self.api_prefix + 'VERSION_'
235
236    @property
237    def KHR_prefix(self):
238        """Return extension name prefix for KHR extensions.
239
240        Implemented in terms of api_prefix.
241
242        May override."""
243        return self.api_prefix + 'KHR_'
244
245    @property
246    def EXT_prefix(self):
247        """Return extension name prefix for EXT extensions.
248
249        Implemented in terms of api_prefix.
250
251        May override."""
252        return self.api_prefix + 'EXT_'
253
254    def writeFeature(self, featureExtraProtect, filename):
255        """Return True if OutputGenerator.endFeature should write this feature.
256
257        Defaults to always True.
258        Used in COutputGenerator.
259
260        May override."""
261        return True
262
263    def requires_error_validation(self, return_type):
264        """Return True if the return_type element is an API result code
265        requiring error validation.
266
267        Defaults to always False.
268
269        May override."""
270        return False
271
272    @property
273    def required_errors(self):
274        """Return a list of required error codes for validation.
275
276        Defaults to an empty list.
277
278        May override."""
279        return []
280
281    def is_voidpointer_alias(self, tag, text, tail):
282        """Return True if the declaration components (tag,text,tail) of an
283        element represents a void * type.
284
285        Defaults to a reasonable implementation.
286
287        May override."""
288        return tag == 'type' and text == 'void' and tail.startswith('*')
289
290    def make_voidpointer_alias(self, tail):
291        """Reformat a void * declaration to include the API alias macro.
292
293        Defaults to a no-op.
294
295        Must override if you actually want to use this feature in your project."""
296        return tail
297
298    def category_requires_validation(self, category):
299        """Return True if the given type 'category' always requires validation.
300
301        Defaults to a reasonable implementation.
302
303        May override."""
304        return category in CATEGORIES_REQUIRING_VALIDATION
305
306    def type_always_valid(self, typename):
307        """Return True if the given type name is always valid (never requires validation).
308
309        This is for things like integers.
310
311        Defaults to a reasonable implementation.
312
313        May override."""
314        return typename in TYPES_KNOWN_ALWAYS_VALID
315
316    @property
317    def should_skip_checking_codes(self):
318        """Return True if more than the basic validation of return codes should
319        be skipped for a command."""
320
321        return False
322
323    @property
324    def generate_index_terms(self):
325        """Return True if asiidoctor index terms should be generated as part
326           of an API interface from the docgenerator."""
327
328        return False
329
330    @property
331    def generate_enum_table(self):
332        """Return True if asciidoctor tables describing enumerants in a
333           group should be generated as part of group generation."""
334        return False
335
336    def extension_include_string(self, ext):
337        """Return format string for include:: line for an extension appendix
338           file. ext is an object with the following members:
339            - name - extension string string
340            - vendor - vendor portion of name
341            - barename - remainder of name
342
343        Must implement."""
344        raise NotImplementedError
345
346    @property
347    def refpage_generated_include_path(self):
348        """Return path relative to the generated reference pages, to the
349           generated API include files.
350
351        Must implement."""
352        raise NotImplementedError
353
354    def valid_flag_bit(self, bitpos):
355        """Return True if bitpos is an allowed numeric bit position for
356           an API flag.
357
358           Behavior depends on the data type used for flags (which may be 32
359           or 64 bits), and may depend on assumptions about compiler
360           handling of sign bits in enumerated types, as well."""
361        return True
362