mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-26 20:06:05 +00:00
[flang] add left(), right(), and comments
Original-commit: flang-compiler/f18@372fd06508 Reviewed-on: https://github.com/flang-compiler/f18/pull/183 Tree-same-pre-rewrite: false
This commit is contained in:
parent
710d635cad
commit
003c8329ba
@ -22,6 +22,7 @@
|
||||
// Intended to be as invisible as a reference, wherever possible.
|
||||
|
||||
#include "../common/idioms.h"
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
@ -113,5 +114,44 @@ private:
|
||||
A *p_{nullptr};
|
||||
};
|
||||
|
||||
// A variant of Indirection suitable for use with forward-referenced types.
|
||||
// These are nullable pointers, not references. Allocation is not available,
|
||||
// and a single externalized destructor must be defined.
|
||||
template<typename A> class OwningPointer {
|
||||
public:
|
||||
using element_type = A;
|
||||
|
||||
OwningPointer() {}
|
||||
OwningPointer(OwningPointer &&that) : p_{that.release()} {}
|
||||
explicit OwningPointer(std::unique_ptr<A> &&that) : p_{that.release()} {}
|
||||
explicit OwningPointer(A *&&p) : p_{p} { p = nullptr; }
|
||||
~OwningPointer();
|
||||
OwningPointer &operator=(OwningPointer &&that) {
|
||||
reset(that.release());
|
||||
return *this;
|
||||
}
|
||||
|
||||
A &operator*() { return *p_; }
|
||||
const A &operator*() const { return *p_; }
|
||||
A *operator->() { return p_; }
|
||||
const A *operator->() const { return p_; }
|
||||
|
||||
A *get() const { return p_; }
|
||||
|
||||
A *release() {
|
||||
A *result{p_};
|
||||
p_ = nullptr;
|
||||
return result;
|
||||
}
|
||||
|
||||
void reset(A *p) {
|
||||
this->~OwningPointer();
|
||||
p_ = p;
|
||||
}
|
||||
|
||||
private:
|
||||
A *p_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace Fortran::common
|
||||
#endif // FORTRAN_COMMON_INDIRECTION_H_
|
||||
|
@ -33,16 +33,16 @@ namespace Fortran::evaluate {
|
||||
template<typename D, typename R, typename... O>
|
||||
auto Operation<D, R, O...>::Fold(FoldingContext &context)
|
||||
-> std::optional<Constant<Result>> {
|
||||
auto c0{operand<0>().Fold(context)};
|
||||
if constexpr (operands() == 1) {
|
||||
auto c0{left().Fold(context)};
|
||||
if constexpr (operands == 1) {
|
||||
if (c0.has_value()) {
|
||||
if (auto scalar{derived().FoldScalar(context, c0->value)}) {
|
||||
return {Constant<Result>{std::move(*scalar)}};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
static_assert(operands() == 2); // TODO: generalize to N operands?
|
||||
auto c1{operand<1>().Fold(context)};
|
||||
static_assert(operands == 2); // TODO: generalize to N operands?
|
||||
auto c1{right().Fold(context)};
|
||||
if (c0.has_value() && c1.has_value()) {
|
||||
if (auto scalar{derived().FoldScalar(context, c0->value, c1->value)}) {
|
||||
return {Constant<Result>{std::move(*scalar)}};
|
||||
@ -399,15 +399,30 @@ auto LogicalOperation<KIND>::FoldScalar(FoldingContext &context,
|
||||
|
||||
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 << derived().infix());
|
||||
left().Dump(derived().Prefix(o));
|
||||
if constexpr (operands > 1) {
|
||||
right().Dump(derived().Infix(o));
|
||||
}
|
||||
return o << derived().suffix();
|
||||
return derived().Suffix(o);
|
||||
}
|
||||
|
||||
template<typename A> std::string Relational<A>::infix() const {
|
||||
return "."s + EnumToString(opr) + '.';
|
||||
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) << '.';
|
||||
}
|
||||
|
||||
std::ostream &Relational<SomeType>::Dump(std::ostream &o) const {
|
||||
@ -415,15 +430,15 @@ std::ostream &Relational<SomeType>::Dump(std::ostream &o) const {
|
||||
return o;
|
||||
}
|
||||
|
||||
template<int KIND> const char *LogicalOperation<KIND>::infix() const {
|
||||
const char *result{nullptr};
|
||||
template<int KIND>
|
||||
std::ostream &LogicalOperation<KIND>::Infix(std::ostream &o) const {
|
||||
switch (logicalOperator) {
|
||||
case LogicalOperator::And: result = ".AND."; break;
|
||||
case LogicalOperator::Or: result = ".OR."; break;
|
||||
case LogicalOperator::Eqv: result = ".EQV."; break;
|
||||
case LogicalOperator::Neqv: result = ".NEQV."; break;
|
||||
case LogicalOperator::And: o << ".AND."; break;
|
||||
case LogicalOperator::Or: o << ".OR."; break;
|
||||
case LogicalOperator::Eqv: o << ".EQV."; break;
|
||||
case LogicalOperator::Neqv: o << ".NEQV."; break;
|
||||
}
|
||||
return result;
|
||||
return o;
|
||||
}
|
||||
|
||||
template<typename T> std::ostream &Constant<T>::Dump(std::ostream &o) const {
|
||||
@ -470,12 +485,11 @@ Expr<SubscriptInteger> Expr<Type<TypeCategory::Character, KIND>>::LEN() const {
|
||||
static_cast<std::uint64_t>(c.value.size())});
|
||||
},
|
||||
[](const Concat<KIND> &c) {
|
||||
return c.template operand<0>().LEN() +
|
||||
c.template operand<1>().LEN();
|
||||
return c.left().LEN() + c.template right().LEN();
|
||||
},
|
||||
[](const Extremum<Result> &c) {
|
||||
return Expr<SubscriptInteger>{Extremum<SubscriptInteger>{
|
||||
c.template operand<0>().LEN(), c.template operand<1>().LEN()}};
|
||||
return Expr<SubscriptInteger>{
|
||||
Extremum<SubscriptInteger>{c.left().LEN(), c.right().LEN()}};
|
||||
},
|
||||
[](const DataReference<Result> &dr) { return dr.reference->LEN(); },
|
||||
[](const CopyableIndirection<Substring> &ss) { return ss->LEN(); },
|
||||
@ -496,7 +510,7 @@ auto ExpressionBase<RESULT>::ScalarValue() const
|
||||
if constexpr (common::HasMember<Parentheses<Result>,
|
||||
decltype(derived().u)>) {
|
||||
if (auto p{common::GetIf<Parentheses<Result>>(derived().u)}) {
|
||||
return p->template operand<0>().ScalarValue();
|
||||
return p->left().ScalarValue();
|
||||
}
|
||||
}
|
||||
} else if constexpr (std::is_same_v<Result, SomeType>) {
|
||||
@ -526,6 +540,8 @@ auto ExpressionBase<RESULT>::ScalarValue() const
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Expr<SomeType>::~Expr() {}
|
||||
|
||||
// Template instantiations to resolve the "extern template" declarations
|
||||
// in expression.h.
|
||||
|
||||
@ -602,3 +618,15 @@ template struct ExpressionBase<SomeLogical>;
|
||||
template struct ExpressionBase<SomeType>;
|
||||
|
||||
} // namespace Fortran::evaluate
|
||||
|
||||
// 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>;
|
||||
} // namespace Fortran::common
|
||||
|
@ -86,18 +86,27 @@ template<typename T> struct FunctionReference {
|
||||
CopyableIndirection<FunctionRef> reference;
|
||||
};
|
||||
|
||||
// Abstract Operation<> base class. The first type parameter is a "CRTP"
|
||||
// reference to the specific operation class; e.g., Add is defined with
|
||||
// struct Add : public Operation<Add, ...>.
|
||||
// Operations always have specific Fortran result types (i.e., with known
|
||||
// intrinsic type category and kind parameter value). The classes that
|
||||
// represent the operations all inherit from this Operation<> base class
|
||||
// template. Note that Operation has as its first type parameter (DERIVED) a
|
||||
// "curiously reoccurring template pattern (CRTP)" reference to the specific
|
||||
// operation class being derived from Operation; e.g., Add is defined with
|
||||
// struct Add : public Operation<Add, ...>. Uses of instances of Operation<>,
|
||||
// including its own member functions, can access each specific class derived
|
||||
// from it via its derived() member function with compile-time type safety.
|
||||
template<typename DERIVED, typename RESULT, typename... OPERANDS>
|
||||
class Operation {
|
||||
using OperandTypes = std::tuple<OPERANDS...>;
|
||||
static_assert(RESULT::kind > 0 || !"bad result Type");
|
||||
static_assert(RESULT::isSpecificType || !"bad result Type");
|
||||
// The extra "int" member is a dummy that allows a safe unused reference
|
||||
// to element 1 to arise indirectly in the definition of "right()" below
|
||||
// when the operation has but a single operand.
|
||||
using OperandTypes = std::tuple<OPERANDS..., int>;
|
||||
|
||||
public:
|
||||
using Derived = DERIVED;
|
||||
using Result = RESULT;
|
||||
static constexpr auto operands() { return std::tuple_size_v<OperandTypes>; }
|
||||
static constexpr std::size_t operands{sizeof...(OPERANDS)};
|
||||
template<int J> using Operand = std::tuple_element_t<J, OperandTypes>;
|
||||
using IsFoldableTrait = std::true_type;
|
||||
|
||||
@ -105,7 +114,7 @@ public:
|
||||
// Binary operations wrap a tuple of CopyableIndirections to Exprs.
|
||||
private:
|
||||
using Container =
|
||||
std::conditional_t<operands() == 1, CopyableIndirection<Expr<Operand<0>>>,
|
||||
std::conditional_t<operands == 1, CopyableIndirection<Expr<Operand<0>>>,
|
||||
std::tuple<CopyableIndirection<Expr<OPERANDS>>...>>;
|
||||
|
||||
public:
|
||||
@ -117,8 +126,13 @@ public:
|
||||
Derived &derived() { return *static_cast<Derived *>(this); }
|
||||
const Derived &derived() const { return *static_cast<const Derived *>(this); }
|
||||
|
||||
// References to operand expressions from member functions of derived
|
||||
// classes for specific operators can be made by index, e.g. operand<0>(),
|
||||
// which must be spelled like "this->template operand<0>()" when
|
||||
// inherited in a derived class template. There are convenience aliases
|
||||
// left() and right() that are not templates.
|
||||
template<int J> Expr<Operand<J>> &operand() {
|
||||
if constexpr (operands() == 1) {
|
||||
if constexpr (operands == 1) {
|
||||
static_assert(J == 0);
|
||||
return *operand_;
|
||||
} else {
|
||||
@ -126,7 +140,7 @@ public:
|
||||
}
|
||||
}
|
||||
template<int J> const Expr<Operand<J>> &operand() const {
|
||||
if constexpr (operands() == 1) {
|
||||
if constexpr (operands == 1) {
|
||||
static_assert(J == 0);
|
||||
return *operand_;
|
||||
} else {
|
||||
@ -134,14 +148,29 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
Expr<Operand<0>> &left() { return operand<0>(); }
|
||||
const Expr<Operand<0>> &left() const { return operand<0>(); }
|
||||
|
||||
std::conditional_t<(operands > 1), Expr<Operand<1>> &, void> right() {
|
||||
if constexpr (operands > 1) {
|
||||
return operand<1>();
|
||||
}
|
||||
}
|
||||
std::conditional_t<(operands > 1), const Expr<Operand<1>> &, void>
|
||||
right() const {
|
||||
if constexpr (operands > 1) {
|
||||
return operand<1>();
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream &Dump(std::ostream &) const;
|
||||
std::optional<Constant<Result>> Fold(FoldingContext &);
|
||||
|
||||
protected:
|
||||
// Overridable string functions for Dump()
|
||||
static const char *prefix() { return "("; }
|
||||
static const char *infix() { return ","; }
|
||||
static const char *suffix() { return ")"; }
|
||||
// Overridable functions for Dump()
|
||||
static std::ostream &Prefix(std::ostream &o) { return o << '('; }
|
||||
static std::ostream &Infix(std::ostream &o) { return o << ','; }
|
||||
static std::ostream &Suffix(std::ostream &o) { return o << ')'; }
|
||||
|
||||
private:
|
||||
Container operand_;
|
||||
@ -149,14 +178,25 @@ private:
|
||||
|
||||
// Unary operations
|
||||
|
||||
// Conversions to specific types from expressions of known category and
|
||||
// dynamic kind.
|
||||
template<typename TO, TypeCategory FROMCAT>
|
||||
struct Convert : public Operation<Convert<TO, FROMCAT>, TO, SomeKind<FROMCAT>> {
|
||||
// Fortran doesn't have conversions between kinds of CHARACTER.
|
||||
// Conversions between kinds of COMPLEX are represented piecewise.
|
||||
static_assert(((TO::category == TypeCategory::Integer ||
|
||||
TO::category == TypeCategory::Real) &&
|
||||
(FROMCAT == TypeCategory::Integer ||
|
||||
FROMCAT == TypeCategory::Real)) ||
|
||||
(TO::category == TypeCategory::Logical &&
|
||||
FROMCAT == TypeCategory::Logical));
|
||||
using Result = TO;
|
||||
using Operand = SomeKind<FROMCAT>;
|
||||
using Base = Operation<Convert, Result, Operand>;
|
||||
using Base::Base;
|
||||
static std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &, const Scalar<Operand> &);
|
||||
std::ostream &Dump(std::ostream &) const;
|
||||
};
|
||||
|
||||
template<typename A>
|
||||
@ -178,7 +218,7 @@ template<typename A> struct Negate : public Operation<Negate<A>, A, A> {
|
||||
using Base::Base;
|
||||
static std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &, const Scalar<Operand> &);
|
||||
static const char *prefix() { return "(-"; }
|
||||
static std::ostream &Prefix(std::ostream &o) { return o << "(-"; }
|
||||
};
|
||||
|
||||
template<int KIND>
|
||||
@ -196,7 +236,9 @@ struct ComplexComponent
|
||||
|
||||
std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &, const Scalar<Operand> &) const;
|
||||
const char *suffix() const { return isImaginaryPart ? "%IM)" : "%RE)"; }
|
||||
std::ostream &Suffix(std::ostream &o) const {
|
||||
return o << (isImaginaryPart ? "%IM)" : "%RE)");
|
||||
}
|
||||
|
||||
bool isImaginaryPart{true};
|
||||
};
|
||||
@ -210,7 +252,7 @@ struct Not : public Operation<Not<KIND>, Type<TypeCategory::Logical, KIND>,
|
||||
using Base::Base;
|
||||
static std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &, const Scalar<Operand> &);
|
||||
static const char *prefix() { return "(.NOT."; }
|
||||
static std::ostream &Prefix(std::ostream &o) { return o << "(.NOT."; }
|
||||
};
|
||||
|
||||
// Binary operations
|
||||
@ -222,7 +264,7 @@ template<typename A> struct Add : public Operation<Add<A>, A, A, A> {
|
||||
using Base::Base;
|
||||
static std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
|
||||
static constexpr const char *infix() { return "+"; }
|
||||
static std::ostream &Infix(std::ostream &o) { return o << '+'; }
|
||||
};
|
||||
|
||||
template<typename A> struct Subtract : public Operation<Subtract<A>, A, A, A> {
|
||||
@ -232,7 +274,7 @@ template<typename A> struct Subtract : public Operation<Subtract<A>, A, A, A> {
|
||||
using Base::Base;
|
||||
static std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
|
||||
static constexpr const char *infix() { return "-"; }
|
||||
static std::ostream &Infix(std::ostream &o) { return o << '-'; }
|
||||
};
|
||||
|
||||
template<typename A> struct Multiply : public Operation<Multiply<A>, A, A, A> {
|
||||
@ -242,7 +284,7 @@ template<typename A> struct Multiply : public Operation<Multiply<A>, A, A, A> {
|
||||
using Base::Base;
|
||||
static std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
|
||||
static constexpr const char *infix() { return "*"; }
|
||||
static std::ostream &Infix(std::ostream &o) { return o << '*'; }
|
||||
};
|
||||
|
||||
template<typename A> struct Divide : public Operation<Divide<A>, A, A, A> {
|
||||
@ -252,7 +294,7 @@ template<typename A> struct Divide : public Operation<Divide<A>, A, A, A> {
|
||||
using Base::Base;
|
||||
static std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
|
||||
static constexpr const char *infix() { return "/"; }
|
||||
static std::ostream &Infix(std::ostream &o) { return o << '/'; }
|
||||
};
|
||||
|
||||
template<typename A> struct Power : public Operation<Power<A>, A, A, A> {
|
||||
@ -262,7 +304,7 @@ template<typename A> struct Power : public Operation<Power<A>, A, A, A> {
|
||||
using Base::Base;
|
||||
static std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
|
||||
static constexpr const char *infix() { return "**"; }
|
||||
static std::ostream &Infix(std::ostream &o) { return o << "**"; }
|
||||
};
|
||||
|
||||
template<typename A>
|
||||
@ -274,7 +316,7 @@ struct RealToIntPower : public Operation<RealToIntPower<A>, A, A, SomeInteger> {
|
||||
using Base::Base;
|
||||
static std::optional<Scalar<Result>> FoldScalar(FoldingContext &,
|
||||
const Scalar<BaseOperand> &, const Scalar<ExponentOperand> &);
|
||||
static constexpr const char *infix() { return "**"; }
|
||||
static std::ostream &Infix(std::ostream &o) { return o << "**"; }
|
||||
};
|
||||
|
||||
template<typename A> struct Extremum : public Operation<Extremum<A>, A, A, A> {
|
||||
@ -291,8 +333,8 @@ template<typename A> struct Extremum : public Operation<Extremum<A>, A, A, A> {
|
||||
|
||||
std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &) const;
|
||||
const char *prefix() const {
|
||||
return ordering == Ordering::Less ? "MIN(" : "MAX(";
|
||||
std::ostream &Prefix(std::ostream &o) const {
|
||||
return o << (ordering == Ordering::Less ? "MIN(" : "MAX(");
|
||||
}
|
||||
|
||||
Ordering ordering{Ordering::Greater};
|
||||
@ -322,7 +364,7 @@ struct Concat
|
||||
using Base::Base;
|
||||
static std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &);
|
||||
static constexpr const char *infix() { return "//"; }
|
||||
static std::ostream &Infix(std::ostream &o) { return o << "//"; }
|
||||
};
|
||||
|
||||
ENUM_CLASS(LogicalOperator, And, Or, Eqv, Neqv)
|
||||
@ -343,7 +385,7 @@ struct LogicalOperation
|
||||
|
||||
std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &, const Scalar<Operand> &, const Scalar<Operand> &) const;
|
||||
const char *infix() const;
|
||||
std::ostream &Infix(std::ostream &) const;
|
||||
|
||||
LogicalOperator logicalOperator;
|
||||
};
|
||||
@ -532,7 +574,7 @@ struct Relational : public Operation<Relational<A>, LogicalResult, A, A> {
|
||||
|
||||
std::optional<Scalar<Result>> FoldScalar(
|
||||
FoldingContext &c, const Scalar<Operand> &, const Scalar<Operand> &);
|
||||
std::string infix() const;
|
||||
std::ostream &Infix(std::ostream &) const;
|
||||
|
||||
RelationalOperator opr;
|
||||
};
|
||||
@ -622,6 +664,11 @@ public:
|
||||
using IsFoldableTrait = std::true_type;
|
||||
CLASS_BOILERPLATE(Expr)
|
||||
|
||||
// Owning references to these generic expressions can appear in other
|
||||
// compiler data structures (viz., the parse tree and symbol table), so
|
||||
// its destructor is externalized to reduce redundant default instances.
|
||||
~Expr();
|
||||
|
||||
template<typename A> Expr(const A &x) : u{x} {}
|
||||
template<typename A>
|
||||
Expr(std::enable_if_t<!std::is_reference_v<A>, A> &&x) : u{std::move(x)} {}
|
||||
@ -649,6 +696,14 @@ public:
|
||||
common::CombineVariants<Others, Categories> u;
|
||||
};
|
||||
|
||||
// This wrapper class is used, by means of a forward reference with
|
||||
// OwningPointer, to implement owning pointers to analyzed expressions
|
||||
// from parse tree nodes.
|
||||
struct GenericExprWrapper {
|
||||
GenericExprWrapper(Expr<SomeType> &&x) : v{std::move(x)} {}
|
||||
Expr<SomeType> v;
|
||||
};
|
||||
|
||||
extern template class Expr<SomeInteger>;
|
||||
extern template class Expr<SomeReal>;
|
||||
extern template class Expr<SomeComplex>;
|
||||
|
@ -57,7 +57,9 @@ ConvertRealOperandsResult ConvertRealOperands(
|
||||
std::move(x.u), std::move(y.u));
|
||||
}
|
||||
|
||||
// A helper template for NumericOperation and its subroutines.
|
||||
// Helpers for NumericOperation and its subroutines below.
|
||||
static std::optional<Expr<SomeType>> NoExpr() { return std::nullopt; }
|
||||
|
||||
template<TypeCategory CAT>
|
||||
std::optional<Expr<SomeType>> Package(Expr<SomeKind<CAT>> &&catExpr) {
|
||||
return {AsGenericExpr(std::move(catExpr))};
|
||||
@ -68,7 +70,7 @@ std::optional<Expr<SomeType>> Package(
|
||||
if (catExpr.has_value()) {
|
||||
return {AsGenericExpr(std::move(*catExpr))};
|
||||
}
|
||||
return std::nullopt;
|
||||
return NoExpr();
|
||||
}
|
||||
|
||||
std::optional<Expr<SomeComplex>> ConstructComplex(
|
||||
@ -141,7 +143,7 @@ std::optional<Expr<SomeType>> MixedComplexLeft(
|
||||
Expr<SomeComplex> zy{ConvertTo(zx, std::move(iry))};
|
||||
return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
|
||||
}
|
||||
return std::nullopt;
|
||||
return NoExpr();
|
||||
}
|
||||
|
||||
// Mixed COMPLEX operations with the COMPLEX operand on the right.
|
||||
@ -173,7 +175,7 @@ std::optional<Expr<SomeType>> MixedComplexRight(
|
||||
Expr<SomeComplex> zx{ConvertTo(zy, std::move(irx))};
|
||||
return Package(PromoteAndCombine<OPR>(std::move(zx), std::move(zy)));
|
||||
}
|
||||
return std::nullopt;
|
||||
return NoExpr();
|
||||
}
|
||||
|
||||
// N.B. When a "typeless" BOZ literal constant appears as one (not both!) of
|
||||
@ -254,8 +256,9 @@ std::optional<Expr<SomeType>> NumericOperation(
|
||||
},
|
||||
// Default case
|
||||
[&](auto &&, auto &&) {
|
||||
// TODO: defined operator
|
||||
messages.Say("non-numeric operands to numeric operation"_err_en_US);
|
||||
return std::optional<Expr<SomeType>>{std::nullopt};
|
||||
return NoExpr();
|
||||
}},
|
||||
std::move(x.u), std::move(y.u));
|
||||
}
|
||||
@ -269,4 +272,28 @@ template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
|
||||
template std::optional<Expr<SomeType>> NumericOperation<Divide>(
|
||||
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
|
||||
|
||||
std::optional<Expr<SomeType>> Negation(
|
||||
parser::ContextualMessages &messages, Expr<SomeType> &&x) {
|
||||
return std::visit(
|
||||
common::visitors{[&](BOZLiteralConstant &&) {
|
||||
messages.Say(
|
||||
"BOZ literal cannot be negated"_err_en_US);
|
||||
return NoExpr();
|
||||
},
|
||||
[&](Expr<SomeInteger> &&x) { return Package(std::move(x)); },
|
||||
[&](Expr<SomeReal> &&x) { return Package(-std::move(x)); },
|
||||
[&](Expr<SomeComplex> &&x) { return Package(-std::move(x)); },
|
||||
[&](Expr<SomeCharacter> &&x) {
|
||||
// TODO: defined operator
|
||||
messages.Say("CHARACTER cannot be negated"_err_en_US);
|
||||
return NoExpr();
|
||||
},
|
||||
[&](Expr<SomeLogical> &&x) {
|
||||
// TODO: defined operator
|
||||
messages.Say("LOGICAL cannot be negated"_err_en_US);
|
||||
return NoExpr();
|
||||
}},
|
||||
std::move(x.u));
|
||||
}
|
||||
|
||||
} // namespace Fortran::evaluate
|
||||
|
@ -299,6 +299,9 @@ extern template std::optional<Expr<SomeType>> NumericOperation<Multiply>(
|
||||
extern template std::optional<Expr<SomeType>> NumericOperation<Divide>(
|
||||
parser::ContextualMessages &, Expr<SomeType> &&, Expr<SomeType> &&);
|
||||
|
||||
std::optional<Expr<SomeType>> Negation(
|
||||
parser::ContextualMessages &, 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<>
|
||||
@ -309,6 +312,14 @@ Expr<Type<C, K>> operator-(Expr<Type<C, K>> &&x) {
|
||||
return {Negate<Type<C, K>>{std::move(x)}};
|
||||
}
|
||||
|
||||
template<int K>
|
||||
Expr<Type<TypeCategory::Complex, K>> operator-(
|
||||
Expr<Type<TypeCategory::Complex, K>> &&x) {
|
||||
using Part = Type<TypeCategory::Real, K>;
|
||||
return {ComplexConstructor<K>{Negate<Part>{ComplexComponent<K>{false, x}},
|
||||
Negate<Part>{ComplexComponent<K>{true, 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)}};
|
||||
|
@ -62,6 +62,17 @@ namespace Fortran::semantics {
|
||||
class Symbol;
|
||||
} // namespace Fortran::semantics
|
||||
|
||||
// Expressions in the parse tree have owning pointers that can be set to
|
||||
// type-checked generic expression representations by semantic analysis.
|
||||
// OwningPointer<> is used for leak safety without having to include
|
||||
// the bulk of lib/evaluate/*.h headers into the parser proper.
|
||||
namespace Fortran::evaluate {
|
||||
struct GenericExprWrapper; // forward definition, wraps Expr<SomeType>
|
||||
} // namespace Fortran::evaluate
|
||||
namespace Fortran::common {
|
||||
extern template class OwningPointer<evaluate::GenericExprWrapper>;
|
||||
} // namespace Fortran::common
|
||||
|
||||
// Most non-template classes in this file use these default definitions
|
||||
// for their move constructor and move assignment operator=, and disable
|
||||
// their copy constructor and copy assignment operator=.
|
||||
@ -1684,6 +1695,9 @@ struct Expr {
|
||||
explicit Expr(Designator &&);
|
||||
explicit Expr(FunctionReference &&);
|
||||
|
||||
// Filled in later during semantic analysis of the expression.
|
||||
common::OwningPointer<evaluate::GenericExprWrapper> typedExpr;
|
||||
|
||||
std::variant<common::Indirection<CharLiteralConstantSubstring>,
|
||||
LiteralConstant, common::Indirection<Designator>, ArrayConstructor,
|
||||
StructureConstructor, common::Indirection<TypeParamInquiry>,
|
||||
|
@ -13,10 +13,13 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include "expression.h"
|
||||
#include "dump-parse-tree.h" // TODO pmk temporary
|
||||
#include "symbol.h"
|
||||
#include "../common/idioms.h"
|
||||
#include "../evaluate/common.h"
|
||||
#include "../evaluate/tools.h"
|
||||
#include "../parser/parse-tree-visitor.h"
|
||||
#include "../parser/parse-tree.h"
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
@ -63,7 +66,9 @@ struct ExprAnalyzer {
|
||||
|
||||
int Analyze(
|
||||
const std::optional<parser::KindParam> &, int defaultKind, int kanjiKind);
|
||||
|
||||
MaybeExpr Analyze(const parser::Expr &);
|
||||
MaybeExpr Analyze(const parser::CharLiteralConstantSubstring &);
|
||||
MaybeExpr Analyze(const parser::LiteralConstant &);
|
||||
MaybeExpr Analyze(const parser::HollerithLiteralConstant &);
|
||||
MaybeExpr Analyze(const parser::IntLiteralConstant &);
|
||||
@ -77,32 +82,36 @@ struct ExprAnalyzer {
|
||||
MaybeExpr Analyze(const parser::Name &);
|
||||
MaybeExpr Analyze(const parser::NamedConstant &);
|
||||
MaybeExpr Analyze(const parser::ComplexPart &);
|
||||
|
||||
MaybeExpr Analyze(const parser::Designator &);
|
||||
MaybeExpr Analyze(const parser::ArrayConstructor &);
|
||||
MaybeExpr Analyze(const parser::StructureConstructor &);
|
||||
MaybeExpr Analyze(const parser::TypeParamInquiry &);
|
||||
MaybeExpr Analyze(const parser::FunctionReference &);
|
||||
MaybeExpr Analyze(const parser::Expr::Parentheses &);
|
||||
MaybeExpr Analyze(const parser::Expr::UnaryPlus &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::Negate &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::NOT &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::DefinedUnary &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::Power &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::UnaryPlus &);
|
||||
MaybeExpr Analyze(const parser::Expr::Negate &);
|
||||
MaybeExpr Analyze(const parser::Expr::NOT &);
|
||||
MaybeExpr Analyze(const parser::Expr::PercentLoc &);
|
||||
MaybeExpr Analyze(const parser::Expr::DefinedUnary &);
|
||||
MaybeExpr Analyze(const parser::Expr::Power &);
|
||||
MaybeExpr Analyze(const parser::Expr::Multiply &);
|
||||
MaybeExpr Analyze(const parser::Expr::Divide &);
|
||||
MaybeExpr Analyze(const parser::Expr::Add &);
|
||||
MaybeExpr Analyze(const parser::Expr::Subtract &);
|
||||
MaybeExpr Analyze(const parser::Expr::Concat &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::LT &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::LE &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::EQ &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::NE &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::GE &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::GT &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::AND &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::OR &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::EQV &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::NEQV &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::XOR &); // TODO
|
||||
MaybeExpr Analyze(const parser::Expr::Concat &);
|
||||
MaybeExpr Analyze(const parser::Expr::LT &);
|
||||
MaybeExpr Analyze(const parser::Expr::LE &);
|
||||
MaybeExpr Analyze(const parser::Expr::EQ &);
|
||||
MaybeExpr Analyze(const parser::Expr::NE &);
|
||||
MaybeExpr Analyze(const parser::Expr::GE &);
|
||||
MaybeExpr Analyze(const parser::Expr::GT &);
|
||||
MaybeExpr Analyze(const parser::Expr::AND &);
|
||||
MaybeExpr Analyze(const parser::Expr::OR &);
|
||||
MaybeExpr Analyze(const parser::Expr::EQV &);
|
||||
MaybeExpr Analyze(const parser::Expr::NEQV &);
|
||||
MaybeExpr Analyze(const parser::Expr::XOR &);
|
||||
MaybeExpr Analyze(const parser::Expr::ComplexConstructor &);
|
||||
MaybeExpr Analyze(const parser::Expr::DefinedBinary &); // TODO
|
||||
// TODO more remain
|
||||
MaybeExpr Analyze(const parser::Expr::DefinedBinary &);
|
||||
|
||||
FoldingContext &context;
|
||||
const semantics::IntrinsicTypeDefaultKinds &defaults;
|
||||
@ -149,13 +158,14 @@ MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const std::variant<As...> &u) {
|
||||
return std::visit([&](const auto &x) { return AnalyzeHelper(ea, x); }, u);
|
||||
}
|
||||
|
||||
template<typename A>
|
||||
MaybeExpr AnalyzeHelper(ExprAnalyzer &ea, const common::Indirection<A> &x) {
|
||||
return AnalyzeHelper(ea, *x);
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr &expr) {
|
||||
return std::visit(common::visitors{[&](const parser::LiteralConstant &c) {
|
||||
return AnalyzeHelper(*this, c);
|
||||
},
|
||||
// TODO: remaining cases
|
||||
[&](const auto &) { return MaybeExpr{}; }},
|
||||
expr.u);
|
||||
return std::visit(
|
||||
[&](const auto &x) { return AnalyzeHelper(*this, x); }, expr.u);
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::LiteralConstant &x) {
|
||||
@ -295,9 +305,9 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::RealLiteralConstant &x) {
|
||||
for (const char *p{x.real.source.begin()}; p < end; ++p) {
|
||||
if (parser::IsLetter(*p)) {
|
||||
switch (*p) {
|
||||
case 'e': letterKind = 4; break;
|
||||
case 'd': letterKind = 8; break;
|
||||
case 'q': letterKind = 16; break;
|
||||
case 'e': letterKind = defaults.defaultRealKind; break;
|
||||
case 'd': letterKind = defaults.defaultDoublePrecisionKind; break;
|
||||
case 'q': letterKind = defaults.defaultQuadPrecisionKind; break;
|
||||
default: ctxMsgs.Say("unknown exponent letter '%c'"_err_en_US, *p);
|
||||
}
|
||||
break;
|
||||
@ -407,6 +417,37 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::NamedConstant &n) {
|
||||
return Analyze(n.v);
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::CharLiteralConstantSubstring &) {
|
||||
context.messages.Say(
|
||||
"pmk: CharLiteralConstantSubstring unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Designator &) {
|
||||
context.messages.Say("pmk: Designator unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::ArrayConstructor &) {
|
||||
context.messages.Say("pmk: ArrayConstructor unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::StructureConstructor &) {
|
||||
context.messages.Say("pmk: StructureConstructor unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::TypeParamInquiry &) {
|
||||
context.messages.Say("pmk: TypeParamInquiry unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::FunctionReference &) {
|
||||
context.messages.Say("pmk: FunctionReference unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
|
||||
if (MaybeExpr operand{AnalyzeHelper(*this, *x.v)}) {
|
||||
return std::visit(
|
||||
@ -433,7 +474,33 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Parentheses &x) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// TODO: defined operators for illegal intrinsic operator cases
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::UnaryPlus &x) {
|
||||
return AnalyzeHelper(*this, *x.v);
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::Negate &x) {
|
||||
if (MaybeExpr operand{AnalyzeHelper(*this, *x.v)}) {
|
||||
return Negation(context.messages, std::move(operand->u));
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::NOT &) {
|
||||
context.messages.Say("pmk: NOT unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::PercentLoc &) {
|
||||
context.messages.Say("pmk: %LOC unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::DefinedUnary &) {
|
||||
context.messages.Say("pmk: DefinedUnary unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
// TODO: check defined operators for illegal intrinsic operator cases
|
||||
template<template<typename> class OPR, typename PARSED>
|
||||
MaybeExpr BinaryOperationHelper(ExprAnalyzer &ea, const PARSED &x) {
|
||||
if (auto both{common::AllPresent(AnalyzeHelper(ea, *std::get<0>(x.t)),
|
||||
@ -466,6 +533,76 @@ MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::ComplexConstructor &x) {
|
||||
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);
|
||||
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::EQ &) {
|
||||
context.messages.Say("pmk: .EQ. unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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::GT &) {
|
||||
context.messages.Say("pmk: .GT. unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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::AND &) {
|
||||
context.messages.Say("pmk: .AND. unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
MaybeExpr ExprAnalyzer::Analyze(const parser::Expr::OR &) {
|
||||
context.messages.Say("pmk: .OR. unimplemented\n"_err_en_US);
|
||||
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::NEQV &) {
|
||||
context.messages.Say("pmk: .NEQV. unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
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::DefinedBinary &) {
|
||||
context.messages.Say("pmk: DefinedBinary unimplemented\n"_err_en_US);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Fortran::evaluate
|
||||
|
||||
namespace Fortran::semantics {
|
||||
@ -475,4 +612,40 @@ MaybeExpr AnalyzeExpr(evaluate::FoldingContext &context,
|
||||
return evaluate::ExprAnalyzer{context, defaults}.Analyze(expr);
|
||||
}
|
||||
|
||||
class Mutator {
|
||||
public:
|
||||
Mutator(evaluate::FoldingContext &context,
|
||||
const IntrinsicTypeDefaultKinds &defaults, std::ostream &o)
|
||||
: context_{context}, defaults_{defaults}, out_{o} {}
|
||||
|
||||
template<typename A> bool Pre(A &) { return true /* visit children */; }
|
||||
template<typename A> void Post(A &) {}
|
||||
|
||||
bool Pre(parser::Expr &expr) {
|
||||
if (expr.typedExpr.get() == nullptr) {
|
||||
if (MaybeExpr checked{AnalyzeExpr(context_, defaults_, expr)}) {
|
||||
checked->Dump(out_ << "pmk checked: ") << '\n';
|
||||
expr.typedExpr.reset(
|
||||
new evaluate::GenericExprWrapper{std::move(*checked)});
|
||||
} else {
|
||||
out_ << "pmk: expression analysis failed for an expression: ";
|
||||
DumpTree(out_, expr);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
evaluate::FoldingContext &context_;
|
||||
const IntrinsicTypeDefaultKinds &defaults_;
|
||||
std::ostream &out_;
|
||||
};
|
||||
|
||||
void AnalyzeExpressions(parser::Program &program,
|
||||
evaluate::FoldingContext &context,
|
||||
const IntrinsicTypeDefaultKinds &defaults, std::ostream &o) {
|
||||
Mutator mutator{context, defaults, o};
|
||||
parser::Walk(program, mutator);
|
||||
}
|
||||
|
||||
} // namespace Fortran::semantics
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "../parser/parse-tree.h"
|
||||
#include <cinttypes>
|
||||
#include <optional>
|
||||
#include <ostream> // TODO pmk
|
||||
|
||||
namespace Fortran::semantics {
|
||||
|
||||
@ -29,6 +30,8 @@ using MaybeExpr = std::optional<evaluate::Expr<evaluate::SomeType>>;
|
||||
struct IntrinsicTypeDefaultKinds {
|
||||
int defaultIntegerKind{evaluate::DefaultInteger::kind};
|
||||
int defaultRealKind{evaluate::DefaultReal::kind};
|
||||
int defaultDoublePrecisionKind{evaluate::DefaultDoublePrecision::kind};
|
||||
int defaultQuadPrecisionKind{evaluate::DefaultDoublePrecision::kind};
|
||||
int defaultCharacterKind{evaluate::DefaultCharacter::kind};
|
||||
int defaultLogicalKind{evaluate::DefaultLogical::kind};
|
||||
};
|
||||
@ -36,5 +39,8 @@ struct IntrinsicTypeDefaultKinds {
|
||||
MaybeExpr AnalyzeExpr(evaluate::FoldingContext &,
|
||||
const IntrinsicTypeDefaultKinds &, const parser::Expr &);
|
||||
|
||||
void AnalyzeExpressions(parser::Program &, evaluate::FoldingContext &,
|
||||
const IntrinsicTypeDefaultKinds &, std::ostream &);
|
||||
|
||||
} // namespace Fortran::semantics
|
||||
#endif // FORTRAN_SEMANTICS_EXPRESSION_H_
|
||||
|
@ -20,4 +20,5 @@ add_executable(f18
|
||||
target_link_libraries(f18
|
||||
FortranParser
|
||||
FortranSemantics
|
||||
FortranEvaluate
|
||||
)
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "../../lib/parser/provenance.h"
|
||||
#include "../../lib/parser/unparse.h"
|
||||
#include "../../lib/semantics/dump-parse-tree.h"
|
||||
#include "../../lib/semantics/expression.h"
|
||||
#include "../../lib/semantics/mod-file.h"
|
||||
#include "../../lib/semantics/resolve-labels.h"
|
||||
#include "../../lib/semantics/resolve-names.h"
|
||||
@ -92,6 +93,7 @@ struct DriverOptions {
|
||||
bool dumpUnparseWithSymbols{false};
|
||||
bool dumpParseTree{false};
|
||||
bool dumpSymbols{false};
|
||||
bool debugExpressions{false};
|
||||
bool debugResolveNames{false};
|
||||
bool measureTree{false};
|
||||
std::vector<std::string> pgf90Args;
|
||||
@ -208,7 +210,7 @@ std::string CompileFortran(
|
||||
MeasureParseTree(parseTree);
|
||||
}
|
||||
if (driver.debugResolveNames || driver.dumpSymbols ||
|
||||
driver.dumpUnparseWithSymbols) {
|
||||
driver.dumpUnparseWithSymbols || driver.debugExpressions) {
|
||||
std::vector<std::string> directories{options.searchDirectories};
|
||||
directories.insert(directories.begin(), "."s);
|
||||
if (driver.moduleDirectory != "."s) {
|
||||
@ -236,6 +238,22 @@ std::string CompileFortran(
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (driver.debugExpressions) {
|
||||
Fortran::parser::CharBlock whole{parsing.cooked().data()};
|
||||
Fortran::parser::Messages messages;
|
||||
Fortran::parser::ContextualMessages contextualMessages{whole, &messages};
|
||||
Fortran::evaluate::FoldingContext context{contextualMessages};
|
||||
Fortran::semantics::IntrinsicTypeDefaultKinds defaults;
|
||||
Fortran::semantics::AnalyzeExpressions(
|
||||
parseTree, context, defaults, std::cout);
|
||||
messages.Emit(std::cerr, parsing.cooked());
|
||||
if (!messages.empty() &&
|
||||
(driver.warningsAreErrors || messages.AnyFatalError())) {
|
||||
std::cerr << driver.prefix << "semantic errors in " << path << '\n';
|
||||
exitStatus = EXIT_FAILURE;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
if (driver.dumpParseTree) {
|
||||
Fortran::semantics::DumpTree(std::cout, parseTree);
|
||||
}
|
||||
@ -394,6 +412,8 @@ int main(int argc, char *const argv[]) {
|
||||
driver.dumpParseTree = true;
|
||||
} else if (arg == "-fdebug-dump-symbols") {
|
||||
driver.dumpSymbols = true;
|
||||
} else if (arg == "-fdebug-expressions") {
|
||||
driver.debugExpressions = true;
|
||||
} else if (arg == "-fdebug-resolve-names") {
|
||||
driver.debugResolveNames = true;
|
||||
} else if (arg == "-fdebug-measure-parse-tree") {
|
||||
|
Loading…
x
Reference in New Issue
Block a user