blob: 079d8aefefad2239bb87c50628d6f393778df992 [file] [log] [blame]
Diana Picus651f58b2021-09-02 08:14:01 +00001//===-- runtime/edit-output.cpp -------------------------------------------===//
peter klausler3b635712020-02-13 14:41:56 -08002//
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 "edit-output.h"
10#include "flang/Common/uint128.h"
Isuru Fernando231fae92020-03-12 12:52:29 -050011#include <algorithm>
peter klausler3b635712020-02-13 14:41:56 -080012
13namespace Fortran::runtime::io {
14
peter klauslerf65f8302021-10-11 15:41:14 -070015template <int KIND>
16bool EditIntegerOutput(IoStatementState &io, const DataEdit &edit,
17 common::HostSignedIntType<8 * KIND> n) {
18 char buffer[130], *end{&buffer[sizeof buffer]}, *p{end};
19 bool isNegative{n < 0};
20 using Unsigned = common::HostUnsignedIntType<8 * KIND>;
21 Unsigned un{static_cast<Unsigned>(n)};
peter klausler3b635712020-02-13 14:41:56 -080022 int signChars{0};
23 switch (edit.descriptor) {
24 case DataEdit::ListDirected:
25 case 'G':
26 case 'I':
peter klauslerf65f8302021-10-11 15:41:14 -070027 if (isNegative) {
peter klauslerc5a55172021-10-12 18:57:58 -070028 un = -un;
peter klauslerf65f8302021-10-11 15:41:14 -070029 }
peter klausler3b635712020-02-13 14:41:56 -080030 if (isNegative || (edit.modes.editingFlags & signPlus)) {
Tim Keith1f879002020-03-28 21:00:16 -070031 signChars = 1; // '-' or '+'
peter klausler3b635712020-02-13 14:41:56 -080032 }
33 while (un > 0) {
peter klauslere99d1842020-09-30 12:26:25 -070034 auto quotient{un / 10u};
peter klauslerc5a55172021-10-12 18:57:58 -070035 *--p = '0' + static_cast<int>(un - Unsigned{10} * quotient);
peter klausler3b635712020-02-13 14:41:56 -080036 un = quotient;
37 }
38 break;
39 case 'B':
40 for (; un > 0; un >>= 1) {
41 *--p = '0' + (static_cast<int>(un) & 1);
42 }
43 break;
44 case 'O':
45 for (; un > 0; un >>= 3) {
46 *--p = '0' + (static_cast<int>(un) & 7);
47 }
48 break;
49 case 'Z':
50 for (; un > 0; un >>= 4) {
51 int digit = static_cast<int>(un) & 0xf;
52 *--p = digit >= 10 ? 'A' + (digit - 10) : '0' + digit;
53 }
54 break;
peter klauslerb83242e2021-10-21 13:33:07 -070055 case 'A': // legacy extension
56 return EditDefaultCharacterOutput(
57 io, edit, reinterpret_cast<char *>(&n), sizeof n);
peter klausler3b635712020-02-13 14:41:56 -080058 default:
59 io.GetIoErrorHandler().Crash(
60 "Data edit descriptor '%c' may not be used with an INTEGER data item",
61 edit.descriptor);
62 return false;
63 }
64
65 int digits = end - p;
66 int leadingZeroes{0};
67 int editWidth{edit.width.value_or(0)};
Tim Keith1f879002020-03-28 21:00:16 -070068 if (edit.digits && digits <= *edit.digits) { // Iw.m
peter klausler3b635712020-02-13 14:41:56 -080069 if (*edit.digits == 0 && n == 0) {
70 // Iw.0 with zero value: output field must be blank. For I0.0
71 // and a zero value, emit one blank character.
Tim Keith1f879002020-03-28 21:00:16 -070072 signChars = 0; // in case of SP
peter klausler3b635712020-02-13 14:41:56 -080073 editWidth = std::max(1, editWidth);
74 } else {
75 leadingZeroes = *edit.digits - digits;
76 }
77 } else if (n == 0) {
78 leadingZeroes = 1;
79 }
peter klausler6a1c3ef2021-05-05 11:37:49 -070080 int subTotal{signChars + leadingZeroes + digits};
81 int leadingSpaces{std::max(0, editWidth - subTotal)};
82 if (editWidth > 0 && leadingSpaces + subTotal > editWidth) {
peter klausler3b635712020-02-13 14:41:56 -080083 return io.EmitRepeated('*', editWidth);
84 }
peter klausler3b635712020-02-13 14:41:56 -080085 if (edit.IsListDirected()) {
peter klausler6a1c3ef2021-05-05 11:37:49 -070086 int total{std::max(leadingSpaces, 1) + subTotal};
87 if (io.GetConnectionState().NeedAdvance(static_cast<std::size_t>(total)) &&
peter klausler3b635712020-02-13 14:41:56 -080088 !io.AdvanceRecord()) {
89 return false;
90 }
91 leadingSpaces = 1;
92 }
93 return io.EmitRepeated(' ', leadingSpaces) &&
94 io.Emit(n < 0 ? "-" : "+", signChars) &&
95 io.EmitRepeated('0', leadingZeroes) && io.Emit(p, digits);
96}
97
98// Formats the exponent (see table 13.1 for all the cases)
99const char *RealOutputEditingBase::FormatExponent(
100 int expo, const DataEdit &edit, int &length) {
101 char *eEnd{&exponent_[sizeof exponent_]};
102 char *exponent{eEnd};
103 for (unsigned e{static_cast<unsigned>(std::abs(expo))}; e > 0;) {
peter klauslere99d1842020-09-30 12:26:25 -0700104 unsigned quotient{e / 10u};
peter klausler3b635712020-02-13 14:41:56 -0800105 *--exponent = '0' + e - 10 * quotient;
106 e = quotient;
107 }
108 if (edit.expoDigits) {
Tim Keith1f879002020-03-28 21:00:16 -0700109 if (int ed{*edit.expoDigits}) { // Ew.dEe with e > 0
peter klausler3b635712020-02-13 14:41:56 -0800110 while (exponent > exponent_ + 2 /*E+*/ && exponent + ed > eEnd) {
111 *--exponent = '0';
112 }
113 } else if (exponent == eEnd) {
Tim Keith1f879002020-03-28 21:00:16 -0700114 *--exponent = '0'; // Ew.dE0 with zero-valued exponent
peter klausler3b635712020-02-13 14:41:56 -0800115 }
Tim Keith1f879002020-03-28 21:00:16 -0700116 } else { // ensure at least two exponent digits
peter klausler3b635712020-02-13 14:41:56 -0800117 while (exponent + 2 > eEnd) {
118 *--exponent = '0';
119 }
120 }
121 *--exponent = expo < 0 ? '-' : '+';
peter klauslerb1afbce2020-12-15 10:49:01 -0800122 if (edit.expoDigits || edit.IsListDirected() || exponent + 3 == eEnd) {
Tim Keith1f879002020-03-28 21:00:16 -0700123 *--exponent = edit.descriptor == 'D' ? 'D' : 'E'; // not 'G'
peter klausler3b635712020-02-13 14:41:56 -0800124 }
125 length = eEnd - exponent;
126 return exponent;
127}
128
129bool RealOutputEditingBase::EmitPrefix(
130 const DataEdit &edit, std::size_t length, std::size_t width) {
131 if (edit.IsListDirected()) {
Tim Keithdd904082020-07-01 16:51:44 -0700132 int prefixLength{edit.descriptor == DataEdit::ListDirectedRealPart ? 2
133 : edit.descriptor == DataEdit::ListDirectedImaginaryPart ? 0
134 : 1};
peter klausler3b635712020-02-13 14:41:56 -0800135 int suffixLength{edit.descriptor == DataEdit::ListDirectedRealPart ||
136 edit.descriptor == DataEdit::ListDirectedImaginaryPart
137 ? 1
138 : 0};
139 length += prefixLength + suffixLength;
140 ConnectionState &connection{io_.GetConnectionState()};
peter klausler6a1c3ef2021-05-05 11:37:49 -0700141 return (!connection.NeedAdvance(length) || io_.AdvanceRecord()) &&
peter klausler3b635712020-02-13 14:41:56 -0800142 io_.Emit(" (", prefixLength);
143 } else if (width > length) {
144 return io_.EmitRepeated(' ', width - length);
145 } else {
146 return true;
147 }
148}
149
150bool RealOutputEditingBase::EmitSuffix(const DataEdit &edit) {
151 if (edit.descriptor == DataEdit::ListDirectedRealPart) {
152 return io_.Emit(edit.modes.editingFlags & decimalComma ? ";" : ",", 1);
153 } else if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
154 return io_.Emit(")", 1);
155 } else {
156 return true;
157 }
158}
159
Tim Keith1f879002020-03-28 21:00:16 -0700160template <int binaryPrecision>
peter klausler3b635712020-02-13 14:41:56 -0800161decimal::ConversionToDecimalResult RealOutputEditing<binaryPrecision>::Convert(
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800162 int significantDigits, enum decimal::FortranRounding rounding, int flags) {
peter klausler3b635712020-02-13 14:41:56 -0800163 auto converted{decimal::ConvertToDecimal<binaryPrecision>(buffer_,
164 sizeof buffer_, static_cast<enum decimal::DecimalConversionFlags>(flags),
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800165 significantDigits, rounding, x_)};
Tim Keith1f879002020-03-28 21:00:16 -0700166 if (!converted.str) { // overflow
peter klausler3b635712020-02-13 14:41:56 -0800167 io_.GetIoErrorHandler().Crash(
168 "RealOutputEditing::Convert : buffer size %zd was insufficient",
169 sizeof buffer_);
170 }
171 return converted;
172}
173
174// 13.7.2.3.3 in F'2018
Tim Keith1f879002020-03-28 21:00:16 -0700175template <int binaryPrecision>
peter klausler3b635712020-02-13 14:41:56 -0800176bool RealOutputEditing<binaryPrecision>::EditEorDOutput(const DataEdit &edit) {
Tim Keith1f879002020-03-28 21:00:16 -0700177 int editDigits{edit.digits.value_or(0)}; // 'd' field
178 int editWidth{edit.width.value_or(0)}; // 'w' field
peter klausler3b635712020-02-13 14:41:56 -0800179 int significantDigits{editDigits};
180 int flags{0};
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800181 if (edit.modes.editingFlags & signPlus) {
182 flags |= decimal::AlwaysSign;
183 }
Tim Keith1f879002020-03-28 21:00:16 -0700184 if (editWidth == 0) { // "the processor selects the field width"
185 if (edit.digits.has_value()) { // E0.d
186 editWidth = editDigits + 6; // -.666E+ee
187 } else { // E0
peter klausler3b635712020-02-13 14:41:56 -0800188 flags |= decimal::Minimize;
189 significantDigits =
Tim Keith1f879002020-03-28 21:00:16 -0700190 sizeof buffer_ - 5; // sign, NUL, + 3 extra for EN scaling
peter klausler3b635712020-02-13 14:41:56 -0800191 }
192 }
193 bool isEN{edit.variation == 'N'};
194 bool isES{edit.variation == 'S'};
Tim Keith1f879002020-03-28 21:00:16 -0700195 int scale{isEN || isES ? 1 : edit.modes.scale}; // 'kP' value
peter klausler3b635712020-02-13 14:41:56 -0800196 int zeroesAfterPoint{0};
197 if (scale < 0) {
198 zeroesAfterPoint = -scale;
199 significantDigits = std::max(0, significantDigits - zeroesAfterPoint);
200 } else if (scale > 0) {
201 ++significantDigits;
202 scale = std::min(scale, significantDigits + 1);
203 }
204 // In EN editing, multiple attempts may be necessary, so it's in a loop.
205 while (true) {
206 decimal::ConversionToDecimalResult converted{
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800207 Convert(significantDigits, edit.modes.round, flags)};
peter klausler0df6f8f2020-06-19 13:32:26 -0700208 if (IsInfOrNaN(converted)) {
peter klausler3b635712020-02-13 14:41:56 -0800209 return EmitPrefix(edit, converted.length, editWidth) &&
210 io_.Emit(converted.str, converted.length) && EmitSuffix(edit);
211 }
212 if (!IsZero()) {
213 converted.decimalExponent -= scale;
214 }
215 if (isEN && scale < 3 && (converted.decimalExponent % 3) != 0) {
216 // EN mode: boost the scale and significant digits, try again; need
217 // an effective exponent field that's a multiple of three.
218 ++scale;
219 ++significantDigits;
220 continue;
221 }
222 // Format the exponent (see table 13.1 for all the cases)
223 int expoLength{0};
224 const char *exponent{
225 FormatExponent(converted.decimalExponent, edit, expoLength)};
226 int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0};
227 int convertedDigits{static_cast<int>(converted.length) - signLength};
228 int zeroesBeforePoint{std::max(0, scale - convertedDigits)};
229 int digitsBeforePoint{std::max(0, scale - zeroesBeforePoint)};
230 int digitsAfterPoint{convertedDigits - digitsBeforePoint};
231 int trailingZeroes{flags & decimal::Minimize
232 ? 0
233 : std::max(0,
234 significantDigits - (convertedDigits + zeroesBeforePoint))};
235 int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint +
236 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingZeroes +
237 expoLength};
238 int width{editWidth > 0 ? editWidth : totalLength};
239 if (totalLength > width) {
240 return io_.EmitRepeated('*', width);
241 }
242 if (totalLength < width && digitsBeforePoint == 0 &&
243 zeroesBeforePoint == 0) {
244 zeroesBeforePoint = 1;
245 ++totalLength;
246 }
247 return EmitPrefix(edit, totalLength, width) &&
248 io_.Emit(converted.str, signLength + digitsBeforePoint) &&
249 io_.EmitRepeated('0', zeroesBeforePoint) &&
250 io_.Emit(edit.modes.editingFlags & decimalComma ? "," : ".", 1) &&
251 io_.EmitRepeated('0', zeroesAfterPoint) &&
252 io_.Emit(
253 converted.str + signLength + digitsBeforePoint, digitsAfterPoint) &&
254 io_.EmitRepeated('0', trailingZeroes) &&
255 io_.Emit(exponent, expoLength) && EmitSuffix(edit);
256 }
257}
258
259// 13.7.2.3.2 in F'2018
Tim Keith1f879002020-03-28 21:00:16 -0700260template <int binaryPrecision>
peter klausler3b635712020-02-13 14:41:56 -0800261bool RealOutputEditing<binaryPrecision>::EditFOutput(const DataEdit &edit) {
Tim Keith1f879002020-03-28 21:00:16 -0700262 int fracDigits{edit.digits.value_or(0)}; // 'd' field
peter klausler0df6f8f2020-06-19 13:32:26 -0700263 const int editWidth{edit.width.value_or(0)}; // 'w' field
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800264 enum decimal::FortranRounding rounding{edit.modes.round};
peter klausler3b635712020-02-13 14:41:56 -0800265 int flags{0};
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800266 if (edit.modes.editingFlags & signPlus) {
267 flags |= decimal::AlwaysSign;
268 }
Tim Keith1f879002020-03-28 21:00:16 -0700269 if (editWidth == 0) { // "the processor selects the field width"
270 if (!edit.digits.has_value()) { // F0
peter klausler3b635712020-02-13 14:41:56 -0800271 flags |= decimal::Minimize;
Tim Keith1f879002020-03-28 21:00:16 -0700272 fracDigits = sizeof buffer_ - 2; // sign & NUL
peter klausler3b635712020-02-13 14:41:56 -0800273 }
274 }
275 // Multiple conversions may be needed to get the right number of
276 // effective rounded fractional digits.
peter klausler0df6f8f2020-06-19 13:32:26 -0700277 int extraDigits{0};
peter klausler48765202021-08-02 14:37:40 -0700278 bool canIncrease{true};
peter klausler3b635712020-02-13 14:41:56 -0800279 while (true) {
280 decimal::ConversionToDecimalResult converted{
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800281 Convert(extraDigits + fracDigits, rounding, flags)};
peter klausler0df6f8f2020-06-19 13:32:26 -0700282 if (IsInfOrNaN(converted)) {
peter klausler3b635712020-02-13 14:41:56 -0800283 return EmitPrefix(edit, converted.length, editWidth) &&
284 io_.Emit(converted.str, converted.length) && EmitSuffix(edit);
285 }
peter klausler567462b2020-09-30 13:04:44 -0700286 int scale{IsZero() ? 1 : edit.modes.scale}; // kP
287 int expo{converted.decimalExponent + scale};
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800288 int signLength{*converted.str == '-' || *converted.str == '+' ? 1 : 0};
289 int convertedDigits{static_cast<int>(converted.length) - signLength};
290 int trailingOnes{0};
peter klausler48765202021-08-02 14:37:40 -0700291 if (expo > extraDigits && extraDigits >= 0 && canIncrease) {
peter klausler3b635712020-02-13 14:41:56 -0800292 extraDigits = expo;
peter klausler0df6f8f2020-06-19 13:32:26 -0700293 if (!edit.digits.has_value()) { // F0
Tim Keith1f879002020-03-28 21:00:16 -0700294 fracDigits = sizeof buffer_ - extraDigits - 2; // sign & NUL
peter klausler3b635712020-02-13 14:41:56 -0800295 }
peter klausler48765202021-08-02 14:37:40 -0700296 canIncrease = false; // only once
peter klausler0df6f8f2020-06-19 13:32:26 -0700297 continue;
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800298 } else if (expo == -fracDigits && convertedDigits > 0) {
299 if (rounding != decimal::FortranRounding::RoundToZero) {
300 // Convert again without rounding so that we can round here
301 rounding = decimal::FortranRounding::RoundToZero;
302 continue;
303 } else if (converted.str[signLength] >= '5') {
304 // Value rounds up to a scaled 1 (e.g., 0.06 for F5.1 -> 0.1)
305 ++expo;
306 convertedDigits = 0;
307 trailingOnes = 1;
308 } else {
309 // Value rounds down to zero
310 expo = 0;
311 convertedDigits = 0;
312 }
peter klausler0df6f8f2020-06-19 13:32:26 -0700313 } else if (expo < extraDigits && extraDigits > -fracDigits) {
314 extraDigits = std::max(expo, -fracDigits);
315 continue;
peter klausler3b635712020-02-13 14:41:56 -0800316 }
peter klausler3b635712020-02-13 14:41:56 -0800317 int digitsBeforePoint{std::max(0, std::min(expo, convertedDigits))};
318 int zeroesBeforePoint{std::max(0, expo - digitsBeforePoint)};
peter klausler0df6f8f2020-06-19 13:32:26 -0700319 int zeroesAfterPoint{std::min(fracDigits, std::max(0, -expo))};
peter klausler3b635712020-02-13 14:41:56 -0800320 int digitsAfterPoint{convertedDigits - digitsBeforePoint};
321 int trailingZeroes{flags & decimal::Minimize
322 ? 0
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800323 : std::max(0,
324 fracDigits -
325 (zeroesAfterPoint + digitsAfterPoint + trailingOnes))};
peter klausler3b635712020-02-13 14:41:56 -0800326 if (digitsBeforePoint + zeroesBeforePoint + zeroesAfterPoint +
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800327 digitsAfterPoint + trailingOnes + trailingZeroes ==
peter klausler3b635712020-02-13 14:41:56 -0800328 0) {
peter klausler0df6f8f2020-06-19 13:32:26 -0700329 zeroesBeforePoint = 1; // "." -> "0."
peter klausler3b635712020-02-13 14:41:56 -0800330 }
331 int totalLength{signLength + digitsBeforePoint + zeroesBeforePoint +
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800332 1 /*'.'*/ + zeroesAfterPoint + digitsAfterPoint + trailingOnes +
333 trailingZeroes};
peter klausler3b635712020-02-13 14:41:56 -0800334 int width{editWidth > 0 ? editWidth : totalLength};
335 if (totalLength > width) {
336 return io_.EmitRepeated('*', width);
337 }
338 if (totalLength < width && digitsBeforePoint + zeroesBeforePoint == 0) {
339 zeroesBeforePoint = 1;
340 ++totalLength;
341 }
342 return EmitPrefix(edit, totalLength, width) &&
343 io_.Emit(converted.str, signLength + digitsBeforePoint) &&
344 io_.EmitRepeated('0', zeroesBeforePoint) &&
345 io_.Emit(edit.modes.editingFlags & decimalComma ? "," : ".", 1) &&
346 io_.EmitRepeated('0', zeroesAfterPoint) &&
347 io_.Emit(
348 converted.str + signLength + digitsBeforePoint, digitsAfterPoint) &&
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800349 io_.EmitRepeated('1', trailingOnes) &&
peter klausler3b635712020-02-13 14:41:56 -0800350 io_.EmitRepeated('0', trailingZeroes) &&
351 io_.EmitRepeated(' ', trailingBlanks_) && EmitSuffix(edit);
352 }
353}
354
355// 13.7.5.2.3 in F'2018
Tim Keith1f879002020-03-28 21:00:16 -0700356template <int binaryPrecision>
peter klausler3b635712020-02-13 14:41:56 -0800357DataEdit RealOutputEditing<binaryPrecision>::EditForGOutput(DataEdit edit) {
358 edit.descriptor = 'E';
peter klausler4fb679d2020-09-30 15:04:43 -0700359 int significantDigits{
360 edit.digits.value_or(BinaryFloatingPoint::decimalPrecision)}; // 'd'
361 if (!edit.width.has_value() || (*edit.width > 0 && significantDigits == 0)) {
Tim Keith1f879002020-03-28 21:00:16 -0700362 return edit; // Gw.0 -> Ew.0 for w > 0
peter klausler3b635712020-02-13 14:41:56 -0800363 }
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800364 int flags{0};
365 if (edit.modes.editingFlags & signPlus) {
366 flags |= decimal::AlwaysSign;
367 }
peter klausler4fb679d2020-09-30 15:04:43 -0700368 decimal::ConversionToDecimalResult converted{
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800369 Convert(significantDigits, edit.modes.round, flags)};
peter klausler0df6f8f2020-06-19 13:32:26 -0700370 if (IsInfOrNaN(converted)) {
peter klausler3b635712020-02-13 14:41:56 -0800371 return edit;
372 }
Tim Keith1f879002020-03-28 21:00:16 -0700373 int expo{IsZero() ? 1 : converted.decimalExponent}; // 's'
peter klausler3b635712020-02-13 14:41:56 -0800374 if (expo < 0 || expo > significantDigits) {
Tim Keith1f879002020-03-28 21:00:16 -0700375 return edit; // Ew.d
peter klausler3b635712020-02-13 14:41:56 -0800376 }
377 edit.descriptor = 'F';
Tim Keith1f879002020-03-28 21:00:16 -0700378 edit.modes.scale = 0; // kP is ignored for G when no exponent field
peter klausler3b635712020-02-13 14:41:56 -0800379 trailingBlanks_ = 0;
380 int editWidth{edit.width.value_or(0)};
381 if (editWidth > 0) {
382 int expoDigits{edit.expoDigits.value_or(0)};
Tim Keith1f879002020-03-28 21:00:16 -0700383 trailingBlanks_ = expoDigits > 0 ? expoDigits + 2 : 4; // 'n'
peter klausler3b635712020-02-13 14:41:56 -0800384 *edit.width = std::max(0, editWidth - trailingBlanks_);
385 }
386 if (edit.digits.has_value()) {
387 *edit.digits = std::max(0, *edit.digits - expo);
388 }
389 return edit;
390}
391
392// 13.10.4 in F'2018
Tim Keith1f879002020-03-28 21:00:16 -0700393template <int binaryPrecision>
peter klausler3b635712020-02-13 14:41:56 -0800394bool RealOutputEditing<binaryPrecision>::EditListDirectedOutput(
395 const DataEdit &edit) {
Peter Klauslerd1b09ad2021-11-10 14:02:30 -0800396 decimal::ConversionToDecimalResult converted{Convert(1, edit.modes.round)};
peter klausler0df6f8f2020-06-19 13:32:26 -0700397 if (IsInfOrNaN(converted)) {
peter klausler3b635712020-02-13 14:41:56 -0800398 return EditEorDOutput(edit);
399 }
400 int expo{converted.decimalExponent};
401 if (expo < 0 || expo > BinaryFloatingPoint::decimalPrecision) {
402 DataEdit copy{edit};
Tim Keith1f879002020-03-28 21:00:16 -0700403 copy.modes.scale = 1; // 1P
peter klausler3b635712020-02-13 14:41:56 -0800404 return EditEorDOutput(copy);
405 }
406 return EditFOutput(edit);
407}
408
409// 13.7.5.2.6 in F'2018
Tim Keith1f879002020-03-28 21:00:16 -0700410template <int binaryPrecision>
peter klausler3b635712020-02-13 14:41:56 -0800411bool RealOutputEditing<binaryPrecision>::EditEXOutput(const DataEdit &) {
412 io_.GetIoErrorHandler().Crash(
Tim Keith1f879002020-03-28 21:00:16 -0700413 "EX output editing is not yet implemented"); // TODO
peter klausler3b635712020-02-13 14:41:56 -0800414}
415
peter klauslerf65f8302021-10-11 15:41:14 -0700416template <int KIND> bool RealOutputEditing<KIND>::Edit(const DataEdit &edit) {
peter klausler3b635712020-02-13 14:41:56 -0800417 switch (edit.descriptor) {
Tim Keith1f879002020-03-28 21:00:16 -0700418 case 'D':
419 return EditEorDOutput(edit);
peter klausler3b635712020-02-13 14:41:56 -0800420 case 'E':
421 if (edit.variation == 'X') {
422 return EditEXOutput(edit);
423 } else {
424 return EditEorDOutput(edit);
425 }
Tim Keith1f879002020-03-28 21:00:16 -0700426 case 'F':
427 return EditFOutput(edit);
peter klausler3b635712020-02-13 14:41:56 -0800428 case 'B':
429 case 'O':
430 case 'Z':
peter klauslerf65f8302021-10-11 15:41:14 -0700431 return EditIntegerOutput<KIND>(io_, edit,
peter klauslerc5a55172021-10-12 18:57:58 -0700432 static_cast<common::HostSignedIntType<8 * KIND>>(
433 decimal::BinaryFloatingPointNumber<binaryPrecision>{x_}.raw()));
Tim Keith1f879002020-03-28 21:00:16 -0700434 case 'G':
435 return Edit(EditForGOutput(edit));
peter klauslerb83242e2021-10-21 13:33:07 -0700436 case 'A': // legacy extension
437 return EditDefaultCharacterOutput(
438 io_, edit, reinterpret_cast<char *>(&x_), sizeof x_);
peter klausler3b635712020-02-13 14:41:56 -0800439 default:
440 if (edit.IsListDirected()) {
441 return EditListDirectedOutput(edit);
442 }
443 io_.GetIoErrorHandler().SignalError(IostatErrorInFormat,
444 "Data edit descriptor '%c' may not be used with a REAL data item",
445 edit.descriptor);
446 return false;
447 }
448 return false;
449}
450
451bool ListDirectedLogicalOutput(IoStatementState &io,
452 ListDirectedStatementState<Direction::Output> &list, bool truth) {
peter klausler6a1c3ef2021-05-05 11:37:49 -0700453 return list.EmitLeadingSpaceOrAdvance(io) && io.Emit(truth ? "T" : "F", 1);
peter klausler3b635712020-02-13 14:41:56 -0800454}
455
456bool EditLogicalOutput(IoStatementState &io, const DataEdit &edit, bool truth) {
457 switch (edit.descriptor) {
458 case 'L':
Tim Keith1f879002020-03-28 21:00:16 -0700459 case 'G':
peter klauslere79a86e2020-07-17 11:14:28 -0700460 return io.EmitRepeated(' ', std::max(0, edit.width.value_or(1) - 1)) &&
461 io.Emit(truth ? "T" : "F", 1);
peter klausler3b635712020-02-13 14:41:56 -0800462 default:
463 io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
464 "Data edit descriptor '%c' may not be used with a LOGICAL data item",
465 edit.descriptor);
466 return false;
467 }
468}
469
470bool ListDirectedDefaultCharacterOutput(IoStatementState &io,
471 ListDirectedStatementState<Direction::Output> &list, const char *x,
472 std::size_t length) {
peter klausler6a1c3ef2021-05-05 11:37:49 -0700473 bool ok{true};
peter klausler3b635712020-02-13 14:41:56 -0800474 MutableModes &modes{io.mutableModes()};
475 ConnectionState &connection{io.GetConnectionState()};
476 if (modes.delim) {
peter klausler6a1c3ef2021-05-05 11:37:49 -0700477 ok = ok && list.EmitLeadingSpaceOrAdvance(io);
peter klausler3b635712020-02-13 14:41:56 -0800478 // Value is delimited with ' or " marks, and interior
peter klausler4d42e162021-07-22 09:47:37 -0700479 // instances of that character are doubled.
peter klausler6a1c3ef2021-05-05 11:37:49 -0700480 ok = ok && io.Emit(&modes.delim, 1);
peter klausler4d42e162021-07-22 09:47:37 -0700481 auto EmitOne{[&](char ch) {
482 if (connection.NeedAdvance(1)) {
483 ok = ok && io.AdvanceRecord();
484 }
485 ok = ok && io.Emit(&ch, 1);
486 }};
peter klausler3b635712020-02-13 14:41:56 -0800487 for (std::size_t j{0}; j < length; ++j) {
peter klausler4d42e162021-07-22 09:47:37 -0700488 // Doubled delimiters must be put on the same record
489 // in order to be acceptable as list-directed or NAMELIST
490 // input; however, this requirement is not always possible
491 // when the records have a fixed length, as is the case with
492 // internal output. The standard is silent on what should
493 // happen, and no two extant Fortran implementations do
494 // the same thing when tested with this case.
495 // This runtime splits the doubled delimiters across
496 // two records for lack of a better alternative.
peter klausler3b635712020-02-13 14:41:56 -0800497 if (x[j] == modes.delim) {
peter klausler4d42e162021-07-22 09:47:37 -0700498 EmitOne(x[j]);
peter klausler3b635712020-02-13 14:41:56 -0800499 }
peter klausler4d42e162021-07-22 09:47:37 -0700500 EmitOne(x[j]);
peter klausler3b635712020-02-13 14:41:56 -0800501 }
peter klausler4d42e162021-07-22 09:47:37 -0700502 EmitOne(modes.delim);
peter klausler3b635712020-02-13 14:41:56 -0800503 } else {
504 // Undelimited list-directed output
peter klausler6a1c3ef2021-05-05 11:37:49 -0700505 ok = ok &&
506 list.EmitLeadingSpaceOrAdvance(
507 io, length > 0 && !list.lastWasUndelimitedCharacter());
peter klausler3b635712020-02-13 14:41:56 -0800508 std::size_t put{0};
peter klausler6a1c3ef2021-05-05 11:37:49 -0700509 while (ok && put < length) {
peter klausler3b635712020-02-13 14:41:56 -0800510 auto chunk{std::min(length - put, connection.RemainingSpaceInRecord())};
peter klausler6a1c3ef2021-05-05 11:37:49 -0700511 ok = ok && io.Emit(x + put, chunk);
peter klausler3b635712020-02-13 14:41:56 -0800512 put += chunk;
513 if (put < length) {
peter klausler6a1c3ef2021-05-05 11:37:49 -0700514 ok = ok && io.AdvanceRecord() && io.Emit(" ", 1);
peter klausler3b635712020-02-13 14:41:56 -0800515 }
516 }
peter klausler6a1c3ef2021-05-05 11:37:49 -0700517 list.set_lastWasUndelimitedCharacter(true);
peter klausler3b635712020-02-13 14:41:56 -0800518 }
519 return ok;
520}
521
522bool EditDefaultCharacterOutput(IoStatementState &io, const DataEdit &edit,
523 const char *x, std::size_t length) {
524 switch (edit.descriptor) {
525 case 'A':
Tim Keith1f879002020-03-28 21:00:16 -0700526 case 'G':
527 break;
peter klausler3b635712020-02-13 14:41:56 -0800528 default:
529 io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
530 "Data edit descriptor '%c' may not be used with a CHARACTER data item",
531 edit.descriptor);
532 return false;
533 }
534 int len{static_cast<int>(length)};
535 int width{edit.width.value_or(len)};
536 return io.EmitRepeated(' ', std::max(0, width - len)) &&
537 io.Emit(x, std::min(width, len));
538}
539
peter klauslerf65f8302021-10-11 15:41:14 -0700540template bool EditIntegerOutput<1>(
541 IoStatementState &, const DataEdit &, std::int8_t);
542template bool EditIntegerOutput<2>(
543 IoStatementState &, const DataEdit &, std::int16_t);
544template bool EditIntegerOutput<4>(
545 IoStatementState &, const DataEdit &, std::int32_t);
546template bool EditIntegerOutput<8>(
peter klausler3b635712020-02-13 14:41:56 -0800547 IoStatementState &, const DataEdit &, std::int64_t);
peter klauslerf65f8302021-10-11 15:41:14 -0700548template bool EditIntegerOutput<16>(
549 IoStatementState &, const DataEdit &, common::int128_t);
peter klausler3b635712020-02-13 14:41:56 -0800550
peter klauslerd56fdc82020-10-02 12:39:05 -0700551template class RealOutputEditing<2>;
552template class RealOutputEditing<3>;
553template class RealOutputEditing<4>;
peter klausler3b635712020-02-13 14:41:56 -0800554template class RealOutputEditing<8>;
peter klauslerd56fdc82020-10-02 12:39:05 -0700555template class RealOutputEditing<10>;
556// TODO: double/double
557template class RealOutputEditing<16>;
Tim Keith1f879002020-03-28 21:00:16 -0700558} // namespace Fortran::runtime::io