Init new repository

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

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

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

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

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

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

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

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

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

1471
src/compiler/bigint.c Normal file

File diff suppressed because it is too large Load Diff

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

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

View File

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

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

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

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

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

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

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

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

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

View File

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

1696
src/compiler/lib_compiler.h Normal file

File diff suppressed because it is too large Load Diff

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

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

1079
src/compiler/parse.c Normal file

File diff suppressed because it is too large Load Diff

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

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

1496
src/compiler/resolve.c Normal file

File diff suppressed because it is too large Load Diff

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

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

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

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

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

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

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

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

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

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

View File

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

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

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

View File

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

View File

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