Browse Source

error_node

Ginger Bill 8 years ago
parent
commit
b76c8abe73
8 changed files with 335 additions and 257 deletions
  1. 18 17
      core/os.odin
  2. 12 11
      src/checker/checker.c
  3. 16 19
      src/checker/decl.c
  4. 164 137
      src/checker/expr.c
  5. 38 38
      src/checker/stmt.c
  6. 16 1
      src/checker/types.c
  7. 46 16
      src/parser.c
  8. 25 18
      src/tokenizer.c

+ 18 - 17
core/os.odin

@@ -6,7 +6,10 @@ when ODIN_OS == "windows" {
 File_Time :: type u64
 
 File :: struct {
-	Handle :: type win32.HANDLE
+	Handle :: raw_union {
+		p: rawptr
+		i: int
+	}
 	handle:          Handle
 	last_write_time: File_Time
 }
@@ -15,10 +18,9 @@ open :: proc(name: string) -> (File, bool) {
 	using win32
 	buf: [300]byte
 	copy(buf[:], name as []byte)
-	f := File{
-		handle = CreateFileA(^buf[0], FILE_GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, nil),
-	}
-	success := f.handle != INVALID_HANDLE_VALUE as File.Handle
+	f: File
+	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
 	f.last_write_time = last_write_time(^f)
 	return f, success
 }
@@ -27,21 +29,20 @@ create :: proc(name: string) -> (File, bool) {
 	using win32
 	buf: [300]byte
 	copy(buf[:], name as []byte)
-	f := File{
-		handle = CreateFileA(^buf[0], FILE_GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS, 0, nil),
-	}
-	success := f.handle != INVALID_HANDLE_VALUE as File.Handle
+	f: File
+	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
 	f.last_write_time = last_write_time(^f)
 	return f, success
 }
 
 close :: proc(using f: ^File) {
-	win32.CloseHandle(handle)
+	win32.CloseHandle(handle.p as win32.HANDLE)
 }
 
 write :: proc(using f: ^File, buf: []byte) -> bool {
 	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 {
@@ -57,7 +58,7 @@ file_has_changed :: proc(f: ^File) -> bool {
 
 last_write_time :: proc(f: ^File) -> File_Time {
 	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
 	h := file_info.last_write_time.high_date_time as File_Time
 	return l | h << 32
@@ -91,9 +92,9 @@ File_Standard :: type enum {
 
 // NOTE(bill): Uses startup to initialize it
 __std_files := [File_Standard.count]File{
-	{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE)},
-	{handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)},
-	{handle = win32.GetStdHandle(win32.STD_ERROR_HANDLE)},
+	{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE)  transmute File.Handle },
+	{handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE) transmute File.Handle },
+	{handle = win32.GetStdHandle(win32.STD_ERROR_HANDLE)  transmute File.Handle },
 }
 
 stdin  := ^__std_files[File_Standard.INPUT]
@@ -113,7 +114,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
 	defer close(^f)
 
 	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 {
 		return nil, false
 	}
@@ -136,7 +137,7 @@ read_entire_file :: proc(name: string) -> ([]byte, bool) {
 			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 {
 			free(data.data)
 			return nil, false

+ 12 - 11
src/checker/checker.c

@@ -546,6 +546,7 @@ void init_universal_scope(void) {
 
 	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_ARCH"),    str_lit("amd64"));
 	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) {
 	GB_ASSERT(identifier != NULL);
 	if (identifier->kind == AstNode_Ident) {
-		GB_ASSERT(identifier->kind == AstNode_Ident);
 		HashKey key = hash_pointer(identifier);
 		map_entity_set(&i->definitions, key, entity);
 	} 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) {
+	GB_ASSERT(identifier->kind == AstNode_Ident);
 	GB_ASSERT(str_eq(identifier->Ident.string, e->token.string));
 	add_entity(c, e->scope, identifier, e);
 	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};
 	check_expr(c, &operand, ws->cond);
 	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) {
-		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) {
-		error(ast_node_token(ws->cond), "Invalid body for `when` statement");
+		error_node(ws->cond, "Invalid body for `when` statement");
 	} else {
 		if (operand.value.kind == ExactValue_Bool &&
 		    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);
 				break;
 			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;
 			}
 		}
@@ -1200,9 +1201,9 @@ void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray
 					if (is_string_an_identifier(filename)) {
 						import_name = filename;
 					} else {
-						error(ast_node_token(decl),
-						      "File name, %.*s, cannot be as an import name as it is not a valid identifier",
-						      LIT(filename));
+						error_node(decl,
+						           "File name, %.*s, cannot be as an import name as it is not a valid identifier",
+						           LIT(filename));
 					}
 				}
 
@@ -1261,9 +1262,9 @@ void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray
 			isize rhs_count = cd->values.count;
 
 			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) {
-				error(ast_node_token(decl), "Extra initial expression");
+				error_node(decl, "Extra initial expression");
 			}
 		case_end;
 		case_ast_node(vd, VarDecl, decl);
@@ -1326,7 +1327,7 @@ void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray
 
 		default:
 			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;
 		}

+ 16 - 19
src/checker/decl.c

@@ -16,7 +16,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex
 			gbString expr_str = expr_to_string(operand->expr);
 
 			// 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",
 			      expr_str,
 			      LIT(context_name));
@@ -175,7 +175,7 @@ void check_var_decl_node(Checker *c, AstNode *node) {
 				entity = found;
 			}
 		} 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) {
 			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) {
 		// 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));
 		if (e->type == NULL) {
 			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)) {
 	// 	gbString type_str = type_to_string(operand->type);
 	// 	defer (gb_string_free(type_str));
-	// 	error(ast_node_token(operand->expr),
+	// 	error_node(operand->expr,
 	// 	      "Invalid constant type: `%s`", type_str);
 	// 	if (e->type == NULL) {
 	// 		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)) {
 		// 	gbString str = type_to_string(t);
 		// 	defer (gb_string_free(str));
-		// 	error(ast_node_token(type_expr),
+		// 	error_node(type_expr),
 		// 	      "Invalid constant type `%s`", str);
 		// 	e->type = t_invalid;
 		// 	return;
@@ -380,19 +380,16 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 	}
 
 	if (is_inline && is_no_inline) {
-		error(ast_node_token(pd->type),
-		      "You cannot apply both `inline` and `no_inline` to a procedure");
+		error_node(pd->type, "You cannot apply both `inline` and `no_inline` to a procedure");
 	}
 
 	if (is_foreign && is_link_name) {
-		error(ast_node_token(pd->type),
-		      "You cannot apply both `foreign` and `link_name` to a procedure");
+		error_node(pd->type, "You cannot apply both `foreign` and `link_name` to a procedure");
 	}
 
 	if (pd->body != NULL) {
 		if (is_foreign) {
-			error(ast_node_token(pd->body),
-			      "A procedure tagged as `#foreign` cannot have a body");
+			error_node(pd->body, "A procedure tagged as `#foreign` cannot have a body");
 		}
 
 		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 *other_type = base_type(f->type);
 			if (!are_signatures_similar_enough(this_type, other_type)) {
-				error(ast_node_token(d->proc_decl),
-				      "Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
-				      "\tat %.*s(%td:%td)",
-				      LIT(name), LIT(pos.file), pos.line, pos.column);
+				error_node(d->proc_decl,
+				           "Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
+				           "\tat %.*s(%td:%td)",
+				           LIT(name), LIT(pos.file), pos.line, pos.column);
 			}
 		} else {
 			map_entity_set(fp, key, e);
@@ -434,10 +431,10 @@ void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
 		if (found) {
 			Entity *f = *found;
 			TokenPos pos = f->token.pos;
-			error(ast_node_token(d->proc_decl),
-			      "Non unique #link_name for procedure `%.*s`\n"
-			      "\tother at %.*s(%td:%td)",
-			      LIT(name), LIT(pos.file), pos.line, pos.column);
+			error_node(d->proc_decl,
+			           "Non unique #link_name for procedure `%.*s`\n"
+			           "\tother at %.*s(%td:%td)",
+			           LIT(name), LIT(pos.file), pos.line, pos.column);
 		} else {
 			map_entity_set(fp, key, e);
 		}

File diff suppressed because it is too large
+ 164 - 137
src/checker/expr.c


+ 38 - 38
src/checker/stmt.c

@@ -216,10 +216,10 @@ Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) {
 		gbString str = expr_to_string(op_b.expr);
 		switch (op_b.mode) {
 		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;
 		default:
-			error(ast_node_token(op_b.expr), "Cannot assign to `%s`", str);
+			error_node(op_b.expr, "Cannot assign to `%s`", str);
 			break;
 		}
 		gb_string_free(str);
@@ -284,13 +284,13 @@ void check_when_stmt(Checker *c, AstNodeWhenStmt *ws, u32 flags) {
 	Operand operand = {Addressing_Invalid};
 	check_expr(c, &operand, ws->cond);
 	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) {
-		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) {
-		error(ast_node_token(ws->cond), "Invalid body for `when` statement");
+		error_node(ws->cond, "Invalid body for `when` statement");
 	} else {
 		if (operand.value.kind == ExactValue_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);
 				break;
 			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;
 			}
 		}
@@ -323,7 +323,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 		ExprKind kind = check_expr_base(c, &operand, es->expr, NULL);
 		switch (operand.mode) {
 		case Addressing_Type:
-			error(ast_node_token(node), "Is not an expression");
+			error_node(node, "Is not an expression");
 			break;
 		case Addressing_NoValue:
 			return;
@@ -335,7 +335,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				return;
 			}
 			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);
 		} break;
 		}
@@ -343,7 +343,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 
 	case_ast_node(ts, TagStmt, node);
 		// 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);
 	case_end;
 
@@ -426,7 +426,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				check_assignment_variable(c, &operands.e[i], lhs);
 			}
 			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);
@@ -479,7 +479,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 		Operand operand = {Addressing_Invalid};
 		check_expr(c, &operand, is->cond);
 		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);
@@ -491,7 +491,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				check_stmt(c, is->else_stmt, mod_flags);
 				break;
 			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;
 			}
 		}
@@ -526,13 +526,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				variables = tuple->variables;
 			}
 			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 {
 				check_init_variables(c, variables, result_count,
 				                     rs->results, str_lit("return statement"));
 			}
 		} 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;
 
@@ -548,8 +548,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			check_expr(c, &operand, fs->cond);
 			if (operand.mode != Addressing_Invalid &&
 			    !is_type_boolean(operand.type)) {
-				error(ast_node_token(fs->cond),
-				      "Non-boolean condition in `for` statement");
+				error_node(fs->cond, "Non-boolean condition in `for` statement");
 			}
 		}
 		if (fs->post != NULL) {
@@ -595,15 +594,16 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 					default_stmt = stmt;
 				}
 			} else {
-				error(ast_node_token(stmt), "Invalid AST - expected case clause");
+				error_node(stmt, "Invalid AST - expected case clause");
 			}
 
 			if (default_stmt != NULL) {
 				if (first_default != NULL) {
 					TokenPos pos = ast_node_token(first_default).pos;
-					error(ast_node_token(stmt),
-					      "multiple `default` clauses\n"
-					      "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
+					error_node(stmt,
+					           "multiple `default` clauses\n"
+					           "\tfirst at %.*s(%td:%td)",
+					           LIT(pos.file), pos.line, pos.column);
 				} else {
 					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)) {
 								TokenPos pos = tap.token.pos;
 								gbString expr_str = expr_to_string(y.expr);
-								error(ast_node_token(y.expr),
-								      "Duplicate case `%s`\n"
-								      "\tprevious case at %.*s(%td:%td)",
-								      expr_str,
-								      LIT(pos.file), pos.line, pos.column);
+								error_node(y.expr,
+								           "Duplicate case `%s`\n"
+								           "\tprevious case at %.*s(%td:%td)",
+								           expr_str,
+								           LIT(pos.file), pos.line, pos.column);
 								gb_string_free(expr_str);
 								continue_outer = true;
 								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"));
 		if (!check_valid_type_match_type(x.type, &is_union_ptr, &is_any)) {
 			gbString str = type_to_string(x.type);
-			error(ast_node_token(x.expr),
-			      "Invalid type for this type match expression, got `%s`", str);
+			error_node(x.expr,
+			           "Invalid type for this type match expression, got `%s`", str);
 			gb_string_free(str);
 			break;
 		}
@@ -732,15 +732,15 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 					default_stmt = stmt;
 				}
 			} else {
-				error(ast_node_token(stmt), "Invalid AST - expected case clause");
+				error_node(stmt, "Invalid AST - expected case clause");
 			}
 
 			if (default_stmt != NULL) {
 				if (first_default != NULL) {
 					TokenPos pos = ast_node_token(first_default).pos;
-					error(ast_node_token(stmt),
-					      "multiple `default` clauses\n"
-					      "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
+					error_node(stmt,
+					           "Multiple `default` clauses\n"
+					           "\tfirst at %.*s(%td:%td)", LIT(pos.file), pos.line, pos.column);
 				} else {
 					first_default = default_stmt;
 				}
@@ -785,8 +785,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 					}
 					if (!tag_type_found) {
 						gbString type_str = type_to_string(y.type);
-						error(ast_node_token(y.expr),
-						      "Unknown tag type, got `%s`", type_str);
+						error_node(y.expr,
+						           "Unknown tag type, got `%s`", type_str);
 						gb_string_free(type_str);
 						continue;
 					}
@@ -802,11 +802,11 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 				if (found) {
 					TokenPos pos = cc->token.pos;
 					gbString expr_str = expr_to_string(y.expr);
-					error(ast_node_token(y.expr),
-					      "Duplicate type case `%s`\n"
-					      "\tprevious type case at %.*s(%td:%td)",
-					      expr_str,
-					      LIT(pos.file), pos.line, pos.column);
+					error_node(y.expr,
+					           "Duplicate type case `%s`\n"
+					           "\tprevious type case at %.*s(%td:%td)",
+					           expr_str,
+					           LIT(pos.file), pos.line, pos.column);
 					gb_string_free(expr_str);
 					break;
 				}

+ 16 - 1
src/checker/types.c

@@ -667,9 +667,24 @@ bool is_type_comparable(Type *t) {
 	t = base_type(get_enum_base_type(t));
 	switch (t->kind) {
 	case Type_Basic:
-		return t->kind != Basic_UntypedNil && t->kind != Basic_any;
+		return t->kind != Basic_UntypedNil;
 	case Type_Pointer:
 		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:
 		return is_type_comparable(t->Vector.elem);
 	case Type_Proc:

+ 46 - 16
src/parser.c

@@ -355,8 +355,6 @@ typedef struct AstNode {
 #define case_end } break;
 
 
-
-
 gb_inline bool is_ast_node_expr(AstNode *node) {
 	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;
 }
 
+
+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++
 AstNode *make_node(AstFile *f, AstNodeKind kind) {
 	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) {
 	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;
 }
@@ -1306,7 +1336,7 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_n
 				*foreign_name = f->curr_token.string;
 				// TODO(bill): Check if valid string
 				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);
@@ -1317,7 +1347,7 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_n
 				*link_name = f->curr_token.string;
 				// TODO(bill): Check if valid string
 				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);
@@ -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(cdecl)
 		else {
-			syntax_error(ast_node_token(tag_expr), "Unknown procedure tag");
+			syntax_error_node(tag_expr, "Unknown procedure tag");
 		}
 
 		#undef ELSE_IF_ADD_TAG
@@ -1437,7 +1467,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
 			AstNode *expr = parse_expr(f, false);
 			operand = make_run_expr(f, token, name, expr);
 			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);
 			}
 			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);
 				} else {
-					syntax_error(ast_node_token(decl), "Illegal %.*s field", LIT(context));
+					syntax_error_node(decl, "Illegal %.*s field", LIT(context));
 				}
 			} break;
 
@@ -2021,7 +2051,7 @@ AstNodeArray parse_record_params(AstFile *f, isize *decl_count_, bool using_allo
 				break;
 			}
 		} 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;
 			// NOTE(bill): Check for reserved identifiers
 			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;
 			}
 		}
@@ -2326,7 +2356,7 @@ AstNode *parse_decl(AstFile *f, AstNodeArray names) {
 				next_token(f);
 			}
 			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);
 			}
 
@@ -3164,7 +3194,7 @@ void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, AstNodeArray
 		    node->kind != AstNode_BadStmt &&
 		    node->kind != AstNode_EmptyStmt) {
 			// 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) {
 			parse_setup_file_when_stmt(p, f, base_dir, &node->WhenStmt);
 		} 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 (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 {
-					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
 				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 (fl->is_system) {
-					syntax_error(ast_node_token(node), "Invalid `foreign_system_library` path");
+					syntax_error_node(node, "Invalid `foreign_system_library` path");
 				} 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
 				f->decls.e[i] = make_bad_decl(f, fl->token, fl->token);

+ 25 - 18
src/tokenizer.c

@@ -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);
 
 	global_error_collector.warning_count++;
 	// NOTE(bill): Duplicate error, skip it
 	if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
-		va_list va;
-
 		global_error_collector.prev = token.pos;
-
-		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);
 	}
 
 	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);
 
 	global_error_collector.count++;
 	// NOTE(bill): Duplicate error, skip it
 	if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
-		va_list va;
-
 		global_error_collector.prev = token.pos;
-
-		va_start(va, fmt);
 		gb_printf_err("%.*s(%td:%td) %s\n",
 		              LIT(token.pos.file), token.pos.line, token.pos.column,
 		              gb_bprintf_va(fmt, va));
-		va_end(va);
 	}
 
 	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);
 
 	global_error_collector.count++;
 	// NOTE(bill): Duplicate error, skip it
 	if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
-		va_list va;
-
 		global_error_collector.prev = token.pos;
-
-		va_start(va, fmt);
 		gb_printf_err("%.*s(%td:%td) Syntax Error: %s\n",
 		              LIT(token.pos.file), token.pos.line, token.pos.column,
 		              gb_bprintf_va(fmt, va));
-		va_end(va);
 	}
 
 	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, ...) {
 	va_list va;
 

Some files were not shown because too many files changed in this diff