mirror of
https://github.com/odin-lang/Odin.git
synced 2026-02-17 08:34:08 +00:00
Compound literals and Warnings
This commit is contained in:
@@ -1,27 +1,10 @@
|
||||
// import "other"
|
||||
|
||||
TAU :: 6.28;
|
||||
PI :: PI/2;
|
||||
|
||||
type AddProc: proc(a, b: int) -> int;
|
||||
|
||||
|
||||
do_thing :: proc(p: AddProc) {
|
||||
p(1, 2);
|
||||
}
|
||||
|
||||
add :: proc(a, b: int) -> int {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
|
||||
main :: proc() {
|
||||
x : int = 2;
|
||||
x = x * 3;
|
||||
|
||||
// do_thing(add(1, x));
|
||||
do_thing(proc(a, b: int) -> f32 {
|
||||
return a*b - a%b;
|
||||
});
|
||||
|
||||
type Vec2: struct { x, y: f32; }
|
||||
v := Vec2{1, 1};
|
||||
a := [2]int{1, 2}; // Array 2 of int
|
||||
s := []int{1, 2}; // Slice of int
|
||||
_, _ = a, s;
|
||||
// Equivalent to
|
||||
// sa := [2]int{1, 2}; s := sa[:];
|
||||
v.x = 1;
|
||||
}
|
||||
|
||||
@@ -151,12 +151,11 @@ gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = {
|
||||
{STR_LIT("println"), 1, true, Expression_Statement},
|
||||
};
|
||||
|
||||
struct ProcedureContext {
|
||||
struct CheckerContext {
|
||||
Scope *scope;
|
||||
DeclarationInfo *decl;
|
||||
};
|
||||
|
||||
|
||||
struct Checker {
|
||||
Parser * parser;
|
||||
Map<TypeAndValue> types; // Key: AstNode * | Expression -> Type (and value)
|
||||
@@ -174,17 +173,14 @@ struct Checker {
|
||||
gbArena arena;
|
||||
gbAllocator allocator;
|
||||
|
||||
ProcedureContext proc_context;
|
||||
CheckerContext context;
|
||||
|
||||
gbArray(Type *) procedure_stack;
|
||||
b32 in_defer; // TODO(bill): Actually handle correctly
|
||||
|
||||
isize error_prev_line;
|
||||
isize error_prev_column;
|
||||
isize error_count;
|
||||
ErrorCollector error_collector;
|
||||
};
|
||||
|
||||
|
||||
gb_global Scope *universal_scope = NULL;
|
||||
|
||||
|
||||
@@ -199,9 +195,20 @@ Scope *make_scope(Scope *parent, gbAllocator allocator) {
|
||||
}
|
||||
|
||||
void destroy_scope(Scope *scope) {
|
||||
isize element_count = gb_array_count(scope->elements.entries);
|
||||
for (isize i = 0; i < element_count; i++) {
|
||||
Entity *e =scope->elements.entries[i].value;
|
||||
if (e->kind == Entity_Variable) {
|
||||
if (!e->variable.used) {
|
||||
warning(e->token, "Unused variable `%.*s`", LIT(e->token.string));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Scope *child = scope->first_child; child != NULL; child = child->next) {
|
||||
destroy_scope(child);
|
||||
}
|
||||
|
||||
map_destroy(&scope->elements);
|
||||
// NOTE(bill): No need to free scope as it "should" be allocated in an arena (except for the global scope)
|
||||
}
|
||||
@@ -254,10 +261,10 @@ void add_dependency(DeclarationInfo *d, Entity *e) {
|
||||
}
|
||||
|
||||
void add_declaration_dependency(Checker *c, Entity *e) {
|
||||
if (c->proc_context.decl) {
|
||||
if (c->context.decl) {
|
||||
auto found = map_get(&c->entities, hash_pointer(e));
|
||||
if (found) {
|
||||
add_dependency(c->proc_context.decl, e);
|
||||
add_dependency(c->context.decl, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -349,7 +356,7 @@ void init_checker(Checker *c, Parser *parser) {
|
||||
c->allocator = gb_arena_allocator(&c->arena);
|
||||
|
||||
c->global_scope = make_scope(universal_scope, c->allocator);
|
||||
c->proc_context.scope = c->global_scope;
|
||||
c->context.scope = c->global_scope;
|
||||
}
|
||||
|
||||
void destroy_checker(Checker *c) {
|
||||
@@ -367,32 +374,6 @@ void destroy_checker(Checker *c) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
#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
|
||||
if (!(c->error_prev_line == token.line && c->error_prev_column == token.column)) {
|
||||
c->error_prev_line = token.line;
|
||||
c->error_prev_column = token.column;
|
||||
|
||||
#if 0
|
||||
gb_printf_err("%s()\n", function);
|
||||
#endif
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
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
|
||||
}
|
||||
|
||||
TypeAndValue *type_and_value_of_expression(Checker *c, AstNode *expression) {
|
||||
TypeAndValue *found = map_get(&c->types, hash_pointer(expression));
|
||||
return found;
|
||||
@@ -458,7 +439,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) {
|
||||
checker_err(c, entity->token, "Redeclared entity in this scope: %.*s", LIT(entity->token.string));
|
||||
error(&c->error_collector, entity->token, "Redeclared entity in this scope: %.*s", LIT(entity->token.string));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -504,13 +485,13 @@ void add_scope(Checker *c, AstNode *node, Scope *scope) {
|
||||
|
||||
|
||||
void check_open_scope(Checker *c, AstNode *statement) {
|
||||
Scope *scope = make_scope(c->proc_context.scope, c->allocator);
|
||||
Scope *scope = make_scope(c->context.scope, c->allocator);
|
||||
add_scope(c, statement, scope);
|
||||
c->proc_context.scope = scope;
|
||||
c->context.scope = scope;
|
||||
}
|
||||
|
||||
void check_close_scope(Checker *c) {
|
||||
c->proc_context.scope = c->proc_context.scope->parent;
|
||||
c->context.scope = c->context.scope->parent;
|
||||
}
|
||||
|
||||
void push_procedure(Checker *c, Type *procedure_type) {
|
||||
@@ -523,8 +504,7 @@ void pop_procedure(Checker *c) {
|
||||
|
||||
|
||||
void add_curr_ast_file(Checker *c, AstFile *file) {
|
||||
c->error_prev_line = 0;
|
||||
c->error_prev_column = 0;
|
||||
gb_zero_item(&c->error_collector);
|
||||
c->curr_ast_file = file;
|
||||
}
|
||||
|
||||
@@ -568,7 +548,7 @@ void check_parsed_files(Checker *c) {
|
||||
name = name->next, value = value->next) {
|
||||
GB_ASSERT(name->kind == AstNode_Identifier);
|
||||
ExactValue v = {ExactValue_Invalid};
|
||||
Entity *e = make_entity_constant(c->allocator, c->proc_context.scope, name->identifier.token, NULL, v);
|
||||
Entity *e = make_entity_constant(c->allocator, c->context.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;
|
||||
@@ -579,9 +559,9 @@ void check_parsed_files(Checker *c) {
|
||||
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");
|
||||
error(&c->error_collector, 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");
|
||||
error(&c->error_collector, ast_node_token(decl), "Extra initial expression");
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -647,7 +627,7 @@ void check_parsed_files(Checker *c) {
|
||||
break;
|
||||
|
||||
default:
|
||||
checker_err(c, ast_node_token(decl), "Only declarations are allowed at file scope");
|
||||
error(&c->error_collector, ast_node_token(decl), "Only declarations are allowed at file scope");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
|
||||
GB_ASSERT(struct_type->kind == Type_Structure);
|
||||
auto *st = &node->struct_type;
|
||||
if (st->field_count == 0) {
|
||||
checker_err(c, ast_node_token(node), "Empty struct{} definition");
|
||||
error(&c->error_collector, ast_node_token(node), "Empty struct{} definition");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) {
|
||||
GB_ASSERT(name->kind == AstNode_Identifier);
|
||||
Token name_token = name->identifier.token;
|
||||
// TODO(bill): is the curr_scope correct?
|
||||
Entity *e = make_entity_field(c->allocator, c->proc_context.scope, name_token, type);
|
||||
Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type);
|
||||
u64 key = hash_string(name_token.string);
|
||||
if (map_get(&entity_map, key)) {
|
||||
// TODO(bill): Scope checking already checks the declaration
|
||||
checker_err(c, name_token, "`%.*s` is already declared in this structure", LIT(name_token.string));
|
||||
error(&c->error_collector, name_token, "`%.*s` is already declared in this structure", LIT(name_token.string));
|
||||
} else {
|
||||
map_set(&entity_map, key, e);
|
||||
fields[field_index++] = e;
|
||||
@@ -77,8 +77,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel
|
||||
add_entity(c, scope, name, param);
|
||||
variables[variable_index++] = param;
|
||||
} else {
|
||||
checker_err(c, ast_node_token(name),
|
||||
"Invalid parameter (invalid AST)");
|
||||
error(&c->error_collector, ast_node_token(name), "Invalid parameter (invalid AST)");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,7 +106,7 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *list, isize list_coun
|
||||
|
||||
if (get_base_type(type)->kind == Type_Array) {
|
||||
// TODO(bill): Should I allow array's to returned?
|
||||
checker_err(c, token, "You cannot return an array from a procedure");
|
||||
error(&c->error_collector, token, "You cannot return an array from a procedure");
|
||||
}
|
||||
}
|
||||
tuple->tuple.variables = variables;
|
||||
@@ -123,10 +122,10 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
|
||||
|
||||
// gb_printf("%td -> %td\n", param_count, result_count);
|
||||
|
||||
Type *params = check_get_params(c, c->proc_context.scope, proc_type_node->procedure_type.param_list, param_count);
|
||||
Type *results = check_get_results(c, c->proc_context.scope, proc_type_node->procedure_type.results_list, result_count);
|
||||
Type *params = check_get_params(c, c->context.scope, proc_type_node->procedure_type.param_list, param_count);
|
||||
Type *results = check_get_results(c, c->context.scope, proc_type_node->procedure_type.results_list, result_count);
|
||||
|
||||
type->procedure.scope = c->proc_context.scope;
|
||||
type->procedure.scope = c->context.scope;
|
||||
type->procedure.params = params;
|
||||
type->procedure.params_count = proc_type_node->procedure_type.param_count;
|
||||
type->procedure.results = results;
|
||||
@@ -138,11 +137,10 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type) {
|
||||
GB_ASSERT(n->kind == AstNode_Identifier);
|
||||
o->mode = Addressing_Invalid;
|
||||
o->expression = n;
|
||||
Entity *e = NULL;
|
||||
scope_lookup_parent_entity(c->proc_context.scope, n->identifier.token.string, NULL, &e);
|
||||
Entity *e = scope_lookup_entity(c->context.scope, n->identifier.token.string);
|
||||
if (e == NULL) {
|
||||
checker_err(c, n->identifier.token,
|
||||
"Undeclared type or identifier `%.*s`", LIT(n->identifier.token.string));
|
||||
error(&c->error_collector, n->identifier.token,
|
||||
"Undeclared type or identifier `%.*s`", LIT(n->identifier.token.string));
|
||||
return;
|
||||
}
|
||||
add_entity_use(c, n, e);
|
||||
@@ -200,7 +198,7 @@ i64 check_array_count(Checker *c, AstNode *e) {
|
||||
check_expression(c, &o, e);
|
||||
if (o.mode != Addressing_Constant) {
|
||||
if (o.mode != Addressing_Invalid) {
|
||||
checker_err(c, ast_node_token(e), "Array count must be a constant");
|
||||
error(&c->error_collector, ast_node_token(e), "Array count must be a constant");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -209,12 +207,12 @@ i64 check_array_count(Checker *c, AstNode *e) {
|
||||
i64 count = o.value.value_integer;
|
||||
if (count >= 0)
|
||||
return count;
|
||||
checker_err(c, ast_node_token(e), "Invalid array count");
|
||||
error(&c->error_collector, ast_node_token(e), "Invalid array count");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
checker_err(c, ast_node_token(e), "Array count must be an integer");
|
||||
error(&c->error_collector, ast_node_token(e), "Array count must be an integer");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -239,11 +237,11 @@ Type *check_type_expression_extra(Checker *c, AstNode *e, Type *named_type) {
|
||||
|
||||
case Addressing_NoValue:
|
||||
err_str = expression_to_string(e);
|
||||
checker_err(c, ast_node_token(e), "`%s` used as a type", err_str);
|
||||
error(&c->error_collector, ast_node_token(e), "`%s` used as a type", err_str);
|
||||
break;
|
||||
default:
|
||||
err_str = expression_to_string(e);
|
||||
checker_err(c, ast_node_token(e), "`%s` used as a type when not a type", err_str);
|
||||
error(&c->error_collector, ast_node_token(e), "`%s` used as a type when not a type", err_str);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
@@ -289,7 +287,7 @@ Type *check_type_expression_extra(Checker *c, AstNode *e, Type *named_type) {
|
||||
|
||||
default:
|
||||
err_str = expression_to_string(e);
|
||||
checker_err(c, ast_node_token(e), "`%s` is not a type", err_str);
|
||||
error(&c->error_collector, ast_node_token(e), "`%s` is not a type", err_str);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -321,11 +319,11 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type) {
|
||||
|
||||
case Addressing_NoValue:
|
||||
err_str = expression_to_string(e);
|
||||
checker_err(c, ast_node_token(e), "`%s` used as a type", err_str);
|
||||
error(&c->error_collector, ast_node_token(e), "`%s` used as a type", err_str);
|
||||
break;
|
||||
default:
|
||||
err_str = expression_to_string(e);
|
||||
checker_err(c, ast_node_token(e), "`%s` used as a type when not a type", err_str);
|
||||
error(&c->error_collector, ast_node_token(e), "`%s` used as a type when not a type", err_str);
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
@@ -378,7 +376,7 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type) {
|
||||
|
||||
default:
|
||||
err_str = expression_to_string(e);
|
||||
checker_err(c, ast_node_token(e), "`%s` is not a type", err_str);
|
||||
error(&c->error_collector, ast_node_token(e), "`%s` is not a type", err_str);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -401,25 +399,25 @@ b32 check_unary_op(Checker *c, Operand *o, Token op) {
|
||||
case Token_Sub:
|
||||
if (!is_type_numeric(o->type)) {
|
||||
str = expression_to_string(o->expression);
|
||||
checker_err(c, op, "Operator `%.*s` is not allowed with `%s`", LIT(op.string), str);
|
||||
error(&c->error_collector, op, "Operator `%.*s` is not allowed with `%s`", LIT(op.string), str);
|
||||
}
|
||||
break;
|
||||
|
||||
case Token_Xor:
|
||||
if (!is_type_integer(o->type)) {
|
||||
checker_err(c, op, "Operator `%.*s` is only allowed with integers", LIT(op.string));
|
||||
error(&c->error_collector, op, "Operator `%.*s` is only allowed with integers", LIT(op.string));
|
||||
}
|
||||
break;
|
||||
|
||||
case Token_Not:
|
||||
if (!is_type_boolean(o->type)) {
|
||||
str = expression_to_string(o->expression);
|
||||
checker_err(c, op, "Operator `%.*s` is only allowed on boolean expression", LIT(op.string));
|
||||
error(&c->error_collector, op, "Operator `%.*s` is only allowed on boolean expression", LIT(op.string));
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
checker_err(c, op, "Unknown operator `%.*s`", LIT(op.string));
|
||||
error(&c->error_collector, op, "Unknown operator `%.*s`", LIT(op.string));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -439,7 +437,7 @@ b32 check_binary_op(Checker *c, Operand *o, Token op) {
|
||||
case Token_MulEq:
|
||||
case Token_QuoEq:
|
||||
if (!is_type_numeric(o->type)) {
|
||||
checker_err(c, op, "Operator `%.*s` is only allowed with numeric expressions", LIT(op.string));
|
||||
error(&c->error_collector, op, "Operator `%.*s` is only allowed with numeric expressions", LIT(op.string));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@@ -456,7 +454,7 @@ b32 check_binary_op(Checker *c, Operand *o, Token op) {
|
||||
case Token_XorEq:
|
||||
case Token_AndNotEq:
|
||||
if (!is_type_integer(o->type)) {
|
||||
checker_err(c, op, "Operator `%.*s` is only allowed with integers", LIT(op.string));
|
||||
error(&c->error_collector, op, "Operator `%.*s` is only allowed with integers", LIT(op.string));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
@@ -467,13 +465,13 @@ b32 check_binary_op(Checker *c, Operand *o, Token op) {
|
||||
case Token_CmpAndEq:
|
||||
case Token_CmpOrEq:
|
||||
if (!is_type_boolean(o->type)) {
|
||||
checker_err(c, op, "Operator `%.*s` is only allowed with boolean expressions", LIT(op.string));
|
||||
error(&c->error_collector, op, "Operator `%.*s` is only allowed with boolean expressions", LIT(op.string));
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
checker_err(c, op, "Unknown operator `%.*s`", LIT(op.string));
|
||||
error(&c->error_collector, op, "Unknown operator `%.*s`", LIT(op.string));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -559,12 +557,12 @@ void check_is_expressible(Checker *c, Operand *o, Type *type) {
|
||||
defer (gb_string_free(b));
|
||||
if (is_type_numeric(o->type) && is_type_numeric(type)) {
|
||||
if (!is_type_integer(o->type) && is_type_integer(type)) {
|
||||
checker_err(c, ast_node_token(o->expression), "`%s` truncated to `%s`", a, b);
|
||||
error(&c->error_collector, ast_node_token(o->expression), "`%s` truncated to `%s`", a, b);
|
||||
} else {
|
||||
checker_err(c, ast_node_token(o->expression), "`%s` overflows to `%s`", a, b);
|
||||
error(&c->error_collector, ast_node_token(o->expression), "`%s` overflows to `%s`", a, b);
|
||||
}
|
||||
} else {
|
||||
checker_err(c, ast_node_token(o->expression), "Cannot convert `%s` to `%s`", a, b);
|
||||
error(&c->error_collector, ast_node_token(o->expression), "Cannot convert `%s` to `%s`", a, b);
|
||||
}
|
||||
|
||||
o->mode = Addressing_Invalid;
|
||||
@@ -577,7 +575,7 @@ void check_unary_expression(Checker *c, Operand *o, Token op, AstNode *node) {
|
||||
if (o->mode != Addressing_Variable) {
|
||||
gbString str = expression_to_string(node->unary_expression.operand);
|
||||
defer (gb_string_free(str));
|
||||
checker_err(c, op, "Cannot take the pointer address of `%s`", str);
|
||||
error(&c->error_collector, op, "Cannot take the pointer address of `%s`", str);
|
||||
o->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
@@ -646,7 +644,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
|
||||
}
|
||||
|
||||
if (err_str) {
|
||||
checker_err(c, op, "Cannot compare expression, %s", err_str);
|
||||
error(&c->error_collector, op, "Cannot compare expression, %s", err_str);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -697,7 +695,7 @@ void check_binary_expression(Checker *c, Operand *x, AstNode *node) {
|
||||
defer (gb_string_free(xt));
|
||||
defer (gb_string_free(yt));
|
||||
err_str = expression_to_string(x->expression);
|
||||
checker_err(c, op, "Mismatched types in binary expression `%s` : `%s` vs `%s`", err_str, xt, yt);
|
||||
error(&c->error_collector, op, "Mismatched types in binary expression `%s` : `%s` vs `%s`", err_str, xt, yt);
|
||||
}
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
@@ -728,7 +726,7 @@ void check_binary_expression(Checker *c, Operand *x, AstNode *node) {
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
checker_err(c, ast_node_token(y->expression), "Division by zero not allowed");
|
||||
error(&c->error_collector, ast_node_token(y->expression), "Division by zero not allowed");
|
||||
x->mode = Addressing_Invalid;
|
||||
return;
|
||||
}
|
||||
@@ -805,7 +803,7 @@ void convert_untyped_error(Checker *c, Operand *operand, Type *target_type) {
|
||||
extra_text = " - Did you want `null`?";
|
||||
}
|
||||
}
|
||||
checker_err(c, ast_node_token(operand->expression), "Cannot convert `%s` to `%s`%s", expr_str, type_str, extra_text);
|
||||
error(&c->error_collector, ast_node_token(operand->expression), "Cannot convert `%s` to `%s`%s", expr_str, type_str, extra_text);
|
||||
|
||||
operand->mode = Addressing_Invalid;
|
||||
}
|
||||
@@ -896,7 +894,7 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu
|
||||
|
||||
if (!is_type_integer(operand.type)) {
|
||||
gbString expr_str = expression_to_string(operand.expression);
|
||||
checker_err(c, ast_node_token(operand.expression),
|
||||
error(&c->error_collector, ast_node_token(operand.expression),
|
||||
"Index `%s` must be an integer", expr_str);
|
||||
gb_string_free(expr_str);
|
||||
if (value) *value = 0;
|
||||
@@ -908,7 +906,7 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu
|
||||
i64 i = exact_value_to_integer(operand.value).value_integer;
|
||||
if (i < 0) {
|
||||
gbString expr_str = expression_to_string(operand.expression);
|
||||
checker_err(c, ast_node_token(operand.expression),
|
||||
error(&c->error_collector, ast_node_token(operand.expression),
|
||||
"Index `%s` cannot be a negative value", expr_str);
|
||||
gb_string_free(expr_str);
|
||||
if (value) *value = 0;
|
||||
@@ -919,7 +917,7 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu
|
||||
|
||||
if (i >= max_count) {
|
||||
gbString expr_str = expression_to_string(operand.expression);
|
||||
checker_err(c, ast_node_token(operand.expression),
|
||||
error(&c->error_collector, ast_node_token(operand.expression),
|
||||
"Index `%s` is out of bounds range [0, %lld)", expr_str, max_count);
|
||||
gb_string_free(expr_str);
|
||||
return false;
|
||||
@@ -973,7 +971,7 @@ void check_selector(Checker *c, Operand *operand, AstNode *node) {
|
||||
gbString sel_str = expression_to_string(selector);
|
||||
defer (gb_string_free(op_str));
|
||||
defer (gb_string_free(sel_str));
|
||||
checker_err(c, ast_node_token(op_expr), "`%s` has no field `%s`", op_str, sel_str);
|
||||
error(&c->error_collector, ast_node_token(op_expr), "`%s` has no field `%s`", op_str, sel_str);
|
||||
operand->mode = Addressing_Invalid;
|
||||
operand->expression = node;
|
||||
return;
|
||||
@@ -1002,9 +1000,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
if (ce->arg_list_count > bp->arg_count && !bp->variadic)
|
||||
err = "Too many";
|
||||
if (err) {
|
||||
checker_err(c, ce->close, "`%s` arguments for `%.*s`, expected %td, got %td",
|
||||
err, LIT(call->call_expression.proc->identifier.token.string),
|
||||
bp->arg_count, ce->arg_list_count);
|
||||
error(&c->error_collector, ce->close, "`%s` arguments for `%.*s`, expected %td, got %td",
|
||||
err, LIT(call->call_expression.proc->identifier.token.string),
|
||||
bp->arg_count, ce->arg_list_count);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1024,7 +1022,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
// size_of :: proc(Type)
|
||||
Type *type = check_type(c, ce->arg_list);
|
||||
if (!type) {
|
||||
checker_err(c, ast_node_token(ce->arg_list), "Expected a type for `size_of`");
|
||||
error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a type for `size_of`");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1049,7 +1047,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
// align_of :: proc(Type)
|
||||
Type *type = check_type(c, ce->arg_list);
|
||||
if (!type) {
|
||||
checker_err(c, ast_node_token(ce->arg_list), "Expected a type for `align_of`");
|
||||
error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a type for `align_of`");
|
||||
return false;
|
||||
}
|
||||
operand->mode = Addressing_Constant;
|
||||
@@ -1074,12 +1072,12 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
AstNode *field_arg = unparen_expression(ce->arg_list->next);
|
||||
if (type) {
|
||||
if (type->kind != Type_Structure) {
|
||||
checker_err(c, ast_node_token(ce->arg_list), "Expected a structure type for `offset_of`");
|
||||
error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a structure type for `offset_of`");
|
||||
return false;
|
||||
}
|
||||
if (field_arg == NULL ||
|
||||
field_arg->kind != AstNode_Identifier) {
|
||||
checker_err(c, ast_node_token(field_arg), "Expected an identifier for field argument");
|
||||
error(&c->error_collector, ast_node_token(field_arg), "Expected an identifier for field argument");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1088,8 +1086,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
Entity *entity = lookup_field(type, field_arg, &index);
|
||||
if (entity == NULL) {
|
||||
gbString type_str = type_to_string(type);
|
||||
checker_err(c, ast_node_token(ce->arg_list),
|
||||
"`%s` has no field named `%.*s`", type_str, LIT(field_arg->identifier.token.string));
|
||||
error(&c->error_collector, ast_node_token(ce->arg_list),
|
||||
"`%s` has no field named `%.*s`", type_str, LIT(field_arg->identifier.token.string));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1103,7 +1101,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
AstNode *arg = unparen_expression(ce->arg_list);
|
||||
if (arg->kind != AstNode_SelectorExpression) {
|
||||
gbString str = expression_to_string(arg);
|
||||
checker_err(c, ast_node_token(arg), "`%s` is not a selector expression", str);
|
||||
error(&c->error_collector, ast_node_token(arg), "`%s` is not a selector expression", str);
|
||||
return false;
|
||||
}
|
||||
auto *s = &arg->selector_expression;
|
||||
@@ -1123,8 +1121,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
Entity *entity = lookup_field(type, s->selector, &index);
|
||||
if (entity == NULL) {
|
||||
gbString type_str = type_to_string(type);
|
||||
checker_err(c, ast_node_token(arg),
|
||||
"`%s` has no field named `%.*s`", type_str, LIT(s->selector->identifier.token.string));
|
||||
error(&c->error_collector, ast_node_token(arg),
|
||||
"`%s` has no field named `%.*s`", type_str, LIT(s->selector->identifier.token.string));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1141,15 +1139,15 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
!is_type_boolean(operand->type)) {
|
||||
gbString str = expression_to_string(ce->arg_list);
|
||||
defer (gb_string_free(str));
|
||||
checker_err(c, ast_node_token(call),
|
||||
"`%s` is not a constant boolean", str);
|
||||
error(&c->error_collector, ast_node_token(call),
|
||||
"`%s` is not a constant boolean", str);
|
||||
return false;
|
||||
}
|
||||
if (!operand->value.value_bool) {
|
||||
gbString str = expression_to_string(ce->arg_list);
|
||||
defer (gb_string_free(str));
|
||||
checker_err(c, ast_node_token(call),
|
||||
"Static assertion: `%s`", str);
|
||||
error(&c->error_collector, ast_node_token(call),
|
||||
"Static assertion: `%s`", str);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
@@ -1188,9 +1186,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
|
||||
if (mode == Addressing_Invalid) {
|
||||
gbString str = expression_to_string(operand->expression);
|
||||
checker_err(c, ast_node_token(operand->expression),
|
||||
"Invalid expression `%s` for `%.*s`",
|
||||
str, LIT(bp->name));
|
||||
error(&c->error_collector, ast_node_token(operand->expression),
|
||||
"Invalid expression `%s` for `%.*s`",
|
||||
str, LIT(bp->name));
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
@@ -1219,7 +1217,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
src_type = s->slice.element;
|
||||
|
||||
if (dest_type == NULL || src_type == NULL) {
|
||||
checker_err(c, ast_node_token(call), "`copy` only expects slices as arguments");
|
||||
error(&c->error_collector, ast_node_token(call), "`copy` only expects slices as arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1232,9 +1230,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
defer (gb_string_free(s_arg));
|
||||
defer (gb_string_free(d_str));
|
||||
defer (gb_string_free(s_str));
|
||||
checker_err(c, ast_node_token(call),
|
||||
"Arguments to `copy`, %s, %s, have different element types: %s vs %s",
|
||||
d_arg, s_arg, d_str, s_str);
|
||||
error(&c->error_collector, ast_node_token(call),
|
||||
"Arguments to `copy`, %s, %s, have different element types: %s vs %s",
|
||||
d_arg, s_arg, d_str, s_str);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1259,7 +1257,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
src_type = s;
|
||||
|
||||
if (dest_type == NULL || src_type == NULL) {
|
||||
checker_err(c, ast_node_token(call), "`copy_bytes` only expects pointers for the destintation and source");
|
||||
error(&c->error_collector, ast_node_token(call), "`copy_bytes` only expects pointers for the destintation and source");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1273,13 +1271,13 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
|
||||
op.type->basic.kind != Basic_int) {
|
||||
gbString str = type_to_string(op.type);
|
||||
defer (gb_string_free(str));
|
||||
checker_err(c, ast_node_token(call), "`copy_bytes` 3rd argument must be of type `int`, a `%s` was given", str);
|
||||
error(&c->error_collector, ast_node_token(call), "`copy_bytes` 3rd argument must be of type `int`, a `%s` was given", str);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (op.mode == Addressing_Constant) {
|
||||
if (exact_value_to_integer(op.value).value_integer <= 0) {
|
||||
checker_err(c, ast_node_token(call), "You cannot copy a zero or negative amount of bytes with `copy_bytes`");
|
||||
error(&c->error_collector, ast_node_token(call), "You cannot copy a zero or negative amount of bytes with `copy_bytes`");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1372,7 +1370,7 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode
|
||||
}
|
||||
|
||||
gbString proc_str = expression_to_string(ce->proc);
|
||||
checker_err(c, ast_node_token(call), err_fmt, proc_str, param_count);
|
||||
error(&c->error_collector, ast_node_token(call), err_fmt, proc_str, param_count);
|
||||
gb_string_free(proc_str);
|
||||
|
||||
operand->mode = Addressing_Invalid;
|
||||
@@ -1406,7 +1404,7 @@ ExpressionKind check_call_expression(Checker *c, Operand *operand, AstNode *call
|
||||
AstNode *e = operand->expression;
|
||||
gbString str = expression_to_string(e);
|
||||
defer (gb_string_free(str));
|
||||
checker_err(c, ast_node_token(e), "Cannot call a non-procedure: `%s`", str);
|
||||
error(&c->error_collector, ast_node_token(e), "Cannot call a non-procedure: `%s`", str);
|
||||
|
||||
operand->mode = Addressing_Invalid;
|
||||
operand->expression = call;
|
||||
@@ -1494,7 +1492,7 @@ void check_cast_expression(Checker *c, Operand *operand, Type *type) {
|
||||
gbString type_str = type_to_string(type);
|
||||
defer (gb_string_free(expr_str));
|
||||
defer (gb_string_free(type_str));
|
||||
checker_err(c, ast_node_token(operand->expression), "Cannot cast `%s` to `%s`", expr_str, type_str);
|
||||
error(&c->error_collector, ast_node_token(operand->expression), "Cannot cast `%s` to `%s`", expr_str, type_str);
|
||||
|
||||
operand->mode = Addressing_Invalid;
|
||||
return;
|
||||
@@ -1503,6 +1501,28 @@ void check_cast_expression(Checker *c, Operand *operand, Type *type) {
|
||||
operand->type = type;
|
||||
}
|
||||
|
||||
void check_expression_with_type_hint(Checker *c, Operand *o, AstNode *e, Type *t) {
|
||||
check_expression_base(c, o, e, t);
|
||||
check_not_tuple(c, o);
|
||||
char *err_str = NULL;
|
||||
switch (o->mode) {
|
||||
case Addressing_NoValue:
|
||||
err_str = "used as a value";
|
||||
break;
|
||||
case Addressing_Type:
|
||||
err_str = "is not an expression";
|
||||
break;
|
||||
case Addressing_Builtin:
|
||||
err_str = "must be called";
|
||||
break;
|
||||
}
|
||||
if (err_str != NULL) {
|
||||
gbString str = expression_to_string(e);
|
||||
defer (gb_string_free(str));
|
||||
error(&c->error_collector, ast_node_token(e), "`%s` %s", str, err_str);
|
||||
o->mode = Addressing_Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Type *type_hint) {
|
||||
@@ -1534,20 +1554,104 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ
|
||||
} break;
|
||||
|
||||
case AstNode_ProcedureLiteral: {
|
||||
Scope *origin_curr_scope = c->proc_context.scope;
|
||||
Scope *origin_curr_scope = c->context.scope;
|
||||
Type *proc_type = check_type(c, node->procedure_literal.type);
|
||||
if (proc_type != NULL) {
|
||||
check_procedure_body(c, empty_token, c->proc_context.decl, proc_type, node->procedure_literal.body);
|
||||
check_procedure_body(c, empty_token, c->context.decl, proc_type, node->procedure_literal.body);
|
||||
o->mode = Addressing_Value;
|
||||
o->type = proc_type;
|
||||
} else {
|
||||
gbString str = expression_to_string(node);
|
||||
checker_err(c, ast_node_token(node), "Invalid procedure literal `%s`", str);
|
||||
error(&c->error_collector, ast_node_token(node), "Invalid procedure literal `%s`", str);
|
||||
gb_string_free(str);
|
||||
goto error;
|
||||
}
|
||||
} break;
|
||||
|
||||
case AstNode_CompoundLiteral: {
|
||||
auto *cl = &node->compound_literal;
|
||||
Type *type = type_hint;
|
||||
if (cl->type_expression != NULL) {
|
||||
type = check_type(c, cl->type_expression);
|
||||
}
|
||||
|
||||
if (type == NULL) {
|
||||
error(&c->error_collector, ast_node_token(node), "Missing type in compound literal");
|
||||
goto error;
|
||||
}
|
||||
|
||||
Type *t = get_base_type(type);
|
||||
switch (t->kind) {
|
||||
case Type_Structure: {
|
||||
if (cl->element_count == 0)
|
||||
break; // NOTE(bill): No need to init
|
||||
{ // Checker values
|
||||
AstNode *elem = cl->element_list;
|
||||
isize field_count = t->structure.field_count;
|
||||
isize index = 0;
|
||||
for (;
|
||||
elem != NULL;
|
||||
elem = elem->next, index++) {
|
||||
Entity *field = t->structure.fields[index];
|
||||
|
||||
check_expression(c, o, elem);
|
||||
if (index >= field_count) {
|
||||
error(&c->error_collector, ast_node_token(o->expression), "Too many values in structure literal, expected %td", field_count);
|
||||
break;
|
||||
}
|
||||
check_assignment(c, o, field->type, make_string("structure literal"));
|
||||
}
|
||||
if (cl->element_count < field_count) {
|
||||
error(&c->error_collector, node->compound_literal.close, "Too few values in structure literal, expected %td, got %td", field_count, cl->element_count);
|
||||
}
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case Type_Slice:
|
||||
case Type_Array:
|
||||
{
|
||||
Type *element_type = NULL;
|
||||
String context_name = {};
|
||||
if (t->kind == Type_Slice) {
|
||||
element_type = t->slice.element;
|
||||
context_name = make_string("slice literal");
|
||||
} else {
|
||||
element_type = t->array.element;
|
||||
context_name = make_string("array literal");
|
||||
}
|
||||
|
||||
|
||||
i64 index = 0;
|
||||
i64 max = 0;
|
||||
for (AstNode *elem = cl->element_list; elem != NULL; elem = elem->next, index++) {
|
||||
AstNode *e = elem;
|
||||
if (t->kind == Type_Array &&
|
||||
t->array.count >= 0 &&
|
||||
index >= t->array.count) {
|
||||
error(&c->error_collector, ast_node_token(elem), "Index %lld is out of bounds (>= %lld)", index, t->array.count);
|
||||
}
|
||||
|
||||
Operand o = {};
|
||||
check_expression_with_type_hint(c, &o, e, element_type);
|
||||
check_assignment(c, &o, element_type, context_name);
|
||||
}
|
||||
if (max < index)
|
||||
max = index;
|
||||
} break;
|
||||
|
||||
default: {
|
||||
gbString str = type_to_string(t);
|
||||
error(&c->error_collector, ast_node_token(node), "Invalid compound literal type `%s`", str);
|
||||
gb_string_free(str);
|
||||
goto error;
|
||||
} break;
|
||||
}
|
||||
|
||||
o->mode = Addressing_Value;
|
||||
o->type = type;
|
||||
} break;
|
||||
|
||||
case AstNode_ParenExpression:
|
||||
kind = check_expression_base(c, o, node->paren_expression.expression, type_hint);
|
||||
o->expression = node;
|
||||
@@ -1555,7 +1659,7 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ
|
||||
|
||||
case AstNode_TagExpression:
|
||||
// TODO(bill): Tag expressions
|
||||
checker_err(c, ast_node_token(node), "Tag expressions are not supported yet");
|
||||
error(&c->error_collector, ast_node_token(node), "Tag expressions are not supported yet");
|
||||
kind = check_expression_base(c, o, node->tag_expression.expression, type_hint);
|
||||
o->expression = node;
|
||||
break;
|
||||
@@ -1624,14 +1728,14 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ
|
||||
|
||||
if (!valid) {
|
||||
gbString str = expression_to_string(o->expression);
|
||||
checker_err(c, ast_node_token(o->expression), "Cannot index `%s`", str);
|
||||
error(&c->error_collector, ast_node_token(o->expression), "Cannot index `%s`", str);
|
||||
gb_string_free(str);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (node->index_expression.value == NULL) {
|
||||
gbString str = expression_to_string(o->expression);
|
||||
checker_err(c, ast_node_token(o->expression), "Missing index for `%s`", str);
|
||||
error(&c->error_collector, ast_node_token(o->expression), "Missing index for `%s`", str);
|
||||
gb_string_free(str);
|
||||
goto error;
|
||||
}
|
||||
@@ -1665,7 +1769,7 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ
|
||||
max_count = t->array.count;
|
||||
if (o->mode != Addressing_Variable) {
|
||||
gbString str = expression_to_string(node);
|
||||
checker_err(c, ast_node_token(node), "Cannot slice array `%s`, value is not addressable", str);
|
||||
error(&c->error_collector, ast_node_token(node), "Cannot slice array `%s`, value is not addressable", str);
|
||||
gb_string_free(str);
|
||||
goto error;
|
||||
}
|
||||
@@ -1687,7 +1791,7 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ
|
||||
|
||||
if (!valid) {
|
||||
gbString str = expression_to_string(o->expression);
|
||||
checker_err(c, ast_node_token(o->expression), "Cannot slice `%s`", str);
|
||||
error(&c->error_collector, ast_node_token(o->expression), "Cannot slice `%s`", str);
|
||||
gb_string_free(str);
|
||||
goto error;
|
||||
}
|
||||
@@ -1715,7 +1819,7 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ
|
||||
for (isize j = i+1; j < gb_count_of(indices); j++) {
|
||||
i64 b = indices[j];
|
||||
if (a > b && b >= 0) {
|
||||
checker_err(c, se->close, "Invalid slice indices: [%td > %td]", a, b);
|
||||
error(&c->error_collector, se->close, "Invalid slice indices: [%td > %td]", a, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1744,7 +1848,7 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ
|
||||
o->type = t->pointer.element;
|
||||
} else {
|
||||
gbString str = expression_to_string(o->expression);
|
||||
checker_err(c, ast_node_token(o->expression), "Cannot dereference `%s`", str);
|
||||
error(&c->error_collector, ast_node_token(o->expression), "Cannot dereference `%s`", str);
|
||||
gb_string_free(str);
|
||||
goto error;
|
||||
}
|
||||
@@ -1812,11 +1916,11 @@ void check_multi_expression(Checker *c, Operand *o, AstNode *e) {
|
||||
|
||||
case Addressing_NoValue:
|
||||
err_str = expression_to_string(e);
|
||||
checker_err(c, ast_node_token(e), "`%s` used as value", err_str);
|
||||
error(&c->error_collector, ast_node_token(e), "`%s` used as value", err_str);
|
||||
break;
|
||||
case Addressing_Type:
|
||||
err_str = expression_to_string(e);
|
||||
checker_err(c, ast_node_token(e), "`%s` is not an expression", err_str);
|
||||
error(&c->error_collector, ast_node_token(e), "`%s` is not an expression", err_str);
|
||||
break;
|
||||
}
|
||||
o->mode = Addressing_Invalid;
|
||||
@@ -1829,8 +1933,8 @@ void check_not_tuple(Checker *c, Operand *o) {
|
||||
if (o->type->kind == Type_Tuple) {
|
||||
isize count = o->type->tuple.variable_count;
|
||||
GB_ASSERT(count != 1);
|
||||
checker_err(c, ast_node_token(o->expression),
|
||||
"%td-valued tuple found where single value expected", count);
|
||||
error(&c->error_collector, ast_node_token(o->expression),
|
||||
"%td-valued tuple found where single value expected", count);
|
||||
o->mode = Addressing_Invalid;
|
||||
}
|
||||
}
|
||||
@@ -1849,8 +1953,8 @@ void check_expression_or_type(Checker *c, Operand *o, AstNode *e) {
|
||||
AstNode *e = o->expression;
|
||||
gbString str = expression_to_string(e);
|
||||
defer (gb_string_free(str));
|
||||
checker_err(c, ast_node_token(e),
|
||||
"`%s` used as value or type", str);
|
||||
error(&c->error_collector, ast_node_token(e),
|
||||
"`%s` used as value or type", str);
|
||||
o->mode = Addressing_Invalid;
|
||||
}
|
||||
}
|
||||
@@ -1898,14 +2002,17 @@ gbString write_expression_to_string(gbString str, AstNode *node) {
|
||||
case AstNode_Identifier:
|
||||
str = string_append_token(str, node->identifier.token);
|
||||
break;
|
||||
|
||||
case AstNode_BasicLiteral:
|
||||
str = string_append_token(str, node->basic_literal);
|
||||
break;
|
||||
|
||||
case AstNode_ProcedureLiteral:
|
||||
str = write_expression_to_string(str, node->procedure_literal.type);
|
||||
break;
|
||||
case AstNode_CompoundLiteral:
|
||||
str = gb_string_appendc(str, "(");
|
||||
str = write_expression_to_string(str, node->compound_literal.type_expression);
|
||||
str = gb_string_appendc(str, " literal)");
|
||||
break;
|
||||
|
||||
case AstNode_TagExpression:
|
||||
str = gb_string_appendc(str, "#");
|
||||
|
||||
@@ -9,29 +9,23 @@ void check_statement_list(Checker *c, AstNode *node) {
|
||||
|
||||
|
||||
// NOTE(bill): The last expression has to be a `return` statement
|
||||
// TODO(bill): This is a mild hack and should be probably handled
|
||||
// TODO(bill): This is a mild hack and should be probably handled properly
|
||||
// TODO(bill): Warn/err against code after `return` that it won't be executed
|
||||
b32 check_is_terminating(Checker *c, AstNode *node);
|
||||
|
||||
b32 check_is_terminating_list(Checker *c, AstNode *node_list) {
|
||||
AstNode *end_of_list = node_list;
|
||||
for (; end_of_list != NULL; end_of_list = end_of_list->next) {
|
||||
if (end_of_list->next == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
for (AstNode *node = end_of_list; node != NULL; node = node->prev) {
|
||||
if (node->kind == AstNode_EmptyStatement)
|
||||
continue;
|
||||
return check_is_terminating(c, node);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
b32 check_is_terminating(Checker *c, AstNode *node) {
|
||||
switch (node->kind) {
|
||||
case AstNode_BlockStatement:
|
||||
return check_is_terminating_list(c, node->block_statement.list);
|
||||
case AstNode_BlockStatement: {
|
||||
for (; node != NULL; node = node->next) {
|
||||
if (node->next == NULL)
|
||||
break;
|
||||
}
|
||||
|
||||
for (AstNode *n = node; node != NULL; n = n->prev) {
|
||||
if (n->kind == AstNode_EmptyStatement)
|
||||
continue;
|
||||
return check_is_terminating(c, n);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
case AstNode_ExpressionStatement:
|
||||
return check_is_terminating(c, node->expression_statement.expression);
|
||||
@@ -138,12 +132,12 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
|
||||
|
||||
|
||||
// TODO(bill): is this a good enough error message?
|
||||
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));
|
||||
error(&c->error_collector, 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;
|
||||
}
|
||||
@@ -151,7 +145,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
|
||||
}
|
||||
|
||||
|
||||
Type *check_assign_variable(Checker *c, Operand *op_a, AstNode *lhs) {
|
||||
Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
|
||||
if (op_a->mode == Addressing_Invalid ||
|
||||
op_a->type == &basic_types[Basic_Invalid]) {
|
||||
return NULL;
|
||||
@@ -172,8 +166,7 @@ Type *check_assign_variable(Checker *c, Operand *op_a, AstNode *lhs) {
|
||||
Entity *e = NULL;
|
||||
b32 used = false;
|
||||
if (node->kind == AstNode_Identifier) {
|
||||
scope_lookup_parent_entity(c->proc_context.scope, node->identifier.token.string,
|
||||
NULL, &e);
|
||||
e = scope_lookup_entity(c->context.scope, node->identifier.token.string);
|
||||
if (e != NULL && e->kind == Entity_Variable) {
|
||||
used = e->variable.used; // TODO(bill): Make backup just in case
|
||||
}
|
||||
@@ -203,7 +196,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));
|
||||
checker_err(c, ast_node_token(op_b.expression), "Cannot assign to `%s`", str);
|
||||
error(&c->error_collector, ast_node_token(op_b.expression), "Cannot assign to `%s`", str);
|
||||
} break;
|
||||
}
|
||||
|
||||
@@ -214,33 +207,6 @@ Type *check_assign_variable(Checker *c, Operand *op_a, AstNode *lhs) {
|
||||
return op_a->type;
|
||||
}
|
||||
|
||||
// TODO(bill): Do I need to pass the *_count?
|
||||
void check_assign_variables(Checker *c,
|
||||
AstNode *lhs_list, isize lhs_count,
|
||||
AstNode *rhs_list, isize rhs_count) {
|
||||
Operand operand = {Addressing_Invalid};
|
||||
AstNode *lhs = lhs_list, *rhs = rhs_list;
|
||||
for (;
|
||||
lhs != NULL && rhs != NULL;
|
||||
lhs = lhs->next, rhs = rhs->next) {
|
||||
check_multi_expression(c, &operand, rhs);
|
||||
if (operand.type->kind != Type_Tuple) {
|
||||
check_assign_variable(c, &operand, lhs);
|
||||
} else {
|
||||
auto *tuple = &operand.type->tuple;
|
||||
for (isize i = 0;
|
||||
i < tuple->variable_count && lhs != NULL;
|
||||
i++, lhs = lhs->next) {
|
||||
// TODO(bill): More error checking
|
||||
operand.type = tuple->variables[i]->type;
|
||||
check_assign_variable(c, &operand, lhs);
|
||||
}
|
||||
if (lhs == NULL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(bill): `content_name` is for debugging
|
||||
Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) {
|
||||
if (operand->mode == Addressing_Invalid ||
|
||||
@@ -256,7 +222,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]) {
|
||||
checker_err(c, e->token, "Use of untyped thing in %.*s", LIT(context_name));
|
||||
error(&c->error_collector, e->token, "Use of untyped thing in %.*s", LIT(context_name));
|
||||
e->type = &basic_types[Basic_Invalid];
|
||||
return NULL;
|
||||
}
|
||||
@@ -299,9 +265,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)
|
||||
checker_err(c, lhs[i]->token, "Too few values on the right hand side of the declaration");
|
||||
error(&c->error_collector, lhs[i]->token, "Too few values on the right hand side of the declaration");
|
||||
} else if (rhs != NULL) {
|
||||
checker_err(c, ast_node_token(rhs), "Too many values on the right hand side of the declaration");
|
||||
error(&c->error_collector, ast_node_token(rhs), "Too many values on the right hand side of the declaration");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,8 +282,8 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) {
|
||||
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
// TODO(bill): better error
|
||||
checker_err(c, ast_node_token(operand->expression),
|
||||
"`%.*s` is not a constant", LIT(ast_node_token(operand->expression).string));
|
||||
error(&c->error_collector, 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;
|
||||
@@ -352,8 +318,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));
|
||||
checker_err(c, ast_node_token(type_expr),
|
||||
"Invalid constant type `%s`", str);
|
||||
error(&c->error_collector, ast_node_token(type_expr),
|
||||
"Invalid constant type `%s`", str);
|
||||
e->type = &basic_types[Basic_Invalid];
|
||||
return;
|
||||
}
|
||||
@@ -382,20 +348,20 @@ void check_type_declaration(Checker *c, Entity *e, AstNode *type_expr, Type *nam
|
||||
void check_procedure_body(Checker *c, Token token, DeclarationInfo *decl, Type *type, AstNode *body) {
|
||||
GB_ASSERT(body->kind == AstNode_BlockStatement);
|
||||
|
||||
ProcedureContext old_proc_context = c->proc_context;
|
||||
c->proc_context.scope = decl->scope;
|
||||
c->proc_context.decl = decl;
|
||||
CheckerContext old_context = c->context;
|
||||
c->context.scope = decl->scope;
|
||||
c->context.decl = decl;
|
||||
|
||||
push_procedure(c, type);
|
||||
check_statement_list(c, body->block_statement.list);
|
||||
if (type->procedure.results_count > 0) {
|
||||
if (!check_is_terminating(c, body)) {
|
||||
checker_err(c, body->block_statement.close, "Missing return statement at the end of the procedure");
|
||||
error(&c->error_collector, body->block_statement.close, "Missing return statement at the end of the procedure");
|
||||
}
|
||||
}
|
||||
pop_procedure(c);
|
||||
|
||||
c->proc_context = old_proc_context;
|
||||
c->context = old_context;
|
||||
}
|
||||
|
||||
void check_procedure_declaration(Checker *c, Entity *e, DeclarationInfo *d, b32 check_body_later) {
|
||||
@@ -406,8 +372,8 @@ void check_procedure_declaration(Checker *c, Entity *e, DeclarationInfo *d, b32
|
||||
auto *pd = &d->proc_decl->procedure_declaration;
|
||||
|
||||
#if 1
|
||||
Scope *original_curr_scope = c->proc_context.scope;
|
||||
c->proc_context.scope = c->global_scope;
|
||||
Scope *original_curr_scope = c->context.scope;
|
||||
c->context.scope = c->global_scope;
|
||||
check_open_scope(c, pd->procedure_type);
|
||||
#endif
|
||||
check_procedure_type(c, proc_type, pd->procedure_type);
|
||||
@@ -425,23 +391,23 @@ 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 {
|
||||
checker_err(c, ast_node_token(tag), "Unknown procedure tag");
|
||||
error(&c->error_collector, ast_node_token(tag), "Unknown procedure tag");
|
||||
}
|
||||
// TODO(bill): Other tags
|
||||
}
|
||||
|
||||
if (is_inline && is_no_inline) {
|
||||
checker_err(c, ast_node_token(pd->tag_list),
|
||||
"You cannot apply both `inline` and `no_inline` to a procedure");
|
||||
error(&c->error_collector, ast_node_token(pd->tag_list),
|
||||
"You cannot apply both `inline` and `no_inline` to a procedure");
|
||||
}
|
||||
|
||||
if (pd->body != NULL) {
|
||||
if (is_foreign) {
|
||||
checker_err(c, ast_node_token(pd->body),
|
||||
"A procedure tagged as `#foreign` cannot have a body");
|
||||
error(&c->error_collector, ast_node_token(pd->body),
|
||||
"A procedure tagged as `#foreign` cannot have a body");
|
||||
}
|
||||
|
||||
d->scope = c->proc_context.scope;
|
||||
d->scope = c->context.scope;
|
||||
|
||||
GB_ASSERT(pd->body->kind == AstNode_BlockStatement);
|
||||
if (check_body_later) {
|
||||
@@ -453,7 +419,7 @@ void check_procedure_declaration(Checker *c, Entity *e, DeclarationInfo *d, b32
|
||||
|
||||
#if 1
|
||||
check_close_scope(c);
|
||||
c->proc_context.scope = original_curr_scope;
|
||||
c->context.scope = original_curr_scope;
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -506,11 +472,11 @@ void check_entity_declaration(Checker *c, Entity *e, Type *named_type) {
|
||||
|
||||
switch (e->kind) {
|
||||
case Entity_Constant:
|
||||
c->proc_context.decl = d;
|
||||
c->context.decl = d;
|
||||
check_constant_declaration(c, e, d->type_expr, d->init_expr);
|
||||
break;
|
||||
case Entity_Variable:
|
||||
c->proc_context.decl = d;
|
||||
c->context.decl = d;
|
||||
check_variable_declaration(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr);
|
||||
break;
|
||||
case Entity_TypeName:
|
||||
@@ -537,19 +503,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:
|
||||
checker_err(c, ast_node_token(node), "Is not an expression");
|
||||
error(&c->error_collector, ast_node_token(node), "Is not an expression");
|
||||
break;
|
||||
default:
|
||||
if (kind == Expression_Statement)
|
||||
return;
|
||||
checker_err(c, ast_node_token(node), "Expression is not used");
|
||||
error(&c->error_collector, ast_node_token(node), "Expression is not used");
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case AstNode_TagStatement:
|
||||
// TODO(bill): Tag Statements
|
||||
checker_err(c, ast_node_token(node), "Tag statements are not supported yet");
|
||||
error(&c->error_collector, ast_node_token(node), "Tag statements are not supported yet");
|
||||
check_statement(c, node->tag_statement.statement);
|
||||
break;
|
||||
|
||||
@@ -567,7 +533,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
op.string.len = 1;
|
||||
break;
|
||||
default:
|
||||
checker_err(c, s->op, "Unknown inc/dec operation %.*s", LIT(s->op.string));
|
||||
error(&c->error_collector, s->op, "Unknown inc/dec operation %.*s", LIT(s->op.string));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -576,7 +542,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
if (operand.mode == Addressing_Invalid)
|
||||
return;
|
||||
if (!is_type_numeric(operand.type)) {
|
||||
checker_err(c, s->op, "Non numeric type");
|
||||
error(&c->error_collector, s->op, "Non numeric type");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -594,25 +560,56 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
|
||||
case AstNode_AssignStatement:
|
||||
switch (node->assign_statement.op.kind) {
|
||||
case Token_Eq:
|
||||
case Token_Eq: {
|
||||
// a, b, c = 1, 2, 3; // Multisided
|
||||
if (node->assign_statement.lhs_count == 0) {
|
||||
checker_err(c, node->assign_statement.op, "Missing lhs in assignment statement");
|
||||
error(&c->error_collector, node->assign_statement.op, "Missing lhs in assignment statement");
|
||||
return;
|
||||
}
|
||||
check_assign_variables(c,
|
||||
node->assign_statement.lhs_list, node->assign_statement.lhs_count,
|
||||
node->assign_statement.rhs_list, node->assign_statement.rhs_count);
|
||||
break;
|
||||
|
||||
Operand operand = {};
|
||||
AstNode *lhs = node->assign_statement.lhs_list;
|
||||
AstNode *rhs = node->assign_statement.rhs_list;
|
||||
isize i = 0;
|
||||
for (;
|
||||
lhs != NULL && rhs != NULL;
|
||||
lhs = lhs->next, rhs = rhs->next) {
|
||||
check_multi_expression(c, &operand, rhs);
|
||||
if (operand.type->kind != Type_Tuple) {
|
||||
check_assignment_variable(c, &operand, lhs);
|
||||
i++;
|
||||
} else {
|
||||
auto *tuple = &operand.type->tuple;
|
||||
for (isize j = 0;
|
||||
j < tuple->variable_count && lhs != NULL;
|
||||
j++, i++, lhs = lhs->next) {
|
||||
// TODO(bill): More error checking
|
||||
operand.type = tuple->variables[j]->type;
|
||||
check_assignment_variable(c, &operand, lhs);
|
||||
}
|
||||
if (lhs == NULL)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < node->assign_statement.lhs_count && i < node->assign_statement.rhs_count) {
|
||||
if (lhs == NULL)
|
||||
error(&c->error_collector, ast_node_token(lhs), "Too few values on the right hand side of the declaration");
|
||||
} else if (rhs != NULL) {
|
||||
error(&c->error_collector, ast_node_token(rhs), "Too many values on the right hand side of the declaration");
|
||||
}
|
||||
} break;
|
||||
|
||||
default: {
|
||||
// a += 1; // Single-sided
|
||||
Token op = node->assign_statement.op;
|
||||
if (node->assign_statement.lhs_count != 1 ||
|
||||
node->assign_statement.rhs_count != 1) {
|
||||
checker_err(c, op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string));
|
||||
error(&c->error_collector, 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));
|
||||
error(&c->error_collector, op, "Unknown Assignment operation `%.*s`", LIT(op.string));
|
||||
return;
|
||||
}
|
||||
// TODO(bill): Check if valid assignment operator
|
||||
@@ -627,7 +624,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
if (operand.mode == Addressing_Invalid)
|
||||
return;
|
||||
// NOTE(bill): Only use the first one will be used
|
||||
check_assign_variable(c, &operand, node->assign_statement.lhs_list);
|
||||
check_assignment_variable(c, &operand, node->assign_statement.lhs_list);
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
@@ -643,7 +640,7 @@ 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)) {
|
||||
checker_err(c, ast_node_token(node->if_statement.cond),
|
||||
error(&c->error_collector, ast_node_token(node->if_statement.cond),
|
||||
"Non-boolean condition in `if` statement");
|
||||
}
|
||||
check_statement(c, node->if_statement.body);
|
||||
@@ -655,7 +652,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
check_statement(c, node->if_statement.else_statement);
|
||||
break;
|
||||
default:
|
||||
checker_err(c, ast_node_token(node->if_statement.else_statement),
|
||||
error(&c->error_collector, ast_node_token(node->if_statement.else_statement),
|
||||
"Invalid `else` statement in `if` statement");
|
||||
break;
|
||||
}
|
||||
@@ -667,7 +664,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
GB_ASSERT(gb_array_count(c->procedure_stack) > 0);
|
||||
|
||||
if (c->in_defer) {
|
||||
checker_err(c, rs->token, "You cannot `return` within a defer statement");
|
||||
error(&c->error_collector, rs->token, "You cannot `return` within a defer statement");
|
||||
// TODO(bill): Should I break here?
|
||||
break;
|
||||
}
|
||||
@@ -677,7 +674,7 @@ 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) {
|
||||
checker_err(c, rs->token, "Expected %td return %s, got %td",
|
||||
error(&c->error_collector, rs->token, "Expected %td return %s, got %td",
|
||||
result_count,
|
||||
(result_count != 1 ? "values" : "value"),
|
||||
rs->result_count);
|
||||
@@ -698,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)) {
|
||||
checker_err(c, ast_node_token(node->for_statement.cond),
|
||||
"Non-boolean condition in `for` statement");
|
||||
error(&c->error_collector, ast_node_token(node->for_statement.cond),
|
||||
"Non-boolean condition in `for` statement");
|
||||
}
|
||||
}
|
||||
check_statement(c, node->for_statement.end);
|
||||
@@ -709,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)) {
|
||||
checker_err(c, ds->token, "You cannot defer a declaration");
|
||||
error(&c->error_collector, ds->token, "You cannot defer a declaration");
|
||||
} else {
|
||||
b32 out_in_defer = c->in_defer;
|
||||
c->in_defer = true;
|
||||
@@ -739,10 +736,10 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
// 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->proc_context.scope, str);
|
||||
found = current_scope_lookup_entity(c->context.scope, str);
|
||||
}
|
||||
if (found == NULL) {
|
||||
entity = make_entity_variable(c->allocator, c->proc_context.scope, token, NULL);
|
||||
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
|
||||
if (!can_be_ignored) {
|
||||
new_entities[new_entity_count++] = entity;
|
||||
}
|
||||
@@ -751,7 +748,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
entity = found;
|
||||
}
|
||||
} else {
|
||||
checker_err(c, token, "A variable declaration must be an identifier");
|
||||
error(&c->error_collector, token, "A variable declaration must be an identifier");
|
||||
}
|
||||
if (entity == NULL)
|
||||
entity = make_entity_dummy_variable(c->allocator, c->global_scope, token);
|
||||
@@ -783,7 +780,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
|
||||
AstNode *name = vd->name_list;
|
||||
for (isize i = 0; i < new_entity_count; i++, name = name->next) {
|
||||
add_entity(c, c->proc_context.scope, name, new_entities[i]);
|
||||
add_entity(c, c->context.scope, name, new_entities[i]);
|
||||
}
|
||||
|
||||
} break;
|
||||
@@ -794,7 +791,7 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
name = name->next, value = value->next) {
|
||||
GB_ASSERT(name->kind == AstNode_Identifier);
|
||||
ExactValue v = {ExactValue_Invalid};
|
||||
Entity *e = make_entity_constant(c->allocator, c->proc_context.scope, name->identifier.token, NULL, v);
|
||||
Entity *e = make_entity_constant(c->allocator, c->context.scope, name->identifier.token, NULL, v);
|
||||
entities[entity_index++] = e;
|
||||
check_constant_declaration(c, e, vd->type_expression, value);
|
||||
}
|
||||
@@ -804,27 +801,27 @@ 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) {
|
||||
checker_err(c, ast_node_token(node), "Missing type or initial expression");
|
||||
error(&c->error_collector, ast_node_token(node), "Missing type or initial expression");
|
||||
} else if (lhs_count < rhs_count) {
|
||||
checker_err(c, ast_node_token(node), "Extra initial expression");
|
||||
error(&c->error_collector, ast_node_token(node), "Extra initial expression");
|
||||
}
|
||||
|
||||
AstNode *name = vd->name_list;
|
||||
for (isize i = 0; i < entity_count; i++, name = name->next) {
|
||||
add_entity(c, c->proc_context.scope, name, entities[i]);
|
||||
add_entity(c, c->context.scope, name, entities[i]);
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
checker_err(c, ast_node_token(node), "Unknown variable declaration kind. Probably an invalid AST.");
|
||||
error(&c->error_collector, ast_node_token(node), "Unknown variable declaration kind. Probably an invalid AST.");
|
||||
return;
|
||||
}
|
||||
} break;
|
||||
|
||||
case AstNode_ProcedureDeclaration: {
|
||||
auto *pd = &node->procedure_declaration;
|
||||
Entity *e = make_entity_procedure(c->allocator, c->proc_context.scope, pd->name->identifier.token, NULL);
|
||||
add_entity(c, c->proc_context.scope, pd->name, e);
|
||||
Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->identifier.token, NULL);
|
||||
add_entity(c, c->context.scope, pd->name, e);
|
||||
|
||||
DeclarationInfo decl = {};
|
||||
init_declaration_info(&decl, e->parent);
|
||||
@@ -836,8 +833,8 @@ void check_statement(Checker *c, AstNode *node) {
|
||||
case AstNode_TypeDeclaration: {
|
||||
auto *td = &node->type_declaration;
|
||||
AstNode *name = td->name;
|
||||
Entity *e = make_entity_type_name(c->allocator, c->proc_context.scope, name->identifier.token, NULL);
|
||||
add_entity(c, c->proc_context.scope, name, e);
|
||||
Entity *e = make_entity_type_name(c->allocator, c->context.scope, name->identifier.token, NULL);
|
||||
add_entity(c, c->context.scope, name, e);
|
||||
check_type_declaration(c, e, td->type_expression, NULL);
|
||||
} break;
|
||||
}
|
||||
|
||||
@@ -24,11 +24,14 @@ int main(int argc, char **argv) {
|
||||
|
||||
parse_files(&parser, init_filename);
|
||||
|
||||
// print_ast(parser.files[0].declarations, 0);
|
||||
|
||||
Checker checker = {};
|
||||
init_checker(&checker, &parser);
|
||||
defer (destroy_checker(&checker));
|
||||
|
||||
check_parsed_files(&checker);
|
||||
|
||||
#if 0
|
||||
Codegen codegen = {};
|
||||
if (init_codegen(&codegen, &checker)) {
|
||||
|
||||
137
src/parser.cpp
137
src/parser.cpp
@@ -18,8 +18,7 @@ struct AstFile {
|
||||
isize scope_level;
|
||||
|
||||
isize error_count;
|
||||
isize error_prev_line;
|
||||
isize error_prev_column;
|
||||
TokenPos error_prev_pos;
|
||||
};
|
||||
|
||||
|
||||
@@ -49,6 +48,7 @@ enum AstNodeKind {
|
||||
AstNode_BasicLiteral,
|
||||
AstNode_Identifier,
|
||||
AstNode_ProcedureLiteral,
|
||||
AstNode_CompoundLiteral,
|
||||
|
||||
AstNode__ExpressionBegin,
|
||||
AstNode_BadExpression, // NOTE(bill): Naughty expression
|
||||
@@ -126,6 +126,12 @@ struct AstNode {
|
||||
AstNode *type; // AstNode_ProcedureType
|
||||
AstNode *body; // AstNode_BlockStatement
|
||||
} procedure_literal;
|
||||
struct {
|
||||
AstNode *type_expression;
|
||||
AstNode *element_list;
|
||||
isize element_count;
|
||||
Token open, close;
|
||||
} compound_literal;
|
||||
|
||||
struct {
|
||||
Token token;
|
||||
@@ -281,6 +287,8 @@ Token ast_node_token(AstNode *node) {
|
||||
return node->identifier.token;
|
||||
case AstNode_ProcedureLiteral:
|
||||
return ast_node_token(node->procedure_literal.type);
|
||||
case AstNode_CompoundLiteral:
|
||||
return ast_node_token(node->compound_literal.type_expression);
|
||||
case AstNode_TagExpression:
|
||||
return node->tag_expression.token;
|
||||
case AstNode_BadExpression:
|
||||
@@ -409,18 +417,17 @@ AstEntity *ast_scope_insert(AstScope *scope, AstEntity entity) {
|
||||
#define ast_file_err(f, token, fmt, ...) ast_file_err_(f, __FUNCTION__, token, fmt, ##__VA_ARGS__)
|
||||
void ast_file_err_(AstFile *file, char *function, Token token, char *fmt, ...) {
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (file->error_prev_line != token.line || file->error_prev_column != token.column) {
|
||||
if (!token_pos_are_equal(file->error_prev_pos, token.pos)) {
|
||||
va_list va;
|
||||
|
||||
file->error_prev_line = token.line;
|
||||
file->error_prev_column = token.column;
|
||||
file->error_prev_pos = token.pos;
|
||||
|
||||
#if 0
|
||||
gb_printf_err("%s()\n", function);
|
||||
#endif
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%.*s(%td:%td) Syntax error: %s\n",
|
||||
LIT(file->tokenizer.fullpath), token.line, token.column,
|
||||
LIT(token.pos.file), token.pos.line, token.pos.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
}
|
||||
@@ -573,6 +580,18 @@ gb_inline AstNode *make_procedure_literal(AstFile *f, AstNode *type, AstNode *bo
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
gb_inline AstNode *make_compound_literal(AstFile *f, AstNode *type_expression, AstNode *element_list, isize element_count,
|
||||
Token open, Token close) {
|
||||
AstNode *result = make_node(f, AstNode_CompoundLiteral);
|
||||
result->compound_literal.type_expression = type_expression;
|
||||
result->compound_literal.element_list = element_list;
|
||||
result->compound_literal.element_count = element_count;
|
||||
result->compound_literal.open = open;
|
||||
result->compound_literal.close = close;
|
||||
return result;
|
||||
}
|
||||
|
||||
gb_inline AstNode *make_bad_statement(AstFile *f, Token begin, Token end) {
|
||||
AstNode *result = make_node(f, AstNode_BadStatement);
|
||||
result->bad_statement.begin = begin;
|
||||
@@ -802,9 +821,12 @@ gb_internal void add_ast_entity(AstFile *f, AstScope *scope, AstNode *declaratio
|
||||
if (insert_entity != NULL &&
|
||||
!are_strings_equal(insert_entity->token.string, make_string("_"))) {
|
||||
ast_file_err(f, entity->token,
|
||||
"There is already a previous declaration of `%.*s` in the current scope at (%td:%td)",
|
||||
"There is already a previous declaration of `%.*s` in the current scope at\n"
|
||||
"\t%.*s(%td:%td)",
|
||||
LIT(insert_entity->token.string),
|
||||
insert_entity->token.line, insert_entity->token.column);
|
||||
LIT(insert_entity->token.pos.file),
|
||||
insert_entity->token.pos.line,
|
||||
insert_entity->token.pos.column);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -850,9 +872,55 @@ AstNode *unparen_expression(AstNode *node) {
|
||||
}
|
||||
}
|
||||
|
||||
AstNode *parse_value(AstFile *f);
|
||||
|
||||
AstNode *parse_element_list(AstFile *f, isize *element_count_) {
|
||||
AstNode *root = NULL;
|
||||
AstNode *curr = NULL;
|
||||
isize element_count = 0;
|
||||
|
||||
AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
|
||||
while (f->cursor[0].kind != Token_CloseBrace &&
|
||||
f->cursor[0].kind != Token_EOF) {
|
||||
AstNode *elem = parse_value(f);
|
||||
#if 0
|
||||
// TODO(bill): Designated Initializers
|
||||
if (f->cursor[0].kind == Token_Eq) {
|
||||
Token eq = expect_token(f, Token_Eq);
|
||||
}
|
||||
#endif
|
||||
DLIST_APPEND(root, curr, elem);
|
||||
element_count++;
|
||||
if (f->cursor[0].kind != Token_Comma)
|
||||
break;
|
||||
next_token(f);
|
||||
}
|
||||
|
||||
if (element_count_) *element_count_ = element_count;
|
||||
return root;
|
||||
}
|
||||
|
||||
AstNode *parse_literal_value(AstFile *f, AstNode *type_expression) {
|
||||
AstNode *element_list = NULL;
|
||||
isize element_count = 0;
|
||||
Token open = expect_token(f, Token_OpenBrace);
|
||||
if (f->cursor[0].kind != Token_CloseBrace)
|
||||
element_list = parse_element_list(f, &element_count);
|
||||
Token close = expect_token(f, Token_CloseBrace);
|
||||
|
||||
return make_compound_literal(f, type_expression, element_list, element_count, open, close);
|
||||
}
|
||||
|
||||
AstNode *parse_value(AstFile *f) {
|
||||
if (f->cursor[0].kind == Token_OpenBrace)
|
||||
return parse_literal_value(f, NULL);
|
||||
|
||||
AstNode *value = parse_expression(f, false);
|
||||
return value;
|
||||
}
|
||||
|
||||
AstNode *parse_identifier_or_type(AstFile *f);
|
||||
|
||||
AstNode *parse_operand(AstFile *f, b32 lhs) {
|
||||
AstNode *operand = NULL; // Operand
|
||||
switch (f->cursor[0].kind) {
|
||||
case Token_Identifier:
|
||||
@@ -860,7 +928,7 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
|
||||
if (!lhs) {
|
||||
// TODO(bill): Handle?
|
||||
}
|
||||
break;
|
||||
return operand;
|
||||
|
||||
case Token_Integer:
|
||||
case Token_Float:
|
||||
@@ -868,7 +936,7 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
|
||||
case Token_Rune:
|
||||
operand = make_basic_literal(f, f->cursor[0]);
|
||||
next_token(f);
|
||||
break;
|
||||
return operand;
|
||||
|
||||
case Token_OpenParen: {
|
||||
Token open, close;
|
||||
@@ -877,11 +945,13 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
|
||||
operand = parse_expression(f, false);
|
||||
close = expect_token(f, Token_CloseParen);
|
||||
operand = make_paren_expression(f, operand, open, close);
|
||||
return operand;
|
||||
} break;
|
||||
|
||||
case Token_Hash: {
|
||||
operand = parse_tag_expression(f, NULL);
|
||||
operand->tag_expression.expression = parse_expression(f, false);
|
||||
return operand;
|
||||
} break;
|
||||
|
||||
// Parse Procedure Type or Literal
|
||||
@@ -895,8 +965,23 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
|
||||
AstNode *body = parse_body(f, scope);
|
||||
return make_procedure_literal(f, type, body);
|
||||
} break;
|
||||
|
||||
default: {
|
||||
AstNode *type = parse_identifier_or_type(f);
|
||||
if (type != NULL) {
|
||||
// NOTE(bill): Sanity check as identifiers should be handled already
|
||||
GB_ASSERT_MSG(type->kind != AstNode_Identifier, "Type Cannot be identifier");
|
||||
return type;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
ast_file_err(f, f->cursor[0], "Expected an operand");
|
||||
return make_bad_expression(f, f->cursor[0], f->cursor[0]);
|
||||
}
|
||||
|
||||
AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
|
||||
AstNode *operand = parse_operand(f, lhs);
|
||||
b32 loop = true;
|
||||
|
||||
while (loop) {
|
||||
@@ -998,6 +1083,24 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
|
||||
operand = make_dereference_expression(f, operand, expect_token(f, Token_Pointer));
|
||||
break;
|
||||
|
||||
case Token_OpenBrace: {
|
||||
switch (operand->kind) {
|
||||
case AstNode_BadExpression:
|
||||
case AstNode_Identifier:
|
||||
case AstNode_ArrayType:
|
||||
case AstNode_StructType: {
|
||||
if (lhs) {
|
||||
// TODO(bill): Handle this
|
||||
}
|
||||
operand = parse_literal_value(f, operand);
|
||||
} break;
|
||||
|
||||
default:
|
||||
loop = false;
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
loop = false;
|
||||
break;
|
||||
@@ -1203,7 +1306,6 @@ AstNode *parse_identfier_list(AstFile *f, isize *list_count_) {
|
||||
}
|
||||
|
||||
|
||||
AstNode *parse_identifier_or_type(AstFile *f);
|
||||
|
||||
AstNode *parse_type_attempt(AstFile *f) {
|
||||
AstNode *type = parse_identifier_or_type(f);
|
||||
@@ -1315,13 +1417,22 @@ AstNode *parse_identifier_or_type(AstFile *f) {
|
||||
return make_paren_expression(f, type_expression, open, close);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// TODO(bill): Why did I add this?
|
||||
case Token_Colon:
|
||||
case Token_Eq:
|
||||
break;
|
||||
#endif
|
||||
case Token_Colon:
|
||||
break;
|
||||
|
||||
case Token_Eq:
|
||||
if (f->cursor[-1].kind == Token_Colon)
|
||||
break;
|
||||
// fallthrough
|
||||
default:
|
||||
ast_file_err(f, f->cursor[0],
|
||||
"Expected a type after `%.*s`, got `%.*s`", LIT(f->cursor[-1].string), LIT(f->cursor[0].string));
|
||||
"Expected a type after `%.*s`, got `%.*s`", LIT(f->cursor[-1].string), LIT(f->cursor[0].string));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,20 @@ void print_ast(AstNode *node, isize indent) {
|
||||
print_indent(indent);
|
||||
print_token(node->identifier.token);
|
||||
break;
|
||||
case AstNode_ProcedureLiteral:
|
||||
print_indent(indent);
|
||||
gb_printf("(proc lit)\n");
|
||||
print_ast(node->procedure_literal.type, indent+1);
|
||||
print_ast(node->procedure_literal.body, indent+1);
|
||||
break;
|
||||
|
||||
case AstNode_CompoundLiteral:
|
||||
print_indent(indent);
|
||||
gb_printf("(compound lit)\n");
|
||||
print_ast(node->compound_literal.type_expression, indent+1);
|
||||
print_ast(node->compound_literal.element_list, indent+1);
|
||||
break;
|
||||
|
||||
|
||||
case AstNode_TagExpression:
|
||||
print_indent(indent);
|
||||
|
||||
@@ -209,18 +209,64 @@ char const *TOKEN_STRINGS[] = {
|
||||
};
|
||||
|
||||
|
||||
// NOTE(bill): Text is UTF-8, thus why u8 and not char
|
||||
typedef struct Token Token;
|
||||
struct Token {
|
||||
TokenKind kind;
|
||||
String string;
|
||||
struct TokenPos {
|
||||
String file;
|
||||
isize line, column;
|
||||
};
|
||||
|
||||
b32 token_pos_are_equal(TokenPos a, TokenPos b) {
|
||||
if (a.line == b.line) {
|
||||
if (a.column == b.column) {
|
||||
return are_strings_equal(a.file, b.file);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
// NOTE(bill): Text is UTF-8, thus why u8 and not char
|
||||
struct Token {
|
||||
TokenKind kind;
|
||||
String string;
|
||||
TokenPos pos;
|
||||
};
|
||||
|
||||
Token empty_token = {Token_Invalid};
|
||||
|
||||
|
||||
struct ErrorCollector {
|
||||
TokenPos prev;
|
||||
isize count;
|
||||
};
|
||||
|
||||
void error(ErrorCollector *ec, Token token, char *fmt, ...) {
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (!token_pos_are_equal(ec->prev, token.pos)) {
|
||||
ec->prev = token.pos;
|
||||
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%.*s(%td:%td) Error: %s\n",
|
||||
LIT(token.pos.file), token.pos.line, token.pos.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
|
||||
}
|
||||
ec->count++;
|
||||
}
|
||||
|
||||
void warning(Token token, char *fmt, ...) {
|
||||
va_list va;
|
||||
va_start(va, fmt);
|
||||
gb_printf_err("%.*s(%td:%td) Warning: %s\n",
|
||||
LIT(token.pos.file), token.pos.line, token.pos.column,
|
||||
gb_bprintf_va(fmt, va));
|
||||
va_end(va);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
char const *token_kind_to_string(TokenKind kind) {
|
||||
return TOKEN_STRINGS[kind];
|
||||
@@ -432,8 +478,9 @@ Token scan_number_to_token(Tokenizer *t, b32 seen_decimal_point) {
|
||||
u8 *start_curr = t->curr;
|
||||
token.kind = Token_Integer;
|
||||
token.string = make_string(start_curr, 1);
|
||||
token.line = t->line_count;
|
||||
token.column = t->curr-t->line+1;
|
||||
token.pos.file = t->fullpath;
|
||||
token.pos.line = t->line_count;
|
||||
token.pos.column = t->curr-t->line+1;
|
||||
|
||||
if (seen_decimal_point) {
|
||||
start_curr--;
|
||||
@@ -595,8 +642,9 @@ Token tokenizer_get_token(Tokenizer *t) {
|
||||
|
||||
tokenizer_skip_whitespace(t);
|
||||
token.string = make_string(t->curr, 1);
|
||||
token.line = t->line_count;
|
||||
token.column = t->curr - t->line + 1;
|
||||
token.pos.file = t->fullpath;
|
||||
token.pos.line = t->line_count;
|
||||
token.pos.column = t->curr - t->line + 1;
|
||||
|
||||
curr_rune = t->curr_rune;
|
||||
if (rune_is_letter(curr_rune)) {
|
||||
|
||||
Reference in New Issue
Block a user