Browse Source

`bit_set` constants

gingerBill 7 years ago
parent
commit
966249c10a
6 changed files with 158 additions and 9 deletions
  1. 11 3
      examples/demo/demo.odin
  2. 72 2
      src/check_expr.cpp
  3. 54 0
      src/ir.cpp
  4. 16 3
      src/parser.cpp
  5. 2 1
      src/parser.hpp
  6. 3 0
      src/types.cpp

+ 11 - 3
examples/demo/demo.odin

@@ -724,11 +724,19 @@ bit_set_type :: proc() {
 	}
 
 	Days :: distinct bit_set[Day];
+	WEEKEND :: Days{Sunday, Saturday};
+
 	d: Days;
-	d = Days{Sunday};
+	d = Days{Sunday} | Days{Monday};
 	x := Tuesday;
-	d |= Days{Saturday, x};
-	fmt.println(d);
+	e := d | WEEKEND;
+	fmt.println(d, e);
+
+	ok := Saturday in e; // `in` is only allowed for `map` and `bit_set` types
+	fmt.println(ok);
+	if Saturday in e {
+		fmt.println("Saturday in", e);
+	}
 }
 
 main :: proc() {

+ 72 - 2
src/check_expr.cpp

@@ -503,6 +503,10 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
 		}
 	}
 
+	if (is_type_bit_set(dst) && are_types_identical(dst->BitSet.base_type, operand->type)) {
+		return 3;
+	}
+
 #if 0
 	if (are_types_identical(dst, src) && (!is_type_named(dst) || !is_type_named(src))) {
 		return 1;
@@ -1345,6 +1349,10 @@ bool check_representable_as_constant(CheckerContext *c, ExactValue in_value, Typ
 			// return true;
 		}
 		if (out_value) *out_value = in_value;
+	} else if (is_type_bit_set(type)) {
+		if (in_value.kind == ExactValue_Integer) {
+			return true;
+		}
 	}
 
 
@@ -2019,6 +2027,42 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node) {
 		break;
 	}
 
+	case Token_in:
+		check_expr(c, x, be->left);
+		check_expr(c, y, be->right);
+		if (x->mode == Addressing_Invalid) {
+			return;
+		}
+		if (y->mode == Addressing_Invalid) {
+			x->mode = Addressing_Invalid;
+			x->expr = y->expr;
+			return;
+		}
+
+		if (is_type_map(y->type)) {
+			Type *yt = base_type(y->type);
+			check_assignment(c, x, yt->Map.key, str_lit("map 'in'"));
+
+			add_package_dependency(c, "runtime", "__dynamic_map_get");
+		} else if (is_type_bit_set(y->type)) {
+			Type *yt = base_type(y->type);
+			check_assignment(c, x, yt->BitSet.base_type, str_lit("bit_set 'in'"));
+		} else {
+			gbString t = type_to_string(y->type);
+			error(x->expr, "expected either a map or bitset for 'in', got %s", t);
+			gb_string_free(t);
+			x->expr = node;
+			x->mode = Addressing_Invalid;
+			return;
+		}
+		if (x->mode != Addressing_Invalid) {
+			x->mode = Addressing_Value;
+			x->type = t_untyped_bool;
+		}
+		x->expr = node;
+
+		return;
+
 	default:
 		check_expr(c, x, be->left);
 		check_expr(c, y, be->right);
@@ -2151,7 +2195,8 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node) {
 		ExactValue a = x->value;
 		ExactValue b = y->value;
 
-		Type *type = base_type(x->type);
+		// Type *type = base_type(x->type);
+		Type *type = x->type;
 		if (is_type_pointer(type)) {
 			GB_ASSERT(op.kind == Token_Sub);
 			i64 bytes = a.value_pointer - b.value_pointer;
@@ -5422,6 +5467,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 
 			if (cl->elems[0]->kind == Ast_FieldValue) {
 				error(cl->elems[0], "'field = value' in a bit_set a literal is not allowed");
+				is_constant = false;
 			} else {
 				for_array(index, cl->elems) {
 					Entity *field = nullptr;
@@ -5457,7 +5503,31 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 
 		if (is_constant) {
 			o->mode = Addressing_Constant;
-			o->value = exact_value_compound(node);
+
+			if (is_type_bit_set(type)) {
+				// NOTE(bill): Encode as an integer
+
+				i64 lower = base_type(type)->BitSet.min;
+
+				u64 bits = 0;
+				for_array(index, cl->elems) {
+					Entity *field = nullptr;
+					Ast *elem = cl->elems[index];
+					GB_ASSERT(elem->kind != Ast_FieldValue);
+					TypeAndValue tav = elem->tav;
+					ExactValue i = exact_value_to_integer(tav.value);
+					if (i.kind != ExactValue_Integer) {
+						continue;
+					}
+					i64 val = big_int_to_i64(&i.value_integer);
+					val -= lower;
+					u64 bit = u64(1ll<<val);
+					bits |= bit;
+				}
+				o->value = exact_value_u64(bits);
+			} else {
+				o->value = exact_value_compound(node);
+			}
 		} else {
 			o->mode = Addressing_Value;
 		}

+ 54 - 0
src/ir.cpp

@@ -2355,6 +2355,11 @@ irValue *ir_emit_unary_arith(irProcedure *proc, TokenKind op, irValue *x, Type *
 
 	}
 
+	if (op == Token_Not) {
+		irValue *cmp = ir_emit_comp(proc, Token_CmpEq, x, v_false);
+		return ir_emit_conv(proc, cmp, type);
+	}
+
 	return ir_emit(proc, ir_instr_unary_op(proc, op, x, type));
 }
 
@@ -4880,6 +4885,55 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
 		case Token_CmpOr:
 			return ir_emit_logical_binary_expr(proc, expr);
 
+
+		case Token_in: {
+			irValue *right = ir_build_expr(proc, be->right);
+			Type *rt = base_type(ir_type(right));
+			switch (rt->kind) {
+			case Type_Map:
+				{
+					ir_emit_comment(proc, str_lit("map in"));
+
+					irValue *addr = ir_address_from_load_or_generate_local(proc, right);
+					irValue *h = ir_gen_map_header(proc, addr, rt);
+					irValue *key = ir_gen_map_key(proc, left, rt->Map.key);
+
+					auto args = array_make<irValue *>(ir_allocator(), 2);
+					args[0] = h;
+					args[1] = key;
+
+					irValue *ptr = ir_emit_runtime_call(proc, "__dynamic_map_get", args);
+					return ir_emit_conv(proc, ir_emit_comp(proc, Token_NotEq, ptr, v_raw_nil), t_bool);
+				}
+				break;
+			case Type_BitSet:
+				{
+					ir_emit_comment(proc, str_lit("bit_set in"));
+
+					Type *key_type = rt->BitSet.base_type;
+					GB_ASSERT(are_types_identical(ir_type(left), key_type));
+
+					Type *it = bit_set_to_int(rt);
+					left = ir_emit_conv(proc, left, it);
+
+					irValue *lower = ir_value_constant(it, exact_value_i64(rt->BitSet.min));
+					irValue *key = ir_emit_arith(proc, Token_Sub, left, lower, it);
+					irValue *bit = ir_emit_arith(proc, Token_Shl, v_one, key, it);
+
+
+					irValue *old_value = ir_emit_bitcast(proc, right, it);
+					irValue *new_value = ir_emit_arith(proc, Token_And, old_value, bit, it);
+
+					return ir_emit_conv(proc, ir_emit_comp(proc, Token_NotEq, new_value, v_zero), t_bool);
+				}
+				break;
+			default:
+				GB_PANIC("Invalid 'in' type");
+			}
+			break;
+
+		}
+
 		default:
 			GB_PANIC("Invalid binary expression");
 			break;

+ 16 - 3
src/parser.cpp

@@ -1137,7 +1137,9 @@ Token expect_token_after(AstFile *f, TokenKind kind, char *msg) {
 
 Token expect_operator(AstFile *f) {
 	Token prev = f->curr_token;
-	if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
+	if (prev.kind == Token_in && (f->expr_level >= 0 || f->allow_in_expr)) {
+		// okay
+	} else if (!gb_is_between(prev.kind, Token__OperatorBegin+1, Token__OperatorEnd-1)) {
 		syntax_error(f->curr_token, "Expected an operator, got '%.*s'",
 		             LIT(token_strings[prev.kind]));
 	} else if (!f->allow_range && (prev.kind == Token_Ellipsis)) {
@@ -2242,11 +2244,16 @@ i32 token_precedence(AstFile *f, TokenKind t) {
 	case Token_LtEq:
 	case Token_GtEq:
 		return 5;
+	case Token_in:
+		if (f->expr_level >= 0 || f->allow_in_expr) {
+			return 6;
+		}
+		return 0;
 	case Token_Add:
 	case Token_Sub:
 	case Token_Or:
 	case Token_Xor:
-		return 6;
+		return 7;
 	case Token_Mul:
 	case Token_Quo:
 	case Token_Mod:
@@ -2255,7 +2262,7 @@ i32 token_precedence(AstFile *f, TokenKind t) {
 	case Token_AndNot:
 	case Token_Shl:
 	case Token_Shr:
-		return 7;
+		return 8;
 	}
 	return 0;
 }
@@ -3088,6 +3095,8 @@ Ast *parse_if_stmt(AstFile *f) {
 
 	isize prev_level = f->expr_level;
 	f->expr_level = -1;
+	bool prev_allow_in_expr = f->allow_in_expr;
+	f->allow_in_expr = true;
 
 	if (allow_token(f, Token_Semicolon)) {
 		cond = parse_expr(f, false);
@@ -3102,6 +3111,7 @@ Ast *parse_if_stmt(AstFile *f) {
 	}
 
 	f->expr_level = prev_level;
+	f->allow_in_expr = prev_allow_in_expr;
 
 	if (cond == nullptr) {
 		syntax_error(f->curr_token, "Expected condition for if statement");
@@ -3320,11 +3330,14 @@ Ast *parse_case_clause(AstFile *f, bool is_type) {
 	Array<Ast *> list = {};
 	expect_token(f, Token_case);
 	bool prev_allow_range = f->allow_range;
+	bool prev_allow_in_expr = f->allow_in_expr;
 	f->allow_range = !is_type;
+	f->allow_in_expr = !is_type;
 	if (f->curr_token.kind != Token_Colon) {
 		list = parse_rhs_expr_list(f);
 	}
 	f->allow_range = prev_allow_range;
+	f->allow_in_expr = prev_allow_in_expr;
 	expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax?
 	Array<Ast *> stmts = parse_stmt_list(f);
 

+ 2 - 1
src/parser.hpp

@@ -89,7 +89,8 @@ struct AstFile {
 	// <  0: In Control Clause
 	// NOTE(bill): Used to prevent type literals in control clauses
 	isize        expr_level;
-	bool         allow_range; // NOTE(bill): Ranges are only allowed in certain cases
+	bool         allow_range;   // NOTE(bill): Ranges are only allowed in certain cases
+	bool         allow_in_expr; // NOTE(bill): in expression are only allowed in certain cases
 	bool         in_foreign_block;
 	bool         allow_type;
 	isize        when_level;

+ 3 - 0
src/types.cpp

@@ -757,6 +757,9 @@ bool is_type_constant_type(Type *t) {
 	if (t->kind == Type_Basic) {
 		return (t->Basic.flags & BasicFlag_ConstantType) != 0;
 	}
+	if (t->kind == Type_BitSet) {
+		return true;
+	}
 	return false;
 }
 bool is_type_float(Type *t) {