2018-06-20 20:55:10 +02:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
#include "expression.h"
|
2018-08-09 01:30:58 +02:00
|
|
|
#include "common.h"
|
2018-10-24 01:48:06 +02:00
|
|
|
#include "fold.h"
|
2018-07-25 22:46:13 +02:00
|
|
|
#include "int-power.h"
|
2018-08-20 18:29:08 +02:00
|
|
|
#include "tools.h"
|
2018-07-07 00:12:33 +02:00
|
|
|
#include "variable.h"
|
2018-06-20 20:55:10 +02:00
|
|
|
#include "../common/idioms.h"
|
2018-07-07 00:12:33 +02:00
|
|
|
#include "../parser/characters.h"
|
2018-07-24 21:58:29 +02:00
|
|
|
#include "../parser/message.h"
|
2018-06-20 20:55:10 +02:00
|
|
|
#include <ostream>
|
|
|
|
#include <string>
|
2018-06-21 01:50:27 +02:00
|
|
|
#include <type_traits>
|
2018-06-20 20:55:10 +02:00
|
|
|
|
2018-06-22 23:51:15 +02:00
|
|
|
using namespace Fortran::parser::literals;
|
|
|
|
|
2018-06-20 20:55:10 +02:00
|
|
|
namespace Fortran::evaluate {
|
|
|
|
|
2018-08-18 00:38:37 +02:00
|
|
|
// Fold
|
2018-08-16 20:46:18 +02:00
|
|
|
|
2018-08-15 22:46:33 +02:00
|
|
|
template<typename D, typename R, typename... O>
|
|
|
|
auto Operation<D, R, O...>::Fold(FoldingContext &context)
|
2018-08-29 00:15:18 +02:00
|
|
|
-> std::optional<Constant<Result>> {
|
2018-09-07 19:33:32 +02:00
|
|
|
auto c0{left().Fold(context)};
|
|
|
|
if constexpr (operands == 1) {
|
2018-08-15 22:46:33 +02:00
|
|
|
if (c0.has_value()) {
|
2018-08-29 00:15:18 +02:00
|
|
|
if (auto scalar{derived().FoldScalar(context, c0->value)}) {
|
|
|
|
return {Constant<Result>{std::move(*scalar)}};
|
|
|
|
}
|
2018-08-15 22:46:33 +02:00
|
|
|
}
|
|
|
|
} else {
|
2018-09-07 19:33:32 +02:00
|
|
|
static_assert(operands == 2); // TODO: generalize to N operands?
|
|
|
|
auto c1{right().Fold(context)};
|
2018-08-15 22:46:33 +02:00
|
|
|
if (c0.has_value() && c1.has_value()) {
|
2018-08-29 00:15:18 +02:00
|
|
|
if (auto scalar{derived().FoldScalar(context, c0->value, c1->value)}) {
|
|
|
|
return {Constant<Result>{std::move(*scalar)}};
|
|
|
|
}
|
2018-08-15 22:46:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2018-08-29 00:15:18 +02:00
|
|
|
template<typename RESULT>
|
|
|
|
auto ExpressionBase<RESULT>::Fold(FoldingContext &context)
|
|
|
|
-> std::optional<Constant<Result>> {
|
|
|
|
using Const = Constant<Result>;
|
2018-10-09 21:07:29 +02:00
|
|
|
if constexpr (Result::isSpecificIntrinsicType) {
|
2018-08-29 00:15:18 +02:00
|
|
|
// Folding an expression of known type category and kind.
|
|
|
|
return std::visit(
|
|
|
|
[&](auto &x) -> std::optional<Const> {
|
|
|
|
using Thing = std::decay_t<decltype(x)>;
|
2018-08-30 19:09:44 +02:00
|
|
|
if constexpr (std::is_same_v<Thing, Const>) {
|
2018-08-29 00:15:18 +02:00
|
|
|
return {x};
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
2018-08-29 00:15:18 +02:00
|
|
|
if constexpr (IsFoldableTrait<Thing>) {
|
|
|
|
if (auto c{x.Fold(context)}) {
|
|
|
|
static constexpr TypeCategory category{Result::category};
|
|
|
|
if constexpr (category == TypeCategory::Real ||
|
|
|
|
category == TypeCategory::Complex) {
|
|
|
|
if (context.flushDenormalsToZero) {
|
|
|
|
c->value = c->value.FlushDenormalToZero();
|
|
|
|
}
|
|
|
|
} else if constexpr (category == TypeCategory::Logical) {
|
|
|
|
// Folding may have produced a constant of some
|
|
|
|
// dissimilar LOGICAL kind.
|
|
|
|
bool truth{c->value.IsTrue()};
|
2018-09-13 01:27:51 +02:00
|
|
|
derived() = Derived{truth};
|
2018-08-29 00:15:18 +02:00
|
|
|
return {Const{truth}};
|
|
|
|
}
|
|
|
|
if constexpr (std::is_same_v<Parentheses<Result>, Thing>) {
|
|
|
|
// Preserve parentheses around constants.
|
2018-09-13 01:27:51 +02:00
|
|
|
derived() = Derived{Thing{Derived{*c}}};
|
2018-08-29 00:15:18 +02:00
|
|
|
} else {
|
2018-09-13 01:27:51 +02:00
|
|
|
derived() = Derived{*c};
|
2018-08-29 00:15:18 +02:00
|
|
|
}
|
|
|
|
return {Const{c->value}};
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
2018-08-29 00:15:18 +02:00
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
|
|
|
derived().u);
|
|
|
|
} else {
|
|
|
|
// Folding a generic expression into a generic constant.
|
|
|
|
return std::visit(
|
|
|
|
[&](auto &x) -> std::optional<Const> {
|
|
|
|
if constexpr (IsFoldableTrait<std::decay_t<decltype(x)>>) {
|
|
|
|
if (auto c{x.Fold(context)}) {
|
2018-10-09 21:07:29 +02:00
|
|
|
if constexpr (ResultType<decltype(*c)>::isSpecificIntrinsicType) {
|
2018-08-29 00:15:18 +02:00
|
|
|
return {Const{c->value}};
|
|
|
|
} else {
|
|
|
|
return {Const{common::MoveVariant<GenericScalar>(c->value.u)}};
|
|
|
|
}
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
|
|
|
}
|
2018-08-29 00:15:18 +02:00
|
|
|
return std::nullopt;
|
|
|
|
},
|
|
|
|
derived().u);
|
|
|
|
}
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
|
|
|
|
2018-08-29 00:15:18 +02:00
|
|
|
// FoldScalar
|
|
|
|
|
|
|
|
template<typename TO, TypeCategory FROMCAT>
|
|
|
|
auto Convert<TO, FROMCAT>::FoldScalar(FoldingContext &context,
|
|
|
|
const Scalar<Operand> &x) -> std::optional<Scalar<Result>> {
|
2018-08-18 00:38:37 +02:00
|
|
|
return std::visit(
|
2018-08-29 00:15:18 +02:00
|
|
|
[&](const auto &c) -> std::optional<Scalar<Result>> {
|
|
|
|
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)};
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
2018-08-29 00:15:18 +02:00
|
|
|
} else 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);
|
2018-08-18 00:38:37 +02:00
|
|
|
} else {
|
2018-08-29 00:15:18 +02:00
|
|
|
return {std::move(converted.value)};
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
|
|
|
}
|
2018-08-29 00:15:18 +02:00
|
|
|
} else 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)};
|
|
|
|
} else 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)};
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
2018-08-29 00:15:18 +02:00
|
|
|
x.u);
|
2018-08-15 22:46:33 +02:00
|
|
|
}
|
|
|
|
|
2018-08-16 20:46:18 +02:00
|
|
|
template<typename A>
|
|
|
|
auto Negate<A>::FoldScalar(FoldingContext &context, const Scalar<Operand> &c)
|
|
|
|
-> std::optional<Scalar<Result>> {
|
|
|
|
if constexpr (Result::category == TypeCategory::Integer) {
|
|
|
|
auto negated{c.Negate()};
|
|
|
|
if (negated.overflow) {
|
|
|
|
context.messages.Say("INTEGER negation overflowed"_en_US);
|
|
|
|
} else {
|
|
|
|
return {std::move(negated.value)};
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return {c.Negate()}; // REAL & COMPLEX: no exceptions possible
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2018-08-17 18:50:32 +02:00
|
|
|
template<int KIND>
|
|
|
|
auto ComplexComponent<KIND>::FoldScalar(FoldingContext &context,
|
|
|
|
const Scalar<Operand> &z) const -> std::optional<Scalar<Result>> {
|
2018-08-23 19:55:16 +02:00
|
|
|
return {isImaginaryPart ? z.AIMAG() : z.REAL()};
|
2018-08-16 20:46:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template<int KIND>
|
|
|
|
auto Not<KIND>::FoldScalar(FoldingContext &context, const Scalar<Operand> &x)
|
|
|
|
-> std::optional<Scalar<Result>> {
|
|
|
|
return {Scalar<Result>{!x.IsTrue()}};
|
2018-06-21 21:47:28 +02:00
|
|
|
}
|
|
|
|
|
2018-08-17 18:50:32 +02:00
|
|
|
template<typename A>
|
|
|
|
auto Add<A>::FoldScalar(FoldingContext &context, const Scalar<Operand> &x,
|
|
|
|
const Scalar<Operand> &y) -> std::optional<Scalar<Result>> {
|
|
|
|
if constexpr (Result::category == TypeCategory::Integer) {
|
|
|
|
auto sum{x.AddSigned(y)};
|
|
|
|
if (sum.overflow) {
|
2018-08-18 00:38:37 +02:00
|
|
|
context.messages.Say(
|
|
|
|
"INTEGER(KIND=%d) addition overflowed"_en_US, Result::kind);
|
|
|
|
return std::nullopt;
|
2018-08-17 18:50:32 +02:00
|
|
|
}
|
2018-08-18 00:38:37 +02:00
|
|
|
return {std::move(sum.value)};
|
2018-08-17 18:50:32 +02:00
|
|
|
} else {
|
|
|
|
auto sum{x.Add(y, context.rounding)};
|
|
|
|
RealFlagWarnings(context, sum.flags, "addition");
|
|
|
|
return {std::move(sum.value)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-18 00:38:37 +02:00
|
|
|
template<typename A>
|
|
|
|
auto Subtract<A>::FoldScalar(FoldingContext &context, const Scalar<Operand> &x,
|
|
|
|
const Scalar<Operand> &y) -> std::optional<Scalar<Result>> {
|
|
|
|
if constexpr (Result::category == TypeCategory::Integer) {
|
|
|
|
auto diff{x.SubtractSigned(y)};
|
|
|
|
if (diff.overflow) {
|
|
|
|
context.messages.Say(
|
|
|
|
"INTEGER(KIND=%d) subtraction overflowed"_en_US, Result::kind);
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
return {std::move(diff.value)};
|
|
|
|
} else {
|
|
|
|
auto difference{x.Subtract(y, context.rounding)};
|
|
|
|
RealFlagWarnings(context, difference.flags, "subtraction");
|
|
|
|
return {std::move(difference.value)};
|
|
|
|
}
|
2018-07-26 00:02:21 +02:00
|
|
|
}
|
|
|
|
|
2018-08-18 00:38:37 +02:00
|
|
|
template<typename A>
|
|
|
|
auto Multiply<A>::FoldScalar(FoldingContext &context, const Scalar<Operand> &x,
|
|
|
|
const Scalar<Operand> &y) -> std::optional<Scalar<Result>> {
|
|
|
|
if constexpr (Result::category == TypeCategory::Integer) {
|
|
|
|
auto product{x.MultiplySigned(y)};
|
|
|
|
if (product.SignedMultiplicationOverflowed()) {
|
|
|
|
context.messages.Say(
|
|
|
|
"INTEGER(KIND=%d) multiplication overflowed"_en_US, Result::kind);
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
return {std::move(product.lower)};
|
|
|
|
} else {
|
|
|
|
auto product{x.Multiply(y, context.rounding)};
|
|
|
|
RealFlagWarnings(context, product.flags, "multiplication");
|
|
|
|
return {std::move(product.value)};
|
|
|
|
}
|
2018-07-26 00:02:21 +02:00
|
|
|
}
|
|
|
|
|
2018-08-18 00:38:37 +02:00
|
|
|
template<typename A>
|
|
|
|
auto Divide<A>::FoldScalar(FoldingContext &context, const Scalar<Operand> &x,
|
|
|
|
const Scalar<Operand> &y) -> std::optional<Scalar<Result>> {
|
|
|
|
if constexpr (Result::category == TypeCategory::Integer) {
|
|
|
|
auto qr{x.DivideSigned(y)};
|
|
|
|
if (qr.divisionByZero) {
|
|
|
|
context.messages.Say("INTEGER division by zero"_en_US);
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
if (qr.overflow) {
|
|
|
|
context.messages.Say(
|
|
|
|
"INTEGER(KIND=%d) division overflowed"_en_US, Result::kind);
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
return {std::move(qr.quotient)};
|
|
|
|
} else {
|
|
|
|
auto quotient{x.Divide(y, context.rounding)};
|
|
|
|
RealFlagWarnings(context, quotient.flags, "division");
|
|
|
|
return {std::move(quotient.value)};
|
2018-08-16 20:46:18 +02:00
|
|
|
}
|
2018-07-19 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
2018-08-18 00:38:37 +02:00
|
|
|
template<typename A>
|
|
|
|
auto Power<A>::FoldScalar(FoldingContext &context, const Scalar<Operand> &x,
|
|
|
|
const Scalar<Operand> &y) -> std::optional<Scalar<Result>> {
|
|
|
|
if constexpr (Result::category == TypeCategory::Integer) {
|
|
|
|
typename Scalar<Result>::PowerWithErrors power{x.Power(y)};
|
|
|
|
if (power.divisionByZero) {
|
|
|
|
context.messages.Say("zero to negative power"_en_US);
|
|
|
|
} else if (power.overflow) {
|
|
|
|
context.messages.Say(
|
|
|
|
"INTEGER(KIND=%d) power overflowed"_en_US, Result::kind);
|
|
|
|
} else if (power.zeroToZero) {
|
|
|
|
context.messages.Say("INTEGER 0**0 is not defined"_en_US);
|
|
|
|
} else {
|
|
|
|
return {std::move(power.power)};
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// TODO: real and complex exponentiation to non-integer powers
|
2018-07-26 01:02:08 +02:00
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2018-08-29 00:15:18 +02:00
|
|
|
template<typename A>
|
|
|
|
auto RealToIntPower<A>::FoldScalar(FoldingContext &context,
|
|
|
|
const Scalar<BaseOperand> &x, const Scalar<ExponentOperand> &y)
|
2018-08-14 22:39:59 +02:00
|
|
|
-> std::optional<Scalar<Result>> {
|
2018-08-18 00:38:37 +02:00
|
|
|
return std::visit(
|
|
|
|
[&](const auto &pow) -> std::optional<Scalar<Result>> {
|
|
|
|
auto power{evaluate::IntPower(x, pow)};
|
|
|
|
RealFlagWarnings(context, power.flags, "raising to INTEGER power");
|
|
|
|
return {std::move(power.value)};
|
|
|
|
},
|
2018-08-23 23:49:28 +02:00
|
|
|
y.u);
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template<typename A>
|
|
|
|
auto Extremum<A>::FoldScalar(FoldingContext &context, const Scalar<Operand> &x,
|
|
|
|
const Scalar<Operand> &y) const -> std::optional<Scalar<Result>> {
|
|
|
|
if constexpr (Operand::category == TypeCategory::Integer) {
|
|
|
|
if (ordering == x.CompareSigned(y)) {
|
|
|
|
return {x};
|
|
|
|
}
|
|
|
|
} else if constexpr (Operand::category == TypeCategory::Real) {
|
|
|
|
if (x.IsNotANumber() ||
|
|
|
|
(x.Compare(y) == Relation::Less) == (ordering == Ordering::Less)) {
|
|
|
|
return {x};
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (ordering == Compare(x, y)) {
|
|
|
|
return {x};
|
|
|
|
}
|
2018-07-26 01:02:08 +02:00
|
|
|
}
|
2018-08-18 00:38:37 +02:00
|
|
|
return {y};
|
2018-07-26 01:02:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template<int KIND>
|
2018-08-18 00:38:37 +02:00
|
|
|
auto ComplexConstructor<KIND>::FoldScalar(
|
|
|
|
FoldingContext &context, const Scalar<Operand> &x, const Scalar<Operand> &y)
|
2018-08-14 22:39:59 +02:00
|
|
|
-> std::optional<Scalar<Result>> {
|
2018-08-18 00:38:37 +02:00
|
|
|
return {Scalar<Result>{x, y}};
|
2018-07-26 01:02:08 +02:00
|
|
|
}
|
|
|
|
|
2018-07-20 21:19:09 +02:00
|
|
|
template<int KIND>
|
2018-08-18 00:38:37 +02:00
|
|
|
auto Concat<KIND>::FoldScalar(FoldingContext &context, const Scalar<Operand> &x,
|
|
|
|
const Scalar<Operand> &y) -> std::optional<Scalar<Result>> {
|
|
|
|
if constexpr (KIND == 1) {
|
|
|
|
return {x + y};
|
2018-08-16 20:46:18 +02:00
|
|
|
}
|
2018-08-18 00:38:37 +02:00
|
|
|
return std::nullopt;
|
2018-07-19 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
2018-07-24 00:04:08 +02:00
|
|
|
template<typename A>
|
2018-08-18 00:38:37 +02:00
|
|
|
auto Relational<A>::FoldScalar(FoldingContext &c, const Scalar<Operand> &a,
|
2018-08-14 22:39:59 +02:00
|
|
|
const Scalar<Operand> &b) -> std::optional<Scalar<Result>> {
|
2018-08-01 18:45:59 +02:00
|
|
|
if constexpr (A::category == TypeCategory::Integer) {
|
2018-07-24 00:04:08 +02:00
|
|
|
switch (a.CompareSigned(b)) {
|
|
|
|
case Ordering::Less:
|
|
|
|
return {opr == RelationalOperator::LE || opr == RelationalOperator::LE ||
|
|
|
|
opr == RelationalOperator::NE};
|
|
|
|
case Ordering::Equal:
|
|
|
|
return {opr == RelationalOperator::LE || opr == RelationalOperator::EQ ||
|
|
|
|
opr == RelationalOperator::GE};
|
|
|
|
case Ordering::Greater:
|
|
|
|
return {opr == RelationalOperator::NE || opr == RelationalOperator::GE ||
|
|
|
|
opr == RelationalOperator::GT};
|
|
|
|
}
|
|
|
|
}
|
2018-08-01 18:45:59 +02:00
|
|
|
if constexpr (A::category == TypeCategory::Real) {
|
2018-07-24 21:58:29 +02:00
|
|
|
switch (a.Compare(b)) {
|
|
|
|
case Relation::Less:
|
|
|
|
return {opr == RelationalOperator::LE || opr == RelationalOperator::LE ||
|
|
|
|
opr == RelationalOperator::NE};
|
|
|
|
case Relation::Equal:
|
|
|
|
return {opr == RelationalOperator::LE || opr == RelationalOperator::EQ ||
|
|
|
|
opr == RelationalOperator::GE};
|
|
|
|
case Relation::Greater:
|
|
|
|
return {opr == RelationalOperator::NE || opr == RelationalOperator::GE ||
|
|
|
|
opr == RelationalOperator::GT};
|
2018-07-26 00:02:21 +02:00
|
|
|
case Relation::Unordered: return std::nullopt;
|
2018-07-24 21:58:29 +02:00
|
|
|
}
|
|
|
|
}
|
2018-08-01 18:45:59 +02:00
|
|
|
if constexpr (A::category == TypeCategory::Character) {
|
2018-07-26 00:52:58 +02:00
|
|
|
switch (Compare(a, b)) {
|
|
|
|
case Ordering::Less:
|
|
|
|
return {opr == RelationalOperator::LE || opr == RelationalOperator::LE ||
|
|
|
|
opr == RelationalOperator::NE};
|
|
|
|
case Ordering::Equal:
|
|
|
|
return {opr == RelationalOperator::LE || opr == RelationalOperator::EQ ||
|
|
|
|
opr == RelationalOperator::GE};
|
|
|
|
case Ordering::Greater:
|
|
|
|
return {opr == RelationalOperator::NE || opr == RelationalOperator::GE ||
|
|
|
|
opr == RelationalOperator::GT};
|
|
|
|
}
|
|
|
|
}
|
2018-07-26 00:02:21 +02:00
|
|
|
return std::nullopt;
|
2018-07-24 00:04:08 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
template<int KIND>
|
2018-08-18 00:38:37 +02:00
|
|
|
auto LogicalOperation<KIND>::FoldScalar(FoldingContext &context,
|
|
|
|
const Scalar<Operand> &x, const Scalar<Operand> &y) const
|
2018-08-14 22:39:59 +02:00
|
|
|
-> std::optional<Scalar<Result>> {
|
2018-08-18 00:38:37 +02:00
|
|
|
bool xt{x.IsTrue()}, yt{y.IsTrue()};
|
|
|
|
switch (logicalOperator) {
|
|
|
|
case LogicalOperator::And: return {Scalar<Result>{xt && yt}};
|
|
|
|
case LogicalOperator::Or: return {Scalar<Result>{xt || yt}};
|
|
|
|
case LogicalOperator::Eqv: return {Scalar<Result>{xt == yt}};
|
|
|
|
case LogicalOperator::Neqv: return {Scalar<Result>{xt != yt}};
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dump
|
|
|
|
|
|
|
|
template<typename D, typename R, typename... O>
|
|
|
|
std::ostream &Operation<D, R, O...>::Dump(std::ostream &o) const {
|
2018-09-07 19:33:32 +02:00
|
|
|
left().Dump(derived().Prefix(o));
|
|
|
|
if constexpr (operands > 1) {
|
|
|
|
right().Dump(derived().Infix(o));
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
2018-09-07 19:33:32 +02:00
|
|
|
return derived().Suffix(o);
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
|
|
|
|
2018-09-07 19:33:32 +02:00
|
|
|
template<typename TO, TypeCategory FROMCAT>
|
|
|
|
std::ostream &Convert<TO, FROMCAT>::Dump(std::ostream &o) const {
|
|
|
|
static_assert(TO::category == TypeCategory::Integer ||
|
|
|
|
TO::category == TypeCategory::Real ||
|
|
|
|
TO::category == TypeCategory::Logical || !"Convert<> to bad category!");
|
|
|
|
if constexpr (TO::category == TypeCategory::Integer) {
|
|
|
|
o << "INT";
|
|
|
|
} else if constexpr (TO::category == TypeCategory::Real) {
|
|
|
|
o << "REAL";
|
|
|
|
} else if constexpr (TO::category == TypeCategory::Logical) {
|
|
|
|
o << "LOGICAL";
|
|
|
|
}
|
|
|
|
return this->left().Dump(o << '(') << ",KIND=" << TO::kind << ')';
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename A> std::ostream &Relational<A>::Infix(std::ostream &o) const {
|
|
|
|
return o << '.' << EnumToString(opr) << '.';
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
|
|
|
|
2018-08-29 00:15:18 +02:00
|
|
|
std::ostream &Relational<SomeType>::Dump(std::ostream &o) const {
|
|
|
|
std::visit([&](const auto &rel) { rel.Dump(o); }, u);
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2018-09-07 19:33:32 +02:00
|
|
|
template<int KIND>
|
|
|
|
std::ostream &LogicalOperation<KIND>::Infix(std::ostream &o) const {
|
2018-08-18 00:38:37 +02:00
|
|
|
switch (logicalOperator) {
|
2018-09-07 19:33:32 +02:00
|
|
|
case LogicalOperator::And: o << ".AND."; break;
|
|
|
|
case LogicalOperator::Or: o << ".OR."; break;
|
|
|
|
case LogicalOperator::Eqv: o << ".EQV."; break;
|
|
|
|
case LogicalOperator::Neqv: o << ".NEQV."; break;
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
2018-09-07 19:33:32 +02:00
|
|
|
return o;
|
2018-08-18 00:38:37 +02:00
|
|
|
}
|
|
|
|
|
2018-08-29 00:15:18 +02:00
|
|
|
template<typename T> std::ostream &Constant<T>::Dump(std::ostream &o) const {
|
|
|
|
if constexpr (T::category == TypeCategory::Integer) {
|
2018-09-08 01:54:33 +02:00
|
|
|
return o << value.SignedDecimal() << '_' << T::kind;
|
2018-08-29 00:15:18 +02:00
|
|
|
} else if constexpr (T::category == TypeCategory::Real ||
|
|
|
|
T::category == TypeCategory::Complex) {
|
2018-09-08 01:54:33 +02:00
|
|
|
return o << value.DumpHexadecimal() << '_' << T::kind;
|
2018-08-29 00:15:18 +02:00
|
|
|
} else if constexpr (T::category == TypeCategory::Character) {
|
2018-09-08 01:54:33 +02:00
|
|
|
if constexpr (T::kind == 1) {
|
|
|
|
return o << T::kind << '_' << parser::QuoteCharacterLiteral(value);
|
|
|
|
} else {
|
|
|
|
return o << T::kind
|
|
|
|
<< "_'(wide character dumping unimplemented)'"; // TODO
|
|
|
|
}
|
2018-08-29 00:15:18 +02:00
|
|
|
} else if constexpr (T::category == TypeCategory::Logical) {
|
|
|
|
if (value.IsTrue()) {
|
|
|
|
o << ".TRUE.";
|
|
|
|
} else {
|
|
|
|
o << ".FALSE.";
|
|
|
|
}
|
|
|
|
return o << '_' << Result::kind;
|
|
|
|
} else {
|
|
|
|
return value.u.Dump(o);
|
|
|
|
}
|
2018-07-19 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
2018-08-29 00:15:18 +02:00
|
|
|
template<typename RESULT>
|
|
|
|
std::ostream &ExpressionBase<RESULT>::Dump(std::ostream &o) const {
|
2018-09-20 21:34:29 +02:00
|
|
|
std::visit(common::visitors{[&](const BOZLiteralConstant &x) {
|
|
|
|
o << "Z'" << x.Hexadecimal() << "'";
|
|
|
|
},
|
|
|
|
[&](const CopyableIndirection<Substring> &s) { s->Dump(o); },
|
|
|
|
[&](const auto &x) { x.Dump(o); }},
|
2018-08-29 00:15:18 +02:00
|
|
|
derived().u);
|
2018-08-18 00:38:37 +02:00
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2018-10-09 00:35:19 +02:00
|
|
|
std::ostream &Expr<SomeDerived>::Dump(std::ostream &o) const {
|
|
|
|
std::visit([&](const auto &x) { x.Dump(o); }, u);
|
|
|
|
return o;
|
|
|
|
}
|
|
|
|
|
2018-08-18 00:38:37 +02:00
|
|
|
template<int KIND>
|
|
|
|
Expr<SubscriptInteger> Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
|
2018-07-19 19:12:25 +02:00
|
|
|
return std::visit(
|
2018-08-29 00:15:18 +02:00
|
|
|
common::visitors{[](const Constant<Result> &c) {
|
2018-10-10 22:54:25 +02:00
|
|
|
return AsExpr(
|
|
|
|
Constant<SubscriptInteger>{c.value.size()});
|
2018-08-18 00:38:37 +02:00
|
|
|
},
|
2018-09-17 20:31:38 +02:00
|
|
|
[](const Parentheses<Result> &x) { return x.left().LEN(); },
|
2018-08-18 00:38:37 +02:00
|
|
|
[](const Concat<KIND> &c) {
|
2018-09-17 20:31:38 +02:00
|
|
|
return c.left().LEN() + c.right().LEN();
|
2018-08-09 01:30:58 +02:00
|
|
|
},
|
2018-08-18 00:38:37 +02:00
|
|
|
[](const Extremum<Result> &c) {
|
2018-09-07 19:33:32 +02:00
|
|
|
return Expr<SubscriptInteger>{
|
|
|
|
Extremum<SubscriptInteger>{c.left().LEN(), c.right().LEN()}};
|
2018-08-18 00:38:37 +02:00
|
|
|
},
|
2018-09-15 00:48:40 +02:00
|
|
|
[](const Designator<Result> &dr) { return dr.LEN(); },
|
2018-09-20 21:34:29 +02:00
|
|
|
[](const FunctionRef<Result> &fr) { return fr.LEN(); }},
|
2018-07-20 21:19:09 +02:00
|
|
|
u);
|
2018-07-19 18:53:42 +02:00
|
|
|
}
|
|
|
|
|
2018-08-29 00:15:18 +02:00
|
|
|
template<typename RESULT>
|
|
|
|
auto ExpressionBase<RESULT>::ScalarValue() const
|
|
|
|
-> std::optional<Scalar<Result>> {
|
2018-10-09 21:07:29 +02:00
|
|
|
if constexpr (Result::isSpecificIntrinsicType) {
|
2018-09-12 22:53:14 +02:00
|
|
|
if (auto *c{std::get_if<Constant<Result>>(&derived().u)}) {
|
2018-08-30 19:09:44 +02:00
|
|
|
return {c->value};
|
|
|
|
}
|
2018-09-17 20:31:38 +02:00
|
|
|
if (auto *p{std::get_if<Parentheses<Result>>(&derived().u)}) {
|
|
|
|
return p->left().ScalarValue();
|
2018-08-30 19:09:44 +02:00
|
|
|
}
|
|
|
|
} else if constexpr (std::is_same_v<Result, SomeType>) {
|
|
|
|
return std::visit(
|
|
|
|
common::visitors{
|
|
|
|
[](const BOZLiteralConstant &) -> std::optional<Scalar<Result>> {
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
2018-09-15 00:48:40 +02:00
|
|
|
[](const Expr<SomeDerived> &) -> std::optional<Scalar<Result>> {
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
2018-08-30 19:09:44 +02:00
|
|
|
[](const auto &catEx) -> std::optional<Scalar<Result>> {
|
|
|
|
if (auto cv{catEx.ScalarValue()}) {
|
|
|
|
// *cv is SomeKindScalar<CAT> for some category; rewrap it.
|
|
|
|
return {common::MoveVariant<GenericScalar>(std::move(cv->u))};
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}},
|
|
|
|
derived().u);
|
|
|
|
} else {
|
|
|
|
return std::visit(
|
|
|
|
[](const auto &kindEx) -> std::optional<Scalar<Result>> {
|
|
|
|
if (auto sv{kindEx.ScalarValue()}) {
|
|
|
|
return {SomeKindScalar<Result::category>{*sv}};
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
},
|
|
|
|
derived().u);
|
|
|
|
}
|
|
|
|
return std::nullopt;
|
2018-08-15 01:48:49 +02:00
|
|
|
}
|
|
|
|
|
2018-09-07 19:33:32 +02:00
|
|
|
Expr<SomeType>::~Expr() {}
|
|
|
|
|
2018-10-09 00:35:19 +02:00
|
|
|
template<typename A>
|
|
|
|
std::optional<DynamicType> ExpressionBase<A>::GetType() const {
|
2018-10-09 21:07:29 +02:00
|
|
|
if constexpr (Result::isSpecificIntrinsicType) {
|
|
|
|
return Result::GetType();
|
2018-10-09 00:35:19 +02:00
|
|
|
} else {
|
|
|
|
return std::visit(
|
|
|
|
[](const auto &x) -> std::optional<DynamicType> {
|
2018-10-15 21:17:30 +02:00
|
|
|
if constexpr (!std::is_same_v<std::decay_t<decltype(x)>,
|
2018-10-09 00:35:19 +02:00
|
|
|
BOZLiteralConstant>) {
|
|
|
|
return x.GetType();
|
|
|
|
}
|
2018-10-15 21:17:30 +02:00
|
|
|
return std::nullopt; // typeless -> no type
|
2018-10-09 00:35:19 +02:00
|
|
|
},
|
|
|
|
derived().u);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<DynamicType> Expr<SomeDerived>::GetType() const {
|
|
|
|
return std::visit([](const auto &x) { return x.GetType(); }, u);
|
|
|
|
}
|
|
|
|
|
2018-09-18 18:34:59 +02:00
|
|
|
template<typename A> int ExpressionBase<A>::Rank() const {
|
|
|
|
return std::visit(
|
2018-10-01 23:36:31 +02:00
|
|
|
[](const auto &x) {
|
|
|
|
if constexpr (std::is_same_v<std::decay_t<decltype(x)>,
|
|
|
|
BOZLiteralConstant>) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return x.Rank();
|
|
|
|
}
|
|
|
|
},
|
2018-09-18 18:34:59 +02:00
|
|
|
derived().u);
|
|
|
|
}
|
|
|
|
|
2018-10-09 00:35:19 +02:00
|
|
|
int Expr<SomeDerived>::Rank() const {
|
|
|
|
return std::visit([](const auto &x) { return x.Rank(); }, u);
|
|
|
|
}
|
|
|
|
|
2018-08-29 00:15:18 +02:00
|
|
|
// Template instantiations to resolve the "extern template" declarations
|
2018-09-12 20:20:30 +02:00
|
|
|
// that appear in expression.h.
|
2018-08-18 00:38:37 +02:00
|
|
|
|
2018-09-25 22:26:35 +02:00
|
|
|
FOR_EACH_INTRINSIC_KIND(template class Expr)
|
|
|
|
FOR_EACH_CATEGORY_TYPE(template class Expr)
|
2018-09-20 21:34:29 +02:00
|
|
|
FOR_EACH_INTEGER_KIND(template struct Relational)
|
|
|
|
FOR_EACH_REAL_KIND(template struct Relational)
|
|
|
|
FOR_EACH_CHARACTER_KIND(template struct Relational)
|
2018-08-29 00:15:18 +02:00
|
|
|
template struct Relational<SomeType>;
|
2018-09-20 21:34:29 +02:00
|
|
|
FOR_EACH_INTRINSIC_KIND(template struct ExpressionBase)
|
|
|
|
FOR_EACH_CATEGORY_TYPE(template struct ExpressionBase)
|
2018-10-25 14:55:23 +02:00
|
|
|
}
|
2018-09-07 19:33:32 +02:00
|
|
|
|
|
|
|
// For reclamation of analyzed expressions to which owning pointers have
|
|
|
|
// been embedded in the parse tree. This destructor appears here, where
|
|
|
|
// definitions for all the necessary types are available, to obviate a
|
|
|
|
// need to include lib/evaluate/*.h headers in the parser proper.
|
|
|
|
namespace Fortran::common {
|
|
|
|
template<> OwningPointer<evaluate::GenericExprWrapper>::~OwningPointer() {
|
|
|
|
delete p_;
|
|
|
|
p_ = nullptr;
|
|
|
|
}
|
|
|
|
template class OwningPointer<evaluate::GenericExprWrapper>;
|
2018-10-25 14:55:23 +02:00
|
|
|
}
|