Browse Source

Basic `when` statement - Compile time if statement
This is similar to an #if in C but handled during the semantic checking stage.

Ginger Bill 8 years ago
parent
commit
b232b9d5ea
12 changed files with 578 additions and 322 deletions
  1. 2 2
      build.bat
  2. 22 5
      code/demo.odin
  3. 1 1
      core/_preload.odin
  4. 3 3
      core/fmt.odin
  5. 257 172
      src/checker/checker.c
  6. 15 25
      src/checker/expr.c
  7. 38 6
      src/checker/stmt.c
  8. 1 1
      src/checker/types.c
  9. 1 1
      src/exact_value.c
  10. 209 106
      src/parser.c
  11. 27 0
      src/ssa.c
  12. 2 0
      src/tokenizer.c

+ 2 - 2
build.bat

@@ -4,7 +4,7 @@
 set exe_name=odin.exe
 set exe_name=odin.exe
 
 
 :: Debug = 0, Release = 1
 :: Debug = 0, Release = 1
-set release_mode=1
+set release_mode=0
 
 
 set compiler_flags= -nologo -Oi -TC -W4 -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
 set compiler_flags= -nologo -Oi -TC -W4 -fp:fast -fp:except- -Gm- -MP -FC -GS- -EHsc- -GR-
 
 
@@ -16,7 +16,7 @@ if %release_mode% EQU 0 ( rem Debug
 )
 )
 
 
 set compiler_warnings= ^
 set compiler_warnings= ^
-	-we4013 -we4706 -we4002 -we4133 ^
+	-we4013 -we4706 -we4002 -we4133 -we4047 -we4024 ^
 	-wd4100 -wd4101 -wd4127 -wd4189 ^
 	-wd4100 -wd4101 -wd4127 -wd4189 ^
 	-wd4201 -wd4204 -wd4244 ^
 	-wd4201 -wd4204 -wd4244 ^
 	-wd4306 ^
 	-wd4306 ^

+ 22 - 5
code/demo.odin

@@ -1,11 +1,27 @@
-#import "fmt.odin"
+// #import "fmt.odin"
 #import "utf8.odin"
 #import "utf8.odin"
 
 
+when ODIN_OS == "window" {
+	when ODIN_OS != "window" {
+	} else {
+		MAX :: 64
+	}
+	#import "fmt.odin"
+} else {
+
+}
+
+
 main :: proc() {
 main :: proc() {
-	MAX :: 64
+	when true {
+		OffsetType :: type int
+	}
+
+	// MAX :: 64
 	buf:     [MAX]rune
 	buf:     [MAX]rune
 	backing: [MAX]byte
 	backing: [MAX]byte
-	offset:  int
+	offset:  OffsetType
+
 
 
 	msg := "Hello"
 	msg := "Hello"
 	count := utf8.rune_count(msg)
 	count := utf8.rune_count(msg)
@@ -17,16 +33,17 @@ main :: proc() {
 		s := msg[offset:]
 		s := msg[offset:]
 		r, len := utf8.decode_rune(s)
 		r, len := utf8.decode_rune(s)
 		runes[count-i-1] = r
 		runes[count-i-1] = r
-		offset += len
+		offset += len as OffsetType
 	}
 	}
 
 
 	offset = 0
 	offset = 0
 	for i := 0; i < count; i++ {
 	for i := 0; i < count; i++ {
 		data, len := utf8.encode_rune(runes[i])
 		data, len := utf8.encode_rune(runes[i])
 		copy(backing[offset:], data[:len])
 		copy(backing[offset:], data[:len])
-		offset += len
+		offset += len as OffsetType
 	}
 	}
 
 
 	reverse := backing[:offset] as string
 	reverse := backing[:offset] as string
 	fmt.println(reverse) // olleH
 	fmt.println(reverse) // olleH
 }
 }
+

+ 1 - 1
core/_preload.odin

@@ -160,7 +160,7 @@ Context :: struct #ordered {
 #thread_local __context: Context
 #thread_local __context: Context
 
 
 
 
-DEFAULT_ALIGNMENT :: align_of({4}f32)
+DEFAULT_ALIGNMENT :: align_of([vector 4]f32)
 
 
 
 
 __check_context :: proc() {
 __check_context :: proc() {

+ 3 - 3
core/fmt.odin

@@ -244,9 +244,9 @@ print_type_to_buffer :: proc(buf: ^[]byte, ti: ^Type_Info) {
 		print_string_to_buffer(buf, "]")
 		print_string_to_buffer(buf, "]")
 		print_type_to_buffer(buf, info.elem)
 		print_type_to_buffer(buf, info.elem)
 	case Vector:
 	case Vector:
-		print_string_to_buffer(buf, "{")
+		print_string_to_buffer(buf, "[vector ")
 		print_i64_to_buffer(buf, info.count as i64)
 		print_i64_to_buffer(buf, info.count as i64)
-		print_string_to_buffer(buf, "}")
+		print_string_to_buffer(buf, "]")
 		print_type_to_buffer(buf, info.elem)
 		print_type_to_buffer(buf, info.elem)
 
 
 	case Struct:
 	case Struct:
@@ -442,7 +442,7 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any) {
 			return false
 			return false
 		}
 		}
 
 
-		bprintf(buf, "{%}%{", info.count, info.elem)
+		bprintf(buf, "[vector %]%{", info.count, info.elem)
 		defer print_string_to_buffer(buf, "}")
 		defer print_string_to_buffer(buf, "}")
 
 
 		if is_bool(info.elem) {
 		if is_bool(info.elem) {

+ 257 - 172
src/checker/checker.c

@@ -65,7 +65,7 @@ typedef struct ProcedureInfo {
 	Token     token;
 	Token     token;
 	DeclInfo *decl;
 	DeclInfo *decl;
 	Type *    type; // Type_Procedure
 	Type *    type; // Type_Procedure
-	AstNode * body; // AstNode_BlockStatement
+	AstNode * body; // AstNode_BlockStmt
 	u32       tags;
 	u32       tags;
 } ProcedureInfo;
 } ProcedureInfo;
 
 
@@ -237,6 +237,7 @@ typedef struct CheckerInfo {
 	MapIsize            type_info_map;   // Key: Type *
 	MapIsize            type_info_map;   // Key: Type *
 	isize               type_info_count;
 	isize               type_info_count;
 	Entity *            implicit_values[ImplicitValue_Count];
 	Entity *            implicit_values[ImplicitValue_Count];
+	Array(String)       foreign_libraries; // For the linker
 } CheckerInfo;
 } CheckerInfo;
 
 
 typedef struct Checker {
 typedef struct Checker {
@@ -256,7 +257,7 @@ typedef struct Checker {
 	CheckerContext         context;
 	CheckerContext         context;
 
 
 	Array(Type *)          proc_stack;
 	Array(Type *)          proc_stack;
-	bool                    in_defer; // TODO(bill): Actually handle correctly
+	bool                   in_defer; // TODO(bill): Actually handle correctly
 } Checker;
 } Checker;
 
 
 typedef struct CycleChecker {
 typedef struct CycleChecker {
@@ -384,6 +385,7 @@ void check_close_scope(Checker *c) {
 
 
 void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entity **entity_) {
 void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entity **entity_) {
 	bool gone_thru_proc = false;
 	bool gone_thru_proc = false;
+	bool gone_thru_file = false;
 	HashKey key = hash_string(name);
 	HashKey key = hash_string(name);
 	for (Scope *s = scope; s != NULL; s = s->parent) {
 	for (Scope *s = scope; s != NULL; s = s->parent) {
 		Entity **found = map_entity_get(&s->elements, key);
 		Entity **found = map_entity_get(&s->elements, key);
@@ -422,12 +424,20 @@ void scope_lookup_parent_entity(Scope *scope, String name, Scope **scope_, Entit
 						continue;
 						continue;
 					}
 					}
 
 
+					if (e->kind == Entity_ImportName && gone_thru_file) {
+						continue;
+					}
+
 					if (entity_) *entity_ = e;
 					if (entity_) *entity_ = e;
 					if (scope_) *scope_ = shared;
 					if (scope_) *scope_ = shared;
 					return;
 					return;
 				}
 				}
 			}
 			}
 		}
 		}
+
+		if (s->is_file) {
+			gone_thru_file = true;
+		}
 	}
 	}
 
 
 
 
@@ -569,6 +579,7 @@ void init_checker_info(CheckerInfo *i) {
 	map_entity_init(&i->foreign_procs, a);
 	map_entity_init(&i->foreign_procs, a);
 	map_isize_init(&i->type_info_map,  a);
 	map_isize_init(&i->type_info_map,  a);
 	map_ast_file_init(&i->files,       a);
 	map_ast_file_init(&i->files,       a);
+	array_init(&i->foreign_libraries,  a);
 	i->type_info_count = 0;
 	i->type_info_count = 0;
 
 
 }
 }
@@ -583,6 +594,7 @@ void destroy_checker_info(CheckerInfo *i) {
 	map_entity_destroy(&i->foreign_procs);
 	map_entity_destroy(&i->foreign_procs);
 	map_isize_destroy(&i->type_info_map);
 	map_isize_destroy(&i->type_info_map);
 	map_ast_file_destroy(&i->files);
 	map_ast_file_destroy(&i->files);
+	array_free(&i->foreign_libraries);
 }
 }
 
 
 
 
@@ -747,6 +759,19 @@ void add_entity_and_decl_info(Checker *c, AstNode *identifier, Entity *e, DeclIn
 	map_decl_info_set(&c->info.entities, hash_pointer(e), d);
 	map_decl_info_set(&c->info.entities, hash_pointer(e), d);
 }
 }
 
 
+// NOTE(bill): Returns true if it's added
+bool try_add_foreign_library_path(Checker *c, String import_file) {
+	for_array(i, c->info.foreign_libraries) {
+		String import = c->info.foreign_libraries.e[i];
+		if (str_eq(import, import_file)) {
+			return false;
+		}
+	}
+	array_add(&c->info.foreign_libraries, import_file);
+	return true;
+}
+
+
 void add_type_info_type(Checker *c, Type *t) {
 void add_type_info_type(Checker *c, Type *t) {
 	if (t == NULL) {
 	if (t == NULL) {
 		return;
 		return;
@@ -941,6 +966,7 @@ MapEntity generate_minimum_dependency_map(CheckerInfo *info, Entity *start) {
 	return map;
 	return map;
 }
 }
 
 
+void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, MapScope *file_scopes);
 
 
 
 
 
 
@@ -1018,190 +1044,93 @@ void add_implicit_value(Checker *c, ImplicitValueId id, String name, String back
 }
 }
 
 
 
 
-void check_global_entity(Checker *c, EntityKind kind) {
+void check_global_entities_by_kind(Checker *c, EntityKind kind) {
 	for_array(i, c->info.entities.entries) {
 	for_array(i, c->info.entities.entries) {
 		MapDeclInfoEntry *entry = &c->info.entities.entries.e[i];
 		MapDeclInfoEntry *entry = &c->info.entities.entries.e[i];
 		Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
 		Entity *e = cast(Entity *)cast(uintptr)entry->key.key;
 		if (e->kind == kind) {
 		if (e->kind == kind) {
 			DeclInfo *d = entry->value;
 			DeclInfo *d = entry->value;
+			if (d->scope != e->scope) {
+				continue;
+			}
 
 
 			add_curr_ast_file(c, d->scope->file);
 			add_curr_ast_file(c, d->scope->file);
-
-			if (d->scope == e->scope) {
-				if (kind != Entity_Procedure && str_eq(e->token.string, str_lit("main"))) {
-					if (e->scope->is_init) {
-						error(e->token, "`main` is reserved as the entry point procedure in the initial scope");
-						continue;
-					}
-				} else if (e->scope->is_global && str_eq(e->token.string, str_lit("main"))) {
+			if (kind != Entity_Procedure && str_eq(e->token.string, str_lit("main"))) {
+				if (e->scope->is_init) {
 					error(e->token, "`main` is reserved as the entry point procedure in the initial scope");
 					error(e->token, "`main` is reserved as the entry point procedure in the initial scope");
 					continue;
 					continue;
 				}
 				}
-
-				Scope *prev_scope = c->context.scope;
-				c->context.scope = d->scope;
-				check_entity_decl(c, e, d, NULL, NULL);
+			} else if (e->scope->is_global && str_eq(e->token.string, str_lit("main"))) {
+				error(e->token, "`main` is reserved as the entry point procedure in the initial scope");
+				continue;
 			}
 			}
+
+			Scope *prev_scope = c->context.scope;
+			c->context.scope = d->scope;
+			check_entity_decl(c, e, d, NULL, NULL);
 		}
 		}
 	}
 	}
 }
 }
 
 
-void check_parsed_files(Checker *c) {
-	AstNodeArray import_decls;
-	array_init(&import_decls, heap_allocator());
-
-	MapScope file_scopes; // Key: String (fullpath)
-	map_scope_init(&file_scopes, heap_allocator());
-
-	// Map full filepaths to Scopes
-	for_array(i, c->parser->files) {
-		AstFile *f = &c->parser->files.e[i];
-		Scope *scope = NULL;
-		scope = make_scope(c->global_scope, c->allocator);
-		scope->is_global = f->is_global_scope;
-		scope->is_file   = true;
-		scope->file      = f;
-		if (i == 0) {
-			// NOTE(bill): First file is always the initial file
-			// thus it must contain main
-			scope->is_init = true;
-		}
 
 
-		if (scope->is_global) {
-			array_add(&c->global_scope->shared, scope);
-		}
-
-		f->scope = scope;
-		f->decl_info = make_declaration_info(c->allocator, f->scope);
-		HashKey key = hash_string(f->tokenizer.fullpath);
-		map_scope_set(&file_scopes, key, scope);
-		map_ast_file_set(&c->info.files, key, f);
+void check_global_when_stmt(Checker *c, Scope *parent_scope, AstNodeWhenStmt *ws, MapScope *file_scopes) {
+	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");
 	}
 	}
-
-	// Collect Entities
-	for_array(i, c->parser->files) {
-		AstFile *f = &c->parser->files.e[i];
-		add_curr_ast_file(c, f);
-
-		Scope *file_scope = f->scope;
-
-		for_array(decl_index, f->decls) {
-			AstNode *decl = f->decls.e[decl_index];
-			if (!is_ast_node_decl(decl)) {
-				continue;
-			}
-
-			switch (decl->kind) {
-			case_ast_node(bd, BadDecl, decl);
-			case_end;
-			case_ast_node(id, ImportDecl, decl);
-				// NOTE(bill): Handle later
-			case_end;
-			case_ast_node(fsl, ForeignLibrary, decl);
-				// NOTE(bill): ignore
-			case_end;
-
-			case_ast_node(cd, ConstDecl, decl);
-				for_array(i, cd->values) {
-					AstNode *name = cd->names.e[i];
-					AstNode *value = cd->values.e[i];
-					ExactValue v = {ExactValue_Invalid};
-					Entity *e = make_entity_constant(c->allocator, file_scope, name->Ident, NULL, v);
-					e->identifier = name;
-					DeclInfo *di = make_declaration_info(c->allocator, file_scope);
-					di->type_expr = cd->type;
-					di->init_expr = value;
-					add_entity_and_decl_info(c, name, e, di);
-				}
-
-				isize lhs_count = cd->names.count;
-				isize rhs_count = cd->values.count;
-
-				if (rhs_count == 0 && cd->type == NULL) {
-					error(ast_node_token(decl), "Missing type or initial expression");
-				} else if (lhs_count < rhs_count) {
-					error(ast_node_token(decl), "Extra initial expression");
-				}
-			case_end;
-
-			case_ast_node(vd, VarDecl, decl);
-				isize entity_count = vd->names.count;
-				isize entity_index = 0;
-				Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
-				DeclInfo *di = NULL;
-				if (vd->values.count > 0) {
-					di = make_declaration_info(heap_allocator(), file_scope);
-					di->entities = entities;
-					di->entity_count = entity_count;
-					di->type_expr = vd->type;
-					di->init_expr = vd->values.e[0];
-				}
-
-				for_array(i, vd->names) {
-					AstNode *name = vd->names.e[i];
-					AstNode *value = NULL;
-					if (i < vd->values.count) {
-						value = vd->values.e[i];
-					}
-					Entity *e = make_entity_variable(c->allocator, file_scope, name->Ident, NULL);
-					e->identifier = name;
-					entities[entity_index++] = e;
-
-					DeclInfo *d = di;
-					if (d == NULL) {
-						AstNode *init_expr = value;
-						d = make_declaration_info(heap_allocator(), file_scope);
-						d->type_expr = vd->type;
-						d->init_expr = init_expr;
-						d->var_decl_tags = vd->tags;
-					}
-
-					add_entity_and_decl_info(c, name, e, d);
-				}
-			case_end;
-
-			case_ast_node(td, TypeDecl, decl);
-				ast_node(n, Ident, td->name);
-				Entity *e = make_entity_type_name(c->allocator, file_scope, *n, NULL);
-				e->identifier = td->name;
-				DeclInfo *d = make_declaration_info(c->allocator, e->scope);
-				d->type_expr = td->type;
-				add_entity_and_decl_info(c, td->name, e, d);
-			case_end;
-
-			case_ast_node(pd, ProcDecl, decl);
-				ast_node(n, Ident, pd->name);
-				Token token = *n;
-				Entity *e = make_entity_procedure(c->allocator, file_scope, token, NULL);
-				e->identifier = pd->name;
-				DeclInfo *d = make_declaration_info(c->allocator, e->scope);
-				d->proc_decl = decl;
-				add_entity_and_decl_info(c, pd->name, e, d);
-			case_end;
-
+	if (operand.mode != Addressing_Constant) {
+		error(ast_node_token(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");
+	} else {
+		if (operand.value.kind == ExactValue_Bool &&
+		    operand.value.value_bool == true) {
+			ast_node(body, BlockStmt, ws->body);
+			check_collect_entities(c, parent_scope, body->stmts, file_scopes);
+		} else if (ws->else_stmt) {
+			switch (ws->else_stmt->kind) {
+			case AstNode_BlockStmt:
+				check_collect_entities(c, parent_scope, ws->else_stmt->BlockStmt.stmts, file_scopes);
+				break;
+			case AstNode_WhenStmt:
+				check_global_when_stmt(c, parent_scope, &ws->else_stmt->WhenStmt, file_scopes);
+				break;
 			default:
 			default:
-				error(ast_node_token(decl), "Only declarations are allowed at file scope");
+				error(ast_node_token(ws->else_stmt), "Invalid `else` statement in `when` statement");
 				break;
 				break;
 			}
 			}
 		}
 		}
 	}
 	}
+}
+void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, MapScope *file_scopes) {
+	for_array(decl_index, nodes) {
+		AstNode *decl = nodes.e[decl_index];
+		if (!is_ast_node_decl(decl) && !is_ast_node_when_stmt(decl)) {
+			continue;
+		}
 
 
-	for_array(i, c->parser->files) {
-		AstFile *f = &c->parser->files.e[i];
-		add_curr_ast_file(c, f);
-
-		Scope *file_scope = f->scope;
-
-		for_array(decl_index, f->decls) {
-			AstNode *decl = f->decls.e[decl_index];
-			if (decl->kind != AstNode_ImportDecl) {
+		switch (decl->kind) {
+		case_ast_node(bd, BadDecl, decl);
+		case_end;
+		case_ast_node(id, ImportDecl, decl);
+			if (!parent_scope->is_file) {
+				// NOTE(bill): _Should_ be caught by the parser
+				// TODO(bill): Better error handling if it isn't
 				continue;
 				continue;
 			}
 			}
-			ast_node(id, ImportDecl, decl);
 
 
 			HashKey key = hash_string(id->fullpath);
 			HashKey key = hash_string(id->fullpath);
-			Scope **found = map_scope_get(&file_scopes, key);
-			GB_ASSERT_MSG(found != NULL, "Unable to find scope for file: %.*s", LIT(id->fullpath));
+			Scope **found = map_scope_get(file_scopes, key);
+			if (found == NULL) {
+				for_array(scope_index, file_scopes->entries) {
+					Scope *scope = file_scopes->entries.e[scope_index].value;
+					gb_printf_err("%.*s\n", LIT(scope->file->tokenizer.fullpath));
+				}
+				gb_printf_err("%.*s(%td:%td)\n", LIT(id->token.pos.file), id->token.pos.line, id->token.pos.column);
+				GB_PANIC("Unable to find scope for file: %.*s", LIT(id->fullpath));
+			}
 			Scope *scope = *found;
 			Scope *scope = *found;
 
 
 			if (scope->is_global) {
 			if (scope->is_global) {
@@ -1210,8 +1139,8 @@ void check_parsed_files(Checker *c) {
 			}
 			}
 
 
 			bool previously_added = false;
 			bool previously_added = false;
-			for_array(import_index, file_scope->imported) {
-				Scope *prev = file_scope->imported.e[import_index];
+			for_array(import_index, parent_scope->imported) {
+				Scope *prev = parent_scope->imported.e[import_index];
 				if (prev == scope) {
 				if (prev == scope) {
 					previously_added = true;
 					previously_added = true;
 					break;
 					break;
@@ -1219,7 +1148,7 @@ void check_parsed_files(Checker *c) {
 			}
 			}
 
 
 			if (!previously_added) {
 			if (!previously_added) {
-				array_add(&file_scope->imported, scope);
+				array_add(&parent_scope->imported, scope);
 			} else {
 			} else {
 				warning(id->token, "Multiple #import of the same file within this scope");
 				warning(id->token, "Multiple #import of the same file within this scope");
 			}
 			}
@@ -1228,15 +1157,15 @@ void check_parsed_files(Checker *c) {
 				// NOTE(bill): Add imported entities to this file's scope
 				// NOTE(bill): Add imported entities to this file's scope
 				for_array(elem_index, scope->elements.entries) {
 				for_array(elem_index, scope->elements.entries) {
 					Entity *e = scope->elements.entries.e[elem_index].value;
 					Entity *e = scope->elements.entries.e[elem_index].value;
-					if (e->scope == file_scope) {
+					if (e->scope == parent_scope) {
 						continue;
 						continue;
 					}
 					}
 
 
 					// NOTE(bill): Do not add other imported entities
 					// NOTE(bill): Do not add other imported entities
-					add_entity(c, file_scope, NULL, e);
+					add_entity(c, parent_scope, NULL, e);
 					if (!id->is_load) { // `#import`ed entities don't get exported
 					if (!id->is_load) { // `#import`ed entities don't get exported
 						HashKey key = hash_string(e->token.string);
 						HashKey key = hash_string(e->token.string);
-						map_entity_set(&file_scope->implicit, key, e);
+						map_entity_set(&parent_scope->implicit, key, e);
 					}
 					}
 				}
 				}
 			} else {
 			} else {
@@ -1279,23 +1208,180 @@ void check_parsed_files(Checker *c) {
 
 
 				if (import_name.len > 0) {
 				if (import_name.len > 0) {
 					id->import_name.string = import_name;
 					id->import_name.string = import_name;
-					Entity *e = make_entity_import_name(c->allocator, file_scope, id->import_name, t_invalid,
+					Entity *e = make_entity_import_name(c->allocator, parent_scope, id->import_name, t_invalid,
 					                                    id->fullpath, id->import_name.string,
 					                                    id->fullpath, id->import_name.string,
 					                                    scope);
 					                                    scope);
-					add_entity(c, file_scope, NULL, e);
+					add_entity(c, parent_scope, NULL, e);
 				}
 				}
 			}
 			}
+		case_end;
+		case_ast_node(fl, ForeignLibrary, decl);
+			if (!parent_scope->is_file) {
+				// NOTE(bill): _Should_ be caught by the parser
+				// TODO(bill): Better error handling if it isn't
+				continue;
+			}
+
+			String file_str = fl->filepath.string;
+			String base_dir = fl->base_dir;
+
+			if (!fl->is_system) {
+				gbAllocator a = heap_allocator(); // TODO(bill): Change this allocator
+
+				String rel_path = get_fullpath_relative(a, base_dir, file_str);
+				String import_file = rel_path;
+				if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
+					String abs_path = get_fullpath_core(a, file_str);
+					if (gb_file_exists(cast(char *)abs_path.text)) {
+						import_file = abs_path;
+					}
+				}
+				file_str = import_file;
+			}
+
+			try_add_foreign_library_path(c, file_str);
+		case_end;
+		case_ast_node(ws, WhenStmt, decl);
+			check_global_when_stmt(c, parent_scope, ws, file_scopes);
+		case_end;
+
+		case_ast_node(cd, ConstDecl, decl);
+			for_array(i, cd->values) {
+				AstNode *name = cd->names.e[i];
+				AstNode *value = cd->values.e[i];
+				ExactValue v = {ExactValue_Invalid};
+				Entity *e = make_entity_constant(c->allocator, parent_scope, name->Ident, NULL, v);
+				e->identifier = name;
+				DeclInfo *di = make_declaration_info(c->allocator, parent_scope);
+				di->type_expr = cd->type;
+				di->init_expr = value;
+				add_entity_and_decl_info(c, name, e, di);
+			}
+
+			isize lhs_count = cd->names.count;
+			isize rhs_count = cd->values.count;
+
+			if (rhs_count == 0 && cd->type == NULL) {
+				error(ast_node_token(decl), "Missing type or initial expression");
+			} else if (lhs_count < rhs_count) {
+				error(ast_node_token(decl), "Extra initial expression");
+			}
+		case_end;
+
+		case_ast_node(vd, VarDecl, decl);
+			if (!parent_scope->is_file) {
+				// NOTE(bill): Within a procedure, variables must be in order
+				continue;
+			}
+
+			isize entity_count = vd->names.count;
+			isize entity_index = 0;
+			Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
+			DeclInfo *di = NULL;
+			if (vd->values.count > 0) {
+				di = make_declaration_info(heap_allocator(), parent_scope);
+				di->entities = entities;
+				di->entity_count = entity_count;
+				di->type_expr = vd->type;
+				di->init_expr = vd->values.e[0];
+			}
+
+			for_array(i, vd->names) {
+				AstNode *name = vd->names.e[i];
+				AstNode *value = NULL;
+				if (i < vd->values.count) {
+					value = vd->values.e[i];
+				}
+				Entity *e = make_entity_variable(c->allocator, parent_scope, name->Ident, NULL);
+				e->identifier = name;
+				entities[entity_index++] = e;
+
+				DeclInfo *d = di;
+				if (d == NULL) {
+					AstNode *init_expr = value;
+					d = make_declaration_info(heap_allocator(), parent_scope);
+					d->type_expr = vd->type;
+					d->init_expr = init_expr;
+					d->var_decl_tags = vd->tags;
+				}
+
+				add_entity_and_decl_info(c, name, e, d);
+			}
+		case_end;
+
+		case_ast_node(td, TypeDecl, decl);
+			ast_node(n, Ident, td->name);
+			Entity *e = make_entity_type_name(c->allocator, parent_scope, *n, NULL);
+			e->identifier = td->name;
+			DeclInfo *d = make_declaration_info(c->allocator, e->scope);
+			d->type_expr = td->type;
+			add_entity_and_decl_info(c, td->name, e, d);
+		case_end;
+
+		case_ast_node(pd, ProcDecl, decl);
+			ast_node(n, Ident, pd->name);
+			Token token = *n;
+			Entity *e = make_entity_procedure(c->allocator, parent_scope, token, NULL);
+			e->identifier = pd->name;
+			DeclInfo *d = make_declaration_info(c->allocator, e->scope);
+			d->proc_decl = decl;
+			add_entity_and_decl_info(c, pd->name, e, d);
+		case_end;
+
+		default:
+			if (parent_scope->is_file) {
+				error(ast_node_token(decl), "Only declarations are allowed at file scope");
+			}
+			break;
 		}
 		}
 	}
 	}
+}
+
+
+void check_parsed_files(Checker *c) {
+	MapScope file_scopes; // Key: String (fullpath)
+	map_scope_init(&file_scopes, heap_allocator());
+
+	// Map full filepaths to Scopes
+	for_array(i, c->parser->files) {
+		AstFile *f = &c->parser->files.e[i];
+		Scope *scope = NULL;
+		scope = make_scope(c->global_scope, c->allocator);
+		scope->is_global = f->is_global_scope;
+		scope->is_file   = true;
+		scope->file      = f;
+		if (i == 0) {
+			// NOTE(bill): First file is always the initial file
+			// thus it must contain main
+			scope->is_init = true;
+		}
+
+		if (scope->is_global) {
+			array_add(&c->global_scope->shared, scope);
+		}
+
+		f->scope = scope;
+		f->decl_info = make_declaration_info(c->allocator, f->scope);
+		HashKey key = hash_string(f->tokenizer.fullpath);
+		map_scope_set(&file_scopes, key, scope);
+		map_ast_file_set(&c->info.files, key, f);
+	}
+
+	// Collect Entities
+	for_array(i, c->parser->files) {
+		AstFile *f = &c->parser->files.e[i];
+		add_curr_ast_file(c, f);
+		check_collect_entities(c, f->scope, f->decls, &file_scopes);
+	}
 
 
-	check_global_entity(c, Entity_TypeName);
+	check_global_entities_by_kind(c, Entity_TypeName);
 
 
 	init_preload_types(c);
 	init_preload_types(c);
 	add_implicit_value(c, ImplicitValue_context, str_lit("context"), str_lit("__context"), t_context);
 	add_implicit_value(c, ImplicitValue_context, str_lit("context"), str_lit("__context"), t_context);
 
 
-	check_global_entity(c, Entity_Constant);
-	check_global_entity(c, Entity_Procedure);
-	check_global_entity(c, Entity_Variable);
+	check_global_entities_by_kind(c, Entity_Constant);
+	check_global_entities_by_kind(c, Entity_Procedure);
+	check_global_entities_by_kind(c, Entity_Variable);
 
 
 	for (isize i = 1; i < ImplicitValue_Count; i++) {
 	for (isize i = 1; i < ImplicitValue_Count; i++) {
 		// NOTE(bill): First is invalid
 		// NOTE(bill): First is invalid
@@ -1361,7 +1447,6 @@ void check_parsed_files(Checker *c) {
 	}
 	}
 
 
 	map_scope_destroy(&file_scopes);
 	map_scope_destroy(&file_scopes);
-	array_free(&import_decls);
 }
 }
 
 
 
 

+ 15 - 25
src/checker/expr.c

@@ -901,6 +901,7 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
 		return;
 		return;
 	}
 	}
 
 
+
 	Type *type = e->type;
 	Type *type = e->type;
 
 
 	switch (e->kind) {
 	switch (e->kind) {
@@ -1179,6 +1180,12 @@ end:
 		type = t_invalid;
 		type = t_invalid;
 	}
 	}
 
 
+	if (is_type_named(type)) {
+		if (type->Named.base == NULL) {
+			type->Named.base = t_invalid;
+		}
+	}
+
 	set_base_type(named_type, type);
 	set_base_type(named_type, type);
 	GB_ASSERT(is_type_typed(type));
 	GB_ASSERT(is_type_typed(type));
 
 
@@ -1282,7 +1289,6 @@ bool check_binary_op(Checker *c, Operand *o, Token op) {
 
 
 	case Token_CmpAnd:
 	case Token_CmpAnd:
 	case Token_CmpOr:
 	case Token_CmpOr:
-
 	case Token_CmpAndEq:
 	case Token_CmpAndEq:
 	case Token_CmpOrEq:
 	case Token_CmpOrEq:
 		if (!is_type_boolean(type)) {
 		if (!is_type_boolean(type)) {
@@ -2024,9 +2030,8 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 		}
 		}
 
 
 		Entity **variables = gb_alloc_array(c->allocator, Entity *, 2);
 		Entity **variables = gb_alloc_array(c->allocator, Entity *, 2);
-		Token tok = make_token_ident(str_lit(""));
-		variables[0] = make_entity_param(c->allocator, NULL, tok, type, false);
-		variables[1] = make_entity_param(c->allocator, NULL, tok, t_bool, false);
+		variables[0] = make_entity_param(c->allocator, NULL, empty_token, type, false);
+		variables[1] = make_entity_param(c->allocator, NULL, empty_token, t_bool, false);
 
 
 		Type *tuple = make_type_tuple(c->allocator);
 		Type *tuple = make_type_tuple(c->allocator);
 		tuple->Tuple.variables = variables;
 		tuple->Tuple.variables = variables;
@@ -2353,8 +2358,7 @@ bool check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *val
 
 
 	if (!is_type_integer(get_enum_base_type(operand.type))) {
 	if (!is_type_integer(get_enum_base_type(operand.type))) {
 		gbString expr_str = expr_to_string(operand.expr);
 		gbString expr_str = expr_to_string(operand.expr);
-		error(ast_node_token(operand.expr),
-		            "Index `%s` must be an integer", expr_str);
+		error(ast_node_token(operand.expr), "Index `%s` must be an integer", expr_str);
 		gb_string_free(expr_str);
 		gb_string_free(expr_str);
 		if (value) *value = 0;
 		if (value) *value = 0;
 		return false;
 		return false;
@@ -2365,8 +2369,7 @@ bool check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *val
 		i64 i = exact_value_to_integer(operand.value).value_integer;
 		i64 i = exact_value_to_integer(operand.value).value_integer;
 		if (i < 0) {
 		if (i < 0) {
 			gbString expr_str = expr_to_string(operand.expr);
 			gbString expr_str = expr_to_string(operand.expr);
-			error(ast_node_token(operand.expr),
-			            "Index `%s` cannot be a negative value", expr_str);
+			error(ast_node_token(operand.expr), "Index `%s` cannot be a negative value", expr_str);
 			gb_string_free(expr_str);
 			gb_string_free(expr_str);
 			if (value) *value = 0;
 			if (value) *value = 0;
 			return false;
 			return false;
@@ -2376,8 +2379,7 @@ bool check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *val
 			if (value) *value = i;
 			if (value) *value = i;
 			if (i >= max_count) {
 			if (i >= max_count) {
 				gbString expr_str = expr_to_string(operand.expr);
 				gbString expr_str = expr_to_string(operand.expr);
-				error(ast_node_token(operand.expr),
-				            "Index `%s` is out of bounds range 0..<%lld", expr_str, max_count);
+				error(ast_node_token(operand.expr), "Index `%s` is out of bounds range 0..<%lld", expr_str, max_count);
 				gb_string_free(expr_str);
 				gb_string_free(expr_str);
 				return false;
 				return false;
 			}
 			}
@@ -2425,12 +2427,10 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
 				check_entity_decl(c, entity, NULL, NULL, NULL);
 				check_entity_decl(c, entity, NULL, NULL, NULL);
 			}
 			}
 			GB_ASSERT(entity->type != NULL);
 			GB_ASSERT(entity->type != NULL);
-			// bool is_not_exported = !is_entity_exported(entity);
 
 
 			b32 is_not_exported = true;
 			b32 is_not_exported = true;
-
 			Entity **found = map_entity_get(&e->ImportName.scope->implicit, hash_string(sel_name));
 			Entity **found = map_entity_get(&e->ImportName.scope->implicit, hash_string(sel_name));
-			if (!found) {
+			if (found == NULL) {
 				is_not_exported = false;
 				is_not_exported = false;
 			} else {
 			} else {
 				Entity *f = *found;
 				Entity *f = *found;
@@ -2439,15 +2439,6 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
 				}
 				}
 			}
 			}
 
 
-			// // TODO(bill): Fix this for `#import "file.odin" as .`
-			// if (true || is_not_exported) {
-			// 	Entity **found =
-			// 	if (!found && e->ImportName.scope != entity->scope) {
-			// 		is_not_exported = false;
-			// 	}
-			// 	gb_printf("%.*s\n", LIT(entity->token.string));
-			// }
-
 			if (is_not_exported) {
 			if (is_not_exported) {
 				gbString sel_str = expr_to_string(selector);
 				gbString sel_str = expr_to_string(selector);
 				error(ast_node_token(op_expr), "`%s` is not exported by `%.*s`", sel_str, LIT(name));
 				error(ast_node_token(op_expr), "`%s` is not exported by `%.*s`", sel_str, LIT(name));
@@ -2778,7 +2769,6 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 			return false;
 			return false;
 		}
 		}
 
 
-
 		operand->mode = Addressing_Constant;
 		operand->mode = Addressing_Constant;
 		// IMPORTANT TODO(bill): Fix for anonymous fields
 		// IMPORTANT TODO(bill): Fix for anonymous fields
 		operand->value = make_exact_value_integer(type_offset_of_from_selection(c->sizes, c->allocator, type, sel));
 		operand->value = make_exact_value_integer(type_offset_of_from_selection(c->sizes, c->allocator, type, sel));
@@ -4461,9 +4451,9 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
 	case_end;
 	case_end;
 
 
 	case_ast_node(vt, VectorType, node);
 	case_ast_node(vt, VectorType, node);
-		str = gb_string_appendc(str, "{");
+		str = gb_string_appendc(str, "[vector ");
 		str = write_expr_to_string(str, vt->count);
 		str = write_expr_to_string(str, vt->count);
-		str = gb_string_appendc(str, "}");
+		str = gb_string_appendc(str, "]");
 		str = write_expr_to_string(str, vt->elem);
 		str = write_expr_to_string(str, vt->elem);
 	case_end;
 	case_end;
 
 

+ 38 - 6
src/checker/stmt.c

@@ -343,6 +343,37 @@ typedef struct TypeAndToken {
 #define MAP_NAME MapTypeAndToken
 #define MAP_NAME MapTypeAndToken
 #include "../map.c"
 #include "../map.c"
 
 
+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");
+	}
+	if (operand.mode != Addressing_Constant) {
+		error(ast_node_token(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");
+	} else {
+		if (operand.value.kind == ExactValue_Bool &&
+		    operand.value.value_bool) {
+			check_stmt_list(c, ws->body->BlockStmt.stmts, flags);
+		} else if (ws->else_stmt) {
+			switch (ws->else_stmt->kind) {
+			case AstNode_BlockStmt:
+				check_stmt_list(c, ws->else_stmt->BlockStmt.stmts, flags);
+				break;
+			case AstNode_WhenStmt:
+				check_when_stmt(c, &ws->else_stmt->WhenStmt, flags);
+				break;
+			default:
+				error(ast_node_token(ws->else_stmt), "Invalid `else` statement in `when` statement");
+				break;
+			}
+		}
+	}
+}
+
 void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
 	u32 mod_flags = flags & (~Stmt_FallthroughAllowed);
 	switch (node->kind) {
 	switch (node->kind) {
@@ -510,10 +541,8 @@ 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)) {
-			error(ast_node_token(is->cond),
-			            "Non-boolean condition in `if` statement");
+		if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) {
+			error(ast_node_token(is->cond), "Non-boolean condition in `if` statement");
 		}
 		}
 
 
 		check_stmt(c, is->body, mod_flags);
 		check_stmt(c, is->body, mod_flags);
@@ -525,8 +554,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(ast_node_token(is->else_stmt), "Invalid `else` statement in `if` statement");
 				break;
 				break;
 			}
 			}
 		}
 		}
@@ -534,6 +562,10 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 		check_close_scope(c);
 		check_close_scope(c);
 	case_end;
 	case_end;
 
 
+	case_ast_node(ws, WhenStmt, node);
+		check_when_stmt(c, ws, flags);
+	case_end;
+
 	case_ast_node(rs, ReturnStmt, node);
 	case_ast_node(rs, ReturnStmt, node);
 		GB_ASSERT(c->proc_stack.count > 0);
 		GB_ASSERT(c->proc_stack.count > 0);
 
 

+ 1 - 1
src/checker/types.c

@@ -1371,7 +1371,7 @@ gbString write_type_to_string(gbString str, Type *type) {
 		break;
 		break;
 
 
 	case Type_Vector:
 	case Type_Vector:
-		str = gb_string_appendc(str, gb_bprintf("{%td}", type->Vector.count));
+		str = gb_string_appendc(str, gb_bprintf("[vector %td]", type->Vector.count));
 		str = write_type_to_string(str, type->Vector.elem);
 		str = write_type_to_string(str, type->Vector.elem);
 		break;
 		break;
 
 

+ 1 - 1
src/exact_value.c

@@ -21,7 +21,7 @@ typedef enum ExactValueKind {
 typedef struct ExactValue {
 typedef struct ExactValue {
 	ExactValueKind kind;
 	ExactValueKind kind;
 	union {
 	union {
-		bool      value_bool;
+		bool     value_bool;
 		String   value_string;
 		String   value_string;
 		i64      value_integer; // NOTE(bill): This must be an integer and not a pointer
 		i64      value_integer; // NOTE(bill): This must be an integer and not a pointer
 		f64      value_float;
 		f64      value_float;

+ 209 - 106
src/parser.c

@@ -162,6 +162,12 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
 		AstNode *body; \
 		AstNode *body; \
 		AstNode *else_stmt; \
 		AstNode *else_stmt; \
 	}) \
 	}) \
+	AST_NODE_KIND(WhenStmt, "when statement", struct { \
+		Token token; \
+		AstNode *cond; \
+		AstNode *body; \
+		AstNode *else_stmt; \
+	}) \
 	AST_NODE_KIND(ReturnStmt, "return statement", struct { \
 	AST_NODE_KIND(ReturnStmt, "return statement", struct { \
 		Token token; \
 		Token token; \
 		AstNodeArray results; \
 		AstNodeArray results; \
@@ -256,6 +262,7 @@ AST_NODE_KIND(_DeclBegin,      "", i32) \
 	}) \
 	}) \
 	AST_NODE_KIND(ForeignLibrary, "foreign library", struct { \
 	AST_NODE_KIND(ForeignLibrary, "foreign library", struct { \
 		Token token, filepath; \
 		Token token, filepath; \
+		String base_dir; \
 		bool is_system; \
 		bool is_system; \
 	}) \
 	}) \
 AST_NODE_KIND(_DeclEnd,   "", i32) \
 AST_NODE_KIND(_DeclEnd,   "", i32) \
@@ -365,6 +372,9 @@ gb_inline bool is_ast_node_decl(AstNode *node) {
 gb_inline bool is_ast_node_type(AstNode *node) {
 gb_inline bool is_ast_node_type(AstNode *node) {
 	return gb_is_between(node->kind, AstNode__TypeBegin+1, AstNode__TypeEnd-1);
 	return gb_is_between(node->kind, AstNode__TypeBegin+1, AstNode__TypeEnd-1);
 }
 }
+gb_inline bool is_ast_node_when_stmt(AstNode *node) {
+	return node->kind == AstNode_WhenStmt;
+}
 
 
 
 
 Token ast_node_token(AstNode *node) {
 Token ast_node_token(AstNode *node) {
@@ -424,6 +434,8 @@ Token ast_node_token(AstNode *node) {
 		return node->BlockStmt.open;
 		return node->BlockStmt.open;
 	case AstNode_IfStmt:
 	case AstNode_IfStmt:
 		return node->IfStmt.token;
 		return node->IfStmt.token;
+	case AstNode_WhenStmt:
+		return node->WhenStmt.token;
 	case AstNode_ReturnStmt:
 	case AstNode_ReturnStmt:
 		return node->ReturnStmt.token;
 		return node->ReturnStmt.token;
 	case AstNode_ForStmt:
 	case AstNode_ForStmt:
@@ -717,6 +729,16 @@ AstNode *make_if_stmt(AstFile *f, Token token, AstNode *init, AstNode *cond, Ast
 	return result;
 	return result;
 }
 }
 
 
+AstNode *make_when_stmt(AstFile *f, Token token, AstNode *cond, AstNode *body, AstNode *else_stmt) {
+	AstNode *result = make_node(f, AstNode_WhenStmt);
+	result->WhenStmt.token = token;
+	result->WhenStmt.cond = cond;
+	result->WhenStmt.body = body;
+	result->WhenStmt.else_stmt = else_stmt;
+	return result;
+}
+
+
 AstNode *make_return_stmt(AstFile *f, Token token, AstNodeArray results) {
 AstNode *make_return_stmt(AstFile *f, Token token, AstNodeArray results) {
 	AstNode *result = make_node(f, AstNode_ReturnStmt);
 	AstNode *result = make_node(f, AstNode_ReturnStmt);
 	result->ReturnStmt.token = token;
 	result->ReturnStmt.token = token;
@@ -1351,12 +1373,39 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
 
 
 	case Token_Integer:
 	case Token_Integer:
 	case Token_Float:
 	case Token_Float:
-	case Token_String:
 	case Token_Rune:
 	case Token_Rune:
 		operand = make_basic_lit(f, f->curr_token);
 		operand = make_basic_lit(f, f->curr_token);
 		next_token(f);
 		next_token(f);
 		return operand;
 		return operand;
 
 
+	case Token_String: {
+		Token token = f->curr_token;
+		next_token(f);
+		if (f->curr_token.kind == Token_String) {
+			// NOTE(bill): Allow neighbouring string literals to be merge together to
+			// become one big string
+			String s = f->curr_token.string;
+			Array(u8) data;
+			array_init_reserve(&data, heap_allocator(), token.string.len+s.len);
+			gb_memmove(data.e, token.string.text, token.string.len);
+			data.count += token.string.len;
+
+			while (f->curr_token.kind == Token_String) {
+				String s = f->curr_token.string;
+				isize old_count = data.count;
+				array_resize(&data, data.count + s.len);
+				gb_memmove(data.e+old_count, s.text, s.len);
+				next_token(f);
+			}
+
+			token.string = make_string(data.e, data.count);
+			array_add(&f->tokenizer.allocated_strings, token.string);
+		}
+
+		return make_basic_lit(f, token);
+	}
+
+
 	case Token_OpenParen: {
 	case Token_OpenParen: {
 		Token open, close;
 		Token open, close;
 		// NOTE(bill): Skip the Paren Expression
 		// NOTE(bill): Skip the Paren Expression
@@ -1614,11 +1663,9 @@ AstNode *parse_unary_expr(AstFile *f, bool lhs) {
 	case Token_Sub:
 	case Token_Sub:
 	case Token_Not:
 	case Token_Not:
 	case Token_Xor: {
 	case Token_Xor: {
-		AstNode *operand;
 		Token op = f->curr_token;
 		Token op = f->curr_token;
 		next_token(f);
 		next_token(f);
-		operand = parse_unary_expr(f, lhs);
-		return make_unary_expr(f, op, operand);
+		return make_unary_expr(f, op, parse_unary_expr(f, lhs));
 	} break;
 	} break;
 	}
 	}
 
 
@@ -1658,7 +1705,6 @@ i32 token_precedence(Token t) {
 	case Token_union_cast:
 	case Token_union_cast:
 		return 6;
 		return 6;
 	}
 	}
-
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1669,8 +1715,9 @@ AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
 			AstNode *right;
 			AstNode *right;
 			Token op = f->curr_token;
 			Token op = f->curr_token;
 			i32 op_prec = token_precedence(op);
 			i32 op_prec = token_precedence(op);
-			if (op_prec != prec)
+			if (op_prec != prec) {
 				break;
 				break;
+			}
 			expect_operator(f); // NOTE(bill): error checks too
 			expect_operator(f); // NOTE(bill): error checks too
 			if (lhs) {
 			if (lhs) {
 				// TODO(bill): error checking
 				// TODO(bill): error checking
@@ -1791,21 +1838,22 @@ AstNode *parse_simple_stmt(AstFile *f) {
 
 
 
 
 
 
-AstNode *parse_block_stmt(AstFile *f) {
-	if (f->curr_proc == NULL) {
+AstNode *parse_block_stmt(AstFile *f, b32 is_when) {
+	if (!is_when && f->curr_proc == NULL) {
 		syntax_error(f->curr_token, "You cannot use a block statement in the file scope");
 		syntax_error(f->curr_token, "You cannot use a block statement in the file scope");
 		return make_bad_stmt(f, f->curr_token, f->curr_token);
 		return make_bad_stmt(f, f->curr_token, f->curr_token);
 	}
 	}
-	AstNode *block_stmt = parse_body(f);
-	return block_stmt;
+	return parse_body(f);
 }
 }
 
 
 AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) {
 AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) {
-	if (statement == NULL)
+	if (statement == NULL) {
 		return NULL;
 		return NULL;
+	}
 
 
-	if (statement->kind == AstNode_ExprStmt)
+	if (statement->kind == AstNode_ExprStmt) {
 		return statement->ExprStmt.expr;
 		return statement->ExprStmt.expr;
+	}
 
 
 	syntax_error(f->curr_token, "Expected `%.*s`, found a simple statement.", LIT(kind));
 	syntax_error(f->curr_token, "Expected `%.*s`, found a simple statement.", LIT(kind));
 	return make_bad_expr(f, f->curr_token, f->tokens.e[f->curr_token_index+1]);
 	return make_bad_expr(f, f->curr_token, f->tokens.e[f->curr_token_index+1]);
@@ -2008,27 +2056,34 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) {
 		f->expr_level++;
 		f->expr_level++;
 		Token token = expect_token(f, Token_OpenBracket);
 		Token token = expect_token(f, Token_OpenBracket);
 		AstNode *count_expr = NULL;
 		AstNode *count_expr = NULL;
+		bool is_vector = false;
 
 
 		if (f->curr_token.kind == Token_Ellipsis) {
 		if (f->curr_token.kind == Token_Ellipsis) {
 			count_expr = make_ellipsis(f, f->curr_token, NULL);
 			count_expr = make_ellipsis(f, f->curr_token, NULL);
 			next_token(f);
 			next_token(f);
+		} else if (f->curr_token.kind == Token_vector) {
+			next_token(f);
+			count_expr = parse_expr(f, false);
+			is_vector = true;
 		} else if (f->curr_token.kind != Token_CloseBracket) {
 		} else if (f->curr_token.kind != Token_CloseBracket) {
 			count_expr = parse_expr(f, false);
 			count_expr = parse_expr(f, false);
 		}
 		}
 		expect_token(f, Token_CloseBracket);
 		expect_token(f, Token_CloseBracket);
 		f->expr_level--;
 		f->expr_level--;
-		AstNode *e = make_array_type(f, token, count_expr, parse_type(f));
-		return e;
+		if (is_vector) {
+			return make_vector_type(f, token, count_expr, parse_type(f));
+		}
+		return make_array_type(f, token, count_expr, parse_type(f));
 	}
 	}
 
 
-	case Token_OpenBrace: {
-		f->expr_level++;
-		Token token = expect_token(f, Token_OpenBrace);
-		AstNode *count_expr = parse_expr(f, false);
-		expect_token(f, Token_CloseBrace);
-		f->expr_level--;
-		return make_vector_type(f, token, count_expr, parse_type(f));
-	}
+	// case Token_OpenBrace: {
+	// 	f->expr_level++;
+	// 	Token token = expect_token(f, Token_OpenBrace);
+	// 	AstNode *count_expr = parse_expr(f, false);
+	// 	expect_token(f, Token_CloseBrace);
+	// 	f->expr_level--;
+	// 	return make_vector_type(f, token, count_expr, parse_type(f));
+	// }
 
 
 	case Token_struct: {
 	case Token_struct: {
 		Token token = expect_token(f, Token_struct);
 		Token token = expect_token(f, Token_struct);
@@ -2359,7 +2414,7 @@ AstNode *parse_if_stmt(AstFile *f) {
 		syntax_error(f->curr_token, "Expected condition for if statement");
 		syntax_error(f->curr_token, "Expected condition for if statement");
 	}
 	}
 
 
-	body = parse_block_stmt(f);
+	body = parse_block_stmt(f, false);
 
 
 	if (allow_token(f, Token_else)) {
 	if (allow_token(f, Token_else)) {
 		switch (f->curr_token.kind) {
 		switch (f->curr_token.kind) {
@@ -2367,7 +2422,7 @@ AstNode *parse_if_stmt(AstFile *f) {
 			else_stmt = parse_if_stmt(f);
 			else_stmt = parse_if_stmt(f);
 			break;
 			break;
 		case Token_OpenBrace:
 		case Token_OpenBrace:
-			else_stmt = parse_block_stmt(f);
+			else_stmt = parse_block_stmt(f, false);
 			break;
 			break;
 		default:
 		default:
 			syntax_error(f->curr_token, "Expected if statement block statement");
 			syntax_error(f->curr_token, "Expected if statement block statement");
@@ -2379,6 +2434,44 @@ AstNode *parse_if_stmt(AstFile *f) {
 	return make_if_stmt(f, token, init, cond, body, else_stmt);
 	return make_if_stmt(f, token, init, cond, body, else_stmt);
 }
 }
 
 
+AstNode *parse_when_stmt(AstFile *f) {
+	Token token = expect_token(f, Token_when);
+	AstNode *cond = NULL;
+	AstNode *body = NULL;
+	AstNode *else_stmt = NULL;
+
+	isize prev_level = f->expr_level;
+	f->expr_level = -1;
+
+	cond = parse_expr(f, false);
+
+	f->expr_level = prev_level;
+
+	if (cond == NULL) {
+		syntax_error(f->curr_token, "Expected condition for when statement");
+	}
+
+	body = parse_block_stmt(f, true);
+
+	if (allow_token(f, Token_else)) {
+		switch (f->curr_token.kind) {
+		case Token_when:
+			else_stmt = parse_when_stmt(f);
+			break;
+		case Token_OpenBrace:
+			else_stmt = parse_block_stmt(f, true);
+			break;
+		default:
+			syntax_error(f->curr_token, "Expected when statement block statement");
+			else_stmt = make_bad_stmt(f, f->curr_token, f->tokens.e[f->curr_token_index+1]);
+			break;
+		}
+	}
+
+	return make_when_stmt(f, token, cond, body, else_stmt);
+}
+
+
 AstNode *parse_return_stmt(AstFile *f) {
 AstNode *parse_return_stmt(AstFile *f) {
 	if (f->curr_proc == NULL) {
 	if (f->curr_proc == NULL) {
 		syntax_error(f->curr_token, "You cannot use a return statement in the file scope");
 		syntax_error(f->curr_token, "You cannot use a return statement in the file scope");
@@ -2436,7 +2529,7 @@ AstNode *parse_for_stmt(AstFile *f) {
 		}
 		}
 		f->expr_level = prev_level;
 		f->expr_level = prev_level;
 	}
 	}
-	body = parse_block_stmt(f);
+	body = parse_block_stmt(f, false);
 
 
 	cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression"));
 	cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression"));
 
 
@@ -2624,6 +2717,7 @@ AstNode *parse_stmt(AstFile *f) {
 
 
 	// TODO(bill): other keywords
 	// TODO(bill): other keywords
 	case Token_if:     return parse_if_stmt(f);
 	case Token_if:     return parse_if_stmt(f);
+	case Token_when:   return parse_when_stmt(f);
 	case Token_return: return parse_return_stmt(f);
 	case Token_return: return parse_return_stmt(f);
 	case Token_for:    return parse_for_stmt(f);
 	case Token_for:    return parse_for_stmt(f);
 	case Token_match:  return parse_match_stmt(f);
 	case Token_match:  return parse_match_stmt(f);
@@ -2678,7 +2772,7 @@ AstNode *parse_stmt(AstFile *f) {
 		AstNode *expr = parse_expr(f, false);
 		AstNode *expr = parse_expr(f, false);
 		f->expr_level = prev_level;
 		f->expr_level = prev_level;
 
 
-		AstNode *body = parse_block_stmt(f);
+		AstNode *body = parse_block_stmt(f, false);
 		return make_push_allocator(f, token, expr, body);
 		return make_push_allocator(f, token, expr, body);
 	} break;
 	} break;
 
 
@@ -2689,7 +2783,7 @@ AstNode *parse_stmt(AstFile *f) {
 		AstNode *expr = parse_expr(f, false);
 		AstNode *expr = parse_expr(f, false);
 		f->expr_level = prev_level;
 		f->expr_level = prev_level;
 
 
-		AstNode *body = parse_block_stmt(f);
+		AstNode *body = parse_block_stmt(f, false);
 		return make_push_context(f, token, expr, body);
 		return make_push_context(f, token, expr, body);
 	} break;
 	} break;
 
 
@@ -2798,7 +2892,7 @@ AstNode *parse_stmt(AstFile *f) {
 	} break;
 	} break;
 
 
 	case Token_OpenBrace:
 	case Token_OpenBrace:
-		return parse_block_stmt(f);
+		return parse_block_stmt(f, false);
 
 
 	case Token_Semicolon:
 	case Token_Semicolon:
 		s = make_empty_stmt(f, token);
 		s = make_empty_stmt(f, token);
@@ -2965,20 +3059,20 @@ String get_fullpath_core(gbAllocator a, String path) {
 	return res;
 	return res;
 }
 }
 
 
-// NOTE(bill): Returns true if it's added
-bool try_add_foreign_library_path(Parser *p, String import_file) {
-	gb_mutex_lock(&p->mutex);
+// // NOTE(bill): Returns true if it's added
+// bool try_add_foreign_library_path(Parser *p, String import_file) {
+// 	gb_mutex_lock(&p->mutex);
 
 
-	for_array(i, p->foreign_libraries) {
-		String import = p->foreign_libraries.e[i];
-		if (str_eq(import, import_file)) {
-			return false;
-		}
-	}
-	array_add(&p->foreign_libraries, import_file);
-	gb_mutex_unlock(&p->mutex);
-	return true;
-}
+// 	for_array(i, p->foreign_libraries) {
+// 		String import = p->foreign_libraries.e[i];
+// 		if (str_eq(import, import_file)) {
+// 			return false;
+// 		}
+// 	}
+// 	array_add(&p->foreign_libraries, import_file);
+// 	gb_mutex_unlock(&p->mutex);
+// 	return true;
+// }
 
 
 gb_global Rune illegal_import_runes[] = {
 gb_global Rune illegal_import_runes[] = {
 	'"', '\'', '`', ' ', '\t', '\r', '\n', '\v', '\f',
 	'"', '\'', '`', ' ', '\t', '\r', '\n', '\v', '\f',
@@ -3040,93 +3134,102 @@ String get_filepath_extension(String path) {
 	return make_string(path.text, dot);
 	return make_string(path.text, dot);
 }
 }
 
 
-void parse_file(Parser *p, AstFile *f) {
-	String filepath = f->tokenizer.fullpath;
-	String base_dir = filepath;
-	for (isize i = filepath.len-1; i >= 0; i--) {
-		if (base_dir.text[i] == '\\' ||
-		    base_dir.text[i] == '/') {
+void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, AstNodeArray decls);
+
+void parse_setup_file_when_stmt(Parser *p, AstFile *f, String base_dir, AstNodeWhenStmt *ws) {
+	if (ws->body != NULL && ws->body->kind == AstNode_BlockStmt) {
+		parse_setup_file_decls(p, f, base_dir, ws->body->BlockStmt.stmts);
+	}
+	if (ws->else_stmt) {
+		switch (ws->else_stmt->kind) {
+		case AstNode_BlockStmt:
+			parse_setup_file_decls(p, f, base_dir, ws->else_stmt->BlockStmt.stmts);
+			break;
+		case AstNode_WhenStmt:
+			parse_setup_file_when_stmt(p, f, base_dir, &ws->else_stmt->WhenStmt);
 			break;
 			break;
 		}
 		}
-		base_dir.len--;
-	}
-
-	while (f->curr_token.kind == Token_Comment) {
-		next_token(f);
 	}
 	}
+}
 
 
-	f->decls = parse_stmt_list(f);
+void parse_setup_file_decls(Parser *p, AstFile *f, String base_dir, AstNodeArray decls) {
+	for_array(i, decls) {
+		AstNode *node = decls.e[i];
 
 
-	for_array(i, f->decls) {
-		AstNode *node = f->decls.e[i];
 		if (!is_ast_node_decl(node) &&
 		if (!is_ast_node_decl(node) &&
+		    !is_ast_node_when_stmt(node) &&
 		    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(ast_node_token(node), "Only declarations are allowed at file scope");
-		} else {
-			if (node->kind == AstNode_ImportDecl) {
-				AstNodeImportDecl *id = &node->ImportDecl;
-				String file_str = id->relpath.string;
-
-				if (!is_import_path_valid(file_str)) {
-					if (id->is_load) {
-						syntax_error(ast_node_token(node), "Invalid #load path: `%.*s`", LIT(file_str));
-					} else {
-						syntax_error(ast_node_token(node), "Invalid #import path: `%.*s`", LIT(file_str));
-					}
-					// NOTE(bill): It's a naughty name
-					f->decls.e[i] = make_bad_decl(f, id->token, id->token);
-					continue;
+		} else if (node->kind == AstNode_WhenStmt) {
+			parse_setup_file_when_stmt(p, f, base_dir, &node->WhenStmt);
+		} else if (node->kind == AstNode_ImportDecl) {
+			AstNodeImportDecl *id = &node->ImportDecl;
+			String file_str = id->relpath.string;
+
+			if (!is_import_path_valid(file_str)) {
+				if (id->is_load) {
+					syntax_error(ast_node_token(node), "Invalid #load path: `%.*s`", LIT(file_str));
+				} else {
+					syntax_error(ast_node_token(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);
+				continue;
+			}
 
 
-				gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator
+			gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator
 
 
-				String rel_path = get_fullpath_relative(allocator, base_dir, file_str);
-				String import_file = rel_path;
-				if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
-					String abs_path = get_fullpath_core(allocator, file_str);
-					if (gb_file_exists(cast(char *)abs_path.text)) {
-						import_file = abs_path;
-					}
+			String rel_path = get_fullpath_relative(allocator, base_dir, file_str);
+			String import_file = rel_path;
+			if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
+				String abs_path = get_fullpath_core(allocator, file_str);
+				if (gb_file_exists(cast(char *)abs_path.text)) {
+					import_file = abs_path;
 				}
 				}
+			}
 
 
-				id->fullpath = import_file;
-				try_add_import_path(p, import_file, file_str, ast_node_token(node).pos);
+			id->fullpath = import_file;
+			try_add_import_path(p, import_file, file_str, ast_node_token(node).pos);
 
 
-			} else if (node->kind == AstNode_ForeignLibrary) {
-				AstNodeForeignLibrary *id = &node->ForeignLibrary;
-				String file_str = id->filepath.string;
+		} else if (node->kind == AstNode_ForeignLibrary) {
+			AstNodeForeignLibrary *fl = &node->ForeignLibrary;
+			String file_str = fl->filepath.string;
 
 
-				if (!is_import_path_valid(file_str)) {
-					if (id->is_system) {
-						syntax_error(ast_node_token(node), "Invalid `foreign_system_library` path");
-					} else {
-						syntax_error(ast_node_token(node), "Invalid `foreign_library` path");
-					}
-					// NOTE(bill): It's a naughty name
-					f->decls.e[i] = make_bad_decl(f, id->token, id->token);
-					continue;
+			if (!is_import_path_valid(file_str)) {
+				if (fl->is_system) {
+					syntax_error(ast_node_token(node), "Invalid `foreign_system_library` path");
+				} else {
+					syntax_error(ast_node_token(node), "Invalid `foreign_library` path");
 				}
 				}
+				// NOTE(bill): It's a naughty name
+				f->decls.e[i] = make_bad_decl(f, fl->token, fl->token);
+				continue;
+			}
 
 
-				if (!id->is_system) {
-					gbAllocator allocator = heap_allocator(); // TODO(bill): Change this allocator
-
-					String rel_path = get_fullpath_relative(allocator, base_dir, file_str);
-					String import_file = rel_path;
-					if (!gb_file_exists(cast(char *)rel_path.text)) { // NOTE(bill): This should be null terminated
-						String abs_path = get_fullpath_core(allocator, file_str);
-						if (gb_file_exists(cast(char *)abs_path.text)) {
-							import_file = abs_path;
-						}
-					}
-					file_str = import_file;
-				}
+			fl->base_dir = base_dir;
+		}
+	}
+}
 
 
-				try_add_foreign_library_path(p, file_str);
-			}
+void parse_file(Parser *p, AstFile *f) {
+	String filepath = f->tokenizer.fullpath;
+	String base_dir = filepath;
+	for (isize i = filepath.len-1; i >= 0; i--) {
+		if (base_dir.text[i] == '\\' ||
+		    base_dir.text[i] == '/') {
+			break;
 		}
 		}
+		base_dir.len--;
 	}
 	}
+
+	while (f->curr_token.kind == Token_Comment) {
+		next_token(f);
+	}
+
+	f->decls = parse_stmt_list(f);
+	parse_setup_file_decls(p, f, base_dir, f->decls);
 }
 }
 
 
 
 

+ 27 - 0
src/ssa.c

@@ -3815,6 +3815,29 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 	proc->module->stmt_state_flags = prev_stmt_state_flags;
 	proc->module->stmt_state_flags = prev_stmt_state_flags;
 }
 }
 
 
+void ssa_build_when_stmt(ssaProcedure *proc, AstNodeWhenStmt *ws) {
+	ssaValue *cond = ssa_build_expr(proc, ws->cond);
+	GB_ASSERT(cond->kind == ssaValue_Constant &&
+	          is_type_boolean(ssa_type(cond)));
+
+	GB_ASSERT(cond->Constant.value.kind == ExactValue_Bool);
+	if (cond->Constant.value.value_bool) {
+		ssa_build_stmt_list(proc, ws->body->BlockStmt.stmts);
+	} else if (ws->else_stmt) {
+		switch (ws->else_stmt->kind) {
+		case AstNode_BlockStmt:
+			ssa_build_stmt_list(proc, ws->else_stmt->BlockStmt.stmts);
+			break;
+		case AstNode_WhenStmt:
+			ssa_build_when_stmt(proc, &ws->else_stmt->WhenStmt);
+			break;
+		default:
+			GB_PANIC("Invalid `else` statement in `when` statement");
+			break;
+		}
+	}
+}
+
 void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
 void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
 	switch (node->kind) {
 	switch (node->kind) {
 	case_ast_node(bs, EmptyStmt, node);
 	case_ast_node(bs, EmptyStmt, node);
@@ -3827,6 +3850,10 @@ void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
 		}
 		}
 	case_end;
 	case_end;
 
 
+	case_ast_node(ws, WhenStmt, node);
+		ssa_build_when_stmt(proc, ws);
+	case_end;
+
 	case_ast_node(vd, VarDecl, node);
 	case_ast_node(vd, VarDecl, node);
 		ssaModule *m = proc->module;
 		ssaModule *m = proc->module;
 		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
 		gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);

+ 2 - 0
src/tokenizer.c

@@ -94,6 +94,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_if,             "if"), \
 	TOKEN_KIND(Token_if,             "if"), \
 	TOKEN_KIND(Token_else,           "else"), \
 	TOKEN_KIND(Token_else,           "else"), \
 	TOKEN_KIND(Token_for,            "for"), \
 	TOKEN_KIND(Token_for,            "for"), \
+	TOKEN_KIND(Token_when,           "when"), \
 	TOKEN_KIND(Token_range,          "range"), \
 	TOKEN_KIND(Token_range,          "range"), \
 	TOKEN_KIND(Token_defer,          "defer"), \
 	TOKEN_KIND(Token_defer,          "defer"), \
 	TOKEN_KIND(Token_return,         "return"), \
 	TOKEN_KIND(Token_return,         "return"), \
@@ -101,6 +102,7 @@ TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
 	TOKEN_KIND(Token_union,          "union"), \
 	TOKEN_KIND(Token_union,          "union"), \
 	TOKEN_KIND(Token_raw_union,      "raw_union"), \
 	TOKEN_KIND(Token_raw_union,      "raw_union"), \
 	TOKEN_KIND(Token_enum,           "enum"), \
 	TOKEN_KIND(Token_enum,           "enum"), \
+	TOKEN_KIND(Token_vector,         "vector"), \
 	TOKEN_KIND(Token_using,          "using"), \
 	TOKEN_KIND(Token_using,          "using"), \
 	TOKEN_KIND(Token_asm,            "asm"), \
 	TOKEN_KIND(Token_asm,            "asm"), \
 	TOKEN_KIND(Token_volatile,       "volatile"), \
 	TOKEN_KIND(Token_volatile,       "volatile"), \