bit_set constants

This commit is contained in:
gingerBill
2018-08-14 18:32:34 +01:00
parent acc010cba5
commit 966249c10a
6 changed files with 158 additions and 9 deletions

View File

@@ -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() {

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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) {