Affix

 view release on metacpan or  search on metacpan

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

/**
 * @internal
 * @brief Parses a C-style identifier from the string.
 * @details This is used for member names, named types, and function argument names.
 * It handles simple identifiers (`my_var`) and C++-style namespaces (`NS::Name`).
 * @param[in,out] state The parser state.
 * @return An arena-allocated string for the identifier, or `nullptr` on failure.
 */
static const char * parse_identifier(parser_state * state) {
    skip_whitespace(state);
    const char * start = state->p;
    if (!isalpha((unsigned char)*start) && *start != '_')
        return nullptr;
    while (isalnum((unsigned char)*state->p) || *state->p == '_' || *state->p == ':') {
        if (*state->p == ':' && state->p[1] != ':')
            break;  // A single ':' is not part of an identifier.
        if (*state->p == ':')
            state->p++;  // Consume first ':' of '::'
        state->p++;
    }
    size_t len = state->p - start;
    if (len == 0)
        return nullptr;
    char * name = infix_arena_calloc(state->arena, 1, len + 1, 1);
    if (!name) {
        _infix_set_error(INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
        return nullptr;
    }
    infix_memcpy((void *)name, start, len);
    name[len] = '\0';
    return name;
}
/**
 * @internal
 * @brief Consumes a specific keyword from the string (e.g., "int", "struct").
 * @details This function is careful to match whole words only. For example, it will
 * successfully consume "int" from "int x", but will fail on "integer", preventing
 * false positives.
 * @param[in,out] state The parser state.
 * @param[in] keyword The keyword to consume.
 * @return `true` if the keyword was successfully consumed.
 */
static bool consume_keyword(parser_state * state, const char * keyword) {
    skip_whitespace(state);
    size_t len = strlen(keyword);
    if (strncmp(state->p, keyword, len) == 0) {
        // Ensure it's not a prefix of a longer word (e.g., "int" vs "integer").
        if (isalnum((unsigned char)state->p[len]) || state->p[len] == '_')
            return false;
        state->p += len;
        skip_whitespace(state);
        return true;
    }
    return false;
}
/**
 * @internal
 * @brief Parses an optional named prefix, like `name: type`.
 * @details If a valid identifier is found followed by a colon, the name is returned
 * and the parser's cursor is advanced past the colon. If not, the parser state is
 * rewound to its original position (backtracking) and `nullptr` is returned.
 * @param[in,out] state The parser state.
 * @return An arena-allocated string for the name, or `nullptr` if no `name:` prefix is present.
 */
static const char * parse_optional_name_prefix(parser_state * state) {
    skip_whitespace(state);
    // Save the current position in case we need to backtrack.
    const char * p_before = state->p;
    const char * name = parse_identifier(state);
    if (name) {
        skip_whitespace(state);
        if (*state->p == ':') {  // Found "identifier:", so consume the colon and return the name.
            state->p++;
            return name;
        }
    }
    // If it wasn't a `name:`, backtrack to the original position.
    state->p = p_before;
    return nullptr;
}
/**
 * @internal
 * @brief A lookahead function to disambiguate a grouped type `(type)` from a
 *        function signature `(...) -> type`.
 *
 * @details This is a classic parser "lookahead". When the parser encounters an opening
 * parenthesis `(`, it calls this function to peek ahead in the string without
 * consuming any input. By scanning for a matching `)` and checking if it is
 * followed by a `->` token, it can decide whether to parse the content as a
 * single, parenthesized type or as a full function signature.
 *
 * @param[in] state The current parser state (read-only).
 * @return `true` if a `->` token follows the closing parenthesis.
 */
static bool is_function_signature_ahead(const parser_state * state) {
    const char * p = state->p;
    if (*p != '(')
        return false;
    p++;
    // Find the matching ')' by tracking nesting depth.
    int depth = 1;
    while (*p != '\0' && depth > 0) {
        if (*p == '(')
            depth++;
        else if (*p == ')')
            depth--;
        p++;
    }
    if (depth != 0)
        return false;  // Mismatched parentheses.
    // Skip any whitespace or comments after the ')'
    while (isspace((unsigned char)*p) || *p == '#') {
        if (*p == '#')
            while (*p != '\n' && *p != '\0')
                p++;
        else
            p++;
    }
    // Check for the '->' arrow.
    return (p[0] == '-' && p[1] == '>');
}
// Aggregate Parsing Logic
/**
 * @internal
 * @brief Parses a comma-separated list of members for a struct or union.
 * @details This function is generic and handles the body of both `{...}` and `<...>` blocks.
 * It uses a temporary linked list to collect members since the count is not known
 * in advance, then converts the list to a flat array in the arena.
 * @param[in,out] state The parser state.
 * @param[in] end_char The character that terminates the list (e.g., '}' or '>').
 * @param[out] out_num_members A pointer to store the number of members found.
 * @return An arena-allocated array of `infix_struct_member`s, or `nullptr` on failure or if empty.
 */
static infix_struct_member * parse_aggregate_members(parser_state * state, char end_char, size_t * out_num_members) {
    // Use a temporary linked list to collect members, as the count is unknown in a single pass.
    typedef struct member_node {
        infix_struct_member m;



( run in 0.705 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )