1#!/usr/bin/python3 -i
2#
3# Copyright (c) 2013-2020 The Khronos Group Inc.
4#
5# SPDX-License-Identifier: Apache-2.0
6
7# Working-group-specific style conventions,
8# used in generation.
9
10import re
11import os
12
13from conventions import ConventionsBase
14
15
16# Modified from default implementation - see category_requires_validation() below
17CATEGORIES_REQUIRING_VALIDATION = set(('handle', 'enum', 'bitmask'))
18
19# Tokenize into "words" for structure types, approximately per spec "Implicit Valid Usage" section 2.7.2
20# This first set is for things we recognize explicitly as words,
21# as exceptions to the general regex.
22# Ideally these would be listed in the spec as exceptions, as OpenXR does.
23SPECIAL_WORDS = set((
24    '16Bit',  # VkPhysicalDevice16BitStorageFeatures
25    '8Bit',  # VkPhysicalDevice8BitStorageFeaturesKHR
26    'AABB',  # VkGeometryAABBNV
27    'ASTC',  # VkPhysicalDeviceTextureCompressionASTCHDRFeaturesEXT
28    'D3D12',  # VkD3D12FenceSubmitInfoKHR
29    'Float16',  # VkPhysicalDeviceShaderFloat16Int8FeaturesKHR
30    'ImagePipe',  # VkImagePipeSurfaceCreateInfoFUCHSIA
31    'Int64',  # VkPhysicalDeviceShaderAtomicInt64FeaturesKHR
32    'Int8',  # VkPhysicalDeviceShaderFloat16Int8FeaturesKHR
33    'MacOS',  # VkMacOSSurfaceCreateInfoMVK
34    'Uint8',  # VkPhysicalDeviceIndexTypeUint8FeaturesEXT
35    'Win32',  # VkWin32SurfaceCreateInfoKHR
36))
37# A regex to match any of the SPECIAL_WORDS
38EXCEPTION_PATTERN = r'(?P<exception>{})'.format(
39    '|'.join('(%s)' % re.escape(w) for w in SPECIAL_WORDS))
40MAIN_RE = re.compile(
41    # the negative lookahead is to prevent the all-caps pattern from being too greedy.
42    r'({}|([0-9]+)|([A-Z][a-z]+)|([A-Z][A-Z]*(?![a-z])))'.format(EXCEPTION_PATTERN))
43
44
45class VulkanConventions(ConventionsBase):
46    @property
47    def null(self):
48        """Preferred spelling of NULL."""
49        return '`NULL`'
50
51    @property
52    def struct_macro(self):
53        """Get the appropriate format macro for a structure.
54
55        Primarily affects generated valid usage statements.
56        """
57
58        return 'slink:'
59
60    @property
61    def constFlagBits(self):
62        """Returns True if static const flag bits should be generated, False if an enumerated type should be generated."""
63        return False
64
65    @property
66    def structtype_member_name(self):
67        """Return name of the structure type member"""
68        return 'sType'
69
70    @property
71    def nextpointer_member_name(self):
72        """Return name of the structure pointer chain member"""
73        return 'pNext'
74
75    @property
76    def valid_pointer_prefix(self):
77        """Return prefix to pointers which must themselves be valid"""
78        return 'valid'
79
80    def is_structure_type_member(self, paramtype, paramname):
81        """Determine if member type and name match the structure type member."""
82        return paramtype == 'VkStructureType' and paramname == self.structtype_member_name
83
84    def is_nextpointer_member(self, paramtype, paramname):
85        """Determine if member type and name match the next pointer chain member."""
86        return paramtype == 'void' and paramname == self.nextpointer_member_name
87
88    def generate_structure_type_from_name(self, structname):
89        """Generate a structure type name, like VK_STRUCTURE_TYPE_CREATE_INSTANCE_INFO"""
90        structure_type_parts = []
91        # Tokenize into "words"
92        for elem in MAIN_RE.findall(structname):
93            word = elem[0]
94            if word == 'Vk':
95                structure_type_parts.append('VK_STRUCTURE_TYPE')
96            else:
97                structure_type_parts.append(word.upper())
98        return '_'.join(structure_type_parts)
99
100    @property
101    def warning_comment(self):
102        """Return warning comment to be placed in header of generated Asciidoctor files"""
103        return '// WARNING: DO NOT MODIFY! This file is automatically generated from the vk.xml registry'
104
105    @property
106    def file_suffix(self):
107        """Return suffix of generated Asciidoctor files"""
108        return '.txt'
109
110    def api_name(self, spectype='api'):
111        """Return API or specification name for citations in ref pages.ref
112           pages should link to for
113
114           spectype is the spec this refpage is for: 'api' is the Vulkan API
115           Specification. Defaults to 'api'. If an unrecognized spectype is
116           given, returns None.
117        """
118        if spectype == 'api' or spectype is None:
119            return 'Vulkan'
120        else:
121            return None
122
123    @property
124    def api_prefix(self):
125        """Return API token prefix"""
126        return 'VK_'
127
128    @property
129    def write_contacts(self):
130        """Return whether contact list should be written to extension appendices"""
131        return True
132
133    @property
134    def write_refpage_include(self):
135        """Return whether refpage include should be written to extension appendices"""
136        return True
137
138    @property
139    def member_used_for_unique_vuid(self):
140        """Return the member name used in the VUID-...-...-unique ID."""
141        return self.structtype_member_name
142
143    def is_externsync_command(self, protoname):
144        """Returns True if the protoname element is an API command requiring
145           external synchronization
146        """
147        return protoname is not None and 'vkCmd' in protoname
148
149    def is_api_name(self, name):
150        """Returns True if name is in the reserved API namespace.
151        For Vulkan, these are names with a case-insensitive 'vk' prefix, or
152        a 'PFN_vk' function pointer type prefix.
153        """
154        return name[0:2].lower() == 'vk' or name[0:6] == 'PFN_vk'
155
156    def specURL(self, spectype='api'):
157        """Return public registry URL which ref pages should link to for the
158           current all-extensions HTML specification, so xrefs in the
159           asciidoc source that aren't to ref pages can link into it
160           instead. N.b. this may need to change on a per-refpage basis if
161           there are multiple documents involved.
162        """
163        return 'https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html'
164
165    @property
166    def xml_api_name(self):
167        """Return the name used in the default API XML registry for the default API"""
168        return 'vulkan'
169
170    @property
171    def registry_path(self):
172        """Return relpath to the default API XML registry in this project."""
173        return 'xml/vk.xml'
174
175    @property
176    def specification_path(self):
177        """Return relpath to the Asciidoctor specification sources in this project."""
178        return '{generated}/meta'
179
180    @property
181    def special_use_section_anchor(self):
182        """Return asciidoctor anchor name in the API Specification of the
183        section describing extension special uses in detail."""
184        return 'extendingvulkan-compatibility-specialuse'
185
186    @property
187    def extra_refpage_headers(self):
188        """Return any extra text to add to refpage headers."""
189        return 'include::{config}/attribs.txt[]'
190
191    @property
192    def extension_index_prefixes(self):
193        """Return a list of extension prefixes used to group extension refpages."""
194        return ['VK_KHR', 'VK_EXT', 'VK']
195
196    @property
197    def unified_flag_refpages(self):
198        """Return True if Flags/FlagBits refpages are unified, False if
199           they're separate.
200        """
201        return False
202
203    @property
204    def spec_reflow_path(self):
205        """Return the path to the spec source folder to reflow"""
206        return os.getcwd()
207
208    @property
209    def spec_no_reflow_dirs(self):
210        """Return a set of directories not to automatically descend into
211           when reflowing spec text
212        """
213        return ('scripts', 'style')
214
215    @property
216    def zero(self):
217        return '`0`'
218
219    def category_requires_validation(self, category):
220        """Return True if the given type 'category' always requires validation.
221
222        Overridden because Vulkan doesn't require "valid" text for basetype in the spec right now."""
223        return category in CATEGORIES_REQUIRING_VALIDATION
224
225    @property
226    def should_skip_checking_codes(self):
227        """Return True if more than the basic validation of return codes should
228        be skipped for a command.
229
230        Vulkan mostly relies on the validation layers rather than API
231        builtin error checking, so these checks are not appropriate.
232
233        For example, passing in a VkFormat parameter will not potentially
234        generate a VK_ERROR_FORMAT_NOT_SUPPORTED code."""
235
236        return True
237
238    def extension_include_string(self, ext):
239        """Return format string for include:: line for an extension appendix
240           file. ext is an object with the following members:
241            - name - extension string string
242            - vendor - vendor portion of name
243            - barename - remainder of name"""
244
245        return 'include::{{appendices}}/{name}{suffix}[]'.format(
246                name=ext.name, suffix=self.file_suffix)
247
248    @property
249    def refpage_generated_include_path(self):
250        """Return path relative to the generated reference pages, to the
251           generated API include files."""
252        return "{generated}"
253
254    def valid_flag_bit(self, bitpos):
255        """Return True if bitpos is an allowed numeric bit position for
256           an API flag bit.
257
258           Vulkan uses 32 bit Vk*Flags types, and assumes C compilers may
259           cause Vk*FlagBits values with bit 31 set to result in a 64 bit
260           enumerated type, so disallows such flags."""
261        return bitpos >= 0 and bitpos < 31
262