Просмотр исходного кода

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

Richard Smith 4 лет назад
Родитель
Сommit
2325c50b3b

+ 1 - 0
executable_semantics/ast/ast_rtti.txt

@@ -52,4 +52,5 @@ abstract class Expression : AstNode;
   class TypeTypeLiteral : Expression;
   class IdentifierExpression : Expression;
   class IntrinsicExpression : Expression;
+  class IfExpression : Expression;
   class UnimplementedExpression : Expression;

+ 7 - 0
executable_semantics/ast/expression.cpp

@@ -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 - 0
executable_semantics/ast/expression.h

@@ -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.

+ 16 - 0
executable_semantics/interpreter/interpreter.cpp

@@ -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)

+ 7 - 0
executable_semantics/interpreter/resolve_names.cpp

@@ -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:

+ 16 - 0
executable_semantics/interpreter/type_checker.cpp

@@ -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 - 0
executable_semantics/syntax/lexer.lpp

@@ -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
executable_semantics/syntax/parser.ypp

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

+ 21 - 0
executable_semantics/testdata/if_expression/if_then_else.carbon

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