Init new repository

This commit is contained in:
Krzosa Karol
2024-04-13 15:29:53 +02:00
commit 5a2e3dcec4
335 changed files with 61571 additions and 0 deletions

View File

@@ -0,0 +1,125 @@
thread_local LC_Map DebugASTMap;
void DebugVerify_WalkAST(LC_ASTWalker *ctx, LC_AST *n) {
bool created = LC_InsertWithoutReplace(&DebugASTMap, n, (void *)(intptr_t)100);
if (!created) LC_ReportASTError(n, "we got to the same ast node twice using ast walker");
// AST_Decl doesn't need to hold types, it's just a standin for LC_Decl
if (LC_IsDecl(n)) {
if (n->type) {
LC_ReportASTError(n, "decl holds type");
}
}
if (LC_IsStmt(n)) {
// These might pop up in a for loop as expressions
if (n->kind == LC_ASTKind_StmtVar || n->kind == LC_ASTKind_StmtConst || n->kind == LC_ASTKind_StmtAssign || n->kind == LC_ASTKind_StmtExpr) {
if (!n->type) {
LC_ReportASTError(n, "typed stmt doesn't hold type");
}
} else {
if (n->type) {
LC_ReportASTError(n, "stmt holds type");
}
}
}
if (LC_IsType(n) && ctx->inside_discarded == 0) {
if (!n->type) {
LC_ReportASTError(n, "typespec doesn't hold type");
}
}
if (LC_IsExpr(n)) {
bool parent_is_const = false;
bool parent_is_builtin = false;
for (int i = ctx->stack.len - 1; i >= 0; i -= 1) {
LC_AST *parent = ctx->stack.data[i];
if (parent->kind == LC_ASTKind_DeclConst || parent->kind == LC_ASTKind_StmtConst || parent->const_val.type) {
parent_is_const = true;
}
if (parent->kind == LC_ASTKind_ExprBuiltin) {
parent_is_builtin = true;
}
}
bool should_have_type = parent_is_builtin == false && ctx->inside_note == 0 && ctx->inside_discarded == 0;
if (should_have_type && !n->type) {
LC_ReportASTError(n, "expression doesn't have type");
}
if (should_have_type && !parent_is_const) {
bool type_is_ok = !LC_IsUntyped(n->type) || n->type == L->tuntypedbool;
if (!type_is_ok) {
LC_ReportASTError(n, "expression is still untyped");
}
}
if (should_have_type && n->kind == LC_ASTKind_ExprIdent) {
if (n->eident.resolved_decl == NULL) {
LC_ReportASTError(n, "identifier doesn't hold resolved declaration");
}
}
}
}
void VerifyCopy_Walk(LC_ASTWalker *ctx, LC_AST *n) {
int *counter = (int *)ctx->user_data;
counter[0] += 1;
}
void VerifyASTCopy(LC_ASTRefList packages) {
for (LC_ASTRef *it = packages.first; it; it = it->next) {
LC_ASTFor(file, it->ast->apackage.ffile) {
LC_TempArena c = LC_BeginTemp(L->arena);
LC_AST *copy = LC_CopyAST(L->arena, file);
int copy_counter = 0;
int original_counter = 0;
{
LC_ASTWalker walker = LC_GetDefaultWalker(L->arena, VerifyCopy_Walk);
walker.visit_notes = walker.visit_discarded = true;
walker.user_data = (void *)&copy_counter;
LC_WalkAST(&walker, copy);
walker.user_data = (void *)&original_counter;
LC_WalkAST(&walker, copy);
}
IO_Assert(copy_counter == original_counter);
IO_Assert(copy_counter > 1);
IO_Assert(original_counter > 1);
LC_EndTemp(c);
}
}
}
void DebugVerifyAST(LC_ASTRefList packages) {
LC_TempArena checkpoint = LC_BeginTemp(L->arena);
DebugASTMap = {L->arena};
LC_MapReserve(&DebugASTMap, 4096);
LC_ASTWalker walker = LC_GetDefaultWalker(L->arena, DebugVerify_WalkAST);
walker.visit_notes = walker.visit_discarded = true;
for (LC_ASTRef *it = packages.first; it; it = it->next) {
LC_WalkAST(&walker, it->ast);
}
LC_WalkAST(&walker, L->tstring->decl->ast);
LC_WalkAST(&walker, L->tany->decl->ast);
for (int i = 0; i < L->ast_count; i += 1) {
LC_AST *it = (LC_AST *)L->ast_arena->memory.data + i;
if (it == L->builtin_package) continue;
if (it->kind == LC_ASTKind_Ignore) continue;
intptr_t value = (intptr_t)LC_MapGetP(&DebugASTMap, it);
if (value != 100) {
LC_ReportASTError(it, "marking verification failed!");
}
}
LC_MapClear(&DebugASTMap);
VerifyASTCopy(packages);
LC_EndTemp(checkpoint);
}

View File

@@ -0,0 +1,166 @@
bool Expand(MA_Arena *arena, S8_List *out, S8_String filepath) {
S8_String c = OS_ReadFile(arena, filepath);
if (c.str == 0) return false;
// S8_String name = S8_SkipToLastSlash(filepath);
// S8_AddF(arena, out, "// %.*s\n", S8_Expand(name));
S8_String path = S8_ChopLastSlash(filepath);
S8_String include = S8_Lit("#include \"");
for (;;) {
int64_t idx = -1;
if (S8_Seek(c, include, 0, &idx)) {
S8_String str_to_add = S8_GetPrefix(c, idx);
S8_AddNode(arena, out, str_to_add);
S8_String save = c;
c = S8_Skip(c, idx + include.len);
S8_String filename = c;
filename.len = 0;
while (filename.str[filename.len] != '"' && filename.len < c.len) {
filename.len += 1;
}
c = S8_Skip(c, filename.len + 1);
S8_String inc_path = S8_Format(arena, "%.*s/%.*s", S8_Expand(path), S8_Expand(filename));
bool dont_expand_and_remove_include = false;
if (S8_EndsWith(inc_path, "lib_compiler.h")) dont_expand_and_remove_include = true;
if (!dont_expand_and_remove_include) {
if (!Expand(arena, out, inc_path)) {
S8_String s = S8_GetPrefix(save, save.len - c.len);
S8_AddNode(arena, out, s);
}
}
} else {
S8_AddNode(arena, out, c);
break;
}
}
return true;
}
S8_String ExpandIncludes(MA_Arena *arena, S8_String filepath) {
S8_List out = S8_MakeEmptyList();
S8_String result = S8_MakeEmpty();
Expand(arena, &out, filepath);
result = S8_Merge(arena, out);
return result;
}
void PackageCompiler() {
MA_Scratch scratch;
S8_String header = ExpandIncludes(scratch, S8_Lit("../src/compiler/lib_compiler.h"));
S8_String impl = ExpandIncludes(scratch, S8_Lit("../src/compiler/lib_compiler.c"));
S8_List list = {0};
S8_AddF(scratch, &list, R"FOO(/*
This is a compiler frontend in a single-header-file library form.
This is a **beta** so things may change between versions!
# How to use
In *one* of your C or C++ files to create the implementation:
```
#define LIB_COMPILER_IMPLEMENTATION
#include "lib_compiler.h"
```
In the rest of your files you can just include it like a regular
header.
# Examples
See online repository for code examples
# Overrides
You can override libc calls, the arena implementation using
preprocessor at compile time, here is an example of how you
would go about it:
```
#define LC_vsnprintf stbsp_vsnprintf
#define LC_MemoryZero(p, size) __builtin_memset(p, 0, size);
#define LC_MemoryCopy(dst, src, size) __builtin_memcpy(dst, src, size)
#define LIB_COMPILER_IMPLEMENTATION
#include "lib_compiler.h"
```
Look for '@override' to find things that can be overridden using macro preprocessor
Look for '@api' to find the main functions that you are supposed to use
Look for '@configurable' to find runtime callbacks you can register and other settings
# License (MIT)
See end of file
*/
)FOO");
S8_AddNode(scratch, &list, header);
S8_AddF(scratch, &list, "\n#ifdef LIB_COMPILER_IMPLEMENTATION\n");
S8_AddNode(scratch, &list, impl);
S8_AddF(scratch, &list, "\n#endif // LIB_COMPILER_IMPLEMENTATION\n");
S8_AddF(scratch, &list, R"FOO(
/*
Copyright (c) 2024 Krzosa Karol
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
)FOO");
S8_String result = S8_Merge(scratch, list);
if (PrintAllFunctions) {
S8_List lines = S8_Split(Perm, result, S8_Lit("\n"), 0);
S8_For(_line, lines) {
S8_String line = _line->string;
if (!S8_Seek(line, "{", S8_FindFlag_MatchFindLast)) {
continue;
}
int64_t idx = 0;
if (S8_Seek(line, ")", 0, &idx)) {
line.len = idx + 1;
}
S8_String proc_name = line;
if (S8_Seek(line, "(", 0, &idx)) {
proc_name.len = idx;
for (int64_t i = proc_name.len - 1; i > 0; i -= 1) {
bool is_ident = CHAR_IsIdent(proc_name.str[i]) || CHAR_IsDigit(proc_name.str[i]);
if (!is_ident) {
proc_name = S8_Skip(proc_name, i + 1);
break;
}
}
}
if (S8_StartsWith(line, "LC_FUNCTION", false)) {
if (PrintAllFunctions) IO_Printf("%.*s;\n", S8_Expand(line));
}
}
}
OS_WriteFile(S8_Lit("../lib_compiler.h"), result);
}

View File

@@ -0,0 +1,37 @@
void ReadmeReadFile(LC_AST *package, LoadedFile *file) {
S8_String result = {};
if (package->apackage.name == LC_ILit("readme_test")) {
S8_String code = *(S8_String *)L->user_data;
file->content = S8_Copy(L->arena, code);
} else {
file->content = OS_ReadFile(L->arena, file->path);
}
}
void TestReadme() {
MA_Scratch scratch;
S8_String readme_path = OS_GetAbsolutePath(scratch, "../README.md");
S8_String readme = OS_ReadFile(scratch, readme_path);
S8_List split = S8_Split(scratch, readme, "```");
S8_For(it, split) {
if (S8_StartsWith(it->string, " odin", S8_IgnoreCase)) {
S8_String code = S8_Skip(it->string, 5);
LC_Lang *lang = LC_LangAlloc();
lang->use_colored_terminal_output = UseColoredIO;
lang->user_data = (void *)&code;
lang->on_file_load = ReadmeReadFile;
LC_LangBegin(lang);
LC_DeclareNote(LC_ILit("do_something"));
LC_DeclareNote(LC_ILit("serialize"));
defer { LC_LangEnd(lang); };
LC_RegisterPackageDir("../pkgs");
LC_Intern name = LC_ILit("readme_test");
LC_AddSingleFilePackage(name, readme_path);
LC_ResolvePackageByName(name);
// LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls();
}
}
}

View File

@@ -0,0 +1,463 @@
enum TestResult {
OK,
Failed_Lex,
Failed_Parse,
Failed_Resolve,
Failed_CCompile,
Failed_Run,
Failed_Condition,
Failed_Package,
};
const char *TestResultString[] = {
"OK",
"Failed_Lex",
"Failed_Parse",
"Failed_Resolve",
"Failed_CCompile",
"Failed_Run",
"Failed_Condition",
"Failed_Package",
};
struct Test {
char *path;
bool msg_found[256];
char *msg_expected[256];
int msg_count;
TestResult expected_result;
bool dont_run;
};
struct Task {
bool do_run;
S8_String exe;
S8_String cmd;
Process process;
};
enum TestKind {
TestKind_File,
TestKind_Dir,
};
struct TestDesc {
TestKind kind;
S8_String absolute_path;
S8_String filename;
S8_String package_name;
bool dont_run;
};
TestResult Compile(S8_String name, S8_String file_to_compile) {
TestResult result = OK;
int errcode = 0;
Array<Task> tasks = {MA_GetAllocator(Perm)};
{
Task tcc = {};
tcc.do_run = UseTCC;
tcc.exe = Fmt("%.*s/tcc_%.*s.exe", S8_Expand(name), S8_Expand(name));
tcc.cmd = Fmt("tcc -std=c99 %.*s " IF_MAC("-lm") IF_LINUX("-lm") " -o %.*s", S8_Expand(file_to_compile), S8_Expand(tcc.exe));
tasks.add(tcc);
Task clang = {};
clang.do_run = UseClang;
clang.exe = Fmt("%.*s/clang_%.*s.exe", S8_Expand(name), S8_Expand(name));
clang.cmd = Fmt("clang -Wno-string-plus-int -g -std=c99 " IF_MAC("-lm") IF_LINUX("-lm") " %.*s -o %.*s", S8_Expand(file_to_compile), S8_Expand(clang.exe));
tasks.add(clang);
Task cl = {};
cl.do_run = UseCL;
cl.exe = Fmt("%.*s/cl_%.*s.exe", S8_Expand(name), S8_Expand(name));
cl.cmd = Fmt("cl %.*s -Zi -std:c11 -nologo /Fd:%.*s.pdb -FC -Fe:%.*s", S8_Expand(file_to_compile), S8_Expand(file_to_compile), S8_Expand(cl.exe));
tasks.add(cl);
Task gcc = {};
gcc.do_run = UseGCC;
gcc.exe = Fmt("%.*s/gcc_%.*s.exe", S8_Expand(name), S8_Expand(name));
gcc.cmd = Fmt("gcc -Wno-string-plus-int -g -std=c99 " IF_MAC("-lm") IF_LINUX("-lm") " %.*s -o %.*s", S8_Expand(file_to_compile), S8_Expand(gcc.exe));
tasks.add(gcc);
}
For(tasks) {
if (!it.do_run) continue;
errcode = Run(it.cmd);
if (errcode != 0) {
result = Failed_CCompile;
goto end_of_test;
}
it.exe = OS_GetAbsolutePath(Perm, it.exe);
}
For(tasks) {
if (!it.do_run) continue;
it.process = RunEx(it.exe);
}
For(tasks) {
if (!it.do_run) continue;
errcode += Wait(&it.process);
}
if (errcode != 0) {
result = Failed_Run;
goto end_of_test;
}
end_of_test:
return result;
}
thread_local Test *T;
thread_local Array<S8_String> Messages;
void HandleOutputMessageGlobalTest(LC_Token *pos, char *str, int len) {
S8_String s = S8_Make(str, len);
s = S8_Trim(s);
if (S8_StartsWith(s, S8_Lit("Executing: "), 0)) return;
if (S8_StartsWith(s, S8_Lit("> "), 0)) return;
if (T && T->msg_expected[0]) {
for (int i = 0; i < 256; i += 1) {
if (T->msg_expected[i] == 0) break;
char *str = T->msg_expected[i];
bool *found = T->msg_found + i;
if (!found[0]) {
S8_String expected = S8_MakeFromChar(str);
if (S8_Seek(s, expected, S8_FindFlag_IgnoreCase, NULL)) {
found[0] = true;
break;
}
}
}
}
if (pos) s = S8_Format(Perm, "%s(%d,%d): error: %.*s\n", (char *)pos->lex->file, pos->line, pos->column, len, str);
Messages.add(S8_Copy(Perm, s));
}
TestResult PrintGlobalTestResult(char *path, TestResult result) {
const char *red = "\033[31m";
const char *reset = "\033[0m";
const char *green = "\033[32m";
if (!UseColoredIO) {
red = "";
reset = "";
green = "";
}
TestResult actual_result = result;
if (result == T->expected_result) {
result = OK;
} else {
result = Failed_Condition;
}
if (T->msg_expected[0]) {
bool all_found = true;
for (int i = 0; i < T->msg_count; i += 1) {
if (T->msg_expected[i] == 0) {
IO_Printf("%sCount of expected messages doesn't match expected messages%s\n", red, reset);
all_found = false;
break;
}
if (!T->msg_found[i]) {
all_found = false;
}
}
if (!all_found) {
result = Failed_Condition;
for (int i = 0; i < 256; i += 1) {
char *str = T->msg_expected[i];
if (str == 0) break;
const char *color = green;
if (!T->msg_found[i]) color = red;
IO_Printf("%sMessage not found - %s%s\n", color, T->msg_expected[i], reset);
}
}
}
if (T->msg_count != Messages.len) {
IO_Printf("%sCount of expected messages: %d doesn't match actual message count: %d%s\n", red, T->msg_count, Messages.len, reset);
result = Failed_Condition;
}
const char *color = green;
if (result != OK) {
color = red;
IO_Printf("%-50s - %s Expected result: %s, got instead: %s%s\n", path, color, TestResultString[T->expected_result], TestResultString[actual_result], reset);
} else {
IO_Printf("%-50s - %s%s%s\n", path, color, TestResultString[result], reset);
}
if (result != OK) {
For(Messages) IO_Printf("%.*s", S8_Expand(it));
}
Messages.reset();
return result;
}
void ParseErrorNotationAndFillGlobalTest(S8_String s) {
S8_String count_lit = S8_Lit("// #expected_error_count: ");
S8_String errlit = S8_Lit("// #error: ");
S8_String resultlit = S8_Lit("// #failed: ");
S8_String dont_run_lit = S8_Lit("// #dont_run");
S8_List errors = S8_Split(Perm, s, S8_Lit("\n"), 0);
S8_For(it, errors) {
if (S8_StartsWith(it->string, errlit, 0)) {
S8_String error = S8_Skip(it->string, errlit.len);
error = S8_Trim(error);
error = S8_Copy(Perm, error);
T->msg_expected[T->msg_count++] = error.str;
}
if (S8_StartsWith(it->string, resultlit, 0)) {
if (S8_Seek(it->string, S8_Lit("parse"), 0, 0)) {
T->expected_result = Failed_Parse;
}
if (S8_Seek(it->string, S8_Lit("resolve"), 0, 0)) {
T->expected_result = Failed_Resolve;
}
if (S8_Seek(it->string, S8_Lit("lex"), 0, 0)) {
T->expected_result = Failed_Lex;
}
if (S8_Seek(it->string, S8_Lit("package"), 0, 0)) {
T->expected_result = Failed_Package;
}
}
if (S8_StartsWith(it->string, count_lit, 0)) {
S8_String count = S8_Skip(it->string, count_lit.len);
count = S8_Copy(Perm, count);
T->msg_count = atoi(count.str);
}
if (S8_StartsWith(it->string, dont_run_lit, 0)) {
T->dont_run = true;
}
}
}
S8_String PushDir(S8_String dir_name) {
S8_String prev = OS_GetWorkingDir(Perm);
OS_MakeDir(dir_name);
OS_SetWorkingDir(dir_name);
return prev;
}
void RunTestFile(TestDesc it) {
LC_Lang *lang = LC_LangAlloc();
lang->on_message = HandleOutputMessageGlobalTest;
lang->use_colored_terminal_output = UseColoredIO;
lang->breakpoint_on_error = BreakpointOnError;
LC_LangBegin(lang);
Test test = {};
test.dont_run = it.dont_run;
T = &test;
TestResult result = OK;
S8_String s = OS_ReadFile(L->arena, it.absolute_path);
if (s.len == 0) {
IO_FatalErrorf("Failed to open file %.*s", S8_Expand(it.absolute_path));
}
ParseErrorNotationAndFillGlobalTest(s);
S8_String code = {};
S8_String out_path = {};
LC_RegisterPackageDir("../../pkgs");
LC_Intern name = LC_ILit("testing");
LC_AddSingleFilePackage(name, it.absolute_path);
L->first_package = name;
LC_ParsePackagesUsingRegistry(name);
if (L->errors) {
result = Failed_Parse;
goto end_of_test;
}
LC_OrderAndResolveTopLevelDecls(name);
LC_ResolveAllProcBodies();
LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls();
if (L->errors) {
result = Failed_Resolve;
goto end_of_test;
}
DebugVerifyAST(L->ordered_packages);
OS_MakeDir(it.filename);
code = LC_GenerateUnityBuild(L->ordered_packages);
out_path = S8_Format(L->arena, "%.*s/%.*s.c", S8_Expand(it.filename), S8_Expand(it.filename));
OS_WriteFile(out_path, code);
{
LC_BeginStringGen(L->arena);
LC_GenLCNode(L->ordered_packages.first->ast);
S8_String c = LC_EndStringGen(L->arena);
S8_String p = S8_Format(L->arena, "%.*s/%.*s.generated.lc", S8_Expand(it.filename), S8_Expand(it.filename));
p = OS_GetAbsolutePath(L->arena, p);
OS_WriteFile(p, c);
LC_ParseFile(NULL, p.str, c.str, 0);
}
if (!T->dont_run) result = Compile(it.filename, out_path);
end_of_test:
result = PrintGlobalTestResult(it.filename.str, result);
LC_LangEnd(lang);
}
void RunTestDir(TestDesc it, S8_String package_name) {
LC_Lang *lang = LC_LangAlloc();
lang->on_message = HandleOutputMessageGlobalTest;
lang->use_colored_terminal_output = UseColoredIO;
lang->breakpoint_on_error = BreakpointOnError;
LC_LangBegin(lang);
Test test = {};
T = &test;
TestResult result = OK;
S8_String path_to_test_file = S8_Format(L->arena, "%.*s/test.txt", S8_Expand(it.absolute_path));
S8_String error_notation = OS_ReadFile(L->arena, path_to_test_file);
if (error_notation.str) ParseErrorNotationAndFillGlobalTest(error_notation);
LC_Intern first_package = LC_ILit(package_name.str);
LC_RegisterPackageDir(it.absolute_path.str);
LC_RegisterPackageDir("../../pkgs");
LC_ASTRefList packages = LC_ResolvePackageByName(first_package);
LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls();
if (L->errors != 0) result = Failed_Package;
if (result == OK && T->expected_result == OK) {
DebugVerifyAST(packages);
OS_MakeDir(it.filename);
S8_String code = LC_GenerateUnityBuild(packages);
S8_String out_path = S8_Format(L->arena, "%.*s/%.*s.c", S8_Expand(it.filename), S8_Expand(it.filename));
OS_WriteFile(out_path, code);
if (!T->dont_run) result = Compile(it.filename, out_path);
}
result = PrintGlobalTestResult(it.filename.str, result);
LC_LangEnd(lang);
}
void TestThread(Array<TestDesc> tests) {
MA_InitScratch();
For(tests) {
if (it.kind == TestKind_Dir) {
RunTestDir(it, it.package_name);
} else if (it.kind == TestKind_File) {
RunTestFile(it);
}
}
}
bool ShouldSkip(S8_String filename, bool is_directory = false) {
if (TestsToRun.first) {
bool found = false;
S8_For(match, TestsToRun) {
if (match->string == filename) {
found = true;
break;
}
}
if (!found) {
return true;
}
}
if (is_directory) {
if (S8_StartsWith(filename, "example_", true) || S8_StartsWith(filename, "compilation", true))
return true;
}
return false;
}
bool ShouldRun(S8_String filename) {
bool result = !ShouldSkip(filename, false);
return result;
}
void RunTests() {
MA_Scratch scratch;
int test_count = 0;
S8_String working_dir = PushDir("test_result");
Array<TestDesc> tests = {MA_GetAllocator(scratch)};
for (OS_FileIter it = OS_IterateFiles(scratch, S8_Lit("../../tests")); OS_IsValid(it); OS_Advance(&it)) {
if (ShouldSkip(it.filename, it.is_directory)) continue;
TestDesc desc = {it.is_directory ? TestKind_Dir : TestKind_File, it.absolute_path, it.filename, "main"};
tests.add(desc);
}
test_count = tests.len;
const int thread_count = ThreadCount;
Array<Array<TestDesc>> work = {MA_GetAllocator(scratch)};
Array<std::thread *> threads = {MA_GetAllocator(scratch)};
for (int i = 0; i < thread_count; i += 1) {
work.add({MA_GetAllocator(scratch)});
}
int i = 0;
For(tests) {
work[i].add(it);
i = (i + 1) % thread_count;
}
For(work) threads.add(new std::thread(TestThread, it));
For(threads) it->join();
//
// Non automated tests
//
if (ShouldRun("example_ui_and_hot_reloading")) {
test_count += 1;
TestResult result = OK;
Test t = {};
t.path = OS_GetAbsolutePath(scratch, "../../tests/example_ui_and_hot_reloading/").str;
t.expected_result = OK;
T = &t;
LC_Lang *lang = LC_LangAlloc();
lang->on_message = HandleOutputMessageGlobalTest;
lang->use_colored_terminal_output = UseColoredIO;
lang->breakpoint_on_error = BreakpointOnError;
LC_LangBegin(lang);
LC_RegisterPackageDir(T->path);
LC_ASTRefList dll_packages = LC_ResolvePackageByName(LC_ILit("dll"));
LC_ASTRefList exe_packages = LC_ResolvePackageByName(LC_ILit("exe"));
result = L->errors >= 1 ? Failed_Package : OK;
if (result == OK) {
DebugVerifyAST(dll_packages);
// DebugVerifyAST(exe_packages);
S8_String prev = PushDir("example_ui_and_hot_reloading");
S8_String code = LC_GenerateUnityBuild(exe_packages);
OS_WriteFile("example_ui_and_hot_reloading", code);
OS_SetWorkingDir(prev);
}
LC_LangEnd(lang);
PrintGlobalTestResult("example_ui_and_hot_reloading", result);
#if 0
OS_SystemF("clang unity_exe.c -o platform.exe -g -O0 -I\"../..\"");
OS_SystemF("clang unity_dll.c -o game.dll -O0 -shared -g -I\"../..\" -Wl,-export:APP_Update");
#endif
}
OS_SetWorkingDir(working_dir);
IO_Printf("Total test count = %d\n", test_count);
}