mirror of
https://github.com/ZDoom/zdoom-macos-deps.git
synced 2024-12-14 22:31:24 +00:00
563 lines
17 KiB
Python
563 lines
17 KiB
Python
#!/usr/bin/env python3 -i
|
|
#
|
|
# Copyright 2013-2024 The Khronos Group Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
# Base class for working-group-specific style conventions,
|
|
# used in generation.
|
|
|
|
from enum import Enum
|
|
import abc
|
|
import re
|
|
|
|
# Type categories that respond "False" to isStructAlwaysValid
|
|
# basetype is home to typedefs like ..Bool32
|
|
CATEGORIES_REQUIRING_VALIDATION = set(('handle',
|
|
'enum',
|
|
'bitmask',
|
|
'basetype',
|
|
None))
|
|
|
|
# These are basic C types pulled in via openxr_platform_defines.h
|
|
TYPES_KNOWN_ALWAYS_VALID = set(('char',
|
|
'float',
|
|
'int8_t', 'uint8_t',
|
|
'int16_t', 'uint16_t',
|
|
'int32_t', 'uint32_t',
|
|
'int64_t', 'uint64_t',
|
|
'size_t',
|
|
'intptr_t', 'uintptr_t',
|
|
'int',
|
|
))
|
|
|
|
# Split an extension name into vendor ID and name portions
|
|
EXT_NAME_DECOMPOSE_RE = re.compile(r'(?P<prefix>[A-Za-z]+)_(?P<vendor>[A-Za-z]+)_(?P<name>[\w_]+)')
|
|
|
|
# Match an API version name.
|
|
# Match object includes API prefix, major, and minor version numbers.
|
|
# This could be refined further for specific APIs.
|
|
API_VERSION_NAME_RE = re.compile(r'(?P<apivariant>[A-Za-z]+)_VERSION_(?P<major>[0-9]+)_(?P<minor>[0-9]+)')
|
|
|
|
class ProseListFormats(Enum):
|
|
"""A connective, possibly with a quantifier."""
|
|
AND = 0
|
|
EACH_AND = 1
|
|
OR = 2
|
|
ANY_OR = 3
|
|
|
|
@classmethod
|
|
def from_string(cls, s):
|
|
if s == 'or':
|
|
return cls.OR
|
|
if s == 'and':
|
|
return cls.AND
|
|
raise RuntimeError("Unrecognized string connective: " + s)
|
|
|
|
@property
|
|
def connective(self):
|
|
if self in (ProseListFormats.OR, ProseListFormats.ANY_OR):
|
|
return 'or'
|
|
return 'and'
|
|
|
|
def quantifier(self, n):
|
|
"""Return the desired quantifier for a list of a given length."""
|
|
if self == ProseListFormats.ANY_OR:
|
|
if n > 1:
|
|
return 'any of '
|
|
elif self == ProseListFormats.EACH_AND:
|
|
if n > 2:
|
|
return 'each of '
|
|
if n == 2:
|
|
return 'both of '
|
|
return ''
|
|
|
|
|
|
class ConventionsBase(abc.ABC):
|
|
"""WG-specific conventions."""
|
|
|
|
def __init__(self):
|
|
self._command_prefix = None
|
|
self._type_prefix = None
|
|
|
|
def formatVersionOrExtension(self, name):
|
|
"""Mark up an API version or extension name as a link in the spec."""
|
|
|
|
# Is this a version name?
|
|
match = API_VERSION_NAME_RE.match(name)
|
|
if match is not None:
|
|
return self.formatVersion(name,
|
|
match.group('apivariant'),
|
|
match.group('major'),
|
|
match.group('minor'))
|
|
else:
|
|
# If not, assumed to be an extension name. Might be worth checking.
|
|
return self.formatExtension(name)
|
|
|
|
def formatVersion(self, name, apivariant, major, minor):
|
|
"""Mark up an API version name as a link in the spec."""
|
|
return '`<<{}>>`'.format(name)
|
|
|
|
def formatExtension(self, name):
|
|
"""Mark up an extension name as a link in the spec."""
|
|
return '`<<{}>>`'.format(name)
|
|
|
|
def formatSPIRVlink(self, name):
|
|
"""Mark up a SPIR-V extension name as an external link in the spec.
|
|
Since these are external links, the formatting probably will be
|
|
the same for all APIs creating such links, so long as they use
|
|
the asciidoctor {spirv} attribute for the base path to the SPIR-V
|
|
extensions."""
|
|
|
|
(vendor, _) = self.extension_name_split(name)
|
|
|
|
return f'{{spirv}}/{vendor}/{name}.html[{name}]'
|
|
|
|
@property
|
|
@abc.abstractmethod
|
|
def null(self):
|
|
"""Preferred spelling of NULL."""
|
|
raise NotImplementedError
|
|
|
|
def makeProseList(self, elements, fmt=ProseListFormats.AND, with_verb=False, *args, **kwargs):
|
|
"""Make a (comma-separated) list for use in prose.
|
|
|
|
Adds a connective (by default, 'and')
|
|
before the last element if there are more than 1.
|
|
|
|
Adds the right one of "is" or "are" to the end if with_verb is true.
|
|
|
|
Optionally adds a quantifier (like 'any') before a list of 2 or more,
|
|
if specified by fmt.
|
|
|
|
Override with a different method or different call to
|
|
_implMakeProseList if you want to add a comma for two elements,
|
|
or not use a serial comma.
|
|
"""
|
|
return self._implMakeProseList(elements, fmt, with_verb, *args, **kwargs)
|
|
|
|
@property
|
|
def struct_macro(self):
|
|
"""Get the appropriate format macro for a structure.
|
|
|
|
May override.
|
|
"""
|
|
return 'slink:'
|
|
|
|
@property
|
|
def external_macro(self):
|
|
"""Get the appropriate format macro for an external type like uint32_t.
|
|
|
|
May override.
|
|
"""
|
|
return 'code:'
|
|
|
|
@property
|
|
def allows_x_number_suffix(self):
|
|
"""Whether vendor tags can be suffixed with X and a number to mark experimental extensions."""
|
|
return False
|
|
|
|
@property
|
|
@abc.abstractmethod
|
|
def structtype_member_name(self):
|
|
"""Return name of the structure type member.
|
|
|
|
Must implement.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@property
|
|
@abc.abstractmethod
|
|
def nextpointer_member_name(self):
|
|
"""Return name of the structure pointer chain member.
|
|
|
|
Must implement.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
@property
|
|
@abc.abstractmethod
|
|
def xml_api_name(self):
|
|
"""Return the name used in the default API XML registry for the default API"""
|
|
raise NotImplementedError()
|
|
|
|
@abc.abstractmethod
|
|
def generate_structure_type_from_name(self, structname):
|
|
"""Generate a structure type name, like XR_TYPE_CREATE_INSTANCE_INFO.
|
|
|
|
Must implement.
|
|
"""
|
|
raise NotImplementedError()
|
|
|
|
def makeStructName(self, name):
|
|
"""Prepend the appropriate format macro for a structure to a structure type name.
|
|
|
|
Uses struct_macro, so just override that if you want to change behavior.
|
|
"""
|
|
return self.struct_macro + name
|
|
|
|
def makeExternalTypeName(self, name):
|
|
"""Prepend the appropriate format macro for an external type like uint32_t to a type name.
|
|
|
|
Uses external_macro, so just override that if you want to change behavior.
|
|
"""
|
|
return self.external_macro + name
|
|
|
|
def _implMakeProseList(self, elements, fmt, with_verb, comma_for_two_elts=False, serial_comma=True):
|
|
"""Internal-use implementation to make a (comma-separated) list for use in prose.
|
|
|
|
Adds a connective (by default, 'and')
|
|
before the last element if there are more than 1,
|
|
and only includes commas if there are more than 2
|
|
(if comma_for_two_elts is False).
|
|
|
|
Adds the right one of "is" or "are" to the end if with_verb is true.
|
|
|
|
Optionally adds a quantifier (like 'any') before a list of 2 or more,
|
|
if specified by fmt.
|
|
|
|
Do not edit these defaults, override self.makeProseList().
|
|
"""
|
|
assert serial_comma # did not implement what we did not need
|
|
if isinstance(fmt, str):
|
|
fmt = ProseListFormats.from_string(fmt)
|
|
|
|
my_elts = list(elements)
|
|
if len(my_elts) > 1:
|
|
my_elts[-1] = '{} {}'.format(fmt.connective, my_elts[-1])
|
|
|
|
if not comma_for_two_elts and len(my_elts) <= 2:
|
|
prose = ' '.join(my_elts)
|
|
else:
|
|
prose = ', '.join(my_elts)
|
|
|
|
quantifier = fmt.quantifier(len(my_elts))
|
|
|
|
parts = [quantifier, prose]
|
|
|
|
if with_verb:
|
|
if len(my_elts) > 1:
|
|
parts.append(' are')
|
|
else:
|
|
parts.append(' is')
|
|
return ''.join(parts)
|
|
|
|
@property
|
|
@abc.abstractmethod
|
|
def file_suffix(self):
|
|
"""Return suffix of generated Asciidoctor files"""
|
|
raise NotImplementedError
|
|
|
|
@abc.abstractmethod
|
|
def api_name(self, spectype=None):
|
|
"""Return API or specification name for citations in ref pages.
|
|
|
|
spectype is the spec this refpage is for.
|
|
'api' (the default value) is the main API Specification.
|
|
If an unrecognized spectype is given, returns None.
|
|
|
|
Must implement."""
|
|
raise NotImplementedError
|
|
|
|
def should_insert_may_alias_macro(self, genOpts):
|
|
"""Return true if we should insert a "may alias" macro in this file.
|
|
|
|
Only used by OpenXR right now."""
|
|
return False
|
|
|
|
@property
|
|
def command_prefix(self):
|
|
"""Return the expected prefix of commands/functions.
|
|
|
|
Implemented in terms of api_prefix."""
|
|
if not self._command_prefix:
|
|
self._command_prefix = self.api_prefix[:].replace('_', '').lower()
|
|
return self._command_prefix
|
|
|
|
@property
|
|
def type_prefix(self):
|
|
"""Return the expected prefix of type names.
|
|
|
|
Implemented in terms of command_prefix (and in turn, api_prefix)."""
|
|
if not self._type_prefix:
|
|
self._type_prefix = ''.join(
|
|
(self.command_prefix[0:1].upper(), self.command_prefix[1:]))
|
|
return self._type_prefix
|
|
|
|
@property
|
|
@abc.abstractmethod
|
|
def api_prefix(self):
|
|
"""Return API token prefix.
|
|
|
|
Typically two uppercase letters followed by an underscore.
|
|
|
|
Must implement."""
|
|
raise NotImplementedError
|
|
|
|
@property
|
|
def extension_name_prefix(self):
|
|
"""Return extension name prefix.
|
|
|
|
Typically two uppercase letters followed by an underscore.
|
|
|
|
Assumed to be the same as api_prefix, but some APIs use different
|
|
case conventions."""
|
|
|
|
return self.api_prefix
|
|
|
|
def extension_short_description(self, elem):
|
|
"""Return a short description of an extension for use in refpages.
|
|
|
|
elem is an ElementTree for the <extension> tag in the XML.
|
|
The default behavior is to use the 'type' field of this tag, but not
|
|
all APIs support this field."""
|
|
|
|
ext_type = elem.get('type')
|
|
|
|
if ext_type is not None:
|
|
return f'{ext_type} extension'
|
|
else:
|
|
return ''
|
|
|
|
@property
|
|
def write_contacts(self):
|
|
"""Return whether contact list should be written to extension appendices"""
|
|
return False
|
|
|
|
@property
|
|
def write_extension_type(self):
|
|
"""Return whether extension type should be written to extension appendices"""
|
|
return True
|
|
|
|
@property
|
|
def write_extension_number(self):
|
|
"""Return whether extension number should be written to extension appendices"""
|
|
return True
|
|
|
|
@property
|
|
def write_extension_revision(self):
|
|
"""Return whether extension revision number should be written to extension appendices"""
|
|
return True
|
|
|
|
@property
|
|
def write_refpage_include(self):
|
|
"""Return whether refpage include should be written to extension appendices"""
|
|
return True
|
|
|
|
@property
|
|
def api_version_prefix(self):
|
|
"""Return API core version token prefix.
|
|
|
|
Implemented in terms of api_prefix.
|
|
|
|
May override."""
|
|
return self.api_prefix + 'VERSION_'
|
|
|
|
@property
|
|
def KHR_prefix(self):
|
|
"""Return extension name prefix for KHR extensions.
|
|
|
|
Implemented in terms of api_prefix.
|
|
|
|
May override."""
|
|
return self.api_prefix + 'KHR_'
|
|
|
|
@property
|
|
def EXT_prefix(self):
|
|
"""Return extension name prefix for EXT extensions.
|
|
|
|
Implemented in terms of api_prefix.
|
|
|
|
May override."""
|
|
return self.api_prefix + 'EXT_'
|
|
|
|
def writeFeature(self, featureName, featureExtraProtect, filename):
|
|
"""Return True if OutputGenerator.endFeature should write this feature.
|
|
|
|
Defaults to always True.
|
|
Used in COutputGenerator.
|
|
|
|
May override."""
|
|
return True
|
|
|
|
def requires_error_validation(self, return_type):
|
|
"""Return True if the return_type element is an API result code
|
|
requiring error validation.
|
|
|
|
Defaults to always False.
|
|
|
|
May override."""
|
|
return False
|
|
|
|
@property
|
|
def required_errors(self):
|
|
"""Return a list of required error codes for validation.
|
|
|
|
Defaults to an empty list.
|
|
|
|
May override."""
|
|
return []
|
|
|
|
def is_voidpointer_alias(self, tag, text, tail):
|
|
"""Return True if the declaration components (tag,text,tail) of an
|
|
element represents a void * type.
|
|
|
|
Defaults to a reasonable implementation.
|
|
|
|
May override."""
|
|
return tag == 'type' and text == 'void' and tail.startswith('*')
|
|
|
|
def make_voidpointer_alias(self, tail):
|
|
"""Reformat a void * declaration to include the API alias macro.
|
|
|
|
Defaults to a no-op.
|
|
|
|
Must override if you actually want to use this feature in your project."""
|
|
return tail
|
|
|
|
def category_requires_validation(self, category):
|
|
"""Return True if the given type 'category' always requires validation.
|
|
|
|
Defaults to a reasonable implementation.
|
|
|
|
May override."""
|
|
return category in CATEGORIES_REQUIRING_VALIDATION
|
|
|
|
def type_always_valid(self, typename):
|
|
"""Return True if the given type name is always valid (never requires validation).
|
|
|
|
This is for things like integers.
|
|
|
|
Defaults to a reasonable implementation.
|
|
|
|
May override."""
|
|
return typename in TYPES_KNOWN_ALWAYS_VALID
|
|
|
|
@property
|
|
def should_skip_checking_codes(self):
|
|
"""Return True if more than the basic validation of return codes should
|
|
be skipped for a command."""
|
|
|
|
return False
|
|
|
|
@property
|
|
def generate_index_terms(self):
|
|
"""Return True if asiidoctor index terms should be generated as part
|
|
of an API interface from the docgenerator."""
|
|
|
|
return False
|
|
|
|
@property
|
|
def generate_enum_table(self):
|
|
"""Return True if asciidoctor tables describing enumerants in a
|
|
group should be generated as part of group generation."""
|
|
return False
|
|
|
|
@property
|
|
def generate_max_enum_in_docs(self):
|
|
"""Return True if MAX_ENUM tokens should be generated in
|
|
documentation includes."""
|
|
return False
|
|
|
|
def extension_name_split(self, name):
|
|
"""Split an extension name, returning (vendor, rest of name).
|
|
The API prefix of the name is ignored."""
|
|
|
|
match = EXT_NAME_DECOMPOSE_RE.match(name)
|
|
vendor = match.group('vendor')
|
|
bare_name = match.group('name')
|
|
|
|
return (vendor, bare_name)
|
|
|
|
@abc.abstractmethod
|
|
def extension_file_path(self, name):
|
|
"""Return file path to an extension appendix relative to a directory
|
|
containing all such appendices.
|
|
- name - extension name
|
|
|
|
Must implement."""
|
|
raise NotImplementedError
|
|
|
|
def extension_include_string(self, name):
|
|
"""Return format string for include:: line for an extension appendix
|
|
file.
|
|
- name - extension name"""
|
|
|
|
return 'include::{{appendices}}/{}[]'.format(
|
|
self.extension_file_path(name))
|
|
|
|
@property
|
|
def provisional_extension_warning(self):
|
|
"""Return True if a warning should be included in extension
|
|
appendices for provisional extensions."""
|
|
return True
|
|
|
|
@property
|
|
def generated_include_path(self):
|
|
"""Return path relative to the generated reference pages, to the
|
|
generated API include files."""
|
|
|
|
return '{generated}'
|
|
|
|
@property
|
|
def include_extension_appendix_in_refpage(self):
|
|
"""Return True if generating extension refpages by embedding
|
|
extension appendix content (default), False otherwise
|
|
(OpenXR)."""
|
|
|
|
return True
|
|
|
|
def valid_flag_bit(self, bitpos):
|
|
"""Return True if bitpos is an allowed numeric bit position for
|
|
an API flag.
|
|
|
|
Behavior depends on the data type used for flags (which may be 32
|
|
or 64 bits), and may depend on assumptions about compiler
|
|
handling of sign bits in enumerated types, as well."""
|
|
return True
|
|
|
|
@property
|
|
def duplicate_aliased_structs(self):
|
|
"""
|
|
Should aliased structs have the original struct definition listed in the
|
|
generated docs snippet?
|
|
"""
|
|
return False
|
|
|
|
@property
|
|
def protectProtoComment(self):
|
|
"""Return True if generated #endif should have a comment matching
|
|
the protection symbol used in the opening #ifdef/#ifndef."""
|
|
return False
|
|
|
|
@property
|
|
def extra_refpage_headers(self):
|
|
"""Return any extra headers (preceding the title) for generated
|
|
reference pages."""
|
|
return ''
|
|
|
|
@property
|
|
def extra_refpage_body(self):
|
|
"""Return any extra text (following the title) for generated
|
|
reference pages."""
|
|
return ''
|
|
|
|
def is_api_version_name(self, name):
|
|
"""Return True if name is an API version name."""
|
|
|
|
return API_VERSION_NAME_RE.match(name) is not None
|
|
|
|
@property
|
|
def docgen_language(self):
|
|
"""Return the language to be used in docgenerator [source]
|
|
blocks."""
|
|
|
|
return 'c++'
|
|
|
|
@property
|
|
def docgen_source_options(self):
|
|
"""Return block options to be used in docgenerator [source] blocks,
|
|
which are appended to the 'source' block type.
|
|
Can be empty."""
|
|
|
|
return '%unbreakable'
|