<commit_before>"""
API generator for the BGAPI protocol using the bleapi.xml format.
"""
import sys
import xml.dom.minidom

# Map "base" datatypes to struct specifiers
BASE_DATA_TYPE_TO_STRUCT = {
    'int8':   'b',
    'int16':  'h',
    'uint8':  'B',
    'uint16': 'H',
    'uint32': 'I',
    'bd_addr': '6s'
    # uint8array is special
}

# Filled in when processing XML
DATA_TYPE_MAP = {}

class APIParserError(Exception):
    """ An error occurred while parsing the API XML specification. """
    pass

def generate_decodable(prefix, name, param_list_element, output_fp):
    """ Generate a Decodable subclass and return its name. """
    new_cls = '{}_{}'.format(prefix, name)
    output_fp.write("class {}(Decodable):\n".format(new_cls))
    output_fp.write("    decoded_type = namedtuple('{}_type', (\n".format(new_cls))

    struct_format = ""
    ends_with_uint8array = False

    for param_element in param_list_element.getElementsByTagName('param'):
        return_param_name = param_element.getAttribute('name')
        return_param_datatype = param_element.getAttribute('datatype')

        # Build up structure definition.  If a bgapi type has a uint8array,
        # we assume there is only one instance and it ends with the array.
        if return_param_datatype != 'uint8array':
            return_param_type = DATA_TYPE_MAP[return_param_datatype]
            struct_format += return_param_type

        else:
            # Append B for uint8array length
            struct_format += "B"
            ends_with_uint8array = True

        # namedtuple field name
        output_fp.write("        '{}',\n".format(return_param_name))

    output_fp.write("    ))\n") # end namedtuple declaration

    # Generate struct format, if it is not the default
    if struct_format:
        output_fp.write("    decode_struct = Struct('<{}')\n".format(struct_format))

    # Note if we end with a uint8array type.
    if ends_with_uint8array:
        output_fp.write("    ends_with_uint8array = True\n")

    output_fp.write('\n')

    return new_cls


def generate_events_from_class(class_element, output_fp):
    event_list = []

    class_name = class_element.getAttribute('name')
    event_element_list = class_element.getElementsByTagName('event')

    for event_element in event_element_list:
        event_name = '{}_{}'.format(class_name, event_element.getAttribute('name'))
        event_index = int(event_element.getAttribute('index'))

        # Event types from BGAPI commands
        event_typename = generate_decodable('event', event_name, event_element, output_fp)
        event_list.append((event_index, event_typename))

    return event_list

def generate_commands_from_class(class_element, output_fp):
    command_list = []

    class_name = class_element.getAttribute('name')
    class_index = int(class_element.getAttribute('index'))
    command_element_list = class_element.getElementsByTagName('command')

    for command_element in command_element_list:
        command_index = int(command_element.getAttribute("index"))
        command_name = "{}_{}".format(class_name, command_element.getAttribute('name'))

        # Command types
        params_element_list = command_element.getElementsByTagName('params')

        if not params_element_list:
            continue

        command_param_list = params_element_list[0].getElementsByTagName('param')

        struct_spec = ""
        ends_with_uint8array = False

        command_cls_name = "command_{}".format(command_name)
        command_field_list = []
        for param_element in command_param_list:
            param_name = param_element.getAttribute('name')
            param_datatype = param_element.getAttribute('datatype')
            # param_type = param_element.getAttribute('type')

            command_field_list.append(param_name)

            # Build up structure definition.  If a bgapi type has a uint8array,
            # we assume there is only one instance and it ends with the array.
            if param_datatype != 'uint8array':
                param_type = DATA_TYPE_MAP[param_datatype]
                struct_spec += param_type

            else:
                # Append B for uint8array length
                struct_spec += "B"
                ends_with_uint8array = True

        output_fp.write("class {0}(namedtuple('{0}', '{1}')):\n".format(command_cls_name, ' '.join(command_field_list)))
        # Performance; don't create instance dict
        output_fp.write("   __slots__ = ()\n")
        output_fp.write("   _id = (0, {}, {})\n".format(class_index, command_index))
        if struct_spec:
            output_fp.write("   _struct = Struct('<{}')\n".format(struct_spec))
        output_fp.write("   _ends_with_uint8array = {}\n".format("True" if ends_with_uint8array else "False"))
        output_fp.write("\n")

        # Return types from BGAPI commands
        return_field_list = command_element.getElementsByTagName('returns')<commit_msg>api-generator: always generate _struct attribute for command type, even if empty<commit_after>"""
API generator for the BGAPI protocol using the bleapi.xml format.
"""
import sys
import xml.dom.minidom

# Map "base" datatypes to struct specifiers
BASE_DATA_TYPE_TO_STRUCT = {
    'int8':   'b',
    'int16':  'h',
    'uint8':  'B',
    'uint16': 'H',
    'uint32': 'I',
    'bd_addr': '6s'
    # uint8array is special
}

# Filled in when processing XML
DATA_TYPE_MAP = {}

class APIParserError(Exception):
    """ An error occurred while parsing the API XML specification. """
    pass

def generate_decodable(prefix, name, param_list_element, output_fp):
    """ Generate a Decodable subclass and return its name. """
    new_cls = '{}_{}'.format(prefix, name)
    output_fp.write("class {}(Decodable):\n".format(new_cls))
    output_fp.write("    decoded_type = namedtuple('{}_type', (\n".format(new_cls))

    struct_format = ""
    ends_with_uint8array = False

    for param_element in param_list_element.getElementsByTagName('param'):
        return_param_name = param_element.getAttribute('name')
        return_param_datatype = param_element.getAttribute('datatype')

        # Build up structure definition.  If a bgapi type has a uint8array,
        # we assume there is only one instance and it ends with the array.
        if return_param_datatype != 'uint8array':
            return_param_type = DATA_TYPE_MAP[return_param_datatype]
            struct_format += return_param_type

        else:
            # Append B for uint8array length
            struct_format += "B"
            ends_with_uint8array = True

        # namedtuple field name
        output_fp.write("        '{}',\n".format(return_param_name))

    output_fp.write("    ))\n") # end namedtuple declaration

    # Generate struct format, if it is not the default
    if struct_format:
        output_fp.write("    decode_struct = Struct('<{}')\n".format(struct_format))

    # Note if we end with a uint8array type.
    if ends_with_uint8array:
        output_fp.write("    ends_with_uint8array = True\n")

    output_fp.write('\n')

    return new_cls


def generate_events_from_class(class_element, output_fp):
    event_list = []

    class_name = class_element.getAttribute('name')
    event_element_list = class_element.getElementsByTagName('event')

    for event_element in event_element_list:
        event_name = '{}_{}'.format(class_name, event_element.getAttribute('name'))
        event_index = int(event_element.getAttribute('index'))

        # Event types from BGAPI commands
        event_typename = generate_decodable('event', event_name, event_element, output_fp)
        event_list.append((event_index, event_typename))

    return event_list

def generate_commands_from_class(class_element, output_fp):
    command_list = []

    class_name = class_element.getAttribute('name')
    class_index = int(class_element.getAttribute('index'))
    command_element_list = class_element.getElementsByTagName('command')

    for command_element in command_element_list:
        command_index = int(command_element.getAttribute("index"))
        command_name = "{}_{}".format(class_name, command_element.getAttribute('name'))

        # Command types
        params_element_list = command_element.getElementsByTagName('params')

        if not params_element_list:
            continue

        command_param_list = params_element_list[0].getElementsByTagName('param')

        struct_spec = ""
        ends_with_uint8array = False

        command_cls_name = "command_{}".format(command_name)
        command_field_list = []
        for param_element in command_param_list:
            param_name = param_element.getAttribute('name')
            param_datatype = param_element.getAttribute('datatype')
            # param_type = param_element.getAttribute('type')

            command_field_list.append(param_name)

            # Build up structure definition.  If a bgapi type has a uint8array,
            # we assume there is only one instance and it ends with the array.
            if param_datatype != 'uint8array':
                param_type = DATA_TYPE_MAP[param_datatype]
                struct_spec += param_type

            else:
                # Append B for uint8array length
                struct_spec += "B"
                ends_with_uint8array = True

        output_fp.write("class {0}(namedtuple('{0}', '{1}')):\n".format(command_cls_name, ' '.join(command_field_list)))
        # Performance; don't create instance dict
        output_fp.write("   __slots__ = ()\n")
        output_fp.write("   _id = (0, {}, {})\n".format(class_index, command_index))
        output_fp.write("   _struct = Struct('<{}')\n".format(struct_spec))
        output_fp.write("   _ends_with_uint8array = {}\n".format("True" if ends_with_uint8array else "False"))
        output_fp.write("\n")

        # Return types from BGAPI commands
        return_field_list = command_element.getElementsByTagName('returns')
{'fraction_non_alphanumeric': 0.10532651218778212, 'fraction_numerical': 0.006018657839301836, 'mean_word_length': 3.123242349048801, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 4}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '571304', 'n_tokens_mistral': 2959, 'n_tokens_neox': 2900, 'n_words': 655}