infix
A JIT-Powered FFI Library for C
Loading...
Searching...
No Matches
signature.c
Go to the documentation of this file.
1
54#include <ctype.h>
55#include <stdarg.h>
56#include <stdbool.h>
57#include <stdio.h>
58#include <stdlib.h>
59#include <string.h>
60
69#define MAX_RECURSION_DEPTH 32
70
79typedef struct {
80 const char * p;
81 const char * start;
84 int depth;
86
87// Forward declarations for the recursive parsing functions.
90 parser_state *, infix_type **, infix_function_argument **, size_t *, size_t *);
91static infix_type * parse_aggregate(parser_state *, char, char);
96
107 // The error state is thread-local, so this is safe to call from any context.
108 _infix_set_error(INFIX_CATEGORY_PARSER, code, (size_t)(state->p - state->start));
109}
110
119static void skip_whitespace(parser_state * state) {
120 while (true) {
121 // First, consume all standard whitespace characters (space, tab, newline, etc.).
122 while (isspace((unsigned char)*state->p))
123 state->p++;
124
125 // Next, check for the start of a line comment.
126 if (*state->p == '#') {
127 // If found, consume all characters until the next newline or the end of the string.
128 while (*state->p != '\n' && *state->p != '\0')
129 state->p++;
130 }
131 else // If neither whitespace nor a comment is found, we're at a significant token.
132 break;
133 }
134}
135
145static bool parse_size_t(parser_state * state, size_t * out_val) {
146 const char * start = state->p;
147 char * end;
148 // Use strtoull for battle-tested parsing of 64-bit unsigned integers.
149 unsigned long long val = strtoull(start, &end, 10);
150 // If the end pointer hasn't moved, it means no digits were consumed.
151 if (end == start) {
153 return false;
154 }
155 *out_val = (size_t)val;
156 state->p = end; // Advance the parser's pointer past the consumed number.
157 return true;
158}
159
169static const char * parse_identifier(parser_state * state) {
170 skip_whitespace(state);
171 const char * start = state->p;
172 // An identifier must start with an alphabetic character or an underscore.
173 if (!isalpha((unsigned char)*start) && *start != '_')
174 return nullptr;
175 // Consume a valid identifier sequence.
176 while (isalnum((unsigned char)*state->p) || *state->p == '_' || *state->p == ':') {
177 // Special handling for the '::' namespace separator. A single ':' is not part of a valid identifier.
178 if (*state->p == ':' && state->p[1] != ':')
179 break;
180 if (*state->p == ':')
181 state->p++; // Consume the first ':' of '::'
182 state->p++;
183 }
184 size_t len = state->p - start;
185 if (len == 0)
186 return nullptr;
187 // Allocate space for the identifier in the arena and copy it. This makes the
188 // resulting type graph self-contained, with no pointers back to the original string.
189 char * name = infix_arena_alloc(state->arena, len + 1, 1);
190 if (!name) {
192 return nullptr;
193 }
194 infix_memcpy(name, start, len);
195 name[len] = '\0';
196 return name;
197}
198
208static bool consume_keyword(parser_state * state, const char * keyword) {
209 skip_whitespace(state);
210 size_t len = strlen(keyword);
211 if (strncmp(state->p, keyword, len) == 0) {
212 // This is the "whole word" check. The character immediately after the keyword
213 // cannot be alphanumeric or '_', which would imply it's part of a larger word.
214 if (isalnum((unsigned char)state->p[len]) || state->p[len] == '_')
215 return false;
216 // The keyword matched. Advance the pointer and return success.
217 state->p += len;
218 skip_whitespace(state);
219 return true;
220 }
221 return false;
222}
223
234static const char * parse_optional_name_prefix(parser_state * state) {
235 skip_whitespace(state);
236 // Save the current position in case we need to backtrack.
237 const char * p_before = state->p;
238 const char * name = parse_identifier(state);
239 if (name) {
240 skip_whitespace(state);
241 if (*state->p == ':') {
242 // Found "identifier:", so consume the colon and return the name.
243 state->p++;
244 return name;
245 }
246 }
247 // If we get here, it wasn't a "name:" pattern (e.g., it was just a type name).
248 // Backtrack the parser state to before the identifier was consumed.
249 state->p = p_before;
250 return nullptr;
251}
252
262static bool is_function_signature_ahead(const parser_state * state) {
263 const char * p = state->p;
264 // The ambiguity only exists if we start with '('.
265 if (*p != '(')
266 return false;
267 p++;
268 // Find the matching closing parenthesis, respecting nested parentheses.
269 int depth = 1;
270 while (*p != '\0' && depth > 0) {
271 if (*p == '(')
272 depth++;
273 else if (*p == ')')
274 depth--;
275 p++;
276 }
277 // If we didn't find a matching parenthesis, it's malformed.
278 if (depth != 0)
279 return false; // Malformed, not a function.
280 // Skip any whitespace or comments between the ')' and the next token.
281 while (isspace((unsigned char)*p) || *p == '#') {
282 if (*p == '#') {
283 while (*p != '\n' && *p != '\0')
284 p++;
285 }
286 else
287 p++;
288 }
289 // The construct is a function signature if and only if the next token is `->`.
290 return (p[0] == '-' && p[1] == '>');
291}
292
308static infix_struct_member * parse_aggregate_members(parser_state * state, char end_char, size_t * out_num_members) {
309 // A temporary node for building the linked list of members on the arena.
310 typedef struct member_node {
312 struct member_node * next;
313 } member_node;
314 member_node *head = nullptr, *tail = nullptr;
315 size_t num_members = 0;
316
317 skip_whitespace(state);
318 if (*state->p != end_char) { // Handle non-empty aggregates
319 while (1) {
320 // Parse one member
321 const char * p_before_member = state->p;
322 const char * name = parse_optional_name_prefix(state);
323 // Check for a subtle syntax error: `name,` or `name}` with no type.
324 if (name && (*state->p == ',' || *state->p == end_char)) {
325 // Backtrack to the end of the name for a more accurate error position.
326 state->p = p_before_member + strlen(name);
328 return nullptr;
329 }
330 infix_type * member_type = parse_type(state);
331 if (!member_type)
332 return nullptr; // Propagate error from sub-parser.
333 if (member_type->category == INFIX_TYPE_VOID) { // C forbids members of type void.
335 return nullptr;
336 }
337 // Add to linked list
338 member_node * node = infix_arena_alloc(state->arena, sizeof(member_node), _Alignof(member_node));
339 if (!node) {
341 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
342 return nullptr;
343 }
344 // Member offset is calculated later by the create_struct/union function.
345 node->m = infix_type_create_member(name, member_type, 0);
346 node->next = nullptr;
347 if (!head)
348 head = tail = node;
349 else {
350 tail->next = node;
351 tail = node;
352 }
353 num_members++;
354 // Check for next token: ',' or end_char
355 skip_whitespace(state);
356 if (*state->p == ',') {
357 state->p++; // Consume comma.
358 skip_whitespace(state);
359 // A trailing comma like `{int,}` is a syntax error.
360 if (*state->p == end_char) {
362 return nullptr;
363 }
364 }
365 else if (*state->p == end_char) // End of the list.
366 break;
367 else { // Anything else (e.g., another identifier) is a syntax error.
368 if (*state->p == '\0') // Check for unterminated string
369 return nullptr;
371 return nullptr;
372 }
373 }
374 }
375 *out_num_members = num_members;
376 if (num_members == 0)
377 return nullptr; // Return null for empty aggregates, but without an error.
378 // Pass 2: Convert the linked list to a contiguous array for the final `infix_type`.
380 infix_arena_alloc(state->arena, sizeof(infix_struct_member) * num_members, _Alignof(infix_struct_member));
381 if (!members) {
383 return nullptr;
384 }
385 member_node * current = head;
386 for (size_t i = 0; i < num_members; i++) {
387 members[i] = current->m;
388 current = current->next;
389 }
390 return members;
391}
392
404 // Default alignment for `!{...}` is 1.
405 size_t alignment = 1;
406 if (*state->p == '!') {
407 state->p++;
408 // Check for an optional alignment specifier like `!4:...`
409 if (isdigit((unsigned char)*state->p)) {
410 if (!parse_size_t(state, &alignment))
411 return nullptr;
412 if (*state->p != ':') {
414 return nullptr;
415 }
416 state->p++; // Consume ':'
417 }
418 }
419 skip_whitespace(state);
420 if (*state->p != '{') {
422 return nullptr;
423 }
424 state->p++; // Consume '{'
425 size_t num_members = 0;
426 infix_struct_member * members = parse_aggregate_members(state, '}', &num_members);
428 return nullptr; // Propagate error.
429 }
430 if (*state->p != '}') {
432 return nullptr;
433 }
434 state->p++; // Consume '}'
435 infix_type * packed_type = nullptr;
436 // For packed structs, we sum the member sizes as a simple heuristic for total size.
437 // The user can override this with a more precise manual API call if needed.
438 size_t total_size = 0;
439 for (size_t i = 0; i < num_members; ++i)
440 total_size += members[i].type->size;
442 infix_type_create_packed_struct(state->arena, &packed_type, total_size, alignment, members, num_members);
443 if (status != INFIX_SUCCESS) {
446 (size_t)(state->p - state->start));
447 return nullptr;
448 }
449 return packed_type;
450}
451
459 state->p++; // Consume 'v'
460 skip_whitespace(state);
461 if (*state->p != '[') {
463 return nullptr;
464 }
465 state->p++; // Consume '['
466 skip_whitespace(state);
467 size_t num_elements;
468 if (!parse_size_t(state, &num_elements))
469 return nullptr;
470 skip_whitespace(state);
471 if (*state->p != ':') {
473 return nullptr;
474 }
475 state->p++; // Consume ':'
476 skip_whitespace(state);
477 infix_type * element_type = parse_type(state);
478 // Vector elements must be primitives.
479 if (!element_type || element_type->category != INFIX_TYPE_PRIMITIVE) {
481 return nullptr;
482 }
483 skip_whitespace(state);
484 if (*state->p != ']') {
486 return nullptr;
487 }
488 state->p++; // Consume ']'
489 infix_type * vector_type = nullptr;
490 if (infix_type_create_vector(state->arena, &vector_type, element_type, num_elements) != INFIX_SUCCESS) {
492 return nullptr;
493 }
494 return vector_type;
495}
496
507 infix_type * ret_type = nullptr;
508 infix_function_argument * args = nullptr;
509 size_t num_args = 0, num_fixed = 0;
510
511 // Delegate the heavy lifting of parsing the argument list, arrow, and return type.
512 if (parse_function_signature_details(state, &ret_type, &args, &num_args, &num_fixed) != INFIX_SUCCESS)
513 return nullptr;
514 // Manually construct an `infix_type` of category INFIX_TYPE_REVERSE_TRAMPOLINE.
515 infix_type * func_type = infix_arena_alloc(state->arena, sizeof(infix_type), _Alignof(infix_type));
516 if (!func_type) {
518 return nullptr;
519 }
520 // A function type itself is treated like a pointer for size/alignment purposes.
521 *func_type = *infix_type_create_pointer();
522 func_type->is_arena_allocated = true;
524
525 // Populate the metadata with the parsed components.
527 func_type->meta.func_ptr_info.args = args;
528 func_type->meta.func_ptr_info.num_args = num_args;
529 func_type->meta.func_ptr_info.num_fixed_args = num_fixed;
530 return func_type;
531}
532
542static infix_type * parse_aggregate(parser_state * state, char start_char, char end_char) {
543 if (state->depth >= MAX_RECURSION_DEPTH) {
545 return nullptr;
546 }
547 state->depth++;
548 if (*state->p != start_char) {
550 state->depth--;
551 return nullptr;
552 }
553 state->p++; // Consume start_char
554 size_t num_members = 0;
555 infix_struct_member * members = parse_aggregate_members(state, end_char, &num_members);
556 // Check if member parsing failed with a specific error.
558 state->depth--;
559 return nullptr;
560 }
561 if (*state->p != end_char) {
563 state->depth--;
564 return nullptr;
565 }
566 state->p++; // Consume end_char
567 infix_type * agg_type = nullptr;
568 // Dispatch to the correct factory function based on the delimiter.
569 infix_status status = (start_char == '{') ? infix_type_create_struct(state->arena, &agg_type, members, num_members)
570 : infix_type_create_union(state->arena, &agg_type, members, num_members);
571 if (status != INFIX_SUCCESS) {
574 (size_t)(state->p - state->start));
575 state->depth--;
576 return nullptr;
577 }
578 state->depth--;
579 return agg_type;
580}
591 if (consume_keyword(state, "int8"))
593 if (consume_keyword(state, "uint8"))
595 if (consume_keyword(state, "int16"))
597 if (consume_keyword(state, "uint16"))
599 if (consume_keyword(state, "int32"))
601 if (consume_keyword(state, "uint32"))
603 if (consume_keyword(state, "int64"))
605 if (consume_keyword(state, "uint64"))
607 if (consume_keyword(state, "int128"))
609 if (consume_keyword(state, "uint128"))
611 if (consume_keyword(state, "float32"))
613 if (consume_keyword(state, "float64"))
615 if (consume_keyword(state, "float80"))
617 if (consume_keyword(state, "float128"))
619 if (consume_keyword(state, "void"))
620 return infix_type_create_void();
621 if (consume_keyword(state, "uchar"))
623 if (consume_keyword(state, "char"))
625 if (consume_keyword(state, "ushort"))
627 if (consume_keyword(state, "short"))
629 if (consume_keyword(state, "uint"))
631 if (consume_keyword(state, "int"))
633 if (consume_keyword(state, "ulonglong"))
635 if (consume_keyword(state, "longlong"))
637 if (consume_keyword(state, "ulong"))
638 return infix_type_create_primitive(sizeof(unsigned long) == 8 ? INFIX_PRIMITIVE_UINT64
640 if (consume_keyword(state, "long"))
642 if (consume_keyword(state, "double"))
644 if (consume_keyword(state, "float"))
646 return nullptr;
647}
648
663 infix_type ** out_ret_type,
664 infix_function_argument ** out_args,
665 size_t * out_num_args,
666 size_t * out_num_fixed_args) {
667 if (*state->p != '(') {
670 }
671 state->p++;
672 skip_whitespace(state);
673 // Use the same two-pass, linked-list approach as parse_aggregate_members for arguments.
674 typedef struct arg_node {
676 struct arg_node * next;
677 } arg_node;
678 arg_node *head = nullptr, *tail = nullptr;
679 size_t num_args = 0;
680 // Pass 1: Parse fixed arguments (before the ';')
681 if (*state->p != ')') {
682 while (1) {
683 skip_whitespace(state);
684 // Stop if we hit the end of the argument list or the variadic separator.
685 if (*state->p == ')' || *state->p == ';')
686 break;
687 const char * name = parse_optional_name_prefix(state);
688 infix_type * arg_type = parse_type(state);
689 if (!arg_type)
691 // Add the parsed argument to our linked list.
692 arg_node * node = infix_arena_alloc(state->arena, sizeof(arg_node), _Alignof(arg_node));
693 if (!node) {
695 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
697 }
698 node->arg.type = arg_type;
699 node->arg.name = name;
700 node->next = nullptr;
701 if (!head)
702 head = tail = node;
703 else {
704 tail->next = node;
705 tail = node;
706 }
707 num_args++;
708 skip_whitespace(state);
709 if (*state->p == ',') {
710 state->p++;
711 skip_whitespace(state);
712 // Handle trailing comma error before ')' or ';'.
713 if (*state->p == ')' || *state->p == ';') {
716 }
717 }
718 else if (*state->p != ')' && *state->p != ';') {
721 }
722 else
723 break;
724 }
725 }
726 *out_num_fixed_args = num_args;
727 // Pass 2: Parse variadic arguments (after the ';')
728 if (*state->p == ';') {
729 state->p++;
730 // The loop is nearly identical to the one for fixed args.
731 if (*state->p != ')') {
732 while (1) {
733 skip_whitespace(state);
734 if (*state->p == ')')
735 break;
736 const char * name = parse_optional_name_prefix(state);
737 infix_type * arg_type = parse_type(state);
738 if (!arg_type)
740 arg_node * node = infix_arena_alloc(state->arena, sizeof(arg_node), _Alignof(arg_node));
741 if (!node) {
743 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
745 }
746 node->arg.type = arg_type;
747 node->arg.name = name;
748 node->next = nullptr;
749 if (!head)
750 head = tail = node;
751 else {
752 tail->next = node;
753 tail = node;
754 }
755 num_args++; // Increment the *total* number of arguments.
756 skip_whitespace(state);
757 if (*state->p == ',') {
758 state->p++;
759 skip_whitespace(state);
760 if (*state->p == ')') {
763 }
764 }
765 else if (*state->p != ')') {
768 }
769 else
770 break;
771 }
772 }
773 }
774 // Finalize and Parse Return Type
775 skip_whitespace(state);
776 if (*state->p != ')') {
779 }
780 state->p++; // Consume ')'
781 skip_whitespace(state);
782 if (state->p[0] != '-' || state->p[1] != '>') {
785 }
786 state->p += 2; // Consume '->'
787 *out_ret_type = parse_type(state);
788 if (!*out_ret_type)
790 // Convert linked list to array
791 infix_function_argument * args = (num_args > 0)
792 ? infix_arena_alloc(state->arena, sizeof(infix_function_argument) * num_args, _Alignof(infix_function_argument))
793 : nullptr;
794 if (num_args > 0 && !args) {
797 }
798 arg_node * current = head;
799 for (size_t i = 0; i < num_args; i++) {
800 args[i] = current->arg;
801 current = current->next;
802 }
803 *out_args = args;
804 *out_num_args = num_args;
805 return INFIX_SUCCESS;
806}
807
820 // Prevent stack overflow from malicious, deeply nested input.
821 if (state->depth >= MAX_RECURSION_DEPTH) {
823 return nullptr;
824 }
825 state->depth++;
826 skip_whitespace(state);
827 infix_type * result_type = nullptr;
828 const char * p_before_type = state->p; // For error reporting on unknown tokens.
829
830 // Dispatch based on the current token.
831 if (*state->p == '@') {
832 // Handle a named type reference like `@Point`.
833 if (state->registry == nullptr) {
834 set_parser_error(state, INFIX_CODE_UNEXPECTED_TOKEN); // Can't use @ without a registry.
835 state->depth--;
836 return nullptr;
837 }
838 state->p++; // Consume '@'.
839 const char * name = parse_identifier(state);
840 if (!name) {
842 state->depth--;
843 return nullptr;
844 }
845 // Create a placeholder. This will be replaced later by the resolver.
846 if (infix_type_create_named_reference(state->arena, &result_type, name, INFIX_AGGREGATE_STRUCT) !=
849 result_type = nullptr;
850 }
851 }
852 else if (*state->p == '*') {
853 // Handle a pointer type.
854 state->p++;
855 skip_whitespace(state);
856 // Recursively call parse_type to get the pointee's type.
857 infix_type * pointee_type = parse_type(state);
858 if (!pointee_type) {
859 state->depth--;
860 return nullptr;
861 }
862 if (infix_type_create_pointer_to(state->arena, &result_type, pointee_type) != INFIX_SUCCESS) {
865 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
866 result_type = nullptr;
867 }
868 }
869 else if (*state->p == '(') {
870 // Ambiguous token: could be a function type or a grouped type.
872 result_type = parse_function_type(state);
873 else {
874 // It's a grouped type, e.g., `*( (int)->void )`.
875 state->p++; // Consume '('.
876 skip_whitespace(state);
877 result_type = parse_type(state); // Parse the inner type.
878 if (!result_type) {
879 state->depth--;
880 return nullptr;
881 }
882 skip_whitespace(state);
883 if (*state->p != ')') {
885 result_type = nullptr;
886 }
887 else
888 state->p++; // Consume ')'.
889 }
890 }
891 else if (*state->p == '[') {
892 // Handle an array type.
893 state->p++;
894 skip_whitespace(state);
895 size_t num_elements;
896 if (!parse_size_t(state, &num_elements)) {
897 state->depth--;
898 return nullptr;
899 }
900 skip_whitespace(state);
901 if (*state->p != ':') {
903 state->depth--;
904 return nullptr;
905 }
906 state->p++;
907 skip_whitespace(state);
908 infix_type * element_type = parse_type(state);
909 if (!element_type) {
910 state->depth--;
911 return nullptr;
912 }
913 // C forbids arrays of `void`.
914 if (element_type->category == INFIX_TYPE_VOID) {
916 state->depth--;
917 return nullptr;
918 }
919 skip_whitespace(state);
920 if (*state->p != ']') {
922 state->depth--;
923 return nullptr;
924 }
925 state->p++;
926 if (infix_type_create_array(state->arena, &result_type, element_type, num_elements) != INFIX_SUCCESS) {
929 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
930 result_type = nullptr;
931 }
932 }
933 else if (*state->p == '!')
934 result_type = parse_packed_struct(state);
935 else if (*state->p == '{')
936 result_type = parse_aggregate(state, '{', '}');
937 else if (*state->p == '<')
938 result_type = parse_aggregate(state, '<', '>');
939 else if (*state->p == 'e') {
940 // Handle an enum type.
941 state->p++;
942 skip_whitespace(state);
943 if (*state->p == '<') { // Old syntax `e<...>` is no longer supported.
945 state->depth--;
946 return nullptr;
947 }
948 if (*state->p != ':') {
950 state->depth--;
951 return nullptr;
952 }
953 state->p++;
954 skip_whitespace(state);
955 // An enum must have an explicit underlying integer type.
956 infix_type * underlying_type = parse_type(state);
957 if (!underlying_type) {
958 state->depth--;
959 return nullptr;
960 }
961 if (underlying_type->category != INFIX_TYPE_PRIMITIVE) {
962 set_parser_error(state, INFIX_CODE_UNEXPECTED_TOKEN); // Must be based on a primitive.
963 state->depth--;
964 return nullptr;
965 }
966 if (infix_type_create_enum(state->arena, &result_type, underlying_type) != INFIX_SUCCESS) {
969 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
970 result_type = nullptr;
971 }
972 }
973 else if (*state->p == 'c' && state->p[1] == '[') {
974 // Handle a _Complex type.
975 state->p++;
976 skip_whitespace(state);
977 if (*state->p != '[') {
979 state->depth--;
980 return nullptr;
981 }
982 state->p++;
983 skip_whitespace(state);
984 infix_type * base_type = parse_type(state);
985 if (!base_type) {
986 state->depth--;
987 return nullptr;
988 }
989 skip_whitespace(state);
990 if (*state->p != ']') {
992 state->depth--;
993 return nullptr;
994 }
995 state->p++;
996 if (infix_type_create_complex(state->arena, &result_type, base_type) != INFIX_SUCCESS) {
999 INFIX_CATEGORY_ALLOCATION, INFIX_CODE_OUT_OF_MEMORY, (size_t)(state->p - state->start));
1000 result_type = nullptr;
1001 }
1002 }
1003 else if (*state->p == 'v' && state->p[1] == '[')
1004 // VECTOR TYPE: v[<N>:<type>]
1005 result_type = parse_vector_type(state);
1006 else {
1007 // PRIMITIVE TYPE
1008 // If no other constructor token matches, it must be a primitive type.
1009 result_type = parse_primitive(state);
1010 if (!result_type) {
1011 // If primitive parsing failed and no error was set, it means we have
1012 // an unknown token. Set the error details now.
1014 state->p = p_before_type;
1015 if (isalpha((unsigned char)*state->p) || *state->p == '_')
1017 else
1019 }
1020 }
1021 }
1022 state->depth--;
1023 return result_type;
1024}
1025
1026// Public API Implementation & Internal Parser
1041 infix_arena_t ** out_arena,
1042 const char * signature,
1043 infix_registry_t * registry) {
1044 if (!out_type || !out_arena || !signature || *signature == '\0') {
1047 }
1048
1049 *out_arena = infix_arena_create(4096);
1050 if (!*out_arena) {
1053 }
1054
1055 parser_state state = {.p = signature, .start = signature, .arena = *out_arena, .registry = registry, .depth = 0};
1056 infix_type * type = parse_type(&state);
1057
1058 if (type) {
1059 // After parsing, check if we consumed the entire string.
1060 // If not, there's trailing garbage, which is a syntax error.
1061 skip_whitespace(&state);
1062 if (state.p[0] != '\0') {
1064 type = nullptr;
1065 }
1066 }
1067
1068 // If parsing failed at any point, `type` will be null.
1069 if (!type) {
1070 // Clean up the arena we created, as the caller will not receive it.
1071 infix_arena_destroy(*out_arena);
1072 *out_arena = nullptr;
1073 *out_type = nullptr;
1075 }
1076
1077 *out_type = type;
1078 return INFIX_SUCCESS;
1079}
1080
1091 infix_arena_t ** out_arena,
1092 const char * signature,
1093 infix_registry_t * registry) {
1095
1096 // Step 1: Parse the string into a (potentially unresolved) type graph.
1097 infix_status status = _infix_parse_type_internal(out_type, out_arena, signature, registry);
1098 if (status != INFIX_SUCCESS) {
1099 return status;
1100 }
1101
1102 // Step 2: If a registry was used, resolve all @Name placeholders.
1103 if (registry) {
1104 if (_infix_resolve_type_graph(out_type, registry) != INFIX_SUCCESS) {
1105 // If resolution fails (e.g., name not found), destroy the partially
1106 // created resources and return an error.
1107 infix_arena_destroy(*out_arena);
1108 *out_arena = nullptr;
1109 *out_type = nullptr;
1111 }
1112 }
1113
1114 return INFIX_SUCCESS;
1115}
1116
1124 infix_arena_t ** out_arena,
1125 infix_type ** out_ret_type,
1126 infix_function_argument ** out_args,
1127 size_t * out_num_args,
1128 size_t * out_num_fixed_args,
1129 infix_registry_t * registry) {
1131
1132 if (!signature || !out_arena || !out_ret_type || !out_args || !out_num_args || !out_num_fixed_args) {
1135 }
1136
1137 *out_arena = infix_arena_create(8192);
1138 if (!*out_arena) {
1141 }
1142
1143 parser_state state = {.p = signature, .start = signature, .arena = *out_arena, .registry = registry, .depth = 0};
1144
1145 // This is the main entry point for parsing a full function signature.
1147 parse_function_signature_details(&state, out_ret_type, out_args, out_num_args, out_num_fixed_args);
1148
1149 // Check for trailing garbage data after a valid signature.
1150 if (status == INFIX_SUCCESS) {
1151 skip_whitespace(&state);
1152 if (state.p[0] != '\0') {
1155 }
1156 }
1157
1158 // If parsing succeeded and a registry was used, resolve all named types.
1159 if (status == INFIX_SUCCESS && registry) {
1160 // Resolve the return type.
1161 if (_infix_resolve_type_graph(out_ret_type, registry) != INFIX_SUCCESS) {
1163 }
1164 else {
1165 // Resolve each argument type.
1166 for (size_t i = 0; i < *out_num_args; ++i) {
1167 if (_infix_resolve_type_graph(&(*out_args)[i].type, registry) != INFIX_SUCCESS) {
1169 break;
1170 }
1171 }
1172 }
1173 }
1174
1175 // If anything failed, clean up and return an error.
1176 if (status != INFIX_SUCCESS) {
1177 infix_arena_destroy(*out_arena);
1178 *out_arena = nullptr;
1180 }
1181
1182 return INFIX_SUCCESS;
1183}
1184
1189typedef struct {
1190 char * p;
1191 size_t remaining;
1194
1199static void _print(printer_state * state, const char * fmt, ...) {
1200 if (state->status != INFIX_SUCCESS)
1201 return;
1202 va_list args;
1203 va_start(args, fmt);
1204 int written = vsnprintf(state->p, state->remaining, fmt, args);
1205 va_end(args);
1206 // If vsnprintf failed or indicated truncation, set an error.
1207 if (written < 0 || (size_t)written >= state->remaining)
1209 else {
1210 state->p += written;
1211 state->remaining -= written;
1212 }
1213}
1214
1224 if (state->status != INFIX_SUCCESS || !type) {
1225 if (state->status == INFIX_SUCCESS)
1227 return;
1228 }
1229 // Dispatch to the correct printing logic based on the type category.
1230 switch (type->category) {
1231 case INFIX_TYPE_VOID:
1232 _print(state, "void");
1233 break;
1235 _print(state, "@%s", type->meta.named_reference.name);
1236 break; // Print @Name for unresolved references
1237 case INFIX_TYPE_POINTER:
1238 _print(state, "*");
1239 // Handle special cases for `void*` or recursive pointer types to avoid infinite loops.
1240 if (type->meta.pointer_info.pointee_type == type || type->meta.pointer_info.pointee_type == nullptr ||
1242 _print(state, "void");
1243 else
1245 break;
1246 case INFIX_TYPE_ARRAY:
1247 _print(state, "[%zu:", type->meta.array_info.num_elements);
1249 _print(state, "]");
1250 break;
1251 case INFIX_TYPE_STRUCT:
1252 // If a struct has a registered name, prefer printing that for conciseness.
1253 if (type->meta.aggregate_info.name)
1254 _print(state, "@%s", type->meta.aggregate_info.name);
1255 else {
1256 _print(state, "{");
1257 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1258 if (i > 0)
1259 _print(state, ",");
1261 }
1262 _print(state, "}");
1263 }
1264 break;
1265 case INFIX_TYPE_UNION:
1266 if (type->meta.aggregate_info.name)
1267 _print(state, "@%s", type->meta.aggregate_info.name);
1268 else {
1269 _print(state, "<");
1270 for (size_t i = 0; i < type->meta.aggregate_info.num_members; ++i) {
1271 if (i > 0)
1272 _print(state, ",");
1274 }
1275 _print(state, ">");
1276 }
1277 break;
1279 _print(state, "(");
1280 // Print fixed arguments, separated by commas.
1281 for (size_t i = 0; i < type->meta.func_ptr_info.num_fixed_args; ++i) {
1282 if (i > 0)
1283 _print(state, ",");
1285 }
1286
1287 // The parser does not distinguish between "(int)->void" and "(int;)->void".
1288 // The presence of variadic args is determined by num_args > num_fixed_args.
1289 bool is_variadic = type->meta.func_ptr_info.num_args > type->meta.func_ptr_info.num_fixed_args;
1290 if (is_variadic) {
1291 _print(state, ";");
1292 // Print variadic arguments, separated by commas.
1293 for (size_t i = type->meta.func_ptr_info.num_fixed_args; i < type->meta.func_ptr_info.num_args; ++i) {
1294 // Add a comma only if it's not the very first variadic argument.
1295 if (i > type->meta.func_ptr_info.num_fixed_args)
1296 _print(state, ",");
1298 }
1299 }
1300 _print(state, ")->");
1302 break;
1303 case INFIX_TYPE_ENUM:
1304 _print(state, "e:");
1306 break;
1307 case INFIX_TYPE_COMPLEX:
1308 _print(state, "c[");
1310 _print(state, "]");
1311 break;
1312 case INFIX_TYPE_VECTOR:
1313 _print(state, "v[%zu:", type->meta.vector_info.num_elements);
1315 _print(state, "]");
1316 break;
1318 // Map the internal primitive ID back to a canonical keyword.
1319 switch (type->meta.primitive_id) {
1321 _print(state, "bool");
1322 break;
1324 _print(state, "char");
1325 break;
1327 _print(state, "uchar");
1328 break;
1330 _print(state, "short");
1331 break;
1333 _print(state, "ushort");
1334 break;
1336 _print(state, "int");
1337 break;
1339 _print(state, "uint");
1340 break;
1342 _print(state, "longlong");
1343 break;
1345 _print(state, "ulonglong");
1346 break;
1348 _print(state, "float");
1349 break;
1351 _print(state, "double");
1352 break;
1354 _print(state, "longdouble");
1355 break;
1356 default:
1358 break;
1359 }
1360 break;
1361 default:
1363 break;
1364 }
1365}
1366
1371 size_t buffer_size,
1372 const infix_type * type,
1373 infix_print_dialect_t dialect) {
1375 if (!buffer || buffer_size == 0 || !type) {
1378 }
1379 printer_state state = {buffer, buffer_size, INFIX_SUCCESS};
1380 *buffer = '\0'; // Ensure buffer is empty initially
1381
1382 switch (dialect) {
1385 break;
1388 // Not yet implemented
1389 _print(&state, "mangling_not_implemented");
1390 break;
1391 default:
1394 }
1395 // Check for buffer overflow and ensure null termination.
1396 if (state.status == INFIX_SUCCESS) {
1397 if (state.remaining > 0)
1398 *state.p = '\0'; // Null-terminate the string
1399 else {
1400 // Buffer was too small, but vsnprintf might not have returned an error.
1401 // Ensure the last character is null to prevent overflow.
1402 buffer[buffer_size - 1] = '\0'; // Truncate
1404 return INFIX_ERROR_INVALID_ARGUMENT; // Indicate buffer was too small
1405 }
1406 }
1407 else if (buffer_size > 0)
1408 buffer[buffer_size - 1] = '\0'; // Ensure null termination on error too.
1409 return state.status;
1410}
1411
1416 size_t buffer_size,
1417 const char * function_name,
1418 const infix_type * ret_type,
1420 size_t num_args,
1421 size_t num_fixed_args,
1422 infix_print_dialect_t dialect) {
1424 if (!buffer || buffer_size == 0 || !ret_type || (num_args > 0 && !args)) {
1427 }
1428 printer_state state = {buffer, buffer_size, INFIX_SUCCESS};
1429 *buffer = '\0';
1430 (void)function_name; // function_name is currently unused, for future mangling support.
1431 switch (dialect) {
1433 _print(&state, "(");
1434 for (size_t i = 0; i < num_fixed_args; ++i) {
1435 if (i > 0)
1436 _print(&state, ",");
1438 }
1439 if (num_args > num_fixed_args) {
1440 _print(&state, ";");
1441 for (size_t i = num_fixed_args; i < num_args; ++i) {
1442 if (i > num_fixed_args)
1443 _print(&state, ",");
1445 }
1446 }
1447 _print(&state, ")->");
1449 break;
1450 default:
1451 _print(&state, "unsupported_dialect");
1452 break;
1453 }
1454 // Final buffer state check and null termination.
1455 if (state.status == INFIX_SUCCESS) {
1456 if (state.remaining > 0)
1457 *state.p = '\0';
1458 else {
1459 if (buffer_size > 0)
1460 buffer[buffer_size - 1] = '\0';
1463 }
1464 }
1465 else if (buffer_size > 0)
1466 buffer[buffer_size - 1] = '\0';
1467 return state.status;
1468}
infix_status status
Definition 103_unions.c:74
infix_struct_member * members
Definition 103_unions.c:68
void * args[]
Definition 202_in_structs.c:75
clock_t start
Definition 901_call_overhead.c:58
infix_type * ret_type
Definition 901_call_overhead.c:75
clock_t end
Definition 901_call_overhead.c:58
#define c23_nodiscard
Definition compat_c23.h:93
infix_error_code_t
Specific error codes providing detailed information about a failure.
Definition infix.h:1055
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:87
@ INFIX_CODE_SUCCESS
The operation completed successfully.
Definition infix.h:1057
@ INFIX_CODE_UNEXPECTED_TOKEN
Parser ran into an invalid character at a given position.
Definition infix.h:1066
@ INFIX_CODE_MISSING_RETURN_TYPE
A function signature was missing the -> or a return type.
Definition infix.h:1069
@ INFIX_CODE_UNKNOWN
An unknown or unspecified error occurred. This is a fallback code.
Definition infix.h:1058
@ INFIX_CODE_RECURSION_DEPTH_EXCEEDED
The parser exceeded the max nesting depth (e.g., {{{{...}}}}).
Definition infix.h:1072
@ INFIX_CODE_UNTERMINATED_AGGREGATE
A {...}, <...>, or [...] was not properly closed.
Definition infix.h:1067
@ INFIX_CODE_INVALID_KEYWORD
Parser found an unknown keyword (e.g., "integer" instead of "int").
Definition infix.h:1068
@ INFIX_CODE_OUT_OF_MEMORY
Failure to allocate memory. Likely due to lack of system resources.
Definition infix.h:1061
@ INFIX_CATEGORY_ALLOCATION
An error related to memory allocation.
Definition infix.h:1042
@ INFIX_CATEGORY_GENERAL
A general or miscellaneous error.
Definition infix.h:1041
@ INFIX_CATEGORY_PARSER
An error that occurred while parsing a signature string.
Definition infix.h:1043
c23_nodiscard infix_status infix_type_from_signature(infix_type **out_type, infix_arena_t **out_arena, const char *signature, infix_registry_t *registry)
Implementation of the public infix_type_from_signature API function.
Definition signature.c:1090
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)
Implementation of the public infix_signature_parse API function.
Definition signature.c:1123
infix_status infix_type_print(char *buffer, size_t buffer_size, const infix_type *type, infix_print_dialect_t dialect)
Implementation of the public infix_type_print API function.
Definition signature.c:1370
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)
Implementation of the public infix_function_print API function.
Definition signature.c:1415
infix_print_dialect_t
Specifies the output format for type-to-string serialization functions.
Definition infix.h:814
@ INFIX_DIALECT_SIGNATURE
The standard infix signature language format.
Definition infix.h:815
@ INFIX_DIALECT_ITANIUM_MANGLING
Itanium C++ ABI name mangling (used by GCC/Clang). (Not yet implemented)
Definition infix.h:816
@ INFIX_DIALECT_MSVC_MANGLING
Microsoft C++ ABI name mangling. (Not yet implemented)
Definition infix.h:817
void infix_arena_destroy(infix_arena_t *)
Frees an entire memory arena and all objects allocated within it.
Definition arena.c:68
#define infix_memcpy
A macro for copying memory from a source to a destination pointer.
Definition infix.h:290
c23_nodiscard void * infix_arena_alloc(infix_arena_t *, size_t, size_t)
Allocates a block of memory from the arena with a specific alignment.
Definition arena.c:86
c23_nodiscard infix_arena_t * infix_arena_create(size_t)
Creates and initializes a new memory arena.
Definition arena.c:41
infix_status
An enumeration of all possible success or failure codes from the public API.
Definition infix.h:351
@ INFIX_ERROR_ALLOCATION_FAILED
A memory allocation request failed.
Definition infix.h:353
@ INFIX_SUCCESS
The operation completed successfully.
Definition infix.h:352
@ INFIX_ERROR_INVALID_ARGUMENT
An invalid argument was provided to a function.
Definition infix.h:354
c23_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 infix_type for a packed struct from an arena.
Definition types.c:519
c23_nodiscard infix_type * infix_type_create_void(void)
Creates an infix_type descriptor for the void type.
Definition types.c:153
c23_nodiscard infix_status infix_type_create_union(infix_arena_t *, infix_type **, infix_struct_member *, size_t)
Creates a new infix_type for a union from an arena.
Definition types.c:392
c23_nodiscard infix_status infix_type_create_enum(infix_arena_t *, infix_type **, infix_type *)
Creates a new infix_type for an enum from an arena.
Definition types.c:289
c23_nodiscard infix_status infix_type_create_vector(infix_arena_t *, infix_type **, infix_type *, size_t)
Creates a new infix_type for a SIMD vector from an arena.
Definition types.c:354
c23_nodiscard infix_type * infix_type_create_pointer(void)
Creates an infix_type descriptor for a generic void* pointer.
Definition types.c:145
c23_nodiscard infix_status infix_type_create_complex(infix_arena_t *, infix_type **, infix_type *)
Creates a new infix_type for a _Complex number from an arena.
Definition types.c:324
c23_nodiscard infix_status infix_type_create_array(infix_arena_t *, infix_type **, infix_type *, size_t)
Creates a new infix_type for a fixed-size array from an arena.
Definition types.c:254
c23_nodiscard infix_status infix_type_create_pointer_to(infix_arena_t *, infix_type **, infix_type *)
Creates an infix_type for a pointer to a specific type from an arena.
Definition types.c:230
c23_nodiscard infix_status infix_type_create_struct(infix_arena_t *, infix_type **, infix_struct_member *, size_t)
Creates a new infix_type for a struct from an arena.
Definition types.c:445
c23_nodiscard infix_status infix_type_create_named_reference(infix_arena_t *, infix_type **, const char *, infix_aggregate_category_t)
Creates a new infix_type for a named reference from an arena.
Definition types.c:563
c23_nodiscard infix_type * infix_type_create_primitive(infix_primitive_type_id)
Creates an infix_type descriptor for a primitive C type.
Definition types.c:97
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:161
@ INFIX_PRIMITIVE_UINT16
unsigned short, uint16_t
Definition infix.h:137
@ INFIX_PRIMITIVE_UINT32
unsigned int, uint32_t
Definition infix.h:139
@ INFIX_PRIMITIVE_LONG_DOUBLE
long double
Definition infix.h:147
@ INFIX_PRIMITIVE_FLOAT
float
Definition infix.h:145
@ INFIX_PRIMITIVE_DOUBLE
double
Definition infix.h:146
@ INFIX_PRIMITIVE_SINT16
signed short, int16_t
Definition infix.h:138
@ INFIX_PRIMITIVE_SINT64
signed long long, int64_t
Definition infix.h:142
@ INFIX_PRIMITIVE_SINT32
signed int, int32_t
Definition infix.h:140
@ INFIX_PRIMITIVE_UINT8
unsigned char, uint8_t
Definition infix.h:135
@ INFIX_PRIMITIVE_UINT128
__uint128_t (GCC/Clang specific)
Definition infix.h:143
@ INFIX_PRIMITIVE_BOOL
bool or _Bool
Definition infix.h:134
@ INFIX_PRIMITIVE_UINT64
unsigned long long, uint64_t
Definition infix.h:141
@ INFIX_PRIMITIVE_SINT128
__int128_t (GCC/Clang specific)
Definition infix.h:144
@ INFIX_PRIMITIVE_SINT8
signed char, int8_t
Definition infix.h:136
@ INFIX_TYPE_UNION
A user-defined union (union).
Definition infix.h:118
@ INFIX_TYPE_PRIMITIVE
A built-in type like int, float, double.
Definition infix.h:115
@ INFIX_TYPE_COMPLEX
A _Complex number type.
Definition infix.h:122
@ INFIX_TYPE_ARRAY
A fixed-size array.
Definition infix.h:119
@ INFIX_TYPE_VECTOR
A SIMD vector type.
Definition infix.h:123
@ INFIX_TYPE_VOID
The void type, used for function returns with no value.
Definition infix.h:125
@ INFIX_TYPE_POINTER
A generic void* pointer type.
Definition infix.h:116
@ INFIX_TYPE_NAMED_REFERENCE
A reference to a named type (e.g., struct<Node>).
Definition infix.h:124
@ INFIX_TYPE_REVERSE_TRAMPOLINE
A callback wrapper.
Definition infix.h:120
@ INFIX_TYPE_ENUM
A C-style enumeration, with an underlying integer type.
Definition infix.h:121
@ INFIX_TYPE_STRUCT
A user-defined structure (struct).
Definition infix.h:117
@ INFIX_AGGREGATE_STRUCT
Definition infix.h:151
Declarations for internal-only functions, types, and constants.
c23_nodiscard infix_status _infix_resolve_type_graph(infix_type **, infix_registry_t *)
Walks a type graph, replacing all @Name placeholders with their concrete definitions from a registry.
Definition type_registry.c:398
void _infix_set_error(infix_error_category_t, infix_error_code_t, size_t)
Sets the thread-local error details for a library-internal error.
Definition error.c:44
void _infix_clear_error(void)
Resets the thread-local error state. Called at the start of every public API function.
Definition error.c:76
static void _print(printer_state *state, const char *fmt,...)
Definition signature.c:1199
static infix_type * parse_vector_type(parser_state *)
Definition signature.c:458
static infix_struct_member * parse_aggregate_members(parser_state *state, char end_char, size_t *out_num_members)
Definition signature.c:308
static bool is_function_signature_ahead(const parser_state *state)
Definition signature.c:262
#define MAX_RECURSION_DEPTH
Definition signature.c:69
static infix_status parse_function_signature_details(parser_state *, infix_type **, infix_function_argument **, size_t *, size_t *)
Definition signature.c:662
static infix_type * parse_primitive(parser_state *)
Definition signature.c:590
static bool parse_size_t(parser_state *state, size_t *out_val)
Definition signature.c:145
static infix_type * parse_packed_struct(parser_state *)
Definition signature.c:403
static void set_parser_error(parser_state *state, infix_error_code_t code)
Definition signature.c:106
static bool consume_keyword(parser_state *state, const char *keyword)
Definition signature.c:208
static infix_type * parse_type(parser_state *)
Definition signature.c:819
static infix_type * parse_function_type(parser_state *)
Definition signature.c:506
static const char * parse_identifier(parser_state *state)
Definition signature.c:169
static void _infix_type_print_signature_recursive(printer_state *state, const infix_type *type)
Definition signature.c:1223
static const char * parse_optional_name_prefix(parser_state *state)
Definition signature.c:234
static void skip_whitespace(parser_state *state)
Definition signature.c:119
static infix_type * parse_aggregate(parser_state *, char, char)
Definition signature.c:542
c23_nodiscard infix_status _infix_parse_type_internal(infix_type **out_type, infix_arena_t **out_arena, const char *signature, infix_registry_t *registry)
The core, non-resolving entry point for the signature parser.
Definition signature.c:1040
Definition infix_internals.h:130
Describes a single argument to a function, pairing an optional name with its type.
Definition infix.h:229
infix_type * type
An infix_type describing the argument's type.
Definition infix.h:231
Definition infix_internals.h:158
Describes a single member of an aggregate type (struct or union).
Definition infix.h:219
infix_type * type
An infix_type describing the member's type.
Definition infix.h:221
The central structure for describing any data type in the FFI system.
Definition infix.h:161
struct infix_type_t::@0::@1 pointer_info
For INFIX_TYPE_POINTER.
union infix_type_t::@0 meta
Type-specific metadata.
struct infix_type_t::@0::@7 vector_info
For INFIX_TYPE_VECTOR.
struct infix_type_t::@0::@4 func_ptr_info
For INFIX_TYPE_REVERSE_TRAMPOLINE.
size_t num_elements
The number of elements in the array.
Definition infix.h:183
infix_struct_member * members
Array of members for the aggregate.
Definition infix.h:177
struct infix_type_t::@0::@6 complex_info
For INFIX_TYPE_COMPLEX.
const char * name
Optional name of the aggregate.
Definition infix.h:176
infix_function_argument * args
Array of function arguments (name and type).
Definition infix.h:188
infix_type_category category
The fundamental category of the type.
Definition infix.h:162
struct infix_type_t::@0::@2 aggregate_info
For INFIX_TYPE_STRUCT and INFIX_TYPE_UNION.
struct infix_type_t::@0::@3 array_info
For INFIX_TYPE_ARRAY.
struct infix_type_t * pointee_type
The type this pointer points to.
Definition infix.h:172
struct infix_type_t * element_type
The type of elements in the array.
Definition infix.h:182
struct infix_type_t * return_type
Reverse trampoline return value.
Definition infix.h:187
struct infix_type_t::@0::@5 enum_info
For INFIX_TYPE_ENUM.
struct infix_type_t * base_type
The floating point type of the real and imaginary parts.
Definition infix.h:198
size_t num_members
Number of members in the aggregate.
Definition infix.h:178
struct infix_type_t * underlying_type
The integer type this enum is based on.
Definition infix.h:194
struct infix_type_t::@0::@8 named_reference
For INFIX_TYPE_NAMED_REFERENCE. This is an internal placeholder for a named type like @Point.
size_t num_fixed_args
The number of non-variadic arguments.
Definition infix.h:190
infix_primitive_type_id primitive_id
For INFIX_TYPE_PRIMITIVE.
Definition infix.h:169
size_t num_args
The total number of fixed and variadic arguments.
Definition infix.h:189
bool is_arena_allocated
If true, this type was allocated from an arena and should not be individually freed.
Definition infix.h:165
Definition signature.c:79
const char * start
Definition signature.c:81
infix_registry_t * registry
Definition signature.c:83
infix_arena_t * arena
Definition signature.c:82
int depth
Definition signature.c:84
const char * p
Definition signature.c:80
Definition signature.c:1189
char * p
Definition signature.c:1190
size_t remaining
Definition signature.c:1191
infix_status status
Definition signature.c:1192