mirror of
https://github.com/carbon-language/carbon-lang.git
synced 2026-10-04 22:02:52 +01:00
Factor out a Pattern sum type from Expression (#685)
`Pattern` is intended to pilot some changes I would like to apply to all our sum types: - The alternatives are expressed as derived classes rather than members of a `std::variant`. - The alternatives are classes in the [style guide sense](https://google.github.io/styleguide/cppguide.html#Structs_vs._Classes), meaning they can have invariants, but can't have public data members. - Creating an object is expressed using a constructor rather than a factory function. - Accessing an alternative is expressed as a cast (using LLVM's RTTI system) rather than `std::get` or a `Get` method. Co-authored-by: Jon Meow <46229924+jonmeow@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
co-authored by
Jon Meow
parent
b08f6bb0f1
commit
6ac3adfa53
@@ -16,6 +16,7 @@ cc_library(
|
||||
deps = [
|
||||
":function_definition",
|
||||
":member",
|
||||
":pattern",
|
||||
":struct_definition",
|
||||
"//common:ostream",
|
||||
"//executable_semantics/interpreter:address",
|
||||
@@ -33,10 +34,26 @@ cc_library(
|
||||
"//common:ostream",
|
||||
"//executable_semantics/common:arena",
|
||||
"//executable_semantics/common:error",
|
||||
"//executable_semantics/syntax:paren_contents",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "expression_test",
|
||||
srcs = ["expression_test.cpp"],
|
||||
env = {
|
||||
# TODO(#580): Remove this when leaks are fixed.
|
||||
"ASAN_OPTIONS": "detect_leaks=0",
|
||||
},
|
||||
deps = [
|
||||
":expression",
|
||||
"//executable_semantics/syntax:paren_contents",
|
||||
"@llvm-project//llvm:gtest",
|
||||
"@llvm-project//llvm:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "function_definition",
|
||||
srcs = ["function_definition.cpp"],
|
||||
@@ -52,19 +69,47 @@ cc_library(
|
||||
name = "member",
|
||||
srcs = ["member.cpp"],
|
||||
hdrs = ["member.h"],
|
||||
deps = [
|
||||
":pattern",
|
||||
"//common:ostream",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "pattern",
|
||||
srcs = ["pattern.cpp"],
|
||||
hdrs = ["pattern.h"],
|
||||
deps = [
|
||||
":expression",
|
||||
"//common:ostream",
|
||||
"//executable_semantics/common:error",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "pattern_test",
|
||||
srcs = ["pattern_test.cpp"],
|
||||
env = {
|
||||
# TODO(#580): Remove this when leaks are fixed.
|
||||
"ASAN_OPTIONS": "detect_leaks=0",
|
||||
},
|
||||
deps = [
|
||||
":pattern",
|
||||
"//executable_semantics/syntax:paren_contents",
|
||||
"@llvm-project//llvm:Support",
|
||||
"@llvm-project//llvm:gtest",
|
||||
"@llvm-project//llvm:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "statement",
|
||||
srcs = ["statement.cpp"],
|
||||
hdrs = ["statement.h"],
|
||||
deps = [
|
||||
":expression",
|
||||
":pattern",
|
||||
"//common:check",
|
||||
"//common:ostream",
|
||||
"@llvm-project//llvm:Support",
|
||||
|
||||
@@ -35,14 +35,13 @@ auto Declaration::MakeChoiceDeclaration(
|
||||
return d;
|
||||
}
|
||||
|
||||
auto Declaration::MakeVariableDeclaration(int source_location, std::string name,
|
||||
const Expression* type,
|
||||
auto Declaration::MakeVariableDeclaration(int source_location,
|
||||
const BindingPattern* binding,
|
||||
const Expression* initializer)
|
||||
-> const Declaration {
|
||||
Declaration d;
|
||||
d.value = VariableDeclaration({.source_location = source_location,
|
||||
.name = std::move(name),
|
||||
.type = type,
|
||||
.binding = binding,
|
||||
.initializer = initializer});
|
||||
return d;
|
||||
}
|
||||
@@ -91,8 +90,7 @@ void Declaration::Print(llvm::raw_ostream& out) const {
|
||||
|
||||
case DeclarationKind::VariableDeclaration: {
|
||||
const auto& var = GetVariableDeclaration();
|
||||
out << "var " << *var.type << " : " << var.name << " = "
|
||||
<< *var.initializer << "\n";
|
||||
out << "var " << *var.binding << " = " << *var.initializer << "\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/ast/function_definition.h"
|
||||
#include "executable_semantics/ast/member.h"
|
||||
#include "executable_semantics/ast/pattern.h"
|
||||
#include "executable_semantics/ast/struct_definition.h"
|
||||
#include "executable_semantics/interpreter/address.h"
|
||||
#include "executable_semantics/interpreter/dictionary.h"
|
||||
@@ -58,8 +59,10 @@ struct ChoiceDeclaration {
|
||||
struct VariableDeclaration {
|
||||
static constexpr DeclarationKind Kind = DeclarationKind::VariableDeclaration;
|
||||
int source_location;
|
||||
std::string name;
|
||||
const Expression* type;
|
||||
// TODO: split this into a non-optional name and a type, initialized by
|
||||
// a constructor that takes a BindingPattern and handles errors like a
|
||||
// missing name.
|
||||
const BindingPattern* binding;
|
||||
const Expression* initializer;
|
||||
};
|
||||
|
||||
@@ -74,8 +77,8 @@ class Declaration {
|
||||
int line_num, std::string name,
|
||||
std::list<std::pair<std::string, const Expression*>> alternatives)
|
||||
-> const Declaration;
|
||||
static auto MakeVariableDeclaration(int source_location, std::string name,
|
||||
const Expression* type,
|
||||
static auto MakeVariableDeclaration(int source_location,
|
||||
const BindingPattern* binding,
|
||||
const Expression* initializer)
|
||||
-> const Declaration;
|
||||
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "executable_semantics/common/arena.h"
|
||||
#include "executable_semantics/common/error.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
@@ -11,6 +13,24 @@
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
auto ExpressionFromParenContents(
|
||||
int line_num, const ParenContents<Expression>& paren_contents)
|
||||
-> const Expression* {
|
||||
std::optional<const Expression*> single_term = paren_contents.SingleTerm();
|
||||
if (single_term.has_value()) {
|
||||
return *single_term;
|
||||
} else {
|
||||
return TupleExpressionFromParenContents(line_num, paren_contents);
|
||||
}
|
||||
}
|
||||
|
||||
auto TupleExpressionFromParenContents(
|
||||
int line_num, const ParenContents<Expression>& paren_contents)
|
||||
-> const Expression* {
|
||||
return Expression::MakeTupleLiteral(
|
||||
line_num, paren_contents.TupleElements<FieldInitializer>(line_num));
|
||||
}
|
||||
|
||||
auto Expression::GetIdentifierExpression() const
|
||||
-> const IdentifierExpression& {
|
||||
return std::get<IdentifierExpression>(value);
|
||||
@@ -25,10 +45,6 @@ auto Expression::GetIndexExpression() const -> const IndexExpression& {
|
||||
return std::get<IndexExpression>(value);
|
||||
}
|
||||
|
||||
auto Expression::GetBindingExpression() const -> const BindingExpression& {
|
||||
return std::get<BindingExpression>(value);
|
||||
}
|
||||
|
||||
auto Expression::GetIntLiteral() const -> int {
|
||||
return std::get<IntLiteral>(value).value;
|
||||
}
|
||||
@@ -75,13 +91,6 @@ auto Expression::MakeBoolTypeLiteral(int line_num) -> const Expression* {
|
||||
return t;
|
||||
}
|
||||
|
||||
auto Expression::MakeAutoTypeLiteral(int line_num) -> const Expression* {
|
||||
auto* t = global_arena->New<Expression>();
|
||||
t->line_num = line_num;
|
||||
t->value = AutoTypeLiteral();
|
||||
return t;
|
||||
}
|
||||
|
||||
// Returns a Continuation type AST node at the given source location.
|
||||
auto Expression::MakeContinuationTypeLiteral(int line_num)
|
||||
-> const Expression* {
|
||||
@@ -108,16 +117,6 @@ auto Expression::MakeIdentifierExpression(int line_num, std::string var)
|
||||
return v;
|
||||
}
|
||||
|
||||
auto Expression::MakeBindingExpression(int line_num,
|
||||
std::optional<std::string> var,
|
||||
const Expression* type)
|
||||
-> const Expression* {
|
||||
auto* v = global_arena->New<Expression>();
|
||||
v->line_num = line_num;
|
||||
v->value = BindingExpression({.name = std::move(var), .type = type});
|
||||
return v;
|
||||
}
|
||||
|
||||
auto Expression::MakeIntLiteral(int line_num, int i) -> const Expression* {
|
||||
auto* e = global_arena->New<Expression>();
|
||||
e->line_num = line_num;
|
||||
@@ -166,21 +165,7 @@ auto Expression::MakeTupleLiteral(int line_num,
|
||||
-> const Expression* {
|
||||
auto* e = global_arena->New<Expression>();
|
||||
e->line_num = line_num;
|
||||
int i = 0;
|
||||
bool seen_named_member = false;
|
||||
for (auto& arg : args) {
|
||||
if (arg.name == "") {
|
||||
if (seen_named_member) {
|
||||
FATAL_USER_ERROR(line_num)
|
||||
<< "positional members must come before named members";
|
||||
}
|
||||
arg.name = std::to_string(i);
|
||||
++i;
|
||||
} else {
|
||||
seen_named_member = true;
|
||||
}
|
||||
}
|
||||
e->value = TupleLiteral({.fields = args});
|
||||
e->value = TupleLiteral({.fields = std::move(args)});
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -269,16 +254,6 @@ void Expression::Print(llvm::raw_ostream& out) const {
|
||||
case ExpressionKind::IdentifierExpression:
|
||||
out << GetIdentifierExpression().name;
|
||||
break;
|
||||
case ExpressionKind::BindingExpression: {
|
||||
const BindingExpression& binding = GetBindingExpression();
|
||||
if (binding.name.has_value()) {
|
||||
out << *binding.name;
|
||||
} else {
|
||||
out << "_";
|
||||
}
|
||||
out << ": " << *binding.type;
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::CallExpression:
|
||||
out << *GetCallExpression().function;
|
||||
if (GetCallExpression().argument->tag() == ExpressionKind::TupleLiteral) {
|
||||
@@ -296,9 +271,6 @@ void Expression::Print(llvm::raw_ostream& out) const {
|
||||
case ExpressionKind::TypeTypeLiteral:
|
||||
out << "Type";
|
||||
break;
|
||||
case ExpressionKind::AutoTypeLiteral:
|
||||
out << "auto";
|
||||
break;
|
||||
case ExpressionKind::ContinuationTypeLiteral:
|
||||
out << "Continuation";
|
||||
break;
|
||||
|
||||
@@ -11,15 +11,33 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/syntax/paren_contents.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
struct Expression;
|
||||
class Pattern;
|
||||
|
||||
// Converts paren_contents to an Expression, interpreting the parentheses as
|
||||
// grouping if their contents permit that interpretation, or as forming a
|
||||
// tuple otherwise.
|
||||
auto ExpressionFromParenContents(
|
||||
int line_num, const ParenContents<Expression>& paren_contents)
|
||||
-> const Expression*;
|
||||
|
||||
// Converts paren_contents to an Expression, interpreting the parentheses as
|
||||
// forming a tuple.
|
||||
auto TupleExpressionFromParenContents(
|
||||
int line_num, const ParenContents<Expression>& paren_contents)
|
||||
-> const Expression*;
|
||||
|
||||
// A FieldInitializer represents the initialization of a single tuple field.
|
||||
struct FieldInitializer {
|
||||
// The field name. For a positional field, this may be empty.
|
||||
FieldInitializer(std::string name, const Expression* expression)
|
||||
: name(std::move(name)), expression(expression) {}
|
||||
|
||||
// The field name. Cannot be empty.
|
||||
std::string name;
|
||||
|
||||
// The expression that initializes the field.
|
||||
@@ -27,7 +45,6 @@ struct FieldInitializer {
|
||||
};
|
||||
|
||||
enum class ExpressionKind {
|
||||
AutoTypeLiteral,
|
||||
BoolTypeLiteral,
|
||||
BoolLiteral,
|
||||
CallExpression,
|
||||
@@ -37,7 +54,6 @@ enum class ExpressionKind {
|
||||
IntTypeLiteral,
|
||||
ContinuationTypeLiteral, // The type of a continuation value.
|
||||
IntLiteral,
|
||||
BindingExpression,
|
||||
PrimitiveOperatorExpression,
|
||||
TupleLiteral,
|
||||
TypeTypeLiteral,
|
||||
@@ -76,13 +92,6 @@ struct IndexExpression {
|
||||
const Expression* offset;
|
||||
};
|
||||
|
||||
struct BindingExpression {
|
||||
static constexpr ExpressionKind Kind = ExpressionKind::BindingExpression;
|
||||
// nullopt represents the `_` placeholder.
|
||||
std::optional<std::string> name;
|
||||
const Expression* type;
|
||||
};
|
||||
|
||||
struct IntLiteral {
|
||||
static constexpr ExpressionKind Kind = ExpressionKind::IntLiteral;
|
||||
int value;
|
||||
@@ -117,10 +126,6 @@ struct FunctionTypeLiteral {
|
||||
const Expression* return_type;
|
||||
};
|
||||
|
||||
struct AutoTypeLiteral {
|
||||
static constexpr ExpressionKind Kind = ExpressionKind::AutoTypeLiteral;
|
||||
};
|
||||
|
||||
struct BoolTypeLiteral {
|
||||
static constexpr ExpressionKind Kind = ExpressionKind::BoolTypeLiteral;
|
||||
};
|
||||
@@ -141,10 +146,6 @@ struct TypeTypeLiteral {
|
||||
struct Expression {
|
||||
static auto MakeIdentifierExpression(int line_num, std::string var)
|
||||
-> const Expression*;
|
||||
static auto MakeBindingExpression(int line_num,
|
||||
std::optional<std::string> var,
|
||||
const Expression* type)
|
||||
-> const Expression*;
|
||||
static auto MakeIntLiteral(int line_num, int i) -> const Expression*;
|
||||
static auto MakeBoolLiteral(int line_num, bool b) -> const Expression*;
|
||||
static auto MakePrimitiveOperatorExpression(
|
||||
@@ -164,13 +165,11 @@ struct Expression {
|
||||
static auto MakeFunctionTypeLiteral(int line_num, const Expression* param,
|
||||
const Expression* ret)
|
||||
-> const Expression*;
|
||||
static auto MakeAutoTypeLiteral(int line_num) -> const Expression*;
|
||||
static auto MakeContinuationTypeLiteral(int line_num) -> const Expression*;
|
||||
|
||||
auto GetIdentifierExpression() const -> const IdentifierExpression&;
|
||||
auto GetFieldAccessExpression() const -> const FieldAccessExpression&;
|
||||
auto GetIndexExpression() const -> const IndexExpression&;
|
||||
auto GetBindingExpression() const -> const BindingExpression&;
|
||||
auto GetIntLiteral() const -> int;
|
||||
auto GetBoolLiteral() const -> bool;
|
||||
auto GetTupleLiteral() const -> const TupleLiteral&;
|
||||
@@ -190,10 +189,10 @@ struct Expression {
|
||||
|
||||
private:
|
||||
std::variant<IdentifierExpression, FieldAccessExpression, IndexExpression,
|
||||
BindingExpression, IntLiteral, BoolLiteral, TupleLiteral,
|
||||
IntLiteral, BoolLiteral, TupleLiteral,
|
||||
PrimitiveOperatorExpression, CallExpression, FunctionTypeLiteral,
|
||||
AutoTypeLiteral, BoolTypeLiteral, IntTypeLiteral,
|
||||
ContinuationTypeLiteral, TypeTypeLiteral>
|
||||
BoolTypeLiteral, IntTypeLiteral, ContinuationTypeLiteral,
|
||||
TypeTypeLiteral>
|
||||
value;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
// 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
|
||||
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "executable_semantics/syntax/paren_contents.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace Carbon {
|
||||
namespace {
|
||||
|
||||
using testing::ElementsAre;
|
||||
using testing::IsEmpty;
|
||||
|
||||
// Matches a FieldInitializer named `name` whose `expression` is an
|
||||
// `IntLiteral`
|
||||
MATCHER_P(IntFieldNamed, name, "") {
|
||||
return arg.name == std::string(name) &&
|
||||
arg.expression->tag() == ExpressionKind::IntLiteral;
|
||||
}
|
||||
|
||||
TEST(ExpressionTest, EmptyAsExpression) {
|
||||
ParenContents<Expression> contents = {.elements = {},
|
||||
.has_trailing_comma = false};
|
||||
const Expression* expression =
|
||||
ExpressionFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(expression->line_num, 1);
|
||||
ASSERT_EQ(expression->tag(), ExpressionKind::TupleLiteral);
|
||||
EXPECT_THAT(expression->GetTupleLiteral().fields, IsEmpty());
|
||||
}
|
||||
|
||||
TEST(ExpressionTest, EmptyAsTuple) {
|
||||
ParenContents<Expression> contents = {.elements = {},
|
||||
.has_trailing_comma = false};
|
||||
const Expression* tuple =
|
||||
TupleExpressionFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(tuple->line_num, 1);
|
||||
ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
|
||||
EXPECT_THAT(tuple->GetTupleLiteral().fields, IsEmpty());
|
||||
}
|
||||
|
||||
TEST(ExpressionTest, UnaryNoCommaAsExpression) {
|
||||
// Equivalent to a code fragment like
|
||||
// ```
|
||||
// (
|
||||
// 42
|
||||
// )
|
||||
// ```
|
||||
ParenContents<Expression> contents = {
|
||||
.elements = {{.name = std::nullopt,
|
||||
.term = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
|
||||
.has_trailing_comma = false};
|
||||
|
||||
const Expression* expression =
|
||||
ExpressionFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(expression->line_num, 2);
|
||||
ASSERT_EQ(expression->tag(), ExpressionKind::IntLiteral);
|
||||
}
|
||||
|
||||
TEST(ExpressionTest, UnaryNoCommaAsTuple) {
|
||||
ParenContents<Expression> contents = {
|
||||
.elements = {{.name = std::nullopt,
|
||||
.term = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
|
||||
.has_trailing_comma = false};
|
||||
|
||||
const Expression* tuple =
|
||||
TupleExpressionFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(tuple->line_num, 1);
|
||||
ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
|
||||
EXPECT_THAT(tuple->GetTupleLiteral().fields, ElementsAre(IntFieldNamed("0")));
|
||||
}
|
||||
|
||||
TEST(ExpressionTest, UnaryWithCommaAsExpression) {
|
||||
ParenContents<Expression> contents = {
|
||||
.elements = {{.name = std::nullopt,
|
||||
.term = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
|
||||
.has_trailing_comma = true};
|
||||
|
||||
const Expression* expression =
|
||||
ExpressionFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(expression->line_num, 1);
|
||||
ASSERT_EQ(expression->tag(), ExpressionKind::TupleLiteral);
|
||||
EXPECT_THAT(expression->GetTupleLiteral().fields,
|
||||
ElementsAre(IntFieldNamed("0")));
|
||||
}
|
||||
|
||||
TEST(ExpressionTest, UnaryWithCommaAsTuple) {
|
||||
ParenContents<Expression> contents = {
|
||||
.elements = {{.name = std::nullopt,
|
||||
.term = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
|
||||
.has_trailing_comma = true};
|
||||
|
||||
const Expression* tuple =
|
||||
TupleExpressionFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(tuple->line_num, 1);
|
||||
ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
|
||||
EXPECT_THAT(tuple->GetTupleLiteral().fields, ElementsAre(IntFieldNamed("0")));
|
||||
}
|
||||
|
||||
TEST(ExpressionTest, BinaryAsExpression) {
|
||||
ParenContents<Expression> contents = {
|
||||
.elements = {{.name = std::nullopt,
|
||||
.term = Expression::MakeIntLiteral(/*line_num=*/2, 42)},
|
||||
{.name = std::nullopt,
|
||||
.term = Expression::MakeIntLiteral(/*line_num=*/3, 42)}},
|
||||
.has_trailing_comma = true};
|
||||
|
||||
const Expression* expression =
|
||||
ExpressionFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(expression->line_num, 1);
|
||||
ASSERT_EQ(expression->tag(), ExpressionKind::TupleLiteral);
|
||||
EXPECT_THAT(expression->GetTupleLiteral().fields,
|
||||
ElementsAre(IntFieldNamed("0"), IntFieldNamed("1")));
|
||||
}
|
||||
|
||||
TEST(ExpressionTest, BinaryAsTuple) {
|
||||
ParenContents<Expression> contents = {
|
||||
.elements = {{.name = std::nullopt,
|
||||
.term = Expression::MakeIntLiteral(/*line_num=*/2, 42)},
|
||||
{.name = std::nullopt,
|
||||
.term = Expression::MakeIntLiteral(/*line_num=*/3, 42)}},
|
||||
.has_trailing_comma = true};
|
||||
|
||||
const Expression* tuple =
|
||||
TupleExpressionFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(tuple->line_num, 1);
|
||||
ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
|
||||
EXPECT_THAT(tuple->GetTupleLiteral().fields,
|
||||
ElementsAre(IntFieldNamed("0"), IntFieldNamed("1")));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Carbon
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/ast/pattern.h"
|
||||
#include "executable_semantics/ast/statement.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
@@ -23,8 +24,8 @@ struct FunctionDefinition {
|
||||
FunctionDefinition() = default;
|
||||
FunctionDefinition(int line_num, std::string name,
|
||||
std::vector<GenericBinding> deduced_params,
|
||||
const Expression* param_pattern,
|
||||
const Expression* return_type, const Statement* body)
|
||||
const TuplePattern* param_pattern,
|
||||
const Pattern* return_type, const Statement* body)
|
||||
: line_num(line_num),
|
||||
name(std::move(name)),
|
||||
deduced_parameters(deduced_params),
|
||||
@@ -39,8 +40,8 @@ struct FunctionDefinition {
|
||||
int line_num;
|
||||
std::string name;
|
||||
std::vector<GenericBinding> deduced_parameters;
|
||||
const Expression* param_pattern;
|
||||
const Expression* return_type;
|
||||
const TuplePattern* param_pattern;
|
||||
const Pattern* return_type;
|
||||
const Statement* body;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
auto Member::MakeFieldMember(int line_num, std::string name,
|
||||
const Expression* type) -> Member* {
|
||||
auto Member::MakeFieldMember(int line_num, const BindingPattern* binding)
|
||||
-> Member* {
|
||||
auto m = new Member();
|
||||
m->line_num = line_num;
|
||||
m->value = FieldMember({.name = std::move(name), .type = type});
|
||||
m->value = FieldMember({.binding = binding});
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ void Member::Print(llvm::raw_ostream& out) const {
|
||||
switch (tag()) {
|
||||
case MemberKind::FieldMember:
|
||||
const auto& field = GetFieldMember();
|
||||
out << "var " << field.name << " : " << *field.type << ";\n";
|
||||
out << "var " << field.binding << ";\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/ast/pattern.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
namespace Carbon {
|
||||
@@ -17,13 +18,15 @@ enum class MemberKind { FieldMember };
|
||||
|
||||
struct FieldMember {
|
||||
static constexpr MemberKind Kind = MemberKind::FieldMember;
|
||||
std::string name;
|
||||
const Expression* type;
|
||||
// TODO: split this into a non-optional name and a type, initialized by
|
||||
// a constructor that takes a BindingPattern and handles errors like a
|
||||
// missing name.
|
||||
const BindingPattern* binding;
|
||||
};
|
||||
|
||||
struct Member {
|
||||
static auto MakeFieldMember(int line_num, std::string name,
|
||||
const Expression* type) -> Member*;
|
||||
static auto MakeFieldMember(int line_num, const BindingPattern* binding)
|
||||
-> Member*;
|
||||
|
||||
auto GetFieldMember() const -> const FieldMember&;
|
||||
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
// 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
|
||||
|
||||
#include "executable_semantics/ast/pattern.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/common/error.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
using llvm::cast;
|
||||
|
||||
void Pattern::Print(llvm::raw_ostream& out) const {
|
||||
switch (Tag()) {
|
||||
case Kind::AutoPattern:
|
||||
out << "auto";
|
||||
break;
|
||||
case Kind::BindingPattern: {
|
||||
const auto& binding = cast<BindingPattern>(*this);
|
||||
if (binding.Name().has_value()) {
|
||||
out << *binding.Name();
|
||||
} else {
|
||||
out << "_";
|
||||
}
|
||||
out << ": " << *binding.Type();
|
||||
break;
|
||||
}
|
||||
case Kind::TuplePattern: {
|
||||
const auto& tuple = cast<TuplePattern>(*this);
|
||||
out << "(";
|
||||
llvm::ListSeparator sep;
|
||||
for (const TuplePattern::Field& field : tuple.Fields()) {
|
||||
out << sep << field.name << " = " << field.pattern;
|
||||
}
|
||||
out << ")";
|
||||
break;
|
||||
}
|
||||
case Kind::AlternativePattern: {
|
||||
const auto& alternative = cast<AlternativePattern>(*this);
|
||||
out << alternative.ChoiceType() << "." << alternative.AlternativeName()
|
||||
<< alternative.Arguments();
|
||||
break;
|
||||
}
|
||||
case Kind::ExpressionPattern:
|
||||
out << cast<ExpressionPattern>(*this).Expression();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TuplePattern::TuplePattern(const Expression* tuple_literal)
|
||||
: Pattern(Kind::TuplePattern, tuple_literal->line_num) {
|
||||
const auto& tuple = tuple_literal->GetTupleLiteral();
|
||||
for (const FieldInitializer& init : tuple.fields) {
|
||||
fields.push_back(Field(init.name, new ExpressionPattern(init.expression)));
|
||||
}
|
||||
}
|
||||
|
||||
auto PatternFromParenContents(int line_num,
|
||||
const ParenContents<Pattern>& paren_contents)
|
||||
-> const Pattern* {
|
||||
std::optional<const Pattern*> single_term = paren_contents.SingleTerm();
|
||||
if (single_term.has_value()) {
|
||||
return *single_term;
|
||||
} else {
|
||||
return TuplePatternFromParenContents(line_num, paren_contents);
|
||||
}
|
||||
}
|
||||
|
||||
auto TuplePatternFromParenContents(int line_num,
|
||||
const ParenContents<Pattern>& paren_contents)
|
||||
-> const TuplePattern* {
|
||||
return new TuplePattern(
|
||||
line_num, paren_contents.TupleElements<TuplePattern::Field>(line_num));
|
||||
}
|
||||
|
||||
AlternativePattern::AlternativePattern(int line_num,
|
||||
const Expression* alternative,
|
||||
const TuplePattern* arguments)
|
||||
: Pattern(Kind::AlternativePattern, line_num), arguments(arguments) {
|
||||
if (alternative->tag() != ExpressionKind::FieldAccessExpression) {
|
||||
FATAL_USER_ERROR(alternative->line_num)
|
||||
<< "Alternative pattern must have the form of a field access.";
|
||||
}
|
||||
const auto& field_access = alternative->GetFieldAccessExpression();
|
||||
choice_type = field_access.aggregate;
|
||||
alternative_name = field_access.field;
|
||||
}
|
||||
|
||||
auto ParenExpressionToParenPattern(const ParenContents<Expression>& contents)
|
||||
-> ParenContents<Pattern> {
|
||||
ParenContents<Pattern> result = {
|
||||
.elements = {}, .has_trailing_comma = contents.has_trailing_comma};
|
||||
for (const auto& element : contents.elements) {
|
||||
result.elements.push_back(
|
||||
{.name = element.name, .term = new ExpressionPattern(element.term)});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
@@ -0,0 +1,199 @@
|
||||
// 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
|
||||
|
||||
#ifndef EXECUTABLE_SEMANTICS_AST_PATTERN_H_
|
||||
#define EXECUTABLE_SEMANTICS_AST_PATTERN_H_
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
// Abstract base class of all AST nodes representing patterns.
|
||||
//
|
||||
// Pattern and its derived classes support LLVM-style RTTI, including
|
||||
// llvm::isa, llvm::cast, and llvm::dyn_cast. To support this, every
|
||||
// class derived from Pattern must provide a `classof` operation, and
|
||||
// every concrete derived class must have a corresponding enumerator
|
||||
// in `Kind`; see https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html for
|
||||
// details.
|
||||
class Pattern {
|
||||
public:
|
||||
enum class Kind {
|
||||
AutoPattern,
|
||||
BindingPattern,
|
||||
TuplePattern,
|
||||
AlternativePattern,
|
||||
ExpressionPattern,
|
||||
};
|
||||
|
||||
Pattern(const Pattern&) = delete;
|
||||
Pattern& operator=(const Pattern&) = delete;
|
||||
|
||||
// Returns the enumerator corresponding to the most-derived type of this
|
||||
// object.
|
||||
auto Tag() const -> Kind { return tag; }
|
||||
|
||||
auto LineNumber() const -> int { return line_num; }
|
||||
|
||||
void Print(llvm::raw_ostream& out) const;
|
||||
|
||||
protected:
|
||||
// Constructs a Pattern representing syntax at the given line number.
|
||||
// `tag` must be the enumerator corresponding to the most-derived type being
|
||||
// constructed.
|
||||
Pattern(Kind tag, int line_num) : tag(tag), line_num(line_num) {}
|
||||
|
||||
private:
|
||||
const Kind tag;
|
||||
int line_num;
|
||||
};
|
||||
|
||||
// A pattern consisting of the `auto` keyword.
|
||||
class AutoPattern : public Pattern {
|
||||
public:
|
||||
explicit AutoPattern(int line_num) : Pattern(Kind::AutoPattern, line_num) {}
|
||||
|
||||
static auto classof(const Pattern* pattern) -> bool {
|
||||
return pattern->Tag() == Kind::AutoPattern;
|
||||
}
|
||||
};
|
||||
|
||||
// A pattern that matches a value of a specified type, and optionally binds
|
||||
// a name to it.
|
||||
class BindingPattern : public Pattern {
|
||||
public:
|
||||
BindingPattern(int line_num, std::optional<std::string> name,
|
||||
const Pattern* type)
|
||||
: Pattern(Kind::BindingPattern, line_num),
|
||||
name(std::move(name)),
|
||||
type(type) {}
|
||||
|
||||
static auto classof(const Pattern* pattern) -> bool {
|
||||
return pattern->Tag() == Kind::BindingPattern;
|
||||
}
|
||||
|
||||
// The name this pattern binds, if any.
|
||||
auto Name() const -> const std::optional<std::string>& { return name; }
|
||||
|
||||
// The pattern specifying the type of values that this pattern matches.
|
||||
auto Type() const -> const Pattern* { return type; }
|
||||
|
||||
private:
|
||||
std::optional<std::string> name;
|
||||
const Pattern* type;
|
||||
};
|
||||
|
||||
// A pattern that matches a tuple value field-wise.
|
||||
class TuplePattern : public Pattern {
|
||||
public:
|
||||
// Represents a portion of a tuple pattern corresponding to a single field.
|
||||
struct Field {
|
||||
Field(std::string name, const Pattern* pattern)
|
||||
: name(std::move(name)), pattern(pattern) {}
|
||||
|
||||
// The field name. Cannot be empty
|
||||
std::string name;
|
||||
|
||||
// The pattern the field must match.
|
||||
const Pattern* pattern;
|
||||
};
|
||||
|
||||
TuplePattern(int line_num, std::vector<Field> fields)
|
||||
: Pattern(Kind::TuplePattern, line_num), fields(std::move(fields)) {}
|
||||
|
||||
// Converts tuple_literal to a TuplePattern, by wrapping each field in an
|
||||
// ExpressionPattern.
|
||||
//
|
||||
// REQUIRES: tuple_literal->Tag() == ExpressionKind::TupleLiteral
|
||||
explicit TuplePattern(const Expression* tuple_literal);
|
||||
|
||||
static auto classof(const Pattern* pattern) -> bool {
|
||||
return pattern->Tag() == Kind::TuplePattern;
|
||||
}
|
||||
|
||||
auto Fields() const -> const std::vector<Field>& { return fields; }
|
||||
|
||||
private:
|
||||
std::vector<Field> fields;
|
||||
};
|
||||
|
||||
// Converts paren_contents to a Pattern, interpreting the parentheses as
|
||||
// grouping if their contents permit that interpretation, or as forming a
|
||||
// tuple otherwise.
|
||||
auto PatternFromParenContents(int line_num,
|
||||
const ParenContents<Pattern>& paren_contents)
|
||||
-> const Pattern*;
|
||||
|
||||
// Converts paren_contents to a TuplePattern, interpreting the parentheses as
|
||||
// forming a tuple.
|
||||
auto TuplePatternFromParenContents(int line_num,
|
||||
const ParenContents<Pattern>& paren_contents)
|
||||
-> const TuplePattern*;
|
||||
|
||||
// Converts `contents` to ParenContents<Pattern> by replacing each Expression
|
||||
// with an ExpressionPattern.
|
||||
auto ParenExpressionToParenPattern(const ParenContents<Expression>& contents)
|
||||
-> ParenContents<Pattern>;
|
||||
|
||||
// A pattern that matches an alternative of a choice type.
|
||||
class AlternativePattern : public Pattern {
|
||||
public:
|
||||
// Constructs an AlternativePattern that matches a value of the type
|
||||
// specified by choice_type if it represents an alternative named
|
||||
// alternative_name, and its arguments match `arguments`.
|
||||
AlternativePattern(int line_num, const Expression* choice_type,
|
||||
std::string alternative_name,
|
||||
const TuplePattern* arguments)
|
||||
: Pattern(Kind::AlternativePattern, line_num),
|
||||
choice_type(choice_type),
|
||||
alternative_name(std::move(alternative_name)),
|
||||
arguments(arguments) {}
|
||||
|
||||
// Constructs an AlternativePattern that matches the alternative specified
|
||||
// by `alternative`, if its arguments match `arguments`.
|
||||
AlternativePattern(int line_num, const Expression* alternative,
|
||||
const TuplePattern* arguments);
|
||||
|
||||
static auto classof(const Pattern* pattern) -> bool {
|
||||
return pattern->Tag() == Kind::AlternativePattern;
|
||||
}
|
||||
|
||||
auto ChoiceType() const -> const Expression* { return choice_type; }
|
||||
auto AlternativeName() const -> const std::string& {
|
||||
return alternative_name;
|
||||
}
|
||||
auto Arguments() const -> const TuplePattern* { return arguments; }
|
||||
|
||||
private:
|
||||
const Expression* choice_type;
|
||||
std::string alternative_name;
|
||||
const TuplePattern* arguments;
|
||||
};
|
||||
|
||||
// A pattern that matches a value if it is equal to the value of a given
|
||||
// expression.
|
||||
class ExpressionPattern : public Pattern {
|
||||
public:
|
||||
ExpressionPattern(const Expression* expression)
|
||||
: Pattern(Kind::ExpressionPattern, expression->line_num),
|
||||
expression(expression) {}
|
||||
|
||||
static auto classof(const Pattern* pattern) -> bool {
|
||||
return pattern->Tag() == Kind::ExpressionPattern;
|
||||
}
|
||||
|
||||
auto Expression() const -> const Expression* { return expression; }
|
||||
|
||||
private:
|
||||
const Carbon::Expression* expression;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // EXECUTABLE_SEMANTICS_AST_PATTERN_H_
|
||||
@@ -0,0 +1,130 @@
|
||||
// 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
|
||||
|
||||
#include "executable_semantics/ast/pattern.h"
|
||||
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/syntax/paren_contents.h"
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
|
||||
namespace Carbon {
|
||||
namespace {
|
||||
|
||||
using llvm::cast;
|
||||
using llvm::isa;
|
||||
using testing::ElementsAre;
|
||||
using testing::IsEmpty;
|
||||
|
||||
// Matches a TuplePattern::Field named `name` whose `pattern` is an
|
||||
// `AutoPattern`.
|
||||
MATCHER_P(AutoFieldNamed, name, "") {
|
||||
return arg.name == std::string(name) && isa<AutoPattern>(arg.pattern);
|
||||
}
|
||||
|
||||
TEST(PatternTest, EmptyAsPattern) {
|
||||
ParenContents<Pattern> contents = {.elements = {},
|
||||
.has_trailing_comma = false};
|
||||
const Pattern* pattern = PatternFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(pattern->LineNumber(), 1);
|
||||
ASSERT_TRUE(isa<TuplePattern>(pattern));
|
||||
EXPECT_THAT(cast<TuplePattern>(pattern)->Fields(), IsEmpty());
|
||||
}
|
||||
|
||||
TEST(PatternTest, EmptyAsTuplePattern) {
|
||||
ParenContents<Pattern> contents = {.elements = {},
|
||||
.has_trailing_comma = false};
|
||||
const TuplePattern* tuple =
|
||||
TuplePatternFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(tuple->LineNumber(), 1);
|
||||
EXPECT_THAT(tuple->Fields(), IsEmpty());
|
||||
}
|
||||
|
||||
TEST(PatternTest, UnaryNoCommaAsPattern) {
|
||||
// Equivalent to a code fragment like
|
||||
// ```
|
||||
// (
|
||||
// auto
|
||||
// )
|
||||
// ```
|
||||
ParenContents<Pattern> contents = {
|
||||
.elements = {{.name = std::nullopt,
|
||||
.term = new AutoPattern(/*line_num=*/2)}},
|
||||
.has_trailing_comma = false};
|
||||
|
||||
const Pattern* pattern = PatternFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(pattern->LineNumber(), 2);
|
||||
ASSERT_TRUE(isa<AutoPattern>(pattern));
|
||||
}
|
||||
|
||||
TEST(PatternTest, UnaryNoCommaAsTuplePattern) {
|
||||
ParenContents<Pattern> contents = {
|
||||
.elements = {{.name = std::nullopt,
|
||||
.term = new AutoPattern(/*line_num=*/2)}},
|
||||
.has_trailing_comma = false};
|
||||
|
||||
const TuplePattern* tuple =
|
||||
TuplePatternFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(tuple->LineNumber(), 1);
|
||||
EXPECT_THAT(tuple->Fields(), ElementsAre(AutoFieldNamed("0")));
|
||||
}
|
||||
|
||||
TEST(PatternTest, UnaryWithCommaAsPattern) {
|
||||
ParenContents<Pattern> contents = {
|
||||
.elements = {{.name = std::nullopt,
|
||||
.term = new AutoPattern(/*line_num=*/2)}},
|
||||
.has_trailing_comma = true};
|
||||
|
||||
const Pattern* pattern = PatternFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(pattern->LineNumber(), 1);
|
||||
ASSERT_TRUE(isa<TuplePattern>(pattern));
|
||||
EXPECT_THAT(cast<TuplePattern>(pattern)->Fields(),
|
||||
ElementsAre(AutoFieldNamed("0")));
|
||||
}
|
||||
|
||||
TEST(PatternTest, UnaryWithCommaAsTuplePattern) {
|
||||
ParenContents<Pattern> contents = {
|
||||
.elements = {{.name = std::nullopt,
|
||||
.term = new AutoPattern(/*line_num=*/2)}},
|
||||
.has_trailing_comma = true};
|
||||
|
||||
const TuplePattern* tuple =
|
||||
TuplePatternFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(tuple->LineNumber(), 1);
|
||||
EXPECT_THAT(tuple->Fields(), ElementsAre(AutoFieldNamed("0")));
|
||||
}
|
||||
|
||||
TEST(PatternTest, BinaryAsPattern) {
|
||||
ParenContents<Pattern> contents = {
|
||||
.elements = {{.name = std::nullopt,
|
||||
.term = new AutoPattern(/*line_num=*/2)},
|
||||
{.name = std::nullopt,
|
||||
.term = new AutoPattern(/*line_num=*/3)}},
|
||||
.has_trailing_comma = true};
|
||||
|
||||
const Pattern* pattern = PatternFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(pattern->LineNumber(), 1);
|
||||
ASSERT_TRUE(isa<TuplePattern>(pattern));
|
||||
EXPECT_THAT(cast<TuplePattern>(pattern)->Fields(),
|
||||
ElementsAre(AutoFieldNamed("0"), AutoFieldNamed("1")));
|
||||
}
|
||||
|
||||
TEST(PatternTest, BinaryAsTuplePattern) {
|
||||
ParenContents<Pattern> contents = {
|
||||
.elements = {{.name = std::nullopt,
|
||||
.term = new AutoPattern(/*line_num=*/2)},
|
||||
{.name = std::nullopt,
|
||||
.term = new AutoPattern(/*line_num=*/3)}},
|
||||
.has_trailing_comma = true};
|
||||
|
||||
const TuplePattern* tuple =
|
||||
TuplePatternFromParenContents(/*line_num=*/1, contents);
|
||||
EXPECT_EQ(tuple->LineNumber(), 1);
|
||||
EXPECT_THAT(tuple->Fields(),
|
||||
ElementsAre(AutoFieldNamed("0"), AutoFieldNamed("1")));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Carbon
|
||||
@@ -76,7 +76,7 @@ auto Statement::MakeAssign(int line_num, const Expression* lhs,
|
||||
return s;
|
||||
}
|
||||
|
||||
auto Statement::MakeVariableDefinition(int line_num, const Expression* pat,
|
||||
auto Statement::MakeVariableDefinition(int line_num, const Pattern* pat,
|
||||
const Expression* init)
|
||||
-> const Statement* {
|
||||
auto* s = new Statement();
|
||||
@@ -142,7 +142,7 @@ auto Statement::MakeBlock(int line_num, const Statement* stmt)
|
||||
|
||||
auto Statement::MakeMatch(
|
||||
int line_num, const Expression* exp,
|
||||
std::list<std::pair<const Expression*, const Statement*>>* clauses)
|
||||
std::list<std::pair<const Pattern*, const Statement*>>* clauses)
|
||||
-> const Statement* {
|
||||
auto* s = new Statement();
|
||||
s->line_num = line_num;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/ast/pattern.h"
|
||||
#include "llvm/Support/Compiler.h"
|
||||
|
||||
namespace Carbon {
|
||||
@@ -45,7 +46,7 @@ struct Assign {
|
||||
|
||||
struct VariableDefinition {
|
||||
static constexpr StatementKind Kind = StatementKind::VariableDefinition;
|
||||
const Expression* pat;
|
||||
const Pattern* pat;
|
||||
const Expression* init;
|
||||
};
|
||||
|
||||
@@ -89,7 +90,7 @@ struct Continue {
|
||||
struct Match {
|
||||
static constexpr StatementKind Kind = StatementKind::Match;
|
||||
const Expression* exp;
|
||||
std::list<std::pair<const Expression*, const Statement*>>* clauses;
|
||||
std::list<std::pair<const Pattern*, const Statement*>>* clauses;
|
||||
};
|
||||
|
||||
struct Continuation {
|
||||
@@ -113,7 +114,7 @@ struct Statement {
|
||||
-> const Statement*;
|
||||
static auto MakeAssign(int line_num, const Expression* lhs,
|
||||
const Expression* rhs) -> const Statement*;
|
||||
static auto MakeVariableDefinition(int line_num, const Expression* pat,
|
||||
static auto MakeVariableDefinition(int line_num, const Pattern* pat,
|
||||
const Expression* init)
|
||||
-> const Statement*;
|
||||
static auto MakeIf(int line_num, const Expression* cond,
|
||||
@@ -129,7 +130,7 @@ struct Statement {
|
||||
static auto MakeContinue(int line_num) -> const Statement*;
|
||||
static auto MakeMatch(
|
||||
int line_num, const Expression* exp,
|
||||
std::list<std::pair<const Expression*, const Statement*>>* clauses)
|
||||
std::list<std::pair<const Pattern*, const Statement*>>* clauses)
|
||||
-> const Statement*;
|
||||
// Returns an AST node for a continuation statement give its line number and
|
||||
// contituent parts.
|
||||
|
||||
@@ -98,6 +98,7 @@ cc_library(
|
||||
"//executable_semantics/ast:expression",
|
||||
"//executable_semantics/ast:function_definition",
|
||||
"//executable_semantics/common:tracing_flag",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -113,6 +114,7 @@ cc_library(
|
||||
"//executable_semantics/ast:function_definition",
|
||||
"//executable_semantics/ast:statement",
|
||||
"//executable_semantics/common:tracing_flag",
|
||||
"@llvm-project//llvm:Support",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -29,6 +29,12 @@ auto Action::MakeExpressionAction(const Expression* e) -> Action* {
|
||||
return act;
|
||||
}
|
||||
|
||||
auto Action::MakePatternAction(const Pattern* p) -> Action* {
|
||||
auto* act = new Action();
|
||||
act->value = PatternAction({.pattern = p});
|
||||
return act;
|
||||
}
|
||||
|
||||
auto Action::MakeStatementAction(const Statement* s) -> Action* {
|
||||
auto* act = new Action();
|
||||
act->value = StatementAction({.stmt = s});
|
||||
@@ -49,6 +55,10 @@ auto Action::GetExpressionAction() const -> const ExpressionAction& {
|
||||
return std::get<ExpressionAction>(value);
|
||||
}
|
||||
|
||||
auto Action::GetPatternAction() const -> const PatternAction& {
|
||||
return std::get<PatternAction>(value);
|
||||
}
|
||||
|
||||
auto Action::GetStatementAction() const -> const StatementAction& {
|
||||
return std::get<StatementAction>(value);
|
||||
}
|
||||
@@ -65,6 +75,9 @@ void Action::Print(llvm::raw_ostream& out) const {
|
||||
case ActionKind::ExpressionAction:
|
||||
out << *GetExpressionAction().exp;
|
||||
break;
|
||||
case ActionKind::PatternAction:
|
||||
out << *GetPatternAction().pattern;
|
||||
break;
|
||||
case ActionKind::StatementAction:
|
||||
GetStatementAction().stmt->PrintDepth(1, out);
|
||||
break;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/ast/pattern.h"
|
||||
#include "executable_semantics/ast/statement.h"
|
||||
#include "executable_semantics/interpreter/stack.h"
|
||||
#include "executable_semantics/interpreter/value.h"
|
||||
@@ -19,6 +20,7 @@ namespace Carbon {
|
||||
enum class ActionKind {
|
||||
LValAction,
|
||||
ExpressionAction,
|
||||
PatternAction,
|
||||
StatementAction,
|
||||
ValAction,
|
||||
};
|
||||
@@ -33,6 +35,11 @@ struct ExpressionAction {
|
||||
const Expression* exp;
|
||||
};
|
||||
|
||||
struct PatternAction {
|
||||
static constexpr ActionKind Kind = ActionKind::PatternAction;
|
||||
const Pattern* pattern;
|
||||
};
|
||||
|
||||
struct StatementAction {
|
||||
static constexpr ActionKind Kind = ActionKind::StatementAction;
|
||||
const Statement* stmt;
|
||||
@@ -46,6 +53,7 @@ struct ValAction {
|
||||
struct Action {
|
||||
static auto MakeLValAction(const Expression* e) -> Action*;
|
||||
static auto MakeExpressionAction(const Expression* e) -> Action*;
|
||||
static auto MakePatternAction(const Pattern* p) -> Action*;
|
||||
static auto MakeStatementAction(const Statement* s) -> Action*;
|
||||
static auto MakeValAction(const Value* v) -> Action*;
|
||||
|
||||
@@ -53,6 +61,7 @@ struct Action {
|
||||
|
||||
auto GetLValAction() const -> const LValAction&;
|
||||
auto GetExpressionAction() const -> const ExpressionAction&;
|
||||
auto GetPatternAction() const -> const PatternAction&;
|
||||
auto GetStatementAction() const -> const StatementAction&;
|
||||
auto GetValAction() const -> const ValAction&;
|
||||
|
||||
@@ -75,7 +84,9 @@ struct Action {
|
||||
std::vector<const Value*> results;
|
||||
|
||||
private:
|
||||
std::variant<LValAction, ExpressionAction, StatementAction, ValAction> value;
|
||||
std::variant<LValAction, ExpressionAction, PatternAction, StatementAction,
|
||||
ValAction>
|
||||
value;
|
||||
};
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include "executable_semantics/interpreter/frame.h"
|
||||
#include "executable_semantics/interpreter/stack.h"
|
||||
#include "llvm/ADT/StringExtras.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
|
||||
using llvm::cast;
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
@@ -114,7 +117,7 @@ void InitEnv(const Declaration& d, Env* env) {
|
||||
state->heap.AllocateValue(Value::MakeVariableType(deduced.name));
|
||||
new_env.Set(deduced.name, a);
|
||||
}
|
||||
auto pt = InterpExp(new_env, func_def.param_pattern);
|
||||
auto pt = InterpPattern(new_env, func_def.param_pattern);
|
||||
auto f = Value::MakeFunctionValue(func_def.name, pt, func_def.body);
|
||||
Address a = state->heap.AllocateValue(f);
|
||||
env->Set(func_def.name, a);
|
||||
@@ -128,9 +131,11 @@ void InitEnv(const Declaration& d, Env* env) {
|
||||
for (const Member* m : struct_def.members) {
|
||||
switch (m->tag()) {
|
||||
case MemberKind::FieldMember: {
|
||||
const auto& field = m->GetFieldMember();
|
||||
auto t = InterpExp(Env(), field.type);
|
||||
fields.push_back(make_pair(field.name, t));
|
||||
const BindingPattern* binding = m->GetFieldMember().binding;
|
||||
const Expression* type_expression =
|
||||
cast<ExpressionPattern>(binding->Type())->Expression();
|
||||
auto type = InterpExp(Env(), type_expression);
|
||||
fields.push_back(make_pair(*binding->Name(), type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -161,7 +166,7 @@ void InitEnv(const Declaration& d, Env* env) {
|
||||
// result of evaluating the initializer.
|
||||
auto v = InterpExp(*env, var.initializer);
|
||||
Address a = state->heap.AllocateValue(v);
|
||||
env->Set(var.name, a);
|
||||
env->Set(*var.binding->Name(), a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -502,9 +507,7 @@ void StepLvalue() {
|
||||
case ExpressionKind::BoolTypeLiteral:
|
||||
case ExpressionKind::TypeTypeLiteral:
|
||||
case ExpressionKind::FunctionTypeLiteral:
|
||||
case ExpressionKind::AutoTypeLiteral:
|
||||
case ExpressionKind::ContinuationTypeLiteral:
|
||||
case ExpressionKind::BindingExpression: {
|
||||
case ExpressionKind::ContinuationTypeLiteral: {
|
||||
FATAL_RUNTIME_ERROR_NO_LINE()
|
||||
<< "Can't treat expression as lvalue: " << *exp;
|
||||
}
|
||||
@@ -521,19 +524,6 @@ void StepExp() {
|
||||
llvm::outs() << "--- step exp " << *exp << " --->\n";
|
||||
}
|
||||
switch (exp->tag()) {
|
||||
case ExpressionKind::BindingExpression: {
|
||||
if (act->pos == 0) {
|
||||
frame->todo.Push(
|
||||
Action::MakeExpressionAction(exp->GetBindingExpression().type));
|
||||
act->pos++;
|
||||
} else {
|
||||
auto v = Value::MakeBindingPlaceholderValue(
|
||||
exp->GetBindingExpression().name, act->results[0]);
|
||||
frame->todo.Pop(1);
|
||||
frame->todo.Push(Action::MakeValAction(v));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::IndexExpression: {
|
||||
if (act->pos == 0) {
|
||||
// { { e[i] :: C, E, F} :: S, H}
|
||||
@@ -696,13 +686,6 @@ void StepExp() {
|
||||
frame->todo.Push(Action::MakeValAction(v));
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::AutoTypeLiteral: {
|
||||
CHECK(act->pos == 0);
|
||||
const Value* v = Value::MakeAutoType();
|
||||
frame->todo.Pop(1);
|
||||
frame->todo.Push(Action::MakeValAction(v));
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::TypeTypeLiteral: {
|
||||
CHECK(act->pos == 0);
|
||||
const Value* v = Value::MakeTypeType();
|
||||
@@ -741,6 +724,91 @@ void StepExp() {
|
||||
} // switch (exp->tag)
|
||||
}
|
||||
|
||||
void StepPattern() {
|
||||
Frame* frame = state->stack.Top();
|
||||
Action* act = frame->todo.Top();
|
||||
const Pattern* pattern = act->GetPatternAction().pattern;
|
||||
if (tracing_output) {
|
||||
llvm::outs() << "--- step pattern " << *pattern << " --->\n";
|
||||
}
|
||||
switch (pattern->Tag()) {
|
||||
case Pattern::Kind::AutoPattern: {
|
||||
CHECK(act->pos == 0);
|
||||
const Value* v = Value::MakeAutoType();
|
||||
frame->todo.Pop(1);
|
||||
frame->todo.Push(Action::MakeValAction(v));
|
||||
break;
|
||||
}
|
||||
case Pattern::Kind::BindingPattern: {
|
||||
const auto& binding = cast<BindingPattern>(*pattern);
|
||||
if (act->pos == 0) {
|
||||
frame->todo.Push(Action::MakePatternAction(binding.Type()));
|
||||
act->pos++;
|
||||
} else {
|
||||
auto v =
|
||||
Value::MakeBindingPlaceholderValue(binding.Name(), act->results[0]);
|
||||
frame->todo.Pop(1);
|
||||
frame->todo.Push(Action::MakeValAction(v));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Pattern::Kind::TuplePattern: {
|
||||
const auto& tuple = cast<TuplePattern>(*pattern);
|
||||
if (act->pos == 0) {
|
||||
if (tuple.Fields().empty()) {
|
||||
frame->todo.Pop(1);
|
||||
frame->todo.Push(Action::MakeValAction(Value::MakeTupleValue({})));
|
||||
} else {
|
||||
const Pattern* p1 = tuple.Fields()[0].pattern;
|
||||
frame->todo.Push(Action::MakePatternAction(p1));
|
||||
act->pos++;
|
||||
}
|
||||
} else if (act->pos != static_cast<int>(tuple.Fields().size())) {
|
||||
// { { vk :: (f1=v1,..., fk=[],fk+1=ek+1,...) :: C, E, F} :: S,
|
||||
// H}
|
||||
// -> { { ek+1 :: (f1=v1,..., fk=vk, fk+1=[],...) :: C, E, F} :: S,
|
||||
// H}
|
||||
const Pattern* elt = tuple.Fields()[act->pos].pattern;
|
||||
frame->todo.Push(Action::MakePatternAction(elt));
|
||||
act->pos++;
|
||||
} else {
|
||||
std::vector<TupleElement> elements;
|
||||
for (size_t i = 0; i < tuple.Fields().size(); ++i) {
|
||||
elements.push_back(
|
||||
{.name = tuple.Fields()[i].name, .value = act->results[i]});
|
||||
}
|
||||
const Value* tuple_value = Value::MakeTupleValue(std::move(elements));
|
||||
frame->todo.Pop(1);
|
||||
frame->todo.Push(Action::MakeValAction(tuple_value));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Pattern::Kind::AlternativePattern: {
|
||||
const auto& alternative = cast<AlternativePattern>(*pattern);
|
||||
if (act->pos == 0) {
|
||||
frame->todo.Push(
|
||||
Action::MakeExpressionAction(alternative.ChoiceType()));
|
||||
act->pos++;
|
||||
} else if (act->pos == 1) {
|
||||
frame->todo.Push(Action::MakePatternAction(alternative.Arguments()));
|
||||
act->pos++;
|
||||
} else {
|
||||
CHECK(act->pos == 2);
|
||||
const auto& choice_type = act->results[0]->GetChoiceType();
|
||||
frame->todo.Pop(1);
|
||||
frame->todo.Push(Action::MakeValAction(Value::MakeAlternativeValue(
|
||||
alternative.AlternativeName(), choice_type.name, act->results[1])));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Pattern::Kind::ExpressionPattern:
|
||||
frame->todo.Pop(1);
|
||||
frame->todo.Push(Action::MakeExpressionAction(
|
||||
cast<ExpressionPattern>(pattern)->Expression()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
auto IsWhileAct(Action* act) -> bool {
|
||||
switch (act->tag()) {
|
||||
case ActionKind::StatementAction:
|
||||
@@ -810,7 +878,7 @@ void StepStmt() {
|
||||
// start interpreting the pattern of the clause
|
||||
// { {v :: (match ([]) ...) :: C, E, F} :: S, H}
|
||||
// -> { {pi :: (match ([]) ...) :: C, E, F} :: S, H}
|
||||
frame->todo.Push(Action::MakeExpressionAction(c->first));
|
||||
frame->todo.Push(Action::MakePatternAction(c->first));
|
||||
act->pos++;
|
||||
} else { // try to match
|
||||
auto v = act->results[0];
|
||||
@@ -916,7 +984,7 @@ void StepStmt() {
|
||||
act->pos++;
|
||||
} else if (act->pos == 1) {
|
||||
frame->todo.Push(
|
||||
Action::MakeExpressionAction(stmt->GetVariableDefinition().pat));
|
||||
Action::MakePatternAction(stmt->GetVariableDefinition().pat));
|
||||
act->pos++;
|
||||
} else if (act->pos == 2) {
|
||||
// { { v :: (x = []) :: C, E, F} :: S, H}
|
||||
@@ -1097,6 +1165,9 @@ void Step() {
|
||||
case ActionKind::ExpressionAction:
|
||||
StepExp();
|
||||
break;
|
||||
case ActionKind::PatternAction:
|
||||
StepPattern();
|
||||
break;
|
||||
case ActionKind::StatementAction:
|
||||
StepStmt();
|
||||
break;
|
||||
@@ -1150,4 +1221,19 @@ auto InterpExp(Env values, const Expression* e) -> const Value* {
|
||||
return v;
|
||||
}
|
||||
|
||||
// Interpret a pattern at compile-time.
|
||||
auto InterpPattern(Env values, const Pattern* p) -> const Value* {
|
||||
auto todo = Stack(Action::MakePatternAction(p));
|
||||
auto* scope = new Scope(values, std::list<std::string>());
|
||||
auto* frame = new Frame("InterpPattern", Stack(scope), todo);
|
||||
state->stack = Stack(frame);
|
||||
|
||||
while (state->stack.Count() > 1 || state->stack.Top()->todo.Count() > 1 ||
|
||||
state->stack.Top()->todo.Top()->tag() != ActionKind::ValAction) {
|
||||
Step();
|
||||
}
|
||||
const Value* v = state->stack.Top()->todo.Top()->GetValAction().val;
|
||||
return v;
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/ast/declaration.h"
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/ast/pattern.h"
|
||||
#include "executable_semantics/interpreter/frame.h"
|
||||
#include "executable_semantics/interpreter/heap.h"
|
||||
#include "executable_semantics/interpreter/stack.h"
|
||||
@@ -35,6 +37,7 @@ void PrintEnv(Env values);
|
||||
|
||||
auto InterpProgram(std::list<Declaration>* fs) -> int;
|
||||
auto InterpExp(Env values, const Expression* e) -> const Value*;
|
||||
auto InterpPattern(Env values, const Pattern* p) -> const Value*;
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
|
||||
@@ -10,10 +10,16 @@
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "common/ostream.h"
|
||||
#include "executable_semantics/ast/function_definition.h"
|
||||
#include "executable_semantics/common/error.h"
|
||||
#include "executable_semantics/common/tracing_flag.h"
|
||||
#include "executable_semantics/interpreter/interpreter.h"
|
||||
#include "executable_semantics/interpreter/value.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
|
||||
using llvm::cast;
|
||||
using llvm::dyn_cast;
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
@@ -53,8 +59,8 @@ auto ReifyType(const Value* t, int line_num) -> const Expression* {
|
||||
case ValKind::TupleValue: {
|
||||
std::vector<FieldInitializer> args;
|
||||
for (const TupleElement& field : t->GetTupleValue().elements) {
|
||||
args.push_back({.name = field.name,
|
||||
.expression = ReifyType(field.value, line_num)});
|
||||
args.push_back(
|
||||
FieldInitializer(field.name, ReifyType(field.value, line_num)));
|
||||
}
|
||||
return Expression::MakeTupleLiteral(0, args);
|
||||
}
|
||||
@@ -230,58 +236,14 @@ auto Substitute(TypeEnv dict, const Value* type) -> const Value* {
|
||||
// types maps variable names to the type of their run-time value.
|
||||
// values maps variable names to their compile-time values. It is not
|
||||
// directly used in this function but is passed to InterExp.
|
||||
// expected is the type that this expression is expected to have.
|
||||
// This parameter is non-null when the expression is in a pattern context
|
||||
// and it is used to implement `auto`, otherwise it is null.
|
||||
// context says what kind of position this expression is nested in,
|
||||
// whether it's a position that expects a value, a pattern, or a type.
|
||||
auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
|
||||
const Value* expected, TCContext context) -> TCResult {
|
||||
auto TypeCheckExp(const Expression* e, TypeEnv types, Env values)
|
||||
-> TCExpression {
|
||||
if (tracing_output) {
|
||||
switch (context) {
|
||||
case TCContext::ValueContext:
|
||||
llvm::outs() << "checking expression ";
|
||||
break;
|
||||
case TCContext::PatternContext:
|
||||
llvm::outs() << "checking pattern, ";
|
||||
if (expected) {
|
||||
llvm::outs() << "expecting " << *expected;
|
||||
}
|
||||
llvm::outs() << ", ";
|
||||
break;
|
||||
case TCContext::TypeContext:
|
||||
llvm::outs() << "checking type ";
|
||||
break;
|
||||
}
|
||||
llvm::outs() << *e << "\n";
|
||||
llvm::outs() << "checking expression " << *e << "\n";
|
||||
}
|
||||
switch (e->tag()) {
|
||||
case ExpressionKind::BindingExpression: {
|
||||
if (context != TCContext::PatternContext) {
|
||||
FATAL_COMPILATION_ERROR(e->line_num)
|
||||
<< "pattern variables are only allowed in pattern context";
|
||||
}
|
||||
auto t = InterpExp(values, e->GetBindingExpression().type);
|
||||
if (t->tag() == ValKind::AutoType) {
|
||||
if (expected == nullptr) {
|
||||
FATAL_COMPILATION_ERROR(e->line_num) << " auto not allowed here";
|
||||
} else {
|
||||
t = expected;
|
||||
}
|
||||
} else if (expected) {
|
||||
ExpectType(e->line_num, "pattern variable", t, expected);
|
||||
}
|
||||
const std::optional<std::string>& name = e->GetBindingExpression().name;
|
||||
auto new_e = Expression::MakeBindingExpression(e->line_num, name,
|
||||
ReifyType(t, e->line_num));
|
||||
if (name.has_value()) {
|
||||
types.Set(*name, t);
|
||||
}
|
||||
return TCResult(new_e, t, types);
|
||||
}
|
||||
case ExpressionKind::IndexExpression: {
|
||||
auto res = TypeCheckExp(e->GetIndexExpression().aggregate, types, values,
|
||||
nullptr, TCContext::ValueContext);
|
||||
auto res = TypeCheckExp(e->GetIndexExpression().aggregate, types, values);
|
||||
auto t = res.type;
|
||||
switch (t->tag()) {
|
||||
case ValKind::TupleValue: {
|
||||
@@ -295,7 +257,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
|
||||
}
|
||||
auto new_e = Expression::MakeIndexExpression(
|
||||
e->line_num, res.exp, Expression::MakeIntLiteral(e->line_num, i));
|
||||
return TCResult(new_e, field_t, res.types);
|
||||
return TCExpression(new_e, field_t, res.types);
|
||||
}
|
||||
default:
|
||||
FATAL_COMPILATION_ERROR(e->line_num) << "expected a tuple";
|
||||
@@ -305,39 +267,21 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
|
||||
std::vector<FieldInitializer> new_args;
|
||||
std::vector<TupleElement> arg_types;
|
||||
auto new_types = types;
|
||||
if (expected && expected->tag() != ValKind::TupleValue) {
|
||||
FATAL_COMPILATION_ERROR(e->line_num) << "didn't expect a tuple";
|
||||
}
|
||||
if (expected && e->GetTupleLiteral().fields.size() !=
|
||||
expected->GetTupleValue().elements.size()) {
|
||||
FATAL_COMPILATION_ERROR(e->line_num) << "tuples of different length";
|
||||
}
|
||||
int i = 0;
|
||||
for (auto arg = e->GetTupleLiteral().fields.begin();
|
||||
arg != e->GetTupleLiteral().fields.end(); ++arg, ++i) {
|
||||
const Value* arg_expected = nullptr;
|
||||
if (expected && expected->tag() == ValKind::TupleValue) {
|
||||
if (expected->GetTupleValue().elements[i].name != arg->name) {
|
||||
FATAL_COMPILATION_ERROR(e->line_num)
|
||||
<< "field names do not match, expected "
|
||||
<< expected->GetTupleValue().elements[i].name << " but got "
|
||||
<< arg->name;
|
||||
}
|
||||
arg_expected = expected->GetTupleValue().elements[i].value;
|
||||
}
|
||||
auto arg_res = TypeCheckExp(arg->expression, new_types, values,
|
||||
arg_expected, context);
|
||||
auto arg_res = TypeCheckExp(arg->expression, new_types, values);
|
||||
new_types = arg_res.types;
|
||||
new_args.push_back({.name = arg->name, .expression = arg_res.exp});
|
||||
new_args.push_back(FieldInitializer(arg->name, arg_res.exp));
|
||||
arg_types.push_back({.name = arg->name, .value = arg_res.type});
|
||||
}
|
||||
auto tuple_e = Expression::MakeTupleLiteral(e->line_num, new_args);
|
||||
auto tuple_t = Value::MakeTupleValue(std::move(arg_types));
|
||||
return TCResult(tuple_e, tuple_t, new_types);
|
||||
return TCExpression(tuple_e, tuple_t, new_types);
|
||||
}
|
||||
case ExpressionKind::FieldAccessExpression: {
|
||||
auto res = TypeCheckExp(e->GetFieldAccessExpression().aggregate, types,
|
||||
values, nullptr, TCContext::ValueContext);
|
||||
auto res =
|
||||
TypeCheckExp(e->GetFieldAccessExpression().aggregate, types, values);
|
||||
auto t = res.type;
|
||||
switch (t->tag()) {
|
||||
case ValKind::StructType:
|
||||
@@ -346,7 +290,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
|
||||
if (e->GetFieldAccessExpression().field == field.first) {
|
||||
const Expression* new_e = Expression::MakeFieldAccessExpression(
|
||||
e->line_num, res.exp, e->GetFieldAccessExpression().field);
|
||||
return TCResult(new_e, field.second, res.types);
|
||||
return TCExpression(new_e, field.second, res.types);
|
||||
}
|
||||
}
|
||||
// Search for a method
|
||||
@@ -354,7 +298,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
|
||||
if (e->GetFieldAccessExpression().field == method.first) {
|
||||
const Expression* new_e = Expression::MakeFieldAccessExpression(
|
||||
e->line_num, res.exp, e->GetFieldAccessExpression().field);
|
||||
return TCResult(new_e, method.second, res.types);
|
||||
return TCExpression(new_e, method.second, res.types);
|
||||
}
|
||||
}
|
||||
FATAL_COMPILATION_ERROR(e->line_num)
|
||||
@@ -366,7 +310,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
|
||||
if (e->GetFieldAccessExpression().field == field.name) {
|
||||
auto new_e = Expression::MakeFieldAccessExpression(
|
||||
e->line_num, res.exp, e->GetFieldAccessExpression().field);
|
||||
return TCResult(new_e, field.value, res.types);
|
||||
return TCExpression(new_e, field.value, res.types);
|
||||
}
|
||||
}
|
||||
FATAL_COMPILATION_ERROR(e->line_num)
|
||||
@@ -380,7 +324,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
|
||||
const Expression* new_e = Expression::MakeFieldAccessExpression(
|
||||
e->line_num, res.exp, e->GetFieldAccessExpression().field);
|
||||
auto fun_ty = Value::MakeFunctionType({}, vt->second, t);
|
||||
return TCResult(new_e, fun_ty, res.types);
|
||||
return TCExpression(new_e, fun_ty, res.types);
|
||||
}
|
||||
}
|
||||
FATAL_COMPILATION_ERROR(e->line_num)
|
||||
@@ -398,24 +342,23 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
|
||||
std::optional<const Value*> type =
|
||||
types.Get(e->GetIdentifierExpression().name);
|
||||
if (type) {
|
||||
return TCResult(e, *type, types);
|
||||
return TCExpression(e, *type, types);
|
||||
} else {
|
||||
FATAL_COMPILATION_ERROR(e->line_num)
|
||||
<< "could not find `" << e->GetIdentifierExpression().name << "`";
|
||||
}
|
||||
}
|
||||
case ExpressionKind::IntLiteral:
|
||||
return TCResult(e, Value::MakeIntType(), types);
|
||||
return TCExpression(e, Value::MakeIntType(), types);
|
||||
case ExpressionKind::BoolLiteral:
|
||||
return TCResult(e, Value::MakeBoolType(), types);
|
||||
return TCExpression(e, Value::MakeBoolType(), types);
|
||||
case ExpressionKind::PrimitiveOperatorExpression: {
|
||||
std::vector<const Expression*> es;
|
||||
std::vector<const Value*> ts;
|
||||
auto new_types = types;
|
||||
for (const Expression* argument :
|
||||
e->GetPrimitiveOperatorExpression().arguments) {
|
||||
auto res = TypeCheckExp(argument, types, values, nullptr,
|
||||
TCContext::ValueContext);
|
||||
auto res = TypeCheckExp(argument, types, values);
|
||||
new_types = res.types;
|
||||
es.push_back(res.exp);
|
||||
ts.push_back(res.type);
|
||||
@@ -425,55 +368,54 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
|
||||
switch (e->GetPrimitiveOperatorExpression().op) {
|
||||
case Operator::Neg:
|
||||
ExpectType(e->line_num, "negation", Value::MakeIntType(), ts[0]);
|
||||
return TCResult(new_e, Value::MakeIntType(), new_types);
|
||||
return TCExpression(new_e, Value::MakeIntType(), new_types);
|
||||
case Operator::Add:
|
||||
ExpectType(e->line_num, "addition(1)", Value::MakeIntType(), ts[0]);
|
||||
ExpectType(e->line_num, "addition(2)", Value::MakeIntType(), ts[1]);
|
||||
return TCResult(new_e, Value::MakeIntType(), new_types);
|
||||
return TCExpression(new_e, Value::MakeIntType(), new_types);
|
||||
case Operator::Sub:
|
||||
ExpectType(e->line_num, "subtraction(1)", Value::MakeIntType(),
|
||||
ts[0]);
|
||||
ExpectType(e->line_num, "subtraction(2)", Value::MakeIntType(),
|
||||
ts[1]);
|
||||
return TCResult(new_e, Value::MakeIntType(), new_types);
|
||||
return TCExpression(new_e, Value::MakeIntType(), new_types);
|
||||
case Operator::Mul:
|
||||
ExpectType(e->line_num, "multiplication(1)", Value::MakeIntType(),
|
||||
ts[0]);
|
||||
ExpectType(e->line_num, "multiplication(2)", Value::MakeIntType(),
|
||||
ts[1]);
|
||||
return TCResult(new_e, Value::MakeIntType(), new_types);
|
||||
return TCExpression(new_e, Value::MakeIntType(), new_types);
|
||||
case Operator::And:
|
||||
ExpectType(e->line_num, "&&(1)", Value::MakeBoolType(), ts[0]);
|
||||
ExpectType(e->line_num, "&&(2)", Value::MakeBoolType(), ts[1]);
|
||||
return TCResult(new_e, Value::MakeBoolType(), new_types);
|
||||
return TCExpression(new_e, Value::MakeBoolType(), new_types);
|
||||
case Operator::Or:
|
||||
ExpectType(e->line_num, "||(1)", Value::MakeBoolType(), ts[0]);
|
||||
ExpectType(e->line_num, "||(2)", Value::MakeBoolType(), ts[1]);
|
||||
return TCResult(new_e, Value::MakeBoolType(), new_types);
|
||||
return TCExpression(new_e, Value::MakeBoolType(), new_types);
|
||||
case Operator::Not:
|
||||
ExpectType(e->line_num, "!", Value::MakeBoolType(), ts[0]);
|
||||
return TCResult(new_e, Value::MakeBoolType(), new_types);
|
||||
return TCExpression(new_e, Value::MakeBoolType(), new_types);
|
||||
case Operator::Eq:
|
||||
ExpectType(e->line_num, "==", ts[0], ts[1]);
|
||||
return TCResult(new_e, Value::MakeBoolType(), new_types);
|
||||
return TCExpression(new_e, Value::MakeBoolType(), new_types);
|
||||
case Operator::Deref:
|
||||
ExpectPointerType(e->line_num, "*", ts[0]);
|
||||
return TCResult(new_e, ts[0]->GetPointerType().type, new_types);
|
||||
return TCExpression(new_e, ts[0]->GetPointerType().type, new_types);
|
||||
case Operator::Ptr:
|
||||
ExpectType(e->line_num, "*", Value::MakeTypeType(), ts[0]);
|
||||
return TCResult(new_e, Value::MakeTypeType(), new_types);
|
||||
return TCExpression(new_e, Value::MakeTypeType(), new_types);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::CallExpression: {
|
||||
auto fun_res = TypeCheckExp(e->GetCallExpression().function, types,
|
||||
values, nullptr, TCContext::ValueContext);
|
||||
auto fun_res =
|
||||
TypeCheckExp(e->GetCallExpression().function, types, values);
|
||||
switch (fun_res.type->tag()) {
|
||||
case ValKind::FunctionType: {
|
||||
auto fun_t = fun_res.type;
|
||||
auto arg_res =
|
||||
TypeCheckExp(e->GetCallExpression().argument, fun_res.types,
|
||||
values, fun_t->GetFunctionType().param, context);
|
||||
auto arg_res = TypeCheckExp(e->GetCallExpression().argument,
|
||||
fun_res.types, values);
|
||||
auto parameter_type = fun_t->GetFunctionType().param;
|
||||
auto return_type = fun_t->GetFunctionType().ret;
|
||||
if (fun_t->GetFunctionType().deduced.size() > 0) {
|
||||
@@ -497,7 +439,7 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
|
||||
}
|
||||
auto new_e = Expression::MakeCallExpression(e->line_num, fun_res.exp,
|
||||
arg_res.exp);
|
||||
return TCResult(new_e, return_type, arg_res.types);
|
||||
return TCExpression(new_e, return_type, arg_res.types);
|
||||
}
|
||||
default: {
|
||||
FATAL_COMPILATION_ERROR(e->line_num)
|
||||
@@ -508,48 +450,157 @@ auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
|
||||
break;
|
||||
}
|
||||
case ExpressionKind::FunctionTypeLiteral: {
|
||||
switch (context) {
|
||||
case TCContext::ValueContext:
|
||||
case TCContext::TypeContext: {
|
||||
auto pt = InterpExp(values, e->GetFunctionTypeLiteral().parameter);
|
||||
auto rt = InterpExp(values, e->GetFunctionTypeLiteral().return_type);
|
||||
auto new_e = Expression::MakeFunctionTypeLiteral(
|
||||
e->line_num, ReifyType(pt, e->line_num),
|
||||
ReifyType(rt, e->line_num));
|
||||
return TCResult(new_e, Value::MakeTypeType(), types);
|
||||
}
|
||||
case TCContext::PatternContext: {
|
||||
auto param_res = TypeCheckExp(e->GetFunctionTypeLiteral().parameter,
|
||||
types, values, nullptr, context);
|
||||
auto ret_res =
|
||||
TypeCheckExp(e->GetFunctionTypeLiteral().return_type,
|
||||
param_res.types, values, nullptr, context);
|
||||
auto new_e = Expression::MakeFunctionTypeLiteral(
|
||||
e->line_num, ReifyType(param_res.type, e->line_num),
|
||||
ReifyType(ret_res.type, e->line_num));
|
||||
return TCResult(new_e, Value::MakeTypeType(), ret_res.types);
|
||||
}
|
||||
}
|
||||
auto pt = InterpExp(values, e->GetFunctionTypeLiteral().parameter);
|
||||
auto rt = InterpExp(values, e->GetFunctionTypeLiteral().return_type);
|
||||
auto new_e = Expression::MakeFunctionTypeLiteral(
|
||||
e->line_num, ReifyType(pt, e->line_num), ReifyType(rt, e->line_num));
|
||||
return TCExpression(new_e, Value::MakeTypeType(), types);
|
||||
}
|
||||
case ExpressionKind::IntTypeLiteral:
|
||||
return TCResult(e, Value::MakeTypeType(), types);
|
||||
return TCExpression(e, Value::MakeTypeType(), types);
|
||||
case ExpressionKind::BoolTypeLiteral:
|
||||
return TCResult(e, Value::MakeTypeType(), types);
|
||||
return TCExpression(e, Value::MakeTypeType(), types);
|
||||
case ExpressionKind::TypeTypeLiteral:
|
||||
return TCResult(e, Value::MakeTypeType(), types);
|
||||
case ExpressionKind::AutoTypeLiteral:
|
||||
return TCResult(e, Value::MakeTypeType(), types);
|
||||
return TCExpression(e, Value::MakeTypeType(), types);
|
||||
case ExpressionKind::ContinuationTypeLiteral:
|
||||
return TCResult(e, Value::MakeTypeType(), types);
|
||||
return TCExpression(e, Value::MakeTypeType(), types);
|
||||
}
|
||||
}
|
||||
|
||||
auto TypecheckCase(const Value* expected, const Expression* pat,
|
||||
// Equivalent to TypeCheckExp, but operates on Patterns instead of Expressions.
|
||||
// `expected` is the type that this pattern is expected to have, if the
|
||||
// surrounding context gives us that information. Otherwise, it is null.
|
||||
auto TypeCheckPattern(const Pattern* p, TypeEnv types, Env values,
|
||||
const Value* expected) -> TCPattern {
|
||||
if (tracing_output) {
|
||||
llvm::outs() << "checking pattern, ";
|
||||
if (expected) {
|
||||
llvm::outs() << "expecting " << *expected;
|
||||
}
|
||||
llvm::outs() << ", " << *p << "\n";
|
||||
}
|
||||
switch (p->Tag()) {
|
||||
case Pattern::Kind::AutoPattern: {
|
||||
return {.pattern = p, .type = Value::MakeTypeType(), .types = types};
|
||||
}
|
||||
case Pattern::Kind::BindingPattern: {
|
||||
const auto& binding = cast<BindingPattern>(*p);
|
||||
const Value* type;
|
||||
switch (binding.Type()->Tag()) {
|
||||
case Pattern::Kind::AutoPattern: {
|
||||
if (expected == nullptr) {
|
||||
FATAL_COMPILATION_ERROR(binding.LineNumber())
|
||||
<< "auto not allowed here";
|
||||
} else {
|
||||
type = expected;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Pattern::Kind::ExpressionPattern: {
|
||||
type = InterpExp(
|
||||
values, cast<ExpressionPattern>(binding.Type())->Expression());
|
||||
CHECK(type->tag() != ValKind::AutoType);
|
||||
if (expected != nullptr) {
|
||||
ExpectType(binding.LineNumber(), "pattern variable", type,
|
||||
expected);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Pattern::Kind::TuplePattern:
|
||||
case Pattern::Kind::BindingPattern:
|
||||
case Pattern::Kind::AlternativePattern:
|
||||
FATAL_COMPILATION_ERROR(binding.LineNumber())
|
||||
<< "Unsupported type pattern";
|
||||
}
|
||||
auto new_p = new BindingPattern(
|
||||
binding.LineNumber(), binding.Name(),
|
||||
new ExpressionPattern(ReifyType(type, binding.LineNumber())));
|
||||
if (binding.Name().has_value()) {
|
||||
types.Set(*binding.Name(), type);
|
||||
}
|
||||
return {.pattern = new_p, .type = type, .types = types};
|
||||
}
|
||||
case Pattern::Kind::TuplePattern: {
|
||||
const auto& tuple = cast<TuplePattern>(*p);
|
||||
std::vector<TuplePattern::Field> new_fields;
|
||||
std::vector<TupleElement> field_types;
|
||||
auto new_types = types;
|
||||
if (expected && expected->tag() != ValKind::TupleValue) {
|
||||
FATAL_COMPILATION_ERROR(p->LineNumber()) << "didn't expect a tuple";
|
||||
}
|
||||
if (expected &&
|
||||
tuple.Fields().size() != expected->GetTupleValue().elements.size()) {
|
||||
FATAL_COMPILATION_ERROR(tuple.LineNumber())
|
||||
<< "tuples of different length";
|
||||
}
|
||||
for (size_t i = 0; i < tuple.Fields().size(); ++i) {
|
||||
const TuplePattern::Field& field = tuple.Fields()[i];
|
||||
const Value* expected_field_type = nullptr;
|
||||
if (expected != nullptr) {
|
||||
const TupleElement& expected_element =
|
||||
expected->GetTupleValue().elements[i];
|
||||
if (expected_element.name != field.name) {
|
||||
FATAL_COMPILATION_ERROR(tuple.LineNumber())
|
||||
<< "field names do not match, expected "
|
||||
<< expected_element.name << " but got " << field.name;
|
||||
}
|
||||
expected_field_type = expected_element.value;
|
||||
}
|
||||
auto field_result = TypeCheckPattern(field.pattern, new_types, values,
|
||||
expected_field_type);
|
||||
new_types = field_result.types;
|
||||
new_fields.push_back(
|
||||
TuplePattern::Field(field.name, field_result.pattern));
|
||||
field_types.push_back({.name = field.name, .value = field_result.type});
|
||||
}
|
||||
auto new_tuple = new TuplePattern(tuple.LineNumber(), new_fields);
|
||||
auto tuple_t = Value::MakeTupleValue(std::move(field_types));
|
||||
return {.pattern = new_tuple, .type = tuple_t, .types = new_types};
|
||||
}
|
||||
case Pattern::Kind::AlternativePattern: {
|
||||
const auto& alternative = cast<AlternativePattern>(*p);
|
||||
const Value* choice_type = InterpExp(values, alternative.ChoiceType());
|
||||
if (choice_type->tag() != ValKind::ChoiceType) {
|
||||
FATAL_COMPILATION_ERROR(alternative.LineNumber())
|
||||
<< "alternative pattern does not name a choice type.";
|
||||
}
|
||||
if (expected != nullptr) {
|
||||
ExpectType(alternative.LineNumber(), "alternative pattern", expected,
|
||||
choice_type);
|
||||
}
|
||||
const Value* parameter_types =
|
||||
FindInVarValues(alternative.AlternativeName(),
|
||||
choice_type->GetChoiceType().alternatives);
|
||||
if (parameter_types == nullptr) {
|
||||
FATAL_COMPILATION_ERROR(alternative.LineNumber())
|
||||
<< "'" << alternative.AlternativeName()
|
||||
<< "' is not an alternative of " << choice_type;
|
||||
}
|
||||
TCPattern arg_results = TypeCheckPattern(alternative.Arguments(), types,
|
||||
values, parameter_types);
|
||||
return {.pattern = new AlternativePattern(
|
||||
alternative.LineNumber(),
|
||||
ReifyType(choice_type, alternative.LineNumber()),
|
||||
alternative.AlternativeName(),
|
||||
cast<TuplePattern>(arg_results.pattern)),
|
||||
.type = choice_type,
|
||||
.types = arg_results.types};
|
||||
}
|
||||
case Pattern::Kind::ExpressionPattern: {
|
||||
TCExpression result =
|
||||
TypeCheckExp(cast<ExpressionPattern>(p)->Expression(), types, values);
|
||||
return {.pattern = new ExpressionPattern(result.exp),
|
||||
.type = result.type,
|
||||
.types = result.types};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto TypecheckCase(const Value* expected, const Pattern* pat,
|
||||
const Statement* body, TypeEnv types, Env values,
|
||||
const Value*& ret_type)
|
||||
-> std::pair<const Expression*, const Statement*> {
|
||||
auto pat_res =
|
||||
TypeCheckExp(pat, types, values, expected, TCContext::PatternContext);
|
||||
-> std::pair<const Pattern*, const Statement*> {
|
||||
auto pat_res = TypeCheckPattern(pat, types, values, expected);
|
||||
auto res = TypeCheckStmt(body, pat_res.types, values, ret_type);
|
||||
return std::make_pair(pat, res.stmt);
|
||||
}
|
||||
@@ -568,11 +619,10 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
|
||||
}
|
||||
switch (s->tag()) {
|
||||
case StatementKind::Match: {
|
||||
auto res = TypeCheckExp(s->GetMatch().exp, types, values, nullptr,
|
||||
TCContext::ValueContext);
|
||||
auto res = TypeCheckExp(s->GetMatch().exp, types, values);
|
||||
auto res_type = res.type;
|
||||
auto new_clauses =
|
||||
new std::list<std::pair<const Expression*, const Statement*>>();
|
||||
new std::list<std::pair<const Pattern*, const Statement*>>();
|
||||
for (auto& clause : *s->GetMatch().clauses) {
|
||||
new_clauses->push_back(TypecheckCase(
|
||||
res_type, clause.first, clause.second, types, values, ret_type));
|
||||
@@ -582,8 +632,7 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
|
||||
return TCStatement(new_s, types);
|
||||
}
|
||||
case StatementKind::While: {
|
||||
auto cnd_res = TypeCheckExp(s->GetWhile().cond, types, values, nullptr,
|
||||
TCContext::ValueContext);
|
||||
auto cnd_res = TypeCheckExp(s->GetWhile().cond, types, values);
|
||||
ExpectType(s->line_num, "condition of `while`", Value::MakeBoolType(),
|
||||
cnd_res.type);
|
||||
auto body_res =
|
||||
@@ -602,11 +651,10 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
|
||||
types);
|
||||
}
|
||||
case StatementKind::VariableDefinition: {
|
||||
auto res = TypeCheckExp(s->GetVariableDefinition().init, types, values,
|
||||
nullptr, TCContext::ValueContext);
|
||||
auto res = TypeCheckExp(s->GetVariableDefinition().init, types, values);
|
||||
const Value* rhs_ty = res.type;
|
||||
auto lhs_res = TypeCheckExp(s->GetVariableDefinition().pat, types, values,
|
||||
rhs_ty, TCContext::PatternContext);
|
||||
auto lhs_res = TypeCheckPattern(s->GetVariableDefinition().pat, types,
|
||||
values, rhs_ty);
|
||||
const Statement* new_s = Statement::MakeVariableDefinition(
|
||||
s->line_num, s->GetVariableDefinition().pat, res.exp);
|
||||
return TCStatement(new_s, lhs_res.types);
|
||||
@@ -623,25 +671,21 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
|
||||
types3);
|
||||
}
|
||||
case StatementKind::Assign: {
|
||||
auto rhs_res = TypeCheckExp(s->GetAssign().rhs, types, values, nullptr,
|
||||
TCContext::ValueContext);
|
||||
auto rhs_res = TypeCheckExp(s->GetAssign().rhs, types, values);
|
||||
auto rhs_t = rhs_res.type;
|
||||
auto lhs_res = TypeCheckExp(s->GetAssign().lhs, types, values, rhs_t,
|
||||
TCContext::ValueContext);
|
||||
auto lhs_res = TypeCheckExp(s->GetAssign().lhs, types, values);
|
||||
auto lhs_t = lhs_res.type;
|
||||
ExpectType(s->line_num, "assign", lhs_t, rhs_t);
|
||||
auto new_s = Statement::MakeAssign(s->line_num, lhs_res.exp, rhs_res.exp);
|
||||
return TCStatement(new_s, lhs_res.types);
|
||||
}
|
||||
case StatementKind::ExpressionStatement: {
|
||||
auto res = TypeCheckExp(s->GetExpressionStatement().exp, types, values,
|
||||
nullptr, TCContext::ValueContext);
|
||||
auto res = TypeCheckExp(s->GetExpressionStatement().exp, types, values);
|
||||
auto new_s = Statement::MakeExpressionStatement(s->line_num, res.exp);
|
||||
return TCStatement(new_s, types);
|
||||
}
|
||||
case StatementKind::If: {
|
||||
auto cnd_res = TypeCheckExp(s->GetIf().cond, types, values, nullptr,
|
||||
TCContext::ValueContext);
|
||||
auto cnd_res = TypeCheckExp(s->GetIf().cond, types, values);
|
||||
ExpectType(s->line_num, "condition of `if`", Value::MakeBoolType(),
|
||||
cnd_res.type);
|
||||
auto thn_res =
|
||||
@@ -653,8 +697,7 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
|
||||
return TCStatement(new_s, types);
|
||||
}
|
||||
case StatementKind::Return: {
|
||||
auto res = TypeCheckExp(s->GetReturn().exp, types, values, nullptr,
|
||||
TCContext::ValueContext);
|
||||
auto res = TypeCheckExp(s->GetReturn().exp, types, values);
|
||||
if (ret_type->tag() == ValKind::AutoType) {
|
||||
// The following infers the return type from the first 'return'
|
||||
// statement. This will get more difficult with subtyping, when we
|
||||
@@ -676,9 +719,8 @@ auto TypeCheckStmt(const Statement* s, TypeEnv types, Env values,
|
||||
return TCStatement(new_continuation, types);
|
||||
}
|
||||
case StatementKind::Run: {
|
||||
TCResult argument_result =
|
||||
TypeCheckExp(s->GetRun().argument, types, values, nullptr,
|
||||
TCContext::ValueContext);
|
||||
TCExpression argument_result =
|
||||
TypeCheckExp(s->GetRun().argument, types, values);
|
||||
ExpectType(s->line_num, "argument of `run`",
|
||||
Value::MakeContinuationType(), argument_result.type);
|
||||
const Statement* new_run =
|
||||
@@ -706,7 +748,7 @@ auto CheckOrEnsureReturn(const Statement* stmt, bool void_return, int line_num)
|
||||
switch (stmt->tag()) {
|
||||
case StatementKind::Match: {
|
||||
auto new_clauses =
|
||||
new std::list<std::pair<const Expression*, const Statement*>>();
|
||||
new std::list<std::pair<const Pattern*, const Statement*>>();
|
||||
for (auto i = stmt->GetMatch().clauses->begin();
|
||||
i != stmt->GetMatch().clauses->end(); ++i) {
|
||||
auto s = CheckOrEnsureReturn(i->second, void_return, stmt->line_num);
|
||||
@@ -774,10 +816,9 @@ auto TypeCheckFunDef(const FunctionDefinition* f, TypeEnv types, Env values)
|
||||
values.Set(deduced.name, a);
|
||||
}
|
||||
// Type check the parameter pattern
|
||||
auto param_res = TypeCheckExp(f->param_pattern, types, values, nullptr,
|
||||
TCContext::PatternContext);
|
||||
auto param_res = TypeCheckPattern(f->param_pattern, types, values, nullptr);
|
||||
// Evaluate the return type expression
|
||||
auto return_type = InterpExp(values, f->return_type);
|
||||
auto return_type = InterpPattern(values, f->return_type);
|
||||
if (f->name == "main") {
|
||||
ExpectType(f->line_num, "return type of `main`", Value::MakeIntType(),
|
||||
return_type);
|
||||
@@ -786,9 +827,9 @@ auto TypeCheckFunDef(const FunctionDefinition* f, TypeEnv types, Env values)
|
||||
auto res = TypeCheckStmt(f->body, param_res.types, values, return_type);
|
||||
bool void_return = TypeEqual(return_type, Value::MakeUnitTypeVal());
|
||||
auto body = CheckOrEnsureReturn(res.stmt, void_return, f->line_num);
|
||||
return new FunctionDefinition(f->line_num, f->name, f->deduced_parameters,
|
||||
f->param_pattern,
|
||||
ReifyType(return_type, f->line_num), body);
|
||||
return new FunctionDefinition(
|
||||
f->line_num, f->name, f->deduced_parameters, f->param_pattern,
|
||||
new ExpressionPattern(ReifyType(return_type, f->line_num)), body);
|
||||
}
|
||||
|
||||
auto TypeOfFunDef(TypeEnv types, Env values, const FunctionDefinition* fun_def)
|
||||
@@ -801,13 +842,13 @@ auto TypeOfFunDef(TypeEnv types, Env values, const FunctionDefinition* fun_def)
|
||||
values.Set(deduced.name, a);
|
||||
}
|
||||
// Type check the parameter pattern
|
||||
auto param_res = TypeCheckExp(fun_def->param_pattern, types, values, nullptr,
|
||||
TCContext::PatternContext);
|
||||
auto param_res =
|
||||
TypeCheckPattern(fun_def->param_pattern, types, values, nullptr);
|
||||
// Evaluate the return type expression
|
||||
auto ret = InterpExp(values, fun_def->return_type);
|
||||
auto ret = InterpPattern(values, fun_def->return_type);
|
||||
if (ret->tag() == ValKind::AutoType) {
|
||||
auto f = TypeCheckFunDef(fun_def, types, values);
|
||||
ret = InterpExp(values, f->return_type);
|
||||
ret = InterpPattern(values, f->return_type);
|
||||
}
|
||||
return Value::MakeFunctionType(fun_def->deduced_parameters, param_res.type,
|
||||
ret);
|
||||
@@ -819,10 +860,22 @@ auto TypeOfStructDef(const StructDefinition* sd, TypeEnv /*types*/, Env ct_top)
|
||||
VarValues methods;
|
||||
for (const Member* m : sd->members) {
|
||||
switch (m->tag()) {
|
||||
case MemberKind::FieldMember:
|
||||
auto t = InterpExp(ct_top, m->GetFieldMember().type);
|
||||
fields.push_back(std::make_pair(m->GetFieldMember().name, t));
|
||||
case MemberKind::FieldMember: {
|
||||
const BindingPattern* binding = m->GetFieldMember().binding;
|
||||
if (!binding->Name().has_value()) {
|
||||
FATAL_COMPILATION_ERROR(binding->LineNumber())
|
||||
<< "Struct members must have names";
|
||||
}
|
||||
const Expression* type_expression =
|
||||
dyn_cast<ExpressionPattern>(binding->Type())->Expression();
|
||||
if (type_expression == nullptr) {
|
||||
FATAL_COMPILATION_ERROR(binding->LineNumber())
|
||||
<< "Struct members must have explicit types";
|
||||
}
|
||||
auto type = InterpExp(ct_top, type_expression);
|
||||
fields.push_back(std::make_pair(*binding->Name(), type));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Value::MakeStructType(sd->name, std::move(fields), std::move(methods));
|
||||
@@ -836,8 +889,14 @@ static auto GetName(const Declaration& d) -> const std::string& {
|
||||
return d.GetStructDeclaration().definition.name;
|
||||
case DeclarationKind::ChoiceDeclaration:
|
||||
return d.GetChoiceDeclaration().name;
|
||||
case DeclarationKind::VariableDeclaration:
|
||||
return d.GetVariableDeclaration().name;
|
||||
case DeclarationKind::VariableDeclaration: {
|
||||
const BindingPattern* binding = d.GetVariableDeclaration().binding;
|
||||
if (!binding->Name().has_value()) {
|
||||
FATAL_COMPILATION_ERROR(binding->LineNumber())
|
||||
<< "Top-level variable declarations must have names";
|
||||
}
|
||||
return *binding->Name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -872,9 +931,16 @@ auto MakeTypeChecked(const Declaration& d, const TypeEnv& types,
|
||||
// Signals a type error if the initializing expression does not have
|
||||
// the declared type of the variable, otherwise returns this
|
||||
// declaration with annotated types.
|
||||
TCResult type_checked_initializer = TypeCheckExp(
|
||||
var.initializer, types, values, nullptr, TCContext::ValueContext);
|
||||
const Value* declared_type = InterpExp(values, var.type);
|
||||
TCExpression type_checked_initializer =
|
||||
TypeCheckExp(var.initializer, types, values);
|
||||
const Expression* type =
|
||||
dyn_cast<ExpressionPattern>(var.binding->Type())->Expression();
|
||||
if (type == nullptr) {
|
||||
// TODO: consider adding support for `auto`
|
||||
FATAL_COMPILATION_ERROR(var.source_location)
|
||||
<< "Type of a top-level variable must be an expression.";
|
||||
}
|
||||
const Value* declared_type = InterpExp(values, type);
|
||||
ExpectType(var.source_location, "initializer of variable", declared_type,
|
||||
type_checked_initializer.type);
|
||||
return d;
|
||||
@@ -926,8 +992,10 @@ static void TopLevel(const Declaration& d, TypeCheckContext* tops) {
|
||||
const auto& var = d.GetVariableDeclaration();
|
||||
// Associate the variable name with it's declared type in the
|
||||
// compile-time symbol table.
|
||||
const Value* declared_type = InterpExp(tops->values, var.type);
|
||||
tops->types.Set(var.name, declared_type);
|
||||
const Expression* type =
|
||||
cast<ExpressionPattern>(var.binding->Type())->Expression();
|
||||
const Value* declared_type = InterpExp(tops->values, type);
|
||||
tops->types.Set(*var.binding->Name(), declared_type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,8 @@ namespace Carbon {
|
||||
|
||||
using TypeEnv = Dictionary<std::string, const Value*>;
|
||||
|
||||
enum class TCContext { ValueContext, PatternContext, TypeContext };
|
||||
|
||||
struct TCResult {
|
||||
TCResult(const Expression* e, const Value* t, TypeEnv types)
|
||||
struct TCExpression {
|
||||
TCExpression(const Expression* e, const Value* t, TypeEnv types)
|
||||
: exp(e), type(t), types(types) {}
|
||||
|
||||
const Expression* exp;
|
||||
@@ -28,6 +26,12 @@ struct TCResult {
|
||||
TypeEnv types;
|
||||
};
|
||||
|
||||
struct TCPattern {
|
||||
const Pattern* pattern;
|
||||
const Value* type;
|
||||
TypeEnv types;
|
||||
};
|
||||
|
||||
struct TCStatement {
|
||||
TCStatement(const Statement* s, TypeEnv types) : stmt(s), types(types) {}
|
||||
|
||||
@@ -35,8 +39,10 @@ struct TCStatement {
|
||||
TypeEnv types;
|
||||
};
|
||||
|
||||
auto TypeCheckExp(const Expression* e, TypeEnv types, Env values,
|
||||
const Value* expected, TCContext context) -> TCResult;
|
||||
auto TypeCheckExp(const Expression* e, TypeEnv types, Env values)
|
||||
-> TCExpression;
|
||||
auto TypeCheckPattern(const Pattern* p, TypeEnv types, Env values,
|
||||
const Value* expected) -> TCPattern;
|
||||
|
||||
auto TypeCheckStmt(const Statement*, TypeEnv, Env, Value const*&)
|
||||
-> TCStatement;
|
||||
|
||||
@@ -42,23 +42,7 @@ cc_library(
|
||||
|
||||
cc_library(
|
||||
name = "paren_contents",
|
||||
srcs = ["paren_contents.cpp"],
|
||||
hdrs = ["paren_contents.h"],
|
||||
deps = ["//executable_semantics/ast:expression"],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "paren_contents_test",
|
||||
srcs = ["paren_contents_test.cpp"],
|
||||
env = {
|
||||
# TODO(#580): Remove this when leaks are fixed.
|
||||
"ASAN_OPTIONS": "detect_leaks=0",
|
||||
},
|
||||
deps = [
|
||||
":paren_contents",
|
||||
"@llvm-project//llvm:gtest",
|
||||
"@llvm-project//llvm:gtest_main",
|
||||
],
|
||||
)
|
||||
|
||||
genrule(
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
// 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
|
||||
|
||||
#include "executable_semantics/syntax/paren_contents.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
const Expression* ParenContents::AsExpression(int line_number) const {
|
||||
if (fields_.size() == 1 && fields_.front().name == "" &&
|
||||
has_trailing_comma_ == HasTrailingComma::No) {
|
||||
return new Expression(*fields_.front().expression);
|
||||
} else {
|
||||
return AsTuple(line_number);
|
||||
}
|
||||
}
|
||||
|
||||
const Expression* ParenContents::AsTuple(int line_number) const {
|
||||
return Expression::MakeTupleLiteral(line_number, fields_);
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
@@ -5,47 +5,82 @@
|
||||
#ifndef EXECUTABLE_SEMANTICS_SYNTAX_PAREN_CONTENTS_H_
|
||||
#define EXECUTABLE_SEMANTICS_SYNTAX_PAREN_CONTENTS_H_
|
||||
|
||||
#include <list>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/common/error.h"
|
||||
|
||||
namespace Carbon {
|
||||
|
||||
// Represents the syntactic contents of an expression delimited by
|
||||
// parentheses. Such expressions can be interpreted as either tuples or
|
||||
// arbitrary expressions, depending on their context and the syntax of their
|
||||
// Represents the syntactic contents of an expression or pattern delimited by
|
||||
// parentheses. In those syntaxes, parentheses can be used either for grouping
|
||||
// or for forming a tuple, depending on their context and the syntax of their
|
||||
// contents; this class helps calling code resolve that ambiguity. Since that
|
||||
// ambiguity is purely syntactic, this class should only be needed during
|
||||
// parsing.
|
||||
class ParenContents {
|
||||
public:
|
||||
// Indicates whether the paren expression's contents end with a comma.
|
||||
enum class HasTrailingComma { Yes, No };
|
||||
//
|
||||
// `Term` is the type of the syntactic grouping being built, and the type of
|
||||
// the individual syntactic units it's built from; typically it should be
|
||||
// either `Expression` or `Pattern`.
|
||||
template <typename Term>
|
||||
struct ParenContents {
|
||||
struct Element {
|
||||
std::optional<std::string> name;
|
||||
const Term* term;
|
||||
};
|
||||
|
||||
// Constructs a ParenContents representing the contents of "()".
|
||||
ParenContents() : fields_({}), has_trailing_comma_(HasTrailingComma::No) {}
|
||||
// If this object represents a single term, with no name and no trailing
|
||||
// comma, this method returns that term. This typically means the parentheses
|
||||
// can be interpreted as grouping.
|
||||
auto SingleTerm() const -> std::optional<const Term*>;
|
||||
|
||||
// Constructs a ParenContents representing the given list of fields,
|
||||
// with or without a trailing comma.
|
||||
ParenContents(std::vector<FieldInitializer> fields,
|
||||
HasTrailingComma has_trailing_comma)
|
||||
: fields_(fields), has_trailing_comma_(has_trailing_comma) {}
|
||||
// Converts `elements` to std::vector<TupleElement>. TupleElement must
|
||||
// have a constructor that takes a std::string and a const Term*.
|
||||
//
|
||||
// TODO: Find a way to deduce TupleElement from Term.
|
||||
template <typename TupleElement>
|
||||
auto TupleElements(int line_num) const -> std::vector<TupleElement>;
|
||||
|
||||
ParenContents(const ParenContents&) = default;
|
||||
ParenContents& operator=(const ParenContents&) = default;
|
||||
|
||||
// Returns the paren expression, interpreted as a tuple.
|
||||
const Expression* AsTuple(int line_number) const;
|
||||
|
||||
// Returns the paren expression, with no external constraints on what kind
|
||||
// of expression it represents.
|
||||
const Expression* AsExpression(int line_number) const;
|
||||
|
||||
private:
|
||||
std::vector<FieldInitializer> fields_;
|
||||
HasTrailingComma has_trailing_comma_;
|
||||
std::vector<Element> elements;
|
||||
bool has_trailing_comma;
|
||||
};
|
||||
|
||||
// Implementation details only below here.
|
||||
|
||||
template <typename Term>
|
||||
auto ParenContents<Term>::SingleTerm() const -> std::optional<const Term*> {
|
||||
if (elements.size() == 1 && !elements.front().name.has_value() &&
|
||||
!has_trailing_comma) {
|
||||
return elements.front().term;
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Term>
|
||||
template <typename TupleElement>
|
||||
auto ParenContents<Term>::TupleElements(int line_num) const
|
||||
-> std::vector<TupleElement> {
|
||||
std::vector<TupleElement> result;
|
||||
int i = 0;
|
||||
bool seen_named_member = false;
|
||||
for (auto element : elements) {
|
||||
if (element.name.has_value()) {
|
||||
seen_named_member = true;
|
||||
result.push_back(TupleElement(*element.name, element.term));
|
||||
} else {
|
||||
if (seen_named_member) {
|
||||
FATAL_USER_ERROR(line_num)
|
||||
<< "positional members must come before named members";
|
||||
}
|
||||
result.push_back(TupleElement(std::to_string(i), element.term));
|
||||
}
|
||||
++i;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace Carbon
|
||||
|
||||
#endif // EXECUTABLE_SEMANTICS_SYNTAX_PAREN_CONTENTS_H_
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
// 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
|
||||
|
||||
#include "executable_semantics/syntax/paren_contents.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
namespace Carbon {
|
||||
namespace {
|
||||
|
||||
TEST(ParenContentsTest, EmptyAsExpression) {
|
||||
ParenContents contents;
|
||||
const Expression* expression = contents.AsExpression(/*line_num=*/1);
|
||||
EXPECT_EQ(expression->line_num, 1);
|
||||
ASSERT_EQ(expression->tag(), ExpressionKind::TupleLiteral);
|
||||
EXPECT_EQ(expression->GetTupleLiteral().fields.size(), 0);
|
||||
}
|
||||
|
||||
TEST(ParenContentsTest, EmptyAsTuple) {
|
||||
ParenContents contents;
|
||||
const Expression* tuple = contents.AsTuple(/*line_num=*/1);
|
||||
EXPECT_EQ(tuple->line_num, 1);
|
||||
ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
|
||||
EXPECT_EQ(tuple->GetTupleLiteral().fields.size(), 0);
|
||||
}
|
||||
|
||||
TEST(ParenContentsTest, UnaryNoCommaAsExpression) {
|
||||
// Equivalent to a code fragment like
|
||||
// ```
|
||||
// (
|
||||
// 42
|
||||
// )
|
||||
// ```
|
||||
ParenContents contents(
|
||||
{{.expression = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
|
||||
ParenContents::HasTrailingComma::No);
|
||||
|
||||
const Expression* expression = contents.AsExpression(/*line_num=*/1);
|
||||
EXPECT_EQ(expression->line_num, 2);
|
||||
ASSERT_EQ(expression->tag(), ExpressionKind::IntLiteral);
|
||||
}
|
||||
|
||||
TEST(ParenContentsTest, UnaryNoCommaAsTuple) {
|
||||
ParenContents contents(
|
||||
{{.expression = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
|
||||
ParenContents::HasTrailingComma::No);
|
||||
|
||||
const Expression* tuple = contents.AsTuple(/*line_num=*/1);
|
||||
EXPECT_EQ(tuple->line_num, 1);
|
||||
ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
|
||||
std::vector<FieldInitializer> fields = tuple->GetTupleLiteral().fields;
|
||||
ASSERT_EQ(fields.size(), 1);
|
||||
EXPECT_EQ(fields[0].expression->tag(), ExpressionKind::IntLiteral);
|
||||
}
|
||||
|
||||
TEST(ParenContentsTest, UnaryWithCommaAsExpression) {
|
||||
ParenContents contents(
|
||||
{{.expression = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
|
||||
ParenContents::HasTrailingComma::Yes);
|
||||
|
||||
const Expression* expression = contents.AsExpression(/*line_num=*/1);
|
||||
EXPECT_EQ(expression->line_num, 1);
|
||||
ASSERT_EQ(expression->tag(), ExpressionKind::TupleLiteral);
|
||||
std::vector<FieldInitializer> fields = expression->GetTupleLiteral().fields;
|
||||
ASSERT_EQ(fields.size(), 1);
|
||||
EXPECT_EQ(fields[0].expression->tag(), ExpressionKind::IntLiteral);
|
||||
}
|
||||
|
||||
TEST(ParenContentsTest, UnaryWithCommaAsTuple) {
|
||||
ParenContents contents(
|
||||
{{.expression = Expression::MakeIntLiteral(/*line_num=*/2, 42)}},
|
||||
ParenContents::HasTrailingComma::Yes);
|
||||
|
||||
const Expression* tuple = contents.AsTuple(/*line_num=*/1);
|
||||
EXPECT_EQ(tuple->line_num, 1);
|
||||
ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
|
||||
std::vector<FieldInitializer> fields = tuple->GetTupleLiteral().fields;
|
||||
ASSERT_EQ(fields.size(), 1);
|
||||
EXPECT_EQ(fields[0].expression->tag(), ExpressionKind::IntLiteral);
|
||||
}
|
||||
|
||||
TEST(ParenContentsTest, BinaryAsExpression) {
|
||||
ParenContents contents(
|
||||
{{.expression = Expression::MakeIntLiteral(/*line_num=*/2, 42)},
|
||||
{.expression = Expression::MakeIntLiteral(/*line_num=*/3, 42)}},
|
||||
ParenContents::HasTrailingComma::Yes);
|
||||
|
||||
const Expression* expression = contents.AsExpression(/*line_num=*/1);
|
||||
EXPECT_EQ(expression->line_num, 1);
|
||||
ASSERT_EQ(expression->tag(), ExpressionKind::TupleLiteral);
|
||||
std::vector<FieldInitializer> fields = expression->GetTupleLiteral().fields;
|
||||
ASSERT_EQ(fields.size(), 2);
|
||||
EXPECT_EQ(fields[0].expression->tag(), ExpressionKind::IntLiteral);
|
||||
EXPECT_EQ(fields[1].expression->tag(), ExpressionKind::IntLiteral);
|
||||
}
|
||||
|
||||
TEST(ParenContentsTest, BinaryAsTuple) {
|
||||
ParenContents contents(
|
||||
{{.expression = Expression::MakeIntLiteral(/*line_num=*/2, 42)},
|
||||
{.expression = Expression::MakeIntLiteral(/*line_num=*/3, 42)}},
|
||||
ParenContents::HasTrailingComma::Yes);
|
||||
|
||||
const Expression* tuple = contents.AsTuple(/*line_num=*/1);
|
||||
EXPECT_EQ(tuple->line_num, 1);
|
||||
ASSERT_EQ(tuple->tag(), ExpressionKind::TupleLiteral);
|
||||
std::vector<FieldInitializer> fields = tuple->GetTupleLiteral().fields;
|
||||
ASSERT_EQ(fields.size(), 2);
|
||||
EXPECT_EQ(fields[0].expression->tag(), ExpressionKind::IntLiteral);
|
||||
EXPECT_EQ(fields[1].expression->tag(), ExpressionKind::IntLiteral);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace Carbon
|
||||
@@ -49,6 +49,7 @@
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
#include "executable_semantics/syntax/syntax_helpers.h"
|
||||
#include "executable_semantics/syntax/parse_and_lex_context.h"
|
||||
@@ -59,7 +60,9 @@
|
||||
|
||||
#include "executable_semantics/ast/abstract_syntax_tree.h"
|
||||
#include "executable_semantics/ast/declaration.h"
|
||||
#include "executable_semantics/ast/expression.h"
|
||||
#include "executable_semantics/ast/function_definition.h"
|
||||
#include "executable_semantics/ast/pattern.h"
|
||||
#include "executable_semantics/syntax/paren_contents.h"
|
||||
|
||||
namespace Carbon {
|
||||
@@ -94,21 +97,28 @@ void yy::parser::error(const location_type&, const std::string& message) {
|
||||
%type <Carbon::GenericBinding> generic_binding
|
||||
%type <std::vector<Carbon::GenericBinding>> deduced_params
|
||||
%type <std::vector<Carbon::GenericBinding>> deduced_param_list
|
||||
%type <const Carbon::Expression*> pattern
|
||||
%type <const Carbon::Pattern*> pattern
|
||||
%type <const Carbon::Pattern*> non_expression_pattern
|
||||
%type <const Carbon::Expression*> return_type
|
||||
%type <const Carbon::Expression*> paren_expression
|
||||
%type <const Carbon::Expression*> tuple
|
||||
%type <std::optional<std::string>> binding_lhs
|
||||
%type <Carbon::Member*> variable_declaration
|
||||
%type <const Carbon::BindingPattern*> variable_declaration
|
||||
%type <Carbon::Member*> member
|
||||
%type <std::list<Carbon::Member*>> member_list
|
||||
%type <Carbon::FieldInitializer> field_initializer
|
||||
%type <Carbon::ParenContents> paren_contents
|
||||
%type <std::vector<Carbon::FieldInitializer>> paren_contents_without_trailing_comma
|
||||
%type <Carbon::ParenContents<Carbon::Expression>::Element> paren_expression_element
|
||||
%type <Carbon::ParenContents<Carbon::Expression>> paren_expression_base
|
||||
%type <Carbon::ParenContents<Carbon::Expression>> paren_expression_contents
|
||||
%type <const Carbon::Pattern*> paren_pattern
|
||||
%type <const Carbon::TuplePattern*> tuple_pattern
|
||||
%type <const Carbon::TuplePattern*> maybe_empty_tuple_pattern
|
||||
%type <Carbon::ParenContents<Carbon::Pattern>> paren_pattern_base
|
||||
%type <Carbon::ParenContents<Carbon::Pattern>::Element> paren_pattern_element
|
||||
%type <Carbon::ParenContents<Carbon::Pattern>> paren_pattern_contents
|
||||
%type <std::pair<std::string, const Carbon::Expression*>> alternative
|
||||
%type <std::list<std::pair<std::string, const Carbon::Expression*>>> alternative_list
|
||||
%type <std::pair<const Carbon::Expression*, const Carbon::Statement*>*> clause
|
||||
%type <std::list<std::pair<const Carbon::Expression*, const Carbon::Statement*>>*> clause_list
|
||||
%type <std::pair<const Carbon::Pattern*, const Carbon::Statement*>*> clause
|
||||
%type <std::list<std::pair<const Carbon::Pattern*, const Carbon::Statement*>>*> clause_list
|
||||
%token END_OF_FILE 0
|
||||
%token AND
|
||||
%token OR
|
||||
@@ -192,14 +202,6 @@ void yy::parser::error(const location_type&, const std::string& message) {
|
||||
input: declaration_list
|
||||
{ parsed_program = $1; }
|
||||
;
|
||||
pattern:
|
||||
expression
|
||||
{ $$ = $1; }
|
||||
;
|
||||
binding_lhs:
|
||||
identifier { $$ = $1; }
|
||||
| UNDERSCORE { $$ = std::nullopt; }
|
||||
;
|
||||
expression:
|
||||
identifier
|
||||
{ $$ = Carbon::Expression::MakeIdentifierExpression(yylineno, $1); }
|
||||
@@ -207,10 +209,6 @@ expression:
|
||||
{ $$ = Carbon::Expression::MakeFieldAccessExpression(yylineno, $1, $2); }
|
||||
| expression "[" expression "]"
|
||||
{ $$ = Carbon::Expression::MakeIndexExpression(yylineno, $1, $3); }
|
||||
| binding_lhs ":" expression
|
||||
{
|
||||
$$ = Carbon::Expression::MakeBindingExpression(yylineno, $1, $3);
|
||||
}
|
||||
| integer_literal
|
||||
{ $$ = Carbon::Expression::MakeIntLiteral(yylineno, $1); }
|
||||
| TRUE
|
||||
@@ -223,8 +221,6 @@ expression:
|
||||
{ $$ = Carbon::Expression::MakeBoolTypeLiteral(yylineno); }
|
||||
| TYPE
|
||||
{ $$ = Carbon::Expression::MakeTypeTypeLiteral(yylineno); }
|
||||
| AUTO
|
||||
{ $$ = Carbon::Expression::MakeAutoTypeLiteral(yylineno); }
|
||||
| CONTINUATION_TYPE
|
||||
{ $$ = Carbon::Expression::MakeContinuationTypeLiteral(yylineno); }
|
||||
| paren_expression { $$ = $1; }
|
||||
@@ -271,55 +267,133 @@ expression:
|
||||
;
|
||||
designator: "." identifier { $$ = $2; }
|
||||
;
|
||||
paren_expression: "(" paren_contents ")"
|
||||
{ $$ = $2.AsExpression(yylineno); }
|
||||
paren_expression: paren_expression_base
|
||||
{ $$ = Carbon::ExpressionFromParenContents(yylineno, $1); }
|
||||
;
|
||||
tuple: "(" paren_contents ")"
|
||||
{ $$ = $2.AsTuple(yylineno); }
|
||||
tuple: paren_expression_base
|
||||
{ $$ = Carbon::TupleExpressionFromParenContents(yylineno, $1); }
|
||||
;
|
||||
field_initializer:
|
||||
pattern
|
||||
{ $$ = Carbon::FieldInitializer({"", $1}); }
|
||||
| designator "=" pattern
|
||||
{ $$ = Carbon::FieldInitializer({$1, $3}); }
|
||||
paren_expression_element:
|
||||
expression
|
||||
{ $$ = {.name = std::nullopt, .term = $1}; }
|
||||
| designator "=" expression
|
||||
{ $$ = {.name = $1, .term = $3}; }
|
||||
;
|
||||
paren_contents:
|
||||
// Empty
|
||||
{ $$ = Carbon::ParenContents(); }
|
||||
| paren_contents_without_trailing_comma
|
||||
paren_expression_base:
|
||||
"(" ")"
|
||||
{ $$ = {.elements = {}, .has_trailing_comma = false}; }
|
||||
| "(" paren_expression_contents ")"
|
||||
{ $$ = $2; }
|
||||
| "(" paren_expression_contents "," ")"
|
||||
{
|
||||
$$ = Carbon::ParenContents($1,
|
||||
Carbon::ParenContents::HasTrailingComma::No);
|
||||
}
|
||||
| paren_contents_without_trailing_comma ","
|
||||
{
|
||||
$$ = Carbon::ParenContents($1,
|
||||
Carbon::ParenContents::HasTrailingComma::Yes);
|
||||
$$ = $2;
|
||||
$$.has_trailing_comma = true;
|
||||
}
|
||||
;
|
||||
paren_contents_without_trailing_comma:
|
||||
field_initializer
|
||||
{ $$ = {$1}; }
|
||||
| paren_contents_without_trailing_comma "," field_initializer
|
||||
paren_expression_contents:
|
||||
paren_expression_element
|
||||
{ $$ = {.elements = {$1}, .has_trailing_comma = false}; }
|
||||
| paren_expression_contents "," paren_expression_element
|
||||
{
|
||||
$$ = $1;
|
||||
$$.push_back($3);
|
||||
$$.elements.push_back($3);
|
||||
}
|
||||
;
|
||||
|
||||
// In many cases, using `pattern` recursively will result in ambiguities.
|
||||
// When that happens, it's necessary to factor out two separate productions,
|
||||
// one for when the sub-pattern is an expression, and one for when it is not.
|
||||
// To facilitate this, non-terminals besides `pattern` whose names contain
|
||||
// `pattern` are structured to be disjoint from `expression`, unless otherwise
|
||||
// specified.
|
||||
pattern:
|
||||
non_expression_pattern
|
||||
{ $$ = $1; }
|
||||
| expression
|
||||
{ $$ = new Carbon::ExpressionPattern($1); }
|
||||
;
|
||||
non_expression_pattern:
|
||||
AUTO
|
||||
{ $$ = new Carbon::AutoPattern(yylineno); }
|
||||
| binding_lhs ":" pattern
|
||||
{ $$ = new Carbon::BindingPattern(yylineno, $1, $3); }
|
||||
| paren_pattern
|
||||
{ $$ = $1; }
|
||||
| expression tuple_pattern
|
||||
{ $$ = new Carbon::AlternativePattern(yylineno, $1, $2); }
|
||||
;
|
||||
binding_lhs:
|
||||
identifier { $$ = $1; }
|
||||
| UNDERSCORE { $$ = std::nullopt; }
|
||||
;
|
||||
paren_pattern: paren_pattern_base
|
||||
{ $$ = Carbon::PatternFromParenContents(yylineno, $1); }
|
||||
;
|
||||
paren_pattern_base:
|
||||
"(" paren_pattern_contents ")"
|
||||
{ $$ = $2; }
|
||||
| "(" paren_pattern_contents "," ")"
|
||||
{
|
||||
$$ = $2;
|
||||
$$.has_trailing_comma = true;
|
||||
}
|
||||
;
|
||||
// paren_pattern is analogous to paren_expression, but in order to avoid
|
||||
// ambiguities, it must be disjoint from paren_expression, meaning it must
|
||||
// contain at least one non_expression_pattern. The structure of this rule
|
||||
// is very different from the corresponding expression rule because is has to
|
||||
// enforce that requirement.
|
||||
paren_pattern_contents:
|
||||
paren_pattern_element
|
||||
{ $$ = {.elements = {$1}, .has_trailing_comma = false }; }
|
||||
| paren_expression_contents "," paren_pattern_element
|
||||
{
|
||||
$$ = Carbon::ParenExpressionToParenPattern($1);
|
||||
$$.elements.push_back($3);
|
||||
}
|
||||
| paren_pattern_contents "," paren_expression_element
|
||||
{
|
||||
$$ = $1;
|
||||
$$.elements.push_back({.name = $3.name, .term = new Carbon::ExpressionPattern($3.term)});
|
||||
}
|
||||
| paren_pattern_contents "," paren_pattern_element
|
||||
{
|
||||
$$ = $1;
|
||||
$$.elements.push_back($3);
|
||||
}
|
||||
;
|
||||
paren_pattern_element:
|
||||
non_expression_pattern
|
||||
{ $$ = {.name = std::nullopt, .term = $1}; }
|
||||
| designator "=" non_expression_pattern
|
||||
{ $$ = {.name = $1, .term = $3}; }
|
||||
;
|
||||
tuple_pattern: paren_pattern_base
|
||||
{ $$ = Carbon::TuplePatternFromParenContents(yylineno, $1); }
|
||||
;
|
||||
// Unlike most `pattern` nonterminals, this one overlaps with `expression`,
|
||||
// so it should be used only when prior context (such as an introducer)
|
||||
// rules out the possibility of an `expression` at this point.
|
||||
maybe_empty_tuple_pattern:
|
||||
"(" ")"
|
||||
{ $$ = new Carbon::TuplePattern(yylineno, {}); }
|
||||
| tuple_pattern
|
||||
{ $$ = $1; }
|
||||
;
|
||||
clause:
|
||||
CASE pattern DBLARROW statement
|
||||
{ $$ = new std::pair<const Carbon::Expression*, const Carbon::Statement*>($2, $4); }
|
||||
{ $$ = new std::pair<const Carbon::Pattern*, const Carbon::Statement*>($2, $4); }
|
||||
| DEFAULT DBLARROW statement
|
||||
{
|
||||
auto vp = Carbon::Expression::MakeBindingExpression(
|
||||
yylineno, "_", Carbon::Expression::MakeAutoTypeLiteral(yylineno));
|
||||
$$ = new std::pair<const Carbon::Expression*, const Carbon::Statement*>(vp, $3);
|
||||
auto vp = new Carbon::BindingPattern(
|
||||
yylineno, std::nullopt, new Carbon::AutoPattern(yylineno));
|
||||
$$ = new std::pair<const Carbon::Pattern*, const Carbon::Statement*>(vp, $3);
|
||||
}
|
||||
;
|
||||
clause_list:
|
||||
// Empty
|
||||
{
|
||||
$$ = new std::list<std::pair<const Carbon::Expression*, const Carbon::Statement*>>();
|
||||
$$ = new std::list<std::pair<const Carbon::Pattern*, const Carbon::Statement*>>();
|
||||
}
|
||||
| clause clause_list
|
||||
{ $$ = $2; $$->push_front(*$1); }
|
||||
@@ -407,25 +481,30 @@ deduced_params:
|
||||
{ $$ = $2; }
|
||||
;
|
||||
function_definition:
|
||||
FN identifier deduced_params tuple return_type block
|
||||
{ $$ = Carbon::FunctionDefinition(yylineno, $2, $3, $4, $5, $6); }
|
||||
| FN identifier deduced_params tuple DBLARROW expression ";"
|
||||
FN identifier deduced_params maybe_empty_tuple_pattern return_type block
|
||||
{
|
||||
$$ = Carbon::FunctionDefinition(
|
||||
yylineno, $2, $3, $4, new Carbon::ExpressionPattern($5), $6);
|
||||
}
|
||||
| FN identifier deduced_params maybe_empty_tuple_pattern DBLARROW expression ";"
|
||||
{
|
||||
$$ = Carbon::FunctionDefinition(
|
||||
yylineno, $2, $3, $4,
|
||||
Carbon::Expression::MakeAutoTypeLiteral(yylineno),
|
||||
new Carbon::AutoPattern(yylineno),
|
||||
Carbon::Statement::MakeReturn(yylineno, $6));
|
||||
}
|
||||
;
|
||||
function_declaration:
|
||||
FN identifier deduced_params tuple return_type ";"
|
||||
{ $$ = Carbon::FunctionDefinition(yylineno, $2, $3, $4, $5, 0); }
|
||||
FN identifier deduced_params maybe_empty_tuple_pattern return_type ";"
|
||||
{
|
||||
$$ = Carbon::FunctionDefinition(
|
||||
yylineno, $2, $3, $4, new Carbon::ExpressionPattern($5), 0); }
|
||||
;
|
||||
variable_declaration: identifier ":" expression
|
||||
{ $$ = Carbon::Member::MakeFieldMember(yylineno, $1, $3); }
|
||||
variable_declaration: identifier ":" pattern
|
||||
{ $$ = new Carbon::BindingPattern(yylineno, $1, $3); }
|
||||
;
|
||||
member: VAR variable_declaration ";"
|
||||
{ $$ = $2; }
|
||||
{ $$ = Carbon::Member::MakeFieldMember(yylineno, $2); }
|
||||
;
|
||||
member_list:
|
||||
// Empty
|
||||
@@ -468,8 +547,7 @@ declaration:
|
||||
}
|
||||
| VAR variable_declaration "=" expression ";"
|
||||
{
|
||||
$$ = Carbon::Declaration::MakeVariableDeclaration(
|
||||
yylineno, $2->GetFieldMember().name, $2->GetFieldMember().type, $4);
|
||||
$$ = Carbon::Declaration::MakeVariableDeclaration(yylineno, $2, $4);
|
||||
}
|
||||
;
|
||||
declaration_list:
|
||||
|
||||
@@ -60,7 +60,6 @@ TEST_LIST = [
|
||||
"match_int",
|
||||
"match_int_default",
|
||||
"match_placeholder",
|
||||
"match_type",
|
||||
"next",
|
||||
"no_match",
|
||||
"pattern_init",
|
||||
|
||||
+3
-1
@@ -1,2 +1,4 @@
|
||||
COMPILATION ERROR: 10: tuples of different length
|
||||
COMPILATION ERROR: 10: type error in call
|
||||
expected: (0 = Int, 1 = Int)
|
||||
actual: (0 = (0 = Int, 1 = Int))
|
||||
EXIT CODE: 255
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
// 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
|
||||
|
||||
fn main() -> Int {
|
||||
var t: auto = fnty (Int,Int);
|
||||
var x: Int = 0;
|
||||
match (t) {
|
||||
case z: Type =>
|
||||
x = x + 1;
|
||||
}
|
||||
match (t) {
|
||||
case fnty(a: Type,b: Type) =>
|
||||
x = x - 1;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
result: 0
|
||||
@@ -1,2 +1,2 @@
|
||||
COMPILATION ERROR: 7: pattern variables are only allowed in pattern context
|
||||
executable_semantics/testdata/pattern_variable_fail.carbon:7: syntax error, unexpected :
|
||||
EXIT CODE: 255
|
||||
|
||||
Reference in New Issue
Block a user