From b1e857cf1471d1871a9396696b22fa531da98249 Mon Sep 17 00:00:00 2001 From: Aldrik Ramaekers Date: Sat, 23 Nov 2024 22:33:43 +0100 Subject: add projbase to repo --- project-base/docs/gen_docs.c | 705 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 705 insertions(+) create mode 100644 project-base/docs/gen_docs.c (limited to 'project-base/docs/gen_docs.c') diff --git a/project-base/docs/gen_docs.c b/project-base/docs/gen_docs.c new file mode 100644 index 0000000..e09f30a --- /dev/null +++ b/project-base/docs/gen_docs.c @@ -0,0 +1,705 @@ +#include + +/* + +# Format +- declarations start with // followed by 1 tab. +- identifiers are prefixed with :/. +- identifiers are postfixed with 1 tab. +- multi line declarations are prefixed with 3 tabs after the first line. +- lists items can be defined with a prefixed '-' + +Example: +// :/Title Hello world! +// :Text Multi +// Line +// String + +*/ + +#define IDENTIFIER_CONTINUATION "// " +#define IDENTIFIER_PREFIX "// :/" +#define INFO_IDENTIFIER "// :/Info" +#define RETURN_IDENTIFIER "// :/Ret" +#define TITLE_IDENTIFIER "// :/Title" +#define TEXT_IDENTIFIER "// :/Text" + +typedef enum t_parse_state +{ + PARSING_SEARCHING, + PARSING_TITLE, + PARSING_INFO, + PARSING_TEXT, + PARSING_RET, + PARSING_STRUCT, +} parse_state; + +typedef enum t_data_tag_type +{ + CHAPTER = 1, // title, text + FUNCTION = 2, // info, return + STRUCT = 3, // struct_def +} data_tag_type; + +typedef struct t_data_tag +{ + data_tag_type type; + char* title; + char* info; + char* text; + char* ret; + char* func; + char* func_name; + char* struct_def; + char* struct_name; +} data_tag; + +array tags; + +data_tag current_tag; + +void reset_tag() +{ + current_tag.type = 0; + current_tag.title = 0; + current_tag.info = 0; + current_tag.text = 0; + current_tag.ret = 0; + current_tag.func = 0; + current_tag.func_name = 0; + current_tag.struct_def = 0; + current_tag.struct_name = 0; +} + +void set_tag_type(data_tag_type type) +{ + if (current_tag.type == 0) current_tag.type = type; +} + +void store_tag() +{ + array_push(&tags, ¤t_tag); + reset_tag(); +} + +char* get_str_after_tab(char *string) +{ + char *last_str = string; + char *str = last_str; + while((str = strchr(str, 0x9)) && str != 0) last_str = ++str; + return last_str; +} + +char* str_append_newline(char* str, char* append, bool append_newlines) +{ + if (str) + { + int new_len = strlen(str) + strlen(append) + 5; + char *newbuf = mem_alloc(new_len); + memset(newbuf, 0, new_len); + strcpy(newbuf, str); + if (append[0] == '-' || append_newlines) strcat(newbuf, "
"); + strcat(newbuf, append); + } + else + { + return append; + } +} + +char* str_replace_tab_with_space(char* str) +{ + char* str_orig = str; + while(*str) + { + if (*str == ' ') *str = ' '; + str++; + } + + return str_orig; +} + +char* str_remove_excess_indent(char* str) +{ + int len = strlen(str) + 1; + char* buf = mem_alloc(len); + memset(buf, 0, len); + + int cursor = 0; + char last = 0; + while(*str) + { + if (!((*str == ' ' || *str == ' ') && (last == ' ' || last == ' '))) buf[cursor++] = *str; + last = *str; + str++; + } + return buf; +} + +char* str_trim(char* str) +{ + while(*str) + { + if (*str != ' ' && *str != ' ') return str; + str++; + } +} + +char* str_get_last_word_from_struct_def(char* str) +{ + int len = strlen(str) + 1; + char* buf = mem_alloc(len); + memset(buf, 0, len); + + char *finder = str; + char *last_indent = str; + while(*finder) + { + if (*finder == ' ' || *finder == ' ' || *finder == '}') last_indent = finder; + if (*finder == ';' || *finder == '{') break; + finder++; + } + + memcpy(buf, last_indent+1, finder - last_indent-1); + + return buf; +} + +char* get_next_word_to_highlight(char* str, char* buf, int buflen) +{ + memset(buf, 0, buflen); + int write_cursor = 0; + while(*str) + { + if (*str == ' ') goto done; + if (*str == '\n') goto done; + if (*str == '(') goto done; + if (*str == '*') goto done; + if (*str == ')') goto done; + if (*str == ',') goto done; + // if (*str == ';') goto done; + if (*str == '>') goto done; + + buf[write_cursor++] = *str; + + str++; + } + + done: + if (!write_cursor) { buf[write_cursor++] = *str; str++;} + return str; +} + +bool str_is_constant(char* str) +{ + while(*str) + { + if (!((*str >= 'A' && *str <= 'Z') || *str == '_')) return false; + str++; + } + + return true; +} + +bool str_is_type(char* str, char** struct_name) +{ + *struct_name = 0; + if (!strcmp(str, "int")) return true; + if (!strcmp(str, "short")) return true; + if (!strcmp(str, "byte")) return true; + if (!strcmp(str, "char")) return true; + if (!strcmp(str, "long")) return true; + if (!strcmp(str, "float")) return true; + if (!strcmp(str, "double")) return true; + if (!strcmp(str, "float32")) return true; + if (!strcmp(str, "float64")) return true; + if (!strcmp(str, "u8")) return true; + if (!strcmp(str, "u16")) return true; + if (!strcmp(str, "u32")) return true; + if (!strcmp(str, "u64")) return true; + if (!strcmp(str, "s8")) return true; + if (!strcmp(str, "s16")) return true; + if (!strcmp(str, "s32")) return true; + if (!strcmp(str, "s64")) return true; + if (!strcmp(str, "signed")) return true; + if (!strcmp(str, "unsigned")) return true; + if (!strcmp(str, "bool")) return true; + if (!strcmp(str, "void")) return true; + + if (!strcmp(str, "typedef")) return true; + if (!strcmp(str, "struct")) return true; + if (!strcmp(str, "enum")) return true; + + for (int i = 0; i < tags.length; i++) + { + data_tag* tag = array_at(&tags, i); + + if (tag->type == STRUCT) { + if (!strcmp(str, tag->struct_name)) { + *struct_name = tag->struct_name; + return true; + } + } + } + + return false; +} + +char* apply_word_highlighting(char* str, int newbuflen) +{ + char* orig = str; + char* buf = mem_alloc(newbuflen); + memset(buf, 0, newbuflen); + + int write_cursor = 0; + char* last_str = 0; + char word_buf[500]; + bool last_str_was_highlighted = false; + while(((last_str = str) && (str = get_next_word_to_highlight(str, word_buf, 500)))) + { + char* struct_name; + bool is_type = str_is_type(word_buf, &struct_name); + bool is_constant = str_is_constant(word_buf); + + bool is_highlighted = is_constant || is_type; + + char* type_color = ""; + if (is_constant) type_color = ""; + + if (is_highlighted && !last_str_was_highlighted) { + if (struct_name) { + strcat(buf, ""); + strcat(buf, type_color); + strcat(buf, word_buf); + strcat(buf, ""); + strcat(buf, ""); + } + else { + strcat(buf, type_color); + strcat(buf, word_buf); + strcat(buf, ""); + } + } + else { + strcat(buf, word_buf); + } + + if (!(strcmp(word_buf, " ") == 0 || strcmp(word_buf, " ") == 0)) + last_str_was_highlighted = is_highlighted; + + if (*str == 0) break; + } + + return buf; +} + +bool str_is_struct_def(char* str) +{ + return (string_contains(str, "struct *") || string_contains(str, "enum *")) && !string_contains(str, ";"); +} + +bool str_is_struct_def_end(char* str) +{ + return string_contains(str, "}*;");// || string_contains(str, "};"); +} + +bool str_is_func_def(char* str) +{ + return string_contains(str, "* *(*);") || string_contains(str, "* *();") || + (string_contains(str, "* *(") && string_contains(str, ");")) || + (string_contains(str, "* *(") && string_contains(str, ");")); +} + +char* struct_name_from_str(char* str) +{ + if (str_is_struct_def(str)) + { + if (string_contains(str, "typedef")) + { + if (string_contains(str, ";")) // example: typedef struct t_backbuffer backbuffer; + { + return str_get_last_word_from_struct_def(str); + } + + return 0; // name is in last line of struct def. + } + + return str_get_last_word_from_struct_def(str); + } + else if (str_is_struct_def_end(str)) + { + return str_get_last_word_from_struct_def(str); + } +} + +char* func_name_from_def(char *str) +{ + int len = strlen(str) + 1; + char* buf = mem_alloc(len); + memset(buf, 0, len); + + char *finder = str; + char *last_indent = 0; + while(*finder) + { + if (*finder == ' ' || *finder == ' ') last_indent = finder; + if (*finder == '(') break; + finder++; + } + + memcpy(buf, last_indent+1, finder - last_indent-1); + + return buf; +} + +#define IS_CONTINUATION(_ident) (string_contains(string, _ident) || string_contains(string, IDENTIFIER_CONTINUATION)) +parse_state current_state = PARSING_SEARCHING; + +void parse_struct(char *string) +{ + //log_assert(current_state == PARSING_STRUCT, "invalid state for parsing struct"); + + if (!current_tag.struct_name) current_tag.struct_name = struct_name_from_str(string); + + if (!(string_contains(string, ";") || string_contains(string, ",")) || str_is_struct_def_end(string)) + current_tag.struct_def = str_append_newline(current_tag.struct_def, "
", false); + else + current_tag.struct_def = str_append_newline(current_tag.struct_def, "
", false); + + current_tag.struct_def = str_append_newline(current_tag.struct_def, + str_replace_tab_with_space(str_trim(str_remove_excess_indent(string))), false); + + current_tag.struct_def = str_append_newline(current_tag.struct_def, "
", false); + if (str_is_struct_def_end(string)) { + current_state = PARSING_SEARCHING; + store_tag(); + } +} + +void parse_chapter(char *string) +{ + //log_assert(current_state == PARSING_TITLE || current_state == PARSING_TEXT, "invalid state for parsing chapter"); + + if (current_state == PARSING_TITLE) + { + if (!IS_CONTINUATION(TITLE_IDENTIFIER)) { + current_state = PARSING_SEARCHING; + if (!string_contains(string, TEXT_IDENTIFIER)) store_tag(); + } + else current_tag.title = str_append_newline(current_tag.title, get_str_after_tab(string), false); + } + if (current_state == PARSING_TEXT) + { + if (!IS_CONTINUATION(TEXT_IDENTIFIER)) { current_state = PARSING_SEARCHING; store_tag(); } + else { + current_tag.text = str_append_newline(current_tag.text, get_str_after_tab(string), false); + current_tag.text = str_append_newline(current_tag.text, " ", false); + } + } +} + +void parse_function(char *string) +{ + //log_assert(current_state == PARSING_INFO || current_state == PARSING_RET, "invalid state for parsing function"); + + if (str_is_func_def(string)) + { + current_tag.func_name = func_name_from_def(string); + current_tag.func = str_append_newline(current_tag.func, str_remove_excess_indent(string), false); + current_state = PARSING_SEARCHING; + store_tag(); + } + if (current_state == PARSING_INFO) + { + if (!IS_CONTINUATION(INFO_IDENTIFIER)) { current_state = PARSING_SEARCHING; } + else current_tag.info = str_append_newline(current_tag.info, get_str_after_tab(string), false); + } + if (current_state == PARSING_RET) + { + if (!IS_CONTINUATION(RETURN_IDENTIFIER)) { current_state = PARSING_SEARCHING; } + else current_tag.ret = str_append_newline(current_tag.ret, get_str_after_tab(string), false); + } +} + +void parse_line(char *string, bool current_file_has_chapter) +{ + int str_len = strlen(string); + + if (str_is_struct_def(string)) { current_state = PARSING_STRUCT; set_tag_type(STRUCT); } + + if (string_contains(string, INFO_IDENTIFIER)) { current_state = PARSING_INFO; set_tag_type(FUNCTION); }; + if (string_contains(string, RETURN_IDENTIFIER)) current_state = PARSING_RET; + + if (string_contains(string, TITLE_IDENTIFIER)) { current_state = PARSING_TITLE; set_tag_type(CHAPTER); }; + if (string_contains(string, TEXT_IDENTIFIER)) { current_state = PARSING_TEXT; }; + + if (current_tag.type == CHAPTER) parse_chapter(string); + + if (current_file_has_chapter) { + if (current_tag.type == FUNCTION) parse_function(string); + if (current_tag.type == STRUCT) parse_struct(string); + } +} + +void parse_file(char* path) +{ + file_content content = platform_read_file_content(path, "rb"); + char* current_line = content.content; + char* ptr = current_line; + + reset_tag(); + bool current_file_has_chapter = false; + + while(*ptr != 0) + { + if (*ptr == '\n'){ + *ptr = 0; + parse_line(current_line, current_file_has_chapter); + if (current_tag.type == PARSING_TITLE) current_file_has_chapter = true; + current_line = ++ptr; + continue; + } + ptr++; + } +} + +#define MAX_BODY_LEN 500000 +#define APPEND(_big, _str) strncat(_big, _str, MAX_BODY_LEN); + +#define DOCUMENT_TITLE "Project-base Technical Reference Manual" + +#define PAGE_BREAK "
" +#define STYLE_TOPBAR "style=\"\"" +#define STYLE_TOPBAR_SUB "style=\"font-size:0.6em;float:right;\"" +#define STYLE_TOPBAR_TITLE "style=\"font-size:0.7em;flex: 3;margin-bottom:-17px;\"" +#define STYLE_DOCUMENT "style=\"margin-left: -15%;width: 130%;text-align: justify; text-justify: inter-word; font-family: 'Roboto Condensed', sans-serif;\"" +#define STYLE_HEADING1 "style=\"font-size:1.1em;\"" +#define STYLE_HEADING2 "style=\"font-size:1.0em;\"" +#define STYLE_HEADING3 "style=\"font-size:0.9em;\"" +#define STYLE_TEXT "style=\"font-size:0.9em;\"" +#define STYLE_SEPARATOR "style=\"width: 100%;background-color:rgb(0, 98, 208);padding:0px;margin:0px;color:white;padding-left: 5px;font-style: italic;\"" +#define STYLE_SEPARATOR_SMALL "style=\"width: 100%;height:5px;background-color:rgb(0, 98, 208);padding:0px;margin:0px;color:white;padding-left: 5px;font-style: italic;margin-top: 20px;\"" +#define STYLE_FUNCTION_CONTAINER "style=\"padding-bottom: 20px;\"" +#define STYLE_FUNCTION_DEF "style=\"padding: 5px;background-color:rgb(243, 243, 248);border-radius:3px;border:2px solid rgb(221, 221, 221);\"" +#define STYLE_STRUCT_DEF "style=\"padding: 5px 5px 15px 5px;background-color:rgb(243, 243, 248);border-radius:3px;border:2px solid rgb(221, 221, 221);\"" + +int count_type_for_chapter(int index, data_tag_type type) +{ + int count = 0; + for (int i = index; i < tags.length; i++) + { + data_tag* tag = array_at(&tags, i); + + if (tag->type == type) { + count++; + } + else if (tag->type == CHAPTER) { + break; + } + } + return count; +} + +char* dump_structs_for_chapter(char* body, int index, int current_chapter_nr, int current_subchapter_nr) +{ + int current_struct_nr = 1; + for (int i = index; i < tags.length; i++) + { + data_tag* tag = array_at(&tags, i); + + if (tag->type == STRUCT) { + APPEND(body, "
"); + + APPEND(body, "

struct_name); APPEND(body, "\">"); + char id[20]; + sprintf(id, "%d.%d.%d ", current_chapter_nr, current_subchapter_nr, current_struct_nr); + APPEND(body, id); + APPEND(body, tag->struct_name); + APPEND(body, "

"); + + APPEND(body, "
"); + APPEND(body, tag->struct_def); + APPEND(body, "
"); + + APPEND(body, "
"); + current_struct_nr++; + } + else if (tag->type == CHAPTER) { + break; + } + } + return body; +} + +char* dump_functions_for_chapter(char* body, int index, int current_chapter_nr, int current_subchapter_nr) +{ + int current_function_nr = 1; + for (int i = index; i < tags.length; i++) + { + data_tag* tag = array_at(&tags, i); + + if (tag->type == FUNCTION) { + APPEND(body, "
"); + + char id[20]; + sprintf(id, "%d.%d.%d", current_chapter_nr, current_subchapter_nr, current_function_nr); + APPEND(body, "

func_name); APPEND(body, "\">"); + APPEND(body, id); APPEND(body, " "); APPEND(body, tag->func_name); + APPEND(body, "

"); + + APPEND(body, "
"); + APPEND(body, tag->func); + APPEND(body, "
"); + + APPEND(body, "
"); + APPEND(body, "
"); + APPEND(body, "Info: "); + APPEND(body, tag->info); + APPEND(body, "
"); + + if (tag->ret) { + APPEND(body, "
"); + APPEND(body, "Returns: "); + APPEND(body, tag->ret); + APPEND(body, "
"); + } + + APPEND(body, "
"); + current_function_nr++; + } + else if (tag->type == CHAPTER) { + break; + } + } + return body; +} + +void dump_html() +{ + char* header = + "" + "" + "" + " " + "" + + "
" + "

"DOCUMENT_TITLE"

" + "
For Version 2.0.0
" + + "

1 About

" + "
Written by Aldrik Ramaekers, aldrik.ramaekers@protonmail.com.
This document is distributed under the BSD 2-Clause 'Simplified' License.
" + + "

2 Introduction

" + "
This document gives a technical description for the Project-base library.The Project-base library is a general purpose library intended for creating graphical programs for the Windows and Linux operating system.This document describes all the components" + "of the Project-base library and gives examples for using these components.
" + "
" + "" + ""; + + char* body = mem_alloc(MAX_BODY_LEN); + memset(body, 0, MAX_BODY_LEN); + + APPEND(body, ""); + APPEND(body, ""); + APPEND(body, ""); + APPEND(body, ""); + APPEND(body, ""); + APPEND(body, ""); + + int current_chapter_nr = 0; + int current_function_nr = 1; + int current_subchapter_nr = 1; + for (int i = 0; i < tags.length; i++) + { + data_tag* tag = array_at(&tags, i); + + if (tag->type == CHAPTER) + { + current_function_nr = 1; + current_subchapter_nr = 1; + current_chapter_nr++; + + APPEND(body, PAGE_BREAK); + APPEND(body, "
"); + APPEND(body, "
"); + + APPEND(body, "
"); + APPEND(body, "
"); + APPEND(body, "
"DOCUMENT_TITLE"
"); + APPEND(body, "
For Version 2.0.0
"); + APPEND(body, "
"); + APPEND(body, "
"); + + APPEND(body, "

"); + char id[20]; + sprintf(id, "%d ", current_chapter_nr); + APPEND(body, id); + APPEND(body, tag->title); + APPEND(body, "

"); + APPEND(body, "
"); + APPEND(body, tag->text); + APPEND(body, "
"); + + // Struct sub-chapter + if (count_type_for_chapter(i+1, STRUCT)) { + APPEND(body, "

"); + sprintf(id, "%d.%d ", current_chapter_nr, current_subchapter_nr); + APPEND(body, id); + APPEND(body, "Structures"); + APPEND(body, "

"); + + dump_structs_for_chapter(body, i+1, current_chapter_nr, current_subchapter_nr++); + } + + // Function sub-chapter + if (count_type_for_chapter(i+1, FUNCTION)) { + APPEND(body, "

"); + sprintf(id, "%d.%d ", current_chapter_nr, current_subchapter_nr); + APPEND(body, id); + APPEND(body, "Functions"); + APPEND(body, "

"); + + dump_functions_for_chapter(body, i+1, current_chapter_nr, current_subchapter_nr++); + } + } + } + + APPEND(body, ""); + APPEND(body, ""); + + platform_write_file_content("build\\docs_title.html", "wb+", header, strlen(header)); + + printf("Length before: %d\n", strlen(body)); + body = apply_word_highlighting(body, MAX_BODY_LEN); + printf("Length after: %d\n", strlen(body)); + + platform_write_file_content("build\\docs.html", "wb+", body, strlen(body)); +} + +int main(int argc, char **argv) +{ + tags = array_create(sizeof(data_tag)); + reset_tag(); + + array files = array_create(sizeof(found_file)); + array filters = string_split("*.h"); + bool is_cancelled = false; + platform_list_files_block(&files, "src\\", filters, true, 0, false, &is_cancelled, 0); + + for (s32 i = 0; i < files.length; i++) + { + found_file *file = array_at(&files, i); + + if (platform_file_exists(file->path)) + { + parse_file(file->path); + } + } + + dump_html(); + + return 0; +} \ No newline at end of file -- cgit v1.2.3-70-g09d2