Dqn/Code/DqnHeader.h

312 lines
11 KiB
C

#ifndef DQN_HEADER_H
#define DQN_HEADER_H
// NOTE: Copy the function prototype, all functions will be whitespaced aligned to the longest return type of the file
// NOTE: If you function contains a comma NOT in the argument list (i.e. multiple template parameters), this macro does NOT work.
// Please use the DQN_HEADER_COPY_BEGIN and DQN_HEADER_COPY_END unfortunately.
#define DQN_HEADER_COPY_PROTOTYPE(func_return, func_name_and_types) func_return func_name_and_types
// NOTE: Copy all contents from the file between the begin and end macro
#define DQN_HEADER_COPY_BEGIN
#define DQN_HEADER_COPY_END
// NOTE: Copy comments by writing comments with the prefix, "// @"
// @ Hello this comment will be copied to the output
// NOTE: Example Input
#if 0
#include "DqnHeader.h"
// @ ptr1: Pointer to the first block of memory
// @ ptr2: Pointer to the second block of memory
// @ num_bytes: The number of bytes to compare in both blocks of memory
DQN_HEADER_COPY_PROTOTYPE(int, Dqn_MemCmp(void const *ptr1, void const *ptr2, size_t num_bytes))
{
int result = memcmp(ptr1, ptr2, num_bytes);
return result;
}
DQN_HEADER_COPY_BEGIN
struct HelloWorld
{
int foo, bar;
};
DQN_HEADER_COPY_END
#endif
// NOTE: Example Output
#if 0
// @ ptr1: Pointer to the first block of memory
// @ ptr2: Pointer to the second block of memory
// @ num_bytes: The number of bytes to compare in both blocks of memory
int Dqn_MemCmp(void const *ptr1, void const *ptr2, size_t num_bytes);
struct HelloWorld
{
int foo, bar;
};
#endif
#endif /* DQN_HEADER_H */
#ifdef DQN_HEADER_IMPLEMENTATION
#define _CRT_SECURE_NO_WARNINGS // NOTE: Undefined at end of file
#include <stddef.h>
#define DQN_USE_PRIMITIVE_TYPEDEFS
#define DQN_IMPLEMENTATION
#include "Dqn.h"
char const *ParseFunctionReturnType(char const *ptr, isize *len)
{
char const *result = Dqn_Str_SkipWhitespace(ptr);
isize result_len = 0;
for (int scope = 0; ptr; ptr++) // NOTE: Parse the function return type
{
if (ptr[0] == '<' || ptr[0] == '>')
{
if (ptr[0] == '<') scope++;
else scope--;
continue;
}
else if (ptr[0] == ',')
{
if (scope != 0) continue;
result_len = static_cast<int>(ptr - result);
break;
}
}
if (len) *len = result_len;
return result;
}
char const *ParseFunctionNameAndParameters(char const *ptr, isize *len)
{
char const *result = ptr;
int result_len = 0;
for (int scope = 0; ptr; ptr++) // NOTE: Parse the function name and parameters
{
if (ptr[0] == '(') scope++;
else if (ptr[0] == ')')
{
if (scope-- != 0) continue;
result_len = static_cast<int>(ptr - result);
break;
}
}
*len = result_len;
return result;
}
char const *ParseFunctionComment(char const *ptr, isize *len)
{
while (ptr[0] != '"') ptr++;
ptr++;
char const *result = ptr;
isize result_len = 0;
for (;;)
{
while (ptr[0] != '"')
ptr++;
if (ptr[-1] != '\\')
{
result_len = ptr - result;
break;
}
}
*len = result_len;
return result;
}
enum struct HeaderEntryType
{
Prototype,
Comment,
CopyBegin,
Count
};
struct HeaderEntry
{
HeaderEntryType type;
union
{
struct
{
Dqn_Slice<char> return_val;
Dqn_Slice<char> name_and_args;
} function_decl;
Dqn_Slice<char> comment;
Dqn_Slice<char> copy_range;
};
};
int main(int argc, char *argv[])
{
if (argc < 1)
{
fprintf(stdout, "Header Usage: %s [<source code filename>, ...]\n", argv[0]);
return 0;
}
Dqn_MemArena arena = {};
for (isize arg_index = 1; arg_index < argc; ++arg_index)
{
char const *file = argv[arg_index];
isize buf_size = 0;
char *buf = Dqn_File_ReadWithArena(&arena, file, &buf_size);
if (!buf)
{
fprintf(stderr, "Failed to read file: %s\n", file);
continue;
}
char constexpr HEADER_COPY_PROTOTYPE[] = "DQN_HEADER_COPY_PROTOTYPE";
char constexpr HEADER_COMMENT[] = "// @";
char constexpr HEADER_COPY_BEGIN[] = "DQN_HEADER_COPY_BEGIN";
char constexpr HEADER_COPY_END[] = "DQN_HEADER_COPY_END";
char const *find_list[] = {HEADER_COPY_PROTOTYPE, HEADER_COMMENT, HEADER_COPY_BEGIN};
isize constexpr find_string_lens[] = {
Dqn_CharCountI(HEADER_COPY_PROTOTYPE),
Dqn_CharCountI(HEADER_COMMENT),
Dqn_CharCountI(HEADER_COPY_BEGIN),
};
isize num_header_entries = 0;
{
char const *ptr = buf;
char const *ptr_end = buf + buf_size;
isize ptr_len = buf_size;
isize matched_find_index = -1;
for (char const *token = Dqn_Str_FindMulti(ptr, find_list, find_string_lens, Dqn_ArrayCountI(find_list), &matched_find_index, ptr_len);
token;
token = Dqn_Str_FindMulti(ptr, find_list, find_string_lens, Dqn_ArrayCountI(find_list), &matched_find_index, ptr_len))
{
ptr = token + find_string_lens[matched_find_index];
num_header_entries++;
ptr_len = ptr_end - ptr;
}
}
HeaderEntry *header_entries = DQN_MEM_ARENA_ALLOC_ARRAY(&arena, HeaderEntry, num_header_entries);
isize header_entries_index = 0;
isize max_prototype_return_val = 0;
char const *ptr = buf;
char const *ptr_end = buf + buf_size;
isize ptr_len = buf_size;
isize matched_find_index = -1;
for (char const *token = Dqn_Str_FindMulti(ptr, find_list, find_string_lens, Dqn_ArrayCountI(find_list), &matched_find_index, ptr_len);
token;
token = Dqn_Str_FindMulti(ptr, find_list, find_string_lens, Dqn_ArrayCountI(find_list), &matched_find_index, ptr_len))
{
HeaderEntry *entry = header_entries + header_entries_index++;
entry->type = static_cast<HeaderEntryType>(matched_find_index);
if (matched_find_index == (isize)HeaderEntryType::Prototype)
{
ptr = token + find_string_lens[matched_find_index] + 1 /*macro start parenthesis*/;
isize func_type_len = 0;
char const *func_type = ParseFunctionReturnType(ptr, &func_type_len);
ptr = func_type + func_type_len + 1; // Ptr is at macro comma, skip the comma
ptr = Dqn_Str_SkipWhitespace(ptr);
isize func_name_len = 0;
char const *func_name = ParseFunctionNameAndParameters(ptr, &func_name_len);
entry->function_decl.return_val = Dqn_AsprintfSlice(&arena, "%.*s", func_type_len, func_type);
entry->function_decl.name_and_args = Dqn_AsprintfSlice(&arena, "%.*s", func_name_len, func_name);
ptr = func_name + func_name_len + 1; // Ptr is at macro closing paren, skip the paren
max_prototype_return_val = DQN_MAX(max_prototype_return_val, func_type_len);
}
else if (matched_find_index == (isize)HeaderEntryType::Comment)
{
char const *comment_start = token;
ptr = token;
while (ptr[0] != '\n' && ptr[0] != '\r')
ptr++;
isize comment_len = ptr - comment_start;
entry->comment.buf = DQN_MEM_ARENA_ALLOC_ARRAY(&arena, char, comment_len);
DQN_FOR_EACH(comment_index, comment_len)
{
// NOTE: We capture "// @", and we want to skip the @ symbol, its ugly which is at the index 3
// Also if a space is given after the '@' symbol then skip that too.
char ch = comment_start[comment_index];
if (comment_index == 3 && ch == '@') continue;
if (comment_index == 4 && ch == ' ') continue;
entry->comment.buf[entry->comment.len++] = ch;
}
while (entry->comment.len > 0 && Dqn_Char_IsWhitespace(entry->comment.buf[entry->comment.len - 1]))
entry->comment.len--;
ptr = comment_start + comment_len;
}
else if (matched_find_index == (isize)HeaderEntryType::CopyBegin)
{
ptr = token + find_string_lens[matched_find_index];
ptr = Dqn_Str_SkipWhitespace(ptr);
char const *copy_start = ptr;
char const *copy_end = Dqn_Str_Find(ptr, HEADER_COPY_END, ptr_len, Dqn_CharCountI(HEADER_COPY_END));
if (!copy_end)
{
fprintf(stderr, "Header copy begin macro: %s not matched with a copy end macro: %s", HEADER_COPY_BEGIN, HEADER_COPY_END);
return -1;
}
isize copy_len = copy_end - copy_start;
entry->copy_range.buf = (char *)DQN_MEM_ARENA_ALLOC(&arena, copy_len);
DQN_FOR_EACH(copy_index, copy_len)
{
char ch = copy_start[copy_index];
if (ch == '\r') continue;
entry->copy_range.buf[entry->copy_range.len++] = ch;
}
ptr = copy_end;
}
ptr_len = ptr_end - ptr;
}
DQN_ASSERT(header_entries_index == num_header_entries);
DQN_FOR_EACH(entry_index, header_entries_index)
{
HeaderEntry const *entry = header_entries + entry_index;
switch(entry->type)
{
case HeaderEntryType::Prototype:
{
isize remaining_space = max_prototype_return_val - entry->function_decl.return_val.len + 1;
fprintf(stdout, "%.*s", (int)entry->function_decl.return_val.len, entry->function_decl.return_val.str);
DQN_FOR_EACH(space, remaining_space) fputs(" ", stdout);
fprintf(stdout, " %.*s;\n", (int)entry->function_decl.name_and_args.len, entry->function_decl.name_and_args.str);
}
break;
case HeaderEntryType::Comment:
{
fprintf(stdout, "%.*s\n", (int)entry->comment.len, entry->comment.buf);
}
break;
case HeaderEntryType::CopyBegin:
{
fprintf(stdout, "%.*s\n", (int)entry->copy_range.len, entry->copy_range.buf);
}
break;
}
}
}
}
#undef _CRT_SECURE_NO_WARNINGS
#endif /* DQN_HEADER_IMPLEMENTATION */