diff --git a/common/fuzzing/carbon.proto b/common/fuzzing/carbon.proto index 0f48dc844966..a2a91f51e287 100644 --- a/common/fuzzing/carbon.proto +++ b/common/fuzzing/carbon.proto @@ -54,6 +54,7 @@ message OperatorExpression { Ptr = 11; Combine = 12; As = 13; + Mod = 14; } 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 06d6630677c5..60faad7cde33 100644 --- a/common/fuzzing/proto_to_carbon.cpp +++ b/common/fuzzing/proto_to_carbon.cpp @@ -117,6 +117,10 @@ static auto OperatorToCarbon(const Fuzzing::OperatorExpression& operator_expr, BinaryOperatorToCarbon(arg0, " * ", arg1, out); break; + case Fuzzing::OperatorExpression::Mod: + BinaryOperatorToCarbon(arg0, " % ", arg1, out); + break; + case Fuzzing::OperatorExpression::Ptr: PostfixUnaryOperatorToCarbon(arg0, "*", out); break; diff --git a/explorer/ast/expression.cpp b/explorer/ast/expression.cpp index a986f2e78573..1db464dae13f 100644 --- a/explorer/ast/expression.cpp +++ b/explorer/ast/expression.cpp @@ -81,6 +81,8 @@ auto ToString(Operator op) -> std::string_view { return "or"; case Operator::Eq: return "=="; + case Operator::Mod: + return "%"; } } diff --git a/explorer/ast/expression.h b/explorer/ast/expression.h index e9b1331399f4..acb8564eb4af 100644 --- a/explorer/ast/expression.h +++ b/explorer/ast/expression.h @@ -120,6 +120,7 @@ enum class Operator { Deref, Eq, Mul, + Mod, Neg, Not, Or, diff --git a/explorer/fuzzing/ast_to_proto.cpp b/explorer/fuzzing/ast_to_proto.cpp index d14e0c7ddbf5..b9dfaacc8304 100644 --- a/explorer/fuzzing/ast_to_proto.cpp +++ b/explorer/fuzzing/ast_to_proto.cpp @@ -55,6 +55,8 @@ static auto OperatorToProtoEnum(const Operator op) return Fuzzing::OperatorExpression::Eq; case Operator::Mul: return Fuzzing::OperatorExpression::Mul; + case Operator::Mod: + return Fuzzing::OperatorExpression::Mod; case Operator::Or: return Fuzzing::OperatorExpression::Or; case Operator::Sub: diff --git a/explorer/interpreter/interpreter.cpp b/explorer/interpreter/interpreter.cpp index 770a67adae13..35e2c71a99ae 100644 --- a/explorer/interpreter/interpreter.cpp +++ b/explorer/interpreter/interpreter.cpp @@ -173,7 +173,6 @@ void Interpreter::PrintState(llvm::raw_ostream& out) { out << "\nmemory: " << heap_; out << "\n}\n"; } - auto Interpreter::EvalPrim(Operator op, Nonnull static_type, const std::vector>& args, SourceLocation source_loc) @@ -190,6 +189,9 @@ auto Interpreter::EvalPrim(Operator op, Nonnull static_type, case Operator::Mul: return arena_->New(cast(*args[0]).value() * cast(*args[1]).value()); + case Operator::Mod: + return arena_->New(cast(*args[0]).value() % + cast(*args[1]).value()); case Operator::Not: return arena_->New(!cast(*args[0]).value()); case Operator::And: diff --git a/explorer/interpreter/type_checker.cpp b/explorer/interpreter/type_checker.cpp index 9c02cf12a8a3..144b3fb9890a 100644 --- a/explorer/interpreter/type_checker.cpp +++ b/explorer/interpreter/type_checker.cpp @@ -1995,6 +1995,17 @@ auto TypeChecker::TypeCheckExp(Nonnull e, return handle_binary_arithmetic(Builtins::SubWith); case Operator::Mul: return handle_binary_arithmetic(Builtins::MulWith); + case Operator::Mod: + CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "modulo(1)", + arena_->New(), ts[0], + impl_scope)); + CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "modulo(2)", + arena_->New(), ts[1], + impl_scope)); + op.set_static_type(arena_->New()); + op.set_value_category(ValueCategory::Let); + + return Success(); case Operator::And: CARBON_RETURN_IF_ERROR(ExpectExactType(e->source_loc(), "&&(1)", arena_->New(), ts[0], diff --git a/explorer/syntax/lexer.lpp b/explorer/syntax/lexer.lpp index 0b5b334d7662..cda7acbeb563 100644 --- a/explorer/syntax/lexer.lpp +++ b/explorer/syntax/lexer.lpp @@ -73,6 +73,7 @@ LET "let" LIBRARY "library" MATCH "match" MINUS "-" +MODULO "%" NOT "not" OR "or" PACKAGE "package" @@ -161,6 +162,7 @@ operand_start [(A-Za-z0-9_\"] {LIBRARY} { return CARBON_SIMPLE_TOKEN(LIBRARY); } {MATCH} { return CARBON_SIMPLE_TOKEN(MATCH); } {MINUS} { return CARBON_SIMPLE_TOKEN(MINUS); } +{MODULO} { return CARBON_SIMPLE_TOKEN(MODULO); } {NOT} { return CARBON_SIMPLE_TOKEN(NOT); } {OR} { return CARBON_SIMPLE_TOKEN(OR); } {PACKAGE} { return CARBON_SIMPLE_TOKEN(PACKAGE); } diff --git a/explorer/syntax/parser.ypp b/explorer/syntax/parser.ypp index 5ef6940a4c55..62f867ebaa29 100644 --- a/explorer/syntax/parser.ypp +++ b/explorer/syntax/parser.ypp @@ -130,6 +130,8 @@ %type > additive_operand %type > additive_lhs %type > additive_expression +%type > modulo_operand +%type > modulo_expression %type > as_operand %type > as_expression %type > unimpl_expression @@ -226,6 +228,7 @@ LIBRARY MATCH MINUS + MODULO NOT OR PACKAGE @@ -446,6 +449,18 @@ multiplicative_expression: std::vector>({$1, $3})); } ; +modulo_operand: + ref_deref_expression +| minus_expression +; +modulo_expression: + modulo_operand MODULO modulo_operand + { + $$ = arena->New( + context.source_loc(), Operator::Mod, + std::vector>({$1, $3})); + } +; additive_operand: ref_deref_expression | multiplicative_expression @@ -492,6 +507,7 @@ unimpl_expression: value_expression: // ref_deref_expression excluded due to precedence diamond. additive_expression +| modulo_expression | as_expression | combine_expression | fn_type_expression diff --git a/explorer/testdata/operator/operator-modulo.carbon b/explorer/testdata/operator/operator-modulo.carbon new file mode 100644 index 000000000000..8350cea2c89f --- /dev/null +++ b/explorer/testdata/operator/operator-modulo.carbon @@ -0,0 +1,56 @@ +// 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: result: 0 + +package ExplorerTest api; + +fn identFn( x: i32 ) -> i32{ + return x; +} + +fn Main() -> i32 { + var x : i32 = 7 % 2; + + + if( not (x == 1) ){ + return 1; + } + x = (3*3) % 3; + if( not (x == 0)){ + return 2; + } + + x = (4+4+4+4) % (2+1); + if( not (x == 1) ){ + return 3; + } + + x = 15 % (identFn(2)+1); + if( not (x == 0)){ + return 4; + } + + x = -15 % 7; + if( not (x == -1)){ + return 5; + } + + x = 15 % -7; + if( not ( x == 1)){ + return 6; + } + + x = -15 % -identFn(7); + if( not (x == -1)){ + return 7; + } + + return 0; +}