namespace Fortran::evaluate {
+template<typename D, typename R, typename... O>
+std::ostream &Operation<D, R, O...>::Dump(std::ostream &o) const {
+ operand<0>().Dump(o << derived().prefix_);
+ if constexpr (operands() > 1) {
+ operand<1>().Dump(o << infix_);
+ }
+ return o << derived().postfix_;
+}
+
+template<typename D, typename R, typename... O>
+auto Operation<D, R, O...>::Fold(FoldingContext &context)
+ -> std::optional<Scalar<Result>> {
+ auto c0{operand<0>().Fold(context)};
+ if constexpr (operands() == 1) {
+ if (c0.has_value()) {
+ return derived().FoldScalar(context, *c0);
+ }
+ } else {
+ auto c1{operand<1>().Fold(context)};
+ if (c0.has_value() && c1.has_value()) {
+ return derived().FoldScalar(context, *c0, *c1);
+ }
+ }
+ return std::nullopt;
+}
+
+template<typename TO, typename FROM>
+auto Convert<TO, FROM>::FoldScalar(FoldingContext &context,
+ const Scalar<Operand> &c) -> std::optional<Scalar<Result>> {
+ if constexpr (std::is_same_v<Result, Operand>) {
+ return {c};
+ }
+ if constexpr (std::is_same_v<Result, SomeType>) {
+ using Generic = SomeKind<Operand::category>;
+ if constexpr (std::is_same_v<Operand, Generic>) {
+ return {Scalar<Result>{c}};
+ } else {
+ return {Scalar<Result>{Generic{c}}};
+ }
+ }
+ if constexpr (std::is_same_v<Operand, SomeType>) {
+ return std::visit(
+ [&](const auto &x) -> std::optional<Scalar<Result>> {
+ return Convert<Result, std::decay_t<decltype(x)>>::FoldScalar(
+ context, x);
+ },
+ c.u);
+ }
+ // Result and Operand are distinct types with known categories.
+ if constexpr (std::is_same_v<Result, SomeKind<Result::category>>) {
+ if constexpr (Result::category == Operand::category) {
+ return {Scalar<Result>{c}};
+ }
+ return std::nullopt;
+ }
+ // Result is a specific type.
+ if constexpr (std::is_same_v<Operand, SomeKind<Operand::category>>) {
+ return std::visit(
+ [&](const auto &x) -> std::optional<Scalar<Result>> {
+ return Convert<Result,
+ ScalarValueType<std::decay_t<decltype(x)>>>::FoldScalar(context,
+ x);
+ },
+ c.u);
+ }
+ // Result and Operand are distinct specific types.
+ if constexpr (Result::category == TypeCategory::Integer) {
+ if constexpr (Operand::category == TypeCategory::Integer) {
+ auto converted{Scalar<Result>::ConvertSigned(c)};
+ if (converted.overflow) {
+ context.messages.Say("INTEGER to INTEGER conversion overflowed"_en_US);
+ } else {
+ return {std::move(converted.value)};
+ }
+ }
+ if constexpr (Operand::category == TypeCategory::Real) {
+ auto converted{c.template ToInteger<Scalar<Result>>()};
+ if (converted.flags.test(RealFlag::InvalidArgument)) {
+ context.messages.Say(
+ "REAL to INTEGER conversion: invalid argument"_en_US);
+ } else if (converted.flags.test(RealFlag::Overflow)) {
+ context.messages.Say("REAL to INTEGER conversion overflowed"_en_US);
+ } else {
+ return {std::move(converted.value)};
+ }
+ }
+ }
+ if constexpr (Result::category == TypeCategory::Real) {
+ if constexpr (Operand::category == TypeCategory::Integer) {
+ auto converted{Scalar<Result>::FromInteger(c)};
+ RealFlagWarnings(context, converted.flags, "INTEGER to REAL conversion");
+ return {std::move(converted.value)};
+ }
+ if constexpr (Operand::category == TypeCategory::Real) {
+ auto converted{Scalar<Result>::Convert(c)};
+ RealFlagWarnings(context, converted.flags, "REAL to REAL conversion");
+ return {std::move(converted.value)};
+ }
+ }
+ return std::nullopt;
+}
+
// Dumping
template<typename... A>
std::ostream &DumpExprWithType(std::ostream &o, const std::variant<A...> &u) {
common::visitors{[&](const Scalar<Result> &n) { o << n.SignedDecimal(); },
[&](const CopyableIndirection<DataRef> &d) { d->Dump(o); },
[&](const CopyableIndirection<FunctionRef> &d) { d->Dump(o); },
- [&](const Parentheses &p) { p.Dump(o, "("); },
+ [&](const Parentheses<Result> &p) { p.Dump(o); },
[&](const Negate &n) { n.Dump(o, "(-"); },
[&](const Add &a) { a.Dump(o, "+"); },
[&](const Subtract &s) { s.Dump(o, "-"); },
[&](const CopyableIndirection<DataRef> &d) { d->Dump(o); },
[&](const CopyableIndirection<ComplexPart> &d) { d->Dump(o); },
[&](const CopyableIndirection<FunctionRef> &d) { d->Dump(o); },
- [&](const Parentheses &p) { p.Dump(o, "("); },
+ [&](const Parentheses<Result> &p) { p.Dump(o); },
[&](const Negate &n) { n.Dump(o, "(-"); },
[&](const Add &a) { a.Dump(o, "+"); },
[&](const Subtract &s) { s.Dump(o, "-"); },
},
[&](const CopyableIndirection<DataRef> &d) { d->Dump(o); },
[&](const CopyableIndirection<FunctionRef> &d) { d->Dump(o); },
- [&](const Parentheses &p) { p.Dump(o, "("); },
+ [&](const Parentheses<Result> &p) { p.Dump(o); },
[&](const Negate &n) { n.Dump(o, "(-"); },
[&](const Add &a) { a.Dump(o, "+"); },
[&](const Subtract &s) { s.Dump(o, "-"); },
std::visit(common::visitors{[&](const Scalar<Result> &s) {
o << parser::QuoteCharacterLiteral(s);
},
+ // [&](const Parentheses<Result> &p) { p.Dump(o); },
[&](const Concat &concat) { concat.Dump(o, "//"); },
[&](const Max &m) { m.Dump(o, ",", "MAX("); },
[&](const Min &m) { m.Dump(o, ",", "MIN("); },
},
[&](const CopyableIndirection<DataRef> &d) { d->Dump(o); },
[&](const CopyableIndirection<FunctionRef> &d) { d->Dump(o); },
+ // [&](const Parentheses<Result> &p) { p.Dump(o); },
[&](const Not &n) { n.Dump(o, "(.NOT."); },
[&](const And &a) { a.Dump(o, ".AND."); },
[&](const Or &a) { a.Dump(o, ".OR."); },
return {x};
}
if constexpr (evaluate::FoldableTrait<Ty>) {
- auto c{x.Fold(context)};
- if (c.has_value()) {
- u_ = *c;
+ if (auto c{x.Fold(context)}) {
+ if constexpr (std::is_same_v<Ty, Parentheses<Result>>) {
+ // Preserve parentheses around constants.
+ u_ = Parentheses<Result>{Expr{*c}};
+ } else {
+ u_ = *c;
+ }
return c;
}
}
return {x};
}
if constexpr (evaluate::FoldableTrait<Ty>) {
- auto c{x.Fold(context)};
- if (c.has_value()) {
+ if (auto c{x.Fold(context)}) {
if (context.flushDenormalsToZero) {
*c = c->FlushDenormalToZero();
}
return {x};
}
if constexpr (evaluate::FoldableTrait<Ty>) {
- auto c{x.Fold(context)};
- if (c.has_value()) {
+ if (auto c{x.Fold(context)}) {
if (context.flushDenormalsToZero) {
*c = c->FlushDenormalToZero();
}
return {x};
}
if constexpr (evaluate::FoldableTrait<Ty>) {
- auto c{x.Fold(context)};
- if (c.has_value()) {
+ if (auto c{x.Fold(context)}) {
u_ = *c;
return c;
}
return {x};
}
if constexpr (evaluate::FoldableTrait<Ty>) {
- std::optional<Scalar<Result>> c{x.Fold(context)};
- if (c.has_value()) {
+ if (auto c{x.Fold(context)}) {
u_ = *c;
return c;
}
#include "../lib/parser/char-block.h"
#include "../lib/parser/message.h"
#include <ostream>
+#include <tuple>
#include <variant>
namespace Fortran::evaluate {
template<typename A> class Expr;
+template<typename DERIVED, typename RESULT, typename... OPERAND>
+class Operation {
+private:
+ using OperandTypes = std::tuple<OPERAND...>;
+
+public:
+ using Derived = DERIVED;
+ using Result = RESULT;
+ template<int J> using Operand = std::tuple_element_t<J, OperandTypes>;
+ using FoldableTrait = std::true_type;
+
+ CLASS_BOILERPLATE(Operation)
+ Operation(Expr<OPERAND> &&... x) : operand_{std::move(x)...} {}
+ Operation(const Expr<OPERAND> &... x) : operand_{x...} {}
+
+ DERIVED &derived() { return *static_cast<DERIVED *>(this); }
+ const DERIVED &derived() const { return *static_cast<const DERIVED *>(this); }
+
+ static constexpr auto operands() { return sizeof...(OPERAND); }
+ template<int J> Expr<Operand<J>> &operand() { return *std::get<J>(operand_); }
+ template<int J> const Expr<Operand<J>> &operand() const {
+ return *std::get<J>(operand_);
+ }
+
+ std::optional<Scalar<Result>> Fold(FoldingContext &); // TODO rank > 0
+
+protected:
+ // Overridable strings for Dump()
+ static constexpr const char *prefix_{"("}, *infix_{""}, *postfix_{")"};
+
+private:
+ std::tuple<CopyableIndirection<Expr<OPERAND>>...> operand_;
+};
+
+template<typename A>
+class Parentheses : public Operation<Parentheses<A>, A, A> {
+ using Base = Operation<Parentheses, A, A>;
+ friend Base;
+ using Base::Base;
+ using typename Base::Result;
+ using Operand = typename Base::template Operand<0>;
+ static std::optional<Scalar<Result>> FoldScalar(
+ FoldingContext &, const Scalar<Operand> &x) {
+ return {x};
+ }
+};
+
+template<typename TO, typename FROM>
+class Convert : public Operation<Convert<TO, FROM>, TO, FROM> {
+ using Base = Operation<Convert<TO, FROM>, TO, FROM>;
+ friend Base;
+ using Base::Base;
+ using typename Base::Result;
+ using Operand = typename Base::template Operand<0>;
+ static std::optional<Scalar<Result>> FoldScalar(
+ FoldingContext &, const Scalar<Operand> &);
+};
+
// Helper base classes for packaging subexpressions.
template<typename CRTP, typename RESULT, typename A = RESULT> class Unary {
public:
template<typename CRTP> using Un = Unary<CRTP, Result>;
template<typename CRTP> using Bin = Binary<CRTP, Result>;
- struct Parentheses : public Un<Parentheses> {
- using Un<Parentheses>::Un;
- static std::optional<Scalar<Result>> FoldScalar(
- FoldingContext &, const Scalar<Result> &x) {
- return {x};
- }
- };
struct Negate : public Un<Negate> {
using Un<Negate>::Un;
static std::optional<Scalar<Result>> FoldScalar(
private:
std::variant<Scalar<Result>, CopyableIndirection<DataRef>,
CopyableIndirection<FunctionRef>, ConvertInteger, ConvertReal,
- Parentheses, Negate, Add, Subtract, Multiply, Divide, Power, Max, Min>
+ Parentheses<Result>, Negate, Add, Subtract, Multiply, Divide, Power, Max,
+ Min>
u_;
};
};
template<typename CRTP> using Un = Unary<CRTP, Result>;
template<typename CRTP> using Bin = Binary<CRTP, Result>;
- struct Parentheses : public Un<Parentheses> {
- using Un<Parentheses>::Un;
- static std::optional<Scalar<Result>> FoldScalar(
- FoldingContext &, const Scalar<Result> &x) {
- return {x};
- }
- };
struct Negate : public Un<Negate> {
using Un<Negate>::Un;
static std::optional<Scalar<Result>> FoldScalar(
private:
std::variant<Scalar<Result>, CopyableIndirection<DataRef>,
CopyableIndirection<ComplexPart>, CopyableIndirection<FunctionRef>,
- ConvertInteger, ConvertReal, Parentheses, Negate, Add, Subtract, Multiply,
- Divide, Power, IntPower, Max, Min, RealPart, AIMAG>
+ ConvertInteger, ConvertReal, Parentheses<Result>, Negate, Add, Subtract,
+ Multiply, Divide, Power, IntPower, Max, Min, RealPart, AIMAG>
u_;
};
template<typename CRTP> using Un = Unary<CRTP, Result>;
template<typename CRTP> using Bin = Binary<CRTP, Result>;
- struct Parentheses : public Un<Parentheses> {
- using Un<Parentheses>::Un;
- static std::optional<Scalar<Result>> FoldScalar(
- FoldingContext &, const Scalar<Result> &x) {
- return {x};
- }
- };
struct Negate : public Un<Negate> {
using Un<Negate>::Un;
static std::optional<Scalar<Result>> FoldScalar(
private:
std::variant<Scalar<Result>, CopyableIndirection<DataRef>,
- CopyableIndirection<FunctionRef>, Parentheses, Negate, Add, Subtract,
- Multiply, Divide, Power, IntPower, CMPLX>
+ CopyableIndirection<FunctionRef>, Parentheses<Result>, Negate, Add,
+ Subtract, Multiply, Divide, Power, IntPower, CMPLX>
u_;
};
private:
std::variant<Scalar<Result>, CopyableIndirection<DataRef>,
- CopyableIndirection<Substring>, CopyableIndirection<FunctionRef>, Concat,
- Max, Min>
+ CopyableIndirection<Substring>, CopyableIndirection<FunctionRef>,
+ // Parentheses<Result>,
+ Concat, Max, Min>
u_;
};
private:
std::variant<Scalar<Result>, CopyableIndirection<DataRef>,
- CopyableIndirection<FunctionRef>, Not, And, Or, Eqv, Neqv,
- CategoryComparison<TypeCategory::Integer>,
+ CopyableIndirection<FunctionRef>,
+ // Parentheses<Result>,
+ Not, And, Or, Eqv, Neqv, CategoryComparison<TypeCategory::Integer>,
CategoryComparison<TypeCategory::Real>,
CategoryComparison<TypeCategory::Complex>,
CategoryComparison<TypeCategory::Character>>
// These definitions are created with temporary helper macros to reduce
// C++ boilerplate. All combinations of lvalue and rvalue references are
// allowed for operands.
-#define UNARY(FUNC, CONSTR) \
- template<typename A> A FUNC(const A &x) { return {typename A::CONSTR{x}}; }
-UNARY(Parentheses, Parentheses)
-UNARY(operator-, Negate)
-#undef UNARY
+template<typename A> A operator-(const A &x) { return {typename A::Negate{x}}; }
#define BINARY(FUNC, CONSTR) \
template<typename A> A FUNC(const A &x, const A &y) { \
using common::TypeCategory;
+// Specific intrinsic types
+
template<TypeCategory C, int KIND> struct TypeBase {
+ static constexpr bool knowKind{true};
static constexpr TypeCategory category{C};
- static constexpr TypeCategory GetCategory() { return C; };
static constexpr int kind{KIND};
static constexpr bool hasLen{false};
static std::string Dump() {
// Represents a type of any supported kind within a particular category.
template<TypeCategory CAT> struct SomeKind {
- static constexpr TypeCategory category{CAT};
+ static constexpr bool knowKind{false};
using Scalar = SomeKindScalar<CAT>;
+ static constexpr TypeCategory category{CAT};
};
using SomeInteger = SomeKind<TypeCategory::Integer>;
// Represents a completely generic type.
struct SomeType {
+ static constexpr bool knowKind{false};
using Scalar = GenericScalar;
};