Init new repository
This commit is contained in:
173
examples/add_dynamic_array_macro/build.cpp
Normal file
173
examples/add_dynamic_array_macro/build.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
bool add_dynamic_array_macro() {
|
||||
LC_Lang *lang = LC_LangAlloc();
|
||||
lang->use_colored_terminal_output = UseColoredIO;
|
||||
lang->breakpoint_on_error = BreakpointOnError;
|
||||
LC_LangBegin(lang);
|
||||
|
||||
LC_RegisterPackageDir("../pkgs");
|
||||
LC_RegisterPackageDir("../examples");
|
||||
|
||||
LC_Intern name = LC_ILit("add_dynamic_array_macro");
|
||||
LC_ParsePackagesUsingRegistry(name);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
Array<LC_Intern> array_of_to_gen = {MA_GetAllocator(L->arena)};
|
||||
|
||||
LC_Intern init_array = LC_ILit("init_array");
|
||||
LC_AST *init_array_ast = LC_ParseStmtf("{ init_array_base(REF, SIZE, sizeof(REF.data[0])); }");
|
||||
LC_AST *init_array_add_ast = LC_ParseStmtf("{ REF.data[REF.len] = ITEM; REF.len += 1; }");
|
||||
|
||||
LC_Intern add = LC_ILit("add");
|
||||
LC_AST *add_ast = LC_ParseStmtf(
|
||||
"{"
|
||||
" try_growing_array(REF, sizeof(REF.data[0]));"
|
||||
" REF.data[REF.len] = ITEM;"
|
||||
" REF.len += 1;"
|
||||
"}");
|
||||
LC_Intern ITEM = LC_ILit("ITEM");
|
||||
LC_Intern REF = LC_ILit("REF");
|
||||
|
||||
LC_AST *package = LC_GetPackageByName(name);
|
||||
LC_AST *first_ast = (LC_AST *)L->ast_arena->memory.data;
|
||||
for (int i = 0; i < L->ast_count; i += 1) {
|
||||
LC_AST *it = first_ast + i;
|
||||
|
||||
// Detect and save to array every unique use of 'ArrayOfT'
|
||||
if (it->kind == LC_ASTKind_TypespecIdent) {
|
||||
S8_String name = S8_MakeFromChar((char *)it->eident.name);
|
||||
if (S8_StartsWith(name, "ArrayOf")) {
|
||||
S8_String s8 = S8_Skip(name, S8_Lit("ArrayOf").len);
|
||||
LC_Intern type = LC_InternStrLen(s8.str, (int)s8.len);
|
||||
|
||||
bool is_unique = true;
|
||||
For2(in_array, array_of_to_gen) {
|
||||
if (in_array == type) {
|
||||
is_unique = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_unique) array_of_to_gen.add(type);
|
||||
}
|
||||
}
|
||||
|
||||
if (it->kind == LC_ASTKind_StmtExpr && it->sexpr.expr->kind == LC_ASTKind_ExprCall) {
|
||||
LC_AST *name = it->sexpr.expr->ecompo.name;
|
||||
LC_AST *call = it->sexpr.expr;
|
||||
|
||||
if (name->kind != LC_ASTKind_ExprIdent) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name->eident.name == add) {
|
||||
if (call->ecompo.size != 2) {
|
||||
LC_ReportASTError(call, "wrong argument count in macro call");
|
||||
continue;
|
||||
}
|
||||
LC_AST *first_item = call->ecompo.first;
|
||||
LC_AST *arr_ref = first_item->ecompo_item.expr;
|
||||
LC_AST *item = first_item->next->ecompo_item.expr;
|
||||
|
||||
LC_AST *macro = LC_CopyAST(L->arena, add_ast);
|
||||
LC_ASTArray array = LC_FlattenAST(L->arena, macro);
|
||||
|
||||
for (int m_i = 0; m_i < array.len; m_i += 1) {
|
||||
LC_AST *m_it = array.data[m_i];
|
||||
if (m_it->kind == LC_ASTKind_ExprIdent) {
|
||||
if (m_it->eident.name == ITEM) {
|
||||
*m_it = *LC_CopyAST(L->arena, item);
|
||||
} else if (m_it->eident.name == REF) {
|
||||
*m_it = *LC_CopyAST(L->arena, arr_ref);
|
||||
}
|
||||
}
|
||||
m_it->pos = it->pos;
|
||||
}
|
||||
|
||||
macro->next = it->next;
|
||||
macro->prev = it->prev;
|
||||
*it = *macro;
|
||||
}
|
||||
|
||||
if (name->eident.name == init_array) {
|
||||
if (call->ecompo.size != 2) {
|
||||
LC_ReportASTError(call, "wrong argument count in macro call");
|
||||
continue;
|
||||
}
|
||||
|
||||
LC_AST *first_item = call->ecompo.first;
|
||||
LC_AST *arr_ref = first_item->ecompo_item.expr;
|
||||
LC_AST *item = first_item->next->ecompo_item.expr;
|
||||
|
||||
if (item->kind != LC_ASTKind_ExprCompound) {
|
||||
LC_ReportASTError(item, "expected compound");
|
||||
continue;
|
||||
}
|
||||
|
||||
LC_AST *macro = LC_CopyAST(L->arena, init_array_ast);
|
||||
LC_ASTArray array = LC_FlattenAST(L->arena, macro);
|
||||
for (int m_i = 0; m_i < array.len; m_i += 1) {
|
||||
LC_AST *m_it = array.data[m_i];
|
||||
if (m_it->kind != LC_ASTKind_ExprIdent) continue;
|
||||
|
||||
if (m_it->eident.name == LC_ILit("SIZE")) {
|
||||
// This is a bit dangerous here because through
|
||||
// this call we are bumping L->ast_count += 1
|
||||
*m_it = *LC_CreateAST(item->pos, LC_ASTKind_ExprInt);
|
||||
m_it->eatom.i = LC_Bigint_u64(item->ecompo.size);
|
||||
} else if (m_it->eident.name == REF) {
|
||||
*m_it = *LC_CopyAST(L->arena, arr_ref);
|
||||
}
|
||||
m_it->pos = it->pos;
|
||||
}
|
||||
|
||||
LC_ASTFor(item_it, item->ecompo.first) {
|
||||
LC_AST *init_item = LC_CopyAST(L->arena, init_array_add_ast);
|
||||
LC_ASTArray array = LC_FlattenAST(L->arena, init_item);
|
||||
for (int m_i = 0; m_i < array.len; m_i += 1) {
|
||||
LC_AST *m_it = array.data[m_i];
|
||||
|
||||
if (m_it->kind == LC_ASTKind_ExprIdent) {
|
||||
if (m_it->eident.name == ITEM) {
|
||||
*m_it = *LC_CopyAST(L->arena, item_it->ecompo_item.expr);
|
||||
} else if (m_it->eident.name == REF) {
|
||||
*m_it = *LC_CopyAST(L->arena, arr_ref);
|
||||
}
|
||||
}
|
||||
m_it->pos = name->pos;
|
||||
}
|
||||
|
||||
LC_DLLAdd(macro->sblock.first, macro->sblock.last, init_item);
|
||||
}
|
||||
|
||||
macro->next = it->next;
|
||||
macro->prev = it->prev;
|
||||
*it = *macro;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @todo: take package into consideration, my current idea is to have a shared package
|
||||
// that is going to be imported into every other package, for now we use only the current package
|
||||
For(array_of_to_gen) {
|
||||
LC_AST *ast = LC_ParseDeclf("ArrayOf%s :: struct { data: *%s; len: int; cap: int; }", (char *)it, (char *)it);
|
||||
LC_AST *file = package->apackage.ffile;
|
||||
|
||||
LC_DLLAdd(file->afile.fdecl, file->afile.ldecl, ast);
|
||||
}
|
||||
|
||||
LC_OrderAndResolveTopLevelDecls(name);
|
||||
LC_ResolveAllProcBodies();
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
LC_String code = LC_GenerateUnityBuild(L->ordered_packages);
|
||||
OS_MakeDir("examples/add_dynamic_array_macro");
|
||||
OS_WriteFile("examples/add_dynamic_array_macro/add_dynamic_array_macro.c", code);
|
||||
|
||||
return true;
|
||||
}
|
||||
64
examples/add_dynamic_array_macro/main.lc
Normal file
64
examples/add_dynamic_array_macro/main.lc
Normal file
@@ -0,0 +1,64 @@
|
||||
import "libc";
|
||||
|
||||
try_growing_array :: proc(arrp: *void, element_size: size_t) {
|
||||
arr: *ArrayOfvoid = arrp;
|
||||
if (arr.len + 1 > arr.cap) {
|
||||
cap := arr.cap * 2;
|
||||
if (cap == 0) cap = 16;
|
||||
|
||||
arr.data = realloc(arr.data, element_size * :size_t(cap));
|
||||
arr.cap = cap;
|
||||
}
|
||||
}
|
||||
|
||||
init_array_base :: proc(arrp: *void, size: int, element_size: size_t) {
|
||||
arr: *ArrayOfvoid = arrp;
|
||||
arr.data = malloc(element_size * :size_t(size * 2));
|
||||
arr.len = size * 2;
|
||||
}
|
||||
|
||||
main :: proc(): int {
|
||||
{
|
||||
arr: ArrayOfint;
|
||||
init_array(&arr, {0, 1, 2, 3, 4, 6, 7, 9, 10});
|
||||
for i := 0; i < 10; i += 1 {
|
||||
assert(arr.data[i] == i);
|
||||
}
|
||||
|
||||
|
||||
|
||||
for it := &arr.data[0]; it < &arr.data[arr.len]; it = &it[1] {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
arr: ArrayOfint;
|
||||
|
||||
for i := 0; i < 128; i += 1 {
|
||||
add(&arr, i);
|
||||
}
|
||||
|
||||
for i := 0; i < 128; i += 1 {
|
||||
assert(arr.data[i] == i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
arr: ArrayOffloat;
|
||||
|
||||
for i: float = 0; i < 128; i += 1 {
|
||||
add(&arr, i);
|
||||
}
|
||||
|
||||
for i := 0; i < 128; i += 1 {
|
||||
i0 := :int(arr.data[i]);
|
||||
assert(i0 == i);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
62
examples/add_instrumentation/build.cpp
Normal file
62
examples/add_instrumentation/build.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
bool OnDeclParsed_AddInstrumentation(bool discard, LC_AST *n) {
|
||||
if (discard) return discard;
|
||||
|
||||
if (n->kind == LC_ASTKind_DeclProc && n->dproc.body) {
|
||||
if (n->dbase.name == LC_ILit("BeginProc")) return false;
|
||||
if (n->dbase.name == LC_ILit("EndProc")) return false;
|
||||
|
||||
LC_AST *body = n->dproc.body;
|
||||
|
||||
LC_AST *begin = LC_ParseStmtf("BeginProc(\"%s\", \"%s\", %d)", n->dbase.name, n->pos->lex->file, n->pos->line);
|
||||
LC_AST *end = LC_ParseStmtf("defer EndProc();");
|
||||
|
||||
LC_DLLAddFront(body->sblock.first, body->sblock.last, end);
|
||||
LC_DLLAddFront(body->sblock.first, body->sblock.last, begin);
|
||||
}
|
||||
return discard;
|
||||
}
|
||||
|
||||
bool add_instrumentation() {
|
||||
LC_Lang *lang = LC_LangAlloc();
|
||||
lang->use_colored_terminal_output = UseColoredIO;
|
||||
lang->breakpoint_on_error = BreakpointOnError;
|
||||
lang->on_decl_parsed = OnDeclParsed_AddInstrumentation;
|
||||
LC_LangBegin(lang);
|
||||
|
||||
LC_RegisterPackageDir("../pkgs");
|
||||
LC_RegisterPackageDir("../examples");
|
||||
|
||||
LC_Intern name = LC_ILit("add_instrumentation");
|
||||
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
DebugVerifyAST(packages);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
S8_String code = LC_GenerateUnityBuild(packages);
|
||||
S8_String path = "examples/add_instrumentation/add_instrumentation.c";
|
||||
OS_MakeDir("examples");
|
||||
OS_MakeDir("examples/add_instrumentation");
|
||||
OS_WriteFile(path, code);
|
||||
if (!UseCL) {
|
||||
LC_LangEnd(lang);
|
||||
return true;
|
||||
}
|
||||
|
||||
S8_String cmd = Fmt("cl %.*s -Zi -std:c11 -nologo -FC -Fd:examples/add_instrumentation/a.pdb -Fe:examples/add_instrumentation/add_instrumentation.exe %.*s", S8_Expand(path), S8_Expand(RaylibLIB));
|
||||
int errcode = Run(cmd);
|
||||
if (errcode != 0) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
// Run("examples/add_instrumentation/add_instrumentation.exe");
|
||||
|
||||
LC_LangEnd(lang);
|
||||
return true;
|
||||
}
|
||||
13
examples/add_instrumentation/main.lc
Normal file
13
examples/add_instrumentation/main.lc
Normal file
@@ -0,0 +1,13 @@
|
||||
import c "libc";
|
||||
|
||||
BeginProc :: proc(func: *char, file: *char, line: int) {
|
||||
c.printf("begin timming proc: %s at %s:%d\n", func, file, line);
|
||||
}
|
||||
|
||||
EndProc :: proc() {
|
||||
c.printf("end timming proc\n");
|
||||
}
|
||||
|
||||
main :: proc(): int {
|
||||
return 0;
|
||||
}
|
||||
56
examples/add_printf_format_check/build.cpp
Normal file
56
examples/add_printf_format_check/build.cpp
Normal file
@@ -0,0 +1,56 @@
|
||||
void OnExprResolved_CheckPrintf(LC_AST *n, LC_Operand *op);
|
||||
|
||||
bool add_printf_format_check() {
|
||||
bool result = false;
|
||||
LC_Lang *lang = LC_LangAlloc();
|
||||
lang->use_colored_terminal_output = UseColoredIO;
|
||||
lang->breakpoint_on_error = BreakpointOnError;
|
||||
lang->user_data = (void *)&result;
|
||||
lang->on_expr_resolved = OnExprResolved_CheckPrintf;
|
||||
lang->on_message = LC_IgnoreMessage;
|
||||
LC_LangBegin(lang);
|
||||
LC_RegisterPackageDir("../pkgs");
|
||||
LC_RegisterPackageDir("../examples");
|
||||
|
||||
LC_Intern name = LC_ILit("add_printf_format_check");
|
||||
LC_ResolvePackageByName(name);
|
||||
|
||||
LC_LangEnd(lang);
|
||||
return result;
|
||||
}
|
||||
|
||||
void OnExprResolved_CheckPrintf(LC_AST *n, LC_Operand *op) {
|
||||
if (n->kind == LC_ASTKind_ExprCall) {
|
||||
LC_AST *name = n->ecompo.name;
|
||||
if (name->kind == LC_ASTKind_ExprIdent) {
|
||||
LC_Decl *decl = name->eident.resolved_decl;
|
||||
if (decl->name == LC_ILit("printf")) {
|
||||
LC_ResolvedCompo *items = n->ecompo.resolved_items;
|
||||
LC_AST *string = items->first->expr;
|
||||
IO_Assert(string->kind == LC_ASTKind_ExprString);
|
||||
|
||||
char *c = (char *)string->eatom.name;
|
||||
LC_ResolvedCompoItem *item = items->first->next;
|
||||
for (int i = 0; c[i]; i += 1) {
|
||||
if (c[i] == '%') {
|
||||
if (item == NULL) {
|
||||
LC_ReportASTError(n, "too few arguments passed");
|
||||
|
||||
// Test reports success if error thrown correctly
|
||||
*(bool *)L->user_data = true;
|
||||
return;
|
||||
}
|
||||
if (c[i + 1] == 'd') {
|
||||
if (item->expr->type != L->tint) LC_ReportASTError(item->expr, "expected int type");
|
||||
item = item->next;
|
||||
} else if (c[i + 1] == 's') {
|
||||
if (item->expr->type != L->tpchar) LC_ReportASTError(item->expr, "expected *char type");
|
||||
item = item->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (item != NULL) LC_ReportASTError(item->expr, "too many arguments passed");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
6
examples/add_printf_format_check/main.lc
Normal file
6
examples/add_printf_format_check/main.lc
Normal file
@@ -0,0 +1,6 @@
|
||||
import "libc";
|
||||
|
||||
main :: proc(): int {
|
||||
printf("testing %d %s %d\n", 32, "str");
|
||||
return 0;
|
||||
}
|
||||
48
examples/add_source_location_macro/build.cpp
Normal file
48
examples/add_source_location_macro/build.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/* This function is called after compiler determined procedure type but
|
||||
** before the arguments got verified. This opens up a window for call manipulation.
|
||||
**
|
||||
** I basically swoop in and add the {file, line} information to the call LC_AST before
|
||||
** the arguments get resolved.
|
||||
*/
|
||||
void ModifyCallsDuringResolution(LC_AST *n, LC_Type *type) {
|
||||
LC_TypeMember *tm = type->tproc.args.last;
|
||||
if (tm && tm->type->kind == LC_TypeKind_Struct && tm->type->decl->name == LC_ILit("SourceLoc")) {
|
||||
LC_AST *call_item = LC_CreateAST(n->pos, LC_ASTKind_ExprCallItem);
|
||||
call_item->ecompo_item.name = LC_ILit("source_loc");
|
||||
call_item->ecompo_item.expr = LC_ParseExprf("{\"%s\", %d}", n->pos->lex->file, n->pos->line);
|
||||
LC_SetASTPosOnAll(call_item->ecompo_item.expr, n->pos);
|
||||
|
||||
LC_DLLAdd(n->ecompo.first, n->ecompo.last, call_item);
|
||||
n->ecompo.size += 1;
|
||||
}
|
||||
}
|
||||
|
||||
bool add_source_location_macro() {
|
||||
LC_Lang *lang = LC_LangAlloc();
|
||||
lang->use_colored_terminal_output = UseColoredIO;
|
||||
lang->breakpoint_on_error = BreakpointOnError;
|
||||
lang->before_call_args_resolved = ModifyCallsDuringResolution;
|
||||
LC_LangBegin(lang);
|
||||
LC_RegisterPackageDir("../examples/");
|
||||
LC_RegisterPackageDir("../pkgs");
|
||||
|
||||
LC_Intern name = LC_ILit("add_source_location_macro");
|
||||
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
S8_String dir = "examples/add_source_location_macro";
|
||||
OS_MakeDir(dir);
|
||||
S8_String cfile = "examples/add_source_location_macro/add_source_location_macro.c";
|
||||
S8_String code = LC_GenerateUnityBuild(packages);
|
||||
OS_WriteFile(cfile, code);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
LC_LangEnd(lang);
|
||||
return true;
|
||||
}
|
||||
16
examples/add_source_location_macro/main.lc
Normal file
16
examples/add_source_location_macro/main.lc
Normal file
@@ -0,0 +1,16 @@
|
||||
import "libc";
|
||||
|
||||
SourceLoc :: struct {
|
||||
file: String;
|
||||
line: int;
|
||||
}
|
||||
|
||||
Allocate :: proc(size: int, source_loc: SourceLoc = {}): *void {
|
||||
printf("%.*s:%d\n", source_loc.file.len, source_loc.file.str, source_loc.line);
|
||||
return malloc(:size_t(size));
|
||||
}
|
||||
|
||||
main :: proc(): int {
|
||||
value: *int = Allocate(32);
|
||||
return 0;
|
||||
}
|
||||
30
examples/create_raylib_window/build.cpp
Normal file
30
examples/create_raylib_window/build.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
bool create_raylib_window() {
|
||||
LC_Lang *lang = LC_LangAlloc();
|
||||
lang->use_colored_terminal_output = UseColoredIO;
|
||||
lang->breakpoint_on_error = BreakpointOnError;
|
||||
LC_LangBegin(lang);
|
||||
LC_RegisterPackageDir("../examples/");
|
||||
LC_RegisterPackageDir("../pkgs");
|
||||
|
||||
LC_Intern name = LC_ILit("create_raylib_window");
|
||||
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
OS_MakeDir("examples/create_raylib_window");
|
||||
S8_String path = "examples/create_raylib_window/create_raylib_window.c";
|
||||
S8_String code = LC_GenerateUnityBuild(packages);
|
||||
OS_WriteFile(path, code);
|
||||
|
||||
bool success = true;
|
||||
if (UseCL) {
|
||||
OS_CopyFile(RaylibDLL, "examples/create_raylib_window/raylib.dll", true);
|
||||
S8_String cmd = Fmt("cl %.*s -Zi -std:c11 -nologo -FC -Fd:examples/create_raylib_window/a.pdb -Fe:examples/create_raylib_window/create_raylib_window.exe %.*s", S8_Expand(path), S8_Expand(RaylibLIB));
|
||||
int errcode = Run(cmd);
|
||||
if (errcode != 0) success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
16
examples/create_raylib_window/main.lc
Normal file
16
examples/create_raylib_window/main.lc
Normal file
@@ -0,0 +1,16 @@
|
||||
import RL "raylib";
|
||||
|
||||
main :: proc(): int {
|
||||
RL.InitWindow(800, 600, "Thing");
|
||||
RL.SetTargetFPS(60);
|
||||
|
||||
for !RL.WindowShouldClose() {
|
||||
RL.BeginDrawing();
|
||||
RL.ClearBackground(RL.RAYWHITE);
|
||||
RL.DrawText("Congrats! You created your first window!", 190, 200, 20, RL.LIGHTGRAY);
|
||||
RL.EndDrawing();
|
||||
}
|
||||
|
||||
RL.CloseWindow();
|
||||
return 0;
|
||||
}
|
||||
64
examples/generate_type_info/build.cpp
Normal file
64
examples/generate_type_info/build.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
bool generate_type_info() {
|
||||
OS_DeleteFile("../examples/generate_type_info/generated.lc");
|
||||
|
||||
LC_Lang *lang = LC_LangAlloc();
|
||||
lang->use_colored_terminal_output = UseColoredIO;
|
||||
lang->breakpoint_on_error = BreakpointOnError;
|
||||
LC_LangBegin(lang);
|
||||
|
||||
LC_RegisterPackageDir("../pkgs");
|
||||
LC_RegisterPackageDir("../examples");
|
||||
|
||||
LC_Intern name = LC_ILit("generate_type_info");
|
||||
|
||||
LC_ParsePackagesUsingRegistry(name);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
LC_OrderAndResolveTopLevelDecls(name);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
LC_BeginStringGen(L->arena);
|
||||
LC_AST *package = LC_GetPackageByName(name);
|
||||
|
||||
// Iterate through all the types and generate struct member information
|
||||
LC_Type *first = (LC_Type *)L->type_arena->memory.data;
|
||||
for (int i = 0; i < L->type_count; i += 1) {
|
||||
LC_Type *type = first + i;
|
||||
if (type->kind != LC_TypeKind_Struct) continue;
|
||||
|
||||
LC_GenLinef("%s_Members := :[]StructMem{", (char *)type->decl->name);
|
||||
L->printer.indent += 1;
|
||||
|
||||
LC_TypeFor(it, type->tagg.mems.first) {
|
||||
LC_GenLinef("{name = \"%s\", offset = %d},", (char *)it->name, it->offset);
|
||||
}
|
||||
|
||||
L->printer.indent -= 1;
|
||||
LC_GenLinef("};");
|
||||
}
|
||||
|
||||
S8_String code = LC_EndStringGen(L->arena);
|
||||
S8_String path = OS_GetAbsolutePath(L->arena, "../examples/generate_type_info/generated.lc");
|
||||
OS_WriteFile(path, code);
|
||||
|
||||
LC_ParseFile(package, path.str, code.str, 0);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Resolve decls again with new content added in
|
||||
LC_OrderAndResolveTopLevelDecls(name);
|
||||
LC_ResolveAllProcBodies();
|
||||
LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls();
|
||||
|
||||
bool result = L->errors ? false : true;
|
||||
LC_LangEnd(lang);
|
||||
return result;
|
||||
}
|
||||
13
examples/generate_type_info/generated.lc
Normal file
13
examples/generate_type_info/generated.lc
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
StructMem_Members := :[]StructMem{
|
||||
{name = "name", offset = 0},
|
||||
{name = "offset", offset = 8},
|
||||
};
|
||||
Typeinfo_Members := :[]StructMem{
|
||||
{name = "kind", offset = 0},
|
||||
{name = "name", offset = 8},
|
||||
{name = "size", offset = 16},
|
||||
{name = "align", offset = 20},
|
||||
{name = "child_count", offset = 24},
|
||||
{name = "struct_mems", offset = 32},
|
||||
};
|
||||
19
examples/generate_type_info/main.lc
Normal file
19
examples/generate_type_info/main.lc
Normal file
@@ -0,0 +1,19 @@
|
||||
TypeKind :: typedef int;
|
||||
TYPE_KIND_STRUCT :: 0;
|
||||
|
||||
StructMem :: struct {
|
||||
name: *char;
|
||||
offset: int;
|
||||
}
|
||||
|
||||
Typeinfo :: struct {
|
||||
kind: TypeKind;
|
||||
name: *char;
|
||||
|
||||
size: int;
|
||||
align: int;
|
||||
|
||||
child_count: int;
|
||||
struct_mems: *StructMem;
|
||||
}
|
||||
|
||||
29
examples/hello_world/build.cpp
Normal file
29
examples/hello_world/build.cpp
Normal file
@@ -0,0 +1,29 @@
|
||||
bool hello_world() {
|
||||
LC_Lang *lang = LC_LangAlloc();
|
||||
lang->use_colored_terminal_output = UseColoredIO;
|
||||
lang->breakpoint_on_error = BreakpointOnError;
|
||||
LC_LangBegin(lang);
|
||||
LC_RegisterPackageDir("../examples/");
|
||||
LC_RegisterPackageDir("../pkgs");
|
||||
|
||||
LC_Intern name = LC_ILit("hello_world");
|
||||
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
OS_MakeDir("examples/hello_world");
|
||||
S8_String code = LC_GenerateUnityBuild(packages);
|
||||
S8_String path = "examples/hello_world/hello_world.c";
|
||||
OS_WriteFile(path, code);
|
||||
|
||||
bool success = true;
|
||||
if (UseCL) {
|
||||
S8_String cmd = Fmt("cl %.*s -Zi -std:c11 -nologo -FC -Fd:examples/hello_world/hello_world.pdb -Fe:examples/hello_world/hello_world.exe %.*s", S8_Expand(path), S8_Expand(RaylibLIB));
|
||||
if (Run(cmd) != 0) success = false;
|
||||
}
|
||||
|
||||
LC_LangEnd(lang);
|
||||
return success;
|
||||
}
|
||||
6
examples/hello_world/main.lc
Normal file
6
examples/hello_world/main.lc
Normal file
@@ -0,0 +1,6 @@
|
||||
import "libc";
|
||||
|
||||
main :: proc(): int {
|
||||
printf("hello world!\n");
|
||||
return 0;
|
||||
}
|
||||
147
examples/pathfinding_visualizer/array.lc
Normal file
147
examples/pathfinding_visualizer/array.lc
Normal file
@@ -0,0 +1,147 @@
|
||||
AddActorToArray :: proc(arr: *ArrayOfActors, actor: Actor) {
|
||||
if arr.len + 1 > arr.cap {
|
||||
new_cap := arr.cap * 2;
|
||||
if (new_cap == 0) new_cap = 16;
|
||||
|
||||
arr.data = libc.realloc(arr.data, sizeof(:Actor) * :libc.size_t(new_cap));
|
||||
arr.cap = new_cap;
|
||||
}
|
||||
|
||||
arr.data[arr.len] = actor;
|
||||
arr.len += 1;
|
||||
}
|
||||
|
||||
TryGrowingActorArray :: proc(a: *ArrayOfActors) {
|
||||
if a.len + 1 > a.cap {
|
||||
new_cap := a.cap * 2;
|
||||
if (new_cap == 0) new_cap = 16;
|
||||
a.data = libc.realloc(a.data, sizeof(:Actor) * :libc.size_t(new_cap));
|
||||
a.cap = new_cap;
|
||||
}
|
||||
}
|
||||
|
||||
InsertActor :: proc(a: *ArrayOfActors, item: Actor, index: int) {
|
||||
if index == a.len {
|
||||
AddActorToArray(a, item);
|
||||
return;
|
||||
}
|
||||
|
||||
Assert(index < a.len);
|
||||
Assert(index >= 0);
|
||||
|
||||
TryGrowingActorArray(a);
|
||||
right_len := :libc.size_t(a.len - index);
|
||||
libc.memmove(&a.data[index + 1], &a.data[index], sizeof(:Actor) * right_len);
|
||||
a.data[index] = item;
|
||||
a.len += 1;
|
||||
}
|
||||
|
||||
GetLastActor :: proc(a: ArrayOfActors): *Actor {
|
||||
Assert(a.len > 0);
|
||||
result := &a.data[a.len - 1];
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
AddPath :: proc(arr: *ArrayOfPaths, actor: Path) {
|
||||
if arr.len + 1 > arr.cap {
|
||||
new_cap := arr.cap * 2;
|
||||
if (new_cap == 0) new_cap = 16;
|
||||
|
||||
arr.data = libc.realloc(arr.data, sizeof(:Path) * :libc.size_t(new_cap));
|
||||
arr.cap = new_cap;
|
||||
}
|
||||
|
||||
arr.data[arr.len] = actor;
|
||||
arr.len += 1;
|
||||
}
|
||||
|
||||
TryGrowingPathArray :: proc(a: *ArrayOfPaths) {
|
||||
if a.len + 1 > a.cap {
|
||||
new_cap := a.cap * 2;
|
||||
if (new_cap == 0) new_cap = 16;
|
||||
a.data = libc.realloc(a.data, sizeof(:Path) * :libc.size_t(new_cap));
|
||||
a.cap = new_cap;
|
||||
}
|
||||
}
|
||||
|
||||
InsertPath :: proc(a: *ArrayOfPaths, item: Path, index: int) {
|
||||
if index == a.len {
|
||||
AddPath(a, item);
|
||||
return;
|
||||
}
|
||||
|
||||
Assert(index < a.len);
|
||||
Assert(index >= 0);
|
||||
|
||||
TryGrowingPathArray(a);
|
||||
right_len := :libc.size_t(a.len - index);
|
||||
libc.memmove(&a.data[index + 1], &a.data[index], sizeof(:Path) * right_len);
|
||||
a.data[index] = item;
|
||||
a.len += 1;
|
||||
}
|
||||
|
||||
InsertSortedPath :: proc(a: *ArrayOfPaths, item: Path) {
|
||||
insert_index := -1;
|
||||
for i := 0; i < a.len; i += 1 {
|
||||
it := &a.data[i];
|
||||
if it.value_to_sort_by <= item.value_to_sort_by {
|
||||
insert_index = i;
|
||||
InsertPath(a, item, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if insert_index == -1 {
|
||||
AddPath(a, item);
|
||||
}
|
||||
}
|
||||
|
||||
GetLastPath :: proc(a: ArrayOfPaths): *Path {
|
||||
Assert(a.len > 0);
|
||||
result := &a.data[a.len - 1];
|
||||
return result;
|
||||
}
|
||||
|
||||
PopPath :: proc(a: *ArrayOfPaths): Path {
|
||||
a.len -= 1;
|
||||
result := a.data[a.len];
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
AddV2I :: proc(arr: *ArrayOfV2Is, item: V2I) {
|
||||
if arr.len + 1 > arr.cap {
|
||||
new_cap := arr.cap * 2;
|
||||
if (new_cap == 0) new_cap = 16;
|
||||
|
||||
arr.data = libc.realloc(arr.data, sizeof(:V2I) * :libc.size_t(new_cap));
|
||||
arr.cap = new_cap;
|
||||
}
|
||||
|
||||
arr.data[arr.len] = item;
|
||||
arr.len += 1;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
AddAnimationSetTile :: proc(arr: *ArrayOfAnimationSetTiles, item: AnimationSetTile) {
|
||||
if arr.len + 1 > arr.cap {
|
||||
new_cap := arr.cap * 2;
|
||||
if (new_cap == 0) new_cap = 16;
|
||||
|
||||
arr.data = libc.realloc(arr.data, sizeof(:AnimationSetTile) * :libc.size_t(new_cap));
|
||||
arr.cap = new_cap;
|
||||
}
|
||||
|
||||
arr.data[arr.len] = item;
|
||||
arr.len += 1;
|
||||
}
|
||||
|
||||
UnorderedRemoveAnimationSetTile :: proc(a: *ArrayOfAnimationSetTiles, item: *AnimationSetTile) {
|
||||
Assert(a.len > 0);
|
||||
Assert(item >= a.data && item < &a.data[a.len]);
|
||||
a.len -= 1;
|
||||
*item = a.data[a.len];
|
||||
}
|
||||
42
examples/pathfinding_visualizer/build.cpp
Normal file
42
examples/pathfinding_visualizer/build.cpp
Normal file
@@ -0,0 +1,42 @@
|
||||
bool pathfinding_visualizer() {
|
||||
LC_Lang *lang = LC_LangAlloc();
|
||||
lang->use_colored_terminal_output = UseColoredIO;
|
||||
lang->breakpoint_on_error = BreakpointOnError;
|
||||
LC_LangBegin(lang);
|
||||
|
||||
LC_RegisterPackageDir("../examples/");
|
||||
LC_RegisterPackageDir("../pkgs");
|
||||
|
||||
LC_Intern name = LC_ILit("pathfinding_visualizer");
|
||||
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||
LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls();
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
DebugVerifyAST(packages);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
OS_MakeDir("examples");
|
||||
OS_MakeDir("examples/pathfinding_visualizer");
|
||||
S8_String code = LC_GenerateUnityBuild(packages);
|
||||
S8_String path = "examples/pathfinding_visualizer/pathfinding_visualizer.c";
|
||||
OS_WriteFile(path, code);
|
||||
|
||||
if (!UseCL) {
|
||||
LC_LangEnd(lang);
|
||||
return true;
|
||||
}
|
||||
|
||||
S8_String cmd = Fmt("cl %.*s -Zi -std:c11 -nologo -FC -Fd:examples/pathfinding_visualizer/a.pdb -Fe:examples/pathfinding_visualizer/pathfinding_visualizer.exe %.*s", S8_Expand(path), S8_Expand(RaylibLIB));
|
||||
int errcode = Run(cmd);
|
||||
OS_CopyFile(RaylibDLL, "examples/pathfinding_visualizer/raylib.dll", true);
|
||||
|
||||
LC_LangEnd(lang);
|
||||
bool result = errcode == 0;
|
||||
return result;
|
||||
}
|
||||
266
examples/pathfinding_visualizer/main.lc
Normal file
266
examples/pathfinding_visualizer/main.lc
Normal file
@@ -0,0 +1,266 @@
|
||||
import "raylib";
|
||||
import libc "libc";
|
||||
import "std_types";
|
||||
|
||||
WinX := 1280;
|
||||
WinY := 720;
|
||||
Mode := 0;
|
||||
RectX :: 16;
|
||||
RectY :: 16;
|
||||
Dt: float;
|
||||
|
||||
MouseX:= 0;
|
||||
MouseY:= 0;
|
||||
MouseP: Vector2 = {0, 0};
|
||||
|
||||
MouseSelecting := false;
|
||||
MouseSelectionPivot: Vector2;
|
||||
MouseSelectionBox: Rectangle;
|
||||
MouseSelectedActors: ArrayOfActors;
|
||||
|
||||
AnimationSetTiles: ArrayOfAnimationSetTiles;
|
||||
|
||||
AnimationSetTile :: struct {
|
||||
set: bool;
|
||||
p: V2I;
|
||||
t: float;
|
||||
}
|
||||
|
||||
ArrayOfAnimationSetTiles :: struct {
|
||||
data: *AnimationSetTile;
|
||||
len: int;
|
||||
cap: int;
|
||||
}
|
||||
|
||||
main :: proc(): int {
|
||||
InitMap();
|
||||
InitWindow(WinX, WinY, "pathfinding visualizer");
|
||||
SetTargetFPS(60);
|
||||
|
||||
orange := ORANGE;
|
||||
orange.a = 255/2;
|
||||
brown := BROWN;
|
||||
brown.a = 255/2;
|
||||
dark_green := DARKGREEN;
|
||||
dark_green.a = 255/2;
|
||||
blue := BLUE;
|
||||
blue.a = 255/2;
|
||||
green := GREEN;
|
||||
green.a = 255/2;
|
||||
red := RED;
|
||||
red.a = 255/2;
|
||||
|
||||
actor_color := dark_green;
|
||||
past_actor_color := blue;
|
||||
target_color := red;
|
||||
selection_box_color := green;
|
||||
selected_color := selection_box_color;
|
||||
|
||||
for !WindowShouldClose() {
|
||||
WinX = GetScreenHeight();
|
||||
WinY = GetScreenWidth();
|
||||
MouseX = GetMouseX();
|
||||
MouseY = GetMouseY();
|
||||
MouseP = GetMousePosition();
|
||||
Dt = GetFrameTime();
|
||||
@unused map := &CurrentMap;
|
||||
|
||||
MouseSelecting = false;
|
||||
if IsMouseButtonDown(MOUSE_BUTTON_LEFT) {
|
||||
MouseSelecting = true;
|
||||
if IsMouseButtonPressed(MOUSE_BUTTON_LEFT) {
|
||||
MouseSelectionPivot = MouseP;
|
||||
}
|
||||
|
||||
MouseSelectionBox = {
|
||||
MouseSelectionPivot.x,
|
||||
MouseSelectionPivot.y,
|
||||
MouseP.x - MouseSelectionPivot.x,
|
||||
MouseP.y - MouseSelectionPivot.y,
|
||||
};
|
||||
|
||||
if MouseSelectionBox.width < 0 {
|
||||
MouseSelectionBox.x += MouseSelectionBox.width;
|
||||
MouseSelectionBox.width = -MouseSelectionBox.width;
|
||||
}
|
||||
if MouseSelectionBox.height < 0 {
|
||||
MouseSelectionBox.y += MouseSelectionBox.height;
|
||||
MouseSelectionBox.height = -MouseSelectionBox.height;
|
||||
}
|
||||
}
|
||||
|
||||
if IsKeyPressed(KEY_F1) {
|
||||
Mode = 0;
|
||||
}
|
||||
|
||||
if IsKeyPressed(KEY_F2) {
|
||||
Mode = 1;
|
||||
}
|
||||
|
||||
if IsKeyPressed(KEY_F3) {
|
||||
for i := 0; i < map.actors.len; i += 1 {
|
||||
it := &map.actors.data[i];
|
||||
MoveTowardsTarget(it);
|
||||
}
|
||||
}
|
||||
PathFindUpdate(map);
|
||||
|
||||
if IsKeyPressed(KEY_F4) {
|
||||
RandomizeActors();
|
||||
}
|
||||
|
||||
BeginDrawing();
|
||||
ClearBackground(RAYWHITE);
|
||||
|
||||
map_rectangle: Rectangle = {0, 0, :float(map.x) * RectX, :float(map.y) * RectY};
|
||||
DrawRectangleRec(map_rectangle, LIGHTGRAY);
|
||||
|
||||
for x := 0; x < map.x; x += 1 {
|
||||
for y := 0; y < map.y; y += 1 {
|
||||
it := map.data[x + y*map.x];
|
||||
r : Rectangle = {:float(x) * RectX, :float(y) * RectY, RectX, RectY};
|
||||
r2: Rectangle = {r.x + 1, r.y + 1, r.width - 2, r.height - 2};
|
||||
|
||||
colliding := CheckCollisionPointRec(MouseP, r);
|
||||
color := RAYWHITE;
|
||||
if it == 1 { color = GRAY; }
|
||||
|
||||
if Mode == 0 {
|
||||
if colliding && IsMouseButtonDown(MOUSE_BUTTON_LEFT) {
|
||||
AddAnimationSetTile(&AnimationSetTiles, {true, {x,y}});
|
||||
}
|
||||
if colliding && IsMouseButtonDown(MOUSE_BUTTON_RIGHT) {
|
||||
AddAnimationSetTile(&AnimationSetTiles, {false, {x,y}});
|
||||
}
|
||||
if colliding {
|
||||
color.a = 100;
|
||||
}
|
||||
}
|
||||
|
||||
DrawRectangleRec(r2, color);
|
||||
}
|
||||
}
|
||||
|
||||
for tile_i := 0; tile_i < AnimationSetTiles.len; tile_i += 1 {
|
||||
tile_it := &AnimationSetTiles.data[tile_i];
|
||||
remove := false;
|
||||
|
||||
t := tile_it.t;
|
||||
if tile_it.set == false {
|
||||
t = 1 - t;
|
||||
}
|
||||
|
||||
x := :float(tile_it.p.x) * RectX + 1;
|
||||
y := :float(tile_it.p.y) * RectY + 1;
|
||||
|
||||
w: float = RectX - 2;
|
||||
h: float = RectY - 2;
|
||||
wt := w * t;
|
||||
ht := h * t;
|
||||
wd := w - wt;
|
||||
hd := h - ht;
|
||||
|
||||
r: Rectangle = {x + wd/2, y + hd/2, wt, ht};
|
||||
DrawRectangleRec(r, GRAY);
|
||||
|
||||
if tile_it.t > 1 {
|
||||
map_tile := &map.data[tile_it.p.x + tile_it.p.y*map.x];
|
||||
if tile_it.set {*map_tile |= TILE_BLOCKER;}
|
||||
else {*map_tile &= ~TILE_BLOCKER;}
|
||||
|
||||
remove = true;
|
||||
}
|
||||
|
||||
tile_it.t += Dt*8;
|
||||
if remove {
|
||||
UnorderedRemoveAnimationSetTile(&AnimationSetTiles, tile_it);
|
||||
tile_i -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
for actor_i := 0; actor_i < map.actors.len; actor_i += 1 {
|
||||
actor_it := &map.actors.data[actor_i];
|
||||
target_r := Rect(actor_it.target_p);
|
||||
|
||||
main_p := Circle(actor_it.p);
|
||||
DrawCircleV(main_p, RectX/2, actor_color);
|
||||
DrawRectangleRec(target_r, target_color);
|
||||
|
||||
smaller_the_further: float;
|
||||
for tile_i := actor_it.tiles_visited.len - 1; tile_i >= 0; tile_i -= 1 {
|
||||
tile_it := &actor_it.tiles_visited.data[tile_i];
|
||||
p := Circle({tile_it.x, tile_it.y});
|
||||
DrawCircleV(p, RectX/2 - smaller_the_further, past_actor_color);
|
||||
smaller_the_further += 0.5;
|
||||
}
|
||||
|
||||
for path_i := 0; path_i < actor_it.open_paths.len; path_i += 1 {
|
||||
path_it := &actor_it.open_paths.data[path_i];
|
||||
path_r := Rect(path_it.p);
|
||||
DrawRectangleRec(path_r, orange);
|
||||
s := TextFormat("%d", :int(libc.sqrtf(:float(path_it.value_to_sort_by))));
|
||||
DrawText(s, :int(path_r.x), :int(path_r.y), 1, RAYWHITE);
|
||||
}
|
||||
|
||||
for path_i := 0; path_i < actor_it.close_paths.len; path_i += 1 {
|
||||
path_it := &actor_it.close_paths.data[path_i];
|
||||
path_r := Rect(path_it.p);
|
||||
DrawRectangleRec(path_r, brown);
|
||||
}
|
||||
|
||||
for path_i := 0; path_i < actor_it.history.len; path_i += 1 {
|
||||
path_it := &actor_it.history.data[path_i];
|
||||
|
||||
p0 := Circle(path_it.came_from);
|
||||
p1 := Circle(path_it.p);
|
||||
|
||||
DrawLineEx(p0, p1, 5, LIGHTGRAY);
|
||||
DrawCircleV(p0, 4, LIGHTGRAY);
|
||||
DrawCircleV(p1, 4, LIGHTGRAY);
|
||||
}
|
||||
}
|
||||
|
||||
if Mode == 1 {
|
||||
for actor_i := 0; actor_i < MouseSelectedActors.len; actor_i += 1 {
|
||||
actor_it := &MouseSelectedActors.data[actor_i];
|
||||
actor_box := Rect(actor_it.p);
|
||||
DrawRectangleRec(actor_box, selected_color);
|
||||
|
||||
if IsMouseButtonPressed(MOUSE_BUTTON_RIGHT) {
|
||||
p := ScreenToMap(MouseP);
|
||||
SetTargetP(actor_it, p);
|
||||
}
|
||||
}
|
||||
|
||||
if MouseSelecting {
|
||||
MouseSelectedActors.len = 0;
|
||||
for actor_i := 0; actor_i < MouseSelectedActors.len; actor_i += 1 {
|
||||
actor_it := &MouseSelectedActors.data[actor_i];
|
||||
actor_box := Rect(actor_it.p);
|
||||
|
||||
if CheckCollisionRecs(actor_box, MouseSelectionBox) {
|
||||
AddActorToArray(&MouseSelectedActors, *actor_it);
|
||||
}
|
||||
}
|
||||
DrawRectangleRec(MouseSelectionBox, selection_box_color);
|
||||
}
|
||||
}
|
||||
|
||||
text_size := 24;
|
||||
text_p := 4;
|
||||
text_y := text_size * 2;
|
||||
|
||||
DrawText("F4 :: Randomize actors", text_p, text_y, text_size, GRAY);
|
||||
text_y -= text_size;
|
||||
DrawText("F3 :: Simulate actors", text_p, text_y, text_size, GRAY);
|
||||
text_y -= text_size;
|
||||
text: *char = "Mode(F1) :: Block placing";
|
||||
if Mode == 1 { text = "Mode(F2) :: Actor placing"; }
|
||||
DrawText(text, text_p, text_y, text_size, GRAY);
|
||||
text_y -= text_size;
|
||||
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
286
examples/pathfinding_visualizer/map.lc
Normal file
286
examples/pathfinding_visualizer/map.lc
Normal file
@@ -0,0 +1,286 @@
|
||||
CurrentMap: Map;
|
||||
|
||||
Tile :: typedef int;
|
||||
TILE_BLOCKER :: 1;
|
||||
TILE_ACTOR_IS_STANDING :: <<;
|
||||
|
||||
Map :: struct {
|
||||
data: *Tile;
|
||||
x: int;
|
||||
y: int;
|
||||
actors: ArrayOfActors;
|
||||
}
|
||||
|
||||
V2I :: struct {
|
||||
x: int;
|
||||
y: int;
|
||||
}
|
||||
|
||||
Actor :: struct {
|
||||
p: V2I;
|
||||
target_p: V2I;
|
||||
map: *Map;
|
||||
|
||||
open_paths: ArrayOfPaths;
|
||||
close_paths: ArrayOfPaths;
|
||||
tiles_visited: ArrayOfV2Is;
|
||||
history: ArrayOfPaths;
|
||||
}
|
||||
|
||||
Path :: struct {
|
||||
value_to_sort_by: int;
|
||||
p: V2I;
|
||||
came_from: V2I;
|
||||
}
|
||||
|
||||
ArrayOfActors :: struct {
|
||||
data: *Actor;
|
||||
len: int;
|
||||
cap: int;
|
||||
}
|
||||
|
||||
ArrayOfPaths :: struct {
|
||||
data: *Path;
|
||||
len: int;
|
||||
cap: int;
|
||||
}
|
||||
|
||||
ArrayOfV2Is :: struct {
|
||||
data: *V2I;
|
||||
len: int;
|
||||
cap: int;
|
||||
}
|
||||
|
||||
Rect :: proc(p: V2I): Rectangle {
|
||||
result: Rectangle = {:float(p.x) * RectX, :float(p.y) * RectY, RectX, RectY};
|
||||
return result;
|
||||
}
|
||||
|
||||
Circle :: proc(p: V2I): Vector2 {
|
||||
result: Vector2 = {:float(p.x) * RectX + RectX/2, :float(p.y) * RectY + RectY/2};
|
||||
return result;
|
||||
}
|
||||
|
||||
ScreenToMap :: proc(p: Vector2): V2I {
|
||||
x := p.x / RectX;
|
||||
y := p.y / RectY;
|
||||
|
||||
result: V2I = {:int(x), :int(y)};
|
||||
return result;
|
||||
}
|
||||
|
||||
MaxInt :: proc(x: int, y: int): int {
|
||||
if x > y return x;
|
||||
return y;
|
||||
}
|
||||
|
||||
Assert :: proc(x: bool) {
|
||||
if (!x) {
|
||||
libc.printf("assertion failed\n");
|
||||
libc.fflush(libc.stdout);
|
||||
libc.debug_break();
|
||||
}
|
||||
}
|
||||
|
||||
InvalidCodepath :: proc() {
|
||||
libc.printf("invalid codepath\n");
|
||||
libc.fflush(libc.stdout);
|
||||
libc.debug_break();
|
||||
}
|
||||
|
||||
AddActor :: proc(map: *Map, p: V2I): *Actor {
|
||||
AddActorToArray(&map.actors, {p, p, map});
|
||||
map.data[p.x + p.y * map.x] |= TILE_ACTOR_IS_STANDING;
|
||||
result := GetLastActor(map.actors);
|
||||
return result;
|
||||
}
|
||||
|
||||
SetActorP :: proc(actor: *Actor, p: V2I) {
|
||||
map := actor.map;
|
||||
new_tile := &map.data[p.x + p.y * map.x];
|
||||
if *new_tile != 0 return;
|
||||
|
||||
tile := &map.data[actor.p.x + actor.p.y * map.x];
|
||||
Assert((*tile & TILE_ACTOR_IS_STANDING) != 0);
|
||||
*tile &= ~TILE_ACTOR_IS_STANDING;
|
||||
|
||||
*new_tile |= TILE_ACTOR_IS_STANDING;
|
||||
actor.p = p;
|
||||
|
||||
actor.tiles_visited.len = 0;
|
||||
actor.history.len = 0;
|
||||
actor.open_paths.len = 0;
|
||||
actor.close_paths.len = 0;
|
||||
}
|
||||
|
||||
SetTargetP :: proc(actor: *Actor, p: V2I) {
|
||||
actor.target_p = p;
|
||||
|
||||
actor.tiles_visited.len = 0;
|
||||
actor.history.len = 0;
|
||||
actor.open_paths.len = 0;
|
||||
actor.close_paths.len = 0;
|
||||
}
|
||||
|
||||
GetRandomP :: proc(m: *Map): V2I {
|
||||
result: V2I = {GetRandomInt(0, m.x - 1), GetRandomInt(0, m.y - 1)};
|
||||
return result;
|
||||
}
|
||||
|
||||
GetRandomUnblockedP :: proc(m: *Map): V2I {
|
||||
for i := 0; i < 128; i += 1 {
|
||||
p := GetRandomP(m);
|
||||
if m.data[p.x + p.y * m.x] == 0 {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
Assert(!:ullong(:*char("invalid codepath")));
|
||||
return {};
|
||||
}
|
||||
|
||||
InitMap :: proc() {
|
||||
CurrentMap.x = WinX / RectX;
|
||||
CurrentMap.y = WinY / RectY;
|
||||
|
||||
bytes := sizeof(:Tile) * CurrentMap.x * CurrentMap.y;
|
||||
CurrentMap.data = libc.malloc(:libc.size_t(bytes));
|
||||
libc.memset(CurrentMap.data, 0, :libc.size_t(bytes));
|
||||
|
||||
actor := AddActor(&CurrentMap, GetRandomUnblockedP(&CurrentMap));
|
||||
actor.target_p = GetRandomUnblockedP(&CurrentMap);
|
||||
|
||||
actor2 := AddActor(&CurrentMap, GetRandomUnblockedP(&CurrentMap));
|
||||
actor2.target_p = GetRandomUnblockedP(&CurrentMap);
|
||||
}
|
||||
|
||||
RandomizeActors :: proc() {
|
||||
map := &CurrentMap;
|
||||
for i := 0; i < map.actors.len; i += 1 {
|
||||
it := &map.actors.data[i];
|
||||
p := GetRandomUnblockedP(&CurrentMap);
|
||||
SetActorP(it, p);
|
||||
it.target_p = GetRandomUnblockedP(&CurrentMap);
|
||||
}
|
||||
}
|
||||
|
||||
InsertOpenPath :: proc(actor: *Actor, p: V2I, came_from: V2I, ignore_blocks: bool = false) {
|
||||
if p.x < 0 || p.x >= actor.map.x return;
|
||||
if p.y < 0 || p.y >= actor.map.y return;
|
||||
if ignore_blocks == false && actor.map.data[p.x + p.y * actor.map.x] != 0 return;
|
||||
|
||||
for i := 0; i < actor.close_paths.len; i += 1 {
|
||||
it := &actor.close_paths.data[i];
|
||||
if it.p.x == p.x && it.p.y == p.y return;
|
||||
}
|
||||
|
||||
for i := 0; i < actor.open_paths.len; i += 1 {
|
||||
it := &actor.open_paths.data[i];
|
||||
if it.p.x == p.x && it.p.y == p.y return;
|
||||
}
|
||||
|
||||
dx := actor.target_p.x - p.x;
|
||||
dy := actor.target_p.y - p.y;
|
||||
d := dx*dx + dy*dy;
|
||||
InsertSortedPath(&actor.open_paths, {d, p, came_from});
|
||||
}
|
||||
|
||||
GetCloseP :: proc(actor: *Actor, p: V2I): *Path {
|
||||
for i := 0; i < actor.close_paths.len; i += 1 {
|
||||
it := &actor.close_paths.data[i];
|
||||
if it.p.x == p.x && it.p.y == p.y return it;
|
||||
}
|
||||
InvalidCodepath();
|
||||
return nil;
|
||||
}
|
||||
|
||||
RecomputeHistory :: proc(actor: *Actor) {
|
||||
if actor.close_paths.len > 1 {
|
||||
actor.history.len = 0;
|
||||
it := GetLastPath(actor.close_paths);
|
||||
AddPath(&actor.history, *it);
|
||||
for i := 0;;i += 1 {
|
||||
if it.p.x == actor.p.x && it.p.y == actor.p.y { break; }
|
||||
if i > 512 { actor.history.len = 0; break; } // @todo: Pop after this and error?
|
||||
it = GetCloseP(actor, it.came_from);
|
||||
AddPath(&actor.history, *it);
|
||||
}
|
||||
PopPath(&actor.history);
|
||||
}
|
||||
}
|
||||
|
||||
MoveTowardsTarget :: proc(actor: *Actor) {
|
||||
tile := &actor.map.data[actor.p.x + actor.p.y * actor.map.x];
|
||||
if actor.history.len > 0 {
|
||||
step := PopPath(&actor.history);
|
||||
new_tile := &actor.map.data[step.p.x + step.p.y * actor.map.x];
|
||||
if *new_tile == 0 {
|
||||
AddV2I(&actor.tiles_visited, actor.p);
|
||||
actor.p = step.p;
|
||||
*tile &= ~TILE_ACTOR_IS_STANDING;
|
||||
*new_tile |= TILE_ACTOR_IS_STANDING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PathFindUpdate :: proc(map: *Map) {
|
||||
for actor_i := 0; actor_i < map.actors.len; actor_i += 1 {
|
||||
actor_it := &map.actors.data[actor_i];
|
||||
for i := 0; i < actor_it.history.len; i += 1 {
|
||||
history_it := &actor_it.history.data[i];
|
||||
|
||||
tile := actor_it.map.data[history_it.p.x + history_it.p.y * actor_it.map.x];
|
||||
if tile != 0 {
|
||||
actor_it.close_paths.len = 0;
|
||||
actor_it.open_paths.len = 0;
|
||||
actor_it.history.len = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
PathFind(actor_it);
|
||||
}
|
||||
}
|
||||
|
||||
PathFindStep :: proc(s: *Actor, compute_history: bool = true): bool {
|
||||
if s.open_paths.len == 0 {
|
||||
// Reset if we didn't find solution
|
||||
if s.close_paths.len != 0 {
|
||||
last := GetLastPath(s.close_paths);
|
||||
reached_target := last.p.x == s.target_p.x && last.p.y == s.target_p.y;
|
||||
if reached_target == false {
|
||||
s.close_paths.len = 0;
|
||||
s.open_paths.len = 0;
|
||||
s.history.len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
InsertOpenPath(s, s.p, s.p, ignore_blocks = true);
|
||||
}
|
||||
|
||||
if s.close_paths.len != 0 {
|
||||
last := GetLastPath(s.close_paths);
|
||||
reached_target := last.p.x == s.target_p.x && last.p.y == s.target_p.y;
|
||||
if reached_target return true;
|
||||
}
|
||||
|
||||
it := PopPath(&s.open_paths);
|
||||
AddPath(&s.close_paths, it);
|
||||
|
||||
for y := -1; y <= 1; y += 1 {
|
||||
for x := -1; x <= 1; x += 1 {
|
||||
if x == 0 && y == 0 continue;
|
||||
p: V2I = {it.p.x + x, it.p.y + y};
|
||||
InsertOpenPath(s, p, it.p);
|
||||
}
|
||||
}
|
||||
|
||||
if compute_history RecomputeHistory(s);
|
||||
return false;
|
||||
}
|
||||
|
||||
PathFind :: proc(actor: *Actor) {
|
||||
for i := 0; i < 32; i += 1 {
|
||||
done := PathFindStep(actor, false);
|
||||
if done break;
|
||||
}
|
||||
RecomputeHistory(actor);
|
||||
}
|
||||
22
examples/pathfinding_visualizer/random.lc
Normal file
22
examples/pathfinding_visualizer/random.lc
Normal file
@@ -0,0 +1,22 @@
|
||||
RandomSeedValue: RandomSeed = {121521923492};
|
||||
RandomSeed :: struct {
|
||||
a: u64;
|
||||
}
|
||||
|
||||
GetRandomU64 :: proc(s: *RandomSeed): u64 {
|
||||
x := s.a;
|
||||
x ^= x << 13;
|
||||
x ^= x >> 7;
|
||||
x ^= x << 17;
|
||||
s.a = x;
|
||||
return x;
|
||||
}
|
||||
|
||||
GetRandomInt :: proc(min: int, max: int): int {
|
||||
random := GetRandomU64(&RandomSeedValue);
|
||||
range_size: u64 = :u64(max - min) + 1;
|
||||
|
||||
result := :int(random % range_size);
|
||||
result = result + min;
|
||||
return result;
|
||||
}
|
||||
68
examples/sandbox/arena.lc
Normal file
68
examples/sandbox/arena.lc
Normal file
@@ -0,0 +1,68 @@
|
||||
import "std_types";
|
||||
import libc "libc";
|
||||
|
||||
PAGE_SIZE :: 4096;
|
||||
|
||||
Arena :: struct {
|
||||
data: *u8;
|
||||
len: usize;
|
||||
commit: usize;
|
||||
reserve: usize;
|
||||
alignment: usize;
|
||||
}
|
||||
|
||||
GetAlignOffset :: proc(size: usize, align: usize): usize {
|
||||
mask: usize = align - 1;
|
||||
val: usize = size & mask;
|
||||
if val {
|
||||
val = align - val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
AlignUp :: proc(size: usize, align: usize): usize {
|
||||
result := size + GetAlignOffset(size, align);
|
||||
return result;
|
||||
}
|
||||
|
||||
InitArena :: proc(arena: *Arena, reserve: usize) {
|
||||
aligned_reserve := AlignUp(reserve, PAGE_SIZE);
|
||||
data := VReserve(aligned_reserve);
|
||||
libc.assert(data != nil);
|
||||
|
||||
if data {
|
||||
arena.data = data;
|
||||
arena.reserve = aligned_reserve;
|
||||
arena.alignment = 8;
|
||||
}
|
||||
}
|
||||
|
||||
PushSize :: proc(arena: *Arena, size: usize): *void {
|
||||
libc.assert(arena.data != nil);
|
||||
|
||||
pointer := :usize(arena.data) + arena.len + size;
|
||||
align := GetAlignOffset(pointer, arena.alignment);
|
||||
aligned_size := size + align;
|
||||
|
||||
a := arena.len + aligned_size;
|
||||
if a > arena.commit {
|
||||
diff := a - arena.commit;
|
||||
commit_size := AlignUp(diff, PAGE_SIZE*4);
|
||||
libc.assert(commit_size + arena.commit <= arena.reserve);
|
||||
|
||||
if VCommit(&arena.data[arena.commit], commit_size) {
|
||||
arena.commit += commit_size;
|
||||
} else {
|
||||
libc.assert(false && "commit failed"[0]);
|
||||
}
|
||||
}
|
||||
|
||||
arena.len += align;
|
||||
result: *void = &arena.data[arena.len];
|
||||
arena.len += size;
|
||||
return result;
|
||||
}
|
||||
|
||||
TestArena :: proc() {
|
||||
arena: Arena;
|
||||
}
|
||||
28
examples/sandbox/arena_win32.lc
Normal file
28
examples/sandbox/arena_win32.lc
Normal file
@@ -0,0 +1,28 @@
|
||||
#build_if(LC_OS == OS_WINDOWS);
|
||||
|
||||
DWORD :: typedef u32;
|
||||
SIZE_T :: typedef uintptr;
|
||||
BOOL :: typedef int;
|
||||
|
||||
MEM_RESERVE :: 0x00002000;
|
||||
MEM_COMMIT :: 0x00001000;
|
||||
PAGE_READWRITE :: 0x04;
|
||||
VirtualAlloc :: proc(lpAddress: *void, dwSize: SIZE_T, flAllocationType: DWORD, flProtect: DWORD): *void; @api
|
||||
|
||||
MEM_RELEASE :: 0x00008000;
|
||||
MEM_DECOMMIT :: 0x00004000;
|
||||
VirtualFree :: proc(lpAddress: *void, dwSize: SIZE_T, dwFreeType: DWORD): BOOL; @api
|
||||
|
||||
VReserve :: proc(size: usize): *void {
|
||||
result := VirtualAlloc(nil, :SIZE_T(size), MEM_RESERVE, PAGE_READWRITE);
|
||||
return result;
|
||||
}
|
||||
|
||||
VCommit :: proc(p: *void, size: usize): bool {
|
||||
result := VirtualAlloc(p, :SIZE_T(size), MEM_COMMIT, PAGE_READWRITE) != 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
VRelease :: proc(p: *void) {
|
||||
VirtualFree(p, 0, MEM_RELEASE);
|
||||
}
|
||||
121
examples/sandbox/build.cpp
Normal file
121
examples/sandbox/build.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
namespace Sandbox {
|
||||
Array<LC_Intern> TypesToGen;
|
||||
|
||||
void RegisterMarkedTypes(LC_AST *n) {
|
||||
if (n->kind == LC_ASTKind_TypespecIdent) {
|
||||
S8_String name = S8_MakeFromChar((char *)n->eident.name);
|
||||
S8_String array_of = "ArrayOf";
|
||||
if (S8_StartsWith(name, array_of)) {
|
||||
if (name == "ArrayOfName") return;
|
||||
|
||||
name = S8_Skip(name, array_of.len);
|
||||
LC_Intern intern = LC_InternStrLen(name.str, (int)name.len);
|
||||
|
||||
bool exists = false;
|
||||
For(TypesToGen) {
|
||||
if (intern == it) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists) {
|
||||
TypesToGen.add(intern);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WalkAndRename(LC_ASTWalker *ctx, LC_AST *n) {
|
||||
LC_Intern *p = NULL;
|
||||
if (n->kind == LC_ASTKind_TypespecIdent || n->kind == LC_ASTKind_ExprIdent) {
|
||||
p = &n->eident.name;
|
||||
}
|
||||
if (LC_IsDecl(n)) {
|
||||
p = &n->dbase.name;
|
||||
}
|
||||
|
||||
if (p) {
|
||||
LC_Intern user = *(LC_Intern *)ctx->user_data;
|
||||
S8_String p8 = S8_MakeFromChar((char *)*p);
|
||||
|
||||
if (S8_Seek(p8, "Name")) {
|
||||
S8_String new_name = S8_ReplaceAll(L->arena, p8, "Name", S8_MakeFromChar((char *)user));
|
||||
*p = LC_InternStrLen(new_name.str, (int)new_name.len);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BeforeCallArgsResolved(LC_AST *n, LC_Type *type) {
|
||||
}
|
||||
|
||||
} // namespace Sandbox
|
||||
|
||||
bool sandbox() {
|
||||
using namespace Sandbox;
|
||||
|
||||
LC_Lang *lang = LC_LangAlloc();
|
||||
lang->use_colored_terminal_output = UseColoredIO;
|
||||
lang->breakpoint_on_error = BreakpointOnError;
|
||||
lang->on_typespec_parsed = RegisterMarkedTypes;
|
||||
lang->before_call_args_resolved = BeforeCallArgsResolved;
|
||||
LC_LangBegin(lang);
|
||||
|
||||
LC_RegisterPackageDir("../pkgs");
|
||||
LC_RegisterPackageDir("../examples");
|
||||
|
||||
LC_Intern name = LC_ILit("sandbox");
|
||||
LC_ParsePackagesUsingRegistry(name);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
TypesToGen.allocator = MA_GetAllocator(lang->arena);
|
||||
|
||||
//
|
||||
// Remove dynamic array file
|
||||
//
|
||||
LC_AST *package = LC_GetPackageByName(name);
|
||||
LC_AST *dynamic_array_file = NULL;
|
||||
LC_ASTFor(it, package->apackage.ffile) {
|
||||
S8_String path = S8_MakeFromChar((char *)it->afile.x->file);
|
||||
if (S8_EndsWith(path, "dynamic_array.lc")) {
|
||||
dynamic_array_file = it;
|
||||
DLL_QUEUE_REMOVE(package->apackage.ffile, package->apackage.lfile, it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate copies
|
||||
LC_ASTWalker walker = LC_GetDefaultWalker(L->arena, WalkAndRename);
|
||||
For(TypesToGen) {
|
||||
LC_AST *new_array_file = LC_CopyAST(L->arena, dynamic_array_file);
|
||||
|
||||
walker.user_data = (void *)⁢
|
||||
LC_WalkAST(&walker, new_array_file);
|
||||
LC_DLLAdd(package->apackage.ffile, package->apackage.lfile, new_array_file);
|
||||
}
|
||||
|
||||
LC_OrderAndResolveTopLevelDecls(name);
|
||||
LC_ResolveAllProcBodies();
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
S8_String code = LC_GenerateUnityBuild(L->ordered_packages);
|
||||
S8_String path = "examples/sandbox/sandbox.c";
|
||||
|
||||
OS_MakeDir("examples/sandbox");
|
||||
OS_WriteFile(path, code);
|
||||
LC_LangEnd(lang);
|
||||
|
||||
if (UseCL) {
|
||||
OS_CopyFile(RaylibDLL, "examples/sandbox/raylib.dll", true);
|
||||
S8_String cmd = Fmt("cl %.*s -Zi -std:c11 -nologo -FC -Fd:examples/sandbox/a.pdb -Fe:examples/sandbox/sandbox.exe %.*s", S8_Expand(path), S8_Expand(RaylibLIB));
|
||||
int errcode = Run(cmd);
|
||||
if (errcode != 0) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
96
examples/sandbox/common.lc
Normal file
96
examples/sandbox/common.lc
Normal file
@@ -0,0 +1,96 @@
|
||||
FloatMin :: proc(a: float, b: float): float {
|
||||
if (a > b) return b;
|
||||
return a;
|
||||
}
|
||||
|
||||
FloatMax :: proc(a: float, b: float): float {
|
||||
if (a > b) return a;
|
||||
return a;
|
||||
}
|
||||
|
||||
FloatClamp :: proc(val: float, min: float, max: float): float {
|
||||
if (val > max) return max;
|
||||
if (val < min) return min;
|
||||
return val;
|
||||
}
|
||||
|
||||
IntMin :: proc(a: int, b: int): int {
|
||||
if (a > b) return b;
|
||||
return a;
|
||||
}
|
||||
|
||||
IntMax :: proc(a: int, b: int): int {
|
||||
if (a > b) return a;
|
||||
return a;
|
||||
}
|
||||
|
||||
IntClamp :: proc(val: int, min: int, max: int): int {
|
||||
if (val > max) return max;
|
||||
if (val < min) return min;
|
||||
return val;
|
||||
}
|
||||
|
||||
Rect2 :: struct {
|
||||
min: Vector2;
|
||||
max: Vector2;
|
||||
}
|
||||
|
||||
CutLeft :: proc(r: *Rect2, value: float): Rect2 {
|
||||
minx := r.min.x;
|
||||
r.min.x = FloatMin(r.max.x, r.min.x + value);
|
||||
return :Rect2{
|
||||
{ minx, r.min.y},
|
||||
{r.min.x, r.max.y},
|
||||
};
|
||||
}
|
||||
|
||||
CutRight :: proc(r: *Rect2, value: float): Rect2 {
|
||||
maxx := r.max.x;
|
||||
r.max.x = FloatMax(r.max.x - value, r.min.x);
|
||||
return :Rect2{
|
||||
{r.max.x, r.min.y},
|
||||
{ maxx, r.max.y},
|
||||
};
|
||||
}
|
||||
|
||||
CutBottom :: proc(r: *Rect2, value: float): Rect2 { // Y is down
|
||||
maxy := r.max.y;
|
||||
r.max.y = FloatMax(r.min.y, r.max.y - value);
|
||||
return :Rect2{
|
||||
{r.min.x, r.max.y},
|
||||
{r.max.x, maxy},
|
||||
};
|
||||
}
|
||||
|
||||
CutTop :: proc(r: *Rect2, value: float): Rect2 { // Y is down
|
||||
miny := r.min.y;
|
||||
r.min.y = FloatMin(r.min.y + value, r.max.y);
|
||||
return :Rect2{
|
||||
{r.min.x, miny},
|
||||
{r.max.x, r.min.y},
|
||||
};
|
||||
}
|
||||
|
||||
Shrink :: proc(r: Rect2, value: float): Rect2 {
|
||||
r.min.x += value;
|
||||
r.max.x -= value;
|
||||
r.min.y += value;
|
||||
r.max.y -= value;
|
||||
return r;
|
||||
}
|
||||
|
||||
RectFromSize :: proc(pos: Vector2, size: Vector2): Rect2 {
|
||||
result: Rect2 = {pos, Vector2Add(pos, size)};
|
||||
return result;
|
||||
}
|
||||
|
||||
GetRectSize :: proc(rect: Rect2): Vector2 {
|
||||
result: Vector2 = {rect.max.x - rect.min.x, rect.max.y - rect.min.y};
|
||||
return result;
|
||||
}
|
||||
|
||||
RectToRectangle :: proc(rect: Rect2): Rectangle {
|
||||
size := GetRectSize(rect);
|
||||
result: Rectangle = {rect.min.x, rect.min.y, size.x, size.y};
|
||||
return result;
|
||||
}
|
||||
49
examples/sandbox/dynamic_array.lc
Normal file
49
examples/sandbox/dynamic_array.lc
Normal file
@@ -0,0 +1,49 @@
|
||||
ArrayOfName :: struct {
|
||||
data: *Name;
|
||||
len: int;
|
||||
cap: int;
|
||||
}
|
||||
|
||||
TryGrowingNameArray :: proc(arr: *ArrayOfName) {
|
||||
if (arr.len + 1 > arr.cap) {
|
||||
cap := arr.cap * 2;
|
||||
if (cap == 0) cap = 16;
|
||||
|
||||
arr.data = libc.realloc(arr.data, sizeof(arr.data[0]) * :libc.size_t(cap));
|
||||
arr.cap = cap;
|
||||
}
|
||||
}
|
||||
|
||||
AddName :: proc(arr: *ArrayOfName, item: Name) {
|
||||
TryGrowingNameArray(arr);
|
||||
arr.data[arr.len] = item;
|
||||
arr.len += 1;
|
||||
}
|
||||
|
||||
InsertName :: proc(a: *ArrayOfName, item: Name, index: int) {
|
||||
if index == a.len {
|
||||
AddName(a, item);
|
||||
return;
|
||||
}
|
||||
|
||||
libc.assert(index < a.len);
|
||||
libc.assert(index >= 0);
|
||||
|
||||
TryGrowingNameArray(a);
|
||||
right_len := :libc.size_t(a.len - index);
|
||||
libc.memmove(&a.data[index + 1], &a.data[index], sizeof(a.data[0]) * right_len);
|
||||
a.data[index] = item;
|
||||
a.len += 1;
|
||||
}
|
||||
|
||||
GetLastName :: proc(a: ArrayOfName): *Name {
|
||||
libc.assert(a.len > 0);
|
||||
result := &a.data[a.len - 1];
|
||||
return result;
|
||||
}
|
||||
|
||||
PopName :: proc(a: *ArrayOfName): Name {
|
||||
a.len -= 1;
|
||||
result := a.data[a.len];
|
||||
return result;
|
||||
}
|
||||
93
examples/sandbox/main.lc
Normal file
93
examples/sandbox/main.lc
Normal file
@@ -0,0 +1,93 @@
|
||||
import "raylib";
|
||||
|
||||
Tile :: struct {
|
||||
value: int;
|
||||
}
|
||||
|
||||
Map :: struct {
|
||||
tiles: *Tile;
|
||||
x: int;
|
||||
y: int;
|
||||
}
|
||||
|
||||
CreateMap :: proc(x: int, y: int, map: *int): Map {
|
||||
result: Map = {x = x, y = y};
|
||||
result.tiles = MemAlloc(:uint(x*y) * sizeof(:Tile));
|
||||
|
||||
for i := 0; i < x*y; i += 1 {
|
||||
result.tiles[i].value = map[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
main :: proc(): int {
|
||||
TestArena();
|
||||
return 0;
|
||||
InitWindow(800, 600, "Thing");
|
||||
SetTargetFPS(60);
|
||||
|
||||
XPIX :: 32;
|
||||
YPIX :: 32;
|
||||
|
||||
camera: Camera2D = {
|
||||
zoom = 1.0,
|
||||
};
|
||||
|
||||
_map_x :: 8;
|
||||
_map_y :: 8;
|
||||
map := CreateMap(_map_x, _map_y, &:[_map_x * _map_y]int{
|
||||
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,
|
||||
1,0,0,0,0,0,0,1,
|
||||
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]);
|
||||
|
||||
for !WindowShouldClose() {
|
||||
if IsMouseButtonDown(MOUSE_BUTTON_LEFT) {
|
||||
delta := GetMouseDelta();
|
||||
camera.offset = Vector2Add(camera.offset, delta);
|
||||
}
|
||||
|
||||
BeginDrawing();
|
||||
ClearBackground(RAYWHITE);
|
||||
|
||||
BeginMode2D(camera);
|
||||
for y_it := 0; y_it < map.y; y_it += 1 {
|
||||
for x_it := 0; x_it < map.x; x_it += 1 {
|
||||
tile := &map.tiles[x_it + y_it * map.x];
|
||||
original_rect := RectFromSize({XPIX * :float(x_it), YPIX * :float(y_it)}, {XPIX, YPIX});
|
||||
rect := Shrink(original_rect, 2);
|
||||
|
||||
r := RectToRectangle(rect);
|
||||
|
||||
min_screen := GetWorldToScreen2D(original_rect.min, camera);
|
||||
size := GetRectSize(original_rect);
|
||||
|
||||
screen_rect: Rectangle = {min_screen.x, min_screen.y, size.x, size.y};
|
||||
mouse_p := GetMousePosition();
|
||||
|
||||
if tile.value == 1 {
|
||||
DrawRectangleRec(r, RED);
|
||||
} else {
|
||||
DrawRectangleRec(r, GREEN);
|
||||
}
|
||||
|
||||
if CheckCollisionPointRec(mouse_p, screen_rect) {
|
||||
DrawRectangleRec(r, BLUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
EndMode2D();
|
||||
|
||||
// DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
CloseWindow();
|
||||
return 0;
|
||||
}
|
||||
|
||||
249
examples/text_editor/buffer.lc
Normal file
249
examples/text_editor/buffer.lc
Normal file
@@ -0,0 +1,249 @@
|
||||
Buffer :: struct {
|
||||
data: *u8;
|
||||
len: int;
|
||||
cap: int;
|
||||
|
||||
lines: Lines;
|
||||
}
|
||||
|
||||
Lines :: struct {
|
||||
data: *Range;
|
||||
len: int;
|
||||
cap: int;
|
||||
}
|
||||
|
||||
Range :: struct {
|
||||
min: int;
|
||||
max: int;
|
||||
}
|
||||
|
||||
LineInfo :: struct {
|
||||
number: int;
|
||||
range: Range;
|
||||
}
|
||||
|
||||
GetRangeSize :: proc(range: Range): int {
|
||||
result := range.max - range.min;
|
||||
return result;
|
||||
}
|
||||
|
||||
ClampInt :: proc(val: int, min: int, max: int): int {
|
||||
result := val;
|
||||
if (val < min) result = min;
|
||||
if (val > max) result = max;
|
||||
return result;
|
||||
}
|
||||
|
||||
MinInt :: proc(a: int, b: int): int {
|
||||
result := a;
|
||||
if (a > b) result = b;
|
||||
return result;
|
||||
}
|
||||
|
||||
MaxInt :: proc(a: int, b: int): int {
|
||||
result := b;
|
||||
if (a > b) result = a;
|
||||
return result;
|
||||
}
|
||||
|
||||
AdjustUTF8PosUnsafe :: proc(buffer: *Buffer, pos: int, direction: int = 1): int {
|
||||
for pos >= 0 && pos < buffer.len && IsUTF8ContinuationByte(buffer.data[pos]) {
|
||||
pos += direction;
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
AdjustUTF8Pos :: proc(buffer: *Buffer, pos: int, direction: int = 1): int {
|
||||
assert(direction == 1 || direction == -1);
|
||||
pos = AdjustUTF8PosUnsafe(buffer, pos, direction);
|
||||
pos = ClampInt(pos, 0, buffer.len - 1);
|
||||
if (buffer.data) assert(!IsUTF8ContinuationByte(buffer.data[pos]));
|
||||
return pos;
|
||||
}
|
||||
|
||||
AdjustRange :: proc(buffer: *Buffer, range: *Range) {
|
||||
range.min = AdjustUTF8Pos(buffer, range.min, direction = -1);
|
||||
range.max = AdjustUTF8Pos(buffer, range.max, direction = +1);
|
||||
}
|
||||
|
||||
ReplaceText :: proc(buffer: *Buffer, replace: Range, with: String) {
|
||||
AdjustRange(buffer, &replace);
|
||||
|
||||
new_buffer_len := buffer.len + :int(with.len) - GetRangeSize(replace);
|
||||
new_buffer_size := new_buffer_len + 1; // possible addition of a null terminator
|
||||
if new_buffer_size > buffer.cap {
|
||||
new_buffer_cap := MaxInt(4096, new_buffer_size * 2);
|
||||
new_buffer := malloc(:usize(new_buffer_cap));
|
||||
if (buffer.data) {
|
||||
memcpy(new_buffer, buffer.data, :usize(buffer.len));
|
||||
free(buffer.data);
|
||||
}
|
||||
buffer.data = new_buffer;
|
||||
buffer.cap = new_buffer_cap;
|
||||
}
|
||||
|
||||
old_text_range: Range = replace;
|
||||
new_text_range: Range = {old_text_range.min, old_text_range.min + :int(with.len)};
|
||||
right_range: Range = {old_text_range.max, buffer.len};
|
||||
|
||||
memmove(&buffer.data[new_text_range.max], &buffer.data[right_range.min], :usize(GetRangeSize(right_range)));
|
||||
memmove(&buffer.data[new_text_range.min], with.str, :usize(GetRangeSize(new_text_range)));
|
||||
|
||||
buffer.len = new_buffer_len;
|
||||
if buffer.len && buffer.data[buffer.len - 1] != 0 {
|
||||
buffer.data[buffer.len] = 0;
|
||||
buffer.len += 1;
|
||||
assert(buffer.len <= buffer.cap);
|
||||
}
|
||||
|
||||
UpdateBufferLines(buffer);
|
||||
}
|
||||
|
||||
AddLine :: proc(lines: *Lines, line: Range) {
|
||||
if lines.len + 1 > lines.cap {
|
||||
new_cap := MaxInt(16, lines.cap * 2);
|
||||
lines.data = realloc(lines.data, :usize(new_cap) * sizeof(:Range));
|
||||
lines.cap = new_cap;
|
||||
}
|
||||
lines.data[lines.len] = line;
|
||||
lines.len += 1;
|
||||
}
|
||||
|
||||
UpdateBufferLines :: proc(buffer: *Buffer) {
|
||||
buffer.lines.len = 0;
|
||||
|
||||
line: Range = {0, 0};
|
||||
for i := 0; i < buffer.len; i += 1 {
|
||||
if buffer.data[i] == '\n' {
|
||||
AddLine(&buffer.lines, line);
|
||||
line.min = i + 1;
|
||||
line.max = i + 1;
|
||||
} else {
|
||||
line.max += 1;
|
||||
}
|
||||
}
|
||||
|
||||
line.min = AdjustUTF8Pos(buffer, line.min);
|
||||
line.max = AdjustUTF8Pos(buffer, line.max);
|
||||
AddLine(&buffer.lines, line);
|
||||
}
|
||||
|
||||
AllocStringFromBuffer :: proc(buffer: *Buffer, range: Range, null_terminate: bool = false): String {
|
||||
AdjustRange(buffer, &range);
|
||||
size := GetRangeSize(range);
|
||||
|
||||
alloc_size := :usize(size);
|
||||
if (null_terminate) alloc_size += 1;
|
||||
data := malloc(alloc_size);
|
||||
memcpy(data, &buffer.data[range.min], :usize(size));
|
||||
|
||||
result: String = {data, size};
|
||||
if (null_terminate) result.str[result.len] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
GetStringFromBuffer :: proc(buffer: *Buffer, range: Range): String {
|
||||
AdjustRange(buffer, &range);
|
||||
size := GetRangeSize(range);
|
||||
result: String = {:*char(&buffer.data[range.min]), size};
|
||||
return result;
|
||||
}
|
||||
|
||||
AddText :: proc(buffer: *Buffer, text: String) {
|
||||
end_of_buffer: Range = {buffer.len - 1, buffer.len - 1};
|
||||
ReplaceText(buffer, end_of_buffer, text);
|
||||
}
|
||||
|
||||
FindLineOfPos :: proc(buffer: *Buffer, pos: int): LineInfo {
|
||||
for i := 0; i < buffer.lines.len; i += 1 {
|
||||
line := buffer.lines.data[i];
|
||||
if pos >= line.min && pos <= line.max {
|
||||
return {i, line};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
IsLineValid :: proc(buffer: *Buffer, line: int): bool {
|
||||
result := line >= 0 && line < buffer.lines.len;
|
||||
return result;
|
||||
}
|
||||
|
||||
GetLine :: proc(buffer: *Buffer, line: int): LineInfo {
|
||||
line = ClampInt(line, 0, buffer.lines.len - 1);
|
||||
range := buffer.lines.data[line];
|
||||
return {line, range};
|
||||
}
|
||||
|
||||
IsPosInBounds :: proc(buffer: *Buffer, pos: int): bool {
|
||||
result := buffer.len != 0 && pos >= 0 && pos < buffer.len;
|
||||
return result;
|
||||
}
|
||||
|
||||
GetChar :: proc(buffer: *Buffer, pos: int): u8 {
|
||||
result: u8;
|
||||
if IsPosInBounds(buffer, pos) {
|
||||
result = buffer.data[pos];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
GetUTF32 :: proc(buffer: *Buffer, pos: int, codepoint_size: *int = nil): u32 {
|
||||
if !IsPosInBounds(buffer, pos) return 0;
|
||||
p := &buffer.data[pos];
|
||||
max := buffer.len - pos;
|
||||
utf32 := UTF8ToUTF32(:*uchar(p), max);
|
||||
assert(utf32.error == 0);
|
||||
if (utf32.error != 0) return 0;
|
||||
if (codepoint_size) codepoint_size[0] = utf32.advance;
|
||||
return utf32.out_str;
|
||||
}
|
||||
|
||||
IsWhitespace :: proc(w: u8): bool {
|
||||
result := w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
|
||||
return result;
|
||||
}
|
||||
|
||||
SeekOnWordBoundary :: proc(buffer: *Buffer, pos: int, direction: int = GO_FORWARD): int {
|
||||
assert(direction == GO_FORWARD || direction == GO_BACKWARD);
|
||||
check_pos: int;
|
||||
if direction == GO_FORWARD {
|
||||
check_pos = AdjustUTF8Pos(buffer, pos + 1, direction);
|
||||
pos = check_pos;
|
||||
// this difference here because the Backward for loop is not inclusive.
|
||||
// It doesn't move an inch forward after "pos"
|
||||
// - - - - pos - -
|
||||
// It goes backward on first Advance
|
||||
} else {
|
||||
check_pos = AdjustUTF8Pos(buffer, pos - 1, direction);
|
||||
}
|
||||
|
||||
cursor_char := GetChar(buffer, check_pos);
|
||||
standing_on_whitespace := IsWhitespace(cursor_char);
|
||||
seek_whitespace := standing_on_whitespace == false;
|
||||
seek_word := standing_on_whitespace;
|
||||
|
||||
end := buffer.len - 1;
|
||||
if (direction == GO_BACKWARD) end = 0;
|
||||
|
||||
result := end;
|
||||
iter := Iterate(buffer, pos, end, direction);
|
||||
prev_pos := iter.pos;
|
||||
for IsValid(iter); Advance(&iter) {
|
||||
if seek_word && !IsWhitespace(:u8(iter.item)) {
|
||||
result = prev_pos;
|
||||
break;
|
||||
}
|
||||
if seek_whitespace && IsWhitespace(:u8(iter.item)) {
|
||||
if direction == GO_FORWARD {
|
||||
result = iter.pos;
|
||||
} else {
|
||||
result = prev_pos;
|
||||
}
|
||||
break;
|
||||
}
|
||||
prev_pos = iter.pos;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
54
examples/text_editor/buffer_iter.lc
Normal file
54
examples/text_editor/buffer_iter.lc
Normal file
@@ -0,0 +1,54 @@
|
||||
GO_FORWARD :: 1;
|
||||
GO_BACKWARD :: -1;
|
||||
|
||||
BufferIter :: struct {
|
||||
buffer: *Buffer;
|
||||
pos: int;
|
||||
end: int;
|
||||
item: u32;
|
||||
|
||||
utf8_codepoint_size: int;
|
||||
direction: int;
|
||||
codepoint_index: int;
|
||||
}
|
||||
|
||||
IsValid :: proc(iter: BufferIter): bool {
|
||||
assert(iter.direction == GO_FORWARD || iter.direction == GO_BACKWARD);
|
||||
|
||||
result := false;
|
||||
if iter.direction == GO_BACKWARD {
|
||||
result = iter.pos >= iter.end;
|
||||
} else {
|
||||
result = iter.pos < iter.end;
|
||||
}
|
||||
|
||||
if result {
|
||||
assert(!IsUTF8ContinuationByte(GetChar(iter.buffer, iter.pos)));
|
||||
assert(IsPosInBounds(iter.buffer, iter.pos));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Advance :: proc(iter: *BufferIter) {
|
||||
assert(iter.direction == GO_FORWARD || iter.direction == GO_BACKWARD);
|
||||
|
||||
iter.codepoint_index += 1;
|
||||
if iter.direction == GO_FORWARD {
|
||||
iter.pos += iter.utf8_codepoint_size;
|
||||
} else {
|
||||
iter.pos = AdjustUTF8PosUnsafe(iter.buffer, iter.pos - 1, GO_BACKWARD);
|
||||
}
|
||||
|
||||
if !IsValid(*iter) return;
|
||||
iter.item = GetUTF32(iter.buffer, iter.pos, &iter.utf8_codepoint_size);
|
||||
}
|
||||
|
||||
Iterate :: proc(buffer: *Buffer, pos: int, end: int, direction: int = GO_FORWARD): BufferIter {
|
||||
assert(!IsUTF8ContinuationByte(GetChar(buffer, pos)));
|
||||
assert(!IsUTF8ContinuationByte(GetChar(buffer, end)));
|
||||
assert(direction == GO_FORWARD || direction == GO_BACKWARD);
|
||||
|
||||
result: BufferIter = {buffer = buffer, pos = pos, end = end, direction = direction, codepoint_index = -1};
|
||||
Advance(&result);
|
||||
return result;
|
||||
}
|
||||
34
examples/text_editor/build.cpp
Normal file
34
examples/text_editor/build.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
bool text_editor() {
|
||||
LC_Lang *lang = LC_LangAlloc();
|
||||
lang->use_colored_terminal_output = UseColoredIO;
|
||||
lang->breakpoint_on_error = BreakpointOnError;
|
||||
LC_LangBegin(lang);
|
||||
defer { LC_LangEnd(lang); };
|
||||
|
||||
LC_RegisterPackageDir("../pkgs");
|
||||
LC_RegisterPackageDir("../examples");
|
||||
|
||||
LC_Intern name = LC_ILit("text_editor");
|
||||
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||
if (L->errors) return false;
|
||||
|
||||
DebugVerifyAST(packages);
|
||||
if (L->errors) return false;
|
||||
|
||||
LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls();
|
||||
|
||||
OS_MakeDir("examples");
|
||||
OS_MakeDir("examples/text_editor");
|
||||
OS_CopyFile(RaylibDLL, "examples/text_editor/raylib.dll", true);
|
||||
|
||||
S8_String code = LC_GenerateUnityBuild(packages);
|
||||
S8_String path = "examples/text_editor/text_editor.c";
|
||||
OS_WriteFile(path, code);
|
||||
|
||||
if (!UseCL) return true;
|
||||
S8_String cmd = Fmt("cl %.*s -Zi -std:c11 -nologo -FC -Fd:examples/text_editor/a.pdb -Fe:examples/text_editor/text_editor.exe %.*s", S8_Expand(path), S8_Expand(RaylibLIB));
|
||||
int errcode = Run(cmd);
|
||||
if (errcode != 0) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
362
examples/text_editor/main.lc
Normal file
362
examples/text_editor/main.lc
Normal file
@@ -0,0 +1,362 @@
|
||||
import "raylib";
|
||||
import "std_types";
|
||||
import "libc";
|
||||
|
||||
MainCursor: Selection;
|
||||
|
||||
Selection :: struct {
|
||||
a: int;
|
||||
b: int;
|
||||
}
|
||||
|
||||
GetRange :: proc(s: Selection): Range {
|
||||
result: Range = {MinInt(s.a, s.b), MaxInt(s.a, s.b)};
|
||||
return result;
|
||||
}
|
||||
|
||||
main :: proc(): int {
|
||||
InitWindow(800, 600, "TextEditor");
|
||||
SetTargetFPS(60);
|
||||
|
||||
font_size: float = 35;
|
||||
font_spacing: float = 1;
|
||||
font: Font = LoadFontEx("C:/Windows/Fonts/consola.ttf", :int(font_size), nil, 0);
|
||||
|
||||
glyph_info: GlyphInfo = GetGlyphInfo(font, 'A');
|
||||
size := MeasureTextEx(font, "A", font_size, font_spacing);
|
||||
Monosize = {:float(glyph_info.image.width), size.y};
|
||||
|
||||
buffer: Buffer;
|
||||
AddText(&buffer, "1Testing and stuff 1Testing and stuff 1Testing and stuff 1Testing and stuff\n");
|
||||
AddText(&buffer, "2Testing and stuff\n");
|
||||
AddText(&buffer, "3Testing and stuff\n");
|
||||
AddText(&buffer, "4Testing and stuff\n");
|
||||
AddText(&buffer, "5Testing and stuff\n");
|
||||
AddText(&buffer, "6Testing and stuff\n");
|
||||
AddText(&buffer, "7Testing and stuff\n");
|
||||
AddText(&buffer, "8Testing and stuff\n");
|
||||
AddText(&buffer, "9Testing and stuff\n");
|
||||
AddText(&buffer, "1Testing and stuff\n");
|
||||
AddText(&buffer, "2Testing and stuff\n");
|
||||
AddText(&buffer, "3Testing and stuff\n");
|
||||
AddText(&buffer, "4Testing and stuff\n");
|
||||
AddText(&buffer, "5Testing and stuff\n");
|
||||
AddText(&buffer, "6Testing and stuff\n");
|
||||
AddText(&buffer, "7Testing and stuff\n");
|
||||
AddText(&buffer, "8Testing and stuff\n");
|
||||
AddText(&buffer, "9Testing and stuff\n");
|
||||
AddText(&buffer, "1Testing and stuff\n");
|
||||
AddText(&buffer, "22esting and stuff\n");
|
||||
AddText(&buffer, "3Testing and stuff\n");
|
||||
AddText(&buffer, "4Testing and stuff\n");
|
||||
AddText(&buffer, "5Testing and stuff\n");
|
||||
AddText(&buffer, "6Testing and stuff\n");
|
||||
AddText(&buffer, "7Testing and stuff\n");
|
||||
AddText(&buffer, "8Testing and stuff\n");
|
||||
AddText(&buffer, "9Testing and stuff\n");
|
||||
AddText(&buffer, "1Testing and stuff\n");
|
||||
AddText(&buffer, "2Testing and stuff\n");
|
||||
AddText(&buffer, "3Testing and stuff\n");
|
||||
AddText(&buffer, "4Testing and stuff\n");
|
||||
|
||||
for !WindowShouldClose() {
|
||||
ScreenSize = {:f32(GetScreenWidth()), :f32(GetScreenHeight())};
|
||||
initial_cursor: Selection = MainCursor;
|
||||
|
||||
if IsKeyPressed(KEY_LEFT) || IsKeyPressedRepeat(KEY_LEFT) {
|
||||
if IsKeyDown(KEY_LEFT_SHIFT) {
|
||||
if IsKeyDown(KEY_LEFT_CONTROL) {
|
||||
MainCursor.b = SeekOnWordBoundary(&buffer, MainCursor.b, GO_BACKWARD);
|
||||
} else {
|
||||
MainCursor.b = MoveLeft(&buffer, MainCursor.b);
|
||||
}
|
||||
} else {
|
||||
if IsKeyDown(KEY_LEFT_CONTROL) {
|
||||
MainCursor.a = SeekOnWordBoundary(&buffer, MainCursor.a, GO_BACKWARD);
|
||||
MainCursor.b = MainCursor.a;
|
||||
} else {
|
||||
MainCursor.a = MoveLeft(&buffer, MainCursor.a);
|
||||
MainCursor.b = MainCursor.a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if IsKeyPressed(KEY_RIGHT) || IsKeyPressedRepeat(KEY_RIGHT) {
|
||||
if IsKeyDown(KEY_LEFT_SHIFT) {
|
||||
if IsKeyDown(KEY_LEFT_CONTROL) {
|
||||
MainCursor.b = SeekOnWordBoundary(&buffer, MainCursor.b, GO_FORWARD);
|
||||
} else {
|
||||
MainCursor.b = MoveRight(&buffer, MainCursor.b);
|
||||
}
|
||||
} else {
|
||||
if IsKeyDown(KEY_LEFT_CONTROL) {
|
||||
MainCursor.a = SeekOnWordBoundary(&buffer, MainCursor.a, GO_FORWARD);
|
||||
MainCursor.b = MainCursor.a;
|
||||
} else {
|
||||
MainCursor.a = MoveRight(&buffer, MainCursor.a);
|
||||
MainCursor.b = MainCursor.a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsKeyPressed(KEY_DOWN) || IsKeyPressedRepeat(KEY_DOWN)) {
|
||||
if IsKeyDown(KEY_LEFT_SHIFT) {
|
||||
MainCursor.b = MoveDown(&buffer, MainCursor.b);
|
||||
} else {
|
||||
range := GetRange(MainCursor);
|
||||
if GetRangeSize(range) > 0 {
|
||||
MainCursor.b = range.max;
|
||||
MainCursor.a = range.max;
|
||||
}
|
||||
MainCursor.a = MoveDown(&buffer, MainCursor.a);
|
||||
MainCursor.b = MainCursor.a;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsKeyPressed(KEY_UP) || IsKeyPressedRepeat(KEY_UP)) {
|
||||
if IsKeyDown(KEY_LEFT_SHIFT) {
|
||||
MainCursor.b = MoveUp(&buffer, MainCursor.b);
|
||||
} else {
|
||||
range := GetRange(MainCursor);
|
||||
if GetRangeSize(range) > 0 {
|
||||
MainCursor.b = range.min;
|
||||
MainCursor.a = range.min;
|
||||
}
|
||||
MainCursor.a = MoveUp(&buffer, MainCursor.a);
|
||||
MainCursor.b = MainCursor.a;
|
||||
}
|
||||
}
|
||||
|
||||
if IsKeyPressed(KEY_BACKSPACE) || IsKeyPressedRepeat(KEY_BACKSPACE) {
|
||||
range := GetRange(MainCursor);
|
||||
range_size := GetRangeSize(range);
|
||||
|
||||
if range_size == 0 {
|
||||
if IsKeyDown(KEY_LEFT_CONTROL) {
|
||||
left := SeekOnWordBoundary(&buffer, MainCursor.a, GO_BACKWARD);
|
||||
ReplaceText(&buffer, {left, MainCursor.a}, "");
|
||||
MainCursor.a = left;
|
||||
MainCursor.b = MainCursor.a;
|
||||
} else {
|
||||
left := MoveLeft(&buffer, MainCursor.a);
|
||||
ReplaceText(&buffer, {left, MainCursor.a}, "");
|
||||
MainCursor.a = left;
|
||||
MainCursor.b = MainCursor.a;
|
||||
}
|
||||
} else {
|
||||
ReplaceText(&buffer, range, "");
|
||||
MainCursor.b = range.min;
|
||||
MainCursor.a = MainCursor.b;
|
||||
}
|
||||
}
|
||||
|
||||
if IsKeyPressed(KEY_DELETE) || IsKeyPressedRepeat(KEY_DELETE) {
|
||||
range := GetRange(MainCursor);
|
||||
range_size := GetRangeSize(range);
|
||||
|
||||
if range_size == 0 {
|
||||
if IsKeyDown(KEY_LEFT_CONTROL) {
|
||||
right := SeekOnWordBoundary(&buffer, MainCursor.a);
|
||||
ReplaceText(&buffer, {MainCursor.a, right}, "");
|
||||
} else {
|
||||
right := MoveRight(&buffer, MainCursor.a);
|
||||
ReplaceText(&buffer, {MainCursor.a, right}, "");
|
||||
}
|
||||
MainCursor.b = MainCursor.a;
|
||||
} else {
|
||||
ReplaceText(&buffer, range, "");
|
||||
MainCursor.b = range.min;
|
||||
MainCursor.a = MainCursor.b;
|
||||
}
|
||||
}
|
||||
|
||||
if IsKeyPressed(KEY_ENTER) || IsKeyPressedRepeat(KEY_ENTER) {
|
||||
ReplaceText(&buffer, GetRange(MainCursor), "\n");
|
||||
MainCursor.a = MoveRight(&buffer, MainCursor.a);
|
||||
MainCursor.b = MainCursor.a;
|
||||
}
|
||||
|
||||
if IsKeyPressed(KEY_HOME) {
|
||||
line := FindLineOfPos(&buffer, MainCursor.b);
|
||||
if IsKeyDown(KEY_LEFT_SHIFT) {
|
||||
MainCursor.b = line.range.min;
|
||||
} else {
|
||||
MainCursor.a = line.range.min;
|
||||
MainCursor.b = line.range.min;
|
||||
}
|
||||
}
|
||||
if IsKeyPressed(KEY_END) {
|
||||
line := FindLineOfPos(&buffer, MainCursor.b);
|
||||
if IsKeyDown(KEY_LEFT_SHIFT) {
|
||||
MainCursor.b = line.range.max;
|
||||
} else {
|
||||
MainCursor.a = line.range.max;
|
||||
MainCursor.b = line.range.max;
|
||||
}
|
||||
}
|
||||
|
||||
if IsKeyPressed(KEY_PAGE_DOWN) || IsKeyPressedRepeat(KEY_PAGE_DOWN) {
|
||||
vpos := CalculateVisualPos(&buffer, MainCursor.b);
|
||||
move_by := :int(roundf(ScreenSize.y / Monosize.y));
|
||||
vpos.y += move_by;
|
||||
MainCursor.b = CalculatePosFromVisualPos(&buffer, vpos);
|
||||
if !IsKeyDown(KEY_LEFT_SHIFT) {
|
||||
MainCursor.a = MainCursor.b;
|
||||
}
|
||||
}
|
||||
if IsKeyPressed(KEY_PAGE_UP) || IsKeyPressedRepeat(KEY_PAGE_UP) {
|
||||
vpos := CalculateVisualPos(&buffer, MainCursor.b);
|
||||
move_by := :int(roundf(ScreenSize.y / Monosize.y));
|
||||
vpos.y -= move_by;
|
||||
MainCursor.b = CalculatePosFromVisualPos(&buffer, vpos);
|
||||
if !IsKeyDown(KEY_LEFT_SHIFT) {
|
||||
MainCursor.a = MainCursor.b;
|
||||
}
|
||||
}
|
||||
|
||||
mouse_p := GetMousePosition();
|
||||
if IsMouseButtonDown(MOUSE_BUTTON_LEFT) {
|
||||
p := Vector2Add(mouse_p, Scroll);
|
||||
p = Vector2Divide(p, Monosize);
|
||||
x := :int(floorf(p.x));
|
||||
y := :int(floorf(p.y));
|
||||
MainCursor.a = CalculatePosFromVisualPos(&buffer, {x, y});
|
||||
MainCursor.b = MainCursor.a;
|
||||
}
|
||||
|
||||
if CheckCollisionPointRec(mouse_p, {0, 0, ScreenSize.x, ScreenSize.y}) {
|
||||
SetMouseCursor(MOUSE_CURSOR_IBEAM);
|
||||
} else {
|
||||
SetMouseCursor(MOUSE_CURSOR_DEFAULT);
|
||||
}
|
||||
|
||||
for key := GetCharPressed(); key; key = GetCharPressed() {
|
||||
selection_range := GetRange(MainCursor);
|
||||
|
||||
result: UTF8_Result = UTF32ToUTF8(:u32(key));
|
||||
if result.error == 0 {
|
||||
ReplaceText(&buffer, selection_range, {:*char(&result.out_str[0]), result.len});
|
||||
} else {
|
||||
ReplaceText(&buffer, selection_range, "?");
|
||||
}
|
||||
|
||||
range_size := GetRangeSize(selection_range);
|
||||
if range_size != 0 {
|
||||
MainCursor.a = selection_range.min;
|
||||
MainCursor.b = selection_range.min;
|
||||
}
|
||||
MainCursor.a = MoveRight(&buffer, MainCursor.a);
|
||||
MainCursor.b = MainCursor.a;
|
||||
}
|
||||
|
||||
//
|
||||
// Scrolling
|
||||
//
|
||||
mouse_wheel := GetMouseWheelMove() * 16;
|
||||
Scroll.y -= mouse_wheel;
|
||||
|
||||
if initial_cursor.b != MainCursor.b {
|
||||
cursor_vpos := CalculateVisualPos(&buffer, MainCursor.b);
|
||||
|
||||
world_pos := CalculateWorldPosUnscrolled(cursor_vpos);
|
||||
world_pos_cursor_end := Vector2Add(world_pos, Monosize);
|
||||
|
||||
scrolled_begin := Scroll;
|
||||
scrolled_end := Vector2Add(Scroll, ScreenSize);
|
||||
|
||||
if world_pos_cursor_end.x > scrolled_end.x {
|
||||
Scroll.x += world_pos_cursor_end.x - scrolled_end.x;
|
||||
}
|
||||
if world_pos.x < scrolled_begin.x {
|
||||
Scroll.x -= scrolled_begin.x - world_pos.x;
|
||||
}
|
||||
if world_pos_cursor_end.y > scrolled_end.y {
|
||||
Scroll.y += world_pos_cursor_end.y - scrolled_end.y;
|
||||
}
|
||||
if world_pos.y < scrolled_begin.y {
|
||||
Scroll.y -= scrolled_begin.y - world_pos.y;
|
||||
}
|
||||
}
|
||||
if (Scroll.x < 0) Scroll.x = 0;
|
||||
if (Scroll.y < 0) Scroll.y = 0;
|
||||
|
||||
|
||||
|
||||
BeginDrawing();
|
||||
ClearBackground(RAYWHITE);
|
||||
|
||||
_miny := Scroll.y / Monosize.y;
|
||||
_maxy := (Scroll.y + ScreenSize.y) / Monosize.y;
|
||||
|
||||
_minx := Scroll.x / Monosize.x;
|
||||
_maxx := (Scroll.x + ScreenSize.x) / Monosize.x;
|
||||
|
||||
miny := :int(floorf(_miny));
|
||||
minx := :int(floorf(_minx));
|
||||
|
||||
maxy := :int(ceilf(_maxy));
|
||||
maxx := :int(ceilf(_maxx));
|
||||
|
||||
// Draw grid
|
||||
{
|
||||
for y := miny; y < maxy; y += 1 {
|
||||
for x := minx; x < maxx; x += 1 {
|
||||
p := CalculateWorldPos({x, y});
|
||||
rect := Rect2PSize(p.x, p.y, Monosize.x, Monosize.y);
|
||||
rect = Shrink(rect, 1);
|
||||
DrawRect(rect, {255, 0, 0, 40});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw text
|
||||
{
|
||||
miny = ClampInt(miny, 0, buffer.lines.len - 1);
|
||||
maxy = ClampInt(maxy, 0, buffer.lines.len - 1);
|
||||
for y := miny; y <= maxy; y += 1 {
|
||||
line := buffer.lines.data[y];
|
||||
|
||||
string := AllocStringFromBuffer(&buffer, line, null_terminate = true);
|
||||
defer free(string.str);
|
||||
|
||||
pos := CalculateWorldPos({0, y});
|
||||
DrawTextEx(font, string.str, pos, font_size, font_spacing, BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw selection
|
||||
{
|
||||
range := GetRange(MainCursor);
|
||||
start_vpos := CalculateVisualPos(&buffer, range.min);
|
||||
pos := start_vpos;
|
||||
for iter := Iterate(&buffer, range.min, range.max); IsValid(iter); Advance(&iter) {
|
||||
world_pos := CalculateWorldPos(pos);
|
||||
rect := Rect2PSize(world_pos.x, world_pos.y, Monosize.x, Monosize.y);
|
||||
DrawRect(rect, {0, 255, 0, 40});
|
||||
|
||||
pos.x += 1;
|
||||
if iter.item == '\n' {
|
||||
pos.x = 0;
|
||||
pos.y += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Draw cursor
|
||||
{
|
||||
c := CalculateVisualPos(&buffer, MainCursor.b);
|
||||
p := CalculateWorldPos(c);
|
||||
|
||||
cursor_size: f32 = Monosize.x * 0.2;
|
||||
rect := Rect2PSize(p.x, p.y, Monosize.x, Monosize.y);
|
||||
rect = CutLeft(&rect, cursor_size);
|
||||
|
||||
DrawRect(rect, RED);
|
||||
}
|
||||
|
||||
|
||||
EndDrawing();
|
||||
}
|
||||
CloseWindow();
|
||||
return 0;
|
||||
}
|
||||
79
examples/text_editor/rect2p.lc
Normal file
79
examples/text_editor/rect2p.lc
Normal file
@@ -0,0 +1,79 @@
|
||||
Rect2P :: struct {
|
||||
min: Vector2;
|
||||
max: Vector2;
|
||||
}
|
||||
|
||||
F32_Max :: proc(a: f32, b: f32): f32 {
|
||||
if a > b return a;
|
||||
return b;
|
||||
}
|
||||
|
||||
F32_Min :: proc(a: f32, b: f32): f32 {
|
||||
if a > b return b;
|
||||
return a;
|
||||
}
|
||||
|
||||
F32_Clamp :: proc(val: f32, min: f32, max: f32): f32 {
|
||||
if (val > max) return max;
|
||||
if (val < min) return min;
|
||||
return val;
|
||||
}
|
||||
|
||||
GetRectSize :: proc(rect: Rect2P): Vector2 {
|
||||
result: Vector2 = {rect.max.x - rect.min.x, rect.max.y - rect.min.y};
|
||||
return result;
|
||||
}
|
||||
|
||||
CutLeft :: proc(r: *Rect2P, value: f32): Rect2P {
|
||||
minx := r.min.x;
|
||||
r.min.x = F32_Min(r.max.x, r.min.x + value);
|
||||
return :Rect2P{
|
||||
{ minx, r.min.y},
|
||||
{r.min.x, r.max.y},
|
||||
};
|
||||
}
|
||||
|
||||
CutRight :: proc(r: *Rect2P, value: f32): Rect2P {
|
||||
maxx := r.max.x;
|
||||
r.max.x = F32_Max(r.max.x - value, r.min.x);
|
||||
return :Rect2P{
|
||||
{r.max.x, r.min.y},
|
||||
{ maxx, r.max.y},
|
||||
};
|
||||
}
|
||||
|
||||
CutBottom :: proc(r: *Rect2P, value: f32): Rect2P { // Y is down
|
||||
maxy := r.max.y;
|
||||
r.max.y = F32_Max(r.min.y, r.max.y - value);
|
||||
return :Rect2P{
|
||||
{r.min.x, r.max.y},
|
||||
{r.max.x, maxy},
|
||||
};
|
||||
}
|
||||
|
||||
CutTop :: proc(r: *Rect2P, value: f32): Rect2P { // Y is down
|
||||
miny := r.min.y;
|
||||
r.min.y = F32_Min(r.min.y + value, r.max.y);
|
||||
return :Rect2P{
|
||||
{r.min.x, miny},
|
||||
{r.max.x, r.min.y},
|
||||
};
|
||||
}
|
||||
|
||||
Rect2PToRectangle :: proc(r: Rect2P): Rectangle {
|
||||
result: Rectangle = {r.min.x, r.min.y, r.max.x - r.min.x, r.max.y - r.min.y};
|
||||
return result;
|
||||
}
|
||||
|
||||
Rect2PSize :: proc(x: f32, y: f32, w: f32, h: f32): Rect2P {
|
||||
result: Rect2P = {{x, y}, {x+w, y+h}};
|
||||
return result;
|
||||
}
|
||||
|
||||
Shrink :: proc(r: Rect2P, v: f32): Rect2P {
|
||||
r.min.x += v;
|
||||
r.min.y += v;
|
||||
r.max.x -= v;
|
||||
r.max.y -= v;
|
||||
return r;
|
||||
}
|
||||
94
examples/text_editor/unicode.lc
Normal file
94
examples/text_editor/unicode.lc
Normal file
@@ -0,0 +1,94 @@
|
||||
UTF32_Result :: struct {
|
||||
out_str: u32;
|
||||
advance: int;
|
||||
error: int;
|
||||
}
|
||||
|
||||
UTF8_Result :: struct {
|
||||
out_str: [4]u8;
|
||||
len: int;
|
||||
error: int;
|
||||
}
|
||||
|
||||
IsUTF8ContinuationByte :: proc(c: u8): bool {
|
||||
result := (c & 0b11000000) == 0b10000000;
|
||||
return result;
|
||||
}
|
||||
|
||||
UTF8ToUTF32 :: proc(c: *uchar, max_advance: int): UTF32_Result {
|
||||
result: UTF32_Result;
|
||||
|
||||
if (c[0] & 0x80) == 0 { // Check if leftmost zero of first byte is unset
|
||||
if max_advance >= 1 {
|
||||
result.out_str = :u32(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 = (:u32(c[0] & 0x1f) << 6) | :u32(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 = (:u32(c[0] & 0xf) << 12) | (:u32(c[1] & 0x3f) << 6) | :u32(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 = :u32(c[0] & 0xf) << 18 | :u32(c[1] & 0x3f) << 12 | :u32(c[2] & 0x3f) << 6 | :u32(c[3] & 0x3f);
|
||||
result.advance = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
}
|
||||
else result.error = 4;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
UTF32ToUTF8 :: proc(codepoint: u32): UTF8_Result {
|
||||
result: UTF8_Result;
|
||||
|
||||
if codepoint <= 0x7F {
|
||||
result.len = 1;
|
||||
result.out_str[0] = :u8(codepoint);
|
||||
}
|
||||
else if codepoint <= 0x7FF {
|
||||
result.len = 2;
|
||||
result.out_str[0] = :u8 (0xc0 | 0x1f & (codepoint >> 6));
|
||||
result.out_str[1] = :u8 (0x80 | 0x3f & codepoint);
|
||||
}
|
||||
else if codepoint <= 0xFFFF { // 16 bit word
|
||||
result.len = 3;
|
||||
result.out_str[0] = :u8 (0xe0 | 0xf & (codepoint >> 12)); // 4 bits
|
||||
result.out_str[1] = :u8 (0x80 | 0x3f & (codepoint >> 6)); // 6 bits
|
||||
result.out_str[2] = :u8 (0x80 | 0x3f & codepoint); // 6 bits
|
||||
}
|
||||
else if codepoint <= 0x10FFFF { // 21 bit word
|
||||
result.len = 4;
|
||||
result.out_str[0] = :u8 (0xf0 | 0x7 & (codepoint >> 18)); // 3 bits
|
||||
result.out_str[1] = :u8 (0x80 | 0x3f & (codepoint >> 12)); // 6 bits
|
||||
result.out_str[2] = :u8 (0x80 | 0x3f & (codepoint >> 6)); // 6 bits
|
||||
result.out_str[3] = :u8 (0x80 | 0x3f & codepoint); // 6 bits
|
||||
}
|
||||
else result.error = 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
76
examples/text_editor/visual_pos.lc
Normal file
76
examples/text_editor/visual_pos.lc
Normal file
@@ -0,0 +1,76 @@
|
||||
Monosize: Vector2;
|
||||
ScreenSize: Vector2;
|
||||
Scroll: Vector2;
|
||||
|
||||
Vec2I :: struct {
|
||||
x: int;
|
||||
y: int;
|
||||
}
|
||||
|
||||
MoveLeft :: proc(buffer: *Buffer, pos: int): int {
|
||||
pos -= 1;
|
||||
pos = AdjustUTF8Pos(buffer, pos, direction = -1);
|
||||
return pos;
|
||||
}
|
||||
|
||||
MoveRight :: proc(buffer: *Buffer, pos: int): int {
|
||||
pos += 1;
|
||||
pos = AdjustUTF8Pos(buffer, pos, direction = +1);
|
||||
return pos;
|
||||
}
|
||||
|
||||
CalculateVisualPos :: proc(buffer: *Buffer, pos: int): Vec2I {
|
||||
line: LineInfo = FindLineOfPos(buffer, pos);
|
||||
|
||||
iter := Iterate(buffer, line.range.min, line.range.max);
|
||||
for IsValid(iter); Advance(&iter) {
|
||||
if iter.pos == pos {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result: Vec2I = {iter.codepoint_index, line.number};
|
||||
return result;
|
||||
}
|
||||
|
||||
CalculatePosFromVisualPos :: proc(buffer: *Buffer, vpos: Vec2I): int {
|
||||
line := GetLine(buffer, vpos.y);
|
||||
iter := Iterate(buffer, line.range.min, line.range.max);
|
||||
for IsValid(iter); Advance(&iter) {
|
||||
if iter.codepoint_index == vpos.x {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return iter.pos;
|
||||
}
|
||||
|
||||
CalculateWorldPosUnscrolled :: proc(vpos: Vec2I): Vector2 {
|
||||
result: Vector2 = {Monosize.x * :f32(vpos.x), Monosize.y * :f32(vpos.y)};
|
||||
return result;
|
||||
}
|
||||
|
||||
CalculateWorldPos :: proc(vpos: Vec2I): Vector2 {
|
||||
result: Vector2 = {Monosize.x * :f32(vpos.x) - Scroll.x, Monosize.y * :f32(vpos.y) - Scroll.y};
|
||||
return result;
|
||||
}
|
||||
|
||||
MoveDown :: proc(buffer: *Buffer, pos: int): int {
|
||||
vpos := CalculateVisualPos(buffer, pos);
|
||||
if !IsLineValid(buffer, vpos.y + 1) return pos;
|
||||
|
||||
result := CalculatePosFromVisualPos(buffer, {vpos.x, vpos.y + 1});
|
||||
return result;
|
||||
}
|
||||
|
||||
MoveUp :: proc(buffer: *Buffer, pos: int): int {
|
||||
vpos := CalculateVisualPos(buffer, pos);
|
||||
if !IsLineValid(buffer, vpos.y - 1) return pos;
|
||||
|
||||
result := CalculatePosFromVisualPos(buffer, {vpos.x, vpos.y - 1});
|
||||
return result;
|
||||
}
|
||||
|
||||
DrawRect :: proc(rec: Rect2P, color: Color) {
|
||||
rectangle := Rect2PToRectangle(rec);
|
||||
DrawRectangleRec(rectangle, color);
|
||||
}
|
||||
105
examples/use_as_data_format_with_typechecking/build.cpp
Normal file
105
examples/use_as_data_format_with_typechecking/build.cpp
Normal file
@@ -0,0 +1,105 @@
|
||||
void OnExprResolved_MakeSureConstantsWork(LC_AST *n, LC_Operand *op);
|
||||
bool use_as_data_format_with_typechecking() {
|
||||
LC_Lang *lang = LC_LangAlloc();
|
||||
lang->use_colored_terminal_output = UseColoredIO;
|
||||
lang->breakpoint_on_error = BreakpointOnError;
|
||||
lang->on_expr_resolved = OnExprResolved_MakeSureConstantsWork;
|
||||
LC_LangBegin(lang);
|
||||
LC_RegisterPackageDir("../examples/");
|
||||
LC_RegisterPackageDir("../pkgs");
|
||||
|
||||
LC_Intern name = LC_ILit("use_as_data_format_with_typechecking");
|
||||
LC_ASTRefList packages = LC_ResolvePackageByName(name);
|
||||
if (L->errors) {
|
||||
LC_LangEnd(lang);
|
||||
return false;
|
||||
}
|
||||
|
||||
LC_AST *package = LC_GetPackageByName(name);
|
||||
|
||||
// Constants
|
||||
{
|
||||
LC_Decl *int_value = LC_FindDeclInScope(package->apackage.scope, LC_ILit("IntValue"));
|
||||
int64_t IntValue = LC_Bigint_as_signed(&int_value->val.i);
|
||||
IO_Assert(IntValue == 232);
|
||||
}
|
||||
|
||||
{
|
||||
LC_Decl *decl = LC_FindDeclInScope(package->apackage.scope, LC_ILit("FloatValue"));
|
||||
IO_Assert(decl->val.d == 13.0);
|
||||
}
|
||||
|
||||
{
|
||||
LC_Decl *decl = LC_FindDeclInScope(package->apackage.scope, LC_ILit("SomeString"));
|
||||
IO_Assert(decl->val.name == LC_ILit("Thing"));
|
||||
}
|
||||
|
||||
// Player
|
||||
|
||||
int64_t LEVEL_BEGIN = -1;
|
||||
{
|
||||
LC_Decl *int_value = LC_FindDeclInScope(package->apackage.scope, LC_ILit("LEVEL_BEGIN"));
|
||||
LEVEL_BEGIN = LC_Bigint_as_signed(&int_value->val.i);
|
||||
IO_Assert(LEVEL_BEGIN == 2);
|
||||
}
|
||||
|
||||
{
|
||||
/* @todo:
|
||||
double DashSpeed = GetFloatValue(p, "Player.DashSpeed");
|
||||
double Hurtbox = GetFloatValue(p, "Player[0].Something.min.x");
|
||||
int64_t DashVFloorSnapDist = GetIntValue(p, "Player.DashVFloorSnapDist");
|
||||
|
||||
double Variable = GetFloatValue(p, "Variable");
|
||||
*/
|
||||
|
||||
LC_Decl *decl = LC_FindDeclInScope(package->apackage.scope, LC_ILit("Player"));
|
||||
LC_ResolvedCompo *items = decl->ast->dvar.expr->ecompo.resolved_items;
|
||||
for (LC_ResolvedCompoItem *it = items->first; it; it = it->next) {
|
||||
LC_Intern name = it->t->name;
|
||||
|
||||
if (name == LC_ILit("DashSpeed")) {
|
||||
double DashSpeed = it->expr->const_val.d;
|
||||
IO_Assert(DashSpeed == 240.0);
|
||||
} else if (name == LC_ILit("DashCooldown")) {
|
||||
double DashCooldown = it->expr->const_val.d;
|
||||
IO_Assert(DashCooldown > 0.19 && DashCooldown < 0.21);
|
||||
} else if (name == LC_ILit("Level")) {
|
||||
int64_t Level = LC_Bigint_as_signed(&it->expr->const_val.i);
|
||||
IO_Assert(Level == LEVEL_BEGIN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
LC_Decl *decl = LC_FindDeclInScope(package->apackage.scope, LC_ILit("Variable"));
|
||||
IO_Assert(decl->val.d == 32.0);
|
||||
}
|
||||
|
||||
{
|
||||
LC_Decl *decl = LC_FindDeclInScope(package->apackage.scope, LC_ILit("Reference"));
|
||||
IO_Assert(decl->val.d == 64.0);
|
||||
}
|
||||
|
||||
LC_LangEnd(lang);
|
||||
return true;
|
||||
}
|
||||
|
||||
void OnExprResolved_MakeSureConstantsWork(LC_AST *n, LC_Operand *op) {
|
||||
switch (n->kind) {
|
||||
case LC_ASTKind_ExprCast:
|
||||
case LC_ASTKind_ExprAddPtr:
|
||||
case LC_ASTKind_ExprIndex:
|
||||
case LC_ASTKind_ExprGetPointerOfValue:
|
||||
case LC_ASTKind_ExprGetValueOfPointer:
|
||||
case LC_ASTKind_ExprCall:
|
||||
LC_ReportASTError(n, "illegal expression kind");
|
||||
return;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
|
||||
if (LC_IsFloat(op->type)) op->type = L->tuntypedfloat;
|
||||
if (LC_IsInt(op->type)) op->type = L->tuntypedint;
|
||||
if (LC_IsStr(op->type)) op->type = L->tuntypedstring;
|
||||
op->flags |= LC_OPF_UTConst;
|
||||
}
|
||||
57
examples/use_as_data_format_with_typechecking/data.lc
Normal file
57
examples/use_as_data_format_with_typechecking/data.lc
Normal file
@@ -0,0 +1,57 @@
|
||||
Vector2 :: struct {
|
||||
x: float;
|
||||
y: float;
|
||||
}
|
||||
|
||||
Hitbox :: struct {
|
||||
min: Vector2;
|
||||
max: Vector2;
|
||||
}
|
||||
|
||||
TPlayer :: struct {
|
||||
DashSpeed: float;
|
||||
EndDashSpeed: float;
|
||||
EndDashUpMult: float;
|
||||
DashTime: float;
|
||||
DashCooldown: float;
|
||||
DashRefillCooldown: float;
|
||||
DashHJumpThruNudge: int;
|
||||
DashCornerCorrection: int;
|
||||
DashVFloorSnapDist: int;
|
||||
DashAttackTime: float;
|
||||
|
||||
Level: int;
|
||||
NormalHitbox: Hitbox;
|
||||
DuckHitbox: Hitbox;
|
||||
|
||||
NormalHurtbox: Hitbox;
|
||||
DuckHurtbox: Hitbox;
|
||||
}
|
||||
|
||||
Player: TPlayer = {
|
||||
DashSpeed = 240,
|
||||
EndDashSpeed = 160,
|
||||
EndDashUpMult = 0.75,
|
||||
DashTime = 0.15,
|
||||
DashCooldown = 0.2,
|
||||
DashRefillCooldown = 0.1,
|
||||
DashHJumpThruNudge = 6,
|
||||
DashCornerCorrection = 4,
|
||||
DashVFloorSnapDist = 3,
|
||||
DashAttackTime = 0.3,
|
||||
|
||||
Level = LEVEL_BEGIN,
|
||||
NormalHitbox = { { 1, 1 }, { 2, 2 } },
|
||||
NormalHurtbox = { min = { x = 1, y = 1 }, max = { 2 } },
|
||||
};
|
||||
|
||||
LEVEL_TUTORIAL :: 1;
|
||||
LEVEL_BEGIN :: ^;
|
||||
|
||||
IntValue :: 232;
|
||||
FloatValue :: 13.0;
|
||||
SomeString :: "Thing";
|
||||
|
||||
Variable: float = 32.0;
|
||||
Reference := Variable + Variable;
|
||||
|
||||
Reference in New Issue
Block a user