Browse Source

`notin` operator

gingerBill 6 years ago
parent
commit
b504d6e12a
4 changed files with 41 additions and 9 deletions
  1. 16 3
      src/check_expr.cpp
  2. 22 5
      src/ir.cpp
  3. 2 1
      src/parser.cpp
  4. 1 0
      src/tokenizer.cpp

+ 16 - 3
src/check_expr.cpp

@@ -2123,6 +2123,7 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as
 	}
 
 	case Token_in:
+	case Token_notin:
 		check_expr(c, x, be->left);
 		check_expr(c, y, be->right);
 		if (x->mode == Addressing_Invalid) {
@@ -2136,13 +2137,21 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as
 
 		if (is_type_map(y->type)) {
 			Type *yt = base_type(y->type);
-			check_assignment(c, x, yt->Map.key, str_lit("map 'in'"));
+			if (op.kind == Token_in) {
+				check_assignment(c, x, yt->Map.key, str_lit("map 'in'"));
+			} else {
+				check_assignment(c, x, yt->Map.key, str_lit("map 'notin'"));
+			}
 
 			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.elem, str_lit("bit_set 'in'"));
+			if (op.kind == Token_in) {
+				check_assignment(c, x, yt->BitSet.elem, str_lit("bit_set 'in'"));
+			} else {
+				check_assignment(c, x, yt->BitSet.elem, str_lit("bit_set 'notin'"));
+			}
 			if (x->mode == Addressing_Constant && y->mode == Addressing_Constant) {
 				ExactValue k = exact_value_to_integer(x->value);
 				ExactValue v = exact_value_to_integer(y->value);
@@ -2158,7 +2167,11 @@ void check_binary_expr(CheckerContext *c, Operand *x, Ast *node, bool use_lhs_as
 
 					x->mode = Addressing_Constant;
 					x->type = t_untyped_bool;
-					x->value = exact_value_bool((bit & bits) != 0);
+					if (op.kind == Token_in) {
+						x->value = exact_value_bool((bit & bits) != 0);
+					} else {
+						x->value = exact_value_bool((bit & bits) == 0);
+					}
 					x->expr = node;
 					return;
 				} else {

+ 22 - 5
src/ir.cpp

@@ -6296,13 +6296,18 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
 			return ir_emit_logical_binary_expr(proc, expr);
 
 
-		case Token_in: {
+		case Token_in:
+		case Token_notin: {
 			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"));
+					if (be->op.kind == Token_in) {
+						ir_emit_comment(proc, str_lit("map in"));
+					} else {
+						ir_emit_comment(proc, str_lit("map notin"));
+					}
 
 					irValue *addr = ir_address_from_load_or_generate_local(proc, right);
 					irValue *h = ir_gen_map_header(proc, addr, rt);
@@ -6313,12 +6318,20 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
 					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);
+					if (be->op.kind == Token_in) {
+						return ir_emit_conv(proc, ir_emit_comp(proc, Token_NotEq, ptr, v_raw_nil), t_bool);
+					} else {
+						return ir_emit_conv(proc, ir_emit_comp(proc, Token_CmpEq, ptr, v_raw_nil), t_bool);
+					}
 				}
 				break;
 			case Type_BitSet:
 				{
-					ir_emit_comment(proc, str_lit("bit_set in"));
+					if (be->op.kind == Token_in) {
+						ir_emit_comment(proc, str_lit("bit_set in"));
+					} else {
+						ir_emit_comment(proc, str_lit("bit_set notin"));
+					}
 
 					Type *key_type = rt->BitSet.elem;
 					GB_ASSERT(are_types_identical(ir_type(left), key_type));
@@ -6333,7 +6346,11 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
 					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);
+					if (be->op.kind == Token_in) {
+						return ir_emit_conv(proc, ir_emit_comp(proc, Token_NotEq, new_value, v_zero), t_bool);
+					} else {
+						return ir_emit_conv(proc, ir_emit_comp(proc, Token_CmpEq, new_value, v_zero), t_bool);
+					}
 				}
 				break;
 			default:

+ 2 - 1
src/parser.cpp

@@ -1158,7 +1158,7 @@ Token expect_token_after(AstFile *f, TokenKind kind, char *msg) {
 
 Token expect_operator(AstFile *f) {
 	Token prev = f->curr_token;
-	if (prev.kind == Token_in && (f->expr_level >= 0 || f->allow_in_expr)) {
+	if ((prev.kind == Token_in || prev.kind == Token_notin) && (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'",
@@ -2355,6 +2355,7 @@ i32 token_precedence(AstFile *f, TokenKind t) {
 	case Token_GtEq:
 		return 5;
 	case Token_in:
+	case Token_notin:
 		if (f->expr_level >= 0 || f->allow_in_expr) {
 			return 6;
 		}

+ 1 - 0
src/tokenizer.cpp

@@ -92,6 +92,7 @@ TOKEN_KIND(Token__KeywordBegin, ""), \
 	TOKEN_KIND(Token_for,         "for"),         \
 	TOKEN_KIND(Token_switch,      "switch"),      \
 	TOKEN_KIND(Token_in,          "in"),          \
+	TOKEN_KIND(Token_notin,       "notin"),       \
 	TOKEN_KIND(Token_do,          "do"),          \
 	TOKEN_KIND(Token_case,        "case"),        \
 	TOKEN_KIND(Token_break,       "break"),       \