Browse Source

Make the parser support as many identifiers on the LHS in `for in` loops to improve error messages

gingerBill 4 years ago
parent
commit
0e3ecc350a
5 changed files with 45 additions and 53 deletions
  1. 18 9
      src/check_stmt.cpp
  2. 10 10
      src/ir.cpp
  3. 10 10
      src/llvm_backend.cpp
  4. 6 22
      src/parser.cpp
  5. 1 2
      src/parser.hpp

+ 18 - 9
src/check_stmt.cpp

@@ -1666,7 +1666,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 
 
 		Ast *expr = unparen_expr(rs->expr);
 		Ast *expr = unparen_expr(rs->expr);
 
 
-
+		isize max_val_count = 2;
 		if (is_ast_range(expr)) {
 		if (is_ast_range(expr)) {
 			ast_node(ie, BinaryExpr, expr);
 			ast_node(ie, BinaryExpr, expr);
 			Operand x = {};
 			Operand x = {};
@@ -1739,9 +1739,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 					break;
 					break;
 
 
 				case Type_Tuple:
 				case Type_Tuple:
-					if (false) {
-						check_not_tuple(ctx, &operand);
-					} else {
+					{
 						isize count = t->Tuple.variables.count;
 						isize count = t->Tuple.variables.count;
 						if (count < 1 || count > 3) {
 						if (count < 1 || count > 3) {
 							check_not_tuple(ctx, &operand);
 							check_not_tuple(ctx, &operand);
@@ -1759,14 +1757,14 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 						if (count > 1) val0 = t->Tuple.variables[0]->type;
 						if (count > 1) val0 = t->Tuple.variables[0]->type;
 						if (count > 2) val1 = t->Tuple.variables[1]->type;
 						if (count > 2) val1 = t->Tuple.variables[1]->type;
 
 
-						if (rs->val1 != nullptr && count < 3) {
+						if (rs->vals.count > 1 && rs->vals[1] != nullptr && count < 3) {
 							gbString s = type_to_string(t);
 							gbString s = type_to_string(t);
 							error(operand.expr, "Expected a 3-value tuple on the rhs, got (%s)", s);
 							error(operand.expr, "Expected a 3-value tuple on the rhs, got (%s)", s);
 							gb_string_free(s);
 							gb_string_free(s);
 							break;
 							break;
 						}
 						}
 
 
-						if (rs->val0 != nullptr && count < 2) {
+						if (rs->vals.count > 0 && rs->vals[0] != nullptr && count < 2) {
 							gbString s = type_to_string(t);
 							gbString s = type_to_string(t);
 							error(operand.expr, "Expected at least a 2-values tuple on the rhs, got (%s)", s);
 							error(operand.expr, "Expected at least a 2-values tuple on the rhs, got (%s)", s);
 							gb_string_free(s);
 							gb_string_free(s);
@@ -1776,6 +1774,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 					}
 					}
 					break;
 					break;
 
 
+				case Type_Struct:
+					if (t->Struct.soa_kind != StructSoa_None) {
+						error(operand.expr, "#soa structures do not yet support for in loop iteration");
+					}
+					break;
 				}
 				}
 			}
 			}
 
 
@@ -1787,9 +1790,9 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 
 
 				error(operand.expr, "Cannot iterate over '%s' of type '%s'", s, t);
 				error(operand.expr, "Cannot iterate over '%s' of type '%s'", s, t);
 
 
-				if (rs->val0 != nullptr && rs->val1 == nullptr) {
+				if (rs->vals.count == 1) {
 					if (is_type_map(operand.type) || is_type_bit_set(operand.type)) {
 					if (is_type_map(operand.type) || is_type_bit_set(operand.type)) {
-						gbString v = expr_to_string(rs->val0);
+						gbString v = expr_to_string(rs->vals[0]);
 						defer (gb_string_free(v));
 						defer (gb_string_free(v));
 						error_line("\tSuggestion: place parentheses around the expression\n");
 						error_line("\tSuggestion: place parentheses around the expression\n");
 						error_line("\t            for (%s in %s) {\n", v, s);
 						error_line("\t            for (%s in %s) {\n", v, s);
@@ -1800,8 +1803,14 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 
 
 		skip_expr_range_stmt:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird.
 		skip_expr_range_stmt:; // NOTE(zhiayang): again, declaring a variable immediately after a label... weird.
 
 
-		Ast * lhs[2] = {rs->val0, rs->val1};
+		if (rs->vals.count > max_val_count) {
+			error(rs->vals[max_val_count], "Expected a maximum of %td identifier%s, got %td", max_val_count, max_val_count == 1 ? "" : "s", rs->vals.count);
+		}
+
+		Ast * lhs[2] = {};
 		Type *rhs[2] = {val0, val1};
 		Type *rhs[2] = {val0, val1};
+		if (rs->vals.count > 1) { lhs[1] = rs->vals[1]; }
+		if (rs->vals.count > 0) { lhs[0] = rs->vals[0]; }
 
 
 		for (isize i = 0; i < 2; i++) {
 		for (isize i = 0; i < 2; i++) {
 			if (lhs[i] == nullptr) {
 			if (lhs[i] == nullptr) {

+ 10 - 10
src/ir.cpp

@@ -10735,18 +10735,18 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
 
 
 		Type *val0_type = nullptr;
 		Type *val0_type = nullptr;
 		Type *val1_type = nullptr;
 		Type *val1_type = nullptr;
-		if (rs->val0 != nullptr && !is_blank_ident(rs->val0)) {
-			val0_type = type_of_expr(rs->val0);
+		if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) {
+			val0_type = type_of_expr(rs->vals[0]);
 		}
 		}
-		if (rs->val1 != nullptr && !is_blank_ident(rs->val1)) {
-			val1_type = type_of_expr(rs->val1);
+		if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) {
+			val1_type = type_of_expr(rs->vals[1]);
 		}
 		}
 
 
 		if (val0_type != nullptr) {
 		if (val0_type != nullptr) {
-			ir_add_local_for_identifier(proc, rs->val0, true);
+			ir_add_local_for_identifier(proc, rs->vals[0], true);
 		}
 		}
 		if (val1_type != nullptr) {
 		if (val1_type != nullptr) {
-			ir_add_local_for_identifier(proc, rs->val1, true);
+			ir_add_local_for_identifier(proc, rs->vals[1], true);
 		}
 		}
 
 
 		irValue *val = nullptr;
 		irValue *val = nullptr;
@@ -10851,11 +10851,11 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
 
 
 
 
 		if (is_map) {
 		if (is_map) {
-			if (val0_type) ir_store_range_stmt_val(proc, rs->val0, key);
-			if (val1_type) ir_store_range_stmt_val(proc, rs->val1, val);
+			if (val0_type) ir_store_range_stmt_val(proc, rs->vals[0], key);
+			if (val1_type) ir_store_range_stmt_val(proc, rs->vals[1], val);
 		} else {
 		} else {
-			if (val0_type) ir_store_range_stmt_val(proc, rs->val0, val);
-			if (val1_type) ir_store_range_stmt_val(proc, rs->val1, key);
+			if (val0_type) ir_store_range_stmt_val(proc, rs->vals[0], val);
+			if (val1_type) ir_store_range_stmt_val(proc, rs->vals[1], key);
 		}
 		}
 
 
 		ir_push_target_list(proc, rs->label, done, loop, nullptr);
 		ir_push_target_list(proc, rs->label, done, loop, nullptr);

+ 10 - 10
src/llvm_backend.cpp

@@ -3889,19 +3889,19 @@ void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
 
 
 	Type *val0_type = nullptr;
 	Type *val0_type = nullptr;
 	Type *val1_type = nullptr;
 	Type *val1_type = nullptr;
-	if (rs->val0 != nullptr && !is_blank_ident(rs->val0)) {
-		val0_type = type_of_expr(rs->val0);
+	if (rs->vals.count > 0 && rs->vals[0] != nullptr && !is_blank_ident(rs->vals[0])) {
+		val0_type = type_of_expr(rs->vals[0]);
 	}
 	}
-	if (rs->val1 != nullptr && !is_blank_ident(rs->val1)) {
-		val1_type = type_of_expr(rs->val1);
+	if (rs->vals.count > 1 && rs->vals[1] != nullptr && !is_blank_ident(rs->vals[1])) {
+		val1_type = type_of_expr(rs->vals[1]);
 	}
 	}
 
 
 	if (val0_type != nullptr) {
 	if (val0_type != nullptr) {
-		Entity *e = entity_of_node(rs->val0);
+		Entity *e = entity_of_node(rs->vals[0]);
 		lb_add_local(p, e->type, e, true);
 		lb_add_local(p, e->type, e, true);
 	}
 	}
 	if (val1_type != nullptr) {
 	if (val1_type != nullptr) {
-		Entity *e = entity_of_node(rs->val1);
+		Entity *e = entity_of_node(rs->vals[1]);
 		lb_add_local(p, e->type, e, true);
 		lb_add_local(p, e->type, e, true);
 	}
 	}
 
 
@@ -4002,11 +4002,11 @@ void lb_build_range_stmt(lbProcedure *p, AstRangeStmt *rs, Scope *scope) {
 
 
 
 
 	if (is_map) {
 	if (is_map) {
-		if (val0_type) lb_store_range_stmt_val(p, rs->val0, key);
-		if (val1_type) lb_store_range_stmt_val(p, rs->val1, val);
+		if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], key);
+		if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], val);
 	} else {
 	} else {
-		if (val0_type) lb_store_range_stmt_val(p, rs->val0, val);
-		if (val1_type) lb_store_range_stmt_val(p, rs->val1, key);
+		if (val0_type) lb_store_range_stmt_val(p, rs->vals[0], val);
+		if (val1_type) lb_store_range_stmt_val(p, rs->vals[1], key);
 	}
 	}
 
 
 	lb_push_target_list(p, rs->label, done, loop, nullptr);
 	lb_push_target_list(p, rs->label, done, loop, nullptr);

+ 6 - 22
src/parser.cpp

@@ -315,8 +315,7 @@ Ast *clone_ast(Ast *node) {
 		break;
 		break;
 	case Ast_RangeStmt:
 	case Ast_RangeStmt:
 		n->RangeStmt.label = clone_ast(n->RangeStmt.label);
 		n->RangeStmt.label = clone_ast(n->RangeStmt.label);
-		n->RangeStmt.val0  = clone_ast(n->RangeStmt.val0);
-		n->RangeStmt.val1  = clone_ast(n->RangeStmt.val1);
+		n->RangeStmt.vals  = clone_ast_array(n->RangeStmt.vals);
 		n->RangeStmt.expr  = clone_ast(n->RangeStmt.expr);
 		n->RangeStmt.expr  = clone_ast(n->RangeStmt.expr);
 		n->RangeStmt.body  = clone_ast(n->RangeStmt.body);
 		n->RangeStmt.body  = clone_ast(n->RangeStmt.body);
 		break;
 		break;
@@ -842,11 +841,10 @@ Ast *ast_for_stmt(AstFile *f, Token token, Ast *init, Ast *cond, Ast *post, Ast
 	return result;
 	return result;
 }
 }
 
 
-Ast *ast_range_stmt(AstFile *f, Token token, Ast *val0, Ast *val1, Token in_token, Ast *expr, Ast *body) {
+Ast *ast_range_stmt(AstFile *f, Token token, Slice<Ast *> vals, Token in_token, Ast *expr, Ast *body) {
 	Ast *result = alloc_ast_node(f, Ast_RangeStmt);
 	Ast *result = alloc_ast_node(f, Ast_RangeStmt);
 	result->RangeStmt.token = token;
 	result->RangeStmt.token = token;
-	result->RangeStmt.val0 = val0;
-	result->RangeStmt.val1 = val1;
+	result->RangeStmt.vals = vals;
 	result->RangeStmt.in_token = in_token;
 	result->RangeStmt.in_token = in_token;
 	result->RangeStmt.expr  = expr;
 	result->RangeStmt.expr  = expr;
 	result->RangeStmt.body  = body;
 	result->RangeStmt.body  = body;
@@ -3914,7 +3912,7 @@ Ast *parse_for_stmt(AstFile *f) {
 			} else {
 			} else {
 				body = parse_block_stmt(f, false);
 				body = parse_block_stmt(f, false);
 			}
 			}
-			return ast_range_stmt(f, token, nullptr, nullptr, in_token, rhs, body);
+			return ast_range_stmt(f, token, {}, in_token, rhs, body);
 		}
 		}
 
 
 		if (f->curr_token.kind != Token_Semicolon) {
 		if (f->curr_token.kind != Token_Semicolon) {
@@ -3954,26 +3952,12 @@ Ast *parse_for_stmt(AstFile *f) {
 	if (is_range) {
 	if (is_range) {
 		GB_ASSERT(cond->kind == Ast_AssignStmt);
 		GB_ASSERT(cond->kind == Ast_AssignStmt);
 		Token in_token = cond->AssignStmt.op;
 		Token in_token = cond->AssignStmt.op;
-		Ast *value = nullptr;
-		Ast *index = nullptr;
-		switch (cond->AssignStmt.lhs.count) {
-		case 1:
-			value = cond->AssignStmt.lhs[0];
-			break;
-		case 2:
-			value = cond->AssignStmt.lhs[0];
-			index = cond->AssignStmt.lhs[1];
-			break;
-		default:
-			syntax_error(cond, "Expected either 1 or 2 identifiers");
-			return ast_bad_stmt(f, token, f->curr_token);
-		}
-
+		Slice<Ast *> vals = cond->AssignStmt.lhs;
 		Ast *rhs = nullptr;
 		Ast *rhs = nullptr;
 		if (cond->AssignStmt.rhs.count > 0) {
 		if (cond->AssignStmt.rhs.count > 0) {
 			rhs = cond->AssignStmt.rhs[0];
 			rhs = cond->AssignStmt.rhs[0];
 		}
 		}
-		return ast_range_stmt(f, token, value, index, in_token, rhs, body);
+		return ast_range_stmt(f, token, vals, in_token, rhs, body);
 	}
 	}
 
 
 	cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression"));
 	cond = convert_stmt_to_expr(f, cond, str_lit("boolean expression"));

+ 1 - 2
src/parser.hpp

@@ -407,8 +407,7 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
 	AST_KIND(RangeStmt, "range statement", struct { \
 	AST_KIND(RangeStmt, "range statement", struct { \
 		Token token; \
 		Token token; \
 		Ast *label; \
 		Ast *label; \
-		Ast *val0; \
-		Ast *val1; \
+		Slice<Ast *> vals; \
 		Token in_token; \
 		Token in_token; \
 		Ast *expr; \
 		Ast *expr; \
 		Ast *body; \
 		Ast *body; \