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 )