1e5c31af7Sopenharmony_ci#!/usr/bin/python3 -i 2e5c31af7Sopenharmony_ci# 3e5c31af7Sopenharmony_ci# Copyright 2013-2024 The Khronos Group Inc. 4e5c31af7Sopenharmony_ci# 5e5c31af7Sopenharmony_ci# SPDX-License-Identifier: Apache-2.0 6e5c31af7Sopenharmony_ci"""Utilities for working with attributes of the XML registry.""" 7e5c31af7Sopenharmony_ci 8e5c31af7Sopenharmony_ciimport re 9e5c31af7Sopenharmony_ci 10e5c31af7Sopenharmony_ci_PARAM_REF_NAME_RE = re.compile( 11e5c31af7Sopenharmony_ci r"(?P<name>[\w]+)(?P<brackets>\[\])?(?P<delim>\.|::|->)?") 12e5c31af7Sopenharmony_ci 13e5c31af7Sopenharmony_ci 14e5c31af7Sopenharmony_cidef _split_param_ref(val): 15e5c31af7Sopenharmony_ci return [name for name, _, _ in _PARAM_REF_NAME_RE.findall(val)] 16e5c31af7Sopenharmony_ci 17e5c31af7Sopenharmony_ci 18e5c31af7Sopenharmony_cidef _human_readable_deref(val, make_param_name=None): 19e5c31af7Sopenharmony_ci """Turn the "name[].member[]" notation into plain English.""" 20e5c31af7Sopenharmony_ci parts = [] 21e5c31af7Sopenharmony_ci matches = _PARAM_REF_NAME_RE.findall(val) 22e5c31af7Sopenharmony_ci for name, brackets, delim in reversed(matches): 23e5c31af7Sopenharmony_ci if make_param_name: 24e5c31af7Sopenharmony_ci name = make_param_name(name) 25e5c31af7Sopenharmony_ci if delim: 26e5c31af7Sopenharmony_ci parts.append('member of') 27e5c31af7Sopenharmony_ci if brackets: 28e5c31af7Sopenharmony_ci parts.append('each element of') 29e5c31af7Sopenharmony_ci parts.append('the') 30e5c31af7Sopenharmony_ci parts.append(name) 31e5c31af7Sopenharmony_ci parts.append('parameter') 32e5c31af7Sopenharmony_ci return ' '.join(parts) 33e5c31af7Sopenharmony_ci 34e5c31af7Sopenharmony_ci 35e5c31af7Sopenharmony_ciclass LengthEntry: 36e5c31af7Sopenharmony_ci """An entry in a (comma-separated) len attribute""" 37e5c31af7Sopenharmony_ci NULL_TERMINATED_STRING = 'null-terminated' 38e5c31af7Sopenharmony_ci MATH_STRING = 'latexmath:' 39e5c31af7Sopenharmony_ci 40e5c31af7Sopenharmony_ci def __init__(self, val): 41e5c31af7Sopenharmony_ci self.full_reference = val 42e5c31af7Sopenharmony_ci self.other_param_name = None 43e5c31af7Sopenharmony_ci self.null_terminated = False 44e5c31af7Sopenharmony_ci self.number = None 45e5c31af7Sopenharmony_ci self.math = None 46e5c31af7Sopenharmony_ci self.param_ref_parts = None 47e5c31af7Sopenharmony_ci if val == LengthEntry.NULL_TERMINATED_STRING: 48e5c31af7Sopenharmony_ci self.null_terminated = True 49e5c31af7Sopenharmony_ci return 50e5c31af7Sopenharmony_ci 51e5c31af7Sopenharmony_ci if val.startswith(LengthEntry.MATH_STRING): 52e5c31af7Sopenharmony_ci self.math = val.replace(LengthEntry.MATH_STRING, '')[1:-1] 53e5c31af7Sopenharmony_ci return 54e5c31af7Sopenharmony_ci 55e5c31af7Sopenharmony_ci if val.isdigit(): 56e5c31af7Sopenharmony_ci self.number = int(val) 57e5c31af7Sopenharmony_ci return 58e5c31af7Sopenharmony_ci 59e5c31af7Sopenharmony_ci # Must be another param name. 60e5c31af7Sopenharmony_ci self.param_ref_parts = _split_param_ref(val) 61e5c31af7Sopenharmony_ci self.other_param_name = self.param_ref_parts[0] 62e5c31af7Sopenharmony_ci 63e5c31af7Sopenharmony_ci def __str__(self): 64e5c31af7Sopenharmony_ci return self.full_reference 65e5c31af7Sopenharmony_ci 66e5c31af7Sopenharmony_ci def get_human_readable(self, make_param_name=None): 67e5c31af7Sopenharmony_ci assert(self.other_param_name) 68e5c31af7Sopenharmony_ci return _human_readable_deref(self.full_reference, make_param_name=make_param_name) 69e5c31af7Sopenharmony_ci 70e5c31af7Sopenharmony_ci def __repr__(self): 71e5c31af7Sopenharmony_ci "Formats an object for repr(), debugger display, etc." 72e5c31af7Sopenharmony_ci return 'spec_tools.attributes.LengthEntry("{}")'.format(self.full_reference) 73e5c31af7Sopenharmony_ci 74e5c31af7Sopenharmony_ci @staticmethod 75e5c31af7Sopenharmony_ci def parse_len_from_param(param): 76e5c31af7Sopenharmony_ci """Get a list of LengthEntry, or None.""" 77e5c31af7Sopenharmony_ci len_str = param.get('len') 78e5c31af7Sopenharmony_ci if len_str is None: 79e5c31af7Sopenharmony_ci return None 80e5c31af7Sopenharmony_ci return [LengthEntry(elt) for elt in len_str.split(',')] 81e5c31af7Sopenharmony_ci 82e5c31af7Sopenharmony_ci 83e5c31af7Sopenharmony_ciclass ExternSyncEntry: 84e5c31af7Sopenharmony_ci """An entry in a (comma-separated) externsync attribute""" 85e5c31af7Sopenharmony_ci 86e5c31af7Sopenharmony_ci TRUE_STRING = 'true' 87e5c31af7Sopenharmony_ci TRUE_WITH_CHILDREN_STRING = 'true_with_children' 88e5c31af7Sopenharmony_ci 89e5c31af7Sopenharmony_ci def __init__(self, val): 90e5c31af7Sopenharmony_ci self.full_reference = val 91e5c31af7Sopenharmony_ci self.entirely_extern_sync = (val in (ExternSyncEntry.TRUE_STRING, ExternSyncEntry.TRUE_WITH_CHILDREN_STRING)) 92e5c31af7Sopenharmony_ci self.children_extern_sync = (val == ExternSyncEntry.TRUE_WITH_CHILDREN_STRING) 93e5c31af7Sopenharmony_ci if self.entirely_extern_sync: 94e5c31af7Sopenharmony_ci return 95e5c31af7Sopenharmony_ci 96e5c31af7Sopenharmony_ci self.param_ref_parts = _split_param_ref(val) 97e5c31af7Sopenharmony_ci self.member = self.param_ref_parts[0] 98e5c31af7Sopenharmony_ci 99e5c31af7Sopenharmony_ci def get_human_readable(self, make_param_name=None): 100e5c31af7Sopenharmony_ci assert(not self.entirely_extern_sync) 101e5c31af7Sopenharmony_ci return _human_readable_deref(self.full_reference, make_param_name=make_param_name) 102e5c31af7Sopenharmony_ci 103e5c31af7Sopenharmony_ci @staticmethod 104e5c31af7Sopenharmony_ci def parse_externsync_from_param(param): 105e5c31af7Sopenharmony_ci """Get a list of ExternSyncEntry.""" 106e5c31af7Sopenharmony_ci sync_str = param.get('externsync') 107e5c31af7Sopenharmony_ci if sync_str is None: 108e5c31af7Sopenharmony_ci return None 109e5c31af7Sopenharmony_ci return [ExternSyncEntry(elt) for elt in sync_str.split(',')] 110e5c31af7Sopenharmony_ci 111e5c31af7Sopenharmony_ci def __repr__(self): 112e5c31af7Sopenharmony_ci "Formats an object for repr(), debugger display, etc." 113e5c31af7Sopenharmony_ci return 'spec_tools.attributes.ExternSyncEntry("{}")'.format(self.full_reference) 114e5c31af7Sopenharmony_ci 115e5c31af7Sopenharmony_ci 116e5c31af7Sopenharmony_ci_TRUE_STRING = 'true' 117e5c31af7Sopenharmony_ci_FALSE_STRING = 'false' 118e5c31af7Sopenharmony_ci 119e5c31af7Sopenharmony_ci 120e5c31af7Sopenharmony_cidef _parse_optional_elt(val): 121e5c31af7Sopenharmony_ci if val not in (_TRUE_STRING, _FALSE_STRING): 122e5c31af7Sopenharmony_ci raise ValueError("Each element of the optional attribute must be 'true', or 'false'") 123e5c31af7Sopenharmony_ci return val == _TRUE_STRING 124e5c31af7Sopenharmony_ci 125e5c31af7Sopenharmony_ci 126e5c31af7Sopenharmony_cidef parse_optional_from_param(param): 127e5c31af7Sopenharmony_ci """Get a list of booleans from a param: always returns at least one element.""" 128e5c31af7Sopenharmony_ci optional_str = param.get('optional', _FALSE_STRING) 129e5c31af7Sopenharmony_ci return [_parse_optional_elt(elt) for elt in optional_str.split(',')] 130e5c31af7Sopenharmony_ci 131e5c31af7Sopenharmony_ci 132e5c31af7Sopenharmony_cidef has_any_optional_in_param(param): 133e5c31af7Sopenharmony_ci """Returns True if we have any true in an optional attribute.""" 134e5c31af7Sopenharmony_ci return any(parse_optional_from_param(param)) 135