ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

build.c (172544B)


      1 /* See LICENSE for license details. */
      2 /* NOTE: inspired by nob: https://github.com/tsoding/nob.h */
      3 
      4 /* TODO(rnp):
      5  * [ ]: refactor: allow @Expand to come before the table definition
      6  * [ ]: cross compile/override baked compiler
      7  * [ ]: msvc build doesn't detect out of date files correctly
      8  * [ ]: seperate dwarf debug info
      9  */
     10 
     11 #include "util.h"
     12 
     13 #include <stdarg.h>
     14 #include <setjmp.h>
     15 #include <stdio.h>
     16 
     17 global char *g_argv0;
     18 
     19 #define META_NAMESPACE_UPPER "Beamformer"
     20 #define META_NAMESPACE_LOWER "beamformer"
     21 
     22 #define OUTDIR    "out"
     23 #define OUTPUT(s) OUTDIR OS_PATH_SEPARATOR s
     24 
     25 #if COMPILER_MSVC
     26   #define COMMON_CFLAGS    "-std:c11"
     27   #define COMMON_FLAGS     "-nologo", "-Fo:" OUTDIR "\\", "-Z7", "-Zo"
     28   #define DEBUG_FLAGS      "-Od", "-D_DEBUG"
     29   #define OPTIMIZED_FLAGS  "-O2"
     30   #define EXTRA_FLAGS      ""
     31 #else
     32   #define COMMON_CFLAGS    "-std=c11"
     33   #define COMMON_FLAGS     "-pipe", "-Wall"
     34   #define DEBUG_FLAGS      "-O0", "-D_DEBUG", "-Wno-unused-function"
     35   #define OPTIMIZED_FLAGS  "-O3"
     36   #define EXTRA_FLAGS_BASE "-Werror", "-Wextra", "-Wno-unused-parameter", \
     37                            "-Wno-error=unused-function", "-fno-builtin"
     38   #if COMPILER_GCC
     39     #define EXTRA_FLAGS EXTRA_FLAGS_BASE, "-Wno-unused-variable"
     40   #else
     41     #define EXTRA_FLAGS EXTRA_FLAGS_BASE
     42   #endif
     43 #endif
     44 
     45 #define is_aarch64 ARCH_ARM64
     46 #define is_amd64   ARCH_X64
     47 #define is_unix    OS_LINUX
     48 #define is_w32     OS_WINDOWS
     49 #define is_clang   COMPILER_CLANG
     50 #define is_gcc     COMPILER_GCC
     51 #define is_msvc    COMPILER_MSVC
     52 
     53 #define BEAMFORMER_IMPORT function
     54 
     55 #if OS_LINUX
     56 
     57   #include <dirent.h>
     58   #include <errno.h>
     59   #include <string.h>
     60   #include <sys/select.h>
     61   #include <sys/wait.h>
     62 
     63   #include "os_linux.c"
     64 
     65   #define W32_DECL(x)
     66 
     67   #define OS_SHARED_LINK_LIB(s) "lib" s ".so"
     68   #define OS_SHARED_LIB(s)      s ".so"
     69   #define OS_STATIC_LIB(s)      s ".a"
     70   #define OS_MAIN "main_linux.c"
     71 
     72 #elif OS_WINDOWS
     73 
     74   #include <string.h>
     75 
     76   #include "os_win32.c"
     77 
     78   #define W32_DECL(x) x
     79 
     80   #define OS_SHARED_LINK_LIB(s) s ".dll"
     81   #define OS_SHARED_LIB(s)      s ".dll"
     82   #define OS_STATIC_LIB(s)      s ".lib"
     83   #define OS_MAIN "main_w32.c"
     84 
     85 #else
     86   #error Unsupported Platform
     87 #endif
     88 
     89 #if COMPILER_CLANG
     90   #define COMPILER     "clang"
     91   #define CPP_COMPILER "clang++"
     92   #define PREPROCESSOR "clang", "-E", "-P"
     93 #elif COMPILER_MSVC
     94   #define COMPILER     "cl"
     95   #define CPP_COMPILER "cl"
     96   #define PREPROCESSOR "cl", "/EP"
     97 #else
     98   #define COMPILER     "cc"
     99   #define CPP_COMPILER "c++"
    100   #define PREPROCESSOR "cc", "-E", "-P"
    101 #endif
    102 
    103 #if COMPILER_MSVC
    104   #define LINK_LIB(name)             name ".lib"
    105   #define OBJECT(name)               name ".obj"
    106   #define OUTPUT_DLL(name)           "/LD", "/Fe:", name
    107   #define OUTPUT_LIB(name)           "/out:" OUTPUT(name)
    108   #define OUTPUT_EXE(name)           "/Fe:", name
    109   #define COMPILER_OUTPUT            "/Fo:"
    110   #define STATIC_LIBRARY_BEGIN(name) "lib", "/nologo", name
    111 #else
    112   #define LINK_LIB(name)             "-l" name
    113   #define OBJECT(name)               name ".o"
    114   #define OUTPUT_DLL(name)           "-fPIC", "-shared", "-o", name
    115   #define OUTPUT_LIB(name)           OUTPUT(name)
    116   #define OUTPUT_EXE(name)           "-o", name
    117   #define COMPILER_OUTPUT            "-o"
    118   #define STATIC_LIBRARY_BEGIN(name) "ar", "rc", name
    119 #endif
    120 
    121 #define shift(list, count) ((count)--, *(list)++)
    122 
    123 #define cmd_append_count da_append_count
    124 #define cmd_append(a, s, ...) da_append_count(a, s, ((char *[]){__VA_ARGS__}), \
    125                                               (i64)(sizeof((char *[]){__VA_ARGS__}) / sizeof(char *)))
    126 
    127 DA_STRUCT(char *, Command);
    128 
    129 typedef struct {
    130 	b32   bake_shaders;
    131 	b32   debug;
    132 	b32   generic;
    133 	b32   sanitize;
    134 	b32   tests;
    135 	b32   time;
    136 } Config;
    137 global Config config;
    138 
    139 read_only global str8 c_file_header = str8_comp(""
    140 	"/* See LICENSE for license details. */\n\n"
    141 	"// GENERATED CODE\n\n"
    142 );
    143 
    144 #define BUILD_LOG_KINDS \
    145 	X(Error,    "\x1B[31m[ERROR]\x1B[0m    ") \
    146 	X(Warning,  "\x1B[33m[WARNING]\x1B[0m  ") \
    147 	X(Generate, "\x1B[32m[GENERATE]\x1B[0m ") \
    148 	X(Info,     "\x1B[33m[INFO]\x1B[0m     ") \
    149 	X(Command,  "\x1B[36m[COMMAND]\x1B[0m  ")
    150 #define X(t, ...) BuildLogKind_##t,
    151 typedef enum {BUILD_LOG_KINDS BuildLogKind_Count} BuildLogKind;
    152 #undef X
    153 
    154 function void
    155 build_log_base(BuildLogKind kind, char *format, va_list args)
    156 {
    157 	#define X(t, pre) pre,
    158 	read_only local_persist char *prefixes[BuildLogKind_Count + 1] = {BUILD_LOG_KINDS "[INVALID] "};
    159 	#undef X
    160 	FILE *out = kind == BuildLogKind_Error? stderr : stdout;
    161 	fputs(prefixes[Min(kind, BuildLogKind_Count)], out);
    162 	vfprintf(out, format, args);
    163 	fputc('\n', out);
    164 }
    165 
    166 #define build_log_failure(format, ...) build_log(BuildLogKind_Error, \
    167                                                  "failed to build: " format, ##__VA_ARGS__)
    168 #define build_log_error(...)    build_log(BuildLogKind_Error,    ##__VA_ARGS__)
    169 #define build_log_generate(...) build_log(BuildLogKind_Generate, ##__VA_ARGS__)
    170 #define build_log_info(...)     build_log(BuildLogKind_Info,     ##__VA_ARGS__)
    171 #define build_log_command(...)  build_log(BuildLogKind_Command,  ##__VA_ARGS__)
    172 #define build_log_warning(...)  build_log(BuildLogKind_Warning,  ##__VA_ARGS__)
    173 
    174 function print_format(2, 3) void
    175 build_log(BuildLogKind kind, char *format, ...)
    176 {
    177 	va_list ap;
    178 	va_start(ap, format);
    179 	build_log_base(kind, format, ap);
    180 	va_end(ap);
    181 }
    182 
    183 #define build_fatal(fmt, ...) build_fatal_("%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
    184 function no_return print_format(1, 2) void
    185 build_fatal_(char *format, ...)
    186 {
    187 	va_list ap;
    188 	va_start(ap, format);
    189 	build_log_base(BuildLogKind_Error, format, ap);
    190 	va_end(ap);
    191 	os_exit(1);
    192 }
    193 
    194 function str8
    195 read_entire_file(const char *file, Arena *arena)
    196 {
    197 	str8 result  = {0};
    198 	result.length = os_read_entire_file(file, arena->beg, arena_capacity(arena, u8));
    199 	if (result.length > 0) result.data = arena_commit(arena, result.length);
    200 	return result;
    201 }
    202 
    203 function b32
    204 str8_contains(str8 s, u8 byte)
    205 {
    206 	b32 result = 0;
    207 	for (i64 i = 0 ; !result && i < s.length; i++)
    208 		result |= s.data[i] == byte;
    209 	return result;
    210 }
    211 
    212 function void
    213 stream_push_command(Stream *s, CommandList *c)
    214 {
    215 	if (!s->errors) {
    216 		for (i64 i = 0; i < c->count; i++) {
    217 			str8 item = str8_from_c_str(c->data[i]);
    218 			if (item.length) {
    219 				b32 escape = str8_contains(item, ' ') || str8_contains(item, '"');
    220 				if (escape) stream_append_byte(s, '\'');
    221 				stream_append_str8(s, item);
    222 				if (escape) stream_append_byte(s, '\'');
    223 				if (i != c->count - 1) stream_append_byte(s, ' ');
    224 			}
    225 		}
    226 	}
    227 }
    228 
    229 function print_format(1, 2) char *
    230 temp_sprintf(char *format, ...)
    231 {
    232 	local_persist char buffer[4096];
    233 	va_list ap;
    234 	va_start(ap, format);
    235 	vsnprintf(buffer, countof(buffer), format, ap);
    236 	va_end(ap);
    237 	return buffer;
    238 }
    239 
    240 #if OS_LINUX
    241 
    242 function b32
    243 os_rename_file(char *name, char *new)
    244 {
    245 	b32 result = rename(name, new) != -1;
    246 	return result;
    247 }
    248 
    249 function b32
    250 os_remove_file(char *name)
    251 {
    252 	b32 result = remove(name) != -1;
    253 	return result;
    254 }
    255 
    256 function void
    257 os_make_directory(char *name)
    258 {
    259 	mkdir(name, 0770);
    260 }
    261 
    262 #define os_remove_directory(f) os_remove_directory_(AT_FDCWD, (f))
    263 function b32
    264 os_remove_directory_(i32 base_fd, char *name)
    265 {
    266 	/* POSix sucks */
    267 	#ifndef DT_DIR
    268 	enum {DT_DIR = 4, DT_REG = 8, DT_LNK = 10};
    269 	#endif
    270 
    271 	i32 dir_fd = openat(base_fd, name, O_DIRECTORY);
    272 	b32 result = dir_fd != -1 || errno == ENOTDIR || errno == ENOENT;
    273 	DIR *dir;
    274 	if (dir_fd != -1 && (dir = fdopendir(dir_fd))) {
    275 		struct dirent *dp;
    276 		while ((dp = readdir(dir))) {
    277 			switch (dp->d_type) {
    278 			case DT_LNK:
    279 			case DT_REG:
    280 			{
    281 				unlinkat(dir_fd, dp->d_name, 0);
    282 			}break;
    283 			case DT_DIR:{
    284 				str8 dir_name = str8_from_c_str(dp->d_name);
    285 				if (!str8_equal(str8("."), dir_name) && !str8_equal(str8(".."), dir_name))
    286 					os_remove_directory_(dir_fd, dp->d_name);
    287 			}break;
    288 			default:{
    289 				build_log_warning("\"%s\": unknown directory entry kind: %d", dp->d_name, dp->d_type);
    290 			}break;
    291 			}
    292 		}
    293 
    294 		closedir(dir);
    295 		result = unlinkat(base_fd, name, AT_REMOVEDIR) == 0;
    296 	}
    297 	return result;
    298 }
    299 
    300 function u64
    301 os_get_filetime(char *file)
    302 {
    303 	struct stat sb;
    304 	u64 result = (u64)-1;
    305 	if (stat(file, &sb) != -1)
    306 		result = (u64)sb.st_mtim.tv_sec;
    307 	return result;
    308 }
    309 
    310 function iptr
    311 os_spawn_process(CommandList *cmd, Stream sb)
    312 {
    313 	pid_t result = fork();
    314 	switch (result) {
    315 	case -1: build_fatal("failed to fork command: %s: %s", cmd->data[0], strerror(errno)); break;
    316 	case  0: {
    317 		if (execvp(cmd->data[0], cmd->data) == -1)
    318 			build_fatal("failed to exec command: %s: %s", cmd->data[0], strerror(errno));
    319 		unreachable();
    320 	} break;
    321 	}
    322 	return (iptr)result;
    323 }
    324 
    325 function b32
    326 os_wait_close_process(iptr handle)
    327 {
    328 	b32 result = 0;
    329 	for (;;) {
    330 		i32   status;
    331 		iptr wait_pid = (iptr)waitpid((i32)handle, &status, 0);
    332 		if (wait_pid == -1)
    333 			build_fatal("failed to wait on child process: %s", strerror(errno));
    334 		if (wait_pid == handle) {
    335 			if (WIFEXITED(status)) {
    336 				status = WEXITSTATUS(status);
    337 				/* TODO(rnp): logging */
    338 				result = status == 0;
    339 				break;
    340 			}
    341 			if (WIFSIGNALED(status)) {
    342 				/* TODO(rnp): logging */
    343 				result = 0;
    344 				break;
    345 			}
    346 		} else {
    347 			/* TODO(rnp): handle multiple children */
    348 			InvalidCodePath;
    349 		}
    350 	}
    351 	return result;
    352 }
    353 
    354 #elif OS_WINDOWS
    355 
    356 enum {
    357 	MOVEFILE_REPLACE_EXISTING = 0x01,
    358 
    359 	FILE_ATTRIBUTE_DIRECTORY  = 0x10,
    360 
    361 	ERROR_FILE_NOT_FOUND = 0x02,
    362 	ERROR_PATH_NOT_FOUND = 0x03,
    363 };
    364 
    365 #pragma pack(push, 1)
    366 typedef struct {
    367   u32 file_attributes;
    368   u64 creation_time;
    369   u64 last_access_time;
    370   u64 last_write_time;
    371   u64 file_size;
    372   u64 reserved;
    373   c8  file_name[260];
    374   c8  alternate_file_name[14];
    375   u32 file_type;
    376   u32 creator_type;
    377   u16 finder_flag;
    378 } w32_find_data;
    379 #pragma pack(pop)
    380 
    381 W32(b32)  CreateDirectoryA(c8 *, void *);
    382 W32(b32)  CreateProcessA(u8 *, u8 *, iptr, iptr, b32, u32, iptr, u8 *, iptr, iptr);
    383 W32(b32)  FindClose(iptr);
    384 W32(iptr) FindFirstFileA(c8 *, w32_find_data *);
    385 W32(b32)  FindNextFileA(iptr, w32_find_data *);
    386 W32(b32)  GetExitCodeProcess(iptr, u32 *);
    387 W32(b32)  GetFileTime(iptr, iptr, iptr, iptr);
    388 W32(b32)  MoveFileExA(c8 *, c8 *, u32);
    389 W32(b32)  RemoveDirectoryA(c8 *);
    390 
    391 function void
    392 os_make_directory(char *name)
    393 {
    394 	CreateDirectoryA(name, 0);
    395 }
    396 
    397 function b32
    398 os_remove_directory(char *name)
    399 {
    400 	w32_find_data find_data[1];
    401 	char *search = temp_sprintf(".\\%s\\*", name);
    402 	iptr  handle = FindFirstFileA(search, find_data);
    403 	b32   result = 1;
    404 	if (handle != INVALID_FILE) {
    405 		do {
    406 			str8 file_name = str8_from_c_str(find_data->file_name);
    407 			if (!str8_equal(str8("."), file_name) && !str8_equal(str8(".."), file_name)) {
    408 				char *full_path = temp_sprintf("%s" OS_PATH_SEPARATOR "%s", name, find_data->file_name);
    409 				if (find_data->file_attributes & FILE_ATTRIBUTE_DIRECTORY) {
    410 					char *wow_w32_is_even_worse_than_POSix = strdup(full_path);
    411 					os_remove_directory(wow_w32_is_even_worse_than_POSix);
    412 					free(wow_w32_is_even_worse_than_POSix);
    413 				} else {
    414 					DeleteFileA(full_path);
    415 				}
    416 			}
    417 		} while (FindNextFileA(handle, find_data));
    418 		FindClose(handle);
    419 	} else {
    420 		i32 error = GetLastError();
    421 		result = error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND;
    422 	}
    423 	RemoveDirectoryA(name);
    424 	return result;
    425 }
    426 
    427 function b32
    428 os_rename_file(char *name, char *new)
    429 {
    430 	b32 result = MoveFileExA(name, new, MOVEFILE_REPLACE_EXISTING) != 0;
    431 	return result;
    432 }
    433 
    434 function b32
    435 os_remove_file(char *name)
    436 {
    437 	b32 result = DeleteFileA(name);
    438 	return result;
    439 }
    440 
    441 function u64
    442 os_get_filetime(char *file)
    443 {
    444 	u64 result = (u64)-1;
    445 	iptr h = CreateFileA(file, 0, 0, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0);
    446 	if (h != INVALID_FILE) {
    447 		union { struct { u32 low, high; }; u64 U64; } w32_filetime;
    448 		GetFileTime(h, 0, 0, (iptr)&w32_filetime);
    449 		result = w32_filetime.U64;
    450 		CloseHandle(h);
    451 	}
    452 	return result;
    453 }
    454 
    455 function iptr
    456 os_spawn_process(CommandList *cmd, Stream sb)
    457 {
    458 	struct {
    459 		u32 cb;
    460 		u8 *reserved, *desktop, *title;
    461 		u32 x, y, x_size, y_size, x_count_chars, y_count_chars;
    462 		u32 fill_attr, flags;
    463 		u16 show_window, reserved_2;
    464 		u8 *reserved_3;
    465 		iptr std_input, std_output, std_error;
    466 	} w32_startup_info = {
    467 		.cb = sizeof(w32_startup_info),
    468 		.flags = 0x100,
    469 		.std_input  = GetStdHandle(STD_INPUT_HANDLE),
    470 		.std_output = GetStdHandle(STD_OUTPUT_HANDLE),
    471 		.std_error  = GetStdHandle(STD_ERROR_HANDLE),
    472 	};
    473 
    474 	struct {
    475 		iptr phandle, thandle;
    476 		u32  pid, tid;
    477 	} w32_process_info = {0};
    478 
    479 	/* TODO(rnp): warn if we need to clamp last string */
    480 	sb.widx = Min(sb.widx, (i32)(KB(32) - 1));
    481 	if (sb.widx < sb.cap) sb.data[sb.widx]     = 0;
    482 	else                  sb.data[sb.widx - 1] = 0;
    483 
    484 	iptr result = INVALID_FILE;
    485 	if (CreateProcessA(0, sb.data, 0, 0, 1, 0, 0, 0, (iptr)&w32_startup_info,
    486 	                   (iptr)&w32_process_info))
    487 	{
    488 		CloseHandle(w32_process_info.thandle);
    489 		result = w32_process_info.phandle;
    490 	}
    491 	return result;
    492 }
    493 
    494 function b32
    495 os_wait_close_process(iptr handle)
    496 {
    497 	b32 result = WaitForSingleObject(handle, (u32)-1) != 0xFFFFFFFFUL;
    498 	if (result) {
    499 		u32 status;
    500 		GetExitCodeProcess(handle, &status);
    501 		result = status == 0;
    502 	}
    503 	CloseHandle(handle);
    504 	return result;
    505 }
    506 
    507 #endif
    508 
    509 #define needs_rebuild(b, ...) needs_rebuild_(b, ((char *[]){__VA_ARGS__}), \
    510                                              (sizeof((char *[]){__VA_ARGS__}) / sizeof(char *)))
    511 function b32
    512 needs_rebuild_(char *binary, char *deps[], i64 deps_count)
    513 {
    514 	u64 binary_filetime = os_get_filetime(binary);
    515 	u64 argv0_filetime  = os_get_filetime(g_argv0);
    516 	b32 result = (binary_filetime == (u64)-1) | (argv0_filetime > binary_filetime);
    517 	for (i64 i = 0; i < deps_count; i++) {
    518 		u64 filetime = os_get_filetime(deps[i]);
    519 		result |= (filetime == (u64)-1) | (filetime > binary_filetime);
    520 	}
    521 	return result;
    522 }
    523 
    524 function b32
    525 run_synchronous(Arena a, CommandList *command)
    526 {
    527 	Stream sb = arena_stream(a);
    528 	stream_push_command(&sb, command);
    529 	build_log_command("%.*s", (i32)sb.widx, sb.data);
    530 	return os_wait_close_process(os_spawn_process(command, sb));
    531 }
    532 
    533 function b32
    534 use_sanitization(void)
    535 {
    536 	return config.sanitize && !is_msvc && !(is_w32 && is_gcc);
    537 }
    538 
    539 function void
    540 cmd_base(Arena *a, CommandList *c, b32 cpp, b32 debug)
    541 {
    542 	Config *o = &config;
    543 
    544 	cmd_append(a, c, cpp ? CPP_COMPILER : COMPILER);
    545 
    546 	if (!is_msvc) {
    547 		/* TODO(rnp): support cross compiling with clang */
    548 		if (!o->generic)     cmd_append(a, c, "-march=native");
    549 		else if (is_amd64)   cmd_append(a, c, "-march=x86-64-v3", "-msse4.1");
    550 		else if (is_aarch64) cmd_append(a, c, "-march=armv8");
    551 	}
    552 
    553 	if (!cpp) cmd_append(a, c, COMMON_CFLAGS);
    554 	cmd_append(a, c, COMMON_FLAGS);
    555 	if (debug) cmd_append(a, c, DEBUG_FLAGS);
    556 	else       cmd_append(a, c, OPTIMIZED_FLAGS);
    557 
    558 	/* NOTE: ancient gcc bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80454 */
    559 	if (is_gcc) cmd_append(a, c, "-Wno-missing-braces");
    560 
    561 	if (!is_msvc) cmd_append(a, c, "-fms-extensions");
    562 
    563 	if (debug && is_unix) cmd_append(a, c, "-gdwarf-4");
    564 
    565 	/* NOTE(rnp): need to avoid w32-gcc for ci */
    566 	b32 sanitize = use_sanitization();
    567 	if (sanitize) cmd_append(a, c, "-fsanitize=address,undefined");
    568 	if (!sanitize && o->sanitize) build_log_warning("santizers not supported with this compiler");
    569 }
    570 
    571 function void
    572 check_rebuild_self(Arena arena, i32 argc, char *argv[])
    573 {
    574 	char *binary = shift(argv, argc);
    575 	if (needs_rebuild(binary, __FILE__, "os_win32.c", "os_linux.c", "util.c", "util.h")) {
    576 		Stream name_buffer = arena_stream(arena);
    577 		stream_append_str8s(&name_buffer, str8_from_c_str(binary), str8(".old"));
    578 		char *old_name = (char *)arena_stream_commit_zero(&arena, &name_buffer).data;
    579 
    580 		if (!os_rename_file(binary, old_name))
    581 			build_fatal("failed to move: %s -> %s", binary, old_name);
    582 
    583 		CommandList c = {0};
    584 		cmd_base(&arena, &c, 0, 0);
    585 		cmd_append(&arena, &c, EXTRA_FLAGS);
    586 		if (!is_msvc) cmd_append(&arena, &c, "-Wno-unused-function");
    587 		cmd_append(&arena, &c, __FILE__, OUTPUT_EXE(binary));
    588 		if (is_msvc) cmd_append(&arena, &c, "/link", "-incremental:no", "-opt:ref");
    589 		cmd_append(&arena, &c, (void *)0);
    590 		if (!run_synchronous(arena, &c)) {
    591 			os_rename_file(old_name, binary);
    592 			build_fatal("failed to rebuild self");
    593 		}
    594 		os_remove_file(old_name);
    595 
    596 		c.count = 0;
    597 		cmd_append(&arena, &c, binary);
    598 		cmd_append_count(&arena, &c, argv, argc);
    599 		cmd_append(&arena, &c, (void *)0);
    600 		if (!run_synchronous(arena, &c))
    601 			os_exit(1);
    602 
    603 		os_exit(0);
    604 	}
    605 }
    606 
    607 function void
    608 usage(char *argv0)
    609 {
    610 	printf("%s [--bake-shaders] [--debug] [--sanitize] [--time]\n"
    611 	       "    --debug:       dynamically link and build with debug symbols\n"
    612 	       "    --generic:     compile for a generic target (x86-64-v3 or armv8 with NEON)\n"
    613 	       "    --sanitize:    build with ASAN and UBSAN\n"
    614 	       "    --tests:       also build programs in tests/\n"
    615 	       "    --time:        print build time\n"
    616 	       , argv0);
    617 	os_exit(0);
    618 }
    619 
    620 function void
    621 parse_config(i32 argc, char *argv[])
    622 {
    623 	char *argv0 = shift(argv, argc);
    624 	while (argc > 0) {
    625 		char *arg = shift(argv, argc);
    626 		str8 str   = str8_from_c_str(arg);
    627 		if (str8_equal(str, str8("--bake-shaders"))) {
    628 			config.bake_shaders = 1;
    629 		} else if (str8_equal(str, str8("--debug"))) {
    630 			config.debug = 1;
    631 		} else if (str8_equal(str, str8("--generic"))) {
    632 			config.generic = 1;
    633 		} else if (str8_equal(str, str8("--sanitize"))) {
    634 			config.sanitize = 1;
    635 		} else if (str8_equal(str, str8("--tests"))) {
    636 			config.tests = 1;
    637 		} else if (str8_equal(str, str8("--time"))) {
    638 			config.time = 1;
    639 		} else {
    640 			usage(argv0);
    641 		}
    642 	}
    643 }
    644 
    645 /* NOTE(rnp): produce pdbs on w32 */
    646 function void
    647 cmd_pdb(Arena *a, CommandList *cmd, char *name)
    648 {
    649 	if (is_w32 && is_clang) {
    650 		cmd_append(a, cmd, "-fuse-ld=lld", "-g", "-gcodeview", "-Wl,--pdb=");
    651 	} else if (is_msvc) {
    652 		Stream sb = arena_stream(*a);
    653 		stream_append_str8s(&sb, str8("-PDB:"), str8_from_c_str(name), str8(".pdb"));
    654 		char *pdb = (char *)arena_stream_commit_zero(a, &sb).data;
    655 		cmd_append(a, cmd, "/link", "-incremental:no", "-opt:ref", "-DEBUG", pdb);
    656 	}
    657 }
    658 
    659 function void
    660 git_submodule_update(Arena a, char *name)
    661 {
    662 	Stream sb = arena_stream(a);
    663 	stream_append_str8s(&sb, str8_from_c_str(name), str8(OS_PATH_SEPARATOR), str8(".git"));
    664 	arena_stream_commit_zero(&a, &sb);
    665 
    666 	CommandList git = {0};
    667 	/* NOTE(rnp): cryptic bs needed to get a simple exit code if name is dirty */
    668 	cmd_append(&a, &git, "git", "diff-index", "--quiet", "HEAD", "--", name, (void *)0);
    669 	if (!os_file_exists((c8 *)sb.data) || !run_synchronous(a, &git)) {
    670 		git.count = 1;
    671 		cmd_append(&a, &git, "submodule", "update", "--init", "--depth=1", name, (void *)0);
    672 		if (!run_synchronous(a, &git))
    673 			build_fatal("failed to clone required module: %s", name);
    674 	}
    675 }
    676 
    677 function b32
    678 build_shared_library(Arena a, CommandList cc, char *name, char *output, char **libs, i64 libs_count, char **srcs, i64 srcs_count)
    679 {
    680 	cmd_append_count(&a, &cc, srcs, srcs_count);
    681 	cmd_append(&a, &cc, OUTPUT_DLL(output));
    682 	cmd_pdb(&a, &cc, name);
    683 	cmd_append_count(&a, &cc, libs, libs_count);
    684 	cmd_append(&a, &cc, (void *)0);
    685 	b32 result = run_synchronous(a, &cc);
    686 	if (!result) build_log_failure("%s", output);
    687 	return result;
    688 }
    689 
    690 function b32
    691 cc_single_file(Arena a, CommandList cc, char *exe, char *src, char *dest, char **tail, i64 tail_count)
    692 {
    693 	char *executable[] = {src, is_msvc? "/Fe:" : "-o", dest};
    694 	char *object[]     = {is_msvc? "/c" : "-c", src, is_msvc? "/Fo:" : "-o", dest};
    695 
    696 	cmd_append_count(&a, &cc, exe? executable : object,
    697 	                 exe? countof(executable) : countof(object));
    698 	if (exe) cmd_pdb(&a, &cc, exe);
    699 	cmd_append_count(&a, &cc, tail, tail_count);
    700 	cmd_append(&a, &cc, (void *)0);
    701 	b32 result = run_synchronous(a, &cc);
    702 	if (!result) build_log_failure("%s", dest);
    703 	return result;
    704 }
    705 
    706 function b32
    707 build_static_library_from_objects(Arena a, char *name, char **flags, i64 flags_count, char **objects, i64 count)
    708 {
    709 	CommandList ar = {0};
    710 	cmd_append(&a, &ar, STATIC_LIBRARY_BEGIN(name));
    711 	cmd_append_count(&a, &ar, flags, flags_count);
    712 	cmd_append_count(&a, &ar, objects, count);
    713 	cmd_append(&a, &ar, (void *)0);
    714 	b32 result = run_synchronous(a, &ar);
    715 	if (!result) build_log_failure("%s", name);
    716 	return result;
    717 }
    718 
    719 function b32
    720 build_static_library(Arena a, CommandList cc, char *name, char **deps, char **outputs, i64 count)
    721 {
    722 	/* TODO(rnp): refactor to not need outputs */
    723 	b32 result = 1;
    724 	for (i64 i = 0; i < count; i++)
    725 		result &= cc_single_file(a, cc, 0, deps[i], outputs[i], 0, 0);
    726 	if (result) result = build_static_library_from_objects(a, name, 0, 0, outputs, count);
    727 	return result;
    728 }
    729 
    730 function b32
    731 build_raylib(Arena a)
    732 {
    733 	b32 result = 1, shared = config.debug;
    734 	char *libraylib = shared ? OS_SHARED_LINK_LIB("raylib") : OUTPUT_LIB(OS_STATIC_LIB("raylib"));
    735 	if (needs_rebuild(libraylib, "external/raylib")) {
    736 		git_submodule_update(a, "external/raylib");
    737 
    738 		CommandList cc = {0};
    739 		cmd_base(&a, &cc, 0, config.debug);
    740 		if (is_unix) cmd_append(&a, &cc, "-D_GLFW_X11");
    741 		cmd_append(&a, &cc, "-DPLATFORM_DESKTOP_GLFW");
    742 		if (!is_msvc) cmd_append(&a, &cc, "-Wno-unused-but-set-variable");
    743 		cmd_append(&a, &cc, "-Iexternal/include", "-Iexternal/raylib/src", "-Iexternal/raylib/src/external/glfw/include");
    744 		#define RAYLIB_SOURCES \
    745 			X(rglfw)     \
    746 			X(rshapes)   \
    747 			X(rtext)     \
    748 			X(rtextures) \
    749 			X(utils)
    750 		#define X(name) "external/raylib/src/" #name ".c",
    751 		char *srcs[] = {"external/rcore_extended.c", RAYLIB_SOURCES};
    752 		#undef X
    753 		#define X(name) OUTPUT(OBJECT(#name)),
    754 		char *outs[] = {OUTPUT(OBJECT("rcore_extended")), RAYLIB_SOURCES};
    755 		#undef X
    756 
    757 		if (shared) {
    758 			char *libs[] = {LINK_LIB("user32"), LINK_LIB("shell32"), LINK_LIB("gdi32"), LINK_LIB("winmm")};
    759 			i64 libs_count = is_w32 ? countof(libs) : 0;
    760 			cmd_append(&a, &cc, "-DBUILD_LIBTYPE_SHARED", "-D_GLFW_BUILD_DLL");
    761 			result = build_shared_library(a, cc, "raylib", libraylib, libs, libs_count, srcs, countof(srcs));
    762 		} else {
    763 			result = build_static_library(a, cc, libraylib, srcs, outs, countof(srcs));
    764 		}
    765 	}
    766 	return result;
    767 }
    768 
    769 function b32
    770 build_glslang(Arena a)
    771 {
    772 	b32 result = 1;
    773 	char *lib = OUTPUT_LIB(OS_STATIC_LIB("glslang"));
    774 	if (needs_rebuild(lib, "external/glslang", "external/glslang_local/glslang.cpp")) {
    775 		git_submodule_update(a, "external/glslang");
    776 
    777 		// NOTE(rnp): do not build this with debug symbols. The size explodes because c++
    778 		CommandList cc = {0};
    779 		cmd_base(&a, &cc, 1, 0);
    780 		cmd_append(&a, &cc, "-std=c++17", "-fno-rtti", "-fno-exceptions", "-Wno-unused-but-set-variable");
    781 		cmd_append(&a, &cc, "-Iexternal/glslang_local", "-Iexternal/glslang");
    782 
    783 		#if OS_WINDOWS
    784 		  #define GLSLANG_SOURCES_OS X(ossource, "glslang/glslang/OSDependent/Windows/")
    785 		#else
    786 		  #define GLSLANG_SOURCES_OS
    787 		#endif
    788 
    789 		#define GLSLANG_SOURCES_COMMON \
    790 			X(glslang,           "glslang_local/") \
    791 			X(spirv_c_interface, "glslang/SPIRV/CInterface/") \
    792 
    793 		#define GLSLANG_SOURCES \
    794 			GLSLANG_SOURCES_COMMON \
    795 			GLSLANG_SOURCES_OS \
    796 
    797 		#define X(name, extra) "external/" extra #name ".cpp",
    798 		char *srcs[] = {GLSLANG_SOURCES};
    799 		#undef X
    800 		#define X(name, ...) OUTPUT(OBJECT(#name)),
    801 		char *outs[] = {GLSLANG_SOURCES};
    802 		#undef X
    803 
    804 		result = build_static_library(a, cc, lib, srcs, outs, countof(srcs));
    805 	}
    806 	return result;
    807 }
    808 
    809 function b32
    810 build_helper_library(Arena arena)
    811 {
    812 	CommandList cc = {0};
    813 	cmd_base(&arena, &cc, 0, 0);
    814 	cmd_append(&arena, &cc, EXTRA_FLAGS);
    815 
    816 	/////////////
    817 	// library
    818 	char *library = OUTPUT(OS_SHARED_LIB("ogl_beamformer_lib"));
    819 	char *libs[]  = {LINK_LIB("Synchronization")};
    820 	i64 libs_count = is_w32 ? countof(libs) : 0;
    821 
    822 	if (!is_msvc) cmd_append(&arena, &cc, "-Wno-unused-function");
    823 	b32 result = build_shared_library(arena, cc, "ogl_beamformer_lib", library,
    824 	                                  libs, libs_count, (char *[]){"lib/ogl_beamformer_lib.c"}, 1);
    825 	return result;
    826 }
    827 
    828 function void
    829 cmd_beamformer_base(Arena *a, CommandList *c)
    830 {
    831 	cmd_base(a, c, 0, config.debug);
    832 	cmd_append(a, c, "-Iexternal/include");
    833 	cmd_append(a, c, EXTRA_FLAGS);
    834 	cmd_append(a, c, config.bake_shaders? "-DBakeShaders=1" : "-DBakeShaders=0");
    835 	if (config.debug) cmd_append(a, c, "-DBEAMFORMER_DEBUG", "-DBEAMFORMER_RENDERDOC_HOOKS");
    836 
    837 	/* NOTE(rnp): impossible to autodetect on GCC versions < 14 (ci has 13) */
    838 	cmd_append(a, c, use_sanitization() ? "-DASAN_ACTIVE=1" : "-DASAN_ACTIVE=0");
    839 }
    840 
    841 function b32
    842 build_beamformer_main(Arena arena)
    843 {
    844 	CommandList c = {0};
    845 	cmd_beamformer_base(&arena, &c);
    846 
    847 	cmd_append(&arena, &c, OS_MAIN, OUTPUT_EXE("ogl"));
    848 	cmd_pdb(&arena, &c, "ogl");
    849 	if (config.debug) {
    850 		if (!is_w32)  cmd_append(&arena, &c, "-Wl,--export-dynamic", "-Wl,-rpath,.");
    851 		if (!is_msvc) cmd_append(&arena, &c, "-L.");
    852 		cmd_append(&arena, &c, LINK_LIB("raylib"));
    853 	} else {
    854 		if (!is_msvc) cmd_append(&arena, &c, "-flto");
    855 		cmd_append(&arena, &c, OUTPUT(OS_STATIC_LIB("raylib")));
    856 	}
    857 	// TODO(rnp): not sure how to do this with msvc. we don't want a runtime dependence on libc++
    858 	cmd_append(&arena, &c, OUTPUT(OS_STATIC_LIB("glslang")), "-Wl,-Bstatic", "-lstdc++", "-Wl,-Bdynamic");
    859 
    860 	if (!is_msvc) cmd_append(&arena, &c, "-lm");
    861 	if (is_unix)  cmd_append(&arena, &c, "-lGL");
    862 
    863 	if (is_w32) {
    864 		cmd_append(&arena, &c, LINK_LIB("user32"), LINK_LIB("shell32"), LINK_LIB("gdi32"),
    865 		           LINK_LIB("opengl32"), LINK_LIB("winmm"), LINK_LIB("Synchronization"));
    866 		if (!is_msvc) cmd_append(&arena, &c, "-Wl,--out-implib," OUTPUT(OS_STATIC_LIB("main")));
    867 	}
    868 
    869 	cmd_append(&arena, &c, (void *)0);
    870 
    871 	return run_synchronous(arena, &c);
    872 }
    873 
    874 function b32
    875 build_beamformer_as_library(Arena arena)
    876 {
    877 	CommandList cc = {0};
    878 	cmd_beamformer_base(&arena, &cc);
    879 
    880 	if (is_msvc) {
    881 		build_static_library_from_objects(arena, OUTPUT_LIB(OS_STATIC_LIB("main")),
    882 		                                  arg_list(char *, "/def", "/name:ogl.exe"),
    883 		                                  arg_list(char *, OUTPUT(OBJECT("main_w32"))));
    884 	}
    885 
    886 	char *library = OS_SHARED_LIB("beamformer");
    887 	char *libs[]  = {!is_msvc? "-L." : "", LINK_LIB("raylib"), LINK_LIB("gdi32"),
    888 	                 LINK_LIB("shell32"), LINK_LIB("user32"), LINK_LIB("opengl32"),
    889 	                 LINK_LIB("winmm"), LINK_LIB("Synchronization"), OUTPUT("main.lib")};
    890 	i64 libs_count = is_w32 ? countof(libs) : 0;
    891 	cmd_append(&arena, &cc, "-D_BEAMFORMER_DLL");
    892 	b32 result = build_shared_library(arena, cc, "beamformer", library,
    893 	                                  libs, libs_count, arg_list(char *, "beamformer_core.c"));
    894 	return result;
    895 }
    896 
    897 function b32
    898 build_tests(Arena arena)
    899 {
    900 	CommandList cc = {0};
    901 	cmd_base(&arena, &cc, 0, config.debug);
    902 	cmd_append(&arena, &cc, EXTRA_FLAGS);
    903 
    904 	#define TEST_PROGRAMS \
    905 		X("throughput", LINK_LIB("m"), LINK_LIB("zstd"), W32_DECL(LINK_LIB("Synchronization"))) \
    906 		X("decode", LINK_LIB("m"), W32_DECL(LINK_LIB("Synchronization"))) \
    907 
    908 	os_make_directory(OUTPUT("tests"));
    909 	if (!is_msvc) cmd_append(&arena, &cc, "-Wno-unused-function");
    910 	cmd_append(&arena, &cc, "-I.", "-Ilib");
    911 
    912 	b32 result = 1;
    913 	i64 cc_count = cc.count;
    914 	#define X(prog, ...) \
    915 		result &= cc_single_file(arena, cc, prog, "tests" OS_PATH_SEPARATOR prog ".c", \
    916 		                         OUTPUT("tests" OS_PATH_SEPARATOR prog), \
    917 		                         arg_list(char *, ##__VA_ARGS__)); \
    918 		cc.count = cc_count;
    919 	TEST_PROGRAMS
    920 	#undef X
    921 	return result;
    922 }
    923 
    924 typedef struct {
    925 	str8     *data;
    926 	da_count  count;
    927 	da_count  capacity;
    928 } str8_list;
    929 
    930 function str8
    931 str8_chop(str8 *in, i64 count)
    932 {
    933 	count = Clamp(count, 0, in->length);
    934 	str8 result = {.data = in->data, .length = count};
    935 	in->data   += count;
    936 	in->length -= count;
    937 	return result;
    938 }
    939 
    940 function void
    941 str8_split(str8 str, str8 *left, str8 *right, u8 byte)
    942 {
    943 	i64 i;
    944 	for (i = 0; i < str.length; i++) if (str.data[i] == byte) break;
    945 
    946 	if (left) *left = (str8){.data = str.data, .length = i};
    947 	if (right) {
    948 		right->data   = str.data + i + 1;
    949 		right->length = Max(0, str.length - (i + 1));
    950 	}
    951 }
    952 
    953 function str8
    954 str8_trim(str8 in)
    955 {
    956 	str8 result = in;
    957 	for (i64 i = 0; i < in.length && *result.data == ' '; i++) result.data++;
    958 	result.length -= result.data - in.data;
    959 	for (; result.length > 0 && result.data[result.length - 1] == ' '; result.length--);
    960 	return result;
    961 }
    962 
    963 typedef struct {
    964 	Stream stream;
    965 	Arena  scratch;
    966 	i32    indentation_level;
    967 } MetaprogramContext;
    968 
    969 function b32
    970 meta_write_and_reset(MetaprogramContext *m, char *file)
    971 {
    972 	b32 result = os_write_new_file(file, stream_to_str8(&m->stream));
    973 	if (!result) build_log_failure("%s", file);
    974 	m->stream.widx       = 0;
    975 	m->indentation_level = 0;
    976 	return result;
    977 }
    978 
    979 #define meta_push(m, ...) meta_push_(m, arg_list(str8, __VA_ARGS__))
    980 function void
    981 meta_push_(MetaprogramContext *m, str8 *items, i64 count)
    982 {
    983 	stream_append_str8s_(&m->stream, items, count);
    984 }
    985 
    986 #define meta_pad(m, b, n)                stream_pad(&(m)->stream, (b), (n))
    987 #define meta_indent(m)                   meta_pad((m), '\t', (m)->indentation_level)
    988 #define meta_begin_line(m, ...)          meta_indent(m), meta_push(m, __VA_ARGS__)
    989 #define meta_end_line(m, ...)                            meta_push(m, ##__VA_ARGS__, str8("\n"))
    990 #define meta_push_line(m, ...)           meta_indent(m), meta_push(m, ##__VA_ARGS__, str8("\n"))
    991 #define meta_begin_scope(m, ...)         meta_push_line(m, __VA_ARGS__), (m)->indentation_level++
    992 #define meta_end_scope(m, ...)           (m)->indentation_level--, meta_push_line(m, __VA_ARGS__)
    993 #define meta_push_f64(m, n)              stream_append_f64(&(m)->stream, (n), 1000000)
    994 #define meta_push_u64(m, n)              stream_append_u64(&(m)->stream, (n))
    995 #define meta_push_i64(m, n)              stream_append_i64(&(m)->stream, (n))
    996 #define meta_push_u64_hex(m, n)          stream_append_hex_u64(&(m)->stream, (n))
    997 #define meta_push_u64_hex_width(m, n, w) stream_append_hex_u64_width(&(m)->stream, (n), (w))
    998 
    999 #define MATLAB_NAMESPACE "OGL"
   1000 
   1001 #define meta_begin_matlab_class_cracker(_1, _2, FN, ...) FN
   1002 #define meta_begin_matlab_class_1(m, name) meta_begin_scope(m, str8("classdef " name))
   1003 #define meta_begin_matlab_class_2(m, name, type) \
   1004   meta_begin_scope(m, str8("classdef " name " < " type))
   1005 
   1006 #define meta_begin_matlab_class(m, ...) \
   1007   meta_begin_matlab_class_cracker(__VA_ARGS__, \
   1008                                   meta_begin_matlab_class_2, \
   1009                                   meta_begin_matlab_class_1)(m, __VA_ARGS__)
   1010 
   1011 function b32
   1012 meta_end_and_write_matlab(MetaprogramContext *m, char *path)
   1013 {
   1014 	while (m->indentation_level > 0) meta_end_scope(m, str8("end"));
   1015 	b32 result = meta_write_and_reset(m, path);
   1016 	return result;
   1017 }
   1018 
   1019 #define META_ENTRY_KIND_LIST \
   1020 	X(Invalid) \
   1021 	X(Array) \
   1022 	X(Bake) \
   1023 	X(BeginScope) \
   1024 	X(Constant) \
   1025 	X(Embed) \
   1026 	X(Emit) \
   1027 	X(EndScope) \
   1028 	X(Enumeration) \
   1029 	X(Expand) \
   1030 	X(Flags) \
   1031 	X(FragmentShader) \
   1032 	X(Library) \
   1033 	X(MATLAB) \
   1034 	X(PushConstants) \
   1035 	X(RenderShader) \
   1036 	X(Shader) \
   1037 	X(ShaderAlias) \
   1038 	X(ShaderGroup) \
   1039 	X(String) \
   1040 	X(Struct) \
   1041 	X(Table) \
   1042 	X(Union) \
   1043 	X(VertexShader) \
   1044 
   1045 typedef enum {
   1046 	#define X(k, ...) MetaEntryKind_## k,
   1047 	META_ENTRY_KIND_LIST
   1048 	#undef X
   1049 	MetaEntryKind_Count,
   1050 } MetaEntryKind;
   1051 
   1052 #define X(k, ...) #k,
   1053 read_only global char *meta_entry_kind_strings[] = {META_ENTRY_KIND_LIST};
   1054 #undef X
   1055 
   1056 #define META_EMIT_LANG_LIST \
   1057 	X(C)        \
   1058 	X(CLibrary) \
   1059 	X(MATLAB)
   1060 
   1061 typedef enum {
   1062 	#define X(k, ...) MetaEmitLang_## k,
   1063 	META_EMIT_LANG_LIST
   1064 	#undef X
   1065 	MetaEmitLang_Count,
   1066 } MetaEmitLang;
   1067 
   1068 #define META_KIND_LIST \
   1069 	X(M4,  m4,   f32mat4,   float,    single, 64, 16) \
   1070 	X(V4,  v4,   f32vec4,   float,    single, 16,  4) \
   1071 	X(SV4, iv4,  i32vec4,   int32_t,  int32,  16,  4) \
   1072 	X(UV4, uv4,  u32vec4,   uint32_t, uint32, 16,  4) \
   1073 	X(UV2, uv2,  u32vec2,   uint32_t, uint32,  8,  2) \
   1074 	X(V3,  v3,   f32vec3,   float,    single, 12,  3) \
   1075 	X(V2,  v2,   f32vec2,   float,    single,  8,  2) \
   1076 	X(F32, f32,  float32_t, float,    single,  4,  1) \
   1077 	X(S32, i32,  int32_t,   int32_t,  int32,   4,  1) \
   1078 	X(S16, i16,  int16_t,   int16_t,  int16,   2,  1) \
   1079 	X(S8,  i8,   int8_t,    int8_t,   int8,    1,  1) \
   1080 	X(B64, b64,  uint64_t,  uint64_t, uint64,  8,  1) \
   1081 	X(B32, b32,  bool,      uint32_t, uint32,  4,  1) \
   1082 	X(B16, b16,  uint16_t,  uint16_t, uint16,  2,  1) \
   1083 	X(B8,  b8,   uint8_t,   uint8_t,  uint8,   1,  1) \
   1084 	X(U64, u64,  uint64_t,  uint64_t, uint64,  8,  1) \
   1085 	X(U32, u32,  uint32_t,  uint32_t, uint32,  4,  1) \
   1086 	X(U16, u16,  uint16_t,  uint16_t, uint16,  2,  1) \
   1087 	X(U8,  u8,   uint8_t,   uint8_t,  uint8,   1,  1) \
   1088 	X(STR, str8, error,     error,    error,  16,  1) \
   1089 
   1090 typedef enum {
   1091 	#define X(k, ...) MetaKind_## k,
   1092 	META_KIND_LIST
   1093 	#undef X
   1094 	MetaKind_Count,
   1095 } MetaKind;
   1096 
   1097 read_only global u8 meta_kind_byte_sizes[] = {
   1098 	#define X(_k, _c, _g, _b, _m, bytes, ...) bytes,
   1099 	META_KIND_LIST
   1100 	#undef X
   1101 };
   1102 
   1103 read_only global u8 meta_kind_elements[] = {
   1104 	#define X(_k, _c, _g, _b, _m, _by, elements, ...) elements,
   1105 	META_KIND_LIST
   1106 	#undef X
   1107 };
   1108 
   1109 read_only global str8 meta_kind_meta_types[] = {
   1110 	#define X(k, ...) str8_comp(#k),
   1111 	META_KIND_LIST
   1112 	#undef X
   1113 };
   1114 
   1115 read_only global str8 meta_kind_matlab_types[] = {
   1116 	#define X(_k, _c, _g, _b, m, ...) str8_comp(#m),
   1117 	META_KIND_LIST
   1118 	#undef X
   1119 };
   1120 
   1121 read_only global str8 meta_kind_base_c_types[] = {
   1122 	#define X(_k, _c, _g, base, ...) str8_comp(#base),
   1123 	META_KIND_LIST
   1124 	#undef X
   1125 };
   1126 
   1127 read_only global str8 meta_kind_glsl_types[] = {
   1128 	#define X(_k, _c, glsl, ...) str8_comp(#glsl),
   1129 	META_KIND_LIST
   1130 	#undef X
   1131 };
   1132 
   1133 read_only global str8 meta_kind_c_types[] = {
   1134 	#define X(_k, c, ...) str8_comp(#c),
   1135 	META_KIND_LIST
   1136 	#undef X
   1137 };
   1138 
   1139 #define META_CURRENT_LOCATION (MetaLocation){__LINE__, 0}
   1140 typedef struct { u32 line, column; } MetaLocation;
   1141 
   1142 #define META_ENTRY_ARGUMENT_KIND_LIST \
   1143 	X(None)   \
   1144 	X(String) \
   1145 	X(Array)
   1146 
   1147 #define X(k, ...) MetaEntryArgumentKind_## k,
   1148 typedef enum {META_ENTRY_ARGUMENT_KIND_LIST} MetaEntryArgumentKind;
   1149 #undef X
   1150 
   1151 typedef struct {
   1152 	MetaEntryArgumentKind kind;
   1153 	MetaLocation          location;
   1154 	union {
   1155 		str8 string;
   1156 		struct {
   1157 			str8 *strings;
   1158 			u64   count;
   1159 		};
   1160 	};
   1161 } MetaEntryArgument;
   1162 
   1163 typedef struct {
   1164 	MetaEntryKind      kind;
   1165 	u32                argument_count;
   1166 	MetaEntryArgument *arguments;
   1167 	str8               name;
   1168 	MetaLocation       location;
   1169 } MetaEntry;
   1170 
   1171 typedef struct {
   1172 	MetaEntry *data;
   1173 	da_count   count;
   1174 	da_count   capacity;
   1175 	str8       raw;
   1176 } MetaEntryStack;
   1177 
   1178 #define META_PARSE_TOKEN_LIST \
   1179 	X('@', Entry)      \
   1180 	X('`', RawString)  \
   1181 	X('(', BeginArgs)  \
   1182 	X(')', EndArgs)    \
   1183 	X('[', BeginArray) \
   1184 	X(']', EndArray)   \
   1185 	X('{', BeginScope) \
   1186 	X('}', EndScope)
   1187 
   1188 typedef enum {
   1189 	MetaParseToken_EOF,
   1190 	MetaParseToken_String,
   1191 	#define X(__1, kind, ...) MetaParseToken_## kind,
   1192 	META_PARSE_TOKEN_LIST
   1193 	#undef X
   1194 	MetaParseToken_Count,
   1195 } MetaParseToken;
   1196 
   1197 typedef union {
   1198 	MetaEntryKind kind;
   1199 	str8          string;
   1200 } MetaParseUnion;
   1201 
   1202 typedef struct {
   1203 	str8 s;
   1204 	MetaLocation location;
   1205 } MetaParsePoint;
   1206 
   1207 typedef struct {
   1208 	MetaParsePoint p;
   1209 	MetaParseUnion u;
   1210 	MetaParsePoint save_point;
   1211 } MetaParser;
   1212 
   1213 global char    *compiler_file;
   1214 global jmp_buf  compiler_jmp_buf;
   1215 
   1216 #define meta_parser_save(v)    (v)->save_point = (v)->p
   1217 #define meta_parser_restore(v) swap((v)->p, (v)->save_point)
   1218 #define meta_parser_commit(v)  meta_parser_restore(v)
   1219 
   1220 #define meta_compiler_message(format, ...) \
   1221 	fprintf(stderr, format, ##__VA_ARGS__)
   1222 
   1223 #define meta_compiler_error_message(loc, format, ...) \
   1224 	fprintf(stderr, "%s:%u:%u: error: "format, compiler_file, \
   1225 	        loc.line + 1, loc.column + 1, ##__VA_ARGS__)
   1226 
   1227 #define meta_compiler_error(loc, format, ...) do { \
   1228 	meta_compiler_error_message(loc, format, ##__VA_ARGS__); \
   1229 	meta_error(); \
   1230 } while (0)
   1231 
   1232 #define meta_entry_error(e, ...) meta_entry_error_column((e), (i32)(e)->location.column, __VA_ARGS__)
   1233 #define meta_entry_error_column(e, column, ...) do { \
   1234 	meta_compiler_error_message((e)->location, __VA_ARGS__); \
   1235 	meta_entry_print((e), 2 * (column), 0); \
   1236 	meta_error(); \
   1237 } while(0)
   1238 
   1239 #define meta_entry_pair_error(e, prefix, base_kind) \
   1240 	meta_entry_error(e, prefix"@%s() in @%s()\n", \
   1241 	                 meta_entry_kind_strings[(e)->kind], \
   1242 	                 meta_entry_kind_strings[(base_kind)])
   1243 
   1244 #define meta_entry_nesting_error(e, base_kind) meta_entry_pair_error(e, "invalid nesting: ", base_kind)
   1245 
   1246 #define meta_entry_error_location(e, loc, ...) do { \
   1247 	meta_compiler_error_message((loc), __VA_ARGS__); \
   1248 	meta_entry_print((e), 1, (i32)(loc).column); \
   1249 	meta_error(); \
   1250 } while (0)
   1251 
   1252 function no_return void
   1253 meta_error(void)
   1254 {
   1255 	assert(0);
   1256 	longjmp(compiler_jmp_buf, 1);
   1257 }
   1258 
   1259 function void
   1260 meta_entry_print(MetaEntry *e, i32 indent, i32 caret)
   1261 {
   1262 	char *kind = meta_entry_kind_strings[e->kind];
   1263 	if (e->kind == MetaEntryKind_BeginScope) kind = "{";
   1264 	if (e->kind == MetaEntryKind_EndScope)   kind = "}";
   1265 
   1266 	fprintf(stderr, "%*s@%s", indent, "", kind);
   1267 
   1268 	if (e->argument_count) {
   1269 		fprintf(stderr, "(");
   1270 		for (u32 i = 0; i < e->argument_count; i++) {
   1271 			MetaEntryArgument *a = e->arguments + i;
   1272 			if (i != 0) fprintf(stderr, " ");
   1273 			if (a->kind == MetaEntryArgumentKind_Array) {
   1274 				fprintf(stderr, "[");
   1275 				for (u64 j = 0; j < a->count; j++) {
   1276 					if (j != 0) fprintf(stderr, " ");
   1277 					fprintf(stderr, "%.*s", (i32)a->strings[j].length, a->strings[j].data);
   1278 				}
   1279 				fprintf(stderr, "]");
   1280 			} else {
   1281 				fprintf(stderr, "%.*s", (i32)a->string.length, a->string.data);
   1282 			}
   1283 		}
   1284 		fprintf(stderr, ")");
   1285 	}
   1286 	if (e->name.length) fprintf(stderr, " %.*s", (i32)e->name.length, e->name.data);
   1287 
   1288 	if (caret >= 0) fprintf(stderr, "\n%*s^", indent + caret, "");
   1289 
   1290 	fprintf(stderr, "\n");
   1291 }
   1292 
   1293 function i64
   1294 meta_lookup_string_slow(str8 *strings, i64 string_count, str8 s)
   1295 {
   1296 	// TODO(rnp): obviously this is slow
   1297 	i64 result = -1;
   1298 	for (i64 i = 0; i < string_count; i++) {
   1299 		if (str8_equal(s, strings[i])) {
   1300 			result = i;
   1301 			break;
   1302 		}
   1303 	}
   1304 	return result;
   1305 }
   1306 
   1307 function MetaEntryKind
   1308 meta_entry_kind_from_string(str8 s)
   1309 {
   1310 	#define X(k, ...) str8_comp(#k),
   1311 	read_only local_persist str8 kinds[] = {META_ENTRY_KIND_LIST};
   1312 	#undef X
   1313 	MetaEntryKind result = MetaEntryKind_Invalid;
   1314 	i64 id = meta_lookup_string_slow(kinds + 1, countof(kinds) - 1, s);
   1315 	if (id > 0) result = (MetaEntryKind)(id + 1);
   1316 	return result;
   1317 }
   1318 
   1319 function void
   1320 meta_parser_trim(MetaParser *p)
   1321 {
   1322 	u8 *s, *end = p->p.s.data + p->p.s.length;
   1323 	b32 done    = 0;
   1324 	b32 comment = 0;
   1325 	for (s = p->p.s.data; !done && s != end;) {
   1326 		switch (*s) {
   1327 		case '\r': case '\t': case ' ':
   1328 		{
   1329 			p->p.location.column++;
   1330 		}break;
   1331 		case '\n':{ p->p.location.line++; p->p.location.column = 0; comment = 0; }break;
   1332 		case '/':{
   1333 			comment |= ((s + 1) != end && s[1] == '/');
   1334 			if (comment) s++;
   1335 		} /* FALLTHROUGH */
   1336 		default:{done = !comment;}break;
   1337 		}
   1338 		if (!done) s++;
   1339 	}
   1340 	p->p.s.data   = s;
   1341 	p->p.s.length = end - s;
   1342 }
   1343 
   1344 function str8
   1345 meta_parser_extract_raw_string(MetaParser *p)
   1346 {
   1347 	str8 result = {.data = p->p.s.data};
   1348 	for (; result.length < p->p.s.length; result.length++) {
   1349 		u8 byte = p->p.s.data[result.length];
   1350 		p->p.location.column++;
   1351 		if (byte == '`') {
   1352 			break;
   1353 		} else if (byte == '\n') {
   1354 			p->p.location.column = 0;
   1355 			p->p.location.line++;
   1356 		}
   1357 	}
   1358 	p->p.s.data   += (result.length + 1);
   1359 	p->p.s.length -= (result.length + 1);
   1360 	return result;
   1361 }
   1362 
   1363 function str8
   1364 meta_parser_extract_string(MetaParser *p)
   1365 {
   1366 	str8 result = {.data = p->p.s.data};
   1367 	for (; result.length < p->p.s.length; result.length++) {
   1368 		b32 done = 0;
   1369 		switch (p->p.s.data[result.length]) {
   1370 		#define X(t, ...) case t:
   1371 		META_PARSE_TOKEN_LIST
   1372 		#undef X
   1373 		case ' ': case '\n': case '\r': case '\t':
   1374 		{done = 1;}break;
   1375 		case '/':{
   1376 			done = (result.length + 1 < p->p.s.length) && (p->p.s.data[result.length + 1] == '/');
   1377 		}break;
   1378 		default:{}break;
   1379 		}
   1380 		if (done) break;
   1381 	}
   1382 	p->p.location.column += (u32)result.length;
   1383 	p->p.s.data          += result.length;
   1384 	p->p.s.length        -= result.length;
   1385 	return result;
   1386 }
   1387 
   1388 function str8
   1389 meta_parser_token_name(MetaParser *p, MetaParseToken t)
   1390 {
   1391 	str8 result = str8("\"invalid\"");
   1392 	read_only local_persist str8 names[MetaParseToken_Count] = {
   1393 		[MetaParseToken_EOF] = str8_comp("\"EOF\""),
   1394 		#define X(k, v, ...) [MetaParseToken_## v] = str8_comp(#k),
   1395 		META_PARSE_TOKEN_LIST
   1396 		#undef X
   1397 	};
   1398 	if (t >= 0 && t < countof(names))  result = names[t];
   1399 	if (t == MetaParseToken_String)    result = p->u.string;
   1400 	if (t == MetaParseToken_RawString) result = (str8){.data = p->u.string.data - 1, .length = p->u.string.length + 1};
   1401 	return result;
   1402 }
   1403 
   1404 function MetaParseToken
   1405 meta_parser_token(MetaParser *p)
   1406 {
   1407 	MetaParseToken result = MetaParseToken_EOF;
   1408 	meta_parser_save(p);
   1409 	if (p->p.s.length > 0) {
   1410 		b32 chop = 1;
   1411 		switch (p->p.s.data[0]) {
   1412 		#define X(t, kind, ...) case t:{ result = MetaParseToken_## kind; }break;
   1413 		META_PARSE_TOKEN_LIST
   1414 		#undef X
   1415 		default:{ result = MetaParseToken_String; chop = 0; }break;
   1416 		}
   1417 		if (chop) { str8_chop(&p->p.s, 1); p->p.location.column++; }
   1418 
   1419 		if (result != MetaParseToken_RawString) meta_parser_trim(p);
   1420 		switch (result) {
   1421 		case MetaParseToken_RawString:{ p->u.string = meta_parser_extract_raw_string(p); }break;
   1422 		case MetaParseToken_String:{    p->u.string = meta_parser_extract_string(p);     }break;
   1423 
   1424 		/* NOTE(rnp): '{' and '}' are shorthand for @BeginScope and @EndScope */
   1425 		case MetaParseToken_BeginScope:{ p->u.kind = MetaEntryKind_BeginScope; }break;
   1426 		case MetaParseToken_EndScope:{   p->u.kind = MetaEntryKind_EndScope;   }break;
   1427 
   1428 		/* NOTE(rnp): loose '[' implies implicit @Array() */
   1429 		case MetaParseToken_BeginArray:{ p->u.kind = MetaEntryKind_Array; }break;
   1430 
   1431 		case MetaParseToken_Entry:{
   1432 			str8 kind = meta_parser_extract_string(p);
   1433 			p->u.kind = meta_entry_kind_from_string(kind);
   1434 			if (p->u.kind == MetaEntryKind_Invalid) {
   1435 				meta_compiler_error(p->p.location, "invalid keyword: @%.*s\n", (i32)kind.length, kind.data);
   1436 			}
   1437 		}break;
   1438 		default:{}break;
   1439 		}
   1440 		meta_parser_trim(p);
   1441 	}
   1442 
   1443 	return result;
   1444 }
   1445 
   1446 function MetaParseToken
   1447 meta_parser_peek_token(MetaParser *p)
   1448 {
   1449 	MetaParseToken result = meta_parser_token(p);
   1450 	meta_parser_restore(p);
   1451 	return result;
   1452 }
   1453 
   1454 function void
   1455 meta_parser_unexpected_token(MetaParser *p, MetaParseToken t)
   1456 {
   1457 	meta_parser_restore(p);
   1458 	str8 token_name = meta_parser_token_name(p, t);
   1459 	meta_compiler_error(p->p.location, "unexpected token: %.*s\n", (i32)token_name.length, token_name.data);
   1460 }
   1461 
   1462 function void
   1463 meta_parser_fill_argument_array(MetaParser *p, MetaEntryArgument *array, Arena *arena)
   1464 {
   1465 	array->kind     = MetaEntryArgumentKind_Array;
   1466 	array->strings  = arena_aligned_start(*arena, alignof(str8));
   1467 	array->location = p->p.location;
   1468 	for (MetaParseToken token = meta_parser_token(p);
   1469 	     token != MetaParseToken_EndArray;
   1470 	     token = meta_parser_token(p))
   1471 	{
   1472 		switch (token) {
   1473 		case MetaParseToken_RawString:
   1474 		case MetaParseToken_String:
   1475 		{
   1476 			assert((u8 *)(array->strings + array->count) == arena->beg);
   1477 			*push_struct(arena, str8) = p->u.string;
   1478 			array->count++;
   1479 		}break;
   1480 		default:{ meta_parser_unexpected_token(p, token); }break;
   1481 		}
   1482 	}
   1483 }
   1484 
   1485 function void
   1486 meta_parser_arguments(MetaParser *p, MetaEntry *e, Arena *arena)
   1487 {
   1488 	if (meta_parser_peek_token(p) == MetaParseToken_BeginArgs) {
   1489 		meta_parser_commit(p);
   1490 
   1491 		e->arguments = arena_aligned_start(*arena, alignof(MetaEntryArgument));
   1492 		for (MetaParseToken token = meta_parser_token(p);
   1493 		     token != MetaParseToken_EndArgs;
   1494 		     token = meta_parser_token(p))
   1495 		{
   1496 			e->argument_count++;
   1497 			MetaEntryArgument *arg = push_struct(arena, MetaEntryArgument);
   1498 			switch (token) {
   1499 			case MetaParseToken_RawString:
   1500 			case MetaParseToken_String:
   1501 			{
   1502 				arg->kind     = MetaEntryArgumentKind_String;
   1503 				arg->string   = p->u.string;
   1504 				arg->location = p->p.location;
   1505 			}break;
   1506 			case MetaParseToken_BeginArray:{
   1507 				meta_parser_fill_argument_array(p, arg, arena);
   1508 			}break;
   1509 			default:{ meta_parser_unexpected_token(p, token); }break;
   1510 			}
   1511 		}
   1512 	}
   1513 }
   1514 
   1515 typedef struct {
   1516 	MetaEntry *start;
   1517 	MetaEntry *one_past_last;
   1518 	i64 consumed;
   1519 } MetaEntryScope;
   1520 
   1521 function MetaEntryScope
   1522 meta_entry_extract_scope(MetaEntry *base, i64 entry_count)
   1523 {
   1524 	assert(base->kind != MetaEntryKind_BeginScope && base->kind != MetaEntryKind_EndScope);
   1525 	assert(entry_count > 0);
   1526 
   1527 	MetaEntryScope result = {.start = base + 1, .consumed = 1};
   1528 	i64 sub_scope = 0;
   1529 	for (MetaEntry *e = result.start; result.consumed < entry_count; result.consumed++, e++) {
   1530 		switch (e->kind) {
   1531 		case MetaEntryKind_BeginScope:{ sub_scope++; }break;
   1532 		case MetaEntryKind_EndScope:{   sub_scope--; }break;
   1533 		default:{}break;
   1534 		}
   1535 		if (sub_scope == 0) break;
   1536 	}
   1537 
   1538 	if (sub_scope != 0)
   1539 		meta_entry_error(base, "unclosed scope for entry\n");
   1540 
   1541 	result.one_past_last = base + result.consumed;
   1542 	if (result.start->kind == MetaEntryKind_BeginScope) result.start++;
   1543 	if (result.one_past_last == result.start) result.one_past_last++;
   1544 
   1545 	return result;
   1546 }
   1547 
   1548 function MetaEntryStack
   1549 meta_entry_stack_from_file(Arena *arena, char *file)
   1550 {
   1551 	MetaParser     parser = {.p.s = read_entire_file(file, arena)};
   1552 	MetaEntryStack result = {.raw = parser.p.s};
   1553 
   1554 	compiler_file = file;
   1555 
   1556 	meta_parser_trim(&parser);
   1557 
   1558 	for (MetaParseToken token = meta_parser_token(&parser);
   1559 	     token != MetaParseToken_EOF;
   1560 	     token = meta_parser_token(&parser))
   1561 	{
   1562 		MetaEntry *e = da_push(arena, &result);
   1563 		switch (token) {
   1564 		case MetaParseToken_String:
   1565 		case MetaParseToken_RawString:
   1566 		{
   1567 			e->kind     = MetaEntryKind_String;
   1568 			e->location = parser.save_point.location;
   1569 			e->name     = parser.u.string;
   1570 		}break;
   1571 
   1572 		case MetaParseToken_BeginScope:
   1573 		case MetaParseToken_EndScope:
   1574 		{
   1575 			e->kind     = parser.u.kind;
   1576 			e->location = parser.save_point.location;
   1577 		}break;
   1578 
   1579 		case MetaParseToken_BeginArray:
   1580 		case MetaParseToken_Entry:
   1581 		{
   1582 			e->kind     = parser.u.kind;
   1583 			e->location = parser.save_point.location;
   1584 
   1585 			if (token == MetaParseToken_Entry)
   1586 				meta_parser_arguments(&parser, e, arena);
   1587 
   1588 			if (token == MetaParseToken_BeginArray) {
   1589 				MetaEntryArgument *a = e->arguments = push_struct(arena, MetaEntryArgument);
   1590 				e->argument_count = 1;
   1591 				meta_parser_fill_argument_array(&parser, a, arena);
   1592 			}
   1593 
   1594 			if (meta_parser_peek_token(&parser) == MetaParseToken_String) {
   1595 				meta_parser_commit(&parser);
   1596 				e->name = parser.u.string;
   1597 			}
   1598 		}break;
   1599 
   1600 		default:{ meta_parser_unexpected_token(&parser, token); }break;
   1601 		}
   1602 	}
   1603 
   1604 	return result;
   1605 }
   1606 
   1607 #define meta_entry_argument_expected(e, ...) \
   1608 	meta_entry_argument_expected_((e), arg_list(str8, __VA_ARGS__))
   1609 function void
   1610 meta_entry_argument_expected_(MetaEntry *e, str8 *args, u64 count)
   1611 {
   1612 	if (e->argument_count != count) {
   1613 		meta_compiler_error_message(e->location, "incorrect argument count for entry %s() got: %u expected: %u\n",
   1614 		                            meta_entry_kind_strings[e->kind], e->argument_count, (u32)count);
   1615 		fprintf(stderr, "  format: @%s(", meta_entry_kind_strings[e->kind]);
   1616 		for EachIndex(count, it) {
   1617 			if (it != 0) fprintf(stderr, ", ");
   1618 			fprintf(stderr, "%.*s", (i32)args[it].length, args[it].data);
   1619 		}
   1620 		fprintf(stderr, ")\n");
   1621 		meta_error();
   1622 	}
   1623 }
   1624 
   1625 function MetaEntryArgument
   1626 meta_entry_argument_expect(MetaEntry *e, u32 index, MetaEntryArgumentKind kind)
   1627 {
   1628 	#define X(k, ...) #k,
   1629 	read_only local_persist char *kinds[] = {META_ENTRY_ARGUMENT_KIND_LIST};
   1630 	#undef X
   1631 
   1632 	assert(e->argument_count > index);
   1633 	MetaEntryArgument result = e->arguments[index];
   1634 
   1635 	if (result.kind != kind) {
   1636 		meta_entry_error_location(e, result.location, "unexpected argument kind: expected %s but got: %s\n",
   1637 		                          kinds[kind], kinds[result.kind]);
   1638 	}
   1639 
   1640 	if (kind == MetaEntryArgumentKind_Array && result.count == 0)
   1641 		meta_entry_error_location(e, result.location, "array arguments must have at least 1 element\n");
   1642 
   1643 	return result;
   1644 }
   1645 
   1646 typedef struct { da_count value; } MetaEntityID;
   1647 
   1648 typedef struct {
   1649 	da_count *data;
   1650 	da_count  count;
   1651 	da_count  capacity;
   1652 } MetaIDList;
   1653 
   1654 typedef enum {
   1655 	MetaExpansionPartKind_Alignment,
   1656 	MetaExpansionPartKind_Conditional,
   1657 	MetaExpansionPartKind_EvalKind,
   1658 	MetaExpansionPartKind_EvalKindCount,
   1659 	MetaExpansionPartKind_Reference,
   1660 	MetaExpansionPartKind_String,
   1661 } MetaExpansionPartKind;
   1662 
   1663 typedef enum {
   1664 	MetaExpansionConditionalArgumentKind_Invalid,
   1665 	MetaExpansionConditionalArgumentKind_Number,
   1666 	MetaExpansionConditionalArgumentKind_Evaluation,
   1667 	MetaExpansionConditionalArgumentKind_Reference,
   1668 } MetaExpansionConditionalArgumentKind;
   1669 
   1670 typedef struct {
   1671 	MetaExpansionConditionalArgumentKind kind;
   1672 	union {
   1673 		str8 *strings;
   1674 		i64 number;
   1675 	};
   1676 } MetaExpansionConditionalArgument;
   1677 
   1678 typedef enum {
   1679 	MetaExpansionOperation_Invalid,
   1680 	MetaExpansionOperation_LessThan,
   1681 	MetaExpansionOperation_GreaterThan,
   1682 } MetaExpansionOperation;
   1683 
   1684 typedef struct {
   1685 	MetaExpansionConditionalArgument lhs;
   1686 	MetaExpansionConditionalArgument rhs;
   1687 	MetaExpansionOperation           op;
   1688 	u32 instruction_skip;
   1689 } MetaExpansionConditional;
   1690 
   1691 typedef struct {
   1692 	MetaExpansionPartKind kind;
   1693 	union {
   1694 		str8  string;
   1695 		str8 *strings;
   1696 		MetaExpansionConditional conditional;
   1697 	};
   1698 } MetaExpansionPart;
   1699 DA_STRUCT(MetaExpansionPart, MetaExpansionPart);
   1700 
   1701 typedef enum {
   1702 	MetaEmitOperationKind_Expand,
   1703 	MetaEmitOperationKind_FileBytes,
   1704 	MetaEmitOperationKind_String,
   1705 } MetaEmitOperationKind;
   1706 
   1707 typedef struct {
   1708 	MetaExpansionPart *parts;
   1709 	u32      part_count;
   1710 	da_count table_entity_id;
   1711 } MetaEmitOperationExpansion;
   1712 
   1713 typedef struct {
   1714 	union {
   1715 		str8 string;
   1716 		MetaEmitOperationExpansion expansion_operation;
   1717 	};
   1718 	MetaEmitOperationKind kind;
   1719 	MetaLocation          location;
   1720 } MetaEmitOperation;
   1721 
   1722 typedef struct {
   1723 	MetaEmitOperation *data;
   1724 	da_count count;
   1725 	da_count capacity;
   1726 
   1727 	str8 filename;
   1728 } MetaEmitOperationList;
   1729 
   1730 typedef struct {
   1731 	MetaEmitOperationList *data;
   1732 	da_count count;
   1733 	da_count capacity;
   1734 } MetaEmitOperationListSet;
   1735 
   1736 typedef enum {
   1737 	MetaShaderKind_Alias,
   1738 	MetaShaderKind_Compute,
   1739 	MetaShaderKind_Render,
   1740 	MetaShaderKind_Count,
   1741 } MetaShaderKind;
   1742 
   1743 typedef enum {
   1744 	MetaShaderPrimitiveKind_Mesh,
   1745 	MetaShaderPrimitiveKind_Vertex,
   1746 	MetaShaderPrimitiveKind_Count,
   1747 } MetaShaderPrimitiveKind;
   1748 
   1749 typedef struct {
   1750 	MetaShaderPrimitiveKind kind;
   1751 } MetaRenderShader;
   1752 
   1753 typedef struct {
   1754 	MetaShaderKind kind;
   1755 	MetaIDList     entity_reference_ids;
   1756 	str8           files[2];
   1757 	union {
   1758 		MetaEntityID      alias_parent_id;
   1759 		MetaRenderShader  render;
   1760 	};
   1761 } MetaShader;
   1762 
   1763 #define META_STRUCT_FIELDS \
   1764 	X(Name,     name) \
   1765 	X(Type,     type) \
   1766 	X(Elements, elements) \
   1767 
   1768 #define X(id, ...) MetaStructField_##id,
   1769 typedef enum {META_STRUCT_FIELDS} MetaStructFields;
   1770 #undef X
   1771 
   1772 #define META_BAKE_FIELDS \
   1773 	X(NameUpper, name_upper) \
   1774 	X(NameLower, name_lower) \
   1775 	X(Type,      type)       \
   1776 
   1777 #define X(id, ...) MetaBakeField_##id,
   1778 typedef enum {META_BAKE_FIELDS} MetaBakeFields;
   1779 #undef X
   1780 
   1781 typedef struct {
   1782 	str8  *fields;
   1783 	str8 **entries;
   1784 	u32    field_count;
   1785 	u32    entry_count;
   1786 	union {
   1787 		i32 struct_info_id;
   1788 	};
   1789 } MetaTable;
   1790 
   1791 typedef enum {
   1792 	MetaConstantKind_Integer,
   1793 	MetaConstantKind_Float,
   1794 	MetaConstantKind_Count,
   1795 } MetaConstantKind;
   1796 
   1797 typedef struct {
   1798 	MetaConstantKind kind;
   1799 	u32 name_id;
   1800 	union {
   1801 		u64 U64;
   1802 		f64 F64;
   1803 	};
   1804 } MetaConstant;
   1805 
   1806 typedef struct {
   1807 	str8         reference_name;
   1808 	MetaEntityID resolved_id;
   1809 	da_count     reference_count;
   1810 
   1811 	// NOTE: only used for namespacing MATLAB unions
   1812 	str8         scope_name;
   1813 } MetaEntityReference;
   1814 
   1815 // X(name, is_table, is_struct, struct_reference_target)
   1816 #define META_ENTITY_KIND_LIST \
   1817 	X(Nil,                0, 0, 0) \
   1818 	X(List,               0, 0, 0) \
   1819 	X(BakeParameters,     1, 1, 0) \
   1820 	X(Constant,           0, 0, 0) \
   1821 	X(Enumeration,        1, 0, 1) \
   1822 	X(Flags,              1, 0, 1) \
   1823 	X(PushConstants,      1, 1, 0) \
   1824 	X(Reference,          0, 0, 0) \
   1825 	X(ReferenceReference, 0, 0, 0) \
   1826 	X(Shader,             0, 0, 0) \
   1827 	X(ShaderGroup,        0, 0, 0) \
   1828 	X(Struct,             1, 1, 1) \
   1829 	X(Table,              1, 0, 0) \
   1830 	X(Union,              1, 1, 1) \
   1831 
   1832 // X(EntityKind, TypeField, ElementsField, NameField, AllowReferences, Emit)
   1833 #define META_STRUCT_MAP_LIST \
   1834 	X(BakeParameters, MetaBakeField_Type,   -1,                       MetaBakeField_NameLower, 0, 1) \
   1835 	X(PushConstants,  MetaStructField_Type, MetaStructField_Elements, MetaStructField_Name,    0, 1) \
   1836 	X(Struct,         MetaStructField_Type, MetaStructField_Elements, MetaStructField_Name,    1, 1) \
   1837 	X(Union,          MetaStructField_Type, MetaStructField_Elements, MetaStructField_Name,    1, 0) \
   1838 
   1839 
   1840 typedef enum {
   1841 	#define X(name, ...) MetaEntityKind_ ##name,
   1842 	META_ENTITY_KIND_LIST
   1843 	#undef X
   1844 	MetaEntityKind_Count,
   1845 } MetaEntityKind;
   1846 
   1847 typedef struct {
   1848 	MetaEntityKind kind;
   1849 	MetaEntityID   parent;
   1850 	MetaEntityID   first_child;
   1851 	MetaEntityID   next_sibling;
   1852 	MetaEntityID   previous_sibling;
   1853 	MetaLocation   location;
   1854 	union {
   1855 		MetaConstant        constant;
   1856 		MetaEntityReference reference;
   1857 		MetaShader          shader;
   1858 		MetaTable           table;
   1859 	};
   1860 } MetaEntity;
   1861 DA_STRUCT(MetaEntity, MetaEntity);
   1862 
   1863 #define X(name, ...) str8_comp(#name),
   1864 read_only global str8 meta_entity_kind_names[] = {META_ENTITY_KIND_LIST};
   1865 #undef X
   1866 #define X(_n, table, ...) table,
   1867 read_only global b8 meta_entity_kind_is_table[] = {META_ENTITY_KIND_LIST};
   1868 #undef X
   1869 #define X(_n, _t, s, ...) s,
   1870 read_only global b8 meta_entity_kind_is_struct[] = {META_ENTITY_KIND_LIST};
   1871 #undef X
   1872 #define X(_n, _t, _s, srt, ...) srt,
   1873 read_only global b8 meta_entity_kind_struct_reference_target[] = {META_ENTITY_KIND_LIST};
   1874 #undef X
   1875 
   1876 #define X(k, ...) MetaEntityKind_##k,
   1877 read_only global MetaEntityKind meta_struct_entity_kinds[] = {META_STRUCT_MAP_LIST};
   1878 #undef X
   1879 #define X(_k, t, ...) t,
   1880 read_only global i32 meta_struct_type_field[] = {META_STRUCT_MAP_LIST};
   1881 #undef X
   1882 #define X(_k, _t, e, ...) e,
   1883 read_only global i32 meta_struct_element_field[] = {META_STRUCT_MAP_LIST};
   1884 #undef X
   1885 #define X(_k, _t, _e, n, ...) n,
   1886 read_only global i32 meta_struct_name_field[] = {META_STRUCT_MAP_LIST};
   1887 #undef X
   1888 #define X(_k, _t, _e, _n, allow, ...) allow,
   1889 read_only global b8 meta_struct_allow_references[] = {META_STRUCT_MAP_LIST};
   1890 #undef X
   1891 #define X(_k, _t, _e, _n, _a, emit, ...) emit,
   1892 read_only global b8 meta_struct_emit[] = {META_STRUCT_MAP_LIST};
   1893 #undef X
   1894 
   1895 typedef enum {
   1896 	MetaStructFlag_Union         = 1 << 0,
   1897 	MetaStructFlag_ContainsUnion = 1 << 1,
   1898 } MetaStructFlags;
   1899 
   1900 typedef enum {
   1901 	MetaStructMemberFlag_ReferenceType     = 1 << 0,
   1902 	MetaStructMemberFlag_ReferenceElements = 1 << 1,
   1903 	MetaStructMemberFlag_EnumerationCount  = 1 << 2,
   1904 } MetaStructMemberFlags;
   1905 
   1906 typedef struct {
   1907 	str8  name;
   1908 
   1909 	str8 *members;
   1910 	i32  *type_ids;
   1911 	i32  *elements;
   1912 
   1913 	MetaStructMemberFlags *member_flags;
   1914 
   1915 	u32   member_count;
   1916 	u32   byte_size;
   1917 
   1918 	MetaStructFlags flags;
   1919 
   1920 	MetaEntityID entity;
   1921 	MetaLocation location;
   1922 } MetaStruct;
   1923 
   1924 typedef struct {
   1925 	Arena *arena, scratch;
   1926 
   1927 	str8 filename;
   1928 	str8 directory;
   1929 	str8 fullpath;
   1930 
   1931 	MetaEntityID                 library_entity;
   1932 	MetaEntityID                 matlab_entity;
   1933 
   1934 	// NOTE(rnp): arrays of entity ids sorted by kind and counted by entity_kind_counts
   1935 	da_count                    *entity_kind_ids[MetaEntityKind_Count];
   1936 
   1937 	da_count                     entity_kind_counts[MetaEntityKind_Count];
   1938 	str8_list                    entity_names;
   1939 	MetaEntityList               entities;
   1940 
   1941 	// NOTE(rnp): list of all entities referenced by shaders. needed for header string baking
   1942 	MetaIDList                   shader_entity_references;
   1943 
   1944 	// NOTE(rnp): fully resolved structs
   1945 	MetaStruct                  *struct_infos;
   1946 	u32                          struct_infos_count;
   1947 
   1948 	// NOTE(rnp): dumb jank to support treating CudaHilbert/CudaDecode as shaders and
   1949 	// allowing shader names to alias.
   1950 	da_count                     base_shader_count;
   1951 	da_count                    *base_shader_ids;
   1952 	// NOTE(rnp): map index in the entity_kind_ids[MetaEntityKind_Shader] to base_shader_ids index
   1953 	da_count                    *base_shader_id_map;
   1954 
   1955 
   1956 	MetaEmitOperationListSet     emit_sets[MetaEmitLang_Count];
   1957 } MetaContext;
   1958 
   1959 function da_count
   1960 meta_lookup_id_slow(da_count *v, da_count count, da_count id)
   1961 {
   1962 	// TODO(rnp): obviously this is slow
   1963 	da_count result = -1;
   1964 	for (da_count i = 0; i < count; i++) {
   1965 		if (id == v[i]) {
   1966 			result = i;
   1967 			break;
   1968 		}
   1969 	}
   1970 	return result;
   1971 }
   1972 
   1973 function da_count
   1974 meta_intern_string(MetaContext *ctx, str8_list *sv, str8 s)
   1975 {
   1976 	da_count result = meta_lookup_string_slow(sv->data, sv->count, s);
   1977 	if (result < 0) {
   1978 		*da_push(ctx->arena, sv) = s;
   1979 		result = sv->count - 1;
   1980 	}
   1981 	return result;
   1982 }
   1983 
   1984 function da_count
   1985 meta_intern_id(MetaContext *ctx, MetaIDList *v, da_count id)
   1986 {
   1987 	da_count result = meta_lookup_id_slow(v->data, v->count, id);
   1988 	if (result < 0) {
   1989 		*da_push(ctx->arena, v) = id;
   1990 		result = v->count - 1;
   1991 	}
   1992 	return result;
   1993 }
   1994 
   1995 function da_count
   1996 meta_entity_children_count(MetaContext *ctx, MetaEntityID entity_id)
   1997 {
   1998 	MetaEntityID child = ctx->entities.data[entity_id.value].first_child;
   1999 	da_count result = 0;
   2000 	if (child.value != 0) {
   2001 		do {
   2002 			result++;
   2003 			child = ctx->entities.data[child.value].next_sibling;
   2004 		} while (child.value != ctx->entities.data[entity_id.value].first_child.value);
   2005 	}
   2006 	return result;
   2007 }
   2008 
   2009 function da_count *
   2010 meta_entity_extract_children(MetaContext *ctx, MetaEntityID entity_id, da_count *children_count, Arena *arena)
   2011 {
   2012 	*children_count  = meta_entity_children_count(ctx, entity_id);
   2013 	da_count *result = push_array_no_zero(arena, da_count, *children_count);
   2014 
   2015 	// NOTE(rnp): children are pushed in LIFO order
   2016 	MetaEntity *e = ctx->entities.data + entity_id.value;
   2017 	da_count index = 0;
   2018 	MetaEntityID child = e->first_child;
   2019 	do {
   2020 		child = ctx->entities.data[child.value].previous_sibling;
   2021 		result[index++] = child.value;
   2022 	} while (child.value != e->first_child.value);
   2023 
   2024 	return result;
   2025 }
   2026 
   2027 function MetaEntity *
   2028 meta_entity(MetaContext *ctx, MetaEntityID id)
   2029 {
   2030 	assert(id.value != 0 && id.value < ctx->entities.count);
   2031 	MetaEntity *result = ctx->entities.data + id.value;
   2032 	return result;
   2033 }
   2034 
   2035 function MetaEntityID
   2036 meta_root_entity_id(MetaContext *ctx)
   2037 {
   2038 	MetaEntityID result = {0};
   2039 	return result;
   2040 }
   2041 
   2042 function MetaEntityID
   2043 meta_intern_entity(MetaContext *ctx, str8 name, MetaEntityKind kind, MetaEntityID parent,
   2044                    MetaLocation location, b32 allow_existing)
   2045 {
   2046 	MetaEntityID result = {0};
   2047 	assert(ctx->entities.data[0].kind == MetaEntityKind_Nil);
   2048 	assert(Between(kind, MetaEntityKind_Nil + 1, MetaEntityKind_Count - 1));
   2049 
   2050 	da_count name_id = meta_intern_string(ctx, &ctx->entity_names, name);
   2051 	if (name_id < ctx->entities.count && ctx->entities.data[name_id].kind != kind) {
   2052 		str8 old_kind = meta_entity_kind_names[ctx->entities.data[name_id].kind];
   2053 		str8 new_kind = meta_entity_kind_names[kind];
   2054 		meta_compiler_error_message(location, "attempting to redefine %.*s as kind %.*s\n",
   2055 		                            (i32)name.length, name.data, (i32)new_kind.length, new_kind.data);
   2056 		meta_compiler_error_message(ctx->entities.data[name_id].location, "previously defined as kind %.*s\n",
   2057 		                            (i32)old_kind.length, old_kind.data);
   2058 		meta_error();
   2059 	} else if (name_id < ctx->entities.count && !allow_existing) {
   2060 		meta_compiler_error_message(location, "redefinition of %.*s\n", (i32)name.length, name.data);
   2061 		meta_compiler_error_message(ctx->entities.data[name_id].location, "previously defined here\n");
   2062 		meta_error();
   2063 	} else {
   2064 		if (name_id < ctx->entities.count) {
   2065 			result.value = name_id;
   2066 		} else {
   2067 			ctx->entity_kind_counts[kind]++;
   2068 			MetaEntity *new = da_push(ctx->arena, &ctx->entities);
   2069 			new->location = location;
   2070 			result.value = da_index(new, &ctx->entities);
   2071 		}
   2072 
   2073 		MetaEntity *e = ctx->entities.data + result.value;
   2074 		e->kind       = kind;
   2075 		e->parent     = parent;
   2076 
   2077 		MetaEntity *p = ctx->entities.data + parent.value;
   2078 		e->next_sibling = p->first_child;
   2079 		p->first_child = result;
   2080 
   2081 		if (e->next_sibling.value == 0)
   2082 			e->next_sibling = p->first_child;
   2083 
   2084 		e->previous_sibling = ctx->entities.data[e->next_sibling.value].previous_sibling;
   2085 		ctx->entities.data[e->next_sibling.value].previous_sibling = result;
   2086 		ctx->entities.data[e->previous_sibling.value].next_sibling = result;
   2087 	}
   2088 
   2089 	return result;
   2090 }
   2091 
   2092 function MetaEntityID
   2093 meta_entity_reference(MetaContext *ctx, str8 name, MetaLocation location)
   2094 {
   2095 	MetaEntityID result = {0};
   2096 	Arena scratch;
   2097 	DeferLoop(scratch = ctx->scratch, ctx->scratch = scratch) {
   2098 		str8 ref_name = push_str8_from_parts(&ctx->scratch, str8(""), str8("R"), name);
   2099 		result = meta_intern_entity(ctx, ref_name, MetaEntityKind_Reference,
   2100 		                            meta_root_entity_id(ctx), location, 1);
   2101 		MetaEntity *r = meta_entity(ctx, result);
   2102 		if (r->reference.reference_count == 0)
   2103 			ctx->entity_names.data[result.value] = push_str8(ctx->arena, ref_name);
   2104 		r->reference.reference_count++;
   2105 		r->reference.reference_name = name;
   2106 	}
   2107 	return result;
   2108 }
   2109 
   2110 function MetaEntityID
   2111 meta_entity_reference_reference(MetaContext *ctx, str8 name, str8 scope_name, MetaLocation location,
   2112                                 MetaEntityID parent, str8 prefix)
   2113 {
   2114 	MetaEntityID result = {0};
   2115 	// NOTE(rnp): base reference
   2116 	MetaEntityID ref_id = meta_entity_reference(ctx, name, location);
   2117 
   2118 	Arena scratch;
   2119 	DeferLoop(scratch = ctx->scratch, ctx->scratch = scratch) {
   2120 		str8 refref_name = push_str8_from_parts(&ctx->scratch, str8(""), prefix, str8("RR"), name);
   2121 		result = meta_intern_entity(ctx, refref_name, MetaEntityKind_ReferenceReference,
   2122 		                            parent, location, 1);
   2123 
   2124 		MetaEntity *rr = meta_entity(ctx, result);
   2125 		if (rr->reference.reference_count == 0)
   2126 			ctx->entity_names.data[result.value] = push_str8(ctx->arena, refref_name);
   2127 		rr->reference.reference_count++;
   2128 		rr->reference.reference_name = name;
   2129 		rr->reference.resolved_id    = ref_id;
   2130 		rr->reference.scope_name     = scope_name;
   2131 	}
   2132 	return result;
   2133 }
   2134 
   2135 function MetaEntityID
   2136 meta_entity_first_child_of_kind(MetaContext *ctx, MetaEntity *e, MetaEntityKind kind)
   2137 {
   2138 	MetaEntityID result = {0};
   2139 	MetaEntityID child  = e->first_child;
   2140 	if (child.value) do {
   2141 		if (ctx->entities.data[child.value].kind == kind) {
   2142 			result = child;
   2143 			break;
   2144 		}
   2145 		child = ctx->entities.data[child.value].next_sibling;
   2146 	} while (child.value != e->first_child.value);
   2147 	return result;
   2148 }
   2149 
   2150 function void
   2151 meta_expansion_string_split(str8 string, str8 *left, str8 *inner, str8 *remainder, MetaLocation loc)
   2152 {
   2153 	b32 found = 0;
   2154 	for (u8 *s = string.data, *e = s + string.length; (s + 1) != e; s++) {
   2155 		u32 val  = (u32)'$'  << 8u | (u32)'(';
   2156 		u32 test = (u32)s[0] << 8u | s[1];
   2157 		if (test == val) {
   2158 			if (left) {
   2159 				left->data   = string.data;
   2160 				left->length = s - string.data;
   2161 			}
   2162 
   2163 			u8 *start = s + 2;
   2164 			while (s != e && *s != ')') s++;
   2165 			if (s == e) {
   2166 				meta_compiler_error_message(loc, "unterminated expansion in raw string:\n  %.*s\n",
   2167 				                            (i32)string.length, string.data);
   2168 				fprintf(stderr, "  %.*s^\n", (i32)(start - string.data), "");
   2169 				meta_error();
   2170 			}
   2171 
   2172 			if (inner) {
   2173 				inner->data   = start;
   2174 				inner->length = s - start;
   2175 			}
   2176 
   2177 			if (remainder) {
   2178 				remainder->data   = s + 1;
   2179 				remainder->length = string.length - (remainder->data - string.data);
   2180 			}
   2181 			found = 1;
   2182 			break;
   2183 		}
   2184 	}
   2185 	if (!found) {
   2186 		if (left)      *left      = string;
   2187 		if (inner)     *inner     = (str8){0};
   2188 		if (remainder) *remainder = (str8){0};
   2189 	}
   2190 }
   2191 
   2192 function MetaExpansionPart *
   2193 meta_push_expansion_part(MetaContext *ctx, Arena *arena, MetaExpansionPartList *parts,
   2194                          MetaExpansionPartKind kind, str8 string, MetaEntity *table, MetaLocation loc)
   2195 {
   2196 	MetaExpansionPart *result = da_push(arena, parts);
   2197 
   2198 	result->kind = kind;
   2199 	switch (kind) {
   2200 	case MetaExpansionPartKind_Alignment:
   2201 	case MetaExpansionPartKind_Conditional:
   2202 	{}break;
   2203 
   2204 	case MetaExpansionPartKind_EvalKind:
   2205 	case MetaExpansionPartKind_EvalKindCount:
   2206 	case MetaExpansionPartKind_Reference:
   2207 	{
   2208 		assert(meta_entity_kind_is_table[table->kind]);
   2209 		MetaTable *t = &table->table;
   2210 
   2211 		da_count index = meta_lookup_string_slow(t->fields, t->field_count, string);
   2212 		result->strings = t->entries[index];
   2213 		if (index < 0) {
   2214 			/* TODO(rnp): fix this location to point directly at the field in the string */
   2215 			str8 table_name = ctx->entity_names.data[da_index(table, &ctx->entities)];
   2216 			meta_compiler_error(loc, "table \"%.*s\" does not contain member: %.*s\n",
   2217 			                    (i32)table_name.length, table_name.data, (i32)string.length, string.data);
   2218 		}
   2219 	}break;
   2220 
   2221 	case MetaExpansionPartKind_String:{ result->string = string; }break;
   2222 	InvalidDefaultCase;
   2223 	}
   2224 	return result;
   2225 }
   2226 
   2227 #define META_EXPANSION_TOKEN_LIST \
   2228 	X('|', Alignment) \
   2229 	X('%', TypeEval) \
   2230 	X('#', TypeEvalElements) \
   2231 	X('"', Quote) \
   2232 	X('-', Dash) \
   2233 	X('>', GreaterThan) \
   2234 	X('<', LessThan) \
   2235 
   2236 typedef enum {
   2237 	MetaExpansionToken_EOF,
   2238 	MetaExpansionToken_Identifier,
   2239 	MetaExpansionToken_Number,
   2240 	MetaExpansionToken_String,
   2241 	#define X(__1, kind, ...) MetaExpansionToken_## kind,
   2242 	META_EXPANSION_TOKEN_LIST
   2243 	#undef X
   2244 	MetaExpansionToken_Count,
   2245 } MetaExpansionToken;
   2246 
   2247 read_only global str8 meta_expansion_token_strings[] = {
   2248 	str8_comp("EOF"),
   2249 	str8_comp("Indentifier"),
   2250 	str8_comp("Number"),
   2251 	str8_comp("String"),
   2252 	#define X(s, kind, ...) str8_comp(#s),
   2253 	META_EXPANSION_TOKEN_LIST
   2254 	#undef X
   2255 };
   2256 
   2257 typedef	struct {
   2258 	str8 s;
   2259 	union {
   2260 		i64  number;
   2261 		str8 string;
   2262 	};
   2263 	str8 save;
   2264 	MetaLocation loc;
   2265 } MetaExpansionParser;
   2266 
   2267 #define meta_expansion_save(v)    (v)->save = (v)->s
   2268 #define meta_expansion_restore(v) swap((v)->s, (v)->save)
   2269 #define meta_expansion_commit(v)  meta_expansion_restore(v)
   2270 
   2271 #define meta_expansion_expected(loc, e, g) \
   2272 	meta_compiler_error(loc, "invalid expansion string: expected %.*s after %.*s\n", \
   2273 	                    (i32)meta_expansion_token_strings[e].length, meta_expansion_token_strings[e].data, \
   2274 	                    (i32)meta_expansion_token_strings[g].length, meta_expansion_token_strings[g].data)
   2275 
   2276 function str8
   2277 meta_expansion_extract_string(MetaExpansionParser *p)
   2278 {
   2279 	str8 result = {.data = p->s.data};
   2280 	for (; result.length < p->s.length; result.length++) {
   2281 		b32 done = 0;
   2282 		switch (p->s.data[result.length]) {
   2283 		#define X(t, ...) case t:
   2284 		META_EXPANSION_TOKEN_LIST
   2285 		#undef X
   2286 		case ' ':
   2287 		{done = 1;}break;
   2288 		default:{}break;
   2289 		}
   2290 		if (done) break;
   2291 	}
   2292 	p->s.data   += result.length;
   2293 	p->s.length -= result.length;
   2294 	return result;
   2295 }
   2296 
   2297 function MetaExpansionToken
   2298 meta_expansion_token(MetaExpansionParser *p)
   2299 {
   2300 	MetaExpansionToken result = MetaExpansionToken_EOF;
   2301 	meta_expansion_save(p);
   2302 	if (p->s.length > 0) {
   2303 		b32 chop = 1;
   2304 		switch (p->s.data[0]) {
   2305 		#define X(t, kind, ...) case t:{ result = MetaExpansionToken_## kind; }break;
   2306 		META_EXPANSION_TOKEN_LIST
   2307 		#undef X
   2308 		default:{
   2309 			chop = 0;
   2310 			if (Between(p->s.data[0], '0', '9')) result = MetaExpansionToken_Number;
   2311 			else                                 result = MetaExpansionToken_Identifier;
   2312 		}break;
   2313 		}
   2314 		if (chop) {
   2315 			str8_chop(&p->s, 1);
   2316 			p->s = str8_trim(p->s);
   2317 		}
   2318 
   2319 		switch (result) {
   2320 		case MetaExpansionToken_Number:{
   2321 			NumberConversion integer = integer_from_str8(p->s);
   2322 			if (integer.result != NumberConversionResult_Success) {
   2323 				/* TODO(rnp): point at start */
   2324 				meta_compiler_error(p->loc, "invalid integer in expansion string\n");
   2325 			}
   2326 			p->number = integer.S64;
   2327 			p->s      = integer.unparsed;
   2328 		}break;
   2329 		case MetaExpansionToken_Identifier:{ p->string = meta_expansion_extract_string(p); }break;
   2330 		default:{}break;
   2331 		}
   2332 		p->s = str8_trim(p->s);
   2333 	}
   2334 	return result;
   2335 }
   2336 
   2337 function MetaExpansionPart *
   2338 meta_expansion_start_conditional(MetaContext *ctx, Arena *arena, MetaExpansionPartList *ops,
   2339                                  MetaExpansionParser *p, MetaExpansionToken token, b32 negate)
   2340 {
   2341 	MetaExpansionPart *result = meta_push_expansion_part(ctx, arena, ops, MetaExpansionPartKind_Conditional,
   2342 	                                                     str8(""), 0, p->loc);
   2343 	switch (token) {
   2344 	case MetaExpansionToken_Number:{
   2345 		result->conditional.lhs.kind   = MetaExpansionConditionalArgumentKind_Number;
   2346 		result->conditional.lhs.number = negate ? -p->number : p->number;
   2347 	}break;
   2348 	default:{}break;
   2349 	}
   2350 	return result;
   2351 }
   2352 
   2353 function void
   2354 meta_expansion_end_conditional(MetaExpansionPart *ep, MetaExpansionParser *p, MetaExpansionToken token, b32 negate)
   2355 {
   2356 	if (ep->conditional.rhs.kind != MetaExpansionConditionalArgumentKind_Invalid) {
   2357 		meta_compiler_error(p->loc, "invalid expansion conditional: duplicate right hand expression: '%.*s'\n",
   2358 		                    (i32)p->save.length, p->save.data);
   2359 	}
   2360 	switch (token) {
   2361 	case MetaExpansionToken_Number:{
   2362 		ep->conditional.rhs.kind   = MetaExpansionConditionalArgumentKind_Number;
   2363 		ep->conditional.rhs.number = negate ? -p->number : p->number;
   2364 	}break;
   2365 	default:{}break;
   2366 	}
   2367 }
   2368 
   2369 function MetaExpansionPartList
   2370 meta_generate_expansion_set(MetaContext *ctx, Arena *arena, str8 expansion_string, MetaEntity *table, MetaLocation loc)
   2371 {
   2372 	MetaExpansionPartList result = {0};
   2373 	str8 left = {0}, inner, remainder = expansion_string;
   2374 	do {
   2375 		meta_expansion_string_split(remainder, &left, &inner, &remainder, loc);
   2376 		if (left.length)  meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_String, left, table, loc);
   2377 		if (inner.length) {
   2378 			MetaExpansionParser p[1] = {{.s = inner, .loc = loc}};
   2379 
   2380 			MetaExpansionPart *test_part = 0;
   2381 			b32 count_test_parts = 0;
   2382 
   2383 			for (MetaExpansionToken token = meta_expansion_token(p);
   2384 			     token != MetaExpansionToken_EOF;
   2385 			     token = meta_expansion_token(p))
   2386 			{
   2387 				if (count_test_parts) test_part->conditional.instruction_skip++;
   2388 				switch (token) {
   2389 				case MetaExpansionToken_Alignment:{
   2390 					meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_Alignment, p->s, table, loc);
   2391 				}break;
   2392 
   2393 				case MetaExpansionToken_Identifier:{
   2394 					meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_Reference, p->string, table, loc);
   2395 				}break;
   2396 
   2397 				case MetaExpansionToken_TypeEval:
   2398 				case MetaExpansionToken_TypeEvalElements:
   2399 				{
   2400 					if (meta_expansion_token(p) != MetaExpansionToken_Identifier) {
   2401 						loc.column += (u32)(p->save.data - expansion_string.data);
   2402 						meta_expansion_expected(loc, MetaExpansionToken_Identifier, token);
   2403 					}
   2404 					MetaExpansionPartKind kind = token == MetaExpansionToken_TypeEval ?
   2405 					                                      MetaExpansionPartKind_EvalKind :
   2406 					                                      MetaExpansionPartKind_EvalKindCount;
   2407 					meta_push_expansion_part(ctx, arena, &result, kind, p->string, table, loc);
   2408 				}break;
   2409 
   2410 				case MetaExpansionToken_Quote:{
   2411 					u8 *point = p->s.data;
   2412 					str8 string = meta_expansion_extract_string(p);
   2413 					token = meta_expansion_token(p);
   2414 					if (token != MetaExpansionToken_Quote) {
   2415 						loc.column += (u32)(point - expansion_string.data);
   2416 						/* TODO(rnp): point at start */
   2417 						meta_compiler_error(loc, "unterminated string in expansion\n");
   2418 					}
   2419 					meta_push_expansion_part(ctx, arena, &result, MetaExpansionPartKind_String, string, table, loc);
   2420 				}break;
   2421 
   2422 				case MetaExpansionToken_Dash:{
   2423 					token = meta_expansion_token(p);
   2424 					switch (token) {
   2425 					case MetaExpansionToken_GreaterThan:{
   2426 						if (!test_part) goto error;
   2427 						if (test_part->conditional.lhs.kind == MetaExpansionConditionalArgumentKind_Invalid ||
   2428 						    test_part->conditional.rhs.kind == MetaExpansionConditionalArgumentKind_Invalid)
   2429 						{
   2430 							b32 lhs = test_part->conditional.lhs.kind == MetaExpansionConditionalArgumentKind_Invalid;
   2431 							b32 rhs = test_part->conditional.rhs.kind == MetaExpansionConditionalArgumentKind_Invalid;
   2432 							if (lhs && rhs)
   2433 								meta_compiler_error(loc, "expansion string test terminated without arguments\n");
   2434 							meta_compiler_error(loc, "expansion string test terminated without %s argument\n",
   2435 							                    lhs? "left" : "right");
   2436 						}
   2437 						count_test_parts = 1;
   2438 					}break;
   2439 					case MetaExpansionToken_Number:{
   2440 						if (test_part) meta_expansion_end_conditional(test_part, p, token, 1);
   2441 						else           test_part = meta_expansion_start_conditional(ctx, arena, &result, p, token, 1);
   2442 					}break;
   2443 					default:{ goto error; }break;
   2444 					}
   2445 				}break;
   2446 
   2447 				case MetaExpansionToken_Number:{
   2448 					if (test_part) meta_expansion_end_conditional(test_part, p, token, 0);
   2449 					else           test_part = meta_expansion_start_conditional(ctx, arena, &result, p, token, 0);
   2450 				}break;
   2451 
   2452 				case MetaExpansionToken_GreaterThan:
   2453 				case MetaExpansionToken_LessThan:
   2454 				{
   2455 					if (test_part && test_part->conditional.op != MetaExpansionOperation_Invalid) goto error;
   2456 					if (!test_part) {
   2457 						if (result.count == 0) {
   2458 							meta_compiler_error(p->loc, "invalid expansion conditional: missing left hand side\n");
   2459 						}
   2460 
   2461 						str8 *strings = result.data[result.count - 1].strings;
   2462 						MetaExpansionPartKind last_kind = result.data[result.count - 1].kind;
   2463 						if (last_kind != MetaExpansionPartKind_EvalKindCount &&
   2464 						    last_kind != MetaExpansionPartKind_Reference)
   2465 						{
   2466 							meta_compiler_error(p->loc, "invalid expansion conditional: left hand side not numeric\n");
   2467 						}
   2468 						result.count--;
   2469 						test_part = meta_expansion_start_conditional(ctx, arena, &result, p, token, 0);
   2470 						if (last_kind == MetaExpansionPartKind_EvalKindCount) {
   2471 							test_part->conditional.lhs.kind = MetaExpansionConditionalArgumentKind_Evaluation;
   2472 						} else {
   2473 							test_part->conditional.lhs.kind = MetaExpansionConditionalArgumentKind_Reference;
   2474 						}
   2475 						test_part->conditional.lhs.strings = strings;
   2476 					}
   2477 					test_part->conditional.op = token == MetaExpansionToken_LessThan ?
   2478 					                                     MetaExpansionOperation_LessThan :
   2479 					                                     MetaExpansionOperation_GreaterThan;
   2480 				}break;
   2481 
   2482 				error:
   2483 				default:
   2484 				{
   2485 					meta_compiler_error(loc, "invalid nested %.*s in expansion string\n",
   2486 					                    (i32)meta_expansion_token_strings[token].length,
   2487 					                    meta_expansion_token_strings[token].data);
   2488 				}break;
   2489 				}
   2490 			}
   2491 		}
   2492 	} while (remainder.length);
   2493 	return result;
   2494 }
   2495 
   2496 function da_count
   2497 meta_expand_table_entity_id(MetaContext *ctx, MetaEntry *e)
   2498 {
   2499 	assert(e->kind == MetaEntryKind_Expand);
   2500 
   2501 	/* TODO(rnp): for now this requires that the @Table came first */
   2502 	meta_entry_argument_expected(e, str8("table_name"));
   2503 	str8 table_name = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string;
   2504 
   2505 	da_count result = meta_lookup_string_slow(ctx->entity_names.data, ctx->entity_names.count, table_name);
   2506 
   2507 	if (result < 0) meta_entry_error(e, "undefined table %.*s\n", (i32)table_name.length, table_name.data);
   2508 
   2509 	MetaEntity *table = ctx->entities.data + result;
   2510 	if (!meta_entity_kind_is_table[table->kind]) {
   2511 		str8 old_kind    = meta_entity_kind_names[table->kind];
   2512 		str8 wanted_kind = meta_entity_kind_names[MetaEntityKind_Table];
   2513 		meta_entry_error(e, "%.*s previously defined as %.*s but should be %.*s\n",
   2514 		                 (i32)table_name.length,  table_name.data,
   2515 		                 (i32)old_kind.length,    old_kind.data,
   2516 		                 (i32)wanted_kind.length, wanted_kind.data);
   2517 	}
   2518 
   2519 	return result;
   2520 }
   2521 
   2522 function str8
   2523 meta_expand_parts_to_str8_at_index(MetaContext *ctx, u64 table_index, MetaExpansionPartList parts)
   2524 {
   2525 	Stream sb = arena_stream(*ctx->arena);
   2526 	for EachIndex((u64)parts.count, part) {
   2527 		MetaExpansionPart *p = parts.data + part;
   2528 		u32 index = 0;
   2529 		if (p->kind == MetaExpansionPartKind_Reference) index = table_index;
   2530 		stream_append_str8(&sb, p->strings[index]);
   2531 	}
   2532 	str8 result = arena_stream_commit(ctx->arena, &sb);
   2533 	return result;
   2534 }
   2535 
   2536 function void
   2537 meta_pack_table_begin(MetaEntry *e, MetaTable *t)
   2538 {
   2539 	switch (e->kind) {
   2540 
   2541 	case MetaEntryKind_Bake:
   2542 	{
   2543 		meta_entry_argument_expected_(e, 0, 0);
   2544 		#define X(_i, name, ...) str8_comp(#name),
   2545 		read_only local_persist str8 bake_fields[] = {META_BAKE_FIELDS};
   2546 		#undef X
   2547 		t->fields      = bake_fields;
   2548 		t->field_count = countof(bake_fields);
   2549 	}break;
   2550 
   2551 	case MetaEntryKind_Enumeration:
   2552 	case MetaEntryKind_Flags:
   2553 	{
   2554 		read_only local_persist str8 enumeration_fields[] = {str8_comp("name")};
   2555 		t->fields      = enumeration_fields;
   2556 		t->field_count = countof(enumeration_fields);
   2557 	}break;
   2558 
   2559 	case MetaEntryKind_PushConstants:
   2560 	case MetaEntryKind_Struct:
   2561 	case MetaEntryKind_Union:
   2562 	{
   2563 		meta_entry_argument_expected_(e, 0, 0);
   2564 		#define X(_i, name, ...) str8_comp(#name),
   2565 		read_only local_persist str8 struct_fields[] = {META_STRUCT_FIELDS};
   2566 		#undef X
   2567 		t->fields      = struct_fields;
   2568 		t->field_count = countof(struct_fields);
   2569 	}break;
   2570 
   2571 	case MetaEntryKind_Table:{
   2572 		meta_entry_argument_expected(e, str8("[field ...]"));
   2573 		MetaEntryArgument fields = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_Array);
   2574 		t->fields      = fields.strings;
   2575 		t->field_count = (u32)fields.count;
   2576 	}break;
   2577 
   2578 	InvalidDefaultCase;
   2579 	}
   2580 }
   2581 
   2582 function i64
   2583 meta_pack_table_entity(MetaContext *ctx, MetaEntry *e, i64 entry_count, str8 name, MetaEntityID parent)
   2584 {
   2585 	MetaEntityKind entity_kind = MetaEntityKind_Nil;
   2586 	switch (e->kind) {
   2587 	case MetaEntryKind_Bake:{         entity_kind = MetaEntityKind_BakeParameters;}break;
   2588 	case MetaEntryKind_Enumeration:{  entity_kind = MetaEntityKind_Enumeration;   }break;
   2589 	case MetaEntryKind_Flags:{        entity_kind = MetaEntityKind_Flags;         }break;
   2590 	case MetaEntryKind_PushConstants:{entity_kind = MetaEntityKind_PushConstants; }break;
   2591 	case MetaEntryKind_Struct:{       entity_kind = MetaEntityKind_Struct;        }break;
   2592 	case MetaEntryKind_Table:{        entity_kind = MetaEntityKind_Table;         }break;
   2593 	case MetaEntryKind_Union:{        entity_kind = MetaEntityKind_Union;         }break;
   2594 	InvalidDefaultCase;
   2595 	}
   2596 
   2597 	MetaEntityID entity_id = meta_intern_entity(ctx, name, entity_kind, parent, e->location, 0);
   2598 
   2599 	MetaTable table = {0}, *t = &table;
   2600 	meta_pack_table_begin(e, t);
   2601 
   2602 	b32 structure = e->kind == MetaEntryKind_Struct ||
   2603 	                e->kind == MetaEntryKind_PushConstants ||
   2604 	                e->kind == MetaEntryKind_Union;
   2605 
   2606 	MetaEntryScope scope = meta_entry_extract_scope(e, entry_count);
   2607 	if (scope.consumed > 1) {
   2608 		Arena scratch = ctx->scratch;
   2609 
   2610 		// NOTE(rnp): count expands
   2611 		i64 expand_count = 0;
   2612 		for (MetaEntry *row = scope.start; row != scope.one_past_last; row++)
   2613 			if (row->kind == MetaEntryKind_Expand)
   2614 				expand_count++;
   2615 
   2616 		// NOTE(rnp): extract expand tables
   2617 		da_count *table_ids = 0;
   2618 		i64 table_id_index = 0;
   2619 		if (expand_count > 0) {
   2620 			table_ids = push_array(&ctx->scratch, da_count, expand_count);
   2621 			for (MetaEntry *row = scope.start; row != scope.one_past_last; row++)
   2622 				if (row->kind == MetaEntryKind_Expand)
   2623 					table_ids[table_id_index++] = meta_expand_table_entity_id(ctx, row);
   2624 		}
   2625 
   2626 		table_id_index = 0;
   2627 		for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) {
   2628 			if (row->kind != MetaEntryKind_Array &&
   2629 			    row->kind != MetaEntryKind_Expand &&
   2630 			    row->kind != MetaEntryKind_String)
   2631 			{
   2632 				meta_entry_nesting_error(row, e->kind);
   2633 			}
   2634 
   2635 			MetaEntryArgument entries = {.count = 1};
   2636 
   2637 			if (row->kind == MetaEntryKind_Expand) {
   2638 				if (row + 1 == scope.one_past_last || (
   2639 				    row[1].kind != MetaEntryKind_Array &&
   2640 				    row[1].kind != MetaEntryKind_String))
   2641 				{
   2642 					meta_entry_nesting_error(row + 1, row->kind);
   2643 				}
   2644 
   2645 				if (row[1].kind == MetaEntryKind_Array)
   2646 					entries.count = meta_entry_argument_expect(row + 1, 0, MetaEntryArgumentKind_Array).count;
   2647 			}
   2648 
   2649 			if (row->kind == MetaEntryKind_Array)
   2650 				entries.count = meta_entry_argument_expect(row, 0, MetaEntryArgumentKind_Array).count;
   2651 
   2652 			if (structure && entries.count != 2 && entries.count != 3) {
   2653 				meta_compiler_error(row->location, "incorrect field count for @%s entry got: %zu expected: "
   2654 				                    "[name type (elements)]\n", meta_entry_kind_strings[e->kind],
   2655 				                    (size_t)entries.count);
   2656 			} else if (!structure && entries.count != t->field_count) {
   2657 				meta_compiler_error_message(row->location, "incorrect field count for @%s entry got: %zu expected: %u\n",
   2658 				                            meta_entry_kind_strings[e->kind], (size_t)entries.count, t->field_count);
   2659 				fprintf(stderr, "  fields: [");
   2660 				for (u64 i = 0; i < t->field_count; i++) {
   2661 					if (i != 0) fprintf(stderr, " ");
   2662 					fprintf(stderr, "%.*s", (i32)t->fields[i].length, t->fields[i].data);
   2663 				}
   2664 				fprintf(stderr, "]\n");
   2665 				meta_error();
   2666 			}
   2667 
   2668 			if (row->kind == MetaEntryKind_Expand) {
   2669 				t->entry_count += ctx->entities.data[table_ids[table_id_index++]].table.entry_count;
   2670 				// NOTE(rnp): skip expand argument
   2671 				row++;
   2672 			} else {
   2673 				t->entry_count++;
   2674 			}
   2675 		}
   2676 
   2677 		t->entries = push_array(ctx->arena, str8 *, t->field_count);
   2678 		for (u32 field = 0; field < t->field_count; field++)
   2679 			t->entries[field] = push_array(ctx->arena, str8, t->entry_count);
   2680 
   2681 		u32 row_index = 0;
   2682 		table_id_index = 0;
   2683 		for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) {
   2684 			u64 argument_count = row->arguments ? row->arguments->count : 1;
   2685 			if (row->kind == MetaEntryKind_Expand) {
   2686 				row++;
   2687 				argument_count = row->arguments ? row->arguments->count : 1;
   2688 
   2689 				MetaEntity *table = ctx->entities.data + table_ids[table_id_index++];
   2690 
   2691 				u32 working_row_index = row_index;
   2692 				for EachIndex(argument_count, it) {
   2693 					working_row_index = row_index;
   2694 					str8 expand = row->arguments ? row->arguments->strings[it] : row->name;
   2695 					MetaExpansionPartList parts = meta_generate_expansion_set(ctx, &ctx->scratch, expand, table, row->location);
   2696 					for EachIndex(table->table.entry_count, entry_index)
   2697 						t->entries[it][working_row_index++] = meta_expand_parts_to_str8_at_index(ctx, entry_index, parts);
   2698 				}
   2699 
   2700 				for (; row_index < working_row_index; row_index++)
   2701 					if (structure && argument_count == 2)
   2702 						t->entries[2][row_index] = str8("1");
   2703 
   2704 			} else {
   2705 				str8 *fs = &row->name;
   2706 				if (row->arguments)
   2707 					fs = row->arguments->strings;
   2708 
   2709 				for (u32 field = 0; field < t->field_count; field++)
   2710 					t->entries[field][row_index] = fs[field];
   2711 
   2712 				// NOTE(rnp): if we are filling out a struct the array element count is optional
   2713 				// and defaults to 1. fill this out here for uniformity elsewhere in the code
   2714 				if (structure && argument_count == 2)
   2715 					t->entries[2][row_index] = str8("1");
   2716 				row_index++;
   2717 			}
   2718 		}
   2719 
   2720 		ctx->scratch = scratch;
   2721 	}
   2722 
   2723 	MetaEntity *entity = meta_entity(ctx, entity_id);
   2724 	entity->table = table;
   2725 
   2726 	switch (e->kind) {
   2727 	case MetaEntryKind_Bake:
   2728 	case MetaEntryKind_Enumeration:
   2729 	case MetaEntryKind_Flags:
   2730 	case MetaEntryKind_PushConstants:
   2731 	case MetaEntryKind_Struct:
   2732 	case MetaEntryKind_Table:
   2733 	case MetaEntryKind_Union:
   2734 	{}break;
   2735 
   2736 	InvalidDefaultCase;
   2737 	}
   2738 
   2739 	return scope.consumed;
   2740 }
   2741 
   2742 function i64
   2743 meta_pack_shader_common(MetaContext *ctx, MetaEntityID shader_id, MetaEntry *e, i64 entry_count, MetaEntityID group_entity_id)
   2744 {
   2745 	assert(ctx->entities.data[shader_id.value].kind == MetaEntityKind_Shader);
   2746 	i64 result = 0;
   2747 
   2748 	switch(e->kind) {
   2749 
   2750 	case MetaEntryKind_Bake:{
   2751 		e->name = push_str8_from_parts(ctx->arena, str8(""), ctx->entity_names.data[shader_id.value], str8("BakeParameters"));
   2752 		result  = meta_pack_table_entity(ctx, e, entry_count, e->name, shader_id);
   2753 	}break;
   2754 
   2755 	case MetaEntryKind_PushConstants:{
   2756 		e->name = push_str8_from_parts(ctx->arena, str8(""), ctx->entity_names.data[shader_id.value], str8("PushConstants"));
   2757 		result  = meta_pack_table_entity(ctx, e, entry_count, e->name, shader_id);
   2758 		goto reference;
   2759 	}break;
   2760 
   2761 	case MetaEntryKind_ShaderAlias:{
   2762 		MetaEntityID alias_id = meta_intern_entity(ctx, e->name, MetaEntityKind_Shader, group_entity_id,
   2763 		                                           e->location, 0);
   2764 		meta_entity(ctx, alias_id)->shader.kind            = MetaShaderKind_Alias;
   2765 		meta_entity(ctx, alias_id)->shader.alias_parent_id = shader_id;
   2766 	}break;
   2767 
   2768 	case MetaEntryKind_Enumeration:
   2769 	case MetaEntryKind_Flags:
   2770 	case MetaEntryKind_Constant:
   2771 	case MetaEntryKind_Struct:
   2772 	reference:
   2773 	{
   2774 		meta_entry_argument_expected(e);
   2775 		// TODO(rnp): MetaIDList.data should be of type MetaEntityID
   2776 		MetaEntityID  ref_id = meta_entity_reference(ctx, e->name, e->location);
   2777 		meta_intern_id(ctx, &meta_entity(ctx, shader_id)->shader.entity_reference_ids, ref_id.value);
   2778 	}break;
   2779 
   2780 	default:{ meta_entry_nesting_error(e, MetaEntryKind_Shader); }break;
   2781 	}
   2782 
   2783 	return result;
   2784 }
   2785 
   2786 function i64
   2787 meta_pack_render_shader(MetaContext *ctx, MetaEntry *entries, i64 entry_count, MetaEntityID group_entity_id)
   2788 {
   2789 	assert(entries[0].kind == MetaEntryKind_RenderShader);
   2790 
   2791 	MetaEntityID entity_id = meta_intern_entity(ctx, entries->name, MetaEntityKind_Shader,
   2792 	                                            group_entity_id, entries->location, 0);
   2793 	meta_entity(ctx, entity_id)->shader.kind = MetaShaderKind_Render;
   2794 
   2795 	meta_entry_argument_expected(entries);
   2796 
   2797 	MetaEntryScope scope = meta_entry_extract_scope(entries, entry_count);
   2798 	if (scope.consumed > 1) {
   2799 		for (MetaEntry *e = scope.start; e < scope.one_past_last; e++) {
   2800 			switch (e->kind) {
   2801 
   2802 			case MetaEntryKind_VertexShader:{
   2803 				if (meta_entity(ctx, entity_id)->shader.files[0].length)
   2804 					meta_entry_error(e, "primitive shader file redefined\n");
   2805 				meta_entity(ctx, entity_id)->shader.files[0] = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string;
   2806 				meta_entity(ctx, entity_id)->shader.render.kind = MetaShaderPrimitiveKind_Vertex;
   2807 			}break;
   2808 
   2809 			case MetaEntryKind_FragmentShader:{
   2810 				if (meta_entity(ctx, entity_id)->shader.files[1].length)
   2811 					meta_entry_error(e, "fragment shader file redefined\n");
   2812 				meta_entity(ctx, entity_id)->shader.files[1] = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string;
   2813 			}break;
   2814 
   2815 			default:{
   2816 				e += meta_pack_shader_common(ctx, entity_id, e, scope.one_past_last - e, group_entity_id);
   2817 			}break;
   2818 			}
   2819 		}
   2820 	}
   2821 	return scope.consumed;
   2822 }
   2823 
   2824 function i64
   2825 meta_pack_compute_shader(MetaContext *ctx, MetaEntry *entries, i64 entry_count, MetaEntityID group_entity_id)
   2826 {
   2827 	assert(entries[0].kind == MetaEntryKind_Shader);
   2828 
   2829 	MetaEntityID entity_id = meta_intern_entity(ctx, entries->name, MetaEntityKind_Shader, group_entity_id,
   2830 	                                        entries->location, 0);
   2831 	meta_entity(ctx, entity_id)->shader.kind = MetaShaderKind_Compute;
   2832 
   2833 	if (entries->argument_count > 1) {
   2834 		meta_entry_argument_expected(entries, str8("[file_name]"));
   2835 	} else if (entries->argument_count == 1) {
   2836 		str8 shader_file = meta_entry_argument_expect(entries, 0, MetaEntryArgumentKind_String).string;
   2837 		meta_entity(ctx, entity_id)->shader.files[0] = shader_file;
   2838 	}
   2839 
   2840 	MetaEntryScope scope = meta_entry_extract_scope(entries, entry_count);
   2841 	if (scope.consumed > 1) {
   2842 		for (MetaEntry *e = scope.start; e < scope.one_past_last; e++)
   2843 			e += meta_pack_shader_common(ctx, entity_id, e, scope.one_past_last - e, group_entity_id);
   2844 	} else {
   2845 		assert(scope.consumed == 1);
   2846 		// TODO(rnp): some functions (@Expand) expect no scope and that the next entry
   2847 		// is treated as in scope; here we do not want that behaviour.
   2848 		scope.consumed = 0;
   2849 	}
   2850 	return scope.consumed;
   2851 }
   2852 
   2853 function i64
   2854 meta_pack_shader_group(MetaContext *ctx, MetaEntry *entries, i64 entry_count)
   2855 {
   2856 	assert(entries->kind == MetaEntryKind_ShaderGroup);
   2857 
   2858 	MetaEntityID entity_id = meta_intern_entity(ctx, entries->name, MetaEntityKind_ShaderGroup,
   2859 	                                            meta_root_entity_id(ctx), entries->location, 0);
   2860 
   2861 	MetaEntryScope scope = meta_entry_extract_scope(entries, entry_count);
   2862 	if (scope.consumed > 1) {
   2863 		for (MetaEntry *e = scope.start; e < scope.one_past_last; e++) {
   2864 			switch (e->kind) {
   2865 			case MetaEntryKind_RenderShader:{
   2866 				e += meta_pack_render_shader(ctx, e, scope.one_past_last - e, entity_id);
   2867 			}break;
   2868 			case MetaEntryKind_Shader:{
   2869 				e += meta_pack_compute_shader(ctx, e, scope.one_past_last - e, entity_id);
   2870 			}break;
   2871 			default:{meta_entry_nesting_error(e, MetaEntryKind_ShaderGroup);}break;
   2872 			}
   2873 		}
   2874 	}
   2875 	return scope.consumed;
   2876 }
   2877 
   2878 function i64
   2879 meta_pack_references(MetaContext *ctx, MetaEntry *entries, i64 entry_count, MetaEntityID parent,
   2880                      str8 scope_name, str8 prefix)
   2881 {
   2882 	MetaEntryScope scope = meta_entry_extract_scope(entries, entry_count);
   2883 	for (MetaEntry *e = scope.start; e < scope.one_past_last; e++) {
   2884 		switch (e->kind) {
   2885 		case MetaEntryKind_Struct:
   2886 		case MetaEntryKind_Union:
   2887 		{
   2888 			meta_entity_reference_reference(ctx, e->name, scope_name, e->location, parent, prefix);
   2889 		}break;
   2890 		default:{meta_entry_nesting_error(e, entries->kind);}break;
   2891 		}
   2892 	}
   2893 	return scope.consumed;
   2894 }
   2895 
   2896 function str8 *
   2897 meta_expand_to_str8_array(MetaContext *ctx, Arena scratch, str8 expand, MetaEntity *table, MetaLocation location)
   2898 {
   2899 	MetaExpansionPartList parts = meta_generate_expansion_set(ctx, &scratch, expand, table, location);
   2900 	str8 *result = push_array(ctx->arena, str8, table->table.entry_count);
   2901 	for EachIndex(table->table.entry_count, expansion)
   2902 		result[expansion] = meta_expand_parts_to_str8_at_index(ctx, expansion, parts);
   2903 	return result;
   2904 }
   2905 
   2906 function i64
   2907 meta_expand(MetaContext *ctx, Arena scratch, MetaEntry *e, i64 entry_count, MetaEmitOperationList *ops)
   2908 {
   2909 	assert(e->kind == MetaEntryKind_Expand);
   2910 
   2911 	MetaEntity *table = ctx->entities.data + meta_expand_table_entity_id(ctx, e);
   2912 	str8 table_name = ctx->entity_names.data[da_index(table, &ctx->entities)];
   2913 
   2914 	MetaEntryScope scope = meta_entry_extract_scope(e, entry_count);
   2915 	for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) {
   2916 		switch (row->kind) {
   2917 		case MetaEntryKind_String:{
   2918 			if (!ops) goto error;
   2919 
   2920 			MetaExpansionPartList parts = meta_generate_expansion_set(ctx, ctx->arena, row->name, table, row->location);
   2921 
   2922 			MetaEmitOperation *op = da_push(ctx->arena, ops);
   2923 			op->kind     = MetaEmitOperationKind_Expand;
   2924 			op->location = row->location;
   2925 			op->expansion_operation.parts           = parts.data;
   2926 			op->expansion_operation.part_count      = (u32)parts.count;
   2927 			op->expansion_operation.table_entity_id = da_index(table, &ctx->entities);
   2928 		}break;
   2929 
   2930 		case MetaEntryKind_Enumeration:
   2931 		case MetaEntryKind_Flags:
   2932 		{
   2933 			if (ops) meta_entry_nesting_error(row, MetaEntryKind_Emit);
   2934 
   2935 			meta_entry_argument_expected(row, str8("`raw_string`"));
   2936 			str8 expand = meta_entry_argument_expect(row, 0, MetaEntryArgumentKind_String).string;
   2937 
   2938 			MetaEntityKind entity_kind = row->kind == MetaEntryKind_Flags ? MetaEntityKind_Flags : MetaEntityKind_Enumeration;
   2939 			MetaEntityID entity_id = meta_intern_entity(ctx, row->name, entity_kind, meta_root_entity_id(ctx),
   2940 			                                            row->location, 0);
   2941 			MetaEntry entry = {.kind = row->kind};
   2942 			MetaEntity *new = ctx->entities.data + entity_id.value;
   2943 			meta_pack_table_begin(&entry, &new->table);
   2944 			new->table.entries     = push_array(ctx->arena, str8 *, new->table.field_count);
   2945 			new->table.entry_count = table->table.entry_count;
   2946 			new->table.entries[0]  = meta_expand_to_str8_array(ctx, scratch, expand, table, row->location);
   2947 		}break;
   2948 
   2949 		case MetaEntryKind_Struct:
   2950 		case MetaEntryKind_Union:
   2951 		{
   2952 			if (ops) meta_entry_nesting_error(row, MetaEntryKind_Emit);
   2953 			MetaEntryArgument fields = meta_entry_argument_expect(row, 0, MetaEntryArgumentKind_Array);
   2954 			if (fields.count != 2 && fields.count != 3) {
   2955 				meta_compiler_error(row->location, "Invalid arguments in table expansion: '%.*s'\n"
   2956 				                                   "Union expansion requires field names for member names, type names, "
   2957 				                                   "and optionally element counts.\n", (i32)table_name.length, table_name.data);
   2958 			}
   2959 
   2960 			MetaEntityKind entity_kind = row->kind == MetaEntryKind_Struct ? MetaEntityKind_Struct : MetaEntityKind_Union;
   2961 			MetaEntityID   entity_id   = meta_intern_entity(ctx, row->name, entity_kind,
   2962 			                                                meta_root_entity_id(ctx), row->location, 0);
   2963 			MetaEntry entry = {.kind = row->kind};
   2964 			MetaEntity *new = ctx->entities.data + entity_id.value;
   2965 			meta_pack_table_begin(&entry, &new->table);
   2966 			new->table.entries     = push_array(ctx->arena, str8 *, new->table.field_count);
   2967 			new->table.entry_count = table->table.entry_count;
   2968 			new->table.entries[MetaStructField_Name] = meta_expand_to_str8_array(ctx, scratch, fields.strings[0],
   2969 			                                                                     table, row->location);
   2970 			new->table.entries[MetaStructField_Type] = meta_expand_to_str8_array(ctx, scratch, fields.strings[1],
   2971 			                                                                     table, row->location);
   2972 			if (fields.count == 3) {
   2973 				new->table.entries[MetaStructField_Elements] = meta_expand_to_str8_array(ctx, scratch, fields.strings[2],
   2974 				                                                                         table, row->location);
   2975 			} else {
   2976 				new->table.entries[MetaStructField_Elements] = push_array(ctx->arena, str8, table->table.entry_count);
   2977 				for EachIndex(new->table.entry_count, entry)
   2978 					new->table.entries[MetaStructField_Elements][entry] = str8("1");
   2979 			}
   2980 		}break;
   2981 
   2982 		error:
   2983 		default:
   2984 		{
   2985 			meta_entry_nesting_error(row, MetaEntryKind_Expand);
   2986 		}break;
   2987 		}
   2988 	}
   2989 	return scope.consumed;
   2990 }
   2991 
   2992 function void
   2993 meta_embed(MetaContext *ctx, Arena scratch, MetaEntry *e, i64 entry_count)
   2994 {
   2995 	assert(e->kind == MetaEntryKind_Embed);
   2996 
   2997 	meta_entry_argument_expected(e, str8("filename"));
   2998 	str8 filename = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string;
   2999 
   3000 	MetaEmitOperationList *ops = da_push(ctx->arena, ctx->emit_sets + MetaEmitLang_C);
   3001 	if (e->name.length == 0) meta_entry_error(e, "name must be provided for output array");
   3002 
   3003 	MetaEmitOperation *op;
   3004 	op = da_push(ctx->arena, ops);
   3005 	op->kind   = MetaEmitOperationKind_String;
   3006 	op->string = push_str8_from_parts(ctx->arena, str8(""), str8("read_only global u8 "), e->name, str8("[] = {"));
   3007 
   3008 	op = da_push(ctx->arena, ops);
   3009 	op->kind   = MetaEmitOperationKind_FileBytes;
   3010 	op->string = filename;
   3011 
   3012 	op = da_push(ctx->arena, ops);
   3013 	op->kind   = MetaEmitOperationKind_String;
   3014 	op->string = str8("};");
   3015 }
   3016 
   3017 function MetaKind
   3018 meta_map_kind(str8 kind, str8 table_name, MetaLocation location)
   3019 {
   3020 	i64 id = meta_lookup_string_slow(meta_kind_meta_types, MetaKind_Count, kind);
   3021 	if (id < 0) {
   3022 		meta_compiler_error(location, "Invalid Kind in '%.*s' table expansion: %.*s\n",
   3023 		                    (i32)table_name.length, table_name.data, (i32)kind.length, kind.data);
   3024 	}
   3025 	MetaKind result = (MetaKind)id;
   3026 	return result;
   3027 }
   3028 
   3029 function MetaEmitLang
   3030 meta_map_emit_lang(str8 lang, MetaEntry *e)
   3031 {
   3032 	#define X(k, ...) str8_comp(#k),
   3033 	read_only local_persist str8 meta_lang_strings[] = {META_EMIT_LANG_LIST};
   3034 	#undef X
   3035 
   3036 	i64 id = meta_lookup_string_slow(meta_lang_strings, MetaEmitLang_Count, lang);
   3037 	if (id < 0) {
   3038 		#define X(k, ...) #k ", "
   3039 		meta_entry_error(e, "Unknown Emit Language: '%.*s'\nPossible Values: "
   3040 		                 META_EMIT_LANG_LIST "\n", (i32)lang.length, lang.data);
   3041 		#undef X
   3042 	}
   3043 	MetaEmitLang result = (MetaEmitLang)id;
   3044 	return result;
   3045 }
   3046 
   3047 function void
   3048 meta_pack_constant(MetaContext *ctx, MetaEntry *e)
   3049 {
   3050 	assert(e->kind == MetaEntryKind_Constant);
   3051 
   3052 	MetaEntityID entity_id = meta_intern_entity(ctx, e->name, MetaEntityKind_Constant,
   3053 	                                            meta_root_entity_id(ctx), e->location, 0);
   3054 
   3055 	meta_entry_argument_expected(e, str8("value"));
   3056 	str8 value = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string;
   3057 
   3058 	NumberConversion number = number_from_str8(value);
   3059 	if (number.result != NumberConversionResult_Success || number.unparsed.length != 0) {
   3060 		meta_compiler_error(e->location, "Invalid integer in definition of Constant '%.*s': %.*s\n",
   3061 		                    (i32)e->name.length, e->name.data, (i32)value.length, value.data);
   3062 	}
   3063 
   3064 	MetaEntity *entity = meta_entity(ctx, entity_id);
   3065 	if (number.kind == NumberConversionKind_Float) {
   3066 		entity->constant.kind = MetaConstantKind_Float;
   3067 		entity->constant.F64  = number.F64;
   3068 	} else {
   3069 		entity->constant.kind = MetaConstantKind_Integer;
   3070 		entity->constant.U64  = number.U64;
   3071 	}
   3072 }
   3073 
   3074 function i64
   3075 meta_pack_emit(MetaContext *ctx, Arena scratch, MetaEntry *e, i64 entry_count)
   3076 {
   3077 	assert(e->kind == MetaEntryKind_Emit);
   3078 
   3079 	MetaEmitLang lang = MetaEmitLang_C;
   3080 	if (e->argument_count) {
   3081 		meta_entry_argument_expected(e, str8("emit_language"));
   3082 		str8 name = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string;
   3083 		lang = meta_map_emit_lang(name, e);
   3084 	}
   3085 
   3086 	MetaEmitOperationList *ops = da_push(ctx->arena, ctx->emit_sets + lang);
   3087 	/* TODO(rnp): probably we should check this is unique */
   3088 	ops->filename = e->name;
   3089 
   3090 	MetaEntryScope scope = meta_entry_extract_scope(e, entry_count);
   3091 	for (MetaEntry *row = scope.start; row != scope.one_past_last; row++) {
   3092 		switch (row->kind) {
   3093 		case MetaEntryKind_String:{
   3094 			MetaEmitOperation *op = da_push(ctx->arena, ops);
   3095 			op->kind     = MetaEmitOperationKind_String;
   3096 			op->string   = row->name;
   3097 			op->location = row->location;
   3098 		}break;
   3099 		case MetaEntryKind_Expand:{
   3100 			row += meta_expand(ctx, scratch, row, entry_count - (row - e), ops);
   3101 		}break;
   3102 		default:{ meta_entry_nesting_error(row, MetaEntryKind_Emit); }break;
   3103 		}
   3104 	}
   3105 	return scope.consumed;
   3106 }
   3107 
   3108 function CommandList
   3109 meta_extract_emit_file_dependencies(MetaContext *ctx, Arena *arena)
   3110 {
   3111 	CommandList result = {0};
   3112 	for (i64 set = 0; set < ctx->emit_sets[MetaEmitLang_C].count; set++) {
   3113 		MetaEmitOperationList *ops = ctx->emit_sets[MetaEmitLang_C].data + set;
   3114 		for (i64 opcode = 0; opcode < ops->count; opcode++) {
   3115 			MetaEmitOperation *op = ops->data + opcode;
   3116 			switch (op->kind) {
   3117 			case MetaEmitOperationKind_FileBytes:{
   3118 				str8 filename = push_str8_from_parts(arena, str8(OS_PATH_SEPARATOR), ctx->directory, op->string);
   3119 				*da_push(arena, &result) = (c8 *)filename.data;
   3120 			}break;
   3121 			default:{}break;
   3122 			}
   3123 		}
   3124 	}
   3125 	return result;
   3126 }
   3127 
   3128 function void
   3129 metagen_push_byte_array(MetaprogramContext *m, str8 bytes)
   3130 {
   3131 	for (i64 i = 0; i < bytes.length; i++) {
   3132 		b32 end_line = (i != 0) && (i % 16) == 0;
   3133 		if (i != 0) meta_push(m, end_line ? str8(",") : str8(", "));
   3134 		if (end_line) meta_end_line(m);
   3135 		if ((i % 16) == 0) meta_indent(m);
   3136 		meta_push(m, str8("0x"));
   3137 		meta_push_u64_hex(m, bytes.data[i]);
   3138 	}
   3139 	meta_end_line(m);
   3140 }
   3141 
   3142 function void
   3143 metagen_push_table(MetaprogramContext *m, Arena scratch, str8 row_start, str8 row_end,
   3144                    str8 **column_strings, u64 rows, u64 columns)
   3145 {
   3146 	u32 *column_widths = 0;
   3147 	if (columns > 1) {
   3148 		column_widths = push_array(&scratch, u32, columns - 1);
   3149 		for (u64 column = 0; column < columns - 1; column++) {
   3150 			str8 *strings = column_strings[column];
   3151 			for (u64 row = 0; row < rows; row++)
   3152 				column_widths[column] = Max(column_widths[column], (u32)strings[row].length);
   3153 		}
   3154 	}
   3155 
   3156 	for (u64 row = 0; row < rows; row++) {
   3157 		meta_begin_line(m, row_start);
   3158 		for (u64 column = 0; column < columns; column++) {
   3159 			str8 text = column_strings[column][row];
   3160 			meta_push(m, text);
   3161 			i32 pad = columns > 1 ? 1 : 0;
   3162 			if (column_widths && column < columns - 1)
   3163 				pad += (i32)column_widths[column] - (i32)text.length;
   3164 			if (column < columns - 1) meta_pad(m, ' ', pad);
   3165 		}
   3166 		meta_end_line(m, row_end);
   3167 	}
   3168 }
   3169 
   3170 function i64
   3171 meta_expansion_part_conditional_argument(MetaExpansionConditionalArgument a, u32 entry,
   3172                                          str8 table_name, MetaLocation loc)
   3173 {
   3174 	i64 result = 0;
   3175 	switch (a.kind) {
   3176 	case MetaExpansionConditionalArgumentKind_Number:{
   3177 		result = a.number;
   3178 	}break;
   3179 
   3180 	case MetaExpansionConditionalArgumentKind_Evaluation:
   3181 	{
   3182 		str8 string   = a.strings[entry];
   3183 		MetaKind kind = meta_map_kind(string, table_name, loc);
   3184 		result        = meta_kind_elements[kind];
   3185 	}break;
   3186 
   3187 	case MetaExpansionConditionalArgumentKind_Reference:{
   3188 		str8 string = a.strings[entry];
   3189 		NumberConversion integer = integer_from_str8(string);
   3190 		if (integer.result != NumberConversionResult_Success) {
   3191 			meta_compiler_error(loc, "Invalid integer in '%.*s' table expansion: %.*s\n",
   3192 			                    (i32)table_name.length, table_name.data, (i32)string.length, string.data);
   3193 		}
   3194 		result = integer.S64;
   3195 	}break;
   3196 
   3197 	InvalidDefaultCase;
   3198 	}
   3199 
   3200 	return result;
   3201 }
   3202 
   3203 function b32
   3204 meta_expansion_part_conditional(MetaExpansionPart *p, u32 entry, str8 table_name, MetaLocation loc)
   3205 {
   3206 	assert(p->kind == MetaExpansionPartKind_Conditional);
   3207 	b32 result = 0;
   3208 	i64 lhs = meta_expansion_part_conditional_argument(p->conditional.lhs, entry, table_name, loc);
   3209 	i64 rhs = meta_expansion_part_conditional_argument(p->conditional.rhs, entry, table_name, loc);
   3210 	switch (p->conditional.op) {
   3211 	case MetaExpansionOperation_LessThan:{    result = lhs < rhs; }break;
   3212 	case MetaExpansionOperation_GreaterThan:{ result = lhs > rhs; }break;
   3213 	InvalidDefaultCase;
   3214 	}
   3215 	return result;
   3216 }
   3217 
   3218 function void
   3219 metagen_run_emit(MetaprogramContext *m, MetaContext *ctx, MetaEmitOperationList *ops, str8 *evaluation_table)
   3220 {
   3221 	for (i64 opcode = 0; opcode < ops->count; opcode++) {
   3222 		MetaEmitOperation *op = ops->data + opcode;
   3223 		switch (op->kind) {
   3224 		case MetaEmitOperationKind_String:{ meta_push_line(m, op->string); }break;
   3225 		case MetaEmitOperationKind_FileBytes:{
   3226 			Arena scratch = m->scratch;
   3227 			str8 filename = push_str8_from_parts(&scratch, str8(OS_PATH_SEPARATOR), ctx->directory, op->string);
   3228 			str8 file     = read_entire_file((c8 *)filename.data, &scratch);
   3229 			m->indentation_level++;
   3230 			metagen_push_byte_array(m, file);
   3231 			m->indentation_level--;
   3232 		}break;
   3233 		case MetaEmitOperationKind_Expand:{
   3234 			Arena scratch = m->scratch;
   3235 
   3236 			MetaEmitOperationExpansion *eop = &op->expansion_operation;
   3237 			MetaTable *t = &ctx->entities.data[eop->table_entity_id].table;
   3238 			str8 table_name = ctx->entity_names.data[eop->table_entity_id];
   3239 
   3240 			u32 alignment_count  = 1;
   3241 			u32 evaluation_count = 0;
   3242 			for (u32 part = 0; part < eop->part_count; part++) {
   3243 				if (eop->parts[part].kind == MetaExpansionPartKind_Alignment)
   3244 					alignment_count++;
   3245 				if (eop->parts[part].kind == MetaExpansionPartKind_EvalKind ||
   3246 				    eop->parts[part].kind == MetaExpansionPartKind_EvalKindCount)
   3247 					evaluation_count++;
   3248 			}
   3249 
   3250 			MetaKind **evaluation_columns = push_array(&scratch, MetaKind *, evaluation_count);
   3251 			for (u32 column = 0; column < evaluation_count; column++)
   3252 				evaluation_columns[column] = push_array(&scratch, MetaKind, t->entry_count);
   3253 
   3254 			for (u32 part = 0; part < eop->part_count; part++) {
   3255 				u32 eval_column = 0;
   3256 				MetaExpansionPart *p = eop->parts + part;
   3257 				if (p->kind == MetaExpansionPartKind_EvalKind) {
   3258 					for (u32 entry = 0; entry < t->entry_count; entry++) {
   3259 						evaluation_columns[eval_column][entry] = meta_map_kind(p->strings[entry],
   3260 						                                                       table_name, op->location);
   3261 					}
   3262 					eval_column++;
   3263 				}
   3264 			}
   3265 
   3266 			str8 **columns = push_array(&scratch, str8 *, alignment_count);
   3267 			for (u32 column = 0; column < alignment_count; column++)
   3268 				columns[column] = push_array(&scratch, str8, t->entry_count);
   3269 
   3270 			Stream sb = arena_stream(scratch);
   3271 			for (u32 entry = 0; entry < t->entry_count; entry++) {
   3272 				u32 column      = 0;
   3273 				u32 eval_column = 0;
   3274 				for (u32 part = 0; part < eop->part_count; part++) {
   3275 					MetaExpansionPart *p = eop->parts + part;
   3276 					switch (p->kind) {
   3277 					case MetaExpansionPartKind_Alignment:{
   3278 						columns[column][entry] = arena_stream_commit_and_reset(&scratch, &sb);
   3279 						column++;
   3280 					}break;
   3281 
   3282 					case MetaExpansionPartKind_Conditional:{
   3283 						if (!meta_expansion_part_conditional(p, entry, table_name, op->location))
   3284 							part += p->conditional.instruction_skip;
   3285 					}break;
   3286 
   3287 					case MetaExpansionPartKind_EvalKind:{
   3288 						str8 kind = evaluation_table[evaluation_columns[eval_column][entry]];
   3289 						stream_append_str8(&sb, kind);
   3290 					}break;
   3291 
   3292 					case MetaExpansionPartKind_EvalKindCount:{
   3293 						stream_append_u64(&sb, meta_kind_elements[evaluation_columns[eval_column][entry]]);
   3294 					}break;
   3295 
   3296 					case MetaExpansionPartKind_Reference:
   3297 					case MetaExpansionPartKind_String:
   3298 					{
   3299 						str8 string = p->kind == MetaExpansionPartKind_Reference ? p->strings[entry] : p->string;
   3300 						stream_append_str8(&sb, string);
   3301 					}break;
   3302 					}
   3303 				}
   3304 
   3305 				columns[column][entry] = arena_stream_commit_and_reset(&scratch, &sb);
   3306 			}
   3307 			metagen_push_table(m, scratch, str8(""), str8(""), columns, t->entry_count, alignment_count);
   3308 		}break;
   3309 		InvalidDefaultCase;
   3310 		}
   3311 	}
   3312 	meta_end_line(m);
   3313 }
   3314 
   3315 function void
   3316 metagen_run_emit_set(MetaprogramContext *m, MetaContext *ctx, MetaEmitOperationListSet *emit_set,
   3317                      str8 *evaluation_table)
   3318 {
   3319 	for (i64 set = 0; set < emit_set->count; set++) {
   3320 		MetaEmitOperationList *ops = emit_set->data + set;
   3321 		metagen_run_emit(m, ctx, ops, evaluation_table);
   3322 	}
   3323 }
   3324 
   3325 function i32
   3326 meta_struct_member_elements(MetaContext *ctx, MetaStruct *s, u32 member)
   3327 {
   3328 	assert(member < s->member_count);
   3329 	i32 result = s->elements[member];
   3330 	if (s->member_flags[member] & MetaStructMemberFlag_ReferenceElements)
   3331 		result = (i32)meta_entity(ctx, (MetaEntityID){result})->constant.U64;
   3332 	if (s->member_flags[member] & MetaStructMemberFlag_EnumerationCount)
   3333 		result = (i32)meta_entity(ctx, (MetaEntityID){result})->table.entry_count;
   3334 	return result;
   3335 }
   3336 
   3337 function void
   3338 metagen_push_counted_enum_body(MetaprogramContext *m, str8 kind, str8 prefix, str8 mid, str8 suffix,
   3339                                str8 *ids, i64 ids_count)
   3340 {
   3341 	i64 max_id_length = 0;
   3342 	for (i64 id = 0; id < ids_count; id++)
   3343 		max_id_length = Max(max_id_length, ids[id].length);
   3344 
   3345 	for (i64 id = 0; id < ids_count; id++) {
   3346 		meta_begin_line(m, prefix, kind, ids[id]);
   3347 		meta_pad(m, ' ', 1 + (i32)(max_id_length - ids[id].length));
   3348 		meta_push(m, mid);
   3349 		meta_push_u64(m, (u64)id);
   3350 		meta_end_line(m, suffix);
   3351 	}
   3352 }
   3353 
   3354 function void
   3355 metagen_push_counted_enum_body_from_ids(MetaprogramContext *m, str8 kind, str8 prefix, str8 mid, str8 suffix,
   3356                                         da_count *ids, str8 *id_names, da_count ids_count)
   3357 {
   3358 	i64 max_id_length = 0;
   3359 	for (i64 id = 0; id < ids_count; id++)
   3360 		max_id_length = Max(max_id_length, id_names[ids[id]].length);
   3361 
   3362 	for (i64 id = 0; id < ids_count; id++) {
   3363 		meta_begin_line(m, prefix, kind, id_names[ids[id]]);
   3364 		meta_pad(m, ' ', 1 + (i32)(max_id_length - id_names[ids[id]].length));
   3365 		meta_push(m, mid);
   3366 		meta_push_i64(m, id);
   3367 		meta_end_line(m, suffix);
   3368 	}
   3369 }
   3370 
   3371 function void
   3372 metagen_push_c_enum(MetaprogramContext *m, Arena scratch, str8 kind, b32 flags, str8 *ids, i64 ids_count)
   3373 {
   3374 	str8 kind_full = push_str8_from_parts(&scratch, str8(""), kind, str8("_"));
   3375 	meta_begin_scope(m, str8("typedef enum {"));
   3376 	metagen_push_counted_enum_body(m, kind_full, str8(""), flags ? str8("= 1 << ") : str8("= "), str8(","), ids, ids_count);
   3377 	if (!flags) meta_push_line(m, kind_full, str8("Count,"));
   3378 	meta_end_scope(m, str8("} "), kind, str8(";\n"));
   3379 }
   3380 
   3381 function u32
   3382 meta_struct_flattened_member_count(Arena scratch, MetaContext *ctx, MetaStruct *meta_struct)
   3383 {
   3384 	struct stack_item {MetaStruct *s; u32 member_offset;} init[16];
   3385 	struct {
   3386 		struct stack_item *data;
   3387 		da_count count;
   3388 		da_count capacity;
   3389 	} stack = {init, 0, countof(init)};
   3390 
   3391 	u32 result = 0;
   3392 	*da_push(&scratch, &stack) = (struct stack_item){meta_struct, 0};
   3393 	while (stack.count > 0) {
   3394 		stack.count--;
   3395 		MetaStruct *s = stack.data[stack.count].s;
   3396 		u32 member    = stack.data[stack.count].member_offset;
   3397 		while (member < s->member_count) {
   3398 			if (s->members[member].length == 0) {
   3399 				assert(s->member_flags[member] & MetaStructMemberFlag_ReferenceType);
   3400 				MetaStruct *ss = ctx->struct_infos + ctx->entities.data[s->type_ids[member]].table.struct_info_id;
   3401 				if (ss->flags & MetaStructFlag_Union) {
   3402 					member++;
   3403 					result++;
   3404 				} else {
   3405 					*da_push(&scratch, &stack) = (struct stack_item){s,  member + 1};
   3406 					*da_push(&scratch, &stack) = (struct stack_item){ss, 0};
   3407 					break;
   3408 				}
   3409 			} else {
   3410 				member++;
   3411 				result++;
   3412 			}
   3413 		}
   3414 	}
   3415 	return result;
   3416 }
   3417 
   3418 typedef enum {
   3419 	MetaPushStructStyle_C,
   3420 	MetaPushStructStyle_MATLAB,
   3421 	MetaPushStructStyle_Count,
   3422 } MetaPushStructStyle;
   3423 
   3424 typedef struct {
   3425 	MetaPushStructStyle layout_style;
   3426 	MetaPushStructStyle union_style;
   3427 	MetaPushStructStyle element_count_style;
   3428 	str8 *base_types;
   3429 	u8   *base_type_element_count_scales;
   3430 	str8  prefix;
   3431 	str8  suffix;
   3432 	str8  str_element_prefix;
   3433 } MetaPushStructParameters;
   3434 
   3435 function void
   3436 meta_push_struct_body(MetaContext *ctx, MetaprogramContext *m, MetaEntity *struct_entity,
   3437                       MetaPushStructParameters p)
   3438 {
   3439 	MetaStruct *meta_struct = ctx->struct_infos + struct_entity->table.struct_info_id;
   3440 	struct stack_item {MetaEntity *se; u32 member_offset;} init[16];
   3441 	struct {
   3442 		struct stack_item *data;
   3443 		da_count count;
   3444 		da_count capacity;
   3445 	} stack = {init, 0, countof(init)};
   3446 
   3447 	u32 flattened_member_count = meta_struct_flattened_member_count(m->scratch, ctx, meta_struct);
   3448 
   3449 	str8 *columns[2];
   3450 	columns[0] = push_array(&m->scratch, str8, flattened_member_count);
   3451 	columns[1] = push_array(&m->scratch, str8, flattened_member_count);
   3452 
   3453 	u32 row = 0, scope = 0;
   3454 	*da_push(&m->scratch, &stack) = (struct stack_item){struct_entity, 0};
   3455 	while (stack.count > 0) {
   3456 		stack.count--;
   3457 		MetaEntity *se = stack.data[stack.count].se;
   3458 		MetaStruct *s  = ctx->struct_infos + se->table.struct_info_id;
   3459 		u32 member     = stack.data[stack.count].member_offset;
   3460 
   3461 		while (member < s->member_count) {
   3462 			b32  type_reference = (s->member_flags[member] & MetaStructMemberFlag_ReferenceType) != 0;
   3463 			i32  type_id        = s->type_ids[member];
   3464 			str8 member_name    = s->members[member];
   3465 
   3466 			assert(member_name.length != 0 || type_reference);
   3467 
   3468 			if (s->members[member].length == 0 &&
   3469 			    (p.union_style != MetaPushStructStyle_MATLAB || ctx->entities.data[type_id].kind != MetaEntityKind_Union))
   3470 			{
   3471 				*da_push(&m->scratch, &stack) = (struct stack_item){se, member + 1};
   3472 				*da_push(&m->scratch, &stack) = (struct stack_item){ctx->entities.data + type_id, 0};
   3473 
   3474 				MetaStruct *ss = ctx->struct_infos + ctx->entities.data[type_id].table.struct_info_id;
   3475 				if (p.layout_style == MetaPushStructStyle_C && ss->flags & MetaStructFlag_Union) {
   3476 					metagen_push_table(m, m->scratch, p.prefix, p.suffix, columns, row, 2);
   3477 					meta_begin_scope(m, str8("union {"));
   3478 					row = 0;
   3479 					scope++;
   3480 				}
   3481 
   3482 				break;
   3483 			} else {
   3484 				Stream sb = arena_stream(m->scratch);
   3485 
   3486 				b32 enum_count         = (s->member_flags[member] & MetaStructMemberFlag_EnumerationCount) != 0;
   3487 				b32 elements_reference = enum_count || (s->member_flags[member] & MetaStructMemberFlag_ReferenceElements) != 0;
   3488 				// NOTE(rnp): member name column
   3489 				{
   3490 					read_only local_persist str8 elements_count_open[MetaPushStructStyle_Count] = {
   3491 						[MetaPushStructStyle_C]      = str8_comp("["),
   3492 						[MetaPushStructStyle_MATLAB] = str8_comp("("),
   3493 					};
   3494 					read_only local_persist str8 elements_count_close[MetaPushStructStyle_Count] = {
   3495 						[MetaPushStructStyle_C]      = str8_comp("]"),
   3496 						[MetaPushStructStyle_MATLAB] = str8_comp(")"),
   3497 					};
   3498 					read_only local_persist i32 name_column[MetaPushStructStyle_Count] = {
   3499 						[MetaPushStructStyle_C]      = 1,
   3500 						[MetaPushStructStyle_MATLAB] = 0,
   3501 					};
   3502 
   3503 					u32 resolved_element_count = meta_struct_member_elements(ctx, s, member);
   3504 
   3505 					if (type_reference && p.union_style == MetaPushStructStyle_MATLAB) {
   3506 						MetaEntity *re = ctx->entities.data + type_id;
   3507 						MetaStruct *rs = ctx->struct_infos + re->table.struct_info_id;
   3508 						if (member_name.length == 0) {
   3509 							assert(rs->flags & MetaStructFlag_Union);
   3510 							member_name = str8("data");
   3511 						}
   3512 						if (rs->flags & MetaStructFlag_Union)
   3513 							resolved_element_count *= rs->byte_size;
   3514 					} else if (!type_reference && p.base_type_element_count_scales) {
   3515 						resolved_element_count *= p.base_type_element_count_scales[type_id];
   3516 					}
   3517 
   3518 					if (resolved_element_count > 1 || p.element_count_style == MetaPushStructStyle_MATLAB) {
   3519 						stream_append_str8s(&sb, member_name, elements_count_open[p.layout_style]);
   3520 						if (elements_reference && p.element_count_style != MetaPushStructStyle_MATLAB) {
   3521 							stream_append_str8s(&sb, p.str_element_prefix, ctx->entity_names.data[s->elements[member]]);
   3522 							if (enum_count) stream_append_str8(&sb, str8("_Count"));
   3523 						} else {
   3524 							if (p.element_count_style == MetaPushStructStyle_MATLAB)
   3525 								stream_append_str8(&sb, str8("1, "));
   3526 							stream_append_u64(&sb, resolved_element_count);
   3527 						}
   3528 						stream_append_str8(&sb, elements_count_close[p.layout_style]);
   3529 						columns[name_column[p.layout_style]][row] = arena_stream_commit_and_reset(&m->scratch, &sb);
   3530 					} else {
   3531 						columns[name_column[p.layout_style]][row] = member_name;
   3532 					}
   3533 				}
   3534 
   3535 				// NOTE(rnp): type column
   3536 				{
   3537 					read_only local_persist i32 type_column[MetaPushStructStyle_Count] = {
   3538 						[MetaPushStructStyle_C]      = 0,
   3539 						[MetaPushStructStyle_MATLAB] = 1,
   3540 					};
   3541 
   3542 					if (type_reference) {
   3543 						MetaEntity *re = ctx->entities.data + type_id;
   3544 						MetaStruct *rs = 0;
   3545 
   3546 						if (meta_entity_kind_is_struct[re->kind])
   3547 							rs = ctx->struct_infos + re->table.struct_info_id;
   3548 
   3549 						if (rs && rs->flags & MetaStructFlag_Union && p.union_style == MetaPushStructStyle_MATLAB) {
   3550 							stream_append_str8(&sb, p.base_types[MetaKind_U8]);
   3551 							if (p.layout_style == MetaPushStructStyle_MATLAB)
   3552 								stream_append_str8(&sb, str8("  % +"));
   3553 						} else if (re->kind == MetaEntityKind_Enumeration && p.layout_style == MetaPushStructStyle_MATLAB) {
   3554 							// NOTE(rnp): matlab enumerations are int32 if we make this uint32
   3555 							// MATLAB won't fuck up the type when the field is assigned
   3556 							stream_append_str8(&sb, p.base_types[MetaKind_U32]);
   3557 							if (p.layout_style == MetaPushStructStyle_MATLAB)
   3558 								stream_append_str8(&sb, str8(" % "));
   3559 						} else {
   3560 							if (p.layout_style == MetaPushStructStyle_MATLAB) {
   3561 								// NOTE(rnp): matlab has really broken requirements around sub structures
   3562 								// we can only use an opaque struct here
   3563 								stream_append_str8(&sb, str8("struct % "));
   3564 							} else {
   3565 								str8 name = rs ? rs->name : ctx->entity_names.data[type_id];
   3566 								stream_append_str8s(&sb, p.str_element_prefix, name);
   3567 							}
   3568 						}
   3569 
   3570 						if (p.layout_style == MetaPushStructStyle_MATLAB) {
   3571 							stream_append_str8s(&sb, p.str_element_prefix,
   3572 							                    rs ? rs->name : ctx->entity_names.data[type_id]);
   3573 						}
   3574 
   3575 						columns[type_column[p.layout_style]][row] = arena_stream_commit_and_reset(&m->scratch, &sb);
   3576 					} else {
   3577 						columns[type_column[p.layout_style]][row] = p.base_types[type_id];
   3578 					}
   3579 				}
   3580 
   3581 				row++;
   3582 				member++;
   3583 			}
   3584 		}
   3585 
   3586 		if (member == s->member_count && s->flags & MetaStructFlag_Union && p.layout_style == MetaPushStructStyle_C) {
   3587 			metagen_push_table(m, m->scratch, p.prefix, p.suffix, columns, row, 2);
   3588 			while (scope > 0) {
   3589 				meta_end_scope(m, str8("};"));
   3590 				scope--;
   3591 			}
   3592 			row = 0;
   3593 		}
   3594 	}
   3595 	metagen_push_table(m, m->scratch, p.prefix, p.suffix, columns, row, 2);
   3596 }
   3597 
   3598 function void
   3599 meta_push_matlab_properties(MetaprogramContext *m, MetaContext *ctx, MetaStruct *meta_struct)
   3600 {
   3601 
   3602 	DeferLoop(meta_begin_scope(m, str8("properties")), meta_end_scope(m, str8("end")))
   3603 	{
   3604 		meta_push_struct_body(ctx, m, meta_entity(ctx, meta_struct->entity), (MetaPushStructParameters){
   3605 			.layout_style        = MetaPushStructStyle_MATLAB,
   3606 			.union_style         = MetaPushStructStyle_MATLAB,
   3607 			.element_count_style = MetaPushStructStyle_MATLAB,
   3608 			.base_types          = meta_kind_matlab_types,
   3609 			.suffix              = str8(""),
   3610 			.str_element_prefix  = str8(MATLAB_NAMESPACE META_NAMESPACE_UPPER),
   3611 			.base_type_element_count_scales = meta_kind_elements,
   3612 		});
   3613 	}
   3614 }
   3615 
   3616 
   3617 function void
   3618 meta_push_shader_reload_info(MetaprogramContext *m, MetaContext *ctx)
   3619 {
   3620 	if (!ctx->base_shader_count)
   3621 		return;
   3622 
   3623 	///////////////////////////////
   3624 	// NOTE(rnp): reloadable infos
   3625 	meta_begin_scope(m, str8("read_only global " META_NAMESPACE_UPPER "ShaderKind " META_NAMESPACE_LOWER "_reloadable_shader_kinds[] = {"));
   3626 	{
   3627 		for (da_count shader = 0; shader < ctx->base_shader_count; shader++) {
   3628 			da_count id = ctx->base_shader_ids[shader];
   3629 			meta_push_line(m, str8(META_NAMESPACE_UPPER "ShaderKind_"), ctx->entity_names.data[id], str8(","));
   3630 		}
   3631 	} meta_end_scope(m, str8("};\n"));
   3632 
   3633 	meta_begin_scope(m, str8("read_only global str8 *" META_NAMESPACE_LOWER "_reloadable_shader_files[] = {"));
   3634 	{
   3635 		for (da_count shader = 0; shader < ctx->base_shader_count; shader++) {
   3636 			da_count    id = ctx->base_shader_ids[shader];
   3637 			MetaShader *s  = &ctx->entities.data[id].shader;
   3638 			meta_begin_line(m, str8("(str8 []){str8_comp(\""), s->files[0], str8("\")"));
   3639 			if (s->files[1].length)
   3640 				meta_push(m, str8(", str8_comp(\""), s->files[1], str8("\")"));
   3641 			meta_end_line(m, str8("},"));
   3642 		}
   3643 	} meta_end_scope(m, str8("};\n"));
   3644 
   3645 	meta_begin_scope(m, str8("read_only global i32 " META_NAMESPACE_LOWER "_shader_reloadable_index_by_shader[] = {"));
   3646 	{
   3647 		for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) {
   3648 			meta_indent(m);
   3649 			meta_push_i64(m, ctx->base_shader_id_map[shader]);
   3650 			meta_end_line(m, str8(","));
   3651 		}
   3652 	} meta_end_scope(m, str8("};\n"));
   3653 
   3654 	{
   3655 		u32 info_index = 0;
   3656 		for (da_count group = 0; group < ctx->entity_kind_counts[MetaEntityKind_ShaderGroup]; group++) {
   3657 			da_count id   = ctx->entity_kind_ids[MetaEntityKind_ShaderGroup][group];
   3658 			str8     name = ctx->entity_names.data[id];
   3659 			meta_begin_line(m, str8("read_only global i32 " META_NAMESPACE_LOWER "_reloadable"));
   3660 			for (i64 i = 0; i < name.length; i++) {
   3661 				if IsUpper(name.data[i])
   3662 					stream_append_byte(&m->stream, '_');
   3663 				stream_append_byte(&m->stream, ToLower(name.data[i]));
   3664 			}
   3665 
   3666 			meta_begin_scope(m, str8("_shader_info_indices[] = {")); {
   3667 				MetaEntityID child = ctx->entities.data[id].first_child;
   3668 				do {
   3669 					/* TODO(rnp): store base shader list in a better format */
   3670 					for (da_count bs = 0; bs < ctx->base_shader_count; bs++) {
   3671 						if (ctx->base_shader_ids[bs] == child.value) {
   3672 							meta_indent(m);
   3673 							meta_push_u64(m, info_index++);
   3674 							meta_end_line(m, str8(","));
   3675 							break;
   3676 						}
   3677 					}
   3678 					child = ctx->entities.data[child.value].next_sibling;
   3679 				} while (child.value != ctx->entities.data[id].first_child.value);
   3680 			} meta_end_scope(m, str8("};\n"));
   3681 		}
   3682 	}
   3683 
   3684 	////////////////////////////////////
   3685 	// NOTE(rnp): shader header strings
   3686 	meta_begin_scope(m, str8("read_only global str8 " META_NAMESPACE_LOWER "_shader_global_header_strings[] = {"));
   3687 	{
   3688 		for (da_count ref = 0; ref < ctx->shader_entity_references.count; ref++) {
   3689 			da_count    entity_id   = ctx->shader_entity_references.data[ref];
   3690 			str8        entity_name = ctx->entity_names.data[entity_id];
   3691 			MetaEntity *e           = ctx->entities.data + entity_id;
   3692 
   3693 			switch (e->kind) {
   3694 
   3695 			case MetaEntityKind_Constant:{
   3696 				meta_begin_line(m, str8("str8_comp(\"#define "), entity_name, str8(" ("));
   3697 				switch(e->constant.kind) {
   3698 				case MetaConstantKind_Integer:{ meta_push_u64(m, e->constant.U64); }break;
   3699 				case MetaConstantKind_Float:{   meta_push_f64(m, e->constant.F64); }break;
   3700 				InvalidDefaultCase;
   3701 				}
   3702 				meta_end_line(m, str8(")\\n\\n\"),"));
   3703 			}break;
   3704 
   3705 			case MetaEntityKind_Struct:{
   3706 				meta_push_line(m, str8("str8_comp(\"\""));
   3707 				meta_push_line(m, str8("\"struct "), entity_name, str8(" {\\n\""));
   3708 				meta_push_struct_body(ctx, m, e, (MetaPushStructParameters){
   3709 					.layout_style        = MetaPushStructStyle_C,
   3710 					.union_style         = MetaPushStructStyle_C,
   3711 					.element_count_style = MetaPushStructStyle_C,
   3712 					.base_types          = meta_kind_glsl_types,
   3713 					.prefix              = str8("\"  "),
   3714 					.suffix              = str8(";\\n\""),
   3715 				});
   3716 				meta_push_line(m, str8("\"};\\n\""));
   3717 				meta_push_line(m, str8("\"\\n\"),"));
   3718 			}break;
   3719 
   3720 			case MetaEntityKind_PushConstants:{
   3721 				meta_push_line(m, str8("str8_comp(\"\""));
   3722 				meta_push_line(m, str8("\"layout(push_constant, std430) uniform PushConstants {\\n\""));
   3723 				meta_push_struct_body(ctx, m, e, (MetaPushStructParameters){
   3724 					.layout_style        = MetaPushStructStyle_C,
   3725 					.union_style         = MetaPushStructStyle_C,
   3726 					.element_count_style = MetaPushStructStyle_C,
   3727 					.base_types          = meta_kind_glsl_types,
   3728 					.prefix              = str8("\"  "),
   3729 					.suffix              = str8(";\\n\""),
   3730 				});
   3731 				meta_push_line(m, str8("\"};\\n\""));
   3732 				meta_push_line(m, str8("\"\\n\"),"));
   3733 			}break;
   3734 
   3735 			case MetaEntityKind_Enumeration:{
   3736 				str8 kind_name = push_str8_from_parts(&m->scratch, str8(""), entity_name, str8("_"));
   3737 				meta_push_line(m, str8("str8_comp(\"\""));
   3738 				metagen_push_counted_enum_body(m, kind_name, str8("\"#define "), str8(""), str8("\\n\""),
   3739 				                               e->table.entries[0], e->table.entry_count);
   3740 				meta_push_line(m, str8("\"\\n\"),"));
   3741 			}break;
   3742 
   3743 			InvalidDefaultCase;
   3744 			}
   3745 
   3746 			m->scratch = ctx->scratch;
   3747 		}
   3748 	} meta_end_scope(m, str8("};\n"));
   3749 
   3750 	meta_begin_scope(m, str8("read_only global b8 " META_NAMESPACE_LOWER "_shader_has_primitive[] = {"));
   3751 	for (da_count bs = 0; bs < ctx->base_shader_count; bs++) {
   3752 		MetaShader *s = &ctx->entities.data[ctx->base_shader_ids[bs]].shader;
   3753 		meta_push_line(m, s->kind == MetaShaderKind_Render ? str8("1,") : str8("0,"));
   3754 	}
   3755 	meta_end_scope(m, str8("};\n"));
   3756 
   3757 	meta_begin_scope(m, str8("read_only global b8 " META_NAMESPACE_LOWER "_shader_primitive_is_vertex[] = {"));
   3758 	for (da_count bs = 0; bs < ctx->base_shader_count; bs++) {
   3759 		MetaShader *s = &ctx->entities.data[ctx->base_shader_ids[bs]].shader;
   3760 		b8 vertex = s->kind == MetaShaderKind_Render && s->render.kind == MetaShaderPrimitiveKind_Vertex;
   3761 		meta_push_line(m, vertex ? str8("1,") : str8("0,"));
   3762 	}
   3763 	meta_end_scope(m, str8("};\n"));
   3764 }
   3765 
   3766 function void
   3767 meta_push_shader_bake(MetaprogramContext *m, MetaContext *ctx)
   3768 {
   3769 	for (da_count bs = 0; bs < ctx->base_shader_count; bs++) {
   3770 		MetaShader *s = &ctx->entities.data[ctx->base_shader_ids[bs]].shader;
   3771 
   3772 		str8 shader_name = ctx->entity_names.data[ctx->base_shader_ids[bs]];
   3773 
   3774 		for EachElement(s->files, it) {
   3775 			if (s->files[it].length > 0) {
   3776 				meta_begin_line(m, str8("read_only global u8 " META_NAMESPACE_LOWER  "_shader_"));
   3777 				for (i64 i = 0; i < shader_name.length; i++)
   3778 					stream_append_byte(&m->stream, ToLower(shader_name.data[i]));
   3779 
   3780 				if (s->kind == MetaShaderKind_Render)
   3781 					meta_push(m, it == 0 ? str8("_primitive") : str8("_fragment"));
   3782 
   3783 				meta_begin_scope(m, str8("_bytes[] = {")); {
   3784 					Arena scratch = m->scratch;
   3785 					str8 filename = push_str8_from_parts(&scratch, str8(OS_PATH_SEPARATOR), str8("shaders"), s->files[it]);
   3786 					str8 file     = read_entire_file((c8 *)filename.data, &scratch);
   3787 					metagen_push_byte_array(m, file);
   3788 				} meta_end_scope(m, str8("};\n"));
   3789 			}
   3790 		}
   3791 	}
   3792 
   3793 	meta_begin_scope(m, str8("read_only global str8 *" META_NAMESPACE_LOWER "_shader_data[] = {")); {
   3794 		for (da_count bs = 0; bs < ctx->base_shader_count; bs++) {
   3795 			MetaShader *s = &ctx->entities.data[ctx->base_shader_ids[bs]].shader;
   3796 
   3797 			str8 shader_name = ctx->entity_names.data[ctx->base_shader_ids[bs]];
   3798 
   3799 			if (s->kind == MetaShaderKind_Render) {
   3800 				meta_begin_scope(m, str8("(str8 []){"));
   3801 				meta_indent(m);
   3802 			} else {
   3803 				meta_begin_line(m,  str8("(str8 []){"));
   3804 			}
   3805 
   3806 			meta_push(m, str8("{.data = " META_NAMESPACE_LOWER "_shader_"));
   3807 			for (i64 i = 0; i < shader_name.length; i++)
   3808 				stream_append_byte(&m->stream, ToLower(shader_name.data[i]));
   3809 
   3810 			if (s->kind == MetaShaderKind_Render)
   3811 				meta_push(m, str8("_primitive"));
   3812 
   3813 			meta_push(m, str8("_bytes, .length = countof(" META_NAMESPACE_LOWER "_shader_"));
   3814 			for (i64 i = 0; i < shader_name.length; i++)
   3815 				stream_append_byte(&m->stream, ToLower(shader_name.data[i]));
   3816 
   3817 			if (s->kind == MetaShaderKind_Render)
   3818 				meta_push(m, str8("_primitive"));
   3819 			meta_push(m, str8("_bytes)}"));
   3820 
   3821 			if (s->kind == MetaShaderKind_Render) {
   3822 				meta_end_line(m, str8(","));
   3823 				meta_begin_line(m, str8("{.data = " META_NAMESPACE_LOWER "_shader_"));
   3824 				for (i64 i = 0; i < shader_name.length; i++)
   3825 					stream_append_byte(&m->stream, ToLower(shader_name.data[i]));
   3826 
   3827 				meta_push(m, str8("_fragment_bytes, .length = countof(" META_NAMESPACE_LOWER "_shader_"));
   3828 				for (i64 i = 0; i < shader_name.length; i++)
   3829 					stream_append_byte(&m->stream, ToLower(shader_name.data[i]));
   3830 				meta_end_line(m, str8("_fragment_bytes)}"));
   3831 			}
   3832 
   3833 			if (s->kind == MetaShaderKind_Render) meta_end_scope(m, str8("},"));
   3834 			else                                  meta_end_line(m,  str8("},"));
   3835 		}
   3836 	} meta_end_scope(m, str8("};\n"));
   3837 }
   3838 
   3839 function void
   3840 metagen_emit_c_str8_list(MetaprogramContext *m, str8 *strs, u32 count)
   3841 {
   3842 	meta_begin_scope(m, str8("(str8 []){"));
   3843 	for (u32 index = 0; index < count; index++)
   3844 		meta_push_line(m, str8("str8_comp(\""), strs[index], str8("\"),"));
   3845 	meta_end_scope(m, str8("},"));
   3846 }
   3847 
   3848 function b32
   3849 metagen_emit_c_code(MetaContext *ctx, Arena arena)
   3850 {
   3851 	b32 result = 1;
   3852 	char *out, *out_shaders;
   3853 	{
   3854 		str8 basename;
   3855 		str8_split(ctx->filename, &basename, 0, '.');
   3856 
   3857 		Stream sb = arena_stream(arena);
   3858 		stream_append_str8s(&sb, ctx->directory, str8(OS_PATH_SEPARATOR), str8("generated"));
   3859 		stream_append_byte(&sb, 0);
   3860 		os_make_directory((c8 *)sb.data);
   3861 		stream_reset(&sb, sb.widx - 1);
   3862 
   3863 		stream_append_str8s(&sb, str8(OS_PATH_SEPARATOR), basename, str8(".c"));
   3864 		stream_append_byte(&sb, 0);
   3865 
   3866 		out = (c8 *)arena_stream_commit_and_reset(&arena, &sb).data;
   3867 
   3868 		stream_append_str8s(&sb, ctx->directory, str8(OS_PATH_SEPARATOR), str8("generated"));
   3869 		stream_append_str8s(&sb, str8(OS_PATH_SEPARATOR), basename, str8("_shader_data.c"));
   3870 		stream_append_byte(&sb, 0);
   3871 
   3872 		out_shaders = (c8 *)arena_stream_commit_zero(&arena, &sb).data;
   3873 	}
   3874 
   3875 	MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}};
   3876 
   3877 	if (setjmp(compiler_jmp_buf)) {
   3878 		build_fatal("Failed to generate C Code");
   3879 	}
   3880 
   3881 	////////////////////////////
   3882 	// NOTE(rnp): shader baking
   3883 	if (ctx->base_shader_count) {
   3884 		char **deps = push_array(&m->scratch, char *, 2 * ctx->base_shader_count);
   3885 		u32 dep_count = 0;
   3886 		for (da_count bs = 0; bs < ctx->base_shader_count; bs++) {
   3887 			MetaShader *s = &ctx->entities.data[ctx->base_shader_ids[bs]].shader;
   3888 			deps[dep_count++] = (c8 *)push_str8_from_parts(&m->scratch, str8(OS_PATH_SEPARATOR),
   3889 			                                               str8("shaders"), s->files[0]).data;
   3890 			if (s->files[1].length > 0)
   3891 				deps[dep_count++] = (c8 *)push_str8_from_parts(&m->scratch, str8(OS_PATH_SEPARATOR),
   3892 				                                               str8("shaders"), s->files[1]).data;
   3893 		}
   3894 
   3895 		if (needs_rebuild_(out_shaders, deps, dep_count)) {
   3896 			build_log_generate("%.*s: baking shaders", (i32)ctx->filename.length, ctx->filename.data);
   3897 			meta_push(m, c_file_header);
   3898 			meta_push_shader_bake(m, ctx);
   3899 			result &= meta_write_and_reset(m, out_shaders);
   3900 		}
   3901 		m->scratch = ctx->scratch;
   3902 	}
   3903 
   3904 	{
   3905 		CommandList deps = meta_extract_emit_file_dependencies(ctx, &m->scratch);
   3906 		*da_push(&m->scratch, &deps) = (c8 *)ctx->fullpath.data;
   3907 		if (!needs_rebuild_(out, deps.data, deps.count))
   3908 			return result;
   3909 		m->scratch = ctx->scratch;
   3910 	}
   3911 
   3912 	build_log_generate("%.*s: C Code", (i32)ctx->filename.length, ctx->filename.data);
   3913 
   3914 	meta_push(m, c_file_header);
   3915 
   3916 	/////////////////////////
   3917 	// NOTE(rnp): constants
   3918 	{
   3919 		u32 integers = 0;
   3920 		u32 floats   = 0;
   3921 
   3922 		for (da_count constant = 0; constant < ctx->entity_kind_counts[MetaEntityKind_Constant]; constant++) {
   3923 			da_count    id = ctx->entity_kind_ids[MetaEntityKind_Constant][constant];
   3924 			MetaEntity *e  = ctx->entities.data + id;
   3925 			if (e->constant.kind == MetaConstantKind_Integer) integers++;
   3926 			if (e->constant.kind == MetaConstantKind_Float)   floats++;
   3927 		}
   3928 
   3929 		u32 row_alloc_count = Max(integers, floats);
   3930 		str8 *columns[2];
   3931 		columns[0] = push_array(&m->scratch, str8, row_alloc_count);
   3932 		columns[1] = push_array(&m->scratch, str8, row_alloc_count);
   3933 
   3934 		u32 row_count;
   3935 
   3936 		row_count = 0;
   3937 		if (integers) meta_push_line(m, str8("// NOTE: Constants (Integer)"));
   3938 		for (da_count constant = 0; constant < ctx->entity_kind_counts[MetaEntityKind_Constant]; constant++) {
   3939 			da_count    id = ctx->entity_kind_ids[MetaEntityKind_Constant][constant];
   3940 			MetaEntity *e  = ctx->entities.data + id;
   3941 			if (e->constant.kind == MetaConstantKind_Integer) {
   3942 				Stream sb = arena_stream(m->scratch);
   3943 				stream_append_str8(&sb, str8("("));
   3944 				stream_append_u64(&sb, e->constant.U64);
   3945 				columns[0][row_count] = ctx->entity_names.data[id];
   3946 				columns[1][row_count] = arena_stream_commit(&m->scratch, &sb);
   3947 				row_count++;
   3948 			}
   3949 		}
   3950 		metagen_push_table(m, m->scratch, str8("#define " META_NAMESPACE_UPPER), str8(")"), columns, row_count, 2);
   3951 
   3952 		row_count = 0;
   3953 		if (floats) meta_push_line(m, str8("\n// NOTE: Constants (Float)"));
   3954 		for (da_count constant = 0; constant < ctx->entity_kind_counts[MetaEntityKind_Constant]; constant++) {
   3955 			da_count    id = ctx->entity_kind_ids[MetaEntityKind_Constant][constant];
   3956 			MetaEntity *e  = ctx->entities.data + id;
   3957 			if (e->constant.kind == MetaConstantKind_Float) {
   3958 				Stream sb = arena_stream(m->scratch);
   3959 				stream_append_str8(&sb, str8("("));
   3960 				stream_append_f64(&sb, e->constant.F64, 1000000);
   3961 				columns[0][row_count] = ctx->entity_names.data[id];
   3962 				columns[1][row_count] = arena_stream_commit(&m->scratch, &sb);
   3963 				row_count++;
   3964 			}
   3965 		}
   3966 		metagen_push_table(m, m->scratch, str8("#define " META_NAMESPACE_UPPER), str8(")"), columns, row_count, 2);
   3967 
   3968 		m->scratch = ctx->scratch;
   3969 
   3970 		if (integers || floats) meta_push(m, str8("\n"));
   3971 	}
   3972 
   3973 	/////////////////////////
   3974 	// NOTE(rnp): enumerants
   3975 	struct {MetaEntityKind kind; b32 flags;} enums[] = {
   3976 		{MetaEntityKind_Enumeration, 0},
   3977 		{MetaEntityKind_Flags,       1},
   3978 	};
   3979 	for EachElement(enums, it) {
   3980 		for (da_count kind = 0; kind < ctx->entity_kind_counts[enums[it].kind]; kind++) {
   3981 			da_count    id = ctx->entity_kind_ids[enums[it].kind][kind];
   3982 			MetaEntity *e  = ctx->entities.data + id;
   3983 
   3984 			str8 enum_name = push_str8_from_parts(&m->scratch, str8(""), str8(META_NAMESPACE_UPPER),
   3985 			                                      ctx->entity_names.data[id]);
   3986 			metagen_push_c_enum(m, m->scratch, enum_name, enums[it].flags, e->table.entries[0], e->table.entry_count);
   3987 			m->scratch = ctx->scratch;
   3988 		}
   3989 	}
   3990 
   3991 	// TODO(rnp): technically this needs to be namespaced to the file they are coming from
   3992 	if (ctx->entity_kind_counts[MetaEntityKind_Shader]) {
   3993 		str8 kind      = str8(META_NAMESPACE_UPPER "ShaderKind");
   3994 		str8 kind_full = str8(META_NAMESPACE_UPPER "ShaderKind_");
   3995 		meta_begin_scope(m, str8("typedef enum {"));
   3996 		metagen_push_counted_enum_body_from_ids(m, kind_full, str8(""), str8("= "), str8(","),
   3997 		                                        ctx->entity_kind_ids[MetaEntityKind_Shader], ctx->entity_names.data,
   3998 		                                        ctx->entity_kind_counts[MetaEntityKind_Shader]);
   3999 		meta_push_line(m, kind_full, str8("Count,\n"));
   4000 
   4001 		str8 *columns[2];
   4002 		columns[0] = push_array(&m->scratch, str8, ctx->entity_kind_counts[MetaEntityKind_ShaderGroup] * 3);
   4003 		columns[1] = push_array(&m->scratch, str8, ctx->entity_kind_counts[MetaEntityKind_ShaderGroup] * 3);
   4004 
   4005 		u32 rows = 0;
   4006 		for (da_count group = 0; group < ctx->entity_kind_counts[MetaEntityKind_ShaderGroup]; group++) {
   4007 			da_count     id    = ctx->entity_kind_ids[MetaEntityKind_ShaderGroup][group];
   4008 			MetaEntityID child = ctx->entities.data[id].first_child;
   4009 			str8         name  = ctx->entity_names.data[id];
   4010 
   4011 			da_count shader_count = meta_entity_children_count(ctx, (MetaEntityID){.value = id});
   4012 
   4013 			if (child.value != 0) {
   4014 				// NOTE(rnp): childen pushed in LIFO order
   4015 				str8 first_name = ctx->entity_names.data[ctx->entities.data[child.value].previous_sibling.value];
   4016 				str8 last_name  = ctx->entity_names.data[child.value];
   4017 
   4018 				columns[0][3 * group + 0] = push_str8_from_parts(&m->scratch, str8(""), kind, str8("_"), name, str8("First"));
   4019 				columns[1][3 * group + 0] = push_str8_from_parts(&m->scratch, str8(""), str8("= "), kind, str8("_"), first_name);
   4020 
   4021 				columns[0][3 * group + 1] = push_str8_from_parts(&m->scratch, str8(""), kind, str8("_"), name, str8("Last"));
   4022 				columns[1][3 * group + 1] = push_str8_from_parts(&m->scratch, str8(""),str8("= "), kind, str8("_"), last_name);
   4023 
   4024 				columns[0][3 * group + 2] = push_str8_from_parts(&m->scratch, str8(""), kind, str8("_"), name, str8("Count"));
   4025 				Stream sb = arena_stream(m->scratch);
   4026 				stream_append_str8(&sb, str8("= "));
   4027 				stream_append_i64(&sb, shader_count);
   4028 				columns[1][3 * group + 2] = arena_stream_commit(&m->scratch, &sb);
   4029 
   4030 				rows += 3;
   4031 			}
   4032 		}
   4033 		metagen_push_table(m, m->scratch, str8(""), str8(","), columns, rows, 2);
   4034 
   4035 		meta_end_scope(m, str8("} "), kind, str8(";\n"));
   4036 		m->scratch = ctx->scratch;
   4037 	}
   4038 
   4039 	//////////////////////
   4040 	// NOTE(rnp): structs
   4041 	{
   4042 		for EachElement(meta_struct_entity_kinds, kind_it) {
   4043 			if (meta_struct_emit[kind_it]) {
   4044 				for (da_count it = 0; it < ctx->entity_kind_counts[meta_struct_entity_kinds[kind_it]]; it++) {
   4045 					da_count entity = ctx->entity_kind_ids[meta_struct_entity_kinds[kind_it]][it];
   4046 
   4047 					meta_begin_scope(m, str8("typedef struct {")); {
   4048 						meta_push_struct_body(ctx, m, ctx->entities.data + entity, (MetaPushStructParameters){
   4049 							.layout_style        = MetaPushStructStyle_C,
   4050 							.union_style         = MetaPushStructStyle_C,
   4051 							.element_count_style = MetaPushStructStyle_C,
   4052 							.base_types          = meta_kind_c_types,
   4053 							.suffix              = str8(";"),
   4054 							.str_element_prefix  = str8(META_NAMESPACE_UPPER),
   4055 						});
   4056 					} meta_end_scope(m, str8("} " META_NAMESPACE_UPPER), ctx->entity_names.data[entity], str8(";"));
   4057 					meta_push(m, str8("\n"));
   4058 				}
   4059 			}
   4060 		}
   4061 	}
   4062 
   4063 	// NOTE: shader bake parameter union
   4064 	if (ctx->entity_kind_counts[MetaEntityKind_BakeParameters])
   4065 	DeferLoop(meta_begin_scope(m, str8("typedef union {")),
   4066 	          meta_end_scope(m, str8("} " META_NAMESPACE_UPPER "ShaderBakeParameters;\n")))
   4067 	{
   4068 		Arena scratch;
   4069 		DeferLoop(scratch = m->scratch, m->scratch = scratch)
   4070 		{
   4071 			str8 *columns[2];
   4072 			columns[0] = push_array(&m->scratch, str8, ctx->entity_kind_counts[MetaEntityKind_BakeParameters]);
   4073 			columns[1] = push_array(&m->scratch, str8, ctx->entity_kind_counts[MetaEntityKind_BakeParameters]);
   4074 
   4075 			for (da_count bake = 0; bake < ctx->entity_kind_counts[MetaEntityKind_BakeParameters]; bake++) {
   4076 				da_count id = ctx->entity_kind_ids[MetaEntityKind_BakeParameters][bake];
   4077 
   4078 				str8 bake_name   = ctx->entity_names.data[id];
   4079 				str8 shader_name = {.data = bake_name.data, .length = bake_name.length - str8("BakeParameters").length};
   4080 
   4081 				columns[0][bake] = push_str8_from_parts(&m->scratch, str8(""), str8(META_NAMESPACE_UPPER), bake_name);
   4082 				columns[1][bake] = shader_name;
   4083 			}
   4084 			metagen_push_table(m, m->scratch, str8(""), str8(";"), columns,
   4085 			                   ctx->entity_kind_counts[MetaEntityKind_BakeParameters], 2);
   4086 		}
   4087 	}
   4088 
   4089 	metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_C, meta_kind_c_types);
   4090 
   4091 	/////////////////////////////////
   4092 	// NOTE(rnp): shader info tables
   4093 	if (ctx->entity_kind_counts[MetaEntityKind_Shader])
   4094 	DeferLoop(meta_begin_scope(m, str8("read_only global str8 " META_NAMESPACE_LOWER "_shader_names[] = {")),
   4095 	          meta_end_scope(m, str8("};\n")))
   4096 	{
   4097 		for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) {
   4098 			da_count id = ctx->entity_kind_ids[MetaEntityKind_Shader][shader];
   4099 			meta_push_line(m, str8("str8_comp(\""), ctx->entity_names.data[id], str8("\"),"));
   4100 		}
   4101 	}
   4102 
   4103 	meta_push_shader_reload_info(m, ctx);
   4104 
   4105 	if (ctx->base_shader_count)
   4106 	DeferLoop(meta_begin_scope(m, str8("read_only global i32 *" META_NAMESPACE_LOWER "_shader_header_vectors[] = {")),
   4107 	          meta_end_scope(m, str8("};\n")))
   4108 	{
   4109 		for (da_count bs = 0; bs < ctx->base_shader_count; bs++) {
   4110 			da_count    id = ctx->base_shader_ids[bs];
   4111 			MetaShader *s  = &ctx->entities.data[id].shader;
   4112 			if (s->entity_reference_ids.count) {
   4113 				meta_begin_line(m, str8("(i32 []){"));
   4114 				for (da_count ref_id = 0; ref_id < s->entity_reference_ids.count; ref_id++) {
   4115 					if (ref_id != 0) meta_push(m, str8(", "));
   4116 					MetaEntityReference *r = &ctx->entities.data[s->entity_reference_ids.data[ref_id]].reference;
   4117 					meta_push_i64(m, meta_lookup_id_slow(ctx->shader_entity_references.data,
   4118 					                                     ctx->shader_entity_references.count,
   4119 					                                     r->resolved_id.value));
   4120 				}
   4121 				meta_end_line(m, str8("},"));
   4122 			} else {
   4123 				meta_push_line(m, str8("0,"));
   4124 			}
   4125 		}
   4126 	}
   4127 
   4128 	if (ctx->base_shader_count)
   4129 	DeferLoop(meta_begin_scope(m, str8("read_only global i32 " META_NAMESPACE_LOWER "_shader_header_vector_lengths[] = {")),
   4130 	          meta_end_scope(m, str8("};\n")))
   4131 	{
   4132 		for (da_count bs= 0; bs < ctx->base_shader_count; bs++) {
   4133 			da_count    id = ctx->base_shader_ids[bs];
   4134 			MetaShader *s  = &ctx->entities.data[id].shader;
   4135 			meta_indent(m);
   4136 			meta_push_i64(m, s->entity_reference_ids.count);
   4137 			meta_end_line(m, str8(","));
   4138 		}
   4139 	}
   4140 
   4141 	if (ctx->base_shader_count)
   4142 	DeferLoop(meta_begin_scope(m, str8("read_only global str8 *" META_NAMESPACE_LOWER "_shader_bake_parameter_names[] = {")),
   4143 	          meta_end_scope(m, str8("};\n")))
   4144 	{
   4145 		for (da_count bs = 0; bs < ctx->base_shader_count; bs++) {
   4146 			da_count    id = ctx->base_shader_ids[bs];
   4147 			MetaEntity *e  = ctx->entities.data + id;
   4148 			MetaEntityID bp_id = meta_entity_first_child_of_kind(ctx, e, MetaEntityKind_BakeParameters);
   4149 			if (bp_id.value != 0) {
   4150 				MetaEntity *bp = meta_entity(ctx, bp_id);
   4151 				metagen_emit_c_str8_list(m, bp->table.entries[MetaBakeField_NameUpper], bp->table.entry_count);
   4152 			} else {
   4153 				meta_push_line(m, str8("0,"));
   4154 			}
   4155 		}
   4156 	}
   4157 
   4158 	if (ctx->base_shader_count)
   4159 	DeferLoop(meta_begin_scope(m, str8("read_only global u32 " META_NAMESPACE_LOWER "_shader_bake_parameter_float_bits[] = {")),
   4160 	          meta_end_scope(m, str8("};\n")))
   4161 	{
   4162 		for (da_count bs = 0; bs < ctx->base_shader_count; bs++) {
   4163 			da_count    id = ctx->base_shader_ids[bs];
   4164 			MetaEntity *e  = ctx->entities.data + id;
   4165 			MetaEntityID bp_id = meta_entity_first_child_of_kind(ctx, e, MetaEntityKind_BakeParameters);
   4166 			u32 hex = 0;
   4167 			if (bp_id.value != 0) {
   4168 				MetaTable  *t = &ctx->entities.data[bp_id.value].table;
   4169 				MetaStruct *s = ctx->struct_infos + t->struct_info_id;
   4170 				for EachIndex(s->member_count, member) {
   4171 					b32 type_reference = (s->member_flags[member] & MetaStructMemberFlag_ReferenceType) != 0;
   4172 					if (!type_reference && s->type_ids[member] == MetaKind_F32)
   4173 						hex |= 1 << member;
   4174 				}
   4175 			}
   4176 			meta_begin_line(m, str8("0x"));
   4177 			meta_push_u64_hex_width(m, hex, 8);
   4178 			meta_end_line(m, str8("UL,"));
   4179 		}
   4180 	}
   4181 
   4182 	if (ctx->base_shader_count)
   4183 	DeferLoop(meta_begin_scope(m, str8("read_only global u8 " META_NAMESPACE_LOWER "_shader_bake_parameter_counts[] = {")),
   4184 	          meta_end_scope(m, str8("};\n")))
   4185 	{
   4186 		for (da_count bs = 0; bs < ctx->base_shader_count; bs++) {
   4187 			da_count    id = ctx->base_shader_ids[bs];
   4188 			MetaEntity *e  = ctx->entities.data + id;
   4189 			MetaEntityID bp_id = meta_entity_first_child_of_kind(ctx, e, MetaEntityKind_BakeParameters);
   4190 			u32 count = 0;
   4191 			if (bp_id.value != 0)
   4192 				count = ctx->entities.data[bp_id.value].table.entry_count;
   4193 			meta_indent(m);
   4194 			meta_push_u64(m, count);
   4195 			meta_end_line(m, str8(","));
   4196 		}
   4197 	}
   4198 
   4199 	if (ctx->base_shader_count)
   4200 	DeferLoop(meta_begin_scope(m, str8("read_only global u8 " META_NAMESPACE_LOWER "_shader_push_constant_sizes[] = {")),
   4201 	          meta_end_scope(m, str8("};\n")))
   4202 	{
   4203 		for (da_count bs = 0; bs < ctx->base_shader_count; bs++) {
   4204 			da_count    id = ctx->base_shader_ids[bs];
   4205 			MetaEntity *e  = ctx->entities.data + id;
   4206 			MetaEntityID pc_id = meta_entity_first_child_of_kind(ctx, e, MetaEntityKind_PushConstants);
   4207 			if (pc_id.value != 0) {
   4208 				meta_push_line(m, str8("sizeof(" META_NAMESPACE_UPPER), ctx->entity_names.data[id], str8("PushConstants),"));
   4209 			} else {
   4210 				meta_push_line(m, str8("0,"));
   4211 			}
   4212 		}
   4213 	}
   4214 
   4215 	result = meta_write_and_reset(m, out);
   4216 
   4217 	return result;
   4218 }
   4219 
   4220 function b32
   4221 metagen_matlab_union(MetaprogramContext *m, MetaContext *ctx, MetaStruct *u, str8 outdir, str8 namespace)
   4222 {
   4223 	b32 result = 1;
   4224 
   4225 	Arena scratch;
   4226 	DeferLoop(scratch = m->scratch, m->scratch = scratch)
   4227 	{
   4228 		str8 outfile = push_str8_from_parts(&m->scratch, str8(OS_PATH_SEPARATOR), outdir, str8("Base.m"));
   4229 		meta_begin_scope(m, str8("classdef Base"));
   4230 		{
   4231 			meta_begin_scope(m, str8("properties (Constant)"));
   4232 			{
   4233 				meta_begin_line(m, str8("byteSize(1,1) uint32 = "));
   4234 				meta_push_u64(m, u->byte_size);
   4235 				meta_end_line(m);
   4236 			} meta_end_scope(m, str8("end"));
   4237 		} meta_end_scope(m, str8("end"));
   4238 		result &= meta_end_and_write_matlab(m, (c8 *)outfile.data);
   4239 	}
   4240 
   4241 	for EachIndex(u->member_count, union_member) {
   4242 		if ((u->member_flags[union_member] & MetaStructMemberFlag_ReferenceType) == 0) {
   4243 			str8 type_name = meta_kind_c_types[u->type_ids[union_member]];
   4244 			str8 name      = u->members[union_member];
   4245 			build_log_failure("%.*s:%u:%u: error: base type in MATLAB union:\n"
   4246 			                  "%.*s %.*s\n"
   4247 			                  "MATLAB unions only support Struct and Union members\n",
   4248 			                  (i32)ctx->filename.length, ctx->filename.data, u->location.line, u->location.column,
   4249 			                  (i32)name.length, name.data, (i32)type_name.length, type_name.data);
   4250 			result = 0;
   4251 			break;
   4252 		}
   4253 
   4254 		DeferLoop(scratch = m->scratch, m->scratch = scratch)
   4255 		{
   4256 			MetaStruct *s = ctx->struct_infos + ctx->entities.data[u->type_ids[union_member]].table.struct_info_id;
   4257 			str8 sub_name = u->members[union_member];
   4258 			str8 outfile  = push_str8_from_parts(&m->scratch, str8(""), outdir, str8(OS_PATH_SEPARATOR), sub_name, str8(".m"));
   4259 			DeferLoop(meta_begin_scope(m, str8("classdef "), sub_name, str8(" < " MATLAB_NAMESPACE META_NAMESPACE_UPPER),
   4260 			                           namespace, str8(".Base")),
   4261 			          meta_end_scope(m, str8("end")))
   4262 			{
   4263 				meta_push_matlab_properties(m, ctx, s);
   4264 
   4265 				meta_push(m, str8("\n"));
   4266 
   4267 				DeferLoop(meta_begin_scope(m, str8("methods")), meta_end_scope(m, str8("end")))
   4268 				{
   4269 					DeferLoop(meta_begin_scope(m, str8("function bytes = toBytes(obj)")),
   4270 					          meta_end_scope(m, str8("end")))
   4271 					{
   4272 						meta_begin_scope(m, str8("arguments (Output)"));
   4273 						{
   4274 							meta_push_line(m, str8("bytes uint8"));
   4275 						} meta_end_scope(m, str8("end"));
   4276 						meta_push_line(m, str8("bytes = zeros(1, obj.byteSize, 'uint8');"));
   4277 
   4278 						str8 *columns[3];
   4279 						columns[0] = push_array(&m->scratch, str8, s->member_count);
   4280 						columns[1] = push_array(&m->scratch, str8, s->member_count);
   4281 						columns[2] = push_array(&m->scratch, str8, s->member_count);
   4282 
   4283 						u32 offset = 1;
   4284 						for EachIndex(s->member_count, member) {
   4285 							Stream sb = arena_stream(m->scratch);
   4286 
   4287 							i32 type_id = s->type_ids[member];
   4288 
   4289 							u32 member_size = 0;
   4290 							if (s->member_flags[member] & MetaStructMemberFlag_ReferenceType) {
   4291 								MetaStruct *ref = ctx->struct_infos + ctx->entities.data[type_id].table.struct_info_id;
   4292 								member_size = ref->byte_size;
   4293 								// TODO(rnp): arrays of structs
   4294 								// - calculate member count with element count multiplied in for struct members
   4295 								// - do a sub loop for struct arrays calling toBytes method on each struct array element
   4296 								if (meta_struct_member_elements(ctx, s, member) != 1) {
   4297 									str8 name = s->members[member];
   4298 									build_log_failure("%.*s:%u:%u: error: array of structs present in struct referenced by MATLAB union:\n"
   4299 									                  "%.*s %.*s\n"
   4300 									                  "MATLAB unions do not currently support array of structs\n",
   4301 									                  (i32)ctx->filename.length, ctx->filename.data, u->location.line, u->location.column,
   4302 									                  (i32)name.length, name.data, (i32)ref->name.length, ref->name.data);
   4303 								}
   4304 							} else {
   4305 								member_size = meta_kind_byte_sizes[type_id];
   4306 							}
   4307 
   4308 							stream_append_u64(&sb, offset);
   4309 							stream_append_byte(&sb, ':');
   4310 							stream_append_u64(&sb, offset - 1 + member_size);
   4311 							stream_append_byte(&sb, ')');
   4312 							offset += member_size;
   4313 
   4314 							columns[0][member] = arena_stream_commit_and_reset(&m->scratch, &sb);
   4315 
   4316 							stream_append_str8s(&sb, str8("= typecast(obj."), s->members[member]);
   4317 							if (s->member_flags[member] & MetaStructMemberFlag_ReferenceType) {
   4318 								// TODO(rnp): arrays of structs
   4319 								// - calculate member count with element count multiplied in for struct members
   4320 								// - do a sub loop for struct arrays calling toBytes method on each struct array element
   4321 								// lookup subtype, if union replace with byte array, else reference sub type
   4322 								MetaStruct *ref = ctx->struct_infos + ctx->entities.data[type_id].table.struct_info_id;
   4323 								if ((ref->flags & MetaStructFlag_Union) == 0) {
   4324 									stream_append_str8(&sb, str8(".toBytes()"));
   4325 								}
   4326 							} else {
   4327 								stream_append_str8(&sb, str8("(:)"));
   4328 							}
   4329 							stream_append_byte(&sb, ',');
   4330 
   4331 							columns[1][member] = arena_stream_commit_and_reset(&m->scratch, &sb);
   4332 							columns[2][member] = str8("'uint8');");
   4333 						}
   4334 
   4335 						metagen_push_table(m, m->scratch, str8("bytes("), str8(""), columns, s->member_count, 3);
   4336 					}
   4337 				}
   4338 			}
   4339 			result &= meta_end_and_write_matlab(m, (c8 *)outfile.data);
   4340 		}
   4341 	}
   4342 
   4343 	return result;
   4344 }
   4345 
   4346 function b32
   4347 metagen_emit_matlab_code(MetaContext *ctx, Arena arena)
   4348 {
   4349 	b32 result = 1;
   4350 	if (!needs_rebuild(OUTPUT("matlab/OGLBeamformerShaderStage.m"), "beamformer.meta"))
   4351 		return result;
   4352 
   4353 	build_log_generate("MATLAB Bindings");
   4354 	char *base_directory = OUTPUT("matlab");
   4355 	if (!os_remove_directory(base_directory))
   4356 		build_fatal("failed to remove directory: %s", base_directory);
   4357 
   4358 	if (setjmp(compiler_jmp_buf)) {
   4359 		os_remove_directory(base_directory);
   4360 		build_log_error("Failed to generate MATLAB Bindings");
   4361 		return 0;
   4362 	}
   4363 
   4364 	os_make_directory(base_directory);
   4365 
   4366 	MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}};
   4367 
   4368 	meta_begin_matlab_class(m, "OGLBeamformerShaderStage", "int32");
   4369 	meta_begin_scope(m, str8("enumeration"));
   4370 	{
   4371 		da_count group_id = -1;
   4372 		for (da_count group = 0; group < ctx->entity_kind_counts[MetaEntityKind_ShaderGroup]; group++) {
   4373 			da_count id = ctx->entity_kind_ids[MetaEntityKind_ShaderGroup][group];
   4374 			str8 group_name = ctx->entity_names.data[id];
   4375 			if (str8_equal(group_name, str8("Compute"))) {
   4376 				group_id = id;
   4377 				break;
   4378 			}
   4379 		}
   4380 		if (group_id != -1) {
   4381 			da_count children;
   4382 			da_count *ids = meta_entity_extract_children(ctx, (MetaEntityID){.value = group_id},
   4383 			                                             &children, &m->scratch);
   4384 			if (children > 0) {
   4385 				metagen_push_counted_enum_body_from_ids(m, str8(""), str8(""), str8("("), str8(")"), ids,
   4386 				                                        ctx->entity_names.data, children);
   4387 			}
   4388 			m->scratch = ctx->scratch;
   4389 		} else {
   4390 			build_log_failure("failed to find Compute shader group in meta info\n");
   4391 		}
   4392 		result &= group_id != -1;
   4393 	}
   4394 	result &= meta_end_and_write_matlab(m, OUTPUT("matlab/OGLBeamformerShaderStage.m"));
   4395 
   4396 	for (da_count kind = 0; kind < ctx->entity_kind_counts[MetaEntityKind_Enumeration]; kind++) {
   4397 		Arena scratch = ctx->scratch;
   4398 		da_count id = ctx->entity_kind_ids[MetaEntityKind_Enumeration][kind];
   4399 		str8 name   = ctx->entity_names.data[id];
   4400 		str8 output = push_str8_from_parts(&scratch, str8(""), str8(OUTPUT("matlab/OGLBeamformer")), name, str8(".m"));
   4401 
   4402 		MetaTable *etable = &ctx->entities.data[id].table;
   4403 		str8 *kinds = etable->entries[0];
   4404 		meta_begin_scope(m, str8("classdef OGLBeamformer"), name, str8(" < int32"));
   4405 		meta_begin_scope(m, str8("enumeration"));
   4406 		str8 prefix = str8("");
   4407 		if (etable->entry_count > 0 && IsDigit(kinds[0].data[0])) prefix = str8("m");
   4408 		metagen_push_counted_enum_body(m, str8(""), prefix, str8("("), str8(")"), kinds, etable->entry_count);
   4409 		result &= meta_end_and_write_matlab(m, (c8 *)output.data);
   4410 	}
   4411 
   4412 	////////////////////
   4413 	// NOTE: emit files
   4414 	{
   4415 		MetaEmitOperationListSet *emit_set = ctx->emit_sets + MetaEmitLang_MATLAB;
   4416 		for (da_count list = 0; list < emit_set->count; list++) {
   4417 			MetaEmitOperationList *ops = emit_set->data + list;
   4418 			Arena scratch = m->scratch;
   4419 			str8 output = push_str8_from_parts(&m->scratch, str8(""),
   4420 			                                   str8(OUTPUT("matlab") OS_PATH_SEPARATOR "OGLBeamformer"),
   4421 			                                   ops->filename, str8(".m"));
   4422 			meta_push_line(m, str8("% GENERATED CODE"));
   4423 			metagen_run_emit(m, ctx, ops, meta_kind_matlab_types);
   4424 			result &= meta_write_and_reset(m, (c8 *)output.data);
   4425 			m->scratch = scratch;
   4426 		}
   4427 	}
   4428 
   4429 	/////////////////////////
   4430 	// NOTE(rnp): entities marked @MATLAB
   4431 	{
   4432 		da_count  children;
   4433 		da_count *ids = meta_entity_extract_children(ctx, ctx->matlab_entity, &children, &m->scratch);
   4434 
   4435 		for EachIndex((u64)children, it) {
   4436 			MetaEntity *rr  = ctx->entities.data + ids[it];
   4437 			da_count ref_id = ctx->entities.data[rr->reference.resolved_id.value].reference.resolved_id.value;
   4438 			MetaEntity *re  = ctx->entities.data + ref_id;
   4439 
   4440 			switch (re->kind) {
   4441 			InvalidDefaultCase;
   4442 			case MetaEntityKind_Union:{
   4443 				Arena scratch;
   4444 				DeferLoop(scratch = m->scratch, m->scratch = scratch) {
   4445 					MetaStruct *s = ctx->struct_infos + re->table.struct_info_id;
   4446 					str8 name   = (rr->reference.scope_name.length > 0) ? rr->reference.scope_name : s->name;
   4447 					str8 outdir = push_str8_from_parts(&m->scratch, str8(""), str8(OUTPUT("matlab") OS_PATH_SEPARATOR),
   4448 					                                   str8("+" MATLAB_NAMESPACE META_NAMESPACE_UPPER), name);
   4449 					os_make_directory((c8 *)outdir.data);
   4450 					result &= metagen_matlab_union(m, ctx, s, outdir, name);
   4451 				}
   4452 			}break;
   4453 
   4454 			case MetaEntityKind_Struct:{
   4455 				MetaStruct *s = ctx->struct_infos + re->table.struct_info_id;
   4456 				str8 name    = (rr->reference.scope_name.length > 0) ? rr->reference.scope_name : s->name;
   4457 				str8 outfile = push_str8_from_parts(&m->scratch, str8(""), str8(OUTPUT("matlab") OS_PATH_SEPARATOR),
   4458 				                                    str8(MATLAB_NAMESPACE META_NAMESPACE_UPPER), name, str8(".m"));
   4459 				meta_begin_scope(m, str8("classdef " MATLAB_NAMESPACE META_NAMESPACE_UPPER), name);
   4460 				{
   4461 					meta_push_matlab_properties(m, ctx, s);
   4462 				} meta_end_scope(m, str8("end"));
   4463 				result &= meta_end_and_write_matlab(m, (c8 *)outfile.data);
   4464 			}break;
   4465 
   4466 			}
   4467 		}
   4468 		m->scratch = ctx->scratch;
   4469 	}
   4470 
   4471 	return result;
   4472 }
   4473 
   4474 function void
   4475 meta_push_helper_library_header_base(MetaprogramContext *m, MetaContext *ctx)
   4476 {
   4477 	meta_push(m, c_file_header);
   4478 	meta_push_line(m, str8("#include <stdint.h>\n"));
   4479 
   4480 	/////////////////////////
   4481 	// NOTE(rnp): Constants
   4482 	{
   4483 		u32 integers = 0;
   4484 		for (da_count constant = 0; constant < ctx->entity_kind_counts[MetaEntityKind_Constant]; constant++) {
   4485 			da_count    id = ctx->entity_kind_ids[MetaEntityKind_Constant][constant];
   4486 			MetaEntity *e  = ctx->entities.data + id;
   4487 			if (e->constant.kind == MetaConstantKind_Integer) integers++;
   4488 		}
   4489 
   4490 		str8 *columns[2];
   4491 		columns[0] = push_array(&m->scratch, str8, integers);
   4492 		columns[1] = push_array(&m->scratch, str8, integers);
   4493 
   4494 		u32 row_count = 0;
   4495 		meta_push_line(m, str8("// NOTE: Constants (Integer)"));
   4496 		for (da_count constant = 0; constant < ctx->entity_kind_counts[MetaEntityKind_Constant]; constant++) {
   4497 			da_count    id = ctx->entity_kind_ids[MetaEntityKind_Constant][constant];
   4498 			MetaEntity *e  = ctx->entities.data + id;
   4499 			if (e->constant.kind == MetaConstantKind_Integer) {
   4500 				Stream sb = arena_stream(m->scratch);
   4501 				stream_append_str8(&sb, str8("("));
   4502 				stream_append_u64(&sb, e->constant.U64);
   4503 				columns[0][row_count] = ctx->entity_names.data[id];
   4504 				columns[1][row_count] = arena_stream_commit(&m->scratch, &sb);
   4505 				row_count++;
   4506 			}
   4507 		}
   4508 		metagen_push_table(m, m->scratch, str8("#define " META_NAMESPACE_UPPER), str8(")"), columns, row_count, 2);
   4509 		meta_push(m, str8("\n"));
   4510 	}
   4511 
   4512 	/////////////////////////
   4513 	// NOTE(rnp): enumerants
   4514 	for (da_count kind = 0; kind < ctx->entity_kind_counts[MetaEntityKind_Enumeration]; kind++) {
   4515 		da_count    id = ctx->entity_kind_ids[MetaEntityKind_Enumeration][kind];
   4516 		MetaEntity *e  = ctx->entities.data + id;
   4517 
   4518 		str8 enum_name = push_str8_from_parts(&m->scratch, str8(""), str8(META_NAMESPACE_UPPER),
   4519 		                                      ctx->entity_names.data[id]);
   4520 		metagen_push_c_enum(m, m->scratch, enum_name, 0, e->table.entries[0], e->table.entry_count);
   4521 		m->scratch = ctx->scratch;
   4522 	}
   4523 
   4524 	{
   4525 		da_count group_id = -1;
   4526 		for (da_count group = 0; group < ctx->entity_kind_counts[MetaEntityKind_ShaderGroup]; group++) {
   4527 			da_count id = ctx->entity_kind_ids[MetaEntityKind_ShaderGroup][group];
   4528 			str8 group_name = ctx->entity_names.data[id];
   4529 			if (str8_equal(group_name, str8("Compute"))) {
   4530 				group_id = id;
   4531 				break;
   4532 			}
   4533 		}
   4534 
   4535 		if (group_id != -1) {
   4536 			da_count children;
   4537 			da_count *ids = meta_entity_extract_children(ctx, (MetaEntityID){.value = group_id},
   4538 			                                             &children, &m->scratch);
   4539 			if (children > 0) {
   4540 				str8 kind      = str8(META_NAMESPACE_UPPER "ShaderKind");
   4541 				str8 kind_full = str8(META_NAMESPACE_UPPER "ShaderKind_");
   4542 				meta_begin_scope(m, str8("typedef enum {"));
   4543 				{
   4544 					metagen_push_counted_enum_body_from_ids(m, kind_full, str8(""), str8("= "), str8(","), ids,
   4545 					                                        ctx->entity_names.data, children);
   4546 					meta_push_line(m, kind_full, str8("Count,"));
   4547 				} meta_end_scope(m, str8("} "), kind, str8(";\n"));
   4548 
   4549 				m->scratch = ctx->scratch;
   4550 
   4551 				meta_begin_line(m, str8("#define "), kind_full, str8("ComputeCount ("));
   4552 				meta_push_i64(m, children);
   4553 				meta_end_line(m, str8(")\n"));
   4554 			}
   4555 			m->scratch = ctx->scratch;
   4556 		} else {
   4557 			build_log_failure("failed to find Compute shader group in meta info\n");
   4558 		}
   4559 	}
   4560 }
   4561 
   4562 function void
   4563 meta_entity_resolve_references(MetaContext *ctx, da_count *ids, u64 id_count)
   4564 {
   4565 	for EachIndex(id_count, it) {
   4566 		MetaEntity *e = meta_entity(ctx, (MetaEntityID){ids[it]});
   4567 		switch (e->kind) {
   4568 		InvalidDefaultCase;
   4569 		case MetaEntityKind_ReferenceReference:{
   4570 			MetaEntity *r = meta_entity(ctx, e->reference.resolved_id);
   4571 			ids[it] = r->reference.resolved_id.value;
   4572 		}break;
   4573 		}
   4574 	}
   4575 }
   4576 
   4577 function b32
   4578 metagen_emit_helper_library_header(MetaContext *ctx, Arena arena)
   4579 {
   4580 	b32 result = 1;
   4581 	char *out = OUTPUT("ogl_beamformer_lib.h");
   4582 	if (!needs_rebuild(out, "lib/ogl_beamformer_lib_base.h", "beamformer.meta"))
   4583 		return result;
   4584 
   4585 	build_log_generate("Library Header");
   4586 
   4587 	str8 base_header = read_entire_file("lib/ogl_beamformer_lib_base.h", &arena);
   4588 
   4589 	MetaprogramContext m[1] = {{.stream = arena_stream(arena), .scratch = ctx->scratch}};
   4590 
   4591 	meta_push_helper_library_header_base(m, ctx);
   4592 	/////////////////////////
   4593 	// NOTE(rnp): entities marked @Library
   4594 	{
   4595 		da_count  children;
   4596 		da_count *ids = meta_entity_extract_children(ctx, ctx->library_entity, &children, &m->scratch);
   4597 
   4598 		meta_entity_resolve_references(ctx, ids, children);
   4599 
   4600 		for EachIndex((u64)children, it) {
   4601 			MetaEntity *e = ctx->entities.data + ids[it];
   4602 			switch (e->kind) {
   4603 			InvalidDefaultCase;
   4604 
   4605 			case MetaEntityKind_Struct:{
   4606 				meta_begin_scope(m, str8("typedef struct {")); {
   4607 					meta_push_struct_body(ctx, m, e, (MetaPushStructParameters){
   4608 						.layout_style        = MetaPushStructStyle_C,
   4609 						.union_style         = MetaPushStructStyle_C,
   4610 						.element_count_style = MetaPushStructStyle_C,
   4611 						.base_types          = meta_kind_base_c_types,
   4612 						.suffix              = str8(";"),
   4613 						.str_element_prefix  = str8(META_NAMESPACE_UPPER),
   4614 						.base_type_element_count_scales = meta_kind_elements,
   4615 					});
   4616 				} meta_end_scope(m, str8("} " META_NAMESPACE_UPPER), ctx->entity_names.data[ids[it]], str8(";\n"));
   4617 			}break;
   4618 
   4619 			case MetaEntityKind_Union:{
   4620 			}break;
   4621 
   4622 			}
   4623 		}
   4624 		m->scratch = ctx->scratch;
   4625 	}
   4626 
   4627 	metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_CLibrary, meta_kind_base_c_types);
   4628 
   4629 	meta_push_line(m, str8("// END GENERATED CODE\n"));
   4630 
   4631 	meta_push(m, base_header);
   4632 	result &= meta_write_and_reset(m, out);
   4633 
   4634 	// NOTE(rnp): matlab compatible header
   4635 	{
   4636 		meta_push_helper_library_header_base(m, ctx);
   4637 		/////////////////////////
   4638 		// NOTE(rnp): entities marked @Library
   4639 		{
   4640 			da_count  children;
   4641 			da_count *ids = meta_entity_extract_children(ctx, ctx->library_entity, &children, &m->scratch);
   4642 
   4643 			meta_entity_resolve_references(ctx, ids, children);
   4644 
   4645 			for EachIndex((u64)children, it) {
   4646 				MetaEntity *e = ctx->entities.data + ids[it];
   4647 				switch (e->kind) {
   4648 				InvalidDefaultCase;
   4649 				case MetaEntityKind_Struct:{
   4650 					meta_begin_scope(m, str8("typedef struct {"));
   4651 					{
   4652 						meta_push_struct_body(ctx, m, e, (MetaPushStructParameters){
   4653 							.layout_style        = MetaPushStructStyle_C,
   4654 							.union_style         = MetaPushStructStyle_MATLAB,
   4655 							.element_count_style = MetaPushStructStyle_C,
   4656 							.base_types          = meta_kind_base_c_types,
   4657 							.suffix              = str8(";"),
   4658 							.str_element_prefix  = str8(META_NAMESPACE_UPPER),
   4659 							.base_type_element_count_scales = meta_kind_elements,
   4660 						});
   4661 					} meta_end_scope(m, str8("} " META_NAMESPACE_UPPER), ctx->entity_names.data[ids[it]], str8(";\n"));
   4662 				}break;
   4663 				case MetaEntityKind_Union:{}break;
   4664 				}
   4665 			}
   4666 			m->scratch = ctx->scratch;
   4667 		}
   4668 
   4669 		metagen_run_emit_set(m, ctx, ctx->emit_sets + MetaEmitLang_CLibrary, meta_kind_base_c_types);
   4670 
   4671 		meta_push_line(m, str8("// END GENERATED CODE\n"));
   4672 
   4673 		meta_push(m, base_header);
   4674 		result &= meta_write_and_reset(m, OUTPUT("ogl_beamformer_lib_matlab.h"));
   4675 	}
   4676 
   4677 	{
   4678 		CommandList cpp = {0};
   4679 		cmd_append(&arena, &cpp, PREPROCESSOR, out, COMPILER_OUTPUT, OUTPUT("ogl_beamformer_lib_python_ffi.h"));
   4680 		result &= run_synchronous(arena, &cpp);
   4681 	}
   4682 
   4683 	return result;
   4684 }
   4685 
   4686 function MetaContext *
   4687 metagen_load_context(Arena *arena, char *filename)
   4688 {
   4689 	if (setjmp(compiler_jmp_buf)) {
   4690 		/* NOTE(rnp): compiler error */
   4691 		return 0;
   4692 	}
   4693 
   4694 	MetaContext *ctx = push_struct(arena, MetaContext);
   4695 	ctx->scratch     = sub_arena(arena, MB(1), 16);
   4696 	ctx->arena       = arena;
   4697 
   4698 	// NOTE(rnp): nil entity
   4699 	*da_push(ctx->arena, &ctx->entity_names) = str8("Nil");
   4700 	da_push(ctx->arena, &ctx->entities);
   4701 
   4702 	MetaContext *result = ctx;
   4703 
   4704 	ctx->filename  = push_str8(ctx->arena, str8_from_c_str(filename));
   4705 	ctx->directory = str8_chop(&ctx->filename, str8_scan_backwards(ctx->filename, OS_PATH_SEPARATOR_CHAR));
   4706 	ctx->fullpath  = str8_from_c_str((c8 *)ctx->directory.data);
   4707 	if (ctx->directory.length > 0) str8_chop(&ctx->filename, 1);
   4708 	if (ctx->directory.length <= 0) {
   4709 		ctx->directory = str8(".");
   4710 		ctx->fullpath = push_str8_from_parts(ctx->arena, str8(""), ctx->directory,
   4711 		                                     str8(OS_PATH_SEPARATOR), ctx->filename);
   4712 	}
   4713 
   4714 	Arena scratch = ctx->scratch;
   4715 	MetaEntryStack entries = meta_entry_stack_from_file(ctx->arena, filename);
   4716 
   4717 	for (i64 i = 0; i < entries.count; i++) {
   4718 		MetaEntry *e = entries.data + i;
   4719 
   4720 		switch (e->kind) {
   4721 		case MetaEntryKind_Constant:{
   4722 			meta_pack_constant(ctx, e);
   4723 		}break;
   4724 
   4725 		case MetaEntryKind_Emit:{
   4726 			i += meta_pack_emit(ctx, scratch, e, entries.count - i);
   4727 		}break;
   4728 
   4729 		case MetaEntryKind_Embed:{
   4730 			meta_embed(ctx, scratch, e, entries.count - i);
   4731 		}break;
   4732 
   4733 		case MetaEntryKind_Expand:{
   4734 			i += meta_expand(ctx, scratch, e, entries.count - i, 0);
   4735 		}break;
   4736 
   4737 		case MetaEntryKind_Library:
   4738 		case MetaEntryKind_MATLAB:
   4739 		{
   4740 			if (e->kind == MetaEntryKind_Library && ctx->library_entity.value == 0) {
   4741 				ctx->library_entity = meta_intern_entity(ctx, str8("LibraryEntity"), MetaEntityKind_List,
   4742 				                                         meta_root_entity_id(ctx), (MetaLocation){0}, 0);
   4743 			}
   4744 
   4745 			if (e->kind == MetaEntryKind_MATLAB && ctx->matlab_entity.value == 0) {
   4746 				ctx->matlab_entity = meta_intern_entity(ctx, str8("MATLABEntity"), MetaEntityKind_List,
   4747 				                                        meta_root_entity_id(ctx), (MetaLocation){0}, 0);
   4748 			}
   4749 
   4750 			MetaEntityID parent = e->kind == MetaEntryKind_Library ? ctx->library_entity : ctx->matlab_entity;
   4751 			str8 prefix     = e->kind == MetaEntryKind_Library ? str8("Library") : str8("MATLAB");
   4752 			str8 scope_name = str8("");
   4753 			if (e->argument_count > 0)
   4754 				scope_name = meta_entry_argument_expect(e, 0, MetaEntryArgumentKind_String).string;
   4755 			i += meta_pack_references(ctx, e, entries.count - i, parent, scope_name, prefix);
   4756 		}break;
   4757 
   4758 		case MetaEntryKind_ShaderGroup:{
   4759 			i += meta_pack_shader_group(ctx, e, entries.count - i);
   4760 		}break;
   4761 
   4762 		case MetaEntryKind_Enumeration:
   4763 		case MetaEntryKind_Flags:
   4764 		case MetaEntryKind_Struct:
   4765 		case MetaEntryKind_Table:
   4766 		case MetaEntryKind_Union:
   4767 		{
   4768 			i += meta_pack_table_entity(ctx, e, entries.count - i, e->name, meta_root_entity_id(ctx));
   4769 		}break;
   4770 
   4771 		default:
   4772 		{
   4773 			meta_entry_error(e, "invalid @%s() in global scope\n", meta_entry_kind_strings[e->kind]);
   4774 		}break;
   4775 		}
   4776 	}
   4777 
   4778 	// NOTE(rnp): sort enitity ids into sub arrays
   4779 	{
   4780 		assert(ctx->entity_kind_counts[MetaEntityKind_Nil] == 0);
   4781 
   4782 		for EachNonZeroEnumValue(MetaEntityKind, it) {
   4783 			if (ctx->entity_kind_counts[it]) {
   4784 				ctx->entity_kind_ids[it] = push_array(ctx->arena, typeof(*ctx->entity_kind_ids[it]), ctx->entity_kind_counts[it]);
   4785 			}
   4786 		}
   4787 
   4788 		da_count entity_counts[MetaEntityKind_Count] = {0};
   4789 		for (da_count entity = 1; entity < ctx->entities.count; entity++) {
   4790 			MetaEntity *e = ctx->entities.data + entity;
   4791 			da_count index = entity_counts[e->kind]++;
   4792 			ctx->entity_kind_ids[e->kind][index] = da_index(e, &ctx->entities);
   4793 		}
   4794 	}
   4795 
   4796 	// NOTE(rnp): resolve reference entities
   4797 	{
   4798 		for (da_count entity = 0; entity < ctx->entity_kind_counts[MetaEntityKind_Reference]; entity++) {
   4799 			MetaEntity *e = ctx->entities.data + ctx->entity_kind_ids[MetaEntityKind_Reference][entity];
   4800 			da_count reference_id = meta_lookup_string_slow(ctx->entity_names.data, ctx->entity_names.count, e->reference.reference_name);
   4801 			if (reference_id >= 0)
   4802 				e->reference.resolved_id.value = reference_id;
   4803 		}
   4804 
   4805 		b32 error = 0;
   4806 		for (da_count entity = 0; entity < ctx->entity_kind_counts[MetaEntityKind_Reference]; entity++) {
   4807 			MetaEntity          *e = ctx->entities.data + ctx->entity_kind_ids[MetaEntityKind_Reference][entity];
   4808 			MetaEntityReference *r = &e->reference;
   4809 			if (e->reference.resolved_id.value == 0) {
   4810 				meta_compiler_error_message(e->location, "undefined reference%s to '%.*s'\n",
   4811 				                            r->reference_count > 1? "s" : "",
   4812 				                            (i32)r->reference_name.length, r->reference_name.data);
   4813 				if (r->reference_count > 1)
   4814 					meta_compiler_message("  referenced in %d other places\n", r->reference_count - 1);
   4815 				error = 1;
   4816 			}
   4817 		}
   4818 		if (error) meta_error();
   4819 	}
   4820 
   4821 	// NOTE(rnp): extract entities referenced by shaders
   4822 	{
   4823 		for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) {
   4824 			da_count    id = ctx->entity_kind_ids[MetaEntityKind_Shader][shader];
   4825 			MetaShader *s  = &ctx->entities.data[id].shader;
   4826 			for (da_count ref = 0; ref < s->entity_reference_ids.count; ref++) {
   4827 				MetaEntityReference *r = &ctx->entities.data[s->entity_reference_ids.data[ref]].reference;
   4828 				meta_intern_id(ctx, &ctx->shader_entity_references, r->resolved_id.value);
   4829 			}
   4830 		}
   4831 	}
   4832 
   4833 	// NOTE(rnp): finalize struct info
   4834 	{
   4835 		da_count struct_infos_count = 0;
   4836 		for EachElement(meta_struct_entity_kinds, kind_it)
   4837 			struct_infos_count += ctx->entity_kind_counts[meta_struct_entity_kinds[kind_it]];
   4838 
   4839 		ctx->struct_infos_count = struct_infos_count;
   4840 		ctx->struct_infos       = push_array(ctx->arena, MetaStruct, struct_infos_count);
   4841 
   4842 		da_count struct_info_index = 0;
   4843 		for EachElement(meta_struct_entity_kinds, kind_it) {
   4844 			for (da_count it = 0; it < ctx->entity_kind_counts[meta_struct_entity_kinds[kind_it]]; it++) {
   4845 				da_count entity = ctx->entity_kind_ids[meta_struct_entity_kinds[kind_it]][it];
   4846 				MetaEntity *e = ctx->entities.data + entity;
   4847 				e->table.struct_info_id = struct_info_index++;
   4848 
   4849 				MetaStruct *s   = ctx->struct_infos + e->table.struct_info_id;
   4850 				s->name         = ctx->entity_names.data[entity];
   4851 				s->members      = e->table.entries[meta_struct_name_field[kind_it]];
   4852 				s->member_count = e->table.entry_count;
   4853 				s->location     = e->location;
   4854 				s->byte_size    = (u32)-1;
   4855 				s->entity       = (MetaEntityID){entity};
   4856 				if (meta_struct_entity_kinds[kind_it] == MetaEntityKind_Union)
   4857 					s->flags = MetaStructFlag_Union;
   4858 
   4859 				s->member_flags = push_array(ctx->arena, MetaStructMemberFlags, s->member_count);
   4860 				s->elements     = push_array_no_zero(ctx->arena, i32, s->member_count);
   4861 				s->type_ids     = push_array_no_zero(ctx->arena, i32, s->member_count);
   4862 				memory_clear(s->type_ids, -1, sizeof(*s->type_ids) * s->member_count);
   4863 				memory_clear(s->elements, -1, sizeof(*s->elements) * s->member_count);
   4864 			}
   4865 		}
   4866 
   4867 		// NOTE(rnp): resolve types
   4868 		for EachElement(meta_struct_entity_kinds, kind_it) {
   4869 			for (da_count it = 0; it < ctx->entity_kind_counts[meta_struct_entity_kinds[kind_it]]; it++) {
   4870 				da_count entity = ctx->entity_kind_ids[meta_struct_entity_kinds[kind_it]][it];
   4871 				MetaEntity *e = ctx->entities.data + entity;
   4872 				MetaStruct *s = ctx->struct_infos + e->table.struct_info_id;
   4873 
   4874 				str8 *types = e->table.entries[meta_struct_type_field[kind_it]];
   4875 				for EachIndex(s->member_count, member) {
   4876 					s->type_ids[member] = meta_lookup_string_slow(meta_kind_meta_types, MetaKind_Count, types[member]);
   4877 
   4878 					if (s->type_ids[member] == -1 && meta_struct_allow_references[kind_it]) {
   4879 						s->member_flags[member] = MetaStructMemberFlag_ReferenceType;
   4880 						i64 id = meta_lookup_string_slow(ctx->entity_names.data, ctx->entity_names.count, types[member]);
   4881 						if (id >= 0) {
   4882 							MetaEntityKind kind = ctx->entities.data[id].kind;
   4883 							if (!meta_entity_kind_struct_reference_target[kind]) {
   4884 								meta_compiler_error(e->location, "struct '%.*s' references entity '%.*s' which is not a valid struct member\n",
   4885 								                    (i32)s->name.length, s->name.data, (i32)types[member].length, types[member].data);
   4886 							}
   4887 							if (ctx->entities.data[id].kind == MetaEntityKind_Union)
   4888 								s->flags |= MetaStructFlag_ContainsUnion;
   4889 							s->type_ids[member] = id;
   4890 						}
   4891 					}
   4892 
   4893 					if (s->type_ids[member] == -1) {
   4894 						meta_compiler_error(e->location, "struct '%.*s' references undefined type '%.*s'\n",
   4895 						                    (i32)s->name.length, s->name.data, (i32)types[member].length, types[member].data);
   4896 					}
   4897 				}
   4898 			}
   4899 		}
   4900 
   4901 		// NOTE(rnp): resolve element counts
   4902 		for EachElement(meta_struct_entity_kinds, kind_it) {
   4903 			for (da_count it = 0; it < ctx->entity_kind_counts[meta_struct_entity_kinds[kind_it]]; it++) {
   4904 				da_count entity = ctx->entity_kind_ids[meta_struct_entity_kinds[kind_it]][it];
   4905 				MetaEntity *e = ctx->entities.data + entity;
   4906 				MetaStruct *s = ctx->struct_infos + e->table.struct_info_id;
   4907 
   4908 				i32 field = meta_struct_element_field[kind_it];
   4909 				str8 *elements = field >= 0 ? e->table.entries[field] : 0;
   4910 				for EachIndex(s->member_count, member) {
   4911 					if (elements) {
   4912 						NumberConversion integer = integer_from_str8(elements[member]);
   4913 						if (integer.result == NumberConversionResult_Success) {
   4914 							s->elements[member] = integer.U64;
   4915 						} else {
   4916 							str8 ref_name = elements[member];
   4917 							if (ref_name.data[0] == '#') {
   4918 								s->member_flags[member] |= MetaStructMemberFlag_EnumerationCount;
   4919 								str8_chop(&ref_name, 1);
   4920 							} else {
   4921 								s->member_flags[member] |= MetaStructMemberFlag_ReferenceElements;
   4922 							}
   4923 
   4924 							i64 id = meta_lookup_string_slow(ctx->entity_names.data, ctx->entity_names.count, ref_name);
   4925 							if (id >= 0) {
   4926 								MetaEntity *ee = ctx->entities.data + id;
   4927 								b32 valid = ee->kind == MetaEntityKind_Enumeration ||
   4928 								           (ee->kind == MetaEntityKind_Constant && ee->constant.kind == MetaConstantKind_Integer);
   4929 								if (!valid) {
   4930 									// TODO(rnp): point at correct member
   4931 									meta_compiler_error(e->location, "struct '%.*s': element count for field '%.*s'"
   4932 									                                 "references '%.*s' which is not an integer constant\n",
   4933 									                    (i32)s->name.length, s->name.data,
   4934 									                    (i32)s->members[member].length, s->members[member].data,
   4935 									                    (i32)elements[member].length, elements[member].data);
   4936 								}
   4937 								s->elements[member] = id;
   4938 							}
   4939 						}
   4940 					} else {
   4941 						s->elements[member] = 1;
   4942 					}
   4943 
   4944 					if (s->elements[member] == -1) {
   4945 						meta_compiler_error(e->location, "struct '%.*s': element count for field '%.*s' could not be determined\n",
   4946 						                    (i32)s->name.length, s->name.data,
   4947 						                    (i32)s->members[member].length, s->members[member].data);
   4948 					}
   4949 				}
   4950 			}
   4951 		}
   4952 
   4953 		// NOTE(rnp): resolve size
   4954 		// TODO(rnp): depth could be predetermined
   4955 		b32 all_done = 0;
   4956 		for (u32 iterations = 0; !all_done && iterations < 16; iterations++) {
   4957 			for EachIndex(ctx->struct_infos_count, structure) {
   4958 				MetaStruct *s = ctx->struct_infos + structure;
   4959 				u32 size = 0;
   4960 				b32 is_union = (s->flags & MetaStructFlag_Union) != 0;
   4961 				for EachIndex(s->member_count, member) {
   4962 					b32 type_reference = (s->member_flags[member] & MetaStructMemberFlag_ReferenceType) != 0;
   4963 					u32 elements       = meta_struct_member_elements(ctx, s, member);
   4964 
   4965 					u32 member_size = 0;
   4966 					if (type_reference) {
   4967 						MetaEntity *ref = ctx->entities.data + s->type_ids[member];
   4968 						if (ref->kind == MetaEntityKind_Enumeration || ref->kind == MetaEntityKind_Flags) {
   4969 							i64 limit = ref->kind == MetaEntityKind_Flags ? 32 : U32_MAX;
   4970 							if (ref->table.entry_count < limit) member_size = sizeof(u32);
   4971 							else                                member_size = sizeof(u64);
   4972 						} else {
   4973 							MetaStruct *sub_struct = ctx->struct_infos + ref->table.struct_info_id;
   4974 							if (sub_struct->byte_size != (u32)-1) {
   4975 								member_size = sub_struct->byte_size * elements;
   4976 							} else {
   4977 								size = (u32)-1;
   4978 								break;
   4979 							}
   4980 						}
   4981 					} else {
   4982 						member_size = meta_kind_byte_sizes[s->type_ids[member]] * elements;
   4983 					}
   4984 					size = is_union ? Max(size, member_size) : size + member_size;
   4985 				}
   4986 				if (size != (u32)-1)
   4987 					s->byte_size = size;
   4988 			}
   4989 
   4990 			all_done = 1;
   4991 			for EachIndex(ctx->struct_infos_count, structure)
   4992 				all_done &= ctx->struct_infos[structure].byte_size != (u32)-1;
   4993 		}
   4994 
   4995 		if (!all_done) {
   4996 			for EachIndex(ctx->struct_infos_count, structure) {
   4997 				MetaStruct *s = ctx->struct_infos + structure;
   4998 				if (s->byte_size == (u32)-1) {
   4999 					meta_compiler_error(s->location, "storage size for struct '%.*s' could not be determined\n",
   5000 					                    (i32)s->name.length, s->name.data);
   5001 				}
   5002 			}
   5003 		}
   5004 	}
   5005 
   5006 	// NOTE(rnp): finalize base shader nonsense
   5007 	{
   5008 		for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) {
   5009 			MetaEntity *e = ctx->entities.data + ctx->entity_kind_ids[MetaEntityKind_Shader][shader];
   5010 			if (e->shader.files[0].length > 0)
   5011 				ctx->base_shader_count++;
   5012 		}
   5013 
   5014 		ctx->base_shader_ids    = push_array(ctx->arena, da_count, ctx->base_shader_count);
   5015 		ctx->base_shader_id_map = push_array(ctx->arena, da_count, ctx->entity_kind_counts[MetaEntityKind_Shader]);
   5016 
   5017 		da_count base_shader_ids_index = 0;
   5018 		for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) {
   5019 			da_count id = ctx->entity_kind_ids[MetaEntityKind_Shader][shader];
   5020 			if (ctx->entities.data[id].shader.files[0].length > 0)
   5021 				ctx->base_shader_ids[base_shader_ids_index++] = id;
   5022 		}
   5023 
   5024 		// NOTE(rnp): first pass to resolve real shaders
   5025 		for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) {
   5026 			da_count id = ctx->entity_kind_ids[MetaEntityKind_Shader][shader];
   5027 			if (ctx->entities.data[id].shader.files[0].length > 0) {
   5028 				ctx->base_shader_id_map[shader] = meta_lookup_id_slow(ctx->base_shader_ids,
   5029 				                                                      ctx->base_shader_count,
   5030 				                                                      id);
   5031 			} else {
   5032 				ctx->base_shader_id_map[shader] = -1;
   5033 			}
   5034 		}
   5035 
   5036 		// NOTE(rnp): second pass to resolve aliases
   5037 		for (da_count shader = 0; shader < ctx->entity_kind_counts[MetaEntityKind_Shader]; shader++) {
   5038 			da_count id = ctx->entity_kind_ids[MetaEntityKind_Shader][shader];
   5039 			if (ctx->base_shader_id_map[shader] == -1) {
   5040 				if (ctx->entities.data[id].shader.kind == MetaShaderKind_Alias) {
   5041 					ctx->base_shader_id_map[shader] = meta_lookup_id_slow(ctx->base_shader_ids,
   5042 					                                                      ctx->base_shader_count,
   5043 					                                                      ctx->entities.data[id].shader.alias_parent_id.value);
   5044 					assert(ctx->base_shader_id_map[shader] != -1);
   5045 				}
   5046 			}
   5047 		}
   5048 	}
   5049 
   5050 	result->arena = 0;
   5051 	return result;
   5052 }
   5053 
   5054 function b32
   5055 metagen_file_direct(Arena arena, char *filename)
   5056 {
   5057 	MetaContext *ctx = metagen_load_context(&arena, filename);
   5058 	b32 result = ctx && metagen_emit_c_code(ctx, arena);
   5059 	return result;
   5060 }
   5061 
   5062 i32
   5063 main(i32 argc, char *argv[])
   5064 {
   5065 	u64 start_time = os_timer_count();
   5066 	g_argv0 = argv[0];
   5067 
   5068 	b32 result  = 1;
   5069 	Arena arena = os_alloc_arena(MB(8));
   5070 	check_rebuild_self(arena, argc, argv);
   5071 
   5072 	os_make_directory(OUTDIR);
   5073 
   5074 	result &= metagen_file_direct(arena, "assets" OS_PATH_SEPARATOR "assets.meta");
   5075 	result &= metagen_file_direct(arena, "beamformer_core.meta");
   5076 
   5077 	MetaContext *meta = metagen_load_context(&arena, "beamformer.meta");
   5078 	if (!meta) return 1;
   5079 
   5080 	result &= metagen_emit_c_code(meta, arena);
   5081 	result &= metagen_emit_helper_library_header(meta, arena);
   5082 	result &= metagen_emit_matlab_code(meta, arena);
   5083 
   5084 	parse_config(argc, argv);
   5085 
   5086 	if (!build_raylib(arena))  return 1;
   5087 	if (!build_glslang(arena)) return 1;
   5088 
   5089 	/////////////////
   5090 	// lib/tests
   5091 	result &= build_helper_library(arena);
   5092 	if (config.tests) result &= build_tests(arena);
   5093 
   5094 	//////////////////
   5095 	// static portion
   5096 	result &= build_beamformer_main(arena);
   5097 
   5098 	/////////////////////////
   5099 	// hot reloadable portion
   5100 	//
   5101 	// NOTE: this is built after main because on w32 we need to export
   5102 	// gl function pointers for the reloadable portion to import
   5103 	if (config.debug) result &= build_beamformer_as_library(arena);
   5104 
   5105 	if (config.time) {
   5106 		f64 seconds = (f64)(os_timer_count() - start_time) / (f64)os_timer_frequency();
   5107 		build_log_info("took %0.03f [s]", seconds);
   5108 	}
   5109 
   5110 	return result != 1;
   5111 }