Browse Source

Branch Statements, if init statement, File parsing errors

gingerBill 9 years ago
parent
commit
9d8355d361
9 changed files with 379 additions and 139 deletions
  1. 1 0
      examples/other.odin
  2. 0 10
      examples/test.odin
  3. 0 4
      run.bat
  4. 75 31
      src/checker/statements.cpp
  5. 2 1
      src/checker/type.cpp
  6. 12 14
      src/main.cpp
  7. 235 67
      src/parser.cpp
  8. 44 9
      src/tokenizer.cpp
  9. 10 3
      todo.md

+ 1 - 0
examples/other.odin

@@ -0,0 +1 @@
+TAU :: 6.28;

+ 0 - 10
examples/test.odin

@@ -1,10 +0,0 @@
-main :: proc() {
-	type Vec2: struct { x, y: f32; }
-	v := Vec2{1, 1};
-	a := [2]int{1, 2}; // Array 2 of int
-	s := []int{1, 2};  // Slice of int
-	_, _ = a, s;
-	// Equivalent to
-	// sa := [2]int{1, 2}; s := sa[:];
-	v.x = 1;
-}

+ 0 - 4
run.bat

@@ -3,10 +3,6 @@
 
 rem del "..\examples\test.bc"
 call ..\bin\odin.exe ..\examples/test.odin
-rem clang -S -emit-llvm ..\examples/test.c -o ..\examples/test.ll
-call llvm-as < ..\examples/test.ll
 rem call lli ..\examples/test.ll
 
-rem call lli ..\examples/test.bc rem JIT
-rem llc ..\examples/test.bc -march=x86-64 -o ..\examples/test.exe
 

+ 75 - 31
src/checker/statements.cpp

@@ -1,43 +1,59 @@
 // Statements and Declarations
 
-void check_statement(Checker *c, AstNode *node);
-
-void check_statement_list(Checker *c, AstNode *node) {
-	for (; node != NULL; node = node->next)
-		check_statement(c, node);
+enum StatementFlag : u32 {
+	Statement_BreakAllowed       = GB_BIT(0),
+	Statement_ContinueAllowed    = GB_BIT(1),
+	// Statement_FallthroughAllowed = GB_BIT(2), // TODO(bill): fallthrough
+};
+
+void check_statement(Checker *c, AstNode *node, u32 flags);
+
+void check_statement_list(Checker *c, AstNode *node, u32 flags) {
+	for (; node != NULL; node = node->next) {
+		if (node->kind != AstNode_EmptyStatement) {
+			check_statement(c, node, flags);
+		}
+	}
 }
 
+b32 check_is_terminating(Checker *c, AstNode *node);
+
+b32 check_is_terminating_list(Checker *c, AstNode *list) {
+	// Get to end of list
+	for (; list != NULL; list = list->next) {
+		if (list->next == NULL)
+			break;
+	}
+
+	// Iterate backwards
+	for (AstNode *n = list; n != NULL; n = n->prev) {
+		if (n->kind != AstNode_EmptyStatement)
+			return check_is_terminating(c, n);
+	}
+
+	return false;
+}
 
 // NOTE(bill): The last expression has to be a `return` statement
 // TODO(bill): This is a mild hack and should be probably handled properly
 // TODO(bill): Warn/err against code after `return` that it won't be executed
 b32 check_is_terminating(Checker *c, AstNode *node) {
 	switch (node->kind) {
-	case AstNode_BlockStatement: {
-		for (; node != NULL; node = node->next) {
-			if (node->next == NULL)
-				break;
-		}
+	case AstNode_ReturnStatement:
+		return true;
 
-		for (AstNode *n = node; node != NULL; n = n->prev) {
-			if (n->kind == AstNode_EmptyStatement)
-				continue;
-			return check_is_terminating(c, n);
-		}
-		return false;
-	}
+	case AstNode_BlockStatement:
+		return check_is_terminating_list(c, node->block_statement.list);
 
 	case AstNode_ExpressionStatement:
 		return check_is_terminating(c, node->expression_statement.expression);
 
-	case AstNode_ReturnStatement:
-		return true;
-
 	case AstNode_IfStatement:
 		if (node->if_statement.else_statement != NULL) {
 			if (check_is_terminating(c, node->if_statement.body) &&
-			    check_is_terminating(c, node->if_statement.else_statement))
+			    check_is_terminating(c, node->if_statement.else_statement)) {
 			    return true;
+		    }
 		}
 		break;
 
@@ -353,7 +369,7 @@ void check_procedure_body(Checker *c, Token token, DeclarationInfo *decl, Type *
 	c->context.decl = decl;
 
 	push_procedure(c, type);
-	check_statement_list(c, body->block_statement.list);
+	check_statement_list(c, body->block_statement.list, 0);
 	if (type->procedure.results_count > 0) {
 		if (!check_is_terminating(c, body)) {
 			error(&c->error_collector, body->block_statement.close, "Missing return statement at the end of the procedure");
@@ -492,7 +508,7 @@ void check_entity_declaration(Checker *c, Entity *e, Type *named_type) {
 
 
 
-void check_statement(Checker *c, AstNode *node) {
+void check_statement(Checker *c, AstNode *node, u32 flags) {
 	switch (node->kind) {
 	case AstNode_EmptyStatement: break;
 	case AstNode_BadStatement:   break;
@@ -516,7 +532,7 @@ void check_statement(Checker *c, AstNode *node) {
 	case AstNode_TagStatement:
 		// TODO(bill): Tag Statements
 		error(&c->error_collector, ast_node_token(node), "Tag statements are not supported yet");
-		check_statement(c, node->tag_statement.statement);
+		check_statement(c, node->tag_statement.statement, flags);
 		break;
 
 	case AstNode_IncDecStatement: {
@@ -631,11 +647,18 @@ void check_statement(Checker *c, AstNode *node) {
 
 	case AstNode_BlockStatement:
 		check_open_scope(c, node);
-		check_statement_list(c, node->block_statement.list);
+		check_statement_list(c, node->block_statement.list, flags);
 		check_close_scope(c);
 		break;
 
 	case AstNode_IfStatement: {
+		check_open_scope(c, node);
+		defer (check_close_scope(c));
+		auto *is = &node->if_statement;
+
+		if (is->init != NULL)
+			check_statement(c, is->init, 0);
+
 		Operand operand = {Addressing_Invalid};
 		check_expression(c, &operand, node->if_statement.cond);
 		if (operand.mode != Addressing_Invalid &&
@@ -643,13 +666,14 @@ void check_statement(Checker *c, AstNode *node) {
 			error(&c->error_collector, ast_node_token(node->if_statement.cond),
 			            "Non-boolean condition in `if` statement");
 		}
-		check_statement(c, node->if_statement.body);
+
+		check_statement(c, node->if_statement.body, flags);
 
 		if (node->if_statement.else_statement) {
 			switch (node->if_statement.else_statement->kind) {
 			case AstNode_IfStatement:
 			case AstNode_BlockStatement:
-				check_statement(c, node->if_statement.else_statement);
+				check_statement(c, node->if_statement.else_statement, flags);
 				break;
 			default:
 				error(&c->error_collector, ast_node_token(node->if_statement.else_statement),
@@ -686,10 +710,12 @@ void check_statement(Checker *c, AstNode *node) {
 	} break;
 
 	case AstNode_ForStatement: {
+		flags |= Statement_BreakAllowed | Statement_ContinueAllowed;
 		check_open_scope(c, node);
 		defer (check_close_scope(c));
 
-		check_statement(c, node->for_statement.init);
+		if (node->for_statement.init != NULL)
+			check_statement(c, node->for_statement.init, 0);
 		if (node->for_statement.cond) {
 			Operand operand = {Addressing_Invalid};
 			check_expression(c, &operand, node->for_statement.cond);
@@ -699,8 +725,9 @@ void check_statement(Checker *c, AstNode *node) {
 				      "Non-boolean condition in `for` statement");
 			}
 		}
-		check_statement(c, node->for_statement.end);
-		check_statement(c, node->for_statement.body);
+		if (node->for_statement.end != NULL)
+			check_statement(c, node->for_statement.end, 0);
+		check_statement(c, node->for_statement.body, flags);
 	} break;
 
 	case AstNode_DeferStatement: {
@@ -710,11 +737,28 @@ void check_statement(Checker *c, AstNode *node) {
 		} else {
 			b32 out_in_defer = c->in_defer;
 			c->in_defer = true;
-			check_statement(c, ds->statement);
+			check_statement(c, ds->statement, 0);
 			c->in_defer = out_in_defer;
 		}
 	} break;
 
+	case AstNode_BranchStatement: {
+		Token token = node->branch_statement.token;
+		switch (token.kind) {
+		case Token_break:
+			if ((flags & Statement_BreakAllowed) == 0)
+				error(&c->error_collector, token, "`break` only allowed in `for` statement");
+			break;
+		case Token_continue:
+			if ((flags & Statement_ContinueAllowed) == 0)
+				error(&c->error_collector, token, "`continue` only allowed in `for` statement");
+			break;
+		default:
+			error(&c->error_collector, token, "Invalid AST: Branch Statement `%.*s`", LIT(token.string));
+			break;
+		}
+	} break;
+
 
 // Declarations
 	case AstNode_VariableDeclaration: {

+ 2 - 1
src/checker/type.cpp

@@ -104,8 +104,9 @@ struct Type {
 };
 
 Type *get_base_type(Type *t) {
-	while (t->kind == Type_Named)
+	while (t->kind == Type_Named) {
 		t = t->named.base;
+	}
 	return t;
 }
 

+ 12 - 14
src/main.cpp

@@ -5,7 +5,6 @@
 #include "checker/checker.cpp"
 // #include "codegen/codegen.cpp"
 
-
 int main(int argc, char **argv) {
 	if (argc < 2) {
 		gb_printf_err("Please specify a .odin file\n");
@@ -22,24 +21,23 @@ int main(int argc, char **argv) {
 		if (init_parser(&parser)) {
 			defer (destroy_parser(&parser));
 
-			parse_files(&parser, init_filename);
-
-			// print_ast(parser.files[0].declarations, 0);
-
-			Checker checker = {};
-			init_checker(&checker, &parser);
-			defer (destroy_checker(&checker));
+			if (parse_files(&parser, init_filename) == ParseFile_None) {
+				// print_ast(parser.files[0].declarations, 0);
 
-			check_parsed_files(&checker);
+				Checker checker = {};
+				init_checker(&checker, &parser);
+				defer (destroy_checker(&checker));
 
+				check_parsed_files(&checker);
 #if 0
-			Codegen codegen = {};
-			if (init_codegen(&codegen, &checker)) {
-				defer (destroy_codegen(&codegen));
+				Codegen codegen = {};
+				if (init_codegen(&codegen, &checker)) {
+					defer (destroy_codegen(&codegen));
 
-				generate_code(&codegen, file_node);
-			}
+					generate_code(&codegen, file_node);
+				}
 #endif
+			}
 		}
 	}
 

+ 235 - 67
src/parser.cpp

@@ -3,6 +3,19 @@ struct Type;
 struct AstScope;
 
 
+enum ParseFileError {
+	ParseFile_None,
+
+	ParseFile_WrongExtension,
+	ParseFile_InvalidFile,
+	ParseFile_EmptyFile,
+	ParseFile_Permission,
+	ParseFile_NotFound,
+	ParseFile_InvalidToken,
+
+	ParseFile_Count,
+};
+
 
 struct AstFile {
 	gbArena        arena;
@@ -10,6 +23,11 @@ struct AstFile {
 	gbArray(Token) tokens;
 	Token *        cursor; // NOTE(bill): Current token, easy to peek forward and backwards if needed
 
+	// >= 0: In Expression
+	// <  0: In Control Clause
+	// NOTE(bill): Used to prevent type literals in control clauses
+	isize expression_level;
+
 	AstNode *declarations;
 	isize declaration_count;
 
@@ -19,6 +37,11 @@ struct AstFile {
 
 	isize error_count;
 	TokenPos error_prev_pos;
+
+	// NOTE(bill): Error recovery
+#define PARSER_MAX_FIX_COUNT 6
+	isize    fix_count;
+	TokenPos fix_prev_pos;
 };
 
 
@@ -78,6 +101,8 @@ AstNode__ComplexStatementBegin,
 	AstNode_ReturnStatement,
 	AstNode_ForStatement,
 	AstNode_DeferStatement,
+	AstNode_BranchStatement,
+
 AstNode__ComplexStatementEnd,
 
 AstNode__StatementEnd,
@@ -180,7 +205,10 @@ struct AstNode {
 		} block_statement;
 		struct {
 			Token token;
-			AstNode *cond, *body, *else_statement;
+			AstNode *init;
+			AstNode *cond;
+			AstNode *body;
+			AstNode *else_statement;
 		} if_statement;
 		struct {
 			Token token;
@@ -196,6 +224,9 @@ struct AstNode {
 			Token token;
 			AstNode *statement;
 		} defer_statement;
+		struct {
+			Token token;
+		} branch_statement;
 
 		struct { Token begin, end; } bad_declaration;
 		struct {
@@ -333,6 +364,8 @@ Token ast_node_token(AstNode *node) {
 		return node->for_statement.token;
 	case AstNode_DeferStatement:
 		return node->defer_statement.token;
+	case AstNode_BranchStatement:
+		return node->branch_statement.token;
 	case AstNode_BadDeclaration:
 		return node->bad_declaration.begin;
 	case AstNode_VariableDeclaration:
@@ -359,8 +392,7 @@ Token ast_node_token(AstNode *node) {
 		return node->struct_type.token;
 	}
 
-	Token null_token = {};
-	return null_token;
+	return empty_token;
 ;}
 
 gb_inline void destroy_ast_scope(AstScope *scope) {
@@ -637,9 +669,10 @@ gb_inline AstNode *make_block_statement(AstFile *f, AstNode *list, isize list_co
 	return result;
 }
 
-gb_inline AstNode *make_if_statement(AstFile *f, Token token, AstNode *cond, AstNode *body, AstNode *else_statement) {
+gb_inline AstNode *make_if_statement(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *body, AstNode *else_statement) {
 	AstNode *result = make_node(f, AstNode_IfStatement);
 	result->if_statement.token = token;
+	result->if_statement.init = init;
 	result->if_statement.cond = cond;
 	result->if_statement.body = body;
 	result->if_statement.else_statement = else_statement;
@@ -670,6 +703,13 @@ gb_inline AstNode *make_defer_statement(AstFile *f, Token token, AstNode *statem
 	return result;
 }
 
+gb_inline AstNode *make_branch_statement(AstFile *f, Token token) {
+	AstNode *result = make_node(f, AstNode_BranchStatement);
+	result->branch_statement.token = token;
+	return result;
+}
+
+
 gb_inline AstNode *make_bad_declaration(AstFile *f, Token begin, Token end) {
 	AstNode *result = make_node(f, AstNode_BadDeclaration);
 	result->bad_declaration.begin = begin;
@@ -770,8 +810,8 @@ gb_inline Token expect_token(AstFile *f, TokenKind kind) {
 	Token prev = f->cursor[0];
 	if (prev.kind != kind) {
 		ast_file_err(f, f->cursor[0], "Expected `%s`, got `%s`",
-		           token_kind_to_string(kind),
-		           token_kind_to_string(prev.kind));
+		             token_kind_to_string(kind),
+		             token_kind_to_string(prev.kind));
 	}
 	next_token(f);
 	return prev;
@@ -781,7 +821,7 @@ gb_inline Token expect_operator(AstFile *f) {
 	Token prev = f->cursor[0];
 	if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
 		ast_file_err(f, f->cursor[0], "Expected an operator, got `%s`",
-		           token_kind_to_string(prev.kind));
+		             token_kind_to_string(prev.kind));
 	}
 	next_token(f);
 	return prev;
@@ -791,7 +831,7 @@ gb_inline Token expect_keyword(AstFile *f) {
 	Token prev = f->cursor[0];
 	if (!gb_is_between(prev.kind, Token__KeywordBegin+1, Token__KeywordEnd-1)) {
 		ast_file_err(f, f->cursor[0], "Expected a keyword, got `%s`",
-		           token_kind_to_string(prev.kind));
+		             token_kind_to_string(prev.kind));
 	}
 	next_token(f);
 	return prev;
@@ -833,6 +873,39 @@ gb_internal void add_ast_entity(AstFile *f, AstScope *scope, AstNode *declaratio
 
 
 
+void fix_advance_to_next_statement(AstFile *f) {
+#if 0
+	for (;;) {
+		Token t = f->cursor[0];
+		switch (t.kind) {
+		case Token_EOF:
+			return;
+
+		case Token_type:
+		case Token_break:
+		case Token_continue:
+		case Token_fallthrough:
+		case Token_if:
+		case Token_for:
+		case Token_defer:
+		case Token_return:
+			if (token_pos_are_equal(t.pos, f->fix_prev_pos) &&
+			    f->fix_count < PARSER_MAX_FIX_COUNT) {
+				f->fix_count++;
+				return;
+			}
+			if (token_pos_cmp(f->fix_prev_pos, t.pos) < 0) {
+				f->fix_prev_pos = t.pos;
+				f->fix_count = 0; // NOTE(bill): Reset
+				return;
+			}
+
+		}
+		next_token(f);
+	}
+#endif
+}
+
 
 
 AstNode *parse_expression(AstFile *f, b32 lhs);
@@ -903,8 +976,10 @@ AstNode *parse_literal_value(AstFile *f, AstNode *type_expression) {
 	AstNode *element_list = NULL;
 	isize element_count = 0;
 	Token open = expect_token(f, Token_OpenBrace);
+	f->expression_level++;
 	if (f->cursor[0].kind != Token_CloseBrace)
 		element_list = parse_element_list(f, &element_count);
+	f->expression_level--;
 	Token close = expect_token(f, Token_CloseBrace);
 
 	return make_compound_literal(f, type_expression, element_list, element_count, open, close);
@@ -942,29 +1017,36 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
 		Token open, close;
 		// NOTE(bill): Skip the Paren Expression
 		open = expect_token(f, Token_OpenParen);
+		f->expression_level++;
 		operand = parse_expression(f, false);
+		f->expression_level--;
 		close = expect_token(f, Token_CloseParen);
-		operand = make_paren_expression(f, operand, open, close);
-		return operand;
-	} break;
+		return make_paren_expression(f, operand, open, close);
+	}
 
 	case Token_Hash: {
 		operand = parse_tag_expression(f, NULL);
 		operand->tag_expression.expression = parse_expression(f, false);
 		return operand;
-	} break;
+	}
 
 	// Parse Procedure Type or Literal
 	case Token_proc: {
 		AstScope *scope = NULL;
 		AstNode *type = parse_procedure_type(f, &scope);
+
 		if (f->cursor[0].kind != Token_OpenBrace) {
 			return type;
-		}
+		} else {
+			AstNode *body;
 
-		AstNode *body = parse_body(f, scope);
-		return make_procedure_literal(f, type, body);
-	} break;
+			f->expression_level++;
+			body = parse_body(f, scope);
+			f->expression_level--;
+
+			return make_procedure_literal(f, type, body);
+		}
+	}
 
 	default: {
 		AstNode *type = parse_identifier_or_type(f);
@@ -973,17 +1055,30 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
 			GB_ASSERT_MSG(type->kind != AstNode_Identifier, "Type Cannot be identifier");
 			return type;
 		}
-	} break;
+	}
 	}
 
-	ast_file_err(f, f->cursor[0], "Expected an operand");
-	return make_bad_expression(f, f->cursor[0], f->cursor[0]);
+	Token begin = f->cursor[0];
+	ast_file_err(f, begin, "Expected an operand");
+	fix_advance_to_next_statement(f);
+	return make_bad_expression(f, begin, f->cursor[0]);
+}
+
+b32 is_literal_type(AstNode *node) {
+	switch (node->kind) {
+	case AstNode_BadExpression:
+	case AstNode_Identifier:
+	case AstNode_ArrayType:
+	case AstNode_StructType:
+		return true;
+	}
+	return false;
 }
 
 AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
 	AstNode *operand = parse_operand(f, lhs);
-	b32 loop = true;
 
+	b32 loop = true;
 	while (loop) {
 		switch (f->cursor[0].kind) {
 		case Token_OpenParen: {
@@ -995,6 +1090,7 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
 			isize arg_list_count = 0;
 			Token open_paren, close_paren;
 
+			f->expression_level++;
 			open_paren = expect_token(f, Token_OpenParen);
 
 			while (f->cursor[0].kind != Token_CloseParen &&
@@ -1013,6 +1109,7 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
 				next_token(f);
 			}
 
+			f->expression_level--;
 			close_paren = expect_token(f, Token_CloseParen);
 
 			operand = make_call_expression(f, operand, arg_list, arg_list_count, open_paren, close_paren);
@@ -1043,7 +1140,9 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
 			Token open, close;
 			AstNode *indices[3] = {};
 
+			f->expression_level++;
 			open = expect_token(f, Token_OpenBracket);
+
 			if (f->cursor[0].kind != Token_Colon)
 				indices[0] = parse_expression(f, false);
 			isize colon_count = 0;
@@ -1058,6 +1157,8 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
 					indices[colon_count] = parse_expression(f, false);
 				}
 			}
+
+			f->expression_level--;
 			close = expect_token(f, Token_CloseBracket);
 
 			if (colon_count == 0) {
@@ -1084,20 +1185,14 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) {
 			break;
 
 		case Token_OpenBrace: {
-			switch (operand->kind) {
-			case AstNode_BadExpression:
-			case AstNode_Identifier:
-			case AstNode_ArrayType:
-			case AstNode_StructType: {
+			if (is_literal_type(operand) && f->expression_level >= 0) {
+				gb_printf_err("here\n");
 				if (lhs) {
 					// TODO(bill): Handle this
 				}
 				operand = parse_literal_value(f, operand);
-			} break;
-
-			default:
+			} else {
 				loop = false;
-				break;
 			}
 		} break;
 
@@ -1275,14 +1370,14 @@ AstNode *parse_block_statement(AstFile *f) {
 	return block_statement;
 }
 
-AstNode *convert_statement_to_expression(AstFile *f, AstNode *statement, char *kind) {
+AstNode *convert_statement_to_expression(AstFile *f, AstNode *statement, String kind) {
 	if (statement == NULL)
 		return NULL;
 
 	if (statement->kind == AstNode_ExpressionStatement)
 		return statement->expression_statement.expression;
 
-	ast_file_err(f, f->cursor[0], "Expected `%s`, found a simple statement.", kind);
+	ast_file_err(f, f->cursor[0], "Expected `%.*s`, found a simple statement.", LIT(kind));
 	return make_bad_expression(f, f->cursor[0], f->cursor[1]);
 }
 
@@ -1371,12 +1466,14 @@ AstNode *parse_identifier_or_type(AstFile *f) {
 		return make_pointer_type(f, expect_token(f, Token_Pointer), parse_type(f));
 
 	case Token_OpenBracket: {
+		f->expression_level++;
 		Token token = expect_token(f, Token_OpenBracket);
 		AstNode *count_expression = NULL;
 
 		if (f->cursor[0].kind != Token_CloseBracket)
 			count_expression = parse_expression(f, false);
 		expect_token(f, Token_CloseBracket);
+		f->expression_level--;
 		return make_array_type(f, token, count_expression, parse_type(f));
 	}
 
@@ -1417,12 +1514,6 @@ AstNode *parse_identifier_or_type(AstFile *f) {
 		return make_paren_expression(f, type_expression, open, close);
 	}
 
-#if 0
-	// TODO(bill): Why did I add this?
-	case Token_Colon:
-	case Token_Eq:
-		break;
-#endif
 	case Token_Colon:
 		break;
 
@@ -1595,8 +1686,10 @@ AstNode *parse_declaration(AstFile *f, AstNode *name_list, isize name_list_count
 			return make_bad_declaration(f, f->cursor[0], f->cursor[0]);
 		}
 	} else {
-		ast_file_err(f, f->cursor[0], "Unknown type of variable declaration");
-		return make_bad_declaration(f, f->cursor[0], f->cursor[0]);
+		Token begin = f->cursor[0];
+		ast_file_err(f, begin, "Unknown type of variable declaration");
+		fix_advance_to_next_statement(f);
+		return make_bad_declaration(f, begin, f->cursor[0]);
 	}
 
 	AstNode *variable_declaration = make_variable_declaration(f, declaration_kind, name_list, name_list_count, type_expression, value_list, value_list_count);
@@ -1612,18 +1705,41 @@ AstNode *parse_if_statement(AstFile *f) {
 	}
 
 	Token token = expect_token(f, Token_if);
-	AstNode *cond, *body, *else_statement;
+	AstNode *init = NULL;
+	AstNode *cond = NULL;
+	AstNode *body = NULL;
+	AstNode *else_statement = NULL;
 
 	open_ast_scope(f);
+	defer (close_ast_scope(f));
+
+	isize prev_level = f->expression_level;
+	f->expression_level = -1;
+
+
+	if (allow_token(f, Token_Semicolon)) {
+		cond = parse_expression(f, false);
+	} else {
+		init = parse_simple_statement(f);
+		if (allow_token(f, Token_Semicolon)) {
+			cond = parse_expression(f, false);
+		} else {
+			cond = convert_statement_to_expression(f, init, make_string("boolean expression"));
+			init = NULL;
+		}
+	}
+
+
+	f->expression_level = prev_level;
+
+
 
-	cond = convert_statement_to_expression(f, parse_simple_statement(f), "boolean expression");
 
 	if (cond == NULL) {
 		ast_file_err(f, f->cursor[0], "Expected condition for if statement");
 	}
 
 	body = parse_block_statement(f);
-	else_statement = NULL;
 	if (allow_token(f, Token_else)) {
 		switch (f->cursor[0].kind) {
 		case Token_if:
@@ -1639,8 +1755,7 @@ AstNode *parse_if_statement(AstFile *f) {
 		}
 	}
 
-	close_ast_scope(f);
-	return make_if_statement(f, token, cond, body, else_statement);
+	return make_if_statement(f, token, init, cond, body, else_statement);
 }
 
 AstNode *parse_return_statement(AstFile *f) {
@@ -1667,32 +1782,42 @@ AstNode *parse_for_statement(AstFile *f) {
 
 	Token token = expect_token(f, Token_for);
 	open_ast_scope(f);
-	AstNode *init_statement = NULL, *cond = NULL, *end_statement = NULL, *body = NULL;
+	defer (close_ast_scope(f));
+
+	AstNode *init = NULL;
+	AstNode *cond = NULL;
+	AstNode *end  = NULL;
+	AstNode *body = NULL;
 
 	if (f->cursor[0].kind != Token_OpenBrace) {
-		cond = parse_simple_statement(f);
-		if (is_ast_node_complex_statement(cond)) {
-			ast_file_err(f, f->cursor[0],
-			           "You are not allowed that type of statement in a for statement, it's too complex!");
+		isize prev_level = f->expression_level;
+		f->expression_level = -1;
+		if (f->cursor[0].kind != Token_Semicolon) {
+			cond = parse_simple_statement(f);
+			if (is_ast_node_complex_statement(cond)) {
+				ast_file_err(f, f->cursor[0],
+				             "You are not allowed that type of statement in a for statement, it is too complex!");
+			}
 		}
 
 		if (allow_token(f, Token_Semicolon)) {
-			init_statement = cond;
+			init = cond;
 			cond = NULL;
 			if (f->cursor[0].kind != Token_Semicolon) {
 				cond = parse_simple_statement(f);
 			}
 			expect_token(f, Token_Semicolon);
 			if (f->cursor[0].kind != Token_OpenBrace) {
-				end_statement = parse_simple_statement(f);
+				end = parse_simple_statement(f);
 			}
 		}
+		f->expression_level = prev_level;
 	}
 	body = parse_block_statement(f);
 
-	close_ast_scope(f);
+	cond = convert_statement_to_expression(f, cond, make_string("boolean expression"));
 
-	return make_for_statement(f, token, init_statement, cond, end_statement, body);
+	return make_for_statement(f, token, init, cond, end, body);
 }
 
 AstNode *parse_defer_statement(AstFile *f) {
@@ -1775,8 +1900,15 @@ AstNode *parse_statement(AstFile *f) {
 	case Token_return: return parse_return_statement(f);
 	case Token_for:    return parse_for_statement(f);
 	case Token_defer:  return parse_defer_statement(f);
-	// case Token_match:
-	// case Token_case:
+	// case Token_match: return NULL; // TODO(bill): Token_match
+	// case Token_case: return NULL; // TODO(bill): Token_case
+
+	case Token_break:
+	case Token_continue:
+	case Token_fallthrough:
+		next_token(f);
+		expect_token(f, Token_Semicolon);
+		return make_branch_statement(f, token);
 
 	case Token_Hash:
 		s = parse_tag_statement(f, NULL);
@@ -1789,9 +1921,12 @@ AstNode *parse_statement(AstFile *f) {
 		s = make_empty_statement(f, token);
 		next_token(f);
 		return s;
+
+
 	}
 
 	ast_file_err(f, token, "Expected a statement, got `%s`", token_kind_to_string(token.kind));
+	fix_advance_to_next_statement(f);
 	return make_bad_statement(f, token, f->cursor[0]);
 }
 
@@ -1814,17 +1949,18 @@ AstNode *parse_statement_list(AstFile *f, isize *list_count_) {
 
 
 
-b32 init_ast_file(AstFile *f, String fullpath) {
+ParseFileError init_ast_file(AstFile *f, String fullpath) {
 	if (!string_has_extension(fullpath, make_string("odin"))) {
 		gb_printf_err("Only `.odin` files are allowed\n");
-		return false;
+		return ParseFile_WrongExtension;
 	}
-	if (init_tokenizer(&f->tokenizer, fullpath)) {
+	TokenizerInitError err = init_tokenizer(&f->tokenizer, fullpath);
+	if (err == TokenizerInit_None) {
 		gb_array_init(f->tokens, gb_heap_allocator());
 		for (;;) {
 			Token token = tokenizer_get_token(&f->tokenizer);
 			if (token.kind == Token_Invalid)
-				return false;
+				return ParseFile_InvalidToken;
 			gb_array_append(f->tokens, token);
 
 			if (token.kind == Token_EOF)
@@ -1841,9 +1977,19 @@ b32 init_ast_file(AstFile *f, String fullpath) {
 		open_ast_scope(f);
 		f->file_scope = f->curr_scope;
 
-		return true;
+		return ParseFile_None;
 	}
-	return false;
+
+	switch (err) {
+	case TokenizerInit_NotExists:
+		return ParseFile_NotFound;
+	case TokenizerInit_Permission:
+		return ParseFile_Permission;
+	case TokenizerInit_Empty:
+		return ParseFile_EmptyFile;
+	}
+
+	return ParseFile_InvalidFile;
 }
 
 void destroy_ast_file(AstFile *f) {
@@ -1928,8 +2074,6 @@ b32 is_import_path_valid(String path) {
 
 
 void parse_file(Parser *p, AstFile *f) {
-	f->declarations = parse_statement_list(f, &f->declaration_count);
-
 	String filepath = f->tokenizer.fullpath;
 	String base_dir = filepath;
 	for (isize i = filepath.len-1; i >= 0; i--) {
@@ -1938,6 +2082,8 @@ void parse_file(Parser *p, AstFile *f) {
 		base_dir.len--;
 	}
 
+	f->declarations = parse_statement_list(f, &f->declaration_count);
+
 	for (AstNode *node = f->declarations; node != NULL; node = node->next) {
 		if (!is_ast_node_declaration(node) &&
 		    node->kind != AstNode_BadStatement &&
@@ -1987,7 +2133,7 @@ void parse_file(Parser *p, AstFile *f) {
 }
 
 
-void parse_files(Parser *p, char *init_filename) {
+ParseFileError parse_files(Parser *p, char *init_filename) {
 	char *fullpath_str = gb_path_get_full_name(gb_heap_allocator(), init_filename);
 	String init_fullpath = make_string(fullpath_str);
 	gb_array_append(p->imports, init_fullpath);
@@ -1995,14 +2141,36 @@ void parse_files(Parser *p, char *init_filename) {
 	for (isize i = 0; i < gb_array_count(p->imports); i++) {
 		String import_path = p->imports[i];
 		AstFile file = {};
-		b32 ok = init_ast_file(&file, import_path);
-		if (!ok) {
+		ParseFileError err = init_ast_file(&file, import_path);
+		if (err != ParseFile_None) {
 			gb_printf_err("Failed to parse file: %.*s\n", LIT(import_path));
-			return;
+			switch (err) {
+			case ParseFile_WrongExtension:
+				gb_printf_err("\tInvalid file extension\n");
+				break;
+			case ParseFile_InvalidFile:
+				gb_printf_err("\tInvalid file\n");
+				break;
+			case ParseFile_EmptyFile:
+				gb_printf_err("\tFile is empty\n");
+				break;
+			case ParseFile_Permission:
+				gb_printf_err("\tFile permissions problem\n");
+				break;
+			case ParseFile_NotFound:
+				gb_printf_err("\tFile cannot be found\n");
+				break;
+			case ParseFile_InvalidToken:
+				gb_printf_err("\tInvalid token found in file\n");
+				break;
+			}
+			return err;
 		}
 		parse_file(p, &file);
 		gb_array_append(p->files, file);
 	}
+
+	return ParseFile_None;
 }
 
 

+ 44 - 9
src/tokenizer.cpp

@@ -214,14 +214,20 @@ struct TokenPos {
 	isize line, column;
 };
 
-b32 token_pos_are_equal(TokenPos a, TokenPos b) {
+i32 token_pos_cmp(TokenPos a, TokenPos b) {
 	if (a.line == b.line) {
 		if (a.column == b.column) {
-			return are_strings_equal(a.file, b.file);
+			isize min_len = gb_min(a.file.len, b.file.len);
+			return gb_memcompare(a.file.text, b.file.text, min_len);
 		}
+		return (a.column < b.column) ? -1 : +1;
 	}
-	return false;
 
+	return (a.line < b.line) ? -1 : +1;
+}
+
+b32 token_pos_are_equal(TokenPos a, TokenPos b) {
+	return token_pos_cmp(a, b) == 0;
 }
 
 // NOTE(bill): Text is UTF-8, thus why u8 and not char
@@ -318,7 +324,19 @@ gb_inline b32 token_is_comparison(Token t) {
 
 gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); }
 
-typedef struct Tokenizer Tokenizer;
+
+enum TokenizerInitError {
+	TokenizerInit_None,
+
+	TokenizerInit_Invalid,
+	TokenizerInit_NotExists,
+	TokenizerInit_Permission,
+	TokenizerInit_Empty,
+
+	TokenizerInit_Count,
+};
+
+
 struct Tokenizer {
 	String fullpath;
 	u8 *start;
@@ -387,7 +405,7 @@ void advance_to_next_rune(Tokenizer *t) {
 	}
 }
 
-b32 init_tokenizer(Tokenizer *t, String fullpath) {
+TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
 	char *c_str = gb_alloc_array(gb_heap_allocator(), char, fullpath.len+1);
 	memcpy(c_str, fullpath.text, fullpath.len);
 	c_str[fullpath.len] = '\0';
@@ -395,7 +413,7 @@ b32 init_tokenizer(Tokenizer *t, String fullpath) {
 
 	gbFileContents fc = gb_file_read_contents(gb_heap_allocator(), true, c_str);
 	gb_zero_item(t);
-	if (fc.data) {
+	if (fc.data != NULL) {
 		t->start = cast(u8 *)fc.data;
 		t->line = t->read_curr = t->curr = t->start;
 		t->end = t->start + fc.size;
@@ -407,13 +425,30 @@ b32 init_tokenizer(Tokenizer *t, String fullpath) {
 		advance_to_next_rune(t);
 		if (t->curr_rune == GB_RUNE_BOM)
 			advance_to_next_rune(t); // Ignore BOM at file beginning
-		return true;
+		return TokenizerInit_None;
 	}
-	return false;
+
+	gbFile f = {};
+	gbFileError err = gb_file_open(&f, c_str);
+	defer (gb_file_close(&f));
+
+	switch (err) {
+	case gbFileError_Invalid:
+		return TokenizerInit_Invalid;
+	case gbFileError_NotExists:
+		return TokenizerInit_NotExists;
+	case gbFileError_Permission:
+		return TokenizerInit_Permission;
+	}
+
+	if (gb_file_size(&f) == 0)
+		return TokenizerInit_Empty;
+	return TokenizerInit_None;
 }
 
 gb_inline void destroy_tokenizer(Tokenizer *t) {
-	gb_free(gb_heap_allocator(), t->start);
+	if (t->start != NULL)
+		gb_free(gb_heap_allocator(), t->start);
 }
 
 void tokenizer_skip_whitespace(Tokenizer *t) {

+ 10 - 3
todo.md

@@ -8,7 +8,8 @@
 
 ## Parser
 * Extra checking here rather than in the checker
-* Mulitple files
+* Mulitple files (done)
+	- Namespaces
 
 ## Checker
 * Cyclic Type Checking
@@ -18,10 +19,10 @@
 	- integer
 	- rational
 	- real
-* Multiple files
+* Multiple files (done)
+	- Namespaces
 
 ## Codegen
-* Begin!!!
 * Emit LLVM-IR using custom library
 * Debug info
 
@@ -29,3 +30,9 @@
 * Begin!!!
 * Choose/determine architecture
 
+
+
+
+## Language
+
+* should `if/for` statements init statement be of the same scope as the block scope or not? (currently not)