Init new repository
This commit is contained in:
138
src/compiler/arena.c
Normal file
138
src/compiler/arena.c
Normal file
@@ -0,0 +1,138 @@
|
||||
#if defined(LC_USE_ADDRESS_SANITIZER)
|
||||
#include <sanitizer/asan_interface.h>
|
||||
#endif
|
||||
|
||||
#if !defined(ASAN_POISON_MEMORY_REGION)
|
||||
#define LC_ASAN_POISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
|
||||
#define LC_ASAN_UNPOISON_MEMORY_REGION(addr, size) ((void)(addr), (void)(size))
|
||||
#else
|
||||
#define LC_ASAN_POISON_MEMORY_REGION(addr, size) ASAN_POISON_MEMORY_REGION(addr, size)
|
||||
#define LC_ASAN_UNPOISON_MEMORY_REGION(addr, size) ASAN_UNPOISON_MEMORY_REGION(addr, size)
|
||||
#endif
|
||||
|
||||
#define LC_DEFAULT_RESERVE_SIZE LC_GIB(1)
|
||||
#define LC_DEFAULT_ALIGNMENT 8
|
||||
#define LC_COMMIT_ADD_SIZE LC_MIB(4)
|
||||
|
||||
LC_FUNCTION uint8_t *LC_V_AdvanceCommit(LC_VMemory *m, size_t *commit_size, size_t page_size) {
|
||||
size_t aligned_up_commit = LC_AlignUp(*commit_size, page_size);
|
||||
size_t to_be_total_commited_size = aligned_up_commit + m->commit;
|
||||
size_t to_be_total_commited_size_clamped_to_reserve = LC_CLAMP_TOP(to_be_total_commited_size, m->reserve);
|
||||
size_t adjusted_to_boundary_commit = to_be_total_commited_size_clamped_to_reserve - m->commit;
|
||||
LC_Assertf(adjusted_to_boundary_commit, "Reached the virtual memory reserved boundary");
|
||||
*commit_size = adjusted_to_boundary_commit;
|
||||
|
||||
if (adjusted_to_boundary_commit == 0) {
|
||||
return 0;
|
||||
}
|
||||
uint8_t *result = m->data + m->commit;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_PopToPos(LC_Arena *arena, size_t pos) {
|
||||
LC_Assertf(arena->len >= arena->base_len, "Bug: arena->len shouldn't ever be smaller then arena->base_len");
|
||||
pos = LC_CLAMP(pos, arena->base_len, arena->len);
|
||||
size_t size = arena->len - pos;
|
||||
arena->len = pos;
|
||||
LC_ASAN_POISON_MEMORY_REGION(arena->memory.data + arena->len, size);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_PopSize(LC_Arena *arena, size_t size) {
|
||||
LC_PopToPos(arena, arena->len - size);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_DeallocateArena(LC_Arena *arena) {
|
||||
LC_VDeallocate(&arena->memory);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_ResetArena(LC_Arena *arena) {
|
||||
LC_PopToPos(arena, 0);
|
||||
}
|
||||
|
||||
LC_FUNCTION size_t LC__AlignLen(LC_Arena *a) {
|
||||
size_t align_offset = a->alignment ? LC_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
|
||||
size_t aligned = a->len + align_offset;
|
||||
return aligned;
|
||||
}
|
||||
|
||||
LC_FUNCTION void *LC__PushSizeNonZeroed(LC_Arena *a, size_t size) {
|
||||
size_t align_offset = a->alignment ? LC_GetAlignOffset((uintptr_t)a->memory.data + (uintptr_t)a->len, a->alignment) : 0;
|
||||
size_t aligned_len = a->len + align_offset;
|
||||
size_t size_with_alignment = size + align_offset;
|
||||
|
||||
if (a->len + size_with_alignment > a->memory.commit) {
|
||||
if (a->memory.reserve == 0) {
|
||||
LC_Assertf(0, "Pushing on uninitialized arena with zero initialization turned off");
|
||||
}
|
||||
bool result = LC_VCommit(&a->memory, size_with_alignment + LC_COMMIT_ADD_SIZE);
|
||||
LC_Assertf(result, "%s(%d): Failed to commit memory more memory! reserve: %zu commit: %zu len: %zu size_with_alignment: %zu", LC_BeginTempdSourceLoc.file, LC_BeginTempdSourceLoc.line, a->memory.reserve, a->memory.commit, a->len, size_with_alignment);
|
||||
(void)result;
|
||||
}
|
||||
|
||||
uint8_t *result = a->memory.data + aligned_len;
|
||||
a->len += size_with_alignment;
|
||||
LC_Assertf(a->len <= a->memory.commit, "%s(%d): Reached commit boundary! reserve: %zu commit: %zu len: %zu base_len: %zu alignment: %d size_with_alignment: %zu", LC_BeginTempdSourceLoc.file, LC_BeginTempdSourceLoc.line, a->memory.reserve, a->memory.commit, a->len, a->base_len, a->alignment, size_with_alignment);
|
||||
LC_ASAN_UNPOISON_MEMORY_REGION(result, size);
|
||||
return (void *)result;
|
||||
}
|
||||
|
||||
LC_FUNCTION void *LC__PushSize(LC_Arena *arena, size_t size) {
|
||||
void *result = LC__PushSizeNonZeroed(arena, size);
|
||||
LC_MemoryZero(result, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Arena LC_PushArena(LC_Arena *arena, size_t size) {
|
||||
LC_Arena result;
|
||||
LC_MemoryZero(&result, sizeof(result));
|
||||
result.memory.data = LC_PushArrayNonZeroed(arena, uint8_t, size);
|
||||
result.memory.commit = size;
|
||||
result.memory.reserve = size;
|
||||
result.alignment = arena->alignment;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Arena *LC_PushArenaP(LC_Arena *arena, size_t size) {
|
||||
LC_Arena *result = LC_PushStruct(arena, LC_Arena);
|
||||
*result = LC_PushArena(arena, size);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_InitArenaEx(LC_Arena *a, size_t reserve) {
|
||||
a->memory = LC_VReserve(reserve);
|
||||
LC_ASAN_POISON_MEMORY_REGION(a->memory.data, a->memory.reserve);
|
||||
a->alignment = LC_DEFAULT_ALIGNMENT;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_InitArena(LC_Arena *a) {
|
||||
LC_InitArenaEx(a, LC_DEFAULT_RESERVE_SIZE);
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Arena *LC_BootstrapArena(void) {
|
||||
LC_Arena bootstrap_arena = {0};
|
||||
LC_InitArena(&bootstrap_arena);
|
||||
|
||||
LC_Arena *arena = LC_PushStruct(&bootstrap_arena, LC_Arena);
|
||||
*arena = bootstrap_arena;
|
||||
arena->base_len = arena->len;
|
||||
return arena;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_InitArenaFromBuffer(LC_Arena *arena, void *buffer, size_t size) {
|
||||
arena->memory.data = (uint8_t *)buffer;
|
||||
arena->memory.commit = size;
|
||||
arena->memory.reserve = size;
|
||||
arena->alignment = LC_DEFAULT_ALIGNMENT;
|
||||
LC_ASAN_POISON_MEMORY_REGION(arena->memory.data, arena->memory.reserve);
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_TempArena LC_BeginTemp(LC_Arena *arena) {
|
||||
LC_TempArena result;
|
||||
result.pos = arena->len;
|
||||
result.arena = arena;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_EndTemp(LC_TempArena checkpoint) {
|
||||
LC_PopToPos(checkpoint.arena, checkpoint.pos);
|
||||
}
|
||||
167
src/compiler/ast.c
Normal file
167
src/compiler/ast.c
Normal file
@@ -0,0 +1,167 @@
|
||||
LC_FUNCTION LC_AST *LC_CreateAST(LC_Token *pos, LC_ASTKind kind) {
|
||||
LC_AST *n = LC_PushStruct(L->ast_arena, LC_AST);
|
||||
n->id = ++L->ast_count;
|
||||
n->kind = kind;
|
||||
n->pos = pos;
|
||||
if (L->parser == &L->quick_parser) n->pos = &L->BuiltinToken;
|
||||
return n;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_AST *LC_CreateUnary(LC_Token *pos, LC_TokenKind op, LC_AST *expr) {
|
||||
LC_AST *r = LC_CreateAST(pos, LC_ASTKind_ExprUnary);
|
||||
r->eunary.op = op;
|
||||
r->eunary.expr = expr;
|
||||
return r;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_AST *LC_CreateBinary(LC_Token *pos, LC_AST *left, LC_AST *right, LC_TokenKind op) {
|
||||
LC_AST *r = LC_CreateAST(pos, LC_ASTKind_ExprBinary);
|
||||
r->ebinary.op = op;
|
||||
r->ebinary.left = left;
|
||||
r->ebinary.right = right;
|
||||
return r;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_AST *LC_CreateIndex(LC_Token *pos, LC_AST *left, LC_AST *index) {
|
||||
LC_AST *r = LC_CreateAST(pos, LC_ASTKind_ExprIndex);
|
||||
r->eindex.index = index;
|
||||
r->eindex.base = left;
|
||||
return r;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_SetPointerSizeAndAlign(int size, int align) {
|
||||
L->pointer_align = align;
|
||||
L->pointer_size = size;
|
||||
if (L->tpvoid) {
|
||||
L->tpvoid->size = size;
|
||||
L->tpvoid->align = align;
|
||||
}
|
||||
if (L->tpchar) {
|
||||
L->tpchar->size = size;
|
||||
L->tpchar->align = align;
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Type *LC_CreateType(LC_TypeKind kind) {
|
||||
LC_Type *r = LC_PushStruct(L->type_arena, LC_Type);
|
||||
L->type_count += 1;
|
||||
r->kind = kind;
|
||||
r->id = ++L->typeids;
|
||||
if (r->kind == LC_TypeKind_Proc || r->kind == LC_TypeKind_Pointer) {
|
||||
r->size = L->pointer_size;
|
||||
r->align = L->pointer_align;
|
||||
r->is_unsigned = true;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Type *LC_CreateTypedef(LC_Decl *decl, LC_Type *base) {
|
||||
LC_Type *n = LC_CreateType(base->kind);
|
||||
*n = *base;
|
||||
decl->typedef_renamed_type_decl = base->decl;
|
||||
n->decl = decl;
|
||||
return n;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Type *LC_CreatePointerType(LC_Type *type) {
|
||||
uint64_t key = (uint64_t)type;
|
||||
LC_Type *entry = (LC_Type *)LC_MapGetU64(&L->type_map, key);
|
||||
if (entry) {
|
||||
return entry;
|
||||
}
|
||||
LC_Type *n = LC_CreateType(LC_TypeKind_Pointer);
|
||||
n->tbase = type;
|
||||
LC_MapInsertU64(&L->type_map, key, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Type *LC_CreateArrayType(LC_Type *type, int size) {
|
||||
uint64_t size_key = LC_HashBytes(&size, sizeof(size));
|
||||
uint64_t type_key = LC_HashBytes(type, sizeof(*type));
|
||||
uint64_t key = LC_HashMix(size_key, type_key);
|
||||
LC_Type *entry = (LC_Type *)LC_MapGetU64(&L->type_map, key);
|
||||
if (entry) {
|
||||
return entry;
|
||||
}
|
||||
LC_Type *n = LC_CreateType(LC_TypeKind_Array);
|
||||
n->tbase = type;
|
||||
n->tarray.size = size;
|
||||
n->size = type->size * size;
|
||||
n->align = type->align;
|
||||
LC_MapInsertU64(&L->type_map, key, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Type *LC_CreateProcType(LC_TypeMemberList args, LC_Type *ret, bool has_vargs, bool has_vargs_any_promotion) {
|
||||
LC_ASSERT(NULL, ret);
|
||||
uint64_t key = LC_HashBytes(ret, sizeof(*ret));
|
||||
key = LC_HashMix(key, LC_HashBytes(&has_vargs, sizeof(has_vargs)));
|
||||
key = LC_HashMix(key, LC_HashBytes(&has_vargs_any_promotion, sizeof(has_vargs_any_promotion)));
|
||||
int procarg_count = 0;
|
||||
LC_TypeFor(it, args.first) {
|
||||
key = LC_HashMix(LC_HashBytes(it->type, sizeof(it->type[0])), key);
|
||||
key = LC_HashMix(LC_HashBytes((char *)it->name, LC_StrLen((char *)it->name)), key);
|
||||
if (it->default_value_expr) {
|
||||
key = LC_HashMix(LC_HashBytes(&it->default_value_expr, sizeof(LC_AST *)), key);
|
||||
}
|
||||
procarg_count += 1;
|
||||
}
|
||||
LC_Type *n = (LC_Type *)LC_MapGetU64(&L->type_map, key);
|
||||
if (n) return n;
|
||||
|
||||
n = LC_CreateType(LC_TypeKind_Proc);
|
||||
n->tproc.args = args;
|
||||
n->tproc.vargs = has_vargs;
|
||||
n->tproc.vargs_any_promotion = has_vargs_any_promotion;
|
||||
n->tproc.ret = ret;
|
||||
LC_MapInsertU64(&L->type_map, key, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Type *LC_CreateIncompleteType(LC_Decl *decl) {
|
||||
LC_Type *n = LC_CreateType(LC_TypeKind_Incomplete);
|
||||
n->decl = decl;
|
||||
return n;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Type *LC_CreateUntypedIntEx(LC_Type *base, LC_Decl *decl) {
|
||||
uint64_t hash_base = LC_HashBytes(base, sizeof(*base));
|
||||
uint64_t untyped_int = LC_TypeKind_UntypedInt;
|
||||
uint64_t key = LC_HashMix(hash_base, untyped_int);
|
||||
LC_Type *n = (LC_Type *)LC_MapGetU64(&L->type_map, key);
|
||||
if (n) return n;
|
||||
|
||||
n = LC_CreateType(LC_TypeKind_UntypedInt);
|
||||
n->tbase = base;
|
||||
n->decl = decl;
|
||||
return n;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Type *LC_CreateUntypedInt(LC_Type *base) {
|
||||
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Type, LC_ILit("UntypedInt"), &L->NullAST);
|
||||
LC_Type *n = LC_CreateUntypedIntEx(base, decl);
|
||||
return n;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_TypeMember *LC_AddTypeToList(LC_TypeMemberList *list, LC_Intern name, LC_Type *type, LC_AST *ast) {
|
||||
LC_TypeFor(it, list->first) {
|
||||
if (name == it->name) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
LC_TypeMember *r = LC_PushStruct(L->arena, LC_TypeMember);
|
||||
r->name = name;
|
||||
r->type = type;
|
||||
r->ast = ast;
|
||||
LC_DLLAdd(list->first, list->last, r);
|
||||
list->count += 1;
|
||||
return r;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Type *LC_StripPointer(LC_Type *type) {
|
||||
if (type->kind == LC_TypeKind_Pointer) {
|
||||
return type->tbase;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
283
src/compiler/ast_copy.c
Normal file
283
src/compiler/ast_copy.c
Normal file
@@ -0,0 +1,283 @@
|
||||
LC_FUNCTION LC_AST *LC_CopyAST(LC_Arena *arena, LC_AST *n) {
|
||||
if (n == NULL) return NULL;
|
||||
|
||||
LC_AST *result = LC_PushStruct(arena, LC_AST);
|
||||
result->kind = n->kind;
|
||||
result->id = ++L->ast_count;
|
||||
result->notes = LC_CopyAST(arena, n->notes);
|
||||
result->pos = n->pos;
|
||||
|
||||
switch (n->kind) {
|
||||
case LC_ASTKind_File: {
|
||||
result->afile.x = n->afile.x;
|
||||
|
||||
LC_ASTFor(it, n->afile.fimport) {
|
||||
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||
LC_DLLAdd(result->afile.fimport, result->afile.limport, it_copy);
|
||||
}
|
||||
LC_ASTFor(it, n->afile.fdecl) {
|
||||
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||
LC_DLLAdd(result->afile.fdecl, result->afile.ldecl, it_copy);
|
||||
}
|
||||
LC_ASTFor(it, n->afile.fdiscarded) {
|
||||
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||
LC_DLLAdd(result->afile.fdiscarded, result->afile.ldiscarded, it_copy);
|
||||
}
|
||||
|
||||
result->afile.build_if = n->afile.build_if;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclProc: {
|
||||
result->dbase.name = n->dbase.name;
|
||||
result->dproc.body = LC_CopyAST(arena, n->dproc.body);
|
||||
result->dproc.type = LC_CopyAST(arena, n->dproc.type);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_NoteList: {
|
||||
LC_ASTFor(it, n->anote_list.first) {
|
||||
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||
LC_DLLAdd(result->anote_list.first, result->anote_list.last, it_copy);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_TypespecAggMem:
|
||||
case LC_ASTKind_TypespecProcArg: {
|
||||
result->tproc_arg.name = n->tproc_arg.name;
|
||||
result->tproc_arg.type = LC_CopyAST(arena, n->tproc_arg.type);
|
||||
result->tproc_arg.expr = LC_CopyAST(arena, n->tproc_arg.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprNote: {
|
||||
result->enote.expr = LC_CopyAST(arena, n->enote.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtSwitch: {
|
||||
LC_ASTFor(it, n->sswitch.first) {
|
||||
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||
LC_DLLAdd(result->sswitch.first, result->sswitch.last, it_copy);
|
||||
}
|
||||
result->sswitch.total_switch_case_count = n->sswitch.total_switch_case_count;
|
||||
result->sswitch.expr = LC_CopyAST(arena, n->sswitch.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtSwitchCase: {
|
||||
LC_ASTFor(it, n->scase.first) {
|
||||
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||
LC_DLLAdd(result->scase.first, result->scase.last, it_copy);
|
||||
}
|
||||
result->scase.body = LC_CopyAST(arena, n->scase.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtSwitchDefault: {
|
||||
LC_ASTFor(it, n->scase.first) {
|
||||
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||
LC_DLLAdd(result->scase.first, result->scase.last, it_copy);
|
||||
}
|
||||
result->scase.body = LC_CopyAST(arena, n->scase.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtIf: {
|
||||
LC_ASTFor(it, n->sif.first) {
|
||||
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||
LC_DLLAdd(result->sswitch.first, result->sswitch.last, it_copy);
|
||||
}
|
||||
result->sif.expr = LC_CopyAST(arena, n->sif.expr);
|
||||
result->sif.body = LC_CopyAST(arena, n->sif.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtElse:
|
||||
case LC_ASTKind_StmtElseIf: {
|
||||
result->sif.expr = LC_CopyAST(arena, n->sif.expr);
|
||||
result->sif.body = LC_CopyAST(arena, n->sif.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_GlobImport: {
|
||||
result->gimport.name = n->gimport.name;
|
||||
result->gimport.path = n->gimport.path;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclNote: {
|
||||
result->dnote.expr = LC_CopyAST(arena, n->dnote.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclUnion:
|
||||
case LC_ASTKind_DeclStruct: {
|
||||
result->dbase.name = n->dbase.name;
|
||||
LC_ASTFor(it, n->dagg.first) {
|
||||
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||
LC_DLLAdd(result->dagg.first, result->dagg.last, it_copy);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclVar: {
|
||||
result->dbase.name = n->dbase.name;
|
||||
result->dvar.type = LC_CopyAST(arena, n->dvar.type);
|
||||
result->dvar.expr = LC_CopyAST(arena, n->dvar.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclConst: {
|
||||
result->dbase.name = n->dbase.name;
|
||||
result->dconst.expr = LC_CopyAST(arena, n->dconst.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclTypedef: {
|
||||
result->dbase.name = n->dbase.name;
|
||||
result->dtypedef.type = LC_CopyAST(arena, n->dtypedef.type);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprField:
|
||||
case LC_ASTKind_TypespecField: {
|
||||
result->efield.left = LC_CopyAST(arena, n->efield.left);
|
||||
result->efield.right = n->efield.right;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_TypespecIdent: {
|
||||
result->eident.name = n->eident.name;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_TypespecPointer: {
|
||||
result->tpointer.base = LC_CopyAST(arena, n->tpointer.base);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_TypespecArray: {
|
||||
result->tarray.base = LC_CopyAST(arena, n->tarray.base);
|
||||
result->tarray.index = LC_CopyAST(arena, n->tarray.index);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_TypespecProc: {
|
||||
LC_ASTFor(it, n->tproc.first) {
|
||||
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||
LC_DLLAdd(result->tproc.first, result->tproc.last, it_copy);
|
||||
}
|
||||
result->tproc.ret = LC_CopyAST(arena, n->tproc.ret);
|
||||
result->tproc.vargs = n->tproc.vargs;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtBlock: {
|
||||
LC_ASTFor(it, n->sblock.first) {
|
||||
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||
LC_DLLAdd(result->sblock.first, result->sblock.last, it_copy);
|
||||
|
||||
if (it_copy->kind == LC_ASTKind_StmtDefer) {
|
||||
LC_SLLStackAddMod(result->sblock.first_defer, it_copy, sdefer.next);
|
||||
}
|
||||
}
|
||||
if (n->sblock.first_defer) {
|
||||
LC_ASSERT(result, result->sblock.first_defer);
|
||||
}
|
||||
result->sblock.kind = n->sblock.kind;
|
||||
result->sblock.name = n->sblock.name;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtNote: {
|
||||
result->snote.expr = LC_CopyAST(arena, n->snote.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtReturn: {
|
||||
result->sreturn.expr = LC_CopyAST(arena, n->sreturn.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtBreak: {
|
||||
result->sbreak.name = n->sbreak.name;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtContinue: {
|
||||
result->scontinue.name = n->scontinue.name;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtDefer: {
|
||||
result->sdefer.body = LC_CopyAST(arena, n->sdefer.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtFor: {
|
||||
result->sfor.init = LC_CopyAST(arena, n->sfor.init);
|
||||
result->sfor.cond = LC_CopyAST(arena, n->sfor.cond);
|
||||
result->sfor.inc = LC_CopyAST(arena, n->sfor.inc);
|
||||
result->sfor.body = LC_CopyAST(arena, n->sfor.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtAssign: {
|
||||
result->sassign.op = n->sassign.op;
|
||||
result->sassign.left = LC_CopyAST(arena, n->sassign.left);
|
||||
result->sassign.right = LC_CopyAST(arena, n->sassign.right);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtExpr: {
|
||||
result->sexpr.expr = LC_CopyAST(arena, n->sexpr.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtVar: {
|
||||
result->svar.type = LC_CopyAST(arena, n->svar.type);
|
||||
result->svar.expr = LC_CopyAST(arena, n->svar.expr);
|
||||
result->svar.name = n->svar.name;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtConst: {
|
||||
result->sconst.expr = LC_CopyAST(arena, n->sconst.expr);
|
||||
result->sconst.name = n->sconst.name;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprIdent: {
|
||||
result->eident.name = n->eident.name;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprBool:
|
||||
case LC_ASTKind_ExprFloat:
|
||||
case LC_ASTKind_ExprInt:
|
||||
case LC_ASTKind_ExprString: {
|
||||
result->eatom = n->eatom;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprType: {
|
||||
result->etype.type = LC_CopyAST(arena, n->etype.type);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprAddPtr:
|
||||
case LC_ASTKind_ExprBinary: {
|
||||
result->ebinary.op = n->ebinary.op;
|
||||
result->ebinary.left = LC_CopyAST(arena, n->ebinary.left);
|
||||
result->ebinary.right = LC_CopyAST(arena, n->ebinary.right);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprGetPointerOfValue:
|
||||
case LC_ASTKind_ExprGetValueOfPointer:
|
||||
case LC_ASTKind_ExprUnary: {
|
||||
result->eunary.op = n->eunary.op;
|
||||
result->eunary.expr = LC_CopyAST(arena, n->eunary.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprCompoundItem:
|
||||
case LC_ASTKind_ExprCallItem: {
|
||||
result->ecompo_item.name = n->ecompo_item.name;
|
||||
result->ecompo_item.index = LC_CopyAST(arena, n->ecompo_item.index);
|
||||
result->ecompo_item.expr = LC_CopyAST(arena, n->ecompo_item.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprBuiltin:
|
||||
case LC_ASTKind_Note:
|
||||
case LC_ASTKind_ExprCall:
|
||||
case LC_ASTKind_ExprCompound: {
|
||||
LC_ASTFor(it, n->ecompo.first) {
|
||||
LC_AST *it_copy = LC_CopyAST(arena, it);
|
||||
LC_DLLAdd(result->ecompo.first, result->ecompo.last, it_copy);
|
||||
}
|
||||
|
||||
result->ecompo.size = n->ecompo.size;
|
||||
result->ecompo.name = LC_CopyAST(arena, n->ecompo.name);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprCast: {
|
||||
result->ecast.type = LC_CopyAST(arena, n->ecast.type);
|
||||
result->ecast.expr = LC_CopyAST(arena, n->ecast.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprIndex: {
|
||||
result->eindex.index = LC_CopyAST(arena, n->eindex.index);
|
||||
result->eindex.base = LC_CopyAST(arena, n->eindex.base);
|
||||
} break;
|
||||
|
||||
default: LC_ReportASTError(n, "internal compiler error: failed to LC_CopyAST, got invalid ast kind: %s", LC_ASTKindToString(n->kind));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
316
src/compiler/ast_walk.c
Normal file
316
src/compiler/ast_walk.c
Normal file
@@ -0,0 +1,316 @@
|
||||
LC_FUNCTION void LC_ReserveAST(LC_ASTArray *stack, int size) {
|
||||
if (size > stack->cap) {
|
||||
LC_AST **new_stack = LC_PushArray(stack->arena, LC_AST *, size);
|
||||
|
||||
LC_MemoryCopy(new_stack, stack->data, stack->len * sizeof(LC_AST *));
|
||||
stack->data = new_stack;
|
||||
stack->cap = size;
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_PushAST(LC_ASTArray *stack, LC_AST *ast) {
|
||||
LC_ASSERT(NULL, stack->len <= stack->cap);
|
||||
LC_ASSERT(NULL, stack->arena);
|
||||
if (stack->len == stack->cap) {
|
||||
int new_cap = stack->cap < 16 ? 16 : stack->cap * 2;
|
||||
LC_AST **new_stack = LC_PushArray(stack->arena, LC_AST *, new_cap);
|
||||
|
||||
LC_MemoryCopy(new_stack, stack->data, stack->len * sizeof(LC_AST *));
|
||||
stack->data = new_stack;
|
||||
stack->cap = new_cap;
|
||||
}
|
||||
stack->data[stack->len++] = ast;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_PopAST(LC_ASTArray *stack) {
|
||||
LC_ASSERT(NULL, stack->arena);
|
||||
LC_ASSERT(NULL, stack->len > 0);
|
||||
stack->len -= 1;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_AST *LC_GetLastAST(LC_ASTArray *arr) {
|
||||
LC_ASSERT(NULL, arr->len > 0);
|
||||
LC_AST *result = arr->data[arr->len - 1];
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_WalkAST(LC_ASTWalker *ctx, LC_AST *n) {
|
||||
if (!ctx->depth_first) {
|
||||
ctx->proc(ctx, n);
|
||||
}
|
||||
|
||||
if (ctx->dont_recurse) {
|
||||
ctx->dont_recurse = false;
|
||||
return;
|
||||
}
|
||||
|
||||
LC_PushAST(&ctx->stack, n);
|
||||
switch (n->kind) {
|
||||
case LC_ASTKind_TypespecIdent:
|
||||
case LC_ASTKind_ExprIdent:
|
||||
case LC_ASTKind_ExprString:
|
||||
case LC_ASTKind_ExprInt:
|
||||
case LC_ASTKind_ExprFloat:
|
||||
case LC_ASTKind_GlobImport:
|
||||
case LC_ASTKind_ExprBool:
|
||||
case LC_ASTKind_StmtBreak:
|
||||
case LC_ASTKind_StmtContinue: break;
|
||||
|
||||
case LC_ASTKind_Package: {
|
||||
LC_ASTFor(it, n->apackage.ffile) LC_WalkAST(ctx, it);
|
||||
|
||||
ctx->inside_discarded += 1;
|
||||
if (ctx->visit_discarded) LC_ASTFor(it, n->apackage.fdiscarded) LC_WalkAST(ctx, it);
|
||||
ctx->inside_discarded -= 1;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_File: {
|
||||
LC_ASTFor(it, n->afile.fimport) LC_WalkAST(ctx, it);
|
||||
LC_ASTFor(it, n->afile.fdecl) {
|
||||
if (ctx->visit_notes == false && it->kind == LC_ASTKind_DeclNote) continue;
|
||||
LC_WalkAST(ctx, it);
|
||||
}
|
||||
|
||||
ctx->inside_discarded += 1;
|
||||
if (ctx->visit_discarded) LC_ASTFor(it, n->afile.fdiscarded) LC_WalkAST(ctx, it);
|
||||
ctx->inside_discarded -= 1;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclProc: {
|
||||
LC_WalkAST(ctx, n->dproc.type);
|
||||
if (n->dproc.body) LC_WalkAST(ctx, n->dproc.body);
|
||||
} break;
|
||||
case LC_ASTKind_NoteList: {
|
||||
if (ctx->visit_notes) LC_ASTFor(it, n->anote_list.first) LC_WalkAST(ctx, it);
|
||||
} break;
|
||||
case LC_ASTKind_TypespecProcArg: {
|
||||
LC_WalkAST(ctx, n->tproc_arg.type);
|
||||
if (n->tproc_arg.expr) LC_WalkAST(ctx, n->tproc_arg.expr);
|
||||
} break;
|
||||
case LC_ASTKind_TypespecAggMem: {
|
||||
LC_WalkAST(ctx, n->tproc_arg.type);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprNote: {
|
||||
ctx->inside_note += 1;
|
||||
if (ctx->visit_notes) LC_WalkAST(ctx, n->enote.expr);
|
||||
ctx->inside_note -= 1;
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtSwitch: {
|
||||
LC_WalkAST(ctx, n->sswitch.expr);
|
||||
LC_ASTFor(it, n->sswitch.first) LC_WalkAST(ctx, it);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtSwitchCase: {
|
||||
LC_ASTFor(it, n->scase.first) LC_WalkAST(ctx, it);
|
||||
LC_WalkAST(ctx, n->scase.body);
|
||||
} break;
|
||||
case LC_ASTKind_StmtSwitchDefault: {
|
||||
LC_ASTFor(it, n->scase.first) LC_WalkAST(ctx, it);
|
||||
LC_WalkAST(ctx, n->scase.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtIf: {
|
||||
LC_WalkAST(ctx, n->sif.expr);
|
||||
LC_WalkAST(ctx, n->sif.body);
|
||||
LC_ASTFor(it, n->sif.first) LC_WalkAST(ctx, it);
|
||||
} break;
|
||||
case LC_ASTKind_StmtElse:
|
||||
case LC_ASTKind_StmtElseIf: {
|
||||
if (n->sif.expr) LC_WalkAST(ctx, n->sif.expr);
|
||||
LC_WalkAST(ctx, n->sif.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclNote: {
|
||||
ctx->inside_note += 1;
|
||||
if (ctx->visit_notes) LC_WalkAST(ctx, n->dnote.expr);
|
||||
ctx->inside_note -= 1;
|
||||
} break;
|
||||
case LC_ASTKind_DeclUnion:
|
||||
case LC_ASTKind_DeclStruct: {
|
||||
LC_ASTFor(it, n->dagg.first) LC_WalkAST(ctx, it);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclVar: {
|
||||
if (n->dvar.type) LC_WalkAST(ctx, n->dvar.type);
|
||||
if (n->dvar.expr) LC_WalkAST(ctx, n->dvar.expr);
|
||||
} break;
|
||||
case LC_ASTKind_DeclConst: {
|
||||
if (n->dconst.expr) LC_WalkAST(ctx, n->dconst.expr);
|
||||
} break;
|
||||
case LC_ASTKind_DeclTypedef: {
|
||||
LC_WalkAST(ctx, n->dtypedef.type);
|
||||
} break;
|
||||
case LC_ASTKind_TypespecField: {
|
||||
LC_WalkAST(ctx, n->efield.left);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_TypespecPointer: {
|
||||
LC_WalkAST(ctx, n->tpointer.base);
|
||||
} break;
|
||||
case LC_ASTKind_TypespecArray: {
|
||||
LC_WalkAST(ctx, n->tarray.base);
|
||||
if (n->tarray.index) LC_WalkAST(ctx, n->tarray.index);
|
||||
} break;
|
||||
case LC_ASTKind_TypespecProc: {
|
||||
LC_ASTFor(it, n->tproc.first) LC_WalkAST(ctx, it);
|
||||
if (n->tproc.ret) LC_WalkAST(ctx, n->tproc.ret);
|
||||
} break;
|
||||
case LC_ASTKind_StmtBlock: {
|
||||
LC_ASTFor(it, n->sblock.first) LC_WalkAST(ctx, it);
|
||||
// hmm should we inline defers or maybe remove them from
|
||||
// the stmt list?
|
||||
// LC_ASTFor(it, n->sblock.first_defer) LC_WalkAST(ctx, it);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtNote: {
|
||||
ctx->inside_note += 1;
|
||||
if (ctx->visit_notes) LC_WalkAST(ctx, n->snote.expr);
|
||||
ctx->inside_note -= 1;
|
||||
} break;
|
||||
case LC_ASTKind_StmtReturn: {
|
||||
if (n->sreturn.expr) LC_WalkAST(ctx, n->sreturn.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtDefer: {
|
||||
LC_WalkAST(ctx, n->sdefer.body);
|
||||
} break;
|
||||
case LC_ASTKind_StmtFor: {
|
||||
if (n->sfor.init) LC_WalkAST(ctx, n->sfor.init);
|
||||
if (n->sfor.cond) LC_WalkAST(ctx, n->sfor.cond);
|
||||
if (n->sfor.inc) LC_WalkAST(ctx, n->sfor.inc);
|
||||
LC_WalkAST(ctx, n->sfor.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtAssign: {
|
||||
LC_WalkAST(ctx, n->sassign.left);
|
||||
LC_WalkAST(ctx, n->sassign.right);
|
||||
} break;
|
||||
case LC_ASTKind_StmtExpr: {
|
||||
LC_WalkAST(ctx, n->sexpr.expr);
|
||||
} break;
|
||||
case LC_ASTKind_StmtVar: {
|
||||
if (n->svar.type) LC_WalkAST(ctx, n->svar.type);
|
||||
if (n->svar.expr) LC_WalkAST(ctx, n->svar.expr);
|
||||
} break;
|
||||
case LC_ASTKind_StmtConst: {
|
||||
LC_WalkAST(ctx, n->sconst.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprType: {
|
||||
LC_WalkAST(ctx, n->etype.type);
|
||||
} break;
|
||||
case LC_ASTKind_ExprAddPtr:
|
||||
case LC_ASTKind_ExprBinary: {
|
||||
LC_WalkAST(ctx, n->ebinary.left);
|
||||
LC_WalkAST(ctx, n->ebinary.right);
|
||||
} break;
|
||||
case LC_ASTKind_ExprGetPointerOfValue:
|
||||
case LC_ASTKind_ExprGetValueOfPointer:
|
||||
case LC_ASTKind_ExprUnary: {
|
||||
LC_WalkAST(ctx, n->eunary.expr);
|
||||
} break;
|
||||
case LC_ASTKind_ExprCompoundItem:
|
||||
case LC_ASTKind_ExprCallItem: {
|
||||
if (n->ecompo_item.index) LC_WalkAST(ctx, n->ecompo_item.index);
|
||||
LC_WalkAST(ctx, n->ecompo_item.expr);
|
||||
} break;
|
||||
case LC_ASTKind_Note: {
|
||||
ctx->inside_note += 1;
|
||||
if (n->ecompo.name) LC_WalkAST(ctx, n->ecompo.name);
|
||||
LC_ASTFor(it, n->ecompo.first) LC_WalkAST(ctx, it);
|
||||
ctx->inside_note -= 1;
|
||||
} break;
|
||||
case LC_ASTKind_ExprBuiltin: {
|
||||
ctx->inside_builtin += 1;
|
||||
if (n->ecompo.name) LC_WalkAST(ctx, n->ecompo.name);
|
||||
LC_ASTFor(it, n->ecompo.first) LC_WalkAST(ctx, it);
|
||||
ctx->inside_builtin -= 1;
|
||||
} break;
|
||||
case LC_ASTKind_ExprCall:
|
||||
case LC_ASTKind_ExprCompound: {
|
||||
if (n->ecompo.name) LC_WalkAST(ctx, n->ecompo.name);
|
||||
LC_ASTFor(it, n->ecompo.first) LC_WalkAST(ctx, it);
|
||||
} break;
|
||||
case LC_ASTKind_ExprCast: {
|
||||
LC_WalkAST(ctx, n->ecast.type);
|
||||
LC_WalkAST(ctx, n->ecast.expr);
|
||||
} break;
|
||||
case LC_ASTKind_ExprField: {
|
||||
LC_WalkAST(ctx, n->efield.left);
|
||||
} break;
|
||||
case LC_ASTKind_ExprIndex: {
|
||||
LC_WalkAST(ctx, n->eindex.index);
|
||||
LC_WalkAST(ctx, n->eindex.base);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_Ignore:
|
||||
case LC_ASTKind_Error:
|
||||
default: LC_ReportASTError(n, "internal compiler error: got invalid ast kind during ast walk: %s", LC_ASTKindToString(n->kind));
|
||||
}
|
||||
|
||||
if (ctx->visit_notes && n->notes) {
|
||||
LC_WalkAST(ctx, n->notes);
|
||||
}
|
||||
LC_PopAST(&ctx->stack);
|
||||
|
||||
if (ctx->depth_first) {
|
||||
ctx->proc(ctx, n);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_ASTWalker LC_GetDefaultWalker(LC_Arena *arena, LC_ASTWalkProc *proc) {
|
||||
LC_ASTWalker result = {0};
|
||||
result.stack.arena = arena;
|
||||
result.proc = proc;
|
||||
result.depth_first = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION void WalkAndFlattenAST(LC_ASTWalker *ctx, LC_AST *n) {
|
||||
LC_ASTArray *array = (LC_ASTArray *)ctx->user_data;
|
||||
LC_PushAST(array, n);
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_ASTArray LC_FlattenAST(LC_Arena *arena, LC_AST *n) {
|
||||
LC_ASTArray array = {arena};
|
||||
LC_ASTWalker walker = LC_GetDefaultWalker(arena, WalkAndFlattenAST);
|
||||
walker.user_data = (void *)&array;
|
||||
LC_WalkAST(&walker, n);
|
||||
return array;
|
||||
}
|
||||
|
||||
LC_FUNCTION void WalkToFindSizeofLike(LC_ASTWalker *w, LC_AST *n) {
|
||||
if (n->kind == LC_ASTKind_ExprBuiltin) {
|
||||
LC_ASSERT(n, n->ecompo.name->kind == LC_ASTKind_ExprIdent);
|
||||
if (n->ecompo.name->eident.name == L->isizeof || n->ecompo.name->eident.name == L->ialignof || n->ecompo.name->eident.name == L->ioffsetof) {
|
||||
((bool *)w->user_data)[0] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_ContainsCBuiltin(LC_AST *n) {
|
||||
LC_TempArena checkpoint = LC_BeginTemp(L->arena);
|
||||
bool found = false;
|
||||
{
|
||||
LC_ASTWalker walker = LC_GetDefaultWalker(L->arena, WalkToFindSizeofLike);
|
||||
walker.depth_first = false;
|
||||
walker.user_data = (void *)&found;
|
||||
LC_WalkAST(&walker, n);
|
||||
}
|
||||
LC_EndTemp(checkpoint);
|
||||
return found;
|
||||
}
|
||||
|
||||
LC_FUNCTION void SetASTPosOnAll_Walk(LC_ASTWalker *ctx, LC_AST *n) {
|
||||
n->pos = (LC_Token *)ctx->user_data;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_SetASTPosOnAll(LC_AST *n, LC_Token *pos) {
|
||||
LC_TempArena check = LC_BeginTemp(L->arena);
|
||||
LC_ASTWalker walker = LC_GetDefaultWalker(L->arena, SetASTPosOnAll_Walk);
|
||||
walker.user_data = (void *)pos;
|
||||
LC_WalkAST(&walker, n);
|
||||
LC_EndTemp(check);
|
||||
}
|
||||
1471
src/compiler/bigint.c
Normal file
1471
src/compiler/bigint.c
Normal file
File diff suppressed because it is too large
Load Diff
198
src/compiler/common.c
Normal file
198
src/compiler/common.c
Normal file
@@ -0,0 +1,198 @@
|
||||
#if __cplusplus
|
||||
#define LC_Alignof(...) alignof(__VA_ARGS__)
|
||||
#else
|
||||
#define LC_Alignof(...) _Alignof(__VA_ARGS__)
|
||||
#endif
|
||||
|
||||
#define LC_WRAP_AROUND_POWER_OF_2(x, pow2) (((x) & ((pow2)-1llu)))
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define LC_DebugBreak() (L->breakpoint_on_error && IsDebuggerPresent() && (__debugbreak(), 0))
|
||||
#else
|
||||
#define LC_DebugBreak() (L->breakpoint_on_error && (__builtin_trap(), 0))
|
||||
#endif
|
||||
#define LC_FatalError() (L->breakpoint_on_error ? LC_DebugBreak() : (LC_Exit(1), 0))
|
||||
|
||||
LC_FUNCTION void LC_IgnoreMessage(LC_Token *pos, char *str, int len) {
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_SendErrorMessage(LC_Token *pos, LC_String s8) {
|
||||
if (L->on_message) {
|
||||
L->on_message(pos, s8.str, (int)s8.len);
|
||||
} else {
|
||||
if (pos) {
|
||||
LC_String line = LC_GetTokenLine(pos);
|
||||
LC_String fmt = LC_Format(L->arena, "%s(%d,%d): error: %.*s\n%.*s", (char *)pos->lex->file, pos->line, pos->column, LC_Expand(s8), LC_Expand(line));
|
||||
LC_Print(fmt.str, fmt.len);
|
||||
} else {
|
||||
LC_Print(s8.str, s8.len);
|
||||
}
|
||||
}
|
||||
LC_DebugBreak();
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_SendErrorMessagef(LC_Lex *x, LC_Token *pos, const char *str, ...) {
|
||||
LC_FORMAT(L->arena, str, s8);
|
||||
LC_SendErrorMessage(pos, s8);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_HandleFatalError(void) {
|
||||
if (L->on_fatal_error) {
|
||||
L->on_fatal_error();
|
||||
return;
|
||||
}
|
||||
LC_FatalError();
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_MapReserve(LC_Map *map, int size) {
|
||||
LC_Map old_map = *map;
|
||||
|
||||
LC_ASSERT(NULL, LC_IS_POW2(size));
|
||||
map->len = 0;
|
||||
map->cap = size;
|
||||
LC_ASSERT(NULL, map->arena);
|
||||
|
||||
map->entries = LC_PushArray(map->arena, LC_MapEntry, map->cap);
|
||||
|
||||
if (old_map.entries) {
|
||||
for (int i = 0; i < old_map.cap; i += 1) {
|
||||
LC_MapEntry *it = old_map.entries + i;
|
||||
if (it->key) LC_InsertMapEntry(map, it->key, it->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FNV HASH (1a?)
|
||||
LC_FUNCTION uint64_t LC_HashBytes(void *data, uint64_t size) {
|
||||
uint8_t *data8 = (uint8_t *)data;
|
||||
uint64_t hash = (uint64_t)14695981039346656037ULL;
|
||||
for (uint64_t i = 0; i < size; i++) {
|
||||
hash = hash ^ (uint64_t)(data8[i]);
|
||||
hash = hash * (uint64_t)1099511628211ULL;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
LC_FUNCTION uint64_t LC_HashMix(uint64_t x, uint64_t y) {
|
||||
x ^= y;
|
||||
x *= 0xff51afd7ed558ccd;
|
||||
x ^= x >> 32;
|
||||
return x;
|
||||
}
|
||||
|
||||
LC_FUNCTION int LC_NextPow2(int v) {
|
||||
v--;
|
||||
v |= v >> 1;
|
||||
v |= v >> 2;
|
||||
v |= v >> 4;
|
||||
v |= v >> 8;
|
||||
v |= v >> 16;
|
||||
v++;
|
||||
return v;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_MapEntry *LC_GetMapEntryEx(LC_Map *map, uint64_t key) {
|
||||
LC_ASSERT(NULL, key);
|
||||
if (map->len * 2 >= map->cap) {
|
||||
LC_MapReserve(map, map->cap * 2);
|
||||
}
|
||||
|
||||
uint64_t hash = LC_HashBytes(&key, sizeof(key));
|
||||
if (hash == 0) hash += 1;
|
||||
uint64_t index = LC_WRAP_AROUND_POWER_OF_2(hash, map->cap);
|
||||
uint64_t i = index;
|
||||
for (;;) {
|
||||
LC_MapEntry *it = map->entries + i;
|
||||
if (it->key == key || it->key == 0) {
|
||||
return it;
|
||||
}
|
||||
|
||||
i = LC_WRAP_AROUND_POWER_OF_2(i + 1, map->cap);
|
||||
if (i == index) return NULL;
|
||||
}
|
||||
LC_ASSERT(NULL, !"invalid codepath");
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_InsertWithoutReplace(LC_Map *map, void *key, void *value) {
|
||||
LC_MapEntry *entry = LC_GetMapEntryEx(map, (uint64_t)key);
|
||||
if (entry->key != 0) return false;
|
||||
|
||||
map->len += 1;
|
||||
entry->key = (uint64_t)key;
|
||||
entry->value = (uint64_t)value;
|
||||
return true;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_MapEntry *LC_InsertMapEntry(LC_Map *map, uint64_t key, uint64_t value) {
|
||||
LC_MapEntry *entry = LC_GetMapEntryEx(map, key);
|
||||
if (entry->key == key) {
|
||||
entry->value = value;
|
||||
}
|
||||
if (entry->key == 0) {
|
||||
entry->key = key;
|
||||
entry->value = value;
|
||||
map->len += 1;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_MapEntry *LC_GetMapEntry(LC_Map *map, uint64_t key) {
|
||||
LC_MapEntry *entry = LC_GetMapEntryEx(map, key);
|
||||
if (entry && entry->key == key) {
|
||||
return entry;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_MapInsert(LC_Map *map, LC_String keystr, void *value) {
|
||||
uint64_t key = LC_HashBytes(keystr.str, keystr.len);
|
||||
LC_InsertMapEntry(map, key, (uint64_t)value);
|
||||
}
|
||||
|
||||
LC_FUNCTION void *LC_MapGet(LC_Map *map, LC_String keystr) {
|
||||
uint64_t key = LC_HashBytes(keystr.str, keystr.len);
|
||||
LC_MapEntry *r = LC_GetMapEntry(map, key);
|
||||
return r ? (void *)r->value : 0;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_MapInsertU64(LC_Map *map, uint64_t key, void *value) {
|
||||
LC_InsertMapEntry(map, key, (uint64_t)value);
|
||||
}
|
||||
|
||||
LC_FUNCTION void *LC_MapGetU64(LC_Map *map, uint64_t key) {
|
||||
LC_MapEntry *r = LC_GetMapEntry(map, key);
|
||||
return r ? (void *)r->value : 0;
|
||||
}
|
||||
|
||||
LC_FUNCTION void *LC_MapGetP(LC_Map *map, void *key) {
|
||||
return LC_MapGetU64(map, (uint64_t)key);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_MapInsertP(LC_Map *map, void *key, void *value) {
|
||||
LC_InsertMapEntry(map, (uint64_t)key, (uint64_t)value);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_MapClear(LC_Map *map) {
|
||||
if (map->len != 0) LC_MemoryZero(map->entries, map->cap * sizeof(LC_MapEntry));
|
||||
map->len = 0;
|
||||
}
|
||||
|
||||
LC_FUNCTION size_t LC_GetAlignOffset(size_t size, size_t align) {
|
||||
size_t mask = align - 1;
|
||||
size_t val = size & mask;
|
||||
if (val) {
|
||||
val = align - val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
LC_FUNCTION size_t LC_AlignUp(size_t size, size_t align) {
|
||||
size_t result = size + LC_GetAlignOffset(size, align);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION size_t LC_AlignDown(size_t size, size_t align) {
|
||||
size += 1; // Make sure when align is 8 doesn't get rounded down to 0
|
||||
size_t result = size - (align - LC_GetAlignOffset(size, align));
|
||||
return result;
|
||||
}
|
||||
72
src/compiler/extended_passes.c
Normal file
72
src/compiler/extended_passes.c
Normal file
@@ -0,0 +1,72 @@
|
||||
LC_FUNCTION void WalkAndCountDeclRefs(LC_ASTWalker *ctx, LC_AST *n) {
|
||||
LC_Decl *decl = NULL;
|
||||
if (n->kind == LC_ASTKind_ExprIdent || n->kind == LC_ASTKind_TypespecIdent) {
|
||||
if (n->eident.resolved_decl) decl = n->eident.resolved_decl;
|
||||
}
|
||||
if (n->kind == LC_ASTKind_ExprField) {
|
||||
if (n->efield.resolved_decl) decl = n->efield.resolved_decl;
|
||||
}
|
||||
if (decl) {
|
||||
LC_Map *map_of_visits = (LC_Map *)ctx->user_data;
|
||||
intptr_t visited = (intptr_t)LC_MapGetP(map_of_visits, decl);
|
||||
LC_MapInsertP(map_of_visits, decl, (void *)(visited + 1));
|
||||
if (visited == 0 && decl->ast->kind != LC_ASTKind_Null) {
|
||||
LC_WalkAST(ctx, decl->ast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Map LC_CountDeclRefs(LC_Arena *arena) {
|
||||
LC_Map map = {arena};
|
||||
LC_MapReserve(&map, 512);
|
||||
|
||||
LC_AST *package = LC_GetPackageByName(L->first_package);
|
||||
LC_ASTWalker walker = LC_GetDefaultWalker(arena, WalkAndCountDeclRefs);
|
||||
walker.user_data = (void *)↦
|
||||
walker.visit_notes = true;
|
||||
LC_WalkAST(&walker, package);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_RemoveUnreferencedGlobalDecls(LC_Map *map_of_visits) {
|
||||
for (LC_ASTRef *it = L->ordered_packages.first; it; it = it->next) {
|
||||
for (LC_Decl *decl = it->ast->apackage.first_ordered; decl;) {
|
||||
intptr_t ref_count = (intptr_t)LC_MapGetP(map_of_visits, decl);
|
||||
|
||||
LC_Decl *remove = decl;
|
||||
decl = decl->next;
|
||||
if (ref_count == 0 && remove->foreign_name != LC_ILit("main")) {
|
||||
LC_DLLRemove(it->ast->apackage.first_ordered, it->ast->apackage.last_ordered, remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_ErrorOnUnreferencedLocals(LC_Map *map_of_visits) {
|
||||
LC_Decl *first = (LC_Decl *)L->decl_arena->memory.data;
|
||||
for (int i = 0; i < L->decl_count; i += 1) {
|
||||
LC_Decl *decl = first + i;
|
||||
if (decl->package == L->builtin_package) {
|
||||
continue;
|
||||
}
|
||||
|
||||
intptr_t ref_count = (intptr_t)LC_MapGetP(map_of_visits, decl);
|
||||
if (ref_count == 0) {
|
||||
if (LC_IsStmt(decl->ast)) {
|
||||
if (!LC_HasNote(decl->ast, L->iunused)) LC_ReportASTError(decl->ast, "unused local variable '%s'", decl->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_FindUnusedLocalsAndRemoveUnusedGlobalDecls(void) {
|
||||
if (L->errors) return;
|
||||
LC_TempArena check = LC_BeginTemp(L->arena);
|
||||
|
||||
LC_Map map = LC_CountDeclRefs(check.arena);
|
||||
LC_ErrorOnUnreferencedLocals(&map);
|
||||
LC_RemoveUnreferencedGlobalDecls(&map);
|
||||
|
||||
LC_EndTemp(check);
|
||||
}
|
||||
677
src/compiler/genc.c
Normal file
677
src/compiler/genc.c
Normal file
@@ -0,0 +1,677 @@
|
||||
const bool LC_GenCInternalGenerateSizeofs = true;
|
||||
|
||||
LC_FUNCTION void LC_GenCLineDirective(LC_AST *node) {
|
||||
if (L->emit_line_directives) {
|
||||
L->printer.last_line_num = node->pos->line;
|
||||
LC_GenLinef("#line %d", L->printer.last_line_num);
|
||||
LC_Intern file = node->pos->lex->file;
|
||||
if (file != L->printer.last_filename) {
|
||||
L->printer.last_filename = file;
|
||||
LC_Genf(" \"%s\"", (char *)L->printer.last_filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenLastCLineDirective(void) {
|
||||
if (L->emit_line_directives) {
|
||||
LC_Genf("#line %d", L->printer.last_line_num);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCLineDirectiveNum(int num) {
|
||||
if (L->emit_line_directives) {
|
||||
LC_Genf("#line %d", num);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION char *LC_GenCTypeParen(char *str, char c) {
|
||||
return c && c != '[' ? LC_Strf("(%s)", str) : str;
|
||||
}
|
||||
|
||||
LC_FUNCTION char *LC_GenCType(LC_Type *type, char *str) {
|
||||
switch (type->kind) {
|
||||
case LC_TypeKind_Pointer: {
|
||||
return LC_GenCType(type->tptr.base, LC_GenCTypeParen(LC_Strf("*%s", str), *str));
|
||||
} break;
|
||||
case LC_TypeKind_Array: {
|
||||
if (type->tarray.size == 0) {
|
||||
return LC_GenCType(type->tarray.base, LC_GenCTypeParen(LC_Strf("%s[]", str), *str));
|
||||
} else {
|
||||
return LC_GenCType(type->tarray.base, LC_GenCTypeParen(LC_Strf("%s[%d]", str, type->tarray.size), *str));
|
||||
}
|
||||
|
||||
} break;
|
||||
case LC_TypeKind_Proc: {
|
||||
LC_StringList out = {0};
|
||||
LC_Addf(L->arena, &out, "(*%s)", str);
|
||||
LC_Addf(L->arena, &out, "(");
|
||||
if (type->tagg.mems.count == 0) {
|
||||
LC_Addf(L->arena, &out, "void");
|
||||
} else {
|
||||
int i = 0;
|
||||
for (LC_TypeMember *it = type->tproc.args.first; it; it = it->next) {
|
||||
LC_Addf(L->arena, &out, "%s%s", i == 0 ? "" : ", ", LC_GenCType(it->type, ""));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
if (type->tproc.vargs) {
|
||||
LC_Addf(L->arena, &out, ", ...");
|
||||
}
|
||||
LC_Addf(L->arena, &out, ")");
|
||||
char *front = LC_MergeString(L->arena, out).str;
|
||||
char *result = LC_GenCType(type->tproc.ret, front);
|
||||
return result;
|
||||
} break;
|
||||
default: return LC_Strf("%s%s%s", type->decl->foreign_name, str[0] ? " " : "", str);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Intern LC_GetStringFromSingleArgNote(LC_AST *note) {
|
||||
LC_ASSERT(note, note->kind == LC_ASTKind_Note);
|
||||
LC_ASSERT(note, note->ecompo.first == note->ecompo.last);
|
||||
LC_AST *arg = note->ecompo.first;
|
||||
LC_ASSERT(note, arg->kind == LC_ASTKind_ExprCallItem);
|
||||
LC_AST *str = arg->ecompo_item.expr;
|
||||
LC_ASSERT(note, str->kind == LC_ASTKind_ExprString);
|
||||
return str->eatom.name;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCCompound(LC_AST *n) {
|
||||
LC_Type *type = n->type;
|
||||
if (LC_IsAggType(type)) {
|
||||
LC_ResolvedCompo *rd = n->ecompo.resolved_items;
|
||||
LC_Genf("{");
|
||||
if (rd->first == NULL) LC_Genf("0");
|
||||
for (LC_ResolvedCompoItem *it = rd->first; it; it = it->next) {
|
||||
LC_Genf(".%s = ", (char *)it->t->name);
|
||||
LC_GenCExpr(it->expr);
|
||||
if (it->next) LC_Genf(", ");
|
||||
}
|
||||
LC_Genf("}");
|
||||
} else if (LC_IsArray(type)) {
|
||||
LC_ResolvedArrayCompo *rd = n->ecompo.resolved_array_items;
|
||||
LC_Genf("{");
|
||||
for (LC_ResolvedCompoArrayItem *it = rd->first; it; it = it->next) {
|
||||
LC_Genf("[%d] = ", it->index);
|
||||
LC_GenCExpr(it->comp->ecompo_item.expr);
|
||||
if (it->next) LC_Genf(", ");
|
||||
}
|
||||
LC_Genf("}");
|
||||
} else {
|
||||
LC_ReportASTError(n, "internal compiler error: got unhandled case in %s", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
LC_THREAD_LOCAL bool GC_SpecialCase_GlobalScopeStringDecl;
|
||||
|
||||
LC_FUNCTION void LC_GenCString(char *s, LC_Type *type) {
|
||||
if (type == L->tstring) {
|
||||
if (!GC_SpecialCase_GlobalScopeStringDecl) LC_Genf("(LC_String)");
|
||||
LC_Genf("{ ");
|
||||
}
|
||||
LC_Genf("\"");
|
||||
for (int i = 0; s[i]; i += 1) {
|
||||
LC_String escape = LC_GetEscapeString(s[i]);
|
||||
if (escape.len) {
|
||||
LC_Genf("%.*s", LC_Expand(escape));
|
||||
} else {
|
||||
LC_Genf("%c", s[i]);
|
||||
}
|
||||
}
|
||||
LC_Genf("\"");
|
||||
if (type == L->tstring) LC_Genf(", %d }", (int)LC_StrLen(s));
|
||||
}
|
||||
|
||||
LC_FUNCTION char *LC_GenCVal(LC_TypeAndVal v, LC_Type *type) {
|
||||
char *str = LC_GenLCTypeVal(v);
|
||||
switch (type->kind) {
|
||||
case LC_TypeKind_uchar:
|
||||
case LC_TypeKind_ushort:
|
||||
case LC_TypeKind_uint: str = LC_Strf("%su", str); break;
|
||||
case LC_TypeKind_ulong: str = LC_Strf("%sul", str); break;
|
||||
case LC_TypeKind_Pointer:
|
||||
case LC_TypeKind_Proc:
|
||||
case LC_TypeKind_ullong: str = LC_Strf("%sull", str); break;
|
||||
case LC_TypeKind_long: str = LC_Strf("%sull", str); break;
|
||||
case LC_TypeKind_llong: str = LC_Strf("%sull", str); break;
|
||||
case LC_TypeKind_float: str = LC_Strf("%sf", str); break;
|
||||
case LC_TypeKind_UntypedFloat: str = LC_Strf(" /*utfloat*/%s", str); break;
|
||||
case LC_TypeKind_UntypedInt: str = LC_Strf(" /*utint*/%sull", str); break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
if (LC_IsUTInt(v.type) && !LC_IsUntyped(type) && type->size < 4) {
|
||||
str = LC_Strf("(%s)%s", LC_GenCType(type, ""), str);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCExpr(LC_AST *n) {
|
||||
LC_ASSERT(n, LC_IsExpr(n));
|
||||
intptr_t is_any = (intptr_t)LC_MapGetP(&L->implicit_any, n);
|
||||
if (is_any) LC_Genf("(LC_Any){%d, (%s[]){", n->type->id, LC_GenCType(n->type, ""));
|
||||
|
||||
if (n->const_val.type) {
|
||||
bool contains_sizeof_like = LC_GenCInternalGenerateSizeofs ? LC_ContainsCBuiltin(n) : false;
|
||||
if (!contains_sizeof_like) {
|
||||
if (LC_IsUTStr(n->const_val.type)) {
|
||||
LC_GenCString((char *)n->const_val.name, n->type);
|
||||
} else {
|
||||
char *val = LC_GenCVal(n->const_val, n->type);
|
||||
LC_Genf("%s", val);
|
||||
}
|
||||
if (is_any) LC_Genf("}}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LC_Type *type = n->type;
|
||||
switch (n->kind) {
|
||||
case LC_ASTKind_ExprIdent: {
|
||||
LC_Genf("%s", (char *)n->eident.resolved_decl->foreign_name);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprCast: {
|
||||
LC_Genf("(");
|
||||
LC_Genf("(%s)", LC_GenCType(type, ""));
|
||||
LC_Genf("(");
|
||||
LC_GenCExpr(n->ecast.expr);
|
||||
LC_Genf(")");
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprUnary: {
|
||||
LC_Genf("%s(", LC_TokenKindToOperator(n->eunary.op));
|
||||
LC_GenCExpr(n->eunary.expr);
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprAddPtr: {
|
||||
LC_Genf("(");
|
||||
LC_GenCExpr(n->ebinary.left);
|
||||
LC_Genf("+");
|
||||
LC_GenCExpr(n->ebinary.right);
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprBinary: {
|
||||
LC_Genf("(");
|
||||
LC_GenCExpr(n->ebinary.left);
|
||||
LC_Genf("%s", LC_TokenKindToOperator(n->ebinary.op));
|
||||
LC_GenCExpr(n->ebinary.right);
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprIndex: {
|
||||
LC_Genf("(");
|
||||
LC_GenCExpr(n->eindex.base);
|
||||
LC_Genf("[");
|
||||
LC_GenCExpr(n->eindex.index);
|
||||
LC_Genf("]");
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprGetValueOfPointer: {
|
||||
LC_Genf("(*(");
|
||||
LC_GenCExpr(n->eunary.expr);
|
||||
LC_Genf("))");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprGetPointerOfValue: {
|
||||
LC_Genf("(&(");
|
||||
LC_GenCExpr(n->eunary.expr);
|
||||
LC_Genf("))");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprField: {
|
||||
if (n->efield.parent_decl->kind != LC_DeclKind_Import) {
|
||||
LC_Type *left_type = n->efield.left->type;
|
||||
LC_GenCExpr(n->efield.left);
|
||||
if (LC_IsPtr(left_type)) LC_Genf("->");
|
||||
else LC_Genf(".");
|
||||
LC_Genf("%s", (char *)n->efield.right);
|
||||
} else {
|
||||
LC_Genf("%s", (char *)n->efield.resolved_decl->foreign_name);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprCall: {
|
||||
LC_ResolvedCompo *rd = n->ecompo.resolved_items;
|
||||
LC_GenCExpr(n->ecompo.name);
|
||||
LC_Genf("(");
|
||||
for (LC_ResolvedCompoItem *it = rd->first; it; it = it->next) {
|
||||
LC_GenCExpr(it->expr);
|
||||
if (it->next) LC_Genf(", ");
|
||||
}
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprCompound: {
|
||||
LC_Genf("(%s)", LC_GenCType(type, ""));
|
||||
LC_GenCCompound(n);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprBuiltin: {
|
||||
LC_ASSERT(n, n->ecompo.name->kind == LC_ASTKind_ExprIdent);
|
||||
if (n->ecompo.name->eident.name == L->isizeof) {
|
||||
LC_Genf("sizeof(");
|
||||
LC_AST *expr = n->ecompo.first->ecompo_item.expr;
|
||||
if (expr->kind == LC_ASTKind_ExprType) {
|
||||
LC_Genf("%s", LC_GenCType(expr->type, ""));
|
||||
} else {
|
||||
LC_GenCExpr(expr);
|
||||
}
|
||||
LC_Genf(")");
|
||||
} else if (n->ecompo.name->eident.name == L->ialignof) {
|
||||
LC_Genf("LC_Alignof(");
|
||||
LC_AST *expr = n->ecompo.first->ecompo_item.expr;
|
||||
if (expr->kind == LC_ASTKind_ExprType) {
|
||||
LC_Genf("%s", LC_GenCType(expr->type, ""));
|
||||
} else {
|
||||
LC_GenCExpr(expr);
|
||||
}
|
||||
LC_Genf(")");
|
||||
} else if (n->ecompo.name->eident.name == L->ioffsetof) {
|
||||
LC_AST *i1 = n->ecompo.first->ecompo_item.expr;
|
||||
LC_AST *i2 = n->ecompo.first->next->ecompo_item.expr;
|
||||
LC_Genf("offsetof(%s, %s)", LC_GenCType(i1->type, ""), (char *)i2->eident.name);
|
||||
} else {
|
||||
LC_ReportASTError(n, "internal compiler error: got unhandled case in %s / LC_ASTKind_ExprBuiltin", __FUNCTION__);
|
||||
}
|
||||
} break;
|
||||
|
||||
default: LC_ReportASTError(n, "internal compiler error: got unhandled case in %s", __FUNCTION__);
|
||||
}
|
||||
|
||||
if (is_any) LC_Genf("}}");
|
||||
}
|
||||
|
||||
const int GC_Stmt_OmitSemicolonAndNewLine = 1;
|
||||
|
||||
LC_FUNCTION void LC_GenCNote(LC_AST *note) {
|
||||
if (note->ecompo.name->eident.name == L->ic) {
|
||||
LC_Genf("%s", (char *)LC_GetStringFromSingleArgNote(note));
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCVarExpr(LC_AST *n, bool is_declaration) {
|
||||
if (LC_HasNote(n, L->inot_init)) return;
|
||||
|
||||
LC_AST *e = n->dvar.expr;
|
||||
if (n->kind == LC_ASTKind_StmtVar) e = n->svar.expr;
|
||||
if (e) {
|
||||
LC_Genf(" = ");
|
||||
if (e->kind == LC_ASTKind_ExprNote) {
|
||||
LC_GenCNote(e->enote.expr);
|
||||
} else if (is_declaration && e->kind == LC_ASTKind_ExprCompound) {
|
||||
LC_GenCCompound(e);
|
||||
} else {
|
||||
LC_GenCExpr(e);
|
||||
}
|
||||
} else {
|
||||
LC_Genf(" = {0}");
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCDefers(LC_AST *block) {
|
||||
LC_AST *first = block->sblock.first_defer;
|
||||
if (first == NULL) return;
|
||||
|
||||
int save = L->printer.last_line_num;
|
||||
LC_GenLine();
|
||||
LC_GenLastCLineDirective();
|
||||
|
||||
LC_GenLinef("/*defer*/");
|
||||
for (LC_AST *it = first; it; it = it->sdefer.next) {
|
||||
LC_GenCStmtBlock(it->sdefer.body);
|
||||
}
|
||||
|
||||
L->printer.last_line_num = save + 1;
|
||||
LC_GenLine();
|
||||
LC_GenLastCLineDirective();
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCDefersLoopBreak(LC_AST *n) {
|
||||
LC_ASSERT(n, n->kind == LC_ASTKind_StmtBreak || n->kind == LC_ASTKind_StmtContinue);
|
||||
LC_AST *it = NULL;
|
||||
for (int i = L->printer.out_block_stack.len - 1; i >= 0; i -= 1) {
|
||||
it = L->printer.out_block_stack.data[i];
|
||||
LC_GenCDefers(it);
|
||||
LC_ASSERT(it, it->sblock.kind != SBLK_Proc);
|
||||
if (it->sblock.kind == SBLK_Loop) {
|
||||
if (!n->sbreak.name) break;
|
||||
if (n->sbreak.name && it->sblock.name == n->sbreak.name) break;
|
||||
}
|
||||
}
|
||||
LC_ASSERT(it, it->sblock.kind == SBLK_Loop);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCDefersReturn(LC_AST *n) {
|
||||
LC_ASSERT(n, n->kind == LC_ASTKind_StmtReturn);
|
||||
LC_AST *it = NULL;
|
||||
for (int i = L->printer.out_block_stack.len - 1; i >= 0; i -= 1) {
|
||||
it = L->printer.out_block_stack.data[i];
|
||||
LC_GenCDefers(it);
|
||||
if (it->sblock.kind == SBLK_Proc) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
LC_ASSERT(it, it);
|
||||
LC_ASSERT(it, it->sblock.kind == SBLK_Proc);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCStmt2(LC_AST *n, int flags) {
|
||||
LC_ASSERT(n, LC_IsStmt(n));
|
||||
bool semicolon = !(flags & GC_Stmt_OmitSemicolonAndNewLine);
|
||||
|
||||
if (semicolon) {
|
||||
LC_GenLine();
|
||||
}
|
||||
|
||||
switch (n->kind) {
|
||||
case LC_ASTKind_StmtVar: {
|
||||
LC_Type *type = n->type;
|
||||
LC_Genf("%s", LC_GenCType(type, (char *)n->svar.name));
|
||||
LC_GenCVarExpr(n, true);
|
||||
} break;
|
||||
case LC_ASTKind_StmtExpr: LC_GenCExpr(n->sexpr.expr); break;
|
||||
|
||||
case LC_ASTKind_StmtAssign: {
|
||||
// Assigning to array doesn't work in C so we need to handle that
|
||||
// specific compo case here. :CompoArray
|
||||
if (LC_IsArray(n->type) && n->sassign.right->kind == LC_ASTKind_ExprCompound) {
|
||||
LC_ASSERT(n, n->sassign.op == LC_TokenKind_Assign);
|
||||
LC_AST *expr = n->sassign.right;
|
||||
LC_Genf("memset(");
|
||||
LC_GenCExpr(n->sassign.left);
|
||||
LC_Genf(", 0, sizeof(");
|
||||
LC_GenCExpr(n->sassign.left);
|
||||
LC_Genf("));");
|
||||
|
||||
LC_ResolvedArrayCompo *rd = expr->ecompo.resolved_array_items;
|
||||
for (LC_ResolvedCompoArrayItem *it = rd->first; it; it = it->next) {
|
||||
LC_GenCExpr(n->sassign.left);
|
||||
LC_Genf("[%d] = ", it->index);
|
||||
LC_GenCExpr(it->comp->ecompo_item.expr);
|
||||
LC_Genf(";");
|
||||
}
|
||||
|
||||
} else {
|
||||
LC_GenCExpr(n->sassign.left);
|
||||
LC_Genf(" %s ", LC_TokenKindToOperator(n->sassign.op));
|
||||
LC_GenCExpr(n->sassign.right);
|
||||
}
|
||||
} break;
|
||||
default: LC_ReportASTError(n, "internal compiler error: got unhandled case in %s", __FUNCTION__);
|
||||
}
|
||||
|
||||
if (semicolon) LC_Genf(";");
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCStmt(LC_AST *n) {
|
||||
LC_ASSERT(n, LC_IsStmt(n));
|
||||
LC_GenCLineDirective(n);
|
||||
switch (n->kind) {
|
||||
case LC_ASTKind_StmtConst:
|
||||
case LC_ASTKind_StmtDefer: break;
|
||||
case LC_ASTKind_StmtNote: {
|
||||
LC_GenLine();
|
||||
LC_GenCNote(n->snote.expr);
|
||||
LC_Genf(";");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtReturn: {
|
||||
LC_GenCDefersReturn(n);
|
||||
LC_GenLinef("return");
|
||||
if (n->sreturn.expr) {
|
||||
LC_Genf(" ");
|
||||
LC_GenCExpr(n->sreturn.expr);
|
||||
}
|
||||
LC_Genf(";");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtContinue:
|
||||
case LC_ASTKind_StmtBreak: {
|
||||
const char *stmt = n->kind == LC_ASTKind_StmtBreak ? "break" : "continue";
|
||||
LC_GenCDefersLoopBreak(n);
|
||||
if (n->sbreak.name) {
|
||||
LC_GenLinef("goto %s_%s;", (char *)n->sbreak.name, stmt);
|
||||
} else {
|
||||
LC_GenLinef("%s;", stmt);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtBlock: {
|
||||
LC_GenLinef("/*block*/");
|
||||
LC_GenCStmtBlock(n);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtSwitch: {
|
||||
LC_GenLinef("switch(");
|
||||
LC_GenCExpr(n->sswitch.expr);
|
||||
LC_Genf(") {");
|
||||
|
||||
L->printer.indent += 1;
|
||||
LC_ASTFor(it, n->sswitch.first) {
|
||||
LC_GenCLineDirective(it);
|
||||
if (it->kind == LC_ASTKind_StmtSwitchCase) {
|
||||
LC_ASTFor(label_it, it->scase.first) {
|
||||
LC_GenLinef("case ");
|
||||
LC_GenCExpr(label_it);
|
||||
LC_Genf(":");
|
||||
}
|
||||
}
|
||||
if (it->kind == LC_ASTKind_StmtSwitchDefault) {
|
||||
LC_GenLinef("default:");
|
||||
}
|
||||
LC_GenCStmtBlock(it->scase.body);
|
||||
if (LC_HasNote(it, L->ifallthrough)) {
|
||||
LC_Genf(" /*@fallthough*/");
|
||||
} else {
|
||||
LC_Genf(" break;");
|
||||
}
|
||||
}
|
||||
L->printer.indent -= 1;
|
||||
LC_GenLinef("}");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtFor: {
|
||||
LC_GenLinef("for (");
|
||||
if (n->sfor.init) LC_GenCStmt2(n->sfor.init, GC_Stmt_OmitSemicolonAndNewLine);
|
||||
LC_Genf(";");
|
||||
if (n->sfor.cond) {
|
||||
LC_Genf(" ");
|
||||
LC_GenCExpr(n->sfor.cond);
|
||||
}
|
||||
LC_Genf(";");
|
||||
if (n->sfor.inc) {
|
||||
LC_Genf(" ");
|
||||
LC_GenCStmt2(n->sfor.inc, GC_Stmt_OmitSemicolonAndNewLine);
|
||||
}
|
||||
LC_Genf(")");
|
||||
LC_GenCStmtBlock(n->sfor.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtIf: {
|
||||
LC_GenLinef("if ");
|
||||
LC_GenCExprParen(n->sif.expr);
|
||||
LC_GenCStmtBlock(n->sif.body);
|
||||
LC_ASTFor(it, n->sif.first) {
|
||||
LC_GenCLineDirective(it);
|
||||
LC_GenLinef("else");
|
||||
if (it->kind == LC_ASTKind_StmtElseIf) {
|
||||
LC_Genf(" if ");
|
||||
LC_GenCExprParen(it->sif.expr);
|
||||
}
|
||||
LC_GenCStmtBlock(it->sif.body);
|
||||
}
|
||||
} break;
|
||||
|
||||
default: LC_GenCStmt2(n, 0);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCExprParen(LC_AST *expr) {
|
||||
bool paren = expr->kind != LC_ASTKind_ExprBinary;
|
||||
if (paren) LC_Genf("(");
|
||||
LC_GenCExpr(expr);
|
||||
if (paren) LC_Genf(")");
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCStmtBlock(LC_AST *n) {
|
||||
LC_PushAST(&L->printer.out_block_stack, n);
|
||||
LC_ASSERT(n, n->kind == LC_ASTKind_StmtBlock);
|
||||
LC_Genf(" {");
|
||||
L->printer.indent += 1;
|
||||
LC_ASTFor(it, n->sblock.first) {
|
||||
LC_GenCStmt(it);
|
||||
}
|
||||
LC_GenCDefers(n);
|
||||
if (n->sblock.name) LC_GenLinef("%s_continue:;", (char *)n->sblock.name);
|
||||
L->printer.indent -= 1;
|
||||
LC_GenLinef("}");
|
||||
if (n->sblock.name) LC_GenLinef("%s_break:;", (char *)n->sblock.name);
|
||||
LC_PopAST(&L->printer.out_block_stack);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCProcDecl(LC_Decl *decl) {
|
||||
LC_StringList out = {0};
|
||||
LC_Type *type = decl->type;
|
||||
LC_AST *n = decl->ast;
|
||||
LC_AST *typespec = n->dproc.type;
|
||||
|
||||
LC_Addf(L->arena, &out, "%s(", (char *)decl->foreign_name);
|
||||
if (type->tagg.mems.count == 0) {
|
||||
LC_Addf(L->arena, &out, "void");
|
||||
} else {
|
||||
int i = 0;
|
||||
LC_ASTFor(it, typespec->tproc.first) {
|
||||
LC_Type *type = it->type;
|
||||
LC_Addf(L->arena, &out, "%s%s", i == 0 ? "" : ", ", LC_GenCType(type, (char *)it->tproc_arg.name));
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
if (type->tproc.vargs) {
|
||||
LC_Addf(L->arena, &out, ", ...");
|
||||
}
|
||||
LC_Addf(L->arena, &out, ")");
|
||||
char *front = LC_MergeString(L->arena, out).str;
|
||||
char *result = LC_GenCType(type->tproc.ret, front);
|
||||
|
||||
LC_GenLine();
|
||||
bool is_public = LC_HasNote(n, L->iapi) || decl->foreign_name == L->imain;
|
||||
if (!is_public) LC_Genf("static ");
|
||||
LC_Genf("%s", result);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCAggForwardDecl(LC_Decl *decl) {
|
||||
LC_ASSERT(decl->ast, LC_IsAgg(decl->ast));
|
||||
char *agg = LC_GenLCAggName(decl->type);
|
||||
LC_GenLinef("typedef %s %s %s;", agg, (char *)decl->foreign_name, (char *)decl->foreign_name);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCTypeDecl(LC_Decl *decl) {
|
||||
LC_AST *n = decl->ast;
|
||||
LC_ASSERT(n, decl->kind == LC_DeclKind_Type);
|
||||
if (n->kind == LC_ASTKind_DeclTypedef) {
|
||||
LC_Type *type = decl->typedef_renamed_type_decl ? decl->typedef_renamed_type_decl->type : decl->type;
|
||||
LC_GenLinef("typedef %s;", LC_GenCType(type, (char *)decl->foreign_name));
|
||||
} else {
|
||||
LC_Type *type = decl->type;
|
||||
LC_Intern name = decl->foreign_name;
|
||||
{
|
||||
bool packed = LC_HasNote(n, L->ipacked) ? true : false;
|
||||
if (packed) LC_GenLinef("#pragma pack(push, 1)");
|
||||
|
||||
LC_GenLinef("%s %s {", LC_GenLCAggName(type), name ? (char *)name : "");
|
||||
L->printer.indent += 1;
|
||||
for (LC_TypeMember *it = type->tagg.mems.first; it; it = it->next) {
|
||||
LC_GenLinef("%s;", LC_GenCType(it->type, (char *)it->name));
|
||||
}
|
||||
L->printer.indent -= 1;
|
||||
LC_GenLinef("};");
|
||||
if (packed) LC_GenLinef("#pragma pack(pop)");
|
||||
LC_GenLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCVarFDecl(LC_Decl *decl) {
|
||||
if (!LC_HasNote(decl->ast, L->iapi)) return;
|
||||
LC_Type *type = decl->type; // make string arrays assignable
|
||||
LC_GenLinef("extern ");
|
||||
if (LC_HasNote(decl->ast, L->ithread_local)) LC_Genf("_Thread_local ");
|
||||
LC_Genf("%s;", LC_GenCType(type, (char *)decl->foreign_name));
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCHeader(LC_AST *package) {
|
||||
// C notes
|
||||
LC_ASTFor(file, package->apackage.ffile) {
|
||||
LC_ASTFor(it, file->afile.fdecl) {
|
||||
if (it->kind != LC_ASTKind_DeclNote) continue;
|
||||
|
||||
LC_AST *note = it->dnote.expr;
|
||||
if (note->ecompo.name->eident.name == L->ic) {
|
||||
LC_GenLinef("%s", (char *)LC_GetStringFromSingleArgNote(note));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// struct forward decls
|
||||
LC_DeclFor(decl, package->apackage.first_ordered) {
|
||||
if (decl->is_foreign) continue;
|
||||
LC_AST *n = decl->ast;
|
||||
if (decl->kind == LC_DeclKind_Type && LC_IsAgg(n)) LC_GenCAggForwardDecl(decl);
|
||||
}
|
||||
|
||||
// type decls
|
||||
LC_GenLine();
|
||||
LC_DeclFor(decl, package->apackage.first_ordered) {
|
||||
if (decl->is_foreign) continue;
|
||||
LC_AST *n = decl->ast;
|
||||
if (decl->kind == LC_DeclKind_Type) LC_GenCTypeDecl(decl);
|
||||
}
|
||||
|
||||
// proc and var forward decls
|
||||
LC_DeclFor(decl, package->apackage.first_ordered) {
|
||||
if (decl->is_foreign) continue;
|
||||
LC_AST *n = decl->ast;
|
||||
if (decl->kind == LC_DeclKind_Var) {
|
||||
LC_GenCVarFDecl(decl);
|
||||
} else if (decl->kind == LC_DeclKind_Proc) {
|
||||
LC_GenCProcDecl(decl);
|
||||
LC_Genf(";");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenCImpl(LC_AST *package) {
|
||||
// implementation of vars
|
||||
LC_DeclFor(decl, package->apackage.first_ordered) {
|
||||
if (decl->kind == LC_DeclKind_Var && !decl->is_foreign) {
|
||||
LC_AST *n = decl->ast;
|
||||
LC_Type *type = decl->type; // make string arrays assignable
|
||||
LC_GenLine();
|
||||
if (!LC_HasNote(n, L->iapi)) LC_Genf("static ");
|
||||
if (LC_HasNote(n, L->ithread_local)) LC_Genf("_Thread_local ");
|
||||
LC_Genf("%s", LC_GenCType(type, (char *)decl->foreign_name));
|
||||
|
||||
GC_SpecialCase_GlobalScopeStringDecl = true;
|
||||
LC_GenCVarExpr(n, true);
|
||||
GC_SpecialCase_GlobalScopeStringDecl = false;
|
||||
LC_Genf(";");
|
||||
LC_GenLine();
|
||||
}
|
||||
}
|
||||
|
||||
// implementation of procs
|
||||
LC_DeclFor(decl, package->apackage.first_ordered) {
|
||||
LC_AST *n = decl->ast;
|
||||
if (decl->kind == LC_DeclKind_Proc && n->dproc.body && !decl->is_foreign) {
|
||||
LC_GenCLineDirective(n);
|
||||
LC_GenCProcDecl(decl);
|
||||
LC_GenCStmtBlock(n->dproc.body);
|
||||
LC_GenLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
259
src/compiler/init.c
Normal file
259
src/compiler/init.c
Normal file
@@ -0,0 +1,259 @@
|
||||
LC_FUNCTION LC_Lang *LC_LangAlloc(void) {
|
||||
LC_Arena *arena = LC_BootstrapArena();
|
||||
LC_Arena *lex_arena = LC_PushStruct(arena, LC_Arena);
|
||||
LC_Arena *decl_arena = LC_PushStruct(arena, LC_Arena);
|
||||
LC_Arena *ast_arena = LC_PushStruct(arena, LC_Arena);
|
||||
LC_Arena *type_arena = LC_PushStruct(arena, LC_Arena);
|
||||
LC_InitArena(lex_arena);
|
||||
LC_InitArena(decl_arena);
|
||||
LC_InitArena(ast_arena);
|
||||
LC_InitArena(type_arena);
|
||||
|
||||
LC_Lang *l = LC_PushStruct(arena, LC_Lang);
|
||||
l->arena = arena;
|
||||
l->lex_arena = lex_arena;
|
||||
l->decl_arena = decl_arena;
|
||||
l->ast_arena = ast_arena;
|
||||
l->type_arena = type_arena;
|
||||
|
||||
l->emit_line_directives = true;
|
||||
l->breakpoint_on_error = true;
|
||||
l->use_colored_terminal_output = true;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_LangEnd(LC_Lang *lang) {
|
||||
LC_DeallocateArena(lang->lex_arena);
|
||||
LC_DeallocateArena(lang->type_arena);
|
||||
LC_DeallocateArena(lang->decl_arena);
|
||||
LC_DeallocateArena(lang->ast_arena);
|
||||
LC_DeallocateArena(lang->arena);
|
||||
if (L == lang) L = NULL;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_LangBegin(LC_Lang *l) {
|
||||
L = l;
|
||||
|
||||
// Init default target settings
|
||||
{
|
||||
if (L->os == LC_OS_Invalid) {
|
||||
L->os = LC_OS_LINUX;
|
||||
#if LC_OPERATING_SYSTEM_WINDOWS
|
||||
L->os = LC_OS_WINDOWS;
|
||||
#elif LC_OPERATING_SYSTEM_MAC
|
||||
L->os = LC_OS_MAC;
|
||||
#endif
|
||||
}
|
||||
if (L->arch == LC_ARCH_Invalid) {
|
||||
L->arch = LC_ARCH_X64;
|
||||
}
|
||||
if (L->gen == LC_GEN_Invalid) {
|
||||
L->gen = LC_GEN_C;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Init declared notes, interns and foreign names checker
|
||||
//
|
||||
{
|
||||
L->declared_notes.arena = L->arena;
|
||||
L->interns.arena = L->arena;
|
||||
L->foreign_names.arena = L->arena;
|
||||
L->implicit_any.arena = L->arena;
|
||||
|
||||
LC_MapReserve(&L->declared_notes, 128);
|
||||
LC_MapReserve(&L->interns, 4096);
|
||||
LC_MapReserve(&L->foreign_names, 256);
|
||||
LC_MapReserve(&L->implicit_any, 64);
|
||||
|
||||
#define X(x) l->k##x = LC_InternStrLen(#x, sizeof(#x) - 1);
|
||||
LC_LIST_KEYWORDS
|
||||
#undef X
|
||||
l->first_keyword = l->kfor;
|
||||
l->last_keyword = l->kfalse;
|
||||
|
||||
#define X(x, declare) l->i##x = LC_InternStrLen(#x, sizeof(#x) - 1);
|
||||
LC_LIST_INTERNS
|
||||
#undef X
|
||||
#define X(x, declare) \
|
||||
if (declare) LC_DeclareNote(L->i##x);
|
||||
LC_LIST_INTERNS
|
||||
#undef X
|
||||
}
|
||||
|
||||
// Nulls
|
||||
{
|
||||
L->NullLEX.begin = "builtin declarations";
|
||||
L->NullLEX.file = LC_ILit("builtin declarations");
|
||||
L->BuiltinToken.lex = &L->NullLEX;
|
||||
L->BuiltinToken.str = "builtin declarations";
|
||||
L->BuiltinToken.len = sizeof("builtin declarations") - 1;
|
||||
L->NullAST.pos = &L->BuiltinToken;
|
||||
}
|
||||
|
||||
{
|
||||
LC_AST *builtins = LC_CreateAST(0, LC_ASTKind_Package);
|
||||
L->builtin_package = builtins;
|
||||
builtins->apackage.name = LC_ILit("builtins");
|
||||
builtins->apackage.scope = LC_CreateScope(256);
|
||||
LC_AddPackageToList(builtins);
|
||||
}
|
||||
|
||||
LC_InitDeclStack(&L->resolver.locals, 128);
|
||||
L->resolver.duplicate_map.arena = L->arena;
|
||||
LC_MapReserve(&L->resolver.duplicate_map, 32);
|
||||
|
||||
L->resolver.stmt_block_stack.arena = L->arena;
|
||||
L->printer.out_block_stack.arena = L->arena;
|
||||
|
||||
LC_PUSH_PACKAGE(L->builtin_package);
|
||||
|
||||
//
|
||||
// Init default type sizes using current platform
|
||||
//
|
||||
// Here we use the sizes of our current platform but
|
||||
// later on it gets swapped based on LC override global variables in
|
||||
// InitTarget
|
||||
//
|
||||
{
|
||||
l->type_map.arena = L->arena;
|
||||
LC_MapReserve(&l->type_map, 256);
|
||||
|
||||
typedef long long llong;
|
||||
typedef unsigned long long ullong;
|
||||
typedef unsigned long ulong;
|
||||
typedef unsigned short ushort;
|
||||
typedef unsigned char uchar;
|
||||
typedef unsigned int uint;
|
||||
|
||||
L->pointer_align = LC_Alignof(void *);
|
||||
L->pointer_size = sizeof(void *);
|
||||
|
||||
int i = 0;
|
||||
#define X(TNAME, IS_UNSIGNED) \
|
||||
l->types[i].kind = LC_TypeKind_##TNAME; \
|
||||
l->types[i].size = sizeof(TNAME); \
|
||||
l->types[i].align = LC_Alignof(TNAME); \
|
||||
l->types[i].is_unsigned = IS_UNSIGNED; \
|
||||
l->t##TNAME = l->types + i++;
|
||||
|
||||
LC_LIST_TYPES
|
||||
#undef X
|
||||
|
||||
//
|
||||
// Overwrite types with target
|
||||
//
|
||||
if (L->arch == LC_ARCH_X64) {
|
||||
LC_SetPointerSizeAndAlign(8, 8);
|
||||
if (L->os == LC_OS_WINDOWS) {
|
||||
L->tlong->size = 4;
|
||||
L->tlong->align = 4;
|
||||
L->tulong->size = 4;
|
||||
L->tulong->align = 4;
|
||||
} else {
|
||||
L->tlong->size = 8;
|
||||
L->tlong->align = 8;
|
||||
L->tulong->size = 8;
|
||||
L->tulong->align = 8;
|
||||
}
|
||||
} else if (L->arch == LC_ARCH_X86) {
|
||||
LC_SetPointerSizeAndAlign(4, 4);
|
||||
L->tlong->size = 4;
|
||||
L->tlong->align = 4;
|
||||
L->tulong->size = 4;
|
||||
L->tulong->align = 4;
|
||||
}
|
||||
|
||||
l->types[i].kind = LC_TypeKind_void;
|
||||
l->tvoid = l->types + i++;
|
||||
|
||||
// Init decls for types
|
||||
for (int i = 0; i < T_Count; i += 1) {
|
||||
char *it = (char *)LC_TypeKindToString((LC_TypeKind)i) + 12;
|
||||
LC_Intern intern = LC_ILit(it);
|
||||
LC_Type *t = l->types + i;
|
||||
t->id = ++l->typeids;
|
||||
|
||||
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Type, intern, &L->NullAST);
|
||||
decl->state = LC_DeclState_Resolved;
|
||||
decl->type = t;
|
||||
t->decl = decl;
|
||||
LC_AddDeclToScope(L->builtin_package->apackage.scope, decl);
|
||||
|
||||
if (t->kind == LC_TypeKind_uchar) decl->foreign_name = LC_ILit("unsigned char");
|
||||
if (t->kind == LC_TypeKind_ushort) decl->foreign_name = LC_ILit("unsigned short");
|
||||
if (t->kind == LC_TypeKind_uint) decl->foreign_name = LC_ILit("unsigned");
|
||||
if (t->kind == LC_TypeKind_ulong) decl->foreign_name = LC_ILit("unsigned long");
|
||||
if (t->kind == LC_TypeKind_llong) decl->foreign_name = LC_ILit("long long");
|
||||
if (t->kind == LC_TypeKind_ullong) decl->foreign_name = LC_ILit("unsigned long long");
|
||||
}
|
||||
}
|
||||
|
||||
l->tpvoid = LC_CreatePointerType(l->tvoid);
|
||||
l->tpchar = LC_CreatePointerType(l->tchar);
|
||||
|
||||
{
|
||||
l->tuntypedint = LC_CreateUntypedInt(L->tint);
|
||||
l->tuntypedbool = LC_CreateUntypedInt(L->tbool);
|
||||
l->tuntypednil = LC_CreateUntypedInt(L->tullong);
|
||||
|
||||
l->ttuntypedfloat.kind = LC_TypeKind_UntypedFloat;
|
||||
l->ttuntypedfloat.id = ++L->typeids;
|
||||
l->tuntypedfloat = &L->ttuntypedfloat;
|
||||
l->tuntypedfloat->tutdefault = l->tdouble;
|
||||
l->tuntypedfloat->decl = LC_CreateDecl(LC_DeclKind_Type, LC_ILit("UntypedFloat"), &L->NullAST);
|
||||
|
||||
l->ttuntypedstring.kind = LC_TypeKind_UntypedString;
|
||||
l->ttuntypedstring.id = ++L->typeids;
|
||||
l->tuntypedstring = &L->ttuntypedstring;
|
||||
l->tuntypedstring->tutdefault = l->tpchar;
|
||||
l->tuntypedstring->decl = LC_CreateDecl(LC_DeclKind_Type, LC_ILit("UntypedString"), &L->NullAST);
|
||||
}
|
||||
|
||||
// Add builtin "String" type
|
||||
{
|
||||
L->ttstring.kind = LC_TypeKind_Incomplete;
|
||||
L->ttstring.id = ++L->typeids;
|
||||
L->tstring = &L->ttstring;
|
||||
|
||||
LC_AST *ast = LC_ParseDeclf("String :: struct { str: *char; len: int; }");
|
||||
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Type, ast->dbase.name, ast);
|
||||
decl->foreign_name = LC_ILit("LC_String");
|
||||
decl->state = LC_DeclState_Resolved;
|
||||
decl->type = L->tstring;
|
||||
L->tstring->decl = decl;
|
||||
LC_AddDeclToScope(L->builtin_package->apackage.scope, decl);
|
||||
LC_Operand result = LC_ResolveTypeAggregate(ast, decl->type);
|
||||
LC_ASSERT(ast, !LC_IsError(result));
|
||||
}
|
||||
|
||||
// Add builtin "Any" type
|
||||
{
|
||||
L->ttany.kind = LC_TypeKind_Incomplete;
|
||||
L->ttany.id = ++L->typeids;
|
||||
L->tany = &L->ttany;
|
||||
|
||||
LC_AST *ast = LC_ParseDeclf("Any :: struct { type: int; data: *void; }");
|
||||
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Type, ast->dbase.name, ast);
|
||||
decl->foreign_name = LC_ILit("LC_Any");
|
||||
decl->state = LC_DeclState_Resolved;
|
||||
decl->type = L->tany;
|
||||
L->tany->decl = decl;
|
||||
LC_AddDeclToScope(L->builtin_package->apackage.scope, decl);
|
||||
LC_Operand result = LC_ResolveTypeAggregate(ast, decl->type);
|
||||
LC_ASSERT(ast, !LC_IsError(result));
|
||||
}
|
||||
|
||||
LC_Decl *decl_nil = LC_AddConstIntDecl("nil", 0);
|
||||
decl_nil->type = L->tuntypednil;
|
||||
|
||||
for (int i = LC_ARCH_X64; i < LC_ARCH_Count; i += 1) LC_AddBuiltinConstInt((char *)LC_ARCHToString((LC_ARCH)i), i);
|
||||
for (int i = LC_OS_WINDOWS; i < LC_OS_Count; i += 1) LC_AddBuiltinConstInt((char *)LC_OSToString((LC_OS)i), i);
|
||||
for (int i = LC_GEN_C; i < LC_GEN_Count; i += 1) LC_AddBuiltinConstInt((char *)LC_GENToString((LC_GEN)i), i);
|
||||
LC_AddBuiltinConstInt("LC_ARCH", L->arch);
|
||||
LC_AddBuiltinConstInt("LC_GEN", L->gen);
|
||||
LC_AddBuiltinConstInt("LC_OS", L->os);
|
||||
|
||||
LC_POP_PACKAGE();
|
||||
}
|
||||
44
src/compiler/intern.c
Normal file
44
src/compiler/intern.c
Normal file
@@ -0,0 +1,44 @@
|
||||
typedef struct {
|
||||
int len;
|
||||
char str[];
|
||||
} INTERN_Entry;
|
||||
|
||||
LC_FUNCTION LC_Intern LC_InternStrLen(char *str, int len) {
|
||||
LC_String key = LC_MakeString(str, len);
|
||||
INTERN_Entry *entry = (INTERN_Entry *)LC_MapGet(&L->interns, key);
|
||||
if (entry == NULL) {
|
||||
LC_ASSERT(NULL, sizeof(INTERN_Entry) == sizeof(int));
|
||||
entry = (INTERN_Entry *)LC_PushSize(L->arena, sizeof(int) + sizeof(char) * (len + 1));
|
||||
entry->len = len;
|
||||
LC_MemoryCopy(entry->str, str, len);
|
||||
entry->str[len] = 0;
|
||||
LC_MapInsert(&L->interns, key, entry);
|
||||
}
|
||||
|
||||
return (uintptr_t)entry->str;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Intern LC_ILit(char *str) {
|
||||
return LC_InternStrLen(str, (int)LC_StrLen(str));
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Intern LC_GetUniqueIntern(const char *name_for_debug) {
|
||||
LC_String name = LC_Format(L->arena, "U%u_%s", ++L->unique_counter, name_for_debug);
|
||||
LC_Intern result = LC_InternStrLen(name.str, (int)name.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION char *LC_GetUniqueName(const char *name_for_debug) {
|
||||
LC_String name = LC_Format(L->arena, "U%u_%s", ++L->unique_counter, name_for_debug);
|
||||
return name.str;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_DeclareNote(LC_Intern intern) {
|
||||
LC_MapInsertU64(&L->declared_notes, intern, (void *)intern);
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_IsNoteDeclared(LC_Intern intern) {
|
||||
void *p = LC_MapGetU64(&L->declared_notes, intern);
|
||||
bool result = p != NULL;
|
||||
return result;
|
||||
}
|
||||
535
src/compiler/lex.c
Normal file
535
src/compiler/lex.c
Normal file
@@ -0,0 +1,535 @@
|
||||
LC_FUNCTION void LC_LexingError(LC_Token *pos, const char *str, ...) {
|
||||
LC_FORMAT(L->arena, str, s8);
|
||||
LC_SendErrorMessage(pos, s8);
|
||||
L->errors += 1;
|
||||
pos->kind = LC_TokenKind_Error;
|
||||
}
|
||||
|
||||
#define LC_IF(cond, ...) \
|
||||
do { \
|
||||
if (cond) { \
|
||||
LC_LexingError(t, __VA_ARGS__); \
|
||||
return; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
LC_FUNCTION bool LC_IsAssign(LC_TokenKind kind) {
|
||||
bool result = kind >= LC_TokenKind_Assign && kind <= LC_TokenKind_RightShiftAssign;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_IsHexDigit(char c) {
|
||||
bool result = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_IsBinDigit(char c) {
|
||||
bool result = (c >= '0' && c <= '1');
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION uint64_t LC_MapCharToNumber(char c) {
|
||||
// clang-format off
|
||||
switch (c) {
|
||||
case '0': return 0;
|
||||
case '1': return 1;
|
||||
case '2': return 2;
|
||||
case '3': return 3;
|
||||
case '4': return 4;
|
||||
case '5': return 5;
|
||||
case '6': return 6;
|
||||
case '7': return 7;
|
||||
case '8': return 8;
|
||||
case '9': return 9;
|
||||
case 'a': case 'A': return 10;
|
||||
case 'b': case 'B': return 11;
|
||||
case 'c': case 'C': return 12;
|
||||
case 'd': case 'D': return 13;
|
||||
case 'e': case 'E': return 14;
|
||||
case 'f': case 'F': return 15;
|
||||
default: return 255;
|
||||
}
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
LC_FUNCTION uint64_t LC_GetEscapeCode(char c) {
|
||||
switch (c) {
|
||||
case 'a': return '\a';
|
||||
case 'b': return '\b';
|
||||
case 'e': return 0x1B;
|
||||
case 'f': return '\f';
|
||||
case 'n': return '\n';
|
||||
case 'r': return '\r';
|
||||
case 't': return '\t';
|
||||
case 'v': return '\v';
|
||||
case '\\': return '\\';
|
||||
case '\'': return '\'';
|
||||
case '\"': return '\"';
|
||||
case '0': return '\0';
|
||||
default: return UINT64_MAX;
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_GetEscapeString(char c) {
|
||||
switch (c) {
|
||||
case '\a': return LC_Lit("\\a");
|
||||
case '\b': return LC_Lit("\\b");
|
||||
case 0x1B: return LC_Lit("\\x1B");
|
||||
case '\f': return LC_Lit("\\f");
|
||||
case '\n': return LC_Lit("\\n");
|
||||
case '\r': return LC_Lit("\\r");
|
||||
case '\t': return LC_Lit("\\t");
|
||||
case '\v': return LC_Lit("\\v");
|
||||
case '\\': return LC_Lit("\\\\");
|
||||
case '\'': return LC_Lit("\\'");
|
||||
case '\"': return LC_Lit("\\\"");
|
||||
case '\0': return LC_Lit("\\0");
|
||||
default: return LC_Lit("");
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_LexAdvance(LC_Lex *x) {
|
||||
if (x->at[0] == 0) {
|
||||
return;
|
||||
} else if (x->at[0] == '\n') {
|
||||
x->line += 1;
|
||||
x->column = 0;
|
||||
}
|
||||
x->column += 1;
|
||||
x->at += 1;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_EatWhitespace(LC_Lex *x) {
|
||||
while (LC_IsWhitespace(x->at[0])) LC_LexAdvance(x);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_EatIdent(LC_Lex *x) {
|
||||
while (x->at[0] == '_' || LC_IsAlphanumeric(x->at[0])) LC_LexAdvance(x);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_SetTokenLen(LC_Lex *x, LC_Token *t) {
|
||||
t->len = (int)(x->at - t->str);
|
||||
LC_ASSERT(NULL, t->len < 2000000000);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_EatUntilIncluding(LC_Lex *x, char c) {
|
||||
while (x->at[0] != 0 && x->at[0] != c) LC_LexAdvance(x);
|
||||
LC_LexAdvance(x);
|
||||
}
|
||||
|
||||
// @todo: add temporary allocation + copy at end to perm
|
||||
LC_FUNCTION LC_BigInt LC_LexBigInt(char *string, int len, uint64_t base) {
|
||||
LC_ASSERT(NULL, base >= 2 && base <= 16);
|
||||
LC_BigInt m = LC_Bigint_u64(1);
|
||||
LC_BigInt base_mul = LC_Bigint_u64(base);
|
||||
LC_BigInt result = LC_Bigint_u64(0);
|
||||
|
||||
LC_BigInt tmp = {0};
|
||||
for (int i = len - 1; i >= 0; --i) {
|
||||
uint64_t u = LC_MapCharToNumber(string[i]);
|
||||
LC_ASSERT(NULL, u < base);
|
||||
LC_BigInt val = LC_Bigint_u64(u);
|
||||
LC_Bigint_mul(&tmp, &val, &m);
|
||||
LC_BigInt new_val = tmp;
|
||||
LC_Bigint_add(&tmp, &result, &new_val);
|
||||
result = tmp;
|
||||
LC_Bigint_mul(&tmp, &m, &base_mul);
|
||||
m = tmp;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_LexNestedComments(LC_Lex *x, LC_Token *t) {
|
||||
t->kind = LC_TokenKind_Comment;
|
||||
LC_LexAdvance(x);
|
||||
|
||||
if (x->at[0] == '*') {
|
||||
LC_LexAdvance(x);
|
||||
t->kind = LC_TokenKind_DocComment;
|
||||
|
||||
if (x->at[0] == ' ' && x->at[1] == 'f' && x->at[2] == 'i' && x->at[3] == 'l' && x->at[4] == 'e') {
|
||||
t->kind = LC_TokenKind_FileDocComment;
|
||||
}
|
||||
|
||||
if (x->at[0] == ' ' && x->at[1] == 'p' && x->at[2] == 'a' && x->at[3] == 'c' && x->at[4] == 'k' && x->at[5] == 'a' && x->at[6] == 'g' && x->at[7] == 'e') {
|
||||
t->kind = LC_TokenKind_PackageDocComment;
|
||||
}
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
for (;;) {
|
||||
if (x->at[0] == '*' && x->at[1] == '/') {
|
||||
if (counter <= 0) break;
|
||||
counter -= 1;
|
||||
} else if (x->at[0] == '/' && x->at[1] == '*') {
|
||||
counter += 1;
|
||||
LC_LexAdvance(x);
|
||||
}
|
||||
LC_IF(x->at[0] == 0, "Unclosed block comment");
|
||||
LC_LexAdvance(x);
|
||||
}
|
||||
t->str += 2;
|
||||
LC_SetTokenLen(x, t);
|
||||
LC_LexAdvance(x);
|
||||
LC_LexAdvance(x);
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_LexStringLiteral(LC_Lex *x, LC_Token *t, LC_TokenKind kind) {
|
||||
t->kind = kind;
|
||||
if (kind == LC_TokenKind_RawString) {
|
||||
LC_EatUntilIncluding(x, '`');
|
||||
} else if (kind == LC_TokenKind_String) {
|
||||
for (;;) {
|
||||
LC_IF(x->at[0] == '\n', "got a new line while parsing a '\"' string literal");
|
||||
LC_IF(x->at[0] == 0, "reached end of file during string lexing");
|
||||
if (x->at[0] == '"') break;
|
||||
if (x->at[0] == '\\' && x->at[1] == '"') LC_LexAdvance(x);
|
||||
LC_LexAdvance(x);
|
||||
}
|
||||
LC_LexAdvance(x);
|
||||
} else {
|
||||
LC_IF(1, "internal compiler error: unhandled case in %s", __FUNCTION__);
|
||||
}
|
||||
|
||||
LC_SetTokenLen(x, t);
|
||||
t->len -= 2;
|
||||
t->str += 1;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_LexUnicodeLiteral(LC_Lex *x, LC_Token *t) {
|
||||
t->kind = LC_TokenKind_Unicode;
|
||||
LC_UTF32Result decode = LC_ConvertUTF8ToUTF32(x->at, 4);
|
||||
LC_IF(decode.error, "invalid utf8 sequence");
|
||||
|
||||
uint8_t c[8] = {0};
|
||||
for (int i = 0; i < decode.advance; i += 1) {
|
||||
c[i] = x->at[0];
|
||||
LC_LexAdvance(x);
|
||||
}
|
||||
uint64_t result = *(uint64_t *)&c[0];
|
||||
|
||||
if (result == '\\') {
|
||||
LC_ASSERT(NULL, decode.advance == 1);
|
||||
result = LC_GetEscapeCode(x->at[0]);
|
||||
LC_IF(result == UINT64_MAX, "invalid escape code");
|
||||
LC_LexAdvance(x);
|
||||
}
|
||||
LC_IF(x->at[0] != '\'', "unclosed unicode literal");
|
||||
|
||||
LC_Bigint_init_signed(&t->i, result);
|
||||
LC_LexAdvance(x);
|
||||
LC_SetTokenLen(x, t);
|
||||
t->str += 1;
|
||||
t->len -= 2;
|
||||
|
||||
LC_IF(t->len == 0, "empty unicode literal");
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_LexIntOrFloat(LC_Lex *x, LC_Token *t) {
|
||||
t->kind = LC_TokenKind_Int;
|
||||
for (;;) {
|
||||
if (x->at[0] == '.') {
|
||||
LC_IF(t->kind == LC_TokenKind_Float, "failed to parse a floating point number, invalid format, found multiple '.'");
|
||||
if (t->kind == LC_TokenKind_Int) t->kind = LC_TokenKind_Float;
|
||||
} else if (!LC_IsDigit(x->at[0])) break;
|
||||
LC_LexAdvance(x);
|
||||
}
|
||||
|
||||
LC_SetTokenLen(x, t);
|
||||
if (t->kind == LC_TokenKind_Int) {
|
||||
t->i = LC_LexBigInt(t->str, t->len, 10);
|
||||
} else if (t->kind == LC_TokenKind_Float) {
|
||||
t->f64 = LC_ParseFloat(t->str, t->len);
|
||||
} else {
|
||||
LC_IF(1, "internal compiler error: unhandled case in %s", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_LexCase2(LC_Lex *x, LC_Token *t, LC_TokenKind tk0, char c, LC_TokenKind tk1) {
|
||||
t->kind = tk0;
|
||||
if (x->at[0] == c) {
|
||||
LC_LexAdvance(x);
|
||||
t->kind = tk1;
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_LexCase3(LC_Lex *x, LC_Token *t, LC_TokenKind tk, char c0, LC_TokenKind tk0, char c1, LC_TokenKind tk1) {
|
||||
t->kind = tk;
|
||||
if (x->at[0] == c0) {
|
||||
t->kind = tk0;
|
||||
LC_LexAdvance(x);
|
||||
} else if (x->at[0] == c1) {
|
||||
t->kind = tk1;
|
||||
LC_LexAdvance(x);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_LexCase4(LC_Lex *x, LC_Token *t, LC_TokenKind tk, char c0, LC_TokenKind tk0, char c1, LC_TokenKind tk1, char c2, LC_TokenKind tk2) {
|
||||
t->kind = tk;
|
||||
if (x->at[0] == c0) {
|
||||
t->kind = tk0;
|
||||
LC_LexAdvance(x);
|
||||
} else if (x->at[0] == c1) {
|
||||
LC_LexAdvance(x);
|
||||
LC_LexCase2(x, t, tk1, c2, tk2);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_LexNext(LC_Lex *x, LC_Token *t) {
|
||||
LC_EatWhitespace(x);
|
||||
LC_MemoryZero(t, sizeof(LC_Token));
|
||||
t->str = x->at;
|
||||
t->line = x->line + 1;
|
||||
t->column = x->column;
|
||||
t->lex = x;
|
||||
char *c = x->at;
|
||||
LC_LexAdvance(x);
|
||||
|
||||
switch (c[0]) {
|
||||
case 0: t->kind = LC_TokenKind_EOF; break;
|
||||
case '(': t->kind = LC_TokenKind_OpenParen; break;
|
||||
case ')': t->kind = LC_TokenKind_CloseParen; break;
|
||||
case '{': t->kind = LC_TokenKind_OpenBrace; break;
|
||||
case '}': t->kind = LC_TokenKind_CloseBrace; break;
|
||||
case '[': t->kind = LC_TokenKind_OpenBracket; break;
|
||||
case ']': t->kind = LC_TokenKind_CloseBracket; break;
|
||||
case ',': t->kind = LC_TokenKind_Comma; break;
|
||||
case ':': t->kind = LC_TokenKind_Colon; break;
|
||||
case ';': t->kind = LC_TokenKind_Semicolon; break;
|
||||
case '~': t->kind = LC_TokenKind_Neg; break;
|
||||
case '#': t->kind = LC_TokenKind_Hash; break;
|
||||
case '@': t->kind = LC_TokenKind_Note; break;
|
||||
case '\'': LC_LexUnicodeLiteral(x, t); break;
|
||||
case '"': LC_LexStringLiteral(x, t, LC_TokenKind_String); break;
|
||||
case '`': LC_LexStringLiteral(x, t, LC_TokenKind_RawString); break;
|
||||
case '=': LC_LexCase2(x, t, LC_TokenKind_Assign, '=', LC_TokenKind_Equals); break;
|
||||
case '!': LC_LexCase2(x, t, LC_TokenKind_Not, '=', LC_TokenKind_NotEquals); break;
|
||||
case '*': LC_LexCase2(x, t, LC_TokenKind_Mul, '=', LC_TokenKind_MulAssign); break;
|
||||
case '%': LC_LexCase2(x, t, LC_TokenKind_Mod, '=', LC_TokenKind_ModAssign); break;
|
||||
case '+': LC_LexCase2(x, t, LC_TokenKind_Add, '=', LC_TokenKind_AddAssign); break;
|
||||
case '-': LC_LexCase2(x, t, LC_TokenKind_Sub, '=', LC_TokenKind_SubAssign); break;
|
||||
case '^': LC_LexCase2(x, t, LC_TokenKind_BitXor, '=', LC_TokenKind_BitXorAssign); break;
|
||||
case '&': LC_LexCase3(x, t, LC_TokenKind_BitAnd, '=', LC_TokenKind_BitAndAssign, '&', LC_TokenKind_And); break;
|
||||
case '|': LC_LexCase3(x, t, LC_TokenKind_BitOr, '=', LC_TokenKind_BitOrAssign, '|', LC_TokenKind_Or); break;
|
||||
case '>': LC_LexCase4(x, t, LC_TokenKind_GreaterThen, '=', LC_TokenKind_GreaterThenEq, '>', LC_TokenKind_RightShift, '=', LC_TokenKind_RightShiftAssign); break;
|
||||
case '<': LC_LexCase4(x, t, LC_TokenKind_LesserThen, '=', LC_TokenKind_LesserThenEq, '<', LC_TokenKind_LeftShift, '=', LC_TokenKind_LeftShiftAssign); break;
|
||||
case '.': {
|
||||
t->kind = LC_TokenKind_Dot;
|
||||
if (x->at[0] == '.' && x->at[1] == '.') {
|
||||
t->kind = LC_TokenKind_ThreeDots;
|
||||
LC_LexAdvance(x);
|
||||
LC_LexAdvance(x);
|
||||
}
|
||||
} break;
|
||||
|
||||
case '0': {
|
||||
if (x->at[0] == 'x') {
|
||||
t->kind = LC_TokenKind_Int;
|
||||
LC_LexAdvance(x);
|
||||
while (LC_IsHexDigit(x->at[0])) LC_LexAdvance(x);
|
||||
LC_SetTokenLen(x, t);
|
||||
LC_IF(t->len < 3, "invalid hex number");
|
||||
t->i = LC_LexBigInt(t->str + 2, t->len - 2, 16);
|
||||
break;
|
||||
}
|
||||
if (x->at[0] == 'b') {
|
||||
t->kind = LC_TokenKind_Int;
|
||||
LC_LexAdvance(x);
|
||||
while (LC_IsBinDigit(x->at[0])) LC_LexAdvance(x);
|
||||
LC_SetTokenLen(x, t);
|
||||
LC_IF(t->len < 3, "invalid binary number");
|
||||
t->i = LC_LexBigInt(t->str + 2, t->len - 2, 2);
|
||||
break;
|
||||
}
|
||||
} // @fallthrough
|
||||
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
case '8':
|
||||
case '9': {
|
||||
LC_LexIntOrFloat(x, t);
|
||||
} break;
|
||||
|
||||
case 'A':
|
||||
case 'a':
|
||||
case 'B':
|
||||
case 'b':
|
||||
case 'C':
|
||||
case 'c':
|
||||
case 'D':
|
||||
case 'd':
|
||||
case 'E':
|
||||
case 'e':
|
||||
case 'F':
|
||||
case 'f':
|
||||
case 'G':
|
||||
case 'g':
|
||||
case 'H':
|
||||
case 'h':
|
||||
case 'I':
|
||||
case 'i':
|
||||
case 'J':
|
||||
case 'j':
|
||||
case 'K':
|
||||
case 'k':
|
||||
case 'L':
|
||||
case 'l':
|
||||
case 'M':
|
||||
case 'm':
|
||||
case 'N':
|
||||
case 'n':
|
||||
case 'O':
|
||||
case 'o':
|
||||
case 'P':
|
||||
case 'p':
|
||||
case 'Q':
|
||||
case 'q':
|
||||
case 'R':
|
||||
case 'r':
|
||||
case 'S':
|
||||
case 's':
|
||||
case 'T':
|
||||
case 't':
|
||||
case 'U':
|
||||
case 'u':
|
||||
case 'V':
|
||||
case 'v':
|
||||
case 'W':
|
||||
case 'w':
|
||||
case 'X':
|
||||
case 'x':
|
||||
case 'Y':
|
||||
case 'y':
|
||||
case 'Z':
|
||||
case 'z':
|
||||
case '_': {
|
||||
t->kind = LC_TokenKind_Ident;
|
||||
LC_EatIdent(x);
|
||||
} break;
|
||||
|
||||
case '/': {
|
||||
t->kind = LC_TokenKind_Div;
|
||||
if (x->at[0] == '=') {
|
||||
t->kind = LC_TokenKind_DivAssign;
|
||||
LC_LexAdvance(x);
|
||||
} else if (x->at[0] == '/') {
|
||||
t->kind = LC_TokenKind_Comment;
|
||||
LC_LexAdvance(x);
|
||||
while (x->at[0] != '\n' && x->at[0] != 0) LC_LexAdvance(x);
|
||||
LC_SetTokenLen(x, t);
|
||||
} else if (x->at[0] == '*') {
|
||||
LC_LexNestedComments(x, t);
|
||||
}
|
||||
} break;
|
||||
|
||||
default: LC_IF(1, "invalid character");
|
||||
}
|
||||
if (t->len == 0 && t->kind != LC_TokenKind_String && t->kind != LC_TokenKind_RawString) LC_SetTokenLen(x, t);
|
||||
if (t->kind == LC_TokenKind_Comment) LC_LexNext(x, t);
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Lex *LC_LexStream(char *file, char *str, int line) {
|
||||
LC_Lex *x = LC_PushStruct(L->lex_arena, LC_Lex);
|
||||
x->begin = str;
|
||||
x->at = str;
|
||||
x->file = LC_ILit(file);
|
||||
x->line = line;
|
||||
|
||||
for (;;) {
|
||||
LC_Token *t = LC_PushStruct(L->lex_arena, LC_Token);
|
||||
if (!x->tokens) x->tokens = t;
|
||||
x->token_count += 1;
|
||||
|
||||
LC_LexNext(x, t);
|
||||
if (t->kind == LC_TokenKind_EOF) break;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_GetTokenLine(LC_Token *token) {
|
||||
LC_Lex *x = token->lex;
|
||||
LC_String content = LC_MakeFromChar(x->begin);
|
||||
LC_StringList lines = LC_Split(L->arena, content, LC_Lit("\n"), 0);
|
||||
|
||||
LC_String l[3] = {LC_MakeEmptyString()};
|
||||
|
||||
int line = 1;
|
||||
for (LC_StringNode *it = lines.first; it; it = it->next) {
|
||||
LC_String sline = it->string;
|
||||
if (token->line - 1 == line) {
|
||||
l[0] = LC_Format(L->arena, "> %.*s\n", LC_Expand(sline));
|
||||
}
|
||||
if (token->line + 1 == line) {
|
||||
l[2] = LC_Format(L->arena, "> %.*s\n", LC_Expand(sline));
|
||||
break;
|
||||
}
|
||||
if (token->line == line) {
|
||||
int begin = (int)(token->str - sline.str);
|
||||
LC_String left = LC_GetPrefix(sline, begin);
|
||||
LC_String past_left = LC_Skip(sline, begin);
|
||||
LC_String mid = LC_GetPrefix(past_left, token->len);
|
||||
LC_String right = LC_Skip(past_left, token->len);
|
||||
|
||||
char *green = "\033[32m";
|
||||
char *reset = "\033[0m";
|
||||
if (!L->use_colored_terminal_output) {
|
||||
green = ">>>>";
|
||||
reset = "<<<<";
|
||||
}
|
||||
l[1] = LC_Format(L->arena, "> %.*s%s%.*s%s%.*s\n", LC_Expand(left), green, LC_Expand(mid), reset, LC_Expand(right));
|
||||
}
|
||||
line += 1;
|
||||
}
|
||||
|
||||
LC_String result = LC_Format(L->arena, "%.*s%.*s%.*s", LC_Expand(l[0]), LC_Expand(l[1]), LC_Expand(l[2]));
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_InternTokens(LC_Lex *x) {
|
||||
// @todo: add scratch, we can dump the LC_PushArray strings
|
||||
for (int i = 0; i < x->token_count; i += 1) {
|
||||
LC_Token *t = x->tokens + i;
|
||||
if (t->kind == LC_TokenKind_String) {
|
||||
int string_len = 0;
|
||||
char *string = LC_PushArray(L->arena, char, t->len);
|
||||
for (int i = 0; i < t->len; i += 1) {
|
||||
char c0 = t->str[i];
|
||||
char c1 = t->str[i + 1];
|
||||
if (i + 1 >= t->len) c1 = 0;
|
||||
|
||||
if (c0 == '\\') {
|
||||
uint64_t code = LC_GetEscapeCode(c1);
|
||||
if (code == UINT64_MAX) {
|
||||
LC_LexingError(t, "invalid escape code in string '%c%c'", c0, c1);
|
||||
break;
|
||||
}
|
||||
|
||||
c0 = (char)code;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
string[string_len++] = c0;
|
||||
}
|
||||
t->ident = LC_InternStrLen(string, string_len);
|
||||
}
|
||||
if (t->kind == LC_TokenKind_Note || t->kind == LC_TokenKind_Ident || t->kind == LC_TokenKind_RawString) {
|
||||
t->ident = LC_InternStrLen(t->str, t->len);
|
||||
}
|
||||
if (t->kind == LC_TokenKind_Ident) {
|
||||
bool is_keyword = t->ident >= L->first_keyword && t->ident <= L->last_keyword;
|
||||
if (is_keyword) {
|
||||
t->kind = LC_TokenKind_Keyword;
|
||||
if (L->kaddptr == t->ident) t->kind = LC_TokenKind_AddPtr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef LC_IF
|
||||
95
src/compiler/lib_compiler.c
Normal file
95
src/compiler/lib_compiler.c
Normal file
@@ -0,0 +1,95 @@
|
||||
#include "lib_compiler.h"
|
||||
|
||||
#if __clang__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wswitch"
|
||||
#pragma clang diagnostic ignored "-Wwritable-strings"
|
||||
#endif
|
||||
|
||||
#ifndef LC_ParseFloat // @override
|
||||
#include <stdlib.h>
|
||||
#define LC_ParseFloat(str, len) strtod(str, NULL)
|
||||
#endif
|
||||
|
||||
#ifndef LC_Print // @override
|
||||
#include <stdio.h>
|
||||
#define LC_Print(str, len) printf("%.*s", (int)len, str)
|
||||
#endif
|
||||
|
||||
#ifndef LC_Exit // @override
|
||||
#include <stdio.h>
|
||||
#define LC_Exit(x) exit(x)
|
||||
#endif
|
||||
|
||||
#ifndef LC_MemoryZero // @override
|
||||
#include <string.h>
|
||||
#define LC_MemoryZero(p, size) memset(p, 0, size)
|
||||
#endif
|
||||
|
||||
#ifndef LC_MemoryCopy // @override
|
||||
#include <string.h>
|
||||
#define LC_MemoryCopy(dst, src, size) memcpy(dst, src, size);
|
||||
#endif
|
||||
|
||||
#ifndef LC_vsnprintf // @override
|
||||
#include <stdio.h>
|
||||
#define LC_vsnprintf vsnprintf
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
LC_THREAD_LOCAL LC_Lang *L;
|
||||
|
||||
#include "unicode.c"
|
||||
#include "string.c"
|
||||
#include "to_string.c"
|
||||
#include "common.c"
|
||||
#include "intern.c"
|
||||
#include "lex.c"
|
||||
#include "bigint.c"
|
||||
#include "value.c"
|
||||
#include "ast.c"
|
||||
#include "ast_walk.c"
|
||||
#include "ast_copy.c"
|
||||
#include "resolver.c"
|
||||
#include "resolve.c"
|
||||
#include "parse.c"
|
||||
#include "printer.c"
|
||||
#include "genc.c"
|
||||
#include "extended_passes.c"
|
||||
#include "packages.c"
|
||||
#include "init.c"
|
||||
|
||||
#if _WIN32
|
||||
#include "win32_filesystem.c"
|
||||
#elif __linux__ || __APPLE__ || __unix__
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "unix_filesystem.c"
|
||||
#endif
|
||||
|
||||
#ifndef LC_USE_CUSTOM_ARENA
|
||||
#include "arena.c"
|
||||
#if _WIN32
|
||||
#include "win32_arena.c"
|
||||
#elif __linux__ || __APPLE__ || __unix__
|
||||
#include "unix_arena.c"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if __clang__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
||||
1696
src/compiler/lib_compiler.h
Normal file
1696
src/compiler/lib_compiler.h
Normal file
File diff suppressed because it is too large
Load Diff
363
src/compiler/packages.c
Normal file
363
src/compiler/packages.c
Normal file
@@ -0,0 +1,363 @@
|
||||
LC_FUNCTION LC_Operand LC_ImportPackage(LC_AST *import, LC_AST *dst, LC_AST *src) {
|
||||
DeclScope *dst_scope = dst->apackage.scope;
|
||||
int scope_size = LC_NextPow2(src->apackage.scope->len * 2 + 1);
|
||||
if (import && import->gimport.name) {
|
||||
LC_PUSH_PACKAGE(dst);
|
||||
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Import, import->gimport.name, import);
|
||||
decl->scope = LC_CreateScope(scope_size);
|
||||
LC_PutGlobalDecl(decl);
|
||||
import->gimport.resolved_decl = decl;
|
||||
LC_POP_PACKAGE();
|
||||
dst_scope = decl->scope;
|
||||
}
|
||||
|
||||
for (int i = 0; i < src->apackage.scope->cap; i += 1) {
|
||||
LC_MapEntry entry = src->apackage.scope->entries[i];
|
||||
if (entry.key != 0) {
|
||||
LC_Decl *decl = (LC_Decl *)entry.value;
|
||||
if (decl->package != src) continue;
|
||||
LC_Decl *existing = (LC_Decl *)LC_MapGetU64(dst_scope, decl->name);
|
||||
if (existing && decl->package == L->builtin_package) {
|
||||
continue;
|
||||
}
|
||||
if (existing) {
|
||||
LC_MarkDeclError(existing);
|
||||
LC_MarkDeclError(decl);
|
||||
return LC_ReportASTErrorEx(decl->ast, existing->ast, "name colission while importing '%s' into '%s', there are 2 decls with the same name '%s'", src->apackage.name, dst->apackage.name, decl->name);
|
||||
}
|
||||
LC_MapInsertU64(dst_scope, decl->name, decl);
|
||||
}
|
||||
}
|
||||
|
||||
if (import && import->gimport.name) {
|
||||
LC_ASSERT(import, dst_scope->cap == scope_size);
|
||||
}
|
||||
|
||||
if (import) import->gimport.resolved = true;
|
||||
return LC_OPNull;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Intern LC_MakePackageNameFromPath(LC_String path) {
|
||||
if (path.str[path.len - 1] == '/') path = LC_Chop(path, 1);
|
||||
LC_String s8name = LC_SkipToLastSlash(path);
|
||||
if (!LC_IsDir(L->arena, path) && LC_EndsWith(path, LC_Lit(".lc"), true)) {
|
||||
s8name = LC_ChopLastPeriod(s8name);
|
||||
}
|
||||
if (s8name.len == 0) {
|
||||
L->errors += 1;
|
||||
LC_SendErrorMessagef(NULL, NULL, "failed to extract name from path %.*s", LC_Expand(path));
|
||||
return LC_GetUniqueIntern("invalid_package_name");
|
||||
}
|
||||
LC_Intern result = LC_InternStrLen(s8name.str, (int)s8name.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_PackageNameValid(LC_Intern name) {
|
||||
char *str = (char *)name;
|
||||
if (LC_IsDigit(str[0])) return false;
|
||||
for (int i = 0; str[i]; i += 1) {
|
||||
bool is_valid = LC_IsIdent(str[i]) || LC_IsDigit(str[i]);
|
||||
if (!is_valid) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_PackageNameDuplicate(LC_Intern name) {
|
||||
LC_ASTFor(it, L->fpackage) {
|
||||
if (it->apackage.name == name) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_AddPackageToList(LC_AST *n) {
|
||||
LC_Intern name = n->apackage.name;
|
||||
if (LC_PackageNameDuplicate(name)) {
|
||||
LC_SendErrorMessagef(NULL, NULL, "found 2 packages with the same name: '%s' / '%.*s'\n", name, LC_Expand(n->apackage.path));
|
||||
L->errors += 1;
|
||||
return;
|
||||
}
|
||||
if (!LC_PackageNameValid(name)) {
|
||||
LC_SendErrorMessagef(NULL, NULL, "invalid package name, please change the name of the package directory: '%s'\n", name);
|
||||
L->errors += 1;
|
||||
return;
|
||||
}
|
||||
LC_DLLAdd(L->fpackage, L->lpackage, n);
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_AST *LC_RegisterPackage(LC_String path) {
|
||||
LC_ASSERT(NULL, path.len != 0);
|
||||
LC_AST *n = LC_CreateAST(NULL, LC_ASTKind_Package);
|
||||
n->apackage.name = LC_MakePackageNameFromPath(path);
|
||||
n->apackage.path = path;
|
||||
LC_AddPackageToList(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_AST *LC_FindImportInRefList(LC_ASTRefList *arr, LC_Intern path) {
|
||||
for (LC_ASTRef *it = arr->first; it; it = it->next) {
|
||||
if (it->ast->gimport.path == path) return it->ast;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_AddASTToRefList(LC_ASTRefList *refs, LC_AST *ast) {
|
||||
LC_ASTRef *ref = LC_PushStruct(L->arena, LC_ASTRef);
|
||||
ref->ast = ast;
|
||||
LC_DLLAdd(refs->first, refs->last, ref);
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_ASTRefList LC_GetPackageImports(LC_AST *package) {
|
||||
LC_ASSERT(package, package->kind == LC_ASTKind_Package);
|
||||
|
||||
LC_ASTRefList refs = {0};
|
||||
LC_ASTFor(file, package->apackage.ffile) {
|
||||
LC_ASTFor(import, file->afile.fimport) {
|
||||
LC_AST *found = LC_FindImportInRefList(&refs, import->gimport.path);
|
||||
if (found) {
|
||||
LC_ReportASTErrorEx(import, found, "duplicate import of: '%s', into package '%s'\n", import->gimport.path, package->apackage.name);
|
||||
continue;
|
||||
}
|
||||
LC_AddASTToRefList(&refs, import);
|
||||
}
|
||||
}
|
||||
|
||||
return refs;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_RegisterPackageDir(char *dir) {
|
||||
LC_String sdir = LC_MakeFromChar(dir);
|
||||
if (!LC_IsDir(L->arena, sdir)) {
|
||||
LC_SendErrorMessagef(NULL, NULL, "dir with name '%s', doesn't exist\n", dir);
|
||||
return;
|
||||
}
|
||||
LC_AddNode(L->arena, &L->package_dirs, sdir);
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_AST *LC_GetPackageByName(LC_Intern name) {
|
||||
LC_ASTFor(it, L->fpackage) {
|
||||
if (it->apackage.name == name) return it;
|
||||
}
|
||||
|
||||
LC_AST *result = NULL;
|
||||
for (LC_StringNode *it = L->package_dirs.first; it; it = it->next) {
|
||||
LC_String s = it->string;
|
||||
LC_String path = LC_Format(L->arena, "%.*s/%s", LC_Expand(s), (char *)name);
|
||||
if (LC_IsDir(L->arena, path)) {
|
||||
if (result != NULL) {
|
||||
LC_SendErrorMessagef(NULL, NULL, "found 2 directories with the same name: '%.*s', '%.*s'\n", LC_Expand(path), LC_Expand(result->apackage.path));
|
||||
L->errors += 1;
|
||||
break;
|
||||
}
|
||||
result = LC_RegisterPackage(path);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_StringList LC_ListFilesInPackage(LC_Arena *arena, LC_String path) {
|
||||
LC_StringList result = LC_MakeEmptyList();
|
||||
for (LC_FileIter it = LC_IterateFiles(arena, path); LC_IsValid(it); LC_Advance(&it)) {
|
||||
if (LC_EndsWith(it.absolute_path, LC_Lit(".lc"), LC_IgnoreCase)) {
|
||||
LC_AddNode(arena, &result, it.absolute_path);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LoadedFile LC_ReadFileHook(LC_AST *package, LC_String path) {
|
||||
LoadedFile result = {path};
|
||||
if (L->on_file_load) {
|
||||
L->on_file_load(package, &result);
|
||||
} else {
|
||||
result.content = LC_ReadFile(L->arena, result.path);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_ParsePackage(LC_AST *n) {
|
||||
LC_ASSERT(n, n->kind == LC_ASTKind_Package);
|
||||
LC_ASSERT(n, n->apackage.scope == NULL);
|
||||
n->apackage.scope = LC_CreateScope(256);
|
||||
|
||||
LC_StringList files = n->apackage.injected_filepaths;
|
||||
if (files.node_count == 0) {
|
||||
files = LC_ListFilesInPackage(L->arena, n->apackage.path);
|
||||
if (files.first == NULL) {
|
||||
LC_SendErrorMessagef(NULL, NULL, "no valid .lc files in '%.*s'", LC_Expand(n->apackage.path));
|
||||
n->apackage.state = LC_DeclState_Error;
|
||||
L->errors += 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (LC_StringNode *it = files.first; it; it = it->next) {
|
||||
LoadedFile file = LC_ReadFileHook(n, it->string);
|
||||
if (file.content.str == NULL) file.content.str = "";
|
||||
|
||||
LC_AST *ast_file = LC_ParseFile(n, file.path.str, file.content.str, file.line);
|
||||
if (!ast_file) {
|
||||
n->apackage.state = LC_DeclState_Error;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_ParsePackagesUsingRegistry(LC_Intern name) {
|
||||
LC_AST *n = LC_GetPackageByName(name);
|
||||
if (!n) {
|
||||
LC_SendErrorMessagef(NULL, NULL, "no package with name '%s'\n", name);
|
||||
L->errors += 1;
|
||||
return;
|
||||
}
|
||||
if (n->apackage.scope) {
|
||||
return;
|
||||
}
|
||||
LC_ParsePackage(n);
|
||||
LC_ASTRefList imports = LC_GetPackageImports(n);
|
||||
for (LC_ASTRef *it = imports.first; it; it = it->next) {
|
||||
LC_ParsePackagesUsingRegistry(it->ast->gimport.path);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_AddOrderedPackageToRefList(LC_AST *n) {
|
||||
LC_ASTRefList *ordered = &L->ordered_packages;
|
||||
for (LC_ASTRef *it = ordered->first; it; it = it->next) {
|
||||
if (it->ast->apackage.name == n->apackage.name) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
LC_AddASTToRefList(ordered, n);
|
||||
}
|
||||
|
||||
// Here we use import statements to produce a list of ordered packages.
|
||||
// While we are at it we also resolve most top level declarations. I say
|
||||
// most because aggregations are handled a bit differently, their resolution
|
||||
// is deffered. This is added because a pointer doesn't require full typeinfo of
|
||||
// an aggregate. It's just a number.
|
||||
LC_FUNCTION LC_AST *LC_OrderPackagesAndBasicResolve(LC_AST *pos, LC_Intern name) {
|
||||
LC_AST *n = LC_GetPackageByName(name);
|
||||
if (n->apackage.state == LC_DeclState_Error) {
|
||||
return NULL;
|
||||
}
|
||||
if (n->apackage.state == LC_DeclState_Resolved) {
|
||||
// This function can be called multiple times, I assume user might
|
||||
// want to use type information to generate something. Pattern:
|
||||
// typecheck -> generate -> typecheck is expected!
|
||||
LC_PackageDecls(n);
|
||||
return n;
|
||||
}
|
||||
if (n->apackage.state == LC_DeclState_Resolving) {
|
||||
LC_ReportASTError(pos, "circular import '%s'", name);
|
||||
n->apackage.state = LC_DeclState_Error;
|
||||
return NULL;
|
||||
}
|
||||
LC_ASSERT(pos, n->apackage.state == LC_DeclState_Unresolved);
|
||||
n->apackage.state = LC_DeclState_Resolving;
|
||||
|
||||
LC_Operand op = LC_ImportPackage(NULL, n, L->builtin_package);
|
||||
LC_ASSERT(pos, !LC_IsError(op));
|
||||
|
||||
// Resolve all imports regardless of errors.
|
||||
// If current package has wrong import it means it's also
|
||||
// wrong but it should still look into all imports
|
||||
// despite this.
|
||||
int wrong_import = 0;
|
||||
LC_ASTRefList refs = LC_GetPackageImports(n);
|
||||
for (LC_ASTRef *it = refs.first; it; it = it->next) {
|
||||
LC_AST *import = LC_OrderPackagesAndBasicResolve(it->ast, it->ast->gimport.path);
|
||||
if (!import) {
|
||||
n->apackage.state = LC_DeclState_Error;
|
||||
wrong_import += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
LC_Operand op = LC_ImportPackage(it->ast, n, import);
|
||||
if (LC_IsError(op)) {
|
||||
n->apackage.state = LC_DeclState_Error;
|
||||
wrong_import += 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (wrong_import) return NULL;
|
||||
|
||||
LC_PackageDecls(n);
|
||||
LC_AddOrderedPackageToRefList(n);
|
||||
n->apackage.state = LC_DeclState_Resolved;
|
||||
return n;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_OrderAndResolveTopLevelDecls(LC_Intern name) {
|
||||
L->first_package = name;
|
||||
LC_OrderPackagesAndBasicResolve(NULL, name);
|
||||
|
||||
// Resolve still incomplete aggregate types, this operates on all packages
|
||||
// that didn't have errors so even if something broke in package ordering
|
||||
// it should still be fine to go forward with this and also proc body analysis
|
||||
for (LC_ASTRef *it = L->ordered_packages.first; it; it = it->next) {
|
||||
LC_AST *package = it->ast;
|
||||
LC_ASSERT(package, package->apackage.state == LC_DeclState_Resolved);
|
||||
LC_ResolveIncompleteTypes(package);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_ResolveAllProcBodies(void) {
|
||||
// We don't need to check errors, only valid packages should have been put into
|
||||
// the list.
|
||||
for (LC_ASTRef *it = L->ordered_packages.first; it; it = it->next) {
|
||||
LC_AST *package = it->ast;
|
||||
LC_ASSERT(package, package->apackage.state == LC_DeclState_Resolved);
|
||||
LC_ResolveProcBodies(package);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_ASTRefList LC_ResolvePackageByName(LC_Intern name) {
|
||||
LC_ParsePackagesUsingRegistry(name);
|
||||
LC_ASTRefList empty = {0};
|
||||
if (L->errors) return empty;
|
||||
|
||||
LC_OrderAndResolveTopLevelDecls(name);
|
||||
LC_ResolveAllProcBodies();
|
||||
return L->ordered_packages;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_GenerateUnityBuild(LC_ASTRefList packages) {
|
||||
if (L->errors) return LC_MakeEmptyString();
|
||||
|
||||
LC_BeginStringGen(L->arena);
|
||||
|
||||
LC_GenLinef("#include <stdbool.h>");
|
||||
LC_GenLinef("#include <stddef.h>");
|
||||
LC_GenLinef("#ifndef LC_String_IMPL");
|
||||
LC_GenLinef("#define LC_String_IMPL");
|
||||
LC_GenLinef("typedef struct { char *str; long long len; } LC_String;");
|
||||
LC_GenLinef("#endif");
|
||||
LC_GenLinef("#ifndef LC_Any_IMPL");
|
||||
LC_GenLinef("#define LC_Any_IMPL");
|
||||
LC_GenLinef("typedef struct { int type; void *data; } LC_Any;");
|
||||
LC_GenLinef("#endif");
|
||||
|
||||
LC_GenLinef("#ifndef LC_Alignof");
|
||||
LC_GenLinef("#if defined(__TINYC__)");
|
||||
LC_GenLinef("#define LC_Alignof(...) __alignof__(__VA_ARGS__)");
|
||||
LC_GenLinef("#else");
|
||||
LC_GenLinef("#define LC_Alignof(...) _Alignof(__VA_ARGS__)");
|
||||
LC_GenLinef("#endif");
|
||||
LC_GenLinef("#endif");
|
||||
LC_GenLinef("void *memset(void *, int, size_t);");
|
||||
|
||||
for (LC_ASTRef *it = packages.first; it; it = it->next) LC_GenCHeader(it->ast);
|
||||
for (LC_ASTRef *it = packages.first; it; it = it->next) LC_GenCImpl(it->ast);
|
||||
LC_String s = LC_EndStringGen(L->arena);
|
||||
return s;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_AddSingleFilePackage(LC_Intern name, LC_String path) {
|
||||
LC_AST *n = LC_CreateAST(0, LC_ASTKind_Package);
|
||||
n->apackage.name = name;
|
||||
n->apackage.path = path;
|
||||
LC_AddNode(L->arena, &n->apackage.injected_filepaths, path);
|
||||
LC_AddPackageToList(n);
|
||||
}
|
||||
1079
src/compiler/parse.c
Normal file
1079
src/compiler/parse.c
Normal file
File diff suppressed because it is too large
Load Diff
477
src/compiler/printer.c
Normal file
477
src/compiler/printer.c
Normal file
@@ -0,0 +1,477 @@
|
||||
LC_FUNCTION LC_StringList *LC_BeginStringGen(LC_Arena *arena) {
|
||||
L->printer.list = LC_MakeEmptyList();
|
||||
L->printer.arena = arena;
|
||||
L->printer.last_filename = 0;
|
||||
L->printer.last_line_num = 0;
|
||||
L->printer.indent = 0;
|
||||
return &L->printer.list;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_EndStringGen(LC_Arena *arena) {
|
||||
LC_String result = LC_MergeString(arena, L->printer.list);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenIndent(void) {
|
||||
LC_String s = LC_Lit(" ");
|
||||
for (int i = 0; i < L->printer.indent; i++) {
|
||||
LC_AddNode(L->printer.arena, &L->printer.list, s);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION char *LC_Strf(const char *str, ...) {
|
||||
LC_FORMAT(L->arena, str, s8);
|
||||
return s8.str;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenLine(void) {
|
||||
LC_Genf("\n");
|
||||
LC_GenIndent();
|
||||
}
|
||||
|
||||
LC_FUNCTION char *LC_GenLCType(LC_Type *type) {
|
||||
LC_StringList out = {0};
|
||||
for (LC_Type *it = type; it;) {
|
||||
if (it->kind == LC_TypeKind_Pointer) {
|
||||
LC_Addf(L->arena, &out, "*");
|
||||
it = it->tptr.base;
|
||||
} else if (it->kind == LC_TypeKind_Array) {
|
||||
LC_Addf(L->arena, &out, "[%d]", it->tarray.size);
|
||||
it = it->tarray.base;
|
||||
} else if (it->kind == LC_TypeKind_Proc) {
|
||||
LC_Addf(L->arena, &out, "proc(");
|
||||
LC_TypeFor(mem, it->tproc.args.first) {
|
||||
LC_Addf(L->arena, &out, "%s: %s", (char *)mem->name, LC_GenLCType(mem->type));
|
||||
if (mem->default_value_expr) LC_Addf(L->arena, &out, "/*has default value*/");
|
||||
if (mem->next) LC_Addf(L->arena, &out, ", ");
|
||||
}
|
||||
if (it->tproc.vargs) LC_Addf(L->arena, &out, "..");
|
||||
LC_Addf(L->arena, &out, ")");
|
||||
if (it->tproc.ret->kind != LC_TypeKind_void) LC_Addf(L->arena, &out, ": %s", LC_GenLCType(it->tproc.ret));
|
||||
break;
|
||||
} else if (it->decl) {
|
||||
LC_Decl *decl = it->decl;
|
||||
LC_ASSERT(decl->ast, decl);
|
||||
LC_Addf(L->arena, &out, "%s", (char *)decl->name);
|
||||
break;
|
||||
} else {
|
||||
LC_SendErrorMessagef(NULL, NULL, "internal compiler error: unhandled type kind in %s", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
LC_String s = LC_MergeString(L->arena, out);
|
||||
return s.str;
|
||||
}
|
||||
|
||||
LC_FUNCTION char *LC_GenLCTypeVal(LC_TypeAndVal v) {
|
||||
if (LC_IsInt(v.type) || LC_IsPtr(v.type) || LC_IsProc(v.type)) {
|
||||
return LC_Bigint_str(&v.i, 10);
|
||||
}
|
||||
if (LC_IsFloat(v.type)) {
|
||||
LC_String s = LC_Format(L->arena, "%f", v.d);
|
||||
return s.str;
|
||||
}
|
||||
LC_ASSERT(NULL, !"invalid codepath");
|
||||
return "";
|
||||
}
|
||||
|
||||
LC_FUNCTION char *LC_GenLCAggName(LC_Type *t) {
|
||||
if (t->kind == LC_TypeKind_Struct) return "struct";
|
||||
if (t->kind == LC_TypeKind_Union) return "union";
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_GenLCNode(LC_AST *n) {
|
||||
switch (n->kind) {
|
||||
case LC_ASTKind_Package: {
|
||||
LC_ASTFor(it, n->apackage.ffile) {
|
||||
LC_GenLCNode(it);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_File: {
|
||||
LC_ASTFor(it, n->afile.fimport) {
|
||||
LC_GenLCNode(it);
|
||||
}
|
||||
|
||||
LC_ASTFor(it, n->afile.fdecl) {
|
||||
LC_GenLCNode(it);
|
||||
}
|
||||
// @todo: we need to do something with notes so we can generate them in order!
|
||||
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_GlobImport: {
|
||||
LC_GenLinef("import %s \"%s\";", (char *)n->gimport.name, (char *)n->gimport.path);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclProc: {
|
||||
LC_GenLinef("%s :: ", (char *)n->dbase.name);
|
||||
LC_GenLCNode(n->dproc.type);
|
||||
if (n->dproc.body) {
|
||||
LC_Genf(" ");
|
||||
LC_GenLCNode(n->dproc.body);
|
||||
} else {
|
||||
LC_Genf(";");
|
||||
}
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclUnion:
|
||||
case LC_ASTKind_DeclStruct: {
|
||||
const char *agg = n->kind == LC_ASTKind_DeclUnion ? "union" : "struct";
|
||||
LC_GenLinef("%s :: %s {", (char *)n->dbase.name, agg);
|
||||
L->printer.indent += 1;
|
||||
LC_ASTFor(it, n->dagg.first) {
|
||||
LC_GenLine();
|
||||
LC_GenLCNode(it);
|
||||
LC_Genf(";");
|
||||
}
|
||||
L->printer.indent -= 1;
|
||||
LC_GenLinef("}");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_TypespecAggMem: {
|
||||
LC_Genf("%s: ", (char *)n->tagg_mem.name);
|
||||
LC_GenLCNode(n->tagg_mem.type);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclVar: {
|
||||
LC_GenLinef("%s ", (char *)n->dbase.name);
|
||||
if (n->dvar.type) {
|
||||
LC_Genf(": ");
|
||||
LC_GenLCNode(n->dvar.type);
|
||||
if (n->dvar.expr) {
|
||||
LC_Genf("= ");
|
||||
LC_GenLCNode(n->dvar.expr);
|
||||
}
|
||||
} else {
|
||||
LC_Genf(":= ");
|
||||
LC_GenLCNode(n->dvar.expr);
|
||||
}
|
||||
LC_Genf(";");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclConst: {
|
||||
LC_GenLinef("%s :: ", (char *)n->dbase.name);
|
||||
LC_GenLCNode(n->dconst.expr);
|
||||
LC_Genf(";");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclTypedef: {
|
||||
LC_GenLinef("%s :: typedef ", (char *)n->dbase.name);
|
||||
LC_GenLCNode(n->dtypedef.type);
|
||||
LC_Genf(";");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprIdent:
|
||||
case LC_ASTKind_TypespecIdent: {
|
||||
LC_Genf("%s", (char *)n->eident.name);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprField:
|
||||
case LC_ASTKind_TypespecField: {
|
||||
LC_GenLCNode(n->efield.left);
|
||||
LC_Genf(".%s", (char *)n->efield.right);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_TypespecPointer: {
|
||||
LC_Genf("*");
|
||||
LC_GenLCNode(n->tpointer.base);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_TypespecArray: {
|
||||
LC_Genf("[");
|
||||
if (n->tarray.index) LC_GenLCNode(n->tarray.index);
|
||||
LC_Genf("]");
|
||||
LC_GenLCNode(n->tpointer.base);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_TypespecProc: {
|
||||
LC_Genf("proc(");
|
||||
LC_ASTFor(it, n->tproc.first) {
|
||||
LC_GenLCNode(it);
|
||||
if (it != n->tproc.last) LC_Genf(", ");
|
||||
}
|
||||
if (n->tproc.vargs) {
|
||||
LC_Genf(", ...");
|
||||
if (n->tproc.vargs_any_promotion) LC_Genf("Any");
|
||||
}
|
||||
LC_Genf(")");
|
||||
if (n->tproc.ret) {
|
||||
LC_Genf(": ");
|
||||
LC_GenLCNode(n->tproc.ret);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_TypespecProcArg: {
|
||||
LC_Genf("%s: ", (char *)n->tproc_arg.name);
|
||||
LC_GenLCNode(n->tproc_arg.type);
|
||||
if (n->tproc_arg.expr) {
|
||||
LC_Genf(" = ");
|
||||
LC_GenLCNode(n->tproc_arg.expr);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtBlock: {
|
||||
if (n->sblock.name && n->sblock.kind != SBLK_Loop) LC_Genf("%s: ", (char *)n->sblock.name);
|
||||
LC_Genf("{");
|
||||
L->printer.indent += 1;
|
||||
LC_ASTFor(it, n->sblock.first) {
|
||||
LC_GenLine();
|
||||
LC_GenLCNode(it);
|
||||
if (it->kind != LC_ASTKind_StmtBlock && it->kind != LC_ASTKind_StmtDefer && it->kind != LC_ASTKind_StmtFor && it->kind != LC_ASTKind_StmtIf && it->kind != LC_ASTKind_StmtSwitch) LC_Genf(";");
|
||||
}
|
||||
L->printer.indent -= 1;
|
||||
LC_GenLinef("}");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtReturn: {
|
||||
LC_Genf("return");
|
||||
if (n->sreturn.expr) {
|
||||
LC_Genf(" ");
|
||||
LC_GenLCNode(n->sreturn.expr);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtBreak: {
|
||||
LC_Genf("break");
|
||||
if (n->sbreak.name) LC_Genf(" %s", (char *)n->sbreak.name);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtContinue: {
|
||||
LC_Genf("continue");
|
||||
if (n->scontinue.name) LC_Genf(" %s", (char *)n->scontinue.name);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtDefer: {
|
||||
LC_Genf("defer ");
|
||||
LC_GenLCNode(n->sdefer.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtFor: {
|
||||
LC_StmtBlock *sblock = &n->sfor.body->sblock;
|
||||
if (sblock->name && sblock->kind == SBLK_Loop) {
|
||||
LC_Genf("%s: ", (char *)sblock->name);
|
||||
}
|
||||
|
||||
LC_Genf("for ");
|
||||
if (n->sfor.init) {
|
||||
LC_GenLCNode(n->sfor.init);
|
||||
if (n->sfor.cond) LC_Genf("; ");
|
||||
}
|
||||
|
||||
if (n->sfor.cond) {
|
||||
LC_GenLCNode(n->sfor.cond);
|
||||
if (n->sfor.inc) {
|
||||
LC_Genf("; ");
|
||||
LC_GenLCNode(n->sfor.inc);
|
||||
}
|
||||
}
|
||||
|
||||
LC_Genf(" ");
|
||||
LC_GenLCNode(n->sfor.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtElseIf:
|
||||
LC_Genf("else ");
|
||||
case LC_ASTKind_StmtIf: {
|
||||
LC_Genf("if ");
|
||||
LC_GenLCNode(n->sif.expr);
|
||||
LC_GenLCNode(n->sif.body);
|
||||
LC_ASTFor(it, n->sif.first) {
|
||||
LC_GenLCNode(it);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtElse: {
|
||||
LC_Genf("else ");
|
||||
LC_GenLCNode(n->sif.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtSwitch: {
|
||||
LC_Genf("switch ");
|
||||
LC_GenLCNode(n->sswitch.expr);
|
||||
LC_Genf("{");
|
||||
L->printer.indent += 1;
|
||||
LC_ASTFor(it, n->sswitch.first) {
|
||||
LC_GenLine();
|
||||
LC_GenLCNode(it);
|
||||
}
|
||||
L->printer.indent -= 1;
|
||||
LC_Genf("}");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtSwitchCase: {
|
||||
LC_Genf("case ");
|
||||
LC_ASTFor(it, n->scase.first) {
|
||||
LC_GenLCNode(it);
|
||||
if (it != n->scase.last) LC_Genf(", ");
|
||||
}
|
||||
LC_Genf(": ");
|
||||
LC_GenLCNode(n->scase.body);
|
||||
} break;
|
||||
case LC_ASTKind_StmtSwitchDefault: {
|
||||
LC_Genf("default: ");
|
||||
LC_GenLCNode(n->scase.body);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtAssign: {
|
||||
LC_GenLCNode(n->sassign.left);
|
||||
LC_Genf(" %s ", LC_TokenKindToOperator(n->sassign.op));
|
||||
LC_GenLCNode(n->sassign.right);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtExpr: {
|
||||
LC_GenLCNode(n->sexpr.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtVar: {
|
||||
LC_Genf("%s", (char *)n->svar.name);
|
||||
if (n->svar.type) {
|
||||
LC_Genf(": ");
|
||||
LC_GenLCNode(n->svar.type);
|
||||
if (n->svar.expr) {
|
||||
LC_Genf(" = ");
|
||||
LC_GenLCNode(n->svar.expr);
|
||||
}
|
||||
} else {
|
||||
LC_Genf(" := ");
|
||||
LC_GenLCNode(n->svar.expr);
|
||||
}
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtConst: {
|
||||
LC_GenLinef("%s :: ", (char *)n->sconst.name);
|
||||
LC_GenLCNode(n->sconst.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprString: {
|
||||
LC_Genf("`%s`", (char *)n->eatom.name);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprInt: {
|
||||
LC_Genf("%s", LC_Bigint_str(&n->eatom.i, 10));
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprFloat: {
|
||||
LC_Genf("%f", n->eatom.d);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprBool: {
|
||||
int64_t value = LC_Bigint_as_unsigned(&n->eatom.i);
|
||||
if (value) {
|
||||
LC_Genf("true");
|
||||
} else {
|
||||
LC_Genf("false");
|
||||
}
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprType: {
|
||||
LC_Genf(":");
|
||||
LC_GenLCNode(n->etype.type);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprBinary: {
|
||||
LC_Genf("(");
|
||||
LC_GenLCNode(n->ebinary.left);
|
||||
LC_Genf("%s", LC_TokenKindToOperator(n->ebinary.op));
|
||||
LC_GenLCNode(n->ebinary.right);
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprUnary: {
|
||||
LC_Genf("%s(", LC_TokenKindToOperator(n->eunary.op));
|
||||
LC_GenLCNode(n->eunary.expr);
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_StmtNote: {
|
||||
LC_Genf("#");
|
||||
LC_GenLCNode(n->snote.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprNote: {
|
||||
LC_Genf("#");
|
||||
LC_GenLCNode(n->enote.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_DeclNote: {
|
||||
LC_GenLinef("#");
|
||||
LC_GenLCNode(n->dnote.expr);
|
||||
LC_Genf(";");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_Note:
|
||||
case LC_ASTKind_ExprBuiltin:
|
||||
case LC_ASTKind_ExprCall: {
|
||||
LC_GenLCNode(n->ecompo.name);
|
||||
LC_Genf("(");
|
||||
LC_ASTFor(it, n->ecompo.first) {
|
||||
LC_GenLCNode(it);
|
||||
if (it != n->ecompo.last) LC_Genf(", ");
|
||||
}
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprCompoundItem:
|
||||
case LC_ASTKind_ExprCallItem: {
|
||||
if (n->ecompo_item.name) {
|
||||
LC_Genf("%s = ", (char *)n->ecompo_item.name);
|
||||
}
|
||||
if (n->ecompo_item.index) {
|
||||
LC_Genf("[");
|
||||
LC_GenLCNode(n->ecompo_item.index);
|
||||
LC_Genf("] = ");
|
||||
}
|
||||
LC_GenLCNode(n->ecompo_item.expr);
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprCompound: {
|
||||
if (n->ecompo.name) LC_GenLCNode(n->ecompo.name);
|
||||
LC_Genf("{");
|
||||
LC_ASTFor(it, n->ecompo.first) {
|
||||
LC_GenLCNode(it);
|
||||
if (it != n->ecompo.last) LC_Genf(", ");
|
||||
}
|
||||
LC_Genf("}");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprCast: {
|
||||
LC_Genf(":");
|
||||
LC_GenLCNode(n->ecast.type);
|
||||
LC_Genf("(");
|
||||
LC_GenLCNode(n->ecast.expr);
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprIndex: {
|
||||
LC_Genf("(");
|
||||
LC_GenLCNode(n->eindex.base);
|
||||
LC_Genf("[");
|
||||
LC_GenLCNode(n->eindex.index);
|
||||
LC_Genf("]");
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprAddPtr: {
|
||||
LC_Genf("addptr(");
|
||||
LC_GenLCNode(n->ebinary.left);
|
||||
LC_Genf(", ");
|
||||
LC_GenLCNode(n->ebinary.right);
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprGetValueOfPointer: {
|
||||
LC_Genf("*(");
|
||||
LC_GenLCNode(n->eunary.expr);
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
case LC_ASTKind_ExprGetPointerOfValue: {
|
||||
LC_Genf("&(");
|
||||
LC_GenLCNode(n->eunary.expr);
|
||||
LC_Genf(")");
|
||||
} break;
|
||||
|
||||
default: LC_ReportASTError(n, "internal compiler error: unhandled ast kind in %s", __FUNCTION__);
|
||||
}
|
||||
}
|
||||
1496
src/compiler/resolve.c
Normal file
1496
src/compiler/resolve.c
Normal file
File diff suppressed because it is too large
Load Diff
192
src/compiler/resolver.c
Normal file
192
src/compiler/resolver.c
Normal file
@@ -0,0 +1,192 @@
|
||||
// clang-format off
|
||||
#define LC_PUSH_COMP_ARRAY_SIZE(SIZE) int PREV_SIZE = L->resolver.compo_context_array_size; L->resolver.compo_context_array_size = SIZE;
|
||||
#define LC_POP_COMP_ARRAY_SIZE() L->resolver.compo_context_array_size = PREV_SIZE
|
||||
#define LC_PUSH_SCOPE(SCOPE) DeclScope *PREV_SCOPE = L->resolver.active_scope; L->resolver.active_scope = SCOPE
|
||||
#define LC_POP_SCOPE() L->resolver.active_scope = PREV_SCOPE
|
||||
#define LC_PUSH_LOCAL_SCOPE() int LOCAL_LEN = L->resolver.locals.len
|
||||
#define LC_POP_LOCAL_SCOPE() L->resolver.locals.len = LOCAL_LEN
|
||||
#define LC_PUSH_PACKAGE(PKG) LC_AST *PREV_PKG = L->resolver.package; L->resolver.package = PKG; LC_PUSH_SCOPE(PKG->apackage.scope)
|
||||
#define LC_POP_PACKAGE() L->resolver.package = PREV_PKG; LC_POP_SCOPE()
|
||||
#define LC_PROP_ERROR(OP, n, ...) OP = __VA_ARGS__; if (LC_IsError(OP)) { n->kind = LC_ASTKind_Error; return OP; }
|
||||
#define LC_DECL_PROP_ERROR(OP, ...) OP = __VA_ARGS__; if (LC_IsError(OP)) { LC_MarkDeclError(decl); return OP; }
|
||||
|
||||
#define LC_IF(COND, N, ...) if (COND) { LC_Operand R_ = LC_ReportASTError(N, __VA_ARGS__); N->kind = LC_ASTKind_Error; return R_; }
|
||||
#define LC_DECL_IF(COND, ...) if (COND) { LC_MarkDeclError(decl); return LC_ReportASTError(__VA_ARGS__); }
|
||||
#define LC_TYPE_IF(COND, ...) if (COND) { LC_MarkDeclError(decl); type->kind = LC_TypeKind_Error; return LC_ReportASTError(__VA_ARGS__); }
|
||||
// clang-format on
|
||||
|
||||
LC_FUNCTION void LC_AddDecl(LC_DeclStack *scope, LC_Decl *decl) {
|
||||
if (scope->len + 1 > scope->cap) {
|
||||
LC_ASSERT(NULL, scope->cap);
|
||||
int new_cap = scope->cap * 2;
|
||||
LC_Decl **new_stack = LC_PushArray(L->arena, LC_Decl *, new_cap);
|
||||
LC_MemoryCopy(new_stack, scope->stack, scope->len * sizeof(LC_Decl *));
|
||||
scope->stack = new_stack;
|
||||
scope->cap = new_cap;
|
||||
}
|
||||
scope->stack[scope->len++] = decl;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_InitDeclStack(LC_DeclStack *stack, int size) {
|
||||
stack->stack = LC_PushArray(L->arena, LC_Decl *, size);
|
||||
stack->cap = size;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_DeclStack *LC_CreateDeclStack(int size) {
|
||||
LC_DeclStack *stack = LC_PushStruct(L->arena, LC_DeclStack);
|
||||
LC_InitDeclStack(stack, size);
|
||||
return stack;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Decl *LC_FindDeclOnStack(LC_DeclStack *scp, LC_Intern name) {
|
||||
for (int i = 0; i < scp->len; i += 1) {
|
||||
LC_Decl *it = scp->stack[i];
|
||||
if (it->name == name) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_MarkDeclError(LC_Decl *decl) {
|
||||
if (decl) {
|
||||
decl->kind = LC_DeclKind_Error;
|
||||
decl->state = LC_DeclState_Error;
|
||||
if (decl->ast) decl->ast->kind = LC_ASTKind_Error;
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Decl *LC_CreateDecl(LC_DeclKind kind, LC_Intern name, LC_AST *n) {
|
||||
LC_Decl *decl = LC_PushStruct(L->decl_arena, LC_Decl);
|
||||
L->decl_count += 1;
|
||||
|
||||
decl->name = name;
|
||||
decl->kind = kind;
|
||||
decl->ast = n;
|
||||
decl->package = L->resolver.package;
|
||||
LC_ASSERT(n, decl->package);
|
||||
|
||||
LC_AST *note = LC_HasNote(n, L->iforeign);
|
||||
if (note) {
|
||||
decl->is_foreign = true;
|
||||
if (note->anote.first) {
|
||||
if (note->anote.size != 1) LC_ReportASTError(note, "invalid format of @foreign(...), more then 1 argument");
|
||||
LC_AST *expr = note->anote.first->ecompo_item.expr;
|
||||
if (expr->kind == LC_ASTKind_ExprIdent) decl->foreign_name = expr->eident.name;
|
||||
if (expr->kind != LC_ASTKind_ExprIdent) LC_ReportASTError(note, "invalid format of @foreign(...), expected identifier");
|
||||
}
|
||||
}
|
||||
if (!decl->foreign_name) decl->foreign_name = decl->name;
|
||||
return decl;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_ThereIsNoDecl(DeclScope *scp, LC_Decl *decl, bool check_locals) {
|
||||
LC_Decl *r = (LC_Decl *)LC_MapGetU64(scp, decl->name);
|
||||
if (check_locals && !r) {
|
||||
r = LC_FindDeclOnStack(&L->resolver.locals, decl->name);
|
||||
}
|
||||
if (r) {
|
||||
LC_MarkDeclError(r);
|
||||
LC_MarkDeclError(decl);
|
||||
return LC_ReportASTErrorEx(decl->ast, r->ast, "there are 2 decls with the same name '%s'", decl->name);
|
||||
}
|
||||
return LC_OPNull;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_AddDeclToScope(DeclScope *scp, LC_Decl *decl) {
|
||||
LC_Operand LC_DECL_PROP_ERROR(op, LC_ThereIsNoDecl(scp, decl, false));
|
||||
LC_MapInsertU64(scp, decl->name, decl);
|
||||
return LC_OPDecl(decl);
|
||||
}
|
||||
|
||||
LC_FUNCTION DeclScope *LC_CreateScope(int size) {
|
||||
DeclScope *scope = LC_PushStruct(L->arena, DeclScope);
|
||||
scope->arena = L->arena;
|
||||
LC_MapReserve(scope, size);
|
||||
return scope;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Decl *LC_FindDeclInScope(DeclScope *scope, LC_Intern name) {
|
||||
LC_Decl *decl = (LC_Decl *)LC_MapGetU64(scope, name);
|
||||
return decl;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Decl *LC_GetLocalOrGlobalDecl(LC_Intern name) {
|
||||
LC_Decl *decl = LC_FindDeclInScope(L->resolver.active_scope, name);
|
||||
if (!decl && L->resolver.package->apackage.scope == L->resolver.active_scope) {
|
||||
decl = LC_FindDeclOnStack(&L->resolver.locals, name);
|
||||
}
|
||||
return decl;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_PutGlobalDecl(LC_Decl *decl) {
|
||||
LC_Operand LC_DECL_PROP_ERROR(op, LC_AddDeclToScope(L->resolver.package->apackage.scope, decl));
|
||||
|
||||
// :Mangle global scope name
|
||||
if (!decl->is_foreign && decl->package != L->builtin_package) {
|
||||
bool mangle = true;
|
||||
if (LC_HasNote(decl->ast, L->idont_mangle)) mangle = false;
|
||||
if (LC_HasNote(decl->ast, L->iapi)) mangle = false;
|
||||
if (decl->name == L->imain) {
|
||||
if (L->first_package) {
|
||||
if (L->first_package == decl->package->apackage.name) {
|
||||
mangle = false;
|
||||
}
|
||||
} else mangle = false;
|
||||
}
|
||||
if (mangle) {
|
||||
LC_String name = LC_Format(L->arena, "lc_%s_%s", (char *)decl->package->apackage.name, (char *)decl->name);
|
||||
decl->foreign_name = LC_InternStrLen(name.str, (int)name.len);
|
||||
}
|
||||
}
|
||||
|
||||
LC_Decl *conflict = (LC_Decl *)LC_MapGetU64(&L->foreign_names, decl->foreign_name);
|
||||
if (conflict && !decl->is_foreign) {
|
||||
LC_ReportASTErrorEx(decl->ast, conflict->ast, "found two global declarations with the same foreign name: %s", decl->foreign_name);
|
||||
} else {
|
||||
LC_MapInsertU64(&L->foreign_names, decl->foreign_name, decl);
|
||||
}
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_CreateLocalDecl(LC_DeclKind kind, LC_Intern name, LC_AST *ast) {
|
||||
LC_Decl *decl = LC_CreateDecl(kind, name, ast);
|
||||
decl->state = LC_DeclState_Resolving;
|
||||
LC_Operand LC_DECL_PROP_ERROR(operr0, LC_ThereIsNoDecl(L->resolver.package->apackage.scope, decl, true));
|
||||
LC_AddDecl(&L->resolver.locals, decl);
|
||||
return LC_OPDecl(decl);
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Decl *LC_AddConstIntDecl(char *key, int64_t value) {
|
||||
LC_Intern intern = LC_ILit(key);
|
||||
LC_Decl *decl = LC_CreateDecl(LC_DeclKind_Const, intern, &L->NullAST);
|
||||
decl->state = LC_DeclState_Resolved;
|
||||
decl->type = L->tuntypedint;
|
||||
LC_Bigint_init_signed(&decl->v.i, value);
|
||||
LC_AddDeclToScope(L->resolver.package->apackage.scope, decl);
|
||||
return decl;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Decl *LC_GetBuiltin(LC_Intern name) {
|
||||
LC_Decl *decl = (LC_Decl *)LC_MapGetU64(L->builtin_package->apackage.scope, name);
|
||||
return decl;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_AddBuiltinConstInt(char *key, int64_t value) {
|
||||
LC_PUSH_PACKAGE(L->builtin_package);
|
||||
LC_AddConstIntDecl(key, value);
|
||||
LC_POP_PACKAGE();
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_AST *LC_HasNote(LC_AST *ast, LC_Intern i) {
|
||||
if (ast && ast->notes) {
|
||||
LC_ASTFor(it, ast->notes->anote_list.first) {
|
||||
LC_ASSERT(it, "internal compiler error: note is not an identifier");
|
||||
if (it->anote.name->eident.name == i) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
422
src/compiler/string.c
Normal file
422
src/compiler/string.c
Normal file
@@ -0,0 +1,422 @@
|
||||
LC_FUNCTION int64_t LC__ClampTop(int64_t val, int64_t max) {
|
||||
if (val > max) val = max;
|
||||
return val;
|
||||
}
|
||||
|
||||
LC_FUNCTION char LC_ToLowerCase(char a) {
|
||||
if (a >= 'A' && a <= 'Z') a += 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
LC_FUNCTION char LC_ToUpperCase(char a) {
|
||||
if (a >= 'a' && a <= 'z') a -= 32;
|
||||
return a;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_IsWhitespace(char w) {
|
||||
bool result = w == '\n' || w == ' ' || w == '\t' || w == '\v' || w == '\r';
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_IsAlphabetic(char a) {
|
||||
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z');
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_IsIdent(char a) {
|
||||
bool result = (a >= 'a' && a <= 'z') || (a >= 'A' && a <= 'Z') || a == '_';
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_IsDigit(char a) {
|
||||
bool result = a >= '0' && a <= '9';
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_IsAlphanumeric(char a) {
|
||||
bool result = LC_IsDigit(a) || LC_IsAlphabetic(a);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_AreEqual(LC_String a, LC_String b, unsigned ignore_case) {
|
||||
if (a.len != b.len) return false;
|
||||
for (int64_t i = 0; i < a.len; i++) {
|
||||
char A = a.str[i];
|
||||
char B = b.str[i];
|
||||
if (ignore_case) {
|
||||
A = LC_ToLowerCase(A);
|
||||
B = LC_ToLowerCase(B);
|
||||
}
|
||||
if (A != B)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_EndsWith(LC_String a, LC_String end, unsigned ignore_case) {
|
||||
LC_String a_end = LC_GetPostfix(a, end.len);
|
||||
bool result = LC_AreEqual(end, a_end, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_StartsWith(LC_String a, LC_String start, unsigned ignore_case) {
|
||||
LC_String a_start = LC_GetPrefix(a, start.len);
|
||||
bool result = LC_AreEqual(start, a_start, ignore_case);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_MakeString(char *str, int64_t len) {
|
||||
LC_String result;
|
||||
result.str = (char *)str;
|
||||
result.len = len;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_CopyString(LC_Arena *allocator, LC_String string) {
|
||||
char *copy = (char *)LC_PushSize(allocator, sizeof(char) * (string.len + 1));
|
||||
LC_MemoryCopy(copy, string.str, string.len);
|
||||
copy[string.len] = 0;
|
||||
LC_String result = LC_MakeString(copy, string.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_CopyChar(LC_Arena *allocator, char *s) {
|
||||
int64_t len = LC_StrLen(s);
|
||||
char *copy = (char *)LC_PushSize(allocator, sizeof(char) * (len + 1));
|
||||
LC_MemoryCopy(copy, s, len);
|
||||
copy[len] = 0;
|
||||
LC_String result = LC_MakeString(copy, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_NormalizePath(LC_Arena *allocator, LC_String s) {
|
||||
LC_String copy = LC_CopyString(allocator, s);
|
||||
for (int64_t i = 0; i < copy.len; i++) {
|
||||
if (copy.str[i] == '\\')
|
||||
copy.str[i] = '/';
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_NormalizePathUnsafe(LC_String s) {
|
||||
for (int64_t i = 0; i < s.len; i++) {
|
||||
if (s.str[i] == '\\')
|
||||
s.str[i] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_Chop(LC_String string, int64_t len) {
|
||||
len = LC__ClampTop(len, string.len);
|
||||
LC_String result = LC_MakeString(string.str, string.len - len);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_Skip(LC_String string, int64_t len) {
|
||||
len = LC__ClampTop(len, string.len);
|
||||
int64_t remain = string.len - len;
|
||||
LC_String result = LC_MakeString(string.str + len, remain);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_GetPostfix(LC_String string, int64_t len) {
|
||||
len = LC__ClampTop(len, string.len);
|
||||
int64_t remain_len = string.len - len;
|
||||
LC_String result = LC_MakeString(string.str + remain_len, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_GetPrefix(LC_String string, int64_t len) {
|
||||
len = LC__ClampTop(len, string.len);
|
||||
LC_String result = LC_MakeString(string.str, len);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_GetNameNoExt(LC_String s) {
|
||||
return LC_SkipToLastSlash(LC_ChopLastPeriod(s));
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_Slice(LC_String string, int64_t first_index, int64_t one_past_last_index) {
|
||||
if (one_past_last_index < 0) one_past_last_index = string.len + one_past_last_index + 1;
|
||||
if (first_index < 0) first_index = string.len + first_index;
|
||||
LC_ASSERT(NULL, first_index < one_past_last_index && "LC_Slice, first_index is bigger then one_past_last_index");
|
||||
LC_ASSERT(NULL, string.len > 0 && "Slicing string of length 0! Might be an error!");
|
||||
LC_String result = string;
|
||||
if (string.len > 0) {
|
||||
if (one_past_last_index > first_index) {
|
||||
first_index = LC__ClampTop(first_index, string.len - 1);
|
||||
one_past_last_index = LC__ClampTop(one_past_last_index, string.len);
|
||||
result.str += first_index;
|
||||
result.len = one_past_last_index - first_index;
|
||||
} else {
|
||||
result.len = 0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_Seek(LC_String string, LC_String find, LC_FindFlag flags, int64_t *index_out) {
|
||||
bool ignore_case = flags & LC_FindFlag_IgnoreCase ? true : false;
|
||||
bool result = false;
|
||||
if (flags & LC_FindFlag_MatchFindLast) {
|
||||
for (int64_t i = string.len; i != 0; i--) {
|
||||
int64_t index = i - 1;
|
||||
LC_String substring = LC_Slice(string, index, index + find.len);
|
||||
if (LC_AreEqual(substring, find, ignore_case)) {
|
||||
if (index_out)
|
||||
*index_out = index;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int64_t i = 0; i < string.len; i++) {
|
||||
LC_String substring = LC_Slice(string, i, i + find.len);
|
||||
if (LC_AreEqual(substring, find, ignore_case)) {
|
||||
if (index_out)
|
||||
*index_out = i;
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION int64_t LC_Find(LC_String string, LC_String find, LC_FindFlag flag) {
|
||||
int64_t result = -1;
|
||||
LC_Seek(string, find, flag, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_StringList LC_Split(LC_Arena *allocator, LC_String string, LC_String find, LC_SplitFlag flags) {
|
||||
LC_StringList result = LC_MakeEmptyList();
|
||||
int64_t index = 0;
|
||||
|
||||
LC_FindFlag find_flag = flags & LC_SplitFlag_IgnoreCase ? LC_FindFlag_IgnoreCase : LC_FindFlag_None;
|
||||
while (LC_Seek(string, find, find_flag, &index)) {
|
||||
LC_String before_match = LC_MakeString(string.str, index);
|
||||
LC_AddNode(allocator, &result, before_match);
|
||||
if (flags & LC_SplitFlag_SplitInclusive) {
|
||||
LC_String match = LC_MakeString(string.str + index, find.len);
|
||||
LC_AddNode(allocator, &result, match);
|
||||
}
|
||||
string = LC_Skip(string, index + find.len);
|
||||
}
|
||||
if (string.len) LC_AddNode(allocator, &result, string);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_MergeWithSeparator(LC_Arena *allocator, LC_StringList list, LC_String separator) {
|
||||
if (list.node_count == 0) return LC_MakeEmptyString();
|
||||
if (list.char_count == 0) return LC_MakeEmptyString();
|
||||
|
||||
int64_t base_size = (list.char_count + 1);
|
||||
int64_t sep_size = (list.node_count - 1) * separator.len;
|
||||
int64_t size = base_size + sep_size;
|
||||
char *buff = (char *)LC_PushSize(allocator, sizeof(char) * (size + 1));
|
||||
LC_String string = LC_MakeString(buff, 0);
|
||||
for (LC_StringNode *it = list.first; it; it = it->next) {
|
||||
LC_ASSERT(NULL, string.len + it->string.len <= size);
|
||||
LC_MemoryCopy(string.str + string.len, it->string.str, it->string.len);
|
||||
string.len += it->string.len;
|
||||
if (it != list.last) {
|
||||
LC_MemoryCopy(string.str + string.len, separator.str, separator.len);
|
||||
string.len += separator.len;
|
||||
}
|
||||
}
|
||||
LC_ASSERT(NULL, string.len == size - 1);
|
||||
string.str[size] = 0;
|
||||
return string;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_MergeString(LC_Arena *allocator, LC_StringList list) {
|
||||
return LC_MergeWithSeparator(allocator, list, LC_Lit(""));
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_ChopLastSlash(LC_String s) {
|
||||
LC_String result = s;
|
||||
LC_Seek(s, LC_Lit("/"), LC_FindFlag_MatchFindLast, &result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_ChopLastPeriod(LC_String s) {
|
||||
LC_String result = s;
|
||||
LC_Seek(s, LC_Lit("."), LC_FindFlag_MatchFindLast, &result.len);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_SkipToLastSlash(LC_String s) {
|
||||
int64_t pos;
|
||||
LC_String result = s;
|
||||
if (LC_Seek(s, LC_Lit("/"), LC_FindFlag_MatchFindLast, &pos)) {
|
||||
result = LC_Skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_SkipToLastPeriod(LC_String s) {
|
||||
int64_t pos;
|
||||
LC_String result = s;
|
||||
if (LC_Seek(s, LC_Lit("."), LC_FindFlag_MatchFindLast, &pos)) {
|
||||
result = LC_Skip(result, pos + 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION int64_t LC_StrLen(char *string) {
|
||||
int64_t len = 0;
|
||||
while (*string++ != 0)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_MakeFromChar(char *string) {
|
||||
LC_String result;
|
||||
result.str = (char *)string;
|
||||
result.len = LC_StrLen(string);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_MakeEmptyString(void) {
|
||||
return LC_MakeString(0, 0);
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_StringList LC_MakeEmptyList(void) {
|
||||
LC_StringList result;
|
||||
result.first = 0;
|
||||
result.last = 0;
|
||||
result.char_count = 0;
|
||||
result.node_count = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_FormatV(LC_Arena *allocator, const char *str, va_list args1) {
|
||||
va_list args2;
|
||||
va_copy(args2, args1);
|
||||
int64_t len = LC_vsnprintf(0, 0, str, args2);
|
||||
va_end(args2);
|
||||
|
||||
char *result = (char *)LC_PushSize(allocator, sizeof(char) * (len + 1));
|
||||
LC_vsnprintf(result, (int)(len + 1), str, args1);
|
||||
LC_String res = LC_MakeString(result, len);
|
||||
return res;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_Format(LC_Arena *allocator, const char *str, ...) {
|
||||
LC_FORMAT(allocator, str, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_StringNode *LC_CreateNode(LC_Arena *allocator, LC_String string) {
|
||||
LC_StringNode *result = (LC_StringNode *)LC_PushSize(allocator, sizeof(LC_StringNode));
|
||||
result->string = string;
|
||||
result->next = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_ReplaceNodeString(LC_StringList *list, LC_StringNode *node, LC_String new_string) {
|
||||
list->char_count -= node->string.len;
|
||||
list->char_count += new_string.len;
|
||||
node->string = new_string;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_AddExistingNode(LC_StringList *list, LC_StringNode *node) {
|
||||
if (list->first) {
|
||||
list->last->next = node;
|
||||
list->last = list->last->next;
|
||||
} else {
|
||||
list->first = list->last = node;
|
||||
}
|
||||
list->node_count += 1;
|
||||
list->char_count += node->string.len;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_AddArray(LC_Arena *allocator, LC_StringList *list, char **array, int count) {
|
||||
for (int i = 0; i < count; i += 1) {
|
||||
LC_String s = LC_MakeFromChar(array[i]);
|
||||
LC_AddNode(allocator, list, s);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_AddArrayWithPrefix(LC_Arena *allocator, LC_StringList *list, char *prefix, char **array, int count) {
|
||||
for (int i = 0; i < count; i += 1) {
|
||||
LC_Addf(allocator, list, "%s%s", prefix, array[i]);
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_StringList LC_MakeList(LC_Arena *allocator, LC_String a) {
|
||||
LC_StringList result = LC_MakeEmptyList();
|
||||
LC_AddNode(allocator, &result, a);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_StringList LC_CopyList(LC_Arena *allocator, LC_StringList a) {
|
||||
LC_StringList result = LC_MakeEmptyList();
|
||||
for (LC_StringNode *it = a.first; it; it = it->next) LC_AddNode(allocator, &result, it->string);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_StringList LC_ConcatLists(LC_Arena *allocator, LC_StringList a, LC_StringList b) {
|
||||
LC_StringList result = LC_MakeEmptyList();
|
||||
for (LC_StringNode *it = a.first; it; it = it->next) LC_AddNode(allocator, &result, it->string);
|
||||
for (LC_StringNode *it = b.first; it; it = it->next) LC_AddNode(allocator, &result, it->string);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_StringNode *LC_AddNode(LC_Arena *allocator, LC_StringList *list, LC_String string) {
|
||||
LC_StringNode *node = LC_CreateNode(allocator, string);
|
||||
LC_AddExistingNode(list, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_StringNode *LC_Add(LC_Arena *allocator, LC_StringList *list, LC_String string) {
|
||||
LC_String copy = LC_CopyString(allocator, string);
|
||||
LC_StringNode *node = LC_CreateNode(allocator, copy);
|
||||
LC_AddExistingNode(list, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_Addf(LC_Arena *allocator, LC_StringList *list, const char *str, ...) {
|
||||
LC_FORMAT(allocator, str, result);
|
||||
LC_AddNode(allocator, list, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String16 LC_ToWidecharEx(LC_Arena *allocator, LC_String string) {
|
||||
LC_ASSERT(NULL, sizeof(wchar_t) == 2);
|
||||
wchar_t *buffer = (wchar_t *)LC_PushSize(allocator, sizeof(wchar_t) * (string.len + 1));
|
||||
int64_t size = LC_CreateWidecharFromChar(buffer, string.len + 1, string.str, string.len);
|
||||
LC_String16 result = {buffer, size};
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION wchar_t *LC_ToWidechar(LC_Arena *allocator, LC_String string) {
|
||||
LC_String16 result = LC_ToWidecharEx(allocator, string);
|
||||
return result.str;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_FromWidecharEx(LC_Arena *allocator, wchar_t *wstring, int64_t wsize) {
|
||||
LC_ASSERT(NULL, sizeof(wchar_t) == 2);
|
||||
|
||||
int64_t buffer_size = (wsize + 1) * 2;
|
||||
char *buffer = (char *)LC_PushSize(allocator, buffer_size);
|
||||
int64_t size = LC_CreateCharFromWidechar(buffer, buffer_size, wstring, wsize);
|
||||
LC_String result = LC_MakeString(buffer, size);
|
||||
|
||||
LC_ASSERT(NULL, size < buffer_size);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION int64_t LC_WideLength(wchar_t *string) {
|
||||
int64_t len = 0;
|
||||
while (*string++ != 0)
|
||||
len++;
|
||||
return len;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_FromWidechar(LC_Arena *allocator, wchar_t *wstring) {
|
||||
int64_t size = LC_WideLength(wstring);
|
||||
LC_String result = LC_FromWidecharEx(allocator, wstring, size);
|
||||
return result;
|
||||
}
|
||||
277
src/compiler/to_string.c
Normal file
277
src/compiler/to_string.c
Normal file
@@ -0,0 +1,277 @@
|
||||
LC_FUNCTION const char *LC_OSToString(LC_OS os) {
|
||||
switch (os) {
|
||||
case LC_OS_WINDOWS: return "OS_WINDOWS";
|
||||
case LC_OS_LINUX: return "OS_LINUX";
|
||||
case LC_OS_MAC: return "OS_MAC";
|
||||
default: return "UNKNOWN_OPERATING_SYSTEM";
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION const char *LC_GENToString(LC_GEN os) {
|
||||
switch (os) {
|
||||
case LC_GEN_C: return "GEN_C";
|
||||
default: return "UNKNOWN_GENERATOR";
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION const char *LC_ARCHToString(LC_ARCH arch) {
|
||||
switch (arch) {
|
||||
case LC_ARCH_X86: return "ARCH_X86";
|
||||
case LC_ARCH_X64: return "ARCH_X64";
|
||||
default: return "UNKNOWN_ARCHITECTURE";
|
||||
}
|
||||
}
|
||||
|
||||
LC_FUNCTION const char *LC_ASTKindToString(LC_ASTKind kind) {
|
||||
static const char *strs[] = {
|
||||
"ast null",
|
||||
"ast error",
|
||||
"ast note",
|
||||
"ast note list",
|
||||
"ast file",
|
||||
"ast package",
|
||||
"ast ignore",
|
||||
"typespec procdure argument",
|
||||
"typespec aggregate member",
|
||||
"expr call item",
|
||||
"expr compound item",
|
||||
"expr note",
|
||||
"stmt switch case",
|
||||
"stmt switch default",
|
||||
"stmt else if",
|
||||
"stmt else",
|
||||
"import",
|
||||
"global note",
|
||||
"decl proc",
|
||||
"decl struct",
|
||||
"decl union",
|
||||
"decl var",
|
||||
"decl const",
|
||||
"decl typedef",
|
||||
"typespec ident",
|
||||
"typespec field",
|
||||
"typespec pointer",
|
||||
"typespec array",
|
||||
"typespec proc",
|
||||
"stmt block",
|
||||
"stmt note",
|
||||
"stmt return",
|
||||
"stmt break",
|
||||
"stmt continue",
|
||||
"stmt defer",
|
||||
"stmt for",
|
||||
"stmt if",
|
||||
"stmt switch",
|
||||
"stmt assign",
|
||||
"stmt expr",
|
||||
"stmt var",
|
||||
"stmt const",
|
||||
"expr ident",
|
||||
"expr string",
|
||||
"expr int",
|
||||
"expr float",
|
||||
"expr bool",
|
||||
"expr type",
|
||||
"expr binary",
|
||||
"expr unary",
|
||||
"expr builtin",
|
||||
"expr call",
|
||||
"expr compound",
|
||||
"expr cast",
|
||||
"expr field",
|
||||
"expr index",
|
||||
"expr pointerindex",
|
||||
"expr getvalueofpointer",
|
||||
"expr getpointerofvalue",
|
||||
};
|
||||
if (kind < 0 || kind >= LC_ASTKind_Count) {
|
||||
return "<invalid_ast_kind>";
|
||||
}
|
||||
return strs[kind];
|
||||
}
|
||||
|
||||
LC_FUNCTION const char *LC_TypeKindToString(LC_TypeKind kind) {
|
||||
static const char *strs[] = {
|
||||
"LC_TypeKind_char",
|
||||
"LC_TypeKind_uchar",
|
||||
"LC_TypeKind_short",
|
||||
"LC_TypeKind_ushort",
|
||||
"LC_TypeKind_bool",
|
||||
"LC_TypeKind_int",
|
||||
"LC_TypeKind_uint",
|
||||
"LC_TypeKind_long",
|
||||
"LC_TypeKind_ulong",
|
||||
"LC_TypeKind_llong",
|
||||
"LC_TypeKind_ullong",
|
||||
"LC_TypeKind_float",
|
||||
"LC_TypeKind_double",
|
||||
"LC_TypeKind_void",
|
||||
"LC_TypeKind_Struct",
|
||||
"LC_TypeKind_Union",
|
||||
"LC_TypeKind_Pointer",
|
||||
"LC_TypeKind_Array",
|
||||
"LC_TypeKind_Proc",
|
||||
"LC_TypeKind_UntypedInt",
|
||||
"LC_TypeKind_UntypedFloat",
|
||||
"LC_TypeKind_UntypedString",
|
||||
"LC_TypeKind_Incomplete",
|
||||
"LC_TypeKind_Completing",
|
||||
"LC_TypeKind_Error",
|
||||
};
|
||||
if (kind < 0 || kind >= T_TotalCount) {
|
||||
return "<invalid_type_kind>";
|
||||
}
|
||||
return strs[kind];
|
||||
}
|
||||
|
||||
LC_FUNCTION const char *LC_DeclKindToString(LC_DeclKind decl_kind) {
|
||||
static const char *strs[] = {
|
||||
"declaration of error kind",
|
||||
"type declaration",
|
||||
"const declaration",
|
||||
"variable declaration",
|
||||
"procedure declaration",
|
||||
"import declaration",
|
||||
};
|
||||
if (decl_kind < 0 || decl_kind >= LC_DeclKind_Count) {
|
||||
return "<invalid_decl_kind>";
|
||||
}
|
||||
return strs[decl_kind];
|
||||
}
|
||||
|
||||
LC_FUNCTION const char *LC_TokenKindToString(LC_TokenKind token_kind) {
|
||||
static const char *strs[] = {
|
||||
"end of file",
|
||||
"token error",
|
||||
"comment",
|
||||
"doc comment",
|
||||
"file doc comment",
|
||||
"package doc comment",
|
||||
"note '@'",
|
||||
"hash '#'",
|
||||
"identifier",
|
||||
"keyword",
|
||||
"string literal",
|
||||
"raw string literal",
|
||||
"integer literal",
|
||||
"float literal",
|
||||
"unicode literal",
|
||||
"open paren '('",
|
||||
"close paren ')'",
|
||||
"open brace '{'",
|
||||
"close brace '}'",
|
||||
"open bracket '['",
|
||||
"close bracket ']'",
|
||||
"comma ','",
|
||||
"question mark '?'",
|
||||
"semicolon ';'",
|
||||
"period '.'",
|
||||
"three dots '...'",
|
||||
"colon ':'",
|
||||
"multiply '*'",
|
||||
"divide '/'",
|
||||
"modulo '%'",
|
||||
"left shift '<<'",
|
||||
"right shift '>>'",
|
||||
"add '+'",
|
||||
"subtract '-'",
|
||||
"equals '=='",
|
||||
"lesser then '<'",
|
||||
"greater then '>'",
|
||||
"lesser then or equal '<='",
|
||||
"greater then or equal '>='",
|
||||
"not equal '!='",
|
||||
"bit and '&'",
|
||||
"bit or '|'",
|
||||
"bit xor '^'",
|
||||
"and '&&'",
|
||||
"or '||'",
|
||||
"addptr keyword",
|
||||
"negation '~'",
|
||||
"exclamation '!'",
|
||||
"assignment '='",
|
||||
"assignment '/='",
|
||||
"assignment '*='",
|
||||
"assignment '%='",
|
||||
"assignment '-='",
|
||||
"assignment '+='",
|
||||
"assignment '&='",
|
||||
"assignment '|='",
|
||||
"assignment '^='",
|
||||
"assignment '<<='",
|
||||
"assignment '>>='",
|
||||
};
|
||||
|
||||
if (token_kind < 0 || token_kind >= LC_TokenKind_Count) {
|
||||
return "<invalid_token_kind>";
|
||||
}
|
||||
return strs[token_kind];
|
||||
}
|
||||
|
||||
LC_FUNCTION const char *LC_TokenKindToOperator(LC_TokenKind token_kind) {
|
||||
static const char *strs[] = {
|
||||
"end of file",
|
||||
"token error",
|
||||
"comment",
|
||||
"doc comment",
|
||||
"file doc comment",
|
||||
"package doc comment",
|
||||
"@",
|
||||
"#",
|
||||
"identifier",
|
||||
"keyword",
|
||||
"string literal",
|
||||
"raw string literal",
|
||||
"integer literal",
|
||||
"float literal",
|
||||
"unicode literal",
|
||||
"(",
|
||||
")",
|
||||
"{",
|
||||
"}",
|
||||
"[",
|
||||
"]",
|
||||
",",
|
||||
"?",
|
||||
";",
|
||||
".",
|
||||
"...",
|
||||
":",
|
||||
"*",
|
||||
"/",
|
||||
"%",
|
||||
"<<",
|
||||
">>",
|
||||
"+",
|
||||
"-",
|
||||
"==",
|
||||
"<",
|
||||
">",
|
||||
"<=",
|
||||
">=",
|
||||
"!=",
|
||||
"&",
|
||||
"|",
|
||||
"^",
|
||||
"&&",
|
||||
"||",
|
||||
"+",
|
||||
"~",
|
||||
"!",
|
||||
"=",
|
||||
"/=",
|
||||
"*=",
|
||||
"%=",
|
||||
"-=",
|
||||
"+=",
|
||||
"&=",
|
||||
"|=",
|
||||
"^=",
|
||||
"<<=",
|
||||
">>=",
|
||||
};
|
||||
if (token_kind < 0 || token_kind >= LC_TokenKind_Count) {
|
||||
return "<invalid_token_operator>";
|
||||
}
|
||||
return strs[token_kind];
|
||||
}
|
||||
158
src/compiler/unicode.c
Normal file
158
src/compiler/unicode.c
Normal file
@@ -0,0 +1,158 @@
|
||||
LC_FUNCTION LC_UTF32Result LC_ConvertUTF16ToUTF32(uint16_t *c, int max_advance) {
|
||||
LC_UTF32Result result;
|
||||
LC_MemoryZero(&result, sizeof(result));
|
||||
if (max_advance >= 1) {
|
||||
result.advance = 1;
|
||||
result.out_str = c[0];
|
||||
if (c[0] >= 0xD800 && c[0] <= 0xDBFF && c[1] >= 0xDC00 && c[1] <= 0xDFFF) {
|
||||
if (max_advance >= 2) {
|
||||
result.out_str = 0x10000;
|
||||
result.out_str += (uint32_t)(c[0] & 0x03FF) << 10u | (c[1] & 0x03FF);
|
||||
result.advance = 2;
|
||||
} else
|
||||
result.error = 2;
|
||||
}
|
||||
} else {
|
||||
result.error = 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_UTF8Result LC_ConvertUTF32ToUTF8(uint32_t codepoint) {
|
||||
LC_UTF8Result result;
|
||||
LC_MemoryZero(&result, sizeof(result));
|
||||
|
||||
if (codepoint <= 0x7F) {
|
||||
result.len = 1;
|
||||
result.out_str[0] = (char)codepoint;
|
||||
} else if (codepoint <= 0x7FF) {
|
||||
result.len = 2;
|
||||
result.out_str[0] = 0xc0 | (0x1f & (codepoint >> 6));
|
||||
result.out_str[1] = 0x80 | (0x3f & codepoint);
|
||||
} else if (codepoint <= 0xFFFF) { // 16 bit word
|
||||
result.len = 3;
|
||||
result.out_str[0] = 0xe0 | (0xf & (codepoint >> 12)); // 4 bits
|
||||
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||
result.out_str[2] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||
} else if (codepoint <= 0x10FFFF) { // 21 bit word
|
||||
result.len = 4;
|
||||
result.out_str[0] = 0xf0 | (0x7 & (codepoint >> 18)); // 3 bits
|
||||
result.out_str[1] = 0x80 | (0x3f & (codepoint >> 12)); // 6 bits
|
||||
result.out_str[2] = 0x80 | (0x3f & (codepoint >> 6)); // 6 bits
|
||||
result.out_str[3] = 0x80 | (0x3f & codepoint); // 6 bits
|
||||
} else {
|
||||
result.error = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_UTF32Result LC_ConvertUTF8ToUTF32(char *c, int max_advance) {
|
||||
LC_UTF32Result result;
|
||||
LC_MemoryZero(&result, sizeof(result));
|
||||
|
||||
if ((c[0] & 0x80) == 0) { // Check if leftmost zero of first byte is unset
|
||||
if (max_advance >= 1) {
|
||||
result.out_str = c[0];
|
||||
result.advance = 1;
|
||||
} else result.error = 1;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xe0) == 0xc0) {
|
||||
if ((c[1] & 0xc0) == 0x80) { // Continuation byte required
|
||||
if (max_advance >= 2) {
|
||||
result.out_str = (uint32_t)(c[0] & 0x1f) << 6u | (c[1] & 0x3f);
|
||||
result.advance = 2;
|
||||
} else result.error = 2;
|
||||
} else result.error = 2;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf0) == 0xe0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80) { // Two continuation bytes required
|
||||
if (max_advance >= 3) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 12u | (uint32_t)(c[1] & 0x3f) << 6u | (c[2] & 0x3f);
|
||||
result.advance = 3;
|
||||
} else result.error = 3;
|
||||
} else result.error = 3;
|
||||
}
|
||||
|
||||
else if ((c[0] & 0xf8) == 0xf0) {
|
||||
if ((c[1] & 0xc0) == 0x80 && (c[2] & 0xc0) == 0x80 && (c[3] & 0xc0) == 0x80) { // Three continuation bytes required
|
||||
if (max_advance >= 4) {
|
||||
result.out_str = (uint32_t)(c[0] & 0xf) << 18u | (uint32_t)(c[1] & 0x3f) << 12u | (uint32_t)(c[2] & 0x3f) << 6u | (uint32_t)(c[3] & 0x3f);
|
||||
result.advance = 4;
|
||||
} else result.error = 4;
|
||||
} else result.error = 4;
|
||||
} else result.error = 4;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_UTF16Result LC_ConvertUTF32ToUTF16(uint32_t codepoint) {
|
||||
LC_UTF16Result result;
|
||||
LC_MemoryZero(&result, sizeof(result));
|
||||
if (codepoint < 0x10000) {
|
||||
result.out_str[0] = (uint16_t)codepoint;
|
||||
result.out_str[1] = 0;
|
||||
result.len = 1;
|
||||
} else if (codepoint <= 0x10FFFF) {
|
||||
uint32_t code = (codepoint - 0x10000);
|
||||
result.out_str[0] = (uint16_t)(0xD800 | (code >> 10));
|
||||
result.out_str[1] = (uint16_t)(0xDC00 | (code & 0x3FF));
|
||||
result.len = 2;
|
||||
} else {
|
||||
result.error = 1;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define LC__HANDLE_DECODE_ERROR(question_mark) \
|
||||
{ \
|
||||
if (outlen < buffer_size - 1) buffer[outlen++] = (question_mark); \
|
||||
break; \
|
||||
}
|
||||
|
||||
LC_FUNCTION int64_t LC_CreateCharFromWidechar(char *buffer, int64_t buffer_size, wchar_t *in, int64_t inlen) {
|
||||
int64_t outlen = 0;
|
||||
for (int64_t i = 0; i < inlen && in[i];) {
|
||||
LC_UTF32Result decode = LC_ConvertUTF16ToUTF32((uint16_t *)(in + i), (int)(inlen - i));
|
||||
if (!decode.error) {
|
||||
i += decode.advance;
|
||||
LC_UTF8Result encode = LC_ConvertUTF32ToUTF8(decode.out_str);
|
||||
if (!encode.error) {
|
||||
for (int64_t j = 0; j < encode.len; j++) {
|
||||
if (outlen < buffer_size - 1) {
|
||||
buffer[outlen++] = encode.out_str[j];
|
||||
}
|
||||
}
|
||||
} else LC__HANDLE_DECODE_ERROR('?');
|
||||
} else LC__HANDLE_DECODE_ERROR('?');
|
||||
}
|
||||
|
||||
buffer[outlen] = 0;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
LC_FUNCTION int64_t LC_CreateWidecharFromChar(wchar_t *buffer, int64_t buffer_size, char *in, int64_t inlen) {
|
||||
int64_t outlen = 0;
|
||||
for (int64_t i = 0; i < inlen;) {
|
||||
LC_UTF32Result decode = LC_ConvertUTF8ToUTF32(in + i, (int)(inlen - i));
|
||||
if (!decode.error) {
|
||||
i += decode.advance;
|
||||
LC_UTF16Result encode = LC_ConvertUTF32ToUTF16(decode.out_str);
|
||||
if (!encode.error) {
|
||||
for (int64_t j = 0; j < encode.len; j++) {
|
||||
if (outlen < buffer_size - 1) {
|
||||
buffer[outlen++] = encode.out_str[j];
|
||||
}
|
||||
}
|
||||
} else LC__HANDLE_DECODE_ERROR(0x003f);
|
||||
} else LC__HANDLE_DECODE_ERROR(0x003f);
|
||||
}
|
||||
|
||||
buffer[outlen] = 0;
|
||||
return outlen;
|
||||
}
|
||||
|
||||
#undef LC__HANDLE_DECODE_ERROR
|
||||
30
src/compiler/unix_arena.c
Normal file
30
src/compiler/unix_arena.c
Normal file
@@ -0,0 +1,30 @@
|
||||
#define LC_V_UNIX_PAGE_SIZE 4096
|
||||
|
||||
LC_FUNCTION LC_VMemory LC_VReserve(size_t size) {
|
||||
LC_VMemory result = {};
|
||||
size_t size_aligned = LC_AlignUp(size, LC_V_UNIX_PAGE_SIZE);
|
||||
result.data = (uint8_t *)mmap(0, size_aligned, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
LC_Assertf(result.data, "Failed to reserve memory using mmap!!");
|
||||
if (result.data) {
|
||||
result.reserve = size_aligned;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_VCommit(LC_VMemory *m, size_t commit) {
|
||||
uint8_t *pointer = LC_V_AdvanceCommit(m, &commit, LC_V_UNIX_PAGE_SIZE);
|
||||
if (pointer) {
|
||||
int mprotect_result = mprotect(pointer, commit, PROT_READ | PROT_WRITE);
|
||||
LC_Assertf(mprotect_result == 0, "Failed to commit more memory using mmap");
|
||||
if (mprotect_result == 0) {
|
||||
m->commit += commit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_VDeallocate(LC_VMemory *m) {
|
||||
int result = munmap(m->data, m->reserve);
|
||||
LC_Assertf(result == 0, "Failed to release virtual memory using munmap");
|
||||
}
|
||||
89
src/compiler/unix_filesystem.c
Normal file
89
src/compiler/unix_filesystem.c
Normal file
@@ -0,0 +1,89 @@
|
||||
LC_FUNCTION bool LC_EnableTerminalColors(void) {
|
||||
return true;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_IsDir(LC_Arena *arena, LC_String path) {
|
||||
bool result = false;
|
||||
LC_TempArena ch = LC_BeginTemp(arena);
|
||||
LC_String copy = LC_CopyString(arena, path);
|
||||
|
||||
struct stat s;
|
||||
if (stat(copy.str, &s) != 0) {
|
||||
result = false;
|
||||
} else {
|
||||
result = S_ISDIR(s.st_mode);
|
||||
}
|
||||
|
||||
LC_EndTemp(ch);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_GetAbsolutePath(LC_Arena *arena, LC_String relative) {
|
||||
LC_String copy = LC_CopyString(arena, relative);
|
||||
char *buffer = (char *)LC_PushSizeNonZeroed(arena, PATH_MAX);
|
||||
realpath((char *)copy.str, buffer);
|
||||
LC_String result = LC_MakeFromChar(buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_IsValid(LC_FileIter it) {
|
||||
return it.is_valid;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_Advance(LC_FileIter *it) {
|
||||
struct dirent *file = 0;
|
||||
while ((file = readdir((DIR *)it->dir)) != NULL) {
|
||||
if (file->d_name[0] == '.' && file->d_name[1] == '.' && file->d_name[2] == 0) continue;
|
||||
if (file->d_name[0] == '.' && file->d_name[1] == 0) continue;
|
||||
|
||||
it->is_directory = file->d_type == DT_DIR;
|
||||
it->filename = LC_CopyChar(it->arena, file->d_name);
|
||||
|
||||
const char *dir_char_ending = it->is_directory ? "/" : "";
|
||||
const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
|
||||
it->relative_path = LC_Format(it->arena, "%.*s%s%s%s", LC_Expand(it->path), separator, file->d_name, dir_char_ending);
|
||||
it->absolute_path = LC_GetAbsolutePath(it->arena, it->relative_path);
|
||||
if (it->is_directory) it->absolute_path = LC_Format(it->arena, "%.*s/", LC_Expand(it->absolute_path));
|
||||
it->is_valid = true;
|
||||
return;
|
||||
}
|
||||
it->is_valid = false;
|
||||
closedir((DIR *)it->dir);
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_FileIter LC_IterateFiles(LC_Arena *arena, LC_String path) {
|
||||
LC_FileIter it = {0};
|
||||
it.arena = arena;
|
||||
it.path = path = LC_CopyString(arena, path);
|
||||
it.dir = (void *)opendir((char *)path.str);
|
||||
if (!it.dir) return it;
|
||||
|
||||
LC_Advance(&it);
|
||||
return it;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_ReadFile(LC_Arena *arena, LC_String path) {
|
||||
LC_String result = LC_MakeEmptyString();
|
||||
|
||||
// ftell returns insane size if file is
|
||||
// a directory **on some machines** KEKW
|
||||
if (LC_IsDir(arena, path)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
path = LC_CopyString(arena, path);
|
||||
FILE *f = fopen(path.str, "rb");
|
||||
if (f) {
|
||||
fseek(f, 0, SEEK_END);
|
||||
result.len = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
result.str = (char *)LC_PushSizeNonZeroed(arena, result.len + 1);
|
||||
fread(result.str, result.len, 1, f);
|
||||
result.str[result.len] = 0;
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
328
src/compiler/value.c
Normal file
328
src/compiler/value.c
Normal file
@@ -0,0 +1,328 @@
|
||||
LC_Operand LC_OPNull;
|
||||
|
||||
LC_FUNCTION LC_Operand LC_OPError(void) {
|
||||
LC_Operand result = {LC_OPF_Error};
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_OPConstType(LC_Type *type) {
|
||||
LC_Operand result = {LC_OPF_UTConst | LC_OPF_Const};
|
||||
result.type = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_OPDecl(LC_Decl *decl) {
|
||||
LC_Operand result = {0};
|
||||
result.decl = decl;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_OPType(LC_Type *type) {
|
||||
LC_Operand result = {0};
|
||||
result.type = type;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_OPLValueAndType(LC_Type *type) {
|
||||
LC_Operand result = LC_OPType(type);
|
||||
result.flags = LC_OPF_LValue;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_ReportASTError(LC_AST *n, const char *str, ...) {
|
||||
LC_FORMAT(L->arena, str, s8);
|
||||
LC_SendErrorMessage(n ? n->pos : NULL, s8);
|
||||
L->errors += 1;
|
||||
return LC_OPError();
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_ReportASTErrorEx(LC_AST *n1, LC_AST *n2, const char *str, ...) {
|
||||
LC_FORMAT(L->arena, str, s8);
|
||||
LC_SendErrorMessage(n1->pos, s8);
|
||||
LC_SendErrorMessage(n2->pos, s8);
|
||||
L->errors += 1;
|
||||
return LC_OPError();
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_ConstCastFloat(LC_AST *pos, LC_Operand op) {
|
||||
LC_ASSERT(pos, LC_IsUTConst(op));
|
||||
LC_ASSERT(pos, LC_IsUntyped(op.type));
|
||||
if (LC_IsUTInt(op.type)) op.val.d = LC_Bigint_as_float(&op.val.i);
|
||||
if (LC_IsUTStr(op.type)) return LC_ReportASTError(pos, "Trying to convert '%s' to float", LC_GenLCType(op.type));
|
||||
op.type = L->tuntypedfloat;
|
||||
return op;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_ConstCastInt(LC_AST *pos, LC_Operand op) {
|
||||
LC_ASSERT(pos, LC_IsUTConst(op));
|
||||
LC_ASSERT(pos, LC_IsUntyped(op.type));
|
||||
if (LC_IsUTFloat(op.type)) {
|
||||
double v = op.val.d; // add rounding?
|
||||
LC_Bigint_init_signed(&op.val.i, (int64_t)v);
|
||||
}
|
||||
if (LC_IsUTStr(op.type)) return LC_ReportASTError(pos, "Trying to convert %s to int", LC_GenLCType(op.type));
|
||||
op.type = L->tuntypedint;
|
||||
return op;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_OPInt(int64_t v) {
|
||||
LC_Operand op = {0};
|
||||
op.type = L->tuntypedint;
|
||||
op.flags |= LC_OPF_UTConst | LC_OPF_Const;
|
||||
LC_Bigint_init_signed(&op.v.i, v);
|
||||
return op;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_OPIntT(LC_Type *type, int64_t v) {
|
||||
LC_ASSERT(NULL, LC_IsInt(type));
|
||||
LC_Operand op = LC_OPInt(v);
|
||||
op.type = type;
|
||||
return op;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_OPModDefaultUT(LC_Operand val) {
|
||||
if (LC_IsUntyped(val.type)) {
|
||||
val.type = val.type->tbase;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_OPModType(LC_Operand op, LC_Type *type) {
|
||||
if (LC_IsUTConst(op)) {
|
||||
if (LC_IsUTInt(op.type) && LC_IsFloat(type)) {
|
||||
op = LC_ConstCastFloat(NULL, op);
|
||||
}
|
||||
}
|
||||
op.type = type;
|
||||
return op;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_OPModBool(LC_Operand op) {
|
||||
op.type = L->tuntypedbool;
|
||||
return op;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_OPModBoolV(LC_Operand op, int v) {
|
||||
op.type = L->tuntypedbool;
|
||||
LC_Bigint_init_signed(&op.v.i, v);
|
||||
return op;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_EvalBinary(LC_AST *pos, LC_Operand a, LC_TokenKind op, LC_Operand b) {
|
||||
LC_ASSERT(pos, LC_IsUTConst(a));
|
||||
LC_ASSERT(pos, LC_IsUTConst(b));
|
||||
LC_ASSERT(pos, LC_IsUntyped(a.type));
|
||||
LC_ASSERT(pos, LC_IsUntyped(b.type));
|
||||
LC_ASSERT(pos, a.type->kind == b.type->kind);
|
||||
|
||||
LC_Operand c = LC_OPConstType(a.type);
|
||||
if (LC_IsUTStr(a.type)) {
|
||||
return LC_ReportASTError(pos, "invalid operand %s for binary expr of type untyped string", LC_TokenKindToString(op));
|
||||
}
|
||||
if (LC_IsUTFloat(a.type)) {
|
||||
switch (op) {
|
||||
case LC_TokenKind_Add: c.v.d = a.v.d + b.v.d; break;
|
||||
case LC_TokenKind_Sub: c.v.d = a.v.d - b.v.d; break;
|
||||
case LC_TokenKind_Mul: c.v.d = a.v.d * b.v.d; break;
|
||||
case LC_TokenKind_Div: {
|
||||
if (b.v.d == 0.0) return LC_ReportASTError(pos, "division by 0");
|
||||
c.v.d = a.v.d / b.v.d;
|
||||
} break;
|
||||
case LC_TokenKind_LesserThenEq: c = LC_OPModBoolV(c, a.v.d <= b.v.d); break;
|
||||
case LC_TokenKind_GreaterThenEq: c = LC_OPModBoolV(c, a.v.d >= b.v.d); break;
|
||||
case LC_TokenKind_GreaterThen: c = LC_OPModBoolV(c, a.v.d > b.v.d); break;
|
||||
case LC_TokenKind_LesserThen: c = LC_OPModBoolV(c, a.v.d < b.v.d); break;
|
||||
case LC_TokenKind_Equals: c = LC_OPModBoolV(c, a.v.d == b.v.d); break;
|
||||
case LC_TokenKind_NotEquals: c = LC_OPModBoolV(c, a.v.d != b.v.d); break;
|
||||
default: return LC_ReportASTError(pos, "invalid operand %s for binary expr of type untyped float", LC_TokenKindToString(op));
|
||||
}
|
||||
}
|
||||
if (LC_IsUTInt(a.type)) {
|
||||
switch (op) {
|
||||
case LC_TokenKind_BitXor: LC_Bigint_xor(&c.v.i, &a.v.i, &b.v.i); break;
|
||||
case LC_TokenKind_BitAnd: LC_Bigint_and(&c.v.i, &a.v.i, &b.v.i); break;
|
||||
case LC_TokenKind_BitOr: LC_Bigint_or(&c.v.i, &a.v.i, &b.v.i); break;
|
||||
case LC_TokenKind_Add: LC_Bigint_add(&c.v.i, &a.v.i, &b.v.i); break;
|
||||
case LC_TokenKind_Sub: LC_Bigint_sub(&c.v.i, &a.v.i, &b.v.i); break;
|
||||
case LC_TokenKind_Mul: LC_Bigint_mul(&c.v.i, &a.v.i, &b.v.i); break;
|
||||
case LC_TokenKind_Div: {
|
||||
if (b.v.i.digit_count == 0) return LC_ReportASTError(pos, "division by zero in constant expression");
|
||||
LC_Bigint_div_floor(&c.v.i, &a.v.i, &b.v.i);
|
||||
} break;
|
||||
case LC_TokenKind_Mod: {
|
||||
if (b.v.i.digit_count == 0) return LC_ReportASTError(pos, "modulo by zero in constant expression");
|
||||
LC_Bigint_mod(&c.v.i, &a.v.i, &b.v.i);
|
||||
} break;
|
||||
case LC_TokenKind_LeftShift: LC_Bigint_shl(&c.v.i, &a.v.i, &b.v.i); break;
|
||||
case LC_TokenKind_RightShift: LC_Bigint_shr(&c.v.i, &a.v.i, &b.v.i); break;
|
||||
case LC_TokenKind_And: {
|
||||
int left = LC_Bigint_cmp_zero(&a.v.i) != LC_CmpRes_EQ;
|
||||
int right = LC_Bigint_cmp_zero(&b.v.i) != LC_CmpRes_EQ;
|
||||
c = LC_OPModBoolV(c, left && right);
|
||||
} break;
|
||||
case LC_TokenKind_Or: {
|
||||
int left = LC_Bigint_cmp_zero(&a.v.i) != LC_CmpRes_EQ;
|
||||
int right = LC_Bigint_cmp_zero(&b.v.i) != LC_CmpRes_EQ;
|
||||
c = LC_OPModBoolV(c, left || right);
|
||||
} break;
|
||||
default: {
|
||||
LC_CmpRes cmp = LC_Bigint_cmp(&a.v.i, &b.v.i);
|
||||
switch (op) {
|
||||
case LC_TokenKind_LesserThenEq: c = LC_OPModBoolV(c, (cmp == LC_CmpRes_LT) || (cmp == LC_CmpRes_EQ)); break;
|
||||
case LC_TokenKind_GreaterThenEq: c = LC_OPModBoolV(c, (cmp == LC_CmpRes_GT) || (cmp == LC_CmpRes_EQ)); break;
|
||||
case LC_TokenKind_GreaterThen: c = LC_OPModBoolV(c, cmp == LC_CmpRes_GT); break;
|
||||
case LC_TokenKind_LesserThen: c = LC_OPModBoolV(c, cmp == LC_CmpRes_LT); break;
|
||||
case LC_TokenKind_Equals: c = LC_OPModBoolV(c, cmp == LC_CmpRes_EQ); break;
|
||||
case LC_TokenKind_NotEquals: c = LC_OPModBoolV(c, cmp != LC_CmpRes_EQ); break;
|
||||
default: return LC_ReportASTError(pos, "invalid operand %s for binary expr of type untyped int", LC_TokenKindToString(op));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_Operand LC_EvalUnary(LC_AST *pos, LC_TokenKind op, LC_Operand a) {
|
||||
LC_ASSERT(pos, LC_IsUTConst(a));
|
||||
LC_ASSERT(pos, LC_IsUntyped(a.type));
|
||||
LC_Operand c = a;
|
||||
|
||||
if (LC_IsUTStr(a.type)) {
|
||||
return LC_ReportASTError(pos, "invalid operand %s for unary expr of type untyped string", LC_TokenKindToString(op));
|
||||
}
|
||||
if (LC_IsUTFloat(a.type)) {
|
||||
switch (op) {
|
||||
case LC_TokenKind_Sub: c.v.d = -a.v.d; break;
|
||||
case LC_TokenKind_Add: c.v.d = +a.v.d; break;
|
||||
default: return LC_ReportASTError(pos, "invalid operand %s for unary expr of type untyped float", LC_TokenKindToString(op));
|
||||
}
|
||||
}
|
||||
if (LC_IsUTInt(a.type)) {
|
||||
switch (op) {
|
||||
case LC_TokenKind_Not: c = LC_OPModBoolV(c, LC_Bigint_cmp_zero(&a.v.i) == LC_CmpRes_EQ); break;
|
||||
case LC_TokenKind_Sub: LC_Bigint_negate(&c.v.i, &a.v.i); break;
|
||||
case LC_TokenKind_Add: c = a; break;
|
||||
case LC_TokenKind_Neg: LC_Bigint_not(&c.v.i, &a.v.i, a.type->tbase->size * 8, !a.type->tbase->is_unsigned); break;
|
||||
default: return LC_ReportASTError(pos, "invalid operand %s for unary expr of type untyped int", LC_TokenKindToString(op));
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_BigIntFits(LC_BigInt i, LC_Type *type) {
|
||||
LC_ASSERT(NULL, LC_IsInt(type));
|
||||
if (!LC_Bigint_fits_in_bits(&i, type->size * 8, !type->is_unsigned)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_OPResult LC_IsBinaryExprValidForType(LC_TokenKind op, LC_Type *type) {
|
||||
if (LC_IsFloat(type)) {
|
||||
switch (op) {
|
||||
case LC_TokenKind_Add: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_Sub: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_Mul: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_Div: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_LesserThenEq: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_GreaterThenEq: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_GreaterThen: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_LesserThen: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_Equals: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_NotEquals: return LC_OPResult_Bool; break;
|
||||
default: return LC_OPResult_Error;
|
||||
}
|
||||
}
|
||||
if (LC_IsInt(type)) {
|
||||
switch (op) {
|
||||
case LC_TokenKind_BitXor: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_BitAnd: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_BitOr: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_Add: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_Sub: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_Mul: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_Div: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_Mod: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_LeftShift: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_RightShift: return LC_OPResult_Ok; break;
|
||||
case LC_TokenKind_And: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_Or: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_LesserThenEq: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_GreaterThenEq: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_GreaterThen: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_LesserThen: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_Equals: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_NotEquals: return LC_OPResult_Bool; break;
|
||||
default: return LC_OPResult_Error;
|
||||
}
|
||||
}
|
||||
if (LC_IsPtrLike(type)) {
|
||||
switch (op) {
|
||||
case LC_TokenKind_And: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_Or: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_LesserThenEq: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_GreaterThenEq: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_GreaterThen: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_LesserThen: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_Equals: return LC_OPResult_Bool; break;
|
||||
case LC_TokenKind_NotEquals: return LC_OPResult_Bool; break;
|
||||
default: return LC_OPResult_Error;
|
||||
}
|
||||
}
|
||||
|
||||
return LC_OPResult_Error;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_OPResult LC_IsUnaryOpValidForType(LC_TokenKind op, LC_Type *type) {
|
||||
if (LC_IsFloat(type)) {
|
||||
if (op == LC_TokenKind_Sub) return LC_OPResult_Ok;
|
||||
if (op == LC_TokenKind_Add) return LC_OPResult_Ok;
|
||||
}
|
||||
if (LC_IsInt(type)) {
|
||||
if (op == LC_TokenKind_Not) return LC_OPResult_Bool;
|
||||
if (op == LC_TokenKind_Sub) return LC_OPResult_Ok;
|
||||
if (op == LC_TokenKind_Add) return LC_OPResult_Ok;
|
||||
if (op == LC_TokenKind_Neg) return LC_OPResult_Ok;
|
||||
}
|
||||
if (LC_IsPtrLike(type)) {
|
||||
if (op == LC_TokenKind_Not) return LC_OPResult_Bool;
|
||||
}
|
||||
return LC_OPResult_Error;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_OPResult LC_IsAssignValidForType(LC_TokenKind op, LC_Type *type) {
|
||||
if (op == LC_TokenKind_Assign) return LC_OPResult_Ok;
|
||||
if (LC_IsInt(type)) {
|
||||
switch (op) {
|
||||
case LC_TokenKind_DivAssign: return LC_OPResult_Ok;
|
||||
case LC_TokenKind_MulAssign: return LC_OPResult_Ok;
|
||||
case LC_TokenKind_ModAssign: return LC_OPResult_Ok;
|
||||
case LC_TokenKind_SubAssign: return LC_OPResult_Ok;
|
||||
case LC_TokenKind_AddAssign: return LC_OPResult_Ok;
|
||||
case LC_TokenKind_BitAndAssign: return LC_OPResult_Ok;
|
||||
case LC_TokenKind_BitOrAssign: return LC_OPResult_Ok;
|
||||
case LC_TokenKind_BitXorAssign: return LC_OPResult_Ok;
|
||||
case LC_TokenKind_LeftShiftAssign: return LC_OPResult_Ok;
|
||||
case LC_TokenKind_RightShiftAssign: return LC_OPResult_Ok;
|
||||
default: return LC_OPResult_Error;
|
||||
}
|
||||
}
|
||||
if (LC_IsFloat(type)) {
|
||||
switch (op) {
|
||||
case LC_TokenKind_DivAssign: return LC_OPResult_Ok;
|
||||
case LC_TokenKind_MulAssign: return LC_OPResult_Ok;
|
||||
case LC_TokenKind_SubAssign: return LC_OPResult_Ok;
|
||||
case LC_TokenKind_AddAssign: return LC_OPResult_Ok;
|
||||
default: return LC_OPResult_Error;
|
||||
}
|
||||
}
|
||||
return LC_OPResult_Error;
|
||||
}
|
||||
|
||||
LC_FUNCTION int LC_GetLevelsOfIndirection(LC_Type *type) {
|
||||
if (type->kind == LC_TypeKind_Pointer) return LC_GetLevelsOfIndirection(type->tbase) + 1;
|
||||
if (type->kind == LC_TypeKind_Array) return LC_GetLevelsOfIndirection(type->tbase) + 1;
|
||||
return 0;
|
||||
}
|
||||
44
src/compiler/win32_arena.c
Normal file
44
src/compiler/win32_arena.c
Normal file
@@ -0,0 +1,44 @@
|
||||
const size_t LC_V_WIN32_PAGE_SIZE = 4096;
|
||||
|
||||
LC_FUNCTION LC_VMemory LC_VReserve(size_t size) {
|
||||
LC_VMemory result;
|
||||
LC_MemoryZero(&result, sizeof(result));
|
||||
size_t adjusted_size = LC_AlignUp(size, LC_V_WIN32_PAGE_SIZE);
|
||||
result.data = (uint8_t *)VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE);
|
||||
LC_Assertf(result.data, "Failed to reserve virtual memory");
|
||||
result.reserve = adjusted_size;
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_VCommit(LC_VMemory *m, size_t commit) {
|
||||
uint8_t *pointer = LC_V_AdvanceCommit(m, &commit, LC_V_WIN32_PAGE_SIZE);
|
||||
if (pointer) {
|
||||
void *result = VirtualAlloc(pointer, commit, MEM_COMMIT, PAGE_READWRITE);
|
||||
LC_Assertf(result, "Failed to commit more memory");
|
||||
if (result) {
|
||||
m->commit += commit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_VDeallocate(LC_VMemory *m) {
|
||||
BOOL result = VirtualFree(m->data, 0, MEM_RELEASE);
|
||||
LC_Assertf(result != 0, "Failed to release LC_VMemory");
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_VDecommitPos(LC_VMemory *m, size_t pos) {
|
||||
size_t aligned = LC_AlignDown(pos, LC_V_WIN32_PAGE_SIZE);
|
||||
size_t adjusted_pos = LC_CLAMP_TOP(aligned, m->commit);
|
||||
size_t size_to_decommit = m->commit - adjusted_pos;
|
||||
if (size_to_decommit) {
|
||||
uint8_t *base_address = m->data + adjusted_pos;
|
||||
BOOL result = VirtualFree(base_address, size_to_decommit, MEM_DECOMMIT);
|
||||
if (result) {
|
||||
m->commit -= size_to_decommit;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
118
src/compiler/win32_filesystem.c
Normal file
118
src/compiler/win32_filesystem.c
Normal file
@@ -0,0 +1,118 @@
|
||||
typedef struct LC_Win32_FileIter {
|
||||
HANDLE handle;
|
||||
WIN32_FIND_DATAW data;
|
||||
} LC_Win32_FileIter;
|
||||
|
||||
LC_FUNCTION bool LC_IsDir(LC_Arena *arena, LC_String path) {
|
||||
wchar_t wbuff[1024];
|
||||
LC_CreateWidecharFromChar(wbuff, LC_StrLenof(wbuff), path.str, path.len);
|
||||
DWORD dwAttrib = GetFileAttributesW(wbuff);
|
||||
return dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY);
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_GetAbsolutePath(LC_Arena *arena, LC_String relative) {
|
||||
wchar_t wpath[1024];
|
||||
LC_CreateWidecharFromChar(wpath, LC_StrLenof(wpath), relative.str, relative.len);
|
||||
wchar_t wpath_abs[1024];
|
||||
DWORD written = GetFullPathNameW((wchar_t *)wpath, LC_StrLenof(wpath_abs), wpath_abs, 0);
|
||||
if (written == 0)
|
||||
return LC_MakeEmptyString();
|
||||
LC_String path = LC_FromWidecharEx(arena, wpath_abs, written);
|
||||
LC_NormalizePathUnsafe(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_IsValid(LC_FileIter it) {
|
||||
return it.is_valid;
|
||||
}
|
||||
|
||||
LC_FUNCTION void LC_Advance(LC_FileIter *it) {
|
||||
while (FindNextFileW(it->w32->handle, &it->w32->data) != 0) {
|
||||
WIN32_FIND_DATAW *data = &it->w32->data;
|
||||
|
||||
// Skip '.' and '..'
|
||||
if (data->cFileName[0] == '.' && data->cFileName[1] == '.' && data->cFileName[2] == 0) continue;
|
||||
if (data->cFileName[0] == '.' && data->cFileName[1] == 0) continue;
|
||||
|
||||
it->is_directory = data->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY;
|
||||
it->filename = LC_FromWidecharEx(it->arena, data->cFileName, LC_WideLength(data->cFileName));
|
||||
const char *is_dir = it->is_directory ? "/" : "";
|
||||
const char *separator = it->path.str[it->path.len - 1] == '/' ? "" : "/";
|
||||
it->relative_path = LC_Format(it->arena, "%.*s%s%.*s%s", LC_Expand(it->path), separator, LC_Expand(it->filename), is_dir);
|
||||
it->absolute_path = LC_GetAbsolutePath(it->arena, it->relative_path);
|
||||
it->is_valid = true;
|
||||
|
||||
if (it->is_directory) {
|
||||
LC_ASSERT(NULL, it->relative_path.str[it->relative_path.len - 1] == '/');
|
||||
LC_ASSERT(NULL, it->absolute_path.str[it->absolute_path.len - 1] == '/');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
it->is_valid = false;
|
||||
DWORD error = GetLastError();
|
||||
LC_ASSERT(NULL, error == ERROR_NO_MORE_FILES);
|
||||
FindClose(it->w32->handle);
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_FileIter LC_IterateFiles(LC_Arena *scratch_arena, LC_String path) {
|
||||
LC_FileIter it = {0};
|
||||
it.arena = scratch_arena;
|
||||
it.path = path;
|
||||
|
||||
LC_String modified_path = LC_Format(it.arena, "%.*s\\*", LC_Expand(path));
|
||||
wchar_t *wbuff = LC_PushArray(it.arena, wchar_t, modified_path.len + 1);
|
||||
int64_t wsize = LC_CreateWidecharFromChar(wbuff, modified_path.len + 1, modified_path.str, modified_path.len);
|
||||
LC_ASSERT(NULL, wsize);
|
||||
|
||||
it.w32 = LC_PushStruct(it.arena, LC_Win32_FileIter);
|
||||
it.w32->handle = FindFirstFileW(wbuff, &it.w32->data);
|
||||
if (it.w32->handle == INVALID_HANDLE_VALUE) {
|
||||
it.is_valid = false;
|
||||
return it;
|
||||
}
|
||||
|
||||
LC_ASSERT(NULL, it.w32->data.cFileName[0] == '.' && it.w32->data.cFileName[1] == 0);
|
||||
LC_Advance(&it);
|
||||
return it;
|
||||
}
|
||||
|
||||
LC_FUNCTION LC_String LC_ReadFile(LC_Arena *arena, LC_String path) {
|
||||
LC_String result = LC_MakeEmptyString();
|
||||
|
||||
wchar_t wpath[1024];
|
||||
LC_CreateWidecharFromChar(wpath, LC_StrLenof(wpath), path.str, path.len);
|
||||
HANDLE handle = CreateFileW(wpath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (handle != INVALID_HANDLE_VALUE) {
|
||||
LARGE_INTEGER file_size;
|
||||
if (GetFileSizeEx(handle, &file_size)) {
|
||||
if (file_size.QuadPart != 0) {
|
||||
result.len = (int64_t)file_size.QuadPart;
|
||||
result.str = (char *)LC_PushSizeNonZeroed(arena, result.len + 1);
|
||||
DWORD read;
|
||||
if (ReadFile(handle, result.str, (DWORD)result.len, &read, NULL)) { // @todo: can only read 32 byte size files?
|
||||
if (read == result.len) {
|
||||
result.str[result.len] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CloseHandle(handle);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
LC_FUNCTION bool LC_EnableTerminalColors(void) {
|
||||
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
if (hOut != INVALID_HANDLE_VALUE) {
|
||||
DWORD dwMode = 0;
|
||||
if (GetConsoleMode(hOut, &dwMode)) {
|
||||
dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
||||
if (SetConsoleMode(hOut, dwMode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user