From 222941727f2b094449838135c3157120e0176e58 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 28 May 2019 12:45:20 +0100 Subject: [PATCH] Add `..<` operator for ranges; Add extra checking for bit set assignments --- examples/demo/demo.odin | 6 ++++-- src/check_decl.cpp | 2 +- src/check_expr.cpp | 11 +++++++++++ src/check_stmt.cpp | 22 ++++++++++++++++------ src/check_type.cpp | 14 ++++++++++++-- src/ir.cpp | 6 ++++-- src/parser.cpp | 27 ++++++++++++++++++--------- src/tokenizer.cpp | 5 +++++ 8 files changed, 71 insertions(+), 22 deletions(-) diff --git a/examples/demo/demo.odin b/examples/demo/demo.odin index dd7590d01..206fa4a2d 100644 --- a/examples/demo/demo.odin +++ b/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..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) { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 928f3f063..274ed9fc2 100644 --- a/src/check_expr.cpp +++ b/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; diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 88f1574e8..2ad804139 100644 --- a/src/check_stmt.cpp +++ b/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); diff --git a/src/check_type.cpp b/src/check_type.cpp index e7eafad17..790cec789 100644 --- a/src/check_type.cpp +++ b/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; diff --git a/src/ir.cpp b/src/ir.cpp index 533414e50..2f2da1826 100644 --- a/src/ir.cpp +++ b/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); diff --git a/src/parser.cpp b/src/parser.cpp index 0d70f4a5c..a72bd8803 100644 --- a/src/parser.cpp +++ b/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; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index e496165a0..dd9aa109c 100644 --- a/src/tokenizer.cpp +++ b/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 {