From e92fdb4a99bf9d27009dd35fdd074ff14facfc03 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Thu, 5 Mar 2020 20:34:30 +0000 Subject: `x if cond else y` and `x when cond else y` expressions --- src/check_expr.cpp | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/check_type.cpp | 20 ++++++++++ src/ir.cpp | 43 +++++++++++++++++++++ src/parser.cpp | 53 ++++++++++++++++++++++++++ src/parser.hpp | 6 ++- 5 files changed, 229 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 1a01eef31..d73aebbe4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7762,6 +7762,99 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type case_end; + case_ast_node(te, TernaryIfExpr, node); + Operand cond = {Addressing_Invalid}; + check_expr(c, &cond, te->cond); + node->viral_state_flags |= te->cond->viral_state_flags; + + if (cond.mode != Addressing_Invalid && !is_type_boolean(cond.type)) { + error(te->cond, "Non-boolean condition in ternary if expression"); + } + + Operand x = {Addressing_Invalid}; + Operand y = {Addressing_Invalid}; + check_expr_or_type(c, &x, te->x, type_hint); + node->viral_state_flags |= te->x->viral_state_flags; + + if (te->y != nullptr) { + check_expr_or_type(c, &y, te->y, type_hint); + node->viral_state_flags |= te->y->viral_state_flags; + } else { + error(node, "A ternary expression must have an else clause"); + return kind; + } + + if (x.type == nullptr || x.type == t_invalid || + y.type == nullptr || y.type == t_invalid) { + return kind; + } + + convert_to_typed(c, &x, y.type); + if (x.mode == Addressing_Invalid) { + return kind; + } + convert_to_typed(c, &y, x.type); + if (y.mode == Addressing_Invalid) { + x.mode = Addressing_Invalid; + return kind; + } + + if (!ternary_compare_types(x.type, y.type)) { + gbString its = type_to_string(x.type); + gbString ets = type_to_string(y.type); + error(node, "Mismatched types in ternary if expression, %s vs %s", its, ets); + gb_string_free(ets); + gb_string_free(its); + return kind; + } + + Type *type = x.type; + if (is_type_untyped_nil(type) || is_type_untyped_undef(type)) { + type = y.type; + } + + o->type = type; + o->mode = Addressing_Value; + + // if (cond.mode == Addressing_Constant && is_type_boolean(cond.type) && + // x.mode == Addressing_Constant && + // y.mode == Addressing_Constant) { + + // o->mode = Addressing_Constant; + + // if (cond.value.value_bool) { + // o->value = x.value; + // } else { + // o->value = y.value; + // } + // } + + case_end; + + case_ast_node(te, TernaryWhenExpr, node); + Operand cond = {}; + check_expr(c, &cond, te->cond); + node->viral_state_flags |= te->cond->viral_state_flags; + + if (cond.mode != Addressing_Constant || !is_type_boolean(cond.type)) { + error(te->cond, "Expected a constant boolean condition in ternary when expression"); + return kind; + } + + if (cond.value.value_bool) { + check_expr_or_type(c, o, te->x, type_hint); + node->viral_state_flags |= te->x->viral_state_flags; + } else { + if (te->y != nullptr) { + check_expr_or_type(c, o, te->y, type_hint); + node->viral_state_flags |= te->y->viral_state_flags; + } else { + error(node, "A ternary when expression must have an else clause"); + return kind; + } + } + case_end; + case_ast_node(cl, CompoundLit, node); Type *type = type_hint; bool is_to_be_determined_array_count = false; @@ -9333,6 +9426,22 @@ gbString write_expr_to_string(gbString str, Ast *node) { str = write_expr_to_string(str, te->y); case_end; + case_ast_node(te, TernaryIfExpr, node); + str = write_expr_to_string(str, te->x); + str = gb_string_appendc(str, " if "); + str = write_expr_to_string(str, te->cond); + str = gb_string_appendc(str, " else "); + str = write_expr_to_string(str, te->y); + case_end; + + case_ast_node(te, TernaryWhenExpr, node); + str = write_expr_to_string(str, te->x); + str = gb_string_appendc(str, " when "); + str = write_expr_to_string(str, te->cond); + str = gb_string_appendc(str, " else "); + str = write_expr_to_string(str, te->y); + case_end; + case_ast_node(pe, ParenExpr, node); str = gb_string_append_rune(str, '('); diff --git a/src/check_type.cpp b/src/check_type.cpp index 6194951c9..010b31f03 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -3454,6 +3454,26 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t return true; } case_end; + + case_ast_node(te, TernaryIfExpr, e); + Operand o = {}; + check_expr_or_type(ctx, &o, e); + if (o.mode == Addressing_Type) { + *type = o.type; + set_base_type(named_type, *type); + return true; + } + case_end; + + case_ast_node(te, TernaryWhenExpr, e); + Operand o = {}; + check_expr_or_type(ctx, &o, e); + if (o.mode == Addressing_Type) { + *type = o.type; + set_base_type(named_type, *type); + return true; + } + case_end; } *type = t_invalid; diff --git a/src/ir.cpp b/src/ir.cpp index 6a56eb387..ece960e2b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7189,6 +7189,49 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { return ir_emit(proc, ir_instr_phi(proc, edges, type)); case_end; + case_ast_node(te, TernaryIfExpr, expr); + ir_emit_comment(proc, str_lit("TernaryIfExpr")); + + auto edges = array_make(ir_allocator(), 0, 2); + + GB_ASSERT(te->y != nullptr); + irBlock *then = ir_new_block(proc, nullptr, "if.then"); + irBlock *done = ir_new_block(proc, nullptr, "if.done"); // NOTE(bill): Append later + irBlock *else_ = ir_new_block(proc, nullptr, "if.else"); + + irValue *cond = ir_build_cond(proc, te->cond, then, else_); + ir_start_block(proc, then); + + Type *type = type_of_expr(expr); + + ir_open_scope(proc); + array_add(&edges, ir_emit_conv(proc, ir_build_expr(proc, te->x), type)); + ir_close_scope(proc, irDeferExit_Default, nullptr); + + ir_emit_jump(proc, done); + ir_start_block(proc, else_); + + ir_open_scope(proc); + array_add(&edges, ir_emit_conv(proc, ir_build_expr(proc, te->y), type)); + ir_close_scope(proc, irDeferExit_Default, nullptr); + + ir_emit_jump(proc, done); + ir_start_block(proc, done); + + return ir_emit(proc, ir_instr_phi(proc, edges, type)); + case_end; + + case_ast_node(te, TernaryWhenExpr, expr); + TypeAndValue tav = type_and_value_of_expr(te->cond); + GB_ASSERT(tav.mode == Addressing_Constant); + GB_ASSERT(tav.value.kind == ExactValue_Bool); + if (tav.value.value_bool) { + return ir_build_expr(proc, te->x); + } else { + return ir_build_expr(proc, te->y); + } + case_end; + case_ast_node(ta, TypeAssertion, expr); TokenPos pos = ast_token(expr).pos; Type *type = tv.type; diff --git a/src/parser.cpp b/src/parser.cpp index f89b5676b..83ae9743f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -35,6 +35,8 @@ Token ast_token(Ast *node) { case Ast_FieldValue: return node->FieldValue.eq; case Ast_DerefExpr: return node->DerefExpr.op; case Ast_TernaryExpr: return ast_token(node->TernaryExpr.cond); + case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x); + case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x); case Ast_TypeAssertion: return ast_token(node->TypeAssertion.expr); case Ast_TypeCast: return node->TypeCast.token; case Ast_AutoCast: return node->AutoCast.token; @@ -198,6 +200,16 @@ Ast *clone_ast(Ast *node) { n->TernaryExpr.x = clone_ast(n->TernaryExpr.x); n->TernaryExpr.y = clone_ast(n->TernaryExpr.y); break; + case Ast_TernaryIfExpr: + n->TernaryIfExpr.x = clone_ast(n->TernaryIfExpr.x); + n->TernaryIfExpr.cond = clone_ast(n->TernaryIfExpr.cond); + n->TernaryIfExpr.y = clone_ast(n->TernaryIfExpr.y); + break; + case Ast_TernaryWhenExpr: + n->TernaryWhenExpr.x = clone_ast(n->TernaryWhenExpr.x); + n->TernaryWhenExpr.cond = clone_ast(n->TernaryWhenExpr.cond); + n->TernaryWhenExpr.y = clone_ast(n->TernaryWhenExpr.y); + break; case Ast_TypeAssertion: n->TypeAssertion.expr = clone_ast(n->TypeAssertion.expr); n->TypeAssertion.type = clone_ast(n->TypeAssertion.type); @@ -638,6 +650,21 @@ Ast *ast_ternary_expr(AstFile *f, Ast *cond, Ast *x, Ast *y) { result->TernaryExpr.y = y; return result; } +Ast *ast_ternary_if_expr(AstFile *f, Ast *x, Ast *cond, Ast *y) { + Ast *result = alloc_ast_node(f, Ast_TernaryIfExpr); + result->TernaryIfExpr.x = x; + result->TernaryIfExpr.cond = cond; + result->TernaryIfExpr.y = y; + return result; +} +Ast *ast_ternary_when_expr(AstFile *f, Ast *x, Ast *cond, Ast *y) { + Ast *result = alloc_ast_node(f, Ast_TernaryWhenExpr); + result->TernaryWhenExpr.x = x; + result->TernaryWhenExpr.cond = cond; + result->TernaryWhenExpr.y = y; + return result; +} + Ast *ast_type_assertion(AstFile *f, Ast *expr, Token dot, Ast *type) { Ast *result = alloc_ast_node(f, Ast_TypeAssertion); result->TypeAssertion.expr = expr; @@ -1199,6 +1226,8 @@ Token expect_operator(AstFile *f) { Token prev = f->curr_token; if ((prev.kind == Token_in || prev.kind == Token_not_in) && (f->expr_level >= 0 || f->allow_in_expr)) { // okay + } else if (prev.kind == Token_if || prev.kind == Token_when) { + // 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])); @@ -2512,6 +2541,8 @@ bool is_ast_range(Ast *expr) { i32 token_precedence(AstFile *f, TokenKind t) { switch (t) { case Token_Question: + case Token_if: + case Token_when: return 1; case Token_Ellipsis: case Token_RangeHalf: @@ -2565,6 +2596,14 @@ Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { // NOTE(bill): This will also catch operators that are not valid "binary" operators break; } + if (op.kind == Token_if || op.kind == Token_when) { + Token prev = f->prev_token; + if (prev.pos.line < op.pos.line) { + // NOTE(bill): Check to see if the `if` or `when` is on the same line of the `lhs` condition + break; + } + } + expect_operator(f); // NOTE(bill): error checks too if (op.kind == Token_Question) { @@ -2574,6 +2613,20 @@ Ast *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) { Token token_c = expect_token(f, Token_Colon); Ast *y = parse_expr(f, lhs); expr = ast_ternary_expr(f, cond, x, y); + } else if (op.kind == Token_if) { + Ast *x = expr; + // Token_if + Ast *cond = parse_expr(f, lhs); + Token tok_else = expect_token(f, Token_else); + Ast *y = parse_expr(f, lhs); + expr = ast_ternary_if_expr(f, x, cond, y); + } else if (op.kind == Token_when) { + Ast *x = expr; + // Token_when + Ast *cond = parse_expr(f, lhs); + Token tok_else = expect_token(f, Token_else); + Ast *y = parse_expr(f, lhs); + expr = ast_ternary_when_expr(f, x, cond, y); } else { Ast *right = parse_binary_expr(f, false, prec+1); if (right == nullptr) { diff --git a/src/parser.hpp b/src/parser.hpp index 6426cc96b..3de848ca6 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -282,8 +282,10 @@ AST_KIND(_ExprBegin, "", bool) \ Token ellipsis; \ ProcInlining inlining; \ }) \ - AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ - AST_KIND(TernaryExpr, "ternary expression", struct { Ast *cond, *x, *y; }) \ + AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ + AST_KIND(TernaryExpr, "ternary expression", struct { Ast *cond, *x, *y; }) \ + AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \ + AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \ AST_KIND(TypeAssertion, "type assertion", struct { Ast *expr; Token dot; Ast *type; }) \ AST_KIND(TypeCast, "type cast", struct { Token token; Ast *type, *expr; }) \ AST_KIND(AutoCast, "auto_cast", struct { Token token; Ast *expr; }) \ -- cgit v1.2.3