infix
A JIT-Powered FFI Library for C
Loading...
Searching...
No Matches
signature.c
Go to the documentation of this file.
1
37#include <ctype.h>
38#include <stdarg.h>
39#include <stdbool.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
47#define MAX_RECURSION_DEPTH 32
53typedef struct {
54 const char * p;
55 const char * start;
57 int depth;
59// Forward Declarations for Mutually Recursive Parser Functions
60static infix_type * parse_type(parser_state * state);
62 infix_type ** out_ret_type,
63 infix_function_argument ** out_args,
64 size_t * out_num_args,
65 size_t * out_num_fixed_args);
66// Parser Helper Functions
74 _infix_set_error(INFIX_CATEGORY_PARSER, code, (size_t)(state->p - state->start));
75}
81static void skip_whitespace(parser_state * state) {
82 while (true) {
83 while (isspace((unsigned char)*state->p))
84 state->p++;
85 if (*state->p == '#') // C-style line comments
86 while (*state->p != '\n' && *state->p != '\0')
87 state->p++;
88 else
89 break;
90 }
91}
99static bool parse_size_t(parser_state * state, size_t * out_val) {
100 const char * start = state->p;
101 char * end;
102 errno = 0; // Reset errno before call
103 unsigned long long val = strtoull(start, &end, 10);
104
105 // Check for no conversion (end==start) OR overflow (ERANGE)
106 if (end == start || errno == ERANGE) {
107 // Use INTEGER_OVERFLOW code for range errors
109 return false;
110 }
111
112 // Check for truncation if size_t is smaller than unsigned long long (e.g. 32-bit builds)
113 if (val > SIZE_MAX) {
115 return false;
116 }
117 *out_val = (size_t)val;
118 state->p = end;
119 return true;
120}
129static const char * parse_identifier(parser_state * state) {
130 skip_whitespace(state);
131 const char * start = state->p;
132 if (!isalpha((unsigned char)*start) && *start != '_')
133 return nullptr;
134 while (isalnum((unsigned char)*state->p) || *state->p == '_' || *state->p == ':') {
135 if (*state->p == ':' && state->p[1] != ':')
136 break; // A single ':' is not part of an identifier.
137 if (*state->p == ':')
138 state->p++; // Consume first ':' of '::'
139 state->p++;
140 }
141 size_t len = state->p - start;
142 if (len == 0)
143 return nullptr;
144 char * name = infix_arena_calloc(state->arena, 1, len + 1, 1);
145 if (!name) {
147 return nullptr;
148 }
149 infix_memcpy((void *)name, start, len);
150 name[len] = '\0';
151 return name;
152}
163static bool consume_keyword(parser_state * state, const char * keyword) {
164 skip_whitespace(state);
165 size_t len = strlen(keyword);
166 if (strncmp(state->p, keyword, len) == 0) {
167 // Ensure it's not a prefix of a longer word (e.g., "int" vs "integer").
168 if (isalnum((unsigned char)state->p[len]) || state->p[len] == '_')
169 return false;
170 state->p += len;
171 skip_whitespace(state);
172 return true;
173 }
174 return false;
175}
185static const char * parse_optional_name_prefix(parser_state * state) {
186 skip_whitespace(state);
187 // Save the current position in case we need to backtrack.
188 const char * p_before = state->p;
189 const char * name = parse_identifier(state);
190 if (name) {
191 skip_whitespace(state);
192 if (*state->p == ':') { // Found "identifier:", so consume the colon and return the name.
193 state->p++;
194 return name;
195 }
196 }
197 // If it wasn't a `name:`, backtrack to the original position.
198 state->p = p_before;
199 return nullptr;
200}
215static bool is_function_signature_ahead(const parser_state * state) {
216 const char * p = state->p;
217 if (*p != '(')
218 return false;
219 p++;
220 // Find the matching ')' by tracking nesting depth.
221 int depth = 1;
222 while (*p != '\0' && depth > 0) {
223 if (*p == '(')
224 depth++;
225 else if (*p == ')')
226 depth--;
227 p++;
228 }
229 if (depth != 0)
230 return false; // Mismatched parentheses.
231 // Skip any whitespace or comments after the ')'
232 while (isspace((unsigned char)*p) || *p == '#') {
233 if (*p == '#')
234 while (*p != '\n' && *p != '\0')
235 p++;
236 else
237 p++;
238 }
239 // Check for the '->' arrow.
240 return (p[0] == '-' && p[1] == '>');
241}
242// Aggregate Parsing Logic
254static infix_struct_member * parse_aggregate_members(parser_state * state, char end_char, size_t * out_num_members) {
255 // Use a temporary linked list to collect members, as the count is unknown in a single pass.
256 typedef struct member_node {
258 struct member_node * next;
259 } member_node;
260 member_node *head = nullptr, *tail = nullptr;
261 size_t num_members = 0;
262 skip_whitespace(state);
263 if (*state->p != end_char) {
264 while (1) {
265 const char * p_before_member = state->p;
266 const char * name = parse_optional_name_prefix(state);
267 // Disallow an empty member definition like `name,` without a type.
268 if (name && (*state->p == ',' || *state->p == end_char)) {
269 state->p = p_before_member + strlen(name); // Position error at end of name
271 return nullptr;
272 }
273 infix_type * member_type = parse_type(state);
274 if (!member_type)
275 return nullptr;
276 // Structs and unions cannot have `void` members.
277 if (member_type->category == INFIX_TYPE_VOID) {
279 return nullptr;
280 }
281
282 // Check for bitfield syntax: "name: type : width"
283 uint8_t bit_width = 0;
284 bool is_bitfield = false;
285 skip_whitespace(state);
286 if (*state->p == ':') {
287 state->p++; // Consume ':'
288 skip_whitespace(state);
289 size_t width_val = 0;
290 if (!parse_size_t(state, &width_val))
291 return nullptr; // Error set by parse_size_t
292 if (width_val > 255) {
294 return nullptr;
295 }
296 bit_width = (uint8_t)width_val;
297 is_bitfield = true;
298 }
299
300 member_node * node = infix_arena_calloc(state->arena, 1, sizeof(member_node), _Alignof(member_node));
301 if (!node) {
303 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
304 return nullptr;
305 }
306 // The member offset is not calculated here; it will be done later
307 // by `infix_type_create_struct` or `_infix_type_recalculate_layout`.
308 if (is_bitfield)
309 node->m = infix_type_create_bitfield_member(name, member_type, 0, bit_width);
310 else
311 node->m = infix_type_create_member(name, member_type, 0);
312
313 node->next = nullptr;
314
315 if (!head)
316 head = tail = node;
317 else {
318 tail->next = node;
319 tail = node;
320 }
321 num_members++;
322 // Check for next token: ',' or end_char
323 skip_whitespace(state);
324 if (*state->p == ',') {
325 state->p++; // Consume comma.
326 skip_whitespace(state);
327 // A trailing comma like `{int,}` is a syntax error.
328 if (*state->p == end_char) {
330 return nullptr;
331 }
332 }
333 else if (*state->p == end_char)
334 break;
335 else { // Unexpected token (e.g., missing comma).
336 if (*state->p == '\0') {
338 return nullptr;
339 }
341 return nullptr;
342 }
343 }
344 }
345 *out_num_members = num_members;
346 if (num_members == 0)
347 return nullptr;
348 // Convert the temporary linked list to a flat array in the arena.
350 infix_arena_calloc(state->arena, num_members, sizeof(infix_struct_member), _Alignof(infix_struct_member));
351 if (!members) {
353 return nullptr;
354 }
355 member_node * current = head;
356 for (size_t i = 0; i < num_members; i++) {
357 members[i] = current->m;
358 current = current->next;
359 }
360 return members;
361}
370static infix_type * parse_aggregate(parser_state * state, char start_char, char end_char) {
371 if (state->depth >= MAX_RECURSION_DEPTH) {
373 return nullptr;
374 }
375 state->depth++;
376 if (*state->p != start_char) {
378 state->depth--;
379 return nullptr;
380 }
381 state->p++;
382 size_t num_members = 0;
383 infix_struct_member * members = parse_aggregate_members(state, end_char, &num_members);
384 // If member parsing failed, an error is already set. Propagate the failure.
386 state->depth--;
387 return nullptr;
388 }
389 if (*state->p != end_char) {
391 state->depth--;
392 return nullptr;
393 }
394 state->p++;
395 infix_type * agg_type = nullptr;
396 infix_status status = (start_char == '{') ? infix_type_create_struct(state->arena, &agg_type, members, num_members)
397 : infix_type_create_union(state->arena, &agg_type, members, num_members);
398 if (status != INFIX_SUCCESS) {
399 state->depth--;
400 return nullptr;
401 }
402 state->depth--;
403 return agg_type;
404}
412 size_t alignment = 1; // Default alignment for `!{...}` is 1.
413 if (*state->p == '!') {
414 state->p++;
415 if (isdigit((unsigned char)*state->p)) {
416 // This is the `!N:{...}` form with an explicit alignment.
417 if (!parse_size_t(state, &alignment))
418 return nullptr;
419 if (*state->p != ':') {
421 return nullptr;
422 }
423 state->p++;
424 }
425 }
426 skip_whitespace(state);
427 if (*state->p != '{') {
429 return nullptr;
430 }
431 state->p++;
432 size_t num_members = 0;
433 infix_struct_member * members = parse_aggregate_members(state, '}', &num_members);
435 return nullptr;
436 if (*state->p != '}') {
438 return nullptr;
439 }
440 state->p++;
441 infix_type * packed_type = nullptr;
442 // For packed structs, the total size is simply the sum of member sizes without padding.
443 // The user of `infix_type_create_packed_struct` must provide pre-calculated offsets.
444 // Since our parser doesn't know the offsets, we pass a preliminary size. The final
445 // layout pass will fix this if needed, but for packed structs, the user's offsets
446 // are king.
447 size_t total_size = 0;
448 for (size_t i = 0; i < num_members; ++i)
449 total_size += members[i].type->size;
451 infix_type_create_packed_struct(state->arena, &packed_type, total_size, alignment, members, num_members);
452 if (status != INFIX_SUCCESS)
453 return nullptr;
454 return packed_type;
455}
456// Main Parser Logic
468 if (consume_keyword(state, "sint8") || consume_keyword(state, "int8"))
470 if (consume_keyword(state, "uint8"))
472 if (consume_keyword(state, "sint16") || consume_keyword(state, "int16"))
474 if (consume_keyword(state, "uint16"))
476 if (consume_keyword(state, "sint32") || consume_keyword(state, "int32"))
478 if (consume_keyword(state, "uint32"))
480 if (consume_keyword(state, "sint64") || consume_keyword(state, "int64"))
482 if (consume_keyword(state, "uint64"))
484 if (consume_keyword(state, "sint128") || consume_keyword(state, "int128"))
486 if (consume_keyword(state, "uint128"))
488 if (consume_keyword(state, "float32"))
490 if (consume_keyword(state, "float64"))
492 if (consume_keyword(state, "bool"))
494 if (consume_keyword(state, "void"))
495 return infix_type_create_void();
496 // C-style convenience aliases
497 if (consume_keyword(state, "uchar"))
499 if (consume_keyword(state, "char"))
501 if (consume_keyword(state, "ushort"))
503 if (consume_keyword(state, "short"))
505 if (consume_keyword(state, "uint"))
507 if (consume_keyword(state, "int"))
509 if (consume_keyword(state, "ulonglong"))
511 if (consume_keyword(state, "longlong"))
513 // `long` is platform-dependent, so we use `sizeof` to pick the correct size.
514 if (consume_keyword(state, "ulong"))
515 return infix_type_create_primitive(sizeof(unsigned long) == 8 ? INFIX_PRIMITIVE_UINT64
517 if (consume_keyword(state, "long"))
519 if (consume_keyword(state, "double"))
521 if (consume_keyword(state, "float"))
523 if (consume_keyword(state, "longdouble"))
525 if (consume_keyword(state, "size_t"))
527 if (consume_keyword(state, "ssize_t"))
529 // uchar.h types
530 if (consume_keyword(state, "char8_t"))
532 if (consume_keyword(state, "char16_t"))
534 if (consume_keyword(state, "char32_t"))
536 // AVX convenience aliases
537 if (consume_keyword(state, "m256d")) {
538 infix_type * type = nullptr;
541 if (status != INFIX_SUCCESS)
542 return nullptr; // Propagate failure
543 type->alignment = 32; // YMM registers require 32-byte alignment
544 return type;
545 }
546 if (consume_keyword(state, "m256")) {
547 infix_type * type = nullptr;
550 if (status != INFIX_SUCCESS)
551 return nullptr; // Propagate failure
552 type->alignment = 32; // YMM registers require 32-byte alignment
553 return type;
554 }
555 if (consume_keyword(state, "m512d")) {
556 infix_type * type = nullptr;
559 if (status != INFIX_SUCCESS)
560 return nullptr;
561 type->alignment = 64; // ZMM registers have 64-byte alignment
562 return type;
563 }
564 if (consume_keyword(state, "m512")) {
565 infix_type * type = nullptr;
568 if (status != INFIX_SUCCESS)
569 return nullptr;
570 type->alignment = 64;
571 return type;
572 }
573 if (consume_keyword(state, "m512i")) {
574 infix_type * type = nullptr;
577 if (status != INFIX_SUCCESS)
578 return nullptr;
579 type->alignment = 64;
580 return type;
581 }
582 return nullptr;
583}
595 if (state->depth >= MAX_RECURSION_DEPTH) {
597 return nullptr;
598 }
599 state->depth++;
600 skip_whitespace(state);
601 // Capture the offset from the start of the signature string *before* parsing the type.
602 size_t current_offset = state->p - state->start;
603 infix_type * result_type = nullptr;
604 const char * p_before_type = state->p;
605 if (*state->p == '@') { // Named type reference: `@MyStruct`
606 state->p++;
607 const char * name = parse_identifier(state);
608 if (!name) {
610 state->depth--;
611 return nullptr;
612 }
613 if (infix_type_create_named_reference(state->arena, &result_type, name, INFIX_AGGREGATE_STRUCT) !=
615 result_type = nullptr;
616 }
617 else if (*state->p == '*') { // Pointer type: `*int`
618 state->p++;
619 skip_whitespace(state);
620 infix_type * pointee_type = parse_type(state);
621 if (!pointee_type) {
622 state->depth--;
623 return nullptr;
624 }
625 if (infix_type_create_pointer_to(state->arena, &result_type, pointee_type) != INFIX_SUCCESS)
626 result_type = nullptr;
627 }
628 else if (*state->p == '(') { // Grouped type `(type)` or function pointer `(...) -> type`
629 if (is_function_signature_ahead(state)) {
630 infix_type * ret_type = nullptr;
631 infix_function_argument * args = nullptr;
632 size_t num_args = 0, num_fixed = 0;
633 if (parse_function_signature_details(state, &ret_type, &args, &num_args, &num_fixed) != INFIX_SUCCESS) {
634 state->depth--;
635 return nullptr;
636 }
637 // Manually construct a function pointer type object.
638 // This is represented internally as a pointer-like type with extra metadata.
639 infix_type * func_type = infix_arena_calloc(state->arena, 1, sizeof(infix_type), _Alignof(infix_type));
640 if (!func_type) {
642 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
643 state->depth--;
644 return nullptr;
645 }
646 func_type->size = sizeof(void *);
647 func_type->alignment = _Alignof(void *);
648 func_type->is_arena_allocated = true;
649 func_type->category = INFIX_TYPE_REVERSE_TRAMPOLINE; // Special category for function types.
651 func_type->meta.func_ptr_info.args = args;
652 func_type->meta.func_ptr_info.num_args = num_args;
653 func_type->meta.func_ptr_info.num_fixed_args = num_fixed;
654 result_type = func_type;
655 }
656 else { // Grouped type: `(type)`
657 state->p++;
658 skip_whitespace(state);
659 result_type = parse_type(state);
660 if (!result_type) {
661 state->depth--;
662 return nullptr;
663 }
664 skip_whitespace(state);
665 if (*state->p != ')') {
667 result_type = nullptr;
668 }
669 else
670 state->p++;
671 }
672 }
673 else if (*state->p == '[') { // Array type: `[size:type]`
674 state->p++;
675 skip_whitespace(state);
676 size_t num_elements = 0;
677 bool is_flexible = false;
678
679 if (*state->p == '?') {
680 // Flexible array member: `[?:type]`
681 is_flexible = true;
682 state->p++;
683 }
684 else if (!parse_size_t(state, &num_elements)) {
685 state->depth--;
686 return nullptr;
687 }
688
689 skip_whitespace(state);
690 if (*state->p != ':') {
692 state->depth--;
693 return nullptr;
694 }
695 state->p++;
696 skip_whitespace(state);
697 infix_type * element_type = parse_type(state);
698 if (!element_type) {
699 state->depth--;
700 return nullptr;
701 }
702 if (element_type->category == INFIX_TYPE_VOID) { // An array of `void` is illegal in C.
704 state->depth--;
705 return nullptr;
706 }
707 skip_whitespace(state);
708 if (*state->p != ']') {
710 state->depth--;
711 return nullptr;
712 }
713 state->p++;
714
715 if (is_flexible) {
716 if (infix_type_create_flexible_array(state->arena, &result_type, element_type) != INFIX_SUCCESS)
717 result_type = nullptr;
718 }
719 else {
720 if (infix_type_create_array(state->arena, &result_type, element_type, num_elements) != INFIX_SUCCESS)
721 result_type = nullptr;
722 }
723 }
724 else if (*state->p == '!') // Packed struct
725 result_type = parse_packed_struct(state);
726 else if (*state->p == '{') // Struct
727 result_type = parse_aggregate(state, '{', '}');
728 else if (*state->p == '<') // Union
729 result_type = parse_aggregate(state, '<', '>');
730 else if (*state->p == 'e' && state->p[1] == ':') { // Enum: `e:type`
731 state->p += 2;
732 skip_whitespace(state);
733 infix_type * underlying_type = parse_type(state);
734 if (!underlying_type || underlying_type->category != INFIX_TYPE_PRIMITIVE) {
736 state->depth--;
737 return nullptr;
738 }
739 if (infix_type_create_enum(state->arena, &result_type, underlying_type) != INFIX_SUCCESS)
740 result_type = nullptr;
741 }
742 else if (*state->p == 'c' && state->p[1] == '[') { // Complex: `c[type]`
743 state->p += 2;
744 skip_whitespace(state);
745 infix_type * base_type = parse_type(state);
746 if (!base_type) {
747 state->depth--;
748 return nullptr;
749 }
750 skip_whitespace(state);
751 if (*state->p != ']') {
753 state->depth--;
754 return nullptr;
755 }
756 state->p++;
757 if (infix_type_create_complex(state->arena, &result_type, base_type) != INFIX_SUCCESS)
758 result_type = nullptr;
759 }
760 else if (*state->p == 'v' && state->p[1] == '[') { // Vector: `v[size:type]`
761 state->p += 2;
762 skip_whitespace(state);
763 size_t num_elements;
764 if (!parse_size_t(state, &num_elements)) {
765 state->depth--;
766 return nullptr;
767 }
768 if (*state->p != ':') {
770 state->depth--;
771 return nullptr;
772 }
773 state->p++;
774 infix_type * element_type = parse_type(state);
775 if (!element_type) {
776 state->depth--;
777 return nullptr;
778 }
779 if (*state->p != ']') {
781 state->depth--;
782 return nullptr;
783 }
784 state->p++;
785 if (infix_type_create_vector(state->arena, &result_type, element_type, num_elements) != INFIX_SUCCESS)
786 result_type = nullptr;
787 }
788 else { // Primitive type or error
789 result_type = parse_primitive(state);
790 if (!result_type) {
791 // If no error was set by a failed `consume_keyword`, set a generic one.
793 state->p = p_before_type;
794 if (isalpha((unsigned char)*state->p) || *state->p == '_')
796 else
798 }
799 }
800 }
801 // Only set source offset for dynamically allocated types (primitives are static singletons).
802 if (result_type && result_type->is_arena_allocated)
803 result_type->source_offset = current_offset;
804 state->depth--;
805 return result_type;
806}
821 infix_type ** out_ret_type,
822 infix_function_argument ** out_args,
823 size_t * out_num_args,
824 size_t * out_num_fixed_args) {
825 if (*state->p != '(') {
828 }
829 state->p++;
830 skip_whitespace(state);
831 // Use a temporary linked list to collect arguments.
832 typedef struct arg_node {
834 struct arg_node * next;
835 } arg_node;
836 arg_node *head = nullptr, *tail = nullptr;
837 size_t num_args = 0;
838 // Parse Fixed Arguments
839 if (*state->p != ')' && *state->p != ';') {
840 while (1) {
841 skip_whitespace(state);
842 if (*state->p == ')' || *state->p == ';')
843 break;
844 const char * name = parse_optional_name_prefix(state);
845 infix_type * arg_type = parse_type(state);
846 if (!arg_type)
848 arg_node * node = infix_arena_calloc(state->arena, 1, sizeof(arg_node), _Alignof(arg_node));
849 if (!node) {
851 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
853 }
854 node->arg.type = arg_type;
855 node->arg.name = name;
856 node->next = nullptr;
857 if (!head)
858 head = tail = node;
859 else {
860 tail->next = node;
861 tail = node;
862 }
863 num_args++;
864 skip_whitespace(state);
865 if (*state->p == ',') {
866 state->p++;
867 skip_whitespace(state);
868 if (*state->p == ')' || *state->p == ';') { // Trailing comma error.
871 }
872 }
873 else if (*state->p != ')' && *state->p != ';') {
876 }
877 else
878 break;
879 }
880 }
881 *out_num_fixed_args = num_args;
882 // Parse Variadic Arguments
883 if (*state->p == ';') {
884 state->p++;
885 if (*state->p != ')') {
886 while (1) {
887 skip_whitespace(state);
888 if (*state->p == ')')
889 break;
890 const char * name = parse_optional_name_prefix(state);
891 infix_type * arg_type = parse_type(state);
892 if (!arg_type)
894 arg_node * node = infix_arena_calloc(state->arena, 1, sizeof(arg_node), _Alignof(arg_node));
895 if (!node) {
897 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
899 }
900 node->arg.type = arg_type;
901 node->arg.name = name;
902 node->next = nullptr;
903 if (!head)
904 head = tail = node;
905 else {
906 tail->next = node;
907 tail = node;
908 }
909 num_args++;
910 skip_whitespace(state);
911 if (*state->p == ',') {
912 state->p++;
913 skip_whitespace(state);
914 if (*state->p == ')') { // Trailing comma error.
917 }
918 }
919 else if (*state->p != ')') {
922 }
923 else
924 break;
925 }
926 }
927 }
928 skip_whitespace(state);
929 if (*state->p != ')') {
932 }
933 state->p++;
934 // Parse Return Type
935 skip_whitespace(state);
936 if (state->p[0] != '-' || state->p[1] != '>') {
939 }
940 state->p += 2;
941 *out_ret_type = parse_type(state);
942 if (!*out_ret_type)
944 // Convert linked list of args to a flat array.
945 infix_function_argument * args = (num_args > 0)
946 ? infix_arena_calloc(state->arena, num_args, sizeof(infix_function_argument), _Alignof(infix_function_argument))
947 : nullptr;
948 if (num_args > 0 && !args) {
951 }
952 arg_node * current = head;
953 for (size_t i = 0; i < num_args; i++) {
954 args[i] = current->arg;
955 current = current->next;
956 }
957 *out_args = args;
958 *out_num_args = num_args;
959 return INFIX_SUCCESS;
960}
961// High-Level API Implementation
979 infix_arena_t ** out_arena,
980 const char * signature) {
981 if (!out_type || !out_arena) {
984 }
985 if (!signature || *signature == '\0') {
988 }
989 // The top-level public API is responsible for setting g_infix_last_signature_context.
990 *out_arena = infix_arena_create(4096);
991 if (!*out_arena) {
994 }
995 parser_state state = {.p = signature, .start = signature, .arena = *out_arena, .depth = 0};
996 infix_type * type = parse_type(&state);
997 if (type) {
998 skip_whitespace(&state);
999 // After successfully parsing a type, ensure there is no trailing junk.
1000 if (state.p[0] != '\0') {
1002 type = nullptr;
1003 }
1004 }
1005 if (!type) {
1006 // If parsing failed at any point, clean up the temporary arena.
1007 infix_arena_destroy(*out_arena);
1008 *out_arena = nullptr;
1009 *out_type = nullptr;
1011 }
1012 *out_type = type;
1013 return INFIX_SUCCESS;
1014}
1029 infix_arena_t ** out_arena,
1030 const char * signature,
1033 g_infix_last_signature_context = signature; // Set context for rich error reporting.
1034 // 1. "Parse" stage: Create a raw, unresolved type graph in a temporary arena.
1035 infix_type * raw_type = nullptr;
1036 infix_arena_t * parser_arena = nullptr;
1037 infix_status status = _infix_parse_type_internal(&raw_type, &parser_arena, signature);
1038 if (status != INFIX_SUCCESS)
1039 return status;
1040 // Create the final arena that will be returned to the caller.
1041 *out_arena = infix_arena_create(4096);
1042 if (!*out_arena) {
1043 infix_arena_destroy(parser_arena);
1046 }
1047 // 2. "Copy" stage: Deep copy the raw graph into the final arena.
1048 infix_type * final_type = _copy_type_graph_to_arena(*out_arena, raw_type);
1049 infix_arena_destroy(parser_arena); // The temporary graph is no longer needed.
1050 if (!final_type) {
1051 infix_arena_destroy(*out_arena);
1052 *out_arena = nullptr;
1055 }
1056 // 3. "Resolve" stage: Replace all named references (`@Name`) with concrete types.
1058 if (status != INFIX_SUCCESS) {
1059 infix_arena_destroy(*out_arena);
1060 *out_arena = nullptr;
1061 *out_type = nullptr;
1062 }
1063 else {
1064 // 4. "Layout" stage: Calculate the final size, alignment, and member offsets.
1066 *out_type = final_type;
1067 }
1068 return status;
1069}
1088 infix_arena_t ** out_arena,
1089 infix_type ** out_ret_type,
1090 infix_function_argument ** out_args,
1091 size_t * out_num_args,
1092 size_t * out_num_fixed_args,
1095
1096 //
1097 if (!signature) {
1100 }
1101 if (*signature == '\0') {
1104 }
1105 if (!out_arena || !out_ret_type || !out_args || !out_num_args || !out_num_fixed_args) {
1108 }
1109
1111
1112 // Parse stage
1113 infix_type * raw_func_type = nullptr;
1114 infix_arena_t * parser_arena = nullptr;
1115 infix_status status = _infix_parse_type_internal(&raw_func_type, &parser_arena, signature);
1116 if (status != INFIX_SUCCESS)
1117 return status;
1118
1119 if (raw_func_type->category != INFIX_TYPE_REVERSE_TRAMPOLINE) {
1120 infix_arena_destroy(parser_arena);
1123 }
1124
1125 // Create final arena
1126 *out_arena = infix_arena_create(8192);
1127 if (!*out_arena) {
1128 infix_arena_destroy(parser_arena);
1131 }
1132
1133 // "Copy" stage
1134 infix_type * final_func_type = _copy_type_graph_to_arena(*out_arena, raw_func_type);
1135 infix_arena_destroy(parser_arena);
1136 if (!final_func_type) {
1137 infix_arena_destroy(*out_arena);
1138 *out_arena = nullptr;
1141 }
1142
1143 // Resolve and layout stages
1145 if (status != INFIX_SUCCESS) {
1146 infix_arena_destroy(*out_arena);
1147 *out_arena = nullptr;
1149 }
1150 _infix_type_recalculate_layout(final_func_type);
1151
1152 // Unpack the results for the caller from the final, processed function type object.
1153 *out_ret_type = final_func_type->meta.func_ptr_info.return_type;
1154 *out_args = final_func_type->meta.func_ptr_info.args;
1155 *out_num_args = final_func_type->meta.func_ptr_info.num_args;
1156 *out_num_fixed_args = final_func_type->meta.func_ptr_info.num_fixed_args;
1157 return INFIX_SUCCESS;
1158}
1159
1160// Type Printing Logic
1166typedef struct {
1167 char * p;
1168 size_t remaining;
1179static void _print(printer_state * state, const char * fmt, ...) {
1180 if (state->status != INFIX_SUCCESS)
1181 return;
1182 va_list args;
1183 va_start(args, fmt);
1184 int written = vsnprintf(state->p, state->remaining, fmt, args);
1185 va_end(args);
1186 if (written < 0 || (size_t)written >= state->remaining)
1187 // If snprintf failed or would have overflowed, mark an error.
1189 else {
1190 state->p += written;
1191 state->remaining -= written;
1192 }
1193}
1194// Forward declaration for mutual recursion in printers.
1195static void _infix_type_print_signature_recursive(printer_state * state, const infix_type * type);
1196static void _infix_type_print_itanium_recursive(printer_state * state, const infix_type * type);
1197static void _infix_type_print_msvc_recursive(printer_state * state, const infix_type * type);
1198
1212 if (state->status != INFIX_SUCCESS || !type) {
1213 if (state->status == INFIX_SUCCESS)
1215 return;
1216 }
1217 // If the type has a semantic name, always prefer printing it.
1218 if (type->name) {
1219 _print(state, "@%s", type->name);
1220 return;
1221 }
1222 switch (type->category) {
1223 case INFIX_TYPE_VOID:
1224 _print(state, "void");
1225 break;
1227 // This case should ideally not be hit with a fully resolved type, but we handle it for robustness.
1228 _print(state, "@%s", type->meta.named_reference.name);
1229 break;
1230 case INFIX_TYPE_POINTER:
1231 _print(state, "*");
1232 // Special handling for `void*` or recursive pointers to avoid infinite recursion.
1233 if (type->meta.pointer_info.pointee_type == type || type->meta.pointer_info.pointee_type == nullptr ||
1235 _print(state, "void");
1236 else
1238 break;
1239 case INFIX_TYPE_ARRAY:
1240 if (type->meta.array_info.is_flexible)
1241 _print(state, "[?:");
1242 else
1243 _print(state, "[%zu:", type->meta.array_info.num_elements);
1245 _print(state, "]");
1246 break;
1247 case INFIX_TYPE_STRUCT:
1248 if (type->meta.aggregate_info.is_packed) {
1249 _print(state, "!");
1250 if (type->alignment != 1)
1251 _print(state, "%zu:", type->alignment);
1252 }
1253 _print(state, "{");
1254 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1255 if (i > 0)
1256 _print(state, ",");
1257 const infix_struct_member * member = &type->meta.aggregate_info.members[i];
1258 if (member->name)
1259 _print(state, "%s:", member->name);
1261 if (member->bit_width > 0)
1262 _print(state, ":%u", member->bit_width);
1263 }
1264 _print(state, "}");
1265 break;
1266 case INFIX_TYPE_UNION:
1267 _print(state, "<");
1268 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1269 if (i > 0)
1270 _print(state, ",");
1271 const infix_struct_member * member = &type->meta.aggregate_info.members[i];
1272 if (member->name)
1273 _print(state, "%s:", member->name);
1275 // Bitfields in unions are rare but syntactically valid in C.
1276 if (member->bit_width > 0)
1277 _print(state, ":%u", member->bit_width);
1278 }
1279 _print(state, ">");
1280 break;
1282 _print(state, "(");
1283 for (size_t i = 0; i < type->meta.func_ptr_info.num_fixed_args; ++i) {
1284 if (i > 0)
1285 _print(state, ",");
1286 const infix_function_argument * arg = &type->meta.func_ptr_info.args[i];
1287 if (arg->name)
1288 _print(state, "%s:", arg->name);
1290 }
1292 _print(state, ";");
1293 for (size_t i = type->meta.func_ptr_info.num_fixed_args; i < type->meta.func_ptr_info.num_args; ++i) {
1294 if (i > type->meta.func_ptr_info.num_fixed_args)
1295 _print(state, ",");
1296 const infix_function_argument * arg = &type->meta.func_ptr_info.args[i];
1297 if (arg->name)
1298 _print(state, "%s:", arg->name);
1300 }
1301 }
1302 _print(state, ")->");
1304 break;
1305 case INFIX_TYPE_ENUM:
1306 _print(state, "e:");
1308 break;
1309 case INFIX_TYPE_COMPLEX:
1310 _print(state, "c[");
1312 _print(state, "]");
1313 break;
1314 case INFIX_TYPE_VECTOR:
1315 {
1316 const infix_type * element_type = type->meta.vector_info.element_type;
1317 size_t num_elements = type->meta.vector_info.num_elements;
1318 bool printed_alias = false;
1319 if (element_type->category == INFIX_TYPE_PRIMITIVE) {
1320 if (num_elements == 8 && is_double(element_type)) {
1321 _print(state, "m512d");
1322 printed_alias = true;
1323 }
1324 else if (num_elements == 16 && is_float(element_type)) {
1325 _print(state, "m512");
1326 printed_alias = true;
1327 }
1328 else if (num_elements == 8 && element_type->meta.primitive_id == INFIX_PRIMITIVE_SINT64) {
1329 _print(state, "m512i");
1330 printed_alias = true;
1331 }
1332 else if (num_elements == 4 && is_double(element_type)) {
1333 _print(state, "m256d");
1334 printed_alias = true;
1335 }
1336 else if (num_elements == 8 && is_float(element_type)) {
1337 _print(state, "m256");
1338 printed_alias = true;
1339 }
1340 }
1341 if (!printed_alias) {
1342 _print(state, "v[%zu:", num_elements);
1343 _infix_type_print_signature_recursive(state, element_type);
1344 _print(state, "]");
1345 }
1346 }
1347 break;
1349 switch (type->meta.primitive_id) {
1351 _print(state, "bool");
1352 break;
1354 _print(state, "sint8");
1355 break;
1357 _print(state, "uint8");
1358 break;
1360 _print(state, "sint16");
1361 break;
1363 _print(state, "uint16");
1364 break;
1366 _print(state, "sint32");
1367 break;
1369 _print(state, "uint32");
1370 break;
1372 _print(state, "sint64");
1373 break;
1375 _print(state, "uint64");
1376 break;
1378 _print(state, "sint128");
1379 break;
1381 _print(state, "uint128");
1382 break;
1384 _print(state, "float");
1385 break;
1387 _print(state, "double");
1388 break;
1390 _print(state, "longdouble");
1391 break;
1392 }
1393 break;
1394 default:
1396 break;
1397 }
1398}
1406 if (state->status != INFIX_SUCCESS || !type) {
1407 if (state->status == INFIX_SUCCESS)
1409 return;
1410 }
1411 switch (type->category) {
1412 case INFIX_TYPE_VOID:
1413 _print(state, "v");
1414 break;
1415 case INFIX_TYPE_POINTER:
1416 _print(state, "P");
1418 break;
1420 switch (type->meta.primitive_id) {
1422 _print(state, "b");
1423 break;
1425 _print(state, "a");
1426 break; // signed char
1428 _print(state, "h");
1429 break; // unsigned char
1431 _print(state, "s");
1432 break; // short
1434 _print(state, "t");
1435 break; // unsigned short
1437 _print(state, "i");
1438 break; // int
1440 _print(state, "j");
1441 break; // unsigned int
1443 _print(state, "x");
1444 break; // long long
1446 _print(state, "y");
1447 break; // unsigned long long
1449 _print(state, "n");
1450 break; // __int128
1452 _print(state, "o");
1453 break; // unsigned __int128
1455 _print(state, "f");
1456 break;
1458 _print(state, "d");
1459 break;
1461 _print(state, "e");
1462 break;
1463 }
1464 break;
1466 {
1467 const char * name = type->meta.named_reference.name;
1468 size_t len = strlen(name);
1469 _print(state, "%zu%s", len, name);
1470 }
1471 break;
1472 case INFIX_TYPE_STRUCT:
1473 case INFIX_TYPE_UNION:
1474 if (type->name) {
1475 // Check for namespaced type (e.g. "Namespace::Class")
1476 // Itanium mangling for namespaces is N...E
1477 const char * p = type->name;
1478 int parts = 0;
1479 // Count parts
1480 while (*p) {
1481 if (p[0] == ':' && p[1] == ':') {
1482 parts++;
1483 p += 2;
1484 }
1485 else {
1486 p++;
1487 }
1488 }
1489 parts++; // Last part
1490
1491 if (parts > 1) {
1492 _print(state, "N");
1493 p = type->name;
1494 while (*p) {
1495 const char * end = strstr(p, "::");
1496 size_t part_len = end ? (size_t)(end - p) : strlen(p);
1497 _print(state, "%zu", part_len);
1498 // Print part_len chars
1499 for (size_t i = 0; i < part_len; i++)
1500 _print(state, "%c", p[i]);
1501 if (end)
1502 p = end + 2;
1503 else
1504 break;
1505 }
1506 _print(state, "E");
1507 }
1508 else {
1509 // Simple name
1510 size_t len = strlen(type->name);
1511 _print(state, "%zu%s", len, type->name);
1512 }
1513 }
1514 else {
1515 // Mangling for anonymous structs isn't standardized.
1516 // Emitting 'void' as a safe placeholder for "unknown type".
1517 _print(state, "v");
1518 }
1519 break;
1520 default:
1521 // Fallback for types that don't map cleanly to Itanium mangling
1522 _print(state, "v");
1523 break;
1524 }
1525}
1533 if (state->status != INFIX_SUCCESS || !type) {
1534 if (state->status == INFIX_SUCCESS)
1536 return;
1537 }
1538 // Handle named types (Struct/Union/Enum or aliases)
1539 if (type->name) {
1540 // MSVC encoding:
1541 // U = Struct
1542 // T = Union
1543 // W = Enum
1544 char prefix = 'U';
1545 if (type->category == INFIX_TYPE_UNION)
1546 prefix = 'T';
1547 else if (type->category == INFIX_TYPE_ENUM)
1548 prefix = 'W';
1549
1550 // Check for namespaces (e.g. "Namespace::Class")
1551 // MSVC format: <Prefix><Name>@<Namespace>@@
1552 // Reverse order of namespaces!
1553 if (strstr(type->name, "::")) {
1554 _print(state, "%c", prefix);
1555
1556 // We need to split and reverse. Since we can't allocate easily here,
1557 // we'll scan the string multiple times or use recursion.
1558 // Let's use a simple stack-based approach for small depth.
1559 const char * parts[MAX_RECURSION_DEPTH];
1560 size_t lens[MAX_RECURSION_DEPTH];
1561 int count = 0;
1562
1563 const char * p = type->name;
1564 while (*p && count < MAX_RECURSION_DEPTH) {
1565 parts[count] = p;
1566 const char * end = strstr(p, "::");
1567 if (end) {
1568 lens[count] = end - p;
1569 p = end + 2;
1570 }
1571 else {
1572 lens[count] = strlen(p);
1573 p += lens[count];
1574 }
1575 count++;
1576 }
1577
1578 // Print in reverse order
1579 for (int i = count - 1; i >= 0; i--) {
1580 for (size_t j = 0; j < lens[i]; j++)
1581 _print(state, "%c", parts[i][j]);
1582 _print(state, "@");
1583 }
1584 _print(state, "@"); // Terminator
1585 }
1586 else {
1587 _print(state, "%c%s@@", prefix, type->name);
1588 }
1589 return;
1590 }
1591
1592 switch (type->category) {
1593 case INFIX_TYPE_VOID:
1594 _print(state, "X");
1595 break;
1597 switch (type->meta.primitive_id) {
1599 _print(state, "_N");
1600 break;
1602 _print(state, "C");
1603 break; // signed char
1605 _print(state, "E");
1606 break; // unsigned char
1608 _print(state, "F");
1609 break; // short
1611 _print(state, "G");
1612 break; // unsigned short
1614 _print(state, "H");
1615 break; // int
1617 _print(state, "I");
1618 break; // unsigned int
1620 _print(state, "_J");
1621 break; // __int64
1623 _print(state, "_K");
1624 break; // unsigned __int64
1626 _print(state, "M");
1627 break;
1629 _print(state, "N");
1630 break;
1631 // MSVC typically maps long double to double (N), but distinct type O exists
1633 _print(state, "O");
1634 break;
1635 default:
1636 _print(state, "?");
1637 break;
1638 }
1639 break;
1640 case INFIX_TYPE_POINTER:
1641 // Standard MSVC pointer encoding for x64:
1642 // P = Pointer
1643 // E = __ptr64
1644 // A = const/volatile qualifiers (A = none)
1645 // Then the pointee type.
1646 _print(state, "PEA");
1648 break;
1649 case INFIX_TYPE_REVERSE_TRAMPOLINE: // Function Pointer
1650 // P6 = Pointer to Function
1651 // A = __cdecl
1652 _print(state, "P6A");
1653 // Return type
1655 // Arguments
1656 if (type->meta.func_ptr_info.num_args == 0)
1657 _print(state, "X");
1658 else
1659 for (size_t i = 0; i < type->meta.func_ptr_info.num_args; ++i)
1661 _print(state, "@Z");
1662 break;
1664 // Unresolved references, treat as Struct for mangling purposes.
1665 _print(state, "U%s@@", type->meta.named_reference.name);
1666 break;
1667 default:
1668 // Arrays, function pointers, etc. are complex. Fallback.
1669 _print(state, "?");
1670 break;
1671 }
1672}
1686 if (state->status != INFIX_SUCCESS || !type) {
1687 if (state->status == INFIX_SUCCESS)
1689 return;
1690 }
1691 // This is the key difference from the main printer: we skip the `if (type->name)` check
1692 // and immediately print the underlying structure of the type.
1693 switch (type->category) {
1694 case INFIX_TYPE_STRUCT:
1695 if (type->meta.aggregate_info.is_packed) {
1696 _print(state, "!");
1697 if (type->alignment != 1)
1698 _print(state, "%zu:", type->alignment);
1699 }
1700 _print(state, "{");
1701 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1702 if (i > 0)
1703 _print(state, ",");
1704 const infix_struct_member * member = &type->meta.aggregate_info.members[i];
1705 if (member->name)
1706 _print(state, "%s:", member->name);
1707 // For nested members, we can use the standard printer, which IS allowed
1708 // to use the `@Name` shorthand for brevity.
1710 if (member->bit_width > 0)
1711 _print(state, ":%u", member->bit_width);
1712 }
1713 _print(state, "}");
1714 break;
1715 case INFIX_TYPE_UNION:
1716 _print(state, "<");
1717 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1718 if (i > 0)
1719 _print(state, ",");
1720 const infix_struct_member * member = &type->meta.aggregate_info.members[i];
1721 if (member->name)
1722 _print(state, "%s:", member->name);
1724 if (member->bit_width > 0)
1725 _print(state, ":%u", member->bit_width);
1726 }
1727 _print(state, ">");
1728 break;
1729 // For all other types, we replicate the printing logic from the main printer
1730 // to ensure we print the structure, not a potential top-level alias name.
1731 case INFIX_TYPE_VOID:
1732 _print(state, "void");
1733 break;
1734 case INFIX_TYPE_POINTER:
1735 _print(state, "*");
1736 if (type->meta.pointer_info.pointee_type == type || type->meta.pointer_info.pointee_type == nullptr ||
1738 _print(state, "void");
1739 else
1741 break;
1742 case INFIX_TYPE_ARRAY:
1743 if (type->meta.array_info.is_flexible)
1744 _print(state, "[?:");
1745 else
1746 _print(state, "[%zu:", type->meta.array_info.num_elements);
1748 _print(state, "]");
1749 break;
1750 case INFIX_TYPE_ENUM:
1751 _print(state, "e:");
1753 break;
1754 case INFIX_TYPE_COMPLEX:
1755 _print(state, "c[");
1757 _print(state, "]");
1758 break;
1760 // This block is now a full copy from the main printer.
1761 switch (type->meta.primitive_id) {
1763 _print(state, "bool");
1764 break;
1766 _print(state, "sint8");
1767 break;
1769 _print(state, "uint8");
1770 break;
1772 _print(state, "sint16");
1773 break;
1775 _print(state, "uint16");
1776 break;
1778 _print(state, "sint32");
1779 break;
1781 _print(state, "uint32");
1782 break;
1784 _print(state, "sint64");
1785 break;
1787 _print(state, "uint64");
1788 break;
1790 _print(state, "sint128");
1791 break;
1793 _print(state, "uint128");
1794 break;
1796 _print(state, "float");
1797 break;
1799 _print(state, "double");
1800 break;
1802 _print(state, "longdouble");
1803 break;
1804 }
1805 break;
1806 // We can safely delegate the remaining complex cases to the main printer, as they
1807 // do not have a top-level `name` field themselves.
1810 case INFIX_TYPE_VECTOR:
1812 break;
1813 default:
1815 break;
1816 }
1817}
1823 size_t buffer_size,
1824 const infix_type * type,
1825 infix_print_dialect_t dialect) {
1826 if (!buffer || buffer_size == 0 || !type || dialect != INFIX_DIALECT_SIGNATURE)
1828 printer_state state = {buffer, buffer_size, INFIX_SUCCESS};
1829 *buffer = '\0';
1831 if (state.remaining > 0)
1832 *state.p = '\0';
1833 else
1834 buffer[buffer_size - 1] = '\0';
1835 return state.status;
1836}
1846 size_t buffer_size,
1847 const infix_type * type,
1848 infix_print_dialect_t dialect) {
1850 if (!buffer || buffer_size == 0 || !type) {
1853 }
1854 printer_state state = {buffer, buffer_size, INFIX_SUCCESS};
1855 *buffer = '\0';
1856 if (dialect == INFIX_DIALECT_SIGNATURE)
1858 else if (dialect == INFIX_DIALECT_ITANIUM_MANGLING)
1860 else if (dialect == INFIX_DIALECT_MSVC_MANGLING)
1862 else {
1863 _print(&state, "unsupported_dialect");
1865 }
1866 if (state.status == INFIX_SUCCESS) {
1867 if (state.remaining > 0)
1868 *state.p = '\0'; // Null-terminate if there is space.
1869 else {
1870 // Buffer was exactly full. Ensure null termination at the very end.
1871 buffer[buffer_size - 1] = '\0';
1872 return INFIX_ERROR_INVALID_ARGUMENT; // Indicate truncation.
1873 }
1874 }
1875 else if (buffer_size > 0)
1876 // Ensure null termination even on error (e.g., buffer too small).
1877 buffer[buffer_size - 1] = '\0';
1878 return state.status;
1879}
1893 size_t buffer_size,
1894 const char * function_name,
1895 const infix_type * ret_type,
1897 size_t num_args,
1898 size_t num_fixed_args,
1899 infix_print_dialect_t dialect) {
1901 if (!buffer || buffer_size == 0 || !ret_type || (num_args > 0 && !args)) {
1904 }
1905 printer_state state = {buffer, buffer_size, INFIX_SUCCESS};
1906 *buffer = '\0';
1907 if (dialect == INFIX_DIALECT_SIGNATURE) {
1908 (void)function_name; // Unused
1909 _print(&state, "(");
1910 for (size_t i = 0; i < num_fixed_args; ++i) {
1911 if (i > 0)
1912 _print(&state, ",");
1914 }
1915 if (num_args > num_fixed_args) {
1916 _print(&state, ";");
1917 for (size_t i = num_fixed_args; i < num_args; ++i) {
1918 if (i > num_fixed_args)
1919 _print(&state, ",");
1921 }
1922 }
1923 _print(&state, ")->");
1925 }
1926 else if (dialect == INFIX_DIALECT_ITANIUM_MANGLING) {
1927 // _Z <name_len> <name> <ret_type?> <args...>
1928 // Note: Itanium mangling usually omits return type for standard functions unless it's a template or special
1929 // case. We omit it here for simplicity to match extern "C" -> C++ linking expectations for simple functions.
1930 _print(&state, "_Z");
1931 if (function_name) {
1932 // Check for namespace in function name (e.g., "MyNS::my_func")
1933 const char * p = function_name;
1934 int parts = 0;
1935 while (*p)
1936 if (p[0] == ':' && p[1] == ':') {
1937 parts++;
1938 p += 2;
1939 }
1940 else
1941 p++;
1942 parts++;
1943
1944 if (parts > 1) {
1945 _print(&state, "N");
1946 p = function_name;
1947 while (*p) {
1948 const char * end = strstr(p, "::");
1949 size_t part_len = end ? (size_t)(end - p) : strlen(p);
1950 _print(&state, "%zu", part_len);
1951 for (size_t i = 0; i < part_len; i++)
1952 _print(&state, "%c", p[i]);
1953 if (end)
1954 p = end + 2;
1955 else
1956 break;
1957 }
1958 _print(&state, "E");
1959 }
1960 else {
1961 size_t name_len = strlen(function_name);
1962 _print(&state, "%zu%s", name_len, function_name);
1963 }
1964 }
1965 else
1966 _print(&state, "4func"); // Default name if NULL
1967
1968 if (num_args == 0)
1969 _print(&state, "v"); // void (no args)
1970 else
1971 for (size_t i = 0; i < num_args; ++i)
1973 }
1974 else if (dialect == INFIX_DIALECT_MSVC_MANGLING) {
1975 // MSVC: ?<name>@@YA<ret><args...>@Z
1976 _print(&state, "?");
1977 if (function_name) {
1978 // MSVC namespace handling: reverse order
1979 if (strstr(function_name, "::")) {
1980 const char * parts[MAX_RECURSION_DEPTH];
1981 size_t lens[MAX_RECURSION_DEPTH];
1982 int count = 0;
1983 const char * p = function_name;
1984 while (*p && count < MAX_RECURSION_DEPTH) {
1985 parts[count] = p;
1986 const char * end = strstr(p, "::");
1987 if (end) {
1988 lens[count] = end - p;
1989 p = end + 2;
1990 }
1991 else {
1992 lens[count] = strlen(p);
1993 p += lens[count];
1994 }
1995 count++;
1996 }
1997 // Print in reverse order
1998 for (int i = count - 1; i >= 0; i--) {
1999 for (size_t j = 0; j < lens[i]; j++)
2000 _print(&state, "%c", parts[i][j]);
2001 _print(&state, "@");
2002 }
2003 }
2004 else {
2005 _print(&state, "%s@", function_name);
2006 }
2007 }
2008 else {
2009 _print(&state, "func@");
2010 }
2011 _print(&state, "@YA"); // __cdecl (default)
2013
2014 if (num_args == 0)
2015 _print(&state, "X"); // void argument list
2016 else
2017 for (size_t i = 0; i < num_args; ++i)
2018 _infix_type_print_msvc_recursive(&state, args[i].type);
2019 _print(&state, "@Z");
2020 }
2021 else {
2022 _print(&state, "unsupported_dialect");
2024 }
2025 if (state.status == INFIX_SUCCESS) {
2026 if (state.remaining > 0)
2027 *state.p = '\0';
2028 else {
2029 if (buffer_size > 0)
2030 buffer[buffer_size - 1] = '\0';
2031 return INFIX_ERROR_INVALID_ARGUMENT; // Indicate truncation.
2032 }
2033 }
2034 else if (buffer_size > 0)
2035 buffer[buffer_size - 1] = '\0';
2036 return state.status;
2037}
2052c23_nodiscard infix_status infix_registry_print(char * buffer, size_t buffer_size, const infix_registry_t * registry) {
2053 if (!buffer || buffer_size == 0 || !registry)
2055 printer_state state = {buffer, buffer_size, INFIX_SUCCESS};
2056 *state.p = '\0';
2057 // Iterate through all buckets and their chains.
2058 for (size_t i = 0; i < registry->num_buckets; ++i) {
2059 for (const _infix_registry_entry_t * entry = registry->buckets[i]; entry != nullptr; entry = entry->next) {
2060 // Only print fully defined types, not forward declarations.
2061 if (entry->type && !entry->is_forward_declaration) {
2062 char type_body_buffer[1024];
2064 type_body_buffer, sizeof(type_body_buffer), entry->type, INFIX_DIALECT_SIGNATURE) !=
2065 INFIX_SUCCESS) {
2067 goto end_print_loop;
2068 }
2069 _print(&state, "@%s = %s;\n", entry->name, type_body_buffer);
2070 }
2071 else if (entry->is_forward_declaration) // Explicitly print forward declarations
2072 _print(&state, "@%s;\n", entry->name);
2073 if (state.status != INFIX_SUCCESS)
2074 goto end_print_loop;
2075 }
2076 }
2077end_print_loop:;
2078 return state.status;
2079}
infix_registry_t * registry
Definition 008_registry_introspection.c:33
infix_status status
Definition 103_unions.c:61
infix_struct_member * members
Definition 103_unions.c:55
void * args[]
Definition 202_in_structs.c:59
clock_t start
Definition 901_call_overhead.c:48
infix_type * ret_type
Definition 901_call_overhead.c:61
clock_t end
Definition 901_call_overhead.c:48
#define c23_nodiscard
Internal alias for the public INFIX_NODISCARD macro.
Definition compat_c23.h:91
#define INFIX_TLS
Definition error.c:68
INFIX_API infix_error_details_t infix_get_last_error(void)
Retrieves detailed information about the last error that occurred on the current thread.
Definition error.c:279
infix_error_code_t
Enumerates specific error codes.
Definition infix.h:1342
@ INFIX_CODE_SUCCESS
Definition infix.h:1344
@ INFIX_CODE_INVALID_MEMBER_TYPE
Definition infix.h:1369
@ INFIX_CODE_INTEGER_OVERFLOW
Definition infix.h:1360
@ INFIX_CODE_TYPE_TOO_LARGE
Definition infix.h:1367
@ INFIX_CODE_EMPTY_SIGNATURE
Definition infix.h:1363
@ INFIX_CODE_UNEXPECTED_TOKEN
Definition infix.h:1356
@ INFIX_CODE_MISSING_RETURN_TYPE
Definition infix.h:1359
@ INFIX_CODE_RECURSION_DEPTH_EXCEEDED
Definition infix.h:1361
@ INFIX_CODE_UNTERMINATED_AGGREGATE
Definition infix.h:1357
@ INFIX_CODE_NULL_POINTER
Definition infix.h:1346
@ INFIX_CODE_INVALID_KEYWORD
Definition infix.h:1358
@ INFIX_CODE_OUT_OF_MEMORY
Definition infix.h:1350
@ INFIX_CATEGORY_ALLOCATION
Definition infix.h:1335
@ INFIX_CATEGORY_GENERAL
Definition infix.h:1334
@ INFIX_CATEGORY_PARSER
Definition infix.h:1336
size_t source_offset
Definition infix.h:281
struct infix_type_t::@0::@1 pointer_info
Metadata for INFIX_TYPE_POINTER.
union infix_type_t::@0 meta
A union containing metadata specific to the type's category.
bool is_packed
Definition infix.h:294
struct infix_type_t::@0::@7 vector_info
Metadata for INFIX_TYPE_VECTOR.
infix_type * type
Definition infix.h:347
c23_nodiscard infix_status infix_signature_parse(const char *signature, infix_arena_t **out_arena, infix_type **out_ret_type, infix_function_argument **out_args, size_t *out_num_args, size_t *out_num_fixed_args, infix_registry_t *registry)
Parses a full function signature string into its constituent parts.
Definition signature.c:1087
struct infix_type_t::@0::@4 func_ptr_info
Metadata for INFIX_TYPE_REVERSE_TRAMPOLINE.
size_t num_elements
Definition infix.h:299
size_t size
Definition infix.h:276
size_t alignment
Definition infix.h:277
infix_struct_member * members
Definition infix.h:292
struct infix_type_t::@0::@6 complex_info
Metadata for INFIX_TYPE_COMPLEX.
c23_nodiscard infix_status infix_type_from_signature(infix_type **out_type, infix_arena_t **out_arena, const char *signature, infix_registry_t *registry)
Parses a signature string representing a single data type.
Definition signature.c:1028
const char * name
Definition infix.h:274
infix_function_argument * args
Definition infix.h:305
infix_status
Enumerates the possible status codes returned by infix API functions.
Definition infix.h:433
const char * name
Definition infix.h:346
const char * name
Definition infix.h:334
infix_type_category category
Definition infix.h:275
struct infix_type_t::@0::@2 aggregate_info
Metadata for INFIX_TYPE_STRUCT and INFIX_TYPE_UNION.
struct infix_type_t::@0::@3 array_info
Metadata for INFIX_TYPE_ARRAY.
struct infix_type_t * pointee_type
Definition infix.h:288
infix_type * type
Definition infix.h:335
struct infix_type_t * element_type
Definition infix.h:298
bool is_flexible
Definition infix.h:300
struct infix_type_t * return_type
Definition infix.h:304
struct infix_type_t::@0::@5 enum_info
Metadata for INFIX_TYPE_ENUM.
struct infix_type_t * base_type
Definition infix.h:315
uint8_t bit_width
Definition infix.h:337
size_t num_members
Definition infix.h:293
struct infix_type_t * underlying_type
Definition infix.h:311
struct infix_type_t::@0::@8 named_reference
Metadata for INFIX_TYPE_NAMED_REFERENCE.
size_t num_fixed_args
Definition infix.h:307
infix_primitive_type_id primitive_id
Metadata for INFIX_TYPE_PRIMITIVE.
Definition infix.h:285
size_t num_args
Definition infix.h:306
bool is_arena_allocated
Definition infix.h:278
@ INFIX_ERROR_ALLOCATION_FAILED
Definition infix.h:435
@ INFIX_SUCCESS
Definition infix.h:434
@ INFIX_ERROR_INVALID_ARGUMENT
Definition infix.h:436
INFIX_API c23_nodiscard infix_status infix_function_print(char *buffer, size_t buffer_size, const char *function_name, const infix_type *ret_type, const infix_function_argument *args, size_t num_args, size_t num_fixed_args, infix_print_dialect_t dialect)
Serializes a function signature's components into a string.
Definition signature.c:1892
INFIX_API c23_nodiscard infix_status infix_type_print(char *buffer, size_t buffer_size, const infix_type *type, infix_print_dialect_t dialect)
Serializes an infix_type object graph back into a signature string.
Definition signature.c:1845
infix_print_dialect_t
Specifies the output format for printing types and function signatures.
Definition infix.h:1157
@ INFIX_DIALECT_SIGNATURE
Definition infix.h:1158
@ INFIX_DIALECT_ITANIUM_MANGLING
Definition infix.h:1159
@ INFIX_DIALECT_MSVC_MANGLING
Definition infix.h:1160
#define infix_memcpy
A macro that can be defined to override the default memcpy function.
Definition infix.h:386
INFIX_API INFIX_NODISCARD infix_arena_t * infix_arena_create(size_t)
Creates a new memory arena.
Definition arena.c:52
INFIX_API INFIX_NODISCARD void * infix_arena_calloc(infix_arena_t *, size_t, size_t, size_t)
Allocates and zero-initializes a block of memory from an arena.
Definition arena.c:188
INFIX_API void infix_arena_destroy(infix_arena_t *)
Destroys an arena and frees all memory allocated from it.
Definition arena.c:83
c23_nodiscard infix_status infix_registry_print(char *buffer, size_t buffer_size, const infix_registry_t *registry)
Serializes all defined types within a registry into a single, human-readable string.
Definition signature.c:2052
INFIX_API INFIX_NODISCARD infix_status infix_type_create_pointer_to(infix_arena_t *, infix_type **, infix_type *)
Creates a new pointer type that points to a specific type.
Definition types.c:431
INFIX_API infix_struct_member infix_type_create_member(const char *, infix_type *, size_t)
A factory function to create an infix_struct_member.
Definition types.c:197
INFIX_API INFIX_NODISCARD infix_status infix_type_create_packed_struct(infix_arena_t *, infix_type **, size_t, size_t, infix_struct_member *, size_t)
Creates a new packed struct type with a user-specified layout.
Definition types.c:750
INFIX_API INFIX_NODISCARD infix_status infix_type_create_complex(infix_arena_t *, infix_type **, infix_type *)
Creates a new _Complex number type.
Definition types.c:576
INFIX_API INFIX_NODISCARD infix_type * infix_type_create_void(void)
Creates a static descriptor for the void type.
Definition types.c:189
INFIX_API INFIX_NODISCARD infix_status infix_type_create_union(infix_arena_t *, infix_type **, infix_struct_member *, size_t)
Creates a new union type from an array of members.
Definition types.c:644
INFIX_API infix_struct_member infix_type_create_bitfield_member(const char *, infix_type *, size_t, uint8_t)
A factory function to create a bitfield infix_struct_member.
Definition types.c:208
INFIX_API INFIX_NODISCARD infix_status infix_type_create_named_reference(infix_arena_t *, infix_type **, const char *, infix_aggregate_category_t)
Creates a placeholder for a named type to be resolved by a registry.
Definition types.c:807
INFIX_API INFIX_NODISCARD infix_type * infix_type_create_primitive(infix_primitive_type_id)
Creates a static descriptor for a primitive C type.
Definition types.c:135
INFIX_API INFIX_NODISCARD infix_status infix_type_create_vector(infix_arena_t *, infix_type **, infix_type *, size_t)
Creates a new SIMD vector type.
Definition types.c:606
INFIX_API INFIX_NODISCARD infix_status infix_type_create_struct(infix_arena_t *, infix_type **, infix_struct_member *, size_t)
Creates a new struct type from an array of members, calculating layout automatically.
Definition types.c:691
INFIX_API INFIX_NODISCARD infix_status infix_type_create_enum(infix_arena_t *, infix_type **, infix_type *)
Creates a new enum type with a specified underlying integer type.
Definition types.c:542
INFIX_API infix_status infix_type_create_flexible_array(infix_arena_t *, infix_type **, infix_type *)
Creates a flexible array member type ([?:type]).
Definition types.c:499
INFIX_API INFIX_NODISCARD infix_status infix_type_create_array(infix_arena_t *, infix_type **, infix_type *, size_t)
Creates a new fixed-size array type.
Definition types.c:461
@ INFIX_PRIMITIVE_UINT16
Definition infix.h:247
@ INFIX_PRIMITIVE_UINT32
Definition infix.h:249
@ INFIX_PRIMITIVE_LONG_DOUBLE
Definition infix.h:257
@ INFIX_PRIMITIVE_FLOAT
Definition infix.h:255
@ INFIX_PRIMITIVE_DOUBLE
Definition infix.h:256
@ INFIX_PRIMITIVE_SINT16
Definition infix.h:248
@ INFIX_PRIMITIVE_SINT64
Definition infix.h:252
@ INFIX_PRIMITIVE_SINT32
Definition infix.h:250
@ INFIX_PRIMITIVE_UINT8
Definition infix.h:245
@ INFIX_PRIMITIVE_UINT128
Definition infix.h:253
@ INFIX_PRIMITIVE_BOOL
Definition infix.h:244
@ INFIX_PRIMITIVE_UINT64
Definition infix.h:251
@ INFIX_PRIMITIVE_SINT128
Definition infix.h:254
@ INFIX_PRIMITIVE_SINT8
Definition infix.h:246
@ INFIX_TYPE_UNION
Definition infix.h:231
@ INFIX_TYPE_PRIMITIVE
Definition infix.h:228
@ INFIX_TYPE_COMPLEX
Definition infix.h:235
@ INFIX_TYPE_ARRAY
Definition infix.h:232
@ INFIX_TYPE_VECTOR
Definition infix.h:236
@ INFIX_TYPE_VOID
Definition infix.h:238
@ INFIX_TYPE_POINTER
Definition infix.h:229
@ INFIX_TYPE_NAMED_REFERENCE
Definition infix.h:237
@ INFIX_TYPE_REVERSE_TRAMPOLINE
Definition infix.h:233
@ INFIX_TYPE_ENUM
Definition infix.h:234
@ INFIX_TYPE_STRUCT
Definition infix.h:230
@ INFIX_AGGREGATE_STRUCT
Definition infix.h:263
#define INFIX_API
Symbol visibility macro.
Definition infix.h:114
Internal data structures, function prototypes, and constants.
INFIX_INTERNAL c23_nodiscard infix_status _infix_resolve_type_graph_inplace(infix_type **type_ptr, infix_registry_t *registry)
Resolves all named type references in a type graph in-place.
Definition type_registry.c:429
static bool is_double(const infix_type *type)
A fast inline check to determine if an infix_type is a double.
Definition infix_internals.h:728
INFIX_INTERNAL infix_type * _copy_type_graph_to_arena(infix_arena_t *, const infix_type *)
Performs a deep copy of a type graph into a destination arena.
Definition types.c:1141
INFIX_INTERNAL void _infix_type_recalculate_layout(infix_type *type)
Recalculates the layout of a fully resolved type graph.
Definition types.c:953
INFIX_INTERNAL void _infix_clear_error(void)
Clears the thread-local error state.
Definition error.c:266
static bool is_float(const infix_type *type)
A fast inline check to determine if an infix_type is a float.
Definition infix_internals.h:720
INFIX_INTERNAL void _infix_set_error(infix_error_category_t category, infix_error_code_t code, size_t position)
Sets the thread-local error state with detailed information.
Definition error.c:173
static void _print(printer_state *state, const char *fmt,...)
Definition signature.c:1179
static infix_struct_member * parse_aggregate_members(parser_state *state, char end_char, size_t *out_num_members)
Definition signature.c:254
static bool is_function_signature_ahead(const parser_state *state)
Definition signature.c:215
static void _infix_type_print_body_only_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1685
c23_nodiscard infix_status _infix_type_print_body_only(char *buffer, size_t buffer_size, const infix_type *type, infix_print_dialect_t dialect)
An internal-only function to serialize a type's body without its registered name.
Definition signature.c:1822
#define MAX_RECURSION_DEPTH
Definition signature.c:47
static bool parse_size_t(parser_state *state, size_t *out_val)
Definition signature.c:99
c23_nodiscard infix_status _infix_parse_type_internal(infix_type **out_type, infix_arena_t **out_arena, const char *signature)
The internal core of the signature parser.
Definition signature.c:978
static void set_parser_error(parser_state *state, infix_error_code_t code)
Definition signature.c:73
static infix_type * parse_primitive(parser_state *state)
Definition signature.c:467
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)
Definition signature.c:820
static bool consume_keyword(parser_state *state, const char *keyword)
Definition signature.c:163
static infix_type * parse_type(parser_state *state)
Definition signature.c:594
static infix_type * parse_packed_struct(parser_state *state)
Definition signature.c:411
static infix_type * parse_aggregate(parser_state *state, char start_char, char end_char)
Definition signature.c:370
static const char * parse_identifier(parser_state *state)
Definition signature.c:129
static void _infix_type_print_itanium_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1405
static void _infix_type_print_signature_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1211
static const char * parse_optional_name_prefix(parser_state *state)
Definition signature.c:185
INFIX_TLS const char * g_infix_last_signature_context
A thread-local pointer to the full signature string being parsed.
Definition error.c:99
static void skip_whitespace(parser_state *state)
Definition signature.c:81
static void _infix_type_print_msvc_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1532
A single entry in the registry's hash table.
Definition infix_internals.h:152
Internal definition of a memory arena.
Definition infix_internals.h:138
Describes a single argument to a C function.
Definition infix.h:345
Internal definition of a named type registry.
Definition infix_internals.h:165
_infix_registry_entry_t ** buckets
Definition infix_internals.h:170
size_t num_buckets
Definition infix_internals.h:168
Describes a single member of a C struct or union.
Definition infix.h:333
A semi-opaque structure that describes a C type.
Definition infix.h:273
Definition signature.c:53
const char * start
Definition signature.c:55
infix_arena_t * arena
Definition signature.c:56
int depth
Definition signature.c:57
const char * p
Definition signature.c:54
Definition signature.c:1166
char * p
Definition signature.c:1167
size_t remaining
Definition signature.c:1168
infix_status status
Definition signature.c:1169