Affix

 view release on metacpan or  search on metacpan

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

/**
 * Copyright (c) 2025 Sanko Robinson
 *
 * This source code is dual-licensed under the Artistic License 2.0 or the MIT License.
 * You may choose to use this code under the terms of either license.
 *
 * SPDX-License-Identifier: (Artistic-2.0 OR MIT)
 *
 * The documentation blocks within this file are licensed under the
 * Creative Commons Attribution 4.0 International License (CC BY 4.0).
 *
 * SPDX-License-Identifier: CC-BY-4.0
 */
/**
 * @file signature.c
 * @brief Implements the `infix` signature string parser and type printer.
 * @ingroup internal_core
 *
 * @details This module is responsible for two key functionalities that form the
 * user-facing API of the library:
 *
 * 1.  **Parsing:** It contains a hand-written recursive descent parser that transforms a
 *     human-readable signature string (e.g., `"({int, *char}) -> void"`) into an
 *     unresolved `infix_type` object graph. This is the **"Parse"** stage of the core
 *     data pipeline. The internal entry point for the "Parse" stage is `_infix_parse_type_internal`.
 *
 * 2.  **Printing:** It provides functions to serialize a fully resolved `infix_type`
 *     graph back into a canonical signature string. This is crucial for introspection,
 *     debugging, and verifying the library's understanding of a type.
 *
 * The public functions `infix_type_from_signature` and `infix_signature_parse`
 * are high-level orchestrators. They manage the entire **"Parse -> Copy -> Resolve -> Layout"**
 * pipeline, providing the user with a fully validated, self-contained, and ready-to-use
 * type object that is safe to use for the lifetime of its returned arena.
 */
#include "common/infix_internals.h"
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/** @internal A thread-local pointer to the full signature string being parsed, used by `error.c` for rich error
 * reporting. */
extern INFIX_TLS const char * g_infix_last_signature_context;
/** @internal A safeguard against stack overflows from malicious or deeply nested signatures (e.g., `{{{{...}}}}`). */
#define MAX_RECURSION_DEPTH 32
static infix_status parse_function_signature_details(parser_state * state,
                                                     infix_type ** out_ret_type,
                                                     infix_function_argument ** out_args,
                                                     size_t * out_num_args,
                                                     size_t * out_num_fixed_args);
// Parser Helper Functions
/**
 * @internal
 * @brief Sets a detailed parser error, capturing the current position in the string.
 * @param[in,out] state The current parser state.
 * @param[in] code The error code to set.
 */
INFIX_INTERNAL void _infix_set_parser_error(parser_state * state, infix_error_code_t code) {
    _infix_set_error(INFIX_CATEGORY_PARSER, code, (size_t)(state->p - state->start));
}
INFIX_INTERNAL void skip_whitespace(parser_state * state);

/**
 * @internal
 * @brief Advances the parser's cursor past any whitespace or C-style line comments.
 * @param[in,out] state The parser state to modify.
 */
INFIX_INTERNAL void skip_whitespace(parser_state * state) {
    while (true) {
        while (isspace((unsigned char)*state->p))
            state->p++;
        if (*state->p == '#')  // C-style line comments
            while (*state->p != '\n' && *state->p != '\0')
                state->p++;
        else
            break;
    }
}
/**
 * @internal
 * @brief Parses an unsigned integer from the string, used for array/vector sizes.
 * @param[in,out] state The parser state.
 * @param[out] out_val A pointer to store the parsed value.
 * @return `true` on success, `false` on failure.
 */
static bool parse_size_t(parser_state * state, size_t * out_val) {
    const char * start = state->p;

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 1.896 second using v1.01-cache-2.11-cpan-39bf76dae61 )