mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-15 23:54:07 +00:00
Support import files as modules (i.e. import only once)
This commit is contained in:
41
build.bat
41
build.bat
@@ -1,6 +1,5 @@
|
||||
@echo off
|
||||
|
||||
set base_dir=W:\Odin
|
||||
:: Make sure this is a decent name and not generic
|
||||
set exe_name=odin.exe
|
||||
|
||||
@@ -23,38 +22,8 @@ set compiler_warnings= ^
|
||||
-wd4480 ^
|
||||
-wd4505 -wd4512 -wd4550
|
||||
|
||||
set compiler_includes=-I"C:\Program Files\LLVM\include"
|
||||
|
||||
set libs= kernel32.lib user32.lib gdi32.lib opengl32.lib ^
|
||||
-libpath:"C:\Program Files\LLVM\lib"
|
||||
|
||||
rem LLVMX86Disassembler.lib ^
|
||||
rem LLVMX86AsmParser.lib ^
|
||||
rem LLVMX86CodeGen.lib ^
|
||||
rem LLVMSelectionDAG.lib ^
|
||||
rem LLVMAsmPrinter.lib ^
|
||||
rem LLVMCodeGen.lib ^
|
||||
rem LLVMTarget.lib ^
|
||||
rem LLVMScalarOpts.lib ^
|
||||
rem LLVMInstCombine.lib ^
|
||||
rem LLVMInstrumentation.lib ^
|
||||
rem LLVMProfileData.lib ^
|
||||
rem LLVMTransformUtils.lib ^
|
||||
rem LLVMBitWriter.lib ^
|
||||
rem LLVMAnalysis.lib ^
|
||||
rem LLVMX86Desc.lib ^
|
||||
rem LLVMObject.lib ^
|
||||
rem LLVMMCParser.lib ^
|
||||
rem LLVMBitReader.lib ^
|
||||
rem LLVMMCDisassembler.lib ^
|
||||
rem LLVMX86Info.lib ^
|
||||
rem LLVMX86AsmPrinter.lib ^
|
||||
rem LLVMMC.lib ^
|
||||
rem LLVMX86Utils.lib ^
|
||||
rem LLVMCore.lib ^
|
||||
rem LLVMSupport.lib
|
||||
|
||||
|
||||
set compiler_includes=
|
||||
set libs= kernel32.lib user32.lib gdi32.lib opengl32.lib
|
||||
|
||||
set linker_flags= -incremental:no -opt:ref -subsystem:console
|
||||
|
||||
@@ -65,7 +34,7 @@ set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
|
||||
set linker_settings=%libs% %linker_flags%
|
||||
|
||||
|
||||
set build_dir= "%base_dir%\bin\"
|
||||
set build_dir= "bin\"
|
||||
if not exist %build_dir% mkdir %build_dir%
|
||||
pushd %build_dir%
|
||||
del *.pdb > NUL 2> NUL
|
||||
@@ -74,9 +43,9 @@ pushd %build_dir%
|
||||
del ..\misc\*.pdb > NUL 2> NUL
|
||||
del ..\misc\*.ilk > NUL 2> NUL
|
||||
|
||||
cl %compiler_settings% "%base_dir%\src\main.cpp" ^
|
||||
cl %compiler_settings% "..\src\main.cpp" ^
|
||||
/link %linker_settings% -OUT:%exe_name% ^
|
||||
&& call run.bat
|
||||
&& call ..\run.bat
|
||||
|
||||
:do_not_compile_exe
|
||||
popd
|
||||
|
||||
3
examples/other.odin
Normal file
3
examples/other.odin
Normal file
@@ -0,0 +1,3 @@
|
||||
add :: proc(a, b: int) -> int {
|
||||
return a + b;
|
||||
}
|
||||
11
examples/test.odin
Normal file
11
examples/test.odin
Normal file
@@ -0,0 +1,11 @@
|
||||
import "other"
|
||||
|
||||
TAU :: 6.28;
|
||||
PI :: PI/2;
|
||||
|
||||
main :: proc() {
|
||||
x : int = 2;
|
||||
x = x * 3;
|
||||
|
||||
y := add(1, x);
|
||||
}
|
||||
12
run.bat
Normal file
12
run.bat
Normal file
@@ -0,0 +1,12 @@
|
||||
@echo off
|
||||
|
||||
|
||||
rem del "..\examples\test.bc"
|
||||
call ..\bin\odin.exe ..\examples/test.odin
|
||||
rem clang -S -emit-llvm ..\examples/test.c -o ..\examples/test.ll
|
||||
call llvm-as < ..\examples/test.ll
|
||||
rem call lli ..\examples/test.ll
|
||||
|
||||
rem call lli ..\examples/test.bc rem JIT
|
||||
rem llc ..\examples/test.bc -march=x86-64 -o ..\examples/test.exe
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "value.cpp"
|
||||
#include "../exact_value.cpp"
|
||||
#include "entity.cpp"
|
||||
#include "type.cpp"
|
||||
|
||||
@@ -18,7 +18,7 @@ enum AddressingMode {
|
||||
struct Operand {
|
||||
AddressingMode mode;
|
||||
Type *type;
|
||||
Value value;
|
||||
ExactValue value;
|
||||
|
||||
AstNode *expression;
|
||||
BuiltinProcedureId builtin_id;
|
||||
@@ -27,7 +27,7 @@ struct Operand {
|
||||
struct TypeAndValue {
|
||||
AddressingMode mode;
|
||||
Type *type;
|
||||
Value value;
|
||||
ExactValue value;
|
||||
};
|
||||
|
||||
struct DeclarationInfo {
|
||||
@@ -44,10 +44,15 @@ struct DeclarationInfo {
|
||||
i32 mark;
|
||||
};
|
||||
|
||||
DeclarationInfo *make_declaration_info(gbAllocator a, Scope *scope) {
|
||||
DeclarationInfo *d = gb_alloc_item(a, DeclarationInfo);
|
||||
|
||||
void init_declaration_info(DeclarationInfo *d, Scope *scope) {
|
||||
d->scope = scope;
|
||||
map_init(&d->deps, gb_heap_allocator());
|
||||
}
|
||||
|
||||
DeclarationInfo *make_declaration_info(gbAllocator a, Scope *scope) {
|
||||
DeclarationInfo *d = gb_alloc_item(a, DeclarationInfo);
|
||||
init_declaration_info(d, scope);
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -71,10 +76,10 @@ struct ExpressionInfo {
|
||||
b32 is_lhs; // Debug info
|
||||
AddressingMode mode;
|
||||
Type *type; // Type_Basic
|
||||
Value value;
|
||||
ExactValue value;
|
||||
};
|
||||
|
||||
ExpressionInfo make_expression_info(b32 is_lhs, AddressingMode mode, Type *type, Value value) {
|
||||
ExpressionInfo make_expression_info(b32 is_lhs, AddressingMode mode, Type *type, ExactValue value) {
|
||||
ExpressionInfo ei = {};
|
||||
ei.is_lhs = is_lhs;
|
||||
ei.mode = mode;
|
||||
@@ -84,6 +89,7 @@ ExpressionInfo make_expression_info(b32 is_lhs, AddressingMode mode, Type *type,
|
||||
}
|
||||
|
||||
struct ProcedureInfo {
|
||||
AstFile *file;
|
||||
Token token;
|
||||
DeclarationInfo *decl;
|
||||
Type * type; // Type_Procedure
|
||||
@@ -155,8 +161,9 @@ struct Checker {
|
||||
Map<ExpressionInfo> untyped; // Key: AstNode * | Expression -> ExpressionInfo
|
||||
Map<DeclarationInfo *> entities; // Key: Entity *
|
||||
|
||||
AstFile * curr_ast_file;
|
||||
BaseTypeSizes sizes;
|
||||
Scope * file_scope;
|
||||
Scope * global_scope;
|
||||
gbArray(ProcedureInfo) procedures; // NOTE(bill): Procedures to check
|
||||
|
||||
gbArena arena;
|
||||
@@ -168,21 +175,20 @@ struct Checker {
|
||||
gbArray(Type *) procedure_stack;
|
||||
b32 in_defer; // TODO(bill): Actually handle correctly
|
||||
|
||||
#define MAX_CHECKER_ERROR_COUNT 10
|
||||
isize error_prev_line;
|
||||
isize error_prev_column;
|
||||
isize error_count;
|
||||
};
|
||||
|
||||
|
||||
gb_global Scope *global_scope = NULL;
|
||||
gb_global Scope *universal_scope = NULL;
|
||||
|
||||
|
||||
Scope *make_scope(Scope *parent, gbAllocator allocator) {
|
||||
Scope *s = gb_alloc_item(allocator, Scope);
|
||||
s->parent = parent;
|
||||
map_init(&s->elements, gb_heap_allocator());
|
||||
if (parent != NULL && parent != global_scope) {
|
||||
if (parent != NULL && parent != universal_scope) {
|
||||
DLIST_APPEND(parent->first_child, parent->last_child, s);
|
||||
}
|
||||
return s;
|
||||
@@ -258,12 +264,12 @@ void add_global_entity(Entity *entity) {
|
||||
if (gb_memchr(name.text, ' ', name.len)) {
|
||||
return; // NOTE(bill): `untyped thing`
|
||||
}
|
||||
if (scope_insert_entity(global_scope, entity)) {
|
||||
if (scope_insert_entity(universal_scope, entity)) {
|
||||
GB_PANIC("Compiler error: double declaration");
|
||||
}
|
||||
}
|
||||
|
||||
void add_global_constant(gbAllocator a, String name, Type *type, Value value) {
|
||||
void add_global_constant(gbAllocator a, String name, Type *type, ExactValue value) {
|
||||
Token token = {Token_Identifier};
|
||||
token.string = name;
|
||||
Entity *entity = alloc_entity(a, Entity_Constant, NULL, token, type);
|
||||
@@ -271,10 +277,10 @@ void add_global_constant(gbAllocator a, String name, Type *type, Value value) {
|
||||
add_global_entity(entity);
|
||||
}
|
||||
|
||||
void init_global_scope(void) {
|
||||
void init_universal_scope(void) {
|
||||
// NOTE(bill): No need to free these
|
||||
gbAllocator a = gb_heap_allocator();
|
||||
global_scope = make_scope(NULL, a);
|
||||
universal_scope = make_scope(NULL, a);
|
||||
|
||||
// Types
|
||||
for (isize i = 0; i < gb_count_of(basic_types); i++) {
|
||||
@@ -289,9 +295,9 @@ void init_global_scope(void) {
|
||||
}
|
||||
|
||||
// Constants
|
||||
add_global_constant(a, make_string("true"), &basic_types[Basic_UntypedBool], make_value_bool(true));
|
||||
add_global_constant(a, make_string("false"), &basic_types[Basic_UntypedBool], make_value_bool(false));
|
||||
add_global_constant(a, make_string("null"), &basic_types[Basic_UntypedPointer], make_value_pointer(NULL));
|
||||
add_global_constant(a, make_string("true"), &basic_types[Basic_UntypedBool], make_exact_value_bool(true));
|
||||
add_global_constant(a, make_string("false"), &basic_types[Basic_UntypedBool], make_exact_value_bool(false));
|
||||
add_global_constant(a, make_string("null"), &basic_types[Basic_UntypedPointer], make_exact_value_pointer(NULL));
|
||||
|
||||
// Builtin Procedures
|
||||
for (isize i = 0; i < gb_count_of(builtin_procedures); i++) {
|
||||
@@ -329,12 +335,17 @@ void init_checker(Checker *c, Parser *parser) {
|
||||
|
||||
// NOTE(bill): Is this big enough or too small?
|
||||
isize item_size = gb_max(gb_max(gb_size_of(Entity), gb_size_of(Type)), gb_size_of(Scope));
|
||||
isize arena_size = 2 * item_size * gb_array_count(c->parser->tokens);
|
||||
isize total_token_count = 0;
|
||||
for (isize i = 0; i < gb_array_count(c->parser->files); i++) {
|
||||
AstFile *f = &c->parser->files[i];
|
||||
total_token_count += gb_array_count(f->tokens);
|
||||
}
|
||||
isize arena_size = 2 * item_size * total_token_count;
|
||||
gb_arena_init_from_allocator(&c->arena, a, arena_size);
|
||||
c->allocator = gb_arena_allocator(&c->arena);
|
||||
|
||||
c->file_scope = make_scope(global_scope, c->allocator);
|
||||
c->curr_scope = c->file_scope;
|
||||
c->global_scope = make_scope(universal_scope, c->allocator);
|
||||
c->curr_scope = c->global_scope;
|
||||
}
|
||||
|
||||
void destroy_checker(Checker *c) {
|
||||
@@ -344,7 +355,7 @@ void destroy_checker(Checker *c) {
|
||||
map_destroy(&c->scopes);
|
||||
map_destroy(&c->untyped);
|
||||
map_destroy(&c->entities);
|
||||
destroy_scope(c->file_scope);
|
||||
destroy_scope(c->global_scope);
|
||||
gb_array_free(c->procedure_stack);
|
||||
gb_array_free(c->procedures);
|
||||
|
||||
@@ -353,8 +364,8 @@ void destroy_checker(Checker *c) {
|
||||
|
||||
|
||||
|
||||
#define print_checker_error(p, token, fmt, ...) print_checker_error_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__)
|
||||
void print_checker_error_(Checker *c, char *function, Token token, char *fmt, ...) {
|
||||
#define checker_err(p, token, fmt, ...) checker_err_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__)
|
||||
void checker_err_(Checker *c, char *function, Token token, char *fmt, ...) {
|
||||
|
||||
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
@@ -368,20 +379,20 @@ void print_checker_error_(Checker *c, char *function, Token token, char *fmt, ..
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%s(%td:%td) %s\n",
|
||||
c->parser->tokenizer.fullpath, token.line, token.column,
|
||||
gb_printf_err("%.*s(%td:%td) %s\n",
|
||||
LIT(c->curr_ast_file->tokenizer.fullpath), token.line, token.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
|
||||
}
|
||||
c->error_count++;
|
||||
// NOTE(bill): If there are too many errors, just quit
|
||||
if (c->error_count > MAX_CHECKER_ERROR_COUNT) {
|
||||
gb_exit(1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TypeAndValue *type_and_value_of_expression(Checker *c, AstNode *expression) {
|
||||
TypeAndValue *found = map_get(&c->types, hash_pointer(expression));
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
Entity *entity_of_identifier(Checker *c, AstNode *identifier) {
|
||||
@@ -397,7 +408,7 @@ Entity *entity_of_identifier(Checker *c, AstNode *identifier) {
|
||||
}
|
||||
|
||||
Type *type_of_expression(Checker *c, AstNode *expression) {
|
||||
TypeAndValue *found = map_get(&c->types, hash_pointer(expression));
|
||||
TypeAndValue *found = type_and_value_of_expression(c, expression);
|
||||
if (found)
|
||||
return found->type;
|
||||
if (expression->kind == AstNode_Identifier) {
|
||||
@@ -410,19 +421,19 @@ Type *type_of_expression(Checker *c, AstNode *expression) {
|
||||
}
|
||||
|
||||
|
||||
void add_untyped(Checker *c, AstNode *expression, b32 lhs, AddressingMode mode, Type *basic_type, Value value) {
|
||||
void add_untyped(Checker *c, AstNode *expression, b32 lhs, AddressingMode mode, Type *basic_type, ExactValue value) {
|
||||
map_set(&c->untyped, hash_pointer(expression), make_expression_info(lhs, mode, basic_type, value));
|
||||
}
|
||||
|
||||
|
||||
void add_type_and_value(Checker *c, AstNode *expression, AddressingMode mode, Type *type, Value value) {
|
||||
void add_type_and_value(Checker *c, AstNode *expression, AddressingMode mode, Type *type, ExactValue value) {
|
||||
GB_ASSERT(expression != NULL);
|
||||
GB_ASSERT(type != NULL);
|
||||
if (mode == Addressing_Invalid)
|
||||
return;
|
||||
|
||||
if (mode == Addressing_Constant) {
|
||||
GB_ASSERT(value.kind != Value_Invalid);
|
||||
GB_ASSERT(value.kind != ExactValue_Invalid);
|
||||
GB_ASSERT(type == &basic_types[Basic_Invalid] || is_type_constant_type(type));
|
||||
}
|
||||
|
||||
@@ -443,7 +454,7 @@ void add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
|
||||
if (!are_strings_equal(entity->token.string, make_string("_"))) {
|
||||
Entity *insert_entity = scope_insert_entity(scope, entity);
|
||||
if (insert_entity) {
|
||||
print_checker_error(c, entity->token, "Redeclared entity in this scope: %.*s", LIT(entity->token.string));
|
||||
checker_err(c, entity->token, "Redeclared entity in this scope: %.*s", LIT(entity->token.string));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -463,14 +474,15 @@ void add_file_entity(Checker *c, AstNode *identifier, Entity *e, DeclarationInfo
|
||||
GB_ASSERT(are_strings_equal(identifier->identifier.token.string, e->token.string));
|
||||
|
||||
|
||||
add_entity(c, c->file_scope, identifier, e);
|
||||
add_entity(c, c->global_scope, identifier, e);
|
||||
map_set(&c->entities, hash_pointer(e), d);
|
||||
e->order = gb_array_count(c->entities.entries);
|
||||
}
|
||||
|
||||
|
||||
void check_procedure_later(Checker *c, Token token, DeclarationInfo *decl, Type *type, AstNode *body) {
|
||||
void check_procedure_later(Checker *c, AstFile *file, Token token, DeclarationInfo *decl, Type *type, AstNode *body) {
|
||||
ProcedureInfo info = {};
|
||||
info.file = file;
|
||||
info.token = token;
|
||||
info.decl = decl;
|
||||
info.type = type;
|
||||
@@ -506,6 +518,12 @@ void pop_procedure(Checker *c) {
|
||||
}
|
||||
|
||||
|
||||
void add_curr_ast_file(Checker *c, AstFile *file) {
|
||||
c->error_prev_line = 0;
|
||||
c->error_prev_column = 0;
|
||||
c->curr_ast_file = file;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -523,162 +541,115 @@ GB_COMPARE_PROC(entity_order_cmp) {
|
||||
return p->order < q->order ? -1 : p->order > q->order;
|
||||
}
|
||||
|
||||
void check_file(Checker *c, AstNode *file_node) {
|
||||
void check_parsed_files(Checker *c) {
|
||||
// Collect Entities
|
||||
for (isize i = 0; i < gb_array_count(c->parser->files); i++) {
|
||||
AstFile *f = &c->parser->files[i];
|
||||
add_curr_ast_file(c, f);
|
||||
for (AstNode *decl = f->declarations; decl != NULL; decl = decl->next) {
|
||||
if (!is_ast_node_declaration(decl))
|
||||
continue;
|
||||
|
||||
// Collect Entities
|
||||
for (AstNode *decl = file_node; decl != NULL; decl = decl->next) {
|
||||
if (!is_ast_node_declaration(decl))
|
||||
continue;
|
||||
switch (decl->kind) {
|
||||
case AstNode_BadDeclaration:
|
||||
break;
|
||||
|
||||
switch (decl->kind) {
|
||||
case AstNode_BadDeclaration:
|
||||
break;
|
||||
case AstNode_VariableDeclaration: {
|
||||
auto *vd = &decl->variable_declaration;
|
||||
|
||||
case AstNode_VariableDeclaration: {
|
||||
auto *vd = &decl->variable_declaration;
|
||||
switch (vd->kind) {
|
||||
case Declaration_Immutable: {
|
||||
for (AstNode *name = vd->name_list, *value = vd->value_list;
|
||||
name != NULL && value != NULL;
|
||||
name = name->next, value = value->next) {
|
||||
GB_ASSERT(name->kind == AstNode_Identifier);
|
||||
ExactValue v = {ExactValue_Invalid};
|
||||
Entity *e = make_entity_constant(c->allocator, c->curr_scope, name->identifier.token, NULL, v);
|
||||
DeclarationInfo *di = make_declaration_info(c->allocator, c->global_scope);
|
||||
di->type_expr = vd->type_expression;
|
||||
di->init_expr = value;
|
||||
add_file_entity(c, name, e, di);
|
||||
}
|
||||
|
||||
switch (vd->kind) {
|
||||
case Declaration_Immutable: {
|
||||
for (AstNode *name = vd->name_list, *value = vd->value_list;
|
||||
name != NULL && value != NULL;
|
||||
name = name->next, value = value->next) {
|
||||
GB_ASSERT(name->kind == AstNode_Identifier);
|
||||
Value v = {Value_Invalid};
|
||||
Entity *e = make_entity_constant(c->allocator, c->curr_scope, name->identifier.token, NULL, v);
|
||||
DeclarationInfo *di = make_declaration_info(c->allocator, c->file_scope);
|
||||
di->type_expr = vd->type_expression;
|
||||
di->init_expr = value;
|
||||
add_file_entity(c, name, e, di);
|
||||
isize lhs_count = vd->name_list_count;
|
||||
isize rhs_count = vd->value_list_count;
|
||||
|
||||
if (rhs_count == 0 && vd->type_expression == NULL) {
|
||||
checker_err(c, ast_node_token(decl), "Missing type or initial expression");
|
||||
} else if (lhs_count < rhs_count) {
|
||||
checker_err(c, ast_node_token(decl), "Extra initial expression");
|
||||
}
|
||||
} break;
|
||||
|
||||
case Declaration_Mutable: {
|
||||
isize entity_count = vd->name_list_count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
DeclarationInfo *di = NULL;
|
||||
if (vd->value_list_count == 1) {
|
||||
di = make_declaration_info(gb_heap_allocator(), c->global_scope);
|
||||
di->entities = entities;
|
||||
di->entity_count = entity_count;
|
||||
di->type_expr = vd->type_expression;
|
||||
di->init_expr = vd->value_list;
|
||||
}
|
||||
|
||||
AstNode *value = vd->value_list;
|
||||
for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
|
||||
Entity *e = make_entity_variable(c->allocator, c->global_scope, name->identifier.token, NULL);
|
||||
entities[entity_index++] = e;
|
||||
|
||||
DeclarationInfo *d = di;
|
||||
if (d == NULL) {
|
||||
AstNode *init_expr = value;
|
||||
d = make_declaration_info(gb_heap_allocator(), c->global_scope);
|
||||
d->type_expr = vd->type_expression;
|
||||
d->init_expr = init_expr;
|
||||
}
|
||||
|
||||
add_file_entity(c, name, e, d);
|
||||
|
||||
if (value != NULL)
|
||||
value = value->next;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
isize lhs_count = vd->name_list_count;
|
||||
isize rhs_count = vd->value_list_count;
|
||||
|
||||
if (rhs_count == 0 && vd->type_expression == NULL) {
|
||||
print_checker_error(c, ast_node_token(decl), "Missing type or initial expression");
|
||||
} else if (lhs_count < rhs_count) {
|
||||
print_checker_error(c, ast_node_token(decl), "Extra initial expression");
|
||||
}
|
||||
} break;
|
||||
|
||||
case Declaration_Mutable: {
|
||||
isize entity_count = vd->name_list_count;
|
||||
isize entity_index = 0;
|
||||
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
|
||||
DeclarationInfo *di = NULL;
|
||||
if (vd->value_list_count == 1) {
|
||||
di = make_declaration_info(gb_heap_allocator(), c->file_scope);
|
||||
di->entities = entities;
|
||||
di->entity_count = entity_count;
|
||||
di->type_expr = vd->type_expression;
|
||||
di->init_expr = vd->value_list;
|
||||
}
|
||||
|
||||
AstNode *value = vd->value_list;
|
||||
for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
|
||||
Entity *e = make_entity_variable(c->allocator, c->file_scope, name->identifier.token, NULL);
|
||||
entities[entity_index++] = e;
|
||||
|
||||
DeclarationInfo *d = di;
|
||||
if (d == NULL) {
|
||||
AstNode *init_expr = value;
|
||||
d = make_declaration_info(gb_heap_allocator(), c->file_scope);
|
||||
d->type_expr = vd->type_expression;
|
||||
d->init_expr = init_expr;
|
||||
}
|
||||
|
||||
add_file_entity(c, name, e, d);
|
||||
|
||||
if (value != NULL)
|
||||
value = value->next;
|
||||
}
|
||||
|
||||
#if 0
|
||||
for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
|
||||
Entity *entity = NULL;
|
||||
Token token = name->identifier.token;
|
||||
if (name->kind == AstNode_Identifier) {
|
||||
String str = token.string;
|
||||
Entity *found = NULL;
|
||||
// NOTE(bill): Ignore assignments to `_`
|
||||
b32 can_be_ignored = are_strings_equal(str, make_string("_"));
|
||||
if (!can_be_ignored) {
|
||||
found = current_scope_lookup_entity(c->file_scope, str);
|
||||
}
|
||||
if (found == NULL) {
|
||||
entity = make_entity_variable(c->allocator, c->file_scope, token, NULL);
|
||||
if (!can_be_ignored) {
|
||||
new_entities[new_entity_count++] = entity;
|
||||
}
|
||||
add_entity_definition(c, name, entity);
|
||||
} else {
|
||||
entity = found;
|
||||
}
|
||||
} else {
|
||||
print_checker_error(c, token, "A variable declaration must be an identifier");
|
||||
}
|
||||
if (entity == NULL)
|
||||
entity = make_entity_dummy_variable(c, token);
|
||||
entities[entity_index++] = entity;
|
||||
}
|
||||
|
||||
Type *init_type = NULL;
|
||||
if (vd->type_expression) {
|
||||
init_type = check_type(c, vd->type_expression, NULL);
|
||||
if (init_type == NULL)
|
||||
init_type = &basic_types[Basic_Invalid];
|
||||
}
|
||||
|
||||
for (isize i = 0; i < entity_count; i++) {
|
||||
Entity *e = entities[i];
|
||||
GB_ASSERT(e != NULL);
|
||||
if (e->variable.visited) {
|
||||
e->type = &basic_types[Basic_Invalid];
|
||||
continue;
|
||||
}
|
||||
e->variable.visited = true;
|
||||
|
||||
if (e->type == NULL)
|
||||
e->type = init_type;
|
||||
}
|
||||
|
||||
|
||||
check_init_variables(c, entities, entity_count, vd->value_list, vd->value_list_count, make_string("variable declaration"));
|
||||
#endif
|
||||
case AstNode_TypeDeclaration: {
|
||||
AstNode *identifier = decl->type_declaration.name;
|
||||
Entity *e = make_entity_type_name(c->allocator, c->global_scope, identifier->identifier.token, NULL);
|
||||
DeclarationInfo *d = make_declaration_info(c->allocator, e->parent);
|
||||
d->type_expr = decl->type_declaration.type_expression;
|
||||
add_file_entity(c, identifier, e, d);
|
||||
} break;
|
||||
|
||||
case AstNode_ProcedureDeclaration: {
|
||||
AstNode *identifier = decl->procedure_declaration.name;
|
||||
Token token = identifier->identifier.token;
|
||||
Entity *e = make_entity_procedure(c->allocator, c->global_scope, token, NULL);
|
||||
add_entity(c, c->global_scope, identifier, e);
|
||||
DeclarationInfo *d = make_declaration_info(c->allocator, e->parent);
|
||||
d->proc_decl = decl;
|
||||
map_set(&c->entities, hash_pointer(e), d);
|
||||
e->order = gb_array_count(c->entities.entries);
|
||||
|
||||
} break;
|
||||
|
||||
case AstNode_ImportDeclaration:
|
||||
// NOTE(bill): ignore
|
||||
break;
|
||||
|
||||
default:
|
||||
checker_err(c, ast_node_token(decl), "Only declarations are allowed at file scope");
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
} break;
|
||||
|
||||
case AstNode_TypeDeclaration: {
|
||||
AstNode *identifier = decl->type_declaration.name;
|
||||
Entity *e = make_entity_type_name(c->allocator, c->file_scope, identifier->identifier.token, NULL);
|
||||
DeclarationInfo *d = make_declaration_info(c->allocator, e->parent);
|
||||
d->type_expr = decl->type_declaration.type_expression;
|
||||
add_file_entity(c, identifier, e, d);
|
||||
} break;
|
||||
|
||||
case AstNode_ProcedureDeclaration: {
|
||||
AstNode *identifier = decl->procedure_declaration.name;
|
||||
Token token = identifier->identifier.token;
|
||||
Entity *e = make_entity_procedure(c->allocator, c->file_scope, token, NULL);
|
||||
add_entity(c, c->file_scope, identifier, e);
|
||||
DeclarationInfo *d = make_declaration_info(c->allocator, e->parent);
|
||||
d->proc_decl = decl;
|
||||
map_set(&c->entities, hash_pointer(e), d);
|
||||
e->order = gb_array_count(c->entities.entries);
|
||||
|
||||
} break;
|
||||
|
||||
default:
|
||||
print_checker_error(c, ast_node_token(decl), "Only declarations are allowed at file scope");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Order entities
|
||||
{
|
||||
{ // Order entities
|
||||
gbArray(Entity *) entities;
|
||||
isize count = gb_array_count(c->entities.entries);
|
||||
gb_array_init_reserve(entities, gb_heap_allocator(), count);
|
||||
@@ -698,10 +669,27 @@ void check_file(Checker *c, AstNode *file_node) {
|
||||
}
|
||||
|
||||
|
||||
// Check procedure bodies
|
||||
for (isize i = 0; i < gb_array_count(c->procedures); i++) {
|
||||
ProcedureInfo *pi = &c->procedures[i];
|
||||
add_curr_ast_file(c, pi->file);
|
||||
check_procedure_body(c, pi->token, pi->decl, pi->type, pi->body);
|
||||
}
|
||||
|
||||
|
||||
{ // Add untyped expression values
|
||||
isize count = gb_array_count(c->untyped.entries);
|
||||
for (isize i = 0; i < count; i++) {
|
||||
auto *entry = c->untyped.entries + i;
|
||||
u64 key = entry->key;
|
||||
AstNode *expr = cast(AstNode *)cast(uintptr)key;
|
||||
ExpressionInfo *info = &entry->value;
|
||||
if (is_type_typed(info->type)) {
|
||||
GB_PANIC("%s (type %s) is typed!", expression_to_string(expr), info->type);
|
||||
}
|
||||
add_type_and_value(c, expr, info->mode, info->type, info->value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ struct Entity {
|
||||
isize order;
|
||||
|
||||
union {
|
||||
struct { Value value; } constant;
|
||||
struct { ExactValue value; } constant;
|
||||
struct {
|
||||
b8 visited;
|
||||
b8 is_field;
|
||||
@@ -59,7 +59,7 @@ Entity *make_entity_variable(gbAllocator a, Scope *parent, Token token, Type *ty
|
||||
return entity;
|
||||
}
|
||||
|
||||
Entity *make_entity_constant(gbAllocator a, Scope *parent, Token token, Type *type, Value value) {
|
||||
Entity *make_entity_constant(gbAllocator a, Scope *parent, Token token, Type *type, ExactValue value) {
|
||||
Entity *entity = alloc_entity(a, Entity_Constant, parent, token, type);
|
||||
entity->constant.value = value;
|
||||
return entity;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -131,16 +131,19 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
|
||||
if (!check_is_assignable_to(c, operand, type)) {
|
||||
gbString type_string = type_to_string(type);
|
||||
gbString op_type_string = type_to_string(operand->type);
|
||||
gbString expr_str = expression_to_string(operand->expression);
|
||||
defer (gb_string_free(type_string));
|
||||
defer (gb_string_free(op_type_string));
|
||||
defer (gb_string_free(expr_str));
|
||||
|
||||
|
||||
// TODO(bill): is this a good enough error message?
|
||||
print_checker_error(c, ast_node_token(operand->expression),
|
||||
"Cannot assign value `%.*s` of type `%s` to `%s` in %.*s",
|
||||
LIT(ast_node_token(operand->expression).string),
|
||||
op_type_string,
|
||||
type_string,
|
||||
LIT(context_name));
|
||||
checker_err(c, ast_node_token(operand->expression),
|
||||
"Cannot assign value `%s` of type `%s` to `%s` in %.*s",
|
||||
expr_str,
|
||||
op_type_string,
|
||||
type_string,
|
||||
LIT(context_name));
|
||||
|
||||
operand->mode = Addressing_Invalid;
|
||||
}
|
||||
@@ -200,8 +203,7 @@ Type *check_assign_variable(Checker *c, Operand *op_a, AstNode *lhs) {
|
||||
|
||||
gbString str = expression_to_string(op_b.expression);
|
||||
defer (gb_string_free(str));
|
||||
print_checker_error(c, ast_node_token(op_b.expression),
|
||||
"Cannot assign to `%s`", str);
|
||||
checker_err(c, ast_node_token(op_b.expression), "Cannot assign to `%s`", str);
|
||||
} break;
|
||||
}
|
||||
|
||||
@@ -254,7 +256,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
|
||||
Type *t = operand->type;
|
||||
if (is_type_untyped(t)) {
|
||||
if (t == &basic_types[Basic_Invalid]) {
|
||||
print_checker_error(c, e->token, "Use of untyped thing in %.*s", LIT(context_name));
|
||||
checker_err(c, e->token, "Use of untyped thing in %.*s", LIT(context_name));
|
||||
e->type = &basic_types[Basic_Invalid];
|
||||
return NULL;
|
||||
}
|
||||
@@ -297,9 +299,9 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNode *in
|
||||
|
||||
if (i < lhs_count && i < init_count) {
|
||||
if (lhs[i]->type == NULL)
|
||||
print_checker_error(c, lhs[i]->token, "Too few values on the right hand side of the declaration");
|
||||
checker_err(c, lhs[i]->token, "Too few values on the right hand side of the declaration");
|
||||
} else if (rhs != NULL) {
|
||||
print_checker_error(c, ast_node_token(rhs), "Too many values on the right hand side of the declaration");
|
||||
checker_err(c, ast_node_token(rhs), "Too many values on the right hand side of the declaration");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,8 +316,8 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) {
|
||||
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
// TODO(bill): better error
|
||||
print_checker_error(c, ast_node_token(operand->expression),
|
||||
"`%.*s` is not a constant", LIT(ast_node_token(operand->expression).string));
|
||||
checker_err(c, ast_node_token(operand->expression),
|
||||
"`%.*s` is not a constant", LIT(ast_node_token(operand->expression).string));
|
||||
if (e->type == NULL)
|
||||
e->type = &basic_types[Basic_Invalid];
|
||||
return;
|
||||
@@ -350,8 +352,8 @@ void check_constant_declaration(Checker *c, Entity *e, AstNode *type_expr, AstNo
|
||||
if (!is_type_constant_type(t)) {
|
||||
gbString str = type_to_string(t);
|
||||
defer (gb_string_free(str));
|
||||
print_checker_error(c, ast_node_token(type_expr),
|
||||
"Invalid constant type `%s`", str);
|
||||
checker_err(c, ast_node_token(type_expr),
|
||||
"Invalid constant type `%s`", str);
|
||||
e->type = &basic_types[Basic_Invalid];
|
||||
return;
|
||||
}
|
||||
@@ -383,10 +385,9 @@ void check_procedure_body(Checker *c, Token token, DeclarationInfo *decl, Type *
|
||||
c->curr_scope = decl->scope;
|
||||
push_procedure(c, type);
|
||||
check_statement_list(c, body->block_statement.list);
|
||||
if (decl->type_expr != NULL &&
|
||||
decl->type_expr->procedure_type.result_count > 0) {
|
||||
if (type->procedure.results_count > 0) {
|
||||
if (!check_is_terminating(c, body)) {
|
||||
print_checker_error(c, body->block_statement.close, "Missing return statement at the end of the procedure");
|
||||
checker_err(c, body->block_statement.close, "Missing return statement at the end of the procedure");
|
||||
}
|
||||
}
|
||||
pop_procedure(c);
|
||||
@@ -403,7 +404,7 @@ void check_procedure_declaration(Checker *c, Entity *e, DeclarationInfo *d, b32
|
||||
|
||||
#if 1
|
||||
Scope *origin_curr_scope = c->curr_scope;
|
||||
c->curr_scope = c->file_scope;
|
||||
c->curr_scope = c->global_scope;
|
||||
check_open_scope(c, pd->procedure_type);
|
||||
#endif
|
||||
check_procedure_type(c, proc_type, pd->procedure_type);
|
||||
@@ -421,27 +422,27 @@ void check_procedure_declaration(Checker *c, Entity *e, DeclarationInfo *d, b32
|
||||
} else if (are_strings_equal(tag_name, make_string("no_inline"))) {
|
||||
is_no_inline = true;
|
||||
} else {
|
||||
print_checker_error(c, ast_node_token(tag), "Unknown procedure tag");
|
||||
checker_err(c, ast_node_token(tag), "Unknown procedure tag");
|
||||
}
|
||||
// TODO(bill): Other tags
|
||||
}
|
||||
|
||||
if (is_inline && is_no_inline) {
|
||||
print_checker_error(c, ast_node_token(pd->tag_list),
|
||||
"You cannot apply both `inline` and `no_inline` to a procedure");
|
||||
checker_err(c, ast_node_token(pd->tag_list),
|
||||
"You cannot apply both `inline` and `no_inline` to a procedure");
|
||||
}
|
||||
|
||||
if (pd->body != NULL) {
|
||||
if (is_foreign) {
|
||||
print_checker_error(c, ast_node_token(pd->body),
|
||||
"A procedure tagged as `#foreign` cannot have a body");
|
||||
checker_err(c, ast_node_token(pd->body),
|
||||
"A procedure tagged as `#foreign` cannot have a body");
|
||||
}
|
||||
|
||||
d->scope = c->curr_scope;
|
||||
|
||||
GB_ASSERT(pd->body->kind == AstNode_BlockStatement);
|
||||
if (check_body_later) {
|
||||
check_procedure_later(c, e->token, d, proc_type, pd->body);
|
||||
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body);
|
||||
} else {
|
||||
check_procedure_body(c, e->token, d, proc_type, pd->body);
|
||||
}
|
||||
@@ -533,19 +534,19 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
ExpressionKind kind = check_expression_base(c, &operand, node->expression_statement.expression);
|
||||
switch (operand.mode) {
|
||||
case Addressing_Type:
|
||||
print_checker_error(c, ast_node_token(node), "Is not an expression");
|
||||
checker_err(c, ast_node_token(node), "Is not an expression");
|
||||
break;
|
||||
default:
|
||||
if (kind == Expression_Statement)
|
||||
return;
|
||||
print_checker_error(c, ast_node_token(node), "Expression is not used");
|
||||
checker_err(c, ast_node_token(node), "Expression is not used");
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case AstNode_TagStatement:
|
||||
// TODO(bill): Tag Statements
|
||||
print_checker_error(c, ast_node_token(node), "Tag statements are not supported yet");
|
||||
checker_err(c, ast_node_token(node), "Tag statements are not supported yet");
|
||||
check_statement(c, node->tag_statement.statement);
|
||||
break;
|
||||
|
||||
@@ -563,7 +564,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
op.string.len = 1;
|
||||
break;
|
||||
default:
|
||||
print_checker_error(c, s->op, "Unknown inc/dec operation %.*s", LIT(s->op.string));
|
||||
checker_err(c, s->op, "Unknown inc/dec operation %.*s", LIT(s->op.string));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -572,7 +573,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
if (operand.mode == Addressing_Invalid)
|
||||
return;
|
||||
if (!is_type_numeric(operand.type)) {
|
||||
print_checker_error(c, s->op, "Non numeric type");
|
||||
checker_err(c, s->op, "Non numeric type");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -592,7 +593,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
switch (node->assign_statement.op.kind) {
|
||||
case Token_Eq:
|
||||
if (node->assign_statement.lhs_count == 0) {
|
||||
print_checker_error(c, node->assign_statement.op, "Missing lhs in assignment statement");
|
||||
checker_err(c, node->assign_statement.op, "Missing lhs in assignment statement");
|
||||
return;
|
||||
}
|
||||
check_assign_variables(c,
|
||||
@@ -604,8 +605,11 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
Token op = node->assign_statement.op;
|
||||
if (node->assign_statement.lhs_count != 1 ||
|
||||
node->assign_statement.rhs_count != 1) {
|
||||
print_checker_error(c, op,
|
||||
"assignment operation `%.*s` requires single-valued expressions", LIT(op.string));
|
||||
checker_err(c, op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string));
|
||||
return;
|
||||
}
|
||||
if (!gb_is_between(op.kind, Token_AddEq, Token_ModEq)) {
|
||||
checker_err(c, op, "Unknown Assignment operation `%.*s`", LIT(op.string));
|
||||
return;
|
||||
}
|
||||
// TODO(bill): Check if valid assignment operator
|
||||
@@ -636,8 +640,8 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
check_expression(c, &operand, node->if_statement.cond);
|
||||
if (operand.mode != Addressing_Invalid &&
|
||||
!is_type_boolean(operand.type)) {
|
||||
print_checker_error(c, ast_node_token(node->if_statement.cond),
|
||||
"Non-boolean condition in `if` statement");
|
||||
checker_err(c, ast_node_token(node->if_statement.cond),
|
||||
"Non-boolean condition in `if` statement");
|
||||
}
|
||||
check_statement(c, node->if_statement.body);
|
||||
|
||||
@@ -648,8 +652,8 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
check_statement(c, node->if_statement.else_statement);
|
||||
break;
|
||||
default:
|
||||
print_checker_error(c, ast_node_token(node->if_statement.else_statement),
|
||||
"Invalid `else` statement in `if` statement");
|
||||
checker_err(c, ast_node_token(node->if_statement.else_statement),
|
||||
"Invalid `else` statement in `if` statement");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -660,7 +664,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
GB_ASSERT(gb_array_count(c->procedure_stack) > 0);
|
||||
|
||||
if (c->in_defer) {
|
||||
print_checker_error(c, rs->token, "You cannot `return` within a defer statement");
|
||||
checker_err(c, rs->token, "You cannot `return` within a defer statement");
|
||||
// TODO(bill): Should I break here?
|
||||
break;
|
||||
}
|
||||
@@ -670,10 +674,10 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
if (proc_type->procedure.results)
|
||||
result_count = proc_type->procedure.results->tuple.variable_count;
|
||||
if (result_count != rs->result_count) {
|
||||
print_checker_error(c, rs->token, "Expected %td return %s, got %td",
|
||||
result_count,
|
||||
(result_count != 1 ? "values" : "value"),
|
||||
rs->result_count);
|
||||
checker_err(c, rs->token, "Expected %td return %s, got %td",
|
||||
result_count,
|
||||
(result_count != 1 ? "values" : "value"),
|
||||
rs->result_count);
|
||||
} else if (result_count > 0) {
|
||||
auto *tuple = &proc_type->procedure.results->tuple;
|
||||
check_init_variables(c, tuple->variables, tuple->variable_count,
|
||||
@@ -691,8 +695,8 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
check_expression(c, &operand, node->for_statement.cond);
|
||||
if (operand.mode != Addressing_Invalid &&
|
||||
!is_type_boolean(operand.type)) {
|
||||
print_checker_error(c, ast_node_token(node->for_statement.cond),
|
||||
"Non-boolean condition in `for` statement");
|
||||
checker_err(c, ast_node_token(node->for_statement.cond),
|
||||
"Non-boolean condition in `for` statement");
|
||||
}
|
||||
}
|
||||
check_statement(c, node->for_statement.end);
|
||||
@@ -702,7 +706,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
case AstNode_DeferStatement: {
|
||||
auto *ds = &node->defer_statement;
|
||||
if (is_ast_node_declaration(ds->statement)) {
|
||||
print_checker_error(c, ds->token, "You cannot defer a declaration");
|
||||
checker_err(c, ds->token, "You cannot defer a declaration");
|
||||
} else {
|
||||
b32 out_in_defer = c->in_defer;
|
||||
c->in_defer = true;
|
||||
@@ -744,10 +748,10 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
entity = found;
|
||||
}
|
||||
} else {
|
||||
print_checker_error(c, token, "A variable declaration must be an identifier");
|
||||
checker_err(c, token, "A variable declaration must be an identifier");
|
||||
}
|
||||
if (entity == NULL)
|
||||
entity = make_entity_dummy_variable(c->allocator, c->file_scope, token);
|
||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, token);
|
||||
entities[entity_index++] = entity;
|
||||
}
|
||||
|
||||
@@ -786,7 +790,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
name != NULL && value != NULL;
|
||||
name = name->next, value = value->next) {
|
||||
GB_ASSERT(name->kind == AstNode_Identifier);
|
||||
Value v = {Value_Invalid};
|
||||
ExactValue v = {ExactValue_Invalid};
|
||||
Entity *e = make_entity_constant(c->allocator, c->curr_scope, name->identifier.token, NULL, v);
|
||||
entities[entity_index++] = e;
|
||||
check_constant_declaration(c, e, vd->type_expression, value);
|
||||
@@ -797,9 +801,9 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
|
||||
// TODO(bill): Better error messages or is this good enough?
|
||||
if (rhs_count == 0 && vd->type_expression == NULL) {
|
||||
print_checker_error(c, ast_node_token(node), "Missing type or initial expression");
|
||||
checker_err(c, ast_node_token(node), "Missing type or initial expression");
|
||||
} else if (lhs_count < rhs_count) {
|
||||
print_checker_error(c, ast_node_token(node), "Extra initial expression");
|
||||
checker_err(c, ast_node_token(node), "Extra initial expression");
|
||||
}
|
||||
|
||||
AstNode *name = vd->name_list;
|
||||
@@ -809,7 +813,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
} break;
|
||||
|
||||
default:
|
||||
print_checker_error(c, ast_node_token(node), "Unknown variable declaration kind. Probably an invalid AST.");
|
||||
checker_err(c, ast_node_token(node), "Unknown variable declaration kind. Probably an invalid AST.");
|
||||
return;
|
||||
}
|
||||
} break;
|
||||
@@ -819,10 +823,11 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
Entity *e = make_entity_procedure(c->allocator, c->curr_scope, pd->name->identifier.token, NULL);
|
||||
add_entity(c, c->curr_scope, pd->name, e);
|
||||
|
||||
DeclarationInfo *decl = make_declaration_info(gb_heap_allocator(), e->parent);
|
||||
decl->proc_decl = node;
|
||||
check_procedure_declaration(c, e, decl, false);
|
||||
destroy_declaration_info(decl);
|
||||
DeclarationInfo decl = {};
|
||||
init_declaration_info(&decl, e->parent);
|
||||
decl.proc_decl = node;
|
||||
check_procedure_declaration(c, e, &decl, false);
|
||||
destroy_declaration_info(&decl);
|
||||
} break;
|
||||
|
||||
case AstNode_TypeDeclaration: {
|
||||
|
||||
@@ -26,6 +26,7 @@ enum BasicKind {
|
||||
|
||||
Basic_Count,
|
||||
|
||||
Basic_byte = Basic_u8,
|
||||
Basic_rune = Basic_i32,
|
||||
};
|
||||
|
||||
@@ -204,7 +205,8 @@ gb_global Type basic_types[] = {
|
||||
};
|
||||
|
||||
gb_global Type basic_type_aliases[] = {
|
||||
{Type_Basic, {Basic_rune, BasicFlag_Integer, STR_LIT("rune")}},
|
||||
{Type_Basic, {Basic_byte, BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("byte")}},
|
||||
{Type_Basic, {Basic_rune, BasicFlag_Integer, STR_LIT("rune")}},
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
// TODO(bill): Big numbers
|
||||
// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!!
|
||||
|
||||
enum ValueKind {
|
||||
Value_Invalid,
|
||||
enum ExactValueKind {
|
||||
ExactValue_Invalid,
|
||||
|
||||
Value_Bool,
|
||||
Value_String,
|
||||
Value_Integer,
|
||||
Value_Float,
|
||||
Value_Pointer, // TODO(bill): Handle Value_Pointer correctly
|
||||
ExactValue_Bool,
|
||||
ExactValue_String,
|
||||
ExactValue_Integer,
|
||||
ExactValue_Float,
|
||||
ExactValue_Pointer, // TODO(bill): Handle ExactValue_Pointer correctly
|
||||
|
||||
Value_Count,
|
||||
ExactValue_Count,
|
||||
};
|
||||
|
||||
struct Value {
|
||||
ValueKind kind;
|
||||
struct ExactValue {
|
||||
ExactValueKind kind;
|
||||
union {
|
||||
b32 value_bool;
|
||||
String value_string;
|
||||
@@ -26,22 +26,22 @@ struct Value {
|
||||
};
|
||||
};
|
||||
|
||||
Value make_value_bool(b32 b) {
|
||||
Value result = {Value_Bool};
|
||||
ExactValue make_exact_value_bool(b32 b) {
|
||||
ExactValue result = {ExactValue_Bool};
|
||||
result.value_bool = (b != 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
Value make_value_string(String string) {
|
||||
ExactValue make_exact_value_string(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
Value result = {Value_String};
|
||||
ExactValue result = {ExactValue_String};
|
||||
result.value_string = string;
|
||||
return result;
|
||||
}
|
||||
|
||||
Value make_value_integer(String string) {
|
||||
ExactValue make_exact_value_integer(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
Value result = {Value_Integer};
|
||||
ExactValue result = {ExactValue_Integer};
|
||||
i32 base = 10;
|
||||
if (string.text[0] == '0') {
|
||||
switch (string.text[1]) {
|
||||
@@ -57,91 +57,91 @@ Value make_value_integer(String string) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Value make_value_integer(i64 i) {
|
||||
Value result = {Value_Integer};
|
||||
ExactValue make_exact_value_integer(i64 i) {
|
||||
ExactValue result = {ExactValue_Integer};
|
||||
result.value_integer = i;
|
||||
return result;
|
||||
}
|
||||
|
||||
Value make_value_float(String string) {
|
||||
ExactValue make_exact_value_float(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
Value result = {Value_Float};
|
||||
ExactValue result = {ExactValue_Float};
|
||||
result.value_float = gb_str_to_f64(cast(char *)string.text, NULL);
|
||||
return result;
|
||||
}
|
||||
|
||||
Value make_value_float(f64 f) {
|
||||
Value result = {Value_Float};
|
||||
ExactValue make_exact_value_float(f64 f) {
|
||||
ExactValue result = {ExactValue_Float};
|
||||
result.value_float = f;
|
||||
return result;
|
||||
}
|
||||
|
||||
Value make_value_pointer(void *ptr) {
|
||||
Value result = {Value_Pointer};
|
||||
ExactValue make_exact_value_pointer(void *ptr) {
|
||||
ExactValue result = {ExactValue_Pointer};
|
||||
result.value_pointer = ptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
Value make_value_from_basic_literal(Token token) {
|
||||
ExactValue make_exact_value_from_basic_literal(Token token) {
|
||||
switch (token.kind) {
|
||||
case Token_String: return make_value_string(token.string);
|
||||
case Token_Integer: return make_value_integer(token.string);
|
||||
case Token_Float: return make_value_float(token.string);
|
||||
case Token_Rune: return make_value_integer(token.string);
|
||||
case Token_String: return make_exact_value_string(token.string);
|
||||
case Token_Integer: return make_exact_value_integer(token.string);
|
||||
case Token_Float: return make_exact_value_float(token.string);
|
||||
case Token_Rune: return make_exact_value_integer(token.string);
|
||||
default:
|
||||
GB_PANIC("Invalid token for basic literal");
|
||||
break;
|
||||
}
|
||||
|
||||
Value result = {Value_Invalid};
|
||||
ExactValue result = {ExactValue_Invalid};
|
||||
return result;
|
||||
}
|
||||
|
||||
Value value_to_integer(Value v) {
|
||||
ExactValue exact_value_to_integer(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case Value_Integer:
|
||||
case ExactValue_Integer:
|
||||
return v;
|
||||
case Value_Float:
|
||||
return make_value_integer(cast(i64)v.value_float);
|
||||
case ExactValue_Float:
|
||||
return make_exact_value_integer(cast(i64)v.value_float);
|
||||
}
|
||||
Value r = {Value_Invalid};
|
||||
ExactValue r = {ExactValue_Invalid};
|
||||
return r;
|
||||
}
|
||||
|
||||
Value value_to_float(Value v) {
|
||||
ExactValue exact_value_to_float(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case Value_Integer:
|
||||
return make_value_float(cast(i64)v.value_integer);
|
||||
case Value_Float:
|
||||
case ExactValue_Integer:
|
||||
return make_exact_value_float(cast(i64)v.value_integer);
|
||||
case ExactValue_Float:
|
||||
return v;
|
||||
}
|
||||
Value r = {Value_Invalid};
|
||||
ExactValue r = {ExactValue_Invalid};
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
Value unary_operator_value(Token op, Value v, i32 precision) {
|
||||
ExactValue exact_unary_operator_value(Token op, ExactValue v, i32 precision) {
|
||||
switch (op.kind) {
|
||||
case Token_Add: {
|
||||
switch (v.kind) {
|
||||
case Value_Invalid:
|
||||
case Value_Integer:
|
||||
case Value_Float:
|
||||
case ExactValue_Invalid:
|
||||
case ExactValue_Integer:
|
||||
case ExactValue_Float:
|
||||
return v;
|
||||
}
|
||||
} break;
|
||||
|
||||
case Token_Sub: {
|
||||
switch (v.kind) {
|
||||
case Value_Invalid:
|
||||
case ExactValue_Invalid:
|
||||
return v;
|
||||
case Value_Integer: {
|
||||
Value i = v;
|
||||
case ExactValue_Integer: {
|
||||
ExactValue i = v;
|
||||
i.value_integer = -i.value_integer;
|
||||
return i;
|
||||
}
|
||||
case Value_Float: {
|
||||
Value i = v;
|
||||
case ExactValue_Float: {
|
||||
ExactValue i = v;
|
||||
i.value_float = -i.value_float;
|
||||
return i;
|
||||
}
|
||||
@@ -151,9 +151,9 @@ Value unary_operator_value(Token op, Value v, i32 precision) {
|
||||
case Token_Xor: {
|
||||
i64 i = 0;
|
||||
switch (v.kind) {
|
||||
case Value_Invalid:
|
||||
case ExactValue_Invalid:
|
||||
return v;
|
||||
case Value_Integer:
|
||||
case ExactValue_Integer:
|
||||
i = v.value_integer;
|
||||
i = ~i;
|
||||
break;
|
||||
@@ -166,14 +166,14 @@ Value unary_operator_value(Token op, Value v, i32 precision) {
|
||||
if (precision > 0)
|
||||
i &= ~((~0ll)<<precision);
|
||||
|
||||
return make_value_integer(i);
|
||||
return make_exact_value_integer(i);
|
||||
} break;
|
||||
|
||||
case Token_Not: {
|
||||
switch (v.kind) {
|
||||
case Value_Invalid: return v;
|
||||
case Value_Bool:
|
||||
return make_value_bool(!v.value_bool);
|
||||
case ExactValue_Invalid: return v;
|
||||
case ExactValue_Bool:
|
||||
return make_exact_value_bool(!v.value_bool);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
@@ -181,23 +181,23 @@ Value unary_operator_value(Token op, Value v, i32 precision) {
|
||||
failure:
|
||||
GB_PANIC("Invalid unary operation, %s", token_kind_to_string(op.kind));
|
||||
|
||||
Value error_value = {};
|
||||
ExactValue error_value = {};
|
||||
return error_value;
|
||||
}
|
||||
|
||||
// NOTE(bill): Make sure things are evaluated in correct order
|
||||
i32 value_order(Value v) {
|
||||
i32 exact_value_order(ExactValue v) {
|
||||
switch (v.kind) {
|
||||
case Value_Invalid:
|
||||
case ExactValue_Invalid:
|
||||
return 0;
|
||||
case Value_Bool:
|
||||
case Value_String:
|
||||
case ExactValue_Bool:
|
||||
case ExactValue_String:
|
||||
return 1;
|
||||
case Value_Integer:
|
||||
case ExactValue_Integer:
|
||||
return 2;
|
||||
case Value_Float:
|
||||
case ExactValue_Float:
|
||||
return 3;
|
||||
case Value_Pointer:
|
||||
case ExactValue_Pointer:
|
||||
return 4;
|
||||
|
||||
default:
|
||||
@@ -206,58 +206,58 @@ i32 value_order(Value v) {
|
||||
}
|
||||
}
|
||||
|
||||
void match_values(Value *x, Value *y) {
|
||||
if (value_order(*y) < value_order(*x)) {
|
||||
match_values(y, x);
|
||||
void match_exact_values(ExactValue *x, ExactValue *y) {
|
||||
if (exact_value_order(*y) < exact_value_order(*x)) {
|
||||
match_exact_values(y, x);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (x->kind) {
|
||||
case Value_Invalid:
|
||||
case ExactValue_Invalid:
|
||||
*y = *x;
|
||||
return;
|
||||
|
||||
case Value_Bool:
|
||||
case Value_String:
|
||||
case ExactValue_Bool:
|
||||
case ExactValue_String:
|
||||
return;
|
||||
|
||||
case Value_Integer:
|
||||
case ExactValue_Integer:
|
||||
switch (y->kind) {
|
||||
case Value_Integer:
|
||||
case ExactValue_Integer:
|
||||
return;
|
||||
case Value_Float:
|
||||
case ExactValue_Float:
|
||||
// TODO(bill): Is this good enough?
|
||||
*x = make_value_float(cast(f64)x->value_integer);
|
||||
*x = make_exact_value_float(cast(f64)x->value_integer);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
case Value_Float:
|
||||
if (y->kind == Value_Float)
|
||||
case ExactValue_Float:
|
||||
if (y->kind == ExactValue_Float)
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
GB_PANIC("How'd you get here? Invalid Value.kind");
|
||||
GB_PANIC("How'd you get here? Invalid ExactValueKind");
|
||||
}
|
||||
|
||||
// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
|
||||
Value binary_operator_value(Token op, Value x, Value y) {
|
||||
match_values(&x, &y);
|
||||
ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
|
||||
match_exact_values(&x, &y);
|
||||
|
||||
switch (x.kind) {
|
||||
case Value_Invalid:
|
||||
case ExactValue_Invalid:
|
||||
return x;
|
||||
|
||||
case Value_Bool:
|
||||
case ExactValue_Bool:
|
||||
switch (op.kind) {
|
||||
case Token_CmpAnd: return make_value_bool(x.value_bool && y.value_bool);
|
||||
case Token_CmpOr: return make_value_bool(x.value_bool || y.value_bool);
|
||||
case Token_CmpAnd: return make_exact_value_bool(x.value_bool && y.value_bool);
|
||||
case Token_CmpOr: return make_exact_value_bool(x.value_bool || y.value_bool);
|
||||
default: goto error;
|
||||
}
|
||||
break;
|
||||
|
||||
case Value_Integer: {
|
||||
case ExactValue_Integer: {
|
||||
i64 a = x.value_integer;
|
||||
i64 b = y.value_integer;
|
||||
i64 c = 0;
|
||||
@@ -265,7 +265,7 @@ Value binary_operator_value(Token op, Value x, Value y) {
|
||||
case Token_Add: c = a + b; break;
|
||||
case Token_Sub: c = a - b; break;
|
||||
case Token_Mul: c = a * b; break;
|
||||
case Token_Quo: return make_value_float(fmod(cast(f64)a, cast(f64)b));
|
||||
case Token_Quo: return make_exact_value_float(fmod(cast(f64)a, cast(f64)b));
|
||||
case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division
|
||||
case Token_Mod: c = a % b; break;
|
||||
case Token_And: c = a & b; break;
|
||||
@@ -274,53 +274,53 @@ Value binary_operator_value(Token op, Value x, Value y) {
|
||||
case Token_AndNot: c = a&(~b); break;
|
||||
default: goto error;
|
||||
}
|
||||
return make_value_integer(c);
|
||||
return make_exact_value_integer(c);
|
||||
} break;
|
||||
|
||||
case Value_Float: {
|
||||
case ExactValue_Float: {
|
||||
f64 a = x.value_float;
|
||||
f64 b = y.value_float;
|
||||
switch (op.kind) {
|
||||
case Token_Add: return make_value_float(a + b);
|
||||
case Token_Sub: return make_value_float(a - b);
|
||||
case Token_Mul: return make_value_float(a * b);
|
||||
case Token_Quo: return make_value_float(a / b);
|
||||
case Token_Add: return make_exact_value_float(a + b);
|
||||
case Token_Sub: return make_exact_value_float(a - b);
|
||||
case Token_Mul: return make_exact_value_float(a * b);
|
||||
case Token_Quo: return make_exact_value_float(a / b);
|
||||
default: goto error;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
error:
|
||||
Value error_value = {};
|
||||
GB_PANIC("Invalid binary operation: %s", token_kind_to_string(op.kind));
|
||||
ExactValue error_value = {};
|
||||
// gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op.kind));
|
||||
return error_value;
|
||||
}
|
||||
|
||||
gb_inline Value value_add(Value x, Value y) { Token op = {Token_Add}; return binary_operator_value(op, x, y); }
|
||||
gb_inline Value value_sub(Value x, Value y) { Token op = {Token_Sub}; return binary_operator_value(op, x, y); }
|
||||
gb_inline Value value_mul(Value x, Value y) { Token op = {Token_Mul}; return binary_operator_value(op, x, y); }
|
||||
gb_inline Value value_quo(Value x, Value y) { Token op = {Token_Quo}; return binary_operator_value(op, x, y); }
|
||||
gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { Token op = {Token_Add}; return exact_binary_operator_value(op, x, y); }
|
||||
gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { Token op = {Token_Sub}; return exact_binary_operator_value(op, x, y); }
|
||||
gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { Token op = {Token_Mul}; return exact_binary_operator_value(op, x, y); }
|
||||
gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { Token op = {Token_Quo}; return exact_binary_operator_value(op, x, y); }
|
||||
|
||||
|
||||
i32 cmp_f64(f64 a, f64 b) {
|
||||
return (a > b) - (a < b);
|
||||
}
|
||||
|
||||
b32 compare_values(Token op, Value x, Value y) {
|
||||
match_values(&x, &y);
|
||||
b32 compare_exact_values(Token op, ExactValue x, ExactValue y) {
|
||||
match_exact_values(&x, &y);
|
||||
|
||||
switch (x.kind) {
|
||||
case Value_Invalid:
|
||||
case ExactValue_Invalid:
|
||||
return false;
|
||||
|
||||
case Value_Bool:
|
||||
case ExactValue_Bool:
|
||||
switch (op.kind) {
|
||||
case Token_CmpEq: return x.value_bool == y.value_bool;
|
||||
case Token_NotEq: return x.value_bool != y.value_bool;
|
||||
}
|
||||
break;
|
||||
|
||||
case Value_Integer: {
|
||||
case ExactValue_Integer: {
|
||||
i64 a = x.value_integer;
|
||||
i64 b = y.value_integer;
|
||||
switch (op.kind) {
|
||||
@@ -333,7 +333,7 @@ b32 compare_values(Token op, Value x, Value y) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case Value_Float: {
|
||||
case ExactValue_Float: {
|
||||
f64 a = x.value_float;
|
||||
f64 b = y.value_float;
|
||||
switch (op.kind) {
|
||||
@@ -346,7 +346,7 @@ b32 compare_values(Token op, Value x, Value y) {
|
||||
}
|
||||
} break;
|
||||
|
||||
case Value_String: {
|
||||
case ExactValue_String: {
|
||||
String a = x.value_string;
|
||||
String b = y.value_string;
|
||||
isize len = gb_min(a.len, b.len);
|
||||
@@ -1631,7 +1631,8 @@ GB_STATIC_ASSERT(GB_ARRAY_GROW_FORMULA(0) > 0);
|
||||
void **gb__array_ = cast(void **)&(x); \
|
||||
gbArrayHeader *gb__ah = cast(gbArrayHeader *)gb_alloc(allocator_, gb_size_of(gbArrayHeader)+gb_size_of(*(x))*(cap)); \
|
||||
gb__ah->allocator = allocator_; \
|
||||
gb__ah->count = gb__ah->capacity = 0; \
|
||||
gb__ah->count = 0; \
|
||||
gb__ah->capacity = cap; \
|
||||
*gb__array_ = cast(void *)(gb__ah+1); \
|
||||
} while (0)
|
||||
|
||||
@@ -8122,6 +8123,10 @@ gb_no_inline isize gb_snprintf_va(char *text, isize max_len, char const *fmt, va
|
||||
info.flags |= (gbFmt_Lower|gbFmt_Unsigned|gbFmt_Alt|gbFmt_Intptr);
|
||||
break;
|
||||
|
||||
case '%':
|
||||
len = gb__print_char(text, remaining, &info, '%');
|
||||
break;
|
||||
|
||||
default: fmt--; break;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#include <llvm-c/Core.h>
|
||||
#include <llvm-c/BitWriter.h>
|
||||
|
||||
struct Generator {
|
||||
Checker *checker;
|
||||
String output_path;
|
||||
|
||||
#define MAX_GENERATOR_ERROR_COUNT 10
|
||||
isize error_prev_line;
|
||||
isize error_prev_column;
|
||||
isize error_count;
|
||||
};
|
||||
|
||||
#define print_generator_error(p, token, fmt, ...) print_generator_error_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__)
|
||||
void print_generator_error_(Generator *g, char *function, Token token, char *fmt, ...) {
|
||||
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (g->error_prev_line != token.line || g->error_prev_column != token.column) {
|
||||
va_list va;
|
||||
|
||||
g->error_prev_line = token.line;
|
||||
g->error_prev_column = token.column;
|
||||
|
||||
#if 0
|
||||
gb_printf_err("%s()\n", function);
|
||||
#endif
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%s(%td:%td) %s\n",
|
||||
g->checker->parser->tokenizer.fullpath, token.line, token.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
|
||||
}
|
||||
g->error_count++;
|
||||
}
|
||||
|
||||
|
||||
b32 init_generator(Generator *g, Checker *checker) {
|
||||
if (checker->error_count > 0)
|
||||
return false;
|
||||
gb_zero_item(g);
|
||||
g->checker = checker;
|
||||
|
||||
char *fullpath = checker->parser->tokenizer.fullpath;
|
||||
char const *ext = gb_path_extension(fullpath);
|
||||
isize len = ext-fullpath;
|
||||
u8 *text = gb_alloc_array(gb_heap_allocator(), u8, len);
|
||||
gb_memcopy(text, fullpath, len);
|
||||
g->output_path = make_string(text, len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void destroy_generator(Generator *g) {
|
||||
if (g->error_count > 0) {
|
||||
|
||||
}
|
||||
|
||||
if (g->output_path.text)
|
||||
gb_free(gb_heap_allocator(), g->output_path.text);
|
||||
}
|
||||
|
||||
|
||||
void emit_var_decl(Generator *g, String name, Type *type) {
|
||||
// gb_printf("%.*s: %s;\n", LIT(name), type_to_string(type));
|
||||
}
|
||||
|
||||
|
||||
void generate_code(Generator *g, AstNode *file_node) {
|
||||
// if (file_node->kind == AstNode_VariableDeclaration) {
|
||||
// auto *vd = &file_node->variable_declaration;
|
||||
// if (vd->kind == Declaration_Mutable) {
|
||||
// for (AstNode *name_item = vd->name_list; name_item != NULL; name_item = name_item->next) {
|
||||
// String name = name_item->identifier.token.string;
|
||||
// Entity *entity = entity_of_identifier(g->checker, name_item);
|
||||
// Type *type = entity->type;
|
||||
// emit_var_decl(g, name, type);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
24
src/main.cpp
24
src/main.cpp
@@ -3,7 +3,7 @@
|
||||
#include "parser.cpp"
|
||||
#include "printer.cpp"
|
||||
#include "checker/checker.cpp"
|
||||
#include "generator.cpp"
|
||||
// #include "codegen/codegen.cpp"
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
@@ -12,29 +12,29 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
init_global_scope();
|
||||
init_universal_scope();
|
||||
|
||||
for (int arg_index = 1; arg_index < argc; arg_index++) {
|
||||
char *arg = argv[arg_index];
|
||||
char *filename = arg;
|
||||
char *init_filename = arg;
|
||||
Parser parser = {0};
|
||||
|
||||
if (init_parser(&parser, filename)) {
|
||||
if (init_parser(&parser)) {
|
||||
defer (destroy_parser(&parser));
|
||||
AstNode *file_node = parse_statement_list(&parser, NULL);
|
||||
// print_ast(file_node, 0);
|
||||
|
||||
parse_files(&parser, init_filename);
|
||||
|
||||
Checker checker = {};
|
||||
init_checker(&checker, &parser);
|
||||
defer (destroy_checker(&checker));
|
||||
|
||||
check_file(&checker, file_node);
|
||||
check_parsed_files(&checker);
|
||||
#if 0
|
||||
Codegen codegen = {};
|
||||
if (init_codegen(&codegen, &checker)) {
|
||||
defer (destroy_codegen(&codegen));
|
||||
|
||||
#if 1
|
||||
Generator generator = {};
|
||||
if (init_generator(&generator, &checker)) {
|
||||
defer (destroy_generator(&generator));
|
||||
generate_code(&generator, file_node);
|
||||
generate_code(&codegen, file_node);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
1181
src/parser.cpp
1181
src/parser.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,26 +0,0 @@
|
||||
main :: proc() {
|
||||
x := "Yep";
|
||||
|
||||
thing :: proc(n: int) -> (int, f32) {
|
||||
return n*n, 13.37;
|
||||
}
|
||||
|
||||
thang(thing(1), x);
|
||||
|
||||
v: Vec2;
|
||||
}
|
||||
|
||||
thang :: proc(a: int, b: f32, s: string) {
|
||||
a = 1;
|
||||
b = 2;
|
||||
s = "Hello";
|
||||
}
|
||||
|
||||
z := y;
|
||||
y := x;
|
||||
x := 1;
|
||||
|
||||
type Vec2: struct {
|
||||
x, y: f32;
|
||||
}
|
||||
|
||||
@@ -103,7 +103,6 @@ Token__OperatorEnd,
|
||||
Token__KeywordBegin,
|
||||
Token_type,
|
||||
Token_proc,
|
||||
|
||||
Token_match, // TODO(bill): switch vs match?
|
||||
Token_break,
|
||||
Token_continue,
|
||||
@@ -275,7 +274,7 @@ gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); }
|
||||
|
||||
typedef struct Tokenizer Tokenizer;
|
||||
struct Tokenizer {
|
||||
char *fullpath;
|
||||
String fullpath;
|
||||
u8 *start;
|
||||
u8 *end;
|
||||
|
||||
@@ -289,8 +288,8 @@ struct Tokenizer {
|
||||
};
|
||||
|
||||
|
||||
#define tokenizer_error(t, msg, ...) tokenizer_error_(t, __FUNCTION__, msg, ##__VA_ARGS__)
|
||||
void tokenizer_error_(Tokenizer *t, char *function, char *msg, ...) {
|
||||
#define tokenizer_err(t, msg, ...) tokenizer_err_(t, __FUNCTION__, msg, ##__VA_ARGS__)
|
||||
void tokenizer_err_(Tokenizer *t, char *function, char *msg, ...) {
|
||||
va_list va;
|
||||
isize column = t->read_curr - t->line+1;
|
||||
if (column < 1)
|
||||
@@ -322,13 +321,13 @@ void advance_to_next_rune(Tokenizer *t) {
|
||||
}
|
||||
rune = *t->read_curr;
|
||||
if (rune == 0) {
|
||||
tokenizer_error(t, "Illegal character NUL");
|
||||
tokenizer_err(t, "Illegal character NUL");
|
||||
} else if (rune >= 0x80) { // not ASCII
|
||||
width = gb_utf8_decode(t->read_curr, t->end-t->read_curr, &rune);
|
||||
if (rune == GB_RUNE_INVALID && width == 1)
|
||||
tokenizer_error(t, "Illegal UTF-8 encoding");
|
||||
tokenizer_err(t, "Illegal UTF-8 encoding");
|
||||
else if (rune == GB_RUNE_BOM && t->curr-t->start > 0)
|
||||
tokenizer_error(t, "Illegal byte order mark");
|
||||
tokenizer_err(t, "Illegal byte order mark");
|
||||
}
|
||||
t->read_curr += width;
|
||||
t->curr_rune = rune;
|
||||
@@ -342,15 +341,20 @@ void advance_to_next_rune(Tokenizer *t) {
|
||||
}
|
||||
}
|
||||
|
||||
b32 init_tokenizer(Tokenizer *t, char *filename) {
|
||||
gbFileContents fc = gb_file_read_contents(gb_heap_allocator(), true, filename);
|
||||
b32 init_tokenizer(Tokenizer *t, String fullpath) {
|
||||
char *c_str = gb_alloc_array(gb_heap_allocator(), char, fullpath.len+1);
|
||||
memcpy(c_str, fullpath.text, fullpath.len);
|
||||
c_str[fullpath.len] = '\0';
|
||||
defer (gb_free(gb_heap_allocator(), c_str));
|
||||
|
||||
gbFileContents fc = gb_file_read_contents(gb_heap_allocator(), true, c_str);
|
||||
gb_zero_item(t);
|
||||
if (fc.data) {
|
||||
t->start = cast(u8 *)fc.data;
|
||||
t->line = t->read_curr = t->curr = t->start;
|
||||
t->end = t->start + fc.size;
|
||||
|
||||
t->fullpath = gb_path_get_full_name(gb_heap_allocator(), filename);
|
||||
t->fullpath = fullpath;
|
||||
|
||||
t->line_count = 1;
|
||||
|
||||
@@ -527,9 +531,9 @@ b32 scan_escape(Tokenizer *t, Rune quote) {
|
||||
len = 8; base = 16; max = GB_RUNE_MAX;
|
||||
} else {
|
||||
if (t->curr_rune < 0)
|
||||
tokenizer_error(t, "Escape sequence was not terminated");
|
||||
tokenizer_err(t, "Escape sequence was not terminated");
|
||||
else
|
||||
tokenizer_error(t, "Unknown escape sequence");
|
||||
tokenizer_err(t, "Unknown escape sequence");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -537,9 +541,9 @@ b32 scan_escape(Tokenizer *t, Rune quote) {
|
||||
u32 d = cast(u32)digit_value(t->curr_rune);
|
||||
if (d >= base) {
|
||||
if (t->curr_rune < 0)
|
||||
tokenizer_error(t, "Escape sequence was not terminated");
|
||||
tokenizer_err(t, "Escape sequence was not terminated");
|
||||
else
|
||||
tokenizer_error(t, "Illegal character %d in escape sequence", t->curr_rune);
|
||||
tokenizer_err(t, "Illegal character %d in escape sequence", t->curr_rune);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -646,7 +650,7 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
for (;;) {
|
||||
Rune r = t->curr_rune;
|
||||
if (r == '\n' || r < 0) {
|
||||
tokenizer_error(t, "String literal not terminated");
|
||||
tokenizer_err(t, "String literal not terminated");
|
||||
break;
|
||||
}
|
||||
advance_to_next_rune(t);
|
||||
@@ -665,7 +669,7 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
Rune r = t->curr_rune;
|
||||
if (r == '\n' || r < 0) {
|
||||
if (valid)
|
||||
tokenizer_error(t, "Rune literal not terminated");
|
||||
tokenizer_err(t, "Rune literal not terminated");
|
||||
break;
|
||||
}
|
||||
advance_to_next_rune(t);
|
||||
@@ -679,7 +683,7 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
}
|
||||
|
||||
if (valid && len != 1)
|
||||
tokenizer_error(t, "Illegal rune literal");
|
||||
tokenizer_err(t, "Illegal rune literal");
|
||||
} break;
|
||||
|
||||
case '.':
|
||||
@@ -722,10 +726,8 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
case '&':
|
||||
token.kind = Token_And;
|
||||
if (t->curr_rune == '~') {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = token_type_variant2(t, Token_AndNot, Token_AndNotEq);
|
||||
} else {
|
||||
advance_to_next_rune(t);
|
||||
token.kind = token_type_variant3(t, Token_And, Token_AndEq, '&', Token_CmpAnd);
|
||||
if (t->curr_rune == '=') {
|
||||
token.kind = Token_CmpAndEq;
|
||||
@@ -735,8 +737,6 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
break;
|
||||
|
||||
case '|':
|
||||
token.kind = Token_Or;
|
||||
advance_to_next_rune(t);
|
||||
token.kind = token_type_variant3(t, Token_Or, Token_OrEq, '|', Token_CmpOr);
|
||||
if (t->curr_rune == '=') {
|
||||
token.kind = Token_CmpOrEq;
|
||||
@@ -746,7 +746,7 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
|
||||
default:
|
||||
if (curr_rune != GB_RUNE_BOM)
|
||||
tokenizer_error(t, "Illegal character: %c (%d) ", cast(char)curr_rune, curr_rune);
|
||||
tokenizer_err(t, "Illegal character: %c (%d) ", cast(char)curr_rune, curr_rune);
|
||||
token.kind = Token_Invalid;
|
||||
break;
|
||||
}
|
||||
|
||||
119
todo.md
119
todo.md
@@ -1,100 +1,31 @@
|
||||
# Odin Language Features
|
||||
# Todo
|
||||
|
||||
* variables
|
||||
* constants (compile-time)
|
||||
* procedures
|
||||
- overloading
|
||||
- polymorphic (poly prockets)
|
||||
- multiple return values
|
||||
- Optional forced checking
|
||||
- inline and outline actually meant it!
|
||||
- local scoped procedures
|
||||
* Maybe closures & lambdas?
|
||||
- named parameters
|
||||
- optional parameters
|
||||
* struct
|
||||
* enum
|
||||
* raw union
|
||||
* tagged union or variants or both?
|
||||
* pointers
|
||||
* pointer arithmetic
|
||||
* defer statement
|
||||
* death to headers
|
||||
- no pre-declaration
|
||||
* maybe both inline assembly and intrinsics
|
||||
* `using`
|
||||
* metaprogramming
|
||||
- Compile execution
|
||||
- Introspection
|
||||
- Any type
|
||||
* type_of?
|
||||
## Tokenizer
|
||||
* Unicode character category check - Letters, Digits
|
||||
* Extra operators
|
||||
- << and <<=
|
||||
- >> and >>=
|
||||
|
||||
## Parser
|
||||
* Extra checking here rather than in the checker
|
||||
* Mulitple files
|
||||
|
||||
## Basic Types
|
||||
bool - true|false
|
||||
- register size or variable size?
|
||||
## Checker
|
||||
* Cyclic Type Checking
|
||||
- type A: struct { b: B; }; type B: struct { a: A; };
|
||||
- ^ Should be illegal as it's a cyclic definition
|
||||
* Big numbers library
|
||||
- integer
|
||||
- rational
|
||||
- real
|
||||
* Multiple files
|
||||
|
||||
u8 - Unsigned integer
|
||||
u16
|
||||
u32
|
||||
u64
|
||||
uint - Register size unsigned integer
|
||||
uintptr - integer big enough to store a pointer
|
||||
|
||||
i8 - Signed integer
|
||||
i16
|
||||
i32
|
||||
i64
|
||||
int - Register size signed integer
|
||||
intptr - integer big enough to store a pointer
|
||||
|
||||
|
||||
f32 - Floating Point 32 bit
|
||||
f64 - Floating Point 64 bit
|
||||
|
||||
byte - alias for u8
|
||||
rune - alias for i32
|
||||
|
||||
string - Immutable: once created, it is impossible to change the contents of a string
|
||||
arrays - ArrayType = [count]ElementType
|
||||
count = Expression
|
||||
ElementType = Type
|
||||
Allow for dynamic arrays? Allow use slices?
|
||||
pointers - PointerType = *BaseType
|
||||
BaseType = Type
|
||||
|
||||
struct
|
||||
enum
|
||||
raw_union
|
||||
tagged_union
|
||||
procedure
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Basic program
|
||||
|
||||
import "io"
|
||||
|
||||
type Vec2: struct {
|
||||
x, y: f32;
|
||||
}
|
||||
|
||||
test_proc :: (x, y: f32) -> f32 {
|
||||
result := x * y;
|
||||
result += 2;
|
||||
result /= x;
|
||||
return result;
|
||||
}
|
||||
|
||||
main :: () {
|
||||
x : i32 = 123;
|
||||
y := 1337; // Type inference (will be `int`)
|
||||
z :: x + y; // Defined and made constant
|
||||
|
||||
io.print("Hellope, World! z = {}", z);
|
||||
}
|
||||
## Codegen
|
||||
* Begin!!!
|
||||
* Emit LLVM-IR using custom library
|
||||
* Debug info
|
||||
|
||||
## Command Line Tool
|
||||
* Begin!!!
|
||||
* Choose/determine architecture
|
||||
|
||||
|
||||
Reference in New Issue
Block a user