add operator to comparsion (#1883)

Implement operator <, <=, > and >=
This commit is contained in:
pmqtt
2022-08-04 09:56:16 -07:00
committed by GitHub
parent 8af9ebda88
commit 63da070671
12 changed files with 418 additions and 6 deletions
+4
View File
@@ -60,6 +60,10 @@ message OperatorExpression {
BitwiseXor = 17;
BitShiftLeft = 18;
BitShiftRight = 19;
Less = 20;
LessEq = 21;
Greater = 22;
GreaterEq = 23;
}
optional Operator op = 1;
repeated Expression arguments = 2;
+12
View File
@@ -149,6 +149,18 @@ static auto OperatorToCarbon(const Fuzzing::OperatorExpression& operator_expr,
case Fuzzing::OperatorExpression::Eq:
BinaryOperatorToCarbon(arg0, " == ", arg1, out);
break;
case Fuzzing::OperatorExpression::Less:
BinaryOperatorToCarbon(arg0, " < ", arg1, out);
break;
case Fuzzing::OperatorExpression::LessEq:
BinaryOperatorToCarbon(arg0, " <= ", arg1, out);
break;
case Fuzzing::OperatorExpression::GreaterEq:
BinaryOperatorToCarbon(arg0, " >= ", arg1, out);
break;
case Fuzzing::OperatorExpression::Greater:
BinaryOperatorToCarbon(arg0, " > ", arg1, out);
break;
case Fuzzing::OperatorExpression::Or:
BinaryOperatorToCarbon(arg0, " or ", arg1, out);
+15 -1
View File
@@ -32,13 +32,15 @@ auto IntrinsicExpression::FindIntrinsic(std::string_view name,
{"delete", Intrinsic::Dealloc},
{"rand", Intrinsic::Rand},
{"int_eq", Intrinsic::IntEq},
{"int_compare", Intrinsic::IntCompare},
{"int_bit_complement", Intrinsic::IntBitComplement},
{"int_bit_and", Intrinsic::IntBitAnd},
{"int_bit_or", Intrinsic::IntBitOr},
{"int_bit_xor", Intrinsic::IntBitXor},
{"int_left_shift", Intrinsic::IntLeftShift},
{"int_right_shift", Intrinsic::IntRightShift},
{"str_eq", Intrinsic::StrEq}});
{"str_eq", Intrinsic::StrEq},
{"str_compare", Intrinsic::StrCompare}});
name.remove_prefix(std::strlen("__intrinsic_"));
auto it = intrinsic_map.find(name);
if (it == intrinsic_map.end()) {
@@ -60,6 +62,8 @@ auto IntrinsicExpression::name() const -> std::string_view {
return "__intrinsic_rand";
case IntrinsicExpression::Intrinsic::IntEq:
return "__intrinsic_int_eq";
case IntrinsicExpression::Intrinsic::IntCompare:
return "__intrinsic_int_compare";
case IntrinsicExpression::Intrinsic::IntBitComplement:
return "__intrinsic_int_bit_complement";
case IntrinsicExpression::Intrinsic::IntBitAnd:
@@ -74,6 +78,8 @@ auto IntrinsicExpression::name() const -> std::string_view {
return "__intrinsic_int_right_shift";
case IntrinsicExpression::Intrinsic::StrEq:
return "__intrinsic_str_eq";
case IntrinsicExpression::Intrinsic::StrCompare:
return "__intrinsic_str_compare";
}
}
@@ -131,6 +137,14 @@ auto ToString(Operator op) -> std::string_view {
return "==";
case Operator::Mod:
return "%";
case Operator::Less:
return "<";
case Operator::LessEq:
return "<=";
case Operator::Greater:
return ">";
case Operator::GreaterEq:
return ">=";
}
}
+6
View File
@@ -124,6 +124,10 @@ enum class Operator {
Complement,
Deref,
Eq,
Less,
LessEq,
Greater,
GreaterEq,
Mul,
Mod,
Neg,
@@ -676,6 +680,8 @@ class IntrinsicExpression : public Expression {
Rand,
IntEq,
StrEq,
StrCompare,
IntCompare,
IntBitAnd,
IntBitOr,
IntBitXor,
+178
View File
@@ -57,6 +57,10 @@ impl forall [U1:! Type, U2:! Type, U3:! Type,
// Comparison interfaces.
// ----------------------
// ----------------------
// EQUAL
// ----------------------
interface EqWith(U:! Type) {
fn Equal[me: Self](other: U) -> bool;
// TODO: NotEqual with default impl
@@ -86,6 +90,180 @@ impl String as EqWith(Self) {
}
}
// ----------------------
// COMPARE
// ----------------------
choice Ordering {
Less,
Equivalent,
Greater,
Incomparable
}
interface CompareWith(U:! Type) {
fn Compare[me: Self](u: U) -> Ordering;
// TODO: Add `default fn` for Less, LessOrEquivalent, Greater, and GreaterOrEquivalent once it's available.
}
// TODO: constraint Ordered { ... }
impl i32 as CompareWith(Self) {
fn Compare[me: Self](other: Self) -> Ordering {
var comp: i32 = __intrinsic_int_compare(me, other);
if (comp == -1) {
return Ordering.Less();
}
if (comp == 0) {
return Ordering.Equivalent();
}
if (comp == 1) {
return Ordering.Greater();
}
return Ordering.Incomparable();
}
}
impl String as CompareWith(Self) {
fn Compare[me: Self](other: Self) -> Ordering {
var comp: i32 = __intrinsic_str_compare(me, other);
if (comp == -1) {
return Ordering.Less();
}
if (comp == 0) {
return Ordering.Equivalent();
}
if (comp == 1) {
return Ordering.Greater();
}
return Ordering.Incomparable();
}
}
interface LessWith(U:! Type) {
fn Less[me: Self](other: U) -> Bool;
}
interface LessEqWith(U:! Type) {
fn LessEq[me: Self](other: U) -> Bool;
}
interface GreaterWith(U:! Type) {
fn Greater[me: Self](other: U) -> Bool;
}
interface GreaterEqWith(U:! Type) {
fn GreaterEq[me: Self](other: U) -> Bool;
}
impl i32 as LessWith(Self) {
fn Less[me: Self](other: Self) -> Bool {
var comp: Ordering = me.(CompareWith(i32).Compare)(other);
match (comp) {
case Ordering.Less() => {
return true;
}
}
return false;
}
}
impl String as LessWith(Self) {
fn Less[me: Self](other: Self) -> Bool {
var comp: Ordering = me.(CompareWith(String).Compare)(other);
match(comp){
case Ordering.Less() => {
return true;
}
}
return false;
}
}
impl i32 as LessEqWith(Self) {
fn LessEq[me: Self](other: Self) -> Bool {
var comp: Ordering = me.(CompareWith(i32).Compare)(other);
match(comp){
case Ordering.Less() => {
return true;
}
case Ordering.Equivalent() => {
return true;
}
}
return false;
}
}
impl String as LessEqWith(Self) {
fn LessEq[me: Self](other: Self) -> Bool {
var comp: Ordering = me.(CompareWith(String).Compare)(other);
match(comp){
case Ordering.Less() => {
return true;
}
case Ordering.Equivalent() => {
return true;
}
}
return false;
}
}
impl i32 as GreaterWith(Self) {
fn Greater[me: Self](other: Self) -> Bool {
var comp: Ordering = me.(CompareWith(i32).Compare)(other);
match(comp){
case Ordering.Greater() => {
return true;
}
}
return false;
}
}
impl String as GreaterWith(Self) {
fn Greater[me: Self](other: Self) -> Bool {
var comp: Ordering = me.(CompareWith(String).Compare)(other);
match(comp){
case Ordering.Greater() => {
return true;
}
}
return false;
}
}
impl i32 as GreaterEqWith(Self) {
fn GreaterEq[me: Self](other: Self) -> Bool {
var comp: Ordering = me.(CompareWith(i32).Compare)(other);
match(comp){
case Ordering.Greater() => {
return true;
}
case Ordering.Equivalent() => {
return true;
}
}
return false;
}
}
impl String as GreaterEqWith(Self) {
fn GreaterEq[me: Self](other: Self) -> Bool {
var comp: Ordering = me.(CompareWith(String).Compare)(other);
match(comp){
case Ordering.Greater() => {
return true;
}
case Ordering.Equivalent() => {
return true;
}
}
return false;
}
}
// ----------------------
// Arithmetic interfaces.
// ----------------------
+8
View File
@@ -53,6 +53,14 @@ static auto OperatorToProtoEnum(const Operator op)
return Fuzzing::OperatorExpression::And;
case Operator::Eq:
return Fuzzing::OperatorExpression::Eq;
case Operator::Less:
return Fuzzing::OperatorExpression::Less;
case Operator::LessEq:
return Fuzzing::OperatorExpression::LessEq;
case Operator::Greater:
return Fuzzing::OperatorExpression::Greater;
case Operator::GreaterEq:
return Fuzzing::OperatorExpression::GreaterEq;
case Operator::Mul:
return Fuzzing::OperatorExpression::Mul;
case Operator::Mod:
+15 -3
View File
@@ -29,6 +29,11 @@ class Builtins {
// Comparison.
EqWith,
LessWith,
LessEqWith,
GreaterWith,
GreaterEqWith,
CompareWith,
// Arithmetic.
Negate,
@@ -51,6 +56,10 @@ class Builtins {
static constexpr Builtin As = Builtin::As;
static constexpr Builtin ImplicitAs = Builtin::ImplicitAs;
static constexpr Builtin EqWith = Builtin::EqWith;
static constexpr Builtin LessWith = Builtin::LessWith;
static constexpr Builtin LessEqWith = Builtin::LessEqWith;
static constexpr Builtin GreaterWith = Builtin::GreaterWith;
static constexpr Builtin GreaterEqWith = Builtin::GreaterEqWith;
static constexpr Builtin Negate = Builtin::Negate;
static constexpr Builtin AddWith = Builtin::AddWith;
static constexpr Builtin SubWith = Builtin::SubWith;
@@ -62,6 +71,7 @@ class Builtins {
static constexpr Builtin BitXorWith = Builtin::BitXorWith;
static constexpr Builtin LeftShiftWith = Builtin::LeftShiftWith;
static constexpr Builtin RightShiftWith = Builtin::RightShiftWith;
static constexpr Builtin CompareWith = Builtin::CompareWith;
// Register a declaration that might be a builtin.
void Register(Nonnull<const Declaration*> decl);
@@ -78,9 +88,11 @@ class Builtins {
private:
static constexpr int NumBuiltins = static_cast<int>(Builtin::Last) + 1;
static constexpr const char* BuiltinNames[NumBuiltins] = {
"As", "ImplicitAs", "EqWith", "Negate", "AddWith",
"SubWith", "MulWith", "ModWith", "BitComplement", "BitAndWith",
"BitOrWith", "BitXorWith", "LeftShiftWith", "RightShiftWith"};
"As", "ImplicitAs", "EqWith", "LessWith",
"LessEqWith", "GreaterWith", "GreaterEqWith", "CompareWith",
"Negate", "AddWith", "SubWith", "MulWith",
"ModWith", "BitComplement", "BitAndWith", "BitOrWith",
"BitXorWith", "LeftShiftWith", "RightShiftWith"};
std::optional<Nonnull<const Declaration*>> builtins_[NumBuiltins] = {};
};
+34
View File
@@ -214,6 +214,10 @@ auto Interpreter::EvalPrim(Operator op, Nonnull<const Value*> static_type,
return &cast<TypeOfConstraintType>(static_type)->constraint_type();
case Operator::As:
case Operator::Eq:
case Operator::Less:
case Operator::LessEq:
case Operator::Greater:
case Operator::GreaterEq:
case Operator::BitwiseOr:
case Operator::BitwiseXor:
case Operator::BitShiftLeft:
@@ -1227,6 +1231,36 @@ auto Interpreter::StepExp() -> ErrorOr<Success> {
auto result = arena_->New<BoolValue>(lhs == rhs);
return todo_.FinishAction(result);
}
case IntrinsicExpression::Intrinsic::IntCompare: {
CARBON_CHECK(args.size() == 2);
auto lhs = cast<IntValue>(*args[0]).value();
auto rhs = cast<IntValue>(*args[1]).value();
if (lhs < rhs) {
auto result = arena_->New<IntValue>(-1);
return todo_.FinishAction(result);
}
if (lhs == rhs) {
auto result = arena_->New<IntValue>(0);
return todo_.FinishAction(result);
}
auto result = arena_->New<IntValue>(1);
return todo_.FinishAction(result);
}
case IntrinsicExpression::Intrinsic::StrCompare: {
CARBON_CHECK(args.size() == 2);
auto& lhs = cast<StringValue>(*args[0]).value();
auto& rhs = cast<StringValue>(*args[1]).value();
if (lhs < rhs) {
auto result = arena_->New<IntValue>(-1);
return todo_.FinishAction(result);
}
if (lhs == rhs) {
auto result = arena_->New<IntValue>(0);
return todo_.FinishAction(result);
}
auto result = arena_->New<IntValue>(1);
return todo_.FinishAction(result);
}
case IntrinsicExpression::Intrinsic::IntBitComplement: {
CARBON_CHECK(args.size() == 1);
return todo_.FinishAction(
+86
View File
@@ -2086,6 +2086,62 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
op.set_rewritten_form(*converted);
return Success();
}
case Operator::Less: {
ErrorOr<Nonnull<Expression*>> converted = BuildBuiltinMethodCall(
impl_scope, op.arguments()[0],
BuiltinInterfaceName{Builtins::LessWith, ts[1]},
BuiltinMethodCall{"Less", op.arguments()[1]});
if (!converted.ok()) {
// We couldn't find a matching `impl`.
return CompilationError(e->source_loc())
<< *ts[0] << " is not less comparable with " << *ts[1]
<< " (" << converted.error().message() << ")";
}
op.set_rewritten_form(*converted);
return Success();
}
case Operator::LessEq: {
ErrorOr<Nonnull<Expression*>> converted = BuildBuiltinMethodCall(
impl_scope, op.arguments()[0],
BuiltinInterfaceName{Builtins::LessEqWith, ts[1]},
BuiltinMethodCall{"LessEq", op.arguments()[1]});
if (!converted.ok()) {
// We couldn't find a matching `impl`.
return CompilationError(e->source_loc())
<< *ts[0] << " is not less equal comparable with " << *ts[1]
<< " (" << converted.error().message() << ")";
}
op.set_rewritten_form(*converted);
return Success();
}
case Operator::GreaterEq: {
ErrorOr<Nonnull<Expression*>> converted = BuildBuiltinMethodCall(
impl_scope, op.arguments()[0],
BuiltinInterfaceName{Builtins::GreaterEqWith, ts[1]},
BuiltinMethodCall{"GreaterEq", op.arguments()[1]});
if (!converted.ok()) {
// We couldn't find a matching `impl`.
return CompilationError(e->source_loc())
<< *ts[0] << " is not greater equal comparable with "
<< *ts[1] << " (" << converted.error().message() << ")";
}
op.set_rewritten_form(*converted);
return Success();
}
case Operator::Greater: {
ErrorOr<Nonnull<Expression*>> converted = BuildBuiltinMethodCall(
impl_scope, op.arguments()[0],
BuiltinInterfaceName{Builtins::GreaterWith, ts[1]},
BuiltinMethodCall{"Greater", op.arguments()[1]});
if (!converted.ok()) {
// We couldn't find a matching `impl`.
return CompilationError(e->source_loc())
<< *ts[0] << " is not greater comparable with " << *ts[1]
<< " (" << converted.error().message() << ")";
}
op.set_rewritten_form(*converted);
return Success();
}
case Operator::Deref:
CARBON_RETURN_IF_ERROR(
ExpectPointerType(e->source_loc(), "*", ts[0]));
@@ -2311,6 +2367,21 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
e->set_value_category(ValueCategory::Let);
return Success();
}
case IntrinsicExpression::Intrinsic::IntCompare: {
if (args.size() != 2) {
return CompilationError(e->source_loc())
<< "__intrinsic_int_compare takes 2 arguments";
}
CARBON_RETURN_IF_ERROR(ExpectExactType(
e->source_loc(), "__intrinsic_int_compare argument 1",
arena_->New<IntType>(), &args[0]->static_type(), impl_scope));
CARBON_RETURN_IF_ERROR(ExpectExactType(
e->source_loc(), "__intrinsic_int_compare argument 2",
arena_->New<IntType>(), &args[1]->static_type(), impl_scope));
e->set_static_type(arena_->New<IntType>());
e->set_value_category(ValueCategory::Let);
return Success();
}
case IntrinsicExpression::Intrinsic::StrEq: {
if (args.size() != 2) {
return CompilationError(e->source_loc())
@@ -2326,6 +2397,21 @@ auto TypeChecker::TypeCheckExp(Nonnull<Expression*> e,
e->set_value_category(ValueCategory::Let);
return Success();
}
case IntrinsicExpression::Intrinsic::StrCompare: {
if (args.size() != 2) {
return CompilationError(e->source_loc())
<< "__intrinsic_str_compare takes 2 arguments";
}
CARBON_RETURN_IF_ERROR(ExpectExactType(
e->source_loc(), "__intrinsic_str_compare argument 1",
arena_->New<StringType>(), &args[0]->static_type(), impl_scope));
CARBON_RETURN_IF_ERROR(ExpectExactType(
e->source_loc(), "__intrinsic_str_compare argument 2",
arena_->New<StringType>(), &args[1]->static_type(), impl_scope));
e->set_static_type(arena_->New<IntType>());
e->set_value_category(ValueCategory::Let);
return Success();
}
case IntrinsicExpression::Intrinsic::IntBitComplement:
if (args.size() != 1) {
return CompilationError(e->source_loc())
+8
View File
@@ -65,6 +65,8 @@ FALSE "false"
FN "fn"
FN_TYPE "__Fn"
FORALL "forall"
GREATER ">"
GREATER_EQUAL ">="
GREATER_GREATER ">>"
IF "if"
IMPL "impl"
@@ -74,6 +76,8 @@ IS "is"
LEFT_CURLY_BRACE "{"
LEFT_PARENTHESIS "("
LEFT_SQUARE_BRACKET "["
LESS "<"
LESS_EQUAL "<="
LESS_LESS "<<"
LET "let"
LIBRARY "library"
@@ -161,7 +165,9 @@ operand_start [(A-Za-z0-9_\"]
{FN_TYPE} { return CARBON_SIMPLE_TOKEN(FN_TYPE); }
{FN} { return CARBON_SIMPLE_TOKEN(FN); }
{FORALL} { return CARBON_SIMPLE_TOKEN(FORALL); }
{GREATER_EQUAL} { return CARBON_SIMPLE_TOKEN(GREATER_EQUAL); }
{GREATER_GREATER} { return CARBON_SIMPLE_TOKEN(GREATER_GREATER); }
{GREATER} { return CARBON_SIMPLE_TOKEN(GREATER); }
{IF} { return CARBON_SIMPLE_TOKEN(IF); }
{IMPL} { return CARBON_SIMPLE_TOKEN(IMPL); }
{IMPORT} { return CARBON_SIMPLE_TOKEN(IMPORT); }
@@ -170,7 +176,9 @@ operand_start [(A-Za-z0-9_\"]
{LEFT_CURLY_BRACE} { return CARBON_SIMPLE_TOKEN(LEFT_CURLY_BRACE); }
{LEFT_PARENTHESIS} { return CARBON_SIMPLE_TOKEN(LEFT_PARENTHESIS); }
{LEFT_SQUARE_BRACKET} { return CARBON_SIMPLE_TOKEN(LEFT_SQUARE_BRACKET); }
{LESS_EQUAL} { return CARBON_SIMPLE_TOKEN(LESS_EQUAL); }
{LESS_LESS} { return CARBON_SIMPLE_TOKEN(LESS_LESS); }
{LESS} { return CARBON_SIMPLE_TOKEN(LESS); }
{LET} { return CARBON_SIMPLE_TOKEN(LET); }
{LIBRARY} { return CARBON_SIMPLE_TOKEN(LIBRARY); }
{MATCH} { return CARBON_SIMPLE_TOKEN(MATCH); }
+20 -2
View File
@@ -190,6 +190,7 @@
%type <std::vector<Nonnull<AlternativeSignature*>>> alternative_list_contents
%type <BisonWrap<Match::Clause>> clause
%type <std::vector<Match::Clause>> clause_list
%type <Operator> comparison_operator;
%token
// Most tokens have their spelling defined in lexer.lpp.
@@ -228,6 +229,8 @@
FN
FN_TYPE
FORALL
GREATER
GREATER_EQUAL
GREATER_GREATER
IF
IMPL
@@ -237,6 +240,8 @@
LEFT_CURLY_BRACE
LEFT_PARENTHESIS
LEFT_SQUARE_BRACKET
LESS
LESS_EQUAL
LESS_LESS
LET
LIBRARY
@@ -584,12 +589,25 @@ comparison_operand:
ref_deref_expression
| value_expression
;
/* TODO: Add NOT_EQUAL */
comparison_operator:
EQUAL_EQUAL
{ $$ = Operator::Eq; }
| LESS
{ $$ = Operator::Less; }
| LESS_EQUAL
{ $$ = Operator::LessEq; }
| GREATER
{ $$ = Operator::Greater; }
| GREATER_EQUAL
{ $$ = Operator::GreaterEq; }
;
comparison_expression:
value_expression
| comparison_operand EQUAL_EQUAL comparison_operand
| comparison_operand comparison_operator comparison_operand
{
$$ = arena->New<OperatorExpression>(
context.source_loc(), Operator::Eq,
context.source_loc(), $2,
std::vector<Nonnull<Expression*>>({$1, $3}));
}
;
+32
View File
@@ -0,0 +1,32 @@
// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
// RUN: %{explorer} %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes=false %s
// RUN: %{explorer} --parser_debug --trace_file=- %s 2>&1 | \
// RUN: %{FileCheck} --match-full-lines --allow-unused-prefixes %s
// AUTOUPDATE: %{explorer} %s
// CHECK: strings less: 1
// CHECK: ints less: 0
// CHECK: strings less eq: 1
// CHECK: ints less eq: 1
// CHECK: strings greater: 0
// CHECK: ints greater: 0
// CHECK: strings greater eq: 0
// CHECK: ints greater eq: 1
// CHECK: result: 0
package ExplorerTest api;
fn Main() -> i32 {
Print("strings less: {0}", if "hello" < "world" then 1 else 0);
Print("ints less: {0}", if 1 < 1 then 1 else 0);
Print("strings less eq: {0}", if "hello" <= "world" then 1 else 0);
Print("ints less eq: {0}", if 1 <= 1 then 1 else 0);
Print("strings greater: {0}", if "hello" > "world" then 1 else 0);
Print("ints greater: {0}", if 1 > 1 then 1 else 0);
Print("strings greater eq: {0}", if "hello" >= "world" then 1 else 0);
Print("ints greater eq: {0}", if 1 >= 1 then 1 else 0);
return 0;
}