Kaynağa Gözat

Minor Style Fixes

Ginger Bill 8 yıl önce
ebeveyn
işleme
f60dc7b0a7

+ 1 - 19
code/demo.odin

@@ -1,25 +1,7 @@
 #import "fmt.odin"
 
 main :: proc() {
-	Entity :: union {
-		Apple: int
-		Banana: f32
-		Goat: struct {
-			x, y: int
-			z, w: f32
-		}
-	}
 
-	a := 123 as Entity.Apple
-	e: Entity = a
-	fmt.println(a)
-
-	if apple, ok := ^e union_cast ^Entity.Apple; ok {
-		apple^ = 321
-		e = apple^
-	}
-
-	apple, ok := e union_cast Entity.Apple
-	fmt.println(apple)
 }
 
+

+ 7 - 5
code/game.odin

@@ -46,9 +46,12 @@ make_window :: proc(title: string, msg, height: int, window_proc: win32.WNDPROC)
 	w: Window
 	w.width, w.height = msg, height
 
-	class_name   := "Win32-Odin-Window\x00"
-	c_class_name := class_name.data
-	w.c_title = to_c_string(title)
+	c_class_name := "Win32-Odin-Window\x00".data
+	if title[title.count-1] != 0 {
+		w.c_title = to_c_string(title)
+	} else {
+		w.c_title = title as []u8
+	}
 
 	instance := GetModuleHandleA(nil)
 
@@ -190,6 +193,7 @@ run :: proc() {
 
 		draw_rect :: proc(x, y, w, h: f32) {
 			gl.Begin(gl.TRIANGLES)
+			defer gl.End()
 
 			gl.Color3f(1, 0, 0); gl.Vertex3f(x,   y,   0)
 			gl.Color3f(0, 1, 0); gl.Vertex3f(x+w, y,   0)
@@ -198,8 +202,6 @@ run :: proc() {
 			gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0)
 			gl.Color3f(1, 1, 0); gl.Vertex3f(x,   y+h, 0)
 			gl.Color3f(1, 0, 0); gl.Vertex3f(x,   y,   0)
-
-			gl.End()
 		}
 
 		draw_rect(pos[0], pos[1], 50, 50)

+ 16 - 17
core/mem.odin

@@ -88,6 +88,7 @@ align_forward :: proc(ptr: rawptr, align: int) -> rawptr {
 }
 
 
+
 AllocationHeader :: struct {
 	size: int
 }
@@ -109,21 +110,25 @@ allocation_header :: proc(data: rawptr) -> ^AllocationHeader {
 
 
 
+
+
 // Custom allocators
 
 Arena :: struct {
 	backing:    Allocator
-	memory:     []u8
+	memory:     []byte
 	temp_count: int
-}
 
-Temp_Arena_Memory :: struct {
-	arena:          ^Arena
-	original_count: int
+	Temp_Memory :: struct {
+		arena:          ^Arena
+		original_count: int
+	}
 }
 
 
 
+
+
 init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
 	backing    = Allocator{}
 	memory     = data[:0]
@@ -132,16 +137,10 @@ init_arena_from_memory :: proc(using a: ^Arena, data: []byte) {
 
 init_arena_from_context :: proc(using a: ^Arena, size: int) {
 	backing = context.allocator
-	memory = new_slice(u8, 0, size)
+	memory = new_slice(byte, 0, size)
 	temp_count = 0
 }
 
-init_sub_arena :: proc(sub, parent: ^Arena, size: int) {
-	push_allocator arena_allocator(parent) {
-		init_arena_from_context(sub, size)
-	}
-}
-
 free_arena :: proc(using a: ^Arena) {
 	if backing.procedure != nil {
 		push_allocator backing {
@@ -181,7 +180,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator.Mode,
 
 	case FREE:
 		// NOTE(bill): Free all at once
-		// Use Temp_Arena_Memory if you want to free a block
+		// Use Arena.Temp_Memory if you want to free a block
 
 	case FREE_ALL:
 		arena.memory.count = 0
@@ -193,15 +192,15 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator.Mode,
 	return nil
 }
 
-begin_temp_arena_memory :: proc(a: ^Arena) -> Temp_Arena_Memory {
-	tmp: Temp_Arena_Memory
+begin_arena_temp_memory :: proc(a: ^Arena) -> Arena.Temp_Memory {
+	tmp: Arena.Temp_Memory
 	tmp.arena = a
 	tmp.original_count = a.memory.count
 	a.temp_count++
 	return tmp
 }
 
-end_temp_arena_memory :: proc(using tmp: Temp_Arena_Memory) {
+end_arena_temp_memory :: proc(using tmp: Arena.Temp_Memory) {
 	assert(arena.memory.count >= original_count)
 	assert(arena.temp_count > 0)
 	arena.memory.count = original_count
@@ -232,7 +231,7 @@ align_of_type_info :: proc(type_info: ^Type_Info) -> int {
 	case Pointer:
 		return WORD_SIZE
 	case Maybe:
-		return align_of_type_info(info.elem)
+		return max(align_of_type_info(info.elem), 1)
 	case Procedure:
 		return WORD_SIZE
 	case Array:

+ 1 - 0
core/os.odin

@@ -43,6 +43,7 @@ File_Standard :: type enum {
 	COUNT,
 }
 
+// NOTE(bill): Uses startup to initialize it
 __std_files := [..]File{
 	File{handle = win32.GetStdHandle(win32.STD_INPUT_HANDLE)},
 	File{handle = win32.GetStdHandle(win32.STD_OUTPUT_HANDLE)},

+ 3 - 0
core/utf8.odin

@@ -1,8 +1,11 @@
 RUNE_ERROR :: #rune "\ufffd"
 RUNE_SELF  :: 0x80
+RUNE_BOM   :: 0xfeff
+RUNE_EOF   :: ~(0 as rune)
 MAX_RUNE   :: #rune "\U0010ffff"
 UTF_MAX    :: 4
 
+
 SURROGATE_MIN :: 0xd800
 SURROGATE_MAX :: 0xdfff
 

+ 1 - 1
misc/debug.bat

@@ -1,3 +1,3 @@
 @echo off
 
-call devenv.exe odin.sln
+start devenv.exe odin.sln

+ 28 - 22
src/checker/checker.cpp

@@ -26,26 +26,23 @@ String const addressing_mode_strings[] = {
 
 struct Operand {
 	AddressingMode mode;
-	Type *type;
-	ExactValue value;
-	AstNode *expr;
-	BuiltinProcId builtin_id;
+	Type *         type;
+	ExactValue     value;
+	AstNode *      expr;
+	BuiltinProcId  builtin_id;
 };
-b32 is_operand_nil(Operand *o) {
-	return o->mode == Addressing_Value && o->type == t_untyped_nil;
-}
 
 struct TypeAndValue {
 	AddressingMode mode;
-	Type *type;
-	ExactValue value;
+	Type *         type;
+	ExactValue     value;
 };
 
 struct DeclInfo {
 	Scope *scope;
 
 	Entity **entities;
-	isize entity_count;
+	isize    entity_count;
 
 	AstNode *type_expr;
 	AstNode *init_expr;
@@ -55,13 +52,11 @@ struct DeclInfo {
 	Map<b32> deps; // Key: Entity *
 };
 
-
-
 struct ExpressionInfo {
-	b32 is_lhs; // Debug info
+	b32            is_lhs; // Debug info
 	AddressingMode mode;
-	Type *type; // Type_Basic
-	ExactValue value;
+	Type *         type; // Type_Basic
+	ExactValue     value;
 };
 
 ExpressionInfo make_expression_info(b32 is_lhs, AddressingMode mode, Type *type, ExactValue value) {
@@ -213,10 +208,9 @@ struct CheckerInfo {
 	Map<ExpressionInfo>    untyped;         // Key: AstNode * | Expression -> ExpressionInfo
 	Map<DeclInfo *>        entities;        // Key: Entity *
 	Map<Entity *>          foreign_procs;   // Key: String
+	Map<AstFile *>         files;           // Key: String (full path)
 	Map<isize>             type_info_map;   // Key: Type *
-	Map<AstFile *>         files;           // Key: String
 	isize                  type_info_index;
-
 	Entity *               implicit_values[ImplicitValue_Count];
 };
 
@@ -282,12 +276,14 @@ void destroy_declaration_info(DeclInfo *d) {
 }
 
 b32 decl_info_has_init(DeclInfo *d) {
-	if (d->init_expr != NULL)
+	if (d->init_expr != NULL) {
 		return true;
+	}
 	if (d->proc_decl != NULL) {
 		ast_node(pd, ProcDecl, d->proc_decl);
-		if (pd->body != NULL)
+		if (pd->body != NULL) {
 			return true;
+		}
 	}
 
 	return false;
@@ -629,12 +625,14 @@ Entity *entity_of_ident(CheckerInfo *i, AstNode *identifier) {
 
 Type *type_of_expr(CheckerInfo *i, AstNode *expression) {
 	TypeAndValue *found = type_and_value_of_expression(i, expression);
-	if (found)
+	if (found) {
 		return found->type;
+	}
 	if (expression->kind == AstNode_Ident) {
 		Entity *entity = entity_of_ident(i, expression);
-		if (entity)
+		if (entity) {
 			return entity->type;
+		}
 	}
 
 	return NULL;
@@ -647,8 +645,9 @@ void add_untyped(CheckerInfo *i, AstNode *expression, b32 lhs, AddressingMode mo
 
 void add_type_and_value(CheckerInfo *i, AstNode *expression, AddressingMode mode, Type *type, ExactValue value) {
 	GB_ASSERT(expression != NULL);
-	if (mode == Addressing_Invalid)
+	if (mode == Addressing_Invalid) {
 		return;
+	}
 
 	if (mode == Addressing_Constant) {
 		if (is_type_constant_type(type)) {
@@ -1145,6 +1144,12 @@ void check_parsed_files(Checker *c) {
 			auto found = map_get(&file_scopes, key);
 			GB_ASSERT_MSG(found != NULL, "Unable to find scope for file: %.*s", LIT(id->fullpath));
 			Scope *scope = *found;
+
+			if (scope->is_global) {
+				error(id->token, "Importing a #shared_global_scope is disallowed and unnecessary");
+				continue;
+			}
+
 			b32 previously_added = false;
 			for_array(import_index, file_scope->imported) {
 				Scope *prev = file_scope->imported[import_index];
@@ -1153,6 +1158,7 @@ void check_parsed_files(Checker *c) {
 					break;
 				}
 			}
+
 			if (!previously_added) {
 				array_add(&file_scope->imported, scope);
 			} else {

+ 1 - 0
src/checker/entity.cpp

@@ -106,6 +106,7 @@ Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, T
 	Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type);
 	entity->using_parent = parent;
 	entity->Variable.anonymous = true;
+	entity->Variable.anonymous = true;
 	return entity;
 }
 

+ 92 - 34
src/checker/expr.cpp

@@ -79,7 +79,7 @@ b32 check_is_assignable_to(Checker *c, Operand *operand, Type *type, b32 is_argu
 			break;
 		}
 		if (type_has_nil(dst)) {
-			return is_operand_nil(operand);
+			return operand->mode == Addressing_Value && operand->type == t_untyped_nil;
 		}
 	}
 
@@ -156,8 +156,9 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
 	PROF_PROC();
 
 	check_not_tuple(c, operand);
-	if (operand->mode == Addressing_Invalid)
+	if (operand->mode == Addressing_Invalid) {
 		return;
+	}
 
 	if (is_type_untyped(operand->type)) {
 		Type *target_type = type;
@@ -1379,10 +1380,12 @@ b32 check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, Exac
 			return true;
 		}
 	} else if (is_type_pointer(type)) {
-		if (in_value.kind == ExactValue_Pointer)
+		if (in_value.kind == ExactValue_Pointer) {
 			return true;
-		if (in_value.kind == ExactValue_Integer)
+		}
+		if (in_value.kind == ExactValue_Integer) {
 			return true;
+		}
 		if (out_value) *out_value = in_value;
 	}
 
@@ -1492,8 +1495,9 @@ void check_unary_expr(Checker *c, Operand *o, Token op, AstNode *node) {
 		o->value = exact_unary_operator_value(op, o->value, precision);
 
 		if (is_type_typed(type)) {
-			if (node != NULL)
+			if (node != NULL) {
 				o->expr = node;
+			}
 			check_is_expressible(c, o, type);
 		}
 		return;
@@ -1509,9 +1513,8 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
 	defer (gb_temp_arena_memory_end(tmp));
 
 	gbString err_str = NULL;
-	defer ({
-		if (err_str != NULL)
-			gb_string_free(err_str);
+	defer (if (err_str != NULL) {
+		gb_string_free(err_str);
 	});
 
 	if (check_is_assignable_to(c, x, y->type) ||
@@ -1520,13 +1523,13 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
 		switch (op.kind) {
 		case Token_CmpEq:
 		case Token_NotEq:
-			defined = is_type_comparable(base_type(x->type));
+			defined = is_type_comparable(get_enum_base_type(base_type(x->type)));
 			break;
 		case Token_Lt:
 		case Token_Gt:
 		case Token_LtEq:
 		case Token_GtEq: {
-			defined = is_type_ordered(base_type(x->type));
+			defined = is_type_ordered(get_enum_base_type(base_type(x->type)));
 		} break;
 		}
 
@@ -1546,7 +1549,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
 	}
 
 	if (err_str != NULL) {
-		error(op, "Cannot compare expression, %s", err_str);
+		error(ast_node_token(x->expr), "Cannot compare expression, %s", err_str);
 		x->type = t_untyped_bool;
 		return;
 	}
@@ -1682,27 +1685,29 @@ b32 check_is_castable_to(Checker *c, Operand *operand, Type *y) {
 
 	// Cast between booleans and integers
 	if (is_type_boolean(xb) || is_type_integer(xb)) {
-		if (is_type_boolean(yb) || is_type_integer(yb))
+		if (is_type_boolean(yb) || is_type_integer(yb)) {
 			return true;
+		}
 	}
 
 	// Cast between numbers
 	if (is_type_integer(xb) || is_type_float(xb)) {
-		if (is_type_integer(yb) || is_type_float(yb))
+		if (is_type_integer(yb) || is_type_float(yb)) {
 			return true;
+		}
 	}
 
 	// Cast between pointers
 	if (is_type_pointer(xb) && is_type_pointer(yb)) {
-			return true;
+		return true;
 	}
 
 	// (u)int <-> pointer
 	if (is_type_int_or_uint(xb) && is_type_rawptr(yb)) {
-			return true;
+		return true;
 	}
 	if (is_type_rawptr(xb) && is_type_int_or_uint(yb)) {
-			return true;
+		return true;
 	}
 
 	// []byte/[]u8 <-> string
@@ -1747,8 +1752,9 @@ String check_down_cast_name(Type *dst_, Type *src_) {
 
 			if (!is_type_pointer(f->type)) {
 				result = check_down_cast_name(f->type, src_);
-				if (result.len > 0)
+				if (result.len > 0) {
 					return result;
+				}
 			}
 		}
 	}
@@ -1808,8 +1814,9 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 	if (be->op.kind == Token_as) {
 		check_expr(c, x, be->left);
 		Type *type = check_type(c, be->right);
-		if (x->mode == Addressing_Invalid)
+		if (x->mode == Addressing_Invalid) {
 			return;
+		}
 
 		b32 is_const_expr = x->mode == Addressing_Constant;
 		b32 can_convert = false;
@@ -1854,8 +1861,9 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 	} else if (be->op.kind == Token_transmute) {
 		check_expr(c, x, be->left);
 		Type *type = check_type(c, be->right);
-		if (x->mode == Addressing_Invalid)
+		if (x->mode == Addressing_Invalid) {
 			return;
+		}
 
 		if (x->mode == Addressing_Constant) {
 			gbString expr_str = expr_to_string(x->expr);
@@ -2160,8 +2168,9 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 		}
 		x->value = exact_binary_operator_value(op, a, b);
 		if (is_type_typed(type)) {
-			if (node != NULL)
+			if (node != NULL) {
 				x->expr = node;
+			}
 			check_is_expressible(c, x, type);
 		}
 		return;
@@ -2176,19 +2185,22 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, b32 final) {
 
 	HashKey key = hash_pointer(e);
 	ExpressionInfo *found = map_get(&c->info.untyped, key);
-	if (found == NULL)
+	if (found == NULL) {
 		return;
+	}
 
 	switch (e->kind) {
 	case_ast_node(ue, UnaryExpr, e);
-		if (found->value.kind != ExactValue_Invalid)
+		if (found->value.kind != ExactValue_Invalid) {
 			break;
+		}
 		update_expr_type(c, ue->expr, type, final);
 	case_end;
 
 	case_ast_node(be, BinaryExpr, e);
-		if (found->value.kind != ExactValue_Invalid)
+		if (found->value.kind != ExactValue_Invalid) {
 			break;
+		}
 		if (!token_is_comparison(be->op)) {
 			if (token_is_shift(be->op)) {
 				update_expr_type(c, be->left,  type, final);
@@ -2222,8 +2234,9 @@ void update_expr_type(Checker *c, AstNode *e, Type *type, b32 final) {
 
 void update_expr_value(Checker *c, AstNode *e, ExactValue value) {
 	ExpressionInfo *found = map_get(&c->info.untyped, hash_pointer(e));
-	if (found)
+	if (found) {
 		found->value = value;
+	}
 }
 
 void convert_untyped_error(Checker *c, Operand *operand, Type *target_type) {
@@ -2389,10 +2402,10 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
 	ast_node(se, SelectorExpr, node);
 
 	b32 check_op_expr = true;
+	Entity *expr_entity = NULL;
 	Entity *entity = NULL;
 	Selection sel = {}; // NOTE(bill): Not used if it's an import name
 
-
 	AstNode *op_expr  = se->expr;
 	AstNode *selector = unparen_expr(se->selector);
 	if (selector == NULL) {
@@ -2401,10 +2414,12 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
 
 	GB_ASSERT(selector->kind == AstNode_Ident);
 
+
 	if (op_expr->kind == AstNode_Ident) {
 		String name = op_expr->Ident.string;
 		Entity *e = scope_lookup_entity(c->context.scope, name);
 		add_entity_use(c, op_expr, e);
+		expr_entity = e;
 		if (e != NULL && e->kind == Entity_ImportName) {
 			String sel_name = selector->Ident.string;
 			check_op_expr = false;
@@ -2459,6 +2474,17 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
 		goto error;
 	}
 
+	if (expr_entity != NULL && expr_entity->kind == Entity_Constant && entity->kind != Entity_Constant) {
+		gbString op_str   = expr_to_string(op_expr);
+		gbString type_str = type_to_string(operand->type);
+		gbString sel_str  = expr_to_string(selector);
+		defer (gb_string_free(op_str));
+		defer (gb_string_free(type_str));
+		defer (gb_string_free(sel_str));
+		error(ast_node_token(op_expr), "Cannot access non-constant field `%s` from `%s`", sel_str, op_str);
+		goto error;
+	}
+
 
 	add_entity_use(c, selector, entity);
 
@@ -2570,8 +2596,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		}
 
 		check_expr(c, &op, len);
-		if (op.mode == Addressing_Invalid)
+		if (op.mode == Addressing_Invalid) {
 			return false;
+		}
 		if (!is_type_integer(op.type)) {
 			gbString type_str = type_to_string(operand->type);
 			defer (gb_string_free(type_str));
@@ -2583,8 +2610,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 
 		if (cap != NULL) {
 			check_expr(c, &op, cap);
-			if (op.mode == Addressing_Invalid)
+			if (op.mode == Addressing_Invalid) {
 				return false;
+			}
 			if (!is_type_integer(op.type)) {
 				gbString type_str = type_to_string(operand->type);
 				defer (gb_string_free(type_str));
@@ -2709,8 +2737,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		ast_node(s, SelectorExpr, arg);
 
 		check_expr(c, operand, s->expr);
-		if (operand->mode == Addressing_Invalid)
+		if (operand->mode == Addressing_Invalid) {
 			return false;
+		}
 
 		Type *type = operand->type;
 		if (base_type(type)->kind == Type_Pointer) {
@@ -2750,8 +2779,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 	case BuiltinProc_type_of_val:
 		// type_of_val :: proc(val: Type) -> type(Type)
 		check_assignment(c, operand, NULL, make_string("argument of `type_of_val`"));
-		if (operand->mode == Addressing_Invalid || operand->mode == Addressing_Builtin)
+		if (operand->mode == Addressing_Invalid || operand->mode == Addressing_Builtin) {
 			return false;
+		}
 		operand->mode = Addressing_Type;
 		break;
 
@@ -3140,8 +3170,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		Operand a = *operand;
 		Operand b = {};
 		check_expr(c, &b, other_arg);
-		if (b.mode == Addressing_Invalid)
+		if (b.mode == Addressing_Invalid) {
 			return false;
+		}
 		if (!is_type_comparable(b.type) || !is_type_numeric(type)) {
 			gbString type_str = type_to_string(b.type);
 			defer (gb_string_free(type_str));
@@ -3151,7 +3182,6 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			return false;
 		}
 
-
 		if (a.mode == Addressing_Constant &&
 		    b.mode == Addressing_Constant) {
 			ExactValue x = a.value;
@@ -3170,6 +3200,15 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			operand->mode = Addressing_Value;
 			operand->type = type;
 
+			convert_to_typed(c, &a, b.type);
+			if (a.mode == Addressing_Invalid) {
+				return false;
+			}
+			convert_to_typed(c, &b, a.type);
+			if (b.mode == Addressing_Invalid) {
+				return false;
+			}
+
 			if (!are_types_identical(operand->type, b.type)) {
 				gbString type_a = type_to_string(a.type);
 				gbString type_b = type_to_string(b.type);
@@ -3200,8 +3239,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		Operand a = *operand;
 		Operand b = {};
 		check_expr(c, &b, other_arg);
-		if (b.mode == Addressing_Invalid)
+		if (b.mode == Addressing_Invalid) {
 			return false;
+		}
 		if (!is_type_comparable(b.type) || !is_type_numeric(type)) {
 			gbString type_str = type_to_string(b.type);
 			defer (gb_string_free(type_str));
@@ -3211,7 +3251,6 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			return false;
 		}
 
-
 		if (a.mode == Addressing_Constant &&
 		    b.mode == Addressing_Constant) {
 			ExactValue x = a.value;
@@ -3230,6 +3269,15 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 			operand->mode = Addressing_Value;
 			operand->type = type;
 
+			convert_to_typed(c, &a, b.type);
+			if (a.mode == Addressing_Invalid) {
+				return false;
+			}
+			convert_to_typed(c, &b, a.type);
+			if (b.mode == Addressing_Invalid) {
+				return false;
+			}
+
 			if (!are_types_identical(operand->type, b.type)) {
 				gbString type_a = type_to_string(a.type);
 				gbString type_b = type_to_string(b.type);
@@ -3847,6 +3895,8 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 		}
 
 		Type *t = base_type(type_deref(o->type));
+		b32 is_const = o->mode == Addressing_Constant;
+
 
 		auto set_index_data = [](Operand *o, Type *t, i64 *max_count) -> b32 {
 			t = base_type(type_deref(t));
@@ -3894,6 +3944,10 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 		i64 max_count = -1;
 		b32 valid = set_index_data(o, t, &max_count);
 
+		if (is_const) {
+			valid = false;
+		}
+
 		if (!valid && (is_type_struct(t) || is_type_raw_union(t))) {
 			Entity *found = find_using_index_expr(t);
 			if (found != NULL) {
@@ -3903,7 +3957,11 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
 
 		if (!valid) {
 			gbString str = expr_to_string(o->expr);
-			error(ast_node_token(o->expr), "Cannot index `%s`", str);
+			if (is_const) {
+				error(ast_node_token(o->expr), "Cannot index a constant `%s`", str);
+			} else {
+				error(ast_node_token(o->expr), "Cannot index `%s`", str);
+			}
 			gb_string_free(str);
 			goto error;
 		}

+ 29 - 15
src/checker/stmt.cpp

@@ -983,7 +983,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 			isize rhs_count = operands.count;
 
 			isize operand_index = 0;
-			for_array(i, as->lhs) {
+			for_array(i, operands) {
 				AstNode *lhs = as->lhs[i];
 				check_assignment_variable(c, &operands[i], lhs);
 			}
@@ -1400,16 +1400,19 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 		Token token = bs->token;
 		switch (token.kind) {
 		case Token_break:
-			if ((flags & Stmt_BreakAllowed) == 0)
+			if ((flags & Stmt_BreakAllowed) == 0) {
 				error(token, "`break` only allowed in `for` or `match` statements");
+			}
 			break;
 		case Token_continue:
-			if ((flags & Stmt_ContinueAllowed) == 0)
+			if ((flags & Stmt_ContinueAllowed) == 0) {
 				error(token, "`continue` only allowed in `for` statements");
+			}
 			break;
 		case Token_fallthrough:
-			if ((flags & Stmt_FallthroughAllowed) == 0)
+			if ((flags & Stmt_FallthroughAllowed) == 0) {
 				error(token, "`fallthrough` statement in illegal position");
+			}
 			break;
 		default:
 			error(token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
@@ -1432,7 +1435,6 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				e = scope_lookup_entity(c->context.scope, name);
 			} else if (expr->kind == AstNode_SelectorExpr) {
 				Operand o = {};
-				check_expr_base(c, &o, expr->SelectorExpr.expr);
 				e = check_selector(c, &o, expr);
 				is_selector = true;
 			}
@@ -1499,15 +1501,6 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				}
 			} break;
 
-			case Entity_Constant:
-				error(us->token, "`using` cannot be applied to a constant");
-				break;
-
-			case Entity_Procedure:
-			case Entity_Builtin:
-				error(us->token, "`using` cannot be applied to a procedure");
-				break;
-
 			case Entity_Variable: {
 				Type *t = base_type(type_deref(e->type));
 				if (is_type_struct(t) || is_type_raw_union(t)) {
@@ -1533,8 +1526,29 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
 				}
 			} break;
 
+			case Entity_Constant:
+				error(us->token, "`using` cannot be applied to a constant");
+				break;
+
+			case Entity_Procedure:
+			case Entity_Builtin:
+				error(us->token, "`using` cannot be applied to a procedure");
+				break;
+
+			case Entity_ImplicitValue:
+				error(us->token, "`using` cannot be applied to an implicit value");
+				break;
+
+			case Entity_Nil:
+				error(us->token, "`using` cannot be applied to `nil`");
+				break;
+
+			case Entity_Invalid:
+				error(us->token, "`using` cannot be applied to an invalid entity");
+				break;
+
 			default:
-				GB_PANIC("TODO(bill): using other expressions?");
+				GB_PANIC("TODO(bill): `using` other expressions?");
 			}
 		case_end;
 

+ 3 - 2
src/codegen/codegen.cpp

@@ -7,8 +7,9 @@ struct ssaGen {
 };
 
 b32 ssa_gen_init(ssaGen *s, Checker *c) {
-	if (global_error_collector.count != 0)
+	if (global_error_collector.count != 0) {
 		return false;
+	}
 
 	isize tc = c->parser->total_token_count;
 	if (tc < 2) {
@@ -43,7 +44,7 @@ String ssa_mangle_name(ssaGen *s, String path, String name) {
 	AstFile *file = *map_get(&info->files, hash_string(path));
 
 	char *str = gb_alloc_array(a, char, path.len+1);
-	gb_memcopy(str, path.text, path.len);
+	gb_memmove(str, path.text, path.len);
 	str[path.len] = 0;
 	for (isize i = 0; i < path.len; i++) {
 		if (str[i] == '\\') {

+ 1 - 1
src/codegen/print_llvm.cpp

@@ -31,7 +31,7 @@ void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) {
 		f->offset = 0;
 	}
 	u8 *cursor = cast(u8 *)f->vm.data + f->offset;
-	gb_memcopy(cursor, data, len);
+	gb_memmove(cursor, data, len);
 	f->offset += len;
 }
 

+ 11 - 14
src/codegen/ssa.cpp

@@ -21,14 +21,14 @@ struct ssaDebugInfo {
 
 	union {
 		struct {
-			AstFile *file;
-			String   producer;
+			AstFile *     file;
+			String        producer;
 			ssaDebugInfo *all_procs;
 		} CompileUnit;
 		struct {
 			AstFile *file;
-			String filename;
-			String directory;
+			String   filename;
+			String   directory;
 		} File;
 		struct {
 			Entity *      entity;
@@ -111,8 +111,8 @@ enum ssaDeferKind {
 
 struct ssaDefer {
 	ssaDeferKind kind;
-	isize scope_index;
-	ssaBlock *block;
+	isize        scope_index;
+	ssaBlock *   block;
 	union {
 		AstNode *stmt;
 		// NOTE(bill): `instr` will be copied every time to create a new one
@@ -121,7 +121,7 @@ struct ssaDefer {
 };
 
 struct ssaProcedure {
-	ssaProcedure *parent;
+	ssaProcedure *        parent;
 	Array<ssaProcedure *> children;
 
 	Entity *      entity;
@@ -1422,7 +1422,7 @@ void ssa_end_procedure_body(ssaProcedure *proc) {
 	proc->curr_block = proc->decl_block;
 	ssa_emit_jump(proc, proc->entry_block);
 
-#if 1
+#if 0
 	ssa_optimize_blocks(proc);
 	ssa_build_referrers(proc);
 	ssa_build_dom_tree(proc);
@@ -2243,7 +2243,6 @@ ssaValue *ssa_emit_union_cast(ssaProcedure *proc, ssaValue *value, Type *tuple)
 		}
 		GB_ASSERT(dst_tag != NULL);
 
-
 		ssaBlock *ok_block = ssa_add_block(proc, NULL, "union_cast.ok");
 		ssaBlock *end_block = ssa_add_block(proc, NULL, "union_cast.end");
 		ssaValue *cond = ssa_emit_comp(proc, Token_CmpEq, tag, dst_tag);
@@ -3647,10 +3646,10 @@ void ssa_mangle_sub_type_name(ssaModule *m, Entity *field, String parent) {
 	child.text = gb_alloc_array(m->allocator, u8, len);
 
 	isize i = 0;
-	gb_memcopy(child.text+i, parent.text, parent.len);
+	gb_memmove(child.text+i, parent.text, parent.len);
 	i += parent.len;
 	child.text[i++] = '.';
-	gb_memcopy(child.text+i, cn.text, cn.len);
+	gb_memmove(child.text+i, cn.text, cn.len);
 
 	map_set(&m->type_names, hash_pointer(field->type), child);
 	ssa_gen_global_type_name(m, field, child);
@@ -4301,8 +4300,6 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 			}
 			break;
 		}
-		// TODO(bill): Handle fallthrough scope exit correctly
-		// if (block != NULL && bs->token.kind != Token_fallthrough) {
 		if (block != NULL) {
 			ssa_emit_defer_stmts(proc, ssaDeferExit_Branch, block);
 		}
@@ -4695,7 +4692,7 @@ void ssa_build_dom_tree(ssaProcedure *proc) {
 
 	// Step 1 - number vertices
 	i32 pre_num = ssa_lt_depth_first_search(&lt, root, 0, preorder);
-	gb_memcopy(buckets, preorder, n*gb_size_of(preorder[0]));
+	gb_memmove(buckets, preorder, n*gb_size_of(preorder[0]));
 
 	for (i32 i = n-1; i > 0; i--) {
 		ssaBlock *w = preorder[i];

+ 27 - 18
src/common.cpp

@@ -1,4 +1,3 @@
-// #define GB_NO_WINDOWS_H
 #define GB_IMPLEMENTATION
 #include "gb/gb.h"
 
@@ -36,9 +35,8 @@ String get_module_dir() {
 
 	wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
 
-	String16 str = {text, len};
 	GetModuleFileNameW(NULL, text, len);
-	String path = string16_to_string(gb_heap_allocator(), str);
+	String path = string16_to_string(gb_heap_allocator(), make_string16(text, len));
 	for (isize i = path.len-1; i >= 0; i--) {
 		u8 c = path.text[i];
 		if (c == '/' || c == '\\') {
@@ -85,18 +83,22 @@ struct BlockTimer {
 
 
 // Hasing
+enum HashKeyKind {
+	HashKey_Default,
+	HashKey_String,
+};
 
 struct HashKey {
+	HashKeyKind kind;
+	u64         key;
 	union {
-		u64   key;
-		void *ptr;
+		String string; // if String, s.len > 0
 	};
-	b32 is_string;
-	String string; // if String, s.len > 0
 };
 
 gb_inline HashKey hashing_proc(void const *data, isize len) {
 	HashKey h = {};
+	h.kind = HashKey_Default;
 	// h.key = gb_murmur64(data, len);
 	h.key = gb_fnv64a(data, len);
 	return h;
@@ -104,22 +106,24 @@ gb_inline HashKey hashing_proc(void const *data, isize len) {
 
 gb_inline HashKey hash_string(String s) {
 	HashKey h = hashing_proc(s.text, s.len);
-	h.is_string = true;
+	h.kind = HashKey_String;
 	h.string = s;
 	return h;
 }
 
 gb_inline HashKey hash_pointer(void *ptr) {
-	uintptr p = cast(uintptr)ptr;
-	HashKey h = {cast(u64)p};
+	HashKey h = {};
+	h.key = cast(u64)cast(uintptr)ptr;
 	return h;
 }
 
 b32 hash_key_equal(HashKey a, HashKey b) {
 	if (a.key == b.key) {
 		// NOTE(bill): If two string's hashes collide, compare the strings themselves
-		if (a.is_string) {
-			if (b.is_string) return a.string == b.string;
+		if (a.kind == HashKey_String) {
+			if (b.kind == HashKey_String) {
+				return a.string == b.string;
+			}
 			return false;
 		}
 		return true;
@@ -156,7 +160,6 @@ i64 prev_pow2(i64 n) {
 }
 
 
-#define gb_for_array(index_, array_) for (isize index_ = 0; (array_) != NULL && index_ < gb_array_count(array_); index_++)
 #define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
 
 
@@ -169,10 +172,11 @@ i64 prev_pow2(i64 n) {
 } while (0)
 
 #define DLIST_APPEND(root_element, curr_element, next_element) do { \
-	if ((root_element) == NULL) \
+	if ((root_element) == NULL) { \
 		(root_element) = (curr_element) = (next_element); \
-	else \
+	} else { \
 		DLIST_SET(curr_element, next_element); \
+	} \
 } while (0)
 
 ////////////////////////////////////////////////////////////////
@@ -309,14 +313,16 @@ void map_rehash(Map<T> *h, isize new_count) {
 		}
 		fr = map__find(&nh, e->key);
 		j = map__add_entry(&nh, e->key);
-		if (fr.entry_prev < 0)
+		if (fr.entry_prev < 0) {
 			nh.hashes[fr.hash_index] = j;
-		else
+		} else {
 			nh.entries[fr.entry_prev].next = j;
+		}
 		nh.entries[j].next = fr.entry_index;
 		nh.entries[j].value = e->value;
-		if (map__full(&nh))
+		if (map__full(&nh)) {
 			map_grow(&nh);
+		}
 	}
 	map_destroy(h);
 	*h = nh;
@@ -467,3 +473,6 @@ void multi_map_remove_all(Map<T> *h, HashKey key) {
 	}
 }
 
+
+
+

+ 2 - 2
src/exact_value.cpp

@@ -12,8 +12,8 @@ enum ExactValueKind {
 	ExactValue_String,
 	ExactValue_Integer,
 	ExactValue_Float,
-	ExactValue_Pointer, // TODO(bill): Handle ExactValue_Pointer correctly
-	ExactValue_Compound,
+	ExactValue_Pointer,
+	ExactValue_Compound, // TODO(bill): Is this good enough?
 
 	ExactValue_Count,
 };

+ 25 - 3
src/main.cpp

@@ -1,3 +1,5 @@
+#define VERSION_STRING "v0.0.3"
+
 #include "common.cpp"
 #include "profiler.cpp"
 #include "unicode.cpp"
@@ -88,9 +90,20 @@ ArchData make_arch_data(ArchKind kind) {
 	return data;
 }
 
+void usage(char *argv0) {
+	gb_printf_err("%s is a tool for managing Odin source code\n", argv0);
+	gb_printf_err("Usage:");
+	gb_printf_err("\n\t%s command [arguments]\n", argv0);
+	gb_printf_err("Commands:");
+	gb_printf_err("\n\tbuild   compile .odin file");
+	gb_printf_err("\n\trun     compile and run .odin file");
+	gb_printf_err("\n\tversion print Odin version");
+	gb_printf_err("\n\n");
+}
+
 int main(int argc, char **argv) {
 	if (argc < 2) {
-		gb_printf_err("using: %s [run] <filename> \n", argv[0]);
+		usage(argv[0]);
 		return 1;
 	}
 	prof_init();
@@ -104,11 +117,20 @@ int main(int argc, char **argv) {
 
 	init_universal_scope();
 
-	char *init_filename = argv[1];
+	char *init_filename = NULL;
 	b32 run_output = false;
-	if (gb_strncmp(argv[1], "run", 3) == 0) {
+	String arg1 = make_string(argv[1]);
+	if (arg1 == "run") {
 		run_output = true;
 		init_filename = argv[2];
+	} else if (arg1 == "build") {
+		init_filename = argv[2];
+	} else if (arg1 == "version") {
+		gb_printf("%s version %s", argv[0], VERSION_STRING);
+		return 0;
+	} else {
+		usage(argv[0]);
+		return 1;
 	}
 	Parser parser = {0};
 

+ 18 - 24
src/parser.cpp

@@ -29,37 +29,36 @@ struct AstFile {
 	// >= 0: In Expression
 	// <  0: In Control Clause
 	// NOTE(bill): Used to prevent type literals in control clauses
-	isize expr_level;
+	isize          expr_level;
 
-	AstNodeArray decls;
-	b32 is_global_scope;
+	AstNodeArray   decls;
+	b32            is_global_scope;
 
-	AstNode * curr_proc;
-	isize     scope_level;
-	Scope *   scope;       // NOTE(bill): Created in checker
-	DeclInfo *decl_info;   // NOTE(bill): Created in checker
+	AstNode *      curr_proc;
+	isize          scope_level;
+	Scope *        scope;       // NOTE(bill): Created in checker
+	DeclInfo *     decl_info;   // NOTE(bill): Created in checker
 
 	// TODO(bill): Error recovery
-	// NOTE(bill): Error recovery
 #define PARSER_MAX_FIX_COUNT 6
 	isize    fix_count;
 	TokenPos fix_prev_pos;
 };
 
 struct ImportedFile {
-	String path;
-	String rel_path;
+	String   path;
+	String   rel_path;
 	TokenPos pos; // #import
 };
 
 struct Parser {
-	String init_fullpath;
+	String              init_fullpath;
 	Array<AstFile>      files;
 	Array<ImportedFile> imports;
-	gbAtomic32 import_index;
+	gbAtomic32          import_index;
 	Array<String>       system_libraries;
-	isize total_token_count;
-	gbMutex mutex;
+	isize               total_token_count;
+	gbMutex             mutex;
 };
 
 enum ProcTag : u64 {
@@ -483,11 +482,6 @@ Token ast_node_token(AstNode *node) {
 	return empty_token;
 }
 
-HashKey hash_token(Token t) {
-	return hash_string(t.string);
-}
-
-
 // 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;
@@ -2969,8 +2963,8 @@ String get_fullpath_relative(gbAllocator a, String base_dir, String path) {
 	defer (gb_free(gb_heap_allocator(), str));
 
 	isize i = 0;
-	gb_memcopy(str+i, base_dir.text, base_dir.len); i += base_dir.len;
-	gb_memcopy(str+i, path.text, path.len);
+	gb_memmove(str+i, base_dir.text, base_dir.len); i += base_dir.len;
+	gb_memmove(str+i, path.text, path.len);
 	str[str_len] = '\0';
 	return path_to_fullpath(a, make_string(str, str_len));
 }
@@ -2985,9 +2979,9 @@ String get_fullpath_core(gbAllocator a, String path) {
 	u8 *str = gb_alloc_array(gb_heap_allocator(), u8, str_len+1);
 	defer (gb_free(gb_heap_allocator(), str));
 
-	gb_memcopy(str, module_dir.text, module_dir.len);
-	gb_memcopy(str+module_dir.len, core, core_len);
-	gb_memcopy(str+module_dir.len+core_len, path.text, path.len);
+	gb_memmove(str, module_dir.text, module_dir.len);
+	gb_memmove(str+module_dir.len, core, core_len);
+	gb_memmove(str+module_dir.len+core_len, path.text, path.len);
 	str[str_len] = '\0';
 
 	return path_to_fullpath(a, make_string(str, str_len));

+ 2 - 2
src/string.cpp

@@ -1,7 +1,7 @@
 gb_global gbArena string_buffer_arena = {};
 gb_global gbAllocator string_buffer_allocator = {};
 
-void init_string_buffer_memory() {
+void init_string_buffer_memory(void) {
 	// NOTE(bill): This should be enough memory for file systems
 	gb_arena_init_from_allocator(&string_buffer_arena, gb_heap_allocator(), gb_megabytes(1));
 	string_buffer_allocator = gb_arena_allocator(&string_buffer_arena);
@@ -404,7 +404,7 @@ i32 unquote_string(gbAllocator a, String *s_) {
 			buf[offset++] = cast(u8)r;
 		} else {
 			isize size = gb_utf8_encode_rune(rune_temp, r);
-			gb_memcopy(buf+offset, rune_temp, size);
+			gb_memmove(buf+offset, rune_temp, size);
 			offset += size;
 		}
 

+ 17 - 14
src/tokenizer.cpp

@@ -158,8 +158,7 @@ Token empty_token = {Token_Invalid};
 Token blank_token = {Token_Identifier, {cast(u8 *)"_", 1}};
 
 Token make_token_ident(String s) {
-	Token t = {Token_Identifier};
-	t.string = s;
+	Token t = {Token_Identifier, s};
 	return t;
 }
 
@@ -356,6 +355,7 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
 	defer (gb_free(gb_heap_allocator(), c_str));
 
 
+	// TODO(bill): Memory map rather than copy contents
 	gbFileContents fc = gb_file_read_contents(gb_heap_allocator(), true, c_str);
 	gb_zero_item(t);
 	if (fc.data != NULL) {
@@ -368,8 +368,9 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
 		t->line_count = 1;
 
 		advance_to_next_rune(t);
-		if (t->curr_rune == GB_RUNE_BOM)
+		if (t->curr_rune == GB_RUNE_BOM) {
 			advance_to_next_rune(t); // Ignore BOM at file beginning
+		}
 
 		array_init(&t->allocated_strings, gb_heap_allocator());
 
@@ -389,9 +390,9 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
 		return TokenizerInit_Permission;
 	}
 
-	if (gb_file_size(&f) == 0)
+	if (gb_file_size(&f) == 0) {
 		return TokenizerInit_Empty;
-
+	}
 
 	return TokenizerInit_None;
 }
@@ -413,12 +414,13 @@ void tokenizer_skip_whitespace(Tokenizer *t) {
 }
 
 gb_inline i32 digit_value(Rune r) {
-	if (gb_char_is_digit(cast(char)r))
+	if (gb_char_is_digit(cast(char)r)) {
 		return r - '0';
-	if (gb_is_between(cast(char)r, 'a', 'f'))
+	} else if (gb_is_between(cast(char)r, 'a', 'f')) {
 		return r - 'a' + 10;
-	if (gb_is_between(cast(char)r, 'A', 'F'))
+	} else if (gb_is_between(cast(char)r, 'A', 'F')) {
 		return r - 'A' + 10;
+	}
 	return 16; // NOTE(bill): Larger than highest possible
 }
 
@@ -426,22 +428,21 @@ gb_inline void scan_mantissa(Tokenizer *t, i32 base) {
 	// TODO(bill): Allow for underscores in numbers as a number separator
 	// TODO(bill): Is this a good idea?
 	// while (digit_value(t->curr_rune) < base || t->curr_rune == '_')
-	while (digit_value(t->curr_rune) < base)
+	while (digit_value(t->curr_rune) < base) {
 		advance_to_next_rune(t);
+	}
 }
 
 
 Token scan_number_to_token(Tokenizer *t, b32 seen_decimal_point) {
 	Token token = {};
-	u8 *start_curr = t->curr;
 	token.kind = Token_Integer;
-	token.string = make_string(start_curr, 1);
+	token.string = make_string(t->curr, 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--;
 		token.kind = Token_Float;
 		scan_mantissa(t, 10);
 		goto exponent;
@@ -497,8 +498,9 @@ exponent:
 	if (t->curr_rune == 'e' || t->curr_rune == 'E') {
 		token.kind = Token_Float;
 		advance_to_next_rune(t);
-		if (t->curr_rune == '-' || t->curr_rune == '+')
+		if (t->curr_rune == '-' || t->curr_rune == '+') {
 			advance_to_next_rune(t);
+		}
 		scan_mantissa(t, 10);
 	}
 
@@ -623,8 +625,9 @@ Token tokenizer_get_token(Tokenizer *t) {
 	curr_rune = t->curr_rune;
 	if (rune_is_letter(curr_rune)) {
 		token.kind = Token_Identifier;
-		while (rune_is_letter(t->curr_rune) || rune_is_digit(t->curr_rune))
+		while (rune_is_letter(t->curr_rune) || rune_is_digit(t->curr_rune)) {
 			advance_to_next_rune(t);
+		}
 
 		token.string.len = t->curr - token.string.text;
 

+ 2 - 2
src/unicode.cpp

@@ -6,7 +6,6 @@
 
 #pragma warning(pop)
 
-// TODO(bill): Unicode support
 b32 rune_is_letter(Rune r) {
 	if ((r < 0x80 && gb_char_is_alpha(cast(char)r)) ||
 	    r == '_') {
@@ -24,8 +23,9 @@ b32 rune_is_letter(Rune r) {
 }
 
 b32 rune_is_digit(Rune r) {
-	if (r < 0x80 && gb_is_between(r, '0', '9'))
+	if (r < 0x80 && gb_is_between(r, '0', '9')) {
 		return true;
+	}
 	return utf8proc_category(r) == UTF8PROC_CATEGORY_ND;
 }