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);
}

127
src/build_tool/cache.cpp Normal file
View File

@@ -0,0 +1,127 @@
#define SRC_CACHE_ENTRY_COUNT 1024
struct SRC_CacheEntry {
uint64_t filepath_hash;
uint64_t file_hash;
uint64_t includes_hash;
uint64_t total_hash;
};
struct SRC_Cache {
int entry_cap;
int entry_len;
SRC_CacheEntry entries[SRC_CACHE_ENTRY_COUNT];
};
double SRC_Time;
SRC_Cache *SRC_InMemoryCache;
SRC_Cache *SRC_FromFileCache;
S8_String SRC_CacheFilename;
CL_SearchPaths SRC_SearchPaths = {}; // @todo;
void SRC_InitCache(MA_Arena *arena, S8_String cachefilename) {
SRC_CacheFilename = cachefilename;
SRC_InMemoryCache = MA_PushStruct(arena, SRC_Cache);
SRC_InMemoryCache->entry_cap = SRC_CACHE_ENTRY_COUNT;
SRC_FromFileCache = MA_PushStruct(arena, SRC_Cache);
SRC_FromFileCache->entry_cap = SRC_CACHE_ENTRY_COUNT;
S8_String cache = OS_ReadFile(arena, SRC_CacheFilename);
if (cache.len) MA_MemoryCopy(SRC_FromFileCache, cache.str, cache.len);
}
void SRC_SaveCache() {
S8_String dump = S8_Make((char *)SRC_InMemoryCache, sizeof(SRC_Cache));
OS_WriteFile(SRC_CacheFilename, dump);
}
SRC_CacheEntry *SRC_AddHash(uint64_t filepath, uint64_t file, uint64_t includes) {
IO_Assert(SRC_InMemoryCache->entry_len + 1 < SRC_InMemoryCache->entry_cap);
SRC_CacheEntry *result = SRC_InMemoryCache->entries + SRC_InMemoryCache->entry_len++;
result->filepath_hash = filepath;
result->file_hash = file;
result->includes_hash = includes;
result->total_hash = HashBytes(result, sizeof(uint64_t) * 3);
return result;
}
SRC_CacheEntry *SRC_FindCache(SRC_Cache *cache, uint64_t filepath_hash) {
for (int cache_i = 0; cache_i < cache->entry_len; cache_i += 1) {
SRC_CacheEntry *it = cache->entries + cache_i;
if (it->filepath_hash == filepath_hash) {
return it;
}
}
return 0;
}
SRC_CacheEntry *SRC_HashFile(S8_String file, char *parent_file) {
char *resolved_file = CL_ResolveFilepath(Perm, &SRC_SearchPaths, file.str, parent_file, false);
if (!resolved_file) {
IO_Printf("Failed to resolve file: %.*s\n", S8_Expand(file));
return 0;
}
uint64_t filepath_hash = HashBytes(resolved_file, S8_Length(resolved_file));
SRC_CacheEntry *entry = SRC_FindCache(SRC_InMemoryCache, filepath_hash);
if (entry) return entry;
S8_String filecontent = OS_ReadFile(Perm, S8_MakeFromChar(resolved_file));
IO_Assert(filecontent.str);
uint64_t file_hash = HashBytes(filecontent.str, filecontent.len);
uint64_t includes_hash = 13;
CL_Lexer lexer = CL_Begin(Perm, filecontent.str, resolved_file);
lexer.select_includes = true;
for (CL_Token token = CL_Next(&lexer); token.kind != CL_EOF; token = CL_Next(&lexer)) {
if (token.is_system_include) continue;
S8_String file_it = S8_MakeFromChar(token.string_literal);
SRC_CacheEntry *cache = SRC_HashFile(file_it, resolved_file);
if (!cache) {
// error was reported already IO_Printf("Missing cache for: %.*s\n", S8_Expand(file_it));
continue;
}
includes_hash = HashMix(includes_hash, cache->total_hash);
}
SRC_CacheEntry *result = SRC_AddHash(filepath_hash, file_hash, includes_hash);
return result;
}
bool SRC_WasModified(S8_String file, S8_String artifact_path) {
double time_start = OS_GetTime();
if (OS_FileExists(file) == false) {
IO_Printf("FAILED File doesnt exist: %.*s\n", S8_Expand(file));
exit(0);
}
if (OS_IsAbsolute(file) == false) {
file = OS_GetAbsolutePath(Perm, file);
}
S8_String without_ext = S8_ChopLastPeriod(file);
S8_String name_only = S8_SkipToLastSlash(without_ext);
if (artifact_path.len == 0) artifact_path = S8_Format(Perm, "%.*s.%s", S8_Expand(name_only), IF_WINDOWS_ELSE("obj", "o"));
bool modified = OS_FileExists(artifact_path) == false;
SRC_CacheEntry *in_memory = SRC_HashFile(file, 0);
IO_Assert(in_memory);
if (modified == false) {
SRC_CacheEntry *from_file = SRC_FindCache(SRC_FromFileCache, in_memory->filepath_hash);
if (from_file == 0 || (in_memory->total_hash != from_file->total_hash)) {
modified = true;
}
}
SRC_Time = SRC_Time + (OS_GetTime() - time_start);
return modified;
}

View File

@@ -0,0 +1,116 @@
S8_String Fmt(const char *str, ...) S8__PrintfFormat(1, 2);
Array<S8_String> operator+(Array<S8_String> a, Array<S8_String> b) {
Array<S8_String> c = a.copy(MA_GetAllocator(Perm));
c.add_array(b);
return c;
}
Array<S8_String> operator+(Array<S8_String> a, S8_String b) {
Array<S8_String> c = a.copy(MA_GetAllocator(Perm));
c.add(b);
return c;
}
Array<S8_String> operator+(S8_String a, Array<S8_String> b) {
Array<S8_String> c = b.copy(MA_GetAllocator(Perm));
c.insert(a, 0);
return c;
}
Array<S8_String> operator+(S8_String a, S8_String b) {
Array<S8_String> c = {MA_GetAllocator(Perm)};
c.add(a);
c.add(b);
return c;
}
Array<S8_String> &operator+=(Array<S8_String> &a, Array<S8_String> b) {
a.add_array(b);
return a;
}
Array<S8_String> &operator+=(Array<S8_String> &a, S8_String s) {
a.add(s);
return a;
}
//@todo: split on any whitespace instead!
Array<S8_String> Split(S8_String s, S8_String sep = " ") {
S8_List list = S8_Split(Perm, s, sep, 0);
Array<S8_String> result = {MA_GetAllocator(Perm)};
S8_For(it, list) result.add(it->string);
return result;
}
S8_String Merge(MA_Arena *arena, Array<S8_String> list, S8_String separator = " "_s) {
int64_t char_count = 0;
For(list) char_count += it.len;
if (char_count == 0) return {};
int64_t node_count = list.len;
int64_t base_size = (char_count + 1);
int64_t sep_size = (node_count - 1) * separator.len;
int64_t size = base_size + sep_size;
char *buff = (char *)MA_PushSize(arena, sizeof(char) * (size + 1));
S8_String string = S8_Make(buff, 0);
For(list) {
IO_Assert(string.len + it.len <= size);
MA_MemoryCopy(string.str + string.len, it.str, it.len);
string.len += it.len;
if (!list.is_last(it)) {
MA_MemoryCopy(string.str + string.len, separator.str, separator.len);
string.len += separator.len;
}
}
IO_Assert(string.len == size - 1);
string.str[size] = 0;
return string;
}
S8_String Merge(Array<S8_String> list, S8_String separator = " ") {
return Merge(Perm, list, separator);
}
S8_String Fmt(const char *str, ...) {
S8_FORMAT(Perm, str, str_fmt);
return str_fmt;
}
Array<S8_String> ListDir(char *dir) {
Array<S8_String> result = {MA_GetAllocator(Perm)};
for (OS_FileIter it = OS_IterateFiles(Perm, S8_MakeFromChar(dir)); OS_IsValid(it); OS_Advance(&it)) {
result.add(S8_Copy(Perm, it.absolute_path));
}
return result;
}
Array<S8_String> CMD_Make(char **argv, int argc) {
Array<S8_String> result = {MA_GetAllocator(Perm)};
for (int i = 1; i < argc; i += 1) {
S8_String it = S8_MakeFromChar(argv[i]);
result.add(it);
}
return result;
}
S8_String CMD_Get(Array<S8_String> &cmd, S8_String name, S8_String default_value = "") {
For(cmd) {
int64_t idx = 0;
if (S8_Seek(it, "="_s, 0, &idx)) {
S8_String key = S8_GetPrefix(it, idx);
S8_String value = S8_Skip(it, idx + 1);
if (key == name) {
return value;
}
}
}
return default_value;
}
bool CMD_Match(Array<S8_String> &cmd, S8_String name) {
For(cmd) {
if (it == name) return true;
}
return false;
}

View File

@@ -0,0 +1,55 @@
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#include "../core/core.c"
#define CL_Allocator MA_Arena *
#define CL_Allocate(a, s) MA_PushSizeNonZeroed(a, s)
#define CL_ASSERT IO_Assert
#define CL_VSNPRINTF stbsp_vsnprintf
#define CL_SNPRINTF stbsp_snprintf
#define AND_CL_STRING_TERMINATE_ON_NEW_LINE
#include "../standalone_libraries/clexer.c"
thread_local MA_Arena PernamentArena;
thread_local MA_Arena *Perm = &PernamentArena;
#include "cache.cpp"
#include "easy_strings.cpp"
#include "process.cpp"
S8_String CL_Flags = "/MP /Zi /FC /WX /W3 /wd4200 /diagnostics:column /nologo -D_CRT_SECURE_NO_WARNINGS /GF /Gm- /Oi";
S8_String CL_Link = "/link /incremental:no";
S8_String CL_StdOff = "/GR- /EHa-";
S8_String CL_StdOn = "/EHsc";
S8_String CL_Debug = "-Od -D_DEBUG -fsanitize=address -RTC1";
S8_String CL_Release = "-O2 -MT -DNDEBUG -GL";
S8_String CL_ReleaseLink = "-opt:ref -opt:icf";
/*
/FC = Print full paths in diagnostics
/Gm- = Old feature, 'minimal compilation', in case it's not off by default
/GF = Pools strings as read-only. If you try to modify strings under /GF, an application error occurs.
/Oi = Replaces some function calls with intrinsic
/MP = Multithreaded compilation
/GR- = Disable runtime type information
/EHa- = Disable exceptions
/EHsc = Enable exceptions
/MT = Link static libc. The 'd' means debug version
/MD = Link dynamic libc. The 'd' means debug version
/GL = Whole program optimization
/RTC1 = runtime error checks
/opt:ref = eliminates functions and data that are never referenced
/opt:icf = eliminates redundant 'COMDAT's
*/
S8_String Clang_Flags = "-fdiagnostics-absolute-paths -Wno-writable-strings";
S8_String Clang_NoStd = "-fno-exceptions";
S8_String Clang_Debug = "-fsanitize=address -g";
/*
-std=c++11
*/
S8_String GCC_Flags = "-Wno-write-strings";
S8_String GCC_NoStd = "-fno-exceptions";
S8_String GCC_Debug = "-fsanitize=address -g";

90
src/build_tool/main.cpp Normal file
View File

@@ -0,0 +1,90 @@
#include "library.cpp"
int main(int argument_count, char **arguments) {
MA_InitScratch();
OS_MakeDir(S8_Lit("build"));
OS_SetWorkingDir(S8_Lit("build"));
S8_String working_dir = OS_GetWorkingDir(Perm);
IO_Printf("WORKING DIR: %.*s\n", S8_Expand(working_dir));
Array<S8_String> cmd = CMD_Make(arguments, argument_count);
S8_String cc = CMD_Get(cmd, "cc", IF_WINDOWS("cl") IF_MAC("clang") IF_LINUX("gcc"));
// Search for build file in the project directory
S8_String build_file = {};
{
for (OS_FileIter it = OS_IterateFiles(Perm, S8_Lit("..")); OS_IsValid(it); OS_Advance(&it)) {
if (S8_Seek(it.filename, S8_Lit("build_file.c"), S8_IgnoreCase)) {
build_file = it.absolute_path;
}
}
if (build_file.str == 0) {
IO_Printf("Couldnt find build file in current dir: %.*s, exiting ... \n", S8_Expand(working_dir));
IO_Printf("- Proper build file contains 'build_file.c' in it's name\n");
IO_Printf("- Alternative compiler can be chosen like this: bld cc=clang\n");
return 0;
}
}
SRC_InitCache(Perm, "build_tool.cache");
S8_String name_no_ext = S8_GetNameNoExt(build_file);
S8_String exe_name = S8_Format(Perm, "%.*s.exe", S8_Expand(name_no_ext));
// Compile the build file only if code changed
if (SRC_WasModified(build_file, exe_name)) {
double time = OS_GetTime();
int result = 0;
if (cc == "cl") {
Array<S8_String> flags = {MA_GetAllocator(Perm)};
flags += "-nologo -Zi";
flags += "-WX -W3 -wd4200 -diagnostics:column";
flags += Fmt("/Fe:%.*s /Fd:%.*s.pdb", S8_Expand(exe_name), S8_Expand(exe_name));
result = Run(cc + build_file + flags);
} else if (cc == "clang") {
Array<S8_String> flags = {MA_GetAllocator(Perm)};
cc = "clang++";
flags += "-std=c++11 -g";
flags += "-fdiagnostics-absolute-paths";
flags += "-Wno-writable-strings";
flags += "-lm";
flags += Fmt("-o %.*s", S8_Expand(exe_name));
result = Run(cc + build_file + flags);
} else {
IO_Assert(cc == "gcc");
cc = "g++";
Array<S8_String> flags = {MA_GetAllocator(Perm)};
flags += "-std=c++11 -g";
flags += "-Wno-write-strings";
flags += "-lm";
flags += Fmt("-o %.*s", S8_Expand(exe_name));
result = Run(cc + build_file + flags);
}
if (result != 0) {
IO_Printf("FAILED compilation of the build file!\n");
OS_DeleteFile("build_tool.cache");
return 1;
}
time = OS_GetTime() - time;
IO_Printf("TIME Build file compilation: %f\n", time);
}
// Run the build file
double time = OS_GetTime();
if (build_file.str) {
exe_name = OS_GetAbsolutePath(Perm, exe_name);
int result = Run(exe_name + cmd);
if (result != 0) {
IO_Printf("FAILED execution of the build file!\n");
OS_DeleteFile("build_tool.cache");
return 1;
}
}
SRC_SaveCache();
time = OS_GetTime() - time;
IO_Printf("TIME total build file execution: %f\n", time);
}

153
src/build_tool/process.cpp Normal file
View File

@@ -0,0 +1,153 @@
struct Process {
bool is_valid;
char platform[32];
};
#if OS_WINDOWS
Process RunEx(S8_String in_cmd) {
MA_Scratch scratch;
wchar_t *application_name = NULL;
wchar_t *cmd = S8_ToWidechar(scratch, in_cmd);
BOOL inherit_handles = FALSE;
DWORD creation_flags = 0;
void *enviroment = NULL;
wchar_t *working_dir = NULL;
STARTUPINFOW startup_info = {};
startup_info.cb = sizeof(STARTUPINFOW);
Process result = {};
IO_Assert(sizeof(result.platform) >= sizeof(PROCESS_INFORMATION));
PROCESS_INFORMATION *process_info = (PROCESS_INFORMATION *)result.platform;
BOOL success = CreateProcessW(application_name, cmd, NULL, NULL, inherit_handles, creation_flags, enviroment, working_dir, &startup_info, process_info);
result.is_valid = true;
if (!success) {
result.is_valid = false;
LPVOID lpMsgBuf;
DWORD dw = GetLastError();
FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL);
LocalFree(lpMsgBuf);
IO_FatalErrorf("Failed to create process \ncmd: %.*s\nwindows_message: %s", S8_Expand(in_cmd), lpMsgBuf);
}
return result;
}
int Wait(Process *process) {
IO_Assert(process->is_valid);
PROCESS_INFORMATION *pi = (PROCESS_INFORMATION *)process->platform;
WaitForSingleObject(pi->hProcess, INFINITE);
DWORD exit_code;
BOOL err = GetExitCodeProcess(pi->hProcess, &exit_code);
IO_Assert(err != 0);
CloseHandle(pi->hProcess);
CloseHandle(pi->hThread);
process[0] = {};
return (int)exit_code;
}
#else
#include <spawn.h>
#include <sys/wait.h>
struct TH_UnixProcess {
pid_t pid;
};
extern char **environ;
Process RunEx(S8_String cmd) {
MA_Scratch scratch;
Process result = {};
IO_Assert(sizeof(result.platform) >= sizeof(TH_UnixProcess));
TH_UnixProcess *u = (TH_UnixProcess *)result.platform;
S8_String exec_file = cmd;
S8_String argv = "";
int64_t pos;
if (S8_Seek(cmd, S8_Lit(" "), 0, &pos)) {
exec_file = S8_GetPrefix(cmd, pos);
argv = S8_Skip(cmd, pos + 1);
}
exec_file = S8_Copy(scratch, exec_file);
// Split string on whitespace and conform with argv format
Array<char *> args = {MA_GetAllocator(scratch)};
{
args.add(exec_file.str);
for (int64_t i = 0; i < argv.len;) {
while (i < argv.len && CHAR_IsWhitespace(argv.str[i])) {
i += 1;
}
S8_String word = {argv.str + i, 0};
while (i < argv.len && !CHAR_IsWhitespace(argv.str[i])) {
word.len += 1;
i += 1;
}
word = S8_Copy(scratch, word);
args.add(word.str);
}
args.add(NULL);
}
int err = posix_spawnp(&u->pid, exec_file.str, NULL, NULL, args.data, environ);
if (err == 0) {
result.is_valid = true;
} else {
perror("posix_spawnp error");
IO_FatalErrorf("Failed to create process, cmd: %.*s", S8_Expand(cmd));
}
return result;
}
int Wait(Process *process) {
if (!process->is_valid) return 1;
TH_UnixProcess *u = (TH_UnixProcess *)process->platform;
int status = 0;
int pid = waitpid(u->pid, &status, 0);
IO_Assert(pid != -1);
int result = 0;
if (WIFEXITED(status)) {
result = WEXITSTATUS(status);
} else {
result = 1;
}
process[0] = {};
return result;
}
#endif
Process RunEx(Array<S8_String> s) {
S8_String cmd = Merge(s);
Process proc = RunEx(cmd);
return proc;
}
Process RunEx(Array<S8_String> s, S8_String process_start_dir) {
OS_MakeDir(process_start_dir);
S8_String working_dir = OS_GetWorkingDir(Perm);
OS_SetWorkingDir(process_start_dir);
S8_String cmd = Merge(s);
Process proc = RunEx(cmd);
OS_SetWorkingDir(working_dir);
return proc;
}
int Run(S8_String cmd) {
Process process = RunEx(cmd);
int result = Wait(&process);
return result;
}
int Run(Array<S8_String> cmd) {
S8_String cmds = Merge(cmd);
int result = Run(cmds);
return result;
}

138
src/compiler/arena.c Normal file
View File

@@ -0,0 +1,138 @@
#if defined(LC_USE_ADDRESS_SANITIZER)
#include <sanitizer/asan_interface.h>
#endif
#if !defined(ASAN_POISON_MEMORY_REGION)
#define LC_ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
#define LC_ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
#else
#define LC_ASAN_POISON_MEMORY_REGION(addr, size) ASAN_POISON_MEMORY_REGION(addr, size)
#define LC_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size)
#endif
#define LC_DEFAULT_RESERVE_SIZE LC_GIB(1)
#define LC_DEFAULT_ALIGNMENT 8
#define LC_COMMIT_ADD_SIZE LC_MIB(4)
LC_FUNCTION uint8_t *LC_V_AdvanceCommit(LC_VMemory *m, size_t *commit_size, size_t page_size) {
size_t aligned_up_commit = LC_AlignUp(*commit_size, page_size);
size_t to_be_total_commited_size = aligned_up_commit + m->commit;
size_t to_be_total_commited_size_clamped_to_reserve = LC_CLAMP_TOP(to_be_total_commited_size, m->reserve);
size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit;
LC_Assertf(adjusted_to_boundary_commit, "Reached the virtual memory reserved boundary");
*commit_size = adjusted_to_boundary_commit;
if (adjusted_to_boundary_commit == 0) {
return 0;
}
uint8_t *result = m->data + m->commit;
return result;
}
LC_FUNCTION void LC_PopToPos(LC_Arena *arena, size_t pos) {
LC_Assertf(arena->len >= arena->base_len, "Bug: arena->len shouldn't ever be smaller then arena->base_len");
pos = LC_CLAMP(pos, arena->base_len, arena->len);
size_t size = arena->len - pos;
arena->len = pos;
LC_ASAN_POISON_MEMORY_REGION(arena->memory.data + arena->len, size);
}
LC_FUNCTION void LC_PopSize(LC_Arena *arena, size_t size) {
LC_PopToPos(arena, arena->len - size);
}
LC_FUNCTION void LC_DeallocateArena(LC_Arena *arena) {
LC_VDeallocate(&arena->memory);
}
LC_FUNCTION void LC_ResetArena(LC_Arena *arena) {
LC_PopToPos(arena, 0);
}
LC_FUNCTION size_t LC__AlignLen(LC_Arena *a) {
size_t align_offset = a->alignment ? LC_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
size_t aligned = a->len + align_offset;
return aligned;
}
LC_FUNCTION void *LC__PushSizeNonZeroed(LC_Arena *a, size_t size) {
size_t align_offset = a->alignment ? LC_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
size_t aligned_len = a->len + align_offset;
size_t size_with_alignment = size + align_offset;
if (a->len + size_with_alignment > a->memory.commit) {
if (a->memory.reserve == 0) {
LC_Assertf(0, "Pushing on uninitialized arena with zero initialization turned off");
}
bool result = LC_VCommit(&a->memory, size_with_alignment + LC_COMMIT_ADD_SIZE);
LC_Assertf(result, "%s(%d): Failed to commit memory more memory! reserve: %zu commit: %zu len: %zu size_with_alignment: %zu", LC_BeginTempdSourceLoc.file, LC_BeginTempdSourceLoc.line, a->memory.reserve, a->memory.commit, a->len, size_with_alignment);
(void)result;
}
uint8_t *result = a->memory.data + aligned_len;
a->len += size_with_alignment;
LC_Assertf(a->len <= a->memory.commit, "%s(%d): Reached commit boundary! reserve: %zu commit: %zu len: %zu base_len: %zu alignment: %d size_with_alignment: %zu", LC_BeginTempdSourceLoc.file, LC_BeginTempdSourceLoc.line, a->memory.reserve, a->memory.commit, a->len, a->base_len, a->alignment, size_with_alignment);
LC_ASAN_UNPOISON_MEMORY_REGION(result, size);
return (void *)result;
}
LC_FUNCTION void *LC__PushSize(LC_Arena *arena, size_t size) {
void *result = LC__PushSizeNonZeroed(arena, size);
LC_MemoryZero(result, size);
return result;
}
LC_FUNCTION LC_Arena LC_PushArena(LC_Arena *arena, size_t size) {
LC_Arena result;
LC_MemoryZero(&result, sizeof(result));
result.memory.data = LC_PushArrayNonZeroed(arena, uint8_t, size);
result.memory.commit = size;
result.memory.reserve = size;
result.alignment = arena->alignment;
return result;
}
LC_FUNCTION LC_Arena *LC_PushArenaP(LC_Arena *arena, size_t size) {
LC_Arena *result = LC_PushStruct(arena, LC_Arena);
*result = LC_PushArena(arena, size);
return result;
}
LC_FUNCTION void LC_InitArenaEx(LC_Arena *a, size_t reserve) {
a->memory = LC_VReserve(reserve);
LC_ASAN_POISON_MEMORY_REGION(a->memory.data, a->memory.reserve);
a->alignment = LC_DEFAULT_ALIGNMENT;
}
LC_FUNCTION void LC_InitArena(LC_Arena *a) {
LC_InitArenaEx(a, LC_DEFAULT_RESERVE_SIZE);
}
LC_FUNCTION LC_Arena *LC_BootstrapArena(void) {
LC_Arena bootstrap_arena = {0};
LC_InitArena(&bootstrap_arena);
LC_Arena *arena = LC_PushStruct(&bootstrap_arena, LC_Arena);
*arena = bootstrap_arena;
arena->base_len = arena->len;
return arena;
}
LC_FUNCTION void LC_InitArenaFromBuffer(LC_Arena *arena, void *buffer, size_t size) {
arena->memory.data = (uint8_t *)buffer;
arena->memory.commit = size;
arena->memory.reserve = size;
arena->alignment = LC_DEFAULT_ALIGNMENT;
LC_ASAN_POISON_MEMORY_REGION(arena->memory.data, arena->memory.reserve);
}
LC_FUNCTION LC_TempArena LC_BeginTemp(LC_Arena *arena) {
LC_TempArena result;
result.pos = arena->len;
result.arena = arena;
return result;
}
LC_FUNCTION void LC_EndTemp(LC_TempArena checkpoint) {
LC_PopToPos(checkpoint.arena, checkpoint.pos);
}

167
src/compiler/ast.c Normal file
View File

@@ -0,0 +1,167 @@
LC_FUNCTION LC_AST *LC_CreateAST(LC_Token *pos, LC_ASTKind kind) {
LC_AST *n = LC_PushStruct(L->ast_arena, LC_AST);
n->id = ++L->ast_count;
n->kind = kind;
n->pos = pos;
if (L->parser == &L->quick_parser) n->pos = &L->BuiltinToken;
return n;
}
LC_FUNCTION LC_AST *LC_CreateUnary(LC_Token *pos, LC_TokenKind op, LC_AST *expr) {
LC_AST *r = LC_CreateAST(pos, LC_ASTKind_ExprUnary);
r->eunary.op = op;
r->eunary.expr = expr;
return r;
}
LC_FUNCTION LC_AST *LC_CreateBinary(LC_Token *pos, LC_AST *left, LC_AST *right, LC_TokenKind op) {
LC_AST *r = LC_CreateAST(pos, LC_ASTKind_ExprBinary);
r->ebinary.op = op;
r->ebinary.left = left;
r->ebinary.right = right;
return r;
}
LC_FUNCTION LC_AST *LC_CreateIndex(LC_Token *pos, LC_AST *left, LC_AST *index) {
LC_AST *r = LC_CreateAST(pos, LC_ASTKind_ExprIndex);
r->eindex.index = index;
r->eindex.base = left;
return r;
}
LC_FUNCTION void LC_SetPointerSizeAndAlign(int size, int align) {
L->pointer_align = align;
L->pointer_size = size;
if (L->tpvoid) {
L->tpvoid->size = size;
L->tpvoid->align = align;
}
if (L->tpchar) {
L->tpchar->size = size;
L->tpchar->align = align;
}
}
LC_FUNCTION LC_Type *LC_CreateType(LC_TypeKind kind) {
LC_Type *r = LC_PushStruct(L->type_arena, LC_Type);
L->type_count += 1;
r->kind = kind;
r->id = ++L->typeids;
if (r->kind == LC_TypeKind_Proc || r->kind == LC_TypeKind_Pointer) {
r->size = L->pointer_size;
r->align = L->pointer_align;
r->is_unsigned = true;
}
return r;
}
LC_FUNCTION LC_Type *LC_CreateTypedef(LC_Decl *decl, LC_Type *base) {
LC_Type *n = LC_CreateType(base->kind);
*n = *base;
decl->typedef_renamed_type_decl = base->decl;
n->decl = decl;
return n;
}
LC_FUNCTION LC_Type *LC_CreatePointerType(LC_Type *type) {
uint64_t key = (uint64_t)type;
LC_Type *entry = (LC_Type *)LC_MapGetU64(&L->type_map, key);
if (entry) {
return entry;
}
LC_Type *n = LC_CreateType(LC_TypeKind_Pointer);
n->tbase = type;
LC_MapInsertU64(&L->type_map, key, n);
return n;
}
LC_FUNCTION LC_Type *LC_CreateArrayType(LC_Type *type, int size) {
uint64_t size_key = LC_HashBytes(&size, sizeof(size));
uint64_t type_key = LC_HashBytes(type, sizeof(*type));
uint64_t key = LC_HashMix(size_key, type_key);
LC_Type *entry = (LC_Type *)LC_MapGetU64(&L->type_map, key);
if (entry) {
return entry;
}
LC_Type *n = LC_CreateType(LC_TypeKind_Array);
n->tbase = type;
n->tarray.size = size;
n->size = type->size * size;
n->align = type->align;
LC_MapInsertU64(&L->type_map, key, n);
return n;
}
LC_FUNCTION LC_Type *LC_CreateProcType(LC_TypeMemberList args, LC_Type *ret, bool has_vargs, bool has_vargs_any_promotion) {
LC_ASSERT(NULL, ret);
uint64_t key = LC_HashBytes(ret, sizeof(*ret));
key = LC_HashMix(key, LC_HashBytes(&has_vargs, sizeof(has_vargs)));
key = LC_HashMix(key, LC_HashBytes(&has_vargs_any_promotion, sizeof(has_vargs_any_promotion)));
int procarg_count = 0;
LC_TypeFor(it, args.first) {
key = LC_HashMix(LC_HashBytes(it->type, sizeof(it->type[0])), key);
key = LC_HashMix(LC_HashBytes((char *)it->name, LC_StrLen((char *)it->name)), key);
if (it->default_value_expr) {
key = LC_HashMix(LC_HashBytes(&it->default_value_expr, sizeof(LC_AST *)), key);
}
procarg_count += 1;
}
LC_Type *n = (LC_Type *)LC_MapGetU64(&L->type_map, key);
if (n) return n;
n = LC_CreateType(LC_TypeKind_Proc);
n->tproc.args = args;
n->tproc.vargs = has_vargs;
n->tproc.vargs_any_promotion = has_vargs_any_promotion;
n->tproc.ret = ret;
LC_MapInsertU64(&L->type_map, key, n);
return n;
}
LC_FUNCTION LC_Type *LC_CreateIncompleteType(LC_Decl *decl) {
LC_Type *n = LC_CreateType(LC_TypeKind_Incomplete);
n->decl = decl;
return n;
}
LC_FUNCTION LC_Type *LC_CreateUntypedIntEx(LC_Type *base, LC_Decl *decl) {
uint64_t hash_base = LC_HashBytes(base, sizeof(*base));
uint64_t untyped_int = LC_TypeKind_UntypedInt;
uint64_t key = LC_HashMix(hash_base, untyped_int);
LC_Type *n = (LC_Type *)LC_MapGetU64(&L->type_map, key);
if (n) return n;
n = LC_CreateType(LC_TypeKind_UntypedInt);
n->tbase = base;
n->decl = decl;
return n;
}
LC_FUNCTION LC_Type *LC_CreateUntypedInt(LC_Type *base) {
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Type, LC_ILit("UntypedInt"), &L->NullAST);
LC_Type *n = LC_CreateUntypedIntEx(base, decl);
return n;
}
LC_FUNCTION LC_TypeMember *LC_AddTypeToList(LC_TypeMemberList *list, LC_Intern name, LC_Type *type, LC_AST *ast) {
LC_TypeFor(it, list->first) {
if (name == it->name) {
return NULL;
}
}
LC_TypeMember *r = LC_PushStruct(L->arena, LC_TypeMember);
r->name = name;
r->type = type;
r->ast = ast;
LC_DLLAdd(list->first, list->last, r);
list->count += 1;
return r;
}
LC_FUNCTION LC_Type *LC_StripPointer(LC_Type *type) {
if (type->kind == LC_TypeKind_Pointer) {
return type->tbase;
}
return type;
}

283
src/compiler/ast_copy.c Normal file
View File

@@ -0,0 +1,283 @@
LC_FUNCTION LC_AST *LC_CopyAST(LC_Arena *arena, LC_AST *n) {
if (n == NULL) return NULL;
LC_AST *result = LC_PushStruct(arena, LC_AST);
result->kind = n->kind;
result->id = ++L->ast_count;
result->notes = LC_CopyAST(arena, n->notes);
result->pos = n->pos;
switch (n->kind) {
case LC_ASTKind_File: {
result->afile.x = n->afile.x;
LC_ASTFor(it, n->afile.fimport) {
LC_AST *it_copy = LC_CopyAST(arena, it);
LC_DLLAdd(result->afile.fimport, result->afile.limport, it_copy);
}
LC_ASTFor(it, n->afile.fdecl) {
LC_AST *it_copy = LC_CopyAST(arena, it);
LC_DLLAdd(result->afile.fdecl, result->afile.ldecl, it_copy);
}
LC_ASTFor(it, n->afile.fdiscarded) {
LC_AST *it_copy = LC_CopyAST(arena, it);
LC_DLLAdd(result->afile.fdiscarded, result->afile.ldiscarded, it_copy);
}
result->afile.build_if = n->afile.build_if;
} break;
case LC_ASTKind_DeclProc: {
result->dbase.name = n->dbase.name;
result->dproc.body = LC_CopyAST(arena, n->dproc.body);
result->dproc.type = LC_CopyAST(arena, n->dproc.type);
} break;
case LC_ASTKind_NoteList: {
LC_ASTFor(it, n->anote_list.first) {
LC_AST *it_copy = LC_CopyAST(arena, it);
LC_DLLAdd(result->anote_list.first, result->anote_list.last, it_copy);
}
} break;
case LC_ASTKind_TypespecAggMem:
case LC_ASTKind_TypespecProcArg: {
result->tproc_arg.name = n->tproc_arg.name;
result->tproc_arg.type = LC_CopyAST(arena, n->tproc_arg.type);
result->tproc_arg.expr = LC_CopyAST(arena, n->tproc_arg.expr);
} break;
case LC_ASTKind_ExprNote: {
result->enote.expr = LC_CopyAST(arena, n->enote.expr);
} break;
case LC_ASTKind_StmtSwitch: {
LC_ASTFor(it, n->sswitch.first) {
LC_AST *it_copy = LC_CopyAST(arena, it);
LC_DLLAdd(result->sswitch.first, result->sswitch.last, it_copy);
}
result->sswitch.total_switch_case_count = n->sswitch.total_switch_case_count;
result->sswitch.expr = LC_CopyAST(arena, n->sswitch.expr);
} break;
case LC_ASTKind_StmtSwitchCase: {
LC_ASTFor(it, n->scase.first) {
LC_AST *it_copy = LC_CopyAST(arena, it);
LC_DLLAdd(result->scase.first, result->scase.last, it_copy);
}
result->scase.body = LC_CopyAST(arena, n->scase.body);
} break;
case LC_ASTKind_StmtSwitchDefault: {
LC_ASTFor(it, n->scase.first) {
LC_AST *it_copy = LC_CopyAST(arena, it);
LC_DLLAdd(result->scase.first, result->scase.last, it_copy);
}
result->scase.body = LC_CopyAST(arena, n->scase.body);
} break;
case LC_ASTKind_StmtIf: {
LC_ASTFor(it, n->sif.first) {
LC_AST *it_copy = LC_CopyAST(arena, it);
LC_DLLAdd(result->sswitch.first, result->sswitch.last, it_copy);
}
result->sif.expr = LC_CopyAST(arena, n->sif.expr);
result->sif.body = LC_CopyAST(arena, n->sif.body);
} break;
case LC_ASTKind_StmtElse:
case LC_ASTKind_StmtElseIf: {
result->sif.expr = LC_CopyAST(arena, n->sif.expr);
result->sif.body = LC_CopyAST(arena, n->sif.body);
} break;
case LC_ASTKind_GlobImport: {
result->gimport.name = n->gimport.name;
result->gimport.path = n->gimport.path;
} break;
case LC_ASTKind_DeclNote: {
result->dnote.expr = LC_CopyAST(arena, n->dnote.expr);
} break;
case LC_ASTKind_DeclUnion:
case LC_ASTKind_DeclStruct: {
result->dbase.name = n->dbase.name;
LC_ASTFor(it, n->dagg.first) {
LC_AST *it_copy = LC_CopyAST(arena, it);
LC_DLLAdd(result->dagg.first, result->dagg.last, it_copy);
}
} break;
case LC_ASTKind_DeclVar: {
result->dbase.name = n->dbase.name;
result->dvar.type = LC_CopyAST(arena, n->dvar.type);
result->dvar.expr = LC_CopyAST(arena, n->dvar.expr);
} break;
case LC_ASTKind_DeclConst: {
result->dbase.name = n->dbase.name;
result->dconst.expr = LC_CopyAST(arena, n->dconst.expr);
} break;
case LC_ASTKind_DeclTypedef: {
result->dbase.name = n->dbase.name;
result->dtypedef.type = LC_CopyAST(arena, n->dtypedef.type);
} break;
case LC_ASTKind_ExprField:
case LC_ASTKind_TypespecField: {
result->efield.left = LC_CopyAST(arena, n->efield.left);
result->efield.right = n->efield.right;
} break;
case LC_ASTKind_TypespecIdent: {
result->eident.name = n->eident.name;
} break;
case LC_ASTKind_TypespecPointer: {
result->tpointer.base = LC_CopyAST(arena, n->tpointer.base);
} break;
case LC_ASTKind_TypespecArray: {
result->tarray.base = LC_CopyAST(arena, n->tarray.base);
result->tarray.index = LC_CopyAST(arena, n->tarray.index);
} break;
case LC_ASTKind_TypespecProc: {
LC_ASTFor(it, n->tproc.first) {
LC_AST *it_copy = LC_CopyAST(arena, it);
LC_DLLAdd(result->tproc.first, result->tproc.last, it_copy);
}
result->tproc.ret = LC_CopyAST(arena, n->tproc.ret);
result->tproc.vargs = n->tproc.vargs;
} break;
case LC_ASTKind_StmtBlock: {
LC_ASTFor(it, n->sblock.first) {
LC_AST *it_copy = LC_CopyAST(arena, it);
LC_DLLAdd(result->sblock.first, result->sblock.last, it_copy);
if (it_copy->kind == LC_ASTKind_StmtDefer) {
LC_SLLStackAddMod(result->sblock.first_defer, it_copy, sdefer.next);
}
}
if (n->sblock.first_defer) {
LC_ASSERT(result, result->sblock.first_defer);
}
result->sblock.kind = n->sblock.kind;
result->sblock.name = n->sblock.name;
} break;
case LC_ASTKind_StmtNote: {
result->snote.expr = LC_CopyAST(arena, n->snote.expr);
} break;
case LC_ASTKind_StmtReturn: {
result->sreturn.expr = LC_CopyAST(arena, n->sreturn.expr);
} break;
case LC_ASTKind_StmtBreak: {
result->sbreak.name = n->sbreak.name;
} break;
case LC_ASTKind_StmtContinue: {
result->scontinue.name = n->scontinue.name;
} break;
case LC_ASTKind_StmtDefer: {
result->sdefer.body = LC_CopyAST(arena, n->sdefer.body);
} break;
case LC_ASTKind_StmtFor: {
result->sfor.init = LC_CopyAST(arena, n->sfor.init);
result->sfor.cond = LC_CopyAST(arena, n->sfor.cond);
result->sfor.inc = LC_CopyAST(arena, n->sfor.inc);
result->sfor.body = LC_CopyAST(arena, n->sfor.body);
} break;
case LC_ASTKind_StmtAssign: {
result->sassign.op = n->sassign.op;
result->sassign.left = LC_CopyAST(arena, n->sassign.left);
result->sassign.right = LC_CopyAST(arena, n->sassign.right);
} break;
case LC_ASTKind_StmtExpr: {
result->sexpr.expr = LC_CopyAST(arena, n->sexpr.expr);
} break;
case LC_ASTKind_StmtVar: {
result->svar.type = LC_CopyAST(arena, n->svar.type);
result->svar.expr = LC_CopyAST(arena, n->svar.expr);
result->svar.name = n->svar.name;
} break;
case LC_ASTKind_StmtConst: {
result->sconst.expr = LC_CopyAST(arena, n->sconst.expr);
result->sconst.name = n->sconst.name;
} break;
case LC_ASTKind_ExprIdent: {
result->eident.name = n->eident.name;
} break;
case LC_ASTKind_ExprBool:
case LC_ASTKind_ExprFloat:
case LC_ASTKind_ExprInt:
case LC_ASTKind_ExprString: {
result->eatom = n->eatom;
} break;
case LC_ASTKind_ExprType: {
result->etype.type = LC_CopyAST(arena, n->etype.type);
} break;
case LC_ASTKind_ExprAddPtr:
case LC_ASTKind_ExprBinary: {
result->ebinary.op = n->ebinary.op;
result->ebinary.left = LC_CopyAST(arena, n->ebinary.left);
result->ebinary.right = LC_CopyAST(arena, n->ebinary.right);
} break;
case LC_ASTKind_ExprGetPointerOfValue:
case LC_ASTKind_ExprGetValueOfPointer:
case LC_ASTKind_ExprUnary: {
result->eunary.op = n->eunary.op;
result->eunary.expr = LC_CopyAST(arena, n->eunary.expr);
} break;
case LC_ASTKind_ExprCompoundItem:
case LC_ASTKind_ExprCallItem: {
result->ecompo_item.name = n->ecompo_item.name;
result->ecompo_item.index = LC_CopyAST(arena, n->ecompo_item.index);
result->ecompo_item.expr = LC_CopyAST(arena, n->ecompo_item.expr);
} break;
case LC_ASTKind_ExprBuiltin:
case LC_ASTKind_Note:
case LC_ASTKind_ExprCall:
case LC_ASTKind_ExprCompound: {
LC_ASTFor(it, n->ecompo.first) {
LC_AST *it_copy = LC_CopyAST(arena, it);
LC_DLLAdd(result->ecompo.first, result->ecompo.last, it_copy);
}
result->ecompo.size = n->ecompo.size;
result->ecompo.name = LC_CopyAST(arena, n->ecompo.name);
} break;
case LC_ASTKind_ExprCast: {
result->ecast.type = LC_CopyAST(arena, n->ecast.type);
result->ecast.expr = LC_CopyAST(arena, n->ecast.expr);
} break;
case LC_ASTKind_ExprIndex: {
result->eindex.index = LC_CopyAST(arena, n->eindex.index);
result->eindex.base = LC_CopyAST(arena, n->eindex.base);
} break;
default: LC_ReportASTError(n, "internal compiler error: failed to LC_CopyAST, got invalid ast kind: %s", LC_ASTKindToString(n->kind));
}
return result;
}

316
src/compiler/ast_walk.c Normal file
View File

@@ -0,0 +1,316 @@
LC_FUNCTION void LC_ReserveAST(LC_ASTArray *stack, int size) {
if (size > stack->cap) {
LC_AST **new_stack = LC_PushArray(stack->arena, LC_AST *, size);
LC_MemoryCopy(new_stack, stack->data, stack->len * sizeof(LC_AST *));
stack->data = new_stack;
stack->cap = size;
}
}
LC_FUNCTION void LC_PushAST(LC_ASTArray *stack, LC_AST *ast) {
LC_ASSERT(NULL, stack->len <= stack->cap);
LC_ASSERT(NULL, stack->arena);
if (stack->len == stack->cap) {
int new_cap = stack->cap < 16 ? 16 : stack->cap * 2;
LC_AST **new_stack = LC_PushArray(stack->arena, LC_AST *, new_cap);
LC_MemoryCopy(new_stack, stack->data, stack->len * sizeof(LC_AST *));
stack->data = new_stack;
stack->cap = new_cap;
}
stack->data[stack->len++] = ast;
}
LC_FUNCTION void LC_PopAST(LC_ASTArray *stack) {
LC_ASSERT(NULL, stack->arena);
LC_ASSERT(NULL, stack->len > 0);
stack->len -= 1;
}
LC_FUNCTION LC_AST *LC_GetLastAST(LC_ASTArray *arr) {
LC_ASSERT(NULL, arr->len > 0);
LC_AST *result = arr->data[arr->len - 1];
return result;
}
LC_FUNCTION void LC_WalkAST(LC_ASTWalker *ctx, LC_AST *n) {
if (!ctx->depth_first) {
ctx->proc(ctx, n);
}
if (ctx->dont_recurse) {
ctx->dont_recurse = false;
return;
}
LC_PushAST(&ctx->stack, n);
switch (n->kind) {
case LC_ASTKind_TypespecIdent:
case LC_ASTKind_ExprIdent:
case LC_ASTKind_ExprString:
case LC_ASTKind_ExprInt:
case LC_ASTKind_ExprFloat:
case LC_ASTKind_GlobImport:
case LC_ASTKind_ExprBool:
case LC_ASTKind_StmtBreak:
case LC_ASTKind_StmtContinue: break;
case LC_ASTKind_Package: {
LC_ASTFor(it, n->apackage.ffile) LC_WalkAST(ctx, it);
ctx->inside_discarded += 1;
if (ctx->visit_discarded) LC_ASTFor(it, n->apackage.fdiscarded) LC_WalkAST(ctx, it);
ctx->inside_discarded -= 1;
} break;
case LC_ASTKind_File: {
LC_ASTFor(it, n->afile.fimport) LC_WalkAST(ctx, it);
LC_ASTFor(it, n->afile.fdecl) {
if (ctx->visit_notes == false && it->kind == LC_ASTKind_DeclNote) continue;
LC_WalkAST(ctx, it);
}
ctx->inside_discarded += 1;
if (ctx->visit_discarded) LC_ASTFor(it, n->afile.fdiscarded) LC_WalkAST(ctx, it);
ctx->inside_discarded -= 1;
} break;
case LC_ASTKind_DeclProc: {
LC_WalkAST(ctx, n->dproc.type);
if (n->dproc.body) LC_WalkAST(ctx, n->dproc.body);
} break;
case LC_ASTKind_NoteList: {
if (ctx->visit_notes) LC_ASTFor(it, n->anote_list.first) LC_WalkAST(ctx, it);
} break;
case LC_ASTKind_TypespecProcArg: {
LC_WalkAST(ctx, n->tproc_arg.type);
if (n->tproc_arg.expr) LC_WalkAST(ctx, n->tproc_arg.expr);
} break;
case LC_ASTKind_TypespecAggMem: {
LC_WalkAST(ctx, n->tproc_arg.type);
} break;
case LC_ASTKind_ExprNote: {
ctx->inside_note += 1;
if (ctx->visit_notes) LC_WalkAST(ctx, n->enote.expr);
ctx->inside_note -= 1;
} break;
case LC_ASTKind_StmtSwitch: {
LC_WalkAST(ctx, n->sswitch.expr);
LC_ASTFor(it, n->sswitch.first) LC_WalkAST(ctx, it);
} break;
case LC_ASTKind_StmtSwitchCase: {
LC_ASTFor(it, n->scase.first) LC_WalkAST(ctx, it);
LC_WalkAST(ctx, n->scase.body);
} break;
case LC_ASTKind_StmtSwitchDefault: {
LC_ASTFor(it, n->scase.first) LC_WalkAST(ctx, it);
LC_WalkAST(ctx, n->scase.body);
} break;
case LC_ASTKind_StmtIf: {
LC_WalkAST(ctx, n->sif.expr);
LC_WalkAST(ctx, n->sif.body);
LC_ASTFor(it, n->sif.first) LC_WalkAST(ctx, it);
} break;
case LC_ASTKind_StmtElse:
case LC_ASTKind_StmtElseIf: {
if (n->sif.expr) LC_WalkAST(ctx, n->sif.expr);
LC_WalkAST(ctx, n->sif.body);
} break;
case LC_ASTKind_DeclNote: {
ctx->inside_note += 1;
if (ctx->visit_notes) LC_WalkAST(ctx, n->dnote.expr);
ctx->inside_note -= 1;
} break;
case LC_ASTKind_DeclUnion:
case LC_ASTKind_DeclStruct: {
LC_ASTFor(it, n->dagg.first) LC_WalkAST(ctx, it);
} break;
case LC_ASTKind_DeclVar: {
if (n->dvar.type) LC_WalkAST(ctx, n->dvar.type);
if (n->dvar.expr) LC_WalkAST(ctx, n->dvar.expr);
} break;
case LC_ASTKind_DeclConst: {
if (n->dconst.expr) LC_WalkAST(ctx, n->dconst.expr);
} break;
case LC_ASTKind_DeclTypedef: {
LC_WalkAST(ctx, n->dtypedef.type);
} break;
case LC_ASTKind_TypespecField: {
LC_WalkAST(ctx, n->efield.left);
} break;
case LC_ASTKind_TypespecPointer: {
LC_WalkAST(ctx, n->tpointer.base);
} break;
case LC_ASTKind_TypespecArray: {
LC_WalkAST(ctx, n->tarray.base);
if (n->tarray.index) LC_WalkAST(ctx, n->tarray.index);
} break;
case LC_ASTKind_TypespecProc: {
LC_ASTFor(it, n->tproc.first) LC_WalkAST(ctx, it);
if (n->tproc.ret) LC_WalkAST(ctx, n->tproc.ret);
} break;
case LC_ASTKind_StmtBlock: {
LC_ASTFor(it, n->sblock.first) LC_WalkAST(ctx, it);
// hmm should we inline defers or maybe remove them from
// the stmt list?
// LC_ASTFor(it, n->sblock.first_defer) LC_WalkAST(ctx, it);
} break;
case LC_ASTKind_StmtNote: {
ctx->inside_note += 1;
if (ctx->visit_notes) LC_WalkAST(ctx, n->snote.expr);
ctx->inside_note -= 1;
} break;
case LC_ASTKind_StmtReturn: {
if (n->sreturn.expr) LC_WalkAST(ctx, n->sreturn.expr);
} break;
case LC_ASTKind_StmtDefer: {
LC_WalkAST(ctx, n->sdefer.body);
} break;
case LC_ASTKind_StmtFor: {
if (n->sfor.init) LC_WalkAST(ctx, n->sfor.init);
if (n->sfor.cond) LC_WalkAST(ctx, n->sfor.cond);
if (n->sfor.inc) LC_WalkAST(ctx, n->sfor.inc);
LC_WalkAST(ctx, n->sfor.body);
} break;
case LC_ASTKind_StmtAssign: {
LC_WalkAST(ctx, n->sassign.left);
LC_WalkAST(ctx, n->sassign.right);
} break;
case LC_ASTKind_StmtExpr: {
LC_WalkAST(ctx, n->sexpr.expr);
} break;
case LC_ASTKind_StmtVar: {
if (n->svar.type) LC_WalkAST(ctx, n->svar.type);
if (n->svar.expr) LC_WalkAST(ctx, n->svar.expr);
} break;
case LC_ASTKind_StmtConst: {
LC_WalkAST(ctx, n->sconst.expr);
} break;
case LC_ASTKind_ExprType: {
LC_WalkAST(ctx, n->etype.type);
} break;
case LC_ASTKind_ExprAddPtr:
case LC_ASTKind_ExprBinary: {
LC_WalkAST(ctx, n->ebinary.left);
LC_WalkAST(ctx, n->ebinary.right);
} break;
case LC_ASTKind_ExprGetPointerOfValue:
case LC_ASTKind_ExprGetValueOfPointer:
case LC_ASTKind_ExprUnary: {
LC_WalkAST(ctx, n->eunary.expr);
} break;
case LC_ASTKind_ExprCompoundItem:
case LC_ASTKind_ExprCallItem: {
if (n->ecompo_item.index) LC_WalkAST(ctx, n->ecompo_item.index);
LC_WalkAST(ctx, n->ecompo_item.expr);
} break;
case LC_ASTKind_Note: {
ctx->inside_note += 1;
if (n->ecompo.name) LC_WalkAST(ctx, n->ecompo.name);
LC_ASTFor(it, n->ecompo.first) LC_WalkAST(ctx, it);
ctx->inside_note -= 1;
} break;
case LC_ASTKind_ExprBuiltin: {
ctx->inside_builtin += 1;
if (n->ecompo.name) LC_WalkAST(ctx, n->ecompo.name);
LC_ASTFor(it, n->ecompo.first) LC_WalkAST(ctx, it);
ctx->inside_builtin -= 1;
} break;
case LC_ASTKind_ExprCall:
case LC_ASTKind_ExprCompound: {
if (n->ecompo.name) LC_WalkAST(ctx, n->ecompo.name);
LC_ASTFor(it, n->ecompo.first) LC_WalkAST(ctx, it);
} break;
case LC_ASTKind_ExprCast: {
LC_WalkAST(ctx, n->ecast.type);
LC_WalkAST(ctx, n->ecast.expr);
} break;
case LC_ASTKind_ExprField: {
LC_WalkAST(ctx, n->efield.left);
} break;
case LC_ASTKind_ExprIndex: {
LC_WalkAST(ctx, n->eindex.index);
LC_WalkAST(ctx, n->eindex.base);
} break;
case LC_ASTKind_Ignore:
case LC_ASTKind_Error:
default: LC_ReportASTError(n, "internal compiler error: got invalid ast kind during ast walk: %s", LC_ASTKindToString(n->kind));
}
if (ctx->visit_notes && n->notes) {
LC_WalkAST(ctx, n->notes);
}
LC_PopAST(&ctx->stack);
if (ctx->depth_first) {
ctx->proc(ctx, n);
}
}
LC_FUNCTION LC_ASTWalker LC_GetDefaultWalker(LC_Arena *arena, LC_ASTWalkProc *proc) {
LC_ASTWalker result = {0};
result.stack.arena = arena;
result.proc = proc;
result.depth_first = true;
return result;
}
LC_FUNCTION void WalkAndFlattenAST(LC_ASTWalker *ctx, LC_AST *n) {
LC_ASTArray *array = (LC_ASTArray *)ctx->user_data;
LC_PushAST(array, n);
}
LC_FUNCTION LC_ASTArray LC_FlattenAST(LC_Arena *arena, LC_AST *n) {
LC_ASTArray array = {arena};
LC_ASTWalker walker = LC_GetDefaultWalker(arena, WalkAndFlattenAST);
walker.user_data = (void *)&array;
LC_WalkAST(&walker, n);
return array;
}
LC_FUNCTION void WalkToFindSizeofLike(LC_ASTWalker *w, LC_AST *n) {
if (n->kind == LC_ASTKind_ExprBuiltin) {
LC_ASSERT(n, n->ecompo.name->kind == LC_ASTKind_ExprIdent);
if (n->ecompo.name->eident.name == L->isizeof || n->ecompo.name->eident.name == L->ialignof || n->ecompo.name->eident.name == L->ioffsetof) {
((bool *)w->user_data)[0] = true;
}
}
}
LC_FUNCTION bool LC_ContainsCBuiltin(LC_AST *n) {
LC_TempArena checkpoint = LC_BeginTemp(L->arena);
bool found = false;
{
LC_ASTWalker walker = LC_GetDefaultWalker(L->arena, WalkToFindSizeofLike);
walker.depth_first = false;
walker.user_data = (void *)&found;
LC_WalkAST(&walker, n);
}
LC_EndTemp(checkpoint);
return found;
}
LC_FUNCTION void SetASTPosOnAll_Walk(LC_ASTWalker *ctx, LC_AST *n) {
n->pos = (LC_Token *)ctx->user_data;
}
LC_FUNCTION void LC_SetASTPosOnAll(LC_AST *n, LC_Token *pos) {
LC_TempArena check = LC_BeginTemp(L->arena);
LC_ASTWalker walker = LC_GetDefaultWalker(L->arena, SetASTPosOnAll_Walk);
walker.user_data = (void *)pos;
LC_WalkAST(&walker, n);
LC_EndTemp(check);
}

1471
src/compiler/bigint.c Normal file

File diff suppressed because it is too large Load Diff

198
src/compiler/common.c Normal file
View File

@@ -0,0 +1,198 @@
#if __cplusplus
#define LC_Alignof(...) alignof(__VA_ARGS__)
#else
#define LC_Alignof(...) _Alignof(__VA_ARGS__)
#endif
#define LC_WRAP_AROUND_POWER_OF_2(x, pow2) (((x) & ((pow2)-1llu)))
#if defined(_MSC_VER)
#define LC_DebugBreak() (L->breakpoint_on_error && IsDebuggerPresent() && (__debugbreak(), 0))
#else
#define LC_DebugBreak() (L->breakpoint_on_error && (__builtin_trap(), 0))
#endif
#define LC_FatalError() (L->breakpoint_on_error ? LC_DebugBreak() : (LC_Exit(1), 0))
LC_FUNCTION void LC_IgnoreMessage(LC_Token *pos, char *str, int len) {
}
LC_FUNCTION void LC_SendErrorMessage(LC_Token *pos, LC_String s8) {
if (L->on_message) {
L->on_message(pos, s8.str, (int)s8.len);
} else {
if (pos) {
LC_String line = LC_GetTokenLine(pos);
LC_String fmt = LC_Format(L->arena, "%s(%d,%d): error: %.*s\n%.*s", (char *)pos->lex->file, pos->line, pos->column, LC_Expand(s8), LC_Expand(line));
LC_Print(fmt.str, fmt.len);
} else {
LC_Print(s8.str, s8.len);
}
}
LC_DebugBreak();
}
LC_FUNCTION void LC_SendErrorMessagef(LC_Lex *x, LC_Token *pos, const char *str, ...) {
LC_FORMAT(L->arena, str, s8);
LC_SendErrorMessage(pos, s8);
}
LC_FUNCTION void LC_HandleFatalError(void) {
if (L->on_fatal_error) {
L->on_fatal_error();
return;
}
LC_FatalError();
}
LC_FUNCTION void LC_MapReserve(LC_Map *map, int size) {
LC_Map old_map = *map;
LC_ASSERT(NULL, LC_IS_POW2(size));
map->len = 0;
map->cap = size;
LC_ASSERT(NULL, map->arena);
map->entries = LC_PushArray(map->arena, LC_MapEntry, map->cap);
if (old_map.entries) {
for (int i = 0; i < old_map.cap; i += 1) {
LC_MapEntry *it = old_map.entries + i;
if (it->key) LC_InsertMapEntry(map, it->key, it->value);
}
}
}
// FNV HASH (1a?)
LC_FUNCTION uint64_t LC_HashBytes(void *data, uint64_t size) {
uint8_t *data8 = (uint8_t *)data;
uint64_t hash = (uint64_t)14695981039346656037ULL;
for (uint64_t i = 0; i < size; i++) {
hash = hash ^ (uint64_t)(data8[i]);
hash = hash * (uint64_t)1099511628211ULL;
}
return hash;
}
LC_FUNCTION uint64_t LC_HashMix(uint64_t x, uint64_t y) {
x ^= y;
x *= 0xff51afd7ed558ccd;
x ^= x >> 32;
return x;
}
LC_FUNCTION int LC_NextPow2(int v) {
v--;
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
v |= v >> 8;
v |= v >> 16;
v++;
return v;
}
LC_FUNCTION LC_MapEntry *LC_GetMapEntryEx(LC_Map *map, uint64_t key) {
LC_ASSERT(NULL, key);
if (map->len * 2 >= map->cap) {
LC_MapReserve(map, map->cap * 2);
}
uint64_t hash = LC_HashBytes(&key, sizeof(key));
if (hash == 0) hash += 1;
uint64_t index = LC_WRAP_AROUND_POWER_OF_2(hash, map->cap);
uint64_t i = index;
for (;;) {
LC_MapEntry *it = map->entries + i;
if (it->key == key || it->key == 0) {
return it;
}
i = LC_WRAP_AROUND_POWER_OF_2(i + 1, map->cap);
if (i == index) return NULL;
}
LC_ASSERT(NULL, !"invalid codepath");
}
LC_FUNCTION bool LC_InsertWithoutReplace(LC_Map *map, void *key, void *value) {
LC_MapEntry *entry = LC_GetMapEntryEx(map, (uint64_t)key);
if (entry->key != 0) return false;
map->len += 1;
entry->key = (uint64_t)key;
entry->value = (uint64_t)value;
return true;
}
LC_FUNCTION LC_MapEntry *LC_InsertMapEntry(LC_Map *map, uint64_t key, uint64_t value) {
LC_MapEntry *entry = LC_GetMapEntryEx(map, key);
if (entry->key == key) {
entry->value = value;
}
if (entry->key == 0) {
entry->key = key;
entry->value = value;
map->len += 1;
}
return entry;
}
LC_FUNCTION LC_MapEntry *LC_GetMapEntry(LC_Map *map, uint64_t key) {
LC_MapEntry *entry = LC_GetMapEntryEx(map, key);
if (entry && entry->key == key) {
return entry;
}
return NULL;
}
LC_FUNCTION void LC_MapInsert(LC_Map *map, LC_String keystr, void *value) {
uint64_t key = LC_HashBytes(keystr.str, keystr.len);
LC_InsertMapEntry(map, key, (uint64_t)value);
}
LC_FUNCTION void *LC_MapGet(LC_Map *map, LC_String keystr) {
uint64_t key = LC_HashBytes(keystr.str, keystr.len);
LC_MapEntry *r = LC_GetMapEntry(map, key);
return r ? (void *)r->value : 0;
}
LC_FUNCTION void LC_MapInsertU64(LC_Map *map, uint64_t key, void *value) {
LC_InsertMapEntry(map, key, (uint64_t)value);
}
LC_FUNCTION void *LC_MapGetU64(LC_Map *map, uint64_t key) {
LC_MapEntry *r = LC_GetMapEntry(map, key);
return r ? (void *)r->value : 0;
}
LC_FUNCTION void *LC_MapGetP(LC_Map *map, void *key) {
return LC_MapGetU64(map, (uint64_t)key);
}
LC_FUNCTION void LC_MapInsertP(LC_Map *map, void *key, void *value) {
LC_InsertMapEntry(map, (uint64_t)key, (uint64_t)value);
}
LC_FUNCTION void LC_MapClear(LC_Map *map) {
if (map->len != 0) LC_MemoryZero(map->entries, map->cap * sizeof(LC_MapEntry));
map->len = 0;
}
LC_FUNCTION size_t LC_GetAlignOffset(size_t size, size_t align) {
size_t mask = align - 1;
size_t val = size & mask;
if (val) {
val = align - val;
}
return val;
}
LC_FUNCTION size_t LC_AlignUp(size_t size, size_t align) {
size_t result = size + LC_GetAlignOffset(size, align);
return result;
}
LC_FUNCTION size_t LC_AlignDown(size_t size, size_t align) {
size += 1; // Make sure when align is 8 doesn't get rounded down to 0
size_t result = size - (align - LC_GetAlignOffset(size, align));
return result;
}

View File

@@ -0,0 +1,72 @@
LC_FUNCTION void WalkAndCountDeclRefs(LC_ASTWalker *ctx, LC_AST *n) {
LC_Decl *decl = NULL;
if (n->kind == LC_ASTKind_ExprIdent || n->kind == LC_ASTKind_TypespecIdent) {
if (n->eident.resolved_decl) decl = n->eident.resolved_decl;
}
if (n->kind == LC_ASTKind_ExprField) {
if (n->efield.resolved_decl) decl = n->efield.resolved_decl;
}
if (decl) {
LC_Map *map_of_visits = (LC_Map *)ctx->user_data;
intptr_t visited = (intptr_t)LC_MapGetP(map_of_visits, decl);
LC_MapInsertP(map_of_visits, decl, (void *)(visited + 1));
if (visited == 0 && decl->ast->kind != LC_ASTKind_Null) {
LC_WalkAST(ctx, decl->ast);
}
}
}
LC_FUNCTION LC_Map LC_CountDeclRefs(LC_Arena *arena) {
LC_Map map = {arena};
LC_MapReserve(&map, 512);
LC_AST *package = LC_GetPackageByName(L->first_package);
LC_ASTWalker walker = LC_GetDefaultWalker(arena, WalkAndCountDeclRefs);
walker.user_data = (void *)&map;
walker.visit_notes = true;
LC_WalkAST(&walker, package);
return map;
}
LC_FUNCTION void LC_RemoveUnreferencedGlobalDecls(LC_Map *map_of_visits) {
for (LC_ASTRef *it = L->ordered_packages.first; it; it = it->next) {
for (LC_Decl *decl = it->ast->apackage.first_ordered; decl;) {
intptr_t ref_count = (intptr_t)LC_MapGetP(map_of_visits, decl);
LC_Decl *remove = decl;
decl = decl->next;
if (ref_count == 0 && remove->foreign_name != LC_ILit("main")) {
LC_DLLRemove(it->ast->apackage.first_ordered, it->ast->apackage.last_ordered, remove);
}
}
}
}
LC_FUNCTION void LC_ErrorOnUnreferencedLocals(LC_Map *map_of_visits) {
LC_Decl *first = (LC_Decl *)L->decl_arena->memory.data;
for (int i = 0; i < L->decl_count; i += 1) {
LC_Decl *decl = first + i;
if (decl->package == L->builtin_package) {
continue;
}
intptr_t ref_count = (intptr_t)LC_MapGetP(map_of_visits, decl);
if (ref_count == 0) {
if (LC_IsStmt(decl->ast)) {
if (!LC_HasNote(decl->ast, L->iunused)) LC_ReportASTError(decl->ast, "unused local variable '%s'", decl->name);
}
}
}
}
LC_FUNCTION void LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls(void) {
if (L->errors) return;
LC_TempArena check = LC_BeginTemp(L->arena);
LC_Map map = LC_CountDeclRefs(check.arena);
LC_ErrorOnUnreferencedLocals(&map);
LC_RemoveUnreferencedGlobalDecls(&map);
LC_EndTemp(check);
}

677
src/compiler/genc.c Normal file
View File

@@ -0,0 +1,677 @@
const bool LC_GenCInternalGenerateSizeofs = true;
LC_FUNCTION void LC_GenCLineDirective(LC_AST *node) {
if (L->emit_line_directives) {
L->printer.last_line_num = node->pos->line;
LC_GenLinef("#line %d", L->printer.last_line_num);
LC_Intern file = node->pos->lex->file;
if (file != L->printer.last_filename) {
L->printer.last_filename = file;
LC_Genf(" \"%s\"", (char *)L->printer.last_filename);
}
}
}
LC_FUNCTION void LC_GenLastCLineDirective(void) {
if (L->emit_line_directives) {
LC_Genf("#line %d", L->printer.last_line_num);
}
}
LC_FUNCTION void LC_GenCLineDirectiveNum(int num) {
if (L->emit_line_directives) {
LC_Genf("#line %d", num);
}
}
LC_FUNCTION char *LC_GenCTypeParen(char *str, char c) {
return c && c != '[' ? LC_Strf("(%s)", str) : str;
}
LC_FUNCTION char *LC_GenCType(LC_Type *type, char *str) {
switch (type->kind) {
case LC_TypeKind_Pointer: {
return LC_GenCType(type->tptr.base, LC_GenCTypeParen(LC_Strf("*%s", str), *str));
} break;
case LC_TypeKind_Array: {
if (type->tarray.size == 0) {
return LC_GenCType(type->tarray.base, LC_GenCTypeParen(LC_Strf("%s[]", str), *str));
} else {
return LC_GenCType(type->tarray.base, LC_GenCTypeParen(LC_Strf("%s[%d]", str, type->tarray.size), *str));
}
} break;
case LC_TypeKind_Proc: {
LC_StringList out = {0};
LC_Addf(L->arena, &out, "(*%s)", str);
LC_Addf(L->arena, &out, "(");
if (type->tagg.mems.count == 0) {
LC_Addf(L->arena, &out, "void");
} else {
int i = 0;
for (LC_TypeMember *it = type->tproc.args.first; it; it = it->next) {
LC_Addf(L->arena, &out, "%s%s", i == 0 ? "" : ", ", LC_GenCType(it->type, ""));
i += 1;
}
}
if (type->tproc.vargs) {
LC_Addf(L->arena, &out, ", ...");
}
LC_Addf(L->arena, &out, ")");
char *front = LC_MergeString(L->arena, out).str;
char *result = LC_GenCType(type->tproc.ret, front);
return result;
} break;
default: return LC_Strf("%s%s%s", type->decl->foreign_name, str[0] ? " " : "", str);
}
}
LC_FUNCTION LC_Intern LC_GetStringFromSingleArgNote(LC_AST *note) {
LC_ASSERT(note, note->kind == LC_ASTKind_Note);
LC_ASSERT(note, note->ecompo.first == note->ecompo.last);
LC_AST *arg = note->ecompo.first;
LC_ASSERT(note, arg->kind == LC_ASTKind_ExprCallItem);
LC_AST *str = arg->ecompo_item.expr;
LC_ASSERT(note, str->kind == LC_ASTKind_ExprString);
return str->eatom.name;
}
LC_FUNCTION void LC_GenCCompound(LC_AST *n) {
LC_Type *type = n->type;
if (LC_IsAggType(type)) {
LC_ResolvedCompo *rd = n->ecompo.resolved_items;
LC_Genf("{");
if (rd->first == NULL) LC_Genf("0");
for (LC_ResolvedCompoItem *it = rd->first; it; it = it->next) {
LC_Genf(".%s = ", (char *)it->t->name);
LC_GenCExpr(it->expr);
if (it->next) LC_Genf(", ");
}
LC_Genf("}");
} else if (LC_IsArray(type)) {
LC_ResolvedArrayCompo *rd = n->ecompo.resolved_array_items;
LC_Genf("{");
for (LC_ResolvedCompoArrayItem *it = rd->first; it; it = it->next) {
LC_Genf("[%d] = ", it->index);
LC_GenCExpr(it->comp->ecompo_item.expr);
if (it->next) LC_Genf(", ");
}
LC_Genf("}");
} else {
LC_ReportASTError(n, "internal compiler error: got unhandled case in %s", __FUNCTION__);
}
}
LC_THREAD_LOCAL bool GC_SpecialCase_GlobalScopeStringDecl;
LC_FUNCTION void LC_GenCString(char *s, LC_Type *type) {
if (type == L->tstring) {
if (!GC_SpecialCase_GlobalScopeStringDecl) LC_Genf("(LC_String)");
LC_Genf("{ ");
}
LC_Genf("\"");
for (int i = 0; s[i]; i += 1) {
LC_String escape = LC_GetEscapeString(s[i]);
if (escape.len) {
LC_Genf("%.*s", LC_Expand(escape));
} else {
LC_Genf("%c", s[i]);
}
}
LC_Genf("\"");
if (type == L->tstring) LC_Genf(", %d }", (int)LC_StrLen(s));
}
LC_FUNCTION char *LC_GenCVal(LC_TypeAndVal v, LC_Type *type) {
char *str = LC_GenLCTypeVal(v);
switch (type->kind) {
case LC_TypeKind_uchar:
case LC_TypeKind_ushort:
case LC_TypeKind_uint: str = LC_Strf("%su", str); break;
case LC_TypeKind_ulong: str = LC_Strf("%sul", str); break;
case LC_TypeKind_Pointer:
case LC_TypeKind_Proc:
case LC_TypeKind_ullong: str = LC_Strf("%sull", str); break;
case LC_TypeKind_long: str = LC_Strf("%sull", str); break;
case LC_TypeKind_llong: str = LC_Strf("%sull", str); break;
case LC_TypeKind_float: str = LC_Strf("%sf", str); break;
case LC_TypeKind_UntypedFloat: str = LC_Strf(" /*utfloat*/%s", str); break;
case LC_TypeKind_UntypedInt: str = LC_Strf(" /*utint*/%sull", str); break;
default: {
}
}
if (LC_IsUTInt(v.type) && !LC_IsUntyped(type) && type->size < 4) {
str = LC_Strf("(%s)%s", LC_GenCType(type, ""), str);
}
return str;
}
LC_FUNCTION void LC_GenCExpr(LC_AST *n) {
LC_ASSERT(n, LC_IsExpr(n));
intptr_t is_any = (intptr_t)LC_MapGetP(&L->implicit_any, n);
if (is_any) LC_Genf("(LC_Any){%d, (%s[]){", n->type->id, LC_GenCType(n->type, ""));
if (n->const_val.type) {
bool contains_sizeof_like = LC_GenCInternalGenerateSizeofs ? LC_ContainsCBuiltin(n) : false;
if (!contains_sizeof_like) {
if (LC_IsUTStr(n->const_val.type)) {
LC_GenCString((char *)n->const_val.name, n->type);
} else {
char *val = LC_GenCVal(n->const_val, n->type);
LC_Genf("%s", val);
}
if (is_any) LC_Genf("}}");
return;
}
}
LC_Type *type = n->type;
switch (n->kind) {
case LC_ASTKind_ExprIdent: {
LC_Genf("%s", (char *)n->eident.resolved_decl->foreign_name);
} break;
case LC_ASTKind_ExprCast: {
LC_Genf("(");
LC_Genf("(%s)", LC_GenCType(type, ""));
LC_Genf("(");
LC_GenCExpr(n->ecast.expr);
LC_Genf(")");
LC_Genf(")");
} break;
case LC_ASTKind_ExprUnary: {
LC_Genf("%s(", LC_TokenKindToOperator(n->eunary.op));
LC_GenCExpr(n->eunary.expr);
LC_Genf(")");
} break;
case LC_ASTKind_ExprAddPtr: {
LC_Genf("(");
LC_GenCExpr(n->ebinary.left);
LC_Genf("+");
LC_GenCExpr(n->ebinary.right);
LC_Genf(")");
} break;
case LC_ASTKind_ExprBinary: {
LC_Genf("(");
LC_GenCExpr(n->ebinary.left);
LC_Genf("%s", LC_TokenKindToOperator(n->ebinary.op));
LC_GenCExpr(n->ebinary.right);
LC_Genf(")");
} break;
case LC_ASTKind_ExprIndex: {
LC_Genf("(");
LC_GenCExpr(n->eindex.base);
LC_Genf("[");
LC_GenCExpr(n->eindex.index);
LC_Genf("]");
LC_Genf(")");
} break;
case LC_ASTKind_ExprGetValueOfPointer: {
LC_Genf("(*(");
LC_GenCExpr(n->eunary.expr);
LC_Genf("))");
} break;
case LC_ASTKind_ExprGetPointerOfValue: {
LC_Genf("(&(");
LC_GenCExpr(n->eunary.expr);
LC_Genf("))");
} break;
case LC_ASTKind_ExprField: {
if (n->efield.parent_decl->kind != LC_DeclKind_Import) {
LC_Type *left_type = n->efield.left->type;
LC_GenCExpr(n->efield.left);
if (LC_IsPtr(left_type)) LC_Genf("->");
else LC_Genf(".");
LC_Genf("%s", (char *)n->efield.right);
} else {
LC_Genf("%s", (char *)n->efield.resolved_decl->foreign_name);
}
} break;
case LC_ASTKind_ExprCall: {
LC_ResolvedCompo *rd = n->ecompo.resolved_items;
LC_GenCExpr(n->ecompo.name);
LC_Genf("(");
for (LC_ResolvedCompoItem *it = rd->first; it; it = it->next) {
LC_GenCExpr(it->expr);
if (it->next) LC_Genf(", ");
}
LC_Genf(")");
} break;
case LC_ASTKind_ExprCompound: {
LC_Genf("(%s)", LC_GenCType(type, ""));
LC_GenCCompound(n);
} break;
case LC_ASTKind_ExprBuiltin: {
LC_ASSERT(n, n->ecompo.name->kind == LC_ASTKind_ExprIdent);
if (n->ecompo.name->eident.name == L->isizeof) {
LC_Genf("sizeof(");
LC_AST *expr = n->ecompo.first->ecompo_item.expr;
if (expr->kind == LC_ASTKind_ExprType) {
LC_Genf("%s", LC_GenCType(expr->type, ""));
} else {
LC_GenCExpr(expr);
}
LC_Genf(")");
} else if (n->ecompo.name->eident.name == L->ialignof) {
LC_Genf("LC_Alignof(");
LC_AST *expr = n->ecompo.first->ecompo_item.expr;
if (expr->kind == LC_ASTKind_ExprType) {
LC_Genf("%s", LC_GenCType(expr->type, ""));
} else {
LC_GenCExpr(expr);
}
LC_Genf(")");
} else if (n->ecompo.name->eident.name == L->ioffsetof) {
LC_AST *i1 = n->ecompo.first->ecompo_item.expr;
LC_AST *i2 = n->ecompo.first->next->ecompo_item.expr;
LC_Genf("offsetof(%s, %s)", LC_GenCType(i1->type, ""), (char *)i2->eident.name);
} else {
LC_ReportASTError(n, "internal compiler error: got unhandled case in %s / LC_ASTKind_ExprBuiltin", __FUNCTION__);
}
} break;
default: LC_ReportASTError(n, "internal compiler error: got unhandled case in %s", __FUNCTION__);
}
if (is_any) LC_Genf("}}");
}
const int GC_Stmt_OmitSemicolonAndNewLine = 1;
LC_FUNCTION void LC_GenCNote(LC_AST *note) {
if (note->ecompo.name->eident.name == L->ic) {
LC_Genf("%s", (char *)LC_GetStringFromSingleArgNote(note));
}
}
LC_FUNCTION void LC_GenCVarExpr(LC_AST *n, bool is_declaration) {
if (LC_HasNote(n, L->inot_init)) return;
LC_AST *e = n->dvar.expr;
if (n->kind == LC_ASTKind_StmtVar) e = n->svar.expr;
if (e) {
LC_Genf(" = ");
if (e->kind == LC_ASTKind_ExprNote) {
LC_GenCNote(e->enote.expr);
} else if (is_declaration && e->kind == LC_ASTKind_ExprCompound) {
LC_GenCCompound(e);
} else {
LC_GenCExpr(e);
}
} else {
LC_Genf(" = {0}");
}
}
LC_FUNCTION void LC_GenCDefers(LC_AST *block) {
LC_AST *first = block->sblock.first_defer;
if (first == NULL) return;
int save = L->printer.last_line_num;
LC_GenLine();
LC_GenLastCLineDirective();
LC_GenLinef("/*defer*/");
for (LC_AST *it = first; it; it = it->sdefer.next) {
LC_GenCStmtBlock(it->sdefer.body);
}
L->printer.last_line_num = save + 1;
LC_GenLine();
LC_GenLastCLineDirective();
}
LC_FUNCTION void LC_GenCDefersLoopBreak(LC_AST *n) {
LC_ASSERT(n, n->kind == LC_ASTKind_StmtBreak || n->kind == LC_ASTKind_StmtContinue);
LC_AST *it = NULL;
for (int i = L->printer.out_block_stack.len - 1; i >= 0; i -= 1) {
it = L->printer.out_block_stack.data[i];
LC_GenCDefers(it);
LC_ASSERT(it, it->sblock.kind != SBLK_Proc);
if (it->sblock.kind == SBLK_Loop) {
if (!n->sbreak.name) break;
if (n->sbreak.name && it->sblock.name == n->sbreak.name) break;
}
}
LC_ASSERT(it, it->sblock.kind == SBLK_Loop);
}
LC_FUNCTION void LC_GenCDefersReturn(LC_AST *n) {
LC_ASSERT(n, n->kind == LC_ASTKind_StmtReturn);
LC_AST *it = NULL;
for (int i = L->printer.out_block_stack.len - 1; i >= 0; i -= 1) {
it = L->printer.out_block_stack.data[i];
LC_GenCDefers(it);
if (it->sblock.kind == SBLK_Proc) {
break;
}
}
LC_ASSERT(it, it);
LC_ASSERT(it, it->sblock.kind == SBLK_Proc);
}
LC_FUNCTION void LC_GenCStmt2(LC_AST *n, int flags) {
LC_ASSERT(n, LC_IsStmt(n));
bool semicolon = !(flags & GC_Stmt_OmitSemicolonAndNewLine);
if (semicolon) {
LC_GenLine();
}
switch (n->kind) {
case LC_ASTKind_StmtVar: {
LC_Type *type = n->type;
LC_Genf("%s", LC_GenCType(type, (char *)n->svar.name));
LC_GenCVarExpr(n, true);
} break;
case LC_ASTKind_StmtExpr: LC_GenCExpr(n->sexpr.expr); break;
case LC_ASTKind_StmtAssign: {
// Assigning to array doesn't work in C so we need to handle that
// specific compo case here. :CompoArray
if (LC_IsArray(n->type) && n->sassign.right->kind == LC_ASTKind_ExprCompound) {
LC_ASSERT(n, n->sassign.op == LC_TokenKind_Assign);
LC_AST *expr = n->sassign.right;
LC_Genf("memset(");
LC_GenCExpr(n->sassign.left);
LC_Genf(", 0, sizeof(");
LC_GenCExpr(n->sassign.left);
LC_Genf("));");
LC_ResolvedArrayCompo *rd = expr->ecompo.resolved_array_items;
for (LC_ResolvedCompoArrayItem *it = rd->first; it; it = it->next) {
LC_GenCExpr(n->sassign.left);
LC_Genf("[%d] = ", it->index);
LC_GenCExpr(it->comp->ecompo_item.expr);
LC_Genf(";");
}
} else {
LC_GenCExpr(n->sassign.left);
LC_Genf(" %s ", LC_TokenKindToOperator(n->sassign.op));
LC_GenCExpr(n->sassign.right);
}
} break;
default: LC_ReportASTError(n, "internal compiler error: got unhandled case in %s", __FUNCTION__);
}
if (semicolon) LC_Genf(";");
}
LC_FUNCTION void LC_GenCStmt(LC_AST *n) {
LC_ASSERT(n, LC_IsStmt(n));
LC_GenCLineDirective(n);
switch (n->kind) {
case LC_ASTKind_StmtConst:
case LC_ASTKind_StmtDefer: break;
case LC_ASTKind_StmtNote: {
LC_GenLine();
LC_GenCNote(n->snote.expr);
LC_Genf(";");
} break;
case LC_ASTKind_StmtReturn: {
LC_GenCDefersReturn(n);
LC_GenLinef("return");
if (n->sreturn.expr) {
LC_Genf(" ");
LC_GenCExpr(n->sreturn.expr);
}
LC_Genf(";");
} break;
case LC_ASTKind_StmtContinue:
case LC_ASTKind_StmtBreak: {
const char *stmt = n->kind == LC_ASTKind_StmtBreak ? "break" : "continue";
LC_GenCDefersLoopBreak(n);
if (n->sbreak.name) {
LC_GenLinef("goto %s_%s;", (char *)n->sbreak.name, stmt);
} else {
LC_GenLinef("%s;", stmt);
}
} break;
case LC_ASTKind_StmtBlock: {
LC_GenLinef("/*block*/");
LC_GenCStmtBlock(n);
} break;
case LC_ASTKind_StmtSwitch: {
LC_GenLinef("switch(");
LC_GenCExpr(n->sswitch.expr);
LC_Genf(") {");
L->printer.indent += 1;
LC_ASTFor(it, n->sswitch.first) {
LC_GenCLineDirective(it);
if (it->kind == LC_ASTKind_StmtSwitchCase) {
LC_ASTFor(label_it, it->scase.first) {
LC_GenLinef("case ");
LC_GenCExpr(label_it);
LC_Genf(":");
}
}
if (it->kind == LC_ASTKind_StmtSwitchDefault) {
LC_GenLinef("default:");
}
LC_GenCStmtBlock(it->scase.body);
if (LC_HasNote(it, L->ifallthrough)) {
LC_Genf(" /*@fallthough*/");
} else {
LC_Genf(" break;");
}
}
L->printer.indent -= 1;
LC_GenLinef("}");
} break;
case LC_ASTKind_StmtFor: {
LC_GenLinef("for (");
if (n->sfor.init) LC_GenCStmt2(n->sfor.init, GC_Stmt_OmitSemicolonAndNewLine);
LC_Genf(";");
if (n->sfor.cond) {
LC_Genf(" ");
LC_GenCExpr(n->sfor.cond);
}
LC_Genf(";");
if (n->sfor.inc) {
LC_Genf(" ");
LC_GenCStmt2(n->sfor.inc, GC_Stmt_OmitSemicolonAndNewLine);
}
LC_Genf(")");
LC_GenCStmtBlock(n->sfor.body);
} break;
case LC_ASTKind_StmtIf: {
LC_GenLinef("if ");
LC_GenCExprParen(n->sif.expr);
LC_GenCStmtBlock(n->sif.body);
LC_ASTFor(it, n->sif.first) {
LC_GenCLineDirective(it);
LC_GenLinef("else");
if (it->kind == LC_ASTKind_StmtElseIf) {
LC_Genf(" if ");
LC_GenCExprParen(it->sif.expr);
}
LC_GenCStmtBlock(it->sif.body);
}
} break;
default: LC_GenCStmt2(n, 0);
}
}
LC_FUNCTION void LC_GenCExprParen(LC_AST *expr) {
bool paren = expr->kind != LC_ASTKind_ExprBinary;
if (paren) LC_Genf("(");
LC_GenCExpr(expr);
if (paren) LC_Genf(")");
}
LC_FUNCTION void LC_GenCStmtBlock(LC_AST *n) {
LC_PushAST(&L->printer.out_block_stack, n);
LC_ASSERT(n, n->kind == LC_ASTKind_StmtBlock);
LC_Genf(" {");
L->printer.indent += 1;
LC_ASTFor(it, n->sblock.first) {
LC_GenCStmt(it);
}
LC_GenCDefers(n);
if (n->sblock.name) LC_GenLinef("%s_continue:;", (char *)n->sblock.name);
L->printer.indent -= 1;
LC_GenLinef("}");
if (n->sblock.name) LC_GenLinef("%s_break:;", (char *)n->sblock.name);
LC_PopAST(&L->printer.out_block_stack);
}
LC_FUNCTION void LC_GenCProcDecl(LC_Decl *decl) {
LC_StringList out = {0};
LC_Type *type = decl->type;
LC_AST *n = decl->ast;
LC_AST *typespec = n->dproc.type;
LC_Addf(L->arena, &out, "%s(", (char *)decl->foreign_name);
if (type->tagg.mems.count == 0) {
LC_Addf(L->arena, &out, "void");
} else {
int i = 0;
LC_ASTFor(it, typespec->tproc.first) {
LC_Type *type = it->type;
LC_Addf(L->arena, &out, "%s%s", i == 0 ? "" : ", ", LC_GenCType(type, (char *)it->tproc_arg.name));
i += 1;
}
}
if (type->tproc.vargs) {
LC_Addf(L->arena, &out, ", ...");
}
LC_Addf(L->arena, &out, ")");
char *front = LC_MergeString(L->arena, out).str;
char *result = LC_GenCType(type->tproc.ret, front);
LC_GenLine();
bool is_public = LC_HasNote(n, L->iapi) || decl->foreign_name == L->imain;
if (!is_public) LC_Genf("static ");
LC_Genf("%s", result);
}
LC_FUNCTION void LC_GenCAggForwardDecl(LC_Decl *decl) {
LC_ASSERT(decl->ast, LC_IsAgg(decl->ast));
char *agg = LC_GenLCAggName(decl->type);
LC_GenLinef("typedef %s %s %s;", agg, (char *)decl->foreign_name, (char *)decl->foreign_name);
}
LC_FUNCTION void LC_GenCTypeDecl(LC_Decl *decl) {
LC_AST *n = decl->ast;
LC_ASSERT(n, decl->kind == LC_DeclKind_Type);
if (n->kind == LC_ASTKind_DeclTypedef) {
LC_Type *type = decl->typedef_renamed_type_decl ? decl->typedef_renamed_type_decl->type : decl->type;
LC_GenLinef("typedef %s;", LC_GenCType(type, (char *)decl->foreign_name));
} else {
LC_Type *type = decl->type;
LC_Intern name = decl->foreign_name;
{
bool packed = LC_HasNote(n, L->ipacked) ? true : false;
if (packed) LC_GenLinef("#pragma pack(push, 1)");
LC_GenLinef("%s %s {", LC_GenLCAggName(type), name ? (char *)name : "");
L->printer.indent += 1;
for (LC_TypeMember *it = type->tagg.mems.first; it; it = it->next) {
LC_GenLinef("%s;", LC_GenCType(it->type, (char *)it->name));
}
L->printer.indent -= 1;
LC_GenLinef("};");
if (packed) LC_GenLinef("#pragma pack(pop)");
LC_GenLine();
}
}
}
LC_FUNCTION void LC_GenCVarFDecl(LC_Decl *decl) {
if (!LC_HasNote(decl->ast, L->iapi)) return;
LC_Type *type = decl->type; // make string arrays assignable
LC_GenLinef("extern ");
if (LC_HasNote(decl->ast, L->ithread_local)) LC_Genf("_Thread_local ");
LC_Genf("%s;", LC_GenCType(type, (char *)decl->foreign_name));
}
LC_FUNCTION void LC_GenCHeader(LC_AST *package) {
// C notes
LC_ASTFor(file, package->apackage.ffile) {
LC_ASTFor(it, file->afile.fdecl) {
if (it->kind != LC_ASTKind_DeclNote) continue;
LC_AST *note = it->dnote.expr;
if (note->ecompo.name->eident.name == L->ic) {
LC_GenLinef("%s", (char *)LC_GetStringFromSingleArgNote(note));
}
}
}
// struct forward decls
LC_DeclFor(decl, package->apackage.first_ordered) {
if (decl->is_foreign) continue;
LC_AST *n = decl->ast;
if (decl->kind == LC_DeclKind_Type && LC_IsAgg(n)) LC_GenCAggForwardDecl(decl);
}
// type decls
LC_GenLine();
LC_DeclFor(decl, package->apackage.first_ordered) {
if (decl->is_foreign) continue;
LC_AST *n = decl->ast;
if (decl->kind == LC_DeclKind_Type) LC_GenCTypeDecl(decl);
}
// proc and var forward decls
LC_DeclFor(decl, package->apackage.first_ordered) {
if (decl->is_foreign) continue;
LC_AST *n = decl->ast;
if (decl->kind == LC_DeclKind_Var) {
LC_GenCVarFDecl(decl);
} else if (decl->kind == LC_DeclKind_Proc) {
LC_GenCProcDecl(decl);
LC_Genf(";");
}
}
}
LC_FUNCTION void LC_GenCImpl(LC_AST *package) {
// implementation of vars
LC_DeclFor(decl, package->apackage.first_ordered) {
if (decl->kind == LC_DeclKind_Var && !decl->is_foreign) {
LC_AST *n = decl->ast;
LC_Type *type = decl->type; // make string arrays assignable
LC_GenLine();
if (!LC_HasNote(n, L->iapi)) LC_Genf("static ");
if (LC_HasNote(n, L->ithread_local)) LC_Genf("_Thread_local ");
LC_Genf("%s", LC_GenCType(type, (char *)decl->foreign_name));
GC_SpecialCase_GlobalScopeStringDecl = true;
LC_GenCVarExpr(n, true);
GC_SpecialCase_GlobalScopeStringDecl = false;
LC_Genf(";");
LC_GenLine();
}
}
// implementation of procs
LC_DeclFor(decl, package->apackage.first_ordered) {
LC_AST *n = decl->ast;
if (decl->kind == LC_DeclKind_Proc && n->dproc.body && !decl->is_foreign) {
LC_GenCLineDirective(n);
LC_GenCProcDecl(decl);
LC_GenCStmtBlock(n->dproc.body);
LC_GenLine();
}
}
}

259
src/compiler/init.c Normal file
View File

@@ -0,0 +1,259 @@
LC_FUNCTION LC_Lang *LC_LangAlloc(void) {
LC_Arena *arena = LC_BootstrapArena();
LC_Arena *lex_arena = LC_PushStruct(arena, LC_Arena);
LC_Arena *decl_arena = LC_PushStruct(arena, LC_Arena);
LC_Arena *ast_arena = LC_PushStruct(arena, LC_Arena);
LC_Arena *type_arena = LC_PushStruct(arena, LC_Arena);
LC_InitArena(lex_arena);
LC_InitArena(decl_arena);
LC_InitArena(ast_arena);
LC_InitArena(type_arena);
LC_Lang *l = LC_PushStruct(arena, LC_Lang);
l->arena = arena;
l->lex_arena = lex_arena;
l->decl_arena = decl_arena;
l->ast_arena = ast_arena;
l->type_arena = type_arena;
l->emit_line_directives = true;
l->breakpoint_on_error = true;
l->use_colored_terminal_output = true;
return l;
}
LC_FUNCTION void LC_LangEnd(LC_Lang *lang) {
LC_DeallocateArena(lang->lex_arena);
LC_DeallocateArena(lang->type_arena);
LC_DeallocateArena(lang->decl_arena);
LC_DeallocateArena(lang->ast_arena);
LC_DeallocateArena(lang->arena);
if (L == lang) L = NULL;
}
LC_FUNCTION void LC_LangBegin(LC_Lang *l) {
L = l;
// Init default target settings
{
if (L->os == LC_OS_Invalid) {
L->os = LC_OS_LINUX;
#if LC_OPERATING_SYSTEM_WINDOWS
L->os = LC_OS_WINDOWS;
#elif LC_OPERATING_SYSTEM_MAC
L->os = LC_OS_MAC;
#endif
}
if (L->arch == LC_ARCH_Invalid) {
L->arch = LC_ARCH_X64;
}
if (L->gen == LC_GEN_Invalid) {
L->gen = LC_GEN_C;
}
}
//
// Init declared notes, interns and foreign names checker
//
{
L->declared_notes.arena = L->arena;
L->interns.arena = L->arena;
L->foreign_names.arena = L->arena;
L->implicit_any.arena = L->arena;
LC_MapReserve(&L->declared_notes, 128);
LC_MapReserve(&L->interns, 4096);
LC_MapReserve(&L->foreign_names, 256);
LC_MapReserve(&L->implicit_any, 64);
#define X(x) l->k##x = LC_InternStrLen(#x, sizeof(#x) - 1);
LC_LIST_KEYWORDS
#undef X
l->first_keyword = l->kfor;
l->last_keyword = l->kfalse;
#define X(x, declare) l->i##x = LC_InternStrLen(#x, sizeof(#x) - 1);
LC_LIST_INTERNS
#undef X
#define X(x, declare) \
if (declare) LC_DeclareNote(L->i##x);
LC_LIST_INTERNS
#undef X
}
// Nulls
{
L->NullLEX.begin = "builtin declarations";
L->NullLEX.file = LC_ILit("builtin declarations");
L->BuiltinToken.lex = &L->NullLEX;
L->BuiltinToken.str = "builtin declarations";
L->BuiltinToken.len = sizeof("builtin declarations") - 1;
L->NullAST.pos = &L->BuiltinToken;
}
{
LC_AST *builtins = LC_CreateAST(0, LC_ASTKind_Package);
L->builtin_package = builtins;
builtins->apackage.name = LC_ILit("builtins");
builtins->apackage.scope = LC_CreateScope(256);
LC_AddPackageToList(builtins);
}
LC_InitDeclStack(&L->resolver.locals, 128);
L->resolver.duplicate_map.arena = L->arena;
LC_MapReserve(&L->resolver.duplicate_map, 32);
L->resolver.stmt_block_stack.arena = L->arena;
L->printer.out_block_stack.arena = L->arena;
LC_PUSH_PACKAGE(L->builtin_package);
//
// Init default type sizes using current platform
//
// Here we use the sizes of our current platform but
// later on it gets swapped based on LC override global variables in
// InitTarget
//
{
l->type_map.arena = L->arena;
LC_MapReserve(&l->type_map, 256);
typedef long long llong;
typedef unsigned long long ullong;
typedef unsigned long ulong;
typedef unsigned short ushort;
typedef unsigned char uchar;
typedef unsigned int uint;
L->pointer_align = LC_Alignof(void *);
L->pointer_size = sizeof(void *);
int i = 0;
#define X(TNAME, IS_UNSIGNED) \
l->types[i].kind = LC_TypeKind_##TNAME; \
l->types[i].size = sizeof(TNAME); \
l->types[i].align = LC_Alignof(TNAME); \
l->types[i].is_unsigned = IS_UNSIGNED; \
l->t##TNAME = l->types + i++;
LC_LIST_TYPES
#undef X
//
// Overwrite types with target
//
if (L->arch == LC_ARCH_X64) {
LC_SetPointerSizeAndAlign(8, 8);
if (L->os == LC_OS_WINDOWS) {
L->tlong->size = 4;
L->tlong->align = 4;
L->tulong->size = 4;
L->tulong->align = 4;
} else {
L->tlong->size = 8;
L->tlong->align = 8;
L->tulong->size = 8;
L->tulong->align = 8;
}
} else if (L->arch == LC_ARCH_X86) {
LC_SetPointerSizeAndAlign(4, 4);
L->tlong->size = 4;
L->tlong->align = 4;
L->tulong->size = 4;
L->tulong->align = 4;
}
l->types[i].kind = LC_TypeKind_void;
l->tvoid = l->types + i++;
// Init decls for types
for (int i = 0; i < T_Count; i += 1) {
char *it = (char *)LC_TypeKindToString((LC_TypeKind)i) + 12;
LC_Intern intern = LC_ILit(it);
LC_Type *t = l->types + i;
t->id = ++l->typeids;
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Type, intern, &L->NullAST);
decl->state = LC_DeclState_Resolved;
decl->type = t;
t->decl = decl;
LC_AddDeclToScope(L->builtin_package->apackage.scope, decl);
if (t->kind == LC_TypeKind_uchar) decl->foreign_name = LC_ILit("unsigned char");
if (t->kind == LC_TypeKind_ushort) decl->foreign_name = LC_ILit("unsigned short");
if (t->kind == LC_TypeKind_uint) decl->foreign_name = LC_ILit("unsigned");
if (t->kind == LC_TypeKind_ulong) decl->foreign_name = LC_ILit("unsigned long");
if (t->kind == LC_TypeKind_llong) decl->foreign_name = LC_ILit("long long");
if (t->kind == LC_TypeKind_ullong) decl->foreign_name = LC_ILit("unsigned long long");
}
}
l->tpvoid = LC_CreatePointerType(l->tvoid);
l->tpchar = LC_CreatePointerType(l->tchar);
{
l->tuntypedint = LC_CreateUntypedInt(L->tint);
l->tuntypedbool = LC_CreateUntypedInt(L->tbool);
l->tuntypednil = LC_CreateUntypedInt(L->tullong);
l->ttuntypedfloat.kind = LC_TypeKind_UntypedFloat;
l->ttuntypedfloat.id = ++L->typeids;
l->tuntypedfloat = &L->ttuntypedfloat;
l->tuntypedfloat->tutdefault = l->tdouble;
l->tuntypedfloat->decl = LC_CreateDecl(LC_DeclKind_Type, LC_ILit("UntypedFloat"), &L->NullAST);
l->ttuntypedstring.kind = LC_TypeKind_UntypedString;
l->ttuntypedstring.id = ++L->typeids;
l->tuntypedstring = &L->ttuntypedstring;
l->tuntypedstring->tutdefault = l->tpchar;
l->tuntypedstring->decl = LC_CreateDecl(LC_DeclKind_Type, LC_ILit("UntypedString"), &L->NullAST);
}
// Add builtin "String" type
{
L->ttstring.kind = LC_TypeKind_Incomplete;
L->ttstring.id = ++L->typeids;
L->tstring = &L->ttstring;
LC_AST *ast = LC_ParseDeclf("String :: struct { str: *char; len: int; }");
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Type, ast->dbase.name, ast);
decl->foreign_name = LC_ILit("LC_String");
decl->state = LC_DeclState_Resolved;
decl->type = L->tstring;
L->tstring->decl = decl;
LC_AddDeclToScope(L->builtin_package->apackage.scope, decl);
LC_Operand result = LC_ResolveTypeAggregate(ast, decl->type);
LC_ASSERT(ast, !LC_IsError(result));
}
// Add builtin "Any" type
{
L->ttany.kind = LC_TypeKind_Incomplete;
L->ttany.id = ++L->typeids;
L->tany = &L->ttany;
LC_AST *ast = LC_ParseDeclf("Any :: struct { type: int; data: *void; }");
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Type, ast->dbase.name, ast);
decl->foreign_name = LC_ILit("LC_Any");
decl->state = LC_DeclState_Resolved;
decl->type = L->tany;
L->tany->decl = decl;
LC_AddDeclToScope(L->builtin_package->apackage.scope, decl);
LC_Operand result = LC_ResolveTypeAggregate(ast, decl->type);
LC_ASSERT(ast, !LC_IsError(result));
}
LC_Decl *decl_nil = LC_AddConstIntDecl("nil", 0);
decl_nil->type = L->tuntypednil;
for (int i = LC_ARCH_X64; i < LC_ARCH_Count; i += 1) LC_AddBuiltinConstInt((char *)LC_ARCHToString((LC_ARCH)i), i);
for (int i = LC_OS_WINDOWS; i < LC_OS_Count; i += 1) LC_AddBuiltinConstInt((char *)LC_OSToString((LC_OS)i), i);
for (int i = LC_GEN_C; i < LC_GEN_Count; i += 1) LC_AddBuiltinConstInt((char *)LC_GENToString((LC_GEN)i), i);
LC_AddBuiltinConstInt("LC_ARCH", L->arch);
LC_AddBuiltinConstInt("LC_GEN", L->gen);
LC_AddBuiltinConstInt("LC_OS", L->os);
LC_POP_PACKAGE();
}

44
src/compiler/intern.c Normal file
View File

@@ -0,0 +1,44 @@
typedef struct {
int len;
char str[];
} INTERN_Entry;
LC_FUNCTION LC_Intern LC_InternStrLen(char *str, int len) {
LC_String key = LC_MakeString(str, len);
INTERN_Entry *entry = (INTERN_Entry *)LC_MapGet(&L->interns, key);
if (entry == NULL) {
LC_ASSERT(NULL, sizeof(INTERN_Entry) == sizeof(int));
entry = (INTERN_Entry *)LC_PushSize(L->arena, sizeof(int) + sizeof(char) * (len + 1));
entry->len = len;
LC_MemoryCopy(entry->str, str, len);
entry->str[len] = 0;
LC_MapInsert(&L->interns, key, entry);
}
return (uintptr_t)entry->str;
}
LC_FUNCTION LC_Intern LC_ILit(char *str) {
return LC_InternStrLen(str, (int)LC_StrLen(str));
}
LC_FUNCTION LC_Intern LC_GetUniqueIntern(const char *name_for_debug) {
LC_String name = LC_Format(L->arena, "U%u_%s", ++L->unique_counter, name_for_debug);
LC_Intern result = LC_InternStrLen(name.str, (int)name.len);
return result;
}
LC_FUNCTION char *LC_GetUniqueName(const char *name_for_debug) {
LC_String name = LC_Format(L->arena, "U%u_%s", ++L->unique_counter, name_for_debug);
return name.str;
}
LC_FUNCTION void LC_DeclareNote(LC_Intern intern) {
LC_MapInsertU64(&L->declared_notes, intern, (void *)intern);
}
LC_FUNCTION bool LC_IsNoteDeclared(LC_Intern intern) {
void *p = LC_MapGetU64(&L->declared_notes, intern);
bool result = p != NULL;
return result;
}

535
src/compiler/lex.c Normal file
View File

@@ -0,0 +1,535 @@
LC_FUNCTION void LC_LexingError(LC_Token *pos, const char *str, ...) {
LC_FORMAT(L->arena, str, s8);
LC_SendErrorMessage(pos, s8);
L->errors += 1;
pos->kind = LC_TokenKind_Error;
}
#define LC_IF(cond, ...) \
do { \
if (cond) { \
LC_LexingError(t, __VA_ARGS__); \
return; \
} \
} while (0)
LC_FUNCTION bool LC_IsAssign(LC_TokenKind kind) {
bool result = kind >= LC_TokenKind_Assign && kind <= LC_TokenKind_RightShiftAssign;
return result;
}
LC_FUNCTION bool LC_IsHexDigit(char c) {
bool result = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
return result;
}
LC_FUNCTION bool LC_IsBinDigit(char c) {
bool result = (c >= '0' && c <= '1');
return result;
}
LC_FUNCTION uint64_t LC_MapCharToNumber(char c) {
// clang-format off
switch (c) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'a': case 'A': return 10;
case 'b': case 'B': return 11;
case 'c': case 'C': return 12;
case 'd': case 'D': return 13;
case 'e': case 'E': return 14;
case 'f': case 'F': return 15;
default: return 255;
}
// clang-format on
}
LC_FUNCTION uint64_t LC_GetEscapeCode(char c) {
switch (c) {
case 'a': return '\a';
case 'b': return '\b';
case 'e': return 0x1B;
case 'f': return '\f';
case 'n': return '\n';
case 'r': return '\r';
case 't': return '\t';
case 'v': return '\v';
case '\\': return '\\';
case '\'': return '\'';
case '\"': return '\"';
case '0': return '\0';
default: return UINT64_MAX;
}
}
LC_FUNCTION LC_String LC_GetEscapeString(char c) {
switch (c) {
case '\a': return LC_Lit("\\a");
case '\b': return LC_Lit("\\b");
case 0x1B: return LC_Lit("\\x1B");
case '\f': return LC_Lit("\\f");
case '\n': return LC_Lit("\\n");
case '\r': return LC_Lit("\\r");
case '\t': return LC_Lit("\\t");
case '\v': return LC_Lit("\\v");
case '\\': return LC_Lit("\\\\");
case '\'': return LC_Lit("\\'");
case '\"': return LC_Lit("\\\"");
case '\0': return LC_Lit("\\0");
default: return LC_Lit("");
}
}
LC_FUNCTION void LC_LexAdvance(LC_Lex *x) {
if (x->at[0] == 0) {
return;
} else if (x->at[0] == '\n') {
x->line += 1;
x->column = 0;
}
x->column += 1;
x->at += 1;
}
LC_FUNCTION void LC_EatWhitespace(LC_Lex *x) {
while (LC_IsWhitespace(x->at[0])) LC_LexAdvance(x);
}
LC_FUNCTION void LC_EatIdent(LC_Lex *x) {
while (x->at[0] == '_' || LC_IsAlphanumeric(x->at[0])) LC_LexAdvance(x);
}
LC_FUNCTION void LC_SetTokenLen(LC_Lex *x, LC_Token *t) {
t->len = (int)(x->at - t->str);
LC_ASSERT(NULL, t->len < 2000000000);
}
LC_FUNCTION void LC_EatUntilIncluding(LC_Lex *x, char c) {
while (x->at[0] != 0 && x->at[0] != c) LC_LexAdvance(x);
LC_LexAdvance(x);
}
// @todo: add temporary allocation + copy at end to perm
LC_FUNCTION LC_BigInt LC_LexBigInt(char *string, int len, uint64_t base) {
LC_ASSERT(NULL, base >= 2 && base <= 16);
LC_BigInt m = LC_Bigint_u64(1);
LC_BigInt base_mul = LC_Bigint_u64(base);
LC_BigInt result = LC_Bigint_u64(0);
LC_BigInt tmp = {0};
for (int i = len - 1; i >= 0; --i) {
uint64_t u = LC_MapCharToNumber(string[i]);
LC_ASSERT(NULL, u < base);
LC_BigInt val = LC_Bigint_u64(u);
LC_Bigint_mul(&tmp, &val, &m);
LC_BigInt new_val = tmp;
LC_Bigint_add(&tmp, &result, &new_val);
result = tmp;
LC_Bigint_mul(&tmp, &m, &base_mul);
m = tmp;
}
return result;
}
LC_FUNCTION void LC_LexNestedComments(LC_Lex *x, LC_Token *t) {
t->kind = LC_TokenKind_Comment;
LC_LexAdvance(x);
if (x->at[0] == '*') {
LC_LexAdvance(x);
t->kind = LC_TokenKind_DocComment;
if (x->at[0] == ' ' && x->at[1] == 'f' && x->at[2] == 'i' && x->at[3] == 'l' && x->at[4] == 'e') {
t->kind = LC_TokenKind_FileDocComment;
}
if (x->at[0] == ' ' && x->at[1] == 'p' && x->at[2] == 'a' && x->at[3] == 'c' && x->at[4] == 'k' && x->at[5] == 'a' && x->at[6] == 'g' && x->at[7] == 'e') {
t->kind = LC_TokenKind_PackageDocComment;
}
}
int counter = 0;
for (;;) {
if (x->at[0] == '*' && x->at[1] == '/') {
if (counter <= 0) break;
counter -= 1;
} else if (x->at[0] == '/' && x->at[1] == '*') {
counter += 1;
LC_LexAdvance(x);
}
LC_IF(x->at[0] == 0, "Unclosed block comment");
LC_LexAdvance(x);
}
t->str += 2;
LC_SetTokenLen(x, t);
LC_LexAdvance(x);
LC_LexAdvance(x);
}
LC_FUNCTION void LC_LexStringLiteral(LC_Lex *x, LC_Token *t, LC_TokenKind kind) {
t->kind = kind;
if (kind == LC_TokenKind_RawString) {
LC_EatUntilIncluding(x, '`');
} else if (kind == LC_TokenKind_String) {
for (;;) {
LC_IF(x->at[0] == '\n', "got a new line while parsing a '\"' string literal");
LC_IF(x->at[0] == 0, "reached end of file during string lexing");
if (x->at[0] == '"') break;
if (x->at[0] == '\\' && x->at[1] == '"') LC_LexAdvance(x);
LC_LexAdvance(x);
}
LC_LexAdvance(x);
} else {
LC_IF(1, "internal compiler error: unhandled case in %s", __FUNCTION__);
}
LC_SetTokenLen(x, t);
t->len -= 2;
t->str += 1;
}
LC_FUNCTION void LC_LexUnicodeLiteral(LC_Lex *x, LC_Token *t) {
t->kind = LC_TokenKind_Unicode;
LC_UTF32Result decode = LC_ConvertUTF8ToUTF32(x->at, 4);
LC_IF(decode.error, "invalid utf8 sequence");
uint8_t c[8] = {0};
for (int i = 0; i < decode.advance; i += 1) {
c[i] = x->at[0];
LC_LexAdvance(x);
}
uint64_t result = *(uint64_t *)&c[0];
if (result == '\\') {
LC_ASSERT(NULL, decode.advance == 1);
result = LC_GetEscapeCode(x->at[0]);
LC_IF(result == UINT64_MAX, "invalid escape code");
LC_LexAdvance(x);
}
LC_IF(x->at[0] != '\'', "unclosed unicode literal");
LC_Bigint_init_signed(&t->i, result);
LC_LexAdvance(x);
LC_SetTokenLen(x, t);
t->str += 1;
t->len -= 2;
LC_IF(t->len == 0, "empty unicode literal");
}
LC_FUNCTION void LC_LexIntOrFloat(LC_Lex *x, LC_Token *t) {
t->kind = LC_TokenKind_Int;
for (;;) {
if (x->at[0] == '.') {
LC_IF(t->kind == LC_TokenKind_Float, "failed to parse a floating point number, invalid format, found multiple '.'");
if (t->kind == LC_TokenKind_Int) t->kind = LC_TokenKind_Float;
} else if (!LC_IsDigit(x->at[0])) break;
LC_LexAdvance(x);
}
LC_SetTokenLen(x, t);
if (t->kind == LC_TokenKind_Int) {
t->i = LC_LexBigInt(t->str, t->len, 10);
} else if (t->kind == LC_TokenKind_Float) {
t->f64 = LC_ParseFloat(t->str, t->len);
} else {
LC_IF(1, "internal compiler error: unhandled case in %s", __FUNCTION__);
}
}
LC_FUNCTION void LC_LexCase2(LC_Lex *x, LC_Token *t, LC_TokenKind tk0, char c, LC_TokenKind tk1) {
t->kind = tk0;
if (x->at[0] == c) {
LC_LexAdvance(x);
t->kind = tk1;
}
}
LC_FUNCTION void LC_LexCase3(LC_Lex *x, LC_Token *t, LC_TokenKind tk, char c0, LC_TokenKind tk0, char c1, LC_TokenKind tk1) {
t->kind = tk;
if (x->at[0] == c0) {
t->kind = tk0;
LC_LexAdvance(x);
} else if (x->at[0] == c1) {
t->kind = tk1;
LC_LexAdvance(x);
}
}
LC_FUNCTION void LC_LexCase4(LC_Lex *x, LC_Token *t, LC_TokenKind tk, char c0, LC_TokenKind tk0, char c1, LC_TokenKind tk1, char c2, LC_TokenKind tk2) {
t->kind = tk;
if (x->at[0] == c0) {
t->kind = tk0;
LC_LexAdvance(x);
} else if (x->at[0] == c1) {
LC_LexAdvance(x);
LC_LexCase2(x, t, tk1, c2, tk2);
}
}
LC_FUNCTION void LC_LexNext(LC_Lex *x, LC_Token *t) {
LC_EatWhitespace(x);
LC_MemoryZero(t, sizeof(LC_Token));
t->str = x->at;
t->line = x->line + 1;
t->column = x->column;
t->lex = x;
char *c = x->at;
LC_LexAdvance(x);
switch (c[0]) {
case 0: t->kind = LC_TokenKind_EOF; break;
case '(': t->kind = LC_TokenKind_OpenParen; break;
case ')': t->kind = LC_TokenKind_CloseParen; break;
case '{': t->kind = LC_TokenKind_OpenBrace; break;
case '}': t->kind = LC_TokenKind_CloseBrace; break;
case '[': t->kind = LC_TokenKind_OpenBracket; break;
case ']': t->kind = LC_TokenKind_CloseBracket; break;
case ',': t->kind = LC_TokenKind_Comma; break;
case ':': t->kind = LC_TokenKind_Colon; break;
case ';': t->kind = LC_TokenKind_Semicolon; break;
case '~': t->kind = LC_TokenKind_Neg; break;
case '#': t->kind = LC_TokenKind_Hash; break;
case '@': t->kind = LC_TokenKind_Note; break;
case '\'': LC_LexUnicodeLiteral(x, t); break;
case '"': LC_LexStringLiteral(x, t, LC_TokenKind_String); break;
case '`': LC_LexStringLiteral(x, t, LC_TokenKind_RawString); break;
case '=': LC_LexCase2(x, t, LC_TokenKind_Assign, '=', LC_TokenKind_Equals); break;
case '!': LC_LexCase2(x, t, LC_TokenKind_Not, '=', LC_TokenKind_NotEquals); break;
case '*': LC_LexCase2(x, t, LC_TokenKind_Mul, '=', LC_TokenKind_MulAssign); break;
case '%': LC_LexCase2(x, t, LC_TokenKind_Mod, '=', LC_TokenKind_ModAssign); break;
case '+': LC_LexCase2(x, t, LC_TokenKind_Add, '=', LC_TokenKind_AddAssign); break;
case '-': LC_LexCase2(x, t, LC_TokenKind_Sub, '=', LC_TokenKind_SubAssign); break;
case '^': LC_LexCase2(x, t, LC_TokenKind_BitXor, '=', LC_TokenKind_BitXorAssign); break;
case '&': LC_LexCase3(x, t, LC_TokenKind_BitAnd, '=', LC_TokenKind_BitAndAssign, '&', LC_TokenKind_And); break;
case '|': LC_LexCase3(x, t, LC_TokenKind_BitOr, '=', LC_TokenKind_BitOrAssign, '|', LC_TokenKind_Or); break;
case '>': LC_LexCase4(x, t, LC_TokenKind_GreaterThen, '=', LC_TokenKind_GreaterThenEq, '>', LC_TokenKind_RightShift, '=', LC_TokenKind_RightShiftAssign); break;
case '<': LC_LexCase4(x, t, LC_TokenKind_LesserThen, '=', LC_TokenKind_LesserThenEq, '<', LC_TokenKind_LeftShift, '=', LC_TokenKind_LeftShiftAssign); break;
case '.': {
t->kind = LC_TokenKind_Dot;
if (x->at[0] == '.' && x->at[1] == '.') {
t->kind = LC_TokenKind_ThreeDots;
LC_LexAdvance(x);
LC_LexAdvance(x);
}
} break;
case '0': {
if (x->at[0] == 'x') {
t->kind = LC_TokenKind_Int;
LC_LexAdvance(x);
while (LC_IsHexDigit(x->at[0])) LC_LexAdvance(x);
LC_SetTokenLen(x, t);
LC_IF(t->len < 3, "invalid hex number");
t->i = LC_LexBigInt(t->str + 2, t->len - 2, 16);
break;
}
if (x->at[0] == 'b') {
t->kind = LC_TokenKind_Int;
LC_LexAdvance(x);
while (LC_IsBinDigit(x->at[0])) LC_LexAdvance(x);
LC_SetTokenLen(x, t);
LC_IF(t->len < 3, "invalid binary number");
t->i = LC_LexBigInt(t->str + 2, t->len - 2, 2);
break;
}
} // @fallthrough
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': {
LC_LexIntOrFloat(x, t);
} break;
case 'A':
case 'a':
case 'B':
case 'b':
case 'C':
case 'c':
case 'D':
case 'd':
case 'E':
case 'e':
case 'F':
case 'f':
case 'G':
case 'g':
case 'H':
case 'h':
case 'I':
case 'i':
case 'J':
case 'j':
case 'K':
case 'k':
case 'L':
case 'l':
case 'M':
case 'm':
case 'N':
case 'n':
case 'O':
case 'o':
case 'P':
case 'p':
case 'Q':
case 'q':
case 'R':
case 'r':
case 'S':
case 's':
case 'T':
case 't':
case 'U':
case 'u':
case 'V':
case 'v':
case 'W':
case 'w':
case 'X':
case 'x':
case 'Y':
case 'y':
case 'Z':
case 'z':
case '_': {
t->kind = LC_TokenKind_Ident;
LC_EatIdent(x);
} break;
case '/': {
t->kind = LC_TokenKind_Div;
if (x->at[0] == '=') {
t->kind = LC_TokenKind_DivAssign;
LC_LexAdvance(x);
} else if (x->at[0] == '/') {
t->kind = LC_TokenKind_Comment;
LC_LexAdvance(x);
while (x->at[0] != '\n' && x->at[0] != 0) LC_LexAdvance(x);
LC_SetTokenLen(x, t);
} else if (x->at[0] == '*') {
LC_LexNestedComments(x, t);
}
} break;
default: LC_IF(1, "invalid character");
}
if (t->len == 0 && t->kind != LC_TokenKind_String && t->kind != LC_TokenKind_RawString) LC_SetTokenLen(x, t);
if (t->kind == LC_TokenKind_Comment) LC_LexNext(x, t);
}
LC_FUNCTION LC_Lex *LC_LexStream(char *file, char *str, int line) {
LC_Lex *x = LC_PushStruct(L->lex_arena, LC_Lex);
x->begin = str;
x->at = str;
x->file = LC_ILit(file);
x->line = line;
for (;;) {
LC_Token *t = LC_PushStruct(L->lex_arena, LC_Token);
if (!x->tokens) x->tokens = t;
x->token_count += 1;
LC_LexNext(x, t);
if (t->kind == LC_TokenKind_EOF) break;
}
return x;
}
LC_FUNCTION LC_String LC_GetTokenLine(LC_Token *token) {
LC_Lex *x = token->lex;
LC_String content = LC_MakeFromChar(x->begin);
LC_StringList lines = LC_Split(L->arena, content, LC_Lit("\n"), 0);
LC_String l[3] = {LC_MakeEmptyString()};
int line = 1;
for (LC_StringNode *it = lines.first; it; it = it->next) {
LC_String sline = it->string;
if (token->line - 1 == line) {
l[0] = LC_Format(L->arena, "> %.*s\n", LC_Expand(sline));
}
if (token->line + 1 == line) {
l[2] = LC_Format(L->arena, "> %.*s\n", LC_Expand(sline));
break;
}
if (token->line == line) {
int begin = (int)(token->str - sline.str);
LC_String left = LC_GetPrefix(sline, begin);
LC_String past_left = LC_Skip(sline, begin);
LC_String mid = LC_GetPrefix(past_left, token->len);
LC_String right = LC_Skip(past_left, token->len);
char *green = "\033[32m";
char *reset = "\033[0m";
if (!L->use_colored_terminal_output) {
green = ">>>>";
reset = "<<<<";
}
l[1] = LC_Format(L->arena, "> %.*s%s%.*s%s%.*s\n", LC_Expand(left), green, LC_Expand(mid), reset, LC_Expand(right));
}
line += 1;
}
LC_String result = LC_Format(L->arena, "%.*s%.*s%.*s", LC_Expand(l[0]), LC_Expand(l[1]), LC_Expand(l[2]));
return result;
}
LC_FUNCTION void LC_InternTokens(LC_Lex *x) {
// @todo: add scratch, we can dump the LC_PushArray strings
for (int i = 0; i < x->token_count; i += 1) {
LC_Token *t = x->tokens + i;
if (t->kind == LC_TokenKind_String) {
int string_len = 0;
char *string = LC_PushArray(L->arena, char, t->len);
for (int i = 0; i < t->len; i += 1) {
char c0 = t->str[i];
char c1 = t->str[i + 1];
if (i + 1 >= t->len) c1 = 0;
if (c0 == '\\') {
uint64_t code = LC_GetEscapeCode(c1);
if (code == UINT64_MAX) {
LC_LexingError(t, "invalid escape code in string '%c%c'", c0, c1);
break;
}
c0 = (char)code;
i += 1;
}
string[string_len++] = c0;
}
t->ident = LC_InternStrLen(string, string_len);
}
if (t->kind == LC_TokenKind_Note || t->kind == LC_TokenKind_Ident || t->kind == LC_TokenKind_RawString) {
t->ident = LC_InternStrLen(t->str, t->len);
}
if (t->kind == LC_TokenKind_Ident) {
bool is_keyword = t->ident >= L->first_keyword && t->ident <= L->last_keyword;
if (is_keyword) {
t->kind = LC_TokenKind_Keyword;
if (L->kaddptr == t->ident) t->kind = LC_TokenKind_AddPtr;
}
}
}
}
#undef LC_IF

View File

@@ -0,0 +1,95 @@
#include "lib_compiler.h"
#if __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wswitch"
#pragma clang diagnostic ignored "-Wwritable-strings"
#endif
#ifndef LC_ParseFloat // @override
#include <stdlib.h>
#define LC_ParseFloat(str, len) strtod(str, NULL)
#endif
#ifndef LC_Print // @override
#include <stdio.h>
#define LC_Print(str, len) printf("%.*s", (int)len, str)
#endif
#ifndef LC_Exit // @override
#include <stdio.h>
#define LC_Exit(x) exit(x)
#endif
#ifndef LC_MemoryZero // @override
#include <string.h>
#define LC_MemoryZero(p, size) memset(p, 0, size)
#endif
#ifndef LC_MemoryCopy // @override
#include <string.h>
#define LC_MemoryCopy(dst, src, size) memcpy(dst, src, size);
#endif
#ifndef LC_vsnprintf // @override
#include <stdio.h>
#define LC_vsnprintf vsnprintf
#endif
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif
LC_THREAD_LOCAL LC_Lang *L;
#include "unicode.c"
#include "string.c"
#include "to_string.c"
#include "common.c"
#include "intern.c"
#include "lex.c"
#include "bigint.c"
#include "value.c"
#include "ast.c"
#include "ast_walk.c"
#include "ast_copy.c"
#include "resolver.c"
#include "resolve.c"
#include "parse.c"
#include "printer.c"
#include "genc.c"
#include "extended_passes.c"
#include "packages.c"
#include "init.c"
#if _WIN32
#include "win32_filesystem.c"
#elif __linux__ || __APPLE__ || __unix__
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include <time.h>
#include <dirent.h>
#include <sys/mman.h>
#include "unix_filesystem.c"
#endif
#ifndef LC_USE_CUSTOM_ARENA
#include "arena.c"
#if _WIN32
#include "win32_arena.c"
#elif __linux__ || __APPLE__ || __unix__
#include "unix_arena.c"
#endif
#endif
#if __clang__
#pragma clang diagnostic pop
#endif

1696
src/compiler/lib_compiler.h Normal file

File diff suppressed because it is too large Load Diff

363
src/compiler/packages.c Normal file
View File

@@ -0,0 +1,363 @@
LC_FUNCTION LC_Operand LC_ImportPackage(LC_AST *import, LC_AST *dst, LC_AST *src) {
DeclScope *dst_scope = dst->apackage.scope;
int scope_size = LC_NextPow2(src->apackage.scope->len * 2 + 1);
if (import && import->gimport.name) {
LC_PUSH_PACKAGE(dst);
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Import, import->gimport.name, import);
decl->scope = LC_CreateScope(scope_size);
LC_PutGlobalDecl(decl);
import->gimport.resolved_decl = decl;
LC_POP_PACKAGE();
dst_scope = decl->scope;
}
for (int i = 0; i < src->apackage.scope->cap; i += 1) {
LC_MapEntry entry = src->apackage.scope->entries[i];
if (entry.key != 0) {
LC_Decl *decl = (LC_Decl *)entry.value;
if (decl->package != src) continue;
LC_Decl *existing = (LC_Decl *)LC_MapGetU64(dst_scope, decl->name);
if (existing && decl->package == L->builtin_package) {
continue;
}
if (existing) {
LC_MarkDeclError(existing);
LC_MarkDeclError(decl);
return LC_ReportASTErrorEx(decl->ast, existing->ast, "name colission while importing '%s' into '%s', there are 2 decls with the same name '%s'", src->apackage.name, dst->apackage.name, decl->name);
}
LC_MapInsertU64(dst_scope, decl->name, decl);
}
}
if (import && import->gimport.name) {
LC_ASSERT(import, dst_scope->cap == scope_size);
}
if (import) import->gimport.resolved = true;
return LC_OPNull;
}
LC_FUNCTION LC_Intern LC_MakePackageNameFromPath(LC_String path) {
if (path.str[path.len - 1] == '/') path = LC_Chop(path, 1);
LC_String s8name = LC_SkipToLastSlash(path);
if (!LC_IsDir(L->arena, path) && LC_EndsWith(path, LC_Lit(".lc"), true)) {
s8name = LC_ChopLastPeriod(s8name);
}
if (s8name.len == 0) {
L->errors += 1;
LC_SendErrorMessagef(NULL, NULL, "failed to extract name from path %.*s", LC_Expand(path));
return LC_GetUniqueIntern("invalid_package_name");
}
LC_Intern result = LC_InternStrLen(s8name.str, (int)s8name.len);
return result;
}
LC_FUNCTION bool LC_PackageNameValid(LC_Intern name) {
char *str = (char *)name;
if (LC_IsDigit(str[0])) return false;
for (int i = 0; str[i]; i += 1) {
bool is_valid = LC_IsIdent(str[i]) || LC_IsDigit(str[i]);
if (!is_valid) return false;
}
return true;
}
LC_FUNCTION bool LC_PackageNameDuplicate(LC_Intern name) {
LC_ASTFor(it, L->fpackage) {
if (it->apackage.name == name) return true;
}
return false;
}
LC_FUNCTION void LC_AddPackageToList(LC_AST *n) {
LC_Intern name = n->apackage.name;
if (LC_PackageNameDuplicate(name)) {
LC_SendErrorMessagef(NULL, NULL, "found 2 packages with the same name: '%s' / '%.*s'\n", name, LC_Expand(n->apackage.path));
L->errors += 1;
return;
}
if (!LC_PackageNameValid(name)) {
LC_SendErrorMessagef(NULL, NULL, "invalid package name, please change the name of the package directory: '%s'\n", name);
L->errors += 1;
return;
}
LC_DLLAdd(L->fpackage, L->lpackage, n);
}
LC_FUNCTION LC_AST *LC_RegisterPackage(LC_String path) {
LC_ASSERT(NULL, path.len != 0);
LC_AST *n = LC_CreateAST(NULL, LC_ASTKind_Package);
n->apackage.name = LC_MakePackageNameFromPath(path);
n->apackage.path = path;
LC_AddPackageToList(n);
return n;
}
LC_FUNCTION LC_AST *LC_FindImportInRefList(LC_ASTRefList *arr, LC_Intern path) {
for (LC_ASTRef *it = arr->first; it; it = it->next) {
if (it->ast->gimport.path == path) return it->ast;
}
return NULL;
}
LC_FUNCTION void LC_AddASTToRefList(LC_ASTRefList *refs, LC_AST *ast) {
LC_ASTRef *ref = LC_PushStruct(L->arena, LC_ASTRef);
ref->ast = ast;
LC_DLLAdd(refs->first, refs->last, ref);
}
LC_FUNCTION LC_ASTRefList LC_GetPackageImports(LC_AST *package) {
LC_ASSERT(package, package->kind == LC_ASTKind_Package);
LC_ASTRefList refs = {0};
LC_ASTFor(file, package->apackage.ffile) {
LC_ASTFor(import, file->afile.fimport) {
LC_AST *found = LC_FindImportInRefList(&refs, import->gimport.path);
if (found) {
LC_ReportASTErrorEx(import, found, "duplicate import of: '%s', into package '%s'\n", import->gimport.path, package->apackage.name);
continue;
}
LC_AddASTToRefList(&refs, import);
}
}
return refs;
}
LC_FUNCTION void LC_RegisterPackageDir(char *dir) {
LC_String sdir = LC_MakeFromChar(dir);
if (!LC_IsDir(L->arena, sdir)) {
LC_SendErrorMessagef(NULL, NULL, "dir with name '%s', doesn't exist\n", dir);
return;
}
LC_AddNode(L->arena, &L->package_dirs, sdir);
}
LC_FUNCTION LC_AST *LC_GetPackageByName(LC_Intern name) {
LC_ASTFor(it, L->fpackage) {
if (it->apackage.name == name) return it;
}
LC_AST *result = NULL;
for (LC_StringNode *it = L->package_dirs.first; it; it = it->next) {
LC_String s = it->string;
LC_String path = LC_Format(L->arena, "%.*s/%s", LC_Expand(s), (char *)name);
if (LC_IsDir(L->arena, path)) {
if (result != NULL) {
LC_SendErrorMessagef(NULL, NULL, "found 2 directories with the same name: '%.*s', '%.*s'\n", LC_Expand(path), LC_Expand(result->apackage.path));
L->errors += 1;
break;
}
result = LC_RegisterPackage(path);
}
}
return result;
}
LC_FUNCTION LC_StringList LC_ListFilesInPackage(LC_Arena *arena, LC_String path) {
LC_StringList result = LC_MakeEmptyList();
for (LC_FileIter it = LC_IterateFiles(arena, path); LC_IsValid(it); LC_Advance(&it)) {
if (LC_EndsWith(it.absolute_path, LC_Lit(".lc"), LC_IgnoreCase)) {
LC_AddNode(arena, &result, it.absolute_path);
}
}
return result;
}
LC_FUNCTION LoadedFile LC_ReadFileHook(LC_AST *package, LC_String path) {
LoadedFile result = {path};
if (L->on_file_load) {
L->on_file_load(package, &result);
} else {
result.content = LC_ReadFile(L->arena, result.path);
}
return result;
}
LC_FUNCTION void LC_ParsePackage(LC_AST *n) {
LC_ASSERT(n, n->kind == LC_ASTKind_Package);
LC_ASSERT(n, n->apackage.scope == NULL);
n->apackage.scope = LC_CreateScope(256);
LC_StringList files = n->apackage.injected_filepaths;
if (files.node_count == 0) {
files = LC_ListFilesInPackage(L->arena, n->apackage.path);
if (files.first == NULL) {
LC_SendErrorMessagef(NULL, NULL, "no valid .lc files in '%.*s'", LC_Expand(n->apackage.path));
n->apackage.state = LC_DeclState_Error;
L->errors += 1;
return;
}
}
for (LC_StringNode *it = files.first; it; it = it->next) {
LoadedFile file = LC_ReadFileHook(n, it->string);
if (file.content.str == NULL) file.content.str = "";
LC_AST *ast_file = LC_ParseFile(n, file.path.str, file.content.str, file.line);
if (!ast_file) {
n->apackage.state = LC_DeclState_Error;
return;
}
}
}
LC_FUNCTION void LC_ParsePackagesUsingRegistry(LC_Intern name) {
LC_AST *n = LC_GetPackageByName(name);
if (!n) {
LC_SendErrorMessagef(NULL, NULL, "no package with name '%s'\n", name);
L->errors += 1;
return;
}
if (n->apackage.scope) {
return;
}
LC_ParsePackage(n);
LC_ASTRefList imports = LC_GetPackageImports(n);
for (LC_ASTRef *it = imports.first; it; it = it->next) {
LC_ParsePackagesUsingRegistry(it->ast->gimport.path);
}
}
LC_FUNCTION void LC_AddOrderedPackageToRefList(LC_AST *n) {
LC_ASTRefList *ordered = &L->ordered_packages;
for (LC_ASTRef *it = ordered->first; it; it = it->next) {
if (it->ast->apackage.name == n->apackage.name) {
return;
}
}
LC_AddASTToRefList(ordered, n);
}
// Here we use import statements to produce a list of ordered packages.
// While we are at it we also resolve most top level declarations. I say
// most because aggregations are handled a bit differently, their resolution
// is deffered. This is added because a pointer doesn't require full typeinfo of
// an aggregate. It's just a number.
LC_FUNCTION LC_AST *LC_OrderPackagesAndBasicResolve(LC_AST *pos, LC_Intern name) {
LC_AST *n = LC_GetPackageByName(name);
if (n->apackage.state == LC_DeclState_Error) {
return NULL;
}
if (n->apackage.state == LC_DeclState_Resolved) {
// This function can be called multiple times, I assume user might
// want to use type information to generate something. Pattern:
// typecheck -> generate -> typecheck is expected!
LC_PackageDecls(n);
return n;
}
if (n->apackage.state == LC_DeclState_Resolving) {
LC_ReportASTError(pos, "circular import '%s'", name);
n->apackage.state = LC_DeclState_Error;
return NULL;
}
LC_ASSERT(pos, n->apackage.state == LC_DeclState_Unresolved);
n->apackage.state = LC_DeclState_Resolving;
LC_Operand op = LC_ImportPackage(NULL, n, L->builtin_package);
LC_ASSERT(pos, !LC_IsError(op));
// Resolve all imports regardless of errors.
// If current package has wrong import it means it's also
// wrong but it should still look into all imports
// despite this.
int wrong_import = 0;
LC_ASTRefList refs = LC_GetPackageImports(n);
for (LC_ASTRef *it = refs.first; it; it = it->next) {
LC_AST *import = LC_OrderPackagesAndBasicResolve(it->ast, it->ast->gimport.path);
if (!import) {
n->apackage.state = LC_DeclState_Error;
wrong_import += 1;
continue;
}
LC_Operand op = LC_ImportPackage(it->ast, n, import);
if (LC_IsError(op)) {
n->apackage.state = LC_DeclState_Error;
wrong_import += 1;
continue;
}
}
if (wrong_import) return NULL;
LC_PackageDecls(n);
LC_AddOrderedPackageToRefList(n);
n->apackage.state = LC_DeclState_Resolved;
return n;
}
LC_FUNCTION void LC_OrderAndResolveTopLevelDecls(LC_Intern name) {
L->first_package = name;
LC_OrderPackagesAndBasicResolve(NULL, name);
// Resolve still incomplete aggregate types, this operates on all packages
// that didn't have errors so even if something broke in package ordering
// it should still be fine to go forward with this and also proc body analysis
for (LC_ASTRef *it = L->ordered_packages.first; it; it = it->next) {
LC_AST *package = it->ast;
LC_ASSERT(package, package->apackage.state == LC_DeclState_Resolved);
LC_ResolveIncompleteTypes(package);
}
}
LC_FUNCTION void LC_ResolveAllProcBodies(void) {
// We don't need to check errors, only valid packages should have been put into
// the list.
for (LC_ASTRef *it = L->ordered_packages.first; it; it = it->next) {
LC_AST *package = it->ast;
LC_ASSERT(package, package->apackage.state == LC_DeclState_Resolved);
LC_ResolveProcBodies(package);
}
}
LC_FUNCTION LC_ASTRefList LC_ResolvePackageByName(LC_Intern name) {
LC_ParsePackagesUsingRegistry(name);
LC_ASTRefList empty = {0};
if (L->errors) return empty;
LC_OrderAndResolveTopLevelDecls(name);
LC_ResolveAllProcBodies();
return L->ordered_packages;
}
LC_FUNCTION LC_String LC_GenerateUnityBuild(LC_ASTRefList packages) {
if (L->errors) return LC_MakeEmptyString();
LC_BeginStringGen(L->arena);
LC_GenLinef("#include <stdbool.h>");
LC_GenLinef("#include <stddef.h>");
LC_GenLinef("#ifndef LC_String_IMPL");
LC_GenLinef("#define LC_String_IMPL");
LC_GenLinef("typedef struct { char *str; long long len; } LC_String;");
LC_GenLinef("#endif");
LC_GenLinef("#ifndef LC_Any_IMPL");
LC_GenLinef("#define LC_Any_IMPL");
LC_GenLinef("typedef struct { int type; void *data; } LC_Any;");
LC_GenLinef("#endif");
LC_GenLinef("#ifndef LC_Alignof");
LC_GenLinef("#if defined(__TINYC__)");
LC_GenLinef("#define LC_Alignof(...) __alignof__(__VA_ARGS__)");
LC_GenLinef("#else");
LC_GenLinef("#define LC_Alignof(...) _Alignof(__VA_ARGS__)");
LC_GenLinef("#endif");
LC_GenLinef("#endif");
LC_GenLinef("void *memset(void *, int, size_t);");
for (LC_ASTRef *it = packages.first; it; it = it->next) LC_GenCHeader(it->ast);
for (LC_ASTRef *it = packages.first; it; it = it->next) LC_GenCImpl(it->ast);
LC_String s = LC_EndStringGen(L->arena);
return s;
}
LC_FUNCTION void LC_AddSingleFilePackage(LC_Intern name, LC_String path) {
LC_AST *n = LC_CreateAST(0, LC_ASTKind_Package);
n->apackage.name = name;
n->apackage.path = path;
LC_AddNode(L->arena, &n->apackage.injected_filepaths, path);
LC_AddPackageToList(n);
}

1079
src/compiler/parse.c Normal file

File diff suppressed because it is too large Load Diff

477
src/compiler/printer.c Normal file
View File

@@ -0,0 +1,477 @@
LC_FUNCTION LC_StringList *LC_BeginStringGen(LC_Arena *arena) {
L->printer.list = LC_MakeEmptyList();
L->printer.arena = arena;
L->printer.last_filename = 0;
L->printer.last_line_num = 0;
L->printer.indent = 0;
return &L->printer.list;
}
LC_FUNCTION LC_String LC_EndStringGen(LC_Arena *arena) {
LC_String result = LC_MergeString(arena, L->printer.list);
return result;
}
LC_FUNCTION void LC_GenIndent(void) {
LC_String s = LC_Lit(" ");
for (int i = 0; i < L->printer.indent; i++) {
LC_AddNode(L->printer.arena, &L->printer.list, s);
}
}
LC_FUNCTION char *LC_Strf(const char *str, ...) {
LC_FORMAT(L->arena, str, s8);
return s8.str;
}
LC_FUNCTION void LC_GenLine(void) {
LC_Genf("\n");
LC_GenIndent();
}
LC_FUNCTION char *LC_GenLCType(LC_Type *type) {
LC_StringList out = {0};
for (LC_Type *it = type; it;) {
if (it->kind == LC_TypeKind_Pointer) {
LC_Addf(L->arena, &out, "*");
it = it->tptr.base;
} else if (it->kind == LC_TypeKind_Array) {
LC_Addf(L->arena, &out, "[%d]", it->tarray.size);
it = it->tarray.base;
} else if (it->kind == LC_TypeKind_Proc) {
LC_Addf(L->arena, &out, "proc(");
LC_TypeFor(mem, it->tproc.args.first) {
LC_Addf(L->arena, &out, "%s: %s", (char *)mem->name, LC_GenLCType(mem->type));
if (mem->default_value_expr) LC_Addf(L->arena, &out, "/*has default value*/");
if (mem->next) LC_Addf(L->arena, &out, ", ");
}
if (it->tproc.vargs) LC_Addf(L->arena, &out, "..");
LC_Addf(L->arena, &out, ")");
if (it->tproc.ret->kind != LC_TypeKind_void) LC_Addf(L->arena, &out, ": %s", LC_GenLCType(it->tproc.ret));
break;
} else if (it->decl) {
LC_Decl *decl = it->decl;
LC_ASSERT(decl->ast, decl);
LC_Addf(L->arena, &out, "%s", (char *)decl->name);
break;
} else {
LC_SendErrorMessagef(NULL, NULL, "internal compiler error: unhandled type kind in %s", __FUNCTION__);
}
}
LC_String s = LC_MergeString(L->arena, out);
return s.str;
}
LC_FUNCTION char *LC_GenLCTypeVal(LC_TypeAndVal v) {
if (LC_IsInt(v.type) || LC_IsPtr(v.type) || LC_IsProc(v.type)) {
return LC_Bigint_str(&v.i, 10);
}
if (LC_IsFloat(v.type)) {
LC_String s = LC_Format(L->arena, "%f", v.d);
return s.str;
}
LC_ASSERT(NULL, !"invalid codepath");
return "";
}
LC_FUNCTION char *LC_GenLCAggName(LC_Type *t) {
if (t->kind == LC_TypeKind_Struct) return "struct";
if (t->kind == LC_TypeKind_Union) return "union";
return NULL;
}
LC_FUNCTION void LC_GenLCNode(LC_AST *n) {
switch (n->kind) {
case LC_ASTKind_Package: {
LC_ASTFor(it, n->apackage.ffile) {
LC_GenLCNode(it);
}
} break;
case LC_ASTKind_File: {
LC_ASTFor(it, n->afile.fimport) {
LC_GenLCNode(it);
}
LC_ASTFor(it, n->afile.fdecl) {
LC_GenLCNode(it);
}
// @todo: we need to do something with notes so we can generate them in order!
} break;
case LC_ASTKind_GlobImport: {
LC_GenLinef("import %s \"%s\";", (char *)n->gimport.name, (char *)n->gimport.path);
} break;
case LC_ASTKind_DeclProc: {
LC_GenLinef("%s :: ", (char *)n->dbase.name);
LC_GenLCNode(n->dproc.type);
if (n->dproc.body) {
LC_Genf(" ");
LC_GenLCNode(n->dproc.body);
} else {
LC_Genf(";");
}
} break;
case LC_ASTKind_DeclUnion:
case LC_ASTKind_DeclStruct: {
const char *agg = n->kind == LC_ASTKind_DeclUnion ? "union" : "struct";
LC_GenLinef("%s :: %s {", (char *)n->dbase.name, agg);
L->printer.indent += 1;
LC_ASTFor(it, n->dagg.first) {
LC_GenLine();
LC_GenLCNode(it);
LC_Genf(";");
}
L->printer.indent -= 1;
LC_GenLinef("}");
} break;
case LC_ASTKind_TypespecAggMem: {
LC_Genf("%s: ", (char *)n->tagg_mem.name);
LC_GenLCNode(n->tagg_mem.type);
} break;
case LC_ASTKind_DeclVar: {
LC_GenLinef("%s ", (char *)n->dbase.name);
if (n->dvar.type) {
LC_Genf(": ");
LC_GenLCNode(n->dvar.type);
if (n->dvar.expr) {
LC_Genf("= ");
LC_GenLCNode(n->dvar.expr);
}
} else {
LC_Genf(":= ");
LC_GenLCNode(n->dvar.expr);
}
LC_Genf(";");
} break;
case LC_ASTKind_DeclConst: {
LC_GenLinef("%s :: ", (char *)n->dbase.name);
LC_GenLCNode(n->dconst.expr);
LC_Genf(";");
} break;
case LC_ASTKind_DeclTypedef: {
LC_GenLinef("%s :: typedef ", (char *)n->dbase.name);
LC_GenLCNode(n->dtypedef.type);
LC_Genf(";");
} break;
case LC_ASTKind_ExprIdent:
case LC_ASTKind_TypespecIdent: {
LC_Genf("%s", (char *)n->eident.name);
} break;
case LC_ASTKind_ExprField:
case LC_ASTKind_TypespecField: {
LC_GenLCNode(n->efield.left);
LC_Genf(".%s", (char *)n->efield.right);
} break;
case LC_ASTKind_TypespecPointer: {
LC_Genf("*");
LC_GenLCNode(n->tpointer.base);
} break;
case LC_ASTKind_TypespecArray: {
LC_Genf("[");
if (n->tarray.index) LC_GenLCNode(n->tarray.index);
LC_Genf("]");
LC_GenLCNode(n->tpointer.base);
} break;
case LC_ASTKind_TypespecProc: {
LC_Genf("proc(");
LC_ASTFor(it, n->tproc.first) {
LC_GenLCNode(it);
if (it != n->tproc.last) LC_Genf(", ");
}
if (n->tproc.vargs) {
LC_Genf(", ...");
if (n->tproc.vargs_any_promotion) LC_Genf("Any");
}
LC_Genf(")");
if (n->tproc.ret) {
LC_Genf(": ");
LC_GenLCNode(n->tproc.ret);
}
} break;
case LC_ASTKind_TypespecProcArg: {
LC_Genf("%s: ", (char *)n->tproc_arg.name);
LC_GenLCNode(n->tproc_arg.type);
if (n->tproc_arg.expr) {
LC_Genf(" = ");
LC_GenLCNode(n->tproc_arg.expr);
}
} break;
case LC_ASTKind_StmtBlock: {
if (n->sblock.name && n->sblock.kind != SBLK_Loop) LC_Genf("%s: ", (char *)n->sblock.name);
LC_Genf("{");
L->printer.indent += 1;
LC_ASTFor(it, n->sblock.first) {
LC_GenLine();
LC_GenLCNode(it);
if (it->kind != LC_ASTKind_StmtBlock && it->kind != LC_ASTKind_StmtDefer && it->kind != LC_ASTKind_StmtFor && it->kind != LC_ASTKind_StmtIf && it->kind != LC_ASTKind_StmtSwitch) LC_Genf(";");
}
L->printer.indent -= 1;
LC_GenLinef("}");
} break;
case LC_ASTKind_StmtReturn: {
LC_Genf("return");
if (n->sreturn.expr) {
LC_Genf(" ");
LC_GenLCNode(n->sreturn.expr);
}
} break;
case LC_ASTKind_StmtBreak: {
LC_Genf("break");
if (n->sbreak.name) LC_Genf(" %s", (char *)n->sbreak.name);
} break;
case LC_ASTKind_StmtContinue: {
LC_Genf("continue");
if (n->scontinue.name) LC_Genf(" %s", (char *)n->scontinue.name);
} break;
case LC_ASTKind_StmtDefer: {
LC_Genf("defer ");
LC_GenLCNode(n->sdefer.body);
} break;
case LC_ASTKind_StmtFor: {
LC_StmtBlock *sblock = &n->sfor.body->sblock;
if (sblock->name && sblock->kind == SBLK_Loop) {
LC_Genf("%s: ", (char *)sblock->name);
}
LC_Genf("for ");
if (n->sfor.init) {
LC_GenLCNode(n->sfor.init);
if (n->sfor.cond) LC_Genf("; ");
}
if (n->sfor.cond) {
LC_GenLCNode(n->sfor.cond);
if (n->sfor.inc) {
LC_Genf("; ");
LC_GenLCNode(n->sfor.inc);
}
}
LC_Genf(" ");
LC_GenLCNode(n->sfor.body);
} break;
case LC_ASTKind_StmtElseIf:
LC_Genf("else ");
case LC_ASTKind_StmtIf: {
LC_Genf("if ");
LC_GenLCNode(n->sif.expr);
LC_GenLCNode(n->sif.body);
LC_ASTFor(it, n->sif.first) {
LC_GenLCNode(it);
}
} break;
case LC_ASTKind_StmtElse: {
LC_Genf("else ");
LC_GenLCNode(n->sif.body);
} break;
case LC_ASTKind_StmtSwitch: {
LC_Genf("switch ");
LC_GenLCNode(n->sswitch.expr);
LC_Genf("{");
L->printer.indent += 1;
LC_ASTFor(it, n->sswitch.first) {
LC_GenLine();
LC_GenLCNode(it);
}
L->printer.indent -= 1;
LC_Genf("}");
} break;
case LC_ASTKind_StmtSwitchCase: {
LC_Genf("case ");
LC_ASTFor(it, n->scase.first) {
LC_GenLCNode(it);
if (it != n->scase.last) LC_Genf(", ");
}
LC_Genf(": ");
LC_GenLCNode(n->scase.body);
} break;
case LC_ASTKind_StmtSwitchDefault: {
LC_Genf("default: ");
LC_GenLCNode(n->scase.body);
} break;
case LC_ASTKind_StmtAssign: {
LC_GenLCNode(n->sassign.left);
LC_Genf(" %s ", LC_TokenKindToOperator(n->sassign.op));
LC_GenLCNode(n->sassign.right);
} break;
case LC_ASTKind_StmtExpr: {
LC_GenLCNode(n->sexpr.expr);
} break;
case LC_ASTKind_StmtVar: {
LC_Genf("%s", (char *)n->svar.name);
if (n->svar.type) {
LC_Genf(": ");
LC_GenLCNode(n->svar.type);
if (n->svar.expr) {
LC_Genf(" = ");
LC_GenLCNode(n->svar.expr);
}
} else {
LC_Genf(" := ");
LC_GenLCNode(n->svar.expr);
}
} break;
case LC_ASTKind_StmtConst: {
LC_GenLinef("%s :: ", (char *)n->sconst.name);
LC_GenLCNode(n->sconst.expr);
} break;
case LC_ASTKind_ExprString: {
LC_Genf("`%s`", (char *)n->eatom.name);
} break;
case LC_ASTKind_ExprInt: {
LC_Genf("%s", LC_Bigint_str(&n->eatom.i, 10));
} break;
case LC_ASTKind_ExprFloat: {
LC_Genf("%f", n->eatom.d);
} break;
case LC_ASTKind_ExprBool: {
int64_t value = LC_Bigint_as_unsigned(&n->eatom.i);
if (value) {
LC_Genf("true");
} else {
LC_Genf("false");
}
} break;
case LC_ASTKind_ExprType: {
LC_Genf(":");
LC_GenLCNode(n->etype.type);
} break;
case LC_ASTKind_ExprBinary: {
LC_Genf("(");
LC_GenLCNode(n->ebinary.left);
LC_Genf("%s", LC_TokenKindToOperator(n->ebinary.op));
LC_GenLCNode(n->ebinary.right);
LC_Genf(")");
} break;
case LC_ASTKind_ExprUnary: {
LC_Genf("%s(", LC_TokenKindToOperator(n->eunary.op));
LC_GenLCNode(n->eunary.expr);
LC_Genf(")");
} break;
case LC_ASTKind_StmtNote: {
LC_Genf("#");
LC_GenLCNode(n->snote.expr);
} break;
case LC_ASTKind_ExprNote: {
LC_Genf("#");
LC_GenLCNode(n->enote.expr);
} break;
case LC_ASTKind_DeclNote: {
LC_GenLinef("#");
LC_GenLCNode(n->dnote.expr);
LC_Genf(";");
} break;
case LC_ASTKind_Note:
case LC_ASTKind_ExprBuiltin:
case LC_ASTKind_ExprCall: {
LC_GenLCNode(n->ecompo.name);
LC_Genf("(");
LC_ASTFor(it, n->ecompo.first) {
LC_GenLCNode(it);
if (it != n->ecompo.last) LC_Genf(", ");
}
LC_Genf(")");
} break;
case LC_ASTKind_ExprCompoundItem:
case LC_ASTKind_ExprCallItem: {
if (n->ecompo_item.name) {
LC_Genf("%s = ", (char *)n->ecompo_item.name);
}
if (n->ecompo_item.index) {
LC_Genf("[");
LC_GenLCNode(n->ecompo_item.index);
LC_Genf("] = ");
}
LC_GenLCNode(n->ecompo_item.expr);
} break;
case LC_ASTKind_ExprCompound: {
if (n->ecompo.name) LC_GenLCNode(n->ecompo.name);
LC_Genf("{");
LC_ASTFor(it, n->ecompo.first) {
LC_GenLCNode(it);
if (it != n->ecompo.last) LC_Genf(", ");
}
LC_Genf("}");
} break;
case LC_ASTKind_ExprCast: {
LC_Genf(":");
LC_GenLCNode(n->ecast.type);
LC_Genf("(");
LC_GenLCNode(n->ecast.expr);
LC_Genf(")");
} break;
case LC_ASTKind_ExprIndex: {
LC_Genf("(");
LC_GenLCNode(n->eindex.base);
LC_Genf("[");
LC_GenLCNode(n->eindex.index);
LC_Genf("]");
LC_Genf(")");
} break;
case LC_ASTKind_ExprAddPtr: {
LC_Genf("addptr(");
LC_GenLCNode(n->ebinary.left);
LC_Genf(", ");
LC_GenLCNode(n->ebinary.right);
LC_Genf(")");
} break;
case LC_ASTKind_ExprGetValueOfPointer: {
LC_Genf("*(");
LC_GenLCNode(n->eunary.expr);
LC_Genf(")");
} break;
case LC_ASTKind_ExprGetPointerOfValue: {
LC_Genf("&(");
LC_GenLCNode(n->eunary.expr);
LC_Genf(")");
} break;
default: LC_ReportASTError(n, "internal compiler error: unhandled ast kind in %s", __FUNCTION__);
}
}

1496
src/compiler/resolve.c Normal file

File diff suppressed because it is too large Load Diff

192
src/compiler/resolver.c Normal file
View File

@@ -0,0 +1,192 @@
// clang-format off
#define LC_PUSH_COMP_ARRAY_SIZE(SIZE) int PREV_SIZE = L->resolver.compo_context_array_size; L->resolver.compo_context_array_size = SIZE;
#define LC_POP_COMP_ARRAY_SIZE() L->resolver.compo_context_array_size = PREV_SIZE
#define LC_PUSH_SCOPE(SCOPE) DeclScope *PREV_SCOPE = L->resolver.active_scope; L->resolver.active_scope = SCOPE
#define LC_POP_SCOPE() L->resolver.active_scope = PREV_SCOPE
#define LC_PUSH_LOCAL_SCOPE() int LOCAL_LEN = L->resolver.locals.len
#define LC_POP_LOCAL_SCOPE() L->resolver.locals.len = LOCAL_LEN
#define LC_PUSH_PACKAGE(PKG) LC_AST *PREV_PKG = L->resolver.package; L->resolver.package = PKG; LC_PUSH_SCOPE(PKG->apackage.scope)
#define LC_POP_PACKAGE() L->resolver.package = PREV_PKG; LC_POP_SCOPE()
#define LC_PROP_ERROR(OP, n, ...) OP = __VA_ARGS__; if (LC_IsError(OP)) { n->kind = LC_ASTKind_Error; return OP; }
#define LC_DECL_PROP_ERROR(OP, ...) OP = __VA_ARGS__; if (LC_IsError(OP)) { LC_MarkDeclError(decl); return OP; }
#define LC_IF(COND, N, ...) if (COND) { LC_Operand R_ = LC_ReportASTError(N, __VA_ARGS__); N->kind = LC_ASTKind_Error; return R_; }
#define LC_DECL_IF(COND, ...) if (COND) { LC_MarkDeclError(decl); return LC_ReportASTError(__VA_ARGS__); }
#define LC_TYPE_IF(COND, ...) if (COND) { LC_MarkDeclError(decl); type->kind = LC_TypeKind_Error; return LC_ReportASTError(__VA_ARGS__); }
// clang-format on
LC_FUNCTION void LC_AddDecl(LC_DeclStack *scope, LC_Decl *decl) {
if (scope->len + 1 > scope->cap) {
LC_ASSERT(NULL, scope->cap);
int new_cap = scope->cap * 2;
LC_Decl **new_stack = LC_PushArray(L->arena, LC_Decl *, new_cap);
LC_MemoryCopy(new_stack, scope->stack, scope->len * sizeof(LC_Decl *));
scope->stack = new_stack;
scope->cap = new_cap;
}
scope->stack[scope->len++] = decl;
}
LC_FUNCTION void LC_InitDeclStack(LC_DeclStack *stack, int size) {
stack->stack = LC_PushArray(L->arena, LC_Decl *, size);
stack->cap = size;
}
LC_FUNCTION LC_DeclStack *LC_CreateDeclStack(int size) {
LC_DeclStack *stack = LC_PushStruct(L->arena, LC_DeclStack);
LC_InitDeclStack(stack, size);
return stack;
}
LC_FUNCTION LC_Decl *LC_FindDeclOnStack(LC_DeclStack *scp, LC_Intern name) {
for (int i = 0; i < scp->len; i += 1) {
LC_Decl *it = scp->stack[i];
if (it->name == name) {
return it;
}
}
return NULL;
}
LC_FUNCTION void LC_MarkDeclError(LC_Decl *decl) {
if (decl) {
decl->kind = LC_DeclKind_Error;
decl->state = LC_DeclState_Error;
if (decl->ast) decl->ast->kind = LC_ASTKind_Error;
}
}
LC_FUNCTION LC_Decl *LC_CreateDecl(LC_DeclKind kind, LC_Intern name, LC_AST *n) {
LC_Decl *decl = LC_PushStruct(L->decl_arena, LC_Decl);
L->decl_count += 1;
decl->name = name;
decl->kind = kind;
decl->ast = n;
decl->package = L->resolver.package;
LC_ASSERT(n, decl->package);
LC_AST *note = LC_HasNote(n, L->iforeign);
if (note) {
decl->is_foreign = true;
if (note->anote.first) {
if (note->anote.size != 1) LC_ReportASTError(note, "invalid format of @foreign(...), more then 1 argument");
LC_AST *expr = note->anote.first->ecompo_item.expr;
if (expr->kind == LC_ASTKind_ExprIdent) decl->foreign_name = expr->eident.name;
if (expr->kind != LC_ASTKind_ExprIdent) LC_ReportASTError(note, "invalid format of @foreign(...), expected identifier");
}
}
if (!decl->foreign_name) decl->foreign_name = decl->name;
return decl;
}
LC_FUNCTION LC_Operand LC_ThereIsNoDecl(DeclScope *scp, LC_Decl *decl, bool check_locals) {
LC_Decl *r = (LC_Decl *)LC_MapGetU64(scp, decl->name);
if (check_locals && !r) {
r = LC_FindDeclOnStack(&L->resolver.locals, decl->name);
}
if (r) {
LC_MarkDeclError(r);
LC_MarkDeclError(decl);
return LC_ReportASTErrorEx(decl->ast, r->ast, "there are 2 decls with the same name '%s'", decl->name);
}
return LC_OPNull;
}
LC_FUNCTION LC_Operand LC_AddDeclToScope(DeclScope *scp, LC_Decl *decl) {
LC_Operand LC_DECL_PROP_ERROR(op, LC_ThereIsNoDecl(scp, decl, false));
LC_MapInsertU64(scp, decl->name, decl);
return LC_OPDecl(decl);
}
LC_FUNCTION DeclScope *LC_CreateScope(int size) {
DeclScope *scope = LC_PushStruct(L->arena, DeclScope);
scope->arena = L->arena;
LC_MapReserve(scope, size);
return scope;
}
LC_FUNCTION LC_Decl *LC_FindDeclInScope(DeclScope *scope, LC_Intern name) {
LC_Decl *decl = (LC_Decl *)LC_MapGetU64(scope, name);
return decl;
}
LC_FUNCTION LC_Decl *LC_GetLocalOrGlobalDecl(LC_Intern name) {
LC_Decl *decl = LC_FindDeclInScope(L->resolver.active_scope, name);
if (!decl && L->resolver.package->apackage.scope == L->resolver.active_scope) {
decl = LC_FindDeclOnStack(&L->resolver.locals, name);
}
return decl;
}
LC_FUNCTION LC_Operand LC_PutGlobalDecl(LC_Decl *decl) {
LC_Operand LC_DECL_PROP_ERROR(op, LC_AddDeclToScope(L->resolver.package->apackage.scope, decl));
// :Mangle global scope name
if (!decl->is_foreign && decl->package != L->builtin_package) {
bool mangle = true;
if (LC_HasNote(decl->ast, L->idont_mangle)) mangle = false;
if (LC_HasNote(decl->ast, L->iapi)) mangle = false;
if (decl->name == L->imain) {
if (L->first_package) {
if (L->first_package == decl->package->apackage.name) {
mangle = false;
}
} else mangle = false;
}
if (mangle) {
LC_String name = LC_Format(L->arena, "lc_%s_%s", (char *)decl->package->apackage.name, (char *)decl->name);
decl->foreign_name = LC_InternStrLen(name.str, (int)name.len);
}
}
LC_Decl *conflict = (LC_Decl *)LC_MapGetU64(&L->foreign_names, decl->foreign_name);
if (conflict && !decl->is_foreign) {
LC_ReportASTErrorEx(decl->ast, conflict->ast, "found two global declarations with the same foreign name: %s", decl->foreign_name);
} else {
LC_MapInsertU64(&L->foreign_names, decl->foreign_name, decl);
}
return op;
}
LC_FUNCTION LC_Operand LC_CreateLocalDecl(LC_DeclKind kind, LC_Intern name, LC_AST *ast) {
LC_Decl *decl = LC_CreateDecl(kind, name, ast);
decl->state = LC_DeclState_Resolving;
LC_Operand LC_DECL_PROP_ERROR(operr0, LC_ThereIsNoDecl(L->resolver.package->apackage.scope, decl, true));
LC_AddDecl(&L->resolver.locals, decl);
return LC_OPDecl(decl);
}
LC_FUNCTION LC_Decl *LC_AddConstIntDecl(char *key, int64_t value) {
LC_Intern intern = LC_ILit(key);
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Const, intern, &L->NullAST);
decl->state = LC_DeclState_Resolved;
decl->type = L->tuntypedint;
LC_Bigint_init_signed(&decl->v.i, value);
LC_AddDeclToScope(L->resolver.package->apackage.scope, decl);
return decl;
}
LC_FUNCTION LC_Decl *LC_GetBuiltin(LC_Intern name) {
LC_Decl *decl = (LC_Decl *)LC_MapGetU64(L->builtin_package->apackage.scope, name);
return decl;
}
LC_FUNCTION void LC_AddBuiltinConstInt(char *key, int64_t value) {
LC_PUSH_PACKAGE(L->builtin_package);
LC_AddConstIntDecl(key, value);
LC_POP_PACKAGE();
}
LC_FUNCTION LC_AST *LC_HasNote(LC_AST *ast, LC_Intern i) {
if (ast && ast->notes) {
LC_ASTFor(it, ast->notes->anote_list.first) {
LC_ASSERT(it, "internal compiler error: note is not an identifier");
if (it->anote.name->eident.name == i) {
return it;
}
}
}
return NULL;
}

422
src/compiler/string.c Normal file
View File

@@ -0,0 +1,422 @@
LC_FUNCTION int64_t LC__ClampTop(int64_t val, int64_t max) {
if (val > max) val = max;
return val;
}
LC_FUNCTION char LC_ToLowerCase(char a) {
if (a >= 'A' && a <= 'Z') a += 32;
return a;
}
LC_FUNCTION char LC_ToUpperCase(char a) {
if (a >= 'a' && a <= 'z') a -= 32;
return a;
}
LC_FUNCTION bool LC_IsWhitespace(char w) {
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
return result;
}
LC_FUNCTION bool LC_IsAlphabetic(char a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
return result;
}
LC_FUNCTION bool LC_IsIdent(char a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
return result;
}
LC_FUNCTION bool LC_IsDigit(char a) {
bool result = a >= '0' && a <= '9';
return result;
}
LC_FUNCTION bool LC_IsAlphanumeric(char a) {
bool result = LC_IsDigit(a) || LC_IsAlphabetic(a);
return result;
}
LC_FUNCTION bool LC_AreEqual(LC_String a, LC_String b, unsigned ignore_case) {
if (a.len != b.len) return false;
for (int64_t i = 0; i < a.len; i++) {
char A = a.str[i];
char B = b.str[i];
if (ignore_case) {
A = LC_ToLowerCase(A);
B = LC_ToLowerCase(B);
}
if (A != B)
return false;
}
return true;
}
LC_FUNCTION bool LC_EndsWith(LC_String a, LC_String end, unsigned ignore_case) {
LC_String a_end = LC_GetPostfix(a, end.len);
bool result = LC_AreEqual(end, a_end, ignore_case);
return result;
}
LC_FUNCTION bool LC_StartsWith(LC_String a, LC_String start, unsigned ignore_case) {
LC_String a_start = LC_GetPrefix(a, start.len);
bool result = LC_AreEqual(start, a_start, ignore_case);
return result;
}
LC_FUNCTION LC_String LC_MakeString(char *str, int64_t len) {
LC_String result;
result.str = (char *)str;
result.len = len;
return result;
}
LC_FUNCTION LC_String LC_CopyString(LC_Arena *allocator, LC_String string) {
char *copy = (char *)LC_PushSize(allocator, sizeof(char) * (string.len + 1));
LC_MemoryCopy(copy, string.str, string.len);
copy[string.len] = 0;
LC_String result = LC_MakeString(copy, string.len);
return result;
}
LC_FUNCTION LC_String LC_CopyChar(LC_Arena *allocator, char *s) {
int64_t len = LC_StrLen(s);
char *copy = (char *)LC_PushSize(allocator, sizeof(char) * (len + 1));
LC_MemoryCopy(copy, s, len);
copy[len] = 0;
LC_String result = LC_MakeString(copy, len);
return result;
}
LC_FUNCTION LC_String LC_NormalizePath(LC_Arena *allocator, LC_String s) {
LC_String copy = LC_CopyString(allocator, s);
for (int64_t i = 0; i < copy.len; i++) {
if (copy.str[i] == '\\')
copy.str[i] = '/';
}
return copy;
}
LC_FUNCTION void LC_NormalizePathUnsafe(LC_String s) {
for (int64_t i = 0; i < s.len; i++) {
if (s.str[i] == '\\')
s.str[i] = '/';
}
}
LC_FUNCTION LC_String LC_Chop(LC_String string, int64_t len) {
len = LC__ClampTop(len, string.len);
LC_String result = LC_MakeString(string.str, string.len - len);
return result;
}
LC_FUNCTION LC_String LC_Skip(LC_String string, int64_t len) {
len = LC__ClampTop(len, string.len);
int64_t remain = string.len - len;
LC_String result = LC_MakeString(string.str + len, remain);
return result;
}
LC_FUNCTION LC_String LC_GetPostfix(LC_String string, int64_t len) {
len = LC__ClampTop(len, string.len);
int64_t remain_len = string.len - len;
LC_String result = LC_MakeString(string.str + remain_len, len);
return result;
}
LC_FUNCTION LC_String LC_GetPrefix(LC_String string, int64_t len) {
len = LC__ClampTop(len, string.len);
LC_String result = LC_MakeString(string.str, len);
return result;
}
LC_FUNCTION LC_String LC_GetNameNoExt(LC_String s) {
return LC_SkipToLastSlash(LC_ChopLastPeriod(s));
}
LC_FUNCTION LC_String LC_Slice(LC_String string, int64_t first_index, int64_t one_past_last_index) {
if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1;
if (first_index < 0) first_index = string.len + first_index;
LC_ASSERT(NULL, first_index < one_past_last_index && "LC_Slice, first_index is bigger then one_past_last_index");
LC_ASSERT(NULL, string.len > 0 && "Slicing string of length 0! Might be an error!");
LC_String result = string;
if (string.len > 0) {
if (one_past_last_index > first_index) {
first_index = LC__ClampTop(first_index, string.len - 1);
one_past_last_index = LC__ClampTop(one_past_last_index, string.len);
result.str += first_index;
result.len = one_past_last_index - first_index;
} else {
result.len = 0;
}
}
return result;
}
LC_FUNCTION bool LC_Seek(LC_String string, LC_String find, LC_FindFlag flags, int64_t *index_out) {
bool ignore_case = flags & LC_FindFlag_IgnoreCase ? true : false;
bool result = false;
if (flags & LC_FindFlag_MatchFindLast) {
for (int64_t i = string.len; i != 0; i--) {
int64_t index = i - 1;
LC_String substring = LC_Slice(string, index, index + find.len);
if (LC_AreEqual(substring, find, ignore_case)) {
if (index_out)
*index_out = index;
result = true;
break;
}
}
} else {
for (int64_t i = 0; i < string.len; i++) {
LC_String substring = LC_Slice(string, i, i + find.len);
if (LC_AreEqual(substring, find, ignore_case)) {
if (index_out)
*index_out = i;
result = true;
break;
}
}
}
return result;
}
LC_FUNCTION int64_t LC_Find(LC_String string, LC_String find, LC_FindFlag flag) {
int64_t result = -1;
LC_Seek(string, find, flag, &result);
return result;
}
LC_FUNCTION LC_StringList LC_Split(LC_Arena *allocator, LC_String string, LC_String find, LC_SplitFlag flags) {
LC_StringList result = LC_MakeEmptyList();
int64_t index = 0;
LC_FindFlag find_flag = flags & LC_SplitFlag_IgnoreCase ? LC_FindFlag_IgnoreCase : LC_FindFlag_None;
while (LC_Seek(string, find, find_flag, &index)) {
LC_String before_match = LC_MakeString(string.str, index);
LC_AddNode(allocator, &result, before_match);
if (flags & LC_SplitFlag_SplitInclusive) {
LC_String match = LC_MakeString(string.str + index, find.len);
LC_AddNode(allocator, &result, match);
}
string = LC_Skip(string, index + find.len);
}
if (string.len) LC_AddNode(allocator, &result, string);
return result;
}
LC_FUNCTION LC_String LC_MergeWithSeparator(LC_Arena *allocator, LC_StringList list, LC_String separator) {
if (list.node_count == 0) return LC_MakeEmptyString();
if (list.char_count == 0) return LC_MakeEmptyString();
int64_t base_size = (list.char_count + 1);
int64_t sep_size = (list.node_count - 1) * separator.len;
int64_t size = base_size + sep_size;
char *buff = (char *)LC_PushSize(allocator, sizeof(char) * (size + 1));
LC_String string = LC_MakeString(buff, 0);
for (LC_StringNode *it = list.first; it; it = it->next) {
LC_ASSERT(NULL, string.len + it->string.len <= size);
LC_MemoryCopy(string.str + string.len, it->string.str, it->string.len);
string.len += it->string.len;
if (it != list.last) {
LC_MemoryCopy(string.str + string.len, separator.str, separator.len);
string.len += separator.len;
}
}
LC_ASSERT(NULL, string.len == size - 1);
string.str[size] = 0;
return string;
}
LC_FUNCTION LC_String LC_MergeString(LC_Arena *allocator, LC_StringList list) {
return LC_MergeWithSeparator(allocator, list, LC_Lit(""));
}
LC_FUNCTION LC_String LC_ChopLastSlash(LC_String s) {
LC_String result = s;
LC_Seek(s, LC_Lit("/"), LC_FindFlag_MatchFindLast, &result.len);
return result;
}
LC_FUNCTION LC_String LC_ChopLastPeriod(LC_String s) {
LC_String result = s;
LC_Seek(s, LC_Lit("."), LC_FindFlag_MatchFindLast, &result.len);
return result;
}
LC_FUNCTION LC_String LC_SkipToLastSlash(LC_String s) {
int64_t pos;
LC_String result = s;
if (LC_Seek(s, LC_Lit("/"), LC_FindFlag_MatchFindLast, &pos)) {
result = LC_Skip(result, pos + 1);
}
return result;
}
LC_FUNCTION LC_String LC_SkipToLastPeriod(LC_String s) {
int64_t pos;
LC_String result = s;
if (LC_Seek(s, LC_Lit("."), LC_FindFlag_MatchFindLast, &pos)) {
result = LC_Skip(result, pos + 1);
}
return result;
}
LC_FUNCTION int64_t LC_StrLen(char *string) {
int64_t len = 0;
while (*string++ != 0)
len++;
return len;
}
LC_FUNCTION LC_String LC_MakeFromChar(char *string) {
LC_String result;
result.str = (char *)string;
result.len = LC_StrLen(string);
return result;
}
LC_FUNCTION LC_String LC_MakeEmptyString(void) {
return LC_MakeString(0, 0);
}
LC_FUNCTION LC_StringList LC_MakeEmptyList(void) {
LC_StringList result;
result.first = 0;
result.last = 0;
result.char_count = 0;
result.node_count = 0;
return result;
}
LC_FUNCTION LC_String LC_FormatV(LC_Arena *allocator, const char *str, va_list args1) {
va_list args2;
va_copy(args2, args1);
int64_t len = LC_vsnprintf(0, 0, str, args2);
va_end(args2);
char *result = (char *)LC_PushSize(allocator, sizeof(char) * (len + 1));
LC_vsnprintf(result, (int)(len + 1), str, args1);
LC_String res = LC_MakeString(result, len);
return res;
}
LC_FUNCTION LC_String LC_Format(LC_Arena *allocator, const char *str, ...) {
LC_FORMAT(allocator, str, result);
return result;
}
LC_FUNCTION LC_StringNode *LC_CreateNode(LC_Arena *allocator, LC_String string) {
LC_StringNode *result = (LC_StringNode *)LC_PushSize(allocator, sizeof(LC_StringNode));
result->string = string;
result->next = 0;
return result;
}
LC_FUNCTION void LC_ReplaceNodeString(LC_StringList *list, LC_StringNode *node, LC_String new_string) {
list->char_count -= node->string.len;
list->char_count += new_string.len;
node->string = new_string;
}
LC_FUNCTION void LC_AddExistingNode(LC_StringList *list, LC_StringNode *node) {
if (list->first) {
list->last->next = node;
list->last = list->last->next;
} else {
list->first = list->last = node;
}
list->node_count += 1;
list->char_count += node->string.len;
}
LC_FUNCTION void LC_AddArray(LC_Arena *allocator, LC_StringList *list, char **array, int count) {
for (int i = 0; i < count; i += 1) {
LC_String s = LC_MakeFromChar(array[i]);
LC_AddNode(allocator, list, s);
}
}
LC_FUNCTION void LC_AddArrayWithPrefix(LC_Arena *allocator, LC_StringList *list, char *prefix, char **array, int count) {
for (int i = 0; i < count; i += 1) {
LC_Addf(allocator, list, "%s%s", prefix, array[i]);
}
}
LC_FUNCTION LC_StringList LC_MakeList(LC_Arena *allocator, LC_String a) {
LC_StringList result = LC_MakeEmptyList();
LC_AddNode(allocator, &result, a);
return result;
}
LC_FUNCTION LC_StringList LC_CopyList(LC_Arena *allocator, LC_StringList a) {
LC_StringList result = LC_MakeEmptyList();
for (LC_StringNode *it = a.first; it; it = it->next) LC_AddNode(allocator, &result, it->string);
return result;
}
LC_FUNCTION LC_StringList LC_ConcatLists(LC_Arena *allocator, LC_StringList a, LC_StringList b) {
LC_StringList result = LC_MakeEmptyList();
for (LC_StringNode *it = a.first; it; it = it->next) LC_AddNode(allocator, &result, it->string);
for (LC_StringNode *it = b.first; it; it = it->next) LC_AddNode(allocator, &result, it->string);
return result;
}
LC_FUNCTION LC_StringNode *LC_AddNode(LC_Arena *allocator, LC_StringList *list, LC_String string) {
LC_StringNode *node = LC_CreateNode(allocator, string);
LC_AddExistingNode(list, node);
return node;
}
LC_FUNCTION LC_StringNode *LC_Add(LC_Arena *allocator, LC_StringList *list, LC_String string) {
LC_String copy = LC_CopyString(allocator, string);
LC_StringNode *node = LC_CreateNode(allocator, copy);
LC_AddExistingNode(list, node);
return node;
}
LC_FUNCTION LC_String LC_Addf(LC_Arena *allocator, LC_StringList *list, const char *str, ...) {
LC_FORMAT(allocator, str, result);
LC_AddNode(allocator, list, result);
return result;
}
LC_FUNCTION LC_String16 LC_ToWidecharEx(LC_Arena *allocator, LC_String string) {
LC_ASSERT(NULL, sizeof(wchar_t) == 2);
wchar_t *buffer = (wchar_t *)LC_PushSize(allocator, sizeof(wchar_t) * (string.len + 1));
int64_t size = LC_CreateWidecharFromChar(buffer, string.len + 1, string.str, string.len);
LC_String16 result = {buffer, size};
return result;
}
LC_FUNCTION wchar_t *LC_ToWidechar(LC_Arena *allocator, LC_String string) {
LC_String16 result = LC_ToWidecharEx(allocator, string);
return result.str;
}
LC_FUNCTION LC_String LC_FromWidecharEx(LC_Arena *allocator, wchar_t *wstring, int64_t wsize) {
LC_ASSERT(NULL, sizeof(wchar_t) == 2);
int64_t buffer_size = (wsize + 1) * 2;
char *buffer = (char *)LC_PushSize(allocator, buffer_size);
int64_t size = LC_CreateCharFromWidechar(buffer, buffer_size, wstring, wsize);
LC_String result = LC_MakeString(buffer, size);
LC_ASSERT(NULL, size < buffer_size);
return result;
}
LC_FUNCTION int64_t LC_WideLength(wchar_t *string) {
int64_t len = 0;
while (*string++ != 0)
len++;
return len;
}
LC_FUNCTION LC_String LC_FromWidechar(LC_Arena *allocator, wchar_t *wstring) {
int64_t size = LC_WideLength(wstring);
LC_String result = LC_FromWidecharEx(allocator, wstring, size);
return result;
}

277
src/compiler/to_string.c Normal file
View File

@@ -0,0 +1,277 @@
LC_FUNCTION const char *LC_OSToString(LC_OS os) {
switch (os) {
case LC_OS_WINDOWS: return "OS_WINDOWS";
case LC_OS_LINUX: return "OS_LINUX";
case LC_OS_MAC: return "OS_MAC";
default: return "UNKNOWN_OPERATING_SYSTEM";
}
}
LC_FUNCTION const char *LC_GENToString(LC_GEN os) {
switch (os) {
case LC_GEN_C: return "GEN_C";
default: return "UNKNOWN_GENERATOR";
}
}
LC_FUNCTION const char *LC_ARCHToString(LC_ARCH arch) {
switch (arch) {
case LC_ARCH_X86: return "ARCH_X86";
case LC_ARCH_X64: return "ARCH_X64";
default: return "UNKNOWN_ARCHITECTURE";
}
}
LC_FUNCTION const char *LC_ASTKindToString(LC_ASTKind kind) {
static const char *strs[] = {
"ast null",
"ast error",
"ast note",
"ast note list",
"ast file",
"ast package",
"ast ignore",
"typespec procdure argument",
"typespec aggregate member",
"expr call item",
"expr compound item",
"expr note",
"stmt switch case",
"stmt switch default",
"stmt else if",
"stmt else",
"import",
"global note",
"decl proc",
"decl struct",
"decl union",
"decl var",
"decl const",
"decl typedef",
"typespec ident",
"typespec field",
"typespec pointer",
"typespec array",
"typespec proc",
"stmt block",
"stmt note",
"stmt return",
"stmt break",
"stmt continue",
"stmt defer",
"stmt for",
"stmt if",
"stmt switch",
"stmt assign",
"stmt expr",
"stmt var",
"stmt const",
"expr ident",
"expr string",
"expr int",
"expr float",
"expr bool",
"expr type",
"expr binary",
"expr unary",
"expr builtin",
"expr call",
"expr compound",
"expr cast",
"expr field",
"expr index",
"expr pointerindex",
"expr getvalueofpointer",
"expr getpointerofvalue",
};
if (kind < 0 || kind >= LC_ASTKind_Count) {
return "<invalid_ast_kind>";
}
return strs[kind];
}
LC_FUNCTION const char *LC_TypeKindToString(LC_TypeKind kind) {
static const char *strs[] = {
"LC_TypeKind_char",
"LC_TypeKind_uchar",
"LC_TypeKind_short",
"LC_TypeKind_ushort",
"LC_TypeKind_bool",
"LC_TypeKind_int",
"LC_TypeKind_uint",
"LC_TypeKind_long",
"LC_TypeKind_ulong",
"LC_TypeKind_llong",
"LC_TypeKind_ullong",
"LC_TypeKind_float",
"LC_TypeKind_double",
"LC_TypeKind_void",
"LC_TypeKind_Struct",
"LC_TypeKind_Union",
"LC_TypeKind_Pointer",
"LC_TypeKind_Array",
"LC_TypeKind_Proc",
"LC_TypeKind_UntypedInt",
"LC_TypeKind_UntypedFloat",
"LC_TypeKind_UntypedString",
"LC_TypeKind_Incomplete",
"LC_TypeKind_Completing",
"LC_TypeKind_Error",
};
if (kind < 0 || kind >= T_TotalCount) {
return "<invalid_type_kind>";
}
return strs[kind];
}
LC_FUNCTION const char *LC_DeclKindToString(LC_DeclKind decl_kind) {
static const char *strs[] = {
"declaration of error kind",
"type declaration",
"const declaration",
"variable declaration",
"procedure declaration",
"import declaration",
};
if (decl_kind < 0 || decl_kind >= LC_DeclKind_Count) {
return "<invalid_decl_kind>";
}
return strs[decl_kind];
}
LC_FUNCTION const char *LC_TokenKindToString(LC_TokenKind token_kind) {
static const char *strs[] = {
"end of file",
"token error",
"comment",
"doc comment",
"file doc comment",
"package doc comment",
"note '@'",
"hash '#'",
"identifier",
"keyword",
"string literal",
"raw string literal",
"integer literal",
"float literal",
"unicode literal",
"open paren '('",
"close paren ')'",
"open brace '{'",
"close brace '}'",
"open bracket '['",
"close bracket ']'",
"comma ','",
"question mark '?'",
"semicolon ';'",
"period '.'",
"three dots '...'",
"colon ':'",
"multiply '*'",
"divide '/'",
"modulo '%'",
"left shift '<<'",
"right shift '>>'",
"add '+'",
"subtract '-'",
"equals '=='",
"lesser then '<'",
"greater then '>'",
"lesser then or equal '<='",
"greater then or equal '>='",
"not equal '!='",
"bit and '&'",
"bit or '|'",
"bit xor '^'",
"and '&&'",
"or '||'",
"addptr keyword",
"negation '~'",
"exclamation '!'",
"assignment '='",
"assignment '/='",
"assignment '*='",
"assignment '%='",
"assignment '-='",
"assignment '+='",
"assignment '&='",
"assignment '|='",
"assignment '^='",
"assignment '<<='",
"assignment '>>='",
};
if (token_kind < 0 || token_kind >= LC_TokenKind_Count) {
return "<invalid_token_kind>";
}
return strs[token_kind];
}
LC_FUNCTION const char *LC_TokenKindToOperator(LC_TokenKind token_kind) {
static const char *strs[] = {
"end of file",
"token error",
"comment",
"doc comment",
"file doc comment",
"package doc comment",
"@",
"#",
"identifier",
"keyword",
"string literal",
"raw string literal",
"integer literal",
"float literal",
"unicode literal",
"(",
")",
"{",
"}",
"[",
"]",
",",
"?",
";",
".",
"...",
":",
"*",
"/",
"%",
"<<",
">>",
"+",
"-",
"==",
"<",
">",
"<=",
">=",
"!=",
"&",
"|",
"^",
"&&",
"||",
"+",
"~",
"!",
"=",
"/=",
"*=",
"%=",
"-=",
"+=",
"&=",
"|=",
"^=",
"<<=",
">>=",
};
if (token_kind < 0 || token_kind >= LC_TokenKind_Count) {
return "<invalid_token_operator>";
}
return strs[token_kind];
}

158
src/compiler/unicode.c Normal file
View File

@@ -0,0 +1,158 @@
LC_FUNCTION LC_UTF32Result LC_ConvertUTF16ToUTF32(uint16_t *c, int max_advance) {
LC_UTF32Result result;
LC_MemoryZero(&result, sizeof(result));
if (max_advance >= 1) {
result.advance = 1;
result.out_str = c[0];
if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) {
if (max_advance >= 2) {
result.out_str = 0x10000;
result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF);
result.advance = 2;
} else
result.error = 2;
}
} else {
result.error = 1;
}
return result;
}
LC_FUNCTION LC_UTF8Result LC_ConvertUTF32ToUTF8(uint32_t codepoint) {
LC_UTF8Result result;
LC_MemoryZero(&result, sizeof(result));
if (codepoint <= 0x7F) {
result.len = 1;
result.out_str[0] = (char)codepoint;
} else if (codepoint <= 0x7FF) {
result.len = 2;
result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6));
result.out_str[1] = 0x80 | (0x3f & codepoint);
} else if (codepoint <= 0xFFFF) { // 16 bit word
result.len = 3;
result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits
} else if (codepoint <= 0x10FFFF) { // 21 bit word
result.len = 4;
result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits
result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits
} else {
result.error = 1;
}
return result;
}
LC_FUNCTION LC_UTF32Result LC_ConvertUTF8ToUTF32(char *c, int max_advance) {
LC_UTF32Result result;
LC_MemoryZero(&result, sizeof(result));
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
if (max_advance >= 1) {
result.out_str = c[0];
result.advance = 1;
} else result.error = 1;
}
else if ((c[0] & 0xe0) == 0xc0) {
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
if (max_advance >= 2) {
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
result.advance = 2;
} else result.error = 2;
} else result.error = 2;
}
else if ((c[0] & 0xf0) == 0xe0) {
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
if (max_advance >= 3) {
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
result.advance = 3;
} else result.error = 3;
} else result.error = 3;
}
else if ((c[0] & 0xf8) == 0xf0) {
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
if (max_advance >= 4) {
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
result.advance = 4;
} else result.error = 4;
} else result.error = 4;
} else result.error = 4;
return result;
}
LC_FUNCTION LC_UTF16Result LC_ConvertUTF32ToUTF16(uint32_t codepoint) {
LC_UTF16Result result;
LC_MemoryZero(&result, sizeof(result));
if (codepoint < 0x10000) {
result.out_str[0] = (uint16_t)codepoint;
result.out_str[1] = 0;
result.len = 1;
} else if (codepoint <= 0x10FFFF) {
uint32_t code = (codepoint - 0x10000);
result.out_str[0] = (uint16_t)(0xD800 | (code >> 10));
result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF));
result.len = 2;
} else {
result.error = 1;
}
return result;
}
#define LC__HANDLE_DECODE_ERROR(question_mark) \
{ \
if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \
break; \
}
LC_FUNCTION int64_t LC_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen) {
int64_t outlen = 0;
for (int64_t i = 0; i < inlen && in[i];) {
LC_UTF32Result decode = LC_ConvertUTF16ToUTF32((uint16_t *)(in + i), (int)(inlen - i));
if (!decode.error) {
i += decode.advance;
LC_UTF8Result encode = LC_ConvertUTF32ToUTF8(decode.out_str);
if (!encode.error) {
for (int64_t j = 0; j < encode.len; j++) {
if (outlen < buffer_size - 1) {
buffer[outlen++] = encode.out_str[j];
}
}
} else LC__HANDLE_DECODE_ERROR('?');
} else LC__HANDLE_DECODE_ERROR('?');
}
buffer[outlen] = 0;
return outlen;
}
LC_FUNCTION int64_t LC_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen) {
int64_t outlen = 0;
for (int64_t i = 0; i < inlen;) {
LC_UTF32Result decode = LC_ConvertUTF8ToUTF32(in + i, (int)(inlen - i));
if (!decode.error) {
i += decode.advance;
LC_UTF16Result encode = LC_ConvertUTF32ToUTF16(decode.out_str);
if (!encode.error) {
for (int64_t j = 0; j < encode.len; j++) {
if (outlen < buffer_size - 1) {
buffer[outlen++] = encode.out_str[j];
}
}
} else LC__HANDLE_DECODE_ERROR(0x003f);
} else LC__HANDLE_DECODE_ERROR(0x003f);
}
buffer[outlen] = 0;
return outlen;
}
#undef LC__HANDLE_DECODE_ERROR

30
src/compiler/unix_arena.c Normal file
View File

@@ -0,0 +1,30 @@
#define LC_V_UNIX_PAGE_SIZE 4096
LC_FUNCTION LC_VMemory LC_VReserve(size_t size) {
LC_VMemory result = {};
size_t size_aligned = LC_AlignUp(size, LC_V_UNIX_PAGE_SIZE);
result.data = (uint8_t *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
LC_Assertf(result.data, "Failed to reserve memory using mmap!!");
if (result.data) {
result.reserve = size_aligned;
}
return result;
}
LC_FUNCTION bool LC_VCommit(LC_VMemory *m, size_t commit) {
uint8_t *pointer = LC_V_AdvanceCommit(m, &commit, LC_V_UNIX_PAGE_SIZE);
if (pointer) {
int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE);
LC_Assertf(mprotect_result == 0, "Failed to commit more memory using mmap");
if (mprotect_result == 0) {
m->commit += commit;
return true;
}
}
return false;
}
LC_FUNCTION void LC_VDeallocate(LC_VMemory *m) {
int result = munmap(m->data, m->reserve);
LC_Assertf(result == 0, "Failed to release virtual memory using munmap");
}

View File

@@ -0,0 +1,89 @@
LC_FUNCTION bool LC_EnableTerminalColors(void) {
return true;
}
LC_FUNCTION bool LC_IsDir(LC_Arena *arena, LC_String path) {
bool result = false;
LC_TempArena ch = LC_BeginTemp(arena);
LC_String copy = LC_CopyString(arena, path);
struct stat s;
if (stat(copy.str, &s) != 0) {
result = false;
} else {
result = S_ISDIR(s.st_mode);
}
LC_EndTemp(ch);
return result;
}
LC_FUNCTION LC_String LC_GetAbsolutePath(LC_Arena *arena, LC_String relative) {
LC_String copy = LC_CopyString(arena, relative);
char *buffer = (char *)LC_PushSizeNonZeroed(arena, PATH_MAX);
realpath((char *)copy.str, buffer);
LC_String result = LC_MakeFromChar(buffer);
return result;
}
LC_FUNCTION bool LC_IsValid(LC_FileIter it) {
return it.is_valid;
}
LC_FUNCTION void LC_Advance(LC_FileIter *it) {
struct dirent *file = 0;
while ((file = readdir((DIR *)it->dir)) != NULL) {
if (file->d_name[0] == '.' && file->d_name[1] == '.' && file->d_name[2] == 0) continue;
if (file->d_name[0] == '.' && file->d_name[1] == 0) continue;
it->is_directory = file->d_type == DT_DIR;
it->filename = LC_CopyChar(it->arena, file->d_name);
const char *dir_char_ending = it->is_directory ? "/" : "";
const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
it->relative_path = LC_Format(it->arena, "%.*s%s%s%s", LC_Expand(it->path), separator, file->d_name, dir_char_ending);
it->absolute_path = LC_GetAbsolutePath(it->arena, it->relative_path);
if (it->is_directory) it->absolute_path = LC_Format(it->arena, "%.*s/", LC_Expand(it->absolute_path));
it->is_valid = true;
return;
}
it->is_valid = false;
closedir((DIR *)it->dir);
}
LC_FUNCTION LC_FileIter LC_IterateFiles(LC_Arena *arena, LC_String path) {
LC_FileIter it = {0};
it.arena = arena;
it.path = path = LC_CopyString(arena, path);
it.dir = (void *)opendir((char *)path.str);
if (!it.dir) return it;
LC_Advance(&it);
return it;
}
LC_FUNCTION LC_String LC_ReadFile(LC_Arena *arena, LC_String path) {
LC_String result = LC_MakeEmptyString();
// ftell returns insane size if file is
// a directory **on some machines** KEKW
if (LC_IsDir(arena, path)) {
return result;
}
path = LC_CopyString(arena, path);
FILE *f = fopen(path.str, "rb");
if (f) {
fseek(f, 0, SEEK_END);
result.len = ftell(f);
fseek(f, 0, SEEK_SET);
result.str = (char *)LC_PushSizeNonZeroed(arena, result.len + 1);
fread(result.str, result.len, 1, f);
result.str[result.len] = 0;
fclose(f);
}
return result;
}

328
src/compiler/value.c Normal file
View File

@@ -0,0 +1,328 @@
LC_Operand LC_OPNull;
LC_FUNCTION LC_Operand LC_OPError(void) {
LC_Operand result = {LC_OPF_Error};
return result;
}
LC_FUNCTION LC_Operand LC_OPConstType(LC_Type *type) {
LC_Operand result = {LC_OPF_UTConst | LC_OPF_Const};
result.type = type;
return result;
}
LC_FUNCTION LC_Operand LC_OPDecl(LC_Decl *decl) {
LC_Operand result = {0};
result.decl = decl;
return result;
}
LC_FUNCTION LC_Operand LC_OPType(LC_Type *type) {
LC_Operand result = {0};
result.type = type;
return result;
}
LC_FUNCTION LC_Operand LC_OPLValueAndType(LC_Type *type) {
LC_Operand result = LC_OPType(type);
result.flags = LC_OPF_LValue;
return result;
}
LC_FUNCTION LC_Operand LC_ReportASTError(LC_AST *n, const char *str, ...) {
LC_FORMAT(L->arena, str, s8);
LC_SendErrorMessage(n ? n->pos : NULL, s8);
L->errors += 1;
return LC_OPError();
}
LC_FUNCTION LC_Operand LC_ReportASTErrorEx(LC_AST *n1, LC_AST *n2, const char *str, ...) {
LC_FORMAT(L->arena, str, s8);
LC_SendErrorMessage(n1->pos, s8);
LC_SendErrorMessage(n2->pos, s8);
L->errors += 1;
return LC_OPError();
}
LC_FUNCTION LC_Operand LC_ConstCastFloat(LC_AST *pos, LC_Operand op) {
LC_ASSERT(pos, LC_IsUTConst(op));
LC_ASSERT(pos, LC_IsUntyped(op.type));
if (LC_IsUTInt(op.type)) op.val.d = LC_Bigint_as_float(&op.val.i);
if (LC_IsUTStr(op.type)) return LC_ReportASTError(pos, "Trying to convert '%s' to float", LC_GenLCType(op.type));
op.type = L->tuntypedfloat;
return op;
}
LC_FUNCTION LC_Operand LC_ConstCastInt(LC_AST *pos, LC_Operand op) {
LC_ASSERT(pos, LC_IsUTConst(op));
LC_ASSERT(pos, LC_IsUntyped(op.type));
if (LC_IsUTFloat(op.type)) {
double v = op.val.d; // add rounding?
LC_Bigint_init_signed(&op.val.i, (int64_t)v);
}
if (LC_IsUTStr(op.type)) return LC_ReportASTError(pos, "Trying to convert %s to int", LC_GenLCType(op.type));
op.type = L->tuntypedint;
return op;
}
LC_FUNCTION LC_Operand LC_OPInt(int64_t v) {
LC_Operand op = {0};
op.type = L->tuntypedint;
op.flags |= LC_OPF_UTConst | LC_OPF_Const;
LC_Bigint_init_signed(&op.v.i, v);
return op;
}
LC_FUNCTION LC_Operand LC_OPIntT(LC_Type *type, int64_t v) {
LC_ASSERT(NULL, LC_IsInt(type));
LC_Operand op = LC_OPInt(v);
op.type = type;
return op;
}
LC_FUNCTION LC_Operand LC_OPModDefaultUT(LC_Operand val) {
if (LC_IsUntyped(val.type)) {
val.type = val.type->tbase;
}
return val;
}
LC_FUNCTION LC_Operand LC_OPModType(LC_Operand op, LC_Type *type) {
if (LC_IsUTConst(op)) {
if (LC_IsUTInt(op.type) && LC_IsFloat(type)) {
op = LC_ConstCastFloat(NULL, op);
}
}
op.type = type;
return op;
}
LC_FUNCTION LC_Operand LC_OPModBool(LC_Operand op) {
op.type = L->tuntypedbool;
return op;
}
LC_FUNCTION LC_Operand LC_OPModBoolV(LC_Operand op, int v) {
op.type = L->tuntypedbool;
LC_Bigint_init_signed(&op.v.i, v);
return op;
}
LC_FUNCTION LC_Operand LC_EvalBinary(LC_AST *pos, LC_Operand a, LC_TokenKind op, LC_Operand b) {
LC_ASSERT(pos, LC_IsUTConst(a));
LC_ASSERT(pos, LC_IsUTConst(b));
LC_ASSERT(pos, LC_IsUntyped(a.type));
LC_ASSERT(pos, LC_IsUntyped(b.type));
LC_ASSERT(pos, a.type->kind == b.type->kind);
LC_Operand c = LC_OPConstType(a.type);
if (LC_IsUTStr(a.type)) {
return LC_ReportASTError(pos, "invalid operand %s for binary expr of type untyped string", LC_TokenKindToString(op));
}
if (LC_IsUTFloat(a.type)) {
switch (op) {
case LC_TokenKind_Add: c.v.d = a.v.d + b.v.d; break;
case LC_TokenKind_Sub: c.v.d = a.v.d - b.v.d; break;
case LC_TokenKind_Mul: c.v.d = a.v.d * b.v.d; break;
case LC_TokenKind_Div: {
if (b.v.d == 0.0) return LC_ReportASTError(pos, "division by 0");
c.v.d = a.v.d / b.v.d;
} break;
case LC_TokenKind_LesserThenEq: c = LC_OPModBoolV(c, a.v.d <= b.v.d); break;
case LC_TokenKind_GreaterThenEq: c = LC_OPModBoolV(c, a.v.d >= b.v.d); break;
case LC_TokenKind_GreaterThen: c = LC_OPModBoolV(c, a.v.d > b.v.d); break;
case LC_TokenKind_LesserThen: c = LC_OPModBoolV(c, a.v.d < b.v.d); break;
case LC_TokenKind_Equals: c = LC_OPModBoolV(c, a.v.d == b.v.d); break;
case LC_TokenKind_NotEquals: c = LC_OPModBoolV(c, a.v.d != b.v.d); break;
default: return LC_ReportASTError(pos, "invalid operand %s for binary expr of type untyped float", LC_TokenKindToString(op));
}
}
if (LC_IsUTInt(a.type)) {
switch (op) {
case LC_TokenKind_BitXor: LC_Bigint_xor(&c.v.i, &a.v.i, &b.v.i); break;
case LC_TokenKind_BitAnd: LC_Bigint_and(&c.v.i, &a.v.i, &b.v.i); break;
case LC_TokenKind_BitOr: LC_Bigint_or(&c.v.i, &a.v.i, &b.v.i); break;
case LC_TokenKind_Add: LC_Bigint_add(&c.v.i, &a.v.i, &b.v.i); break;
case LC_TokenKind_Sub: LC_Bigint_sub(&c.v.i, &a.v.i, &b.v.i); break;
case LC_TokenKind_Mul: LC_Bigint_mul(&c.v.i, &a.v.i, &b.v.i); break;
case LC_TokenKind_Div: {
if (b.v.i.digit_count == 0) return LC_ReportASTError(pos, "division by zero in constant expression");
LC_Bigint_div_floor(&c.v.i, &a.v.i, &b.v.i);
} break;
case LC_TokenKind_Mod: {
if (b.v.i.digit_count == 0) return LC_ReportASTError(pos, "modulo by zero in constant expression");
LC_Bigint_mod(&c.v.i, &a.v.i, &b.v.i);
} break;
case LC_TokenKind_LeftShift: LC_Bigint_shl(&c.v.i, &a.v.i, &b.v.i); break;
case LC_TokenKind_RightShift: LC_Bigint_shr(&c.v.i, &a.v.i, &b.v.i); break;
case LC_TokenKind_And: {
int left = LC_Bigint_cmp_zero(&a.v.i) != LC_CmpRes_EQ;
int right = LC_Bigint_cmp_zero(&b.v.i) != LC_CmpRes_EQ;
c = LC_OPModBoolV(c, left && right);
} break;
case LC_TokenKind_Or: {
int left = LC_Bigint_cmp_zero(&a.v.i) != LC_CmpRes_EQ;
int right = LC_Bigint_cmp_zero(&b.v.i) != LC_CmpRes_EQ;
c = LC_OPModBoolV(c, left || right);
} break;
default: {
LC_CmpRes cmp = LC_Bigint_cmp(&a.v.i, &b.v.i);
switch (op) {
case LC_TokenKind_LesserThenEq: c = LC_OPModBoolV(c, (cmp == LC_CmpRes_LT) || (cmp == LC_CmpRes_EQ)); break;
case LC_TokenKind_GreaterThenEq: c = LC_OPModBoolV(c, (cmp == LC_CmpRes_GT) || (cmp == LC_CmpRes_EQ)); break;
case LC_TokenKind_GreaterThen: c = LC_OPModBoolV(c, cmp == LC_CmpRes_GT); break;
case LC_TokenKind_LesserThen: c = LC_OPModBoolV(c, cmp == LC_CmpRes_LT); break;
case LC_TokenKind_Equals: c = LC_OPModBoolV(c, cmp == LC_CmpRes_EQ); break;
case LC_TokenKind_NotEquals: c = LC_OPModBoolV(c, cmp != LC_CmpRes_EQ); break;
default: return LC_ReportASTError(pos, "invalid operand %s for binary expr of type untyped int", LC_TokenKindToString(op));
}
}
}
}
return c;
}
LC_FUNCTION LC_Operand LC_EvalUnary(LC_AST *pos, LC_TokenKind op, LC_Operand a) {
LC_ASSERT(pos, LC_IsUTConst(a));
LC_ASSERT(pos, LC_IsUntyped(a.type));
LC_Operand c = a;
if (LC_IsUTStr(a.type)) {
return LC_ReportASTError(pos, "invalid operand %s for unary expr of type untyped string", LC_TokenKindToString(op));
}
if (LC_IsUTFloat(a.type)) {
switch (op) {
case LC_TokenKind_Sub: c.v.d = -a.v.d; break;
case LC_TokenKind_Add: c.v.d = +a.v.d; break;
default: return LC_ReportASTError(pos, "invalid operand %s for unary expr of type untyped float", LC_TokenKindToString(op));
}
}
if (LC_IsUTInt(a.type)) {
switch (op) {
case LC_TokenKind_Not: c = LC_OPModBoolV(c, LC_Bigint_cmp_zero(&a.v.i) == LC_CmpRes_EQ); break;
case LC_TokenKind_Sub: LC_Bigint_negate(&c.v.i, &a.v.i); break;
case LC_TokenKind_Add: c = a; break;
case LC_TokenKind_Neg: LC_Bigint_not(&c.v.i, &a.v.i, a.type->tbase->size * 8, !a.type->tbase->is_unsigned); break;
default: return LC_ReportASTError(pos, "invalid operand %s for unary expr of type untyped int", LC_TokenKindToString(op));
}
}
return c;
}
LC_FUNCTION bool LC_BigIntFits(LC_BigInt i, LC_Type *type) {
LC_ASSERT(NULL, LC_IsInt(type));
if (!LC_Bigint_fits_in_bits(&i, type->size * 8, !type->is_unsigned)) {
return false;
}
return true;
}
LC_FUNCTION LC_OPResult LC_IsBinaryExprValidForType(LC_TokenKind op, LC_Type *type) {
if (LC_IsFloat(type)) {
switch (op) {
case LC_TokenKind_Add: return LC_OPResult_Ok; break;
case LC_TokenKind_Sub: return LC_OPResult_Ok; break;
case LC_TokenKind_Mul: return LC_OPResult_Ok; break;
case LC_TokenKind_Div: return LC_OPResult_Ok; break;
case LC_TokenKind_LesserThenEq: return LC_OPResult_Bool; break;
case LC_TokenKind_GreaterThenEq: return LC_OPResult_Bool; break;
case LC_TokenKind_GreaterThen: return LC_OPResult_Bool; break;
case LC_TokenKind_LesserThen: return LC_OPResult_Bool; break;
case LC_TokenKind_Equals: return LC_OPResult_Bool; break;
case LC_TokenKind_NotEquals: return LC_OPResult_Bool; break;
default: return LC_OPResult_Error;
}
}
if (LC_IsInt(type)) {
switch (op) {
case LC_TokenKind_BitXor: return LC_OPResult_Ok; break;
case LC_TokenKind_BitAnd: return LC_OPResult_Ok; break;
case LC_TokenKind_BitOr: return LC_OPResult_Ok; break;
case LC_TokenKind_Add: return LC_OPResult_Ok; break;
case LC_TokenKind_Sub: return LC_OPResult_Ok; break;
case LC_TokenKind_Mul: return LC_OPResult_Ok; break;
case LC_TokenKind_Div: return LC_OPResult_Ok; break;
case LC_TokenKind_Mod: return LC_OPResult_Ok; break;
case LC_TokenKind_LeftShift: return LC_OPResult_Ok; break;
case LC_TokenKind_RightShift: return LC_OPResult_Ok; break;
case LC_TokenKind_And: return LC_OPResult_Bool; break;
case LC_TokenKind_Or: return LC_OPResult_Bool; break;
case LC_TokenKind_LesserThenEq: return LC_OPResult_Bool; break;
case LC_TokenKind_GreaterThenEq: return LC_OPResult_Bool; break;
case LC_TokenKind_GreaterThen: return LC_OPResult_Bool; break;
case LC_TokenKind_LesserThen: return LC_OPResult_Bool; break;
case LC_TokenKind_Equals: return LC_OPResult_Bool; break;
case LC_TokenKind_NotEquals: return LC_OPResult_Bool; break;
default: return LC_OPResult_Error;
}
}
if (LC_IsPtrLike(type)) {
switch (op) {
case LC_TokenKind_And: return LC_OPResult_Bool; break;
case LC_TokenKind_Or: return LC_OPResult_Bool; break;
case LC_TokenKind_LesserThenEq: return LC_OPResult_Bool; break;
case LC_TokenKind_GreaterThenEq: return LC_OPResult_Bool; break;
case LC_TokenKind_GreaterThen: return LC_OPResult_Bool; break;
case LC_TokenKind_LesserThen: return LC_OPResult_Bool; break;
case LC_TokenKind_Equals: return LC_OPResult_Bool; break;
case LC_TokenKind_NotEquals: return LC_OPResult_Bool; break;
default: return LC_OPResult_Error;
}
}
return LC_OPResult_Error;
}
LC_FUNCTION LC_OPResult LC_IsUnaryOpValidForType(LC_TokenKind op, LC_Type *type) {
if (LC_IsFloat(type)) {
if (op == LC_TokenKind_Sub) return LC_OPResult_Ok;
if (op == LC_TokenKind_Add) return LC_OPResult_Ok;
}
if (LC_IsInt(type)) {
if (op == LC_TokenKind_Not) return LC_OPResult_Bool;
if (op == LC_TokenKind_Sub) return LC_OPResult_Ok;
if (op == LC_TokenKind_Add) return LC_OPResult_Ok;
if (op == LC_TokenKind_Neg) return LC_OPResult_Ok;
}
if (LC_IsPtrLike(type)) {
if (op == LC_TokenKind_Not) return LC_OPResult_Bool;
}
return LC_OPResult_Error;
}
LC_FUNCTION LC_OPResult LC_IsAssignValidForType(LC_TokenKind op, LC_Type *type) {
if (op == LC_TokenKind_Assign) return LC_OPResult_Ok;
if (LC_IsInt(type)) {
switch (op) {
case LC_TokenKind_DivAssign: return LC_OPResult_Ok;
case LC_TokenKind_MulAssign: return LC_OPResult_Ok;
case LC_TokenKind_ModAssign: return LC_OPResult_Ok;
case LC_TokenKind_SubAssign: return LC_OPResult_Ok;
case LC_TokenKind_AddAssign: return LC_OPResult_Ok;
case LC_TokenKind_BitAndAssign: return LC_OPResult_Ok;
case LC_TokenKind_BitOrAssign: return LC_OPResult_Ok;
case LC_TokenKind_BitXorAssign: return LC_OPResult_Ok;
case LC_TokenKind_LeftShiftAssign: return LC_OPResult_Ok;
case LC_TokenKind_RightShiftAssign: return LC_OPResult_Ok;
default: return LC_OPResult_Error;
}
}
if (LC_IsFloat(type)) {
switch (op) {
case LC_TokenKind_DivAssign: return LC_OPResult_Ok;
case LC_TokenKind_MulAssign: return LC_OPResult_Ok;
case LC_TokenKind_SubAssign: return LC_OPResult_Ok;
case LC_TokenKind_AddAssign: return LC_OPResult_Ok;
default: return LC_OPResult_Error;
}
}
return LC_OPResult_Error;
}
LC_FUNCTION int LC_GetLevelsOfIndirection(LC_Type *type) {
if (type->kind == LC_TypeKind_Pointer) return LC_GetLevelsOfIndirection(type->tbase) + 1;
if (type->kind == LC_TypeKind_Array) return LC_GetLevelsOfIndirection(type->tbase) + 1;
return 0;
}

View File

@@ -0,0 +1,44 @@
const size_t LC_V_WIN32_PAGE_SIZE = 4096;
LC_FUNCTION LC_VMemory LC_VReserve(size_t size) {
LC_VMemory result;
LC_MemoryZero(&result, sizeof(result));
size_t adjusted_size = LC_AlignUp(size, LC_V_WIN32_PAGE_SIZE);
result.data = (uint8_t *)VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE);
LC_Assertf(result.data, "Failed to reserve virtual memory");
result.reserve = adjusted_size;
return result;
}
LC_FUNCTION bool LC_VCommit(LC_VMemory *m, size_t commit) {
uint8_t *pointer = LC_V_AdvanceCommit(m, &commit, LC_V_WIN32_PAGE_SIZE);
if (pointer) {
void *result = VirtualAlloc(pointer, commit, MEM_COMMIT, PAGE_READWRITE);
LC_Assertf(result, "Failed to commit more memory");
if (result) {
m->commit += commit;
return true;
}
}
return false;
}
LC_FUNCTION void LC_VDeallocate(LC_VMemory *m) {
BOOL result = VirtualFree(m->data, 0, MEM_RELEASE);
LC_Assertf(result != 0, "Failed to release LC_VMemory");
}
LC_FUNCTION bool LC_VDecommitPos(LC_VMemory *m, size_t pos) {
size_t aligned = LC_AlignDown(pos, LC_V_WIN32_PAGE_SIZE);
size_t adjusted_pos = LC_CLAMP_TOP(aligned, m->commit);
size_t size_to_decommit = m->commit - adjusted_pos;
if (size_to_decommit) {
uint8_t *base_address = m->data + adjusted_pos;
BOOL result = VirtualFree(base_address, size_to_decommit, MEM_DECOMMIT);
if (result) {
m->commit -= size_to_decommit;
return true;
}
}
return false;
}

View File

@@ -0,0 +1,118 @@
typedef struct LC_Win32_FileIter {
HANDLE handle;
WIN32_FIND_DATAW data;
} LC_Win32_FileIter;
LC_FUNCTION bool LC_IsDir(LC_Arena *arena, LC_String path) {
wchar_t wbuff[1024];
LC_CreateWidecharFromChar(wbuff, LC_StrLenof(wbuff), path.str, path.len);
DWORD dwAttrib = GetFileAttributesW(wbuff);
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
}
LC_FUNCTION LC_String LC_GetAbsolutePath(LC_Arena *arena, LC_String relative) {
wchar_t wpath[1024];
LC_CreateWidecharFromChar(wpath, LC_StrLenof(wpath), relative.str, relative.len);
wchar_t wpath_abs[1024];
DWORD written = GetFullPathNameW((wchar_t *)wpath, LC_StrLenof(wpath_abs), wpath_abs, 0);
if (written == 0)
return LC_MakeEmptyString();
LC_String path = LC_FromWidecharEx(arena, wpath_abs, written);
LC_NormalizePathUnsafe(path);
return path;
}
LC_FUNCTION bool LC_IsValid(LC_FileIter it) {
return it.is_valid;
}
LC_FUNCTION void LC_Advance(LC_FileIter *it) {
while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) {
WIN32_FIND_DATAW *data = &it->w32->data;
// Skip '.' and '..'
if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue;
if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue;
it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
it->filename = LC_FromWidecharEx(it->arena, data->cFileName, LC_WideLength(data->cFileName));
const char *is_dir = it->is_directory ? "/" : "";
const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
it->relative_path = LC_Format(it->arena, "%.*s%s%.*s%s", LC_Expand(it->path), separator, LC_Expand(it->filename), is_dir);
it->absolute_path = LC_GetAbsolutePath(it->arena, it->relative_path);
it->is_valid = true;
if (it->is_directory) {
LC_ASSERT(NULL, it->relative_path.str[it->relative_path.len - 1] == '/');
LC_ASSERT(NULL, it->absolute_path.str[it->absolute_path.len - 1] == '/');
}
return;
}
it->is_valid = false;
DWORD error = GetLastError();
LC_ASSERT(NULL, error == ERROR_NO_MORE_FILES);
FindClose(it->w32->handle);
}
LC_FUNCTION LC_FileIter LC_IterateFiles(LC_Arena *scratch_arena, LC_String path) {
LC_FileIter it = {0};
it.arena = scratch_arena;
it.path = path;
LC_String modified_path = LC_Format(it.arena, "%.*s\\*", LC_Expand(path));
wchar_t *wbuff = LC_PushArray(it.arena, wchar_t, modified_path.len + 1);
int64_t wsize = LC_CreateWidecharFromChar(wbuff, modified_path.len + 1, modified_path.str, modified_path.len);
LC_ASSERT(NULL, wsize);
it.w32 = LC_PushStruct(it.arena, LC_Win32_FileIter);
it.w32->handle = FindFirstFileW(wbuff, &it.w32->data);
if (it.w32->handle == INVALID_HANDLE_VALUE) {
it.is_valid = false;
return it;
}
LC_ASSERT(NULL, it.w32->data.cFileName[0] == '.' && it.w32->data.cFileName[1] == 0);
LC_Advance(&it);
return it;
}
LC_FUNCTION LC_String LC_ReadFile(LC_Arena *arena, LC_String path) {
LC_String result = LC_MakeEmptyString();
wchar_t wpath[1024];
LC_CreateWidecharFromChar(wpath, LC_StrLenof(wpath), path.str, path.len);
HANDLE handle = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
LARGE_INTEGER file_size;
if (GetFileSizeEx(handle, &file_size)) {
if (file_size.QuadPart != 0) {
result.len = (int64_t)file_size.QuadPart;
result.str = (char *)LC_PushSizeNonZeroed(arena, result.len + 1);
DWORD read;
if (ReadFile(handle, result.str, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files?
if (read == result.len) {
result.str[result.len] = 0;
}
}
}
}
CloseHandle(handle);
}
return result;
}
LC_FUNCTION bool LC_EnableTerminalColors(void) {
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut != INVALID_HANDLE_VALUE) {
DWORD dwMode = 0;
if (GetConsoleMode(hOut, &dwMode)) {
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (SetConsoleMode(hOut, dwMode)) {
return true;
}
}
}
return false;
}

115
src/core/allocator.c Normal file
View File

@@ -0,0 +1,115 @@
#ifndef MA_CMalloc
#include <stdlib.h>
#define MA_CMalloc(x) malloc(x)
#define MA_CFree(x) free(x)
#define MA_CRealloc(p, size) realloc(p, size)
#endif
MA_API M_Allocator MA_BootstrapExclusive(void) {
MA_Arena bootstrap_arena = {0};
MA_Arena *arena = MA_PushStruct(&bootstrap_arena, MA_Arena);
*arena = bootstrap_arena;
arena->base_len = arena->len;
return MA_GetExclusiveAllocator(arena);
}
MA_API void *M__AllocNonZeroed(M_Allocator allocator, size_t size) {
void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size, 0);
return p;
}
MA_API void *M__Alloc(M_Allocator allocator, size_t size) {
void *p = allocator.p(allocator.obj, M_AllocatorOp_Allocate, NULL, size, 0);
MA_MemoryZero(p, size);
return p;
}
MA_API void *M__AllocCopy(M_Allocator allocator, void *p, size_t size) {
void *copy_buffer = M__AllocNonZeroed(allocator, size);
MA_MemoryCopy(copy_buffer, p, size);
return copy_buffer;
}
MA_API void M__Dealloc(M_Allocator allocator, void *p) {
allocator.p(allocator.obj, M_AllocatorOp_Deallocate, p, 0, 0);
}
MA_API void *M__Realloc(M_Allocator allocator, void *p, size_t size, size_t old_size) {
void *result = allocator.p(allocator.obj, M_AllocatorOp_Reallocate, p, size, old_size);
// @todo: add old_size? because we can't zero
return result;
}
MA_StaticFunc void *M_ClibAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) {
if (kind == M_AllocatorOp_Allocate) {
return MA_CMalloc(size);
}
if (kind == M_AllocatorOp_Deallocate) {
MA_CFree(p);
return NULL;
}
if (kind == M_AllocatorOp_Reallocate) {
return MA_CRealloc(p, size);
}
MA_Assertf(0, "MA_Arena invalid codepath");
return NULL;
}
MA_API void *MA_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) {
if (kind == M_AllocatorOp_Allocate) {
return MA__PushSizeNonZeroed((MA_Arena *)allocator, size);
}
else if (kind == M_AllocatorOp_Reallocate) {
void *new_p = MA__PushSizeNonZeroed((MA_Arena *)allocator, size);
MA_MemoryCopy(new_p, p, old_size);
return new_p;
}
else if (kind == M_AllocatorOp_Deallocate) {
return NULL;
}
MA_Assertf(0, "MA_Arena invalid codepath");
return NULL;
}
MA_API void *MA_ExclusiveAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size) {
MA_Arena *arena = (MA_Arena *)allocator;
if (kind == M_AllocatorOp_Reallocate) {
if (size > old_size) {
size_t size_to_push = size - old_size;
void *result = MA__PushSizeNonZeroed(arena, size_to_push);
if (!p) p = result;
return p;
}
}
if (kind == M_AllocatorOp_Deallocate) {
MA_DeallocateArena(arena);
return NULL;
}
MA_Assertf(0, "MA_Arena invalid codepath");
return NULL;
}
MA_API M_Allocator MA_GetExclusiveAllocator(MA_Arena *arena) {
M_Allocator allocator = {(int *)arena, MA_ExclusiveAllocatorProc};
return allocator;
}
MA_API M_Allocator MA_GetAllocator(MA_Arena *arena) {
M_Allocator allocator = {(int *)arena, MA_AllocatorProc};
return allocator;
}
MA_API M_Allocator M_GetSystemAllocator(void) {
M_Allocator allocator;
allocator.obj = 0;
allocator.p = M_ClibAllocatorProc;
return allocator;
}

44
src/core/allocator.h Normal file
View File

@@ -0,0 +1,44 @@
typedef struct M_Allocator M_Allocator;
typedef enum M_AllocatorOp {
M_AllocatorOp_Invalid,
M_AllocatorOp_Allocate,
M_AllocatorOp_Deallocate,
M_AllocatorOp_Reallocate,
} M_AllocatorOp;
typedef void *M_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size);
MA_API void *MA_AllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size);
MA_API void *MA_ExclusiveAllocatorProc(void *allocator, M_AllocatorOp kind, void *p, size_t size, size_t old_size);
struct M_Allocator {
// it's int for type safety because C++ somehow allows this:
// struct Array { M_Allocator allocator; }
// Array = {arena}; WTF???!??!?
// without actually passing M_Allocator but just a pointer
int *obj;
M_AllocatorProc *p;
};
#define M_AllocStruct(a, T) (T *)M_Alloc((a), sizeof(T))
#define M_AllocArray(a, T, c) (T *)M_Alloc((a), sizeof(T) * (c))
#define M_AllocStructNonZeroed(a, T) (T *)M_AllocNonZeroed((a), sizeof(T))
#define M_AllocArrayNonZeroed(a, T, c) (T *)M_AllocNonZeroed((a), sizeof(T) * (c))
#define M_AllocStructCopy(a, T, p) (T *)M_PushCopy(a, (p), sizeof(T))
#define M_Alloc(a, size) M__Alloc(a, size)
#define M_AllocNonZeroed(a, size) M__AllocNonZeroed(a, size)
#define M_AllocCopy(a, p, size) M__AllocCopy(a, p, size)
#define M_Realloc(a, p, size, old_size) M__Realloc(a, p, size, old_size)
#define M_Dealloc(a, p) M__Dealloc(a, p)
MA_API void *M__AllocNonZeroed(M_Allocator allocator, size_t size);
MA_API void *M__Alloc(M_Allocator allocator, size_t size);
MA_API void *M__AllocCopy(M_Allocator allocator, void *p, size_t size);
MA_API void *M__Realloc(M_Allocator allocator, void *p, size_t size, size_t old_size);
MA_API void M__Dealloc(M_Allocator allocator, void *p);
MA_API M_Allocator M_GetSystemAllocator(void);
MA_API M_Allocator MA_GetExclusiveAllocator(MA_Arena *arena);
MA_API M_Allocator MA_GetAllocator(MA_Arena *arena);
MA_API M_Allocator MA_BootstrapExclusive(void);

270
src/core/array.hpp Normal file
View File

@@ -0,0 +1,270 @@
// Iterating and removing elements
//
// ForArrayRemovable(array) {
// ForArrayRemovablePrepare(array);
// if (it == 4) ForArrayRemovableDeclare();
// }
//
#define ForArrayRemovable(a) for (int __i = 0; __i < (a).len; __i += 1)
#define ForArrayRemovablePrepare(a) \
auto &it = (a)[__i]; \
bool remove_it = false; \
defer { \
if (remove_it) { \
(a).ordered_remove(it); \
__i -= 1; \
} \
}
#define ForArrayRemovableDeclare() (remove_it = true)
#define For2(it, array) for (auto &it : (array))
#define For(array) For2(it, array)
template <class T>
struct Array {
M_Allocator allocator;
T *data;
int cap, len;
T &operator[](int index) {
IO_Assert(index >= 0 && index < len);
return data[index];
}
bool is_first(T &item) { return &item == first(); }
bool is_last(T &item) { return &item == last(); }
bool contains(T &item) {
bool result = &item >= data && &item < data + len;
return result;
}
int get_index(T &item) {
IO_Assert((data <= &item) && ((data + len) > &item));
size_t offset = &item - data;
return (int)offset;
}
void add(const T &item) {
try_growing();
data[len++] = item;
}
// Struct needs to have 'value_to_sort_by' field
void sorted_insert_decreasing(T item) {
int insert_index = -1;
For(*this) {
if (it.value_to_sort_by <= item.value_to_sort_by) {
insert_index = get_index(it);
insert(item, insert_index);
break;
}
}
if (insert_index == -1) {
add(item);
}
}
void bounded_add(T item) {
IO_Assert(len + 1 <= cap);
try_growing(); // in case of error
data[len++] = item;
}
T *alloc(const T &item) {
try_growing();
T *ref = data + len++;
*ref = item;
return ref;
}
T *alloc() {
try_growing();
T *ref = data + len++;
*ref = {};
return ref;
}
T *alloc_multiple(int size) {
try_growing_to_fit_item_count(size);
T *result = data + len;
len += size;
return result;
}
void add_array(T *items, int item_count) {
for (int i = 0; i < item_count; i += 1) {
add(items[i]);
}
}
void add_array(Array<T> items) {
add_array(items.data, items.len);
}
void reserve(int size) {
if (size > cap) {
if (!allocator.p) allocator = M_GetSystemAllocator();
void *p = M_Realloc(allocator, data, size * sizeof(T), cap * sizeof(T));
IO_Assert(p);
data = (T *)p;
cap = size;
}
}
void init(M_Allocator allocator, int size) {
len = 0;
cap = 0;
data = 0;
this->allocator = allocator;
reserve(size);
}
void reset() {
len = 0;
}
T pop() {
IO_Assert(len > 0);
return data[--len];
}
void unordered_remove(T &item) { // DONT USE IN LOOPS !!!!
IO_Assert(len > 0);
IO_Assert(&item >= begin() && &item < end());
item = data[--len];
}
void unordered_remove_index(int index) {
IO_Assert(index >= 0 && index < len);
data[index] = data[--len];
}
int get_index(const T &item) {
ptrdiff_t index = (ptrdiff_t)(&item - data);
IO_Assert(index >= 0 && index < len);
// IO_Assert(index > INT_MIN && index < INT_MAX);
return (int)index;
}
void ordered_remove(T &item) { // DONT USE IN LOOPS !!!
IO_Assert(len > 0);
IO_Assert(&item >= begin() && &item < end());
int index = get_index(item);
ordered_remove_index(index);
}
void ordered_remove_index(int index) {
IO_Assert(index >= 0 && index < len);
int right_len = len - index - 1;
memmove(data + index, data + index + 1, right_len * sizeof(T));
len -= 1;
}
void insert(T item, int index) {
if (index == len) {
add(item);
return;
}
IO_Assert(index < len);
IO_Assert(index >= 0);
try_growing();
int right_len = len - index;
memmove(data + index + 1, data + index, sizeof(T) * right_len);
data[index] = item;
len += 1;
}
void dealloc() {
if (data) M_Dealloc(allocator, data);
data = 0;
len = cap = 0;
}
Array<T> copy(M_Allocator allocator) {
Array result = {};
result.allocator = allocator;
result.reserve(cap);
memmove(result.data, data, sizeof(T) * len);
result.len = len;
return result;
}
Array<T> tight_copy(M_Allocator allocator) {
Array result = {};
result.allocator = allocator;
result.reserve(len);
memmove(result.data, data, sizeof(T) * len);
result.len = len;
return result;
}
T *first() {
IO_Assert(len > 0);
return data;
}
T *last() {
IO_Assert(len > 0);
return data + len - 1;
}
T *front() {
IO_Assert(len > 0);
return data;
}
T *back() {
IO_Assert(len > 0);
return data + len - 1;
}
T *begin() { return data; }
T *end() { return data + len; }
// for (auto it = integers.begin(), end = integers.end(); it != end; ++it)
struct Reverse_Iter {
T *data;
Array<T> *arr;
Reverse_Iter operator++(int) {
Reverse_Iter ret = *this;
data -= 1;
return ret;
}
Reverse_Iter &operator++() {
data -= 1;
return *this;
}
T &operator*() { return data[0]; }
T *operator->() { return data; }
friend bool operator==(const Reverse_Iter &a, const Reverse_Iter &b) { return a.data == b.data; };
friend bool operator!=(const Reverse_Iter &a, const Reverse_Iter &b) { return a.data != b.data; };
Reverse_Iter begin() { return Reverse_Iter{arr->end() - 1, arr}; }
Reverse_Iter end() { return Reverse_Iter{arr->begin() - 1, arr}; }
};
Reverse_Iter reverse() { return {end() - 1, this}; }
void try_growing() {
if (len + 1 > cap) {
int new_size = cap * 2;
if (new_size < 16) new_size = 16;
reserve(new_size);
}
}
void try_growing_to_fit_item_count(int item_count) {
if (len + item_count > cap) {
int new_size = (cap + item_count) * 2;
if (new_size < 16) new_size = 16;
reserve(new_size);
}
}
};

186
src/core/cmd.c Normal file
View File

@@ -0,0 +1,186 @@
CmdParser MakeCmdParser(MA_Arena *arena, int argc, char **argv, const char *custom_help) {
CmdParser result = {argc, argv, arena, custom_help};
return result;
}
void AddBool(CmdParser *p, bool *result, const char *name, const char *help) {
CmdDecl *decl = MA_PushStruct(p->arena, CmdDecl);
decl->kind = CmdDeclKind_Bool;
decl->name = S8_MakeFromChar((char *)name);
decl->help = S8_MakeFromChar((char *)help);
decl->bool_result = result;
SLL_QUEUE_ADD(p->fdecl, p->ldecl, decl);
}
void AddInt(CmdParser *p, int *result, const char *name, const char *help) {
CmdDecl *decl = MA_PushStruct(p->arena, CmdDecl);
decl->kind = CmdDeclKind_Int;
decl->name = S8_MakeFromChar((char *)name);
decl->help = S8_MakeFromChar((char *)help);
decl->int_result = result;
SLL_QUEUE_ADD(p->fdecl, p->ldecl, decl);
}
void AddList(CmdParser *p, S8_List *result, const char *name, const char *help) {
CmdDecl *decl = MA_PushStruct(p->arena, CmdDecl);
decl->kind = CmdDeclKind_List;
decl->name = S8_MakeFromChar((char *)name);
decl->help = S8_MakeFromChar((char *)help);
decl->list_result = result;
SLL_QUEUE_ADD(p->fdecl, p->ldecl, decl);
}
void AddEnum(CmdParser *p, int *result, const char *name, const char *help, const char **enum_options, int enum_option_count) {
CmdDecl *decl = MA_PushStruct(p->arena, CmdDecl);
decl->kind = CmdDeclKind_Enum;
decl->name = S8_MakeFromChar((char *)name);
decl->help = S8_MakeFromChar((char *)help);
decl->enum_result = result;
decl->enum_options = enum_options;
decl->enum_option_count = enum_option_count;
SLL_QUEUE_ADD(p->fdecl, p->ldecl, decl);
}
CmdDecl *FindDecl(CmdParser *p, S8_String name) {
for (CmdDecl *it = p->fdecl; it; it = it->next) {
if (S8_AreEqual(it->name, name, true)) {
return it;
}
}
return NULL;
}
S8_String StrEnumValues(MA_Arena *arena, CmdDecl *decl) {
S8_List list = {0};
S8_AddF(arena, &list, "[");
for (int i = 0; i < decl->enum_option_count; i += 1) {
S8_AddF(arena, &list, "%s", decl->enum_options[i]);
if (i != decl->enum_option_count - 1) S8_AddF(arena, &list, "|");
}
S8_AddF(arena, &list, "]");
return S8_Merge(arena, list);
}
void PrintCmdUsage(CmdParser *p) {
IO_Printf("%s\nCommands:\n", p->custom_help);
for (CmdDecl *it = p->fdecl; it; it = it->next) {
IO_Printf(" ");
if (it->kind == CmdDeclKind_List) {
S8_String example = S8_Format(p->arena, "-%.*s a b c", S8_Expand(it->name));
IO_Printf("%-30.*s %.*s\n", S8_Expand(example), S8_Expand(it->help));
} else if (it->kind == CmdDeclKind_Bool) {
S8_String example = S8_Format(p->arena, "-%.*s", S8_Expand(it->name));
IO_Printf("%-30.*s %.*s\n", S8_Expand(example), S8_Expand(it->help));
} else if (it->kind == CmdDeclKind_Enum) {
S8_String enum_vals = StrEnumValues(p->arena, it);
S8_String example = S8_Format(p->arena, "-%.*s %.*s", S8_Expand(it->name), S8_Expand(enum_vals));
IO_Printf("%-30.*s %.*s\n", S8_Expand(example), S8_Expand(it->help));
} else if (it->kind == CmdDeclKind_Int) {
S8_String example = S8_Format(p->arena, "-%.*s 8", S8_Expand(it->name));
IO_Printf("%-30.*s %.*s\n", S8_Expand(example), S8_Expand(it->help));
} else IO_Todo();
}
}
bool InvalidCmdArg(CmdParser *p, S8_String arg) {
IO_Printf("invalid command line argument: %.*s\n", S8_Expand(arg));
return false;
}
bool ParseCmd(MA_Arena *arg_arena, CmdParser *p) {
for (int i = 1; i < p->argc; i += 1) {
S8_String arg = S8_MakeFromChar(p->argv[i]);
if (S8_AreEqual(arg, S8_Lit("--help"), true) || S8_AreEqual(arg, S8_Lit("-h"), true) || S8_AreEqual(arg, S8_Lit("-help"), true)) {
PrintCmdUsage(p);
return false;
}
if (arg.str[0] == '-') {
arg = S8_Skip(arg, 1);
if (arg.str[0] == '-') {
arg = S8_Skip(arg, 1);
}
CmdDecl *decl = FindDecl(p, arg);
if (!decl) return InvalidCmdArg(p, arg);
if (decl->kind == CmdDeclKind_Bool) {
*decl->bool_result = !*decl->bool_result;
} else if (decl->kind == CmdDeclKind_Int) {
if (i + 1 >= p->argc) {
IO_Printf("expected at least 1 argument after %.*s\n", S8_Expand(arg));
return false;
}
S8_String num = S8_MakeFromChar(p->argv[++i]);
for (int i = 0; i < num.len; i += 1) {
if (!CHAR_IsDigit(num.str[i])) {
IO_Printf("expected argument to be a number, got instead: %.*s", S8_Expand(num));
return false;
}
}
int count = atoi(num.str);
decl->int_result[0] = count;
} else if (decl->kind == CmdDeclKind_Enum) {
if (i + 1 >= p->argc) {
IO_Printf("expected at least 1 argument after %.*s\n", S8_Expand(arg));
return false;
}
S8_String option_from_cmd = S8_MakeFromChar(p->argv[++i]);
bool found_option = false;
for (int i = 0; i < decl->enum_option_count; i += 1) {
S8_String option = S8_MakeFromChar((char *)decl->enum_options[i]);
if (S8_AreEqual(option, option_from_cmd, true)) {
*decl->enum_result = i;
found_option = true;
break;
}
}
if (!found_option) {
IO_Printf("expected one of the enum values: %.*s", S8_Expand(StrEnumValues(p->arena, decl)));
IO_Printf(" got instead: %.*s\n", S8_Expand(option_from_cmd));
return false;
}
} else if (decl->kind == CmdDeclKind_List) {
if (i + 1 >= p->argc) {
IO_Printf("expected at least 1 argument after %.*s\n", S8_Expand(arg));
return false;
}
i += 1;
for (int counter = 0; i < p->argc; i += 1, counter += 1) {
S8_String arg = S8_MakeFromChar(p->argv[i]);
if (arg.str[0] == '-') {
if (counter == 0) {
IO_Printf("expected at least 1 argument after %.*s\n", S8_Expand(arg));
return false;
}
i -= 1;
break;
}
S8_AddNode(arg_arena, decl->list_result, arg);
}
} else IO_Todo();
} else {
if (p->require_one_standalone_arg && p->args.node_count == 0) {
S8_AddNode(arg_arena, &p->args, arg);
} else {
return InvalidCmdArg(p, arg);
}
}
}
if (p->require_one_standalone_arg && p->args.node_count == 0) {
PrintCmdUsage(p);
return false;
}
return true;
}

37
src/core/cmd.h Normal file
View File

@@ -0,0 +1,37 @@
typedef enum {
CmdDeclKind_Bool,
CmdDeclKind_Int,
CmdDeclKind_List,
CmdDeclKind_Enum,
} CmdDeclKind;
typedef struct CmdDecl CmdDecl;
struct CmdDecl {
CmdDecl *next;
CmdDeclKind kind;
S8_String name;
S8_String help;
bool *bool_result;
S8_List *list_result;
int *int_result;
int *enum_result;
const char **enum_options;
int enum_option_count;
};
typedef struct CmdParser CmdParser;
struct CmdParser {
int argc;
char **argv;
MA_Arena *arena;
const char *custom_help;
CmdDecl *fdecl;
CmdDecl *ldecl;
bool require_one_standalone_arg;
S8_List args;
};

23
src/core/core.c Normal file
View File

@@ -0,0 +1,23 @@
#include "core.h"
#include "../standalone_libraries/stb_sprintf.c"
#define IO_VSNPRINTF stbsp_vsnprintf
#define IO_SNPRINTF stbsp_snprintf
#include "../standalone_libraries/io.c"
#define MA_Assertf(x, ...) IO_Assertf(x, __VA_ARGS__)
#include "../standalone_libraries/arena.c"
#define RE_ASSERT(x) IO_Assert(x)
#include "../standalone_libraries/regex.c"
#include "../standalone_libraries/unicode.c"
#define S8_VSNPRINTF stbsp_vsnprintf
#define S8_ALLOCATE(allocator, size) MA_PushSize(allocator, size)
#define S8_ASSERT(x) IO_Assert(x)
#define S8_MemoryCopy MA_MemoryCopy
#include "../standalone_libraries/string.c"
#define MU_ASSERT IO_Assert
#include "../standalone_libraries/multimedia.h"
#include "../standalone_libraries/hash.c"
#include "../standalone_libraries/load_library.c"
#include "filesystem.c"
#include "cmd.c"
#include "allocator.c"

1
src/core/core.cpp Normal file
View File

@@ -0,0 +1 @@
#include "core.c"

25
src/core/core.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef FIRST_CORE_HEADER
#define FIRST_CORE_HEADER
#include "../standalone_libraries/preproc_env.h"
#include "../standalone_libraries/stb_sprintf.h"
#include "../standalone_libraries/io.h"
#include "../standalone_libraries/arena.h"
#include "../standalone_libraries/unicode.h"
#include "../standalone_libraries/string.h"
#include "../standalone_libraries/hash.h"
#include "../standalone_libraries/linked_list.h"
#include "../standalone_libraries/regex.h"
#include "../standalone_libraries/multimedia.h"
#include "../standalone_libraries/load_library.h"
#include "filesystem.h"
#include "cmd.h"
#include "allocator.h"
#if LANG_CPP
#include "../standalone_libraries/defer.hpp"
#include "table.hpp"
#include "array.hpp"
#endif
#endif

720
src/core/filesystem.c Normal file
View File

@@ -0,0 +1,720 @@
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
OS_API bool OS_EnableTerminalColors(void) {
// Enable color terminal output
{
// Set output mode to handle virtual terminal sequences
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
if (hOut != INVALID_HANDLE_VALUE) {
DWORD dwMode = 0;
if (GetConsoleMode(hOut, &dwMode)) {
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
if (SetConsoleMode(hOut, dwMode)) {
return true;
} else {
IO_Printf("Failed to enable colored terminal output C\n");
}
} else {
IO_Printf("Failed to enable colored terminal output B\n");
}
} else {
IO_Printf("Failed to enable colored terminal output A\n");
}
}
return false;
}
OS_API bool OS_IsAbsolute(S8_String path) {
bool result = path.len > 3 && CHAR_IsAlphabetic(path.str[0]) && path.str[1] == ':' && path.str[2] == '/';
return result;
}
OS_API S8_String OS_GetExePath(MA_Arena *arena) {
wchar_t wbuffer[1024];
DWORD wsize = GetModuleFileNameW(0, wbuffer, MA_Lengthof(wbuffer));
IO_Assert(wsize != 0);
S8_String path = S8_FromWidecharEx(arena, wbuffer, wsize);
S8_NormalizePathUnsafe(path);
return path;
}
OS_API S8_String OS_GetExeDir(MA_Arena *arena) {
MA_Temp scratch = MA_GetScratch();
S8_String path = OS_GetExePath(scratch.arena);
path = S8_ChopLastSlash(path);
path = S8_Copy(arena, path);
MA_ReleaseScratch(scratch);
return path;
}
OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) {
wchar_t wbuffer[1024];
DWORD wsize = GetCurrentDirectoryW(MA_Lengthof(wbuffer), wbuffer);
IO_Assert(wsize != 0);
IO_Assert(wsize < 1022);
wbuffer[wsize++] = '/';
wbuffer[wsize] = 0;
S8_String path = S8_FromWidecharEx(arena, wbuffer, wsize);
S8_NormalizePathUnsafe(path);
return path;
}
OS_API void OS_SetWorkingDir(S8_String path) {
wchar_t wpath[1024];
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
SetCurrentDirectoryW(wpath);
}
OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) {
wchar_t wpath[1024];
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), relative.str, relative.len);
wchar_t wpath_abs[1024];
DWORD written = GetFullPathNameW((wchar_t *)wpath, MA_Lengthof(wpath_abs), wpath_abs, 0);
if (written == 0)
return S8_MakeEmpty();
S8_String path = S8_FromWidecharEx(arena, wpath_abs, written);
S8_NormalizePathUnsafe(path);
return path;
}
OS_API bool OS_FileExists(S8_String path) {
wchar_t wbuff[1024];
UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len);
DWORD attribs = GetFileAttributesW(wbuff);
bool result = attribs == INVALID_FILE_ATTRIBUTES ? false : true;
return result;
}
OS_API bool OS_IsDir(S8_String path) {
wchar_t wbuff[1024];
UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len);
DWORD dwAttrib = GetFileAttributesW(wbuff);
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
}
OS_API bool OS_IsFile(S8_String path) {
wchar_t wbuff[1024];
UTF_CreateWidecharFromChar(wbuff, MA_Lengthof(wbuff), path.str, path.len);
DWORD dwAttrib = GetFileAttributesW(wbuff);
bool is_file = (dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == 0;
return dwAttrib != INVALID_FILE_ATTRIBUTES && is_file;
}
OS_API double OS_GetTime(void) {
static int64_t counts_per_second;
if (counts_per_second == 0) {
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
counts_per_second = freq.QuadPart;
}
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
double result = (double)time.QuadPart / (double)counts_per_second;
return result;
}
/*
User needs to copy particular filename to keep it.
for (OS_FileIter it = OS_IterateFiles(it); OS_IsValid(iter); OS_Advance(it)) {
}
*/
typedef struct OS_Win32_FileIter {
HANDLE handle;
WIN32_FIND_DATAW data;
} OS_Win32_FileIter;
OS_API bool OS_IsValid(OS_FileIter it) {
return it.is_valid;
}
OS_API void OS_Advance(OS_FileIter *it) {
while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) {
WIN32_FIND_DATAW *data = &it->w32->data;
// Skip '.' and '..'
if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue;
if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue;
it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
it->filename = S8_FromWidecharEx(it->arena, data->cFileName, S8_WideLength(data->cFileName));
const char *is_dir = it->is_directory ? "/" : "";
const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
it->relative_path = S8_Format(it->arena, "%.*s%s%.*s%s", S8_Expand(it->path), separator, S8_Expand(it->filename), is_dir);
it->absolute_path = OS_GetAbsolutePath(it->arena, it->relative_path);
it->is_valid = true;
if (it->is_directory) {
IO_Assert(it->relative_path.str[it->relative_path.len - 1] == '/');
IO_Assert(it->absolute_path.str[it->absolute_path.len - 1] == '/');
}
return;
}
it->is_valid = false;
DWORD error = GetLastError();
IO_Assert(error == ERROR_NO_MORE_FILES);
FindClose(it->w32->handle);
}
OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path) {
OS_FileIter it = {0};
it.arena = scratch_arena;
it.path = path;
S8_String modified_path = S8_Format(it.arena, "%.*s\\*", S8_Expand(path));
wchar_t *wbuff = MA_PushArray(it.arena, wchar_t, modified_path.len + 1);
int64_t wsize = UTF_CreateWidecharFromChar(wbuff, modified_path.len + 1, modified_path.str, modified_path.len);
IO_Assert(wsize);
it.w32 = MA_PushStruct(it.arena, OS_Win32_FileIter);
it.w32->handle = FindFirstFileW(wbuff, &it.w32->data);
if (it.w32->handle == INVALID_HANDLE_VALUE) {
it.is_valid = false;
return it;
}
IO_Assert(it.w32->data.cFileName[0] == '.' && it.w32->data.cFileName[1] == 0);
OS_Advance(&it);
return it;
}
OS_API OS_Result OS_MakeDir(S8_String path) {
wchar_t wpath[1024];
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
BOOL success = CreateDirectoryW(wpath, NULL);
OS_Result result = OS_SUCCESS;
if (success == 0) {
DWORD error = GetLastError();
if (error == ERROR_ALREADY_EXISTS) {
result = OS_ALREADY_EXISTS;
} else if (error == ERROR_PATH_NOT_FOUND) {
result = OS_PATH_NOT_FOUND;
} else {
IO_Assert(0);
}
}
return result;
}
OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite) {
wchar_t wfrom[1024];
UTF_CreateWidecharFromChar(wfrom, MA_Lengthof(wfrom), from.str, from.len);
wchar_t wto[1024];
UTF_CreateWidecharFromChar(wto, MA_Lengthof(wto), to.str, to.len);
BOOL fail_if_exists = !overwrite;
BOOL success = CopyFileW(wfrom, wto, fail_if_exists);
OS_Result result = OS_SUCCESS;
if (success == FALSE)
result = OS_FAILURE;
return result;
}
OS_API OS_Result OS_DeleteFile(S8_String path) {
wchar_t wpath[1024];
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
BOOL success = DeleteFileW(wpath);
OS_Result result = OS_SUCCESS;
if (success == 0)
result = OS_PATH_NOT_FOUND;
return result;
}
OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) {
IO_Todo();
return OS_FAILURE;
#if 0
if (flags & OS_RECURSIVE) {
MA_Temp scratch = MA_GetScratch();
S8_List list = OS_ListDir(scratch.arena, path, OS_RECURSIVE);
S8_Node *dirs_to_remove = 0;
for (S8_Node *it = list.first; it; it = it->next) {
if (!S8_EndsWith(it->string, S8_Lit("/"), S8_IgnoreCase)) {
OS_DeleteFile(it->string);
}
else {
S8_Node *node = S8_CreateNode(scratch.arena, it->string);
SLL_STACK_ADD(dirs_to_remove, node);
}
}
for (S8_Node *it = dirs_to_remove; it; it = it->next) {
OS_DeleteDir(it->string, OS_NO_FLAGS);
}
OS_Result result = OS_DeleteDir(path, OS_NO_FLAGS);
MA_ReleaseScratch(scratch);
return result;
}
else {
wchar_t wpath[1024];
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
BOOL success = RemoveDirectoryW(wpath);
OS_Result result = OS_SUCCESS;
if (success == 0)
result = OS_PATH_NOT_FOUND;
return result;
}
#endif
}
static OS_Result OS__WriteFile(S8_String path, S8_String data, bool append) {
wchar_t wpath[1024];
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
OS_Result result = OS_FAILURE;
DWORD access = GENERIC_WRITE;
DWORD creation_disposition = CREATE_ALWAYS;
if (append) {
access = FILE_APPEND_DATA;
creation_disposition = OPEN_ALWAYS;
}
HANDLE handle = CreateFileW(wpath, access, 0, NULL, creation_disposition, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
DWORD bytes_written = 0;
IO_Assert(data.len == (DWORD)data.len); // @Todo: can only read 32 byte size files?
BOOL error = WriteFile(handle, data.str, (DWORD)data.len, &bytes_written, NULL);
if (error == TRUE) {
if (bytes_written == data.len) {
result = OS_SUCCESS;
}
}
CloseHandle(handle);
} else result = OS_PATH_NOT_FOUND;
return result;
}
OS_API OS_Result OS_AppendFile(S8_String path, S8_String string) {
return OS__WriteFile(path, string, true);
}
OS_API OS_Result OS_WriteFile(S8_String path, S8_String string) {
return OS__WriteFile(path, string, false);
}
OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) {
bool success = false;
S8_String result = S8_MakeEmpty();
MA_Temp checkpoint = MA_BeginTemp(arena);
wchar_t wpath[1024];
UTF_CreateWidecharFromChar(wpath, MA_Lengthof(wpath), path.str, path.len);
HANDLE handle = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle != INVALID_HANDLE_VALUE) {
LARGE_INTEGER file_size;
if (GetFileSizeEx(handle, &file_size)) {
if (file_size.QuadPart != 0) {
result.len = (int64_t)file_size.QuadPart;
result.str = (char *)MA_PushSizeNonZeroed(arena, result.len + 1);
DWORD read;
if (ReadFile(handle, result.str, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files?
if (read == result.len) {
success = true;
result.str[result.len] = 0;
}
}
}
}
CloseHandle(handle);
}
if (!success) {
result = S8_MakeEmpty();
MA_EndTemp(checkpoint);
}
return result;
}
OS_API int64_t OS_GetFileModTime(S8_String file) {
FILETIME time = {0};
WIN32_FIND_DATAW data;
wchar_t wpath[1024];
UTF_CreateWidecharFromChar(wpath, 1024, file.str, file.len);
HANDLE handle = FindFirstFileW(wpath, &data);
if (handle != INVALID_HANDLE_VALUE) {
FindClose(handle);
time = data.ftLastWriteTime;
} else {
return -1;
}
int64_t result = (int64_t)time.dwHighDateTime << 32 | time.dwLowDateTime;
return result;
}
OS_API OS_Date OS_GetDate(void) {
SYSTEMTIME local;
GetLocalTime(&local);
OS_Date result = {0};
result.year = local.wYear;
result.month = local.wMonth;
result.day = local.wDay;
result.hour = local.wHour;
result.second = local.wSecond;
// result.milliseconds = local.wMilliseconds;
return result;
}
#elif __linux__ || __APPLE__ || __unix__
#include <unistd.h>
#include <limits.h>
#include <sys/stat.h>
#include <time.h>
#include <dirent.h>
#if OS_MAC
#include <mach-o/dyld.h>
OS_API S8_String OS_GetExePath(MA_Arena *arena) {
char buf[PATH_MAX];
uint32_t bufsize = PATH_MAX;
if (_NSGetExecutablePath(buf, &bufsize)) {
return S8_MakeEmpty();
}
S8_String result = S8_Copy(arena, S8_MakeFromChar(buf));
return result;
}
#else
OS_API S8_String OS_GetExePath(MA_Arena *arena) {
char buffer[PATH_MAX] = {};
if (readlink("/proc/self/exe", buffer, PATH_MAX) == -1) {
return S8_MakeEmpty();
}
S8_String result = S8_Copy(arena, S8_MakeFromChar(buffer));
return result;
}
#endif
OS_API bool OS_EnableTerminalColors(void) { return true; }
OS_API bool OS_IsAbsolute(S8_String path) {
bool result = path.len >= 1 && path.str[0] == '/';
return result;
}
OS_API S8_String OS_GetExeDir(MA_Arena *arena) {
MA_Temp scratch = MA_GetScratch();
S8_String path = OS_GetExePath(scratch.arena);
S8_String dir = S8_ChopLastSlash(path);
S8_String copy = S8_Copy(arena, dir);
MA_ReleaseScratch(scratch);
return copy;
}
OS_API S8_String OS_GetWorkingDir(MA_Arena *arena) {
char *buffer = (char *)MA_PushSizeNonZeroed(arena, PATH_MAX);
char *cwd = getcwd(buffer, PATH_MAX);
S8_String result = S8_MakeFromChar(cwd);
return result;
}
OS_API void OS_SetWorkingDir(S8_String path) {
MA_Temp scratch = MA_GetScratch();
S8_String copy = S8_Copy(scratch.arena, path);
chdir(copy.str);
MA_ReleaseScratch(scratch);
}
OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative) {
MA_Temp scratch = MA_GetScratch1(arena);
S8_String copy = S8_Copy(scratch.arena, relative);
char *buffer = (char *)MA_PushSizeNonZeroed(arena, PATH_MAX);
realpath((char *)copy.str, buffer);
S8_String result = S8_MakeFromChar(buffer);
MA_ReleaseScratch(scratch);
return result;
}
OS_API bool OS_FileExists(S8_String path) {
MA_Temp scratch = MA_GetScratch();
S8_String copy = S8_Copy(scratch.arena, path);
bool result = false;
if (access((char *)copy.str, F_OK) == 0) {
result = true;
}
MA_ReleaseScratch(scratch);
return result;
}
OS_API bool OS_IsDir(S8_String path) {
MA_Temp scratch = MA_GetScratch();
S8_String copy = S8_Copy(scratch.arena, path);
struct stat s;
if (stat(copy.str, &s) != 0)
return false;
bool result = S_ISDIR(s.st_mode);
MA_ReleaseScratch(scratch);
return result;
}
OS_API bool OS_IsFile(S8_String path) {
MA_Temp scratch = MA_GetScratch();
S8_String copy = S8_Copy(scratch.arena, path);
struct stat s;
if (stat(copy.str, &s) != 0)
return false;
bool result = S_ISREG(s.st_mode);
MA_ReleaseScratch(scratch);
return result;
}
OS_API double OS_GetTime(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
uint64_t timeu64 = (((uint64_t)ts.tv_sec) * 1000000ull) + ((uint64_t)ts.tv_nsec) / 1000ull;
double timef = (double)timeu64;
double result = timef / 1000000.0; // Microseconds to seconds
return result;
}
OS_API bool OS_IsValid(OS_FileIter it) {
return it.is_valid;
}
OS_API void OS_Advance(OS_FileIter *it) {
struct dirent *file = 0;
while ((file = readdir((DIR *)it->dir)) != NULL) {
if (file->d_name[0] == '.' && file->d_name[1] == '.' && file->d_name[2] == 0) continue;
if (file->d_name[0] == '.' && file->d_name[1] == 0) continue;
it->is_directory = file->d_type == DT_DIR;
it->filename = S8_CopyChar(it->arena, file->d_name);
const char *dir_char_ending = it->is_directory ? "/" : "";
const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
it->relative_path = S8_Format(it->arena, "%.*s%s%s%s", S8_Expand(it->path), separator, file->d_name, dir_char_ending);
it->absolute_path = OS_GetAbsolutePath(it->arena, it->relative_path);
if (it->is_directory) it->absolute_path = S8_Format(it->arena, "%.*s/", S8_Expand(it->absolute_path));
it->is_valid = true;
return;
}
it->is_valid = false;
closedir((DIR *)it->dir);
}
OS_API OS_FileIter OS_IterateFiles(MA_Arena *arena, S8_String path) {
OS_FileIter it = {0};
it.arena = arena;
it.path = path = S8_Copy(arena, path);
it.dir = (void *)opendir((char *)path.str);
if (!it.dir) return it;
OS_Advance(&it);
return it;
}
OS_API OS_Result OS_MakeDir(S8_String path) {
MA_Temp scratch = MA_GetScratch();
path = S8_Copy(scratch.arena, path);
int error = mkdir(path.str, 0755);
MA_ReleaseScratch(scratch);
return error == 0 ? OS_SUCCESS : OS_FAILURE;
}
OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite) {
const char *ow = overwrite ? "-n" : "";
int result = OS_SystemF("cp %s %.*s %.*s", ow, S8_Expand(from), S8_Expand(to));
return result == 0 ? OS_SUCCESS : OS_FAILURE;
}
OS_API OS_Result OS_DeleteFile(S8_String path) {
int result = OS_SystemF("rm %.*s", S8_Expand(path));
return result == 0 ? OS_SUCCESS : OS_FAILURE;
}
OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags) {
IO_Assert(flags & OS_RECURSIVE);
int result = OS_SystemF("rm -r %.*s", S8_Expand(path));
return result == 0 ? OS_SUCCESS : OS_FAILURE;
}
OS_API int64_t OS_GetFileModTime(S8_String file) {
MA_Temp scratch = MA_GetScratch();
file = S8_Copy(scratch.arena, file);
struct stat attrib = {};
stat(file.str, &attrib);
struct timespec ts = attrib.IF_LINUX_ELSE(st_mtim, st_mtimespec);
int64_t result = (((int64_t)ts.tv_sec) * 1000000ll) + ((int64_t)ts.tv_nsec) / 1000ll;
MA_ReleaseScratch(scratch);
return result;
}
OS_API OS_Date OS_GetDate(void) {
time_t t = time(NULL);
struct tm date = *localtime(&t);
OS_Date s = {0};
s.second = date.tm_sec;
s.year = date.tm_year;
s.month = date.tm_mon;
s.day = date.tm_mday;
s.hour = date.tm_hour;
s.minute = date.tm_min;
return s;
}
OS_API OS_Result OS_AppendFile(S8_String path, S8_String string) {
MA_Temp scratch = MA_GetScratch();
path = S8_Copy(scratch.arena, path);
OS_Result result = OS_FAILURE;
FILE *f = fopen((const char *)path.str, "a");
if (f) {
result = OS_SUCCESS;
size_t written = fwrite(string.str, 1, string.len, f);
if (written < string.len) {
result = OS_FAILURE;
}
int error = fclose(f);
if (error != 0) {
result = OS_FAILURE;
}
}
MA_ReleaseScratch(scratch);
return result;
}
OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path) {
S8_String result = {};
// ftell returns insane size if file is
// a directory **on some machines** KEKW
if (OS_IsDir(path)) {
return result;
}
MA_Temp scratch = MA_GetScratch1(arena);
path = S8_Copy(scratch.arena, path);
FILE *f = fopen(path.str, "rb");
if (f) {
fseek(f, 0, SEEK_END);
result.len = ftell(f);
fseek(f, 0, SEEK_SET);
result.str = (char *)MA_PushSizeNonZeroed(arena, result.len + 1);
fread(result.str, result.len, 1, f);
result.str[result.len] = 0;
fclose(f);
}
MA_ReleaseScratch(scratch);
return result;
}
OS_API OS_Result OS_WriteFile(S8_String path, S8_String string) {
MA_Temp scratch = MA_GetScratch();
path = S8_Copy(scratch.arena, path);
OS_Result result = OS_FAILURE;
FILE *f = fopen((const char *)path.str, "w");
if (f) {
result = OS_SUCCESS;
size_t written = fwrite(string.str, 1, string.len, f);
if (written < string.len) {
result = OS_FAILURE;
}
int error = fclose(f);
if (error != 0) {
result = OS_FAILURE;
}
}
MA_ReleaseScratch(scratch);
return result;
}
#endif
#if _WIN32 || __linux__ || __APPLE__ || __unix__
OS_API int OS_SystemF(const char *string, ...) {
MA_Temp scratch = MA_GetScratch();
S8_FORMAT(scratch.arena, string, result);
IO_Printf("Executing: %.*s\n", S8_Expand(result));
fflush(stdout);
int error_code = system(result.str);
MA_ReleaseScratch(scratch);
return error_code;
}
OS_API bool OS_ExpandIncludesList(MA_Arena *arena, S8_List *out, S8_String filepath) {
S8_String c = OS_ReadFile(arena, filepath);
if (c.str == 0) return false;
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));
if (!OS_ExpandIncludesList(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;
}
OS_API S8_String OS_ExpandIncludes(MA_Arena *arena, S8_String filepath) {
S8_List out = S8_MakeEmptyList();
S8_String result = S8_MakeEmpty();
MA_ScratchScope(s) {
OS_ExpandIncludesList(s.arena, &out, filepath);
result = S8_Merge(arena, out);
}
return result;
}
#endif

73
src/core/filesystem.h Normal file
View File

@@ -0,0 +1,73 @@
// Quick and dirty filesystem operations
#ifndef OS_API
#define OS_API
#endif
typedef enum OS_Result {
OS_SUCCESS,
OS_ALREADY_EXISTS,
OS_PATH_NOT_FOUND,
OS_FAILURE,
} OS_Result;
enum {
OS_NO_FLAGS = 0,
OS_RECURSIVE = 1,
OS_RELATIVE_PATHS = 2,
};
typedef struct OS_Date OS_Date;
struct OS_Date {
uint32_t year;
uint32_t month;
uint32_t day;
uint32_t hour;
uint32_t minute;
uint32_t second;
};
typedef struct OS_FileIter OS_FileIter;
struct OS_FileIter {
bool is_valid;
bool is_directory;
S8_String absolute_path;
S8_String relative_path;
S8_String filename;
S8_String path;
MA_Arena *arena;
union {
struct OS_Win32_FileIter *w32;
void *dir;
};
};
OS_API bool OS_IsAbsolute(S8_String path);
OS_API S8_String OS_GetExePath(MA_Arena *arena);
OS_API S8_String OS_GetExeDir(MA_Arena *arena);
OS_API S8_String OS_GetWorkingDir(MA_Arena *arena);
OS_API void OS_SetWorkingDir(S8_String path);
OS_API S8_String OS_GetAbsolutePath(MA_Arena *arena, S8_String relative);
OS_API bool OS_FileExists(S8_String path);
OS_API bool OS_IsDir(S8_String path);
OS_API bool OS_IsFile(S8_String path);
OS_API double OS_GetTime(void);
OS_API OS_Result OS_MakeDir(S8_String path);
OS_API OS_Result OS_CopyFile(S8_String from, S8_String to, bool overwrite);
OS_API OS_Result OS_DeleteFile(S8_String path);
OS_API OS_Result OS_DeleteDir(S8_String path, unsigned flags);
OS_API OS_Result OS_AppendFile(S8_String path, S8_String string);
OS_API OS_Result OS_WriteFile(S8_String path, S8_String string);
OS_API S8_String OS_ReadFile(MA_Arena *arena, S8_String path);
OS_API int OS_SystemF(const char *string, ...);
OS_API int64_t OS_GetFileModTime(S8_String file);
OS_API OS_Date OS_GetDate(void);
OS_API S8_String UTF_CreateStringFromWidechar(MA_Arena *arena, wchar_t *wstr, int64_t wsize);
OS_API bool OS_ExpandIncludesList(MA_Arena *arena, S8_List *out, S8_String filepath);
OS_API S8_String OS_ExpandIncludes(MA_Arena *arena, S8_String filepath);
OS_API bool OS_EnableTerminalColors(void);
OS_API bool OS_IsValid(OS_FileIter it);
OS_API void OS_Advance(OS_FileIter *it);
OS_API OS_FileIter OS_IterateFiles(MA_Arena *scratch_arena, S8_String path);

204
src/core/table.hpp Normal file
View File

@@ -0,0 +1,204 @@
/*
Hash table implementation:
Pointers to values
Open adressing
Linear Probing
Power of 2
Robin Hood hashing
Resizes on high probe count (min max load factor)
Hash 0 is reserved for empty hash table entry
*/
template <class Value>
struct Table {
struct Entry {
uint64_t hash;
uint64_t key;
size_t distance;
Value value;
};
M_Allocator allocator;
size_t len, cap;
Entry *values;
static const size_t max_load_factor = 80;
static const size_t min_load_factor = 50;
static const size_t significant_distance = 8;
// load factor calculation was rearranged
// to get rid of division:
//> 100 * len / cap = load_factor
//> len * 100 = load_factor * cap
inline bool reached_load_factor(size_t lfactor) {
return (len + 1) * 100 >= lfactor * cap;
}
inline bool is_empty(Entry *entry) { return entry->hash == 0; }
inline bool is_occupied(Entry *entry) { return entry->hash != 0; }
void reserve(size_t size) {
IO_Assert(size > cap && "New size is smaller then original size");
IO_Assert(MA_IS_POW2(size));
if (!allocator.p) allocator = M_GetSystemAllocator();
Entry *old_values = values;
size_t old_cap = cap;
values = (Entry *)M_Alloc(allocator, sizeof(Entry) * size);
for (int i = 0; i < size; i += 1) values[i] = {};
cap = size;
IO_Assert(!(old_values == 0 && len != 0));
if (len == 0) {
if (old_values) M_Dealloc(allocator, old_values);
return;
}
len = 0;
for (size_t i = 0; i < old_cap; i += 1) {
Entry *it = old_values + i;
if (is_occupied(it)) {
insert(it->key, it->value);
}
}
M_Dealloc(allocator, old_values);
}
Entry *get_table_entry(uint64_t key) {
if (len == 0) return 0;
uint64_t hash = HashBytes(&key, sizeof(key));
if (hash == 0) hash += 1;
uint64_t index = WRAP_AROUND_POWER_OF_2(hash, cap);
uint64_t i = index;
uint64_t distance = 0;
for (;;) {
Entry *it = values + i;
if (distance > it->distance) {
return 0;
}
if (it->hash == hash && it->key == key) {
return it;
}
distance += 1;
i = WRAP_AROUND_POWER_OF_2(i + 1, cap);
if (i == index) return 0;
}
IO_Assert(!"Invalid codepath");
}
void insert(uint64_t key, const Value &value) {
if (reached_load_factor(max_load_factor)) {
if (cap == 0) cap = 16; // 32 cause cap*2
reserve(cap * 2);
}
uint64_t hash = HashBytes(&key, sizeof(key));
if (hash == 0) hash += 1;
uint64_t index = WRAP_AROUND_POWER_OF_2(hash, cap);
uint64_t i = index;
Entry to_insert = {hash, key, 0, value};
for (;;) {
Entry *it = values + i;
if (is_empty(it)) {
*it = to_insert;
len += 1;
// If we have more then 8 consecutive items we try to resize
if (to_insert.distance > 8 && reached_load_factor(min_load_factor)) {
reserve(cap * 2);
}
return;
}
if (it->hash == hash && it->key == key) {
*it = to_insert;
// If we have more then 8 consecutive items we try to resize
if (to_insert.distance > 8 && reached_load_factor(min_load_factor)) {
reserve(cap * 2);
}
return;
}
// Robin hood hashing
if (to_insert.distance > it->distance) {
Entry temp = to_insert;
to_insert = *it;
*it = temp;
}
to_insert.distance += 1;
i = WRAP_AROUND_POWER_OF_2(i + 1, cap);
IO_Assert(i != index && "Did a full 360 through a hash table, no good :( that shouldnt be possible");
}
IO_Assert(!"Invalid codepath");
}
void remove(uint64_t key) {
Entry *entry = get_table_entry(key);
entry->hash = 0;
entry->distance = 0;
len -= 1;
}
Value *get(uint64_t key) {
Entry *v = get_table_entry(key);
if (!v) return 0;
return &v->value;
}
Value get(uint64_t key, Value default_value) {
Entry *v = get_table_entry(key);
if (!v) return default_value;
return v->value;
}
Value *gets(char *str) {
int len = S8_Length(str);
uint64_t hash = HashBytes(str, len);
return get(hash);
}
Value gets(char *str, Value default_value) {
int len = S8_Length(str);
uint64_t hash = HashBytes(str, len);
return get(hash, default_value);
}
Value *get(S8_String s) {
uint64_t hash = HashBytes(s.str, (unsigned)s.len);
return get(hash);
}
Value get(S8_String s, Value default_value) {
uint64_t hash = HashBytes(s.str, (unsigned)s.len);
return get(hash, default_value);
}
void put(S8_String s, const Value &value) {
uint64_t hash = HashBytes(s.str, (unsigned)s.len);
insert(hash, value);
}
void puts(char *str, const Value &value) {
int len = S8_Length(str);
uint64_t hash = HashBytes(str, len);
insert(hash, value);
}
void reset() {
len = 0;
for (size_t i = 0; i < cap; i += 1) {
Entry *it = values + i;
it->hash = 0;
}
}
void dealloc() {
M_Dealloc(allocator, values);
len = 0;
cap = 0;
values = 0;
}
};

62
src/profiler/profiler.c Normal file
View File

@@ -0,0 +1,62 @@
#if defined(LC_ENABLE_PROFILER)
#include "spall.h"
static SpallProfile spall_ctx;
static SpallBuffer spall_buffer;
LC_FUNCTION void LC_BeginProfiling() {
spall_ctx = spall_init_file("lc.spall", 1);
int buffer_size = 1 * 1024 * 1024 * 4;
unsigned char *buffer = (unsigned char *)malloc(buffer_size);
spall_buffer.length = buffer_size;
spall_buffer.data = buffer;
spall_buffer_init(&spall_ctx, &spall_buffer);
}
LC_FUNCTION void LC_EndProfiling() {
spall_buffer_quit(&spall_ctx, &spall_buffer);
free(spall_buffer.data);
spall_quit(&spall_ctx);
}
#if _WIN32
#include <Windows.h>
double LC_GetTime(void) {
static double invfreq;
if (!invfreq) {
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
invfreq = 1000000.0 / frequency.QuadPart;
}
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return counter.QuadPart * invfreq;
}
#else
#include <unistd.h>
double LC_GetTime(void) {
struct timespec spec;
clock_gettime(CLOCK_MONOTONIC, &spec);
return (((double)spec.tv_sec) * 1000000) + (((double)spec.tv_nsec) / 1000);
}
#endif
struct LC_ProfileScope {
LC_ProfileScope(const char *fn, int size) {
spall_buffer_begin(&spall_ctx, &spall_buffer, fn, size - 1, LC_GetTime());
}
~LC_ProfileScope() {
spall_buffer_end(&spall_ctx, &spall_buffer, LC_GetTime());
}
};
#define LC_ProfileScope() \
LC_ProfileScope TIME_SCOPE_(__FUNCTION__, sizeof(__FUNCTION__))
#else
#define LC_ProfileScope()
LC_FUNCTION void LC_BeginProfiling() {
}
LC_FUNCTION void LC_EndProfiling() {
}
#endif

448
src/profiler/spall.h Normal file
View File

@@ -0,0 +1,448 @@
// SPDX-FileCopyrightText: © 2023 Phillip Trudeau-Tavara <pmttavara@protonmail.com>
// SPDX-License-Identifier: MIT
/*
TODO: Optional Helper APIs:
- Compression API: would require a mutexed lockable context (yuck...)
- Either using a ZIP library, a name cache + TIDPID cache, or both (but ZIP is likely more than enough!!!)
- begin()/end() writes compressed chunks to a caller-determined destination
- The destination can be the buffered-writing API or a custom user destination
- Ultimately need to take a lock with some granularity... can that be the caller's responsibility?
- Counter Event: should allow tracking arbitrary named values with a single event, for memory and frame profiling
- Ring-buffer API
spall_ring_init
spall_ring_emit_begin
spall_ring_emit_end
spall_ring_flush
*/
#ifndef SPALL_H
#define SPALL_H
#if !defined(_MSC_VER) || defined(__clang__)
#define SPALL_NOINSTRUMENT __attribute__((no_instrument_function))
#define SPALL_FORCEINLINE __attribute__((always_inline))
#else
#define _CRT_SECURE_NO_WARNINGS
#define SPALL_NOINSTRUMENT // Can't noinstrument on MSVC!
#define SPALL_FORCEINLINE __forceinline
#endif
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define SPALL_FN static inline SPALL_NOINSTRUMENT
#define SPALL_MIN(a, b) (((a) < (b)) ? (a) : (b))
#pragma pack(push, 1)
typedef struct SpallHeader {
uint64_t magic_header; // = 0x0BADF00D
uint64_t version; // = 1
double timestamp_unit;
uint64_t must_be_0;
} SpallHeader;
enum {
SpallEventType_Invalid = 0,
SpallEventType_Custom_Data = 1, // Basic readers can skip this.
SpallEventType_StreamOver = 2,
SpallEventType_Begin = 3,
SpallEventType_End = 4,
SpallEventType_Instant = 5,
SpallEventType_Overwrite_Timestamp = 6, // Retroactively change timestamp units - useful for incrementally improving RDTSC frequency.
SpallEventType_Pad_Skip = 7,
};
typedef struct SpallBeginEvent {
uint8_t type; // = SpallEventType_Begin
uint8_t category;
uint32_t pid;
uint32_t tid;
double when;
uint8_t name_length;
uint8_t args_length;
} SpallBeginEvent;
typedef struct SpallBeginEventMax {
SpallBeginEvent event;
char name_bytes[255];
char args_bytes[255];
} SpallBeginEventMax;
typedef struct SpallEndEvent {
uint8_t type; // = SpallEventType_End
uint32_t pid;
uint32_t tid;
double when;
} SpallEndEvent;
typedef struct SpallPadSkipEvent {
uint8_t type; // = SpallEventType_Pad_Skip
uint32_t size;
} SpallPadSkipEvent;
#pragma pack(pop)
typedef struct SpallProfile SpallProfile;
// Important!: If you define your own callbacks, mark them SPALL_NOINSTRUMENT!
typedef bool (*SpallWriteCallback)(SpallProfile *self, const void *data, size_t length);
typedef bool (*SpallFlushCallback)(SpallProfile *self);
typedef void (*SpallCloseCallback)(SpallProfile *self);
struct SpallProfile {
double timestamp_unit;
bool is_json;
SpallWriteCallback write;
SpallFlushCallback flush;
SpallCloseCallback close;
void *data;
};
// Important!: If you are writing Begin/End events, then do NOT write
// events for the same PID + TID pair on different buffers!!!
typedef struct SpallBuffer {
void *data;
size_t length;
// Internal data - don't assign this
size_t head;
SpallProfile *ctx;
} SpallBuffer;
#ifdef __cplusplus
extern "C" {
#endif
#if defined(SPALL_BUFFER_PROFILING) && !defined(SPALL_BUFFER_PROFILING_GET_TIME)
#error "You must #define SPALL_BUFFER_PROFILING_GET_TIME() to profile buffer flushes."
#endif
SPALL_FN SPALL_FORCEINLINE void spall__buffer_profile(SpallProfile *ctx, SpallBuffer *wb, double spall_time_begin, double spall_time_end, const char *name, int name_len);
#ifdef SPALL_BUFFER_PROFILING
#define SPALL_BUFFER_PROFILE_BEGIN() double spall_time_begin = (SPALL_BUFFER_PROFILING_GET_TIME())
// Don't call this with anything other than a string literal
#define SPALL_BUFFER_PROFILE_END(name) spall__buffer_profile(ctx, wb, spall_time_begin, (SPALL_BUFFER_PROFILING_GET_TIME()), "" name "", sizeof("" name "") - 1)
#else
#define SPALL_BUFFER_PROFILE_BEGIN()
#define SPALL_BUFFER_PROFILE_END(name)
#endif
SPALL_FN SPALL_FORCEINLINE bool spall__file_write(SpallProfile *ctx, const void *p, size_t n) {
if (!ctx->data) return false;
#ifdef SPALL_DEBUG
if (feof((FILE *)ctx->data)) return false;
if (ferror((FILE *)ctx->data)) return false;
#endif
if (fwrite(p, n, 1, (FILE *)ctx->data) != 1) return false;
return true;
}
SPALL_FN bool spall__file_flush(SpallProfile *ctx) {
if (!ctx->data) return false;
if (fflush((FILE *)ctx->data)) return false;
return true;
}
SPALL_FN void spall__file_close(SpallProfile *ctx) {
if (!ctx->data) return;
if (ctx->is_json) {
#ifdef SPALL_DEBUG
if (!feof((FILE *)ctx->data) && !ferror((FILE *)ctx->data))
#endif
{
fseek((FILE *)ctx->data, -2, SEEK_CUR); // seek back to overwrite trailing comma
fwrite("\n]}\n", sizeof("\n]}\n") - 1, 1, (FILE *)ctx->data);
}
}
fflush((FILE *)ctx->data);
fclose((FILE *)ctx->data);
ctx->data = NULL;
}
SPALL_FN SPALL_FORCEINLINE bool spall__buffer_flush(SpallProfile *ctx, SpallBuffer *wb) {
// precon: wb
// precon: wb->data
// precon: wb->head <= wb->length
// precon: !ctx || ctx->write
#ifdef SPALL_DEBUG
if (wb->ctx != ctx) return false; // Buffer must be bound to this context (or to NULL)
#endif
if (wb->head && ctx) {
SPALL_BUFFER_PROFILE_BEGIN();
if (!ctx->write) return false;
if (ctx->write == spall__file_write) {
if (!spall__file_write(ctx, wb->data, wb->head)) return false;
}
else {
if (!ctx->write(ctx, wb->data, wb->head)) return false;
}
SPALL_BUFFER_PROFILE_END("Buffer Flush");
}
wb->head = 0;
return true;
}
SPALL_FN SPALL_FORCEINLINE bool spall__buffer_write(SpallProfile *ctx, SpallBuffer *wb, void *p, size_t n) {
// precon: !wb || wb->head < wb->length
// precon: !ctx || ctx->write
if (!wb) return ctx->write && ctx->write(ctx, p, n);
#ifdef SPALL_DEBUG
if (wb->ctx != ctx) return false; // Buffer must be bound to this context (or to NULL)
#endif
if (wb->head + n > wb->length && !spall__buffer_flush(ctx, wb)) return false;
if (n > wb->length) {
SPALL_BUFFER_PROFILE_BEGIN();
if (!ctx->write || !ctx->write(ctx, p, n)) return false;
SPALL_BUFFER_PROFILE_END("Unbuffered Write");
return true;
}
memcpy((char *)wb->data + wb->head, p, n);
wb->head += n;
return true;
}
SPALL_FN bool spall_buffer_flush(SpallProfile *ctx, SpallBuffer *wb) {
#ifdef SPALL_DEBUG
if (!wb) return false;
if (!wb->data) return false;
#endif
if (!spall__buffer_flush(ctx, wb)) return false;
return true;
}
SPALL_FN bool spall_buffer_init(SpallProfile *ctx, SpallBuffer *wb) {
if (!spall_buffer_flush(NULL, wb)) return false;
wb->ctx = ctx;
return true;
}
SPALL_FN bool spall_buffer_quit(SpallProfile *ctx, SpallBuffer *wb) {
if (!spall_buffer_flush(ctx, wb)) return false;
wb->ctx = NULL;
return true;
}
SPALL_FN bool spall_buffer_abort(SpallBuffer *wb) {
if (!wb) return false;
wb->ctx = NULL;
if (!spall__buffer_flush(NULL, wb)) return false;
return true;
}
SPALL_FN size_t spall_build_header(void *buffer, size_t rem_size, double timestamp_unit) {
size_t header_size = sizeof(SpallHeader);
if (header_size > rem_size) {
return 0;
}
SpallHeader *header = (SpallHeader *)buffer;
header->magic_header = 0x0BADF00D;
header->version = 1;
header->timestamp_unit = timestamp_unit;
header->must_be_0 = 0;
return header_size;
}
SPALL_FN SPALL_FORCEINLINE size_t spall_build_begin(void *buffer, size_t rem_size, const char *name, signed long name_len, const char *args, signed long args_len, double when, uint32_t tid, uint32_t pid) {
SpallBeginEventMax *ev = (SpallBeginEventMax *)buffer;
uint8_t trunc_name_len = (uint8_t)SPALL_MIN(name_len, 255); // will be interpreted as truncated in the app (?)
uint8_t trunc_args_len = (uint8_t)SPALL_MIN(args_len, 255); // will be interpreted as truncated in the app (?)
size_t ev_size = sizeof(SpallBeginEvent) + trunc_name_len + trunc_args_len;
if (ev_size > rem_size) {
return 0;
}
ev->event.type = SpallEventType_Begin;
ev->event.category = 0;
ev->event.pid = pid;
ev->event.tid = tid;
ev->event.when = when;
ev->event.name_length = trunc_name_len;
ev->event.args_length = trunc_args_len;
memcpy(ev->name_bytes, name, trunc_name_len);
memcpy(ev->name_bytes + trunc_name_len, args, trunc_args_len);
return ev_size;
}
SPALL_FN SPALL_FORCEINLINE size_t spall_build_end(void *buffer, size_t rem_size, double when, uint32_t tid, uint32_t pid) {
size_t ev_size = sizeof(SpallEndEvent);
if (ev_size > rem_size) {
return 0;
}
SpallEndEvent *ev = (SpallEndEvent *)buffer;
ev->type = SpallEventType_End;
ev->pid = pid;
ev->tid = tid;
ev->when = when;
return ev_size;
}
SPALL_FN void spall_quit(SpallProfile *ctx) {
if (!ctx) return;
if (ctx->close) ctx->close(ctx);
memset(ctx, 0, sizeof(*ctx));
}
SPALL_FN SpallProfile spall_init_callbacks(double timestamp_unit,
SpallWriteCallback write,
SpallFlushCallback flush,
SpallCloseCallback close,
void *userdata,
bool is_json) {
SpallProfile ctx;
memset(&ctx, 0, sizeof(ctx));
if (timestamp_unit < 0) return ctx;
ctx.timestamp_unit = timestamp_unit;
ctx.is_json = is_json;
ctx.data = userdata;
ctx.write = write;
ctx.flush = flush;
ctx.close = close;
if (ctx.is_json) {
if (!ctx.write(&ctx, "{\"traceEvents\":[\n", sizeof("{\"traceEvents\":[\n") - 1)) {
spall_quit(&ctx);
return ctx;
}
}
else {
SpallHeader header;
size_t len = spall_build_header(&header, sizeof(header), timestamp_unit);
if (!ctx.write(&ctx, &header, len)) {
spall_quit(&ctx);
return ctx;
}
}
return ctx;
}
SPALL_FN SpallProfile spall_init_file_ex(const char *filename, double timestamp_unit, bool is_json) {
SpallProfile ctx;
memset(&ctx, 0, sizeof(ctx));
if (!filename) return ctx;
ctx.data = fopen(filename, "wb"); // TODO: handle utf8 and long paths on windows
if (ctx.data) { // basically freopen() but we don't want to force users to lug along another macro define
fclose((FILE *)ctx.data);
ctx.data = fopen(filename, "ab");
}
if (!ctx.data) {
spall_quit(&ctx);
return ctx;
}
ctx = spall_init_callbacks(timestamp_unit, spall__file_write, spall__file_flush, spall__file_close, ctx.data, is_json);
return ctx;
}
SPALL_FN SpallProfile spall_init_file(const char *filename, double timestamp_unit) { return spall_init_file_ex(filename, timestamp_unit, false); }
SPALL_FN SpallProfile spall_init_file_json(const char *filename, double timestamp_unit) { return spall_init_file_ex(filename, timestamp_unit, true); }
SPALL_FN bool spall_flush(SpallProfile *ctx) {
#ifdef SPALL_DEBUG
if (!ctx) return false;
#endif
if (!ctx->flush || !ctx->flush(ctx)) return false;
return true;
}
SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_args(SpallProfile *ctx, SpallBuffer *wb, const char *name, signed long name_len, const char *args, signed long args_len, double when, uint32_t tid, uint32_t pid) {
#ifdef SPALL_DEBUG
if (!ctx) return false;
if (!name) return false;
if (name_len <= 0) return false;
if (!wb) return false;
#endif
if (ctx->is_json) {
char buf[1024];
int buf_len = snprintf(buf, sizeof(buf),
"{\"ph\":\"B\",\"ts\":%f,\"pid\":%u,\"tid\":%u,\"name\":\"%.*s\",\"args\":\"%.*s\"},\n",
when * ctx->timestamp_unit, pid, tid, (int)(uint8_t)name_len, name, (int)(uint8_t)args_len, args);
if (buf_len <= 0) return false;
if (buf_len >= sizeof(buf)) return false;
if (!spall__buffer_write(ctx, wb, buf, buf_len)) return false;
}
else {
if ((wb->head + sizeof(SpallBeginEventMax)) > wb->length) {
if (!spall__buffer_flush(ctx, wb)) {
return false;
}
}
wb->head += spall_build_begin((char *)wb->data + wb->head, wb->length - wb->head, name, name_len, args, args_len, when, tid, pid);
}
return true;
}
SPALL_FN SPALL_FORCEINLINE bool spall_buffer_begin_ex(SpallProfile *ctx, SpallBuffer *wb, const char *name, signed long name_len, double when, uint32_t tid, uint32_t pid) {
return spall_buffer_begin_args(ctx, wb, name, name_len, "", 0, when, tid, pid);
}
SPALL_FN bool spall_buffer_begin(SpallProfile *ctx, SpallBuffer *wb, const char *name, signed long name_len, double when) {
return spall_buffer_begin_args(ctx, wb, name, name_len, "", 0, when, 0, 0);
}
SPALL_FN SPALL_FORCEINLINE bool spall_buffer_end_ex(SpallProfile *ctx, SpallBuffer *wb, double when, uint32_t tid, uint32_t pid) {
#ifdef SPALL_DEBUG
if (!ctx) return false;
if (!wb) return false;
#endif
if (ctx->is_json) {
char buf[512];
int buf_len = snprintf(buf, sizeof(buf),
"{\"ph\":\"E\",\"ts\":%f,\"pid\":%u,\"tid\":%u},\n",
when * ctx->timestamp_unit, pid, tid);
if (buf_len <= 0) return false;
if (buf_len >= sizeof(buf)) return false;
if (!spall__buffer_write(ctx, wb, buf, buf_len)) return false;
}
else {
if ((wb->head + sizeof(SpallEndEvent)) > wb->length) {
if (!spall__buffer_flush(ctx, wb)) {
return false;
}
}
wb->head += spall_build_end((char *)wb->data + wb->head, wb->length - wb->head, when, tid, pid);
}
return true;
}
SPALL_FN bool spall_buffer_end(SpallProfile *ctx, SpallBuffer *wb, double when) { return spall_buffer_end_ex(ctx, wb, when, 0, 0); }
SPALL_FN SPALL_FORCEINLINE void spall__buffer_profile(SpallProfile *ctx, SpallBuffer *wb, double spall_time_begin, double spall_time_end, const char *name, int name_len) {
// precon: ctx
// precon: ctx->write
char temp_buffer_data[2048];
SpallBuffer temp_buffer = {temp_buffer_data, sizeof(temp_buffer_data)};
if (!spall_buffer_begin_ex(ctx, &temp_buffer, name, name_len, spall_time_begin, (uint32_t)(uintptr_t)wb->data, 4222222222)) return;
if (!spall_buffer_end_ex(ctx, &temp_buffer, spall_time_end, (uint32_t)(uintptr_t)wb->data, 4222222222)) return;
if (ctx->write) ctx->write(ctx, temp_buffer_data, temp_buffer.head);
}
#ifdef __cplusplus
}
#endif
#endif // SPALL_H

View File

@@ -0,0 +1,366 @@
#include "arena.h"
#ifndef MA_Assertf
#include <assert.h>
#define MA_Assertf(x, ...) assert(x)
#endif
#ifndef MA_StaticFunc
#if defined(__GNUC__) || defined(__clang__)
#define MA_StaticFunc __attribute__((unused)) static
#else
#define MA_StaticFunc static
#endif
#endif
#if defined(MA_USE_ADDRESS_SANITIZER)
#include <sanitizer/asan_interface.h>
#endif
#if !defined(ASAN_POISON_MEMORY_REGION)
#define MA_ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
#define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
#else
#define MA_ASAN_POISON_MEMORY_REGION(addr, size) ASAN_POISON_MEMORY_REGION(addr, size)
#define MA_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size)
#endif
MA_THREAD_LOCAL MA_SourceLoc MA_SavedSourceLoc;
MA_API void MA_SaveSourceLocEx(const char *file, int line) {
MA_SavedSourceLoc.file = file;
MA_SavedSourceLoc.line = line;
}
MA_API size_t MA_GetAlignOffset(size_t size, size_t align) {
size_t mask = align - 1;
size_t val = size & mask;
if (val) {
val = align - val;
}
return val;
}
MA_API size_t MA_AlignUp(size_t size, size_t align) {
size_t result = size + MA_GetAlignOffset(size, align);
return result;
}
MA_API size_t MA_AlignDown(size_t size, size_t align) {
size += 1; // Make sure when align is 8 doesn't get rounded down to 0
size_t result = size - (align - MA_GetAlignOffset(size, align));
return result;
}
MA_StaticFunc uint8_t *MV__AdvanceCommit(MV_Memory *m, size_t *commit_size, size_t page_size) {
size_t aligned_up_commit = MA_AlignUp(*commit_size, page_size);
size_t to_be_total_commited_size = aligned_up_commit + m->commit;
size_t to_be_total_commited_size_clamped_to_reserve = MA_CLAMP_TOP(to_be_total_commited_size, m->reserve);
size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit;
MA_Assertf(adjusted_to_boundary_commit, "Reached the virtual memory reserved boundary");
*commit_size = adjusted_to_boundary_commit;
if (adjusted_to_boundary_commit == 0) {
return 0;
}
uint8_t *result = m->data + m->commit;
return result;
}
MA_API void MA_PopToPos(MA_Arena *arena, size_t pos) {
MA_Assertf(arena->len >= arena->base_len, "Bug: arena->len shouldn't ever be smaller then arena->base_len");
pos = MA_CLAMP(pos, arena->base_len, arena->len);
size_t size = arena->len - pos;
arena->len = pos;
MA_ASAN_POISON_MEMORY_REGION(arena->memory.data + arena->len, size);
}
MA_API void MA_PopSize(MA_Arena *arena, size_t size) {
MA_PopToPos(arena, arena->len - size);
}
MA_API void MA_DeallocateArena(MA_Arena *arena) {
MV_Deallocate(&arena->memory);
}
MA_API void MA_Reset(MA_Arena *arena) {
MA_PopToPos(arena, 0);
}
MA_StaticFunc size_t MA__AlignLen(MA_Arena *a) {
size_t align_offset = a->alignment ? MA_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
size_t aligned = a->len + align_offset;
return aligned;
}
MA_API void MA_SetAlignment(MA_Arena *arena, int alignment) {
arena->alignment = alignment;
}
MA_API uint8_t *MA_GetTop(MA_Arena *a) {
MA_Assertf(a->memory.data, "Arena needs to be inited, there is no top to get!");
return a->memory.data + a->len;
}
MA_API void *MA__PushSizeNonZeroed(MA_Arena *a, size_t size) {
size_t align_offset = a->alignment ? MA_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
size_t aligned_len = a->len + align_offset;
size_t size_with_alignment = size + align_offset;
if (a->len + size_with_alignment > a->memory.commit) {
if (a->memory.reserve == 0) {
#if MA_ZERO_IS_INITIALIZATION
MA_Init(a);
#else
MA_Assertf(0, "Pushing on uninitialized arena with zero initialization turned off");
#endif
}
bool result = MV_Commit(&a->memory, size_with_alignment + MA_COMMIT_ADD_SIZE);
MA_Assertf(result, "%s(%d): Failed to commit memory more memory! reserve: %zu commit: %zu len: %zu size_with_alignment: %zu", MA_SavedSourceLoc.file, MA_SavedSourceLoc.line, a->memory.reserve, a->memory.commit, a->len, size_with_alignment);
(void)result;
}
uint8_t *result = a->memory.data + aligned_len;
a->len += size_with_alignment;
MA_Assertf(a->len <= a->memory.commit, "%s(%d): Reached commit boundary! reserve: %zu commit: %zu len: %zu base_len: %zu alignment: %d size_with_alignment: %zu", MA_SavedSourceLoc.file, MA_SavedSourceLoc.line, a->memory.reserve, a->memory.commit, a->len, a->base_len, a->alignment, size_with_alignment);
MA_ASAN_UNPOISON_MEMORY_REGION(result, size);
return (void *)result;
}
MA_API void *MA__PushSize(MA_Arena *arena, size_t size) {
void *result = MA__PushSizeNonZeroed(arena, size);
MA_MemoryZero(result, size);
return result;
}
MA_API char *MA__PushStringCopy(MA_Arena *arena, char *p, size_t size) {
char *copy_buffer = (char *)MA__PushSizeNonZeroed(arena, size + 1);
MA_MemoryCopy(copy_buffer, p, size);
copy_buffer[size] = 0;
return copy_buffer;
}
MA_API void *MA__PushCopy(MA_Arena *arena, void *p, size_t size) {
void *copy_buffer = MA__PushSizeNonZeroed(arena, size);
MA_MemoryCopy(copy_buffer, p, size);
return copy_buffer;
}
MA_API MA_Arena MA_PushArena(MA_Arena *arena, size_t size) {
MA_Arena result;
MA_MemoryZero(&result, sizeof(result));
result.memory.data = MA_PushArrayNonZeroed(arena, uint8_t, size);
result.memory.commit = size;
result.memory.reserve = size;
result.alignment = arena->alignment;
return result;
}
MA_API MA_Arena *MA_PushArenaP(MA_Arena *arena, size_t size) {
MA_Arena *result = MA_PushStruct(arena, MA_Arena);
*result = MA_PushArena(arena, size);
return result;
}
MA_API void MA_InitEx(MA_Arena *a, size_t reserve) {
a->memory = MV_Reserve(reserve);
MA_ASAN_POISON_MEMORY_REGION(a->memory.data, a->memory.reserve);
a->alignment = MA_DEFAULT_ALIGNMENT;
}
MA_API void MA_Init(MA_Arena *a) {
MA_InitEx(a, MA_DEFAULT_RESERVE_SIZE);
}
MA_API void MA_MakeSureInitialized(MA_Arena *a) {
if (a->memory.data == 0) {
MA_Init(a);
}
}
MA_API MA_Arena *MA_Bootstrap(void) {
MA_Arena bootstrap_arena = {0};
MA_Arena *arena = MA_PushStruct(&bootstrap_arena, MA_Arena);
*arena = bootstrap_arena;
arena->base_len = arena->len;
return arena;
}
MA_API void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size) {
arena->memory.data = (uint8_t *)buffer;
arena->memory.commit = size;
arena->memory.reserve = size;
arena->alignment = MA_DEFAULT_ALIGNMENT;
MA_ASAN_POISON_MEMORY_REGION(arena->memory.data, arena->memory.reserve);
}
MA_API MA_Arena MA_MakeFromBuffer(void *buffer, size_t size) {
MA_Arena arena;
MA_MemoryZero(&arena, sizeof(arena));
MA_InitFromBuffer(&arena, buffer, size);
return arena;
}
MA_API MA_Arena MA_Create() {
MA_Arena arena = {0};
MA_Init(&arena);
return arena;
}
MA_API bool MA_IsPointerInside(MA_Arena *arena, void *p) {
uintptr_t pointer = (uintptr_t)p;
uintptr_t start = (uintptr_t)arena->memory.data;
uintptr_t stop = start + (uintptr_t)arena->len;
bool result = pointer >= start && pointer < stop;
return result;
}
MA_API MA_Temp MA_BeginTemp(MA_Arena *arena) {
MA_Temp result;
result.pos = arena->len;
result.arena = arena;
return result;
}
MA_API void MA_EndTemp(MA_Temp checkpoint) {
MA_PopToPos(checkpoint.arena, checkpoint.pos);
}
MA_THREAD_LOCAL MA_Arena *MA_ScratchArenaPool[4];
MA_API void MA_InitScratch(void) {
for (int i = 0; i < MA_Lengthof(MA_ScratchArenaPool); i += 1) {
MA_ScratchArenaPool[i] = MA_Bootstrap();
}
}
MA_API MA_Temp MA_GetScratchEx(MA_Arena **conflicts, int conflict_count) {
MA_Arena *unoccupied = 0;
for (int i = 0; i < MA_Lengthof(MA_ScratchArenaPool); i += 1) {
MA_Arena *from_pool = MA_ScratchArenaPool[i];
unoccupied = from_pool;
for (int conflict_i = 0; conflict_i < conflict_count; conflict_i += 1) {
MA_Arena *from_conflict = conflicts[conflict_i];
if (from_pool == from_conflict) {
unoccupied = 0;
break;
}
}
if (unoccupied) {
break;
}
}
MA_Assertf(unoccupied, "Failed to get free scratch memory, this is a fatal error, this shouldnt happen");
MA_Temp result = MA_BeginTemp(unoccupied);
return result;
}
MA_API MA_Temp MA_GetScratch(void) {
MA_Temp result = MA_BeginTemp(MA_ScratchArenaPool[0]);
return result;
}
MA_API MA_Temp MA_GetScratch1(MA_Arena *conflict) {
MA_Arena *conflicts[] = {conflict};
return MA_GetScratchEx(conflicts, 1);
}
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
const size_t MV__WIN32_PAGE_SIZE = 4096;
MA_API MV_Memory MV_Reserve(size_t size) {
MV_Memory result;
MA_MemoryZero(&result, sizeof(result));
size_t adjusted_size = MA_AlignUp(size, MV__WIN32_PAGE_SIZE);
result.data = (uint8_t *)VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE);
MA_Assertf(result.data, "Failed to reserve virtual memory");
result.reserve = adjusted_size;
return result;
}
MA_API bool MV_Commit(MV_Memory *m, size_t commit) {
uint8_t *pointer = MV__AdvanceCommit(m, &commit, MV__WIN32_PAGE_SIZE);
if (pointer) {
void *result = VirtualAlloc(pointer, commit, MEM_COMMIT, PAGE_READWRITE);
MA_Assertf(result, "Failed to commit more memory");
if (result) {
m->commit += commit;
return true;
}
}
return false;
}
MA_API void MV_Deallocate(MV_Memory *m) {
BOOL result = VirtualFree(m->data, 0, MEM_RELEASE);
MA_Assertf(result != 0, "Failed to release MV_Memory");
}
MA_API bool MV_DecommitPos(MV_Memory *m, size_t pos) {
size_t aligned = MA_AlignDown(pos, MV__WIN32_PAGE_SIZE);
size_t adjusted_pos = MA_CLAMP_TOP(aligned, m->commit);
size_t size_to_decommit = m->commit - adjusted_pos;
if (size_to_decommit) {
uint8_t *base_address = m->data + adjusted_pos;
BOOL result = VirtualFree(base_address, size_to_decommit, MEM_DECOMMIT);
if (result) {
m->commit -= size_to_decommit;
return true;
}
}
return false;
}
#elif __unix__ || __linux__ || __APPLE__
#include <sys/mman.h>
#define MV__UNIX_PAGE_SIZE 4096
MA_API MV_Memory MV_Reserve(size_t size) {
MV_Memory result = {};
size_t size_aligned = MA_AlignUp(size, MV__UNIX_PAGE_SIZE);
result.data = (uint8_t *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
MA_Assertf(result.data, "Failed to reserve memory using mmap!!");
if (result.data) {
result.reserve = size_aligned;
}
return result;
}
MA_API bool MV_Commit(MV_Memory *m, size_t commit) {
uint8_t *pointer = MV__AdvanceCommit(m, &commit, MV__UNIX_PAGE_SIZE);
if (pointer) {
int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE);
MA_Assertf(mprotect_result == 0, "Failed to commit more memory using mmap");
if (mprotect_result == 0) {
m->commit += commit;
return true;
}
}
return false;
}
MA_API void MV_Deallocate(MV_Memory *m) {
int result = munmap(m->data, m->reserve);
MA_Assertf(result == 0, "Failed to release virtual memory using munmap");
}
#else
MA_API MV_Memory MV_Reserve(size_t size) {
MV_Memory result = {0};
return result;
}
MA_API bool MV_Commit(MV_Memory *m, size_t commit) {
return false;
}
MA_API void MV_Deallocate(MV_Memory *m) {
}
#endif

View File

@@ -0,0 +1,179 @@
#ifndef MA_HEADER
#define MA_HEADER
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define MA_KIB(x) ((x##ull) * 1024ull)
#define MA_MIB(x) (MA_KIB(x) * 1024ull)
#define MA_GIB(x) (MA_MIB(x) * 1024ull)
#define MA_TIB(x) (MA_GIB(x) * 1024ull)
typedef struct MV_Memory MV_Memory;
typedef struct MA_Temp MA_Temp;
typedef struct MA_Arena MA_Arena;
typedef struct MA_SourceLoc MA_SourceLoc;
#ifndef MA_DEFAULT_RESERVE_SIZE
#define MA_DEFAULT_RESERVE_SIZE MA_GIB(1)
#endif
#ifndef MA_DEFAULT_ALIGNMENT
#define MA_DEFAULT_ALIGNMENT 8
#endif
#ifndef MA_COMMIT_ADD_SIZE
#define MA_COMMIT_ADD_SIZE MA_MIB(4)
#endif
#ifndef MA_ZERO_IS_INITIALIZATION
#define MA_ZERO_IS_INITIALIZATION 1
#endif
#ifndef MA_API
#ifdef __cplusplus
#define MA_API extern "C"
#else
#define MA_API
#endif
#endif
#ifndef MA_THREAD_LOCAL
#if defined(__cplusplus) && __cplusplus >= 201103L
#define MA_THREAD_LOCAL thread_local
#elif defined(__GNUC__)
#define MA_THREAD_LOCAL __thread
#elif defined(_MSC_VER)
#define MA_THREAD_LOCAL __declspec(thread)
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
#define MA_THREAD_LOCAL _Thread_local
#elif defined(__TINYC__)
#define MA_THREAD_LOCAL _Thread_local
#else
#error Couldnt figure out thread local, needs to be provided manually
#endif
#endif
#ifndef MA_MemoryZero
#include <string.h>
#define MA_MemoryZero(p, size) memset(p, 0, size)
#endif
#ifndef MA_MemoryCopy
#include <string.h>
#define MA_MemoryCopy(dst, src, size) memcpy(dst, src, size);
#endif
struct MV_Memory {
size_t commit;
size_t reserve;
uint8_t *data;
};
struct MA_Arena {
MV_Memory memory;
int alignment;
size_t len;
size_t base_len; // When popping to 0 this is the minimum "len" value
// It's so that Bootstrapped arena won't delete itself when Reseting.
};
struct MA_Temp {
MA_Arena *arena;
size_t pos;
};
struct MA_SourceLoc {
const char *file;
int line;
};
extern MA_THREAD_LOCAL MA_SourceLoc MA_SavedSourceLoc;
#define MA_SaveSourceLoc() MA_SaveSourceLocEx(__FILE__, __LINE__)
MA_API void MA_SaveSourceLocEx(const char *file, int line);
#define MA_PushSize(a, size) MA__PushSize(a, size)
#define MA_PushSizeNonZeroed(a, size) MA__PushSizeNonZeroed(a, size)
#define MA_PushCopy(a, p, size) MA__PushCopy(a, p, size)
#define MA_PushStringCopy(a, p, size) MA__PushStringCopy(a, p, size)
#define MA_PushArrayNonZeroed(a, T, c) (T *)MA__PushSizeNonZeroed(a, sizeof(T) * (c))
#define MA_PushStructNonZeroed(a, T) (T *)MA__PushSizeNonZeroed(a, sizeof(T))
#define MA_PushStruct(a, T) (T *)MA__PushSize(a, sizeof(T))
#define MA_PushArray(a, T, c) (T *)MA__PushSize(a, sizeof(T) * (c))
#define MA_PushStructCopy(a, T, p) (T *)MA__PushCopy(a, (p), sizeof(T))
// clang-format off
MA_API void MA_InitEx(MA_Arena *a, size_t reserve);
MA_API void MA_Init(MA_Arena *a);
MA_API MA_Arena MA_Create();
MA_API void MA_MakeSureInitialized(MA_Arena *a);
MA_API void MA_InitFromBuffer(MA_Arena *arena, void *buffer, size_t size);
MA_API MA_Arena MA_MakeFromBuffer(void *buffer, size_t size);
MA_API MA_Arena * MA_Bootstrap(void);
MA_API MA_Arena MA_PushArena(MA_Arena *arena, size_t size);
MA_API MA_Arena * MA_PushArenaP(MA_Arena *arena, size_t size);
MA_API void * MA__PushSizeNonZeroed(MA_Arena *a, size_t size);
MA_API void * MA__PushSize(MA_Arena *arena, size_t size);
MA_API char * MA__PushStringCopy(MA_Arena *arena, char *p, size_t size);
MA_API void * MA__PushCopy(MA_Arena *arena, void *p, size_t size);
MA_API MA_Temp MA_BeginTemp(MA_Arena *arena);
MA_API void MA_EndTemp(MA_Temp checkpoint);
MA_API void MA_PopToPos(MA_Arena *arena, size_t pos);
MA_API void MA_PopSize(MA_Arena *arena, size_t size);
MA_API void MA_DeallocateArena(MA_Arena *arena);
MA_API void MA_Reset(MA_Arena *arena);
MA_API size_t MA_GetAlignOffset(size_t size, size_t align);
MA_API size_t MA_AlignUp(size_t size, size_t align);
MA_API size_t MA_AlignDown(size_t size, size_t align);
MA_API bool MA_IsPointerInside(MA_Arena *arena, void *p);
MA_API void MA_SetAlignment(MA_Arena *arena, int alignment);
MA_API uint8_t * MA_GetTop(MA_Arena *a);
MA_API MV_Memory MV_Reserve(size_t size);
MA_API bool MV_Commit(MV_Memory *m, size_t commit);
MA_API void MV_Deallocate(MV_Memory *m);
MA_API bool MV_DecommitPos(MV_Memory *m, size_t pos);
// clang-format on
extern MA_THREAD_LOCAL MA_Arena *MA_ScratchArenaPool[4];
#define MA_CheckpointScope(name, InArena) for (MA_Temp name = MA_BeginTemp(InArena); name.arena; (MA_EndTemp(name), name.arena = 0))
#define MA_ScratchScope(x) for (MA_Temp x = MA_GetScratch(); x.arena; (MA_ReleaseScratch(x), x.arena = 0))
#define MA_ReleaseScratch MA_EndTemp
MA_API MA_Temp MA_GetScratchEx(MA_Arena **conflicts, int conflict_count);
MA_API MA_Temp MA_GetScratch(void);
MA_API MA_Temp MA_GetScratch1(MA_Arena *conflict);
#if defined(__cplusplus)
struct MA_Scratch {
MA_Temp checkpoint;
MA_Scratch() { this->checkpoint = MA_GetScratch(); }
MA_Scratch(MA_Temp conflict) { this->checkpoint = MA_GetScratch1(conflict.arena); }
MA_Scratch(MA_Temp c1, MA_Temp c2) {
MA_Arena *conflicts[] = {c1.arena, c2.arena};
this->checkpoint = MA_GetScratchEx(conflicts, 2);
}
~MA_Scratch() { MA_EndTemp(checkpoint); }
operator MA_Arena *() { return checkpoint.arena; }
private: // @Note: Disable copy constructors, cause its error prone
MA_Scratch(MA_Scratch &arena);
MA_Scratch(MA_Scratch &arena, MA_Scratch &a2);
};
#endif // __cplusplus
#define MA_IS_POW2(x) (((x) & ((x)-1)) == 0)
#define MA_MIN(x, y) ((x) <= (y) ? (x) : (y))
#define MA_MAX(x, y) ((x) >= (y) ? (x) : (y))
#define MA_Lengthof(x) ((int64_t)((sizeof(x) / sizeof((x)[0]))))
#define MA_CLAMP_TOP(x, max) ((x) >= (max) ? (max) : (x))
#define MA_CLAMP_BOT(x, min) ((x) <= (min) ? (min) : (x))
#define MA_CLAMP(x, min, max) ((x) >= (max) ? (max) : (x) <= (min) ? (min) \
: (x))
#endif // MA_HEADER

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,302 @@
#ifndef FIRST_CL_HEADER
#define FIRST_CL_HEADER
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#ifndef CL_API_FUNCTION
#ifdef __cplusplus
#define CL_API_FUNCTION extern "C"
#else
#define CL_API_FUNCTION
#endif
#endif
#ifndef CL_INLINE
#ifndef _MSC_VER
#ifdef __cplusplus
#define CL_INLINE inline
#else
#define CL_INLINE
#endif
#else
#define CL_INLINE __forceinline
#endif
#endif
#ifndef CL_Allocator
struct MA_Arena;
#define CL_Allocator MA_Arena *
#endif
#ifndef AND_CL_STRING_TERMINATE_ON_NEW_LINE
#define AND_CL_STRING_TERMINATE_ON_NEW_LINE &&*T->stream != '\n'
#endif
typedef enum CL_Kind {
CL_EOF,
CL_MUL,
CL_DIV,
CL_MOD,
CL_LEFTSHIFT,
CL_RIGHTSHIFT,
CL_ADD,
CL_SUB,
CL_EQUALS,
CL_LESSERTHEN,
CL_GREATERTHEN,
CL_LESSERTHEN_OR_EQUAL,
CL_GREATERTHEN_OR_EQUAL,
CL_NOTEQUALS,
CL_BITAND,
CL_BITOR,
CL_BITXOR,
CL_AND,
CL_OR,
CL_NEG,
CL_NOT,
CL_DECREMENT,
CL_INCREMENT,
CL_POSTDECREMENT,
CL_POSTINCREMENT,
CL_ASSIGN,
CL_DIVASSIGN,
CL_MULASSIGN,
CL_MODASSIGN,
CL_SUBASSIGN,
CL_ADDASSIGN,
CL_ANDASSIGN,
CL_ORASSIGN,
CL_XORASSIGN,
CL_LEFTSHIFTASSIGN,
CL_RIGHTSHIFTASSIGN,
CL_OPENPAREN,
CL_CLOSEPAREN,
CL_OPENBRACE,
CL_CLOSEBRACE,
CL_OPENBRACKET,
CL_CLOSEBRACKET,
CL_COMMA,
CL_MACRO_CONCAT,
CL_PREPROC_STRINGIFY,
CL_QUESTION,
CL_THREEDOTS,
CL_SEMICOLON,
CL_DOT,
CL_COLON,
CL_TAG,
CL_ARROW,
CL_EXPRSIZEOF,
CL_DOCCOMMENT,
CL_COMMENT,
CL_IDENTIFIER,
CL_STRINGLIT,
CL_CHARLIT,
CL_ERROR,
CL_FLOAT,
CL_INT,
CL_PREPROC_NULL,
CL_PREPROC_DEFINE,
CL_PREPROC_IFDEF,
CL_PREPROC_IFNDEF,
CL_PREPROC_INCLUDE,
CL_PREPROC_ENDIF,
CL_PREPROC_IF,
CL_PREPROC_PRAGMA,
CL_PREPROC_ERROR,
CL_PREPROC_ELSE,
CL_PREPROC_ELIF,
CL_PREPROC_UNDEF,
CL_KEYWORD_VOID,
CL_KEYWORD_INT,
CL_KEYWORD_CHAR,
CL_KEYWORD_UNSIGNED,
CL_KEYWORD_SIGNED,
CL_KEYWORD_LONG,
CL_KEYWORD_SHORT,
CL_KEYWORD_DOUBLE,
CL_KEYWORD_FLOAT,
CL_KEYWORD__BOOL,
CL_KEYWORD__COMPLEX,
CL_KEYWORD__IMAGINARY,
CL_KEYWORD_STATIC,
CL_KEYWORD_AUTO,
CL_KEYWORD_CONST,
CL_KEYWORD_EXTERN,
CL_KEYWORD_INLINE,
CL_KEYWORD_REGISTER,
CL_KEYWORD_RESTRICT,
CL_KEYWORD_VOLATILE,
CL_KEYWORD__THREAD_LOCAL,
CL_KEYWORD__ATOMIC,
CL_KEYWORD__NORETURN,
CL_KEYWORD_STRUCT,
CL_KEYWORD_UNION,
CL_KEYWORD_ENUM,
CL_KEYWORD_TYPEDEF,
CL_KEYWORD_DEFAULT,
CL_KEYWORD_BREAK,
CL_KEYWORD_RETURN,
CL_KEYWORD_SWITCH,
CL_KEYWORD_IF,
CL_KEYWORD_ELSE,
CL_KEYWORD_FOR,
CL_KEYWORD_WHILE,
CL_KEYWORD_CASE,
CL_KEYWORD_CONTINUE,
CL_KEYWORD_DO,
CL_KEYWORD_GOTO,
CL_KEYWORD_SIZEOF,
CL_KEYWORD__ALIGNAS,
CL_KEYWORD__ALIGNOF,
CL_KEYWORD__STATIC_ASSERT,
CL_KEYWORD__GENERIC,
CL_COUNT,
} CL_Kind;
typedef enum CL_Fix {
CL_FIX_NONE,
CL_SUFFIX_U,
CL_SUFFIX_UL,
CL_SUFFIX_ULL,
CL_SUFFIX_L,
CL_SUFFIX_LL,
CL_SUFFIX_F,
CL_SUFFIX_FL,
CL_PREFIX_U8,
CL_PREFIX_U16,
CL_PREFIX_U32,
CL_PREFIX_L,
} CL_Fix;
typedef struct CL_Token CL_Token;
struct CL_Token {
CL_Kind kind;
CL_Fix fix;
bool is_hex : 1;
bool is_inside_macro : 1;
bool is_system_include : 1;
bool is_there_whitespace_before_token : 1;
uint32_t id;
int len;
char *str;
// Not storing line_begin like I would normally cause the user could
// override the line and file information using directives.
// On error need to do search if I want nice error context.
int line, column;
char *file;
union {
double f64;
uint64_t u64;
char *intern;
char *string_literal;
struct CL_Message *error;
};
};
typedef struct CL_Message CL_Message;
struct CL_Message {
CL_Message *next;
char *string;
CL_Token token;
};
typedef struct CL_Lexer CL_Lexer;
struct CL_Lexer {
CL_Message *first_message;
CL_Message *last_message;
int errors;
char *stream;
char *stream_begin;
int line;
int column;
char *file;
bool inside_of_macro;
// filters
bool skip_comments : 1;
bool skip_macros : 1;
bool select_includes : 1;
bool select_comments : 1;
bool select_macros : 1;
CL_Allocator arena;
};
typedef struct CL_SearchPaths CL_SearchPaths;
struct CL_SearchPaths {
char **include_path;
int include_path_count;
char **system_include_path;
int system_include_path_count;
char *file_begin_to_ignore;
};
CL_API_FUNCTION CL_Token CL_Next(CL_Lexer *T);
CL_API_FUNCTION CL_Lexer CL_Begin(CL_Allocator arena, char *stream, char *filename);
CL_API_FUNCTION char *CL_ResolveFilepath(CL_Allocator arena, CL_SearchPaths *search_paths, char *filename, char *parent_file, bool is_system_include);
CL_API_FUNCTION void CL_StringifyMessage(char *buff, int buff_size, CL_Message *msg);
CL_API_FUNCTION void CL_Stringify(char *buff, int buff_size, CL_Token *token);
extern const char *CL_FixString[];
extern const char *CL_KindString[];
CL_INLINE int CL_StringLength(char *string) {
int len = 0;
while (*string++ != 0) len++;
return len;
}
CL_INLINE bool CL_StringsAreEqual(char *a, int64_t alen, const char *b, int64_t blen) {
if (alen != blen) return false;
for (int i = 0; i < alen; i += 1) {
if (a[i] != b[i]) return false;
}
return true;
}
CL_INLINE bool CL_IsIdentifier(CL_Token *token, char *str) {
int str_len = CL_StringLength(str);
bool result = token->kind == CL_IDENTIFIER && CL_StringsAreEqual(token->str, token->len, str, str_len);
return result;
}
CL_INLINE bool CL_IsAssign(CL_Kind op) {
bool result = op >= CL_ASSIGN && op <= CL_RIGHTSHIFTASSIGN;
return result;
}
CL_INLINE bool CL_IsKeywordType(CL_Kind op) {
bool result = op >= CL_KEYWORD_VOID && op <= CL_KEYWORD__IMAGINARY;
return result;
}
CL_INLINE bool CL_IsKeywordTypeOrSpec(CL_Kind op) {
bool result = op >= CL_KEYWORD_VOID && op <= CL_KEYWORD_TYPEDEF;
return result;
}
CL_INLINE bool CL_IsMacro(CL_Kind kind) {
bool result = kind >= CL_PREPROC_DEFINE && kind <= CL_PREPROC_UNDEF;
return result;
}
CL_INLINE bool CL_IsKeyword(CL_Kind kind) {
bool result = kind >= CL_KEYWORD_VOID && kind <= CL_KEYWORD__GENERIC;
return result;
}
CL_INLINE bool CL_IsKeywordOrIdent(CL_Kind kind) {
bool result = CL_IsKeyword(kind) || kind == CL_IDENTIFIER;
return result;
}
#endif

View File

@@ -0,0 +1,25 @@
#ifndef FIRST_DEFER_HEADER
#define FIRST_DEFER_HEADER
template <typename T>
struct DEFER_ExitScope {
T lambda;
DEFER_ExitScope(T lambda) : lambda(lambda) {}
~DEFER_ExitScope() { lambda(); }
DEFER_ExitScope(const DEFER_ExitScope &i) : lambda(i.lambda){};
private:
DEFER_ExitScope &operator=(const DEFER_ExitScope &);
};
class DEFER_ExitScopeHelp {
public:
template <typename T>
DEFER_ExitScope<T> operator+(T t) { return t; }
};
#define DEFER_CONCAT_INTERNAL(x, y) x##y
#define DEFER_CONCAT(x, y) DEFER_CONCAT_INTERNAL(x, y)
#define defer const auto DEFER_CONCAT(defer__, __LINE__) = DEFER_ExitScopeHelp() + [&]()
#endif

View File

@@ -0,0 +1,53 @@
#include "hash.h"
// FNV HASH (1a?)
HASH_API_FUNCTION uint64_t HashBytes(void *data, uint64_t size) {
uint8_t *data8 = (uint8_t *)data;
uint64_t hash = (uint64_t)14695981039346656037ULL;
for (uint64_t i = 0; i < size; i++) {
hash = hash ^ (uint64_t)(data8[i]);
hash = hash * (uint64_t)1099511628211ULL;
}
return hash;
}
HASH_API_FUNCTION RandomSeed MakeRandomSeed(uint64_t value) {
RandomSeed result;
result.a = value;
return result;
}
HASH_API_FUNCTION uint64_t GetRandomU64(RandomSeed *state) {
uint64_t x = state->a;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
return state->a = x;
}
HASH_API_FUNCTION int GetRandomRangeI(RandomSeed *seed, int first, int last_included) {
uint64_t random = GetRandomU64(seed);
int range = (last_included - first + 1);
int mapped = random % range;
int result = mapped + first;
return result;
}
HASH_API_FUNCTION double GetRandomNormal(RandomSeed *series) {
uint64_t rnd = GetRandomU64(series);
double result = (double)rnd / (double)UINT64_MAX;
return result;
}
HASH_API_FUNCTION double GetRandomNormalRange(RandomSeed *seed, double min, double max) {
double value = GetRandomNormal(seed);
double result = value * (max - min) + min;
return result;
}
HASH_API_FUNCTION uint64_t HashMix(uint64_t x, uint64_t y) {
x ^= y;
x *= 0xff51afd7ed558ccd;
x ^= x >> 32;
return x;
}

View File

@@ -0,0 +1,28 @@
#ifndef FIRST_HASH_HEADER
#define FIRST_HASH_HEADER
#include <stdint.h>
#ifndef HASH_API_FUNCTION
#ifdef __cplusplus
#define HASH_API_FUNCTION extern "C"
#else
#define HASH_API_FUNCTION
#endif
#endif
typedef struct RandomSeed RandomSeed;
struct RandomSeed {
uint64_t a;
};
HASH_API_FUNCTION uint64_t HashBytes(void *data, uint64_t size);
HASH_API_FUNCTION RandomSeed MakeRandomSeed(uint64_t value);
HASH_API_FUNCTION uint64_t GetRandomU64(RandomSeed *state);
HASH_API_FUNCTION int GetRandomRangeI(RandomSeed *seed, int first, int last_included);
HASH_API_FUNCTION double GetRandomNormal(RandomSeed *series);
HASH_API_FUNCTION double GetRandomNormalRange(RandomSeed *seed, double min, double max);
HASH_API_FUNCTION uint64_t HashMix(uint64_t x, uint64_t y);
#define WRAP_AROUND_POWER_OF_2(x, pow2) (((x) & ((pow2)-1llu)))
static inline float GetRandomNormalF(RandomSeed *series) { return (float)GetRandomNormal(series); }
#endif

View File

@@ -0,0 +1,236 @@
#include "io.h"
#include <stdarg.h>
#ifndef IO_SNPRINTF
#include <stdio.h>
#define IO_SNPRINTF snprintf
#endif
#ifndef IO_VSNPRINTF
#include <stdio.h>
#define IO_VSNPRINTF vsnprintf
#endif
#ifndef IO_ALLOCATE
#include <stdlib.h>
#define IO_ALLOCATE(x) malloc(x)
#define IO_FREE(x) free(x)
#endif
#ifndef IO_StaticFunc
#if defined(__GNUC__) || defined(__clang__)
#define IO_StaticFunc __attribute__((unused)) static
#else
#define IO_StaticFunc static
#endif
#endif
IO_StaticFunc int IO_Strlen(char *string) {
int len = 0;
while (*string++ != 0) len++;
return len;
}
IO_THREAD_LOCAL void (*IO_User_OutputMessage)(int kind, const char *file, int line, char *str, int len);
IO_API bool IO__FatalErrorf(const char *file, int line, const char *msg, ...) {
va_list args1;
va_list args2;
char buff[2048];
va_start(args1, msg);
va_copy(args2, args1);
int size = IO_VSNPRINTF(buff, sizeof(buff), msg, args2);
va_end(args2);
char *new_buffer = 0;
char *user_message = buff;
if (size >= sizeof(buff)) {
size += 4;
new_buffer = (char *)IO_ALLOCATE(size);
IO_VSNPRINTF(new_buffer, size, msg, args1);
user_message = new_buffer;
}
va_end(args1);
IO_ErrorResult ret = IO_ErrorResult_Continue;
{
char buff2[2048];
char *result = buff2;
char *b = 0;
int size2 = IO_SNPRINTF(buff2, sizeof(buff2), "%s(%d): error: %s \n", file, line, user_message);
if (size2 >= sizeof(buff2)) {
size2 += 4;
b = (char *)IO_ALLOCATE(size2);
size2 = IO_SNPRINTF(b, size2, "%s(%d): error: %s \n", file, line, user_message);
result = b;
}
ret = IO_OutputError(result, size2);
if (ret == IO_ErrorResult_Exit) {
IO_Exit(1);
}
if (b) {
IO_FREE(b);
}
}
if (new_buffer) {
IO_FREE(new_buffer);
}
return ret == IO_ErrorResult_Break;
}
IO_API void IO__Printf(int kind, const char *file, int line, const char *msg, ...) {
// First try to use a static buffer. That can fail because the message
// can be bigger then the buffer. Allocate enough memory to fit in that
// case.
va_list args1;
va_list args2;
char buff[2048];
va_start(args1, msg);
va_copy(args2, args1);
int size = IO_VSNPRINTF(buff, sizeof(buff), msg, args2);
va_end(args2);
char *new_buffer = 0;
char *result = buff;
if (size >= sizeof(buff)) {
size += 4;
new_buffer = (char *)IO_ALLOCATE(size);
IO_VSNPRINTF(new_buffer, size, msg, args1);
result = new_buffer;
}
va_end(args1);
if (IO_User_OutputMessage) {
IO_User_OutputMessage(kind, file, line, result, size);
} else {
IO_OutputMessage(result, size);
}
if (new_buffer) {
IO_FREE(new_buffer);
}
}
IO_API bool IO__FatalError(const char *msg) {
int len = IO_Strlen((char *)msg);
IO_ErrorResult result = IO_OutputError((char *)msg, len);
if (result == IO_ErrorResult_Exit) {
IO_Exit(1);
}
return result == IO_ErrorResult_Break;
}
IO_API void IO_Print(int kind, const char *file, int line, char *msg, int len) {
if (IO_User_OutputMessage) {
IO_User_OutputMessage(kind, file, line, msg, len);
} else {
IO_OutputMessage(msg, len);
}
}
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#pragma comment(lib, "user32")
#include <stdio.h>
IO_API bool IO_IsDebuggerPresent(void) {
return IsDebuggerPresent();
}
IO_API void IO_OutputMessage(char *str, int len) {
if (IsDebuggerPresent()) {
OutputDebugStringA(str);
}
printf("%.*s", len, str);
fflush(stdout);
}
IO_API IO_ErrorResult IO_OutputError(char *str, int len) {
IO_ErrorResult result = IO_ErrorResult_Continue;
IO_OutputMessage(str, len);
char *msg = str;
if (str[len] != 0) {
msg = (char *)IO_ALLOCATE(len + 1);
for (int i = 0; i < len; i += 1) msg[i] = str[i];
msg[len] = 0;
}
OutputDebugStringA(msg);
if (!IsDebuggerPresent()) {
// Limit size of error output message
char tmp = 0;
if (len > 4096) {
tmp = str[4096];
str[4096] = 0;
}
MessageBoxA(0, msg, "Error!", 0);
if (tmp != 0) {
str[4096] = tmp;
}
result = IO_ErrorResult_Exit;
} else {
result = IO_ErrorResult_Break;
}
if (msg != str) {
IO_FREE(msg);
}
return result;
}
IO_API void IO_Exit(int error_code) {
ExitProcess(error_code);
}
#elif __linux__ || __unix__ || __APPLE__
#include <stdio.h>
IO_API IO_ErrorResult IO_OutputError(char *str, int len) {
fprintf(stderr, "%.*s", len, str);
return IO_ErrorResult_Exit;
}
IO_API void IO_OutputMessage(char *str, int len) {
fprintf(stdout, "%.*s", len, str);
}
IO_API void IO_Exit(int error_code) {
exit(error_code);
}
IO_API bool IO_IsDebuggerPresent(void) {
return false;
}
#else
IO_API IO_ErrorResult IO_OutputError(char *str, int len) {
return IO_ErrorResult_Exit;
}
IO_API void IO_OutputMessage(char *str, int len) {
}
IO_API void IO_Exit(int error_code) {
}
IO_API bool IO_IsDebuggerPresent(void) {
return false;
}
#endif // LIBC

View File

@@ -0,0 +1,108 @@
#ifndef FIRST_IO_HEADER
#define FIRST_IO_HEADER
#include <stdbool.h>
#ifndef IO_API
#ifdef __cplusplus
#define IO_API extern "C"
#else
#define IO_API
#endif
#endif
typedef enum IO_ErrorResult {
IO_ErrorResult_Continue,
IO_ErrorResult_Break,
IO_ErrorResult_Exit,
} IO_ErrorResult;
#if defined(_MSC_VER)
#define IO_DebugBreak() (__debugbreak(), 0)
#else
#define IO_DebugBreak() (__builtin_trap(), 0)
#endif
#ifndef IO_THREAD_LOCAL
#if defined(__cplusplus) && __cplusplus >= 201103L
#define IO_THREAD_LOCAL thread_local
#elif defined(__GNUC__)
#define IO_THREAD_LOCAL __thread
#elif defined(_MSC_VER)
#define IO_THREAD_LOCAL __declspec(thread)
#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__)
#define IO_THREAD_LOCAL _Thread_local
#elif defined(__TINYC__)
#define IO_THREAD_LOCAL _Thread_local
#else
#error Couldnt figure out thread local, needs to be provided manually
#endif
#endif
#if defined(__has_attribute)
#if __has_attribute(format)
#define IO__PrintfFormat(fmt, va) __attribute__((format(printf, fmt, va)))
#endif
#endif
#ifndef IO__PrintfFormat
#define IO__PrintfFormat(fmt, va)
#endif
typedef void IO_MessageHandler(int kind, const char *file, int line, char *str, int len);
extern IO_THREAD_LOCAL void (*IO_User_OutputMessage)(int kind, const char *file, int line, char *str, int len);
#define IO__STRINGIFY(x) #x
#define IO__TOSTRING(x) IO__STRINGIFY(x)
#define IO_LINE IO__TOSTRING(__LINE__)
#define IO_Assert(x) !(x) && IO__FatalError((__FILE__ "(" IO_LINE "): " \
"error: " #x "\n")) && \
IO_DebugBreak()
#define IO_FatalErrorf(...) \
do { \
bool result = IO__FatalErrorf(__FILE__, __LINE__, __VA_ARGS__); \
if (result) IO_DebugBreak(); \
} while (0)
#define IO_FatalError(...) \
do { \
bool result = IO__FatalError(__FILE__ "(" IO_LINE "): error - " __VA_ARGS__); \
if (result) IO_DebugBreak(); \
} while (0)
#define IO_Assertf(x, ...) \
do { \
if (!(x)) { \
bool result = IO__FatalErrorf(__FILE__, __LINE__, __VA_ARGS__); \
if (result) IO_DebugBreak(); \
} \
} while (0)
#define IO_InvalidElseIf(c) \
else if (c) { \
IO_InvalidCodepath(); \
}
#define IO_InvalidElse() \
else { \
IO_InvalidCodepath(); \
}
#define IO_InvalidCodepath() IO_FatalError("This codepath is invalid")
#define IO_InvalidDefaultCase() \
default: { \
IO_FatalError("Entered invalid switch statement case"); \
}
#define IO_Todo() IO_FatalError("This codepath is not implemented yet")
IO_API bool IO__FatalErrorf(const char *file, int line, const char *msg, ...) IO__PrintfFormat(3, 4);
IO_API void IO__Printf(int kind, const char *file, int line, const char *msg, ...) IO__PrintfFormat(4, 5);
IO_API bool IO__FatalError(const char *msg);
IO_API void IO_Print(int kind, const char *file, int line, char *msg, int len);
IO_API void IO_OutputMessage(char *str, int len);
IO_API IO_ErrorResult IO_OutputError(char *str, int len);
IO_API void IO_Exit(int error_code);
IO_API bool IO_IsDebuggerPresent(void);
static const int IO_KindPrintf = 1;
static const int IO_KindWarningf = 2;
#define IO_Printf(...) IO__Printf(IO_KindPrintf, __FILE__, __LINE__, __VA_ARGS__)
#define IO_Warningf(...) IO__Printf(IO_KindWarningf, __FILE__, __LINE__, __VA_ARGS__)
#endif

View File

@@ -0,0 +1,119 @@
#ifndef FIRST_LL_HEADER
#define FIRST_LL_HEADER
#define SLL_QUEUE_ADD_MOD(f, l, n, next) \
do { \
(n)->next = 0; \
if ((f) == 0) { \
(f) = (l) = (n); \
} else { \
(l) = (l)->next = (n); \
} \
} while (0)
#define SLL_QUEUE_ADD(f, l, n) SLL_QUEUE_ADD_MOD(f, l, n, next)
#define SLL_QUEUE_POP_FIRST_MOD(f, l, next) \
do { \
if ((f) == (l)) { \
(f) = (l) = 0; \
} else { \
(f) = (f)->next; \
} \
} while (0)
#define SLL_QUEUE_POP_FIRST(f, l) SLL_QUEUE_POP_FIRST_MOD(f, l, next)
#define SLL_STACK_ADD_MOD(stack_base, new_stack_base, next) \
do { \
(new_stack_base)->next = (stack_base); \
(stack_base) = (new_stack_base); \
} while (0)
#define SLL_STACK_ADD(stack_base, new_stack_base) \
SLL_STACK_ADD_MOD(stack_base, new_stack_base, next)
#define SLL_STACK_POP_AND_STORE(stack_base, out_node) \
do { \
if (stack_base) { \
(out_node) = (stack_base); \
(stack_base) = (stack_base)->next; \
(out_node)->next = 0; \
} \
} while (0)
#define DLL_QUEUE_ADD_MOD(f, l, node, next, prev) \
do { \
if ((f) == 0) { \
(f) = (l) = (node); \
(node)->prev = 0; \
(node)->next = 0; \
} else { \
(l)->next = (node); \
(node)->prev = (l); \
(node)->next = 0; \
(l) = (node); \
} \
} while (0)
#define DLL_QUEUE_ADD(f, l, node) DLL_QUEUE_ADD_MOD(f, l, node, next, prev)
#define DLL_QUEUE_ADD_FRONT(f, l, node) DLL_QUEUE_ADD_MOD(l, f, node, prev, next)
#define DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev) \
do { \
if ((first) == (last)) { \
(first) = (last) = 0; \
} else if ((last) == (node)) { \
(last) = (last)->prev; \
(last)->next = 0; \
} else if ((first) == (node)) { \
(first) = (first)->next; \
(first)->prev = 0; \
} else { \
(node)->prev->next = (node)->next; \
(node)->next->prev = (node)->prev; \
} \
if (node) { \
(node)->prev = 0; \
(node)->next = 0; \
} \
} while (0)
#define DLL_QUEUE_REMOVE(first, last, node) DLL_QUEUE_REMOVE_MOD(first, last, node, next, prev)
#define DLL_STACK_ADD_MOD(first, node, next, prev) \
do { \
(node)->next = (first); \
if ((first)) \
(first)->prev = (node); \
(first) = (node); \
(node)->prev = 0; \
} while (0)
#define DLL_STACK_ADD(first, node) DLL_STACK_ADD_MOD(first, node, next, prev)
#define DLL_STACK_REMOVE_MOD(first, node, next, prev) \
do { \
if ((node) == (first)) { \
(first) = (first)->next; \
if ((first)) \
(first)->prev = 0; \
} else { \
(node)->prev->next = (node)->next; \
if ((node)->next) \
(node)->next->prev = (node)->prev; \
} \
if (node) { \
(node)->prev = 0; \
(node)->next = 0; \
} \
} while (0)
#define DLL_STACK_REMOVE(first, node) DLL_STACK_REMOVE_MOD(first, node, next, prev)
#define DLL_INSERT_NEXT_MOD(base, new, next, prev) \
do { \
if ((base) == 0) { \
(base) = (new); \
(new)->next = 0; \
(new)->prev = 0; \
} else { \
(new)->next = (base)->next; \
(base)->next = (new); \
(new)->prev = (base); \
if ((new)->next) (new)->next->prev = (new); \
} \
} while (0)
#define DLL_INSERT_NEXT(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev)
#define DLL_INSERT_PREV(base, new) DLL_INSERT_NEXT_MOD(base, new, next, prev)
#endif

View File

@@ -0,0 +1,26 @@
#include "load_library.h"
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
LIB_Library LIB_LoadLibrary(char *str) {
HMODULE module = LoadLibraryA(str);
return (LIB_Library)module;
}
void *LIB_LoadSymbol(LIB_Library lib, char *symbol) {
void *result = (void *)GetProcAddress((HMODULE)lib, symbol);
return result;
}
bool LIB_UnloadLibrary(LIB_Library lib) {
BOOL result = FreeLibrary((HMODULE)lib);
if (result == 0) return false;
return true;
}
#endif // _WIN32

View File

@@ -0,0 +1,12 @@
#ifndef FIRST_LIB_HEADER
#define FIRST_LIB_HEADER
typedef void *LIB_Library;
LIB_Library LIB_LoadLibrary(char *str);
void *LIB_LoadSymbol(LIB_Library lib, char *symbol);
bool LIB_UnloadLibrary(LIB_Library lib);
#ifndef LIB_EXPORT
#define LIB_EXPORT __declspec(dllexport)
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,370 @@
#ifndef FIRST_MU_HEADER
#define FIRST_MU_HEADER
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#ifndef MU_API
#ifdef __cplusplus
#define MU_API extern "C"
#else
#define MU_API
#endif
#endif
#ifndef MU_INLINE
#ifndef _MSC_VER
#ifdef __cplusplus
#define MU_INLINE inline
#else
#define MU_INLINE
#endif
#else
#define MU_INLINE __forceinline
#endif
#endif
#ifndef MU_Float2
#define MU_Float2 MU__Float2
typedef struct MU__Float2 {
float x;
float y;
} MU__Float2;
#endif
#ifndef MU_Int2
#define MU_Int2 MU__Int2
typedef struct MU__Int2 {
int x;
int y;
} MU__Int2;
#endif
//@begin gen_structs
typedef struct MU_UTF32Result MU_UTF32Result;
typedef struct MU_UTF8Result MU_UTF8Result;
typedef struct MU_Win32 MU_Win32;
typedef struct MU_Win32_Window MU_Win32_Window;
typedef struct MU_Window_Params MU_Window_Params;
typedef struct MU_Params MU_Params;
typedef struct MU_Key_State MU_Key_State;
typedef struct MU_Mouse_State MU_Mouse_State;
typedef struct MU_DroppedFile MU_DroppedFile;
typedef struct MU_Arena MU_Arena;
typedef struct MU_Window MU_Window;
typedef struct MU_Time MU_Time;
typedef struct MU_Sound MU_Sound;
typedef struct MU_Context MU_Context;
//@end gen_structs
typedef void *MU_glGetProcAddress(const char *);
struct MU_Window_Params {
MU_Int2 size;
MU_Int2 pos;
char *title;
bool enable_canvas;
bool resizable;
bool borderless;
bool fps_cursor;
};
struct MU_Params {
void *memory;
size_t cap;
bool enable_opengl;
int opengl_major;
int opengl_minor;
double delta_time;
MU_Window_Params window; // this controls window when calling MU_Start
void (*sound_callback)(MU_Context *mu, uint16_t *buffer, uint32_t samples_to_fill);
};
struct MU_Key_State {
bool down;
bool press;
bool unpress;
bool raw_press;
};
typedef enum MU_Key {
MU_KEY_INVALID,
MU_KEY_ESCAPE,
MU_KEY_ENTER,
MU_KEY_TAB,
MU_KEY_BACKSPACE,
MU_KEY_INSERT,
MU_KEY_DELETE,
MU_KEY_RIGHT,
MU_KEY_LEFT,
MU_KEY_DOWN,
MU_KEY_UP,
MU_KEY_PAGE_UP,
MU_KEY_PAGE_DOWN,
MU_KEY_HOME,
MU_KEY_END,
MU_KEY_F1,
MU_KEY_F2,
MU_KEY_F3,
MU_KEY_F4,
MU_KEY_F5,
MU_KEY_F6,
MU_KEY_F7,
MU_KEY_F8,
MU_KEY_F9,
MU_KEY_F10,
MU_KEY_F11,
MU_KEY_F12,
MU_KEY_SPACE = 32,
MU_KEY_APOSTROPHE = 39,
MU_KEY_PLUS = 43,
MU_KEY_COMMA = 44,
MU_KEY_MINUS = 45,
MU_KEY_PERIOD = 46,
MU_KEY_SLASH = 47,
MU_KEY_0 = 48,
MU_KEY_1 = 49,
MU_KEY_2 = 50,
MU_KEY_3 = 51,
MU_KEY_4 = 52,
MU_KEY_5 = 53,
MU_KEY_6 = 54,
MU_KEY_7 = 55,
MU_KEY_8 = 56,
MU_KEY_9 = 57,
MU_KEY_SEMICOLON = 59,
MU_KEY_EQUAL = 61,
MU_KEY_A = 65,
MU_KEY_B = 66,
MU_KEY_C = 67,
MU_KEY_D = 68,
MU_KEY_E = 69,
MU_KEY_F = 70,
MU_KEY_G = 71,
MU_KEY_H = 72,
MU_KEY_I = 73,
MU_KEY_J = 74,
MU_KEY_K = 75,
MU_KEY_L = 76,
MU_KEY_M = 77,
MU_KEY_N = 78,
MU_KEY_O = 79,
MU_KEY_P = 80,
MU_KEY_Q = 81,
MU_KEY_R = 82,
MU_KEY_S = 83,
MU_KEY_T = 84,
MU_KEY_U = 85,
MU_KEY_V = 86,
MU_KEY_W = 87,
MU_KEY_X = 88,
MU_KEY_Y = 89,
MU_KEY_Z = 90,
MU_KEY_LEFT_BRACKET = 91,
MU_KEY_BACKSLASH = 92,
MU_KEY_RIGHT_BRACKET = 93,
MU_KEY_GRAVE_ACCENT = 96,
MU_KEY_F13,
MU_KEY_F14,
MU_KEY_F15,
MU_KEY_F16,
MU_KEY_F17,
MU_KEY_F18,
MU_KEY_F19,
MU_KEY_F20,
MU_KEY_F21,
MU_KEY_F22,
MU_KEY_F23,
MU_KEY_F24,
MU_KEY_KP_0,
MU_KEY_KP_1,
MU_KEY_KP_2,
MU_KEY_KP_3,
MU_KEY_KP_4,
MU_KEY_KP_5,
MU_KEY_KP_6,
MU_KEY_KP_7,
MU_KEY_KP_8,
MU_KEY_KP_9,
MU_KEY_KP_DECIMAL,
MU_KEY_KP_DIVIDE,
MU_KEY_KP_MULTIPLY,
MU_KEY_KP_SUBTRACT,
MU_KEY_KP_ADD,
MU_KEY_KP_ENTER,
MU_KEY_LEFT_SHIFT,
MU_KEY_LEFT_CONTROL,
MU_KEY_LEFT_ALT,
MU_KEY_LEFT_SUPER,
MU_KEY_RIGHT_SHIFT,
MU_KEY_RIGHT_CONTROL,
MU_KEY_RIGHT_ALT,
MU_KEY_RIGHT_SUPER,
MU_KEY_CAPS_LOCK,
MU_KEY_SCROLL_LOCK,
MU_KEY_NUM_LOCK,
MU_KEY_PRINT_SCREEN,
MU_KEY_PAUSE,
MU_KEY_SHIFT,
MU_KEY_CONTROL,
MU_KEY_COUNT,
} MU_Key;
struct MU_Mouse_State {
MU_Int2 pos;
MU_Float2 posf;
MU_Int2 delta_pos;
MU_Float2 delta_pos_normalized;
MU_Key_State left;
MU_Key_State middle;
MU_Key_State right;
float delta_wheel; // @todo: add smooth delta?
};
struct MU_DroppedFile {
MU_DroppedFile *next;
char *filename; // null terminated
int filename_size;
};
struct MU_Arena {
char *memory;
size_t len;
size_t cap;
};
// Most of the fields in the window struct are read only. They are updated
// in appropriate update functions. The window should belong to the MU_Context
// but you get access to the information.
struct MU_Window {
MU_Int2 size;
MU_Float2 sizef;
MU_Int2 pos;
MU_Float2 posf;
float dpi_scale;
bool is_fullscreen;
bool is_fps_mode;
bool is_focused;
bool change_cursor_on_mouse_hold; // @in @out
uint64_t processed_events_this_frame;
bool should_render; // @in @out this is false on first frame but it doesn't matter cause it shouldnt be rendered
MU_DroppedFile *first_dropped_file;
uint32_t *canvas;
bool canvas_enabled; // @in @out
MU_Mouse_State mouse;
MU_Key_State key[MU_KEY_COUNT];
uint32_t user_text32[32];
int user_text32_count;
char user_text8[32];
int user_text8_count;
MU_Window *next;
void *handle;
void *platform;
};
struct MU_Time {
double app_start;
double frame_start;
double update;
double update_total;
double delta;
float deltaf;
double total;
float totalf;
};
struct MU_Sound {
bool initialized;
unsigned samples_per_second;
unsigned number_of_channels;
unsigned bytes_per_sample;
void (*callback)(MU_Context *mu, uint16_t *buffer, uint32_t samples_to_fill);
};
struct MU_Context {
bool quit;
MU_Sound sound;
MU_Time time;
bool first_frame;
int _MU_Update_count;
size_t frame;
size_t consecutive_missed_frames;
size_t total_missed_frames;
MU_Int2 primary_monitor_size;
bool opengl_initialized;
int opengl_major;
int opengl_minor;
void *(*gl_get_proc_address)(const char *str);
MU_Params params;
MU_Window *window;
MU_Window *all_windows;
MU_Arena perm_arena;
MU_Arena frame_arena; // Reset at beginning of MU_Update
void *platform;
};
//@begin gen_api_funcs
MU_API void MU_Quit(MU_Context *mu);
MU_API void MU_DefaultSoundCallback(MU_Context *mu, uint16_t *buffer, uint32_t samples_to_fill);
MU_API double MU_GetTime(void);
MU_API void MU_ToggleFPSMode(MU_Window *window);
MU_API void MU_DisableFPSMode(MU_Window *window);
MU_API void MU_EnableFPSMode(MU_Window *window);
MU_API void MU_ToggleFullscreen(MU_Window *window);
MU_API void MU_Init(MU_Context *mu, MU_Params params, size_t len);
MU_API MU_Window *MU_AddWindow(MU_Context *mu, MU_Window_Params params);
MU_API void MU_InitWindow(MU_Context *mu, MU_Window *window, MU_Window_Params params);
MU_API MU_Context *MU_Start(MU_Params params);
MU_API bool MU_Update(MU_Context *mu);
//@end gen_api_funcs
/* @! In the future, api for processing messages manually
while(true) {
MU_Event event;
while (mu_get_event_blocking(&event)) {
switch(event.kind) {
}
}
}
typedef int MU_Modifier;
enum MU_Modifier {
MU_MODIFIER_SHIFT = 0x1, // left or right shift key
MU_MODIFIER_CTRL = 0x2, // left or right control key
MU_MODIFIER_ALT = 0x4, // left or right alt key
MU_MODIFIER_SUPER = 0x8, // left or right 'super' key
MU_MODIFIER_LMB = 0x100, // left mouse button
MU_MODIFIER_RMB = 0x200, // right mouse button
MU_MODIFIER_MMB = 0x400, // middle mouse button
};
typedef enum MU_Event_Kind {
MU_EVENT_KIND_INVALID,
MU_EVENT_KIND_KEY_DOWN,
MU_EVENT_KIND_KEY_UP,
MU_EVENT_KIND_MOUSE_MOVE,
} MU_Event_Kind;
typedef struct MU_Event {
MU_Event_Kind kind;
MU_Modifier modifier;
MU_Key key;
} MU_Event;
*/
#endif

View File

@@ -0,0 +1,119 @@
#ifndef FIRST_ENV_HEADER
#define FIRST_ENV_HEADER
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
#if defined(__APPLE__) && defined(__MACH__)
#define OS_MAC 1
#elif defined(_WIN32)
#define OS_WINDOWS 1
#elif defined(__linux__)
#define OS_POSIX 1
#define OS_LINUX 1
#elif OS_WASM
#else
#error Unsupported platform
#endif
#if defined(__clang__)
#define COMPILER_CLANG 1
#elif defined(__GNUC__) || defined(__GNUG__)
#define COMPILER_GCC 1
#elif defined(_MSC_VER)
#define COMPILER_MSVC 1
#elif defined(__TINYC__)
#define COMPILER_TCC 1
#else
#error Unsupported compiler
#endif
#ifdef __cplusplus
#define LANG_CPP 1
#else
#define LANG_C 1
#endif
#ifndef OS_MAC
#define OS_MAC 0
#endif
#ifndef OS_WINDOWS
#define OS_WINDOWS 0
#endif
#ifndef OS_LINUX
#define OS_LINUX 0
#endif
#ifndef OS_POSIX
#define OS_POSIX 0
#endif
#ifndef COMPILER_MSVC
#define COMPILER_MSVC 0
#endif
#ifndef COMPILER_CLANG
#define COMPILER_CLANG 0
#endif
#ifndef COMPILER_GCC
#define COMPILER_GCC 0
#endif
#ifndef COMPILER_TCC
#define COMPILER_TCC 0
#endif
#ifndef LANG_CPP
#define LANG_CPP 0
#endif
#ifndef LANG_C
#define LANG_C 0
#endif
#if COMPILER_MSVC
#define FORCE_INLINE __forceinline
#elif COMPILER_GCC || COMPILER_CLANG
#define FORCE_INLINE __attribute__((always_inline)) inline
#else
#define FORCE_INLINE inline
#endif
#if OS_MAC
#define IF_MAC(x) x
#else
#define IF_MAC(x)
#endif
#if OS_WINDOWS
#define IF_WINDOWS(x) x
#define IF_WINDOWS_ELSE(x, y) x
#else
#define IF_WINDOWS(x)
#define IF_WINDOWS_ELSE(x, y) y
#endif
#if OS_LINUX
#define IF_LINUX(x) x
#define IF_LINUX_ELSE(x, y) x
#else
#define IF_LINUX(x)
#define IF_LINUX_ELSE(x, y) y
#endif
#if OS_WINDOWS
#define OS_NAME "windows"
#elif OS_LINUX
#define OS_NAME "linux"
#elif OS_MAC
#define OS_NAME "mac_os"
#elif OS_WASM
#define OS_NAME "wasm"
#else
#error couldnt figure out OS
#endif
#endif

View File

@@ -0,0 +1,558 @@
#include "regex.h"
#ifndef RE_ASSERT
#include <assert.h>
#define RE_ASSERT(x) assert(x)
#endif
#ifndef RE_STRICT_ASSERT
#define RE_STRICT_ASSERT RE_ASSERT
#endif
#ifndef RE_MemoryZero
#include <string.h>
#define RE_MemoryZero(p, size) memset(p, 0, size)
#endif
typedef struct RE__Arena {
char *buff;
RE_Int len;
RE_Int cap;
} RE_Arena;
struct RE_String {
char *str;
RE_Int len;
};
struct RE_Utf32Result {
uint32_t out_str;
int advance;
int error;
};
static RE_Regex RE_NullRegex;
static char RE_NullChar;
struct RE_Parser {
RE_String string;
RE_Int i;
RE_Regex *first;
RE_Regex *last;
};
RE_API RE_Regex *RE1_ParseEx(RE_Arena *arena, char *string);
RE_API RE_Regex *RE2_ParseEx(RE_Arena *arena, char *string, RE_Int len);
RE_StaticFunc void *RE_PushSize(RE_Arena *arena, RE_Int size) {
if (arena->len + size > arena->cap) {
RE_ASSERT(!"RE_Regex: Not enough memory passed for this regex");
}
void *result = arena->buff + arena->len;
arena->len += size;
return result;
}
RE_StaticFunc RE_Arena RE_ArenaFromBuffer(char *buff, RE_Int size) {
RE_Arena result;
result.len = 0;
result.cap = size;
result.buff = buff;
return result;
}
RE_StaticFunc RE_String RE_Skip(RE_String string, RE_Int len) {
if (len > string.len) len = string.len;
RE_Int remain = string.len - len;
RE_String result;
result.str = string.str + len;
result.len = remain;
return result;
}
RE_StaticFunc RE_Int RE_StringLength(char *string) {
RE_Int len = 0;
while (*string++ != 0) len++;
return len;
}
RE_StaticFunc RE_Utf32Result RE_ConvertUTF8ToUTF32(char *c, int max_advance) {
RE_Utf32Result result;
RE_MemoryZero(&result, sizeof(result));
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
if (max_advance >= 1) {
result.out_str = c[0];
result.advance = 1;
}
else result.error = 1;
}
else if ((c[0] & 0xe0) == 0xc0) {
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
if (max_advance >= 2) {
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
result.advance = 2;
}
else result.error = 2;
}
else result.error = 2;
}
else if ((c[0] & 0xf0) == 0xe0) {
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
if (max_advance >= 3) {
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
result.advance = 3;
}
else result.error = 3;
}
else result.error = 3;
}
else if ((c[0] & 0xf8) == 0xf0) {
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
if (max_advance >= 4) {
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
result.advance = 4;
}
else result.error = 4;
}
else result.error = 4;
}
else result.error = 4;
return result;
}
#define RE_DLL_QUEUE_REMOVE(first, last, node) \
do { \
if ((first) == (last)) { \
(first) = (last) = 0; \
} \
else if ((last) == (node)) { \
(last) = (last)->prev; \
(last)->next = 0; \
} \
else if ((first) == (node)) { \
(first) = (first)->next; \
(first)->prev = 0; \
} \
else { \
(node)->prev->next = (node)->next; \
(node)->next->prev = (node)->prev; \
} \
if (node) (node)->prev = 0; \
} while (0)
#define RE_DLL_QUEUE_ADD(f, l, node) \
do { \
if ((f) == 0) { \
(f) = (l) = (node); \
(node)->prev = 0; \
(node)->next = 0; \
} \
else { \
(l)->next = (node); \
(node)->prev = (l); \
(node)->next = 0; \
(l) = (node); \
} \
} while (0)
RE_StaticFunc char *RE_GetP(RE_Parser *P) {
if (P->i >= P->string.len) return &RE_NullChar;
return P->string.str + P->i;
}
RE_StaticFunc char RE_Get(RE_Parser *P) {
if (P->i >= P->string.len) return 0;
return P->string.str[P->i];
}
RE_StaticFunc char RE_Get1(RE_Parser *P) {
if ((P->i + 1) >= P->string.len || P->i >= P->string.len) return 0;
return P->string.str[P->i + 1];
}
RE_StaticFunc void RE_Advance(RE_Parser *P) {
if (P->i >= P->string.len) return;
P->i += 1;
}
RE_StaticFunc RE_Regex *RE_ParseSingle(RE_Parser *P, RE_Arena *arena, RE_Regex **first, RE_Regex **last) {
RE_Regex *regex = (RE_Regex *)RE_PushSize(arena, sizeof(RE_Regex));
RE_MemoryZero(regex, sizeof(*regex));
char *c = RE_GetP(P);
RE_Int size_left = P->string.len - P->i;
RE_Advance(P);
switch (*c) {
case ')': RE_STRICT_ASSERT(regex->kind != RE_MATCH_NULL && "Invalid regex syntax, ')' appeared without matching '('"); break;
case '\0': RE_STRICT_ASSERT(regex->kind != RE_MATCH_NULL && "Invalid regex syntax, reached end of string obruptly"); break;
case '.': regex->kind = RE_MATCH_ANY; break;
case '^': regex->kind = RE_MATCH_FRONT; break;
case '$': regex->kind = RE_MATCH_BACK; break;
case '*': {
if (*last) {
regex->kind = RE_MATCH_ZERO_OR_MORE;
RE_Regex *prev = *last;
RE_DLL_QUEUE_REMOVE(*first, *last, *last);
regex->child = prev;
}
else {
RE_STRICT_ASSERT(!"Invalid regex syntax, '*' is not attached to anything");
}
} break;
case '+': {
if (*last) {
regex->kind = RE_MATCH_ONE_OR_MORE;
RE_Regex *prev = *last;
RE_DLL_QUEUE_REMOVE(*first, *last, *last);
regex->child = prev;
}
else {
RE_STRICT_ASSERT(!"Invalid regex syntax, '+' is not attached to anything");
}
} break;
case '?': {
if (*last) {
regex->kind = RE_MATCH_ZERO_OR_ONE;
RE_Regex *prev = *last;
RE_DLL_QUEUE_REMOVE(*first, *last, *last);
regex->child = prev;
}
else {
RE_STRICT_ASSERT(!"Invalid regex syntax, '?' is not attached to anything");
}
} break;
case '[': {
regex->kind = RE_MATCH_SELECTED;
if (RE_Get(P) == '^') {
regex->kind = RE_MATCH_NOT_SELECTED;
RE_Advance(P);
}
while (RE_Get(P) != 0 && RE_Get(P) != ']') {
RE_Regex *r = RE_ParseSingle(P, arena, &regex->group.first, &regex->group.last);
if (r->kind == RE_MATCH_NULL) {
regex->kind = RE_MATCH_NULL;
break;
}
if (r->kind == RE_MATCH_WORD && RE_Get(P) == '-') {
char word = RE_Get1(P);
if (word >= '!' && word <= '~') {
RE_Advance(P);
RE_Regex *right = RE_ParseSingle(P, arena, 0, 0);
if (right->kind == RE_MATCH_NULL) {
regex->kind = RE_MATCH_NULL;
break;
}
RE_ASSERT(right->kind == RE_MATCH_WORD);
RE_ASSERT(right->word == word);
r->word_min = word > r->word ? r->word : word;
r->word_max = word > r->word ? word : r->word;
r->kind = RE_MATCH_RANGE;
}
}
RE_DLL_QUEUE_ADD(regex->group.first, regex->group.last, r);
}
RE_Advance(P);
} break;
case '(': {
regex->kind = RE_MATCH_GROUP;
while (RE_Get(P) != 0 && RE_Get(P) != ')') {
RE_Regex *r = RE_ParseSingle(P, arena, &regex->group.first, &regex->group.last);
if (r->kind == RE_MATCH_NULL) {
regex->kind = RE_MATCH_NULL;
break;
}
RE_DLL_QUEUE_ADD(regex->group.first, regex->group.last, r);
}
RE_Advance(P);
} break;
case '|': {
if (*last) {
regex->kind = RE_MATCH_OR;
RE_Regex *left = *last;
RE_Regex *right = RE_ParseSingle(P, arena, first, last);
if (right->kind == RE_MATCH_NULL) {
regex->kind = RE_MATCH_NULL;
RE_STRICT_ASSERT(!"Invalid regex syntax, '|' appeared but it's right option is invalid");
}
else {
RE_DLL_QUEUE_REMOVE(*first, *last, left);
regex->left = left;
regex->right = right;
}
}
} break;
case '\\': {
regex->kind = RE_MATCH_WORD;
regex->word = RE_Get(P);
switch (regex->word) {
case 'n': regex->word = '\n'; break;
case 't': regex->word = '\t'; break;
case 'r': regex->word = '\r'; break;
case 'w': regex->kind = RE_MATCH_ANY_WORD; break;
case 'd': regex->kind = RE_MATCH_ANY_DIGIT; break;
case 's': regex->kind = RE_MATCH_ANY_WHITESPACE; break;
case '\0': {
regex->kind = RE_MATCH_NULL;
RE_STRICT_ASSERT(!"Invalid regex syntax, escape '\\' followed by end of string");
} break;
}
RE_Advance(P);
} break;
default: {
regex->kind = RE_MATCH_WORD;
RE_Utf32Result decode = RE_ConvertUTF8ToUTF32(c, (int)size_left);
if (decode.error) {
regex->kind = RE_MATCH_NULL;
RE_STRICT_ASSERT(!"Invalid regex syntax, string is an invalid utf8");
}
else {
regex->word32 = decode.out_str;
for (int i = 0; i < decode.advance - 1; i += 1)
RE_Advance(P);
}
}
}
return regex;
}
RE_StaticFunc RE_Int RE_MatchSingle(RE_Regex *regex, RE_String string) {
switch (regex->kind) {
case RE_MATCH_ZERO_OR_MORE: {
RE_Int result = 0;
for (; string.len;) {
// @idea
// In this case (asd)*(asd) we just quit with 0
// when we meet asd
// Maybe this should be collapsed in parsing stage/
// asd should be combined with *asd etc. cause
// now it's a bit weird but I dont know why you would
// type that in the first place
if (RE_MatchSingle(regex->next, string) != -1) break;
RE_Int index = RE_MatchSingle(regex->child, string);
if (index == -1) break;
string = RE_Skip(string, index);
result += index;
}
return result;
} break;
case RE_MATCH_ONE_OR_MORE: {
RE_Int result = 0;
for (; string.len;) {
RE_Int index = RE_MatchSingle(regex->child, string);
if (index == -1) break;
string = RE_Skip(string, index);
result += index;
}
if (result == 0) return -1;
return result;
} break;
case RE_MATCH_OR: {
RE_Int right = RE_MatchSingle(regex->right, string);
RE_Int left = RE_MatchSingle(regex->left, string);
if (left > right) return left;
else return right;
} break;
case RE_MATCH_GROUP: {
RE_Int result = 0;
for (RE_Regex *it = regex->group.first; it; it = it->next) {
if (string.len == 0) return -1;
RE_Int index = RE_MatchSingle(it, string);
if (index == -1) return -1;
result += index;
string = RE_Skip(string, index);
}
return result;
} break;
case RE_MATCH_NOT_SELECTED: {
for (RE_Regex *it = regex->group.first; it; it = it->next) {
RE_Int index = RE_MatchSingle(it, string);
if (index != -1) return -1;
}
RE_Utf32Result decode = RE_ConvertUTF8ToUTF32(string.str, (int)string.len);
if (decode.error) return -1;
return decode.advance;
} break;
case RE_MATCH_SELECTED: {
for (RE_Regex *it = regex->group.first; it; it = it->next) {
RE_Int index = RE_MatchSingle(it, string);
if (index != -1) return index;
}
return -1;
} break;
case RE_MATCH_RANGE: {
if (string.str[0] >= regex->word_min && string.str[0] <= regex->word_max)
return 1;
return -1;
}
case RE_MATCH_ANY_WORD: {
if ((string.str[0] >= 'a' && string.str[0] <= 'z') || (string.str[0] >= 'A' && string.str[0] <= 'Z'))
return 1;
return -1;
} break;
case RE_MATCH_ANY_DIGIT: {
if (string.str[0] >= '0' && string.str[0] <= '9')
return 1;
return -1;
} break;
case RE_MATCH_ANY_WHITESPACE: {
if (string.str[0] == ' ' || string.str[0] == '\n' || string.str[0] == '\t' || string.str[0] == '\r')
return 1;
return -1;
} break;
case RE_MATCH_ANY: {
if (string.str[0] != '\n') {
return 1;
}
return -1;
} break;
case RE_MATCH_ZERO_OR_ONE: {
RE_Int index = RE_MatchSingle(regex->child, string);
if (index == -1) index = 0;
return index;
} break;
case RE_MATCH_WORD: {
RE_Utf32Result decode = RE_ConvertUTF8ToUTF32(string.str, (int)string.len);
if (decode.error) return -1;
if (decode.out_str == regex->word32) return decode.advance;
return -1;
} break;
case RE_MATCH_BACK:
case RE_MATCH_NULL: return -1;
default: RE_ASSERT(!"Invalid codepath");
}
return -1;
}
RE_API bool RE1_AreEqual(char *regex, char *string) {
char buff[4096];
RE_Regex *re = RE1_Parse(buff, sizeof(buff), regex);
bool result = RE3_AreEqual(re, string, RE_StringLength(string));
return result;
}
RE_API bool RE2_AreEqual(RE_Regex *regex, char *string) {
return RE3_AreEqual(regex, string, RE_StringLength(string));
}
RE_API bool RE3_AreEqual(RE_Regex *regex, char *string, RE_Int len) {
RE_Int result = RE3_MatchFront(regex, string, len, string);
return result == len ? true : false;
}
RE_API RE_Match RE1_Find(char *regex, char *string) {
char buff[4096];
RE_Regex *re = RE1_Parse(buff, sizeof(buff), regex);
RE_Match result = RE2_Find(re, string);
return result;
}
RE_API RE_Match RE2_Find(RE_Regex *regex, char *string) {
return RE3_Find(regex, string, RE_StringLength(string));
}
RE_API RE_Match RE3_Find(RE_Regex *regex, char *string, RE_Int len) {
RE_Match result;
for (RE_Int i = 0; i < len; i += 1) {
result.size = RE3_MatchFront(regex, string + i, len - i, string);
if (result.size != -1) {
result.pos = i;
return result;
}
}
result.size = 0;
result.pos = -1;
return result;
}
RE_API RE_Match RE2_FindAgain(RE_Regex *regex, char *string, RE_Match prev_match) {
return RE2_Find(regex, string + prev_match.pos);
}
RE_API RE_Match RE3_FindAgain(RE_Regex *regex, char *string, RE_Int len, RE_Match prev_match) {
return RE3_Find(regex, string + prev_match.pos, len - prev_match.pos);
}
RE_API RE_Int RE3_MatchFront(RE_Regex *regex, char *string, RE_Int len, char *string_front) {
RE_String re_string;
re_string.str = string;
re_string.len = len;
RE_Int submatch_len = 0;
for (RE_Regex *it = regex; it; it = it->next) {
if (it->kind == RE_MATCH_FRONT) {
if (re_string.str == string_front)
continue;
return -1;
}
if (it->kind == RE_MATCH_BACK) {
if (re_string.len == 0)
continue;
return -1;
}
RE_Int index = RE_MatchSingle(it, re_string);
if (index == -1) return -1;
re_string = RE_Skip(re_string, index);
submatch_len += index;
}
return submatch_len;
}
RE_API RE_Regex *RE1_ParseEx(RE_Arena *arena, char *string) {
return RE2_ParseEx(arena, string, RE_StringLength(string));
}
RE_API RE_Regex *RE2_ParseEx(RE_Arena *arena, char *string, RE_Int len) {
RE_Parser P;
RE_MemoryZero(&P, sizeof(P));
P.string.str = string;
P.string.len = len;
for (; P.i < P.string.len;) {
RE_Regex *regex = RE_ParseSingle(&P, arena, &P.first, &P.last);
RE_DLL_QUEUE_ADD(P.first, P.last, regex);
if (regex->kind == RE_MATCH_NULL) {
P.first = &RE_NullRegex;
break;
}
}
return P.first;
}
RE_API RE_Regex *RE1_Parse(char *buff, RE_Int buffsize, char *string) {
RE_Arena arena = RE_ArenaFromBuffer(buff, buffsize);
RE_Regex *result = RE1_ParseEx(&arena, string);
return result;
}
RE_API RE_Regex *RE2_Parse(char *buff, RE_Int buffsize, char *string, RE_Int len) {
RE_Arena arena = RE_ArenaFromBuffer(buff, buffsize);
RE_Regex *result = RE2_ParseEx(&arena, string, len);
return result;
}

View File

@@ -0,0 +1,95 @@
#ifndef FIRST_REGEX_HEADER
#define FIRST_REGEX_HEADER
#include <stdint.h>
#include <stdbool.h>
#ifndef RE_Int
#define RE_Int int64_t
#endif
#ifndef RE_API
#ifdef __cplusplus
#define RE_API extern "C"
#else
#define RE_API
#endif
#endif
#ifndef RE_StaticFunc
#if defined(__GNUC__) || defined(__clang__)
#define RE_StaticFunc __attribute__((unused)) static
#else
#define RE_StaticFunc static
#endif
#endif
typedef struct RE_String RE_String;
typedef struct RE_Utf32Result RE_Utf32Result;
typedef struct RE_Parser RE_Parser;
typedef struct RE_Regex RE_Regex;
typedef struct RE_Match RE_Match;
/* @todo
Add \W \D \S oppsites
*/
typedef enum RE_MatchKind {
RE_MATCH_NULL,
RE_MATCH_FRONT,
RE_MATCH_BACK,
RE_MATCH_WORD,
RE_MATCH_OR,
RE_MATCH_GROUP,
RE_MATCH_SELECTED,
RE_MATCH_NOT_SELECTED,
RE_MATCH_RANGE,
RE_MATCH_ANY,
RE_MATCH_ANY_WORD,
RE_MATCH_ANY_DIGIT,
RE_MATCH_ANY_WHITESPACE,
RE_MATCH_ONE_OR_MORE,
RE_MATCH_ZERO_OR_MORE,
RE_MATCH_ZERO_OR_ONE,
} RE_MatchKind;
struct RE_Regex {
RE_MatchKind kind;
RE_Regex *next;
RE_Regex *prev;
union {
struct {
char word_min;
char word_max;
};
char word;
uint32_t word32;
RE_Regex *child;
struct {
RE_Regex *left;
RE_Regex *right;
};
struct {
RE_Regex *first;
RE_Regex *last;
} group;
};
};
struct RE_Match {
RE_Int pos;
RE_Int size;
};
RE_API bool RE1_AreEqual(char *regex, char *string);
RE_API bool RE2_AreEqual(RE_Regex *regex, char *string);
RE_API bool RE3_AreEqual(RE_Regex *regex, char *string, RE_Int len);
RE_API RE_Match RE1_Find(char *regex, char *string);
RE_API RE_Match RE2_Find(RE_Regex *regex, char *string);
RE_API RE_Match RE3_Find(RE_Regex *regex, char *string, RE_Int len);
RE_API RE_Match RE2_FindAgain(RE_Regex *regex, char *string, RE_Match prev_match);
RE_API RE_Match RE3_FindAgain(RE_Regex *regex, char *string, RE_Int len, RE_Match prev_match);
RE_API RE_Int RE3_MatchFront(RE_Regex *regex, char *string, RE_Int len, char *string_front);
RE_API RE_Regex *RE1_Parse(char *buff, RE_Int buffsize, char *string);
RE_API RE_Regex *RE2_Parse(char *buff, RE_Int buffsize, char *string, RE_Int len);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,262 @@
// stb_sprintf - v1.10 - public domain snprintf() implementation
// originally by Jeff Roberts / RAD Game Tools, 2015/10/20
// http://github.com/nothings/stb
//
// allowed types: sc uidBboXx p AaGgEef n
// lengths : hh h ll j z t I64 I32 I
//
// Contributors:
// Fabian "ryg" Giesen (reformatting)
// github:aganm (attribute format)
//
// Contributors (bugfixes):
// github:d26435
// github:trex78
// github:account-login
// Jari Komppa (SI suffixes)
// Rohit Nirmal
// Marcin Wojdyr
// Leonard Ritter
// Stefano Zanotti
// Adam Allison
// Arvid Gerstmann
// Markus Kolb
//
// LICENSE:
//
// See end of file for license information.
#ifndef STB_SPRINTF_H_INCLUDE
#define STB_SPRINTF_H_INCLUDE
#include <stdint.h>
/*
Single file sprintf replacement.
Originally written by Jeff Roberts at RAD Game Tools - 2015/10/20.
Hereby placed in public domain.
This is a full sprintf replacement that supports everything that
the C runtime sprintfs support, including float/double, 64-bit integers,
hex floats, field parameters (%*.*d stuff), length reads backs, etc.
Why would you need this if sprintf already exists? Well, first off,
it's *much* faster (see below). It's also much smaller than the CRT
versions code-space-wise. We've also added some simple improvements
that are super handy (commas in thousands, callbacks at buffer full,
for example). Finally, the format strings for MSVC and GCC differ
for 64-bit integers (among other small things), so this lets you use
the same format strings in cross platform code.
It uses the standard single file trick of being both the header file
and the source itself. If you just include it normally, you just get
the header file function definitions. To get the code, you include
it from a C or C++ file and define STB_SPRINTF_IMPLEMENTATION first.
It only uses va_args macros from the C runtime to do it's work. It
does cast doubles to S64s and shifts and divides U64s, which does
drag in CRT code on most platforms.
It compiles to roughly 8K with float support, and 4K without.
As a comparison, when using MSVC static libs, calling sprintf drags
in 16K.
API:
====
int stbsp_sprintf( char * buf, char const * fmt, ... )
int stbsp_snprintf( char * buf, int count, char const * fmt, ... )
Convert an arg list into a buffer. stbsp_snprintf always returns
a zero-terminated string (unlike regular snprintf).
int stbsp_vsprintf( char * buf, char const * fmt, va_list va )
int stbsp_vsnprintf( char * buf, int count, char const * fmt, va_list va )
Convert a va_list arg list into a buffer. stbsp_vsnprintf always returns
a zero-terminated string (unlike regular snprintf).
int stbsp_vsprintfcb( STBSP_SPRINTFCB * callback, void * user, char * buf, char const * fmt, va_list va )
typedef char * STBSP_SPRINTFCB( char const * buf, void * user, int len );
Convert into a buffer, calling back every STB_SPRINTF_MIN chars.
Your callback can then copy the chars out, print them or whatever.
This function is actually the workhorse for everything else.
The buffer you pass in must hold at least STB_SPRINTF_MIN characters.
// you return the next buffer to use or 0 to stop converting
void stbsp_set_separators( char comma, char period )
Set the comma and period characters to use.
FLOATS/DOUBLES:
===============
This code uses a internal float->ascii conversion method that uses
doubles with error correction (double-doubles, for ~105 bits of
precision). This conversion is round-trip perfect - that is, an atof
of the values output here will give you the bit-exact double back.
One difference is that our insignificant digits will be different than
with MSVC or GCC (but they don't match each other either). We also
don't attempt to find the minimum length matching float (pre-MSVC15
doesn't either).
If you don't need float or doubles at all, define STB_SPRINTF_NOFLOAT
and you'll save 4K of code space.
64-BIT INTS:
============
This library also supports 64-bit integers and you can use MSVC style or
GCC style indicators (%I64d or %lld). It supports the C99 specifiers
for size_t and ptr_diff_t (%jd %zd) as well.
EXTRAS:
=======
Like some GCCs, for integers and floats, you can use a ' (single quote)
specifier and commas will be inserted on the thousands: "%'d" on 12345
would print 12,345.
For integers and floats, you can use a "$" specifier and the number
will be converted to float and then divided to get kilo, mega, giga or
tera and then printed, so "%$d" 1000 is "1.0 k", "%$.2d" 2536000 is
"2.53 M", etc. For byte values, use two $:s, like "%$$d" to turn
2536000 to "2.42 Mi". If you prefer JEDEC suffixes to SI ones, use three
$:s: "%$$$d" -> "2.42 M". To remove the space between the number and the
suffix, add "_" specifier: "%_$d" -> "2.53M".
In addition to octal and hexadecimal conversions, you can print
integers in binary: "%b" for 256 would print 100.
PERFORMANCE vs MSVC 2008 32-/64-bit (GCC is even slower than MSVC):
===================================================================
"%d" across all 32-bit ints (4.8x/4.0x faster than 32-/64-bit MSVC)
"%24d" across all 32-bit ints (4.5x/4.2x faster)
"%x" across all 32-bit ints (4.5x/3.8x faster)
"%08x" across all 32-bit ints (4.3x/3.8x faster)
"%f" across e-10 to e+10 floats (7.3x/6.0x faster)
"%e" across e-10 to e+10 floats (8.1x/6.0x faster)
"%g" across e-10 to e+10 floats (10.0x/7.1x faster)
"%f" for values near e-300 (7.9x/6.5x faster)
"%f" for values near e+300 (10.0x/9.1x faster)
"%e" for values near e-300 (10.1x/7.0x faster)
"%e" for values near e+300 (9.2x/6.0x faster)
"%.320f" for values near e-300 (12.6x/11.2x faster)
"%a" for random values (8.6x/4.3x faster)
"%I64d" for 64-bits with 32-bit values (4.8x/3.4x faster)
"%I64d" for 64-bits > 32-bit values (4.9x/5.5x faster)
"%s%s%s" for 64 char strings (7.1x/7.3x faster)
"...512 char string..." ( 35.0x/32.5x faster!)
*/
#if defined(__clang__)
#if defined(__has_feature) && defined(__has_attribute)
#if __has_feature(address_sanitizer)
#if __has_attribute(__no_sanitize__)
#define STBSP__ASAN __attribute__((__no_sanitize__("address")))
#elif __has_attribute(__no_sanitize_address__)
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
#elif __has_attribute(__no_address_safety_analysis__)
#define STBSP__ASAN __attribute__((__no_address_safety_analysis__))
#endif
#endif
#endif
#elif defined(__GNUC__) && (__GNUC__ >= 5 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
#if defined(__SANITIZE_ADDRESS__) && __SANITIZE_ADDRESS__
#define STBSP__ASAN __attribute__((__no_sanitize_address__))
#endif
#elif defined(_MSC_VER)
#ifdef __SANITIZE_ADDRESS__
#define STBSP__ASAN __declspec(no_sanitize_address)
#endif
#endif
#ifndef STBSP__ASAN
#define STBSP__ASAN
#endif
#ifdef STB_SPRINTF_STATIC
#define STBSP__PUBLICDEC static
#define STBSP__PUBLICDEF static STBSP__ASAN
#else
#ifdef __cplusplus
#define STBSP__PUBLICDEC extern "C"
#define STBSP__PUBLICDEF extern "C" STBSP__ASAN
#else
#define STBSP__PUBLICDEC extern
#define STBSP__PUBLICDEF STBSP__ASAN
#endif
#endif
#if defined(__has_attribute)
#if __has_attribute(format)
#define STBSP__ATTRIBUTE_FORMAT(fmt, va) __attribute__((format(printf, fmt, va)))
#endif
#endif
#ifndef STBSP__ATTRIBUTE_FORMAT
#define STBSP__ATTRIBUTE_FORMAT(fmt, va)
#endif
#ifdef _MSC_VER
#define STBSP__NOTUSED(v) (void)(v)
#else
#define STBSP__NOTUSED(v) (void)sizeof(v)
#endif
#include <stdarg.h> // for va_arg(), va_list()
#include <stddef.h> // size_t, ptrdiff_t
#ifndef STB_SPRINTF_MIN
#define STB_SPRINTF_MIN 512 // how many characters per callback
#endif
typedef char *STBSP_SPRINTFCB(const char *buf, void *user, int len);
#ifndef STB_SPRINTF_DECORATE
#define STB_SPRINTF_DECORATE(name) stbsp_##name // define this before including if you want to change the names
#endif
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintf)(char *buf, char const *fmt, va_list va);
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsnprintf)(char *buf, int count, char const *fmt, va_list va);
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(sprintf)(char *buf, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(2, 3);
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(snprintf)(char *buf, int count, char const *fmt, ...) STBSP__ATTRIBUTE_FORMAT(3, 4);
STBSP__PUBLICDEC int STB_SPRINTF_DECORATE(vsprintfcb)(STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va);
STBSP__PUBLICDEC void STB_SPRINTF_DECORATE(set_separators)(char comma, char period);
#endif // STB_SPRINTF_H_INCLUDE
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
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.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
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 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.
------------------------------------------------------------------------------
*/

View File

@@ -0,0 +1,566 @@
#include "string.h"
#include <stdarg.h>
#ifndef S8_VSNPRINTF
#include <stdio.h>
#define S8_VSNPRINTF vsnprintf
#endif
#ifndef S8_ALLOCATE
#include <stdlib.h>
#define S8_ALLOCATE(allocator, size) malloc(size)
#endif
#ifndef S8_ASSERT
#include <assert.h>
#define S8_ASSERT(x) assert(x)
#endif
#ifndef S8_MemoryCopy
#include <string.h>
#define S8_MemoryCopy(dst, src, s) memcpy(dst, src, s)
#endif
#ifndef S8_StaticFunc
#if defined(__GNUC__) || defined(__clang__)
#define S8_StaticFunc __attribute__((unused)) static
#else
#define S8_StaticFunc static
#endif
#endif
S8_StaticFunc int64_t S8__ClampTop(int64_t val, int64_t max) {
if (val > max) val = max;
return val;
}
S8_API char CHAR_ToLowerCase(char a) {
if (a >= 'A' && a <= 'Z') a += 32;
return a;
}
S8_API char CHAR_ToUpperCase(char a) {
if (a >= 'a' && a <= 'z') a -= 32;
return a;
}
S8_API bool CHAR_IsWhitespace(char w) {
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
return result;
}
S8_API bool CHAR_IsAlphabetic(char a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
return result;
}
S8_API bool CHAR_IsIdent(char a) {
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
return result;
}
S8_API bool CHAR_IsDigit(char a) {
bool result = a >= '0' && a <= '9';
return result;
}
S8_API bool CHAR_IsAlphanumeric(char a) {
bool result = CHAR_IsDigit(a) || CHAR_IsAlphabetic(a);
return result;
}
S8_API bool S8_AreEqual(S8_String a, S8_String b, unsigned ignore_case) {
if (a.len != b.len) return false;
for (int64_t i = 0; i < a.len; i++) {
char A = a.str[i];
char B = b.str[i];
if (ignore_case) {
A = CHAR_ToLowerCase(A);
B = CHAR_ToLowerCase(B);
}
if (A != B)
return false;
}
return true;
}
S8_API bool S8_EndsWith(S8_String a, S8_String end, unsigned ignore_case) {
S8_String a_end = S8_GetPostfix(a, end.len);
bool result = S8_AreEqual(end, a_end, ignore_case);
return result;
}
S8_API bool S8_StartsWith(S8_String a, S8_String start, unsigned ignore_case) {
S8_String a_start = S8_GetPrefix(a, start.len);
bool result = S8_AreEqual(start, a_start, ignore_case);
return result;
}
S8_API S8_String S8_Make(char *str, int64_t len) {
S8_String result;
result.str = (char *)str;
result.len = len;
return result;
}
S8_API S8_String S8_Copy(S8_Allocator allocator, S8_String string) {
char *copy = (char *)S8_ALLOCATE(allocator, sizeof(char) * (string.len + 1));
S8_MemoryCopy(copy, string.str, string.len);
copy[string.len] = 0;
S8_String result = S8_Make(copy, string.len);
return result;
}
S8_API S8_String S8_CopyChar(S8_Allocator allocator, char *s) {
int64_t len = S8_Length(s);
char *copy = (char *)S8_ALLOCATE(allocator, sizeof(char) * (len + 1));
S8_MemoryCopy(copy, s, len);
copy[len] = 0;
S8_String result = S8_Make(copy, len);
return result;
}
S8_API S8_String S8_NormalizePath(S8_Allocator allocator, S8_String s) {
S8_String copy = S8_Copy(allocator, s);
for (int64_t i = 0; i < copy.len; i++) {
if (copy.str[i] == '\\')
copy.str[i] = '/';
}
return copy;
}
S8_API void S8_NormalizePathUnsafe(S8_String s) {
for (int64_t i = 0; i < s.len; i++) {
if (s.str[i] == '\\')
s.str[i] = '/';
}
}
S8_API S8_String S8_Chop(S8_String string, int64_t len) {
len = S8__ClampTop(len, string.len);
S8_String result = S8_Make(string.str, string.len - len);
return result;
}
S8_API S8_String S8_Skip(S8_String string, int64_t len) {
len = S8__ClampTop(len, string.len);
int64_t remain = string.len - len;
S8_String result = S8_Make(string.str + len, remain);
return result;
}
S8_API bool S8_IsPointerInside(S8_String string, char *p) {
uintptr_t pointer = (uintptr_t)p;
uintptr_t start = (uintptr_t)string.str;
uintptr_t stop = start + (uintptr_t)string.len;
bool result = pointer >= start && pointer < stop;
return result;
}
S8_API S8_String S8_SkipToP(S8_String string, char *p) {
if (S8_IsPointerInside(string, p)) {
S8_String result = S8_Make(p, p - string.str);
return result;
}
return string;
}
S8_API S8_String S8_SkipPast(S8_String string, S8_String a) {
if (S8_IsPointerInside(string, a.str)) {
S8_String on_p = S8_Make(a.str, a.str - string.str);
S8_String result = S8_Skip(on_p, a.len);
return result;
}
return string;
}
S8_API S8_String S8_GetPostfix(S8_String string, int64_t len) {
len = S8__ClampTop(len, string.len);
int64_t remain_len = string.len - len;
S8_String result = S8_Make(string.str + remain_len, len);
return result;
}
S8_API S8_String S8_GetPrefix(S8_String string, int64_t len) {
len = S8__ClampTop(len, string.len);
S8_String result = S8_Make(string.str, len);
return result;
}
S8_API S8_String S8_GetNameNoExt(S8_String s) {
return S8_SkipToLastSlash(S8_ChopLastPeriod(s));
}
S8_API S8_String S8_Slice(S8_String string, int64_t first_index, int64_t one_past_last_index) {
if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1;
if (first_index < 0) first_index = string.len + first_index;
S8_ASSERT(first_index < one_past_last_index && "S8_Slice, first_index is bigger then one_past_last_index");
S8_ASSERT(string.len > 0 && "Slicing string of length 0! Might be an error!");
S8_String result = string;
if (string.len > 0) {
if (one_past_last_index > first_index) {
first_index = S8__ClampTop(first_index, string.len - 1);
one_past_last_index = S8__ClampTop(one_past_last_index, string.len);
result.str += first_index;
result.len = one_past_last_index - first_index;
}
else {
result.len = 0;
}
}
return result;
}
S8_API S8_String S8_Trim(S8_String string) {
if (string.len == 0)
return string;
int64_t whitespace_begin = 0;
for (; whitespace_begin < string.len; whitespace_begin++) {
if (!CHAR_IsWhitespace(string.str[whitespace_begin])) {
break;
}
}
int64_t whitespace_end = string.len;
for (; whitespace_end != whitespace_begin; whitespace_end--) {
if (!CHAR_IsWhitespace(string.str[whitespace_end - 1])) {
break;
}
}
if (whitespace_begin == whitespace_end) {
string.len = 0;
}
else {
string = S8_Slice(string, whitespace_begin, whitespace_end);
}
return string;
}
S8_API S8_String S8_TrimEnd(S8_String string) {
int64_t whitespace_end = string.len;
for (; whitespace_end != 0; whitespace_end--) {
if (!CHAR_IsWhitespace(string.str[whitespace_end - 1])) {
break;
}
}
S8_String result = S8_GetPrefix(string, whitespace_end);
return result;
}
S8_API S8_String S8_ToLowerCase(S8_Allocator allocator, S8_String s) {
S8_String copy = S8_Copy(allocator, s);
for (int64_t i = 0; i < copy.len; i++) {
copy.str[i] = CHAR_ToLowerCase(copy.str[i]);
}
return copy;
}
S8_API S8_String S8_ToUpperCase(S8_Allocator allocator, S8_String s) {
S8_String copy = S8_Copy(allocator, s);
for (int64_t i = 0; i < copy.len; i++) {
copy.str[i] = CHAR_ToUpperCase(copy.str[i]);
}
return copy;
}
S8_API bool S8_Seek(S8_String string, S8_String find, S8_FindFlag flags, int64_t *index_out) {
bool ignore_case = flags & S8_FindFlag_IgnoreCase ? true : false;
bool result = false;
if (flags & S8_FindFlag_MatchFindLast) {
for (int64_t i = string.len; i != 0; i--) {
int64_t index = i - 1;
S8_String substring = S8_Slice(string, index, index + find.len);
if (S8_AreEqual(substring, find, ignore_case)) {
if (index_out)
*index_out = index;
result = true;
break;
}
}
}
else {
for (int64_t i = 0; i < string.len; i++) {
S8_String substring = S8_Slice(string, i, i + find.len);
if (S8_AreEqual(substring, find, ignore_case)) {
if (index_out)
*index_out = i;
result = true;
break;
}
}
}
return result;
}
S8_API int64_t S8_Find(S8_String string, S8_String find, S8_FindFlag flag) {
int64_t result = -1;
S8_Seek(string, find, flag, &result);
return result;
}
S8_API S8_List S8_Split(S8_Allocator allocator, S8_String string, S8_String find, S8_SplitFlag flags) {
S8_List result = S8_MakeEmptyList();
int64_t index = 0;
S8_FindFlag find_flag = flags & S8_SplitFlag_IgnoreCase ? S8_FindFlag_IgnoreCase : S8_FindFlag_None;
while (S8_Seek(string, find, find_flag, &index)) {
S8_String before_match = S8_Make(string.str, index);
S8_AddNode(allocator, &result, before_match);
if (flags & S8_SplitFlag_SplitInclusive) {
S8_String match = S8_Make(string.str + index, find.len);
S8_AddNode(allocator, &result, match);
}
string = S8_Skip(string, index + find.len);
}
if (string.len) S8_AddNode(allocator, &result, string);
return result;
}
S8_API S8_String S8_MergeWithSeparator(S8_Allocator allocator, S8_List list, S8_String separator) {
if (list.node_count == 0) return S8_MakeEmpty();
if (list.char_count == 0) return S8_MakeEmpty();
int64_t base_size = (list.char_count + 1);
int64_t sep_size = (list.node_count - 1) * separator.len;
int64_t size = base_size + sep_size;
char *buff = (char *)S8_ALLOCATE(allocator, sizeof(char) * (size + 1));
S8_String string = S8_Make(buff, 0);
for (S8_Node *it = list.first; it; it = it->next) {
S8_ASSERT(string.len + it->string.len <= size);
S8_MemoryCopy(string.str + string.len, it->string.str, it->string.len);
string.len += it->string.len;
if (it != list.last) {
S8_MemoryCopy(string.str + string.len, separator.str, separator.len);
string.len += separator.len;
}
}
S8_ASSERT(string.len == size - 1);
string.str[size] = 0;
return string;
}
S8_API S8_String S8_Merge(S8_Allocator allocator, S8_List list) {
return S8_MergeWithSeparator(allocator, list, S8_Lit(""));
}
S8_API S8_String S8_ReplaceAll(S8_Allocator allocator, S8_String string, S8_String replace, S8_String with, bool ignore_case) {
S8_SplitFlag split_flag = ignore_case ? S8_SplitFlag_IgnoreCase : S8_SplitFlag_None;
S8_List list = S8_Split(allocator, string, replace, split_flag | S8_SplitFlag_SplitInclusive);
for (S8_Node *it = list.first; it; it = it->next) {
if (S8_AreEqual(it->string, replace, ignore_case)) {
S8_ReplaceNodeString(&list, it, with);
}
}
S8_String result = S8_Merge(allocator, list);
return result;
}
S8_API S8_List S8_FindAll(S8_Allocator allocator, S8_String string, S8_String find, bool ignore_case) { // @untested
S8_List result = S8_MakeEmptyList();
int64_t index = 0;
S8_FindFlag find_flag = ignore_case ? S8_FindFlag_IgnoreCase : 0;
while (S8_Seek(string, find, find_flag, &index)) {
S8_String match = S8_Make(string.str + index, find.len);
S8_AddNode(allocator, &result, match);
string = S8_Skip(string, index + find.len);
}
return result;
}
S8_API S8_String S8_ChopLastSlash(S8_String s) {
S8_String result = s;
S8_Seek(s, S8_Lit("/"), S8_FindFlag_MatchFindLast, &result.len);
return result;
}
S8_API S8_String S8_ChopLastPeriod(S8_String s) {
S8_String result = s;
S8_Seek(s, S8_Lit("."), S8_FindFlag_MatchFindLast, &result.len);
return result;
}
S8_API S8_String S8_SkipToLastSlash(S8_String s) {
int64_t pos;
S8_String result = s;
if (S8_Seek(s, S8_Lit("/"), S8_FindFlag_MatchFindLast, &pos)) {
result = S8_Skip(result, pos + 1);
}
return result;
}
S8_API S8_String S8_SkipToLastPeriod(S8_String s) {
int64_t pos;
S8_String result = s;
if (S8_Seek(s, S8_Lit("."), S8_FindFlag_MatchFindLast, &pos)) {
result = S8_Skip(result, pos + 1);
}
return result;
}
S8_API int64_t S8_Length(char *string) {
int64_t len = 0;
while (*string++ != 0)
len++;
return len;
}
S8_API int64_t S8_WideLength(wchar_t *string) {
int64_t len = 0;
while (*string++ != 0)
len++;
return len;
}
S8_API S8_String S8_MakeFromChar(char *string) {
S8_String result;
result.str = (char *)string;
result.len = S8_Length(string);
return result;
}
S8_API S8_String S8_MakeEmpty(void) {
return S8_Make(0, 0);
}
S8_API S8_List S8_MakeEmptyList(void) {
S8_List result;
result.first = 0;
result.last = 0;
result.char_count = 0;
result.node_count = 0;
return result;
}
S8_API S8_String S8_FormatV(S8_Allocator allocator, const char *str, va_list args1) {
va_list args2;
va_copy(args2, args1);
int64_t len = S8_VSNPRINTF(0, 0, str, args2);
va_end(args2);
char *result = (char *)S8_ALLOCATE(allocator, sizeof(char) * (len + 1));
S8_VSNPRINTF(result, (int)(len + 1), str, args1);
S8_String res = S8_Make(result, len);
return res;
}
S8_API S8_String S8_Format(S8_Allocator allocator, const char *str, ...) {
S8_FORMAT(allocator, str, result);
return result;
}
S8_API S8_Node *S8_CreateNode(S8_Allocator allocator, S8_String string) {
S8_Node *result = (S8_Node *)S8_ALLOCATE(allocator, sizeof(S8_Node));
result->string = string;
result->next = 0;
return result;
}
S8_API void S8_ReplaceNodeString(S8_List *list, S8_Node *node, S8_String new_string) {
list->char_count -= node->string.len;
list->char_count += new_string.len;
node->string = new_string;
}
S8_API void S8_AddExistingNode(S8_List *list, S8_Node *node) {
if (list->first) {
list->last->next = node;
list->last = list->last->next;
}
else {
list->first = list->last = node;
}
list->node_count += 1;
list->char_count += node->string.len;
}
S8_API void S8_AddArray(S8_Allocator allocator, S8_List *list, char **array, int count) {
for (int i = 0; i < count; i += 1) {
S8_String s = S8_MakeFromChar(array[i]);
S8_AddNode(allocator, list, s);
}
}
S8_API void S8_AddArrayWithPrefix(S8_Allocator allocator, S8_List *list, char *prefix, char **array, int count) {
for (int i = 0; i < count; i += 1) {
S8_AddF(allocator, list, "%s%s", prefix, array[i]);
}
}
S8_API S8_List S8_MakeList(S8_Allocator allocator, S8_String a) {
S8_List result = S8_MakeEmptyList();
S8_AddNode(allocator, &result, a);
return result;
}
S8_API S8_List S8_CopyList(S8_Allocator allocator, S8_List a) {
S8_List result = S8_MakeEmptyList();
for (S8_Node *it = a.first; it; it = it->next) S8_AddNode(allocator, &result, it->string);
return result;
}
S8_API S8_List S8_ConcatLists(S8_Allocator allocator, S8_List a, S8_List b) {
S8_List result = S8_MakeEmptyList();
for (S8_Node *it = a.first; it; it = it->next) S8_AddNode(allocator, &result, it->string);
for (S8_Node *it = b.first; it; it = it->next) S8_AddNode(allocator, &result, it->string);
return result;
}
S8_API S8_Node *S8_AddNode(S8_Allocator allocator, S8_List *list, S8_String string) {
S8_Node *node = S8_CreateNode(allocator, string);
S8_AddExistingNode(list, node);
return node;
}
S8_API S8_Node *S8_Add(S8_Allocator allocator, S8_List *list, S8_String string) {
S8_String copy = S8_Copy(allocator, string);
S8_Node *node = S8_CreateNode(allocator, copy);
S8_AddExistingNode(list, node);
return node;
}
S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, ...) {
S8_FORMAT(allocator, str, result);
S8_AddNode(allocator, list, result);
return result;
}
#ifdef FIRST_UTF_HEADER
S8_API S16_String S8_ToWidecharEx(S8_Allocator allocator, S8_String string) {
S8_ASSERT(sizeof(wchar_t) == 2);
wchar_t *buffer = (wchar_t *)S8_ALLOCATE(allocator, sizeof(wchar_t) * (string.len + 1));
int64_t size = UTF_CreateWidecharFromChar(buffer, string.len + 1, string.str, string.len);
S16_String result = {buffer, size};
return result;
}
S8_API wchar_t *S8_ToWidechar(S8_Allocator allocator, S8_String string) {
S16_String result = S8_ToWidecharEx(allocator, string);
return result.str;
}
S8_API S8_String S8_FromWidecharEx(S8_Allocator allocator, wchar_t *wstring, int64_t wsize) {
S8_ASSERT(sizeof(wchar_t) == 2);
int64_t buffer_size = (wsize + 1) * 2;
char *buffer = (char *)S8_ALLOCATE(allocator, buffer_size);
int64_t size = UTF_CreateCharFromWidechar(buffer, buffer_size, wstring, wsize);
S8_String result = S8_Make(buffer, size);
S8_ASSERT(size < buffer_size);
return result;
}
S8_API S8_String S8_FromWidechar(S8_Allocator allocator, wchar_t *wstring) {
int64_t size = S8_WideLength(wstring);
S8_String result = S8_FromWidecharEx(allocator, wstring, size);
return result;
}
#endif

View File

@@ -0,0 +1,197 @@
#ifndef FIRST_S8_STRING
#define FIRST_S8_STRING
#include <stdint.h>
#include <stdbool.h>
#include <stdarg.h>
#ifndef S8_API
#define S8_API
#endif
#ifdef __cplusplus
#define S8_IF_CPP(x) x
#else
#define S8_IF_CPP(x)
#endif
#ifndef S8_Allocator
struct MA_Arena;
#define S8_Allocator MA_Arena *
#endif
typedef struct S8_String S8_String;
typedef struct S8_Node S8_Node;
typedef struct S8_List S8_List;
S8_API int64_t S8_Length(char *string);
struct S8_String {
char *str;
int64_t len;
#if defined(__cplusplus)
S8_String() = default;
S8_String(char *s) : str(s), len(S8_Length(s)) {}
S8_String(char *s, int64_t l) : str(s), len(l) {}
S8_String(const char *s) : str((char *)s), len(S8_Length((char *)s)) {}
S8_String(const char *s, int64_t l) : str((char *)s), len(l) {}
#if defined(FIRST_UTF_HEADER)
struct Iter {
UTF8_Iter i;
Iter &operator++() {
UTF8_Advance(&i);
return *this;
}
friend bool operator!=(const Iter &a, const Iter &b) { return a.i.item != b.i.item; }
UTF8_Iter &operator*() { return i; }
};
Iter begin() { return {UTF8_IterateEx(str, (int)len)}; }
Iter end() { return {}; }
#endif // FIRST_UTF_HEADER
#endif // __cplusplus
};
struct S8_Node {
S8_Node *next;
S8_String string;
};
struct S8_List {
int64_t node_count;
int64_t char_count;
S8_Node *first;
S8_Node *last;
#if defined(__cplusplus)
struct Iter {
S8_Node *it;
Iter &operator++() {
it = it->next;
return *this;
}
friend bool operator!=(const Iter &a, const Iter &b) { return a.it != b.it; }
S8_String &operator*() { return it->string; }
};
Iter begin() { return {first}; }
Iter end() { return {0}; }
#endif
};
typedef struct S16_String {
wchar_t *str;
int64_t len;
} S16_String;
typedef int S8_FindFlag;
enum {
S8_FindFlag_None = 0,
S8_FindFlag_IgnoreCase = 1,
S8_FindFlag_MatchFindLast = 2,
};
typedef int S8_SplitFlag;
enum {
S8_SplitFlag_None = 0,
S8_SplitFlag_IgnoreCase = 1,
S8_SplitFlag_SplitInclusive = 2,
};
static const bool S8_IgnoreCase = true;
#if defined(__has_attribute)
#if __has_attribute(format)
#define S8__PrintfFormat(fmt, va) __attribute__((format(printf, fmt, va)))
#endif
#endif
#ifndef S8__PrintfFormat
#define S8__PrintfFormat(fmt, va)
#endif
#define S8_Lit(string) S8_Make((char *)string, sizeof(string) - 1)
#define S8_ConstLit(string) \
{ string, sizeof(string) - 1 }
#define S8_Expand(string) (int)(string).len, (string).str
#define S8_FORMAT(allocator, str, result) \
va_list args1; \
va_start(args1, str); \
S8_String result = S8_FormatV(allocator, str, args1); \
va_end(args1)
#define S8_For(it, x) for (S8_Node *it = (x).first; it; it = it->next)
S8_API char CHAR_ToLowerCase(char a);
S8_API char CHAR_ToUpperCase(char a);
S8_API bool CHAR_IsWhitespace(char w);
S8_API bool CHAR_IsAlphabetic(char a);
S8_API bool CHAR_IsIdent(char a);
S8_API bool CHAR_IsDigit(char a);
S8_API bool CHAR_IsAlphanumeric(char a);
S8_API bool S8_AreEqual(S8_String a, S8_String b, unsigned ignore_case S8_IF_CPP(= false));
S8_API bool S8_EndsWith(S8_String a, S8_String end, unsigned ignore_case S8_IF_CPP(= false));
S8_API bool S8_StartsWith(S8_String a, S8_String start, unsigned ignore_case S8_IF_CPP(= false));
S8_API S8_String S8_Make(char *str, int64_t len);
S8_API S8_String S8_Copy(S8_Allocator allocator, S8_String string);
S8_API S8_String S8_CopyChar(S8_Allocator allocator, char *s);
S8_API S8_String S8_NormalizePath(S8_Allocator allocator, S8_String s);
S8_API void S8_NormalizePathUnsafe(S8_String s); // make sure there is no way string is const etc.
S8_API S8_String S8_Chop(S8_String string, int64_t len);
S8_API S8_String S8_Skip(S8_String string, int64_t len);
S8_API S8_String S8_GetPostfix(S8_String string, int64_t len);
S8_API S8_String S8_GetPrefix(S8_String string, int64_t len);
S8_API S8_String S8_Slice(S8_String string, int64_t first_index, int64_t one_past_last_index);
S8_API S8_String S8_Trim(S8_String string);
S8_API S8_String S8_TrimEnd(S8_String string);
S8_API S8_String S8_ToLowerCase(S8_Allocator allocator, S8_String s);
S8_API S8_String S8_ToUpperCase(S8_Allocator allocator, S8_String s);
S8_API bool S8_Seek(S8_String string, S8_String find, S8_FindFlag flags S8_IF_CPP(= S8_FindFlag_None), int64_t *index_out S8_IF_CPP(= 0));
S8_API int64_t S8_Find(S8_String string, S8_String find, S8_FindFlag flags S8_IF_CPP(= S8_FindFlag_None));
S8_API S8_String S8_ChopLastSlash(S8_String s);
S8_API S8_String S8_ChopLastPeriod(S8_String s);
S8_API S8_String S8_SkipToLastSlash(S8_String s);
S8_API S8_String S8_SkipToLastPeriod(S8_String s);
S8_API S8_String S8_GetNameNoExt(S8_String s);
S8_API bool S8_IsPointerInside(S8_String string, char *p);
S8_API S8_String S8_SkipToP(S8_String string, char *p);
S8_API S8_String S8_SkipPast(S8_String string, S8_String a);
S8_API int64_t S8_WideLength(wchar_t *string);
S8_API S8_String S8_MakeFromChar(char *string);
S8_API S8_String S8_MakeEmpty(void);
S8_API S8_List S8_MakeEmptyList(void);
S8_API S8_String S8_FormatV(S8_Allocator allocator, const char *str, va_list args1);
S8_API S8_String S8_Format(S8_Allocator allocator, const char *str, ...) S8__PrintfFormat(2, 3);
S8_API S8_List S8_Split(S8_Allocator allocator, S8_String string, S8_String find, S8_SplitFlag flags S8_IF_CPP(= S8_SplitFlag_None));
S8_API S8_String S8_MergeWithSeparator(S8_Allocator allocator, S8_List list, S8_String separator S8_IF_CPP(= S8_Lit(" ")));
S8_API S8_String S8_Merge(S8_Allocator allocator, S8_List list);
S8_API S8_String S8_ReplaceAll(S8_Allocator allocator, S8_String string, S8_String replace, S8_String with, bool ignore_case S8_IF_CPP(= false));
S8_API S8_List S8_FindAll(S8_Allocator allocator, S8_String string, S8_String find, bool ignore_case S8_IF_CPP(= false));
S8_API S8_Node *S8_CreateNode(S8_Allocator allocator, S8_String string);
S8_API void S8_ReplaceNodeString(S8_List *list, S8_Node *node, S8_String new_string);
S8_API void S8_AddExistingNode(S8_List *list, S8_Node *node);
S8_API void S8_AddArray(S8_Allocator allocator, S8_List *list, char **array, int count);
S8_API void S8_AddArrayWithPrefix(S8_Allocator allocator, S8_List *list, char *prefix, char **array, int count);
S8_API S8_List S8_MakeList(S8_Allocator allocator, S8_String a);
S8_API S8_List S8_CopyList(S8_Allocator allocator, S8_List a);
S8_API S8_List S8_ConcatLists(S8_Allocator allocator, S8_List a, S8_List b);
S8_API S8_Node *S8_AddNode(S8_Allocator allocator, S8_List *list, S8_String string);
S8_API S8_Node *S8_Add(S8_Allocator allocator, S8_List *list, S8_String string);
S8_API S8_String S8_AddF(S8_Allocator allocator, S8_List *list, const char *str, ...) S8__PrintfFormat(3, 4);
S8_API S16_String S8_ToWidecharEx(S8_Allocator allocator, S8_String string);
S8_API wchar_t *S8_ToWidechar(S8_Allocator allocator, S8_String string);
S8_API S8_String S8_FromWidecharEx(S8_Allocator allocator, wchar_t *wstring, int64_t wsize);
S8_API S8_String S8_FromWidechar(S8_Allocator allocator, wchar_t *wstring);
#if defined(__cplusplus)
inline S8_String operator""_s(const char *str, size_t size) { return {(char *)str, (int64_t)size}; }
inline bool operator==(S8_String a, S8_String b) { return S8_AreEqual(a, b, 0); }
inline bool operator!=(S8_String a, S8_String b) { return !S8_AreEqual(a, b, 0); }
#endif
#endif

View File

@@ -0,0 +1,210 @@
#include "unicode.h"
#ifndef UTF__MemoryZero
#include <string.h>
#define UTF__MemoryZero(p, size) memset(p, 0, size)
#endif
UTF_API UTF32_Result UTF_ConvertUTF16ToUTF32(uint16_t *c, int max_advance) {
UTF32_Result result;
UTF__MemoryZero(&result, sizeof(result));
if (max_advance >= 1) {
result.advance = 1;
result.out_str = c[0];
if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) {
if (max_advance >= 2) {
result.out_str = 0x10000;
result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF);
result.advance = 2;
}
else
result.error = 2;
}
}
else {
result.error = 1;
}
return result;
}
UTF_API UTF8_Result UTF_ConvertUTF32ToUTF8(uint32_t codepoint) {
UTF8_Result result;
UTF__MemoryZero(&result, sizeof(result));
if (codepoint <= 0x7F) {
result.len = 1;
result.out_str[0] = (char)codepoint;
}
else if (codepoint <= 0x7FF) {
result.len = 2;
result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6));
result.out_str[1] = 0x80 | (0x3f & codepoint);
}
else if (codepoint <= 0xFFFF) { // 16 bit word
result.len = 3;
result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits
}
else if (codepoint <= 0x10FFFF) { // 21 bit word
result.len = 4;
result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits
result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits
}
else {
result.error = 1;
}
return result;
}
UTF_API UTF32_Result UTF_ConvertUTF8ToUTF32(char *c, int max_advance) {
UTF32_Result result;
UTF__MemoryZero(&result, sizeof(result));
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
if (max_advance >= 1) {
result.out_str = c[0];
result.advance = 1;
}
else result.error = 1;
}
else if ((c[0] & 0xe0) == 0xc0) {
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
if (max_advance >= 2) {
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
result.advance = 2;
}
else result.error = 2;
}
else result.error = 2;
}
else if ((c[0] & 0xf0) == 0xe0) {
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
if (max_advance >= 3) {
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
result.advance = 3;
}
else result.error = 3;
}
else result.error = 3;
}
else if ((c[0] & 0xf8) == 0xf0) {
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
if (max_advance >= 4) {
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
result.advance = 4;
}
else result.error = 4;
}
else result.error = 4;
}
else result.error = 4;
return result;
}
UTF_API UTF16_Result UTF_ConvertUTF32ToUTF16(uint32_t codepoint) {
UTF16_Result result;
UTF__MemoryZero(&result, sizeof(result));
if (codepoint < 0x10000) {
result.out_str[0] = (uint16_t)codepoint;
result.out_str[1] = 0;
result.len = 1;
}
else if (codepoint <= 0x10FFFF) {
uint32_t code = (codepoint - 0x10000);
result.out_str[0] = (uint16_t)(0xD800 | (code >> 10));
result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF));
result.len = 2;
}
else {
result.error = 1;
}
return result;
}
#define UTF__HANDLE_DECODE_ERROR(question_mark) \
{ \
if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \
break; \
}
UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen) {
int64_t outlen = 0;
for (int64_t i = 0; i < inlen && in[i];) {
UTF32_Result decode = UTF_ConvertUTF16ToUTF32((uint16_t *)(in + i), (int)(inlen - i));
if (!decode.error) {
i += decode.advance;
UTF8_Result encode = UTF_ConvertUTF32ToUTF8(decode.out_str);
if (!encode.error) {
for (int64_t j = 0; j < encode.len; j++) {
if (outlen < buffer_size - 1) {
buffer[outlen++] = encode.out_str[j];
}
}
}
else UTF__HANDLE_DECODE_ERROR('?');
}
else UTF__HANDLE_DECODE_ERROR('?');
}
buffer[outlen] = 0;
return outlen;
}
UTF_API int64_t UTF_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen) {
int64_t outlen = 0;
for (int64_t i = 0; i < inlen;) {
UTF32_Result decode = UTF_ConvertUTF8ToUTF32(in + i, (int)(inlen - i));
if (!decode.error) {
i += decode.advance;
UTF16_Result encode = UTF_ConvertUTF32ToUTF16(decode.out_str);
if (!encode.error) {
for (int64_t j = 0; j < encode.len; j++) {
if (outlen < buffer_size - 1) {
buffer[outlen++] = encode.out_str[j];
}
}
}
else UTF__HANDLE_DECODE_ERROR(0x003f);
}
else UTF__HANDLE_DECODE_ERROR(0x003f);
}
buffer[outlen] = 0;
return outlen;
}
UTF_API void UTF8_Advance(UTF8_Iter *iter) {
iter->i += iter->utf8_codepoint_byte_size;
UTF32_Result r = UTF_ConvertUTF8ToUTF32(iter->str + iter->i, iter->len - iter->i);
if (r.error) {
iter->item = 0;
return;
}
iter->utf8_codepoint_byte_size = r.advance;
iter->item = r.out_str;
}
UTF_API UTF8_Iter UTF8_IterateEx(char *str, int len) {
UTF8_Iter result;
UTF__MemoryZero(&result, sizeof(result));
result.str = str;
result.len = len;
if (len) UTF8_Advance(&result);
return result;
}
UTF_API UTF8_Iter UTF8_Iterate(char *str) {
int length = 0;
while (str[length]) length += 1;
return UTF8_IterateEx(str, length);
}

View File

@@ -0,0 +1,55 @@
#ifndef FIRST_UTF_HEADER
#define FIRST_UTF_HEADER
#define UTF_HEADER
#include <stdint.h>
typedef struct UTF32_Result UTF32_Result;
typedef struct UTF8_Result UTF8_Result;
typedef struct UTF16_Result UTF16_Result;
typedef struct UTF8_Iter UTF8_Iter;
#ifndef UTF_API
#ifdef __cplusplus
#define UTF_API extern "C"
#else
#define UTF_API
#endif
#endif
struct UTF32_Result {
uint32_t out_str;
int advance;
int error;
};
struct UTF8_Result {
uint8_t out_str[4];
int len;
int error;
};
struct UTF16_Result {
uint16_t out_str[2];
int len;
int error;
};
struct UTF8_Iter {
char *str;
int len;
int utf8_codepoint_byte_size;
int i;
uint32_t item;
};
UTF_API UTF32_Result UTF_ConvertUTF16ToUTF32(uint16_t *c, int max_advance);
UTF_API UTF8_Result UTF_ConvertUTF32ToUTF8(uint32_t codepoint);
UTF_API UTF32_Result UTF_ConvertUTF8ToUTF32(char *c, int max_advance);
UTF_API UTF16_Result UTF_ConvertUTF32ToUTF16(uint32_t codepoint);
UTF_API int64_t UTF_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen);
UTF_API int64_t UTF_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen);
UTF_API void UTF8_Advance(UTF8_Iter *iter);
UTF_API UTF8_Iter UTF8_IterateEx(char *str, int len);
UTF_API UTF8_Iter UTF8_Iterate(char *str);
#define UTF8_For(name, str, len) for (UTF8_Iter name = UTF8_IterateEx(str, (int)len); name.item; UTF8_Advance(&name))
#endif

View File

@@ -0,0 +1,9 @@
// You can edit this code!
// Click here and start typing.
import "libc";
main :: proc(): int {
printf("hello world!\n");
return 0;
}

View File

@@ -0,0 +1,72 @@
import c "libc";
generation_count :: 5;
board_x :: 20;
board_y :: 10;
board_copy: [board_x * board_y]int;
board: [board_x * board_y]int = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
};
get_neighbor :: proc(xcol: int, yrow: int): int {
x := xcol % board_x;
y := yrow % board_y;
result := board_copy[x + y * board_x];
return result;
}
get_neighbor_count :: proc(xcol: int, yrow: int): int {
result := 0;
for dc := -1; dc <= 1; dc += 1 {
for dr := -1; dr <= 1; dr += 1 {
if (dr || dc) result += get_neighbor(xcol + dc, yrow + dr);
}
}
return result;
}
main :: proc(): int {
for gen := 0; gen < generation_count; gen += 1 {
c.printf("generation: %d\n", gen);
// show cells
for y := 0; y < board_y; y += 1 {
c.printf("|");
for x := 0; x < board_x; x += 1 {
cell := board[x + y * board_x];
if cell {
c.printf("*");
} else {
c.printf(".");
}
}
c.printf("|\n");
}
// update cells
c.memcpy(&board_copy[0], &board[0], sizeof(board));
for y := 0; y < board_y; y += 1 {
for x := 0; x < board_x; x += 1 {
src_cell := board_copy[x + y * board_x];
dst_cell := addptr(board, x + y * board_x);
count := get_neighbor_count(x, y);
birth := !src_cell && count == 3;
survive := src_cell && (count == 3 || count == 2);
dst_cell[0] = birth || survive;
}
}
}
c.printf("End\n");
return 0;
}

View File

@@ -0,0 +1,110 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Playground</title>
</head>
<body>
<select id="example-select">
<option value="0">hello world</option>
<option value="1">conway's game of life</option>
</select>
<textarea autofocus id="code" name="code" autocorrect="off" autocomplete="off" spellcheck="false" wrap="off" autocapitalize="off"></textarea>
<textarea disabled readonly id="output" name="output" autocorrect="off" autocomplete="off" spellcheck="false" wrap="off" autocapitalize="off"></textarea>
</body>
<style>
* { margin: 5px; padding: 0; }
body {
height: 100%;
margin: 60px auto;
max-width: 750px;
background: #000;
}
#code { width: 98%; height: 49vh; padding: 10px; }
#output { width: 98%; height: 46vh; padding: 10px; }
textarea { background: #000; color: #FFF; }
</style>
</html>
<script>
Programs = [
<InsertPrograms>
]
Program = Programs[0];
const ExampleSelect = document.getElementById("example-select");
const Output = document.getElementById("output");
const Code = document.getElementById("code");
ExampleSelect.addEventListener("change", (event) => {
Program = Programs[ExampleSelect.value];
Code.value = Program;
Output.value = "";
WASM_Exports.test();
});
Code.addEventListener("input", (event) => {
Program = event.target.value;
Code.value = Program;
Output.value = "";
WASM_Exports.test();
});
Code.addEventListener('keydown', function(e) {
if (e.key == 'Tab') {
e.preventDefault();
var start = this.selectionStart;
var end = this.selectionEnd;
// set textarea value to: text before caret + tab + text after caret
this.value = this.value.substring(0, start) +
" " + this.value.substring(end);
// put caret at right position again
this.selectionStart = this.selectionEnd = start + 4;
}
});
Code.value = Program;
(async function main() {
let request = await fetch('main.wasm')
let binary = await request.arrayBuffer()
WASM_Memory = new WebAssembly['Memory']({ 'initial': 32 })
WASM_MemoryU8 = new Uint8Array(WASM_Memory['buffer']) // is this memory iterator?
WASM_Imports = {
memory: WASM_Memory,
JS_LoadFile: () => {
const buffer = new TextEncoder().encode(Program);
const pointer = 30 * 1024 * 64;
const slice = new Uint8Array(WASM_Memory.buffer, pointer, buffer.length + 1);
slice.set(buffer);
slice[buffer.length] = 0; // null byte to null-terminate the string
return pointer;
},
JS_ConsoleLog: (str, len) => {
let arr = WASM_MemoryU8.subarray( str, str+len );
let out = utf8decoder.decode( arr );
Output.value += out;
},
JS_ParseFloat: (str, len) => {
let arr = WASM_MemoryU8.subarray( str, str+len );
let out = utf8decoder.decode( arr );
let result = parseFloat(out);
return result;
},
}
let utf8decoder = new TextDecoder( "utf-8" );
let program = await WebAssembly['instantiate'](binary, { "env": WASM_Imports})
let instance = program['instance']
WASM_Exports = instance['exports']
WASM_Exports["test"]();
})()
</script>

View File

@@ -0,0 +1 @@
python -m http.server

View File

@@ -0,0 +1,132 @@
#define WASM_IMPORT(name) __attribute__((import_name(#name))) name
#define WASM_EXPORT(name) __attribute__((export_name(#name))) name
double WASM_IMPORT(JS_ParseFloat)(char *str, int len);
void WASM_IMPORT(JS_ConsoleLog)(char *str, int len);
char *WASM_IMPORT(JS_LoadFile)(void);
#define LC_ParseFloat(str, len) JS_ParseFloat(str, len)
#define LC_Print(str, len) JS_ConsoleLog(str, len)
#define LC_Exit(x) (*(volatile int *)0 = 0)
#define LC_THREAD_LOCAL
#define LC_MemoryZero(p, size) __builtin_memset(p, 0, size);
#define LC_MemoryCopy(dst, src, size) __builtin_memcpy(dst, src, size)
#include "../standalone_libraries/stb_sprintf.h"
#include "../standalone_libraries/stb_sprintf.c"
#define LC_vsnprintf stbsp_vsnprintf
#include "../compiler/lib_compiler.c"
bool LC_IsDir(LC_Arena *temp, LC_String path) {
if (LC_AreEqual(path, LC_Lit("virtual_dir"), false)) return true;
if (LC_AreEqual(path, LC_Lit("virtual_dir/libc"), false)) return true;
return false;
}
LC_FileIter LC_IterateFiles(LC_Arena *arena, LC_String path) {
LC_FileIter result = {0};
result.is_valid = true;
result.is_directory = false;
result.filename = LC_Lit("file.lc");
if (LC_StartsWith(path, LC_Lit("virtual_dir"), false)) {
result.absolute_path = LC_Format(arena, "%.*s/file.lc", LC_Expand(path));
result.relative_path = LC_Format(arena, "%.*s/file.lc", LC_Expand(path));
}
return result;
}
bool LC_IsValid(LC_FileIter it) {
return it.is_valid;
}
void LC_Advance(LC_FileIter *it) {
it->is_valid = false;
}
LC_String LC_ReadFile(LC_Arena *arena, LC_String path) {
LC_String result = LC_Lit("main :: proc(): int { return 0; }");
return result;
}
LC_FUNCTION void LC_VDeallocate(LC_VMemory *m) {}
LC_FUNCTION bool LC_VCommit(LC_VMemory *m, size_t commit) { return false; }
char Memory[64 * 1024 * 24];
LC_Arena MainArena;
LC_Lang *Wasm_LC_LangAlloc(LC_Arena *arena) {
LC_Arena *lex_arena = LC_PushStruct(arena, LC_Arena);
*lex_arena = LC_PushArena(arena, 64 * 1024 * 4);
LC_Arena *ast_arena = LC_PushStruct(arena, LC_Arena);
*ast_arena = LC_PushArena(arena, 64 * 1024 * 4);
LC_Arena *decl_arena = LC_PushStruct(arena, LC_Arena);
*decl_arena = LC_PushArena(arena, 64 * 1024 * 2);
LC_Arena *type_arena = LC_PushStruct(arena, LC_Arena);
*type_arena = LC_PushArena(arena, 64 * 1024 * 2);
LC_Lang *l = LC_PushStruct(arena, LC_Lang);
l->arena = arena;
l->lex_arena = lex_arena;
l->decl_arena = decl_arena;
l->type_arena = type_arena;
l->ast_arena = ast_arena;
return l;
}
void Wasm_OnFileLoad(LC_AST *package, LoadedFile *file) {
if (LC_StartsWith(file->path, LC_Lit("virtual_dir/libc/file.lc"), false)) {
file->content = LC_Lit(
" size_t :: typedef ullong; @foreign"
" printf :: proc(format: *char, ...): int; @foreign"
" memset :: proc(s: *void, value: int, n: size_t): *void; @foreign"
" memcpy :: proc(s1: *void, s2: *void, n: size_t): *void; @foreign");
} else {
file->content.str = JS_LoadFile();
file->content.len = LC_StrLen(file->content.str);
}
}
void WASM_EXPORT(test)(void) {
LC_MemoryZero(&MainArena, sizeof(MainArena));
LC_InitArenaFromBuffer(&MainArena, Memory, sizeof(Memory));
LC_Lang *lang = Wasm_LC_LangAlloc(&MainArena);
{
lang->on_file_load = Wasm_OnFileLoad;
lang->os = 32;
lang->arch = 32;
}
LC_LangBegin(lang);
{
LC_AddBuiltinConstInt("LC_WASM", 32);
LC_AddBuiltinConstInt("ARCH_WASM", 32);
L->tlong->size = 4;
L->tlong->align = 4;
L->tulong->size = 4;
L->tulong->align = 4;
LC_SetPointerSizeAndAlign(4, 4);
}
LC_RegisterPackageDir("virtual_dir");
LC_Intern name = LC_ILit("file");
LC_AddSingleFilePackage(name, LC_Lit("file.lc"));
LC_ASTRefList packages = LC_ResolvePackageByName(name);
if (L->errors == 0) {
LC_BeginStringGen(L->arena);
for (LC_ASTRef *it = packages.first; it; it = it->next) LC_GenCHeader(it->ast);
for (LC_ASTRef *it = packages.first; it; it = it->next) LC_GenCImpl(it->ast);
LC_String result = LC_EndStringGen(L->arena);
LC_String code_output = LC_Lit("//\n// Code output\n//");
JS_ConsoleLog(code_output.str, code_output.len);
JS_ConsoleLog(result.str, (int)result.len);
}
LC_LangEnd(lang);
}

18
src/x64/misc.cpp Normal file
View File

@@ -0,0 +1,18 @@
S8_String GetCodeLine(LC_AST *n) {
LC_Token *token = n->pos;
LC_Lex *x = n->file->afile.x;
S8_String content = S8_MakeFromChar(x->begin);
S8_List lines = S8_Split(L->arena, content, S8_Lit("\n"), 0);
int line = 1;
S8_For(it, lines) {
if (token->line == line) return it->string;
line += 1;
}
return S8_MakeEmpty();
}
void OutASTLine(LC_AST *n) {
S8_String line = GetCodeLine(n);
LC_GenLinef(";%.*s", S8_Expand(line));
}

28
src/x64/registers.cpp Normal file
View File

@@ -0,0 +1,28 @@
#define MAX_REG 3
bool RegTaken[MAX_REG] = {0, 0, 0};
Register Regs[MAX_REG] = {R10, R11, R12};
Register AllocReg() {
for (int i = 0; i < 3; i += 1) {
if (RegTaken[i] == false) {
RegTaken[i] = true;
return Regs[i];
}
}
IO_InvalidCodepath();
return INVALID_REGISTER;
}
void DeallocReg(Register reg) {
int i = (int)(reg - R10);
RegTaken[i] = false;
}
const char *Str(Register reg) {
const char *registers[] = {
#define X(x) #x,
REGISTERS
#undef X
};
return registers[reg];
}

4
src/x64/tests/array.lc Normal file
View File

@@ -0,0 +1,4 @@
main :: proc(): int {
//i: [8]int;
return 0;
}

10
src/x64/tests/assignop.lc Normal file
View File

@@ -0,0 +1,10 @@
main :: proc(): int {
i := 0;
i += 10;
i -= 10;
i += 2;
i *= 2;
i /= 4;
i -= 1;
return i;
}

10
src/x64/tests/expr.lc Normal file
View File

@@ -0,0 +1,10 @@
a1 := 1;
a2 := 2;
a4 := 4;
a5 := 5;
a10 := 10;
main :: proc(): int {
i := (a1 + a4) / a5 * a10 + (a10 - a5) - 15;
return i;
}

31
src/x64/tests/first.lc Normal file
View File

@@ -0,0 +1,31 @@
S :: struct {
a: llong;
b: llong;
c: llong;
}
main :: proc(): llong {
a: [5]llong;
a[2] = 1;
a[2] -= 1;
s: S;
i4: llong = 4;
i8: llong = i4 + i4;
i12: llong = i4 + i4 + i4;
@unused other_type: llong = 10;
p := &i4;
*p -= 1;
*&i4 -= 1;
pp := &p;
**pp += 2;
@unused ret := second_proc();
return i12 - i8 - i4 + s.c + a[2];
}
second_proc :: proc(): int {
return 0;
}

18
src/x64/tests/for_loop.lc Normal file
View File

@@ -0,0 +1,18 @@
main :: proc(): int {
a := 0;
for i := 0; i < 10; i += 1 {
a += 1;
}
for a > 0; a -= 1 {
c := 20;
c += 1;
}
a = 10;
for a > 0 {
a -= 1;
}
return a;
}

View File

@@ -0,0 +1,11 @@
a := 10;
b := 20;
main :: proc(): int {
a = 0;
b = 0;
if (a == b) {
return 0;
}
return 1;
}

25
src/x64/tests/pointers.lc Normal file
View File

@@ -0,0 +1,25 @@
stack_value :: proc(): int {
i := 2;
ip := &i;
return *ip - 2;
}
I := 2;
glob_value :: proc(): int {
ip := &I;
return *ip - 2;
}
param_value :: proc(i: int): int {
ip := &i;
ipp := &ip;
return *ip - **ipp;
}
main :: proc(): int {
i0 := stack_value();
i1 := glob_value();
i2 := param_value(i0);
return i0 + i1 + i2;
}

View File

@@ -0,0 +1,17 @@
call :: proc(): int {
return 0;
}
void_call :: proc() {
}
args :: proc(a: int, b: int, c: int, d: int): int {
return a + b + c + d;
}
main :: proc(): int {
void_call();
val := call() + call() + call() * call();
result := args(1,2,3,4) - 10;
return result + val;
}

15
src/x64/value.cpp Normal file
View File

@@ -0,0 +1,15 @@
const char *Str(Value value) {
if (value.mode == Mode_Reg) {
return LC_Strf("%s", Str(value.reg));
} else if (value.mode == Mode_Direct) {
return LC_Strf("[%s]", Str(value.reg));
} else if (value.mode == Mode_Imm) {
return LC_Strf("%llu", value.u);
} else IO_Todo();
return "INVALID_VALUE";
}
void DeallocReg(Value value) {
if (value.mode == Mode_Imm) return;
DeallocReg(value.reg);
}

41
src/x64/x64.h Normal file
View File

@@ -0,0 +1,41 @@
struct X64Decl {
int offset;
};
/*
a: int;
a // d lvalue
&a // r not lvalue
*&a // d lvalue
*a // illegal
b: *int;
*b // d lvalue
b // d lvalue
&b // r not lvalue
**b // illegal
*/
enum Mode {
Mode_Imm, // Value is a constant and can be folded into instruction
Mode_Reg, // Value is in register
Mode_Direct, // Register that holds address to value, (but this is lvalue, not "*a" but just "a")
};
#define REGISTERS\
X(INVALID_REGISTER)\
X(RAX) X(RBX) X(RCX) X(RDX) X(RSI) X(RDI) X(RBP) X(RSP)\
X(R8) X(R9) X(R10) X(R11) X(R12) X(R13) X(R14) X(R15)
enum Register {
#define X(x) x,
REGISTERS
#undef X
};
struct Value {
Mode mode;
Register reg;
uint64_t u;
};
Value GenExpr(LC_AST *n);

309
src/x64/x64main.cpp Normal file
View File

@@ -0,0 +1,309 @@
/*
I assume this is going to be a command line program, so:
- don't care about allocation
- global variables are OK
*/
#include "../core/core.c"
#include "../compiler/lib_compiler.c"
#include "x64.h"
#include "misc.cpp"
#include "registers.cpp"
#include "value.cpp"
Value GenExprInt(LC_AST *n) {
uint64_t u = LC_Bigint_as_unsigned(&n->eatom.i);
Value result = {Mode_Imm, INVALID_REGISTER, u};
return result;
}
Value GenExprIdent(LC_AST *n) {
LC_Decl *decl = n->eident.resolved_decl;
X64Decl *x = decl->x64;
Register reg = AllocReg();
if (decl->ast->kind == LC_ASTKind_StmtVar) {
LC_GenLinef("lea %s, [RBP - %d]", Str(reg), x->offset);
} else IO_Todo();
Value result = {Mode_Direct, reg};
return result;
}
Value GenExprField(LC_AST *n) {
LC_Decl *resolved_decl = n->efield.resolved_decl;
LC_TypeMember *mem = resolved_decl->type_member;
Value value = GenExpr(n->efield.left);
IO_Assert(value.mode == Mode_Direct);
LC_GenLinef("add %s, %d", Str(value.reg), mem->offset);
return value;
}
void LoadValueIntoReg(Value *v) {
if (v->mode == Mode_Direct) {
LC_GenLinef("mov %s, %s", Str(v->reg), Str(*v));
v->mode = Mode_Reg;
} else if (v->mode == Mode_Imm) {
v->reg = AllocReg();
LC_GenLinef("mov %s, %llu", Str(v->reg), v->u);
v->mode = Mode_Reg;
}
}
Value GenExprBinary(LC_AST *n) {
Value l = GenExpr(n->ebinary.left);
Value r = GenExpr(n->ebinary.right);
LoadValueIntoReg(&l);
LoadValueIntoReg(&r);
switch (n->ebinary.op) {
case LC_TokenKind_Add: LC_GenLinef("add %s, %s", Str(l), Str(r)); break;
case LC_TokenKind_Sub: LC_GenLinef("sub %s, %s", Str(l), Str(r)); break;
default: LC_ReportASTError(n, "internal compiler error: unhandled binary op %s in %s", TokenKindString[n->ebinary.op], __FUNCTION__);
}
DeallocReg(r.reg);
return l;
}
Value GenExprIndex(LC_AST *n) {
Value index = GenExpr(n->eindex.index);
Value base = GenExpr(n->eindex.base);
IO_Assert(index.mode != Mode_Direct);
IO_Assert(base.mode == Mode_Direct);
int esize = n->type->size;
if (index.mode == Mode_Imm) {
index.u *= esize;
} else if (index.mode == Mode_Reg) {
LC_GenLinef("imul %s, %d", Str(index), esize);
} else IO_Todo();
LC_GenLinef("add %s, %s", Str(base.reg), Str(index));
DeallocReg(index);
return base;
}
Value GenExprGetPointerOfValue(LC_AST *n) {
Value value = GenExpr(n->eunary.expr);
IO_Assert(value.mode == Mode_Direct);
value.mode = Mode_Reg;
return value;
}
Value GenExprGetValueOfPointer(LC_AST *n) {
Value value = GenExpr(n->eunary.expr);
if (value.mode == Mode_Reg) {
value.mode = Mode_Direct;
} else if (value.mode == Mode_Direct) {
LC_GenLinef("mov %s, %s", Str(value.reg), Str(value));
} else IO_Todo();
return value;
}
Value GenExprCall(LC_AST *n) {
Register abi_regs[] = {RCX, RDX, R8, R9};
int i = 0;
LC_ResolvedCompo *com = n->ecompo.resolved_items;
for (LC_ResolvedCompoItem *it = com->first; it; it = it->next) {
IO_Assert(i < 4);
LC_Type *type = it->t->type;
Value value = GenExpr(it->expr);
Register reg = abi_regs[i++];
LC_GenLinef("mov %s, %s", Str(reg), Str(value.reg));
DeallocReg(value);
}
LC_GenLinef("call");
Value result = {0};
if (n->type->tproc.ret != L->tvoid) {
result.reg = AllocReg();
result.mode = Mode_Reg;
}
return result;
}
Value GenExpr(LC_AST *n) {
IO_Assert(LC_IsExpr(n));
switch (n->kind) {
case LC_ASTKind_ExprIndex: return GenExprIndex(n); break;
case LC_ASTKind_ExprInt: return GenExprInt(n); break;
case LC_ASTKind_ExprIdent: return GenExprIdent(n); break;
case LC_ASTKind_ExprField: return GenExprField(n); break;
case LC_ASTKind_ExprBinary: return GenExprBinary(n); break;
case LC_ASTKind_ExprGetPointerOfValue: return GenExprGetPointerOfValue(n); break;
case LC_ASTKind_ExprGetValueOfPointer: return GenExprGetValueOfPointer(n); break;
case LC_ASTKind_ExprCall: return GenExprCall(n); break;
}
LC_ReportASTError(n, "internal compiler error: unhandled ast kind %s in %s", ASTKindString[n->kind], __FUNCTION__);
return {};
}
void GenEpilogue() {
LC_GenLinef("mov rsp, rbp");
LC_GenLinef("pop rbp");
}
void GenStmtReturn(LC_AST *n) {
if (n->sreturn.expr) {
Value value = GenExpr(n->sreturn.expr);
LC_GenLinef("mov RAX, %s", Str(value));
DeallocReg(value);
}
GenEpilogue();
LC_GenLinef("ret");
}
void GenStmtVar(LC_AST *n) {
LC_Decl *decl = n->svar.resolved_decl;
X64Decl *x = decl->x64;
LC_AST *expr = n->svar.expr;
if (expr) {
Value value = GenExpr(expr);
LC_GenLinef("lea RAX, [RBP - %d]", x->offset);
if (value.mode == Mode_Imm) {
LC_GenLinef("mov qword [RAX], %u", value.u);
} else if (value.mode == Mode_Direct) {
LC_GenLinef("mov RBX, [%s]", Str(value.reg));
LC_GenLinef("mov qword [RAX], RBX");
} else if (value.mode == Mode_Reg) {
LC_GenLinef("mov qword [RAX], %s", Str(value.reg));
} else IO_Todo();
DeallocReg(value);
} else {
LC_GenLinef("lea RAX, [RBP - %d]", x->offset);
for (int i = 0; i < n->type->size; i += 1) {
LC_GenLinef("mov byte [RAX+%d], 0", i);
}
}
}
void GenStmtAssign(LC_AST *n) {
Value l = GenExpr(n->sassign.left);
IO_Assert(l.mode == Mode_Direct);
Value r = GenExpr(n->sassign.right);
LC_TokenKind op = n->sassign.op;
switch (op) {
case LC_TokenKind_Assign: LC_GenLinef("mov qword %s, %s", Str(l), Str(r)); break;
case LC_TokenKind_AddAssign: LC_GenLinef("add qword %s, %s", Str(l), Str(r)); break;
case LC_TokenKind_SubAssign: LC_GenLinef("sub qword %s, %s", Str(l), Str(r)); break;
default: LC_ReportASTError(n, "internal compiler error: unhandled assign op %s in %s", TokenKindString[n->ebinary.op], __FUNCTION__);
}
DeallocReg(l);
DeallocReg(r);
}
void GenStmt(LC_AST *n) {
OutASTLine(n);
IO_Assert(LC_IsStmt(n));
switch (n->kind) {
case LC_ASTKind_StmtExpr:
Value val = GenExpr(n->sexpr.expr);
DeallocReg(val);
break;
case LC_ASTKind_StmtVar: GenStmtVar(n); break;
case LC_ASTKind_StmtReturn: GenStmtReturn(n); break;
case LC_ASTKind_StmtAssign: GenStmtAssign(n); break;
default: LC_ReportASTError(n, "internal compiler error: unhandled ast kind %s in %s", ASTKindString[n->kind], __FUNCTION__);
}
}
void GenStmtBlock(LC_AST *n) {
LC_ASTFor(it, n->sblock.first) {
GenStmt(it);
}
}
void WalkToComputeStackSize(LC_ASTWalker *w, LC_AST *n) {
int *stack_size = (int *)w->user_data;
if (n->kind == LC_ASTKind_StmtVar) {
LC_Decl *decl = n->svar.resolved_decl;
decl->x64 = MA_PushStruct(L->arena, X64Decl);
stack_size[0] += n->type->size;
decl->x64->offset = stack_size[0]; // stack goes backwards
stack_size[0] = (int)MA_AlignUp(stack_size[0], 8);
}
}
void GenProc(LC_Decl *decl) {
LC_AST *n = decl->ast;
OutASTLine(n);
LC_GenLinef("global %s", decl->foreign_name);
LC_GenLinef("%s:", decl->foreign_name);
int stack_size = 0;
AST_WalkBreathFirst(MA_GetAllocator(L->arena), WalkToComputeStackSize, n->dproc.body, (void *)&stack_size);
LC_GenLinef("push rbp");
LC_GenLinef("mov rbp, rsp");
if ((stack_size % 16) == 0) stack_size += 8;
LC_GenLinef("sub rsp, %d", stack_size);
printer.indent += 1;
for (LC_TypeMember *it = n->type->tproc.args.first; it; it = it->next) {
}
GenStmtBlock(n->dproc.body);
printer.indent -= 1;
GenEpilogue();
}
void GenPackages(LC_ASTRefList packages) {
LC_GenLinef("BITS 64");
LC_GenLinef("section .text");
for (LC_ASTRef *it = packages.first; it; it = it->next) {
LC_AST *n = it->ast;
LC_DeclFor(decl, n->apackage.first_ordered) {
if (decl->kind != LC_DeclKind_Proc) continue;
if (decl->is_foreign) continue;
GenProc(decl);
}
}
}
int main(int argc, char **argv) {
MA_Arena arena = {0};
for (OS_FileIter iter = OS_IterateFiles(&arena, S8_Lit("../../src/x64/tests")); OS_IsValid(iter); OS_Advance(&iter)) {
if (!S8_EndsWith(iter.filename, S8_Lit("first.lc"))) continue;
LC_Lang *lang = LC_LangAlloc();
lang->use_colored_terminal_output = UseColoredIO;
LC_LangBegin(lang);
LC_ASTRefList package = ParseAndResolveFile(iter.absolute_path.str);
LC_BeginStringGen(L->arena);
GenPackages(package);
S8_String s = LC_EndStringGen(L->arena);
S8_String asmf = S8_Format(L->arena, "%.*s.asm", S8_Expand(iter.filename));
OS_WriteFile(asmf, s);
OS_SystemF("..\\..\\tools\\nasm.exe %.*s -o %.*s.obj -gcv8 -f win64 -O0", S8_Expand(asmf), S8_Expand(asmf));
OS_SystemF("\"C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.36.32532/bin/Hostx64/x64/link.exe\" %.*s.obj /nologo /subsystem:console /debug /entry:main /out:%.*s.exe", S8_Expand(asmf), S8_Expand(asmf));
int result = OS_SystemF("%.*s.exe", S8_Expand(asmf));
IO_Assert(result == 0);
LC_LangEnd();
}
IO_DebugBreak();
return 0;
}