mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-06 07:38:48 +00:00
if expression
This commit is contained in:
+6
-3
@@ -12,8 +12,11 @@ import {
|
|||||||
}
|
}
|
||||||
|
|
||||||
proc main() {
|
proc main() {
|
||||||
var a, b, c = {
|
var cond = true;
|
||||||
give 1, 2, 123*321;
|
var msg = if cond {
|
||||||
|
give "hello";
|
||||||
|
} else {
|
||||||
|
give "goodbye";
|
||||||
};
|
};
|
||||||
fmt.println(a, b, c);
|
fmt.println(msg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -379,7 +379,8 @@ void check_open_scope(Checker *c, AstNode *node) {
|
|||||||
GB_ASSERT(node->kind == AstNode_Invalid ||
|
GB_ASSERT(node->kind == AstNode_Invalid ||
|
||||||
is_ast_node_stmt(node) ||
|
is_ast_node_stmt(node) ||
|
||||||
is_ast_node_type(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);
|
Scope *scope = make_scope(c->context.scope, c->allocator);
|
||||||
add_scope(c, node, scope);
|
add_scope(c, node, scope);
|
||||||
if (node->kind == AstNode_ProcType) {
|
if (node->kind == AstNode_ProcType) {
|
||||||
|
|||||||
+61
-2
@@ -3744,7 +3744,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
|
|||||||
|
|
||||||
case_ast_node(be, BlockExpr, node);
|
case_ast_node(be, BlockExpr, node);
|
||||||
if (c->proc_stack.count == 0) {
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3777,9 +3777,68 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
|
|||||||
o->mode = Addressing_Value;
|
o->mode = Addressing_Value;
|
||||||
case_end;
|
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);
|
case_ast_node(ye, GiveExpr, node);
|
||||||
if (c->proc_stack.count == 0) {
|
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;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -504,7 +504,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
|
|||||||
|
|
||||||
check_stmt(c, is->body, mod_flags);
|
check_stmt(c, is->body, mod_flags);
|
||||||
|
|
||||||
if (is->else_stmt) {
|
if (is->else_stmt != NULL) {
|
||||||
switch (is->else_stmt->kind) {
|
switch (is->else_stmt->kind) {
|
||||||
case AstNode_IfStmt:
|
case AstNode_IfStmt:
|
||||||
case AstNode_BlockStmt:
|
case AstNode_BlockStmt:
|
||||||
|
|||||||
+105
-23
@@ -153,6 +153,13 @@ AST_NODE_KIND(_ExprBegin, "", i32) \
|
|||||||
Token token; \
|
Token token; \
|
||||||
AstNodeArray results; \
|
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(_ExprEnd, "", i32) \
|
||||||
AST_NODE_KIND(_StmtBegin, "", i32) \
|
AST_NODE_KIND(_StmtBegin, "", i32) \
|
||||||
AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \
|
AST_NODE_KIND(BadStmt, "bad statement", struct { Token begin, end; }) \
|
||||||
@@ -445,6 +452,9 @@ Token ast_node_token(AstNode *node) {
|
|||||||
return node->BlockExpr.open;
|
return node->BlockExpr.open;
|
||||||
case AstNode_GiveExpr:
|
case AstNode_GiveExpr:
|
||||||
return node->GiveExpr.token;
|
return node->GiveExpr.token;
|
||||||
|
case AstNode_IfExpr:
|
||||||
|
return node->IfExpr.token;
|
||||||
|
|
||||||
case AstNode_BadStmt:
|
case AstNode_BadStmt:
|
||||||
return node->BadStmt.begin;
|
return node->BadStmt.begin;
|
||||||
case AstNode_EmptyStmt:
|
case AstNode_EmptyStmt:
|
||||||
@@ -758,6 +768,16 @@ AstNode *make_give_expr(AstFile *f, Token token, AstNodeArray results) {
|
|||||||
return result;
|
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) {
|
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_lhs_expr_list(AstFile *f);
|
||||||
AstNodeArray parse_rhs_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 *parse_operand(AstFile *f, bool lhs) {
|
||||||
AstNode *operand = NULL; // Operand
|
AstNode *operand = NULL; // Operand
|
||||||
@@ -1657,16 +1760,8 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
|
|||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Token_OpenBrace: {
|
case Token_OpenBrace: return parse_block_expr(f);
|
||||||
AstNodeArray stmts = {0};
|
case Token_if: return parse_if_expr(f);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
AstNode *type = parse_identifier_or_type(f);
|
AstNode *type = parse_identifier_or_type(f);
|
||||||
@@ -2233,19 +2328,6 @@ AstNode *parse_block_stmt(AstFile *f, b32 is_when) {
|
|||||||
return parse_body(f);
|
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_type_info (ssaProcedure *proc, Type *type);
|
||||||
ssaValue *ssa_build_expr (ssaProcedure *proc, AstNode *expr);
|
ssaValue *ssa_build_expr (ssaProcedure *proc, AstNode *expr);
|
||||||
void ssa_build_stmt (ssaProcedure *proc, AstNode *node);
|
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);
|
void ssa_build_defer_stmt (ssaProcedure *proc, ssaDefer d);
|
||||||
ssaAddr ssa_build_addr (ssaProcedure *proc, AstNode *expr);
|
ssaAddr ssa_build_addr (ssaProcedure *proc, AstNode *expr);
|
||||||
void ssa_build_proc (ssaValue *value, ssaProcedure *parent);
|
void ssa_build_proc (ssaValue *value, ssaProcedure *parent);
|
||||||
@@ -2645,6 +2645,43 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
|
|||||||
return value;
|
return value;
|
||||||
case_end;
|
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);
|
case_ast_node(ge, GiveExpr, expr);
|
||||||
ssa_emit_comment(proc, str_lit("GiveExpr"));
|
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);
|
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) {
|
switch (cond->kind) {
|
||||||
case_ast_node(pe, ParenExpr, cond);
|
case_ast_node(pe, ParenExpr, cond);
|
||||||
ssa_build_cond(proc, pe->expr, true_block, false_block);
|
return ssa_build_cond(proc, pe->expr, true_block, false_block);
|
||||||
return;
|
|
||||||
case_end;
|
case_end;
|
||||||
|
|
||||||
case_ast_node(ue, UnaryExpr, cond);
|
case_ast_node(ue, UnaryExpr, cond);
|
||||||
if (ue->op.kind == Token_Not) {
|
if (ue->op.kind == Token_Not) {
|
||||||
ssa_build_cond(proc, ue->expr, false_block, true_block);
|
return ssa_build_cond(proc, ue->expr, false_block, true_block);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
case_end;
|
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");
|
ssaBlock *block = ssa_add_block(proc, NULL, "cmp.and");
|
||||||
ssa_build_cond(proc, be->left, block, false_block);
|
ssa_build_cond(proc, be->left, block, false_block);
|
||||||
proc->curr_block = block;
|
proc->curr_block = block;
|
||||||
ssa_build_cond(proc, be->right, true_block, false_block);
|
return ssa_build_cond(proc, be->right, true_block, false_block);
|
||||||
return;
|
|
||||||
} else if (be->op.kind == Token_CmpOr) {
|
} else if (be->op.kind == Token_CmpOr) {
|
||||||
ssaBlock *block = ssa_add_block(proc, NULL, "cmp.or");
|
ssaBlock *block = ssa_add_block(proc, NULL, "cmp.or");
|
||||||
ssa_build_cond(proc, be->left, true_block, block);
|
ssa_build_cond(proc, be->left, true_block, block);
|
||||||
proc->curr_block = block;
|
proc->curr_block = block;
|
||||||
ssa_build_cond(proc, be->right, true_block, false_block);
|
return ssa_build_cond(proc, be->right, true_block, false_block);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
case_end;
|
case_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssaValue *expr = ssa_build_expr(proc, cond);
|
ssaValue *v = ssa_build_expr(proc, cond);
|
||||||
expr = ssa_emit_conv(proc, expr, t_bool);
|
v = ssa_emit_conv(proc, v, t_bool);
|
||||||
ssa_emit_if(proc, expr, true_block, false_block);
|
ssa_emit_if(proc, v, true_block, false_block);
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user