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 )