From 63da0706711995c01435150bf6442c2be3d630c4 Mon Sep 17 00:00:00 2001 From: pmqtt <51272730+pmqtt@users.noreply.github.com> Date: Thu, 4 Aug 2022 18:56:16 +0200 Subject: [PATCH] add operator to comparsion (#1883) Implement operator <, <=, > and >= --- common/fuzzing/carbon.proto | 4 + common/fuzzing/proto_to_carbon.cpp | 12 ++ explorer/ast/expression.cpp | 16 +- explorer/ast/expression.h | 6 + explorer/data/prelude.carbon | 178 ++++++++++++++++++ explorer/fuzzing/ast_to_proto.cpp | 8 + explorer/interpreter/builtins.h | 18 +- explorer/interpreter/interpreter.cpp | 34 ++++ explorer/interpreter/type_checker.cpp | 86 +++++++++ explorer/syntax/lexer.lpp | 8 + explorer/syntax/parser.ypp | 22 ++- .../comparison/builtin_comparsion.carbon | 32 ++++ 12 files changed, 418 insertions(+), 6 deletions(-) create mode 100644 explorer/testdata/comparison/builtin_comparsion.carbon diff --git a/common/fuzzing/carbon.proto b/common/fuzzing/carbon.proto index a6ab747e17b4..b60c8dc4c478 100644 --- a/common/fuzzing/carbon.proto +++ b/common/fuzzing/carbon.proto @@ -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; diff --git a/common/fuzzing/proto_to_carbon.cpp b/common/fuzzing/proto_to_carbon.cpp index 8185c62d09f5..7204e6026e0e 100644 --- a/common/fuzzing/proto_to_carbon.cpp +++ b/common/fuzzing/proto_to_carbon.cpp @@ -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); diff --git a/explorer/ast/expression.cpp b/explorer/ast/expression.cpp index 7a7bf933abcd..607f213dc5ba 100644 --- a/explorer/ast/expression.cpp +++ b/explorer/ast/expression.cpp @@ -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 ">="; } } diff --git a/explorer/ast/expression.h b/explorer/ast/expression.h index 14e1ed1e0f7b..68efcd22179f 100644 --- a/explorer/ast/expression.h +++ b/explorer/ast/expression.h @@ -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, diff --git a/explorer/data/prelude.carbon b/explorer/data/prelude.carbon index 542244259862..d0f3244dd725 100644 --- a/explorer/data/prelude.carbon +++ b/explorer/data/prelude.carbon @@ -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. // ---------------------- diff --git a/explorer/fuzzing/ast_to_proto.cpp b/explorer/fuzzing/ast_to_proto.cpp index a7c4edb8bcc6..d5f4795320ae 100644 --- a/explorer/fuzzing/ast_to_proto.cpp +++ b/explorer/fuzzing/ast_to_proto.cpp @@ -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: diff --git a/explorer/interpreter/builtins.h b/explorer/interpreter/builtins.h index ca43b3f70074..0e77665b456b 100644 --- a/explorer/interpreter/builtins.h +++ b/explorer/interpreter/builtins.h @@ -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 decl); @@ -78,9 +88,11 @@ class Builtins { private: static constexpr int NumBuiltins = static_cast(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> builtins_[NumBuiltins] = {}; }; diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index f0130719fe4d..088954a7e317 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -214,6 +214,10 @@ auto Interpreter::EvalPrim(Operator op, Nonnull static_type, return &cast(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 { auto result = arena_->New(lhs == rhs); return todo_.FinishAction(result); } + case IntrinsicExpression::Intrinsic::IntCompare: { + CARBON_CHECK(args.size() == 2); + auto lhs = cast(*args[0]).value(); + auto rhs = cast(*args[1]).value(); + if (lhs < rhs) { + auto result = arena_->New(-1); + return todo_.FinishAction(result); + } + if (lhs == rhs) { + auto result = arena_->New(0); + return todo_.FinishAction(result); + } + auto result = arena_->New(1); + return todo_.FinishAction(result); + } + case IntrinsicExpression::Intrinsic::StrCompare: { + CARBON_CHECK(args.size() == 2); + auto& lhs = cast(*args[0]).value(); + auto& rhs = cast(*args[1]).value(); + if (lhs < rhs) { + auto result = arena_->New(-1); + return todo_.FinishAction(result); + } + if (lhs == rhs) { + auto result = arena_->New(0); + return todo_.FinishAction(result); + } + auto result = arena_->New(1); + return todo_.FinishAction(result); + } case IntrinsicExpression::Intrinsic::IntBitComplement: { CARBON_CHECK(args.size() == 1); return todo_.FinishAction( diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index b32028612a1c..0e8d077fae17 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -2086,6 +2086,62 @@ auto TypeChecker::TypeCheckExp(Nonnull e, op.set_rewritten_form(*converted); return Success(); } + case Operator::Less: { + ErrorOr> 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> 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> 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> 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 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(), &args[0]->static_type(), impl_scope)); + CARBON_RETURN_IF_ERROR(ExpectExactType( + e->source_loc(), "__intrinsic_int_compare argument 2", + arena_->New(), &args[1]->static_type(), impl_scope)); + e->set_static_type(arena_->New()); + 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 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(), &args[0]->static_type(), impl_scope)); + CARBON_RETURN_IF_ERROR(ExpectExactType( + e->source_loc(), "__intrinsic_str_compare argument 2", + arena_->New(), &args[1]->static_type(), impl_scope)); + e->set_static_type(arena_->New()); + e->set_value_category(ValueCategory::Let); + return Success(); + } case IntrinsicExpression::Intrinsic::IntBitComplement: if (args.size() != 1) { return CompilationError(e->source_loc()) diff --git a/explorer/syntax/lexer.lpp b/explorer/syntax/lexer.lpp index 6c1ee2b204a3..3ccfa550b707 100644 --- a/explorer/syntax/lexer.lpp +++ b/explorer/syntax/lexer.lpp @@ -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); } diff --git a/explorer/syntax/parser.ypp b/explorer/syntax/parser.ypp index b6ffa628edac..85e268dc2917 100644 --- a/explorer/syntax/parser.ypp +++ b/explorer/syntax/parser.ypp @@ -190,6 +190,7 @@ %type >> alternative_list_contents %type > clause %type > clause_list +%type 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( - context.source_loc(), Operator::Eq, + context.source_loc(), $2, std::vector>({$1, $3})); } ; diff --git a/explorer/testdata/comparison/builtin_comparsion.carbon b/explorer/testdata/comparison/builtin_comparsion.carbon new file mode 100644 index 000000000000..5dddd2c890e2 --- /dev/null +++ b/explorer/testdata/comparison/builtin_comparsion.carbon @@ -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; +}