Selaa lähdekoodia

Interval expressions for match statements

Ginger Bill 8 vuotta sitten
vanhempi
commit
9a1566d665
4 muutettua tiedostoa jossa 148 lisäystä ja 53 poistoa
  1. 1 1
      src/check_expr.c
  2. 110 49
      src/check_stmt.c
  3. 18 2
      src/ir.c
  4. 19 1
      src/parser.c

+ 1 - 1
src/check_expr.c

@@ -250,7 +250,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n
 				return;
 			}
 			target_type = default_type(operand->type);
-			if (!is_type_any(type)) {
+			if (type != NULL && !is_type_any(type)) {
 				GB_ASSERT_MSG(is_type_typed(target_type), "%s", type_to_string(type));
 			}
 			add_type_info_type(c, type);

+ 110 - 49
src/check_stmt.c

@@ -915,6 +915,13 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 			token.string = str_lit("true");
 			x.expr       = ast_ident(c->curr_ast_file, token);
 		}
+		if (is_type_vector(x.type)) {
+			gbString str = type_to_string(x.type);
+			error_node(x.expr, "Invalid match expression type: %s", str);
+			gb_string_free(str);
+			break;
+		}
+
 
 		// NOTE(bill): Check for multiple defaults
 		AstNode *first_default = NULL;
@@ -957,65 +964,119 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
 
 			for_array(j, cc->list) {
 				AstNode *expr = cc->list.e[j];
-				Operand y = {0};
 
-				check_expr(c, &y, expr);
-				if (x.mode == Addressing_Invalid ||
-				    y.mode == Addressing_Invalid) {
-					continue;
-				}
+				if (expr->kind == AstNode_IntervalExpr) {
+					ast_node(ie, IntervalExpr, expr);
+					Operand lhs = {0};
+					Operand rhs = {0};
+					check_expr(c, &lhs, ie->left);
+					if (x.mode == Addressing_Invalid) {
+						continue;
+					}
+					if (lhs.mode == Addressing_Invalid) {
+						continue;
+					}
+					check_expr(c, &rhs, ie->right);
+					if (rhs.mode == Addressing_Invalid) {
+						continue;
+					}
 
-				convert_to_typed(c, &y, x.type, 0);
-				if (y.mode == Addressing_Invalid) {
-					continue;
-				}
+					if (!is_type_ordered(x.type)) {
+						gbString str = type_to_string(x.type);
+						error_node(x.expr, "Unordered type `%s`, is invalid for an interval expression", str);
+						gb_string_free(str);
+						continue;
+					}
 
-				// NOTE(bill): the ordering here matters
-				Operand z = y;
-				check_comparison(c, &z, &x, Token_CmpEq);
-				if (z.mode == Addressing_Invalid) {
-					continue;
-				}
-				if (y.mode != Addressing_Constant) {
-					continue;
-				}
 
+					TokenKind op = {0};
 
-				if (y.value.kind != ExactValue_Invalid) {
-					HashKey key = hash_exact_value(y.value);
-					TypeAndToken *found = map_type_and_token_get(&seen, key);
-					if (found != NULL) {
-						gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
-						isize count = map_type_and_token_multi_count(&seen, key);
-						TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count);
-
-						map_type_and_token_multi_get_all(&seen, key, taps);
-						bool continue_outer = false;
-
-						for (isize i = 0; i < count; i++) {
-							TypeAndToken tap = taps[i];
-							if (are_types_identical(y.type, tap.type)) {
-								TokenPos pos = tap.token.pos;
-								gbString expr_str = expr_to_string(y.expr);
-								error_node(y.expr,
-								           "Duplicate case `%s`\n"
-								           "\tprevious case at %.*s(%td:%td)",
-								           expr_str,
-								           LIT(pos.file), pos.line, pos.column);
-								gb_string_free(expr_str);
-								continue_outer = true;
-								break;
+					Operand a = lhs;
+					Operand b = rhs;
+					check_comparison(c, &a, &x, Token_LtEq);
+					if (a.mode == Addressing_Invalid) {
+						continue;
+					}
+					switch (ie->op.kind) {
+					case Token_Ellipsis:   op = Token_GtEq; break;
+					case Token_HalfClosed: op = Token_Gt;   break;
+					default: error(ie->op, "Invalid interval operator"); continue;
+					}
+
+					check_comparison(c, &b, &x, op);
+					if (b.mode == Addressing_Invalid) {
+						continue;
+					}
+
+					switch (ie->op.kind) {
+					case Token_Ellipsis:   op = Token_LtEq; break;
+					case Token_HalfClosed: op = Token_Lt;   break;
+					default: error(ie->op, "Invalid interval operator"); continue;
+					}
+
+					Operand a1 = lhs;
+					Operand b1 = rhs;
+					check_comparison(c, &a1, &b1, op);
+				} else {
+					Operand y = {0};
+					check_expr(c, &y, expr);
+					if (x.mode == Addressing_Invalid ||
+					    y.mode == Addressing_Invalid) {
+						continue;
+					}
+
+					convert_to_typed(c, &y, x.type, 0);
+					if (y.mode == Addressing_Invalid) {
+						continue;
+					}
+
+					// NOTE(bill): the ordering here matters
+					Operand z = y;
+					check_comparison(c, &z, &x, Token_CmpEq);
+					if (z.mode == Addressing_Invalid) {
+						continue;
+					}
+					if (y.mode != Addressing_Constant) {
+						continue;
+					}
+
+
+					if (y.value.kind != ExactValue_Invalid) {
+						HashKey key = hash_exact_value(y.value);
+						TypeAndToken *found = map_type_and_token_get(&seen, key);
+						if (found != NULL) {
+							gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
+							isize count = map_type_and_token_multi_count(&seen, key);
+							TypeAndToken *taps = gb_alloc_array(c->tmp_allocator, TypeAndToken, count);
+
+							map_type_and_token_multi_get_all(&seen, key, taps);
+							bool continue_outer = false;
+
+							for (isize i = 0; i < count; i++) {
+								TypeAndToken tap = taps[i];
+								if (are_types_identical(y.type, tap.type)) {
+									TokenPos pos = tap.token.pos;
+									gbString expr_str = expr_to_string(y.expr);
+									error_node(y.expr,
+									           "Duplicate case `%s`\n"
+									           "\tprevious case at %.*s(%td:%td)",
+									           expr_str,
+									           LIT(pos.file), pos.line, pos.column);
+									gb_string_free(expr_str);
+									continue_outer = true;
+									break;
+								}
 							}
-						}
 
-						gb_temp_arena_memory_end(tmp);
+							gb_temp_arena_memory_end(tmp);
 
-						if (continue_outer) {
-							continue;
+							if (continue_outer) {
+								continue;
+							}
 						}
+						TypeAndToken tap = {y.type, ast_node_token(y.expr)};
+						map_type_and_token_multi_insert(&seen, key, tap);
 					}
-					TypeAndToken tap = {y.type, ast_node_token(y.expr)};
-					map_type_and_token_multi_insert(&seen, key, tap);
 				}
 			}
 

+ 18 - 2
src/ir.c

@@ -6191,8 +6191,24 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
 			for_array(j, cc->list) {
 				AstNode *expr = cc->list.e[j];
 				next_cond = ir_new_block(proc, clause, "match.case.next");
-
-				irValue *cond = ir_emit_comp(proc, Token_CmpEq, tag, ir_build_expr(proc, expr));
+				irValue *cond = v_false;
+				if (expr->kind == AstNode_IntervalExpr) {
+					ast_node(ie, IntervalExpr, expr);
+					TokenKind op = {0};
+					switch (ie->op.kind) {
+					case Token_Ellipsis:   op = Token_LtEq; break;
+					case Token_HalfClosed: op = Token_Lt;   break;
+					default: GB_PANIC("Invalid interval operator"); break;
+					}
+					irValue *lhs = ir_build_expr(proc, ie->left);
+					irValue *rhs = ir_build_expr(proc, ie->right);
+					// TODO(bill): do short circuit here
+					irValue *cond_lhs = ir_emit_comp(proc, Token_LtEq, lhs, tag);
+					irValue *cond_rhs = ir_emit_comp(proc, op, tag, rhs);
+					cond = ir_emit_arith(proc, Token_And, cond_lhs, cond_rhs, t_bool);
+				} else {
+					cond = ir_emit_comp(proc, Token_CmpEq, tag, ir_build_expr(proc, expr));
+				}
 				ir_emit_if(proc, cond, body, next_cond);
 				ir_start_block(proc, next_cond);
 			}

+ 19 - 1
src/parser.c

@@ -3088,7 +3088,25 @@ AstNode *parse_case_clause(AstFile *f) {
 	Token token = f->curr_token;
 	AstNodeArray list = make_ast_node_array(f);
 	if (allow_token(f, Token_case)) {
-		list = parse_rhs_expr_list(f);
+		list = make_ast_node_array(f);
+		for (;;) {
+			AstNode *e = parse_expr(f, false);
+			if (f->curr_token.kind == Token_Ellipsis ||
+			    f->curr_token.kind == Token_HalfClosed) {
+				Token op = f->curr_token;
+				next_token(f);
+				AstNode *lhs = e;
+				AstNode *rhs = parse_expr(f, false);
+				e = ast_interval_expr(f, op, lhs, rhs);
+			}
+
+			array_add(&list, e);
+			if (f->curr_token.kind != Token_Comma ||
+			    f->curr_token.kind == Token_EOF) {
+			    break;
+			}
+			next_token(f);
+		}
 	} else {
 		expect_token(f, Token_default);
 	}