Automatic recompilation might be broken on MSVC, haven't tried it.
# define OS_NONE 0
#endif
+#define internal static
+
#define BOOL_DEFINED 1
#if __STDC_VERSION__ >= 199901L
# include <stdbool.h>
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
+#include <assert.h>
+
+#if OS_WINDOWS
+# if COMPILER_CL
+# define CC "cl"
+# else
+# define CC "x86_64-w64-mingw32-gcc"
+# endif
+#else
+# define CC "cc"
+#endif
#if OS_WINDOWS
# define WIN32_LEAN_AND_MEAN
# include <direct.h>
# include <io.h>
# define win32_stdin stdin
-# undef stdin
# define win32_stdout stdout
-# undef stdout
# define win32_stderr stderr
+# undef stdin
+# undef stdout
# undef stderr
# define _cb_platform_mkdir(Path) _mkdir((Path));
# define MAX_ENVVAR 32767
#define ANSI_COLOR_CYAN "\x1b[36m"
#define ANSI_COLOR_RESET "\x1b[0m"
-#define internal static
#ifndef _assert_break
# if OS_WINDOWS
# define _assert_break() __debugbreak()
# endif
#endif
-
-internal void cb_assertion_break(char *condition,
- char *file, int32_t line) {
+internal void cb_assertion_break(char *condition, char *file, int32_t line) {
+ (void)condition;
+ (void)file;
+ (void)line;
_assert_break();
}
-typedef void (*cb_assertion_handler_fn)(char *condition,
- char *file, int32_t line);
+typedef void (*cb_assertion_handler_fn)(char *condition, char *file, int32_t line);
internal cb_assertion_handler_fn cb_assertion_handler = cb_assertion_break;
#define cb_assert(COND) \
} \
} while (0)
-#define StackPush(Head, Nodeptr) \
- LLPushFrontCustom((Head), (Head), (Nodeptr), next)
-#define StackPop(Head) (Head ? (Head = Head->next) : 0)
-#define LLPushFrontCustom(Head, Last, Nodeptr, Next) \
- (!(Head) ? (Head) = (Nodeptr), (Last) = (Nodeptr) \
- : ((Nodeptr)->Next = (Head), (Head) = (Nodeptr)))
-#define QueuePush(Head, Last, Nodeptr) \
- LLPushBackCustom((Head), (Last), (Nodeptr), next)
-#define QueuePop(Head) (Head ? (Head = Head->next) : 0)
-#define LLPushBackCustom(Head, Last, Nodeptr, Next) \
- (!(Head) ? (Head) = (Last) = (Nodeptr) \
- : ((Last) ? ((Last)->Next = (Nodeptr), (Last) = (Nodeptr)) \
- : ((Head)->Next = (Last) = (Nodeptr))))
-
struct CB_PathList {
- char **values;
+ const char **values;
size_t count;
size_t capacity;
};
typedef struct CB_PathList CB_Cmd;
-struct Cb_Cmd_RunArgs {
- bool async;
- bool reset;
-
+struct CB_Cmd_RunArgs {
CB_Handle stdin;
CB_Handle stdout;
CB_Handle stderr;
+ bool async;
+ bool reset;
};
typedef struct {
- int32_t status_code;
CB_ProcHandle handle;
+ int32_t status_code;
} CB_Process;
typedef struct {
typedef uint8_t CB_LogLevel;
enum {
+ CB_LogLevel_None,
CB_LogLevel_Info,
CB_LogLevel_Warn,
CB_LogLevel_Error,
+ CB_LogLevel_COUNT,
};
-#ifndef CB_DYN_DEFAULT_CAPACITY
-# define CB_DYN_DEFAULT_CAPACITY 8
+typedef uint8_t CB_FileType;
+enum {
+ CB_FileType_Device_Block = 1 << 0,
+ CB_FileType_Device_Char = 1 << 1,
+ CB_FileType_Directory = 1 << 2,
+ CB_FileType_Pipe = 1 << 3,
+ CB_FileType_Link = 1 << 4,
+ CB_FileType_Socket = 1 << 5,
+ CB_FileType_Regular = 1 << 6,
+};
+
+#if OS_WINDOWS
+#else
+# include <dirent.h>
+ struct CB_FileIterator_Handle {
+ const char *path;
+ DIR *dir;
+ struct dirent *dir_entry;
+ };
#endif
-#ifndef CB_RECOMPILE_OPTIONS
-# define CB_RECOMPILE_OPTIONS
+typedef struct {
+ CB_FileType filter_allowed;
+ struct CB_FileIterator_Handle handle;
+} CB_FileIterator;
+
+typedef struct {
+ size_t size;
+ char *path;
+ CB_FileType type;
+} CB_FileInfo;
+
+typedef struct CB_StringView {
+ const char *data;
+ ssize_t length;
+} CB_StringView;
+
+struct CB_StringBuilder_Node {
+ struct CB_StringBuilder_Node *prev;
+ struct CB_StringBuilder_Node *next;
+ CB_StringView value;
+};
+
+typedef struct CB_StringBuilder {
+ struct CB_StringBuilder_Node *first;
+ struct CB_StringBuilder_Node *last;
+ int32_t total_length;
+ int32_t node_count;
+} CB_StringBuilder;
+
+#ifndef CB_DYN_DEFAULT_CAPACITY
+# define CB_DYN_DEFAULT_CAPACITY 8
#endif
-#if OS_WINDOWS
-# define CB_CMD_REBUILD_SELF(Exe_name, Builder_src) "cl.exe", "/Fe:", (Exe_name), \
- (Builder_src), "/nologo", \
- CB_RECOMPILE_OPTIONS
+#if COMPILER_CL
+# define CB_CMD_REBUILD_SELF(Exe_name, Builder_src) CC, "/Fe:", (Exe_name), (Builder_src), "/nologo"
#else
-# define CB_CMD_REBUILD_SELF(Exe_name, Builder_src) "cc", "-o", (Exe_name), (Builder_src), \
- CB_RECOMPILE_OPTIONS
+# define CB_CMD_REBUILD_SELF(Exe_name, Builder_src) CC, "-o", (Exe_name), (Builder_src)
#endif
+#define CB_CMD_REBUILD_SELF_WITH_OPTION(Exe_name, Builder_src, ...) CB_CMD_REBUILD_SELF(Exe_name, Builder_src), __VA_ARGS__
+#define cb_strlit(cstr) (CB_StringView) { .data = (cstr), .length = cb_arrlength(cstr) - 1 }
+#define cb_strinit(cstr) { .data = (cstr), .length = cb_arrlength(cstr) - 1 }
+#define cb_arrlength(Arr) (sizeof((Arr)) / sizeof(*(Arr)))
#define cb_dyn_reserve(Dynarr, HowMany) cb_dyn_reserve_custom((Dynarr), (HowMany), values, count, capacity)
#define cb_dyn_free(Dynarr) cb_dyn_free_custom((Dynarr), values, count)
#define cb_dyn_push(Dynarr, Node) cb_dyn_push_custom((Dynarr), (Node), values, count, capacity)
#define cb_dyn_append(Dynarr, Array, Size) cb_dyn_append_custom((Dynarr), (Array), (Size), values, count, capacity)
#define cb_cmd_push(Dynarr, Value) cb_dyn_push(Dynarr, Value)
#define cb_cmd_append_dyn(Dynarr, Values, Count) cb_dyn_append((Dynarr), (Values), (Count));
-#define cb_cmd_append(Dynarr, ...) cb_dyn_append((Dynarr), \
- ((char*[]){__VA_ARGS__}), \
- (sizeof((char*[]){__VA_ARGS__}) / sizeof(char*)))
-#define cb_println(Level, Fmt, ...) cb_print((Level), Fmt "\n", ##__VA_ARGS__)
-#define cb_rebuild_self(argc, argv) _cb_rebuild(argc, argv, __FILE__, (char *)0)
-#define cb_rebuild_self_with(argc, argv, ...) _cb_rebuild(argc, argv, __FILE__, __VA_ARGS__, (char*)0)
+#define cb_cmd_append(Dynarr, ...) cb_dyn_append((Dynarr), ((const char*[]){__VA_ARGS__}), (sizeof((const char*[]){__VA_ARGS__}) / sizeof(char*)))
+#define cb_rebuild_self(argc, argv) _cb_rebuild(argc, argv, __FILE__, NULL)
+#define cb_rebuild_self_with(argc, argv, ...) _cb_rebuild(argc, argv, __FILE__, __VA_ARGS__, NULL)
#define cb_is_outdated(OutputFile, ...) _cb_is_outdated((OutputFile), __VA_ARGS__, 0)
-#define cb_cmd_run(Cmd, ...) _cb_cmd_run((Cmd), (struct Cb_Cmd_RunArgs) { \
- .async = false, \
- .reset = true, \
- __VA_ARGS__ \
- })
#define cb_proclist_push(Dynarr, Value) cb_dyn_push(Dynarr, Value)
+#define cb_cmd_exec(Cmd) \
+ _cb_cmd_run((Cmd), \
+ (struct CB_Cmd_RunArgs) { \
+ .async = false, \
+ .reset = true, \
+ })
+#define cb_cmd_exec_with_option(Cmd, ...) \
+ _cb_cmd_run((Cmd), \
+ (struct CB_Cmd_RunArgs) { \
+ .async = false, \
+ .reset = true, \
+ __VA_ARGS__ \
+ })
#define cb_dyn_free_custom(Dynarr, Values, Count) \
do { \
(Dynarr)->Count += (Size); \
} while(0)
-#define cb_handle_write(Handle, Content) _is_literal(Content) ? _cb_handle_write_lit(Handle, Content) : _cb_handle_write_dyn(Handle, Content)
-#define _cb_handle_write_lit(Handle, Content) \
- _cb_handle_write((Handle), (Content), (sizeof((Content)) / sizeof(*(Content))))
-#define _cb_handle_write_dyn(Handle, Content) \
- _cb_handle_write((Handle), (Content), strlen((Content)))
-#define _is_literal(x) _is_literal_(x)
-#define _is_literal_(x) _is_literal_f(#x, sizeof(#x) - 1)
-
-static char* cb_format(char *format, ...);
-static void cb_print(CB_LogLevel level, char *fmt, ...);
-static char* cb_getenv(char *varname);
-static bool cb_setenv(char *varname, char *value);
-static void cb_cmd_print(CB_Cmd *cmd);
+#define stack_push(Head, Nodeptr) ull_push_front_custom((Head), (Head), (Nodeptr), next)
+#define stack_pop(Head) (Head ? (Head = Head->next) : 0)
+#define ull_push_front_custom(Head, Last, Nodeptr, Next) \
+ (!(Head) ? (Head) = (Nodeptr), (Last) = (Nodeptr) \
+ : ((Nodeptr)->Next = (Head), (Head) = (Nodeptr)))
+
+#define queue_push(Head, Last, Nodeptr) ull_push_back_custom((Head), (Last), (Nodeptr), next)
+#define queue_pop(Head) (Head ? (Head = Head->next) : 0)
+#define ull_push_back_custom(Head, Last, Nodeptr, Next) \
+ (!(Head) ? (Head) = (Last) = (Nodeptr) \
+ : ((Last) ? ((Last)->Next = (Nodeptr), (Last) = (Nodeptr)) \
+ : ((Head)->Next = (Last) = (Nodeptr))))
+
+#define bll_push_front(Head, Last, Nodeptr) bll_push_front_custom(Head, Last, Nodeptr, next, prev)
+#define bll_push_back(Head, Last, Nodeptr) bll_push_back_custom(Head, Last, Nodeptr, next, prev)
+#define bll_push_front_custom(Head, Last, Nodeptr, Next, Prev) \
+ (!(Head) ? (Head) = (Last) = (Nodeptr) \
+ : ((Nodeptr)->Next = (Head), (Head)->Prev = (Nodeptr), \
+ (Head) = (Nodeptr)))
+#define bll_push_back_custom(DLLNodeHead, DLLNodeLast, NodeToInsertptr, Next, Prev) \
+ (!DLLNodeHead \
+ ? DLLNodeHead = DLLNodeLast = NodeToInsertptr \
+ : (DLLNodeLast->Next = NodeToInsertptr, \
+ NodeToInsertptr->Prev = DLLNodeLast, DLLNodeLast = NodeToInsertptr))
+#define bll_remove(Head, Last, Nodeptr) \
+ ((Head) == (Last) && (Head) == (Nodeptr) \
+ ? (Head) = (Last) = 0 \
+ : ((Last) == (Nodeptr) \
+ ? ((Last) = (Last)->prev, (Last)->next = 0) \
+ : ((Head) == (Nodeptr) \
+ ? ((Head) = (Head)->next, (Head)->prev = 0) \
+ : ((Nodeptr)->prev->next = (Nodeptr)->next, \
+ (Nodeptr)->next->prev = (Nodeptr)->prev))))
+#define bll_pop(Head, Last) bll_pop_back(Head, Last)
+#define bll_pop_back(Head, Last) \
+ (!(Last) \
+ ? 0 \
+ : (!(Last)->prev ? (Head) = (Last) = 0 \
+ : ((Last)->prev->next = 0, (Last) = (Last)->prev)))
+#define bll_pop_front(Head, Last) \
+ (!(Head) \
+ ? 0 \
+ : (!(Head)->next ? (Head) = (Last) = 0 \
+ : ((Head)->next->prev = 0, (Head) = (Head)->next)))
+
+#define cb_file_write(Handle, Content) _Generic((Content), \
+ char *: cb_file_write_cstr, \
+ const char *: cb_file_write_cstr, \
+ CB_StringView: cb_file_write_sv \
+ )((Handle), (Content))
+
+static char* cb_format(const char *format, ...);
+static void cb_print(CB_LogLevel level, const char *fmt, ...);
+static char* cb_getenv(const char *varname);
+static bool cb_setenv(const char *varname, const char *value);
+static void cb_cmd_print(const CB_Cmd *cmd);
static void cb_process_wait(CB_Process *handle);
static void cb_proclist_wait(CB_ProcessList *procs);
-static CB_Handle cb_handle_open(char *path, CB_AccessFlag permission);
-static void cb_handle_close(CB_Handle fd);
-static char* cb_handle_read(CB_Handle fd);
-static bool cb_dir_create(char *path);
-static void cb_dir_delete(char *path);
-static void cb_file_delete(char *path);
-static bool cb_file_rename(char *path, char *to);
-static bool cb_file_exists(char *path);
-static bool cb_cli_contains(int32_t argc, char **argv, char *target);
-
-internal void _cb_handle_write(CB_Handle fd, char *buffer, size_t buffsize);
-internal char* _cb_format(char *format, va_list args);
-internal bool _cb_need_rebuild(char *output_path, struct CB_PathList sources);
-internal void _cb_rebuild(int argc, char **argv, char *cb_src, ...);
-internal bool _cb_is_outdated(char *output, ...);
-internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct Cb_Cmd_RunArgs args);
-internal size_t _last_occurance_of(char *string, char ch);
-internal bool _is_literal_f(char *str, size_t l);
+static bool cb_dir_create(const char *path);
+static void cb_dir_delete(const char *path);
+static CB_Handle cb_file_open(const char *path, CB_AccessFlag permission);
+static void cb_file_close(CB_Handle fd);
+static CB_StringView cb_file_read(CB_Handle fd);
+static void cb_file_write_cstr(CB_Handle fd, const char *cstr);
+static void cb_file_write_sv(CB_Handle fd, CB_StringView sv);
+static void cb_file_delete(const char *path);
+static bool cb_file_rename(const char *path, const char *to);
+static bool cb_file_exists(const char *path);
+static bool cb_symlink_create(const char *target, const char *linkpath);
+static bool cb_cli_contains(int32_t argc, char **argv, const char *target);
+static bool cb_view_starts_with(const char *string, CB_StringView sv);
+static void cb_view_trim(CB_StringView *sv);
+static CB_StringBuilder cb_view_split(CB_StringView sv, char delim);
+static void cb_sb_append(CB_StringBuilder *sb, CB_StringView sv);
+static void cb_sb_concat(CB_StringBuilder *sb, const CB_StringBuilder *other);
+
+internal void _cb_file_write_buffer(CB_Handle fd, const char *buffer, size_t size);
+internal char* cb_format_va(const char *format, va_list args);
+internal bool _cb_need_rebuild(const char *output_path, struct CB_PathList sources);
+internal void _cb_rebuild(int argc, char **argv, const char *cb_src, ...);
+internal bool _cb_is_outdated(const char *output, ...);
+internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct CB_Cmd_RunArgs args);
+internal size_t _last_occurance_of(const char *string, char ch);
// ==============================================================================
// Implementation
-static char* cb_format(char *format, ...) {
- va_list args;
+static char* cb_format(const char *format, ...) {
+ va_list args = {0};
va_start(args, format);
- char *res = _cb_format(format, args);
+ char *res = cb_format_va(format, args);
va_end(args);
return res;
}
-static void cb_print(CB_LogLevel level, char *fmt, ...) {
- va_list args;
+static void cb_print(CB_LogLevel level, const char *fmt, ...) {
+ cb_assert(fmt != NULL);
+ cb_assert(level >= CB_LogLevel_None && level <= CB_LogLevel_COUNT);
switch (level) {
case CB_LogLevel_Info: {
printf(ANSI_COLOR_CYAN "[INFO] ");
}
printf(ANSI_COLOR_RESET);
- print_str: ;
+ print_str:;
+ va_list args = {0};
va_start(args, fmt);
- printf("%s", _cb_format(fmt, args));
+ printf("%s", cb_format_va(fmt, args));
va_end(args);
}
-static char* cb_getenv(char *varname) {
+static char* cb_getenv(const char *varname) {
+ cb_assert(varname != NULL);
#if OS_WINDOWS
char *res = malloc(MAX_ENVVAR);
if (!GetEnvironmentVariableA(varname, res, MAX_ENVVAR)) {
#endif
}
-static bool cb_setenv(char *varname, char *value) {
+static bool cb_setenv(const char *varname, const char *value) {
+ cb_assert(varname != NULL);
+ cb_assert(value != NULL);
#if OS_WINDOWS
return SetEnvironmentVariableA(varname, value);
#else
#endif
}
-static void cb_cmd_print(CB_Cmd *cmd) {
+static void cb_cmd_print(const CB_Cmd *cmd) {
+ cb_assert(cmd != NULL);
cb_print(CB_LogLevel_Info, "Command: `");
- for (int32_t i = 0; i < cmd->count; ++i) {
+ for (size_t i = 0; i < cmd->count; ++i) {
printf("%s ", cmd->values[i]);
}
printf("\b`\n");
}
static void cb_process_wait(CB_Process *proc) {
+ cb_assert(proc != NULL);
if (proc->handle == CB_PROC_INVALID) {
- cb_println(CB_LogLevel_Warn, "Waiting on invalid process handle");
- return;
+ cb_print(CB_LogLevel_Error, "Waiting on invalid process handle\n");
+ cb_assert(false);
}
#if OS_WINDOWS
CloseHandle(proc->handle);
#else
int32_t status = 0;
- cb_assert(waitpid(proc->handle, &status, 0) == proc->handle);
+ int32_t wait_res = waitpid(proc->handle, &status, 0);
+ cb_assert(wait_res == proc->handle);
if (WIFEXITED(status)) {
proc->status_code = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
}
static void cb_proclist_wait(CB_ProcessList *procs) {
+ cb_assert(procs != NULL);
+ if (procs->count == 0) { return; }
for (size_t i = 0; i < procs->count; ++i) {
cb_process_wait(&procs->values[i]);
}
cb_dyn_free(procs);
}
-static CB_Handle cb_handle_open(char *path, CB_AccessFlag permission) {
+static CB_Handle cb_file_open(const char *path, CB_AccessFlag permission) {
+ cb_assert(path != NULL);
+
#if OS_WINDOWS
SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES), 0, 0};
DWORD access_flags = 0;
FILE_ATTRIBUTE_NORMAL, 0);
#else
int32_t flags = O_CREAT;
- if (permission & CB_AccessFlag_Append) {
- flags |= (permission & CB_AccessFlag_Read) ? O_RDWR : O_WRONLY;
- flags |= O_APPEND;
- } else if ((permission & CB_AccessFlag_Write) &&
- (permission & CB_AccessFlag_Read)) {
+ if ((permission & CB_AccessFlag_Read) && ((permission & CB_AccessFlag_Write) ||
+ (permission & CB_AccessFlag_Append))) {
flags |= O_RDWR;
- } else if (permission & CB_AccessFlag_Read) {
- flags |= O_RDONLY;
- } else if (permission & CB_AccessFlag_Write) {
- flags |= O_WRONLY | O_TRUNC;
+ } else {
+ if (permission & CB_AccessFlag_Read) {
+ flags |= O_RDONLY;
+ } else if ((permission & CB_AccessFlag_Write) ||
+ (permission & CB_AccessFlag_Append)) {
+ flags |= O_WRONLY;
+ }
}
- return open(path, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (permission & CB_AccessFlag_Append) { flags |= O_APPEND; }
+ int32_t res = open(path, flags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (res == -1) { return CB_HANDLE_INVALID; }
+ return (CB_Handle)res;
#endif
}
-static void cb_handle_close(CB_Handle fd) {
+static void cb_file_close(CB_Handle fd) {
if (fd == CB_HANDLE_INVALID) {
- cb_println(CB_LogLevel_Warn, "Closing invalid handle");
- return;
+ cb_print(CB_LogLevel_Error, "Closing invalid handle\n");
+ cb_assert(false);
}
#if OS_WINDOWS
FlushFileBuffers(fd);
CloseHandle(fd);
#else
- fsync(fd);
close(fd);
#endif
}
-static char* cb_handle_read(CB_Handle fd) {
+static CB_StringView cb_file_read(CB_Handle fd) {
if (fd == CB_HANDLE_INVALID) {
- cb_println(CB_LogLevel_Warn, "Reading from invalid handle");
- return 0;
+ cb_print(CB_LogLevel_Warn, "Reading from invalid handle\n");
+ return (CB_StringView) {0};
}
#if OS_WINDOWS
LARGE_INTEGER file_size = {0};
GetFileSizeEx(fd, &file_size);
- uint64_t to_read = file_size.QuadPart;
- uint64_t total_read = 0;
- uint8_t *ptr = malloc(to_read + 1);
+ LONGLONG size = file_size.QuadPart;
+ LONGLONG total_read = 0;
+ char *buffer = malloc((size_t)(size + 1));
+ ReadFile(fd, buffer, (DWORD)size, NULL, NULL);
+ buffer[size] = 0;
+ return (CB_StringView) {
+ .data = buffer,
+ .length = size,
+ };
+#else
+ struct stat file_stat = {0};
+ if (fstat(fd, &file_stat) == -1 || file_stat.st_size <= 0) { return (CB_StringView) {0}; }
+ char *res = malloc(file_stat.st_size + 1);
+ if (pread(fd, res, file_stat.st_size, 0) == -1) { return (CB_StringView) {0}; }
+ res[file_stat.st_size] = 0;
+ return (CB_StringView) {
+ .data = res,
+ .length = file_stat.st_size,
+ };
+#endif
+}
- for(;total_read < to_read;) {
- uint64_t amount64 = to_read - total_read;
- uint32_t amount32 = amount64 > 0xFFFFFFFF ? 0xFFFFFFFF : (uint32_t)amount64;
- OVERLAPPED overlapped = {0};
- DWORD bytes_read = 0;
- (void)ReadFile(fd, ptr + total_read, amount32, &bytes_read, &overlapped);
- total_read += bytes_read;
- if(bytes_read != amount32) { break; }
- }
+static void cb_file_write_sv(CB_Handle fd, CB_StringView sv) {
+ _cb_file_write_buffer(fd, sv.data, sv.length);
+}
- ptr[to_read] = 0;
- if(total_read != to_read) {
- free(ptr);
- ptr = 0;
- }
- return ptr;
-#else
- struct stat file_stat;
- if (!fstat(fd, &file_stat)) {
- char *res = (char *)malloc(file_stat.st_size);
- if (pread(fd, res, file_stat.st_size, 0) >= 0) {
- return res;
- }
+static void cb_file_write_cstr(CB_Handle fd, const char *buffer) {
+ if (fd == CB_HANDLE_INVALID) {
+ cb_print(CB_LogLevel_Error, "Writing to invalid handle\n");
+ assert(false);
}
- return 0;
-#endif
+ size_t buffsize = strlen(buffer);
+ _cb_file_write_buffer(fd, buffer, buffsize);
}
-static bool cb_dir_create(char *path) {
+static bool cb_dir_create(const char *path) {
+ cb_assert(path != NULL);
int32_t mkdir_res = _cb_platform_mkdir(path);
if (mkdir_res < 0 && errno == ENOENT) {
size_t parent_end = _last_occurance_of(path, '/');
free(parent);
_cb_platform_mkdir(path);
}
-
return !mkdir_res;
}
-static void cb_dir_delete(char *path) {
+static void cb_dir_delete(const char *path) {
#if OS_WINDOWS
_rmdir(path);
#else
#endif
}
-static void cb_file_delete(char *path) {
+static void cb_file_delete(const char *path) {
#if OS_WINDOWS
_unlink(path);
#else
#endif
}
-static bool cb_file_rename(char *path, char *to) {
+static bool cb_file_rename(const char *path, const char *to) {
#if OS_WINDOWS
- return MoveFileEx(path, to,
- MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
+ return MoveFileExA(path, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
#else
return !rename(path, to);
#endif
}
-static bool cb_file_exists(char *path) {
+static bool cb_file_exists(const char *path) {
#if OS_WINDOWS
# define F_OK 0
# define access _access
return access(path, F_OK) == 0;
}
-static bool cb_cli_contains(int32_t argc, char **argv, char *target) {
+static bool cb_symlink_create(const char *target, const char *linkpath) {
+ cb_assert(target != NULL);
+ cb_assert(linkpath != NULL);
+
+#if OS_WINDOWS
+#else
+ int32_t res = symlink(target, linkpath);
+ return res == 0;
+#endif
+}
+
+static void cb_file_iter_begin(CB_FileIterator *it, const char *rootpath) {
+ memset(&it->handle, 0, sizeof it->handle);
+#if OS_WINDOWS
+ // TODO(lb): file iteration on win32
+#else
+ it->handle.path = rootpath;
+ it->handle.dir = opendir(rootpath);
+#endif
+}
+
+static bool cb_file_iter_next(CB_FileIterator *it, CB_FileInfo *output) {
+ static const char *const currdir = ".";
+ static const char *const parentdir = "..";
+
+#if OS_WINDOWS
+ // TODO(lb): file iteration on win32
+#else
+ struct dirent *entry = NULL;
+ char *filepath = NULL;
+
+ if (!it->handle.dir) { return false; }
+ for (;;) {
+ const char *filename = NULL;
+ do {
+ filename = NULL;
+ entry = readdir(it->handle.dir);
+ if (!entry) { return false; }
+ filename = entry->d_name;
+ } while ((strcmp(filename, currdir) == 0) || (strcmp(filename, parentdir) == 0));
+ if (!filename) { return false; }
+
+ int32_t filepath_size = snprintf(NULL, 0, "%s/%s", it->handle.path, filename) + 1;
+ filepath = malloc(filepath_size);
+ if (!filepath) { return false; }
+ snprintf(filepath, filepath_size, "%s/%s", it->handle.path, filename);
+
+ struct stat file_stat = {0};
+ if (stat(filepath, &file_stat) != 0) {
+ free(filepath);
+ return false;
+ }
+
+ CB_FileType filetype = 0;
+ switch (file_stat.st_mode & S_IFMT) {
+ case S_IFBLK: filetype = CB_FileType_Device_Block; break;
+ case S_IFCHR: filetype = CB_FileType_Device_Char; break;
+ case S_IFDIR: filetype = CB_FileType_Directory; break;
+ case S_IFIFO: filetype = CB_FileType_Pipe; break;
+ case S_IFLNK: filetype = CB_FileType_Link; break;
+ case S_IFSOCK: filetype = CB_FileType_Socket; break;
+ case S_IFREG: filetype = CB_FileType_Regular; break;
+ }
+
+ if (!(it->filter_allowed & filetype)) {
+ free(filepath);
+ } else {
+ if (!output) {
+ free(filepath);
+ } else {
+ output->path = filepath;
+ output->type = filetype;
+ output->size = file_stat.st_size;
+ }
+ return true;
+ }
+ }
+ return false;
+#endif
+}
+
+static void cb_file_iter_end(CB_FileIterator *it) {
+#if OS_WINDOWS
+ // TODO(lb): file iteration on win32
+#else
+ closedir(it->handle.dir);
+#endif
+}
+
+static bool cb_cli_contains(int32_t argc, char **argv, const char *target) {
for (int32_t i = 0; i < argc; ++i) {
if (!strcmp(argv[i], target)) {
return true;
return false;
}
+static bool cb_view_starts_with(const char *string, CB_StringView sv) {
+ if (string == NULL || sv.length == 0 || sv.data == NULL) { return false; }
+ for (int32_t i = 0; i < sv.length; ++i) {
+ if (string[i] == 0 || string[i] != sv.data[i]) { return false; }
+ }
+ return true;
+}
-internal bool _cb_is_outdated(char *output, ...) {
- struct CB_PathList sources = {};
- va_list args;
- va_start(args, output);
- for (;;) {
- char *path = va_arg(args, char*);
- if (!path) { break; }
- cb_dyn_push(&sources, path);
+static void cb_view_trim(CB_StringView *sv) {
+ cb_assert(sv != NULL);
+ if (sv->length <= 0) { return; }
+ cb_assert(sv->data != NULL);
+ cb_assert(sv->length > 0);
+
+ for (int32_t i = 0;
+ sv->data[i] == ' ' || sv->data[i] == '\n' || sv->data[i] == '\r' || sv->data[i] == '\t';
+ ++i) {
+ sv->length -= 1;
+ sv->data += 1;
+ }
+ for (int32_t i = sv->length - 1;
+ i >= 0 && sv->data[i] == ' ' || sv->data[i] == '\n' || sv->data[i] == '\r' || sv->data[i] == '\t';
+ --i) {
+ sv->length -= 1;
}
- va_end(args);
- return _cb_need_rebuild(output, sources);
}
-internal void _cb_handle_write(CB_Handle fd, char *buffer, size_t buffsize) {
- if (fd == CB_HANDLE_INVALID) {
- cb_println(CB_LogLevel_Warn, "Writing to invalid handle");
- return;
+static CB_StringBuilder cb_view_split(CB_StringView sv, char delim) {
+ CB_StringBuilder res = {0};
+ if (sv.data == NULL || sv.length == 0) { return res; }
+ size_t prev = 0;
+ for (size_t i = 0; i < sv.length;) {
+ if (sv.data[i] == delim) {
+ if (prev != i) {
+ cb_sb_append(&res, (CB_StringView) {
+ .data = sv.data + prev,
+ .length = i - prev,
+ });
+ }
+ do {
+ prev = ++i;
+ } while (sv.data[i] == delim);
+ } else {
+ ++i;
+ }
+ }
+ if (prev != sv.length) {
+ cb_sb_append(&res, (CB_StringView) {
+ .data = sv.data + prev,
+ .length = sv.length - prev,
+ });
+ }
+ cb_assert(res.node_count > 0);
+ return res;
+}
+
+static CB_StringView cb_view_from_cstr(const char *cstr) {
+ CB_StringView res = {0};
+ res.data = cstr;
+ res.length = strlen(cstr);
+ return res;
+}
+
+static void cb_sb_append(CB_StringBuilder *sb, CB_StringView sv) {
+ cb_assert(sb != NULL);
+ if (sv.length == 0 || sv.data == NULL) { return; }
+ struct CB_StringBuilder_Node *node = malloc(sizeof *node);
+ cb_assert(node != NULL);
+ memset(node, 0, sizeof *node);
+ node->value = sv;
+ bll_push_back(sb->first, sb->last, node);
+ sb->total_length += sv.length;
+ sb->node_count += 1;
+}
+
+static void cb_sb_concat(CB_StringBuilder *sb, const CB_StringBuilder *other) {
+ cb_assert(sb != NULL);
+ cb_assert(sb->node_count >= 0);
+ cb_assert(sb->total_length >= 0);
+ cb_assert(other != NULL);
+ cb_assert(other->node_count >= 0);
+ cb_assert(other->total_length >= 0);
+ sb->node_count += other->node_count;
+ sb->total_length += other->total_length;
+ bll_push_back(sb->first, sb->last, other->first);
+ sb->last = other->last;
+}
+
+static CB_StringView cb_sb_join_with_ch(CB_StringBuilder sb, char ch) {
+ CB_StringView sv = {0};
+ sv.data = malloc(sb.total_length + sb.node_count);
+ sv.length = sb.total_length + sb.node_count - 1;
+ int32_t offset = 0;
+ for (struct CB_StringBuilder_Node *curr = sb.first; curr;) {
+ memcpy((char*)(sv.data + offset), curr->value.data, curr->value.length);
+ offset += curr->value.length;
+ ((char*)sv.data)[offset++] = ch;
+ struct CB_StringBuilder_Node *next = curr->next;
+ free(curr);
+ curr = next;
}
+ ((char*)sv.data)[sv.length] = 0;
+ return sv;
+}
- while (!buffer[buffsize - 1]) { buffsize -= 1; }
+internal void _cb_file_write_buffer(CB_Handle fd, const char *buffer, size_t size) {
+ cb_assert(fd != CB_HANDLE_INVALID);
+ cb_assert(buffer != NULL);
+ cb_assert(size > 0);
+ while (!buffer[size - 1]) { size -= 1; }
#if OS_WINDOWS
uint64_t to_write = buffsize;
uint64_t total_write = 0;
if(bytes_written != amount32) { break; }
}
#else
- write(fd, buffer, buffsize);
+ write(fd, buffer, size);
#endif
}
-internal char* _cb_format(char *format, va_list args) {
+internal bool _cb_is_outdated(const char *output, ...) {
+ struct CB_PathList sources = {0};
+ va_list args;
+ va_start(args, output);
+ for (;;) {
+ char *path = va_arg(args, char*);
+ if (!path) { break; }
+ cb_dyn_push(&sources, path);
+ }
+ va_end(args);
+ return _cb_need_rebuild(output, sources);
+}
+
+
+internal char* cb_format_va(const char *format, va_list args) {
va_list args2;
va_copy(args2, args);
- uint32_t needed_bytes = vsnprintf(0, 0, format, args2) + 1;
+ size_t needed_bytes = (size_t)vsnprintf(0, 0, format, args2) + 1;
va_end(args2);
- char *res = (char *)malloc(needed_bytes);
+ char *res = malloc(needed_bytes);
(void)vsnprintf(res, needed_bytes, format, args);
return res;
}
-internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct Cb_Cmd_RunArgs args) {
- CB_Process res = {};
+internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct CB_Cmd_RunArgs args) {
+ CB_Process res = {0};
#if OS_WINDOWS
- char cmdline[32767] = {};
+ char cmdline[32767] = {0};
size_t offset = 0;
for (size_t i = 0; i < cmd->count; ++i) {
strcat(cmdline, strstr(cmd->values[i], " ") || strstr(cmd->values[i], "\\")
if (i != cmd->count - 1) { strcat(cmdline, " "); }
}
- STARTUPINFO si = {0};
+ STARTUPINFOA si = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = args.stdin ? args.stdin : GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = args.stdout ? args.stdout : GetStdHandle(STD_OUTPUT_HANDLE);
si.hStdError = args.stderr ? args.stderr : GetStdHandle(STD_ERROR_HANDLE);
- PROCESS_INFORMATION pi = {};
+ PROCESS_INFORMATION pi = {0};
if (!CreateProcessA(0, cmdline, 0, 0, TRUE, 0, 0, 0, &si, &pi)) {
LPVOID lpMsgBuf;
DWORD error = GetLastError();
FORMAT_MESSAGE_IGNORE_INSERTS,
0, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf, 0, 0);
- cb_println(CB_LogLevel_Error, "Child process `%s` creation failed with error %u: %s",
- cmd->values[0], error, lpMsgBuf);
+ cb_print(CB_LogLevel_Error, "Child process `%s` creation failed with error %u: %s\n",
+ cmd->values[0], error, lpMsgBuf);
exit(-1);
}
#else
res.handle = fork();
if (res.handle < 0) {
- cb_println(CB_LogLevel_Error, "Child process `%s` creation failed with error %d: %s\n",
- cmd->values[0], errno, strerror(errno));
+ cb_print(CB_LogLevel_Error, "Child process `%s` creation failed with error %d: %s\n",
+ cmd->values[0], errno, strerror(errno));
exit(-1);
} else if (!res.handle) {
if (args.stdout) { dup2(args.stdout, STDOUT_FILENO); }
dup2(args.stdin, STDIN_FILENO);
}
- CB_Cmd _cmd = {};
+ CB_Cmd _cmd = {0};
cb_cmd_append_dyn(&_cmd, cmd->values, cmd->count);
cb_cmd_push(&_cmd, 0);
if (execvp(_cmd.values[0], (char **)_cmd.values) < 0) {
- cb_println(CB_LogLevel_Error, "Child process `%s` creation failed with error %d: %s\n",
- cmd->values[0], errno, strerror(errno));
+ cb_print(CB_LogLevel_Error, "Child process `%s` creation failed with error %d: %s\n",
+ cmd->values[0], errno, strerror(errno));
exit(-1);
}
// NOTE(lb): unreachable, execvp only returns on error.
return res;
}
-internal void _cb_rebuild(int argc, char **argv, char *builder_src, ...) {
+internal void _cb_rebuild(int argc, char **argv, const char *builder_src, ...) {
cb_assert(argc >= 1);
char *exe_name = argv[0];
- struct CB_PathList sources = {};
+ struct CB_PathList sources = {0};
cb_dyn_push(&sources, builder_src);
va_list args;
va_start(args, builder_src);
cb_dyn_free(&sources);
return;
}
- cb_println(CB_LogLevel_Info, "rebuilding %s", exe_name);
+ cb_print(CB_LogLevel_Info, "rebuilding %s\n", exe_name);
#if OS_WINDOWS
char *exe_name_old = cb_format("%s.old", exe_name);
if (!cb_file_rename(exe_name, exe_name_old)) {
- cb_println(CB_LogLevel_Info, "File rename failed: %s -> %s",
- exe_name, exe_name_old);
+ cb_print(CB_LogLevel_Info, "File rename failed: %s -> %s\n",
+ exe_name, exe_name_old);
exit(-1);
}
#endif
- CB_Cmd cmd = {};
+ CB_Cmd cmd = {0};
cb_cmd_append(&cmd, CB_CMD_REBUILD_SELF(exe_name, builder_src));
cb_print(CB_LogLevel_Info, "running: `");
- for (int32_t i = 0; i < cmd.count; ++i) {
+ for (size_t i = 0; i < cmd.count; ++i) {
printf("%s ", cmd.values[i]);
}
printf("\b`\n");
- CB_Process recompiler = cb_cmd_run(&cmd);
+ CB_Process recompiler = cb_cmd_exec(&cmd);
if (recompiler.status_code) {
#if OS_WINDOWS
cb_file_rename(exe_name_old, exe_name);
}
cb_cmd_push(&cmd, exe_name);
- cb_cmd_append_dyn(&cmd, argv, argc);
- (void)cb_cmd_run(&cmd);
+ cb_cmd_append_dyn(&cmd, argv, (size_t)argc);
+ (void)cb_cmd_exec(&cmd);
exit(0);
}
-internal bool _cb_need_rebuild(char *output_path, struct CB_PathList sources) {
+internal bool _cb_need_rebuild(const char *output_path, struct CB_PathList sources) {
#if OS_WINDOWS
- FILETIME output_mtime_large = {};
+ FILETIME output_mtime_large = {0};
HANDLE output_handle = CreateFileA(output_path, GENERIC_READ,
FILE_SHARE_READ, 0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
}
CloseHandle(output_handle);
- ULARGE_INTEGER output_mtime = {};
+ ULARGE_INTEGER output_mtime = {0};
output_mtime.LowPart = output_mtime_large.dwLowDateTime;
output_mtime.HighPart = output_mtime_large.dwHighDateTime;
for (size_t i = 0; i < sources.count; ++i) {
- FILETIME source_mtime_large = {};
+ FILETIME source_mtime_large = {0};
HANDLE source_handle = CreateFileA(sources.values[i], GENERIC_READ,
FILE_SHARE_READ, 0, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
}
CloseHandle(output_handle);
- ULARGE_INTEGER source_mtime = {};
+ ULARGE_INTEGER source_mtime = {0};
source_mtime.LowPart = source_mtime_large.dwLowDateTime;
source_mtime.HighPart = source_mtime_large.dwHighDateTime;
if (output_mtime.QuadPart < source_mtime.QuadPart) {
struct stat source_stat = {};
if (stat(sources.values[i], &source_stat) < 0) {
rebuild_failure:
- cb_println(CB_LogLevel_Error,
- "`%s` modification time unreadable: %s",
- sources.values[i], strerror(errno));
+ cb_print(CB_LogLevel_Error,
+ "`%s` modification time unreadable: %s\n",
+ sources.values[i], strerror(errno));
exit(-1);
}
if (output_stat.st_mtime < source_stat.st_mtime) { return true; }
#endif
}
-internal size_t _last_occurance_of(char *string, char ch) {
- char *res = string;
- for (char *curr = string; curr && *curr; ++curr) {
+internal size_t _last_occurance_of(const char *string, char ch) {
+ const char *res = string;
+ for (const char *curr = string; curr && *curr; ++curr) {
if (*curr == ch) { res = curr; }
}
- return res - string;
-}
-
-internal bool _is_literal_f(char *str, size_t l) {
- char *e = str + l;
- if (str[0] == 'L') str++;
- if (str[0] != '"') return false;
- for (; str != e; str = strchr(str + 1, '"')) {
- if (!str) { return false; }
- for (str++;
- *str == '\f' || *str == '\n' || *str == '\r' ||
- *str == '\t' || *str == '\v';
- ++str);
- if (*str != '"') { return false; }
- }
- return true;
+ return (size_t)(res - string);
}
#endif
--- /dev/null
+#ifndef CBUILD_BIBLO_H
+#define CBUILD_BIBLO_H
+
+#if OS_WINDOWS
+#else
+# include <time.h>
+#endif
+
+typedef struct CB_BibloElement {
+ CB_StringView name;
+ CB_StringView brief;
+ CB_StringView description;
+ CB_StringView ref_block;
+ CB_StringBuilder aliases;
+} CB_BibloElement;
+
+struct CB_BibloParser {
+ CB_StringView prefix;
+ CB_StringView content;
+ ssize_t offset;
+ CB_Handle hfile;
+};
+
+static void cb_biblo_build_man(const char *rootpath, const char *output_path);
+
+internal void cb_biblo_generate_troff(const CB_BibloElement *element, CB_StringView path_dirdoc, const char *source_path);
+internal CB_BibloElement cb_biblo_parse(CB_Handle file, struct CB_BibloParser *parser);
+internal void cb_biblo_parser_advance(struct CB_BibloParser *parser, size_t by);
+internal char cb_biblo_parser_peek(struct CB_BibloParser *parser, ssize_t offset);
+internal void cb_biblo_parser_until_newline(struct CB_BibloParser *parser, CB_StringView *sv);
+internal void cb_biblo_find_prefix(struct CB_BibloParser *parser);
+
+// ===============================================================
+// Implementation
+
+typedef uint8_t CB_BibloAnnotationID;
+enum {
+ CB_BibloAnnotationID_Name,
+ CB_BibloAnnotationID_Brief,
+ CB_BibloAnnotationID_Long,
+ CB_BibloAnnotationID_Alias,
+ CB_BibloAnnotationID_ReferenceBegin,
+ CB_BibloAnnotationID_ReferenceEnd,
+};
+
+static CB_StringView cb_biblo_annotation[] = {
+ [CB_BibloAnnotationID_Name] = cb_strinit("@name"),
+ [CB_BibloAnnotationID_Brief] = cb_strinit("@brief"),
+ [CB_BibloAnnotationID_Long] = cb_strinit("@long"),
+ [CB_BibloAnnotationID_Alias] = cb_strinit("@alias"),
+ [CB_BibloAnnotationID_ReferenceBegin] = cb_strinit("@ref-begin"),
+ [CB_BibloAnnotationID_ReferenceEnd] = cb_strinit("@ref-end"),
+};
+
+static void cb_biblo_build_man(const char *rootpath, const char *output_path)
+{
+ CB_FileIterator it = { .filter_allowed = CB_FileType_Regular | CB_FileType_Directory };
+ cb_file_iter_begin(&it, rootpath);
+
+ for (CB_FileInfo file = {0}; cb_file_iter_next(&it, &file); free(file.path)) {
+ if (file.type == CB_FileType_Directory) {
+ cb_biblo_build_man(file.path, output_path);
+ } else {
+ int32_t line = 1;
+ CB_Handle fd = cb_file_open(file.path, CB_AccessFlag_Read);
+ assert(fd != CB_HANDLE_INVALID);
+ struct CB_BibloParser parser = {0};
+ for (;;) {
+ CB_BibloElement doc = cb_biblo_parse(fd, &parser);
+ if (doc.name.length == 0) { break; }
+ CB_StringBuilder sb = {0};
+ cb_sb_append(&sb, cb_view_from_cstr(output_path));
+ cb_sb_append(&sb, cb_strlit("man3"));
+ CB_StringView doc_fullpath = cb_sb_join_with_ch(sb, '/');
+ cb_dir_create(doc_fullpath.data);
+ cb_biblo_generate_troff(&doc, doc_fullpath, file.path);
+ }
+ cb_file_close(fd);
+ }
+ }
+}
+
+internal void cb_biblo_generate_troff(const CB_BibloElement *element, CB_StringView path_dirdoc, const char *source_path)
+{
+ assert(element != NULL);
+
+ CB_StringBuilder sb_target = {0};
+ cb_sb_append(&sb_target, element->name);
+ cb_sb_append(&sb_target, cb_strlit("3"));
+ CB_StringView troff_filename = cb_sb_join_with_ch(sb_target, '.');
+
+ CB_StringBuilder sb = {0};
+ cb_sb_append(&sb, path_dirdoc);
+ cb_sb_append(&sb, troff_filename);
+ CB_StringView troff_filepath = cb_sb_join_with_ch(sb, '/');
+
+ cb_file_delete(troff_filepath.data);
+ CB_Handle troff_fd = cb_file_open(troff_filepath.data, CB_AccessFlag_Write);
+ assert(troff_fd != CB_HANDLE_INVALID);
+
+ char *date = NULL;
+#if OS_WINDOWS
+ // TODO(lb): retrieve date on win32
+#else
+ {
+ time_t now = time(NULL);
+ struct tm *t = localtime(&now);
+ size_t length = snprintf(NULL, 0, "%4d-%02d-%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday) + 1;
+ date = malloc(length);
+ assert(date != NULL);
+ snprintf(date, length, "%4d-%02d-%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday);
+ }
+#endif
+ assert(date != NULL);
+
+ cb_file_write(troff_fd, ".TH ");
+ cb_file_write(troff_fd, element->name);
+ cb_file_write(troff_fd, " 3 ");
+ cb_file_write(troff_fd, date);
+ cb_file_write(troff_fd, " GNU\n.SH NAME\n");
+ if (element->aliases.node_count == 0) {
+ cb_file_write(troff_fd, element->name);
+ } else {
+ for (struct CB_StringBuilder_Node *curr = element->aliases.first; curr; curr = curr->next) {
+ cb_file_write(troff_fd, curr->value);
+ if (curr->next) { cb_file_write(troff_fd, ",\\ "); }
+ }
+ }
+ if (element->brief.length > 0) {
+ cb_file_write(troff_fd, " \\- ");
+ cb_file_write(troff_fd, element->brief);
+ }
+
+ cb_file_write(troff_fd, "\n.SH SYNOPSIS\n.RS\n.nf\n");
+ cb_file_write(troff_fd, element->ref_block);
+ cb_file_write(troff_fd, "\n.fi\n.RE");
+
+ if (element->description.length > 0) {
+ cb_file_write(troff_fd, "\n.SH DESCRIPTION\n");
+ cb_file_write(troff_fd, element->description);
+ }
+ cb_file_write(troff_fd, "\n.SH FILES\n.TP\n.I ");
+ cb_file_write(troff_fd, source_path);
+
+ if (element->aliases.node_count > 0) {
+ for (struct CB_StringBuilder_Node *curr = element->aliases.first; curr; curr = curr->next) {
+ CB_StringBuilder sb_link = {0};
+ cb_sb_append(&sb_link, path_dirdoc);
+ cb_sb_append(&sb_link, curr->value);
+ CB_StringView link_filepath = cb_sb_join_with_ch(sb_link, '/');
+ memset(&sb_link, 0, sizeof sb_link);
+ cb_sb_append(&sb_link, link_filepath);
+ cb_sb_append(&sb_link, cb_strlit("3"));
+ link_filepath = cb_sb_join_with_ch(sb_link, '.');
+ cb_symlink_create(troff_filename.data, link_filepath.data);
+ }
+ }
+
+ cb_file_close(troff_fd);
+}
+
+internal CB_BibloElement cb_biblo_parse(CB_Handle file, struct CB_BibloParser *parser)
+{
+ CB_BibloElement res = {0};
+
+ assert(parser != NULL);
+ if (parser->hfile == 0) {
+ assert(file != 0);
+ parser->hfile = file;
+ parser->content = cb_file_read(file);
+ parser->offset = 0;
+ if (parser->content.length == 0) { return res; }
+ cb_biblo_find_prefix(parser);
+ }
+ assert(parser->content.data != NULL);
+ assert(parser->content.length > 0);
+
+ bool has_explicit_ref_block = false, started_ref_block = false;
+ if (parser->prefix.data == NULL || parser->prefix.length <= 0) { return res; }
+
+ // NOTE(lb): skip lines until one that starts with `PREFIX@`
+ for (;;) {
+ for (; !(cb_view_starts_with(&parser->content.data[parser->offset], parser->prefix));) {
+ cb_biblo_parser_until_newline(parser, NULL);
+ if (parser->offset >= parser->content.length) { return res; }
+ }
+ if (cb_biblo_parser_peek(parser, parser->prefix.length) != '@') {
+ cb_biblo_parser_until_newline(parser, NULL);
+ } else {
+ break;
+ }
+ }
+
+ for (CB_StringView line = {0}; cb_view_starts_with(&parser->content.data[parser->offset], parser->prefix);) {
+ cb_biblo_parser_advance(parser, parser->prefix.length);
+ CB_StringBuilder sb = {0};
+
+ if (cb_view_starts_with(&parser->content.data[parser->offset], cb_biblo_annotation[CB_BibloAnnotationID_Brief])) {
+ cb_biblo_parser_advance(parser, cb_biblo_annotation[CB_BibloAnnotationID_Brief].length);
+ if (parser->offset >= parser->content.length) {
+ cb_print(CB_LogLevel_Error, "Missing brief description.\n");
+ assert(false);
+ }
+ if (cb_biblo_parser_peek(parser, 0) == '\n') {
+ cb_biblo_parser_advance(parser, 1);
+ } else {
+ cb_biblo_parser_until_newline(parser, &line);
+ cb_view_trim(&line);
+ cb_sb_append(&sb, line);
+ }
+ for (; cb_view_starts_with(&parser->content.data[parser->offset], parser->prefix) &&
+ cb_biblo_parser_peek(parser, parser->prefix.length) != '@';) {
+ cb_biblo_parser_advance(parser, parser->prefix.length);
+ cb_biblo_parser_until_newline(parser, &line);
+ cb_view_trim(&line);
+ cb_sb_append(&sb, line);
+ }
+ if (cb_view_starts_with(&parser->content.data[parser->offset], parser->prefix)) {
+ assert(cb_biblo_parser_peek(parser, parser->prefix.length) == '@');
+ }
+ res.brief = cb_sb_join_with_ch(sb, ' ');
+ cb_view_trim(&res.brief);
+ }
+ else if (cb_view_starts_with(&parser->content.data[parser->offset], cb_biblo_annotation[CB_BibloAnnotationID_Name])) {
+ cb_biblo_parser_advance(parser, cb_biblo_annotation[CB_BibloAnnotationID_Name].length);
+ if (parser->offset >= parser->content.length) {
+ cb_print(CB_LogLevel_Error, "Missing name.\n");
+ assert(false);
+ }
+ if (cb_biblo_parser_peek(parser, 0) == '\n') {
+ cb_print(CB_LogLevel_Error, "Name field cannot be on a newline.\n");
+ assert(false);
+ }
+ cb_biblo_parser_until_newline(parser, &line);
+ cb_view_trim(&line);
+ cb_sb_append(&sb, line);
+ if (cb_view_starts_with(&parser->content.data[parser->offset], parser->prefix)) {
+ assert(cb_biblo_parser_peek(parser, parser->prefix.length) == '@');
+ }
+ res.name = cb_sb_join_with_ch(sb, ' ');
+ cb_view_trim(&res.name);
+ }
+ else if (cb_view_starts_with(&parser->content.data[parser->offset], cb_biblo_annotation[CB_BibloAnnotationID_Long])) {
+ cb_biblo_parser_advance(parser, cb_biblo_annotation[CB_BibloAnnotationID_Long].length);
+ if (parser->offset >= parser->content.length) {
+ cb_print(CB_LogLevel_Error, "Missing description.\n");
+ assert(false);
+ }
+ if (cb_biblo_parser_peek(parser, 0) == '\n') {
+ cb_biblo_parser_advance(parser, 1);
+ } else {
+ cb_biblo_parser_until_newline(parser, &line);
+ cb_view_trim(&line);
+ cb_sb_append(&sb, line);
+ }
+ for (; cb_view_starts_with(&parser->content.data[parser->offset], parser->prefix) &&
+ cb_biblo_parser_peek(parser, parser->prefix.length) != '@';) {
+ cb_biblo_parser_advance(parser, parser->prefix.length);
+ cb_biblo_parser_until_newline(parser, &line);
+ cb_view_trim(&line);
+ cb_sb_append(&sb, line);
+ }
+ if (cb_view_starts_with(&parser->content.data[parser->offset], parser->prefix)) {
+ assert(cb_biblo_parser_peek(parser, parser->prefix.length) == '@');
+ }
+ res.description = cb_sb_join_with_ch(sb, ' ');
+ cb_view_trim(&res.description);
+ }
+ else if (cb_view_starts_with(&parser->content.data[parser->offset], cb_biblo_annotation[CB_BibloAnnotationID_Alias])) {
+ cb_biblo_parser_advance(parser, cb_biblo_annotation[CB_BibloAnnotationID_Alias].length);
+ if (parser->offset >= parser->content.length) {
+ cb_print(CB_LogLevel_Error, "Missing alias list.\n");
+ assert(false);
+ }
+ if (cb_biblo_parser_peek(parser, 0) == '\n') {
+ cb_biblo_parser_advance(parser, 1);
+ assert(cb_view_starts_with(&parser->content.data[parser->offset], parser->prefix));
+ cb_biblo_parser_advance(parser, parser->prefix.length);
+ assert(cb_biblo_parser_peek(parser, 0) != '@');
+ }
+
+ for (; parser->offset < parser->content.length;) {
+ cb_biblo_parser_until_newline(parser, &line);
+ CB_StringBuilder split_line = cb_view_split(line, ',');
+ for (struct CB_StringBuilder_Node *curr = split_line.first; curr; curr = curr->next) {
+ cb_view_trim(&curr->value);
+ }
+ cb_sb_concat(&res.aliases, &split_line);
+ if (cb_view_starts_with(&parser->content.data[parser->offset], parser->prefix) &&
+ cb_biblo_parser_peek(parser, parser->prefix.length) != '@') {
+ cb_biblo_parser_advance(parser, parser->prefix.length);
+ } else {
+ break;
+ }
+ }
+ if (cb_view_starts_with(&parser->content.data[parser->offset], parser->prefix)) {
+ assert(cb_biblo_parser_peek(parser, parser->prefix.length) == '@');
+ }
+ }
+ else if (cb_view_starts_with(&parser->content.data[parser->offset], cb_biblo_annotation[CB_BibloAnnotationID_ReferenceBegin])) {
+ has_explicit_ref_block = true;
+ started_ref_block = true;
+ cb_biblo_parser_advance(parser, cb_biblo_annotation[CB_BibloAnnotationID_ReferenceBegin].length);
+ cb_biblo_parser_until_newline(parser, NULL);
+ if (parser->offset >= parser->content.length) {
+ cb_print(CB_LogLevel_Error, "Biblo entry doesn't reference any code block.\n");
+ assert(false);
+ }
+ for (; parser->offset < parser->content.length &&
+ !(cb_view_starts_with(&parser->content.data[parser->offset], parser->prefix) &&
+ cb_view_starts_with(&parser->content.data[parser->offset] + parser->prefix.length,
+ cb_biblo_annotation[CB_BibloAnnotationID_ReferenceEnd]));) {
+ cb_biblo_parser_until_newline(parser, &line);
+ cb_sb_append(&sb, line);
+ }
+ res.ref_block = cb_sb_join_with_ch(sb, '\n');
+ cb_view_trim(&res.ref_block);
+ }
+ else if (cb_view_starts_with(&parser->content.data[parser->offset], cb_biblo_annotation[CB_BibloAnnotationID_ReferenceEnd])) {
+ assert(started_ref_block == true);
+ cb_biblo_parser_until_newline(parser, NULL);
+ }
+ else {
+ cb_print(CB_LogLevel_Error, "Invalid biblo annotation.\n");
+ assert(false);
+ exit(-1);
+ }
+ }
+ if (!has_explicit_ref_block) {
+ CB_StringBuilder sb = {0};
+ for (CB_StringView line = {0};
+ parser->offset < parser->content.length && cb_biblo_parser_peek(parser, 0) != '\n';) {
+ cb_biblo_parser_until_newline(parser, &line);
+ cb_sb_append(&sb, line);
+ }
+ res.ref_block = cb_sb_join_with_ch(sb, '\n');
+ cb_view_trim(&res.ref_block);
+ }
+ if (res.name.length <= 0) {
+ cb_print(CB_LogLevel_Error, "Missing mandatory biblo name annotation.\n");
+ assert(false);
+ }
+ return res;
+}
+
+internal void cb_biblo_parser_advance(struct CB_BibloParser *parser, size_t by)
+{
+ assert(parser != NULL);
+ assert(parser->content.data != NULL);
+ assert(parser->content.length > 0);
+ assert(parser->offset + by >= 0);
+ parser->offset += by;
+ if (parser->offset > parser->content.length) {
+ parser->offset = parser->content.length;
+ }
+ assert(parser->offset <= parser->content.length);
+}
+
+internal char cb_biblo_parser_peek(struct CB_BibloParser *parser, ssize_t offset)
+{
+ assert(parser != NULL);
+ assert(parser->content.data != NULL);
+ assert(parser->content.length > 0);
+ assert(parser->offset + offset >= 0);
+ assert(parser->offset + offset <= parser->content.length);
+ return parser->content.data[parser->offset + offset];
+}
+
+internal void cb_biblo_parser_until_newline(struct CB_BibloParser *parser, CB_StringView *sv)
+{
+ assert(parser != NULL);
+ assert(parser->content.data != NULL);
+ assert(parser->content.length > 0);
+ if (parser->offset >= parser->content.length) {
+ parser->offset = parser->content.length;
+ return;
+ }
+
+ size_t start = parser->offset;
+ if (sv) { sv->data = &parser->content.data[parser->offset]; }
+ for (;
+ parser->offset < parser->content.length && cb_biblo_parser_peek(parser, 0) != '\n';
+ parser->offset += 1) {}
+ if (sv) { sv->length = parser->offset - start; }
+ if (parser->offset < parser->content.length) {
+ assert(cb_biblo_parser_peek(parser, 0) == '\n');
+ parser->offset += 1;
+ }
+}
+
+internal void cb_biblo_find_prefix(struct CB_BibloParser *parser)
+{
+ assert(parser != NULL);
+ assert(parser->content.data != NULL);
+ assert(parser->content.length > 0);
+ memset(&parser->prefix, 0, sizeof parser->prefix);
+ for (size_t line_start = 0; parser->prefix.data == NULL && parser->offset < parser->content.length; ) {
+ switch (cb_biblo_parser_peek(parser, 0)) {
+ case '@': {
+ for (int32_t i = 0; i < cb_arrlength(cb_biblo_annotation); ++i) {
+ if (cb_view_starts_with(&parser->content.data[parser->offset], cb_biblo_annotation[i])) {
+ parser->prefix.data = &parser->content.data[line_start];
+ parser->prefix.length = parser->offset - line_start;
+ parser->offset = line_start;
+ goto found;
+ }
+ }
+ cb_print(CB_LogLevel_Error, "Unknown cbuild_biblo annotation.\n");
+ exit(-1);
+ found:;
+ } break;
+ case '\n': {
+ line_start = parser->offset + 1;
+ } // fallthrough
+ default: {
+ parser->offset += 1;
+ } break;
+ }
+ }
+ if (parser->prefix.length > 0) { assert(parser->offset < parser->content.length); }
+}
+
+#endif
size_t total_length;
struct {
int32_t has_args;
- int32_t spacing;
+ size_t spacing;
} signature;
} CB_Generator;
// ======================================================================
// Implementations
static void cb_gen_write(CB_Generator *gen, char *filepath, bool append_mode) {
- CB_Handle file = cb_handle_open(filepath, append_mode
- ? CB_AccessFlag_Append
- : CB_AccessFlag_Write);
+ CB_Handle file = cb_file_open(filepath, append_mode
+ ? CB_AccessFlag_Append
+ : CB_AccessFlag_Write);
char *full_string = malloc(gen->total_length + 1);
memset(full_string, 0, gen->total_length + 1);
for (size_t i = 0; i < gen->count; ++i) {
strcat(full_string, gen->values[i]);
}
- _cb_handle_write_dyn(file, full_string);
+ cb_file_write(file, full_string);
free(full_string);
- cb_handle_close(file);
+ cb_file_close(file);
cb_dyn_free(gen);
memset(gen, 0, sizeof *gen);
}
gen->total_length += strlen(string);
}
+#define cb_gen_push_line(GeneratorPtr, String) \
+ _cb_gen_push_line((GeneratorPtr), (String), __FILE__, __LINE__)
+
+static void _cb_gen_push_line(CB_Generator *gen, char *string,
+ const char *file, int32_t line) {
+ string = cb_format("%s // %s:%d\n", string, file, line);
+ cb_dyn_push(gen, string);
+ gen->total_length += strlen(string);
+}
+
static void cb_gen_push_func_begin(CB_Generator *gen, char *signature_no_args) {
cb_gen_push(gen, cb_format("%s(", signature_no_args));
gen->signature.spacing = strlen(signature_no_args) + 1;
cb_gen_push(gen, argument);
}
-static void cb_gen_push_func_end(CB_Generator *gen, bool implementation) {
+#define cb_gen_push_func_end(GeneratorPtr, Implementation) \
+ _cb_gen_push_func_end((GeneratorPtr), (Implementation), __FILE__, __LINE__)
+
+static void _cb_gen_push_func_end(CB_Generator *gen, bool implementation,
+ const char *file, int32_t line) {
if (!gen->signature.has_args) {
cb_gen_push(gen, "void");
}
cb_gen_push(gen, ")");
if (!implementation) {
- cb_gen_push(gen, ";\n");
+ cb_gen_push(gen, ";");
}
+ cb_gen_push(gen, cb_format(" // %s:%d\n", file, line));
gen->signature.has_args = 0;
gen->signature.spacing = 0;
}
#define _USE_MATH_DEFINES
#include <math.h>
#include <stdarg.h>
+#include <assert.h>
internal void
cb_linagen_defun_vecn_element_wise(CB_Generator *gen, char *type,
// Implementations
static void
cb_linagen_typedef_vecn(CB_Generator *gen, char *type, int32_t n, ...) {
- va_list args = {};
+ va_list args = {0};
va_start(args, n);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
cb_gen_push(gen, cb_format("typedef union {"
"\n %s values[%d];"
-#ifdef __cplusplus
- "\n struct Fields {",
-#else
- "\n struct {",
-#endif
- type, n));
+ "\n struct {", type, n));
bool found_last_named_field = false;
for (int32_t i = 0; i < n; ++i) {
char *prefix = " ";
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {" "\n Vec%d%s res = {0};", n, suffix));
+ cb_gen_push_line(gen, cb_format("{" "\n Vec%d%s res = {0};", n, suffix));
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n res.values[%d] = _%d;", i, i));
+ cb_gen_push_line(gen, cb_format(" res.values[%d] = _%d;", i, i));
}
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
}
static inline void
static inline void
cb_linagen_defun_vecn_equal(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
cb_gen_push_func_begin(gen, cb_format("linagen_fn bool vec%d%s_equal", n, type));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {\n return v1->values[0] == v2->values[0]");
+ cb_gen_push_line(gen, "{\n return v1->values[0] == v2->values[0] ");
for (int32_t i = 1; i < n; ++i) {
- cb_gen_push(gen, cb_format(" &&\n v1->values[%d] == v2->values[%d]",
- i, i));
+ cb_gen_push_line(gen, cb_format(" && v1->values[%d] == v2->values[%d]%c",
+ i, i, (i == n - 1 ? ';' : ' ')));
}
- cb_gen_push(gen, ";\n}\n\n");
+ cb_gen_push(gen, "}\n\n");
}
static inline void
cb_linagen_defun_vecn_near(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
cb_gen_push_func_begin(gen, cb_format("linagen_fn bool vec%d%s_near",
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {\n return (v1->values[0] + eps >= v2->values[0] && v1->values[0] - eps <= v2->values[0])");
+ cb_gen_push_line(gen, "{\n return (v1->values[0] + eps >= v2->values[0] && v1->values[0] - eps <= v2->values[0]) ");
for (int32_t i = 1; i < n; ++i) {
- cb_gen_push(gen, cb_format(" &&\n (v1->values[%d] + eps >= v2->values[%d] && v1->values[%d] - eps <= v2->values[%d])",
- i, i, i, i));
+ cb_gen_push_line(gen, cb_format(" && (v1->values[%d] + eps >= v2->values[%d] && v1->values[%d] - eps <= v2->values[%d])%c",
+ i, i, i, i, (i == n - 1 ? ';' : ' ')));
}
- cb_gen_push(gen, ";\n}\n\n");
+ cb_gen_push(gen, "}\n\n");
}
static inline void
cb_linagen_defun_vecn_lerp(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
if (n <= CB_LINAGEN_VECN_COPY_CAP) {
cb_gen_push_func_arg(gen, cb_format("%s t", type));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {" "\n Vec%d%s res = {0};", n, suffix));
+ cb_gen_push_line(gen, cb_format("{" "\n Vec%d%s res = {0};", n, suffix));
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n res.values[%d] = start->values[%d] * (1 - t) + end->values[%d] * t;", i, i, i));
+ cb_gen_push_line(gen, cb_format(" res.values[%d] = start->values[%d] * (1 - t) + end->values[%d] * t;", i, i, i));
}
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen,"}\n\n");
} else {
cb_gen_push_func_begin(gen, cb_format("linagen_fn void vec%d%s_lerp", n, type));
cb_gen_push_func_arg(gen, cb_format("Vec%d%s *res", n, suffix));
cb_gen_push_func_arg(gen, cb_format("%s t", type));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n res->values[%d] = start->values[%d] * (1 - t) + end->values[%d] * t;", i, i, i));
+ cb_gen_push_line(gen, cb_format(" res->values[%d] = start->values[%d] * (1 - t) + end->values[%d] * t;", i, i, i));
}
- cb_gen_push(gen, "\n}\n\n");
+ cb_gen_push(gen, "}\n\n");
}
}
static void
cb_linagen_defun_vecn_scale(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
if (n <= CB_LINAGEN_VECN_COPY_CAP) {
cb_gen_push_func_arg(gen, cb_format("%s k", type));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n v.values[%d] *= k;", i, i));
+ cb_gen_push_line(gen, cb_format(" v.values[%d] *= k;", i, i));
}
- cb_gen_push(gen, "\n return v;" "\n}\n\n");
+ cb_gen_push_line(gen, " return v;");
+ cb_gen_push(gen, "}\n\n");
} else {
cb_gen_push_func_begin(gen, cb_format("linagen_fn void vec%d%s_scale", n, type));
cb_gen_push_func_arg(gen, cb_format("Vec%d%s *res", n, suffix));
cb_gen_push_func_arg(gen, cb_format("%s k", type));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n res->values[%d] = v1->values[%d] * k;", i, i));
+ cb_gen_push_line(gen, cb_format(" res->values[%d] = v1->values[%d] * k;", i, i));
}
- cb_gen_push(gen, "\n}\n\n");
+ cb_gen_push(gen, "}\n\n");
}
}
static void
cb_linagen_defun_vecn_normalize(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
if (n <= CB_LINAGEN_VECN_COPY_CAP) {
cb_gen_push_func_arg(gen, cb_format("Vec%d%s v", n, suffix));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {\n %s dot = {0};", type));
+ cb_gen_push_line(gen, cb_format("{\n %s dot = {0};", type));
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n dot += v.values[%d] * v.values[%d];", i, i));
+ cb_gen_push_line(gen, cb_format(" dot += v.values[%d] * v.values[%d];", i, i));
}
- cb_gen_push(gen, "\n float magnitude = sqrtf((float)dot);"
- "\n if (magnitude == 0.f) { return v; }");
+ cb_gen_push_line(gen, " float magnitude = sqrtf((float)dot);");
+ cb_gen_push_line(gen, " if (magnitude == 0.f) { return v; }");
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n v.values[%d] /= (%s)magnitude;", i, type));
+ cb_gen_push_line(gen, cb_format(" v.values[%d] /= (%s)magnitude;", i, type));
}
- cb_gen_push(gen, "\n return v;" "\n}\n\n");
+ cb_gen_push_line(gen, " return v;");
+ cb_gen_push(gen, "}\n\n");
} else {
cb_gen_push_func_begin(gen, cb_format("linagen_fn void vec%d%s_normalize", n, type));
cb_gen_push_func_arg(gen, cb_format("Vec%d%s *res", n, suffix));
cb_gen_push_func_arg(gen, cb_format("Vec%d%s *v1", n, suffix));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {\n %s dot = {0};", type));
+ cb_gen_push_line(gen, cb_format("{\n %s dot = {0};", type));
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n dot += v1->values[%d] * v1->values[%d];", i, i, i));
+ cb_gen_push_line(gen, cb_format(" dot += v1->values[%d] * v1->values[%d];", i, i, i));
}
- cb_gen_push(gen, "\n float magnitude = sqrtf((float)dot);"
- "\n if (magnitude == 0.f) { return; }");
+ cb_gen_push_line(gen, " float magnitude = sqrtf((float)dot);");
+ cb_gen_push_line(gen, " if (magnitude == 0.f) { return; }");
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n res->values[%d] = v1->values[%d] / magnitude;",
- i, i, type));
+ cb_gen_push_line(gen, cb_format(" res->values[%d] = v1->values[%d] / magnitude;",
+ i, i, type));
}
- cb_gen_push(gen, "\n}\n\n");
+ cb_gen_push(gen, "}\n\n");
}
}
static void
cb_linagen_defun_vecn_dot(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
cb_gen_push_func_begin(gen, cb_format("linagen_fn %s vec%d%s_dot", type, n, type));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {" "\n %s res = {0};", type));
+ cb_gen_push_line(gen, cb_format("{" "\n %s res = {0};", type));
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n res += v1->values[%d] * v2->values[%d];", i, i, i));
+ cb_gen_push_line(gen, cb_format(" res += v1->values[%d] * v2->values[%d];", i, i, i));
}
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
}
static void
cb_linagen_defun_vecn_mulm(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
if (n < MIN(CB_LINAGEN_VECN_COPY_CAP, CB_LINAGEN_MATNN_COPY_CAP)) {
cb_gen_push_func_arg(gen, cb_format("Mat%d%s m", n, suffix));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {" "\n Vec%d%s res = {0};", n, suffix));
+ cb_gen_push_line(gen, cb_format("{" "\n Vec%d%s res = {0};", n, suffix));
for (int32_t i = 0; i < n; ++i) {
char *prefix = "";
- cb_gen_push(gen, cb_format("\n res.values[%d] = ", i));
+ cb_gen_push(gen, cb_format(" res.values[%d] = ", i));
for (int32_t j = 0; j < n; ++j) {
cb_gen_push(gen, cb_format("%s(v.values[%d] * m.values[%d][%d])",
prefix, j, i, j));
prefix = " + ";
}
- cb_gen_push(gen, ";");
+ cb_gen_push_line(gen, ";");
}
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
} else {
cb_gen_push_func_begin(gen, cb_format("linagen_fn void vec%d%s_mulm", n, type));
cb_gen_push_func_arg(gen, cb_format("Vec%d%s *res", n, suffix));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t i = 0; i < n; ++i) {
char *prefix = "";
- cb_gen_push(gen, cb_format("\n res->values[%d] = ", i));
+ cb_gen_push(gen, cb_format(" res->values[%d] = ", i));
for (int32_t j = 0; j < n; ++j) {
cb_gen_push(gen, cb_format("%s(v->values[%d] * m->values[%d][%d])",
prefix, j, i, j));
prefix = " + ";
}
- cb_gen_push(gen, ";");
+ cb_gen_push_line(gen, ";");
}
- cb_gen_push(gen, "\n}\n\n");
+ cb_gen_push(gen, "}\n\n");
}
}
static void
cb_linagen_defun_vecn_magnitude(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
cb_gen_push_func_begin(gen, cb_format("linagen_fn float vec%d%s_magnitude",
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {\n float dot = {0};");
+ cb_gen_push_line(gen, "{\n float dot = {0};");
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n dot += (float)(v1->values[%d] * v1->values[%d]);",
- i, i));
+ cb_gen_push_line(gen, cb_format(" dot += (float)(v1->values[%d] * v1->values[%d]);", i, i));
}
- cb_gen_push(gen, "\n float res = sqrtf(dot);"
- "\n return res;"
- "\n}\n\n");
+ cb_gen_push_line(gen, " float res = sqrtf(dot);");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
}
static void
cb_linagen_defun_vecn_magnitude64(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
cb_gen_push_func_begin(gen, cb_format("linagen_fn double vec%d%s_magnitude64",
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {\n double dot = {0};");
+ cb_gen_push_line(gen, "{\n double dot = {0};");
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n dot += (double)(v1->values[%d] * v1->values[%d]);",
- i, i));
+ cb_gen_push_line(gen, cb_format(" dot += (double)(v1->values[%d] * v1->values[%d]);", i, i));
}
- cb_gen_push(gen, "\n double res = sqrt(dot);"
- "\n return res;"
- "\n}\n\n");
+ cb_gen_push_line(gen, " double res = sqrt(dot);");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
}
static void
cb_linagen_defun_vecn_distance(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
cb_gen_push_func_begin(gen, cb_format("linagen_fn float vec%d%s_distance",
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {" "\n Vec%d%s diff = {0};", n, suffix));
+ cb_gen_push_line(gen, cb_format("{" "\n Vec%d%s diff = {0};", n, suffix));
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n diff.values[%d] = v1->values[%d] - v2->values[%d];", i, i, i));
+ cb_gen_push_line(gen, cb_format(" diff.values[%d] = v1->values[%d] - v2->values[%d];", i, i, i));
}
- cb_gen_push(gen, "\n float dot = {0};");
+ cb_gen_push_line(gen, " float dot = {0};");
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n dot += (float)(diff.values[%d] * diff.values[%d]);",
- i, i));
+ cb_gen_push_line(gen, cb_format(" dot += (float)(diff.values[%d] * diff.values[%d]);", i, i));
}
- cb_gen_push(gen, "\n float res = sqrtf(dot);"
- "\n return res;"
- "\n}\n\n");
+ cb_gen_push_line(gen, " float res = sqrtf(dot);");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
}
static void
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {" "\n Vec%d%s diff = {0};", n, suffix));
+ cb_gen_push_line(gen, cb_format("{" "\n Vec%d%s diff = {0};", n, suffix));
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n diff.values[%d] = v1->values[%d] - v2->values[%d];", i, i, i));
+ cb_gen_push_line(gen, cb_format(" diff.values[%d] = v1->values[%d] - v2->values[%d];", i, i, i));
}
- cb_gen_push(gen, "\n float dot = {0};");
+ cb_gen_push_line(gen, " float dot = {0};");
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n dot += (float)(diff.values[%d] * diff.values[%d]);",
- i, i));
+ cb_gen_push_line(gen, cb_format(" dot += (float)(diff.values[%d] * diff.values[%d]);", i, i));
}
- cb_gen_push(gen, "\n return dot;"
- "\n}\n\n");
+ cb_gen_push_line(gen, " return dot;");
+ cb_gen_push(gen, "}\n\n");
}
static inline void
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {" "\n Vec3%s res = {0};", suffix));
- cb_gen_push(gen, "\n res.values[0] = v1.values[1] * v2.values[2] - v1.values[2] * v2.values[1];");
- cb_gen_push(gen, "\n res.values[1] = v1.values[2] * v2.values[0] - v1.values[0] * v2.values[2];");
- cb_gen_push(gen, "\n res.values[2] = v1.values[0] * v2.values[1] - v1.values[1] * v2.values[0];");
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push_line(gen, cb_format("{" "\n Vec3%s res = {0};", suffix));
+ cb_gen_push_line(gen, " res.values[0] = v1.values[1] * v2.values[2] - v1.values[2] * v2.values[1];");
+ cb_gen_push_line(gen, " res.values[1] = v1.values[2] * v2.values[0] - v1.values[0] * v2.values[2];");
+ cb_gen_push_line(gen, " res.values[2] = v1.values[0] * v2.values[1] - v1.values[1] * v2.values[0];");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
}
static void
cb_linagen_defun_matnn_scale(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
if (n < CB_LINAGEN_MATNN_COPY_CAP) {
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t i = 0; i < n; ++i) {
for (int32_t j = 0; j < n; ++j) {
- cb_gen_push(gen, cb_format("\n m1.values[%d][%d] *= k;", i, j));
+ cb_gen_push_line(gen, cb_format(" m1.values[%d][%d] *= k;", i, j));
}
}
- cb_gen_push(gen, "\n return m1;" "\n}\n\n");
+ cb_gen_push_line(gen, " return m1;");
+ cb_gen_push(gen, "}\n\n");
} else {
cb_gen_push_func_begin(gen, cb_format("linagen_fn void mat%d%s_scale", n, type));
cb_gen_push_func_arg(gen, cb_format("Mat%d%s *res", n, suffix));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t i = 0; i < n; ++i) {
for (int32_t j = 0; j < n; ++j) {
- cb_gen_push(gen, cb_format("\n res->values[%d][%d] = m1->values[%d][%d] * k;",
- i, j, i, j));
+ cb_gen_push_line(gen, cb_format(" res->values[%d][%d] = m1->values[%d][%d] * k;", i, j, i, j));
}
}
- cb_gen_push(gen, "\n}\n\n");
+ cb_gen_push(gen, "}\n\n");
}
}
static void
cb_linagen_defun_matnn_dot(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
if (n <= CB_LINAGEN_MATNN_COPY_CAP) {
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {" "\n Mat%d%s res = {0};", n, suffix));
+ cb_gen_push_line(gen, cb_format("{" "\n Mat%d%s res = {0};", n, suffix));
for (int32_t col = 0; col < n; ++col) {
for (int32_t row = 0; row < n; ++row) {
- cb_gen_push(gen, cb_format("\n res.values[%d][%d] = ", col, row));
+ cb_gen_push(gen, cb_format(" res.values[%d][%d] = ", col, row));
char *prefix = "";
for (int32_t k = 0; k < n; ++k) {
cb_gen_push(gen, cb_format("%s(m1.values[%d][%d] * m2.values[%d][%d])",
prefix, k, row, col, k));
prefix = " + ";
}
- cb_gen_push(gen, ";");
+ cb_gen_push_line(gen, ";");
}
}
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
} else {
cb_gen_push_func_begin(gen, cb_format("linagen_fn void mat%d%s_dot", n, type));
cb_gen_push_func_arg(gen, cb_format("Mat%d%s *res", n, suffix));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t col = 0; col < n; ++col) {
for (int32_t row = 0; row < n; ++row) {
- cb_gen_push(gen, cb_format("\n res->values[%d][%d] = ", col, row));
+ cb_gen_push(gen, cb_format(" res->values[%d][%d] = ", col, row));
char *prefix = "";
for (int32_t k = 0; k < n; ++k) {
cb_gen_push(gen, cb_format("%s(m1->values[%d][%d] * m2->values[%d][%d])",
prefix, k, row, col, k));
prefix = " + ";
}
- cb_gen_push(gen, ";");
+ cb_gen_push_line(gen, ";");
}
}
- cb_gen_push(gen, "\n}\n\n");
+ cb_gen_push(gen, "}\n\n");
}
}
static void
cb_linagen_defun_matnn_mulv(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
if (n <= CB_LINAGEN_MATNN_COPY_CAP) {
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {" "\n Vec%d%s res = {0};", n, suffix));
+ cb_gen_push_line(gen, cb_format("{" "\n Vec%d%s res = {0};", n, suffix));
for (int32_t i = 0; i < n; ++i) {
char *prefix = "";
- cb_gen_push(gen, cb_format("\n res.values[%d] = ", i));
+ cb_gen_push(gen, cb_format(" res.values[%d] = ", i));
for (int32_t j = 0; j < n; ++j) {
cb_gen_push(gen, cb_format("%s(m.values[%d][%d] * v.values[%d])",
prefix, j, i, j));
prefix = " + ";
}
- cb_gen_push(gen, ";");
+ cb_gen_push_line(gen, ";");
}
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push_line(gen, "}\n\n");
} else {
cb_gen_push_func_begin(gen, cb_format("linagen_fn void mat%d%s_mulv", n, type));
cb_gen_push_func_arg(gen, cb_format("Vec%d%s *res", n, suffix));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t i = 0; i < n; ++i) {
char *prefix = "";
- cb_gen_push(gen, cb_format("\n res->values[%d] = ", i));
+ cb_gen_push(gen, cb_format(" res->values[%d] = ", i));
for (int32_t j = 0; j < n; ++j) {
cb_gen_push(gen, cb_format("%s(m->values[%d][%d] * v->values[%d])",
prefix, j, i, j));
prefix = " + ";
}
- cb_gen_push(gen, ";");
+ cb_gen_push_line(gen, ";");
}
- cb_gen_push(gen, "\n}\n\n");
+ cb_gen_push(gen, "}\n\n");
}
}
static void
cb_linagen_defun_matnn_identity(CB_Generator *gen, char *type, int32_t n, bool implementation) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
if (n <= CB_LINAGEN_MATNN_COPY_CAP) {
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {" "\n Mat%d%s res = {0};", n, suffix));
+ cb_gen_push_line(gen, cb_format("{" "\n Mat%d%s res = {0};", n, suffix));
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n res.values[%d][%d] = 1;", i, i));
+ cb_gen_push_line(gen, cb_format(" res.values[%d][%d] = 1;", i, i));
}
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
} else {
cb_gen_push_func_begin(gen, cb_format("linagen_fn void mat%d%s_identity", n, type));
cb_gen_push_func_arg(gen, cb_format("Mat%d%s *res", n, suffix));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
- cb_gen_push(gen, "\n memset(res, 0, sizeof *res);");
+ cb_gen_push_line(gen, "{" "\n memset(res, 0, sizeof *res);");
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n res->values[%d][%d] = 1;", i, i));
+ cb_gen_push_line(gen, cb_format(" res->values[%d][%d] = 1;", i, i));
}
- cb_gen_push(gen, "\n}\n\n");
+ cb_gen_push(gen, "}\n\n");
}
}
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t i = 0; i < 3; ++i) {
- cb_gen_push(gen, cb_format("\n m.values[3][%d] += by.values[%d];", i, i));
+ cb_gen_push_line(gen, cb_format(" m.values[3][%d] += by.values[%d];", i, i));
}
- cb_gen_push(gen, "\n return m;" "\n}\n\n");
+ cb_gen_push_line(gen, " return m;");
+ cb_gen_push(gen, "}\n\n");
}
static void
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t i = 0; i < 3; ++i) {
- cb_gen_push(gen, cb_format("\n m.values[3][%d] = to.values[%d];", i, i));
+ cb_gen_push_line(gen, cb_format(" m.values[3][%d] = to.values[%d];", i, i));
}
- cb_gen_push(gen, "\n return m;" "\n}\n\n");
+ cb_gen_push_line(gen, " return m;");
+ cb_gen_push(gen, "}\n\n");
}
static void
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {"
- "\n Mat4%s res = {0};"
- "\n float radians = degrees * %.16ff / 180.f;", suffix, (float)M_PI));
- cb_gen_push(gen, cb_format("\n Mat4%s rot = { .values = {"
- "\n { cosf(radians) + axis.values[0]*axis.values[0]*(1 - cosf(radians)), axis.values[0]*axis.values[1]*(1 - cosf(radians)) + axis.values[2]*sinf(radians), axis.values[0]*axis.values[2]*(1 - cosf(radians)) - axis.values[1]*sinf(radians), 0 },"
- "\n { axis.values[1]*axis.values[0]*(1 - cosf(radians)) - axis.values[2]*sinf(radians), cosf(radians) + axis.values[1]*axis.values[1]*(1 - cosf(radians)), axis.values[1]*axis.values[2]*(1 - cosf(radians)) + axis.values[0]*sinf(radians), 0 },"
- "\n { axis.values[2]*axis.values[0]*(1 - cosf(radians)) + axis.values[1]*sinf(radians), axis.values[2]*axis.values[1]*(1 - cosf(radians)) - axis.values[0]*sinf(radians), cosf(radians) + axis.values[2]*axis.values[2]*(1 - cosf(radians)), 0 },"
- "\n { 0, 0, 0, 1 }"
- "\n }};" , suffix));
+ cb_gen_push(gen, "{\n");
+ cb_gen_push_line(gen, cb_format(" Mat4%s res = {0};", suffix));
+ cb_gen_push_line(gen, cb_format(" float radians = degrees * %.16ff / 180.f;", (double)M_PI));
+ cb_gen_push_line(gen, cb_format(" Mat4%s rot = { .values = {", suffix));
+ cb_gen_push_line(gen, " { cosf(radians) + axis.values[0]*axis.values[0]*(1 - cosf(radians)), axis.values[0]*axis.values[1]*(1 - cosf(radians)) + axis.values[2]*sinf(radians), axis.values[0]*axis.values[2]*(1 - cosf(radians)) - axis.values[1]*sinf(radians), 0 },");
+ cb_gen_push_line(gen, " { axis.values[1]*axis.values[0]*(1 - cosf(radians)) - axis.values[2]*sinf(radians), cosf(radians) + axis.values[1]*axis.values[1]*(1 - cosf(radians)), axis.values[1]*axis.values[2]*(1 - cosf(radians)) + axis.values[0]*sinf(radians), 0 },");
+ cb_gen_push_line(gen, " { axis.values[2]*axis.values[0]*(1 - cosf(radians)) + axis.values[1]*sinf(radians), axis.values[2]*axis.values[1]*(1 - cosf(radians)) - axis.values[0]*sinf(radians), cosf(radians) + axis.values[2]*axis.values[2]*(1 - cosf(radians)), 0 },");
+ cb_gen_push_line(gen, " { 0, 0, 0, 1 }");
+ cb_gen_push_line(gen, " }};");
for (int32_t i = 0; i < 4; ++i) {
for (int32_t j = 0; j < 4; ++j) {
- cb_gen_push(gen, cb_format("\n res.values[%d][%d] = ", i, j));
+ cb_gen_push(gen, cb_format(" res.values[%d][%d] = ", i, j));
char *prefix = "";
for (int32_t k = 0; k < 4; ++k) {
cb_gen_push(gen, cb_format("%s(rot.values[%d][%d]) * m.values[%d][%d]",
prefix, k, j, i, k));
prefix = " + ";
}
- cb_gen_push(gen, ";");
+ cb_gen_push_line(gen, ";");
}
}
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
}
static void
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {" "\n Mat4%s res = {0};", suffix));
- cb_gen_push(gen, cb_format("\n Mat4%s rot = { .values = {"
- "\n { cosf(radians) + axis.values[0]*axis.values[0]*(1 - cosf(radians)), axis.values[0]*axis.values[1]*(1 - cosf(radians)) + axis.values[2]*sinf(radians), axis.values[0]*axis.values[2]*(1 - cosf(radians)) - axis.values[1]*sinf(radians), 0 },"
- "\n { axis.values[1]*axis.values[0]*(1 - cosf(radians)) - axis.values[2]*sinf(radians), cosf(radians) + axis.values[1]*axis.values[1]*(1 - cosf(radians)), axis.values[1]*axis.values[2]*(1 - cosf(radians)) + axis.values[0]*sinf(radians), 0 },"
- "\n { axis.values[2]*axis.values[0]*(1 - cosf(radians)) + axis.values[1]*sinf(radians), axis.values[2]*axis.values[1]*(1 - cosf(radians)) - axis.values[0]*sinf(radians), cosf(radians) + axis.values[2]*axis.values[2]*(1 - cosf(radians)), 0 },"
- "\n { 0, 0, 0, 1 }"
- "\n }};" , suffix));
+ cb_gen_push_line(gen, cb_format("{" "\n Mat4%s res = {0};", suffix));
+ cb_gen_push(gen, cb_format(" Mat4%s rot = { .values = {", suffix));
+ cb_gen_push_line(gen, " { cosf(radians) + axis.values[0]*axis.values[0]*(1 - cosf(radians)), axis.values[0]*axis.values[1]*(1 - cosf(radians)) + axis.values[2]*sinf(radians), axis.values[0]*axis.values[2]*(1 - cosf(radians)) - axis.values[1]*sinf(radians), 0 },");
+ cb_gen_push_line(gen, " { axis.values[1]*axis.values[0]*(1 - cosf(radians)) - axis.values[2]*sinf(radians), cosf(radians) + axis.values[1]*axis.values[1]*(1 - cosf(radians)), axis.values[1]*axis.values[2]*(1 - cosf(radians)) + axis.values[0]*sinf(radians), 0 },");
+ cb_gen_push_line(gen, " { axis.values[2]*axis.values[0]*(1 - cosf(radians)) + axis.values[1]*sinf(radians), axis.values[2]*axis.values[1]*(1 - cosf(radians)) - axis.values[0]*sinf(radians), cosf(radians) + axis.values[2]*axis.values[2]*(1 - cosf(radians)), 0 },");
+ cb_gen_push_line(gen, " { 0, 0, 0, 1 }");
+ cb_gen_push_line(gen, " }};");
for (int32_t i = 0; i < 4; ++i) {
for (int32_t j = 0; j < 4; ++j) {
- cb_gen_push(gen, cb_format("\n res.values[%d][%d] = ", i, j));
+ cb_gen_push(gen, cb_format(" res.values[%d][%d] = ", i, j));
char *prefix = "";
for (int32_t k = 0; k < 4; ++k) {
cb_gen_push(gen, cb_format("%s(rot.values[%d][%d]) * m.values[%d][%d]",
prefix, k, j, i, k));
prefix = " + ";
}
- cb_gen_push(gen, ";");
+ cb_gen_push_line(gen, ";");
}
}
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
}
static void
-cb_linagen_defun_mat4_transform_scale(CB_Generator *gen, char *type,
- bool implementation) {
+cb_linagen_defun_mat4_transform_scale(CB_Generator *gen, char *type, bool implementation) {
char *suffix = strdup(type);
*suffix = char_toupper(*type);
cb_gen_push_func_begin(gen, cb_format("linagen_fn Mat4%s mat4%s_transform_scale", suffix, type));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
- cb_gen_push(gen, cb_format("\n Mat4%s res = {0};"
- "\n Mat4%s scale = {0};"
- "\n scale.values[0][0] = v.values[0];"
- "\n scale.values[1][1] = v.values[1];"
- "\n scale.values[2][2] = v.values[2];"
- "\n scale.values[3][3] = 1;",
- suffix, suffix));
+ cb_gen_push(gen, "{\n");
+ cb_gen_push_line(gen, cb_format(" Mat4%s res = {0};", suffix));
+ cb_gen_push_line(gen, cb_format(" Mat4%s scale = {0};", suffix));
+ cb_gen_push_line(gen, " scale.values[0][0] = v.values[0];");
+ cb_gen_push_line(gen, " scale.values[1][1] = v.values[1];");
+ cb_gen_push_line(gen, " scale.values[2][2] = v.values[2];");
+ cb_gen_push_line(gen, " scale.values[3][3] = 1;");
for (int32_t i = 0; i < 4; ++i) {
for (int32_t j = 0; j < 4; ++j) {
- cb_gen_push(gen, cb_format("\n res.values[%d][%d] = ", i, j));
+ cb_gen_push(gen, cb_format(" res.values[%d][%d] = ", i, j));
char *prefix = "";
for (int32_t k = 0; k < 4; ++k) {
cb_gen_push(gen, cb_format("%s(scale.values[%d][%d]) * m.values[%d][%d]",
prefix, k, j, i, k));
prefix = " + ";
}
- cb_gen_push(gen, ";");
+ cb_gen_push_line(gen, ";");
}
}
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
}
static void
-cb_linagen_defun_mat4_projection_orthographic(CB_Generator *gen, char *type,
- bool implementation) {
+cb_linagen_defun_mat4_projection_orthographic(CB_Generator *gen, char *type, bool implementation) {
char *suffix = strdup(type);
*suffix = char_toupper(*type);
cb_gen_push_func_begin(gen, cb_format("linagen_fn Mat4%s mat4%s_projection_orthographic", suffix, type));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
- cb_gen_push(gen, cb_format("\n Mat4%s res = {0};"
- "\n res.values[0][0] = 2 / (right - left);"
- "\n res.values[1][1] = 2 / (top - bottom);"
- "\n res.values[2][2] = -2 / (far - near);"
- "\n res.values[3][0] = -(right + left) / (right - left);"
- "\n res.values[3][1] = -(top + bottom) / (top - bottom);"
- "\n res.values[3][2] = -(far + near) / (far - near);"
- "\n res.values[3][3] = 1;",
- suffix, suffix));
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push(gen, "{\n");
+ cb_gen_push_line(gen, cb_format(" Mat4%s res = {0};", suffix));
+ cb_gen_push_line(gen, " res.values[0][0] = 2 / (right - left);");
+ cb_gen_push_line(gen, " res.values[1][1] = 2 / (top - bottom);");
+ cb_gen_push_line(gen, " res.values[2][2] = -2 / (far - near);");
+ cb_gen_push_line(gen, " res.values[3][0] = -(right + left) / (right - left);");
+ cb_gen_push_line(gen, " res.values[3][1] = -(top + bottom) / (top - bottom);");
+ cb_gen_push_line(gen, " res.values[3][2] = -(far + near) / (far - near);");
+ cb_gen_push_line(gen, " res.values[3][3] = 1;");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
}
static void
-cb_linagen_defun_mat4_projection_perspective(CB_Generator *gen, char *type,
- bool implementation) {
+cb_linagen_defun_mat4_projection_perspective(CB_Generator *gen, char *type, bool implementation) {
char *suffix = strdup(type);
*suffix = char_toupper(*type);
cb_gen_push_func_begin(gen, cb_format("linagen_fn Mat4%s mat4%s_projection_perspective", suffix, type));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {"
- "\n float yfov_radians = yfov_degree * %.16ff / 180.f;"
- "\n %s top = near * tanf(yfov_radians/2);"
- "\n %s right = top * aspect_ratio;"
- "\n Mat4%s res = {0};"
- "\n res.arr[0] = near / right;"
- "\n res.arr[5] = near / top;"
- "\n res.arr[10] = -(far + near) / (far - near);"
- "\n res.arr[11] = -1;"
- "\n res.arr[14] = -(2 * far * near) / (far - near);",
- M_PI, type, type, suffix, suffix));
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push(gen, "{\n");
+ cb_gen_push_line(gen, cb_format(" float yfov_radians = yfov_degree * %.16ff / 180.f;", M_PI));
+ cb_gen_push_line(gen, cb_format(" %s top = near * tanf(yfov_radians/2);", type));
+ cb_gen_push_line(gen, cb_format(" %s right = top * aspect_ratio;", type));
+ cb_gen_push_line(gen, cb_format(" Mat4%s res = {0};", suffix));
+ cb_gen_push_line(gen, " res.arr[0] = near / right;");
+ cb_gen_push_line(gen, " res.arr[5] = near / top;");
+ cb_gen_push_line(gen, " res.arr[10] = -(far + near) / (far - near);");
+ cb_gen_push_line(gen, " res.arr[11] = -1;");
+ cb_gen_push_line(gen, " res.arr[14] = -(2 * far * near) / (far - near);");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
}
static void
-cb_linagen_defun_mat4_projection_perspective_rad(CB_Generator *gen, char *type,
- bool implementation) {
+cb_linagen_defun_mat4_projection_perspective_rad(CB_Generator *gen, char *type, bool implementation) {
char *suffix = strdup(type);
*suffix = char_toupper(*type);
cb_gen_push_func_begin(gen, cb_format("linagen_fn Mat4%s mat4%s_projection_perspective_rad", suffix, type));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
- cb_gen_push(gen, cb_format("\n %s top = near * tanf(yfov_radians/2);"
- "\n %s right = top * aspect_ratio;"
- "\n Mat4%s res = {0};"
- "\n res.arr[0] = near / right;"
- "\n res.arr[5] = near / top;"
- "\n res.arr[10] = -(far + near) / (far - near);"
- "\n res.arr[11] = -1;"
- "\n res.arr[14] = -(2 * far * near) / (far - near);",
- type, type, suffix, suffix));
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push(gen, "{\n");
+ cb_gen_push_line(gen, cb_format(" %s top = near * tanf(yfov_radians/2);", type));
+ cb_gen_push_line(gen, cb_format(" %s right = top * aspect_ratio;", type));
+ cb_gen_push_line(gen, cb_format(" Mat4%s res = {0};", suffix));
+ cb_gen_push_line(gen, " res.arr[0] = near / right;");
+ cb_gen_push_line(gen, " res.arr[5] = near / top;");
+ cb_gen_push_line(gen, " res.arr[10] = -(far + near) / (far - near);");
+ cb_gen_push_line(gen, " res.arr[11] = -1;");
+ cb_gen_push_line(gen, " res.arr[14] = -(2 * far * near) / (far - near);");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
}
cb_linagen_defun_vecn_element_wise(CB_Generator *gen, char *type,
int32_t n, bool implementation,
char *func_name, char op) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
if (n <= CB_LINAGEN_VECN_COPY_CAP) {
cb_gen_push_func_begin(gen, cb_format("linagen_fn Vec%d%s vec%d%s_%s",
n, suffix, n, type, func_name));
- cb_gen_push_func_arg(gen, cb_format("Vec%d%s v1", n, suffix));
- cb_gen_push_func_arg(gen, cb_format("Vec%d%s v2", n, suffix));
+ cb_gen_push_func_arg(gen, cb_format("Vec%d%s v1", n, suffix));
+ cb_gen_push_func_arg(gen, cb_format("Vec%d%s v2", n, suffix));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n v1.values[%d] %c= v2.values[%d];",
- i, op, i));
+ cb_gen_push_line(gen, cb_format(" v1.values[%d] %c= v2.values[%d];", i, op, i));
}
- cb_gen_push(gen, "\n return v1;" "\n}\n\n");
+ cb_gen_push_line(gen, " return v1;");
+ cb_gen_push(gen, "}\n\n");
} else {
cb_gen_push_func_begin(gen, cb_format("linagen_fn void vec%d%s_%s",
n, type, func_name));
- cb_gen_push_func_arg(gen, cb_format("Vec%d%s *res", n, suffix));
- cb_gen_push_func_arg(gen, cb_format("Vec%d%s *v1", n, suffix));
- cb_gen_push_func_arg(gen, cb_format("Vec%d%s *v2", n, suffix));
+ cb_gen_push_func_arg(gen, cb_format("Vec%d%s *res", n, suffix));
+ cb_gen_push_func_arg(gen, cb_format("Vec%d%s *v1", n, suffix));
+ cb_gen_push_func_arg(gen, cb_format("Vec%d%s *v2", n, suffix));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t i = 0; i < n; ++i) {
- cb_gen_push(gen, cb_format("\n res->values[%d] = v1->values[%d] %c v2->values[%d];",
- i, i, op, i));
+ cb_gen_push_line(gen, cb_format(" res->values[%d] = v1->values[%d] %c v2->values[%d];", i, i, op, i));
}
- cb_gen_push(gen, "\n}\n\n");
+ cb_gen_push(gen, "}\n\n");
}
}
cb_linagen_defun_matnn_element_wise(CB_Generator *gen, char *type,
int32_t n, bool implementation,
char *func_name, char op) {
+ assert(n > 1);
char *suffix = strdup(type);
*suffix = char_toupper(*type);
if (n < CB_LINAGEN_MATNN_COPY_CAP) {
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, cb_format(" {" "\n Mat%d%s res = {0};", n, suffix));
+ cb_gen_push_line(gen, cb_format("{" "\n Mat%d%s res = {0};", n, suffix));
for (int32_t i = 0; i < n; ++i) {
for (int32_t j = 0; j < n; ++j) {
- cb_gen_push(gen, cb_format("\n res.values[%d][%d] = m1.values[%d][%d] %c m2.values[%d][%d];",
- i, j, i, j, op, i, j));
+ cb_gen_push_line(gen, cb_format(" res.values[%d][%d] = m1.values[%d][%d] %c m2.values[%d][%d];", i, j, i, j, op, i, j));
}
}
- cb_gen_push(gen, "\n return res;" "\n}\n\n");
+ cb_gen_push_line(gen, " return res;");
+ cb_gen_push(gen, "}\n\n");
} else {
cb_gen_push_func_begin(gen, cb_format("linagen_fn void mat%d%s_%s",
n, type, func_name));
cb_gen_push_func_end(gen, implementation);
if (!implementation) { return; }
- cb_gen_push(gen, " {");
+ cb_gen_push(gen, "{\n");
for (int32_t i = 0; i < n; ++i) {
for (int32_t j = 0; j < n; ++j) {
- cb_gen_push(gen, cb_format("\n res->values[%d][%d] = m1->values[%d][%d] %c m2->values[%d][%d];",
- i, j, i, j, op, i, j));
+ cb_gen_push_line(gen, cb_format(" res->values[%d][%d] = m1->values[%d][%d] %c m2->values[%d][%d];", i, j, i, j, op, i, j));
}
}
- cb_gen_push(gen, "\n}\n\n");
+ cb_gen_push(gen, "}\n\n");
}
}
}
}
cb_assertion_handler = cb_assertion_break;
- cb_println(CB_LogLevel_Info, "Summary: %d/%d tests passed",
- test_passed_count, cb_tests.count);
+ cb_print(CB_LogLevel_Info, "Summary: %d/%d tests passed\n",
+ test_passed_count, cb_tests.count);
}
internal void cb_test_register(struct CB_Test test) {