Support for if ... then ... else following #911. (#944)

This commit is contained in:
Richard Smith
2022-03-03 16:34:34 -08:00
committed by GitHub
parent 4a98acc3ea
commit 2325c50b3b
9 changed files with 113 additions and 3 deletions
+1
View File
@@ -52,4 +52,5 @@ abstract class Expression : AstNode;
class TypeTypeLiteral : Expression;
class IdentifierExpression : Expression;
class IntrinsicExpression : Expression;
class IfExpression : Expression;
class UnimplementedExpression : Expression;
+7
View File
@@ -187,6 +187,13 @@ void Expression::Print(llvm::raw_ostream& out) const {
}
out << ")";
break;
case ExpressionKind::IfExpression: {
const auto& if_expr = cast<IfExpression>(*this);
out << "if " << *if_expr.condition() << " then "
<< *if_expr.then_expression() << " else "
<< *if_expr.else_expression();
break;
}
case ExpressionKind::UnimplementedExpression: {
const auto& unimplemented = cast<UnimplementedExpression>(*this);
out << "UnimplementedExpression<" << unimplemented.label() << ">(";
+29
View File
@@ -490,6 +490,35 @@ class IntrinsicExpression : public Expression {
Nonnull<TupleLiteral*> args_;
};
class IfExpression : public Expression {
public:
explicit IfExpression(SourceLocation source_loc,
Nonnull<Expression*> condition,
Nonnull<Expression*> then_expression,
Nonnull<Expression*> else_expression)
: Expression(AstNodeKind::IfExpression, source_loc),
condition_(condition),
then_expression_(then_expression),
else_expression_(else_expression) {}
static auto classof(const AstNode* node) -> bool {
return InheritsFromIfExpression(node->kind());
}
auto condition() const -> Nonnull<Expression*> { return condition_; }
auto then_expression() const -> Nonnull<Expression*> {
return then_expression_;
}
auto else_expression() const -> Nonnull<Expression*> {
return else_expression_;
}
private:
Nonnull<Expression*> condition_;
Nonnull<Expression*> then_expression_;
Nonnull<Expression*> else_expression_;
};
// An expression whose semantics have not been implemented. This can be used
// as a placeholder during development, in order to implement and test parsing
// of a new expression syntax without having to implement its semantics.
@@ -346,6 +346,7 @@ void Interpreter::StepLvalue() {
case ExpressionKind::StringLiteral:
case ExpressionKind::StringTypeLiteral:
case ExpressionKind::IntrinsicExpression:
case ExpressionKind::IfExpression:
FATAL() << "Can't treat expression as lvalue: " << exp;
case ExpressionKind::UnimplementedExpression:
FATAL() << "Unimplemented: " << exp;
@@ -690,6 +691,21 @@ void Interpreter::StepExp() {
CHECK(act.pos() == 0);
return todo_.FinishAction(arena_->New<StringType>());
}
case ExpressionKind::IfExpression: {
const auto& if_expr = cast<IfExpression>(exp);
if (act.pos() == 0) {
return todo_.Spawn(
std::make_unique<ExpressionAction>(if_expr.condition()));
} else if (act.pos() == 1) {
const BoolValue& condition = cast<BoolValue>(*act.results()[0]);
return todo_.Spawn(std::make_unique<ExpressionAction>(
condition.value() ? if_expr.then_expression()
: if_expr.else_expression()));
} else {
return todo_.FinishAction(act.results()[1]);
}
break;
}
case ExpressionKind::UnimplementedExpression:
FATAL() << "Unimplemented: " << exp;
} // switch (exp->kind)
@@ -132,6 +132,13 @@ static void ResolveNames(Expression& expression,
ResolveNames(cast<IntrinsicExpression>(expression).args(),
enclosing_scope);
break;
case ExpressionKind::IfExpression: {
auto& if_expr = cast<IfExpression>(expression);
ResolveNames(*if_expr.condition(), enclosing_scope);
ResolveNames(*if_expr.then_expression(), enclosing_scope);
ResolveNames(*if_expr.else_expression(), enclosing_scope);
break;
}
case ExpressionKind::BoolTypeLiteral:
case ExpressionKind::BoolLiteral:
case ExpressionKind::IntTypeLiteral:
@@ -800,6 +800,22 @@ void TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
e->set_value_category(ValueCategory::Let);
e->set_static_type(arena_->New<TypeType>());
return;
case ExpressionKind::IfExpression: {
auto& if_expr = cast<IfExpression>(*e);
TypeCheckExp(if_expr.condition(), impl_scope);
ExpectType(if_expr.source_loc(), "condition of `if`",
arena_->New<BoolType>(), &if_expr.condition()->static_type());
// TODO: Compute the common type and convert both operands to it.
TypeCheckExp(if_expr.then_expression(), impl_scope);
TypeCheckExp(if_expr.else_expression(), impl_scope);
ExpectExactType(e->source_loc(), "expression of `if` expression",
&if_expr.then_expression()->static_type(),
&if_expr.else_expression()->static_type());
e->set_static_type(&if_expr.then_expression()->static_type());
e->set_value_category(ValueCategory::Let);
return;
}
case ExpressionKind::UnimplementedExpression:
FATAL() << "Unimplemented: " << *e;
}
+2
View File
@@ -88,6 +88,7 @@ RUN "__run"
SEMICOLON ";"
SLASH "/"
STRING "String"
THEN "then"
TRUE "true"
TYPE "Type"
UNDERSCORE "_"
@@ -185,6 +186,7 @@ string_literal \"([^\\\"\n\v\f\r]|\\.)*\"
{SEMICOLON} { return SIMPLE_TOKEN(SEMICOLON); }
{SLASH} { return SIMPLE_TOKEN(SLASH); }
{STRING} { return SIMPLE_TOKEN(STRING); }
{THEN} { return SIMPLE_TOKEN(THEN); }
{TRUE} { return SIMPLE_TOKEN(TRUE); }
{TYPE} { return SIMPLE_TOKEN(TYPE); }
{UNDERSCORE} { return SIMPLE_TOKEN(UNDERSCORE); }
+14 -3
View File
@@ -134,6 +134,8 @@
%type <Nonnull<Expression*>> and_expression
%type <Nonnull<Expression*>> or_lhs
%type <Nonnull<Expression*>> or_expression
%type <Nonnull<Expression*>> statement_expression
%type <Nonnull<Expression*>> if_expression
%type <Nonnull<Expression*>> expression
%type <Nonnull<GenericBinding*>> generic_binding
%type <std::vector<Nonnull<AstNode*>>> deduced_params
@@ -216,6 +218,7 @@
SEMICOLON
SLASH
STRING
THEN
TRUE
TYPE
UNDERSCORE
@@ -477,12 +480,20 @@ or_expression:
std::vector<Nonnull<Expression*>>({$1, $3}));
}
;
expression:
statement_expression:
ref_deref_expression
| predicate_expression
| and_expression
| or_expression
;
if_expression:
statement_expression
| IF expression THEN if_expression ELSE if_expression
{ $$ = arena->New<IfExpression>(context.source_loc(), $2, $4, $6); }
;
expression:
if_expression
;
designator: PERIOD identifier { $$ = $2; }
;
paren_expression: paren_expression_base
@@ -645,11 +656,11 @@ clause_list:
}
;
statement:
expression EQUAL expression SEMICOLON
statement_expression EQUAL expression SEMICOLON
{ $$ = arena->New<Assign>(context.source_loc(), $1, $3); }
| VAR pattern EQUAL expression SEMICOLON
{ $$ = arena->New<VariableDefinition>(context.source_loc(), $2, $4); }
| expression SEMICOLON
| statement_expression SEMICOLON
{ $$ = arena->New<ExpressionStatement>(context.source_loc(), $1); }
| if_statement
{ $$ = $1; }
@@ -0,0 +1,21 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{executable_semantics} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{executable_semantics} --trace %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{executable_semantics} %s
// CHECK: result: 12
package ExecutableSemanticsTest api;
fn Main() -> i32 {
var cond: if true then Bool else i32 = true;
if (if cond then true else false) {}
while (if cond then false else true) {}
return if if cond then true or false else false and true
then if not cond then 1 + 2 else 3 * 4
else if not cond then 5 + 6 else 7 * 8;
}