Affix

 view release on metacpan or  search on metacpan

infix/src/core/signature.c  view on Meta::CPAN

                    tail = node;
                }
                num_args++;
                skip_whitespace(state);
                if (*state->p == ',') {
                    state->p++;
                    skip_whitespace(state);
                    if (*state->p == ')') {  // Trailing comma error.
                        _infix_set_parser_error(state, INFIX_CODE_UNEXPECTED_TOKEN);
                        return INFIX_ERROR_INVALID_ARGUMENT;
                    }
                }
                else if (*state->p != ')') {
                    _infix_set_parser_error(state, INFIX_CODE_UNEXPECTED_TOKEN);
                    return INFIX_ERROR_INVALID_ARGUMENT;
                }
                else
                    break;
            }
        }
    }
    skip_whitespace(state);
    if (*state->p != ')') {
        _infix_set_parser_error(state, INFIX_CODE_UNTERMINATED_AGGREGATE);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    state->p++;
    // Parse Return Type
    skip_whitespace(state);
    if (state->p[0] != '-' || state->p[1] != '>') {
        _infix_set_parser_error(state, INFIX_CODE_MISSING_RETURN_TYPE);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    state->p += 2;
    *out_ret_type = parse_type(state);
    if (!*out_ret_type)
        return INFIX_ERROR_INVALID_ARGUMENT;
    // Convert linked list of args to a flat array.
    infix_function_argument * args = (num_args > 0)
        ? infix_arena_calloc(state->arena, num_args, sizeof(infix_function_argument), _Alignof(infix_function_argument))
        : nullptr;
    if (num_args > 0 && !args) {
        _infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
        return INFIX_ERROR_ALLOCATION_FAILED;
    }
    arg_node * current = head;
    for (size_t i = 0; i < num_args; i++) {
        args[i] = current->arg;
        current = current->next;
    }
    *out_args = args;
    *out_num_args = num_args;
    return INFIX_SUCCESS;
}
// High-Level API Implementation
/**
 * @internal
 * @brief The internal entry point for the signature parser (the "Parse" stage).
 *
 * This function takes a signature string and produces a raw, unresolved type
 * graph in a new, temporary arena. It is the core parsing logic, separated from the
 * higher-level functions that manage the full data pipeline. It is careful not to
 * modify the global error context string (`g_infix_last_signature_context`), which
 * is the responsibility of its public API callers.
 *
 * @param[out] out_type On success, receives the parsed type graph.
 * @param[out] out_arena On success, receives the temporary arena holding the graph. The caller is responsible for
 * destroying it.
 * @param[in] signature The signature string to parse.
 * @return `INFIX_SUCCESS` on success.
 */
c23_nodiscard infix_status _infix_parse_type_internal(infix_type ** out_type,
                                                      infix_arena_t ** out_arena,
                                                      const char * signature) {
    if (!out_type || !out_arena) {
        _infix_set_error(INFIX_CATEGORY_GENERAL, INFIX_CODE_NULL_POINTER, 0);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    if (!signature || *signature == '\0') {
        _infix_set_error(INFIX_CATEGORY_PARSER, INFIX_CODE_EMPTY_SIGNATURE, 0);
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    // The top-level public API is responsible for setting g_infix_last_signature_context.
    *out_arena = infix_arena_create(4096);
    if (!*out_arena) {
        _infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, 0);
        return INFIX_ERROR_ALLOCATION_FAILED;
    }
    parser_state state = {.p = signature, .start = signature, .arena = *out_arena, .depth = 0};
    infix_type * type = parse_type(&state);
    if (type) {
        skip_whitespace(&state);
        // After successfully parsing a type, ensure there is no trailing junk.
        if (state.p[0] != '\0') {
            _infix_set_parser_error(&state, INFIX_CODE_UNEXPECTED_TOKEN);
            type = nullptr;
        }
    }
    if (!type) {
        // If parsing failed at any point, clean up the temporary arena.
        infix_arena_destroy(*out_arena);
        *out_arena = nullptr;
        *out_type = nullptr;
        return INFIX_ERROR_INVALID_ARGUMENT;
    }
    *out_type = type;
    return INFIX_SUCCESS;
}
/**
 * @brief Parses a signature string representing a single data type.
 *
 * This function orchestrates the full **"Parse -> Estimate -> Copy -> Resolve -> Layout"** pipeline
 * for a single type, resulting in a fully resolved and laid-out `infix_type` object graph.
 *
 * @param[out] out_type On success, receives a pointer to the parsed type object.
 * @param[out] out_arena On success, receives a pointer to the arena holding the type. The caller is responsible for
 * freeing this with `infix_arena_destroy`.
 * @param[in] signature The signature string of the data type (e.g., `"{id:int, name:*char}"`).
 * @param[in] registry An optional type registry for resolving named types. Can be `nullptr`.
 * @return `INFIX_SUCCESS` on success.
 */

infix/src/core/signature.c  view on Meta::CPAN

        _print(state, "X");
        break;
    }
}
/**
 * @internal
 * @brief Serializes an `infix_type`'s structural body, ignoring its top-level semantic name.
 *
 * This function is a special-purpose printer used by `infix_registry_print`. Its job
 * is to produce the right-hand side of a type definition. It *always* prints the
 * full structural definition, ignoring the top-level `name` field to prevent
 * self-referential output like `@MyInt = @MyInt;`.
 *
 * @param state The printer state.
 * @param type The type whose body to print.
 */
static void _infix_type_print_body_only_recursive(printer_state * state, const infix_type * type) {
    if (state->status != INFIX_SUCCESS || !type) {
        if (state->status == INFIX_SUCCESS)
            state->status = INFIX_ERROR_INVALID_ARGUMENT;
        return;
    }
    // This is the key difference from the main printer: we skip the `if (type->name)` check
    // and immediately print the underlying structure of the type.
    switch (type->category) {
    case INFIX_TYPE_STRUCT:
        if (type->meta.aggregate_info.is_packed) {
            _print(state, "!");
            if (type->alignment != 1)
                _print(state, "%zu:", type->alignment);
        }
        _print(state, "{");
        for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
            if (i > 0)
                _print(state, ",");
            const infix_struct_member * member = &type->meta.aggregate_info.members[i];
            if (member->name)
                _print(state, "%s:", member->name);
            // For nested members, we can use the standard printer, which IS allowed
            // to use the `@Name` shorthand for brevity.
            _infix_type_print_signature_recursive(state, member->type);
            if (member->bit_width > 0)
                _print(state, ":%u", member->bit_width);
        }
        _print(state, "}");
        break;
    case INFIX_TYPE_UNION:
        _print(state, "<");
        for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
            if (i > 0)
                _print(state, ",");
            const infix_struct_member * member = &type->meta.aggregate_info.members[i];
            if (member->name)
                _print(state, "%s:", member->name);
            _infix_type_print_signature_recursive(state, member->type);
            if (member->bit_width > 0)
                _print(state, ":%u", member->bit_width);
        }
        _print(state, ">");
        break;
    // For all other types, we replicate the printing logic from the main printer
    // to ensure we print the structure, not a potential top-level alias name.
    case INFIX_TYPE_VOID:
        _print(state, "void");
        break;
    case INFIX_TYPE_POINTER:
        _print(state, "*");
        if (type->meta.pointer_info.pointee_type == type || type->meta.pointer_info.pointee_type == nullptr ||
            type->meta.pointer_info.pointee_type->category == INFIX_TYPE_VOID)
            _print(state, "void");
        else
            _infix_type_print_signature_recursive(state, type->meta.pointer_info.pointee_type);
        break;
    case INFIX_TYPE_ARRAY:
        if (type->meta.array_info.is_flexible)
            _print(state, "[?:");
        else
            _print(state, "[%zu:", type->meta.array_info.num_elements);
        _infix_type_print_signature_recursive(state, type->meta.array_info.element_type);
        _print(state, "]");
        break;
    case INFIX_TYPE_ENUM:
        _print(state, "e:");
        _infix_type_print_signature_recursive(state, type->meta.enum_info.underlying_type);
        break;
    case INFIX_TYPE_COMPLEX:
        _print(state, "c[");
        _infix_type_print_signature_recursive(state, type->meta.complex_info.base_type);
        _print(state, "]");
        break;
    case INFIX_TYPE_PRIMITIVE:
        // This block is now a full copy from the main printer.
        switch (type->meta.primitive_id) {
        case INFIX_PRIMITIVE_BOOL:
            _print(state, "bool");
            break;
        case INFIX_PRIMITIVE_SINT8:
            _print(state, "sint8");
            break;
        case INFIX_PRIMITIVE_UINT8:
            _print(state, "uint8");
            break;
        case INFIX_PRIMITIVE_SINT16:
            _print(state, "sint16");
            break;
        case INFIX_PRIMITIVE_UINT16:
            _print(state, "uint16");
            break;
        case INFIX_PRIMITIVE_SINT32:
            _print(state, "sint32");
            break;
        case INFIX_PRIMITIVE_UINT32:
            _print(state, "uint32");
            break;
        case INFIX_PRIMITIVE_SINT64:
            _print(state, "sint64");
            break;
        case INFIX_PRIMITIVE_UINT64:
            _print(state, "uint64");
            break;
        case INFIX_PRIMITIVE_SINT128:

infix/src/core/signature.c  view on Meta::CPAN

                int count = 0;
                const char * p = function_name;
                while (*p && count < MAX_RECURSION_DEPTH) {
                    parts[count] = p;
                    const char * end = strstr(p, "::");
                    if (end) {
                        lens[count] = end - p;
                        p = end + 2;
                    }
                    else {
                        lens[count] = strlen(p);
                        p += lens[count];
                    }
                    count++;
                }
                // Print in reverse order
                for (int i = count - 1; i >= 0; i--) {
                    for (size_t j = 0; j < lens[i]; j++)
                        _print(&state, "%c", parts[i][j]);
                    _print(&state, "@");
                }
            }
            else {
                _print(&state, "%s@", function_name);
            }
        }
        else {
            _print(&state, "func@");
        }
        _print(&state, "@YA");  // __cdecl (default)
        _infix_type_print_msvc_recursive(&state, ret_type);

        if (num_args == 0)
            _print(&state, "X");  // void argument list
        else
            for (size_t i = 0; i < num_args; ++i)
                _infix_type_print_msvc_recursive(&state, args[i].type);
        _print(&state, "@Z");
    }
    else {
        _print(&state, "unsupported_dialect");
        state.status = INFIX_ERROR_INVALID_ARGUMENT;
    }
    if (state.status == INFIX_SUCCESS) {
        if (state.remaining > 0)
            *state.p = '\0';
        else {
            if (buffer_size > 0)
                buffer[buffer_size - 1] = '\0';
            return INFIX_ERROR_INVALID_ARGUMENT;  // Indicate truncation.
        }
    }
    else if (buffer_size > 0)
        buffer[buffer_size - 1] = '\0';
    return state.status;
}
/**
 * @brief Serializes all defined types within a registry into a single, human-readable string.
 *
 * @details The output format is a sequence of definitions (e.g., `@Name = { ... };`) separated
 * by newlines, suitable for logging, debugging, or saving to a file. This function
 * will not print forward declarations that have not been fully defined. The order of
 * definitions in the output string is not guaranteed.
 *
 * @param[out] buffer The output buffer to write the string into.
 * @param[in] buffer_size The size of the output buffer.
 * @param[in] registry The registry to serialize.
 * @return `INFIX_SUCCESS` on success, or `INFIX_ERROR_INVALID_ARGUMENT` if the buffer is too small
 *         or another error occurs.
 */
c23_nodiscard infix_status infix_registry_print(char * buffer, size_t buffer_size, const infix_registry_t * registry) {
    if (!buffer || buffer_size == 0 || !registry)
        return INFIX_ERROR_INVALID_ARGUMENT;
    printer_state state = {buffer, buffer_size, INFIX_SUCCESS, {0}, 0, {0}, 0};
    *state.p = '\0';
    // Iterate through all buckets and their chains.
    for (size_t i = 0; i < registry->num_buckets; ++i) {
        for (const _infix_registry_entry_t * entry = registry->buckets[i]; entry != nullptr; entry = entry->next) {
            // Only print fully defined types, not forward declarations.
            if (entry->type && !entry->is_forward_declaration) {
                char type_body_buffer[1024];
                if (_infix_type_print_body_only(
                        type_body_buffer, sizeof(type_body_buffer), entry->type, INFIX_DIALECT_SIGNATURE) !=
                    INFIX_SUCCESS) {
                    state.status = INFIX_ERROR_INVALID_ARGUMENT;
                    goto end_print_loop;
                }
                _print(&state, "@%s = %s;\n", entry->name, type_body_buffer);
            }
            else if (entry->is_forward_declaration)  // Explicitly print forward declarations
                _print(&state, "@%s;\n", entry->name);
            if (state.status != INFIX_SUCCESS)
                goto end_print_loop;
        }
    }
end_print_loop:;
    return state.status;
}



( run in 0.621 second using v1.01-cache-2.11-cpan-5735350b133 )