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
49#define MAX_PRINT_RECURSION_DEPTH 128
51 infix_type ** out_ret_type,
52 infix_function_argument ** out_args,
53 size_t * out_num_args,
54 size_t * out_num_fixed_args);
55// Parser Helper Functions
66
73 while (true) {
74 while (isspace((unsigned char)*state->p))
75 state->p++;
76 if (*state->p == '#') // C-style line comments
77 while (*state->p != '\n' && *state->p != '\0')
78 state->p++;
79 else
80 break;
81 }
82}
90static bool parse_size_t(parser_state * state, size_t * out_val) {
91 const char * start = state->p;
92 char * end;
93 errno = 0; // Reset errno before call
94 unsigned long long val = strtoull(start, &end, 10);
95
96 // Check for no conversion (end==start) OR overflow (ERANGE)
97 if (end == start || errno == ERANGE) {
98 // Use INTEGER_OVERFLOW code for range errors
100 return false;
101 }
102
103 // Check for truncation if size_t is smaller than unsigned long long (e.g. 32-bit builds)
104 if (val > SIZE_MAX) {
106 return false;
107 }
108 // ULLONG_MAX is returned by strtoull both for the literal 18446744073709551615 and
109 // for values that overflowed the range, so it can never be treated as a valid size.
110 // Accepting it would let `[18446744073709551615:uint8]` pass through and overflow
111 // downstream size arithmetic (e.g. (SIZE_MAX + 15) & ~15 wrapping to 0).
112 if (val == (unsigned long long)SIZE_MAX) {
114 return false;
115 }
116 *out_val = (size_t)val;
117 state->p = end;
118 return true;
119}
128static const char * parse_identifier(parser_state * state) {
129 skip_whitespace(state);
130 const char * start = state->p;
131 if (!isalpha((unsigned char)*start) && *start != '_')
132 return nullptr;
133 while (isalnum((unsigned char)*state->p) || *state->p == '_' || *state->p == ':') {
134 if (*state->p == ':' && state->p[1] != ':')
135 break; // A single ':' is not part of an identifier.
136 if (*state->p == ':')
137 state->p++; // Consume first ':' of '::'
138 state->p++;
139 }
140 size_t len = state->p - start;
141 if (len == 0)
142 return nullptr;
143 char * name = infix_arena_calloc(state->arena, 1, len + 1, 1);
144 if (!name) {
146 return nullptr;
147 }
148 infix_memcpy((void *)name, start, len);
149 name[len] = '\0';
150 return name;
151}
162static bool consume_keyword(parser_state * state, const char * keyword) {
163 skip_whitespace(state);
164 size_t len = strlen(keyword);
165 if (strncmp(state->p, keyword, len) == 0) {
166 // Ensure it's not a prefix of a longer word (e.g., "int" vs "integer").
167 if (isalnum((unsigned char)state->p[len]) || state->p[len] == '_')
168 return false;
169 state->p += len;
170 skip_whitespace(state);
171 return true;
172 }
173 return false;
174}
184static const char * parse_optional_name_prefix(parser_state * state) {
185 skip_whitespace(state);
186 // Save the current position in case we need to backtrack.
187 const char * p_before = state->p;
188 const char * name = parse_identifier(state);
189 if (name) {
190 skip_whitespace(state);
191 if (*state->p == ':') { // Found "identifier:", so consume the colon and return the name.
192 state->p++;
193 return name;
194 }
195 }
196 // If it wasn't a `name:`, backtrack to the original position.
197 state->p = p_before;
198 return nullptr;
199}
214static bool is_function_signature_ahead(const parser_state * state) {
215 const char * p = state->p;
216 if (*p != '(')
217 return false;
218 p++;
219 // Find the matching ')' by tracking nesting depth.
220 int depth = 1;
221 while (*p != '\0' && depth > 0) {
222 if (*p == '(')
223 depth++;
224 else if (*p == ')')
225 depth--;
226 p++;
227 }
228 if (depth != 0)
229 return false; // Mismatched parentheses.
230 // Skip any whitespace or comments after the ')'
231 while (isspace((unsigned char)*p) || *p == '#') {
232 if (*p == '#')
233 while (*p != '\n' && *p != '\0')
234 p++;
235 else
236 p++;
237 }
238 // Check for the '->' arrow.
239 return (p[0] == '-' && p[1] == '>');
240}
241// Aggregate Parsing Logic
253static infix_struct_member * parse_aggregate_members(parser_state * state, char end_char, size_t * out_num_members) {
254 // Use a temporary linked list to collect members, as the count is unknown in a single pass.
255 typedef struct member_node {
257 struct member_node * next;
258 } member_node;
259 member_node *head = nullptr, *tail = nullptr;
260 size_t num_members = 0;
261 skip_whitespace(state);
262 if (*state->p != end_char) {
263 while (1) {
264 const char * p_before_member = state->p;
265 const char * name = parse_optional_name_prefix(state);
266 // Disallow an empty member definition like `name,` without a type.
267 if (name && (*state->p == ',' || *state->p == end_char)) {
268 state->p = p_before_member + strlen(name); // Position error at end of name
270 return nullptr;
271 }
272 infix_type * member_type = parse_type(state);
273 if (!member_type)
274 return nullptr;
275 // Structs and unions cannot have `void` members.
276 if (member_type->category == INFIX_TYPE_VOID) {
278 return nullptr;
279 }
280
281 // Check for bitfield syntax: "name: type : width"
282 uint8_t bit_width = 0;
283 bool is_bitfield = false;
284 const char * p_before_colon = state->p;
285 skip_whitespace(state);
286 if (*state->p == ':') {
287 state->p++; // Consume ':'
288 skip_whitespace(state);
289 if (!isdigit((unsigned char)*state->p)) {
290 // Not a bitfield width, backtrack. This handles "name: type" where ':' is part of name prefix.
291 state->p = p_before_colon;
292 }
293 else {
294 size_t width_val = 0;
295 if (!parse_size_t(state, &width_val))
296 return nullptr; // Error set by parse_size_t
297 size_t type_bits = member_type->size * 8;
298 // Unresolved named reference (size 0) or a huge base type: cap at the
299 // uint8_t storage limit so the width is never truncated.
300 if (type_bits == 0 || type_bits > 255)
301 type_bits = 255;
302 if (width_val > type_bits) {
304 return nullptr;
305 }
306 bit_width = (uint8_t)width_val;
307 is_bitfield = true;
308 }
309 }
310
311 member_node * node = infix_arena_calloc(state->arena, 1, sizeof(member_node), _Alignof(member_node));
312 if (!node) {
314 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
315 return nullptr;
316 }
317 // The member offset is not calculated here; it will be done later
318 // by `infix_type_create_struct` or `_infix_type_recalculate_layout`.
319 if (is_bitfield)
320 node->m = infix_type_create_bitfield_member(name, member_type, 0, bit_width);
321 else
322 node->m = infix_type_create_member(name, member_type, 0);
323
324 node->next = nullptr;
325
326 if (!head)
327 head = tail = node;
328 else {
329 tail->next = node;
330 tail = node;
331 }
332 num_members++;
333 // Check for next token: ',' or end_char
334 skip_whitespace(state);
335 if (*state->p == ',') {
336 state->p++; // Consume comma.
337 skip_whitespace(state);
338 // A trailing comma like `{int,}` is a syntax error.
339 if (*state->p == end_char) {
341 return nullptr;
342 }
343 }
344 else if (*state->p == end_char)
345 break;
346 else { // Unexpected token (e.g., missing comma).
347 if (*state->p == '\0') {
349 return nullptr;
350 }
352 return nullptr;
353 }
354 }
355 }
356 *out_num_members = num_members;
357 if (num_members == 0)
358 return nullptr;
359 // Convert the temporary linked list to a flat array in the arena.
361 infix_arena_calloc(state->arena, num_members, sizeof(infix_struct_member), _Alignof(infix_struct_member));
362 if (!members) {
364 return nullptr;
365 }
366 member_node * current = head;
367 for (size_t i = 0; i < num_members; i++) {
368 members[i] = current->m;
369 current = current->next;
370 }
371 return members;
372}
381static infix_type * parse_aggregate(parser_state * state, char start_char, char end_char) {
382 if (state->depth >= MAX_RECURSION_DEPTH) {
384 return nullptr;
385 }
386 state->depth++;
387 if (*state->p != start_char) {
389 state->depth--;
390 return nullptr;
391 }
392 state->p++;
393 size_t num_members = 0;
394 infix_struct_member * members = parse_aggregate_members(state, end_char, &num_members);
395 // If member parsing failed, an error is already set. Propagate the failure.
397 state->depth--;
398 return nullptr;
399 }
400 if (*state->p != end_char) {
402 state->depth--;
403 return nullptr;
404 }
405 state->p++;
406 infix_type * agg_type = nullptr;
407 infix_status status = (start_char == '{') ? infix_type_create_struct(state->arena, &agg_type, members, num_members)
408 : infix_type_create_union(state->arena, &agg_type, members, num_members);
409 if (status != INFIX_SUCCESS) {
410 state->depth--;
411 return nullptr;
412 }
413 state->depth--;
414 return agg_type;
415}
423 size_t alignment = 1; // Default alignment for `!{...}` is 1.
424 if (*state->p == '!') {
425 state->p++;
426 if (isdigit((unsigned char)*state->p)) {
427 // This is the `!N:{...}` form with an explicit alignment.
428 if (!parse_size_t(state, &alignment))
429 return nullptr;
430 if (*state->p != ':') {
432 return nullptr;
433 }
434 state->p++;
435 }
436 }
437 skip_whitespace(state);
438 if (*state->p != '{') {
440 return nullptr;
441 }
442 state->p++;
443 size_t num_members = 0;
444 infix_struct_member * members = parse_aggregate_members(state, '}', &num_members);
446 return nullptr;
447 if (*state->p != '}') {
449 return nullptr;
450 }
451 state->p++;
452 infix_type * packed_type = nullptr;
453 // For packed structs, the total size is simply the sum of member sizes without padding.
454 // The user of `infix_type_create_packed_struct` must provide pre-calculated offsets.
455 // Since our parser doesn't know the offsets, we pass a preliminary size. The final
456 // layout pass will fix this if needed, but for packed structs, the user's offsets
457 // are king.
458 size_t total_size = 0;
459 for (size_t i = 0; i < num_members; ++i)
460 total_size += members[i].type->size;
462 infix_type_create_packed_struct(state->arena, &packed_type, total_size, alignment, members, num_members);
463 if (status != INFIX_SUCCESS)
464 return nullptr;
465 return packed_type;
466}
475 infix_type * t = infix_arena_calloc(state->arena, 1, sizeof(infix_type), _Alignof(infix_type));
476 if (!t)
477 return nullptr;
478
480 t->is_arena_allocated = true;
481 t->arena = state->arena;
482
483 size_t len = strlen(name) + 1;
484 char * arena_name = infix_arena_alloc(state->arena, len, 1);
485 if (arena_name) {
486 infix_memcpy(arena_name, name, len);
487 t->name = arena_name;
488 }
489 return t;
490}
491// Main Parser Logic
503 if (consume_keyword(state, "sint8") || consume_keyword(state, "int8"))
505 if (consume_keyword(state, "uint8"))
507 if (consume_keyword(state, "sint16") || consume_keyword(state, "int16"))
509 if (consume_keyword(state, "uint16"))
511 if (consume_keyword(state, "sint32") || consume_keyword(state, "int32"))
513 if (consume_keyword(state, "uint32"))
515 if (consume_keyword(state, "sint64") || consume_keyword(state, "int64"))
517 if (consume_keyword(state, "uint64"))
519 if (consume_keyword(state, "sint128") || consume_keyword(state, "int128"))
521 if (consume_keyword(state, "uint128"))
523 if (consume_keyword(state, "float16"))
525 if (consume_keyword(state, "float32"))
527 if (consume_keyword(state, "float64"))
529 if (consume_keyword(state, "bool"))
531 if (consume_keyword(state, "void"))
532 return infix_type_create_void();
533 // C-style convenience aliases
534 if (consume_keyword(state, "uchar"))
536 if (consume_keyword(state, "char"))
538 if (consume_keyword(state, "ushort"))
540 if (consume_keyword(state, "short"))
542 if (consume_keyword(state, "uint"))
544 if (consume_keyword(state, "int"))
546 if (consume_keyword(state, "ulonglong"))
548 if (consume_keyword(state, "longlong"))
550 // `long` is platform-dependent, so we use `sizeof` to pick the correct size.
551 if (consume_keyword(state, "ulong"))
552 return infix_type_create_primitive(sizeof(unsigned long) == 8 ? INFIX_PRIMITIVE_UINT64
554 if (consume_keyword(state, "long"))
556 if (consume_keyword(state, "double"))
558 if (consume_keyword(state, "float"))
560 if (consume_keyword(state, "longdouble"))
562 if (consume_keyword(state, "size_t"))
564 if (consume_keyword(state, "ssize_t"))
566 // Explicit Character Types
567 if (consume_keyword(state, "char8_t"))
568 return _create_named_primitive(state, INFIX_PRIMITIVE_UINT8, "char8_t");
569 if (consume_keyword(state, "char16_t"))
570 return _create_named_primitive(state, INFIX_PRIMITIVE_UINT16, "char16_t");
571 if (consume_keyword(state, "char32_t"))
572 return _create_named_primitive(state, INFIX_PRIMITIVE_UINT32, "char32_t");
573 if (consume_keyword(state, "wchar_t"))
575 state, sizeof(wchar_t) == 2 ? INFIX_PRIMITIVE_UINT16 : INFIX_PRIMITIVE_SINT32, "wchar_t");
576 // AVX convenience aliases
577 if (consume_keyword(state, "m256d")) {
578 infix_type * type = nullptr;
581 if (status != INFIX_SUCCESS)
582 return nullptr; // Propagate failure
583 type->alignment = 32; // YMM registers require 32-byte alignment
584 return type;
585 }
586 if (consume_keyword(state, "m256")) {
587 infix_type * type = nullptr;
590 if (status != INFIX_SUCCESS)
591 return nullptr; // Propagate failure
592 type->alignment = 32; // YMM registers require 32-byte alignment
593 return type;
594 }
595 if (consume_keyword(state, "m512d")) {
596 infix_type * type = nullptr;
599 if (status != INFIX_SUCCESS)
600 return nullptr;
601 type->alignment = 64; // ZMM registers have 64-byte alignment
602 return type;
603 }
604 if (consume_keyword(state, "m512")) {
605 infix_type * type = nullptr;
608 if (status != INFIX_SUCCESS)
609 return nullptr;
610 type->alignment = 64;
611 return type;
612 }
613 if (consume_keyword(state, "m512i")) {
614 infix_type * type = nullptr;
617 if (status != INFIX_SUCCESS)
618 return nullptr;
619 type->alignment = 64;
620 return type;
621 }
622 return nullptr;
623}
635 if (state->depth >= MAX_RECURSION_DEPTH) {
637 return nullptr;
638 }
639 state->depth++;
640 skip_whitespace(state);
641 // Capture the offset from the start of the signature string *before* parsing the type.
642 size_t current_offset = state->p - state->start;
643 infix_type * result_type = nullptr;
644 const char * p_before_type = state->p;
645 if (*state->p == '@') { // Named type reference: `@MyStruct`
646 state->p++;
647 const char * name = parse_identifier(state);
648 if (!name) {
650 state->depth--;
651 return nullptr;
652 }
653 if (infix_type_create_named_reference(state->arena, &result_type, name, INFIX_AGGREGATE_STRUCT) !=
655 result_type = nullptr;
656 }
657 else if (*state->p == '*') { // Pointer type: `*int`
658 state->p++;
659 skip_whitespace(state);
660 infix_type * pointee_type = parse_type(state);
661 if (!pointee_type) {
662 state->depth--;
663 return nullptr;
664 }
665 if (infix_type_create_pointer_to(state->arena, &result_type, pointee_type) != INFIX_SUCCESS)
666 result_type = nullptr;
667 }
668 else if (*state->p == '(') { // Grouped type `(type)` or function pointer `(...) -> type`
669 if (is_function_signature_ahead(state)) {
670 infix_type * ret_type = nullptr;
671 infix_function_argument * args = nullptr;
672 size_t num_args = 0, num_fixed = 0;
673 if (parse_function_signature_details(state, &ret_type, &args, &num_args, &num_fixed) != INFIX_SUCCESS) {
674 state->depth--;
675 return nullptr;
676 }
677 // Manually construct a function pointer type object.
678 // This is represented internally as a pointer-like type with extra metadata.
679 infix_type * func_type = infix_arena_calloc(state->arena, 1, sizeof(infix_type), _Alignof(infix_type));
680 if (!func_type) {
682 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
683 state->depth--;
684 return nullptr;
685 }
686 func_type->size = sizeof(void *);
687 func_type->alignment = _Alignof(void *);
688 func_type->is_arena_allocated = true;
689 func_type->category = INFIX_TYPE_REVERSE_TRAMPOLINE; // Special category for function types.
691 func_type->meta.func_ptr_info.args = args;
692 func_type->meta.func_ptr_info.num_args = num_args;
693 func_type->meta.func_ptr_info.num_fixed_args = num_fixed;
694 result_type = func_type;
695 }
696 else { // Grouped type: `(type)`
697 state->p++;
698 skip_whitespace(state);
699 result_type = parse_type(state);
700 if (!result_type) {
701 state->depth--;
702 return nullptr;
703 }
704 skip_whitespace(state);
705 if (*state->p != ')') {
707 result_type = nullptr;
708 }
709 else
710 state->p++;
711 }
712 }
713 else if (*state->p == '[') { // Array type: `[size:type]`
714 state->p++;
715 skip_whitespace(state);
716 size_t num_elements = 0;
717 bool is_flexible = false;
718
719 if (*state->p == '?') {
720 // Flexible array member: `[?:type]`
721 is_flexible = true;
722 state->p++;
723 }
724 else if (!parse_size_t(state, &num_elements)) {
725 state->depth--;
726 return nullptr;
727 }
728
729 skip_whitespace(state);
730 if (*state->p != ':') {
732 state->depth--;
733 return nullptr;
734 }
735 state->p++;
736 skip_whitespace(state);
737 infix_type * element_type = parse_type(state);
738 if (!element_type) {
739 state->depth--;
740 return nullptr;
741 }
742 if (element_type->category == INFIX_TYPE_VOID) { // An array of `void` is illegal in C.
744 state->depth--;
745 return nullptr;
746 }
747 skip_whitespace(state);
748 if (*state->p != ']') {
750 state->depth--;
751 return nullptr;
752 }
753 state->p++;
754
755 if (is_flexible) {
756 if (infix_type_create_flexible_array(state->arena, &result_type, element_type) != INFIX_SUCCESS)
757 result_type = nullptr;
758 }
759 else {
760 if (infix_type_create_array(state->arena, &result_type, element_type, num_elements) != INFIX_SUCCESS)
761 result_type = nullptr;
762 }
763 }
764 else if (*state->p == '!') // Packed struct
765 result_type = parse_packed_struct(state);
766 else if (*state->p == '{') // Struct
767 result_type = parse_aggregate(state, '{', '}');
768 else if (*state->p == '<') // Union
769 result_type = parse_aggregate(state, '<', '>');
770 else if (*state->p == 'e' && state->p[1] == ':') { // Enum: `e:type`
771 state->p += 2;
772 skip_whitespace(state);
773 infix_type * underlying_type = parse_type(state);
774 if (!underlying_type || underlying_type->category != INFIX_TYPE_PRIMITIVE) {
776 state->depth--;
777 return nullptr;
778 }
779 if (infix_type_create_enum(state->arena, &result_type, underlying_type) != INFIX_SUCCESS)
780 result_type = nullptr;
781 }
782 else if (*state->p == 'c' && state->p[1] == '[') { // Complex: `c[type]`
783 state->p += 2;
784 skip_whitespace(state);
785 infix_type * base_type = parse_type(state);
786 if (!base_type) {
787 state->depth--;
788 return nullptr;
789 }
790 skip_whitespace(state);
791 if (*state->p != ']') {
793 state->depth--;
794 return nullptr;
795 }
796 state->p++;
797 if (infix_type_create_complex(state->arena, &result_type, base_type) != INFIX_SUCCESS)
798 result_type = nullptr;
799 }
800 else if (*state->p == 'v' && state->p[1] == '[') { // Vector: `v[size:type]`
801 state->p += 2;
802 skip_whitespace(state);
803 size_t num_elements;
804 if (!parse_size_t(state, &num_elements)) {
805 state->depth--;
806 return nullptr;
807 }
808 if (*state->p != ':') {
810 state->depth--;
811 return nullptr;
812 }
813 state->p++;
814 infix_type * element_type = parse_type(state);
815 if (!element_type) {
816 state->depth--;
817 return nullptr;
818 }
819 if (*state->p != ']') {
821 state->depth--;
822 return nullptr;
823 }
824 state->p++;
825 if (infix_type_create_vector(state->arena, &result_type, element_type, num_elements) != INFIX_SUCCESS)
826 result_type = nullptr;
827 }
828 else { // Primitive type or error
829 result_type = parse_primitive(state);
830 if (!result_type) {
831 // If no error was set by a failed `consume_keyword`, set a generic one.
833 state->p = p_before_type;
834 if (isalpha((unsigned char)*state->p) || *state->p == '_')
836 else
838 }
839 }
840 }
841 // Only set source offset for dynamically allocated types (primitives are static singletons).
842 if (result_type && result_type->is_arena_allocated)
843 result_type->source_offset = current_offset;
844 state->depth--;
845 return result_type;
846}
861 infix_type ** out_ret_type,
862 infix_function_argument ** out_args,
863 size_t * out_num_args,
864 size_t * out_num_fixed_args) {
865 if (*state->p != '(') {
868 }
869 state->p++;
870 skip_whitespace(state);
871 // Use a temporary linked list to collect arguments.
872 typedef struct arg_node {
874 struct arg_node * next;
875 } arg_node;
876 arg_node *head = nullptr, *tail = nullptr;
877 size_t num_args = 0;
878 // Parse Fixed Arguments
879 if (*state->p != ')' && *state->p != ';') {
880 while (1) {
881 skip_whitespace(state);
882 if (*state->p == ')' || *state->p == ';')
883 break;
884 const char * name = parse_optional_name_prefix(state);
885 infix_type * arg_type = parse_type(state);
886 if (!arg_type)
888 arg_node * node = infix_arena_calloc(state->arena, 1, sizeof(arg_node), _Alignof(arg_node));
889 if (!node) {
891 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
893 }
894 node->arg.type = arg_type;
895 node->arg.name = name;
896 node->next = nullptr;
897 if (!head)
898 head = tail = node;
899 else {
900 tail->next = node;
901 tail = node;
902 }
903 num_args++;
904 skip_whitespace(state);
905 if (*state->p == ',') {
906 state->p++;
907 skip_whitespace(state);
908 if (*state->p == ')' || *state->p == ';') { // Trailing comma error.
911 }
912 }
913 else if (*state->p != ')' && *state->p != ';') {
916 }
917 else
918 break;
919 }
920 }
921 *out_num_fixed_args = num_args;
922 // Parse Variadic Arguments
923 if (*state->p == ';') {
924 state->p++;
925 if (*state->p != ')') {
926 while (1) {
927 skip_whitespace(state);
928 if (*state->p == ')')
929 break;
930 const char * name = parse_optional_name_prefix(state);
931 infix_type * arg_type = parse_type(state);
932 if (!arg_type)
934 arg_node * node = infix_arena_calloc(state->arena, 1, sizeof(arg_node), _Alignof(arg_node));
935 if (!node) {
937 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
939 }
940 node->arg.type = arg_type;
941 node->arg.name = name;
942 node->next = nullptr;
943 if (!head)
944 head = tail = node;
945 else {
946 tail->next = node;
947 tail = node;
948 }
949 num_args++;
950 skip_whitespace(state);
951 if (*state->p == ',') {
952 state->p++;
953 skip_whitespace(state);
954 if (*state->p == ')') { // Trailing comma error.
957 }
958 }
959 else if (*state->p != ')') {
962 }
963 else
964 break;
965 }
966 }
967 }
968 skip_whitespace(state);
969 if (*state->p != ')') {
972 }
973 state->p++;
974 // Parse Return Type
975 skip_whitespace(state);
976 if (state->p[0] != '-' || state->p[1] != '>') {
979 }
980 state->p += 2;
981 *out_ret_type = parse_type(state);
982 if (!*out_ret_type)
984 // Convert linked list of args to a flat array.
985 infix_function_argument * args = (num_args > 0)
986 ? infix_arena_calloc(state->arena, num_args, sizeof(infix_function_argument), _Alignof(infix_function_argument))
987 : nullptr;
988 if (num_args > 0 && !args) {
991 }
992 arg_node * current = head;
993 for (size_t i = 0; i < num_args; i++) {
994 args[i] = current->arg;
995 current = current->next;
996 }
997 *out_args = args;
998 *out_num_args = num_args;
999 return INFIX_SUCCESS;
1000}
1001// High-Level API Implementation
1019 infix_arena_t ** out_arena,
1020 const char * signature) {
1021 if (!out_type || !out_arena) {
1024 }
1025 if (!signature || *signature == '\0') {
1028 }
1029 // The top-level public API is responsible for setting g_infix_last_signature_context.
1030 *out_arena = infix_arena_create(4096);
1031 if (!*out_arena) {
1034 }
1035 parser_state state = {.p = signature, .start = signature, .arena = *out_arena, .depth = 0};
1036 infix_type * type = parse_type(&state);
1037 if (type) {
1038 skip_whitespace(&state);
1039 // After successfully parsing a type, ensure there is no trailing junk.
1040 if (state.p[0] != '\0') {
1042 type = nullptr;
1043 }
1044 }
1045 if (!type) {
1046 // If parsing failed at any point, clean up the temporary arena.
1047 infix_arena_destroy(*out_arena);
1048 *out_arena = nullptr;
1049 *out_type = nullptr;
1051 }
1052 *out_type = type;
1053 return INFIX_SUCCESS;
1054}
1069 infix_arena_t ** out_arena,
1070 const char * signature,
1073 g_infix_last_signature_context = signature; // Set context for rich error reporting.
1074 // "Parse" stage: Create a raw, unresolved type graph in a temporary arena.
1075 infix_type * raw_type = nullptr;
1076 infix_arena_t * parser_arena = nullptr;
1077 infix_status status = _infix_parse_type_internal(&raw_type, &parser_arena, signature);
1078 if (status != INFIX_SUCCESS)
1079 return status;
1080 // Create the final arena that will be returned to the caller.
1081 *out_arena = infix_arena_create(4096);
1082 if (!*out_arena) {
1083 infix_arena_destroy(parser_arena);
1086 }
1087 // "Copy" stage: Deep copy the raw graph into the final arena.
1088 infix_type * final_type = _copy_type_graph_to_arena(*out_arena, raw_type);
1089 infix_arena_destroy(parser_arena); // The temporary graph is no longer needed.
1090 if (!final_type) {
1091 infix_arena_destroy(*out_arena);
1092 *out_arena = nullptr;
1095 }
1096 // "Resolve" stage: Replace all named references (`@Name`) with concrete types.
1098 if (status != INFIX_SUCCESS) {
1099 infix_arena_destroy(*out_arena);
1100 *out_arena = nullptr;
1101 *out_type = nullptr;
1102 }
1103 else {
1104 // "Layout" stage: Calculate the final size, alignment, and member offsets.
1106 *out_type = final_type;
1107 }
1108 return status;
1109}
1128 infix_arena_t ** out_arena,
1129 infix_type ** out_ret_type,
1130 infix_function_argument ** out_args,
1131 size_t * out_num_args,
1132 size_t * out_num_fixed_args,
1135
1136 //
1137 if (!signature) {
1140 }
1141 if (*signature == '\0') {
1144 }
1145 if (!out_arena || !out_ret_type || !out_args || !out_num_args || !out_num_fixed_args) {
1148 }
1149
1151
1152 // Parse stage
1153 infix_type * raw_func_type = nullptr;
1154 infix_arena_t * parser_arena = nullptr;
1155 infix_status status = _infix_parse_type_internal(&raw_func_type, &parser_arena, signature);
1156 if (status != INFIX_SUCCESS)
1157 return status;
1158
1159 if (raw_func_type->category != INFIX_TYPE_REVERSE_TRAMPOLINE) {
1160 infix_arena_destroy(parser_arena);
1163 }
1164
1165 // Create final arena
1166 *out_arena = infix_arena_create(8192);
1167 if (!*out_arena) {
1168 infix_arena_destroy(parser_arena);
1171 }
1172
1173 // "Copy" stage
1174 infix_type * final_func_type = _copy_type_graph_to_arena(*out_arena, raw_func_type);
1175 infix_arena_destroy(parser_arena);
1176 if (!final_func_type) {
1177 infix_arena_destroy(*out_arena);
1178 *out_arena = nullptr;
1181 }
1182
1183 // Resolve and layout stages
1185 if (status != INFIX_SUCCESS) {
1186 infix_arena_destroy(*out_arena);
1187 *out_arena = nullptr;
1189 }
1190 _infix_type_recalculate_layout(final_func_type);
1191
1192 // Unpack the results for the caller from the final, processed function type object.
1193 *out_ret_type = final_func_type->meta.func_ptr_info.return_type;
1194 *out_args = final_func_type->meta.func_ptr_info.args;
1195 *out_num_args = final_func_type->meta.func_ptr_info.num_args;
1196 *out_num_fixed_args = final_func_type->meta.func_ptr_info.num_fixed_args;
1197 return INFIX_SUCCESS;
1198}
1199
1200// Type Printing Logic
1206typedef struct {
1207 char * p;
1208 size_t remaining;
1210 // Itanium mangling state
1211 const void * itanium_subs[64];
1213 // MSVC mangling state
1214 const infix_type * msvc_types[10];
1216 size_t depth;
1226static void _print(printer_state * state, const char * fmt, ...) {
1227 if (state->status != INFIX_SUCCESS)
1228 return;
1229 va_list args;
1230 va_start(args, fmt);
1231 int written = vsnprintf(state->p, state->remaining, fmt, args);
1232 va_end(args);
1233 if (written < 0 || (size_t)written >= state->remaining)
1234 // If snprintf failed or would have overflowed, mark an error.
1236 else {
1237 state->p += written;
1238 state->remaining -= written;
1239 }
1240}
1241// Forward declaration for mutual recursion in printers.
1242static void _infix_type_print_signature_recursive(printer_state * state, const infix_type * type);
1243static void _infix_type_print_itanium_recursive(printer_state * state, const infix_type * type);
1244static void _infix_type_print_msvc_recursive(printer_state * state, const infix_type * type);
1245static void _infix_type_print_body_only_recursive(printer_state * state, const infix_type * type);
1253#define PRINT_RECURSE(state, fn, arg) \
1254 do { \
1255 if ((state)->depth >= MAX_PRINT_RECURSION_DEPTH) { \
1256 (state)->status = INFIX_ERROR_INVALID_ARGUMENT; \
1257 return; \
1258 } \
1259 (state)->depth++; \
1260 fn((state), (arg)); \
1261 (state)->depth--; \
1262 } while (0)
1263
1264// Itanium Mangling Helpers
1265static bool _find_itanium_sub(printer_state * state, const void * component, size_t * index) {
1266 for (size_t i = 0; i < state->itanium_sub_count; i++) {
1267 if (state->itanium_subs[i] == component) {
1268 *index = i;
1269 return true;
1270 }
1271 }
1272 return false;
1273}
1274
1275static void _add_itanium_sub(printer_state * state, const void * component) {
1276 if (state->itanium_sub_count < 64)
1277 state->itanium_subs[state->itanium_sub_count++] = component;
1278}
1279
1280static void _print_itanium_sub(printer_state * state, size_t index) {
1281 if (index == 0) {
1282 _print(state, "S_");
1283 }
1284 else {
1285 index--; // S0_ is index 1
1286 _print(state, "S");
1287 if (index == 0) {
1288 _print(state, "0");
1289 }
1290 else {
1291 char buf[16];
1292 int pos = 0;
1293 size_t val = index;
1294 while (val > 0) {
1295 int digit = val % 36;
1296 buf[pos++] = (digit < 10) ? (char)('0' + digit) : (char)('A' + digit - 10);
1297 val /= 36;
1298 }
1299 while (pos > 0)
1300 _print(state, "%c", buf[--pos]);
1301 }
1302 _print(state, "_");
1303 }
1304}
1305
1319 if (state->status != INFIX_SUCCESS || !type) {
1320 if (state->status == INFIX_SUCCESS)
1322 return;
1323 }
1324 // If the type has a semantic name, always prefer printing it.
1325 if (type->name) {
1326 // Only prepend '@' if it is an aggregate or named reference.
1327 // Primitives like WChar or aliases like size_t should not get '@'.
1328 if (type->category == INFIX_TYPE_PRIMITIVE || type->category == INFIX_TYPE_VOID)
1329 _print(state, "%s", type->name);
1330 else
1331 _print(state, "@%s", type->name);
1332 return;
1333 }
1334 switch (type->category) {
1335 case INFIX_TYPE_VOID:
1336 _print(state, "void");
1337 break;
1339 // This case should ideally not be hit with a fully resolved type, but we handle it for robustness.
1340 _print(state, "@%s", type->meta.named_reference.name);
1341 break;
1342 case INFIX_TYPE_POINTER:
1343 _print(state, "*");
1344 // Special handling for `void*` or recursive pointers to avoid infinite recursion.
1345 if (type->meta.pointer_info.pointee_type == type || type->meta.pointer_info.pointee_type == nullptr ||
1347 _print(state, "void");
1348 else
1350 break;
1351 case INFIX_TYPE_ARRAY:
1352 if (type->meta.array_info.is_flexible)
1353 _print(state, "[?:");
1354 else
1355 _print(state, "[%zu:", type->meta.array_info.num_elements);
1357 _print(state, "]");
1358 break;
1359 case INFIX_TYPE_STRUCT:
1360 if (type->meta.aggregate_info.is_packed) {
1361 _print(state, "!");
1362 if (type->alignment != 1)
1363 _print(state, "%zu:", type->alignment);
1364 }
1365 _print(state, "{");
1366 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1367 if (i > 0)
1368 _print(state, ",");
1369 const infix_struct_member * member = &type->meta.aggregate_info.members[i];
1370 if (member->name)
1371 _print(state, "%s:", member->name);
1373 if (member->bit_width > 0)
1374 _print(state, ":%u", member->bit_width);
1375 }
1376 _print(state, "}");
1377 break;
1378 case INFIX_TYPE_UNION:
1379 _print(state, "<");
1380 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1381 if (i > 0)
1382 _print(state, ",");
1383 const infix_struct_member * member = &type->meta.aggregate_info.members[i];
1384 if (member->name)
1385 _print(state, "%s:", member->name);
1387 // Bitfields in unions are rare but syntactically valid in C.
1388 if (member->bit_width > 0)
1389 _print(state, ":%u", member->bit_width);
1390 }
1391 _print(state, ">");
1392 break;
1394 _print(state, "(");
1395 for (size_t i = 0; i < type->meta.func_ptr_info.num_fixed_args; ++i) {
1396 if (i > 0)
1397 _print(state, ",");
1398 const infix_function_argument * arg = &type->meta.func_ptr_info.args[i];
1399 if (arg->name)
1400 _print(state, "%s:", arg->name);
1402 }
1404 _print(state, ";");
1405 for (size_t i = type->meta.func_ptr_info.num_fixed_args; i < type->meta.func_ptr_info.num_args; ++i) {
1406 if (i > type->meta.func_ptr_info.num_fixed_args)
1407 _print(state, ",");
1408 const infix_function_argument * arg = &type->meta.func_ptr_info.args[i];
1409 if (arg->name)
1410 _print(state, "%s:", arg->name);
1412 }
1413 }
1414 _print(state, ")->");
1416 break;
1417 case INFIX_TYPE_ENUM:
1418 _print(state, "e:");
1420 break;
1421 case INFIX_TYPE_COMPLEX:
1422 _print(state, "c[");
1424 _print(state, "]");
1425 break;
1426 case INFIX_TYPE_VECTOR:
1427 {
1428 const infix_type * element_type = type->meta.vector_info.element_type;
1429 size_t num_elements = type->meta.vector_info.num_elements;
1430 bool printed_alias = false;
1431 if (element_type->category == INFIX_TYPE_PRIMITIVE) {
1432 if (num_elements == 8 && is_double(element_type)) {
1433 _print(state, "m512d");
1434 printed_alias = true;
1435 }
1436 else if (num_elements == 16 && is_float(element_type)) {
1437 _print(state, "m512");
1438 printed_alias = true;
1439 }
1440 else if (num_elements == 8 && element_type->meta.primitive_id == INFIX_PRIMITIVE_SINT64) {
1441 _print(state, "m512i");
1442 printed_alias = true;
1443 }
1444 else if (num_elements == 4 && is_double(element_type)) {
1445 _print(state, "m256d");
1446 printed_alias = true;
1447 }
1448 else if (num_elements == 8 && is_float(element_type)) {
1449 _print(state, "m256");
1450 printed_alias = true;
1451 }
1452 }
1453 if (!printed_alias) {
1454 _print(state, "v[%zu:", num_elements);
1456 _print(state, "]");
1457 }
1458 }
1459 break;
1461 switch (type->meta.primitive_id) {
1463 _print(state, "bool");
1464 break;
1466 _print(state, "sint8");
1467 break;
1469 _print(state, "uint8");
1470 break;
1472 _print(state, "sint16");
1473 break;
1475 _print(state, "uint16");
1476 break;
1478 _print(state, "sint32");
1479 break;
1481 _print(state, "uint32");
1482 break;
1484 _print(state, "sint64");
1485 break;
1487 _print(state, "uint64");
1488 break;
1490 _print(state, "sint128");
1491 break;
1493 _print(state, "uint128");
1494 break;
1496 _print(state, "float16");
1497 break;
1499 _print(state, "float");
1500 break;
1502 _print(state, "double");
1503 break;
1505 _print(state, "longdouble");
1506 break;
1507 }
1508 break;
1509 default:
1511 break;
1512 }
1513}
1521 if (state->status != INFIX_SUCCESS || !type) {
1522 if (state->status == INFIX_SUCCESS)
1524 return;
1525 }
1526
1527 // These built-in types must ALWAYS use ABI codes, ignoring any name aliases.
1528 if (type->category == INFIX_TYPE_VOID) {
1529 _print(state, "v");
1530 return;
1531 }
1532
1533 if (type->category == INFIX_TYPE_PRIMITIVE) {
1534 switch (type->meta.primitive_id) {
1536 _print(state, "b");
1537 return;
1538 case INFIX_PRIMITIVE_SINT8: // signed char
1539 _print(state, "a");
1540 return;
1541 case INFIX_PRIMITIVE_UINT8: // unsigned char
1542 _print(state, "h");
1543 return;
1544 case INFIX_PRIMITIVE_SINT16: // short
1545 _print(state, "s");
1546 return;
1547 case INFIX_PRIMITIVE_UINT16: // unsigned short
1548 _print(state, "t");
1549 return;
1550 case INFIX_PRIMITIVE_SINT32: // int
1551 _print(state, "i");
1552 return;
1553 case INFIX_PRIMITIVE_UINT32: // unsigned int
1554 _print(state, "j");
1555 return;
1556 case INFIX_PRIMITIVE_SINT64: // long long
1557 _print(state, "x");
1558 return;
1559 case INFIX_PRIMITIVE_UINT64: // unsigned long long
1560 _print(state, "y");
1561 return;
1562 case INFIX_PRIMITIVE_SINT128: // __int128
1563 _print(state, "n");
1564 return;
1565 case INFIX_PRIMITIVE_UINT128: // unsigned __int128
1566 _print(state, "o");
1567 return;
1568 case INFIX_PRIMITIVE_FLOAT16: // half-precision float (IEEE 754)
1569 _print(state, "Dh");
1570 return;
1572 _print(state, "f");
1573 return;
1575 _print(state, "d");
1576 return;
1578 _print(state, "e");
1579 return;
1580 }
1581 }
1582
1583 // Substitutions. Check if this complex type (Pointer/Struct) was already seen.
1584 size_t sub_index;
1585 bool is_builtin = (type->category == INFIX_TYPE_VOID || type->category == INFIX_TYPE_PRIMITIVE);
1586
1587 if (!is_builtin && _find_itanium_sub(state, type, &sub_index)) {
1588 _print_itanium_sub(state, sub_index);
1589 return;
1590 }
1591
1592 // Complex types
1593 switch (type->category) {
1594 case INFIX_TYPE_POINTER:
1595 _print(state, "P");
1597 _add_itanium_sub(state, type);
1598 break;
1600 {
1601 const char * name = type->meta.named_reference.name;
1602 size_t len = strlen(name);
1603 _print(state, "%zu%s", len, name);
1604 _add_itanium_sub(state, type);
1605 }
1606 break;
1607 case INFIX_TYPE_STRUCT:
1608 case INFIX_TYPE_UNION:
1609 if (type->name) {
1610 // Check for namespaced type (e.g. "Namespace::Class")
1611 // Itanium mangling for namespaces is N...E
1612 const char * p = type->name;
1613 int parts = 0;
1614 // Count parts
1615 while (*p) {
1616 if (p[0] == ':' && p[1] == ':') {
1617 parts++;
1618 p += 2;
1619 }
1620 else {
1621 p++;
1622 }
1623 }
1624 parts++; // Last part
1625
1626 if (parts > 1) {
1627 _print(state, "N");
1628 p = type->name;
1629 while (*p) {
1630 const char * end = strstr(p, "::");
1631 size_t part_len = end ? (size_t)(end - p) : strlen(p);
1632 _print(state, "%zu", part_len);
1633 // Print part_len chars
1634 for (size_t i = 0; i < part_len; i++)
1635 _print(state, "%c", p[i]);
1636 if (end)
1637 p = end + 2;
1638 else
1639 break;
1640 }
1641 _print(state, "E");
1642 }
1643 else {
1644 // Simple name
1645 size_t len = strlen(type->name);
1646 _print(state, "%zu%s", len, type->name);
1647 }
1648 _add_itanium_sub(state, type);
1649 }
1650 else {
1651 // Mangling for anonymous structs isn't standardized.
1652 // Emitting 'void' as a safe placeholder for "unknown type".
1653 _print(state, "v");
1654 }
1655 break;
1656 case INFIX_TYPE_COMPLEX:
1657 _print(state, "C");
1659 _add_itanium_sub(state, type);
1660 break;
1661 case INFIX_TYPE_VECTOR:
1662 _print(state, "Dv%zu_", type->size / type->meta.vector_info.element_type->size);
1664 _add_itanium_sub(state, type);
1665 break;
1666 default:
1667 // Fallback for types that don't map cleanly to Itanium mangling
1668 _print(state, "v");
1669 break;
1670 }
1671}
1679 if (state->status != INFIX_SUCCESS || !type) {
1680 if (state->status == INFIX_SUCCESS)
1682 return;
1683 }
1684
1685 // Built-in types: Use MSVC ABI codes immediately.
1686 if (type->category == INFIX_TYPE_VOID) {
1687 _print(state, "X");
1688 return;
1689 }
1690
1691 if (type->category == INFIX_TYPE_PRIMITIVE) {
1692 switch (type->meta.primitive_id) {
1694 _print(state, "_N");
1695 return;
1697 _print(state, "C");
1698 return;
1700 _print(state, "E");
1701 return;
1703 _print(state, "F");
1704 return;
1706 _print(state, "G");
1707 return;
1709 _print(state, "H");
1710 return;
1712 _print(state, "I");
1713 return;
1715 _print(state, "_J");
1716 return;
1718 _print(state, "_K");
1719 return;
1721 _print(state, "_L");
1722 return;
1724 _print(state, "_M");
1725 return;
1727 _print(state, "_T");
1728 return;
1730 _print(state, "M");
1731 return;
1733 _print(state, "N");
1734 return;
1736 _print(state, "O");
1737 return;
1738 }
1739 }
1740
1741 // Check for type back-references (0-9)
1742 // MSVC only back-references complex types or pointers to them.
1743 bool can_backref = (type->category == INFIX_TYPE_POINTER || type->category == INFIX_TYPE_STRUCT ||
1744 type->category == INFIX_TYPE_UNION || type->category == INFIX_TYPE_ENUM);
1745
1746 if (can_backref) {
1747 for (size_t i = 0; i < state->msvc_type_count; i++) {
1748 if (state->msvc_types[i] == type) {
1749 _print(state, "%zu", i);
1750 return;
1751 }
1752 }
1753 }
1754
1755 // Handle named types (Struct/Union/Enum or aliases)
1756 if (type->name) {
1757 // MSVC encoding:
1758 // U = Struct
1759 // T = Union
1760 // W = Enum
1761 char prefix = 'U';
1762 if (type->category == INFIX_TYPE_UNION)
1763 prefix = 'T';
1764 else if (type->category == INFIX_TYPE_ENUM)
1765 prefix = 'W';
1766
1767 // Check for namespaces (e.g. "Namespace::Class")
1768 // MSVC format: <Prefix><Name>@<Namespace>@@
1769 // Reverse order of namespaces!
1770 if (strstr(type->name, "::")) {
1771 _print(state, "%c", prefix);
1772
1773 // We need to split and reverse. Since we can't allocate easily here,
1774 // we'll scan the string multiple times or use recursion.
1775 // Let's use a simple stack-based approach for small depth.
1776 const char * parts[MAX_RECURSION_DEPTH];
1777 size_t lens[MAX_RECURSION_DEPTH];
1778 int count = 0;
1779
1780 const char * p = type->name;
1781 while (*p && count < MAX_RECURSION_DEPTH) {
1782 parts[count] = p;
1783 const char * end = strstr(p, "::");
1784 if (end) {
1785 lens[count] = end - p;
1786 p = end + 2;
1787 }
1788 else {
1789 lens[count] = strlen(p);
1790 p += lens[count];
1791 }
1792 count++;
1793 }
1794
1795 // Print in reverse order
1796 for (int i = count - 1; i >= 0; i--) {
1797 for (size_t j = 0; j < lens[i]; j++)
1798 _print(state, "%c", parts[i][j]);
1799 _print(state, "@");
1800 }
1801 _print(state, "@"); // Terminator
1802 }
1803 else {
1804 _print(state, "%c%s@@", prefix, type->name);
1805 }
1806
1807 if (can_backref && state->msvc_type_count < 10)
1808 state->msvc_types[state->msvc_type_count++] = type;
1809 return;
1810 }
1811
1812 switch (type->category) {
1813 case INFIX_TYPE_POINTER:
1814 // Standard MSVC pointer encoding for x64:
1815 // P = Pointer
1816 // E = __ptr64
1817 // A = const/volatile qualifiers (A = none)
1818 // Then the pointee type.
1819 _print(state, "PEA");
1821 if (can_backref && state->msvc_type_count < 10)
1822 state->msvc_types[state->msvc_type_count++] = type;
1823 break;
1824 case INFIX_TYPE_REVERSE_TRAMPOLINE: // Function Pointer
1825 // P6 = Pointer to Function
1826 // A = __cdecl
1827 _print(state, "P6A");
1828 // Return type
1830 // Arguments
1831 if (type->meta.func_ptr_info.num_args == 0)
1832 _print(state, "X");
1833 else
1834 for (size_t i = 0; i < type->meta.func_ptr_info.num_args; ++i)
1836 _print(state, "@Z");
1837 if (can_backref && state->msvc_type_count < 10)
1838 state->msvc_types[state->msvc_type_count++] = type;
1839 break;
1840 case INFIX_TYPE_COMPLEX:
1841 // MSVC doesn't have a built-in complex type, it uses structs.
1842 _print(state, "U_Complex@@");
1843 if (can_backref && state->msvc_type_count < 10)
1844 state->msvc_types[state->msvc_type_count++] = type;
1845 break;
1846 case INFIX_TYPE_VECTOR:
1847 _print(state, "T__m%zu@@", type->size * 8);
1848 if (can_backref && state->msvc_type_count < 10)
1849 state->msvc_types[state->msvc_type_count++] = type;
1850 break;
1852 // Unresolved references, treat as Struct for mangling purposes.
1853 _print(state, "U%s@@", type->meta.named_reference.name);
1854 if (can_backref && state->msvc_type_count < 10)
1855 state->msvc_types[state->msvc_type_count++] = type;
1856 break;
1857 default:
1858 _print(state, "X");
1859 break;
1860 }
1861}
1875 if (state->status != INFIX_SUCCESS || !type) {
1876 if (state->status == INFIX_SUCCESS)
1878 return;
1879 }
1880 // This is the key difference from the main printer: we skip the `if (type->name)` check
1881 // and immediately print the underlying structure of the type.
1882 switch (type->category) {
1883 case INFIX_TYPE_STRUCT:
1884 if (type->meta.aggregate_info.is_packed) {
1885 _print(state, "!");
1886 if (type->alignment != 1)
1887 _print(state, "%zu:", type->alignment);
1888 }
1889 _print(state, "{");
1890 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1891 if (i > 0)
1892 _print(state, ",");
1893 const infix_struct_member * member = &type->meta.aggregate_info.members[i];
1894 if (member->name)
1895 _print(state, "%s:", member->name);
1896 // For nested members, we can use the standard printer, which IS allowed
1897 // to use the `@Name` shorthand for brevity.
1899 if (member->bit_width > 0)
1900 _print(state, ":%u", member->bit_width);
1901 }
1902 _print(state, "}");
1903 break;
1904 case INFIX_TYPE_UNION:
1905 _print(state, "<");
1906 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1907 if (i > 0)
1908 _print(state, ",");
1909 const infix_struct_member * member = &type->meta.aggregate_info.members[i];
1910 if (member->name)
1911 _print(state, "%s:", member->name);
1913 if (member->bit_width > 0)
1914 _print(state, ":%u", member->bit_width);
1915 }
1916 _print(state, ">");
1917 break;
1918 // For all other types, we replicate the printing logic from the main printer
1919 // to ensure we print the structure, not a potential top-level alias name.
1920 case INFIX_TYPE_VOID:
1921 _print(state, "void");
1922 break;
1923 case INFIX_TYPE_POINTER:
1924 _print(state, "*");
1925 if (type->meta.pointer_info.pointee_type == type || type->meta.pointer_info.pointee_type == nullptr ||
1927 _print(state, "void");
1928 else
1930 break;
1931 case INFIX_TYPE_ARRAY:
1932 if (type->meta.array_info.is_flexible)
1933 _print(state, "[?:");
1934 else
1935 _print(state, "[%zu:", type->meta.array_info.num_elements);
1937 _print(state, "]");
1938 break;
1939 case INFIX_TYPE_ENUM:
1940 _print(state, "e:");
1942 break;
1943 case INFIX_TYPE_COMPLEX:
1944 _print(state, "c[");
1946 _print(state, "]");
1947 break;
1949 // This block is now a full copy from the main printer.
1950 switch (type->meta.primitive_id) {
1952 _print(state, "bool");
1953 break;
1955 _print(state, "sint8");
1956 break;
1958 _print(state, "uint8");
1959 break;
1961 _print(state, "sint16");
1962 break;
1964 _print(state, "uint16");
1965 break;
1967 _print(state, "sint32");
1968 break;
1970 _print(state, "uint32");
1971 break;
1973 _print(state, "sint64");
1974 break;
1976 _print(state, "uint64");
1977 break;
1979 _print(state, "sint128");
1980 break;
1982 _print(state, "uint128");
1983 break;
1985 _print(state, "float16");
1986 break;
1988 _print(state, "float");
1989 break;
1991 _print(state, "double");
1992 break;
1994 _print(state, "longdouble");
1995 break;
1996 }
1997 break;
1998 // We can safely delegate the remaining complex cases to the main printer, as they
1999 // do not have a top-level `name` field themselves.
2002 case INFIX_TYPE_VECTOR:
2004 break;
2005 default:
2007 break;
2008 }
2009}
2015 size_t buffer_size,
2016 const infix_type * type,
2017 infix_print_dialect_t dialect) {
2018 if (!buffer || buffer_size == 0 || !type || dialect != INFIX_DIALECT_SIGNATURE)
2020 printer_state state = {buffer, buffer_size, INFIX_SUCCESS, {0}, 0, {0}, 0, 0};
2021 *buffer = '\0';
2023 if (state.remaining > 0)
2024 *state.p = '\0';
2025 else
2026 buffer[buffer_size - 1] = '\0';
2027 return state.status;
2028}
2038 size_t buffer_size,
2039 const infix_type * type,
2040 infix_print_dialect_t dialect) {
2042 if (!buffer || buffer_size == 0 || !type) {
2045 }
2046 printer_state state = {buffer, buffer_size, INFIX_SUCCESS, {0}, 0, {0}, 0, 0};
2047 *buffer = '\0';
2048 if (dialect == INFIX_DIALECT_SIGNATURE)
2050 else if (dialect == INFIX_DIALECT_ITANIUM_MANGLING)
2052 else if (dialect == INFIX_DIALECT_MSVC_MANGLING)
2054 else {
2055 _print(&state, "unsupported_dialect");
2057 }
2058 if (state.status == INFIX_SUCCESS) {
2059 if (state.remaining > 0)
2060 *state.p = '\0'; // Null-terminate if there is space.
2061 else {
2062 // Buffer was exactly full. Ensure null termination at the very end.
2063 buffer[buffer_size - 1] = '\0';
2064 return INFIX_ERROR_INVALID_ARGUMENT; // Indicate truncation.
2065 }
2066 }
2067 else if (buffer_size > 0)
2068 // Ensure null termination even on error (e.g., buffer too small).
2069 buffer[buffer_size - 1] = '\0';
2070 return state.status;
2071}
2085 size_t buffer_size,
2086 const char * function_name,
2087 const infix_type * ret_type,
2089 size_t num_args,
2090 size_t num_fixed_args,
2091 infix_print_dialect_t dialect) {
2093 if (!buffer || buffer_size == 0 || !ret_type || (num_args > 0 && !args)) {
2096 }
2097 printer_state state = {buffer, buffer_size, INFIX_SUCCESS, {0}, 0, {0}, 0, 0};
2098 *buffer = '\0';
2099 if (dialect == INFIX_DIALECT_SIGNATURE) {
2100 (void)function_name; // Unused
2101 _print(&state, "(");
2102 for (size_t i = 0; i < num_fixed_args; ++i) {
2103 if (i > 0)
2104 _print(&state, ",");
2106 }
2107 if (num_args > num_fixed_args) {
2108 _print(&state, ";");
2109 for (size_t i = num_fixed_args; i < num_args; ++i) {
2110 if (i > num_fixed_args)
2111 _print(&state, ",");
2113 }
2114 }
2115 _print(&state, ")->");
2117 }
2118 else if (dialect == INFIX_DIALECT_ITANIUM_MANGLING) {
2119 // _Z <name_len> <name> <ret_type?> <args...>
2120 // Note: Itanium mangling usually omits return type for standard functions unless it's a template or special
2121 // case. We omit it here for simplicity to match extern "C" -> C++ linking expectations for simple functions.
2122 _print(&state, "_Z");
2123 if (function_name) {
2124 // Check for namespace in function name (e.g., "MyNS::my_func")
2125 const char * p = function_name;
2126 int parts = 0;
2127 while (*p)
2128 if (p[0] == ':' && p[1] == ':') {
2129 parts++;
2130 p += 2;
2131 }
2132 else
2133 p++;
2134 parts++;
2135
2136 if (parts > 1) {
2137 _print(&state, "N");
2138 p = function_name;
2139 while (*p) {
2140 const char * end = strstr(p, "::");
2141 size_t part_len = end ? (size_t)(end - p) : strlen(p);
2142 _print(&state, "%zu", part_len);
2143 for (size_t i = 0; i < part_len; i++)
2144 _print(&state, "%c", p[i]);
2145 if (end)
2146 p = end + 2;
2147 else
2148 break;
2149 }
2150 _print(&state, "E");
2151 }
2152 else {
2153 size_t name_len = strlen(function_name);
2154 _print(&state, "%zu%s", name_len, function_name);
2155 }
2156 }
2157 else
2158 _print(&state, "4func"); // Default name if NULL
2159
2160 if (num_args == 0)
2161 _print(&state, "v"); // void (no args)
2162 else
2163 for (size_t i = 0; i < num_args; ++i)
2165 }
2166 else if (dialect == INFIX_DIALECT_MSVC_MANGLING) {
2167 // MSVC: ?<name>@@YA<ret><args...>@Z
2168 _print(&state, "?");
2169 if (function_name) {
2170 // MSVC namespace handling: reverse order
2171 if (strstr(function_name, "::")) {
2172 const char * parts[MAX_RECURSION_DEPTH];
2173 size_t lens[MAX_RECURSION_DEPTH];
2174 int count = 0;
2175 const char * p = function_name;
2176 while (*p && count < MAX_RECURSION_DEPTH) {
2177 parts[count] = p;
2178 const char * end = strstr(p, "::");
2179 if (end) {
2180 lens[count] = end - p;
2181 p = end + 2;
2182 }
2183 else {
2184 lens[count] = strlen(p);
2185 p += lens[count];
2186 }
2187 count++;
2188 }
2189 // Print in reverse order
2190 for (int i = count - 1; i >= 0; i--) {
2191 for (size_t j = 0; j < lens[i]; j++)
2192 _print(&state, "%c", parts[i][j]);
2193 _print(&state, "@");
2194 }
2195 }
2196 else {
2197 _print(&state, "%s@", function_name);
2198 }
2199 }
2200 else {
2201 _print(&state, "func@");
2202 }
2203 _print(&state, "@YA"); // __cdecl (default)
2205
2206 if (num_args == 0)
2207 _print(&state, "X"); // void argument list
2208 else
2209 for (size_t i = 0; i < num_args; ++i)
2210 _infix_type_print_msvc_recursive(&state, args[i].type);
2211 _print(&state, "@Z");
2212 }
2213 else {
2214 _print(&state, "unsupported_dialect");
2216 }
2217 if (state.status == INFIX_SUCCESS) {
2218 if (state.remaining > 0)
2219 *state.p = '\0';
2220 else {
2221 if (buffer_size > 0)
2222 buffer[buffer_size - 1] = '\0';
2223 return INFIX_ERROR_INVALID_ARGUMENT; // Indicate truncation.
2224 }
2225 }
2226 else if (buffer_size > 0)
2227 buffer[buffer_size - 1] = '\0';
2228 return state.status;
2229}
2244c23_nodiscard infix_status infix_registry_print(char * buffer, size_t buffer_size, const infix_registry_t * registry) {
2245 if (!buffer || buffer_size == 0 || !registry)
2247 printer_state state = {buffer, buffer_size, INFIX_SUCCESS, {0}, 0, {0}, 0, 0};
2248 *state.p = '\0';
2249 // Iterate through all buckets and their chains.
2250 for (size_t i = 0; i < registry->num_buckets; ++i) {
2251 for (const _infix_registry_entry_t * entry = registry->buckets[i]; entry != nullptr; entry = entry->next) {
2252 // Only print fully defined types, not forward declarations.
2253 if (entry->type && !entry->is_forward_declaration) {
2254 char type_body_buffer[1024];
2256 type_body_buffer, sizeof(type_body_buffer), entry->type, INFIX_DIALECT_SIGNATURE) !=
2257 INFIX_SUCCESS) {
2259 goto end_print_loop;
2260 }
2261 _print(&state, "@%s = %s;\n", entry->name, type_body_buffer);
2262 }
2263 else if (entry->is_forward_declaration) // Explicitly print forward declarations
2264 _print(&state, "@%s;\n", entry->name);
2265 if (state.status != INFIX_SUCCESS)
2266 goto end_print_loop;
2267 }
2268 }
2269end_print_loop:;
2270 return state.status;
2271}
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
char * p
Definition 904_registry_benchmark.c:25
#define c23_nodiscard
Internal alias for the public INFIX_NODISCARD macro.
Definition compat_c23.h:92
#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:281
infix_error_code_t
Enumerates specific error codes.
Definition infix.h:1412
@ INFIX_CODE_SUCCESS
Definition infix.h:1414
@ INFIX_CODE_INVALID_MEMBER_TYPE
Definition infix.h:1440
@ INFIX_CODE_INTEGER_OVERFLOW
Definition infix.h:1431
@ INFIX_CODE_TYPE_TOO_LARGE
Definition infix.h:1438
@ INFIX_CODE_EMPTY_SIGNATURE
Definition infix.h:1434
@ INFIX_CODE_UNEXPECTED_TOKEN
Definition infix.h:1427
@ INFIX_CODE_MISSING_RETURN_TYPE
Definition infix.h:1430
@ INFIX_CODE_RECURSION_DEPTH_EXCEEDED
Definition infix.h:1432
@ INFIX_CODE_UNTERMINATED_AGGREGATE
Definition infix.h:1428
@ INFIX_CODE_NULL_POINTER
Definition infix.h:1416
@ INFIX_CODE_INVALID_KEYWORD
Definition infix.h:1429
@ INFIX_CODE_OUT_OF_MEMORY
Definition infix.h:1421
@ INFIX_CATEGORY_ALLOCATION
Definition infix.h:1405
@ INFIX_CATEGORY_GENERAL
Definition infix.h:1404
@ INFIX_CATEGORY_PARSER
Definition infix.h:1406
size_t source_offset
Definition infix.h:284
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:297
struct infix_type_t::@0::@7 vector_info
Metadata for INFIX_TYPE_VECTOR.
infix_type * type
Definition infix.h:352
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:1127
struct infix_type_t::@0::@4 func_ptr_info
Metadata for INFIX_TYPE_REVERSE_TRAMPOLINE.
size_t num_elements
Definition infix.h:302
infix_arena_t * arena
Definition infix.h:283
size_t size
Definition infix.h:279
size_t alignment
Definition infix.h:280
infix_struct_member * members
Definition infix.h:295
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:1068
const char * name
Definition infix.h:277
infix_function_argument * args
Definition infix.h:308
infix_status
Enumerates the possible status codes returned by infix API functions.
Definition infix.h:486
const char * name
Definition infix.h:351
const char * name
Definition infix.h:337
infix_type_category category
Definition infix.h:278
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:291
infix_type * type
Definition infix.h:338
struct infix_type_t * element_type
Definition infix.h:301
bool is_flexible
Definition infix.h:303
struct infix_type_t * return_type
Definition infix.h:307
struct infix_type_t::@0::@5 enum_info
Metadata for INFIX_TYPE_ENUM.
struct infix_type_t * base_type
Definition infix.h:318
uint8_t bit_width
Definition infix.h:341
size_t num_members
Definition infix.h:296
struct infix_type_t * underlying_type
Definition infix.h:314
struct infix_type_t::@0::@8 named_reference
Metadata for INFIX_TYPE_NAMED_REFERENCE.
size_t num_fixed_args
Definition infix.h:310
infix_primitive_type_id primitive_id
Metadata for INFIX_TYPE_PRIMITIVE.
Definition infix.h:288
size_t num_args
Definition infix.h:309
bool is_arena_allocated
Definition infix.h:281
@ INFIX_ERROR_ALLOCATION_FAILED
Definition infix.h:488
@ INFIX_SUCCESS
Definition infix.h:487
@ INFIX_ERROR_INVALID_ARGUMENT
Definition infix.h:489
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:2084
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:2037
infix_print_dialect_t
Specifies the output format for printing types and function signatures.
Definition infix.h:1227
@ INFIX_DIALECT_SIGNATURE
Definition infix.h:1228
@ INFIX_DIALECT_ITANIUM_MANGLING
Definition infix.h:1229
@ INFIX_DIALECT_MSVC_MANGLING
Definition infix.h:1230
INFIX_API INFIX_NODISCARD void * infix_arena_alloc(infix_arena_t *, size_t, size_t)
Allocates a block of memory from an arena.
Definition arena.c:117
#define infix_memcpy
A macro that can be defined to override the default memcpy function.
Definition infix.h:439
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:2244
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:399
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:208
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:725
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:548
infix_primitive_type_id
Enumerates the supported primitive C types.
Definition infix.h:245
INFIX_API INFIX_NODISCARD infix_type * infix_type_create_void(void)
Creates a static descriptor for the void type.
Definition types.c:200
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:618
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:219
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:788
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:144
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:579
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:666
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:513
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:469
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:430
@ INFIX_PRIMITIVE_UINT16
Definition infix.h:249
@ INFIX_PRIMITIVE_UINT32
Definition infix.h:251
@ INFIX_PRIMITIVE_LONG_DOUBLE
Definition infix.h:260
@ INFIX_PRIMITIVE_FLOAT
Definition infix.h:258
@ INFIX_PRIMITIVE_DOUBLE
Definition infix.h:259
@ INFIX_PRIMITIVE_SINT16
Definition infix.h:250
@ INFIX_PRIMITIVE_SINT64
Definition infix.h:254
@ INFIX_PRIMITIVE_SINT32
Definition infix.h:252
@ INFIX_PRIMITIVE_UINT8
Definition infix.h:247
@ INFIX_PRIMITIVE_UINT128
Definition infix.h:255
@ INFIX_PRIMITIVE_BOOL
Definition infix.h:246
@ INFIX_PRIMITIVE_UINT64
Definition infix.h:253
@ INFIX_PRIMITIVE_FLOAT16
Definition infix.h:257
@ INFIX_PRIMITIVE_SINT128
Definition infix.h:256
@ INFIX_PRIMITIVE_SINT8
Definition infix.h:248
@ INFIX_TYPE_UNION
Definition infix.h:233
@ INFIX_TYPE_PRIMITIVE
Definition infix.h:230
@ INFIX_TYPE_COMPLEX
Definition infix.h:237
@ INFIX_TYPE_ARRAY
Definition infix.h:234
@ INFIX_TYPE_VECTOR
Definition infix.h:238
@ INFIX_TYPE_VOID
Definition infix.h:240
@ INFIX_TYPE_POINTER
Definition infix.h:231
@ INFIX_TYPE_NAMED_REFERENCE
Definition infix.h:239
@ INFIX_TYPE_REVERSE_TRAMPOLINE
Definition infix.h:235
@ INFIX_TYPE_ENUM
Definition infix.h:236
@ INFIX_TYPE_STRUCT
Definition infix.h:232
@ INFIX_AGGREGATE_STRUCT
Definition infix.h:266
#define INFIX_API
Symbol visibility macro.
Definition infix.h:114
#define INFIX_INTERNAL
When compiling with -fvisibility=hidden, we use this to explicitly mark internal-but-shared functions...
Definition infix_config.h:232
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:428
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:799
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:1128
INFIX_INTERNAL void _infix_type_recalculate_layout(infix_type *type)
Recalculates the layout of a fully resolved type graph.
Definition types.c:940
INFIX_INTERNAL void _infix_clear_error(void)
Clears the thread-local error state.
Definition error.c:268
static bool is_float(const infix_type *type)
A fast inline check to determine if an infix_type is a float (32-bit).
Definition infix_internals.h:791
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:175
static void _print(printer_state *state, const char *fmt,...)
Definition signature.c:1226
static void _add_itanium_sub(printer_state *state, const void *component)
Definition signature.c:1275
static infix_struct_member * parse_aggregate_members(parser_state *state, char end_char, size_t *out_num_members)
Definition signature.c:253
static bool is_function_signature_ahead(const parser_state *state)
Definition signature.c:214
static void _infix_type_print_body_only_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1874
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:2014
INFIX_INTERNAL infix_type * parse_primitive(parser_state *state)
Definition signature.c:502
#define MAX_RECURSION_DEPTH
Definition signature.c:47
static bool _find_itanium_sub(printer_state *state, const void *component, size_t *index)
Definition signature.c:1265
INFIX_INTERNAL void skip_whitespace(parser_state *state)
Definition signature.c:72
static bool parse_size_t(parser_state *state, size_t *out_val)
Definition signature.c:90
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:1018
static void _print_itanium_sub(printer_state *state, size_t index)
Definition signature.c:1280
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:860
INFIX_INTERNAL void _infix_set_parser_error(parser_state *state, infix_error_code_t code)
Definition signature.c:62
static infix_type * _create_named_primitive(parser_state *state, infix_primitive_type_id id, const char *name)
Definition signature.c:474
static bool consume_keyword(parser_state *state, const char *keyword)
Definition signature.c:162
static infix_type * parse_packed_struct(parser_state *state)
Definition signature.c:422
#define PRINT_RECURSE(state, fn, arg)
Recursively prints a type with a depth guard.
Definition signature.c:1253
static infix_type * parse_aggregate(parser_state *state, char start_char, char end_char)
Definition signature.c:381
static const char * parse_identifier(parser_state *state)
Definition signature.c:128
INFIX_INTERNAL infix_type * parse_type(parser_state *state)
Definition signature.c:634
static void _infix_type_print_itanium_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1520
static void _infix_type_print_signature_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1318
static const char * parse_optional_name_prefix(parser_state *state)
Definition signature.c:184
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 _infix_type_print_msvc_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1678
A single entry in the registry's hash table.
Definition infix_internals.h:174
Internal definition of a memory arena.
Definition infix_internals.h:143
Describes a single argument to a C function.
Definition infix.h:350
Internal definition of a named type registry.
Definition infix_internals.h:188
_infix_registry_entry_t ** buckets
Definition infix_internals.h:193
size_t num_buckets
Definition infix_internals.h:191
Describes a single member of a C struct or union.
Definition infix.h:336
A semi-opaque structure that describes a C type.
Definition infix.h:276
Holds the complete state of the recursive descent parser during a single parse operation.
Definition infix_internals.h:199
const char * start
Definition infix_internals.h:201
infix_arena_t * arena
Definition infix_internals.h:202
int depth
Definition infix_internals.h:203
const char * p
Definition infix_internals.h:200
Definition signature.c:1206
size_t depth
Definition signature.c:1216
char * p
Definition signature.c:1207
size_t remaining
Definition signature.c:1208
infix_status status
Definition signature.c:1209
const void * itanium_subs[64]
Definition signature.c:1211
size_t msvc_type_count
Definition signature.c:1215
const infix_type * msvc_types[10]
Definition signature.c:1214
size_t itanium_sub_count
Definition signature.c:1212