constexpr bool Contains(const Interval &that) const {
return Contains(that.start_) && Contains(that.start_ + (that.size_ - 1));
}
+ constexpr bool IsDisjointWith(const Interval &that) const {
+ return that.NextAfter() <= start_ || NextAfter() <= that.start_;
+ }
constexpr bool ImmediatelyPrecedes(const Interval &that) const {
return NextAfter() == that.start_;
}
return {start_ + n, size_ - n};
}
+ constexpr Interval Intersection(const Interval &that) const {
+ if (start_ >= that.NextAfter()) {
+ return {};
+ } else if (start_ >= that.start_) {
+ auto skip{start_ - that.start_};
+ return {start_, std::min(size_, that.size_ - skip)};
+ } else if (NextAfter() <= that.start_) {
+ return {};
+ } else {
+ auto skip{that.start_ - start_};
+ return {that.start_, std::min(that.size_ - size_ - skip)};
+ }
+ }
+
private:
A start_;
std::size_t size_{0};
ENUM_CLASS(
RealFlag, Overflow, DivideByZero, InvalidArgument, Underflow, Inexact)
-using RealFlags = common::EnumSet<RealFlag, 5>;
+using RealFlags = common::EnumSet<RealFlag, RealFlag_enumSize>;
template<typename A> struct ValueWithRealFlags {
A AccumulateFlags(RealFlags &f) {
template<typename D, typename R, typename... O>
auto Operation<D, R, O...>::Fold(FoldingContext &context)
-> std::optional<Constant<Result>> {
- // TODO pmk: generalize
auto c0{operand<0>().Fold(context)};
if constexpr (operands() == 1) {
if (c0.has_value()) {
}
}
} else {
+ static_assert(operands() == 2); // TODO: generalize to N operands?
auto c1{operand<1>().Fold(context)};
if (c0.has_value() && c1.has_value()) {
if (auto scalar{derived().FoldScalar(context, c0->value, c1->value)}) {
}
}
-template<typename T> std::ostream &BOZConstant<T>::Dump(std::ostream &o) const {
- return o << "Z'" << value.Hexadecimal() << "'";
-}
-
template<typename RESULT>
std::ostream &ExpressionBase<RESULT>::Dump(std::ostream &o) const {
std::visit(
};
// BOZ literal "typeless" constants must be wide enough to hold a numeric
-// value of any supported kind. They must also be distinguishable from
-// other integer constants, since they are permitted to be used in only a
-// few situations.
-using BOZLiteralConstant = value::Integer<8 * 2 * DefaultComplex::kind>;
-
-// "Typeless" operands to INTEGER, REAL, and COMPLEX operations.
-template<typename T> struct BOZConstant {
- using Result = T;
- using Value = BOZLiteralConstant;
- CLASS_BOILERPLATE(BOZConstant)
- BOZConstant(const BOZLiteralConstant &x) : value{x} {}
- BOZConstant(BOZLiteralConstant &&x) : value{std::move(x)} {}
- std::ostream &Dump(std::ostream &) const;
- Value value;
-};
+// value of any supported kind of INTEGER or REAL. They must also be
+// distinguishable from other integer constants, since they are permitted
+// to be used in only a few situations.
+using BOZLiteralConstant = typename LargestReal::Scalar::Word;
// These wrappers around data and function references expose their resolved
// types.
using Operations = std::variant<Parentheses<Result>, Negate<Result>,
Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
Power<Result>, Extremum<Result>>;
- using Others = std::variant<Constant<Result>, BOZConstant<Result>,
- DataReference<Result>, FunctionReference<Result>>;
+ using Others = std::variant<Constant<Result>, DataReference<Result>,
+ FunctionReference<Result>>;
public:
common::CombineVariants<Operations, Conversions, Others> u;
using Operations = std::variant<ComplexComponent<KIND>, Parentheses<Result>,
Negate<Result>, Add<Result>, Subtract<Result>, Multiply<Result>,
Divide<Result>, Power<Result>, RealToIntPower<Result>, Extremum<Result>>;
- using Others = std::variant<Constant<Result>, BOZConstant<Result>,
- DataReference<Result>, FunctionReference<Result>>;
+ using Others = std::variant<Constant<Result>, DataReference<Result>,
+ FunctionReference<Result>>;
public:
common::CombineVariants<Operations, Conversions, Others> u;
Expr(const DataRef &x) : u{DataReference<Result>{x}} {}
Expr(const FunctionRef &x) : u{FunctionReference<Result>{x}} {}
+ // TODO pmk: Remove Negate, Add, Subtract in favor of component-wise
+ // operations.
using Operations = std::variant<Parentheses<Result>, Negate<Result>,
Add<Result>, Subtract<Result>, Multiply<Result>, Divide<Result>,
Power<Result>, RealToIntPower<Result>, ComplexConstructor<KIND>>;
- using Others = std::variant<Constant<Result>, BOZConstant<Result>,
- DataReference<Result>, FunctionReference<Result>>;
+ using Others = std::variant<Constant<Result>, DataReference<Result>,
+ FunctionReference<Result>>;
public:
common::CombineVariants<Operations, Others> u;
template class Logical<16>;
template class Logical<32>;
template class Logical<64>;
-template class Logical<128>;
} // namespace Fortran::evaluate::value
extern template class Logical<16>;
extern template class Logical<32>;
extern template class Logical<64>;
-extern template class Logical<128>;
} // namespace Fortran::evaluate::value
#endif // FORTRAN_EVALUATE_LOGICAL_H_
z.u);
}
-template<template<typename> class OPR>
-std::optional<Expr<SomeType>> MixedComplex(parser::ContextualMessages &messages,
- Expr<SomeComplex> &&zx, Expr<SomeType> &&iry) {
+// Handle mixed COMPLEX+REAL (or INTEGER) operations in a smarter way
+// than just converting the second operand to COMPLEX and performing the
+// corresponding COMPLEX+COMPLEX operation.
+template<template<typename> class OPR, TypeCategory RCAT>
+std::optional<Expr<SomeType>> MixedComplexLeft(
+ parser::ContextualMessages &messages, Expr<SomeComplex> &&zx,
+ Expr<SomeKind<RCAT>> &&iry) {
Expr<SomeReal> zr{GetComplexPart(zx, false)};
Expr<SomeReal> zi{GetComplexPart(zx, true)};
if constexpr (std::is_same_v<OPR<DefaultReal>, Add<DefaultReal>> ||
std::is_same_v<OPR<DefaultReal>, Subtract<DefaultReal>>) {
- // Addition and subtraction: apply the operation to the real part of the
- // complex operand, and a transfer/convert its imaginary part.
- // i.e., (a,b) + c = (a+c, b)
- if (std::optional<Expr<SomeType>> rr{
- NumericOperation<OPR>(messages, std::move(zr), std::move(iry))}) {
- return Package(ConstructComplex(messages, AsGenericExpr(std::move(*rr)),
- AsGenericExpr(std::move(zi))));
+ // (a,b) + x -> (a+x, b)
+ // (a,b) - x -> (a-x, b)
+ if (std::optional<Expr<SomeType>> rr{NumericOperation<OPR>(messages,
+ AsGenericExpr(std::move(zr)), AsGenericExpr(std::move(iry)))}) {
+ return Package(ConstructComplex(
+ messages, std::move(*rr), AsGenericExpr(std::move(zi))));
}
} else if constexpr (std::is_same_v<OPR<DefaultReal>,
Multiply<DefaultReal>> ||
std::is_same_v<OPR<DefaultReal>, Divide<DefaultReal>>) {
- // Multiplication and division of a COMPLEX value by an INTEGER or REAL
- // operand: apply the operation to both components of the COMPLEX value,
- // then convert and recombine them.
- // i.e., (a,b) * c = (a*c, b*c)
+ // (a,b) * x -> (a*x, b*x)
+ // (a,b) / x -> (a/x, b/x)
auto copy{iry};
- auto rr{NumericOperation<OPR>(messages, std::move(zr), std::move(iry))};
- auto ri{NumericOperation<OPR>(messages, std::move(zi), std::move(copy))};
+ auto rr{NumericOperation<Multiply>(
+ messages, AsGenericExpr(std::move(zr)), AsGenericExpr(std::move(iry)))};
+ auto ri{NumericOperation<Multiply>(messages, AsGenericExpr(std::move(zi)),
+ AsGenericExpr(std::move(copy)))};
if (auto parts{common::AllPresent(std::move(rr), std::move(ri))}) {
return Package(ConstructComplex(messages, std::move(std::get<0>(*parts)),
std::move(std::get<1>(*parts))));
}
+ } else {
+ // (a,b) ? x -> (a,b) ? (x,0)
+ Expr<SomeComplex> zy{ConvertTo(zx, std::move(iry))};
+ return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
+ }
+ return std::nullopt;
+}
+
+// Mixed COMPLEX operations with the COMPLEX operand on the right.
+// x + (a,b) -> (x+a, b)
+// x - (a,b) -> (x-a, -b)
+// x * (a,b) -> (x*a, x*b)
+// x / (a,b) -> (x,0) / (a,b)
+template<template<typename> class OPR, TypeCategory LCAT>
+std::optional<Expr<SomeType>> MixedComplexRight(
+ parser::ContextualMessages &messages, Expr<SomeKind<LCAT>> &&irx,
+ Expr<SomeComplex> &&zy) {
+ if constexpr (std::is_same_v<OPR<DefaultReal>, Add<DefaultReal>> ||
+ std::is_same_v<OPR<DefaultReal>, Multiply<DefaultReal>>) {
+ // x + (a,b) -> (a,b) + x -> (a+x, b)
+ // x * (a,b) -> (a,b) * x -> (a*x, b*x)
+ return MixedComplexLeft<Add, LCAT>(messages, std::move(zy), std::move(irx));
+ } else if constexpr (std::is_same_v<OPR<DefaultReal>,
+ Subtract<DefaultReal>>) {
+ // x - (a,b) -> (x-a, -b)
+ Expr<SomeReal> zr{GetComplexPart(zy, false)};
+ Expr<SomeReal> zi{GetComplexPart(zy, true)};
+ if (std::optional<Expr<SomeType>> rr{NumericOperation<Subtract>(messages,
+ AsGenericExpr(std::move(irx)), AsGenericExpr(std::move(zr)))}) {
+ return Package(ConstructComplex(
+ messages, std::move(*rr), AsGenericExpr(-std::move(zi))));
+ }
+ } else {
+ // x / (a,b) -> (x,0) / (a,b) and any other operators that make it here
+ Expr<SomeComplex> zx{ConvertTo(zy, std::move(irx))};
+ return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
}
return std::nullopt;
}
// N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
-// the operands to a dyadic INTEGER or REAL operation, it assumes the type
-// and kind of the other operand.
+// the operands to a dyadic operation, it assumes the type and kind of the
+// other operand.
+// TODO pmk: add Power, RealToIntPower, &c.
template<template<typename> class OPR>
std::optional<Expr<SomeType>> NumericOperation(
parser::ContextualMessages &messages, Expr<SomeType> &&x,
return Package(PromoteAndCombine<OPR, TypeCategory::Real>(
std::move(rx), std::move(ry)));
},
+ // Mixed INTEGER/REAL operations
[](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
return Package(std::visit(
[&](auto &&rxk) -> Expr<SomeReal> {
},
std::move(ry.u)));
},
+ // Homogenous and mixed COMPLEX operations
[](Expr<SomeComplex> &&zx, Expr<SomeComplex> &&zy) {
return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
std::move(zx), std::move(zy)));
},
[&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&zy) {
- return MixedComplex<OPR>(messages, std::move(zx), std::move(zy));
+ return MixedComplexLeft<OPR>(
+ messages, std::move(zx), std::move(zy));
},
[&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&zy) {
- return MixedComplex<OPR>(messages, std::move(zx), std::move(zy));
+ return MixedComplexLeft<OPR>(
+ messages, std::move(zx), std::move(zy));
+ },
+ [&](Expr<SomeInteger> &&zx, Expr<SomeComplex> &&zy) {
+ return MixedComplexRight<OPR>(
+ messages, std::move(zx), std::move(zy));
+ },
+ [&](Expr<SomeReal> &&zx, Expr<SomeComplex> &&zy) {
+ return MixedComplexRight<OPR>(
+ messages, std::move(zx), std::move(zy));
},
- // TODO pmk: mixed r+complex, &c.; r/z is tricky
- // TODO pmk: mixed complex + boz? yes but what about COMPLEX*16?
+ // Operations with one typeless operand
[&](BOZLiteralConstant &&bx, Expr<SomeInteger> &&iy) {
return NumericOperation<OPR>(
messages, ConvertTo(iy, std::move(bx)), std::move(y));
return NumericOperation<OPR>(
messages, std::move(x), ConvertTo(rx, std::move(by)));
},
+ // Default case
[&](auto &&, auto &&) {
messages.Say("non-numeric operands to numeric operation"_err_en_US);
return std::optional<Expr<SomeType>>{std::nullopt};
Expr<TO> ConvertToType(Expr<SomeKind<FROMCAT>> &&x) {
static_assert(TO::isSpecificType);
if constexpr (FROMCAT != TO::category) {
- return {Convert<TO, FROMCAT>{std::move(x)}};
+ if constexpr (TO::category == TypeCategory::Complex) {
+ using Part = typename TO::Part;
+ Scalar<Part> zero;
+ return {ComplexConstructor<TO::kind>{
+ ConvertToType<Part>(std::move(x)), Expr<Part>{Constant<Part>{zero}}}};
+ } else {
+ return {Convert<TO, FROMCAT>{std::move(x)}};
+ }
} else {
+ // Same type category
if (auto already{common::GetIf<Expr<TO>>(x.u)}) {
return std::move(*already);
}
}
template<typename TO> Expr<TO> ConvertToType(BOZLiteralConstant &&x) {
- // TODO: check rank == 0
- // TODO: pmk: truncation warnings
static_assert(TO::isSpecificType);
- return Expr<TO>{BOZConstant<TO>{std::move(x)}};
+ using Value = typename Constant<TO>::Value;
+ if constexpr (TO::category == TypeCategory::Integer) {
+ return Expr<TO>{Constant<TO>{Value::ConvertUnsigned(std::move(x)).value}};
+ } else {
+ static_assert(TO::category == TypeCategory::Real);
+ using Word = typename Value::Word;
+ return Expr<TO>{Constant<TO>{Word::ConvertUnsigned(std::move(x)).value}};
+ }
}
template<TypeCategory TC, int TK, TypeCategory FC>
// Given two expressions of arbitrary kind in the same intrinsic type
// category, convert one of them if necessary to the larger kind of the
-// other, then combine them with a operation and return a new expression
-// in the same type category.
+// other, then combine the resulting homogenized operands with a given
+// operation, returning a new expression in the same type category.
template<template<typename> class OPR, TypeCategory CAT>
Expr<SomeKind<CAT>> PromoteAndCombine(
Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
// Convenience functions and operator overloadings for expression construction.
+// These interfaces are defined only for those situations that cannot possibly
+// need to emit any messages. Use the more general NumericOperation<>
+// template (above) in other situations.
template<TypeCategory C, int K>
Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
using SubscriptInteger = Type<TypeCategory::Integer, 8>;
using LogicalResult = Type<TypeCategory::Logical, 1>;
+using LargestReal = Type<TypeCategory::Real, 16>;
// For an intrinsic type category CAT, CategoryTypes<CAT> is an instantiation
// of std::tuple<Type<CAT, K>> over each supported kind K in that category.
ProgramParentheses, PercentRefAndVal, OmitFunctionDummies, CrayPointer,
Hollerith, ArithmeticIF, Assign, AssignedGOTO, Pause, OpenMP)
-using LanguageFeatures = common::EnumSet<LanguageFeature, 64>;
+using LanguageFeatures =
+ common::EnumSet<LanguageFeature, LanguageFeature_enumSize>;
class LanguageFeatureControl {
public: