]> git.leonardobizzoni.com Git - CBuild/commitdiff
new extra lib + linagen reference to generator line
authorLeonardoBizzoni <leo2002714@gmail.com>
Wed, 4 Feb 2026 11:04:59 +0000 (12:04 +0100)
committerLeonardoBizzoni <leo2002714@gmail.com>
Wed, 4 Feb 2026 11:04:59 +0000 (12:04 +0100)
Automatic recompilation might be broken on MSVC, haven't tried it.

cbuild.h
extra/cbuild_biblo.h [new file with mode: 0755]
extra/cbuild_codegen.h
extra/cbuild_linagen.h
extra/cbuild_tests.h

index 0651bbd5817c7ea777f01452e24874471fcc7978..4b12616aeea750b9afb795f1327fa83fad7c8179 100644 (file)
--- a/cbuild.h
+++ b/cbuild.h
@@ -49,6 +49,8 @@
 #  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)                                \
@@ -128,39 +140,24 @@ internal cb_assertion_handler_fn cb_assertion_handler = cb_assertion_break;
     }                                                  \
   } 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 {
@@ -178,47 +175,101 @@ enum {
 
 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 {                                            \
@@ -251,53 +302,104 @@ enum {
     (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] ");
@@ -312,13 +414,15 @@ static void cb_print(CB_LogLevel level, char *fmt, ...) {
   }
   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)) {
@@ -331,7 +435,9 @@ static char* cb_getenv(char *varname) {
 #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
@@ -339,18 +445,20 @@ static bool cb_setenv(char *varname, char *value) {
 #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
@@ -359,7 +467,8 @@ static void cb_process_wait(CB_Process *proc) {
   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)) {
@@ -371,13 +480,17 @@ static void cb_process_wait(CB_Process *proc) {
 }
 
 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;
@@ -399,79 +512,85 @@ static CB_Handle cb_handle_open(char *path, CB_AccessFlag permission) {
                      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, '/');
@@ -483,11 +602,10 @@ static bool cb_dir_create(char *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
@@ -495,7 +613,7 @@ static void cb_dir_delete(char *path) {
 #endif
 }
 
-static void cb_file_delete(char *path) {
+static void cb_file_delete(const char *path) {
 #if OS_WINDOWS
   _unlink(path);
 #else
@@ -503,16 +621,15 @@ static void cb_file_delete(char *path) {
 #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
@@ -520,7 +637,96 @@ static bool cb_file_exists(char *path) {
   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;
@@ -529,27 +735,116 @@ static bool cb_cli_contains(int32_t argc, char **argv, char *target) {
   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;
@@ -563,26 +858,40 @@ internal void _cb_handle_write(CB_Handle fd, char *buffer, size_t buffsize) {
     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], "\\")
@@ -591,14 +900,14 @@ internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct Cb_Cmd_RunArgs args) {
     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();
@@ -607,8 +916,8 @@ internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct Cb_Cmd_RunArgs args) {
                   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);
   }
 
@@ -617,8 +926,8 @@ internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct Cb_Cmd_RunArgs args) {
 #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); }
@@ -628,12 +937,12 @@ internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct Cb_Cmd_RunArgs args) {
       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.
@@ -650,11 +959,11 @@ internal CB_Process _cb_cmd_run(CB_Cmd *cmd, struct Cb_Cmd_RunArgs args) {
   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);
@@ -669,25 +978,25 @@ internal void _cb_rebuild(int argc, char **argv, char *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);
@@ -696,14 +1005,14 @@ internal void _cb_rebuild(int argc, char **argv, char *builder_src, ...) {
   }
 
   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);
@@ -714,12 +1023,12 @@ internal bool _cb_need_rebuild(char *output_path, struct CB_PathList sources) {
   }
   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);
@@ -730,7 +1039,7 @@ internal bool _cb_need_rebuild(char *output_path, struct CB_PathList sources) {
     }
     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) {
@@ -745,9 +1054,9 @@ internal bool _cb_need_rebuild(char *output_path, struct CB_PathList sources) {
     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; }
@@ -756,27 +1065,12 @@ internal bool _cb_need_rebuild(char *output_path, struct CB_PathList sources) {
 #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
diff --git a/extra/cbuild_biblo.h b/extra/cbuild_biblo.h
new file mode 100755 (executable)
index 0000000..11a09db
--- /dev/null
@@ -0,0 +1,423 @@
+#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
index 1eca80e59c7d75dffc7dd6ab326cd6f003d22595..f7d3e73a3e7bd7ee85468c7136bfe8a769d7acc2 100644 (file)
@@ -9,7 +9,7 @@ typedef struct {
   size_t total_length;
   struct {
     int32_t has_args;
-    int32_t spacing;
+    size_t spacing;
   } signature;
 } CB_Generator;
 
@@ -23,17 +23,17 @@ static void cb_gen_push_func_end(CB_Generator *gen, bool implementation);
 // ======================================================================
 // 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);
 }
@@ -43,6 +43,16 @@ static void cb_gen_push(CB_Generator *gen, char *string) {
   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;
@@ -59,14 +69,19 @@ static void cb_gen_push_func_arg(CB_Generator *gen, char *argument) {
   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;
 }
index 77519a6cbcee4a06631b798b8c859dc1149951fc..cf6f1f0bbe0c9b8ce00ef60d3b24e5304caf9918 100644 (file)
@@ -9,6 +9,7 @@
 #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,
@@ -103,18 +104,13 @@ cb_linagen_defun_mat4_projection_perspective_rad(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 = " ";
@@ -177,11 +173,12 @@ cb_linagen_defun_vecn(CB_Generator *gen, char *type, int32_t n, bool implementat
   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
@@ -208,6 +205,7 @@ cb_linagen_defun_vecn_hadamard_div(CB_Generator *gen, char *type, int32_t n, boo
 
 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));
@@ -216,16 +214,17 @@ cb_linagen_defun_vecn_equal(CB_Generator *gen, char *type, int32_t n, bool imple
   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",
@@ -236,16 +235,17 @@ cb_linagen_defun_vecn_near(CB_Generator *gen, char *type, int32_t n, bool implem
   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) {
@@ -255,11 +255,12 @@ cb_linagen_defun_vecn_lerp(CB_Generator *gen, char *type, int32_t n, bool implem
       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));
@@ -268,16 +269,17 @@ cb_linagen_defun_vecn_lerp(CB_Generator *gen, char *type, int32_t n, bool implem
       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) {
@@ -286,11 +288,12 @@ cb_linagen_defun_vecn_scale(CB_Generator *gen, char *type, int32_t n, bool imple
       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));
@@ -298,16 +301,17 @@ cb_linagen_defun_vecn_scale(CB_Generator *gen, char *type, int32_t n, bool imple
       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) {
@@ -315,38 +319,40 @@ cb_linagen_defun_vecn_normalize(CB_Generator *gen, char *type, int32_t n, bool i
       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));
@@ -355,15 +361,17 @@ cb_linagen_defun_vecn_dot(CB_Generator *gen, char *type, int32_t n, bool impleme
   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)) {
@@ -372,18 +380,19 @@ cb_linagen_defun_vecn_mulm(CB_Generator *gen, char *type, int32_t n, bool implem
       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));
@@ -392,23 +401,24 @@ cb_linagen_defun_vecn_mulm(CB_Generator *gen, char *type, int32_t n, bool implem
     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",
@@ -417,18 +427,18 @@ cb_linagen_defun_vecn_magnitude(CB_Generator *gen, char *type, int32_t n, bool i
   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",
@@ -437,18 +447,18 @@ cb_linagen_defun_vecn_magnitude64(CB_Generator *gen, char *type, int32_t n, bool
   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",
@@ -458,19 +468,18 @@ cb_linagen_defun_vecn_distance(CB_Generator *gen, char *type, int32_t n, bool im
   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
@@ -484,18 +493,17 @@ cb_linagen_defun_vecn_distance2(CB_Generator *gen, char *type, int32_t n, bool i
   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
@@ -508,15 +516,17 @@ cb_linagen_defun_vec3_cross(CB_Generator *gen, char *type, bool implementation)
   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) {
@@ -526,13 +536,14 @@ cb_linagen_defun_matnn_scale(CB_Generator *gen, char *type, int32_t n, bool impl
     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));
@@ -541,14 +552,13 @@ cb_linagen_defun_matnn_scale(CB_Generator *gen, char *type, int32_t n, bool impl
     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");
   }
 }
 
@@ -576,6 +586,7 @@ cb_linagen_defun_matnn_hadamard_div(CB_Generator *gen, char *type, int32_t n, bo
 
 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) {
@@ -585,20 +596,21 @@ cb_linagen_defun_matnn_dot(CB_Generator *gen, char *type, int32_t n, bool implem
     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));
@@ -607,25 +619,26 @@ cb_linagen_defun_matnn_dot(CB_Generator *gen, char *type, int32_t n, bool implem
     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) {
@@ -635,18 +648,19 @@ cb_linagen_defun_matnn_mulv(CB_Generator *gen, char *type, int32_t n, bool imple
     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));
@@ -655,23 +669,24 @@ cb_linagen_defun_matnn_mulv(CB_Generator *gen, char *type, int32_t n, bool imple
     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) {
@@ -679,23 +694,23 @@ cb_linagen_defun_matnn_identity(CB_Generator *gen, char *type, int32_t n, bool i
     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");
   }
 }
 
@@ -709,11 +724,12 @@ cb_linagen_defun_mat4_transform_translate_by(CB_Generator *gen, char *type, bool
   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
@@ -726,11 +742,12 @@ cb_linagen_defun_mat4_transform_translate_to(CB_Generator *gen, char *type, bool
   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
@@ -744,28 +761,29 @@ cb_linagen_defun_mat4_transform_rotate(CB_Generator *gen, char *type, bool imple
   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
@@ -779,31 +797,31 @@ cb_linagen_defun_mat4_transform_rotate_rad(CB_Generator *gen, char *type, bool i
   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));
@@ -812,32 +830,31 @@ cb_linagen_defun_mat4_transform_scale(CB_Generator *gen, char *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));
@@ -846,22 +863,21 @@ cb_linagen_defun_mat4_projection_orthographic(CB_Generator *gen, char *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));
@@ -869,23 +885,22 @@ cb_linagen_defun_mat4_projection_perspective(CB_Generator *gen, char *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));
@@ -893,17 +908,17 @@ cb_linagen_defun_mat4_projection_perspective_rad(CB_Generator *gen, char *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");
 }
 
 
@@ -911,37 +926,37 @@ internal void
 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");
   }
 }
 
@@ -949,6 +964,7 @@ internal void
 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) {
@@ -959,14 +975,14 @@ cb_linagen_defun_matnn_element_wise(CB_Generator *gen, char *type,
     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));
@@ -976,14 +992,13 @@ cb_linagen_defun_matnn_element_wise(CB_Generator *gen, char *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) {
       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");
   }
 }
 
index f0a8d41f54d39b804f296adab047131cbfe4dd16..f79e0e7a70cef756dd849cc13bac222fcf2ad837 100644 (file)
@@ -67,8 +67,8 @@ static void cb_test_run(void) {
     }
   }
   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) {