Browse Source

Tags, enclosed proc results and better error handling

gingerBill 9 years ago
parent
commit
9f90ff50cf
10 changed files with 326 additions and 235 deletions
  1. 0 1
      build.bat
  2. 30 34
      src/checker/checker.cpp
  3. 108 52
      src/checker/expression.cpp
  4. 36 9
      src/checker/statements.cpp
  5. 0 4
      src/checker/type.cpp
  6. 3 0
      src/main.cpp
  7. 145 113
      src/parser.cpp
  8. 2 1
      src/printer.cpp
  9. 2 10
      src/test.odin
  10. 0 11
      src/tokenizer.cpp

+ 0 - 1
build.bat

@@ -43,7 +43,6 @@ set libs= kernel32.lib user32.lib gdi32.lib opengl32.lib ^
 
 
 
-
 set linker_flags= -incremental:no -opt:ref -subsystem:console
 
 rem Debug

+ 30 - 34
src/checker/checker.cpp

@@ -68,6 +68,7 @@ enum BuiltinProcedureId {
 	BuiltinProcedure_len,
 	BuiltinProcedure_cap,
 	BuiltinProcedure_copy,
+	BuiltinProcedure_copy_bytes,
 	BuiltinProcedure_print,
 	BuiltinProcedure_println,
 
@@ -91,6 +92,7 @@ gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = {
 	{STR_LIT("len"),              1, false, Expression_Expression},
 	{STR_LIT("cap"),              1, false, Expression_Expression},
 	{STR_LIT("copy"),             2, false, Expression_Expression},
+	{STR_LIT("copy_bytes"),       3, false, Expression_Statement},
 	{STR_LIT("print"),            1, true,  Expression_Statement},
 	{STR_LIT("println"),          1, true,  Expression_Statement},
 };
@@ -112,7 +114,7 @@ struct Checker {
 
 	Scope *curr_scope;
 	gbArray(Type *) procedure_stack;
-	b32 in_defer;
+	b32 in_defer; // TODO(bill): Actually handle correctly
 
 #define MAX_CHECKER_ERROR_COUNT 10
 	isize error_prev_line;
@@ -199,6 +201,14 @@ void add_global_entity(Entity *entity) {
 	}
 }
 
+void add_global_constant(gbAllocator a, String name, Type *type, Value value) {
+	Token token = {Token_Identifier};
+	token.string = name;
+	Entity *entity = alloc_entity(a, Entity_Constant, NULL, token, type);
+	entity->constant.value = value;
+	add_global_entity(entity);
+}
+
 void init_global_scope(void) {
 	// NOTE(bill): No need to free these
 	gbAllocator a = gb_heap_allocator();
@@ -217,23 +227,9 @@ void init_global_scope(void) {
 	}
 
 // Constants
-	Token true_token = {Token_Identifier};
-	true_token.string = make_string("true");
-	Entity *true_entity = alloc_entity(a, Entity_Constant, NULL, true_token, &basic_types[Basic_UntypedBool]);
-	true_entity->constant.value = make_value_bool(true);
-	add_global_entity(true_entity);
-
-	Token false_token = {Token_Identifier};
-	false_token.string = make_string("false");
-	Entity *false_entity = alloc_entity(a, Entity_Constant, NULL, false_token, &basic_types[Basic_UntypedBool]);
-	false_entity->constant.value = make_value_bool(false);
-	add_global_entity(false_entity);
-
-	Token null_token = {Token_Identifier};
-	null_token.string = make_string("null");
-	Entity *null_entity = alloc_entity(a, Entity_Constant, NULL, null_token, &basic_types[Basic_UntypedPointer]);
-	null_entity->constant.value = make_value_pointer(NULL);
-	add_global_entity(null_entity);
+	add_global_constant(a, make_string("true"),  &basic_types[Basic_UntypedBool],    make_value_bool(true));
+	add_global_constant(a, make_string("false"), &basic_types[Basic_UntypedBool],    make_value_bool(false));
+	add_global_constant(a, make_string("null"),  &basic_types[Basic_UntypedPointer], make_value_pointer(NULL));
 
 // Builtin Procedures
 	for (isize i = 0; i < gb_count_of(builtin_procedures); i++) {
@@ -290,25 +286,25 @@ void destroy_checker(Checker *c) {
 
 #define print_checker_error(p, token, fmt, ...) print_checker_error_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__)
 void print_checker_error_(Checker *c, char *function, Token token, char *fmt, ...) {
-	va_list va;
+
 
 	// NOTE(bill): Duplicate error, skip it
-	if (c->error_prev_line == token.line && c->error_prev_column == token.column) {
-		goto error;
+	if (!(c->error_prev_line == token.line && c->error_prev_column == token.column)) {
+		c->error_prev_line = token.line;
+		c->error_prev_column = token.column;
+
+	#if 0
+		gb_printf_err("%s()\n", function);
+	#endif
+
+		va_list va;
+		va_start(va, fmt);
+		gb_printf_err("%s(%td:%td) %s\n",
+		              c->parser->tokenizer.fullpath, token.line, token.column,
+		              gb_bprintf_va(fmt, va));
+		va_end(va);
+
 	}
-	c->error_prev_line = token.line;
-	c->error_prev_column = token.column;
-
-#if 0
-	gb_printf_err("%s()\n", function);
-#endif
-	va_start(va, fmt);
-	gb_printf_err("%s(%td:%td) %s\n",
-	              c->parser->tokenizer.fullpath, token.line, token.column,
-	              gb_bprintf_va(fmt, va));
-	va_end(va);
-
-error:
 	c->error_count++;
 	// NOTE(bill): If there are too many errors, just quit
 	if (c->error_count > MAX_CHECKER_ERROR_COUNT) {

+ 108 - 52
src/checker/expression.cpp

@@ -70,10 +70,14 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel
 		if (type_expression) {
 			Type *type = check_type(c, type_expression);
 			for (AstNode *name = field->field.name_list; name != NULL; name = name->next) {
-				GB_ASSERT(name->kind == AstNode_Identifier);
-				Entity *param = make_entity_param(c, scope, name->identifier.token, type);
-				add_entity(c, scope, name, param);
-				variables[variable_index++] = param;
+				if (name->kind == AstNode_Identifier) {
+					Entity *param = make_entity_param(c, scope, name->identifier.token, type);
+					add_entity(c, scope, name, param);
+					variables[variable_index++] = param;
+				} else {
+					print_checker_error(c, ast_node_token(name),
+					                    "Invalid parameter (invalid AST)");
+				}
 			}
 		}
 	}
@@ -98,6 +102,10 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *list, isize list_coun
 		Entity *param = make_entity_param(c, scope, token, type);
 		// NOTE(bill): No need to record
 		variables[variable_index++] = param;
+
+		if (get_base_type(type)->kind == Type_Array) {
+			print_checker_error(c, token, "You cannot return an array from a procedure");
+		}
 	}
 	tuple->tuple.variables = variables;
 	tuple->tuple.variable_count = list_count;
@@ -107,23 +115,12 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *list, isize list_coun
 
 
 void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
-	isize param_count = 0;
-	isize result_count = 0;
+	isize param_count = proc_type_node->procedure_type.param_count;
+	isize result_count = proc_type_node->procedure_type.result_count;
 
-	// NOTE(bill): Each field can store multiple items
-	for (AstNode *field = proc_type_node->procedure_type.param_list;
-	     field != NULL;
-	     field = field->next) {
-		param_count += field->field.name_list_count;
-	}
+	// gb_printf("%td -> %td\n", param_count, result_count);
 
-	for (AstNode *item = proc_type_node->procedure_type.results_list;
-	     item != NULL;
-	     item = item->next) {
-		result_count++;
-	}
-
-	Type *params  = check_get_params (c, c->curr_scope, proc_type_node->procedure_type.param_list, param_count);
+	Type *params  = check_get_params(c, c->curr_scope, proc_type_node->procedure_type.param_list,   param_count);
 	Type *results = check_get_results(c, c->curr_scope, proc_type_node->procedure_type.results_list, result_count);
 
 	type->procedure.scope = c->curr_scope;
@@ -1227,6 +1224,52 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
 		operand->mode = Addressing_Value;
 	} break;
 
+	case BuiltinProcedure_copy_bytes: {
+		// copy_bytes :: proc(dest, source: rawptr, byte_count: int)
+		Type *dest_type = NULL, *src_type = NULL;
+
+		Type *d = get_base_type(operand->type);
+		if (is_type_pointer(d))
+			dest_type = d;
+
+		Operand op = {};
+		check_expression(c, &op, ce->arg_list->next);
+		if (op.mode == Addressing_Invalid)
+			return false;
+		Type *s = get_base_type(op.type);
+		if (is_type_pointer(s))
+			src_type = s;
+
+		if (dest_type == NULL || src_type == NULL) {
+			print_checker_error(c, ast_node_token(call), "`copy_bytes` only expects pointers for the destintation and source");
+			return false;
+		}
+
+		check_expression(c, &op, ce->arg_list->next->next);
+		if (op.mode == Addressing_Invalid)
+			return false;
+
+		convert_to_typed(c, &op, &basic_types[Basic_int]);
+		if (op.mode == Addressing_Invalid ||
+		    op.type->kind != Type_Basic ||
+		    op.type->basic.kind != Basic_int) {
+			gbString str = type_to_string(op.type);
+			defer (gb_string_free(str));
+			print_checker_error(c, ast_node_token(call), "`copy_bytes` 3rd argument must be of type `int`, a `%s` was given", str);
+			return false;
+		}
+
+		if (op.mode == Addressing_Constant) {
+			if (value_to_integer(op.value).value_integer <= 0) {
+				print_checker_error(c, ast_node_token(call), "You cannot copy a zero or negative amount of bytes with `copy_bytes`");
+				return false;
+			}
+		}
+
+		operand->type = NULL;
+		operand->mode = Addressing_NoValue;
+	} break;
+
 
 	case BuiltinProcedure_print:
 	case BuiltinProcedure_println: {
@@ -1445,62 +1488,69 @@ void check_cast_expression(Checker *c, Operand *operand, Type *type) {
 
 
 
-ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expression, Type *type_hint) {
+ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *node, Type *type_hint) {
 	ExpressionKind kind = Expression_Statement;
 
 	operand->mode = Addressing_Invalid;
 	operand->type = &basic_types[Basic_Invalid];
 
-	switch (expression->kind) {
+	switch (node->kind) {
 	case AstNode_BadExpression:
 		goto error;
 
 	case AstNode_Identifier:
-		check_identifier(c, operand, expression, type_hint);
+		check_identifier(c, operand, node, type_hint);
 		break;
 	case AstNode_BasicLiteral: {
-		BasicKind kind = Basic_Invalid;
-		Token lit = expression->basic_literal;
+		BasicKind basic_kind = Basic_Invalid;
+		Token lit = node->basic_literal;
 		switch (lit.kind) {
-		case Token_Integer: kind = Basic_UntypedInteger; break;
-		case Token_Float:   kind = Basic_UntypedFloat;   break;
-		case Token_String:  kind = Basic_UntypedString;  break;
-		case Token_Rune:    kind = Basic_UntypedRune;    break;
-		default:            GB_PANIC("Unknown literal"); break;
+		case Token_Integer: basic_kind = Basic_UntypedInteger; break;
+		case Token_Float:   basic_kind = Basic_UntypedFloat;   break;
+		case Token_String:  basic_kind = Basic_UntypedString;  break;
+		case Token_Rune:    basic_kind = Basic_UntypedRune;    break;
+		default:            GB_PANIC("Unknown literal");       break;
 		}
 		operand->mode  = Addressing_Constant;
-		operand->type  = &basic_types[kind];
+		operand->type  = &basic_types[basic_kind];
 		operand->value = make_value_from_basic_literal(lit);
 	} break;
 
 	case AstNode_ParenExpression:
-		kind = check_expression_base(c, operand, expression->paren_expression.expression);
-		operand->expression = expression;
+		kind = check_expression_base(c, operand, node->paren_expression.expression, type_hint);
+		operand->expression = node;
+		break;
+
+	case AstNode_TagExpression:
+		// TODO(bill): Tag expressions
+		print_checker_error(c, ast_node_token(node), "Tag expressions are not supported yet");
+		kind = check_expression_base(c, operand, node->tag_expression.expression, type_hint);
+		operand->expression = node;
 		break;
 
 	case AstNode_UnaryExpression:
-		check_expression(c, operand, expression->unary_expression.operand);
+		check_expression(c, operand, node->unary_expression.operand);
 		if (operand->mode == Addressing_Invalid)
 			goto error;
-		check_unary_expression(c, operand, expression->unary_expression.op, expression);
+		check_unary_expression(c, operand, node->unary_expression.op, node);
 		if (operand->mode == Addressing_Invalid)
 			goto error;
 		break;
 
 	case AstNode_BinaryExpression:
-		check_binary_expression(c, operand, expression);
+		check_binary_expression(c, operand, node);
 		if (operand->mode == Addressing_Invalid)
 			goto error;
 		break;
 
 
 	case AstNode_SelectorExpression:
-		check_expression_base(c, operand, expression->selector_expression.operand);
-		check_selector(c, operand, expression);
+		check_expression_base(c, operand, node->selector_expression.operand);
+		check_selector(c, operand, node);
 		break;
 
 	case AstNode_IndexExpression: {
-		check_expression(c, operand, expression->index_expression.expression);
+		check_expression(c, operand, node->index_expression.expression);
 		if (operand->mode == Addressing_Invalid)
 			goto error;
 
@@ -1548,7 +1598,7 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
 			goto error;
 		}
 
-		if (expression->index_expression.value == NULL) {
+		if (node->index_expression.value == NULL) {
 			gbString str = expression_to_string(operand->expression);
 			print_checker_error(c, ast_node_token(operand->expression),
 			                    "Missing index for `%s`", str);
@@ -1556,12 +1606,12 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
 			goto error;
 		}
 
-		check_index_value(c, expression->index_expression.value, max_count, NULL);
+		check_index_value(c, node->index_expression.value, max_count, NULL);
 	} break;
 
 
 	case AstNode_SliceExpression: {
-		auto *se = &expression->slice_expression;
+		auto *se = &node->slice_expression;
 		check_expression(c, operand, se->expression);
 		if (operand->mode == Addressing_Invalid)
 			goto error;
@@ -1584,8 +1634,8 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
 			valid = true;
 			max_count = t->array.count;
 			if (operand->mode != Addressing_Variable) {
-				gbString str = expression_to_string(expression);
-				print_checker_error(c, ast_node_token(expression), "Cannot slice array `%s`, value is not addressable", str);
+				gbString str = expression_to_string(node);
+				print_checker_error(c, ast_node_token(node), "Cannot slice array `%s`, value is not addressable", str);
 				gb_string_free(str);
 				goto error;
 			}
@@ -1645,18 +1695,18 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
 	} break;
 
 	case AstNode_CastExpression: {
-		Type *cast_type = check_type(c, expression->cast_expression.type_expression);
-		check_expression_or_type(c, operand, expression->cast_expression.operand);
+		Type *cast_type = check_type(c, node->cast_expression.type_expression);
+		check_expression_or_type(c, operand, node->cast_expression.operand);
 		if (operand->mode != Addressing_Invalid)
 			check_cast_expression(c, operand, cast_type);
 
 	} break;
 
 	case AstNode_CallExpression:
-		return check_call_expression(c, operand, expression);
+		return check_call_expression(c, operand, node);
 
 	case AstNode_DereferenceExpression:
-		check_expression_or_type(c, operand, expression->dereference_expression.operand);
+		check_expression_or_type(c, operand, node->dereference_expression.operand);
 		if (operand->mode == Addressing_Invalid) {
 			goto error;
 		} else {
@@ -1679,17 +1729,17 @@ ExpressionKind check_expression_base(Checker *c, Operand *operand, AstNode *expr
 	case AstNode_ArrayType:
 	case AstNode_StructType:
 		operand->mode = Addressing_Type;
-		operand->type = check_type(c, expression);
+		operand->type = check_type(c, node);
 		break;
 	}
 
 	kind = Expression_Expression;
-	operand->expression = expression;
+	operand->expression = node;
 	goto after_error;
 
 error:
 	operand->mode = Addressing_Invalid;
-	operand->expression = expression;
+	operand->expression = node;
 	goto after_error;
 
 after_error:
@@ -1713,9 +1763,9 @@ after_error:
 
 	if (type) {
 		if (is_type_untyped(type)) {
-			add_untyped(c, expression, false, operand->mode, type, value);
+			add_untyped(c, node, false, operand->mode, type, value);
 		} else {
-			add_type_and_value(c, expression, operand->mode, type, value);
+			add_type_and_value(c, node, operand->mode, type, value);
 		}
 	}
 	return kind;
@@ -1824,6 +1874,12 @@ gbString write_expression_to_string(gbString str, AstNode *node) {
 		str = string_append_token(str, node->basic_literal);
 		break;
 
+	case AstNode_TagExpression:
+		str = gb_string_appendc(str, "#");
+		str = string_append_token(str, node->tag_expression.name);
+		str = write_expression_to_string(str, node->tag_expression.expression);
+		break;
+
 	case AstNode_UnaryExpression:
 		str = string_append_token(str, node->unary_expression.op);
 		str = write_expression_to_string(str, node->unary_expression.operand);

+ 36 - 9
src/checker/statements.cpp

@@ -385,6 +385,12 @@ void check_statement(Checker *c, AstNode *node) {
 		}
 	} break;
 
+	case AstNode_TagStatement:
+		// TODO(bill): Tag Statements
+		print_checker_error(c, ast_node_token(node), "Tag statements are not supported yet");
+		check_statement(c, node->tag_statement.statement);
+		break;
+
 	case AstNode_IncDecStatement: {
 		Token op = {};
 		auto *s = &node->inc_dec_statement;
@@ -653,7 +659,7 @@ void check_statement(Checker *c, AstNode *node) {
 
 	case AstNode_ProcedureDeclaration: {
 		auto *pd = &node->procedure_declaration;
-		GB_ASSERT_MSG(pd->kind == Declaration_Immutable, "Mutable/temp procedures are not yet implemented");
+		GB_ASSERT_MSG(pd->kind == Declaration_Immutable, "Mutable/temp/anonymous procedures are not yet implemented");
 		Entity *e = make_entity_procedure(c, c->curr_scope, pd->name->identifier.token, NULL);
 		add_entity(c, c->curr_scope, pd->name, e);
 
@@ -667,9 +673,38 @@ void check_statement(Checker *c, AstNode *node) {
 		check_open_scope(c, pd->procedure_type);
 		{
 			check_procedure_type(c, proc_type, pd->procedure_type);
+			b32 is_foreign = false;
+			b32 is_inline = false;
+			b32 is_no_inline = false;
+			for (AstNode *tag = pd->tag_list; tag != NULL; tag = tag->next) {
+				GB_ASSERT(tag->kind == AstNode_TagExpression);
+
+				String tag_name = tag->tag_expression.name.string;
+				if (are_strings_equal(tag_name, make_string("foreign"))) {
+					is_foreign = true;
+				} else if (are_strings_equal(tag_name, make_string("inline"))) {
+					is_inline = true;
+				} else if (are_strings_equal(tag_name, make_string("no_inline"))) {
+					is_no_inline = true;
+				} else {
+					print_checker_error(c, ast_node_token(tag), "Unknown procedure tag");
+				}
+				// TODO(bill): Other tags
+			}
+
+			if (is_inline && is_no_inline) {
+				print_checker_error(c, ast_node_token(pd->tag_list),
+				                    "You cannot apply both `inline` and `no_inline` to a procedure");
+			}
+
 			if (pd->body) {
 				GB_ASSERT(pd->body->kind == AstNode_BlockStatement);
 
+				if (is_foreign) {
+					print_checker_error(c, ast_node_token(pd->body),
+					                    "A procedure tagged as `#foreign` cannot have a body");
+				}
+
 				push_procedure(c, proc_type);
 				check_statement_list(c, pd->body->block_statement.list);
 				if (pd->procedure_type->procedure_type.result_count > 0) {
@@ -678,14 +713,6 @@ void check_statement(Checker *c, AstNode *node) {
 					}
 				}
 				pop_procedure(c);
-			} else if (pd->tag) {
-				GB_ASSERT(pd->tag->kind == AstNode_TagExpression);
-
-				String tag_name = pd->tag->tag_expression.name.string;
-				if (are_strings_equal(tag_name, make_string("foreign"))) {
-					// NOTE(bill): Foreign procedure (linking stage)
-				}
-				// TODO(bill): Other tags
 			}
 		}
 		check_close_scope(c);

+ 0 - 4
src/checker/type.cpp

@@ -570,10 +570,6 @@ gbString write_type_to_string(gbString str, Type *type) {
 				GB_ASSERT(var->kind == Entity_Variable);
 				if (i > 0)
 					str = gb_string_appendc(str, ", ");
-				if (var->token.string.len > 0) {
-					str = gb_string_append_length(str, var->token.string.text, var->token.string.len);
-					str = gb_string_appendc(str, ": ");
-				}
 				str = write_type_to_string(str, var->type);
 			}
 		}

+ 3 - 0
src/main.cpp

@@ -5,6 +5,7 @@
 #include "checker.cpp"
 #include "generator.cpp"
 
+
 int main(int argc, char **argv) {
 	if (argc < 2) {
 		gb_printf_err("Please specify a .odin file\n");
@@ -29,11 +30,13 @@ int main(int argc, char **argv) {
 
 			check_statement_list(&checker, root_node);
 
+#if 0
 			Generator generator = {};
 			if (init_generator(&generator, &checker)) {
 				defer (destroy_generator(&generator));
 				generate_code(&generator, root_node);
 			}
+#endif
 		}
 	}
 

+ 145 - 113
src/parser.cpp

@@ -55,6 +55,7 @@ AstNode__ExpressionEnd,
 AstNode__StatementBegin,
 	AstNode_BadStatement, // NOTE(bill): Naughty statement
 	AstNode_EmptyStatement,
+	AstNode_TagStatement,
 	AstNode_ExpressionStatement,
 	AstNode_IncDecStatement,
 	AstNode_AssignStatement,
@@ -111,6 +112,7 @@ struct AstNode {
 		struct {
 			Token token;
 			Token name;
+			AstNode *expression;
 		} tag_expression;
 
 		struct { Token begin, end; }                                bad_expression;
@@ -137,6 +139,11 @@ struct AstNode {
 		struct { Token token; }                   empty_statement;
 		struct { AstNode *expression; }           expression_statement;
 		struct { Token op; AstNode *expression; } inc_dec_statement;
+		struct {
+			Token token;
+			Token name;
+			AstNode *statement;
+		} tag_statement;
 		struct {
 			Token op;
 			AstNode *lhs_list, *rhs_list;
@@ -182,7 +189,7 @@ struct AstNode {
 		} field;
 		struct {
 			Token token;
-			AstNode *param_list; // AstNode_Field
+			AstNode *param_list; // AstNode_Field list
 			isize param_count;
 			AstNode *results_list; // type expression list
 			isize result_count;
@@ -192,9 +199,8 @@ struct AstNode {
 			AstNode *name;           // AstNode_Identifier
 			AstNode *procedure_type; // AstNode_ProcedureType
 			AstNode *body;           // AstNode_BlockStatement
-			AstNode *tag;            // AstNode_TagExpression
-			// TODO(bill): Allow for multiple tags
-			// TODO(bill): Modifiers: inline, no_inline, etc.
+			AstNode *tag_list;       // AstNode_TagExpression
+			isize tag_count;
 		} procedure_declaration;
 		struct {
 			Token token;
@@ -252,6 +258,8 @@ Token ast_node_token(AstNode *node) {
 		return node->basic_literal;
 	case AstNode_Identifier:
 		return node->identifier.token;
+	case AstNode_TagExpression:
+		return node->tag_expression.token;
 	case AstNode_BadExpression:
 		return node->bad_expression.begin;
 	case AstNode_UnaryExpression:
@@ -278,6 +286,8 @@ Token ast_node_token(AstNode *node) {
 		return node->empty_statement.token;
 	case AstNode_ExpressionStatement:
 		return ast_node_token(node->expression_statement.expression);
+	case AstNode_TagStatement:
+		return node->tag_statement.token;
 	case AstNode_IncDecStatement:
 		return node->inc_dec_statement.op;
 	case AstNode_AssignStatement:
@@ -366,8 +376,36 @@ AstEntity *ast_scope_insert(AstScope *scope, AstEntity entity) {
 	return prev;
 }
 
-// NOTE(bill): And this below is why is need a new language! Discriminated unions are a pain in C/C++
+
+#define print_parse_error(p, token, fmt, ...) print_parse_error_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__)
+void print_parse_error_(Parser *p, char *function, Token token, char *fmt, ...) {
+
+	// NOTE(bill): Duplicate error, skip it
+	if (p->error_prev_line != token.line || p->error_prev_column != token.column) {
+		va_list va;
+
+		p->error_prev_line = token.line;
+		p->error_prev_column = token.column;
+
+	#if 1
+		gb_printf_err("%s()\n", function);
+	#endif
+		va_start(va, fmt);
+		gb_printf_err("%s(%td:%td) Syntax error: %s\n",
+		              p->tokenizer.fullpath, token.line, token.column,
+		              gb_bprintf_va(fmt, va));
+		va_end(va);
+	}
+	p->error_count++;
+}
+
+
+// NOTE(bill): And this below is why is I/we need a new language! Discriminated unions are a pain in C/C++
 gb_inline AstNode *make_node(Parser *p, AstNodeKind kind) {
+	if (gb_arena_size_remaining(&p->arena, GB_DEFAULT_MEMORY_ALIGNMENT) < gb_size_of(AstNode)) {
+		// NOTE(bill): If a syntax error is so bad, just quit!
+		gb_exit(1);
+	}
 	AstNode *node = gb_alloc_item(gb_arena_allocator(&p->arena), AstNode);
 	node->kind = kind;
 	return node;
@@ -380,10 +418,19 @@ gb_inline AstNode *make_bad_expression(Parser *p, Token begin, Token end) {
 	return result;
 }
 
-gb_inline AstNode *make_tag_expression(Parser *p, Token token, Token name) {
+gb_inline AstNode *make_tag_expression(Parser *p, Token token, Token name, AstNode *expression) {
 	AstNode *result = make_node(p, AstNode_TagExpression);
 	result->tag_expression.token = token;
 	result->tag_expression.name = name;
+	result->tag_expression.expression = expression;
+	return result;
+}
+
+gb_inline AstNode *make_tag_statement(Parser *p, Token token, Token name, AstNode *statement) {
+	AstNode *result = make_node(p, AstNode_TagStatement);
+	result->tag_statement.token = token;
+	result->tag_statement.name = name;
+	result->tag_statement.statement = statement;
 	return result;
 }
 
@@ -593,13 +640,14 @@ gb_inline AstNode *make_procedure_type(Parser *p, Token token, AstNode *param_li
 	return result;
 }
 
-gb_inline AstNode *make_procedure_declaration(Parser *p, DeclarationKind kind, AstNode *name, AstNode *procedure_type, AstNode *body, AstNode *tag) {
+gb_inline AstNode *make_procedure_declaration(Parser *p, DeclarationKind kind, AstNode *name, AstNode *procedure_type, AstNode *body, AstNode *tag_list, isize tag_count) {
 	AstNode *result = make_node(p, AstNode_ProcedureDeclaration);
 	result->procedure_declaration.kind = kind;
 	result->procedure_declaration.name = name;
 	result->procedure_declaration.procedure_type = procedure_type;
 	result->procedure_declaration.body = body;
-	result->procedure_declaration.tag = tag;
+	result->procedure_declaration.tag_list = tag_list;
+	result->procedure_declaration.tag_count = tag_count;
 	return result;
 }
 
@@ -635,28 +683,6 @@ gb_inline AstNode *make_type_declaration(Parser *p, Token token, AstNode *name,
 }
 
 
-#define print_parse_error(p, token, fmt, ...) print_parse_error_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__)
-void print_parse_error_(Parser *p, char *function, Token token, char *fmt, ...) {
-
-	// NOTE(bill): Duplicate error, skip it
-	if (p->error_prev_line != token.line || p->error_prev_column != token.column) {
-		va_list va;
-
-		p->error_prev_line = token.line;
-		p->error_prev_column = token.column;
-
-	#if 0
-		gb_printf_err("%s()\n", function);
-	#endif
-		va_start(va, fmt);
-		gb_printf_err("%s(%td:%td) %s\n",
-		              p->tokenizer.fullpath, token.line, token.column,
-		              gb_bprintf_va(fmt, va));
-		va_end(va);
-	}
-	p->error_count++;
-}
-
 
 gb_inline b32 next_token(Parser *p) {
 	if (p->cursor+1 < p->tokens + gb_array_count(p->tokens)) {
@@ -724,7 +750,7 @@ b32 init_parser(Parser *p, char *filename) {
 
 		// NOTE(bill): Is this big enough or too small?
 		isize arena_size = gb_max(gb_size_of(AstNode), gb_size_of(AstScope));
-		arena_size *= 1.25*gb_array_count(p->tokens);
+		arena_size *= 2*gb_array_count(p->tokens);
 		gb_arena_init_from_allocator(&p->arena, gb_heap_allocator(), arena_size);
 
 		open_ast_scope(p);
@@ -746,7 +772,10 @@ void destroy_parser(Parser *p) {
 
 gb_internal void add_ast_entity(Parser *p, AstScope *scope, AstNode *declaration, AstNode *name_list) {
 	for (AstNode *n = name_list; n != NULL; n = n->next) {
-		GB_ASSERT_MSG(n->kind == AstNode_Identifier, "Identifier is already declared or resolved");
+		if (n->kind != AstNode_Identifier) {
+			print_parse_error(p, ast_node_token(declaration), "Identifier is already declared or resolved");
+			continue;
+		}
 
 		AstEntity *entity = make_ast_entity(p, n->identifier.token, declaration, scope);
 		n->identifier.entity = entity;
@@ -765,8 +794,26 @@ gb_internal void add_ast_entity(Parser *p, AstScope *scope, AstNode *declaration
 AstNode *parse_expression(Parser *p, b32 lhs);
 
 AstNode *parse_identifier(Parser *p) {
-	Token identifier = expect_token(p, Token_Identifier);
-	return make_identifier(p, identifier);
+	Token token = p->cursor[0];
+	if (token.kind == Token_Identifier) {
+		next_token(p);
+	} else {
+		token.string = make_string("_");
+		expect_token(p, Token_Identifier);
+	}
+	return make_identifier(p, token);
+}
+
+AstNode *parse_tag_expression(Parser *p, AstNode *expression) {
+	Token token = expect_token(p, Token_Hash);
+	Token name  = expect_token(p, Token_Identifier);
+	return make_tag_expression(p, token, name, expression);
+}
+
+AstNode *parse_tag_statement(Parser *p, AstNode *statement) {
+	Token token = expect_token(p, Token_Hash);
+	Token name  = expect_token(p, Token_Identifier);
+	return make_tag_statement(p, token, name, statement);
 }
 
 AstNode *unparen_expression(AstNode *node) {
@@ -803,6 +850,11 @@ AstNode *parse_atom_expression(Parser *p, b32 lhs) {
 		close = expect_token(p, Token_CloseParen);
 		operand = make_paren_expression(p, operand, open, close);
 	} break;
+
+	case Token_Hash: {
+		operand = parse_tag_expression(p, NULL);
+		operand->tag_expression.expression = parse_expression(p, false);
+	} break;
 	}
 
 	b32 loop = true;
@@ -852,10 +904,9 @@ AstNode *parse_atom_expression(Parser *p, b32 lhs) {
 				operand = make_selector_expression(p, token, operand, parse_identifier(p));
 				break;
 			default: {
-				Token token = p->cursor[0];
-				print_parse_error(p, token, "Expected a selector");
+				print_parse_error(p, p->cursor[0], "Expected a selector");
 				next_token(p);
-				operand = make_selector_expression(p, token, operand, NULL);
+				operand = make_selector_expression(p, p->cursor[0], operand, NULL);
 			} break;
 			}
 		} break;
@@ -1153,50 +1204,21 @@ AstNode *parse_field_declaration(Parser *p, AstScope *scope) {
 	return field;
 }
 
+Token parse_procedure_signature(Parser *p, AstScope *scope,
+                                AstNode **param_list, isize *param_count,
+                                AstNode **result_list, isize *result_count);
+
 AstNode *parse_procedure_type(Parser *p, AstScope **scope_) {
-	Token token = expect_token(p, Token_proc);
 	AstScope *scope = make_ast_scope(p, p->file_scope); // Procedure's scope
 	AstNode *params = NULL;
 	AstNode *results = NULL;
 	isize param_count = 0;
 	isize result_count = 0;
 
-	expect_token(p, Token_OpenParen);
-	if (p->cursor[0].kind != Token_CloseParen) {
-		// IMPORTANT TODO(bill): Allow for lhs-expression list style types
-		// proc(x, y: int, a, b: f32);
-		AstNode *params_curr = NULL;
-		do {
-			AstNode *type_node = parse_type(p);
-			DLIST_APPEND(params, params_curr, type_node);
-			param_count++;
-			if (p->cursor[0].kind != Token_Comma ||
-			    p->cursor[0].kind == Token_EOF)
-			    break;
-			next_token(p);
-		} while (true);
-	}
-	expect_token(p, Token_CloseParen);
-
-	// NOTE(bill): Has results
-	if (allow_token(p, Token_ArrowRight)) {
-		if (p->cursor[0].kind != Token_Semicolon) {
-			AstNode *results_curr = NULL;
-			do {
-				DLIST_APPEND(results, results_curr, parse_type(p));
-				result_count++;
-				if (p->cursor[0].kind != Token_Comma ||
-				    p->cursor[0].kind == Token_EOF)
-				    break;
-				next_token(p);
-			} while (true);
-		} else {
-			print_parse_error(p, p->cursor[0], "Expected at least one type after the `->`");
-		}
-	}
+	Token proc_token = parse_procedure_signature(p, scope, &params, &param_count, &results, &result_count);
 
 	if (scope_) *scope_ = scope;
-	return make_procedure_type(p, token, params, param_count, results, result_count);
+	return make_procedure_type(p, proc_token, params, param_count, results, result_count);
 }
 
 
@@ -1260,22 +1282,23 @@ AstNode *parse_identifier_or_type(Parser *p) {
 		break;
 
 	default:
-		print_parse_error(p, p->cursor[0], "Expected type after type separator `:`");
+		print_parse_error(p, p->cursor[0],
+		                  "Expected a type after `%.*s`, got `%.*s`", LIT(p->cursor[-1].string), LIT(p->cursor[0].string));
 		break;
 	}
 
 	return NULL;
 }
 
-// TODO(bill): Probably unify `parse_parameters` and `parse_results`
 AstNode *parse_parameters(Parser *p, AstScope *scope, isize *param_count_) {
 	AstNode *param_list = NULL;
 	AstNode *param_list_curr = NULL;
 	isize param_count = 0;
 	expect_token(p, Token_OpenParen);
 	while (p->cursor[0].kind != Token_CloseParen) {
-		DLIST_APPEND(param_list, param_list_curr, parse_field_declaration(p, scope));
-		param_count++;
+		AstNode *field = parse_field_declaration(p, scope);
+		DLIST_APPEND(param_list, param_list_curr, field);
+		param_count += field->field.name_list_count;
 		if (p->cursor[0].kind != Token_Comma)
 			break;
 		next_token(p);
@@ -1286,34 +1309,42 @@ AstNode *parse_parameters(Parser *p, AstScope *scope, isize *param_count_) {
 	return param_list;
 }
 
-AstNode *parse_results(Parser *p, AstScope *scope, isize *result_count_) {
-	AstNode *result_list = NULL;
-	AstNode *result_list_curr = NULL;
-	isize result_count = 0;
-
+AstNode *parse_results(Parser *p, AstScope *scope, isize *result_count) {
 	if (allow_token(p, Token_ArrowRight)) {
-		while (p->cursor[0].kind != Token_OpenBrace &&
-		       p->cursor[0].kind != Token_Semicolon) {
-			DLIST_APPEND(result_list, result_list_curr, parse_type(p));
-			result_count++;
-			if (p->cursor[0].kind != Token_Comma)
-				break;
-			next_token(p);
+		if (p->cursor[0].kind == Token_OpenParen) {
+			expect_token(p, Token_OpenParen);
+			AstNode *list = NULL;
+			AstNode *list_curr = NULL;
+			isize count = 0;
+			while (p->cursor[0].kind != Token_CloseParen &&
+			       p->cursor[0].kind != Token_EOF) {
+				DLIST_APPEND(list, list_curr, parse_type(p));
+				count++;
+				if (p->cursor[0].kind != Token_Comma)
+					break;
+				next_token(p);
+			}
+			expect_token(p, Token_CloseParen);
+
+			if (result_count) *result_count = count;
+			return list;
 		}
 
-		if (result_count == 0)
-			print_parse_error(p, p->cursor[0], "Expected return types after `->`");
+		AstNode *field = make_field(p, NULL, 0, parse_type(p));
+		if (result_count) *result_count = 1;
+		return field;
 	}
-
-	if (result_count_) *result_count_ = result_count;
-	return result_list;
+	if (result_count) *result_count = 0;
+	return NULL;
 }
 
-void parse_procedure_signature(Parser *p, AstScope *scope,
+Token parse_procedure_signature(Parser *p, AstScope *scope,
                                AstNode **param_list, isize *param_count,
                                AstNode **result_list, isize *result_count) {
+	Token proc_token = expect_token(p, Token_proc);
 	*param_list  = parse_parameters(p, scope, param_count);
 	*result_list = parse_results(p, scope, result_count);
+	return proc_token;
 }
 
 AstNode *parse_body(Parser *p, AstScope *scope) {
@@ -1327,13 +1358,6 @@ AstNode *parse_body(Parser *p, AstScope *scope) {
 	return make_block_statement(p, statement_list, statement_list_count, open, close);
 }
 
-
-AstNode *parse_tag_expression(Parser *p) {
-	Token token = expect_token(p, Token_Hash);
-	Token name  = expect_token(p, Token_Identifier);
-	return make_tag_expression(p, token, name);
-}
-
 AstNode *parse_procedure_declaration(Parser *p, Token proc_token, AstNode *name, DeclarationKind kind) {
 	AstNode *param_list = NULL;
 	AstNode *result_list = NULL;
@@ -1344,17 +1368,22 @@ AstNode *parse_procedure_declaration(Parser *p, Token proc_token, AstNode *name,
 
 	parse_procedure_signature(p, scope, &param_list, &param_count, &result_list, &result_count);
 
-	AstNode *body = NULL, *tag = NULL;
+	AstNode *body = NULL;
+	AstNode *tag_list = NULL;
+	AstNode *tag_list_curr = NULL;
+	isize tag_count = 0;
+	while (p->cursor[0].kind == Token_Hash) {
+		DLIST_APPEND(tag_list, tag_list_curr, parse_tag_expression(p, NULL));
+		tag_count++;
+	}
 	if (p->cursor[0].kind == Token_OpenBrace) {
 		body = parse_body(p, scope);
-	} else if (p->cursor[0].kind == Token_Hash) {
-		tag = parse_tag_expression(p);
 	}
 
 	close_ast_scope(p);
 
 	AstNode *proc_type = make_procedure_type(p, proc_token, param_list, param_count, result_list, result_count);
-	return make_procedure_declaration(p, kind, name, proc_type, body, tag);
+	return make_procedure_declaration(p, kind, name, proc_type, body, tag_list, tag_count);
 }
 
 AstNode *parse_declaration(Parser *p, AstNode *name_list, isize name_list_count) {
@@ -1379,16 +1408,15 @@ AstNode *parse_declaration(Parser *p, AstNode *name_list, isize name_list_count)
 			Token proc_token = p->cursor[0];
 			AstNode *name = name_list;
 			if (name_list_count != 1) {
-				print_parse_error(p, p->cursor[0], "You can only declare one procedure at a time (at the moment)");
-				return make_bad_declaration(p, name->identifier.token, p->cursor[0]);
+				print_parse_error(p, proc_token, "You can only declare one procedure at a time (at the moment)");
+				return make_bad_declaration(p, name->identifier.token, proc_token);
 			}
 
 			// TODO(bill): Allow for mutable procedures
 			if (declaration_kind != Declaration_Immutable) {
-				print_parse_error(p, p->cursor[0], "Only immutable procedures are supported (at the moment)");
-				return make_bad_declaration(p, name->identifier.token, p->cursor[0]);
+				print_parse_error(p, proc_token, "Only immutable procedures are supported (at the moment)");
+				return make_bad_declaration(p, name->identifier.token, proc_token);
 			}
-			next_token(p); // Skip `proc` token
 
 			AstNode *procedure_declaration = parse_procedure_declaration(p, proc_token, name, declaration_kind);
 			add_ast_entity(p, p->curr_scope, procedure_declaration, name_list);
@@ -1549,7 +1577,8 @@ AstNode *parse_type_declaration(Parser *p) {
 
 	AstNode *type_declaration = make_type_declaration(p, token, name, type_expression);
 
-	if (type_expression->kind != AstNode_StructType)
+	if (type_expression->kind != AstNode_StructType &&
+	    type_expression->kind != AstNode_ProcedureType)
 		expect_token(p, Token_Semicolon);
 
 	return type_declaration;
@@ -1588,8 +1617,12 @@ AstNode *parse_statement(Parser *p) {
 	// case Token_match:
 	// case Token_case:
 
+	case Token_Hash:
+		s = parse_tag_statement(p, NULL);
+		s->tag_statement.statement = parse_statement(p); // TODO(bill): Find out why this doesn't work as an argument
+		return s;
+
 	case Token_OpenBrace: return parse_block_statement(p);
-	// case Token_CloseBrace: s = make_empty_statement(p, token); break;
 
 	case Token_Semicolon:
 		s = make_empty_statement(p, token);
@@ -1607,7 +1640,6 @@ AstNode *parse_statement_list(Parser *p, isize *list_count_) {
 	isize list_count = 0;
 
 	while (p->cursor[0].kind != Token_case &&
-	       p->cursor[0].kind != Token_default &&
 	       p->cursor[0].kind != Token_CloseBrace &&
 	       p->cursor[0].kind != Token_EOF) {
 		DLIST_APPEND(list_root, list_curr, parse_statement(p));

+ 2 - 1
src/printer.cpp

@@ -24,6 +24,7 @@ void print_ast(AstNode *node, isize indent) {
 		gb_printf("(tag)\n");
 		print_indent(indent+1);
 		print_token(node->tag_expression.name);
+		print_ast(node->tag_expression.expression, indent+1);
 		break;
 
 	case AstNode_UnaryExpression:
@@ -137,7 +138,7 @@ void print_ast(AstNode *node, isize indent) {
 			gb_printf("(decl:proc,immutable)\n");
 		print_ast(node->procedure_declaration.procedure_type, indent+1);
 		print_ast(node->procedure_declaration.body, indent+1);
-		print_ast(node->procedure_declaration.tag, indent+1);
+		print_ast(node->procedure_declaration.tag_list, indent+1);
 		break;
 
 	case AstNode_TypeDeclaration:

+ 2 - 10
src/test.odin

@@ -1,15 +1,7 @@
-type Vec2: struct {
-	x, y: f32;
-}
-
-print_string_array :: proc(args: []string) {
-	args[0] = "";
-}
-
 main :: proc() {
-	x := 0;
+	x : u8 = 0;
 
-	thing :: proc(n: int) -> int, f32 {
+	thing :: proc(n: int) -> (int, f32) {
 		return n*n, 13.37;
 	}
 

+ 0 - 11
src/tokenizer.cpp

@@ -109,7 +109,6 @@ Token__KeywordBegin,
 	Token_continue,
 	Token_fallthrough,
 	Token_case,
-	Token_default,
 
 	Token_if,
 	Token_else,
@@ -122,9 +121,6 @@ Token__KeywordBegin,
 	Token_struct,
 	Token_union,
 	Token_enum,
-
-	Token_inline,
-	Token_no_inline,
 Token__KeywordEnd,
 
 	Token_Count,
@@ -200,7 +196,6 @@ char const *TOKEN_STRINGS[] = {
 	"continue",
 	"fallthrough",
 	"case",
-	"default",
 	"if",
 	"else",
 	"for",
@@ -211,9 +206,6 @@ char const *TOKEN_STRINGS[] = {
 	"struct",
 	"union",
 	"enum",
-	"inline",
-	"no_inline",
-	"import",
 "_KeywordEnd",
 };
 
@@ -621,7 +613,6 @@ Token tokenizer_get_token(Tokenizer *t) {
 			KWT("continue",    Token_continue);
 			KWT("fallthrough", Token_fallthrough);
 			KWT("case",        Token_case);
-			KWT("default",     Token_default);
 			KWT("if",          Token_if);
 			KWT("else",        Token_else);
 			KWT("for",         Token_for);
@@ -632,8 +623,6 @@ Token tokenizer_get_token(Tokenizer *t) {
 			KWT("struct",      Token_struct);
 			KWT("union",       Token_union);
 			KWT("enum",        Token_enum);
-			KWT("inline",      Token_inline);
-			KWT("no_inline",   Token_no_inline);
 			KWE
 
 		#undef KWB