case Relation::Unordered: return std::nullopt;
}
}
- if constexpr (A::category == TypeCategory::Complex) {
- bool eqOk{opr == RelationalOperator::LE || opr == RelationalOperator::EQ ||
- opr == RelationalOperator::GE};
- return {eqOk == a.Equals(b)};
- }
if constexpr (A::category == TypeCategory::Character) {
switch (Compare(a, b)) {
case Ordering::Less:
template struct Relational<Type<TypeCategory::Real, 8>>;
template struct Relational<Type<TypeCategory::Real, 10>>;
template struct Relational<Type<TypeCategory::Real, 16>>;
-template struct Relational<Type<TypeCategory::Complex, 2>>;
-template struct Relational<Type<TypeCategory::Complex, 4>>;
-template struct Relational<Type<TypeCategory::Complex, 8>>;
-template struct Relational<Type<TypeCategory::Complex, 10>>;
-template struct Relational<Type<TypeCategory::Complex, 16>>;
template struct Relational<Type<TypeCategory::Character, 1>>; // TODO others
template struct Relational<SomeType>;
using common::RelationalOperator;
-// Expressions are represented by specializations of Expr.
+// Expressions are represented by specializations of the class template Expr.
// Each of these specializations wraps a single data member "u" that
-// is a std::variant<> discriminated union over the representational
+// is a std::variant<> discriminated union over all of the representational
// types for the constants, variables, operations, and other entities that
// can be valid expressions in that context:
-// - Expr<Type<CATEGORY, KIND>> is an expression whose result is of a
+// - Expr<Type<CATEGORY, KIND>> represents an expression whose result is of a
// specific intrinsic type category and kind, e.g. Type<TypeCategory::Real, 4>
// - Expr<SomeKind<CATEGORY>> is a union of Expr<Type<CATEGORY, K>> for each
-// kind type parameter value K in that intrinsic type category
+// kind type parameter value K in that intrinsic type category. It represents
+// an expression with known category and any kind.
// - Expr<SomeType> is a union of Expr<SomeKind<CATEGORY>> over the five
-// intrinsic type categories of Fortran.
+// intrinsic type categories of Fortran. It represents any valid expression.
template<typename A> class Expr;
// Everything that can appear in, or as, a valid Fortran expression must be
// or SomeType.
template<typename A> using ResultType = typename std::decay_t<A>::Result;
-// Wraps a constant value in a class to make its type clear.
+// Wraps a constant value in a class with its resolved type.
template<typename T> struct Constant {
using Result = T;
using Value = Scalar<Result>; // TODO rank > 0
using Base = Operation<LogicalOperation, Result, Operand, Operand>;
CLASS_BOILERPLATE(LogicalOperation)
LogicalOperation(
- const Expr<Operand> &x, const Expr<Operand> &y, LogicalOperator opr)
+ LogicalOperator opr, const Expr<Operand> &x, const Expr<Operand> &y)
: Base{x, y}, logicalOperator{opr} {}
- LogicalOperation(Expr<Operand> &&x, Expr<Operand> &&y, LogicalOperator opr)
+ LogicalOperation(LogicalOperator opr, Expr<Operand> &&x, Expr<Operand> &&y)
: Base{std::move(x), std::move(y)}, logicalOperator{opr} {}
std::optional<Scalar<Result>> FoldScalar(
LogicalOperator logicalOperator;
};
-// Per-category expressions
+// Per-category expression representations
// Common Expr<> behaviors
template<typename RESULT> struct ExpressionBase {
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>>;
+ // Note that many COMPLEX operations are represented as REAL operations
+ // over their components (viz., conversions, negation, add, and subtract).
+ using Operations =
+ std::variant<Parentheses<Result>, Multiply<Result>, Divide<Result>,
+ Power<Result>, RealToIntPower<Result>, ComplexConstructor<KIND>>;
using Others = std::variant<Constant<Result>, DataReference<Result>,
FunctionReference<Result>>;
// expressions with polymorphism over the cross product of the possible
// categories and kinds of comparable operands.
// Fortran defines a numeric relation with distinct types or kinds as
-// undergoing the same operand conversions that occur with the addition
-// intrinsic operator first. Character relations must have the same kind.
+// first undergoing the same operand conversions that occur with the intrinsic
+// addition operator. Character relations must have the same kind.
// There are no relations between LOGICAL values.
template<typename A>
RelationalOperator opr;
};
-template<> struct Relational<SomeType> {
+template<> class Relational<SomeType> {
+ // COMPLEX data is compared piecewise.
+ using DirectlyComparableTypes =
+ common::CombineTuples<IntegerTypes, RealTypes, CharacterTypes>;
+
+public:
using Result = LogicalResult;
CLASS_BOILERPLATE(Relational)
template<typename A> Relational(const A &x) : u(x) {}
Relational(std::enable_if_t<!std::is_reference_v<A>, A> &&x)
: u{std::move(x)} {}
std::ostream &Dump(std::ostream &o) const;
- common::MapTemplate<Relational, RelationalTypes> u;
+ common::MapTemplate<Relational, DirectlyComparableTypes> u;
};
extern template struct Relational<Type<TypeCategory::Integer, 1>>;
extern template struct Relational<Type<TypeCategory::Real, 8>>;
extern template struct Relational<Type<TypeCategory::Real, 10>>;
extern template struct Relational<Type<TypeCategory::Real, 16>>;
-extern template struct Relational<Type<TypeCategory::Complex, 2>>;
-extern template struct Relational<Type<TypeCategory::Complex, 4>>;
-extern template struct Relational<Type<TypeCategory::Complex, 8>>;
-extern template struct Relational<Type<TypeCategory::Complex, 10>>;
-extern template struct Relational<Type<TypeCategory::Complex, 16>>;
extern template struct Relational<Type<TypeCategory::Character, 1>>; // TODO
// more
extern template struct Relational<SomeType>;
Expr(const FunctionRef &x) : u{FunctionReference<Result>{x}} {}
private:
- using Operations =
- std::variant<Not<KIND>, LogicalOperation<KIND>, Relational<SomeType>>;
+ using Operations = std::variant<Convert<Result, TypeCategory::Logical>,
+ Not<KIND>, LogicalOperation<KIND>, Relational<SomeType>>;
using Others = std::variant<Constant<Result>, DataReference<Result>,
FunctionReference<Result>>;
namespace Fortran::evaluate {
+// Conversions of complex component expressions to REAL.
ConvertRealOperandsResult ConvertRealOperands(
parser::ContextualMessages &messages, Expr<SomeType> &&x,
Expr<SomeType> &&y) {
return {AsSameKindExprs<TypeCategory::Real>(
std::move(rx), std::move(ry))};
},
+ [&](Expr<SomeInteger> &&ix,
+ BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
+ return {AsSameKindExprs<TypeCategory::Real>(
+ ConvertToType<DefaultReal>(std::move(ix)),
+ ConvertToType<DefaultReal>(std::move(by)))};
+ },
+ [&](BOZLiteralConstant &&bx,
+ Expr<SomeInteger> &&iy) -> ConvertRealOperandsResult {
+ return {AsSameKindExprs<TypeCategory::Real>(
+ ConvertToType<DefaultReal>(std::move(bx)),
+ ConvertToType<DefaultReal>(std::move(iy)))};
+ },
+ [&](Expr<SomeReal> &&rx,
+ BOZLiteralConstant &&by) -> ConvertRealOperandsResult {
+ return {AsSameKindExprs<TypeCategory::Real>(
+ std::move(rx), ConvertTo(rx, std::move(by)))};
+ },
+ [&](BOZLiteralConstant &&bx,
+ Expr<SomeReal> &&ry) -> ConvertRealOperandsResult {
+ return {AsSameKindExprs<TypeCategory::Real>(
+ ConvertTo(ry, std::move(bx)), std::move(ry))};
+ },
[&](auto &&, auto &&) -> ConvertRealOperandsResult {
- // TODO: allow BOZ here?
messages.Say("operands must be INTEGER or REAL"_err_en_US);
return std::nullopt;
}},
return NoExpr();
}
+// Mixed REAL+INTEGER operations. REAL**INTEGER is a special case that
+// does not require conversion of the exponent expression.
+template<template<typename> class OPR>
+std::optional<Expr<SomeType>> MixedRealLeft(
+ Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
+ return Package(std::visit(
+ [&](auto &&rxk) -> Expr<SomeReal> {
+ using resultType = ResultType<decltype(rxk)>;
+ if constexpr (std::is_same_v<OPR<resultType>, Power<resultType>>) {
+ return AsCategoryExpr(AsExpr(
+ RealToIntPower<resultType>{std::move(rxk), std::move(iy)}));
+ }
+ // G++ 8.1.0 emits bogus warnings about missing return statements if
+ // this statement is wrapped in an "else", as it should be.
+ return AsCategoryExpr(AsExpr(OPR<resultType>{
+ std::move(rxk), ConvertToType<resultType>(std::move(iy))}));
+ },
+ std::move(rx.u)));
+}
+
std::optional<Expr<SomeComplex>> ConstructComplex(
parser::ContextualMessages &messages, Expr<SomeType> &&real,
Expr<SomeType> &&imaginary) {
z.u);
}
-// Handle mixed COMPLEX+REAL (or INTEGER) operations in a smarter way
+// Handle mixed COMPLEX+REAL (or INTEGER) operations in a better way
// than just converting the second operand to COMPLEX and performing the
// corresponding COMPLEX+COMPLEX operation.
template<template<typename> class OPR, TypeCategory RCAT>
return Package(ConstructComplex(messages, std::move(std::get<0>(*parts)),
std::move(std::get<1>(*parts))));
}
+ } else if constexpr (RCAT == TypeCategory::Integer &&
+ std::is_same_v<OPR<DefaultReal>, Power<DefaultReal>>) {
+ // COMPLEX**INTEGER is a special case that doesn't convert the exponent.
+ static_assert(RCAT == TypeCategory::Integer);
+ return Package(std::visit(
+ [&](auto &&zxk) {
+ using Ty = ResultType<decltype(zxk)>;
+ return AsCategoryExpr(
+ AsExpr(RealToIntPower<Ty>{std::move(zxk), std::move(iry)}));
+ },
+ std::move(zx.u)));
} else {
- // (a,b) ? x -> (a,b) ? (x,0)
+ // (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)));
}
// 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)
+// x / (a,b) -> (x,0) / (a,b) (and **)
template<template<typename> class OPR, TypeCategory LCAT>
std::optional<Expr<SomeType>> MixedComplexRight(
parser::ContextualMessages &messages, Expr<SomeKind<LCAT>> &&irx,
messages, std::move(*rr), AsGenericExpr(-std::move(zi))));
}
} else {
- // x / (a,b) -> (x,0) / (a,b) and any other operators that make it here
+ // x / (a,b) -> (x,0) / (a,b)
Expr<SomeComplex> zx{ConvertTo(zy, std::move(irx))};
return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
}
}
// N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
-// the operands to a dyadic operation, it assumes the type and kind of the
-// other operand.
-// TODO pmk: add Power, RealToIntPower, &c.
+// the operands to a dyadic operation where one is permitted, it assumes the
+// type and kind of the other operand.
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
+ // Mixed REAL/INTEGER operations
[](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
- return Package(std::visit(
- [&](auto &&rxk) -> Expr<SomeReal> {
- using resultType = ResultType<decltype(rxk)>;
- return AsCategoryExpr(AsExpr(OPR<resultType>{std::move(rxk),
- ConvertToType<resultType>(std::move(iy))}));
- },
- std::move(rx.u)));
+ return MixedRealLeft<OPR>(std::move(rx), std::move(iy));
},
[](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) {
return Package(std::visit(
},
std::move(ry.u)));
},
- // Homogenous and mixed COMPLEX operations
+ // Homogeneous and mixed COMPLEX operations
[](Expr<SomeComplex> &&zx, Expr<SomeComplex> &&zy) {
return Package(PromoteAndCombine<OPR, TypeCategory::Complex>(
std::move(zx), std::move(zy)));
std::move(x.u), std::move(y.u));
}
-template std::optional<Expr<SomeType>> NumericOperation<Add>(
- parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
-template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
+template std::optional<Expr<SomeType>> NumericOperation<Power>(
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
template std::optional<Expr<SomeType>> NumericOperation<Divide>(
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
+template std::optional<Expr<SomeType>> NumericOperation<Add>(
+ parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
+template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
+ parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
std::optional<Expr<SomeType>> Negation(
parser::ContextualMessages &messages, Expr<SomeType> &&x) {
std::move(x.u));
}
+template<typename T>
+Expr<LogicalResult> PackageRelation(
+ RelationalOperator opr, Expr<T> &&x, Expr<T> &&y) {
+ static_assert(T::isSpecificType);
+ return Expr<LogicalResult>{
+ Relational<SomeType>{Relational<T>{opr, std::move(x), std::move(y)}}};
+}
+
+template<TypeCategory CAT>
+Expr<LogicalResult> PromoteAndRelate(
+ RelationalOperator opr, Expr<SomeKind<CAT>> &&x, Expr<SomeKind<CAT>> &&y) {
+ return std::visit(
+ [=](auto &&xy) {
+ return PackageRelation(opr, std::move(xy[0]), std::move(xy[1]));
+ },
+ AsSameKindExprs(std::move(x), std::move(y)));
+}
+
+std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &messages,
+ RelationalOperator opr, Expr<SomeType> &&x, Expr<SomeType> &&y) {
+ return std::visit(
+ common::visitors{[=](Expr<SomeInteger> &&ix, Expr<SomeInteger> &&iy) {
+ return std::make_optional(PromoteAndRelate(
+ opr, std::move(ix), std::move(iy)));
+ },
+ [=](Expr<SomeReal> &&rx, Expr<SomeReal> &&ry) {
+ return std::make_optional(
+ PromoteAndRelate(opr, std::move(rx), std::move(ry)));
+ },
+ [&](Expr<SomeReal> &&rx, Expr<SomeInteger> &&iy) {
+ return Relate(
+ messages, opr, std::move(x), ConvertTo(rx, std::move(iy)));
+ },
+ [&](Expr<SomeInteger> &&ix, Expr<SomeReal> &&ry) {
+ return Relate(
+ messages, opr, ConvertTo(ry, std::move(ix)), std::move(y));
+ },
+ [&](Expr<SomeComplex> &&zx, Expr<SomeComplex> &&zy) {
+ if (opr != RelationalOperator::EQ &&
+ opr != RelationalOperator::NE) {
+ messages.Say(
+ "COMPLEX data may be compared only for equality"_err_en_US);
+ return std::optional<Expr<LogicalResult>>{};
+ } else {
+ auto rr{Relate(messages, opr, GetComplexPart(zx, false),
+ GetComplexPart(zy, false))};
+ auto ri{Relate(messages, opr, GetComplexPart(zx, true),
+ GetComplexPart(zy, true))};
+ if (auto parts{
+ common::AllPresent(std::move(rr), std::move(ri))}) {
+ // (a,b)==(c,d) -> (a==c) .AND. (b==d)
+ // (a,b)/=(c,d) -> (a/=c) .OR. (b/=d)
+ LogicalOperator combine{opr == RelationalOperator::EQ
+ ? LogicalOperator::And
+ : LogicalOperator::Or};
+ return std::make_optional(
+ Expr<LogicalResult>{LogicalOperation<LogicalResult::kind>{
+ combine, std::move(std::get<0>(*parts)),
+ std::move(std::get<1>(*parts))}});
+ } else {
+ return std::optional<Expr<LogicalResult>>{};
+ }
+ }
+ },
+ [&](Expr<SomeComplex> &&zx, Expr<SomeInteger> &&iy) {
+ return Relate(
+ messages, opr, std::move(x), ConvertTo(zx, std::move(iy)));
+ },
+ [&](Expr<SomeComplex> &&zx, Expr<SomeReal> &&ry) {
+ return Relate(
+ messages, opr, std::move(x), ConvertTo(zx, std::move(ry)));
+ },
+ [&](Expr<SomeInteger> &&ix, Expr<SomeComplex> &&zy) {
+ return Relate(
+ messages, opr, ConvertTo(zy, std::move(ix)), std::move(y));
+ },
+ [&](Expr<SomeReal> &&rx, Expr<SomeComplex> &&zy) {
+ return Relate(
+ messages, opr, ConvertTo(zy, std::move(rx)), std::move(y));
+ },
+ [&](Expr<SomeCharacter> &&cx, Expr<SomeCharacter> &&cy) {
+ return std::visit(
+ [&](auto &&cxk, auto &&cyk) {
+ using Ty = ResultType<decltype(cxk)>;
+ if constexpr (std::is_same_v<Ty, ResultType<decltype(cyk)>>) {
+ return std::make_optional(
+ PackageRelation(opr, std::move(cxk), std::move(cyk)));
+ } else {
+ messages.Say(
+ "CHARACTER operands do not have same KIND"_err_en_US);
+ return std::optional<Expr<LogicalResult>>{};
+ }
+ },
+ std::move(cx.u), std::move(cy.u));
+ },
+ // Default case
+ [&](auto &&, auto &&) {
+ // TODO: defined operator
+ messages.Say(
+ "relational operands do not have comparable types"_err_en_US);
+ return std::optional<Expr<LogicalResult>>{};
+ }},
+ std::move(x.u), std::move(y.u));
+}
+
+Expr<SomeLogical> BinaryLogicalOperation(
+ LogicalOperator opr, Expr<SomeLogical> &&x, Expr<SomeLogical> &&y) {
+ return std::visit(
+ [=](auto &&xy) {
+ using Ty = ResultType<decltype(xy[0])>;
+ return Expr<SomeLogical>{Expr<Ty>{LogicalOperation<Ty::kind>{
+ opr, std::move(xy[0]), std::move(xy[1])}}};
+ },
+ AsSameKindExprs(std::move(x), std::move(y)));
+}
+
} // namespace Fortran::evaluate
}
template<TypeCategory CAT>
-Expr<SomeType> ConvertTo(
+Expr<SomeKind<CAT>> ConvertTo(
const Expr<SomeKind<CAT>> &to, BOZLiteralConstant &&from) {
- return AsGenericExpr(std::visit(
+ return std::visit(
[&](const auto &tok) {
using Ty = ResultType<decltype(tok)>;
return AsCategoryExpr(ConvertToType<Ty>(std::move(from)));
},
- to.u));
+ to.u);
}
template<typename A, int N = 2> using SameExprs = std::array<Expr<A>, N>;
return {Constant<Ty>{x}};
}
+// Combine two expressions of the same specific numeric type with an operation
+// to produce a new expression. Implements piecewise addition and subtraction
+// for COMPLEX.
+template<template<typename> class OPR, typename SPECIFIC>
+Expr<SPECIFIC> Combine(Expr<SPECIFIC> &&x, Expr<SPECIFIC> &&y) {
+ static_assert(SPECIFIC::isSpecificType);
+ if constexpr (SPECIFIC::category == TypeCategory::Complex &&
+ (std::is_same_v<OPR<DefaultReal>, Add<DefaultReal>> ||
+ std::is_same_v<OPR<DefaultReal>, Subtract<DefaultReal>>)) {
+ static constexpr int kind{SPECIFIC::kind};
+ using Part = Type<TypeCategory::Real, kind>;
+ return AsExpr(
+ ComplexConstructor<kind>{OPR<Part>{ComplexComponent<kind>{false, x},
+ ComplexComponent<kind>{false, y}},
+ OPR<Part>{ComplexComponent<kind>{true, x},
+ ComplexComponent<kind>{true, y}}});
+ } else {
+ return AsExpr(OPR<SPECIFIC>{std::move(x), std::move(y)});
+ }
+}
+
// 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 the resulting homogenized operands with a given
[](auto &&xy) {
using Ty = ResultType<decltype(xy[0])>;
return AsCategoryExpr(
- AsExpr(OPR<Ty>{std::move(xy[0]), std::move(xy[1])}));
+ Combine<OPR, Ty>(std::move(xy[0]), std::move(xy[1])));
},
AsSameKindExprs(std::move(x), std::move(y)));
}
// Given two expressions of arbitrary type, try to combine them with a
// binary numeric operation (e.g., Add), possibly with data type conversion of
-// one of the operands to the type of the other.
+// one of the operands to the type of the other. Handles special cases with
+// typeless literal operands and with REAL/COMPLEX exponentiation to INTEGER
+// powers.
template<template<typename> class OPR>
std::optional<Expr<SomeType>> NumericOperation(
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
-extern template std::optional<Expr<SomeType>> NumericOperation<Add>(
- parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
-extern template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
+extern template std::optional<Expr<SomeType>> NumericOperation<Power>(
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
extern template std::optional<Expr<SomeType>> NumericOperation<Divide>(
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
+extern template std::optional<Expr<SomeType>> NumericOperation<Add>(
+ parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
+extern template std::optional<Expr<SomeType>> NumericOperation<Subtract>(
+ parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
std::optional<Expr<SomeType>> Negation(
parser::ContextualMessages &, Expr<SomeType> &&);
+// Given two expressions of arbitrary type, try to combine them with a
+// relational operator (e.g., .LT.), possibly with data type conversion.
+std::optional<Expr<LogicalResult>> Relate(parser::ContextualMessages &,
+ RelationalOperator, Expr<SomeType> &&, Expr<SomeType> &&);
+
+Expr<SomeLogical> BinaryLogicalOperation(
+ LogicalOperator, Expr<SomeLogical> &&, Expr<SomeLogical> &&);
+
// 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.
+// These interfaces are defined only for those situations that can never
+// emit any message. Use the more general templates (above) in other
+// situations.
template<TypeCategory C, int K>
Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
template<TypeCategory C, int K>
Expr<Type<C, K>> operator+(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
- return {Add<Type<C, K>>{std::move(x), std::move(y)}};
+ return {Combine<Add, Type<C, K>>(std::move(x), std::move(y))};
}
template<TypeCategory C, int K>
Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
- return {Subtract<Type<C, K>>{std::move(x), std::move(y)}};
+ return {Combine<Subtract, Type<C, K>>(std::move(x), std::move(y))};
}
template<TypeCategory C, int K>
Expr<Type<C, K>> operator*(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
- return {Multiply<Type<C, K>>{std::move(x), std::move(y)}};
+ return {Combine<Multiply, Type<C, K>>(std::move(x), std::move(y))};
}
template<TypeCategory C, int K>
Expr<Type<C, K>> operator/(Expr<Type<C, K>> &&x, Expr<Type<C, K>> &&y) {
- return {Divide<Type<C, K>>{std::move(x), std::move(y)}};
+ return {Combine<Divide, Type<C, K>>(std::move(x), std::move(y))};
}
template<TypeCategory C> Expr<SomeKind<C>> operator-(Expr<SomeKind<C>> &&x) {
using MaybeExpr = std::optional<Expr<SomeType>>;
+// Utility subroutines for repackaging optional values.
template<typename A> MaybeExpr AsMaybeExpr(std::optional<A> &&x) {
if (x.has_value()) {
return {AsGenericExpr(AsCategoryExpr(AsExpr(std::move(*x))))};
return std::nullopt;
}
-template<TypeCategory CAT, int KIND>
-MaybeExpr PackageGeneric(std::optional<Expr<Type<CAT, KIND>>> &&x) {
+template<TypeCategory CAT>
+MaybeExpr AsMaybeExpr(std::optional<Expr<SomeKind<CAT>>> &&x) {
if (x.has_value()) {
- return {AsGenericExpr(AsCategoryExpr(std::move(*x)))};
+ return {AsGenericExpr(std::move(*x))};
}
return std::nullopt;
}
-template<TypeCategory CAT>
-MaybeExpr AsMaybeExpr(std::optional<Expr<SomeKind<CAT>>> &&x) {
+template<TypeCategory CAT, int KIND>
+MaybeExpr AsMaybeExpr(std::optional<Expr<Type<CAT, KIND>>> &&x) {
if (x.has_value()) {
- return {AsGenericExpr(std::move(*x))};
+ return {AsGenericExpr(AsCategoryExpr(std::move(*x)))};
}
return std::nullopt;
}
return std::nullopt;
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Add &x) {
- return BinaryOperationHelper<Add>(*this, x);
-}
-
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Subtract &x) {
- return BinaryOperationHelper<Subtract>(*this, x);
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Power &x) {
+ return BinaryOperationHelper<Power>(*this, x);
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Multiply &x) {
return BinaryOperationHelper<Divide>(*this, x);
}
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Add &x) {
+ return BinaryOperationHelper<Add>(*this, x);
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Subtract &x) {
+ return BinaryOperationHelper<Subtract>(*this, x);
+}
+
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::ComplexConstructor &x) {
return AsMaybeExpr(ConstructComplex(context.messages,
AnalyzeHelper(*this, *std::get<0>(x.t)),
AnalyzeHelper(*this, *std::get<1>(x.t))));
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Power &) {
- context.messages.Say("pmk: Power unimplemented\n"_err_en_US);
- return std::nullopt;
-}
-
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Concat &) {
context.messages.Say("pmk: Concat unimplemented\n"_err_en_US);
return std::nullopt;
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::LT &) {
- context.messages.Say("pmk: .LT. unimplemented\n"_err_en_US);
+// TODO: check defined operators for illegal intrinsic operator cases
+template<typename PARSED>
+MaybeExpr RelationHelper(
+ ExprAnalyzer &ea, RelationalOperator opr, const PARSED &x) {
+ if (auto both{common::AllPresent(AnalyzeHelper(ea, *std::get<0>(x.t)),
+ AnalyzeHelper(ea, *std::get<1>(x.t)))}) {
+ return AsMaybeExpr(Relate(ea.context.messages, opr,
+ std::move(std::get<0>(*both)), std::move(std::get<1>(*both))));
+ }
return std::nullopt;
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::LE &) {
- context.messages.Say("pmk: .LE. unimplemented\n"_err_en_US);
- return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::LT &x) {
+ return RelationHelper(*this, RelationalOperator::LT, x);
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::EQ &) {
- context.messages.Say("pmk: .EQ. unimplemented\n"_err_en_US);
- return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::LE &x) {
+ return RelationHelper(*this, RelationalOperator::LE, x);
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NE &) {
- context.messages.Say("pmk: .NE. unimplemented\n"_err_en_US);
- return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::EQ &x) {
+ return RelationHelper(*this, RelationalOperator::EQ, x);
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::GT &) {
- context.messages.Say("pmk: .GT. unimplemented\n"_err_en_US);
- return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NE &x) {
+ return RelationHelper(*this, RelationalOperator::NE, x);
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::GE &) {
- context.messages.Say("pmk: .GE. unimplemented\n"_err_en_US);
- return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::GE &x) {
+ return RelationHelper(*this, RelationalOperator::GE, x);
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::AND &) {
- context.messages.Say("pmk: .AND. unimplemented\n"_err_en_US);
- return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::GT &x) {
+ return RelationHelper(*this, RelationalOperator::GT, x);
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::OR &) {
- context.messages.Say("pmk: .OR. unimplemented\n"_err_en_US);
+// TODO: check defined operators for illegal intrinsic operator cases
+template<typename PARSED>
+MaybeExpr LogicalHelper(
+ ExprAnalyzer &ea, LogicalOperator opr, const PARSED &x) {
+ if (auto both{common::AllPresent(AnalyzeHelper(ea, *std::get<0>(x.t)),
+ AnalyzeHelper(ea, *std::get<1>(x.t)))}) {
+ return std::visit(
+ common::visitors{
+ [=](Expr<SomeLogical> &&lx, Expr<SomeLogical> &&ly) -> MaybeExpr {
+ return {AsGenericExpr(
+ BinaryLogicalOperation(opr, std::move(lx), std::move(ly)))};
+ },
+ [&](auto &&, auto &&) -> MaybeExpr {
+ // TODO pmk: extensions: INTEGER and typeless operands
+ ea.context.messages.Say(
+ "operands to LOGICAL operation must be LOGICAL"_err_en_US);
+ return {};
+ }},
+ std::move(std::get<0>(*both).u), std::move(std::get<1>(*both).u));
+ }
return std::nullopt;
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::EQV &) {
- context.messages.Say("pmk: .EQV. unimplemented\n"_err_en_US);
- return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::AND &x) {
+ return LogicalHelper(*this, LogicalOperator::And, x);
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NEQV &) {
- context.messages.Say("pmk: .NEQV. unimplemented\n"_err_en_US);
- return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::OR &x) {
+ return LogicalHelper(*this, LogicalOperator::Or, x);
}
-MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::XOR &) {
- context.messages.Say("pmk: .XOR. unimplemented\n"_err_en_US);
- return std::nullopt;
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::EQV &x) {
+ return LogicalHelper(*this, LogicalOperator::Eqv, x);
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NEQV &x) {
+ return LogicalHelper(*this, LogicalOperator::Neqv, x);
+}
+
+MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::XOR &x) {
+ return LogicalHelper(*this, LogicalOperator::Neqv, x);
}
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::DefinedBinary &) {
}
std::ostream &operator<<(std::ostream &os, const DerivedTypeDetails &x) {
- if (const Symbol *extends{x.extends()}) {
+ if (const Symbol * extends{x.extends()}) {
os << " extends:" << extends->name();
}
if (x.sequence()) {