// Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #ifndef FORTRAN_EVALUATE_EXPRESSION_H_ #define FORTRAN_EVALUATE_EXPRESSION_H_ // Represent Fortran expressions in a type-safe manner. // Expressions are the sole owners of their constituents; there is no // context-independent hash table or sharing of common subexpressions. // Both deep copy and move semantics are supported for expression construction. // TODO: variable and function references #include "common.h" #include "type.h" #include #include #include namespace Fortran::evaluate { // Some forward definitions template struct IntegerExpr; template struct RealExpr; template struct ComplexExpr; template struct CharacterExpr; struct AnyIntegerExpr; struct AnyRealExpr; struct AnyComplexExpr; struct AnyCharacterExpr; struct AnyIntegerOrRealExpr; // Helper base classes to manage subexpressions, which are known as data members // named 'x' and (for binary operations) 'y'. template struct Unary { Unary(const A &a) : x{std::make_unique(a)} {} Unary(std::unique_ptr &&a) : x{std::move(a)} {} Unary(A &&a) : x{std::make_unique(std::move(a))} {} Unary(const Unary &that) : x{std::make_unique(*that.x)} {} Unary(Unary &&) = default; Unary &operator=(const Unary &that) { x = std::make_unique(*that.x); return *this; } Unary &operator=(Unary &&) = default; std::unique_ptr x; }; template struct Binary { Binary(const A &a, const B &b) : x{std::make_unique(a)}, y{std::make_unique(b)} {} Binary(std::unique_ptr &&a, std::unique_ptr &&b) : x{std::move(a)}, y{std::move(b)} {} Binary(A &&a, B &&b) : x{std::make_unique(std::move(a))}, y{std::make_unique( std::move(b))} {} Binary(const Binary &that) : x{std::make_unique(*that.x)}, y{std::make_unique(*that.y)} {} Binary(Binary &&) = default; Binary &operator=(const Binary &that) { x = std::make_unique(*that.x); y = std::make_unique(*that.y); return *this; } Binary &operator=(Binary &&) = default; std::unique_ptr x; std::unique_ptr y; }; template struct IntegerExpr { using Result = Type; using Constant = typename Result::Value; struct Convert : Unary { using Unary::Unary; }; using Un = Unary; using Bin = Binary; struct Parentheses : public Un { using Un::Un; }; struct Negate : public Un { using Un::Un; }; struct Add : public Bin { using Bin::Bin; }; struct Subtract : public Bin { using Bin::Bin; }; struct Multiply : public Bin { using Bin::Bin; }; struct Divide : public Bin { using Bin::Bin; }; struct Power : public Bin { using Bin::Bin; }; IntegerExpr() = delete; IntegerExpr(const IntegerExpr &) = default; IntegerExpr(IntegerExpr &&) = default; IntegerExpr(const Constant &x) : u{x} {} IntegerExpr(std::int64_t n) : u{Constant{n}} {} IntegerExpr(int n) : u{Constant{n}} {} template IntegerExpr(A &&x) : u{std::move(x)} {} IntegerExpr &operator=(const IntegerExpr &) = default; IntegerExpr &operator=(IntegerExpr &&) = default; std::ostream &Dump(std::ostream &) const; std::variant u; }; using DefaultIntegerExpr = IntegerExpr; template struct RealExpr { using Result = Type; using Constant = typename Result::Value; // N.B. Real->Complex and Complex->Real conversions are done with CMPLX // and part access operations (resp.). Conversions between kinds of // Complex are done via decomposition to Real and reconstruction. struct Convert : Unary { using Unary::Unary; }; using Un = Unary; using Bin = Binary; struct Parentheses : public Un { using Un::Un; }; struct Negate : public Un { using Un::Un; }; struct Add : public Bin { using Bin::Bin; }; struct Subtract : public Bin { using Bin::Bin; }; struct Multiply : public Bin { using Bin::Bin; }; struct Divide : public Bin { using Bin::Bin; }; struct Power : public Bin { using Bin::Bin; }; struct IntPower : public Binary { using Binary::Binary; }; using CplxUn = Unary>; struct RealPart : public CplxUn { using CplxUn::CplxUn; }; struct AIMAG : public CplxUn { using CplxUn::CplxUn; }; RealExpr() = delete; RealExpr(const RealExpr &) = default; RealExpr(RealExpr &&) = default; RealExpr(const Constant &x) : u{x} {} template RealExpr(A &&x) : u{std::move(x)} {} RealExpr &operator=(const RealExpr &) = default; RealExpr &operator=(RealExpr &&) = default; std::ostream &Dump(std::ostream &) const; std::variant u; }; template struct ComplexExpr { using Result = Type; using Constant = typename Result::Value; using Un = Unary; using Bin = Binary; struct Parentheses : public Un { using Un::Un; }; struct Negate : public Un { using Un::Un; }; struct Add : public Bin { using Bin::Bin; }; struct Subtract : public Bin { using Bin::Bin; }; struct Multiply : public Bin { using Bin::Bin; }; struct Divide : public Bin { using Bin::Bin; }; struct Power : public Bin { using Bin::Bin; }; struct IntPower : public Binary { using Binary::Binary; }; struct CMPLX : public Binary, RealExpr> { using Binary, RealExpr>::Binary; }; ComplexExpr() = delete; ComplexExpr(const ComplexExpr &) = default; ComplexExpr(ComplexExpr &&) = default; ComplexExpr(const Constant &x) : u{x} {} template ComplexExpr(A &&x) : u{std::move(x)} {} ComplexExpr &operator=(const ComplexExpr &) = default; ComplexExpr &operator=(ComplexExpr &&) = default; std::ostream &Dump(std::ostream &) const; std::variant u; }; template struct CharacterExpr { using Result = Type; using Constant = typename Result::Value; using LengthExpr = IntegerExpr; struct Concat : public Binary { using Binary::Binary; }; CharacterExpr() = delete; CharacterExpr(const CharacterExpr &) = default; CharacterExpr(CharacterExpr &&) = default; CharacterExpr(const Constant &x) : u{x} {} CharacterExpr(Constant &&x) : u{std::move(x)} {} CharacterExpr(Concat &&x) : u{std::move(x)} {} CharacterExpr &operator=(const CharacterExpr &) = default; CharacterExpr &operator=(CharacterExpr &&) = default; IntegerExpr LEN() const; std::ostream &Dump(std::ostream &) const; std::variant u; }; // The Comparison class template is a helper for constructing logical // expressions with polymorphism over the cross product of the possible // categories and kinds of comparable operands. template struct Comparison { struct LT : public Binary { using Binary::Binary; }; struct LE : public Binary { using Binary::Binary; }; struct EQ : public Binary { using Binary::Binary; }; struct NE : public Binary { using Binary::Binary; }; struct GE : public Binary { using Binary::Binary; }; struct GT : public Binary { using Binary::Binary; }; Comparison() = delete; Comparison(const Comparison &) = default; Comparison(Comparison &&) = default; template Comparison(A &&x) : u{std::move(x)} {} Comparison &operator=(const Comparison &) = default; Comparison &operator=(Comparison &&) = default; std::ostream &Dump(std::ostream &) const; std::variant u; }; // COMPLEX admits only .EQ. and .NE. comparisons. template struct Comparison> { using Bin = Binary, ComplexExpr>; struct EQ : public Bin { using Bin::Bin; }; struct NE : public Bin { using Bin::Bin; }; Comparison() = delete; Comparison(const Comparison &) = default; Comparison(Comparison &&) = default; template Comparison(A &&x) : u{std::move(x)} {} Comparison &operator=(const Comparison &) = default; Comparison &operator=(Comparison &&) = default; std::ostream &Dump(std::ostream &) const; std::variant u; }; struct IntegerComparison { IntegerComparison() = delete; IntegerComparison(const IntegerComparison &) = default; IntegerComparison(IntegerComparison &&) = default; template IntegerComparison(A &&x) : u{std::move(x)} {} IntegerComparison &operator=(const IntegerComparison &) = default; IntegerComparison &operator=(IntegerComparison &&) = default; std::ostream &Dump(std::ostream &) const; template using C = Comparison>; IntegerKindsVariant u; }; struct RealComparison { RealComparison() = delete; RealComparison(const RealComparison &) = default; RealComparison(RealComparison &&) = default; template RealComparison(A &&x) : u{std::move(x)} {} RealComparison &operator=(const RealComparison &) = default; RealComparison &operator=(RealComparison &&) = default; std::ostream &Dump(std::ostream &) const; template using C = Comparison>; RealKindsVariant u; }; struct ComplexComparison { ComplexComparison() = delete; ComplexComparison(ComplexComparison &&) = default; template ComplexComparison(A &&x) : u{std::move(x)} {} ComplexComparison &operator=(const ComplexComparison &) = default; ComplexComparison &operator=(ComplexComparison &&) = default; std::ostream &Dump(std::ostream &) const; template using C = Comparison>; ComplexKindsVariant u; }; struct CharacterComparison { CharacterComparison() = delete; CharacterComparison(const CharacterComparison &) = default; CharacterComparison(CharacterComparison &&) = default; template CharacterComparison(A &&x) : u{std::move(x)} {} CharacterComparison &operator=(const CharacterComparison &) = default; CharacterComparison &operator=(CharacterComparison &&) = default; std::ostream &Dump(std::ostream &) const; template using C = Comparison>; CharacterKindsVariant u; }; // No need to distinguish the various kinds of LOGICAL expression results. struct LogicalExpr { using Constant = bool; struct Not : Unary { using Unary::Unary; }; using Bin = Binary; struct And : public Bin { using Bin::Bin; }; struct Or : public Bin { using Bin::Bin; }; struct Eqv : public Bin { using Bin::Bin; }; struct Neqv : public Bin { using Bin::Bin; }; LogicalExpr() = delete; LogicalExpr(const LogicalExpr &) = default; LogicalExpr(LogicalExpr &&) = default; LogicalExpr(Constant x) : u{x} {} template LogicalExpr(const Comparison> &x) : u{IntegerComparison{x}} {} template LogicalExpr(Comparison> &&x) : u{IntegerComparison{std::move(x)}} {} template LogicalExpr(const Comparison> &x) : u{RealComparison{x}} {} template LogicalExpr(Comparison> &&x) : u{RealComparison{std::move(x)}} {} template LogicalExpr(const Comparison> &x) : u{ComplexComparison{x}} {} template LogicalExpr(Comparison> &&x) : u{ComplexComparison{std::move(x)}} {} template LogicalExpr(const Comparison> &x) : u{CharacterComparison{x}} {} template LogicalExpr(Comparison> &&x) : u{CharacterComparison{std::move(x)}} {} template LogicalExpr(A &&x) : u{std::move(x)} {} LogicalExpr &operator=(const LogicalExpr &) = default; LogicalExpr &operator=(LogicalExpr &&) = default; std::ostream &Dump(std::ostream &) const; std::variant u; }; // Dynamically polymorphic expressions that can hold any supported kind. struct AnyIntegerExpr { AnyIntegerExpr() = delete; AnyIntegerExpr(const AnyIntegerExpr &) = default; AnyIntegerExpr(AnyIntegerExpr &&) = default; template AnyIntegerExpr(const IntegerExpr &x) : u{x} {} template AnyIntegerExpr(IntegerExpr &&x) : u{std::move(x)} {} AnyIntegerExpr &operator=(const AnyIntegerExpr &) = default; AnyIntegerExpr &operator=(AnyIntegerExpr &&) = default; std::ostream &Dump(std::ostream &) const; IntegerKindsVariant u; }; struct AnyRealExpr { AnyRealExpr() = delete; AnyRealExpr(const AnyRealExpr &) = default; AnyRealExpr(AnyRealExpr &&) = default; template AnyRealExpr(const RealExpr &x) : u{x} {} template AnyRealExpr(RealExpr &&x) : u{std::move(x)} {} AnyRealExpr &operator=(const AnyRealExpr &) = default; AnyRealExpr &operator=(AnyRealExpr &&) = default; std::ostream &Dump(std::ostream &) const; RealKindsVariant u; }; struct AnyComplexExpr { AnyComplexExpr() = delete; AnyComplexExpr(const AnyComplexExpr &) = default; AnyComplexExpr(AnyComplexExpr &&) = default; template AnyComplexExpr(const ComplexExpr &x) : u{x} {} template AnyComplexExpr(ComplexExpr &&x) : u{std::move(x)} {} AnyComplexExpr &operator=(const AnyComplexExpr &) = default; AnyComplexExpr &operator=(AnyComplexExpr &&) = default; std::ostream &Dump(std::ostream &) const; ComplexKindsVariant u; }; struct AnyCharacterExpr { AnyCharacterExpr() = delete; AnyCharacterExpr(const AnyCharacterExpr &) = default; AnyCharacterExpr(AnyCharacterExpr &&) = default; template AnyCharacterExpr(const CharacterExpr &x) : u{x} {} template AnyCharacterExpr(CharacterExpr &&x) : u{std::move(x)} {} AnyCharacterExpr &operator=(const AnyCharacterExpr &) = default; AnyCharacterExpr &operator=(AnyCharacterExpr &&) = default; std::ostream &Dump(std::ostream &) const; CharacterKindsVariant u; }; struct AnyIntegerOrRealExpr { AnyIntegerOrRealExpr() = delete; AnyIntegerOrRealExpr(const AnyIntegerOrRealExpr &) = default; AnyIntegerOrRealExpr(AnyIntegerOrRealExpr &&) = default; template AnyIntegerOrRealExpr(const IntegerExpr &x) : u{AnyIntegerExpr{x}} {} template AnyIntegerOrRealExpr(IntegerExpr &&x) : u{AnyIntegerExpr{std::move(x)}} {} template AnyIntegerOrRealExpr(const RealExpr &x) : u{AnyRealExpr{x}} {} template AnyIntegerOrRealExpr(RealExpr &&x) : u{AnyRealExpr{std::move(x)}} {} AnyIntegerOrRealExpr(const AnyIntegerExpr &x) : u{x} {} AnyIntegerOrRealExpr(AnyIntegerExpr &&x) : u{std::move(x)} {} AnyIntegerOrRealExpr(const AnyRealExpr &x) : u{x} {} AnyIntegerOrRealExpr(AnyRealExpr &&x) : u{std::move(x)} {} AnyIntegerOrRealExpr &operator=(const AnyIntegerOrRealExpr &) = default; AnyIntegerOrRealExpr &operator=(AnyIntegerOrRealExpr &&) = default; std::ostream &Dump(std::ostream &) const; std::variant u; }; // Convenience functions and operator overloadings for expression construction. #define UNARY(FUNC, CONSTR) \ template A FUNC(const A &x) { return {typename A::CONSTR{x}}; } UNARY(Parentheses, Parentheses) UNARY(operator-, Negate) #undef UNARY #define BINARY(FUNC, CONSTR) \ template A FUNC(const A &x, const A &y) { \ return {typename A::CONSTR{x, y}}; \ } \ template \ std::enable_if_t, A> FUNC(const A &x, A &&y) { \ return {typename A::CONSTR{A{x}, std::move(y)}}; \ } \ template \ std::enable_if_t, A> FUNC(A &&x, const A &y) { \ return {typename A::CONSTR{std::move(x), A{y}}}; \ } \ template \ std::enable_if_t, A> FUNC(A &&x, A &&y) { \ return {typename A::CONSTR{std::move(x), std::move(y)}}; \ } BINARY(operator+, Add) BINARY(operator-, Subtract) BINARY(operator*, Multiply) BINARY(operator/, Divide) BINARY(Power, Power) #undef BINARY #define BINARY(FUNC, CONSTR) \ template LogicalExpr FUNC(const A &x, const A &y) { \ return {Comparison{typename Comparison::CONSTR{x, y}}}; \ } \ template \ std::enable_if_t, LogicalExpr> FUNC( \ const A &x, A &&y) { \ return { \ Comparison{typename Comparison::CONSTR{A{x}, std::move(y)}}}; \ } \ template \ std::enable_if_t, LogicalExpr> FUNC( \ A &&x, const A &y) { \ return { \ Comparison{typename Comparison::CONSTR{std::move(x), A{y}}}}; \ } \ template \ std::enable_if_t, LogicalExpr> FUNC(A &&x, A &&y) { \ return {Comparison{ \ typename Comparison::CONSTR{std::move(x), std::move(y)}}}; \ } BINARY(operator<, LT) BINARY(operator<=, LE) BINARY(operator==, EQ) BINARY(operator!=, NE) BINARY(operator>=, GE) BINARY(operator>, GT) #undef BINARY // External class template instantiations are in expression.cc. extern template struct IntegerExpr<1>; extern template struct IntegerExpr<2>; extern template struct IntegerExpr<4>; extern template struct IntegerExpr<8>; extern template struct IntegerExpr<16>; extern template struct RealExpr<2>; extern template struct RealExpr<4>; extern template struct RealExpr<8>; extern template struct RealExpr<10>; extern template struct RealExpr<16>; extern template struct ComplexExpr<2>; extern template struct ComplexExpr<4>; extern template struct ComplexExpr<8>; extern template struct ComplexExpr<10>; extern template struct ComplexExpr<16>; extern template struct CharacterExpr<1>; } // namespace Fortran::evaluate #endif // FORTRAN_EVALUATE_EXPRESSION_H_