Browse Source

if expression

Ginger Bill 8 years ago
parent
commit
2ecafda1d3
6 changed files with 222 additions and 43 deletions
  1. 6 3
      code/demo.odin
  2. 2 1
      src/checker/checker.c
  3. 61 2
      src/checker/expr.c
  4. 1 1
      src/checker/stmt.c
  5. 105 23
      src/parser.c
  6. 47 13
      src/ssa.c

+ 6 - 3
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);
 }

+ 2 - 1
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) {

+ 61 - 2
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;
 		}
 

+ 1 - 1
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:

+ 105 - 23
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]);
-}
-
 
 
 

+ 47 - 13
src/ssa.c

@@ -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;
 }