error_node

This commit is contained in:
Ginger Bill
2016-11-30 10:52:09 +00:00
parent d9c686b53d
commit b76c8abe73
8 changed files with 394 additions and 325 deletions
+18 -17
View File
@@ -6,7 +6,10 @@ when ODIN_OS == "windows" {
File_Time :: type u64 File_Time :: type u64
File :: struct { File :: struct {
Handle :: type win32.HANDLE Handle :: raw_union {
p: rawptr
i: int
}
handle: Handle handle: Handle
last_write_time: File_Time last_write_time: File_Time
} }
@@ -15,10 +18,9 @@ open :: proc(name: string) -> (File, bool) {
using win32 using win32
buf: [300]byte buf: [300]byte
copy(buf[:], name as []byte) copy(buf[:], name as []byte)
f := File{ f: File
handle = CreateFileA(^buf[0], FILE_GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, nil), f.handle.p = CreateFileA(^buf[0], FILE_GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, nil) as rawptr
} success := f.handle.p != INVALID_HANDLE_VALUE
success := f.handle != INVALID_HANDLE_VALUE as File.Handle
f.last_write_time = last_write_time(^f) f.last_write_time = last_write_time(^f)
return f, success return f, success
} }
@@ -27,21 +29,20 @@ create :: proc(name: string) -> (File, bool) {
using win32 using win32
buf: [300]byte buf: [300]byte
copy(buf[:], name as []byte) copy(buf[:], name as []byte)
f := File{ f: File
handle = CreateFileA(^buf[0], FILE_GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, 0, nil), f.handle.p = CreateFileA(^buf[0], FILE_GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, 0, nil) as rawptr
} success := f.handle.p != INVALID_HANDLE_VALUE
success := f.handle != INVALID_HANDLE_VALUE as File.Handle
f.last_write_time = last_write_time(^f) f.last_write_time = last_write_time(^f)
return f, success return f, success
} }
close :: proc(using f: ^File) { close :: proc(using f: ^File) {
win32.CloseHandle(handle) win32.CloseHandle(handle.p as win32.HANDLE)
} }
write :: proc(using f: ^File, buf: []byte) -> bool { write :: proc(using f: ^File, buf: []byte) -> bool {
bytes_written: i32 bytes_written: i32
return win32.WriteFile(handle, buf.data, buf.count as i32, ^bytes_written, nil) != 0 return win32.WriteFile(handle.p as win32.HANDLE, buf.data, buf.count as i32, ^bytes_written, nil) != 0
} }
file_has_changed :: proc(f: ^File) -> bool { file_has_changed :: proc(f: ^File) -> bool {
@@ -57,7 +58,7 @@ file_has_changed :: proc(f: ^File) -> bool {
last_write_time :: proc(f: ^File) -> File_Time { last_write_time :: proc(f: ^File) -> File_Time {
file_info: win32.BY_HANDLE_FILE_INFORMATION file_info: win32.BY_HANDLE_FILE_INFORMATION
win32.GetFileInformationByHandle(f.handle, ^file_info) win32.GetFileInformationByHandle(f.handle.p as win32.HANDLE, ^file_info)
l := file_info.last_write_time.low_date_time as File_Time l := file_info.last_write_time.low_date_time as File_Time
h := file_info.last_write_time.high_date_time as File_Time h := file_info.last_write_time.high_date_time as File_Time
return l | h << 32 return l | h << 32
@@ -91,9 +92,9 @@ File_Standard :: type enum {
// NOTE(bill): Uses startup to initialize it // NOTE(bill): Uses startup to initialize it
__std_files := [File_Standard.count]File{ __std_files := [File_Standard.count]File{
{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE)}, {handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE) transmute File.Handle },
{handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)}, {handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE) transmute File.Handle },
{handle = win32.GetStdHandle(win32.STD_ERROR_HANDLE)}, {handle = win32.GetStdHandle(win32.STD_ERROR_HANDLE) transmute File.Handle },
} }
stdin := ^__std_files[File_Standard.INPUT] stdin := ^__std_files[File_Standard.INPUT]
@@ -113,7 +114,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
defer close(^f) defer close(^f)
length: i64 length: i64
file_size_ok := win32.GetFileSizeEx(f.handle as win32.HANDLE, ^length) != 0 file_size_ok := win32.GetFileSizeEx(f.handle.p as win32.HANDLE, ^length) != 0
if !file_size_ok { if !file_size_ok {
return nil, false return nil, false
} }
@@ -136,7 +137,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
to_read = MAX to_read = MAX
} }
win32.ReadFile(f.handle as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, nil) win32.ReadFile(f.handle.p as win32.HANDLE, ^data[total_read], to_read, ^single_read_length, nil)
if single_read_length <= 0 { if single_read_length <= 0 {
free(data.data) free(data.data)
return nil, false return nil, false
+12 -11
View File
@@ -546,6 +546,7 @@ void init_universal_scope(void) {
add_global_entity(make_entity_nil(a, str_lit("nil"), t_untyped_nil)); add_global_entity(make_entity_nil(a, str_lit("nil"), t_untyped_nil));
// TODO(bill): Set through flags in the compiler
add_global_string_constant(a, str_lit("ODIN_OS"), str_lit("windows")); add_global_string_constant(a, str_lit("ODIN_OS"), str_lit("windows"));
add_global_string_constant(a, str_lit("ODIN_ARCH"), str_lit("amd64")); add_global_string_constant(a, str_lit("ODIN_ARCH"), str_lit("amd64"));
add_global_string_constant(a, str_lit("ODIN_VENDOR"), str_lit("odin")); add_global_string_constant(a, str_lit("ODIN_VENDOR"), str_lit("odin"));
@@ -702,7 +703,6 @@ void add_type_and_value(CheckerInfo *i, AstNode *expression, AddressingMode mode
void add_entity_definition(CheckerInfo *i, AstNode *identifier, Entity *entity) { void add_entity_definition(CheckerInfo *i, AstNode *identifier, Entity *entity) {
GB_ASSERT(identifier != NULL); GB_ASSERT(identifier != NULL);
if (identifier->kind == AstNode_Ident) { if (identifier->kind == AstNode_Ident) {
GB_ASSERT(identifier->kind == AstNode_Ident);
HashKey key = hash_pointer(identifier); HashKey key = hash_pointer(identifier);
map_entity_set(&i->definitions, key, entity); map_entity_set(&i->definitions, key, entity);
} else { } else {
@@ -754,6 +754,7 @@ void add_entity_use(Checker *c, AstNode *identifier, Entity *entity) {
void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d) { void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclInfo *d) {
GB_ASSERT(identifier->kind == AstNode_Ident);
GB_ASSERT(str_eq(identifier->Ident.string, e->token.string)); GB_ASSERT(str_eq(identifier->Ident.string, e->token.string));
add_entity(c, e->scope, identifier, e); add_entity(c, e->scope, identifier, e);
map_decl_info_set(&c->info.entities, hash_pointer(e), d); map_decl_info_set(&c->info.entities, hash_pointer(e), d);
@@ -1077,13 +1078,13 @@ void check_global_when_stmt(Checker *c, Scope *parent_scope, AstNodeWhenStmt *ws
Operand operand = {Addressing_Invalid}; Operand operand = {Addressing_Invalid};
check_expr(c, &operand, ws->cond); check_expr(c, &operand, ws->cond);
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
error(ast_node_token(ws->cond), "Non-boolean condition in `when` statement"); error_node(ws->cond, "Non-boolean condition in `when` statement");
} }
if (operand.mode != Addressing_Constant) { if (operand.mode != Addressing_Constant) {
error(ast_node_token(ws->cond), "Non-constant condition in `when` statement"); error_node(ws->cond, "Non-constant condition in `when` statement");
} }
if (ws->body == NULL || ws->body->kind != AstNode_BlockStmt) { if (ws->body == NULL || ws->body->kind != AstNode_BlockStmt) {
error(ast_node_token(ws->cond), "Invalid body for `when` statement"); error_node(ws->cond, "Invalid body for `when` statement");
} else { } else {
if (operand.value.kind == ExactValue_Bool && if (operand.value.kind == ExactValue_Bool &&
operand.value.value_bool == true) { operand.value.value_bool == true) {
@@ -1098,7 +1099,7 @@ void check_global_when_stmt(Checker *c, Scope *parent_scope, AstNodeWhenStmt *ws
check_global_when_stmt(c, parent_scope, &ws->else_stmt->WhenStmt, file_scopes); check_global_when_stmt(c, parent_scope, &ws->else_stmt->WhenStmt, file_scopes);
break; break;
default: default:
error(ast_node_token(ws->else_stmt), "Invalid `else` statement in `when` statement"); error_node(ws->else_stmt, "Invalid `else` statement in `when` statement");
break; break;
} }
} }
@@ -1200,9 +1201,9 @@ void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray
if (is_string_an_identifier(filename)) { if (is_string_an_identifier(filename)) {
import_name = filename; import_name = filename;
} else { } else {
error(ast_node_token(decl), error_node(decl,
"File name, %.*s, cannot be as an import name as it is not a valid identifier", "File name, %.*s, cannot be as an import name as it is not a valid identifier",
LIT(filename)); LIT(filename));
} }
} }
@@ -1261,9 +1262,9 @@ void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray
isize rhs_count = cd->values.count; isize rhs_count = cd->values.count;
if (rhs_count == 0 && cd->type == NULL) { if (rhs_count == 0 && cd->type == NULL) {
error(ast_node_token(decl), "Missing type or initial expression"); error_node(decl, "Missing type or initial expression");
} else if (lhs_count < rhs_count) { } else if (lhs_count < rhs_count) {
error(ast_node_token(decl), "Extra initial expression"); error_node(decl, "Extra initial expression");
} }
case_end; case_end;
case_ast_node(vd, VarDecl, decl); case_ast_node(vd, VarDecl, decl);
@@ -1326,7 +1327,7 @@ void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray
default: default:
if (parent_scope->is_file) { if (parent_scope->is_file) {
error(ast_node_token(decl), "Only declarations are allowed at file scope"); error_node(decl, "Only declarations are allowed at file scope");
} }
break; break;
} }
+16 -19
View File
@@ -16,7 +16,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
gbString expr_str = expr_to_string(operand->expr); gbString expr_str = expr_to_string(operand->expr);
// TODO(bill): is this a good enough error message? // TODO(bill): is this a good enough error message?
error(ast_node_token(operand->expr), error_node(operand->expr,
"Cannot assign builtin procedure `%s` in %.*s", "Cannot assign builtin procedure `%s` in %.*s",
expr_str, expr_str,
LIT(context_name)); LIT(context_name));
@@ -175,7 +175,7 @@ void check_var_decl_node(Checker *c, AstNode *node) {
entity = found; entity = found;
} }
} else { } else {
error(ast_node_token(name), "A variable declaration must be an identifier"); error_node(name, "A variable declaration must be an identifier");
} }
if (entity == NULL) { if (entity == NULL) {
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name)); entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
@@ -227,7 +227,7 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) {
if (operand->mode != Addressing_Constant) { if (operand->mode != Addressing_Constant) {
// TODO(bill): better error // TODO(bill): better error
error(ast_node_token(operand->expr), error_node(operand->expr,
"`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string)); "`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string));
if (e->type == NULL) { if (e->type == NULL) {
e->type = t_invalid; e->type = t_invalid;
@@ -237,7 +237,7 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) {
// if (!is_type_constant_type(operand->type)) { // if (!is_type_constant_type(operand->type)) {
// gbString type_str = type_to_string(operand->type); // gbString type_str = type_to_string(operand->type);
// defer (gb_string_free(type_str)); // defer (gb_string_free(type_str));
// error(ast_node_token(operand->expr), // error_node(operand->expr,
// "Invalid constant type: `%s`", type_str); // "Invalid constant type: `%s`", type_str);
// if (e->type == NULL) { // if (e->type == NULL) {
// e->type = t_invalid; // e->type = t_invalid;
@@ -272,7 +272,7 @@ void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_e
// if (!is_type_constant_type(t)) { // if (!is_type_constant_type(t)) {
// gbString str = type_to_string(t); // gbString str = type_to_string(t);
// defer (gb_string_free(str)); // defer (gb_string_free(str));
// error(ast_node_token(type_expr), // error_node(type_expr),
// "Invalid constant type `%s`", str); // "Invalid constant type `%s`", str);
// e->type = t_invalid; // e->type = t_invalid;
// return; // return;
@@ -380,19 +380,16 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
} }
if (is_inline && is_no_inline) { if (is_inline && is_no_inline) {
error(ast_node_token(pd->type), error_node(pd->type, "You cannot apply both `inline` and `no_inline` to a procedure");
"You cannot apply both `inline` and `no_inline` to a procedure");
} }
if (is_foreign && is_link_name) { if (is_foreign && is_link_name) {
error(ast_node_token(pd->type), error_node(pd->type, "You cannot apply both `foreign` and `link_name` to a procedure");
"You cannot apply both `foreign` and `link_name` to a procedure");
} }
if (pd->body != NULL) { if (pd->body != NULL) {
if (is_foreign) { if (is_foreign) {
error(ast_node_token(pd->body), error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
"A procedure tagged as `#foreign` cannot have a body");
} }
d->scope = c->context.scope; d->scope = c->context.scope;
@@ -416,10 +413,10 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
Type *this_type = base_type(e->type); Type *this_type = base_type(e->type);
Type *other_type = base_type(f->type); Type *other_type = base_type(f->type);
if (!are_signatures_similar_enough(this_type, other_type)) { if (!are_signatures_similar_enough(this_type, other_type)) {
error(ast_node_token(d->proc_decl), error_node(d->proc_decl,
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n" "Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
"\tat %.*s(%td:%td)", "\tat %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column); LIT(name), LIT(pos.file), pos.line, pos.column);
} }
} else { } else {
map_entity_set(fp, key, e); map_entity_set(fp, key, e);
@@ -434,10 +431,10 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
if (found) { if (found) {
Entity *f = *found; Entity *f = *found;
TokenPos pos = f->token.pos; TokenPos pos = f->token.pos;
error(ast_node_token(d->proc_decl), error_node(d->proc_decl,
"Non unique #link_name for procedure `%.*s`\n" "Non unique #link_name for procedure `%.*s`\n"
"\tother at %.*s(%td:%td)", "\tother at %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column); LIT(name), LIT(pos.file), pos.line, pos.column);
} else { } else {
map_entity_set(fp, key, e); map_entity_set(fp, key, e);
} }
+223 -205
View File
File diff suppressed because it is too large Load Diff
+38 -38
View File
@@ -216,10 +216,10 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
gbString str = expr_to_string(op_b.expr); gbString str = expr_to_string(op_b.expr);
switch (op_b.mode) { switch (op_b.mode) {
case Addressing_Value: case Addressing_Value:
error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str); error_node(op_b.expr, "Cannot assign to `%s`", str);
break; break;
default: default:
error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str); error_node(op_b.expr, "Cannot assign to `%s`", str);
break; break;
} }
gb_string_free(str); gb_string_free(str);
@@ -284,13 +284,13 @@ void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
Operand operand = {Addressing_Invalid}; Operand operand = {Addressing_Invalid};
check_expr(c, &operand, ws->cond); check_expr(c, &operand, ws->cond);
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
error(ast_node_token(ws->cond), "Non-boolean condition in `when` statement"); error_node(ws->cond, "Non-boolean condition in `when` statement");
} }
if (operand.mode != Addressing_Constant) { if (operand.mode != Addressing_Constant) {
error(ast_node_token(ws->cond), "Non-constant condition in `when` statement"); error_node(ws->cond, "Non-constant condition in `when` statement");
} }
if (ws->body == NULL || ws->body->kind != AstNode_BlockStmt) { if (ws->body == NULL || ws->body->kind != AstNode_BlockStmt) {
error(ast_node_token(ws->cond), "Invalid body for `when` statement"); error_node(ws->cond, "Invalid body for `when` statement");
} else { } else {
if (operand.value.kind == ExactValue_Bool && if (operand.value.kind == ExactValue_Bool &&
operand.value.value_bool) { operand.value.value_bool) {
@@ -304,7 +304,7 @@ void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
check_when_stmt(c, &ws->else_stmt->WhenStmt, flags); check_when_stmt(c, &ws->else_stmt->WhenStmt, flags);
break; break;
default: default:
error(ast_node_token(ws->else_stmt), "Invalid `else` statement in `when` statement"); error_node(ws->else_stmt, "Invalid `else` statement in `when` statement");
break; break;
} }
} }
@@ -323,7 +323,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
ExprKind kind = check_expr_base(c, &operand, es->expr, NULL); ExprKind kind = check_expr_base(c, &operand, es->expr, NULL);
switch (operand.mode) { switch (operand.mode) {
case Addressing_Type: case Addressing_Type:
error(ast_node_token(node), "Is not an expression"); error_node(node, "Is not an expression");
break; break;
case Addressing_NoValue: case Addressing_NoValue:
return; return;
@@ -335,7 +335,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
return; return;
} }
gbString expr_str = expr_to_string(operand.expr); gbString expr_str = expr_to_string(operand.expr);
error(ast_node_token(node), "Expression is not used: `%s`", expr_str); error_node(node, "Expression is not used: `%s`", expr_str);
gb_string_free(expr_str); gb_string_free(expr_str);
} break; } break;
} }
@@ -343,7 +343,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
case_ast_node(ts, TagStmt, node); case_ast_node(ts, TagStmt, node);
// TODO(bill): Tag Statements // TODO(bill): Tag Statements
error(ast_node_token(node), "Tag statements are not supported yet"); error_node(node, "Tag statements are not supported yet");
check_stmt(c, ts->stmt, flags); check_stmt(c, ts->stmt, flags);
case_end; case_end;
@@ -426,7 +426,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
check_assignment_variable(c, &operands.e[i], lhs); check_assignment_variable(c, &operands.e[i], lhs);
} }
if (lhs_count != rhs_count) { if (lhs_count != rhs_count) {
error(ast_node_token(as->lhs.e[0]), "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count); error_node(as->lhs.e[0], "Assignment count mismatch `%td` = `%td`", lhs_count, rhs_count);
} }
gb_temp_arena_memory_end(tmp); gb_temp_arena_memory_end(tmp);
@@ -479,7 +479,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
Operand operand = {Addressing_Invalid}; Operand operand = {Addressing_Invalid};
check_expr(c, &operand, is->cond); check_expr(c, &operand, is->cond);
if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
error(ast_node_token(is->cond), "Non-boolean condition in `if` statement"); error_node(is->cond, "Non-boolean condition in `if` statement");
} }
check_stmt(c, is->body, mod_flags); check_stmt(c, is->body, mod_flags);
@@ -491,7 +491,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
check_stmt(c, is->else_stmt, mod_flags); check_stmt(c, is->else_stmt, mod_flags);
break; break;
default: default:
error(ast_node_token(is->else_stmt), "Invalid `else` statement in `if` statement"); error_node(is->else_stmt, "Invalid `else` statement in `if` statement");
break; break;
} }
} }
@@ -526,13 +526,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
variables = tuple->variables; variables = tuple->variables;
} }
if (rs->results.count == 0) { if (rs->results.count == 0) {
error(ast_node_token(node), "Expected %td return values, got 0", result_count); error_node(node, "Expected %td return values, got 0", result_count);
} else { } else {
check_init_variables(c, variables, result_count, check_init_variables(c, variables, result_count,
rs->results, str_lit("return statement")); rs->results, str_lit("return statement"));
} }
} else if (rs->results.count > 0) { } else if (rs->results.count > 0) {
error(ast_node_token(rs->results.e[0]), "No return values expected"); error_node(rs->results.e[0], "No return values expected");
} }
case_end; case_end;
@@ -548,8 +548,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
check_expr(c, &operand, fs->cond); check_expr(c, &operand, fs->cond);
if (operand.mode != Addressing_Invalid && if (operand.mode != Addressing_Invalid &&
!is_type_boolean(operand.type)) { !is_type_boolean(operand.type)) {
error(ast_node_token(fs->cond), error_node(fs->cond, "Non-boolean condition in `for` statement");
"Non-boolean condition in `for` statement");
} }
} }
if (fs->post != NULL) { if (fs->post != NULL) {
@@ -595,15 +594,16 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
default_stmt = stmt; default_stmt = stmt;
} }
} else { } else {
error(ast_node_token(stmt), "Invalid AST - expected case clause"); error_node(stmt, "Invalid AST - expected case clause");
} }
if (default_stmt != NULL) { if (default_stmt != NULL) {
if (first_default != NULL) { if (first_default != NULL) {
TokenPos pos = ast_node_token(first_default).pos; TokenPos pos = ast_node_token(first_default).pos;
error(ast_node_token(stmt), error_node(stmt,
"multiple `default` clauses\n" "multiple `default` clauses\n"
"\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column); "\tfirst at %.*s(%td:%td)",
LIT(pos.file), pos.line, pos.column);
} else { } else {
first_default = default_stmt; first_default = default_stmt;
} }
@@ -664,11 +664,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
if (are_types_identical(y.type, tap.type)) { if (are_types_identical(y.type, tap.type)) {
TokenPos pos = tap.token.pos; TokenPos pos = tap.token.pos;
gbString expr_str = expr_to_string(y.expr); gbString expr_str = expr_to_string(y.expr);
error(ast_node_token(y.expr), error_node(y.expr,
"Duplicate case `%s`\n" "Duplicate case `%s`\n"
"\tprevious case at %.*s(%td:%td)", "\tprevious case at %.*s(%td:%td)",
expr_str, expr_str,
LIT(pos.file), pos.line, pos.column); LIT(pos.file), pos.line, pos.column);
gb_string_free(expr_str); gb_string_free(expr_str);
continue_outer = true; continue_outer = true;
break; break;
@@ -713,8 +713,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
check_assignment(c, &x, NULL, str_lit("type match expression")); check_assignment(c, &x, NULL, str_lit("type match expression"));
if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) { if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) {
gbString str = type_to_string(x.type); gbString str = type_to_string(x.type);
error(ast_node_token(x.expr), error_node(x.expr,
"Invalid type for this type match expression, got `%s`", str); "Invalid type for this type match expression, got `%s`", str);
gb_string_free(str); gb_string_free(str);
break; break;
} }
@@ -732,15 +732,15 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
default_stmt = stmt; default_stmt = stmt;
} }
} else { } else {
error(ast_node_token(stmt), "Invalid AST - expected case clause"); error_node(stmt, "Invalid AST - expected case clause");
} }
if (default_stmt != NULL) { if (default_stmt != NULL) {
if (first_default != NULL) { if (first_default != NULL) {
TokenPos pos = ast_node_token(first_default).pos; TokenPos pos = ast_node_token(first_default).pos;
error(ast_node_token(stmt), error_node(stmt,
"multiple `default` clauses\n" "Multiple `default` clauses\n"
"\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column); "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
} else { } else {
first_default = default_stmt; first_default = default_stmt;
} }
@@ -785,8 +785,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
} }
if (!tag_type_found) { if (!tag_type_found) {
gbString type_str = type_to_string(y.type); gbString type_str = type_to_string(y.type);
error(ast_node_token(y.expr), error_node(y.expr,
"Unknown tag type, got `%s`", type_str); "Unknown tag type, got `%s`", type_str);
gb_string_free(type_str); gb_string_free(type_str);
continue; continue;
} }
@@ -802,11 +802,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
if (found) { if (found) {
TokenPos pos = cc->token.pos; TokenPos pos = cc->token.pos;
gbString expr_str = expr_to_string(y.expr); gbString expr_str = expr_to_string(y.expr);
error(ast_node_token(y.expr), error_node(y.expr,
"Duplicate type case `%s`\n" "Duplicate type case `%s`\n"
"\tprevious type case at %.*s(%td:%td)", "\tprevious type case at %.*s(%td:%td)",
expr_str, expr_str,
LIT(pos.file), pos.line, pos.column); LIT(pos.file), pos.line, pos.column);
gb_string_free(expr_str); gb_string_free(expr_str);
break; break;
} }
+16 -1
View File
@@ -667,9 +667,24 @@ bool is_type_comparable(Type *t) {
t = base_type(get_enum_base_type(t)); t = base_type(get_enum_base_type(t));
switch (t->kind) { switch (t->kind) {
case Type_Basic: case Type_Basic:
return t->kind != Basic_UntypedNil && t->kind != Basic_any; return t->kind != Basic_UntypedNil;
case Type_Pointer: case Type_Pointer:
return true; return true;
case Type_Record: {
if (false && is_type_struct(t)) {
// TODO(bill): Should I even allow this?
for (isize i = 0; i < t->Record.field_count; i++) {
if (!is_type_comparable(t->Record.fields[i]->type))
return false;
}
} else if (is_type_enum(t)) {
return is_type_comparable(t->Record.enum_base);
}
return false;
} break;
case Type_Array:
return false;
// return is_type_comparable(t->Array.elem);
case Type_Vector: case Type_Vector:
return is_type_comparable(t->Vector.elem); return is_type_comparable(t->Vector.elem);
case Type_Proc: case Type_Proc:
+46 -16
View File
@@ -355,8 +355,6 @@ typedef struct AstNode {
#define case_end } break; #define case_end } break;
gb_inline bool is_ast_node_expr(AstNode *node) { gb_inline bool is_ast_node_expr(AstNode *node) {
return gb_is_between(node->kind, AstNode__ExprBegin+1, AstNode__ExprEnd-1); return gb_is_between(node->kind, AstNode__ExprBegin+1, AstNode__ExprEnd-1);
} }
@@ -500,6 +498,38 @@ Token ast_node_token(AstNode *node) {
return empty_token; return empty_token;
} }
void error_node(AstNode *node, char *fmt, ...) {
va_list va;
va_start(va, fmt);
error_va(ast_node_token(node), fmt, va);
va_end(va);
}
void warning_node(AstNode *node, char *fmt, ...) {
va_list va;
va_start(va, fmt);
warning_va(ast_node_token(node), fmt, va);
va_end(va);
}
void syntax_error_node(AstNode *node, char *fmt, ...) {
va_list va;
va_start(va, fmt);
syntax_error_va(ast_node_token(node), fmt, va);
va_end(va);
}
bool ast_node_expect(AstNode *node, AstNodeKind kind) {
if (node->kind != kind) {
error_node(node, "Expected %.*s, got %.*s", LIT(ast_node_strings[node->kind]));
return false;
}
return true;
}
// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++ // NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
AstNode *make_node(AstFile *f, AstNodeKind kind) { AstNode *make_node(AstFile *f, AstNodeKind kind) {
gbArena *arena = &f->arena; gbArena *arena = &f->arena;
@@ -1232,7 +1262,7 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags);
void check_proc_add_tag(AstFile *f, AstNode *tag_expr, u64 *tags, ProcTag tag, String tag_name) { void check_proc_add_tag(AstFile *f, AstNode *tag_expr, u64 *tags, ProcTag tag, String tag_name) {
if (*tags & tag) { if (*tags & tag) {
syntax_error(ast_node_token(tag_expr), "Procedure tag already used: %.*s", LIT(tag_name)); syntax_error_node(tag_expr, "Procedure tag already used: %.*s", LIT(tag_name));
} }
*tags |= tag; *tags |= tag;
} }
@@ -1306,7 +1336,7 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_n
*foreign_name = f->curr_token.string; *foreign_name = f->curr_token.string;
// TODO(bill): Check if valid string // TODO(bill): Check if valid string
if (!is_foreign_name_valid(*foreign_name)) { if (!is_foreign_name_valid(*foreign_name)) {
syntax_error(ast_node_token(tag_expr), "Invalid alternative foreign procedure name: `%.*s`", LIT(*foreign_name)); syntax_error_node(tag_expr, "Invalid alternative foreign procedure name: `%.*s`", LIT(*foreign_name));
} }
next_token(f); next_token(f);
@@ -1317,7 +1347,7 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_n
*link_name = f->curr_token.string; *link_name = f->curr_token.string;
// TODO(bill): Check if valid string // TODO(bill): Check if valid string
if (!is_foreign_name_valid(*link_name)) { if (!is_foreign_name_valid(*link_name)) {
syntax_error(ast_node_token(tag_expr), "Invalid alternative link procedure name `%.*s`", LIT(*link_name)); syntax_error_node(tag_expr, "Invalid alternative link procedure name `%.*s`", LIT(*link_name));
} }
next_token(f); next_token(f);
@@ -1335,7 +1365,7 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_n
ELSE_IF_ADD_TAG(fastcall) ELSE_IF_ADD_TAG(fastcall)
// ELSE_IF_ADD_TAG(cdecl) // ELSE_IF_ADD_TAG(cdecl)
else { else {
syntax_error(ast_node_token(tag_expr), "Unknown procedure tag"); syntax_error_node(tag_expr, "Unknown procedure tag");
} }
#undef ELSE_IF_ADD_TAG #undef ELSE_IF_ADD_TAG
@@ -1437,7 +1467,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
AstNode *expr = parse_expr(f, false); AstNode *expr = parse_expr(f, false);
operand = make_run_expr(f, token, name, expr); operand = make_run_expr(f, token, name, expr);
if (unparen_expr(expr)->kind != AstNode_CallExpr) { if (unparen_expr(expr)->kind != AstNode_CallExpr) {
error(ast_node_token(expr), "#run can only be applied to procedure calls"); error_node(expr, "#run can only be applied to procedure calls");
operand = make_bad_expr(f, token, f->curr_token); operand = make_bad_expr(f, token, f->curr_token);
} }
warning(token, "#run is not yet implemented"); warning(token, "#run is not yet implemented");
@@ -1999,7 +2029,7 @@ AstNodeArray parse_record_params(AstFile *f, isize *decl_count_, bool using_allo
} }
array_add(&decls, vd); array_add(&decls, vd);
} else { } else {
syntax_error(ast_node_token(decl), "Illegal %.*s field", LIT(context)); syntax_error_node(decl, "Illegal %.*s field", LIT(context));
} }
} break; } break;
@@ -2021,7 +2051,7 @@ AstNodeArray parse_record_params(AstFile *f, isize *decl_count_, bool using_allo
break; break;
} }
} else { } else {
syntax_error(ast_node_token(decl), "Illegal record field: %.*s", LIT(ast_node_strings[decl->kind])); syntax_error_node(decl, "Illegal record field: %.*s", LIT(ast_node_strings[decl->kind]));
} }
} }
@@ -2293,7 +2323,7 @@ AstNode *parse_decl(AstFile *f, AstNodeArray names) {
String n = name->Ident.string; String n = name->Ident.string;
// NOTE(bill): Check for reserved identifiers // NOTE(bill): Check for reserved identifiers
if (str_eq(n, str_lit("context"))) { if (str_eq(n, str_lit("context"))) {
syntax_error(ast_node_token(name), "`context` is a reserved identifier"); syntax_error_node(name, "`context` is a reserved identifier");
break; break;
} }
} }
@@ -2326,7 +2356,7 @@ AstNode *parse_decl(AstFile *f, AstNodeArray names) {
next_token(f); next_token(f);
} }
if (names.count != 1) { if (names.count != 1) {
syntax_error(ast_node_token(names.e[0]), "You can only declare one type at a time"); syntax_error_node(names.e[0], "You can only declare one type at a time");
return make_bad_decl(f, names.e[0]->Ident, token); return make_bad_decl(f, names.e[0]->Ident, token);
} }
@@ -3164,7 +3194,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, AstNodeArray
node->kind != AstNode_BadStmt && node->kind != AstNode_BadStmt &&
node->kind != AstNode_EmptyStmt) { node->kind != AstNode_EmptyStmt) {
// NOTE(bill): Sanity check // NOTE(bill): Sanity check
syntax_error(ast_node_token(node), "Only declarations are allowed at file scope"); syntax_error_node(node, "Only declarations are allowed at file scope");
} else if (node->kind == AstNode_WhenStmt) { } else if (node->kind == AstNode_WhenStmt) {
parse_setup_file_when_stmt(p, f, base_dir, &node->WhenStmt); parse_setup_file_when_stmt(p, f, base_dir, &node->WhenStmt);
} else if (node->kind == AstNode_ImportDecl) { } else if (node->kind == AstNode_ImportDecl) {
@@ -3173,9 +3203,9 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, AstNodeArray
if (!is_import_path_valid(file_str)) { if (!is_import_path_valid(file_str)) {
if (id->is_load) { if (id->is_load) {
syntax_error(ast_node_token(node), "Invalid #load path: `%.*s`", LIT(file_str)); syntax_error_node(node, "Invalid #load path: `%.*s`", LIT(file_str));
} else { } else {
syntax_error(ast_node_token(node), "Invalid #import path: `%.*s`", LIT(file_str)); syntax_error_node(node, "Invalid #import path: `%.*s`", LIT(file_str));
} }
// NOTE(bill): It's a naughty name // NOTE(bill): It's a naughty name
decls.e[i] = make_bad_decl(f, id->token, id->token); decls.e[i] = make_bad_decl(f, id->token, id->token);
@@ -3202,9 +3232,9 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, AstNodeArray
if (!is_import_path_valid(file_str)) { if (!is_import_path_valid(file_str)) {
if (fl->is_system) { if (fl->is_system) {
syntax_error(ast_node_token(node), "Invalid `foreign_system_library` path"); syntax_error_node(node, "Invalid `foreign_system_library` path");
} else { } else {
syntax_error(ast_node_token(node), "Invalid `foreign_library` path"); syntax_error_node(node, "Invalid `foreign_library` path");
} }
// NOTE(bill): It's a naughty name // NOTE(bill): It's a naughty name
f->decls.e[i] = make_bad_decl(f, fl->token, fl->token); f->decls.e[i] = make_bad_decl(f, fl->token, fl->token);
+25 -18
View File
@@ -177,67 +177,74 @@ void init_global_error_collector(void) {
} }
void warning(Token token, char *fmt, ...) { void warning_va(Token token, char *fmt, va_list va) {
gb_mutex_lock(&global_error_collector.mutex); gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.warning_count++; global_error_collector.warning_count++;
// NOTE(bill): Duplicate error, skip it // NOTE(bill): Duplicate error, skip it
if (!token_pos_are_equal(global_error_collector.prev, token.pos)) { if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
va_list va;
global_error_collector.prev = token.pos; global_error_collector.prev = token.pos;
va_start(va, fmt);
gb_printf_err("%.*s(%td:%td) Warning: %s\n", gb_printf_err("%.*s(%td:%td) Warning: %s\n",
LIT(token.pos.file), token.pos.line, token.pos.column, LIT(token.pos.file), token.pos.line, token.pos.column,
gb_bprintf_va(fmt, va)); gb_bprintf_va(fmt, va));
va_end(va);
} }
gb_mutex_unlock(&global_error_collector.mutex); gb_mutex_unlock(&global_error_collector.mutex);
} }
void error(Token token, char *fmt, ...) { void error_va(Token token, char *fmt, va_list va) {
gb_mutex_lock(&global_error_collector.mutex); gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.count++; global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it // NOTE(bill): Duplicate error, skip it
if (!token_pos_are_equal(global_error_collector.prev, token.pos)) { if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
va_list va;
global_error_collector.prev = token.pos; global_error_collector.prev = token.pos;
va_start(va, fmt);
gb_printf_err("%.*s(%td:%td) %s\n", gb_printf_err("%.*s(%td:%td) %s\n",
LIT(token.pos.file), token.pos.line, token.pos.column, LIT(token.pos.file), token.pos.line, token.pos.column,
gb_bprintf_va(fmt, va)); gb_bprintf_va(fmt, va));
va_end(va);
} }
gb_mutex_unlock(&global_error_collector.mutex); gb_mutex_unlock(&global_error_collector.mutex);
} }
void syntax_error(Token token, char *fmt, ...) { void syntax_error_va(Token token, char *fmt, va_list va) {
gb_mutex_lock(&global_error_collector.mutex); gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.count++; global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it // NOTE(bill): Duplicate error, skip it
if (!token_pos_are_equal(global_error_collector.prev, token.pos)) { if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
va_list va;
global_error_collector.prev = token.pos; global_error_collector.prev = token.pos;
va_start(va, fmt);
gb_printf_err("%.*s(%td:%td) Syntax Error: %s\n", gb_printf_err("%.*s(%td:%td) Syntax Error: %s\n",
LIT(token.pos.file), token.pos.line, token.pos.column, LIT(token.pos.file), token.pos.line, token.pos.column,
gb_bprintf_va(fmt, va)); gb_bprintf_va(fmt, va));
va_end(va);
} }
gb_mutex_unlock(&global_error_collector.mutex); gb_mutex_unlock(&global_error_collector.mutex);
} }
void warning(Token token, char *fmt, ...) {
va_list va;
va_start(va, fmt);
warning_va(token, fmt, va);
va_end(va);
}
void error(Token token, char *fmt, ...) {
va_list va;
va_start(va, fmt);
error_va(token, fmt, va);
va_end(va);
}
void syntax_error(Token token, char *fmt, ...) {
va_list va;
va_start(va, fmt);
syntax_error_va(token, fmt, va);
va_end(va);
}
void compiler_error(char *fmt, ...) { void compiler_error(char *fmt, ...) {
va_list va; va_list va;