xref: /llvm-project/flang/runtime/edit-input.cpp (revision b8452dba28e7449d0b1e47b5341ddf0bef55be68)
1 //===-- runtime/edit-input.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 "edit-input.h"
10 #include "namelist.h"
11 #include "flang/Common/real.h"
12 #include "flang/Common/uint128.h"
13 #include <algorithm>
14 
15 namespace Fortran::runtime::io {
16 
17 static bool EditBOZInput(IoStatementState &io, const DataEdit &edit, void *n,
18     int base, int totalBitSize) {
19   std::optional<int> remaining;
20   std::optional<char32_t> next{io.PrepareInput(edit, remaining)};
21   common::UnsignedInt128 value{0};
22   for (; next; next = io.NextInField(remaining)) {
23     char32_t ch{*next};
24     if (ch == ' ' || ch == '\t') {
25       continue;
26     }
27     int digit{0};
28     if (ch >= '0' && ch <= '1') {
29       digit = ch - '0';
30     } else if (base >= 8 && ch >= '2' && ch <= '7') {
31       digit = ch - '0';
32     } else if (base >= 10 && ch >= '8' && ch <= '9') {
33       digit = ch - '0';
34     } else if (base == 16 && ch >= 'A' && ch <= 'Z') {
35       digit = ch + 10 - 'A';
36     } else if (base == 16 && ch >= 'a' && ch <= 'z') {
37       digit = ch + 10 - 'a';
38     } else {
39       io.GetIoErrorHandler().SignalError(
40           "Bad character '%lc' in B/O/Z input field", ch);
41       return false;
42     }
43     value *= base;
44     value += digit;
45   }
46   // TODO: check for overflow
47   std::memcpy(n, &value, totalBitSize >> 3);
48   return true;
49 }
50 
51 // Prepares input from a field, and consumes the sign, if any.
52 // Returns true if there's a '-' sign.
53 static bool ScanNumericPrefix(IoStatementState &io, const DataEdit &edit,
54     std::optional<char32_t> &next, std::optional<int> &remaining) {
55   next = io.PrepareInput(edit, remaining);
56   bool negative{false};
57   if (next) {
58     negative = *next == '-';
59     if (negative || *next == '+') {
60       io.GotChar();
61       io.SkipSpaces(remaining);
62       next = io.NextInField(remaining);
63     }
64   }
65   return negative;
66 }
67 
68 bool EditIntegerInput(
69     IoStatementState &io, const DataEdit &edit, void *n, int kind) {
70   RUNTIME_CHECK(io.GetIoErrorHandler(), kind >= 1 && !(kind & (kind - 1)));
71   switch (edit.descriptor) {
72   case DataEdit::ListDirected:
73     if (IsNamelistName(io)) {
74       return false;
75     }
76     break;
77   case 'G':
78   case 'I':
79     break;
80   case 'B':
81     return EditBOZInput(io, edit, n, 2, kind << 3);
82   case 'O':
83     return EditBOZInput(io, edit, n, 8, kind << 3);
84   case 'Z':
85     return EditBOZInput(io, edit, n, 16, kind << 3);
86   default:
87     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
88         "Data edit descriptor '%c' may not be used with an INTEGER data item",
89         edit.descriptor);
90     return false;
91   }
92   std::optional<int> remaining;
93   std::optional<char32_t> next;
94   bool negate{ScanNumericPrefix(io, edit, next, remaining)};
95   common::UnsignedInt128 value;
96   for (; next; next = io.NextInField(remaining)) {
97     char32_t ch{*next};
98     if (ch == ' ' || ch == '\t') {
99       if (edit.modes.editingFlags & blankZero) {
100         ch = '0'; // BZ mode - treat blank as if it were zero
101       } else {
102         continue;
103       }
104     }
105     int digit{0};
106     if (ch >= '0' && ch <= '9') {
107       digit = ch - '0';
108     } else {
109       io.GetIoErrorHandler().SignalError(
110           "Bad character '%lc' in INTEGER input field", ch);
111       return false;
112     }
113     value *= 10;
114     value += digit;
115   }
116   if (negate) {
117     value = -value;
118   }
119   std::memcpy(n, &value, kind);
120   return true;
121 }
122 
123 // Parses a REAL input number from the input source as a normalized
124 // fraction into a supplied buffer -- there's an optional '-', a
125 // decimal point, and at least one digit.  The adjusted exponent value
126 // is returned in a reference argument.  The returned value is the number
127 // of characters that (should) have been written to the buffer -- this can
128 // be larger than the buffer size and can indicate overflow.  Replaces
129 // blanks with zeroes if appropriate.
130 static int ScanRealInput(char *buffer, int bufferSize, IoStatementState &io,
131     const DataEdit &edit, int &exponent) {
132   std::optional<int> remaining;
133   std::optional<char32_t> next;
134   int got{0};
135   std::optional<int> decimalPoint;
136   auto Put{[&](char ch) -> void {
137     if (got < bufferSize) {
138       buffer[got] = ch;
139     }
140     ++got;
141   }};
142   if (ScanNumericPrefix(io, edit, next, remaining)) {
143     Put('-');
144   }
145   if (next.value_or(' ') == ' ') { // empty/blank field means zero
146     remaining.reset();
147     Put('0');
148     return got;
149   }
150   char32_t decimal = edit.modes.editingFlags & decimalComma ? ',' : '.';
151   char32_t first{*next >= 'a' && *next <= 'z' ? *next + 'A' - 'a' : *next};
152   if (first == 'N' || first == 'I') {
153     // NaN or infinity - convert to upper case
154     // Subtle: a blank field of digits could be followed by 'E' or 'D',
155     for (; next &&
156          ((*next >= 'a' && *next <= 'z') || (*next >= 'A' && *next <= 'Z'));
157          next = io.NextInField(remaining)) {
158       if (*next >= 'a' && *next <= 'z') {
159         Put(*next - 'a' + 'A');
160       } else {
161         Put(*next);
162       }
163     }
164     if (next && *next == '(') { // NaN(...)
165       while (next && *next != ')') {
166         next = io.NextInField(remaining);
167       }
168     }
169     exponent = 0;
170   } else if (first == decimal || (first >= '0' && first <= '9') ||
171       first == 'E' || first == 'D' || first == 'Q') {
172     Put('.'); // input field is normalized to a fraction
173     auto start{got};
174     bool bzMode{(edit.modes.editingFlags & blankZero) != 0};
175     for (; next; next = io.NextInField(remaining)) {
176       char32_t ch{*next};
177       if (ch == ' ' || ch == '\t') {
178         if (bzMode) {
179           ch = '0'; // BZ mode - treat blank as if it were zero
180         } else {
181           continue;
182         }
183       }
184       if (ch == '0' && got == start && !decimalPoint) {
185         // omit leading zeroes before the decimal
186       } else if (ch >= '0' && ch <= '9') {
187         Put(ch);
188       } else if (ch == decimal && !decimalPoint) {
189         // the decimal point is *not* copied to the buffer
190         decimalPoint = got - start; // # of digits before the decimal point
191       } else {
192         break;
193       }
194     }
195     if (got == start) {
196       Put('0'); // emit at least one digit
197     }
198     if (next &&
199         (*next == 'e' || *next == 'E' || *next == 'd' || *next == 'D' ||
200             *next == 'q' || *next == 'Q')) {
201       // Optional exponent letter.  Blanks are allowed between the
202       // optional exponent letter and the exponent value.
203       io.SkipSpaces(remaining);
204       next = io.NextInField(remaining);
205     }
206     // The default exponent is -kP, but the scale factor doesn't affect
207     // an explicit exponent.
208     exponent = -edit.modes.scale;
209     if (next &&
210         (*next == '-' || *next == '+' || (*next >= '0' && *next <= '9') ||
211             (bzMode && (*next == ' ' || *next == '\t')))) {
212       bool negExpo{*next == '-'};
213       if (negExpo || *next == '+') {
214         next = io.NextInField(remaining);
215       }
216       for (exponent = 0; next; next = io.NextInField(remaining)) {
217         if (*next >= '0' && *next <= '9') {
218           exponent = 10 * exponent + *next - '0';
219         } else if (bzMode && (*next == ' ' || *next == '\t')) {
220           exponent = 10 * exponent;
221         } else {
222           break;
223         }
224       }
225       if (negExpo) {
226         exponent = -exponent;
227       }
228     }
229     if (decimalPoint) {
230       exponent += *decimalPoint;
231     } else {
232       // When no decimal point (or comma) appears in the value, the 'd'
233       // part of the edit descriptor must be interpreted as the number of
234       // digits in the value to be interpreted as being to the *right* of
235       // the assumed decimal point (13.7.2.3.2)
236       exponent += got - start - edit.digits.value_or(0);
237     }
238   } else {
239     // TODO: hex FP input
240     exponent = 0;
241     return 0;
242   }
243   // Consume the trailing ')' of a list-directed or NAMELIST complex
244   // input value.
245   if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
246     if (next && (*next == ' ' || *next == '\t')) {
247       next = io.NextInField(remaining);
248     }
249     if (!next) { // NextInField fails on separators like ')'
250       next = io.GetCurrentChar();
251       if (next && *next == ')') {
252         io.HandleRelativePosition(1);
253       }
254     }
255   } else if (remaining) {
256     while (next && (*next == ' ' || *next == '\t')) {
257       next = io.NextInField(remaining);
258     }
259     if (next) {
260       return 0; // error: unused nonblank character in fixed-width field
261     }
262   }
263   return got;
264 }
265 
266 template <int KIND>
267 bool EditCommonRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
268   constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)};
269   static constexpr int maxDigits{
270       common::MaxDecimalConversionDigits(binaryPrecision)};
271   static constexpr int bufferSize{maxDigits + 18};
272   char buffer[bufferSize];
273   int exponent{0};
274   int got{ScanRealInput(buffer, maxDigits + 2, io, edit, exponent)};
275   if (got >= maxDigits + 2) {
276     io.GetIoErrorHandler().Crash("EditCommonRealInput: buffer was too small");
277     return false;
278   }
279   if (got == 0) {
280     io.GetIoErrorHandler().SignalError("Bad REAL input value");
281     return false;
282   }
283   bool hadExtra{got > maxDigits};
284   if (exponent != 0) {
285     got += std::snprintf(&buffer[got], bufferSize - got, "e%d", exponent);
286   }
287   buffer[got] = '\0';
288   const char *p{buffer};
289   decimal::ConversionToBinaryResult<binaryPrecision> converted{
290       decimal::ConvertToBinary<binaryPrecision>(p, edit.modes.round)};
291   if (hadExtra) {
292     converted.flags = static_cast<enum decimal::ConversionResultFlags>(
293         converted.flags | decimal::Inexact);
294   }
295   // TODO: raise converted.flags as exceptions?
296   *reinterpret_cast<decimal::BinaryFloatingPointNumber<binaryPrecision> *>(n) =
297       converted.binary;
298   return true;
299 }
300 
301 template <int KIND>
302 bool EditRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
303   constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)};
304   switch (edit.descriptor) {
305   case DataEdit::ListDirected:
306     if (IsNamelistName(io)) {
307       return false;
308     }
309     return EditCommonRealInput<KIND>(io, edit, n);
310   case DataEdit::ListDirectedRealPart:
311   case DataEdit::ListDirectedImaginaryPart:
312   case 'F':
313   case 'E': // incl. EN, ES, & EX
314   case 'D':
315   case 'G':
316     return EditCommonRealInput<KIND>(io, edit, n);
317   case 'B':
318     return EditBOZInput(
319         io, edit, n, 2, common::BitsForBinaryPrecision(binaryPrecision));
320   case 'O':
321     return EditBOZInput(
322         io, edit, n, 8, common::BitsForBinaryPrecision(binaryPrecision));
323   case 'Z':
324     return EditBOZInput(
325         io, edit, n, 16, common::BitsForBinaryPrecision(binaryPrecision));
326   default:
327     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
328         "Data edit descriptor '%c' may not be used for REAL input",
329         edit.descriptor);
330     return false;
331   }
332 }
333 
334 // 13.7.3 in Fortran 2018
335 bool EditLogicalInput(IoStatementState &io, const DataEdit &edit, bool &x) {
336   switch (edit.descriptor) {
337   case DataEdit::ListDirected:
338     if (IsNamelistName(io)) {
339       return false;
340     }
341     break;
342   case 'L':
343   case 'G':
344     break;
345   default:
346     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
347         "Data edit descriptor '%c' may not be used for LOGICAL input",
348         edit.descriptor);
349     return false;
350   }
351   std::optional<int> remaining;
352   std::optional<char32_t> next{io.PrepareInput(edit, remaining)};
353   if (next && *next == '.') { // skip optional period
354     next = io.NextInField(remaining);
355   }
356   if (!next) {
357     io.GetIoErrorHandler().SignalError("Empty LOGICAL input field");
358     return false;
359   }
360   switch (*next) {
361   case 'T':
362   case 't':
363     x = true;
364     break;
365   case 'F':
366   case 'f':
367     x = false;
368     break;
369   default:
370     io.GetIoErrorHandler().SignalError(
371         "Bad character '%lc' in LOGICAL input field", *next);
372     return false;
373   }
374   if (remaining) { // ignore the rest of the field
375     io.HandleRelativePosition(*remaining);
376   } else if (edit.descriptor == DataEdit::ListDirected) {
377     while (io.NextInField(remaining)) { // discard rest of field
378     }
379   }
380   return true;
381 }
382 
383 // See 13.10.3.1 paragraphs 7-9 in Fortran 2018
384 static bool EditDelimitedCharacterInput(
385     IoStatementState &io, char *x, std::size_t length, char32_t delimiter) {
386   bool result{true};
387   while (true) {
388     auto ch{io.GetCurrentChar()};
389     if (!ch) {
390       if (io.AdvanceRecord()) {
391         continue;
392       } else {
393         result = false; // EOF in character value
394         break;
395       }
396     }
397     io.HandleRelativePosition(1);
398     if (*ch == delimiter) {
399       auto next{io.GetCurrentChar()};
400       if (next && *next == delimiter) {
401         // Repeated delimiter: use as character value
402         io.HandleRelativePosition(1);
403       } else {
404         break; // closing delimiter
405       }
406     }
407     if (length > 0) {
408       *x++ = *ch;
409       --length;
410     }
411   }
412   std::fill_n(x, length, ' ');
413   return result;
414 }
415 
416 static bool EditListDirectedDefaultCharacterInput(
417     IoStatementState &io, char *x, std::size_t length) {
418   auto ch{io.GetCurrentChar()};
419   if (ch && (*ch == '\'' || *ch == '"')) {
420     io.HandleRelativePosition(1);
421     return EditDelimitedCharacterInput(io, x, length, *ch);
422   }
423   if (IsNamelistName(io)) {
424     return false;
425   }
426   // Undelimited list-directed character input: stop at a value separator
427   // or the end of the current record.
428   std::optional<int> remaining{length};
429   for (std::optional<char32_t> next{io.NextInField(remaining)}; next;
430        next = io.NextInField(remaining)) {
431     switch (*next) {
432     case ' ':
433     case '\t':
434     case ',':
435     case ';':
436     case '/':
437       remaining = 0; // value separator: stop
438       break;
439     default:
440       *x++ = *next;
441       --length;
442     }
443   }
444   std::fill_n(x, length, ' ');
445   return true;
446 }
447 
448 bool EditDefaultCharacterInput(
449     IoStatementState &io, const DataEdit &edit, char *x, std::size_t length) {
450   switch (edit.descriptor) {
451   case DataEdit::ListDirected:
452     return EditListDirectedDefaultCharacterInput(io, x, length);
453   case 'A':
454   case 'G':
455     break;
456   default:
457     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
458         "Data edit descriptor '%c' may not be used with a CHARACTER data item",
459         edit.descriptor);
460     return false;
461   }
462   std::optional<int> remaining{length};
463   if (edit.width && *edit.width > 0) {
464     remaining = *edit.width;
465   }
466   // When the field is wider than the variable, we drop the leading
467   // characters.  When the variable is wider than the field, there's
468   // trailing padding.
469   std::int64_t skip{*remaining - static_cast<std::int64_t>(length)};
470   for (std::optional<char32_t> next{io.NextInField(remaining)}; next;
471        next = io.NextInField(remaining)) {
472     if (skip > 0) {
473       --skip;
474       io.GotChar(-1);
475     } else {
476       *x++ = *next;
477       --length;
478     }
479   }
480   std::fill_n(x, length, ' ');
481   return true;
482 }
483 
484 template bool EditRealInput<2>(IoStatementState &, const DataEdit &, void *);
485 template bool EditRealInput<3>(IoStatementState &, const DataEdit &, void *);
486 template bool EditRealInput<4>(IoStatementState &, const DataEdit &, void *);
487 template bool EditRealInput<8>(IoStatementState &, const DataEdit &, void *);
488 template bool EditRealInput<10>(IoStatementState &, const DataEdit &, void *);
489 // TODO: double/double
490 template bool EditRealInput<16>(IoStatementState &, const DataEdit &, void *);
491 } // namespace Fortran::runtime::io
492