Init new repository
This commit is contained in:
125
src/build_file/ast_verify.cpp
Normal file
125
src/build_file/ast_verify.cpp
Normal 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 *)©_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);
|
||||
}
|
||||
166
src/build_file/package_compiler.cpp
Normal file
166
src/build_file/package_compiler.cpp
Normal 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);
|
||||
}
|
||||
37
src/build_file/test_readme.cpp
Normal file
37
src/build_file/test_readme.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
463
src/build_file/testsuite.cpp
Normal file
463
src/build_file/testsuite.cpp
Normal 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
127
src/build_tool/cache.cpp
Normal 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;
|
||||
}
|
||||
116
src/build_tool/easy_strings.cpp
Normal file
116
src/build_tool/easy_strings.cpp
Normal 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;
|
||||
}
|
||||
55
src/build_tool/library.cpp
Normal file
55
src/build_tool/library.cpp
Normal 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
90
src/build_tool/main.cpp
Normal 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
153
src/build_tool/process.cpp
Normal 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
138
src/compiler/arena.c
Normal 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
167
src/compiler/ast.c
Normal 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
283
src/compiler/ast_copy.c
Normal 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
316
src/compiler/ast_walk.c
Normal 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
1471
src/compiler/bigint.c
Normal file
File diff suppressed because it is too large
Load Diff
198
src/compiler/common.c
Normal file
198
src/compiler/common.c
Normal 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;
|
||||
}
|
||||
72
src/compiler/extended_passes.c
Normal file
72
src/compiler/extended_passes.c
Normal 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 *)↦
|
||||
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
677
src/compiler/genc.c
Normal 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
259
src/compiler/init.c
Normal 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
44
src/compiler/intern.c
Normal 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
535
src/compiler/lex.c
Normal 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
|
||||
95
src/compiler/lib_compiler.c
Normal file
95
src/compiler/lib_compiler.c
Normal 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
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
363
src/compiler/packages.c
Normal 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
1079
src/compiler/parse.c
Normal file
File diff suppressed because it is too large
Load Diff
477
src/compiler/printer.c
Normal file
477
src/compiler/printer.c
Normal 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
1496
src/compiler/resolve.c
Normal file
File diff suppressed because it is too large
Load Diff
192
src/compiler/resolver.c
Normal file
192
src/compiler/resolver.c
Normal 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
422
src/compiler/string.c
Normal 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
277
src/compiler/to_string.c
Normal 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
158
src/compiler/unicode.c
Normal 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
30
src/compiler/unix_arena.c
Normal 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");
|
||||
}
|
||||
89
src/compiler/unix_filesystem.c
Normal file
89
src/compiler/unix_filesystem.c
Normal 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
328
src/compiler/value.c
Normal 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;
|
||||
}
|
||||
44
src/compiler/win32_arena.c
Normal file
44
src/compiler/win32_arena.c
Normal 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;
|
||||
}
|
||||
118
src/compiler/win32_filesystem.c
Normal file
118
src/compiler/win32_filesystem.c
Normal 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
115
src/core/allocator.c
Normal 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
44
src/core/allocator.h
Normal 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
270
src/core/array.hpp
Normal 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
186
src/core/cmd.c
Normal 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
37
src/core/cmd.h
Normal 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
23
src/core/core.c
Normal 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
1
src/core/core.cpp
Normal file
@@ -0,0 +1 @@
|
||||
#include "core.c"
|
||||
25
src/core/core.h
Normal file
25
src/core/core.h
Normal 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
720
src/core/filesystem.c
Normal 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
73
src/core/filesystem.h
Normal 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
204
src/core/table.hpp
Normal 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
62
src/profiler/profiler.c
Normal 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
448
src/profiler/spall.h
Normal 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
|
||||
366
src/standalone_libraries/arena.c
Normal file
366
src/standalone_libraries/arena.c
Normal 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
|
||||
179
src/standalone_libraries/arena.h
Normal file
179
src/standalone_libraries/arena.h
Normal 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
|
||||
1549
src/standalone_libraries/clexer.c
Normal file
1549
src/standalone_libraries/clexer.c
Normal file
File diff suppressed because it is too large
Load Diff
302
src/standalone_libraries/clexer.h
Normal file
302
src/standalone_libraries/clexer.h
Normal 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
|
||||
25
src/standalone_libraries/defer.hpp
Normal file
25
src/standalone_libraries/defer.hpp
Normal 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
|
||||
53
src/standalone_libraries/hash.c
Normal file
53
src/standalone_libraries/hash.c
Normal 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;
|
||||
}
|
||||
28
src/standalone_libraries/hash.h
Normal file
28
src/standalone_libraries/hash.h
Normal 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
|
||||
236
src/standalone_libraries/io.c
Normal file
236
src/standalone_libraries/io.c
Normal 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
|
||||
108
src/standalone_libraries/io.h
Normal file
108
src/standalone_libraries/io.h
Normal 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
|
||||
119
src/standalone_libraries/linked_list.h
Normal file
119
src/standalone_libraries/linked_list.h
Normal 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
|
||||
26
src/standalone_libraries/load_library.c
Normal file
26
src/standalone_libraries/load_library.c
Normal 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
|
||||
12
src/standalone_libraries/load_library.h
Normal file
12
src/standalone_libraries/load_library.h
Normal 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
|
||||
1740
src/standalone_libraries/multimedia.c
Normal file
1740
src/standalone_libraries/multimedia.c
Normal file
File diff suppressed because it is too large
Load Diff
370
src/standalone_libraries/multimedia.h
Normal file
370
src/standalone_libraries/multimedia.h
Normal 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
|
||||
119
src/standalone_libraries/preproc_env.h
Normal file
119
src/standalone_libraries/preproc_env.h
Normal 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
|
||||
558
src/standalone_libraries/regex.c
Normal file
558
src/standalone_libraries/regex.c
Normal 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, ®ex->group.first, ®ex->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, ®ex->group.first, ®ex->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;
|
||||
}
|
||||
95
src/standalone_libraries/regex.h
Normal file
95
src/standalone_libraries/regex.h
Normal 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
|
||||
1669
src/standalone_libraries/stb_sprintf.c
Normal file
1669
src/standalone_libraries/stb_sprintf.c
Normal file
File diff suppressed because it is too large
Load Diff
262
src/standalone_libraries/stb_sprintf.h
Normal file
262
src/standalone_libraries/stb_sprintf.h
Normal 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.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
566
src/standalone_libraries/string.c
Normal file
566
src/standalone_libraries/string.c
Normal 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
|
||||
197
src/standalone_libraries/string.h
Normal file
197
src/standalone_libraries/string.h
Normal 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
|
||||
210
src/standalone_libraries/unicode.c
Normal file
210
src/standalone_libraries/unicode.c
Normal 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);
|
||||
}
|
||||
55
src/standalone_libraries/unicode.h
Normal file
55
src/standalone_libraries/unicode.h
Normal 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
|
||||
9
src/wasm_playground/01_hello_world.lc
Normal file
9
src/wasm_playground/01_hello_world.lc
Normal 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;
|
||||
}
|
||||
72
src/wasm_playground/02_conways_game_of_life.lc
Normal file
72
src/wasm_playground/02_conways_game_of_life.lc
Normal 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;
|
||||
}
|
||||
110
src/wasm_playground/index.html
Normal file
110
src/wasm_playground/index.html
Normal 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>
|
||||
1
src/wasm_playground/run_server.bat
Normal file
1
src/wasm_playground/run_server.bat
Normal file
@@ -0,0 +1 @@
|
||||
python -m http.server
|
||||
132
src/wasm_playground/wasm_main.c
Normal file
132
src/wasm_playground/wasm_main.c
Normal 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
18
src/x64/misc.cpp
Normal 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
28
src/x64/registers.cpp
Normal 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
4
src/x64/tests/array.lc
Normal file
@@ -0,0 +1,4 @@
|
||||
main :: proc(): int {
|
||||
//i: [8]int;
|
||||
return 0;
|
||||
}
|
||||
10
src/x64/tests/assignop.lc
Normal file
10
src/x64/tests/assignop.lc
Normal 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
10
src/x64/tests/expr.lc
Normal 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
31
src/x64/tests/first.lc
Normal 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
18
src/x64/tests/for_loop.lc
Normal 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;
|
||||
}
|
||||
11
src/x64/tests/ifreturnvar.lc
Normal file
11
src/x64/tests/ifreturnvar.lc
Normal 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
25
src/x64/tests/pointers.lc
Normal 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;
|
||||
}
|
||||
17
src/x64/tests/proc_call.lc
Normal file
17
src/x64/tests/proc_call.lc
Normal 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
15
src/x64/value.cpp
Normal 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
41
src/x64/x64.h
Normal 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
309
src/x64/x64main.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user