diff options
| author | Ginger Bill <bill@gingerbill.org> | 2016-12-30 16:21:45 +0000 |
|---|---|---|
| committer | Ginger Bill <bill@gingerbill.org> | 2016-12-30 16:21:45 +0000 |
| commit | 2ecafda1d34c1157268c94d40c507b98f0070a35 (patch) | |
| tree | c3aca07a8e40dfbf905eaa09653ccdc654674c11 | |
| parent | 23d32f34e526cfb657a72e5b2dab86d1df765f0f (diff) | |
if expression
| -rw-r--r-- | code/demo.odin | 9 | ||||
| -rw-r--r-- | src/checker/checker.c | 3 | ||||
| -rw-r--r-- | src/checker/expr.c | 63 | ||||
| -rw-r--r-- | src/checker/stmt.c | 2 | ||||
| -rw-r--r-- | src/parser.c | 128 | ||||
| -rw-r--r-- | src/ssa.c | 60 |
6 files changed, 222 insertions, 43 deletions
diff --git a/code/demo.odin b/code/demo.odin index bf484ba2a..e79e29a3c 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -12,8 +12,11 @@ import { } proc main() { - var a, b, c = { - give 1, 2, 123*321; + var cond = true; + var msg = if cond { + give "hello"; + } else { + give "goodbye"; }; - fmt.println(a, b, c); + fmt.println(msg); } diff --git a/src/checker/checker.c b/src/checker/checker.c index c61884253..c125049ac 100644 --- a/src/checker/checker.c +++ b/src/checker/checker.c @@ -379,7 +379,8 @@ void check_open_scope(Checker *c, AstNode *node) { GB_ASSERT(node->kind == AstNode_Invalid || is_ast_node_stmt(node) || is_ast_node_type(node) || - node->kind == AstNode_BlockExpr); + node->kind == AstNode_BlockExpr || + node->kind == AstNode_IfExpr ); Scope *scope = make_scope(c->context.scope, c->allocator); add_scope(c, node, scope); if (node->kind == AstNode_ProcType) { diff --git a/src/checker/expr.c b/src/checker/expr.c index 390979cbb..3033a3ab8 100644 --- a/src/checker/expr.c +++ b/src/checker/expr.c @@ -3744,7 +3744,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint case_ast_node(be, BlockExpr, node); if (c->proc_stack.count == 0) { - error_node(node, "A block expression is only allowed withing a procedure"); + error_node(node, "A block expression is only allowed within a procedure"); goto error; } @@ -3777,9 +3777,68 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint o->mode = Addressing_Value; case_end; + case_ast_node(ie, IfExpr, node); + if (c->proc_stack.count == 0) { + error_node(node, "An if expression is only allowed within a procedure"); + goto error; + } + + check_open_scope(c, node); + + if (ie->init != NULL) { + check_stmt(c, ie->init, 0); + } + + Operand operand = {Addressing_Invalid}; + check_expr(c, &operand, ie->cond); + if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { + error_node(ie->cond, "Non-boolean condition in if expression"); + } + + Type *if_type = NULL; + Type *else_type = NULL; + check_expr(c, &operand, ie->body); + if_type = operand.type; + + if (ie->else_expr != NULL) { + switch (ie->else_expr->kind) { + case AstNode_IfExpr: + case AstNode_BlockExpr: + check_expr(c, &operand, ie->else_expr); + else_type = operand.type; + break; + default: + error_node(ie->else_expr, "Invalid else expression in if expression"); + break; + } + } else { + error_node(ie->else_expr, "An if expression must have an else expression"); + check_close_scope(c); + goto error; + } + + check_close_scope(c); + + GB_ASSERT(if_type != NULL && + else_type != NULL); + + if (!are_types_identical(if_type, else_type)) { + gbString its = type_to_string(if_type); + gbString ets = type_to_string(else_type); + error_node(node, "if expression type and else expression type do not match, %s != %s", its, ets); + gb_string_free(ets); + gb_string_free(its); + goto error; + } + + + o->type = if_type; + o->mode = Addressing_Value; + case_end; + case_ast_node(ye, GiveExpr, node); if (c->proc_stack.count == 0) { - error_node(node, "A give expression is only allowed withing a procedure"); + error_node(node, "A give expression is only allowed within a procedure"); goto error; } diff --git a/src/checker/stmt.c b/src/checker/stmt.c index fd10fab31..83ccf69e8 100644 --- a/src/checker/stmt.c +++ b/src/checker/stmt.c @@ -504,7 +504,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { check_stmt(c, is->body, mod_flags); - if (is->else_stmt) { + if (is->else_stmt != NULL) { switch (is->else_stmt->kind) { case AstNode_IfStmt: case AstNode_BlockStmt: diff --git a/src/parser.c b/src/parser.c index 09a7d1ae1..75e2c7d90 100644 --- a/src/parser.c +++ b/src/parser.c @@ -153,6 +153,13 @@ AST_NODE_KIND(_ExprBegin, "", i32) \ Token token; \ AstNodeArray results; \ }) \ + AST_NODE_KIND(IfExpr, "if expression", struct { \ + Token token; \ + AstNode *init; \ + AstNode *cond; \ + AstNode *body; \ + AstNode *else_expr; \ + }) \ AST_NODE_KIND(_ExprEnd, "", i32) \ AST_NODE_KIND(_StmtBegin, "", i32) \ AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \ @@ -445,6 +452,9 @@ Token ast_node_token(AstNode *node) { return node->BlockExpr.open; case AstNode_GiveExpr: return node->GiveExpr.token; + case AstNode_IfExpr: + return node->IfExpr.token; + case AstNode_BadStmt: return node->BadStmt.begin; case AstNode_EmptyStmt: @@ -758,6 +768,16 @@ AstNode *make_give_expr(AstFile *f, Token token, AstNodeArray results) { return result; } +AstNode *make_if_expr(AstFile *f, Token token, AstNode *init, AstNode *cond, AstNode *body, AstNode *else_expr) { + AstNode *result = make_node(f, AstNode_IfExpr); + result->IfExpr.token = token; + result->IfExpr.init = init; + result->IfExpr.cond = cond; + result->IfExpr.body = body; + result->IfExpr.else_expr = else_expr; + return result; +} + AstNode *make_bad_stmt(AstFile *f, Token begin, Token end) { @@ -1541,6 +1561,89 @@ void parse_proc_tags(AstFile *f, u64 *tags, String *foreign_name, String *link_n AstNodeArray parse_lhs_expr_list(AstFile *f); AstNodeArray parse_rhs_expr_list(AstFile *f); +AstNode * parse_simple_stmt (AstFile *f); + +AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) { + if (statement == NULL) { + return NULL; + } + + if (statement->kind == AstNode_ExprStmt) { + return statement->ExprStmt.expr; + } + + syntax_error(f->curr_token, "Expected `%.*s`, found a simple statement.", LIT(kind)); + return make_bad_expr(f, f->curr_token, f->tokens.e[f->curr_token_index+1]); +} + + + +AstNode *parse_block_expr(AstFile *f) { + AstNodeArray stmts = {0}; + Token open, close; + open = expect_token(f, Token_OpenBrace); + f->expr_level++; + stmts = parse_stmt_list(f); + f->expr_level--; + close = expect_token(f, Token_CloseBrace); + return make_block_expr(f, stmts, open, close); +} + +AstNode *parse_if_expr(AstFile *f) { + if (f->curr_proc == NULL) { + syntax_error(f->curr_token, "You cannot use an if expression in the file scope"); + return make_bad_stmt(f, f->curr_token, f->curr_token); + } + + Token token = expect_token(f, Token_if); + AstNode *init = NULL; + AstNode *cond = NULL; + AstNode *body = NULL; + AstNode *else_expr = NULL; + + isize prev_level = f->expr_level; + f->expr_level = -1; + + if (allow_token(f, Token_Semicolon)) { + cond = parse_expr(f, false); + } else { + init = parse_simple_stmt(f); + if (allow_token(f, Token_Semicolon)) { + cond = parse_expr(f, false); + } else { + cond = convert_stmt_to_expr(f, init, str_lit("boolean expression")); + init = NULL; + } + } + + f->expr_level = prev_level; + + if (cond == NULL) { + syntax_error(f->curr_token, "Expected condition for if statement"); + } + + body = parse_block_expr(f); + + if (allow_token(f, Token_else)) { + switch (f->curr_token.kind) { + case Token_if: + else_expr = parse_if_expr(f); + break; + case Token_OpenBrace: + else_expr = parse_block_expr(f); + break; + default: + syntax_error(f->curr_token, "Expected if expression block statement"); + else_expr = make_bad_expr(f, f->curr_token, f->tokens.e[f->curr_token_index+1]); + break; + } + } else { + syntax_error(f->curr_token, "An if expression must have an else clause"); + return make_bad_stmt(f, f->curr_token, f->tokens.e[f->curr_token_index+1]); + } + + return make_if_expr(f, token, init, cond, body, else_expr); +} AstNode *parse_operand(AstFile *f, bool lhs) { AstNode *operand = NULL; // Operand @@ -1657,16 +1760,8 @@ AstNode *parse_operand(AstFile *f, bool lhs) { return type; } - case Token_OpenBrace: { - AstNodeArray stmts = {0}; - Token open, close; - open = expect_token(f, Token_OpenBrace); - f->expr_level++; - stmts = parse_stmt_list(f); - f->expr_level--; - close = expect_token(f, Token_CloseBrace); - return make_block_expr(f, stmts, open, close); - } + case Token_OpenBrace: return parse_block_expr(f); + case Token_if: return parse_if_expr(f); default: { AstNode *type = parse_identifier_or_type(f); @@ -2233,19 +2328,6 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) { return parse_body(f); } -AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) { - if (statement == NULL) { - return NULL; - } - - if (statement->kind == AstNode_ExprStmt) { - return statement->ExprStmt.expr; - } - - syntax_error(f->curr_token, "Expected `%.*s`, found a simple statement.", LIT(kind)); - return make_bad_expr(f, f->curr_token, f->tokens.e[f->curr_token_index+1]); -} - @@ -719,7 +719,7 @@ ssaValue *ssa_emit_conv (ssaProcedure *proc, ssaValue *value, Type *t) ssaValue *ssa_type_info (ssaProcedure *proc, Type *type); ssaValue *ssa_build_expr (ssaProcedure *proc, AstNode *expr); void ssa_build_stmt (ssaProcedure *proc, AstNode *node); -void ssa_build_cond (ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block); +ssaValue *ssa_build_cond (ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block); void ssa_build_defer_stmt (ssaProcedure *proc, ssaDefer d); ssaAddr ssa_build_addr (ssaProcedure *proc, AstNode *expr); void ssa_build_proc (ssaValue *value, ssaProcedure *parent); @@ -2645,6 +2645,43 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue return value; case_end; + case_ast_node(ie, IfExpr, expr); + ssa_emit_comment(proc, str_lit("IfExpr")); + if (ie->init != NULL) { + ssaBlock *init = ssa_add_block(proc, expr, "if.init"); + ssa_emit_jump(proc, init); + proc->curr_block = init; + ssa_build_stmt(proc, ie->init); + } + + GB_ASSERT(ie->else_expr != NULL); + ssaBlock *then = ssa_add_block(proc, expr, "if.then"); + ssaBlock *done = ssa_add_block(proc, expr, "if.done"); // NOTE(bill): Append later + ssaBlock *else_ = ssa_add_block(proc, ie->else_expr, "if.else"); + + ssaValue *cond = ssa_build_cond(proc, ie->cond, then, else_); + proc->curr_block = then; + + ssaValue *iv = NULL; + ssaValue *ev = NULL; + + ssa_open_scope(proc); + iv = ssa_build_expr(proc, ie->body); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + + ssa_emit_jump(proc, done); + proc->curr_block = else_; + + ssa_open_scope(proc); + ev = ssa_build_expr(proc, ie->else_expr); + ssa_close_scope(proc, ssaDeferExit_Default, NULL); + + ssa_emit_jump(proc, done); + proc->curr_block = done; + + return ssa_emit_select(proc, cond, iv, ev); + case_end; + case_ast_node(ge, GiveExpr, expr); ssa_emit_comment(proc, str_lit("GiveExpr")); @@ -3782,17 +3819,15 @@ void ssa_build_assign_op(ssaProcedure *proc, ssaAddr lhs, ssaValue *value, Token ssa_addr_store(proc, lhs, new_value); } -void ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block) { +ssaValue *ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssaBlock *false_block) { switch (cond->kind) { case_ast_node(pe, ParenExpr, cond); - ssa_build_cond(proc, pe->expr, true_block, false_block); - return; + return ssa_build_cond(proc, pe->expr, true_block, false_block); case_end; case_ast_node(ue, UnaryExpr, cond); if (ue->op.kind == Token_Not) { - ssa_build_cond(proc, ue->expr, false_block, true_block); - return; + return ssa_build_cond(proc, ue->expr, false_block, true_block); } case_end; @@ -3801,21 +3836,20 @@ void ssa_build_cond(ssaProcedure *proc, AstNode *cond, ssaBlock *true_block, ssa ssaBlock *block = ssa_add_block(proc, NULL, "cmp.and"); ssa_build_cond(proc, be->left, block, false_block); proc->curr_block = block; - ssa_build_cond(proc, be->right, true_block, false_block); - return; + return ssa_build_cond(proc, be->right, true_block, false_block); } else if (be->op.kind == Token_CmpOr) { ssaBlock *block = ssa_add_block(proc, NULL, "cmp.or"); ssa_build_cond(proc, be->left, true_block, block); proc->curr_block = block; - ssa_build_cond(proc, be->right, true_block, false_block); - return; + return ssa_build_cond(proc, be->right, true_block, false_block); } case_end; } - ssaValue *expr = ssa_build_expr(proc, cond); - expr = ssa_emit_conv(proc, expr, t_bool); - ssa_emit_if(proc, expr, true_block, false_block); + ssaValue *v = ssa_build_expr(proc, cond); + v = ssa_emit_conv(proc, v, t_bool); + ssa_emit_if(proc, v, true_block, false_block); + return v; } |