Affix
view release on metacpan or search on metacpan
infix/src/core/signature.c view on Meta::CPAN
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;
struct member_node * next;
} member_node;
member_node *head = nullptr, *tail = nullptr;
size_t num_members = 0;
skip_whitespace(state);
if (*state->p != end_char) {
while (1) {
const char * p_before_member = state->p;
const char * name = parse_optional_name_prefix(state);
// Disallow an empty member definition like `name,` without a type.
if (name && (*state->p == ',' || *state->p == end_char)) {
state->p = p_before_member + strlen(name); // Position error at end of name
( run in 0.692 second using v1.01-cache-2.11-cpan-98e64b0badf )