FLANG
match.h
1//===-- include/flang/Evaluate/match.h --------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8#ifndef FORTRAN_EVALUATE_MATCH_H_
9#define FORTRAN_EVALUATE_MATCH_H_
10
11#include "flang/Common/Fortran-consts.h"
12#include "flang/Common/visit.h"
13#include "flang/Evaluate/expression.h"
14#include "flang/Support/Fortran.h"
15#include "llvm/ADT/STLExtras.h"
16
17#include <tuple>
18#include <type_traits>
19#include <utility>
20#include <variant>
21
22namespace Fortran::evaluate {
23namespace match {
24namespace detail {
25template <typename, typename = void> //
27 static constexpr bool value{false};
28};
29
30template <typename T>
31struct IsOperation<T, std::void_t<decltype(T::operands)>> {
32 static constexpr bool value{true};
33};
34} // namespace detail
35
36template <typename T>
37constexpr bool is_operation_v{detail::IsOperation<T>::value};
38
39template <common::TypeCategory C, int K>
40const evaluate::Expr<Type<C, K>> &deparen(const evaluate::Expr<Type<C, K>> &x) {
41 if (auto *parens{std::get_if<Parentheses<Type<C, K>>>(&x.u)}) {
42 return deparen(parens->template operand<0>());
43 } else {
44 return x;
45 }
46}
47
48template <common::TypeCategory C>
49const evaluate::Expr<SomeKind<C>> &deparen(
50 const evaluate::Expr<SomeKind<C>> &x) {
51 return x;
52}
53
54// Some expressions (e.g. TypelessExpression) don't allow parentheses, while
55// those that do have Expr<Type> as the argument to the parentheses. This means
56// that there is no consistent return type that works for all expressions.
57// Delete this overload explicitly so an attempt to use it creates a clearer
58// error message.
59const evaluate::Expr<SomeType> &deparen(
60 const evaluate::Expr<SomeType> &) = delete;
61
62// Expr<T> matchers (patterns)
63//
64// Each pattern should implement
65// bool match(const U &input) const
66// member function that returns `true` when the match was successful,
67// and `false` otherwise.
68//
69// Patterns are intended to be composable, i.e. a pattern can take operands
70// which themselves are patterns. This composition is expected to match if
71// the root pattern and all its operands match given input.
72
75template <typename T> struct TypePattern {
76 using MatchType = llvm::remove_cvref_t<T>;
77
78 template <typename U> bool match(const U &input) const {
79 if constexpr (std::is_same_v<MatchType, U>) {
80 ref = &input;
81 return true;
82 } else {
83 return false;
84 }
85 }
86
87 mutable const MatchType *ref{nullptr};
88};
89
98template <typename... Patterns> struct AnyOfPattern {
99 static_assert(sizeof...(Patterns) != 0);
100
101private:
102 using PatternTuple = std::tuple<Patterns...>;
103
104 template <size_t I>
105 using Pattern = typename std::tuple_element<I, PatternTuple>::type;
106
107 template <size_t... Is, typename... Ops>
108 AnyOfPattern(std::index_sequence<Is...>, const Ops &...ops)
109 : patterns(std::make_tuple(Pattern<Is>(ops...)...)) {}
110
111 template <typename P, typename U>
112 bool matchOne(const P &pattern, const U &input) const {
113 if (pattern.match(input)) {
114 ref = &pattern;
115 return true;
116 }
117 return false;
118 }
119
120 template <typename U, size_t... Is>
121 bool matchImpl(const U &input, std::index_sequence<Is...>) const {
122 return (matchOne(std::get<Is>(patterns), input) || ...);
123 }
124
125 PatternTuple patterns;
126
127public:
128 using Indexes = std::index_sequence_for<Patterns...>;
129 using MatchTypes = std::tuple<typename Patterns::MatchType...>;
130
131 template <typename... Ops>
132 AnyOfPattern(const Ops &...ops) : AnyOfPattern(Indexes{}, ops...) {}
133
134 template <typename U> bool match(const U &input) const {
135 return matchImpl(input, Indexes{});
136 }
137
138 mutable std::variant<const Patterns *..., std::monostate> ref{
139 std::monostate{}};
140};
141
144template <typename T> //
145struct ExprPattern : public TypePattern<evaluate::Expr<T>> {};
146
148template <typename OpType, typename... Ops>
149struct OperationPattern : public TypePattern<OpType> {
150private:
151 using Indexes = std::index_sequence_for<Ops...>;
152
153 template <typename S, size_t... Is>
154 bool matchImpl(const S &op, std::index_sequence<Is...>) const {
155 using TypeS = llvm::remove_cvref_t<S>;
156 if constexpr (is_operation_v<TypeS>) {
157 if constexpr (TypeS::operands == Indexes::size()) {
158 return TypePattern<OpType>::match(op) &&
159 (std::get<Is>(operands).match(op.template operand<Is>()) && ...);
160 }
161 }
162 return false;
163 }
164
165 std::tuple<const Ops &...> operands;
166
167public:
168 using MatchType = OpType;
169
170 OperationPattern(const Ops &...ops, llvm::type_identity<OpType> = {})
171 : operands(ops...) {}
172
173 template <typename T> bool match(const evaluate::Expr<T> &input) const {
174 return common::visit(
175 [&](auto &&s) { return matchImpl(s, Indexes{}); }, deparen(input).u);
176 }
177
178 template <typename U> bool match(const U &input) const {
179 // Only match Expr<T>
180 return false;
181 }
182};
183
184template <typename OpType, typename... Ops>
185OperationPattern(const Ops &..., llvm::type_identity<OpType>)
186 -> OperationPattern<OpType, Ops...>;
187
188// Encode the actual operator in the type, so that the class is constructible
189// only from operand patterns. This will make it usable in AnyOfPattern.
190template <common::LogicalOperator Operator, typename ValType, typename... Ops>
191struct LogicalOperationPattern
192 : public OperationPattern<LogicalOperation<ValType::kind>, Ops...> {
193 using Base = OperationPattern<LogicalOperation<ValType::kind>, Ops...>;
194 static constexpr common::LogicalOperator opCode{Operator};
195
196private:
197 template <int K> bool matchOp(const LogicalOperation<K> &op) const {
198 if constexpr (ValType::kind == K) {
199 return op.logicalOperator == opCode;
200 }
201 return false;
202 }
203 template <typename U> bool matchOp(const U &) const { return false; }
204
205public:
206 LogicalOperationPattern(const Ops &...ops, llvm::type_identity<ValType> = {})
207 : Base(ops...) {}
208
209 template <typename T> bool match(const evaluate::Expr<T> &input) const {
210 // All logical operations (for a given type T) have the same operation
211 // type (LogicalOperation<T::kind>), so the type-based matching will not
212 // be able to tell specific operations from one another.
213 // Check the operation code first, if that matches then use the the
214 // base class's match.
215 if (common::visit([&](auto &&s) { return matchOp(s); }, deparen(input).u)) {
216 return Base::match(input);
217 } else {
218 return false;
219 }
220 }
221
222 template <typename U> bool match(const U &input) const { //
223 return false;
224 }
225};
226
227// No deduction guide for LogicalOperationPattern, since the "Operator"
228// parameter cannot be deduced from the constructor arguments.
229
230// Namespace-level definitions
231
232template <typename T> using Expr = ExprPattern<T>;
233
234template <typename OpType, typename... Ops>
235using Op = OperationPattern<OpType, Ops...>;
236
237template <common::LogicalOperator Operator, typename ValType, typename... Ops>
238using LogicalOp = LogicalOperationPattern<Operator, ValType, Ops...>;
239
240template <common::LogicalOperator Operator, typename Type, typename Op0,
241 typename Op1>
242LogicalOp<Operator, Type, Op0, Op1> logical(const Op0 &op0, const Op1 &op1) {
243 return LogicalOp<Operator, Type, Op0, Op1>(op0, op1);
244}
245
246template <typename Pattern, typename Input>
247bool match(const Pattern &pattern, const Input &input) {
248 return pattern.match(input);
249}
250
251// Specific operation patterns
252
253// -- Add
254template <typename Type, typename Op0, typename Op1>
255struct Add : public Op<evaluate::Add<Type>, Op0, Op1> {
256 using Base = Op<evaluate::Add<Type>, Op0, Op1>;
257
258 Add(const Op0 &op0, const Op1 &op1) : Base(op0, op1) {}
259};
260
261template <typename Type, typename Op0, typename Op1>
262Add<Type, Op0, Op1> add(const Op0 &op0, const Op1 &op1) {
263 return Add<Type, Op0, Op1>(op0, op1);
264}
265
266// -- Mul
267template <typename Type, typename Op0, typename Op1>
268struct Mul : public Op<evaluate::Multiply<Type>, Op0, Op1> {
269 using Base = Op<evaluate::Multiply<Type>, Op0, Op1>;
270
271 Mul(const Op0 &op0, const Op1 &op1) : Base(op0, op1) {}
272};
273
274template <typename Type, typename Op0, typename Op1>
275Mul<Type, Op0, Op1> mul(const Op0 &op0, const Op1 &op1) {
276 return Mul<Type, Op0, Op1>(op0, op1);
277}
278} // namespace match
279} // namespace Fortran::evaluate
280
281#endif // FORTRAN_EVALUATE_MATCH_H_
Definition common.h:214
Definition type.h:57
Definition call.h:34
Definition expression.h:379
Definition match.h:255
Definition match.h:268
Matches evaluate::Expr<T> that contains evaluate::Opreration<OpType>.
Definition match.h:149