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