Browse Source

Add `..<` operator for ranges; Add extra checking for bit set assignments

gingerBill 6 years ago
parent
commit
222941727f
8 changed files with 71 additions and 22 deletions
  1. 4 2
      examples/demo/demo.odin
  2. 1 1
      src/check_decl.cpp
  3. 11 0
      src/check_expr.cpp
  4. 16 6
      src/check_stmt.cpp
  5. 12 2
      src/check_type.cpp
  6. 4 2
      src/ir.cpp
  7. 18 9
      src/parser.cpp
  8. 5 0
      src/tokenizer.cpp

+ 4 - 2
examples/demo/demo.odin

@@ -60,8 +60,10 @@ general_stuff :: proc() {
 
 	{
 		// .. open range
+		// ..< half-closed range
 
 		for in 0..2  {} // 0, 1, 2
+		for in 0..<2 {} // 0, 1
 	}
 
 	{ // Multiple sized booleans
@@ -480,7 +482,7 @@ parametric_polymorphism :: proc() {
 
 		get_hash :: proc(s: string) -> u32 { // fnv32a
 			h: u32 = 0x811c9dc5;
-			for i in 0..len(s)-1 {
+			for i in 0..<len(s) {
 				h = (h ~ u32(s[i])) * 0x01000193;
 			}
 			return h;
@@ -530,7 +532,7 @@ parametric_polymorphism :: proc() {
 			// `T` is the type passed
 			fmt.printf("Generating an array of type %v from the value %v of type %v\n",
 					   typeid_of(type_of(res)), N, typeid_of(I));
-			for i in 0..N-1 {
+			for i in 0..<N {
 				res[i] = T(i*i);
 			}
 			return;

+ 1 - 1
src/check_decl.cpp

@@ -1057,7 +1057,7 @@ void check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *decl, Type *ty
 				continue;
 			}
 			bool is_immutable = e->Variable.is_immutable;
-			bool is_value     = (e->flags & EntityFlag_Value) != 0;
+			bool is_value     = (e->flags & EntityFlag_Value) != 0 && !is_type_pointer(e->type);
 			String name = e->token.string;
 			Type *t = base_type(type_deref(e->type));
 			if (t->kind == Type_Struct) {

+ 11 - 0
src/check_expr.cpp

@@ -6383,6 +6383,17 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
 					}
 
 					check_assignment(c, o, t->BitSet.elem, str_lit("bit_set literal"));
+					if (o->mode == Addressing_Constant) {
+						i64 lower = t->BitSet.lower;
+						i64 upper = t->BitSet.upper;
+						i64 v = exact_value_to_i64(o->value);
+						if (lower <= v && v <= upper) {
+							// okay
+						} else {
+							error(elem, "Bit field value out of bounds, %lld not in the range %lld .. %lld", v, lower, upper);
+							continue;
+						}
+					}
 				}
 			}
 			break;

+ 16 - 6
src/check_stmt.cpp

@@ -693,17 +693,17 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 			Ast *expr = unparen_expr(cc->list[j]);
 
 			if (is_ast_range(expr)) {
-				ast_node(ie, BinaryExpr, expr);
+				ast_node(be, BinaryExpr, expr);
 				Operand lhs = {};
 				Operand rhs = {};
-				check_expr_with_type_hint(ctx, &lhs, ie->left, x.type);
+				check_expr_with_type_hint(ctx, &lhs, be->left, x.type);
 				if (x.mode == Addressing_Invalid) {
 					continue;
 				}
 				if (lhs.mode == Addressing_Invalid) {
 					continue;
 				}
-				check_expr_with_type_hint(ctx, &rhs, ie->right, x.type);
+				check_expr_with_type_hint(ctx, &rhs, be->right, x.type);
 				if (rhs.mode == Addressing_Invalid) {
 					continue;
 				}
@@ -715,6 +715,13 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 					continue;
 				}
 
+				TokenKind upper_op = Token_Invalid;
+				switch (be->op.kind) {
+				case Token_Ellipsis:  upper_op = Token_GtEq; break;
+				case Token_RangeHalf: upper_op = Token_Gt;   break;
+				default: GB_PANIC("Invalid range operator"); break;
+				}
+
 
 				Operand a = lhs;
 				Operand b = rhs;
@@ -723,7 +730,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 					continue;
 				}
 
-				check_comparison(ctx, &b, &x, Token_GtEq);
+				check_comparison(ctx, &b, &x, upper_op);
 				if (b.mode == Addressing_Invalid) {
 					continue;
 				}
@@ -736,7 +743,9 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
 				}
 
 				add_constant_switch_case(ctx, &seen, lhs);
-				add_constant_switch_case(ctx, &seen, rhs);
+				if (upper_op == Token_GtEq) {
+					add_constant_switch_case(ctx, &seen, rhs);
+				}
 			} else {
 				Operand y = {};
 				if (is_type_typeid(x.type)) {
@@ -1380,7 +1389,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
 
 				TokenKind op = Token_Lt;
 				switch (ie->op.kind) {
-				case Token_Ellipsis: op = Token_LtEq; break;
+				case Token_Ellipsis:  op = Token_LtEq; break;
+				case Token_RangeHalf: op = Token_Lt; break;
 				default: error(ie->op, "Invalid range operator"); break;
 				}
 				bool ok = compare_exact_values(op, a, b);

+ 12 - 2
src/check_type.cpp

@@ -1044,8 +1044,18 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
 			bits = 8*type_size_of(type->BitSet.underlying);
 		}
 
-		if (upper - lower >= bits) {
-			error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower+1));
+		switch (be->op.kind) {
+		case Token_Ellipsis:
+			if (upper - lower >= bits) {
+				error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower+1));
+			}
+			break;
+		case Token_RangeHalf:
+			if (upper - lower > bits) {
+				error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower));
+			}
+			upper -= 1;
+			break;
 		}
 		type->BitSet.elem  = t;
 		type->BitSet.lower = lower;

+ 4 - 2
src/ir.cpp

@@ -8156,7 +8156,8 @@ void ir_build_range_interval(irProcedure *proc, AstBinaryExpr *node, Type *val_t
 
 	TokenKind op = Token_Lt;
 	switch (node->op.kind) {
-	case Token_Ellipsis: op = Token_LtEq;   break;
+	case Token_Ellipsis:  op = Token_LtEq; break;
+	case Token_RangeHalf: op = Token_Lt;  break;
 	default: GB_PANIC("Invalid interval operator"); break;
 	}
 
@@ -8832,7 +8833,8 @@ void ir_build_stmt_internal(irProcedure *proc, Ast *node) {
 					ast_node(ie, BinaryExpr, expr);
 					TokenKind op = Token_Invalid;
 					switch (ie->op.kind) {
-					case Token_Ellipsis: op = Token_LtEq; break;
+					case Token_Ellipsis:  op = Token_LtEq; break;
+					case Token_RangeHalf: op = Token_Lt;   break;
 					default: GB_PANIC("Invalid interval operator"); break;
 					}
 					irValue *lhs = ir_build_expr(proc, ie->left);

+ 18 - 9
src/parser.cpp

@@ -1146,6 +1146,19 @@ Token expect_token_after(AstFile *f, TokenKind kind, char *msg) {
 }
 
 
+bool is_token_range(TokenKind kind) {
+	switch (kind) {
+	case Token_Ellipsis:
+	case Token_RangeHalf:
+		return true;
+	}
+	return false;
+}
+bool is_token_range(Token tok) {
+	return is_token_range(tok.kind);
+}
+
+
 Token expect_operator(AstFile *f) {
 	Token prev = f->curr_token;
 	if ((prev.kind == Token_in || prev.kind == Token_notin) && (f->expr_level >= 0 || f->allow_in_expr)) {
@@ -1153,7 +1166,7 @@ Token expect_operator(AstFile *f) {
 	} 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)) {
+	} else if (!f->allow_range && is_token_range(prev)) {
 		syntax_error(f->curr_token, "Expected an non-range operator, got '%.*s'",
 		             LIT(token_strings[prev.kind]));
 	}
@@ -2131,9 +2144,9 @@ Ast *parse_call_expr(AstFile *f, Ast *operand) {
 		}
 
 		bool prefix_ellipsis = false;
-		if (f->curr_token.kind == Token_Ellipsis) {
+		if (is_token_range(f->curr_token)) {
 			prefix_ellipsis = true;
-			ellipsis = expect_token(f, Token_Ellipsis);
+			ellipsis = expect_token(f, f->curr_token.kind);
 		}
 
 		Ast *arg = parse_expr(f, false);
@@ -2320,12 +2333,7 @@ bool is_ast_range(Ast *expr) {
 	if (expr->kind != Ast_BinaryExpr) {
 		return false;
 	}
-	TokenKind op = expr->BinaryExpr.op.kind;
-	switch (op) {
-	case Token_Ellipsis:
-		return true;
-	}
-	return false;
+	return is_token_range(expr->BinaryExpr.op.kind);
 }
 
 // NOTE(bill): result == priority
@@ -2334,6 +2342,7 @@ i32 token_precedence(AstFile *f, TokenKind t) {
 	case Token_Question:
 		return 1;
 	case Token_Ellipsis:
+	case Token_RangeHalf:
 		if (!f->allow_range) {
 			return 0;
 		}

+ 5 - 0
src/tokenizer.cpp

@@ -76,6 +76,7 @@ TOKEN_KIND(Token__ComparisonEnd, ""), \
 	TOKEN_KIND(Token_Period,        "."),   \
 	TOKEN_KIND(Token_Comma,         ","),   \
 	TOKEN_KIND(Token_Ellipsis,      ".."),  \
+	TOKEN_KIND(Token_RangeHalf,     "..<"), \
 	TOKEN_KIND(Token_BackSlash,     "\\"),  \
 TOKEN_KIND(Token__OperatorEnd, ""), \
 \
@@ -985,6 +986,10 @@ Token tokenizer_get_token(Tokenizer *t) {
 			if (t->curr_rune == '.') { // Could be an ellipsis
 				advance_to_next_rune(t);
 				token.kind = Token_Ellipsis;
+				if (t->curr_rune == '<') {
+					advance_to_next_rune(t);
+					token.kind = Token_RangeHalf;
+				}
 			} else if ('0' <= t->curr_rune && t->curr_rune <= '9') {
 				token = scan_number_to_token(t, true);
 			} else {