1 | //===-- lib/Evaluate/formatting.cpp ---------------------------------------===// |
---|---|
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 | |
9 | #include "flang/Evaluate/formatting.h" |
10 | #include "flang/Evaluate/call.h" |
11 | #include "flang/Evaluate/constant.h" |
12 | #include "flang/Evaluate/expression.h" |
13 | #include "flang/Evaluate/fold.h" |
14 | #include "flang/Evaluate/tools.h" |
15 | #include "flang/Parser/characters.h" |
16 | #include "flang/Semantics/semantics.h" |
17 | #include "flang/Semantics/symbol.h" |
18 | #include "flang/Support/Fortran.h" |
19 | #include "llvm/Support/raw_ostream.h" |
20 | |
21 | namespace Fortran::evaluate { |
22 | |
23 | // Constant arrays can have non-default lower bounds, but this can't be |
24 | // expressed in Fortran syntax directly, only implied through the use of |
25 | // named constant (PARAMETER) definitions. For debugging, setting this flag |
26 | // enables a non-standard %LBOUND=[...] argument to the RESHAPE intrinsic |
27 | // calls used to dumy constants. It's off by default so that this syntax |
28 | // doesn't show up in module files. |
29 | static const bool printLbounds{false}; |
30 | |
31 | static void ShapeAsFortran(llvm::raw_ostream &o, |
32 | const ConstantSubscripts &shape, const ConstantSubscripts &lbounds, |
33 | bool hasNonDefaultLowerBound) { |
34 | if (GetRank(shape) > 1 || hasNonDefaultLowerBound) { |
35 | o << ",shape="; |
36 | char ch{'['}; |
37 | for (auto dim : shape) { |
38 | o << ch << dim; |
39 | ch = ','; |
40 | } |
41 | o << ']'; |
42 | if (hasNonDefaultLowerBound) { |
43 | o << ",%lbound="; |
44 | ch = '['; |
45 | for (auto lb : lbounds) { |
46 | o << ch << lb; |
47 | ch = ','; |
48 | } |
49 | o << ']'; |
50 | } |
51 | o << ')'; |
52 | } |
53 | } |
54 | |
55 | template <typename RESULT, typename VALUE> |
56 | llvm::raw_ostream &ConstantBase<RESULT, VALUE>::AsFortran( |
57 | llvm::raw_ostream &o) const { |
58 | bool hasNonDefaultLowerBound{printLbounds && HasNonDefaultLowerBound()}; |
59 | if (Rank() > 1 || hasNonDefaultLowerBound) { |
60 | o << "reshape("; |
61 | } |
62 | if (Rank() > 0) { |
63 | o << '[' << GetType().AsFortran() << "::"; |
64 | } |
65 | bool first{true}; |
66 | for (const auto &value : values_) { |
67 | if (first) { |
68 | first = false; |
69 | } else { |
70 | o << ','; |
71 | } |
72 | if constexpr (Result::category == TypeCategory::Integer) { |
73 | o << value.SignedDecimal() << '_' << Result::kind; |
74 | } else if constexpr (Result::category == TypeCategory::Unsigned) { |
75 | o << value.UnsignedDecimal() << "U_"<< Result::kind; |
76 | } else if constexpr (Result::category == TypeCategory::Real || |
77 | Result::category == TypeCategory::Complex) { |
78 | value.AsFortran(o, Result::kind); |
79 | } else if constexpr (Result::category == TypeCategory::Character) { |
80 | o << Result::kind << '_' << parser::QuoteCharacterLiteral(value, true); |
81 | } else if constexpr (Result::category == TypeCategory::Logical) { |
82 | if (!value.IsCanonical()) { |
83 | o << "transfer("<< value.word().ToInt64() << "_8,.false._" |
84 | << Result::kind << ')'; |
85 | } else if (value.IsTrue()) { |
86 | o << ".true."<< '_' << Result::kind; |
87 | } else { |
88 | o << ".false."<< '_' << Result::kind; |
89 | } |
90 | } else { |
91 | StructureConstructor{result_.derivedTypeSpec(), value}.AsFortran(o); |
92 | } |
93 | } |
94 | if (Rank() > 0) { |
95 | o << ']'; |
96 | } |
97 | ShapeAsFortran(o, shape(), lbounds(), hasNonDefaultLowerBound); |
98 | return o; |
99 | } |
100 | |
101 | template <int KIND> |
102 | llvm::raw_ostream &Constant<Type<TypeCategory::Character, KIND>>::AsFortran( |
103 | llvm::raw_ostream &o) const { |
104 | bool hasNonDefaultLowerBound{printLbounds && HasNonDefaultLowerBound()}; |
105 | if (Rank() > 1 || hasNonDefaultLowerBound) { |
106 | o << "reshape("; |
107 | } |
108 | if (Rank() > 0) { |
109 | o << '[' << GetType().AsFortran(std::to_string(length_)) << "::"; |
110 | } |
111 | auto total{static_cast<ConstantSubscript>(size())}; |
112 | for (ConstantSubscript j{0}; j < total; ++j) { |
113 | Scalar<Result> value{values_.substr(j * length_, length_)}; |
114 | if (j > 0) { |
115 | o << ','; |
116 | } |
117 | if (Result::kind != 1) { |
118 | o << Result::kind << '_'; |
119 | } |
120 | o << parser::QuoteCharacterLiteral(value); |
121 | } |
122 | if (Rank() > 0) { |
123 | o << ']'; |
124 | } |
125 | ShapeAsFortran(o, shape(), lbounds(), hasNonDefaultLowerBound); |
126 | return o; |
127 | } |
128 | |
129 | llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const Symbol &symbol, |
130 | std::optional<parser::CharBlock> name = std::nullopt) { |
131 | const auto &renamings{symbol.owner().context().moduleFileOutputRenamings()}; |
132 | if (auto iter{renamings.find(&symbol.GetUltimate())}; |
133 | iter != renamings.end()) { |
134 | return o << iter->second.ToString(); |
135 | } else if (name) { |
136 | return o << name->ToString(); |
137 | } else { |
138 | return o << symbol.name().ToString(); |
139 | } |
140 | } |
141 | |
142 | llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::string &lit) { |
143 | return o << parser::QuoteCharacterLiteral(lit); |
144 | } |
145 | |
146 | llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::u16string &lit) { |
147 | return o << parser::QuoteCharacterLiteral(lit); |
148 | } |
149 | |
150 | llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::u32string &lit) { |
151 | return o << parser::QuoteCharacterLiteral(lit); |
152 | } |
153 | |
154 | template <typename A> |
155 | llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const A &x) { |
156 | return x.AsFortran(o); |
157 | } |
158 | |
159 | template <typename A> |
160 | llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, common::Reference<A> x) { |
161 | return EmitVar(o, *x); |
162 | } |
163 | |
164 | template <typename A> |
165 | llvm::raw_ostream &EmitVar( |
166 | llvm::raw_ostream &o, const A *p, const char *kw = nullptr) { |
167 | if (p) { |
168 | if (kw) { |
169 | o << kw; |
170 | } |
171 | EmitVar(o, *p); |
172 | } |
173 | return o; |
174 | } |
175 | |
176 | template <typename A> |
177 | llvm::raw_ostream &EmitVar( |
178 | llvm::raw_ostream &o, const std::optional<A> &x, const char *kw = nullptr) { |
179 | if (x) { |
180 | if (kw) { |
181 | o << kw; |
182 | } |
183 | EmitVar(o, *x); |
184 | } |
185 | return o; |
186 | } |
187 | |
188 | template <typename A, bool COPY> |
189 | llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, |
190 | const common::Indirection<A, COPY> &p, const char *kw = nullptr) { |
191 | if (kw) { |
192 | o << kw; |
193 | } |
194 | EmitVar(o, p.value()); |
195 | return o; |
196 | } |
197 | |
198 | template <typename A> |
199 | llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::shared_ptr<A> &p) { |
200 | CHECK(p); |
201 | return EmitVar(o, *p); |
202 | } |
203 | |
204 | template <typename... A> |
205 | llvm::raw_ostream &EmitVar(llvm::raw_ostream &o, const std::variant<A...> &u) { |
206 | common::visit([&](const auto &x) { EmitVar(o, x); }, u); |
207 | return o; |
208 | } |
209 | |
210 | llvm::raw_ostream &ActualArgument::AssumedType::AsFortran( |
211 | llvm::raw_ostream &o) const { |
212 | return EmitVar(o, *symbol_); |
213 | } |
214 | |
215 | llvm::raw_ostream &ActualArgument::AsFortran(llvm::raw_ostream &o) const { |
216 | if (keyword_) { |
217 | o << keyword_->ToString() << '='; |
218 | } |
219 | if (isPercentVal()) { |
220 | o << "%VAL("; |
221 | } else if (isPercentRef()) { |
222 | o << "%REF("; |
223 | } |
224 | common::visit( |
225 | common::visitors{ |
226 | [&](const common::CopyableIndirection<Expr<SomeType>> &expr) { |
227 | expr.value().AsFortran(o); |
228 | }, |
229 | [&](const AssumedType &assumedType) { assumedType.AsFortran(o); }, |
230 | [&](const common::Label &label) { o << '*' << label; }, |
231 | }, |
232 | u_); |
233 | if (isPercentVal() || isPercentRef()) { |
234 | o << ')'; |
235 | } |
236 | return o; |
237 | } |
238 | |
239 | llvm::raw_ostream &SpecificIntrinsic::AsFortran(llvm::raw_ostream &o) const { |
240 | return o << name; |
241 | } |
242 | |
243 | llvm::raw_ostream &ProcedureRef::AsFortran(llvm::raw_ostream &o) const { |
244 | for (const auto &arg : arguments_) { |
245 | if (arg && arg->isPassedObject()) { |
246 | arg->AsFortran(o) << '%'; |
247 | break; |
248 | } |
249 | } |
250 | proc_.AsFortran(o); |
251 | if (!chevrons_.empty()) { |
252 | bool first{true}; |
253 | for (const auto &expr : chevrons_) { |
254 | if (first) { |
255 | expr.AsFortran(o << "<<<"); |
256 | first = false; |
257 | } else { |
258 | expr.AsFortran(o << ","); |
259 | } |
260 | } |
261 | o << ">>>"; |
262 | } |
263 | char separator{'('}; |
264 | for (const auto &arg : arguments_) { |
265 | if (arg && !arg->isPassedObject()) { |
266 | arg->AsFortran(o << separator); |
267 | separator = ','; |
268 | } |
269 | } |
270 | if (separator == '(') { |
271 | o << '('; |
272 | } |
273 | return o << ')'; |
274 | } |
275 | |
276 | // Operator precedence formatting; insert parentheses around operands |
277 | // only when necessary. |
278 | |
279 | enum class Precedence { // in increasing order for sane comparisons |
280 | DefinedBinary, |
281 | Or, |
282 | And, |
283 | Equivalence, // .EQV., .NEQV. |
284 | Not, // which binds *less* tightly in Fortran than relations |
285 | Relational, |
286 | Additive, // +, -, and (arbitrarily) // |
287 | Negate, // which binds *less* tightly than *, /, ** |
288 | Multiplicative, // *, / |
289 | Power, // **, which is right-associative unlike the other dyadic operators |
290 | DefinedUnary, |
291 | Top, |
292 | }; |
293 | |
294 | template <typename A> constexpr Precedence ToPrecedence(const A &) { |
295 | return Precedence::Top; |
296 | } |
297 | template <int KIND> |
298 | static Precedence ToPrecedence(const LogicalOperation<KIND> &x) { |
299 | switch (x.logicalOperator) { |
300 | SWITCH_COVERS_ALL_CASES |
301 | case LogicalOperator::And: |
302 | return Precedence::And; |
303 | case LogicalOperator::Or: |
304 | return Precedence::Or; |
305 | case LogicalOperator::Not: |
306 | return Precedence::Not; |
307 | case LogicalOperator::Eqv: |
308 | case LogicalOperator::Neqv: |
309 | return Precedence::Equivalence; |
310 | } |
311 | } |
312 | template <int KIND> constexpr Precedence ToPrecedence(const Not<KIND> &) { |
313 | return Precedence::Not; |
314 | } |
315 | template <typename T> constexpr Precedence ToPrecedence(const Relational<T> &) { |
316 | return Precedence::Relational; |
317 | } |
318 | template <typename T> constexpr Precedence ToPrecedence(const Add<T> &) { |
319 | return Precedence::Additive; |
320 | } |
321 | template <typename T> constexpr Precedence ToPrecedence(const Subtract<T> &) { |
322 | return Precedence::Additive; |
323 | } |
324 | template <int KIND> constexpr Precedence ToPrecedence(const Concat<KIND> &) { |
325 | return Precedence::Additive; |
326 | } |
327 | template <typename T> constexpr Precedence ToPrecedence(const Negate<T> &) { |
328 | return Precedence::Negate; |
329 | } |
330 | template <typename T> constexpr Precedence ToPrecedence(const Multiply<T> &) { |
331 | return Precedence::Multiplicative; |
332 | } |
333 | template <typename T> constexpr Precedence ToPrecedence(const Divide<T> &) { |
334 | return Precedence::Multiplicative; |
335 | } |
336 | template <typename T> constexpr Precedence ToPrecedence(const Power<T> &) { |
337 | return Precedence::Power; |
338 | } |
339 | template <typename T> |
340 | constexpr Precedence ToPrecedence(const RealToIntPower<T> &) { |
341 | return Precedence::Power; |
342 | } |
343 | template <typename T> static Precedence ToPrecedence(const Constant<T> &x) { |
344 | static constexpr TypeCategory cat{T::category}; |
345 | if constexpr (cat == TypeCategory::Integer || cat == TypeCategory::Real) { |
346 | if (auto n{GetScalarConstantValue<T>(x)}) { |
347 | if (n->IsNegative()) { |
348 | return Precedence::Negate; |
349 | } |
350 | } |
351 | } |
352 | return Precedence::Top; |
353 | } |
354 | template <typename T> static Precedence ToPrecedence(const Expr<T> &expr) { |
355 | return common::visit([](const auto &x) { return ToPrecedence(x); }, expr.u); |
356 | } |
357 | |
358 | template <typename T> static bool IsNegatedScalarConstant(const Expr<T> &expr) { |
359 | static constexpr TypeCategory cat{T::category}; |
360 | if constexpr (cat == TypeCategory::Integer || cat == TypeCategory::Real) { |
361 | if (auto n{GetScalarConstantValue<T>(expr)}) { |
362 | return n->IsNegative(); |
363 | } |
364 | } |
365 | return false; |
366 | } |
367 | |
368 | template <TypeCategory CAT> |
369 | static bool IsNegatedScalarConstant(const Expr<SomeKind<CAT>> &expr) { |
370 | return common::visit( |
371 | [](const auto &x) { return IsNegatedScalarConstant(x); }, expr.u); |
372 | } |
373 | |
374 | struct OperatorSpelling { |
375 | const char *prefix{""}, *infix{ ","}, *suffix{ ""}; |
376 | }; |
377 | |
378 | template <typename A> constexpr OperatorSpelling SpellOperator(const A &) { |
379 | return OperatorSpelling{}; |
380 | } |
381 | template <typename A> |
382 | constexpr OperatorSpelling SpellOperator(const Negate<A> &) { |
383 | return OperatorSpelling{.prefix: "-", .infix: "", .suffix: ""}; |
384 | } |
385 | template <typename A> |
386 | constexpr OperatorSpelling SpellOperator(const Parentheses<A> &) { |
387 | return OperatorSpelling{.prefix: "(", .infix: "", .suffix: ")"}; |
388 | } |
389 | template <int KIND> |
390 | static OperatorSpelling SpellOperator(const ComplexComponent<KIND> &x) { |
391 | return {x.isImaginaryPart ? "aimag(": "real(", "", ")"}; |
392 | } |
393 | template <int KIND> |
394 | constexpr OperatorSpelling SpellOperator(const Not<KIND> &) { |
395 | return OperatorSpelling{.prefix: ".NOT.", .infix: "", .suffix: ""}; |
396 | } |
397 | template <int KIND> |
398 | constexpr OperatorSpelling SpellOperator(const SetLength<KIND> &) { |
399 | return OperatorSpelling{.prefix: "%SET_LENGTH(", .infix: ",", .suffix: ")"}; |
400 | } |
401 | template <int KIND> |
402 | constexpr OperatorSpelling SpellOperator(const ComplexConstructor<KIND> &) { |
403 | return OperatorSpelling{.prefix: "(", .infix: ",", .suffix: ")"}; |
404 | } |
405 | template <typename A> constexpr OperatorSpelling SpellOperator(const Add<A> &) { |
406 | return OperatorSpelling{.prefix: "", .infix: "+", .suffix: ""}; |
407 | } |
408 | template <typename A> |
409 | constexpr OperatorSpelling SpellOperator(const Subtract<A> &) { |
410 | return OperatorSpelling{.prefix: "", .infix: "-", .suffix: ""}; |
411 | } |
412 | template <typename A> |
413 | constexpr OperatorSpelling SpellOperator(const Multiply<A> &) { |
414 | return OperatorSpelling{.prefix: "", .infix: "*", .suffix: ""}; |
415 | } |
416 | template <typename A> |
417 | constexpr OperatorSpelling SpellOperator(const Divide<A> &) { |
418 | return OperatorSpelling{.prefix: "", .infix: "/", .suffix: ""}; |
419 | } |
420 | template <typename A> |
421 | constexpr OperatorSpelling SpellOperator(const Power<A> &) { |
422 | return OperatorSpelling{.prefix: "", .infix: "**", .suffix: ""}; |
423 | } |
424 | template <typename A> |
425 | constexpr OperatorSpelling SpellOperator(const RealToIntPower<A> &) { |
426 | return OperatorSpelling{.prefix: "", .infix: "**", .suffix: ""}; |
427 | } |
428 | template <typename A> |
429 | static OperatorSpelling SpellOperator(const Extremum<A> &x) { |
430 | return OperatorSpelling{ |
431 | x.ordering == Ordering::Less ? "min(": "max(", ",", ")"}; |
432 | } |
433 | template <int KIND> |
434 | constexpr OperatorSpelling SpellOperator(const Concat<KIND> &) { |
435 | return OperatorSpelling{.prefix: "", .infix: "//", .suffix: ""}; |
436 | } |
437 | template <int KIND> |
438 | static OperatorSpelling SpellOperator(const LogicalOperation<KIND> &x) { |
439 | return OperatorSpelling{"", AsFortran(x.logicalOperator), ""}; |
440 | } |
441 | template <typename T> |
442 | static OperatorSpelling SpellOperator(const Relational<T> &x) { |
443 | return OperatorSpelling{"", AsFortran(x.opr), ""}; |
444 | } |
445 | |
446 | template <typename D, typename R, typename... O> |
447 | llvm::raw_ostream &Operation<D, R, O...>::AsFortran( |
448 | llvm::raw_ostream &o) const { |
449 | Precedence lhsPrec{ToPrecedence(left())}; |
450 | OperatorSpelling spelling{SpellOperator(derived())}; |
451 | o << spelling.prefix; |
452 | Precedence thisPrec{ToPrecedence(derived())}; |
453 | if constexpr (operands == 1) { |
454 | if (thisPrec != Precedence::Top && lhsPrec < thisPrec) { |
455 | left().AsFortran(o << '(') << ')'; |
456 | } else { |
457 | left().AsFortran(o); |
458 | } |
459 | } else { |
460 | if (thisPrec != Precedence::Top && |
461 | (lhsPrec < thisPrec || |
462 | (lhsPrec == Precedence::Power && thisPrec == Precedence::Power))) { |
463 | left().AsFortran(o << '(') << ')'; |
464 | } else { |
465 | left().AsFortran(o); |
466 | } |
467 | o << spelling.infix; |
468 | Precedence rhsPrec{ToPrecedence(right())}; |
469 | if (thisPrec != Precedence::Top && rhsPrec < thisPrec) { |
470 | right().AsFortran(o << '(') << ')'; |
471 | } else { |
472 | right().AsFortran(o); |
473 | } |
474 | } |
475 | return o << spelling.suffix; |
476 | } |
477 | |
478 | template <typename TO, TypeCategory FROMCAT> |
479 | llvm::raw_ostream &Convert<TO, FROMCAT>::AsFortran(llvm::raw_ostream &o) const { |
480 | static_assert(TO::category == TypeCategory::Integer || |
481 | TO::category == TypeCategory::Real || |
482 | TO::category == TypeCategory::Complex || |
483 | TO::category == TypeCategory::Character || |
484 | TO::category == TypeCategory::Logical || |
485 | TO::category == TypeCategory::Unsigned, |
486 | "Convert<> to bad category!"); |
487 | if constexpr (TO::category == TypeCategory::Character) { |
488 | this->left().AsFortran(o << "achar(iachar(") << ')'; |
489 | } else if constexpr (TO::category == TypeCategory::Integer) { |
490 | this->left().AsFortran(o << "int("); |
491 | } else if constexpr (TO::category == TypeCategory::Real) { |
492 | this->left().AsFortran(o << "real("); |
493 | } else if constexpr (TO::category == TypeCategory::Complex) { |
494 | this->left().AsFortran(o << "cmplx("); |
495 | } else if constexpr (TO::category == TypeCategory::Logical) { |
496 | this->left().AsFortran(o << "logical("); |
497 | } else { |
498 | this->left().AsFortran(o << "uint("); |
499 | } |
500 | return o << ",kind="<< TO::kind << ')'; |
501 | } |
502 | |
503 | llvm::raw_ostream &Relational<SomeType>::AsFortran(llvm::raw_ostream &o) const { |
504 | common::visit([&](const auto &rel) { rel.AsFortran(o); }, u); |
505 | return o; |
506 | } |
507 | |
508 | template <typename T> |
509 | llvm::raw_ostream &EmitArray(llvm::raw_ostream &o, const Expr<T> &expr) { |
510 | return expr.AsFortran(o); |
511 | } |
512 | |
513 | template <typename T> |
514 | llvm::raw_ostream &EmitArray( |
515 | llvm::raw_ostream &, const ArrayConstructorValues<T> &); |
516 | |
517 | template <typename T> |
518 | llvm::raw_ostream &EmitArray(llvm::raw_ostream &o, const ImpliedDo<T> &implDo) { |
519 | o << '('; |
520 | EmitArray(o, implDo.values()); |
521 | o << ',' << ImpliedDoIndex::Result::AsFortran() |
522 | << "::"<< implDo.name().ToString() << '='; |
523 | implDo.lower().AsFortran(o) << ','; |
524 | implDo.upper().AsFortran(o) << ','; |
525 | implDo.stride().AsFortran(o) << ')'; |
526 | return o; |
527 | } |
528 | |
529 | template <typename T> |
530 | llvm::raw_ostream &EmitArray( |
531 | llvm::raw_ostream &o, const ArrayConstructorValues<T> &values) { |
532 | const char *sep{""}; |
533 | for (const auto &value : values) { |
534 | o << sep; |
535 | common::visit([&](const auto &x) { EmitArray(o, x); }, value.u); |
536 | sep = ","; |
537 | } |
538 | return o; |
539 | } |
540 | |
541 | template <typename T> |
542 | llvm::raw_ostream &ArrayConstructor<T>::AsFortran(llvm::raw_ostream &o) const { |
543 | o << '[' << GetType().AsFortran() << "::"; |
544 | EmitArray(o, *this); |
545 | return o << ']'; |
546 | } |
547 | |
548 | template <int KIND> |
549 | llvm::raw_ostream & |
550 | ArrayConstructor<Type<TypeCategory::Character, KIND>>::AsFortran( |
551 | llvm::raw_ostream &o) const { |
552 | o << '['; |
553 | if (const auto *len{LEN()}) { |
554 | o << GetType().AsFortran(len->AsFortran()) << "::"; |
555 | } |
556 | EmitArray(o, *this); |
557 | return o << ']'; |
558 | } |
559 | |
560 | llvm::raw_ostream &ArrayConstructor<SomeDerived>::AsFortran( |
561 | llvm::raw_ostream &o) const { |
562 | o << '[' << GetType().AsFortran() << "::"; |
563 | EmitArray(o, *this); |
564 | return o << ']'; |
565 | } |
566 | |
567 | template <typename RESULT> |
568 | std::string ExpressionBase<RESULT>::AsFortran() const { |
569 | std::string buf; |
570 | llvm::raw_string_ostream ss{buf}; |
571 | AsFortran(ss); |
572 | return buf; |
573 | } |
574 | |
575 | template <typename RESULT> |
576 | llvm::raw_ostream &ExpressionBase<RESULT>::AsFortran( |
577 | llvm::raw_ostream &o) const { |
578 | common::visit(common::visitors{ |
579 | [&](const BOZLiteralConstant &x) { |
580 | o << "z'"<< x.Hexadecimal() << "'"; |
581 | }, |
582 | [&](const NullPointer &) { o << "NULL()"; }, |
583 | [&](const common::CopyableIndirection<Substring> &s) { |
584 | s.value().AsFortran(o); |
585 | }, |
586 | [&](const ImpliedDoIndex &i) { o << i.name.ToString(); }, |
587 | [&](const auto &x) { x.AsFortran(o); }, |
588 | }, |
589 | derived().u); |
590 | return o; |
591 | } |
592 | |
593 | static std::string DerivedTypeSpecAsFortran( |
594 | const semantics::DerivedTypeSpec &spec) { |
595 | std::string buf; |
596 | llvm::raw_string_ostream ss{buf}; |
597 | EmitVar(ss, spec.typeSymbol(), spec.name()); |
598 | char ch{'('}; |
599 | for (const auto &[name, value] : spec.parameters()) { |
600 | ss << ch << name.ToString() << '='; |
601 | ch = ','; |
602 | if (value.isAssumed()) { |
603 | ss << '*'; |
604 | } else if (value.isDeferred()) { |
605 | ss << ':'; |
606 | } else { |
607 | value.GetExplicit()->AsFortran(ss); |
608 | } |
609 | } |
610 | if (ch != '(') { |
611 | ss << ')'; |
612 | } |
613 | return buf; |
614 | } |
615 | |
616 | llvm::raw_ostream &StructureConstructor::AsFortran(llvm::raw_ostream &o) const { |
617 | o << DerivedTypeSpecAsFortran(result_.derivedTypeSpec()); |
618 | if (values_.empty()) { |
619 | o << '('; |
620 | } else { |
621 | char ch{'('}; |
622 | for (const auto &[symbol, value] : values_) { |
623 | value.value().AsFortran(EmitVar(o << ch, *symbol) << '='); |
624 | ch = ','; |
625 | } |
626 | } |
627 | return o << ')'; |
628 | } |
629 | |
630 | std::string DynamicType::AsFortran() const { |
631 | if (derived_) { |
632 | CHECK(category_ == TypeCategory::Derived); |
633 | std::string result{DerivedTypeSpecAsFortran(*derived_)}; |
634 | if (IsPolymorphic()) { |
635 | result = "CLASS("s+ result + ')'; |
636 | } |
637 | return result; |
638 | } else if (charLengthParamValue_ || knownLength()) { |
639 | std::string result{"CHARACTER(KIND="s+ std::to_string(kind_) + ",LEN="}; |
640 | if (knownLength()) { |
641 | result += std::to_string(*knownLength()) + "_8"; |
642 | } else if (charLengthParamValue_->isAssumed()) { |
643 | result += '*'; |
644 | } else if (charLengthParamValue_->isDeferred()) { |
645 | result += ':'; |
646 | } else if (const auto &length{charLengthParamValue_->GetExplicit()}) { |
647 | result += length->AsFortran(); |
648 | } |
649 | return result + ')'; |
650 | } else if (IsAssumedType()) { |
651 | return "TYPE(*)"; |
652 | } else if (IsUnlimitedPolymorphic()) { |
653 | return "CLASS(*)"; |
654 | } else if (IsTypelessIntrinsicArgument()) { |
655 | return "(typeless intrinsic function argument)"; |
656 | } else { |
657 | return parser::ToUpperCaseLetters(EnumToString(category_)) + '(' + |
658 | std::to_string(kind_) + ')'; |
659 | } |
660 | } |
661 | |
662 | std::string DynamicType::AsFortran(std::string &&charLenExpr) const { |
663 | if (!charLenExpr.empty() && category_ == TypeCategory::Character) { |
664 | return "CHARACTER(KIND="+ std::to_string(kind_) + |
665 | ",LEN="+ std::move(charLenExpr) + ')'; |
666 | } else { |
667 | return AsFortran(); |
668 | } |
669 | } |
670 | |
671 | std::string SomeDerived::AsFortran() const { |
672 | if (IsUnlimitedPolymorphic()) { |
673 | return "CLASS(*)"; |
674 | } else { |
675 | return "TYPE("s+ DerivedTypeSpecAsFortran(derivedTypeSpec()) + ')'; |
676 | } |
677 | } |
678 | |
679 | llvm::raw_ostream &BaseObject::AsFortran(llvm::raw_ostream &o) const { |
680 | return EmitVar(o, u); |
681 | } |
682 | |
683 | llvm::raw_ostream &TypeParamInquiry::AsFortran(llvm::raw_ostream &o) const { |
684 | if (base_) { |
685 | base_.value().AsFortran(o) << '%'; |
686 | } |
687 | return EmitVar(o, parameter_); |
688 | } |
689 | |
690 | llvm::raw_ostream &Component::AsFortran(llvm::raw_ostream &o) const { |
691 | base_.value().AsFortran(o); |
692 | return EmitVar(o << '%', symbol_); |
693 | } |
694 | |
695 | llvm::raw_ostream &NamedEntity::AsFortran(llvm::raw_ostream &o) const { |
696 | common::visit(common::visitors{ |
697 | [&](SymbolRef s) { EmitVar(o, s); }, |
698 | [&](const Component &c) { c.AsFortran(o); }, |
699 | }, |
700 | u_); |
701 | return o; |
702 | } |
703 | |
704 | llvm::raw_ostream &Triplet::AsFortran(llvm::raw_ostream &o) const { |
705 | EmitVar(o, lower_) << ':'; |
706 | EmitVar(o, upper_); |
707 | EmitVar(o << ':', stride_.value()); |
708 | return o; |
709 | } |
710 | |
711 | llvm::raw_ostream &Subscript::AsFortran(llvm::raw_ostream &o) const { |
712 | return EmitVar(o, u); |
713 | } |
714 | |
715 | llvm::raw_ostream &ArrayRef::AsFortran(llvm::raw_ostream &o) const { |
716 | base_.AsFortran(o); |
717 | char separator{'('}; |
718 | for (const Subscript &ss : subscript_) { |
719 | ss.AsFortran(o << separator); |
720 | separator = ','; |
721 | } |
722 | return o << ')'; |
723 | } |
724 | |
725 | llvm::raw_ostream &CoarrayRef::AsFortran(llvm::raw_ostream &o) const { |
726 | base().AsFortran(o); |
727 | char separator{'['}; |
728 | for (const auto &css : cosubscript_) { |
729 | EmitVar(o << separator, css); |
730 | separator = ','; |
731 | } |
732 | if (stat_) { |
733 | EmitVar(o << separator, stat_, "STAT="); |
734 | separator = ','; |
735 | } |
736 | if (team_) { |
737 | EmitVar(o << separator, team_, |
738 | std::holds_alternative<Expr<SomeInteger>>(team_->value().u) |
739 | ? "TEAM_NUMBER=" |
740 | : "TEAM="); |
741 | } |
742 | return o << ']'; |
743 | } |
744 | |
745 | llvm::raw_ostream &DataRef::AsFortran(llvm::raw_ostream &o) const { |
746 | return EmitVar(o, u); |
747 | } |
748 | |
749 | llvm::raw_ostream &Substring::AsFortran(llvm::raw_ostream &o) const { |
750 | EmitVar(o, parent_) << '('; |
751 | EmitVar(o, lower_) << ':'; |
752 | return EmitVar(o, upper_) << ')'; |
753 | } |
754 | |
755 | llvm::raw_ostream &ComplexPart::AsFortran(llvm::raw_ostream &o) const { |
756 | return complex_.AsFortran(o) << '%' << EnumToString(part_); |
757 | } |
758 | |
759 | llvm::raw_ostream &ProcedureDesignator::AsFortran(llvm::raw_ostream &o) const { |
760 | return EmitVar(o, u); |
761 | } |
762 | |
763 | template <typename T> |
764 | llvm::raw_ostream &Designator<T>::AsFortran(llvm::raw_ostream &o) const { |
765 | common::visit(common::visitors{ |
766 | [&](SymbolRef symbol) { EmitVar(o, symbol); }, |
767 | [&](const auto &x) { x.AsFortran(o); }, |
768 | }, |
769 | u); |
770 | return o; |
771 | } |
772 | |
773 | llvm::raw_ostream &DescriptorInquiry::AsFortran(llvm::raw_ostream &o) const { |
774 | switch (field_) { |
775 | case Field::LowerBound: |
776 | o << "lbound("; |
777 | break; |
778 | case Field::Extent: |
779 | o << "size("; |
780 | break; |
781 | case Field::Stride: |
782 | o << "%STRIDE("; |
783 | break; |
784 | case Field::Rank: |
785 | o << "int(rank("; |
786 | break; |
787 | case Field::Len: |
788 | o << "int("; |
789 | break; |
790 | } |
791 | base_.AsFortran(o); |
792 | if (field_ == Field::Len) { |
793 | o << "%len"; |
794 | } else if (field_ == Field::Rank) { |
795 | o << ")"; |
796 | } else { |
797 | if (dimension_ >= 0) { |
798 | o << ",dim="<< (dimension_ + 1); |
799 | } |
800 | } |
801 | return o << ",kind="<< DescriptorInquiry::Result::kind << ")"; |
802 | } |
803 | |
804 | llvm::raw_ostream &Assignment::AsFortran(llvm::raw_ostream &o) const { |
805 | common::visit( |
806 | common::visitors{ |
807 | [&](const Assignment::Intrinsic &) { |
808 | rhs.AsFortran(lhs.AsFortran(o) << '='); |
809 | }, |
810 | [&](const ProcedureRef &proc) { proc.AsFortran(o << "CALL "); }, |
811 | [&](const BoundsSpec &bounds) { |
812 | lhs.AsFortran(o); |
813 | if (!bounds.empty()) { |
814 | char sep{'('}; |
815 | for (const auto &bound : bounds) { |
816 | bound.AsFortran(o << sep) << ':'; |
817 | sep = ','; |
818 | } |
819 | o << ')'; |
820 | } |
821 | rhs.AsFortran(o << " => "); |
822 | }, |
823 | [&](const BoundsRemapping &bounds) { |
824 | lhs.AsFortran(o); |
825 | if (!bounds.empty()) { |
826 | char sep{'('}; |
827 | for (const auto &bound : bounds) { |
828 | bound.first.AsFortran(o << sep) << ':'; |
829 | bound.second.AsFortran(o); |
830 | sep = ','; |
831 | } |
832 | o << ')'; |
833 | } |
834 | rhs.AsFortran(o << " => "); |
835 | }, |
836 | }, |
837 | u); |
838 | return o; |
839 | } |
840 | |
841 | #ifdef _MSC_VER // disable bogus warning about missing definitions |
842 | #pragma warning(disable : 4661) |
843 | #endif |
844 | INSTANTIATE_CONSTANT_TEMPLATES |
845 | INSTANTIATE_EXPRESSION_TEMPLATES |
846 | INSTANTIATE_VARIABLE_TEMPLATES |
847 | } // namespace Fortran::evaluate |
848 |
Definitions
- printLbounds
- ShapeAsFortran
- EmitVar
- EmitVar
- EmitVar
- EmitVar
- EmitVar
- EmitVar
- EmitVar
- EmitVar
- EmitVar
- EmitVar
- EmitVar
- Precedence
- ToPrecedence
- ToPrecedence
- ToPrecedence
- ToPrecedence
- ToPrecedence
- ToPrecedence
- ToPrecedence
- ToPrecedence
- ToPrecedence
- ToPrecedence
- ToPrecedence
- ToPrecedence
- ToPrecedence
- ToPrecedence
- IsNegatedScalarConstant
- IsNegatedScalarConstant
- OperatorSpelling
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- SpellOperator
- EmitArray
- EmitArray
- EmitArray
- DerivedTypeSpecAsFortran
Learn to use CMake with our Intro Training
Find out more