xref: /llvm-project/flang/runtime/edit-input.cpp (revision da25f968a90ad4560fc920a6d18fc2a0221d2750)
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   case 'A': // legacy extension
87     return EditDefaultCharacterInput(
88         io, edit, reinterpret_cast<char *>(n), kind);
89   default:
90     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
91         "Data edit descriptor '%c' may not be used with an INTEGER data item",
92         edit.descriptor);
93     return false;
94   }
95   std::optional<int> remaining;
96   std::optional<char32_t> next;
97   bool negate{ScanNumericPrefix(io, edit, next, remaining)};
98   common::UnsignedInt128 value;
99   for (; next; next = io.NextInField(remaining)) {
100     char32_t ch{*next};
101     if (ch == ' ' || ch == '\t') {
102       if (edit.modes.editingFlags & blankZero) {
103         ch = '0'; // BZ mode - treat blank as if it were zero
104       } else {
105         continue;
106       }
107     }
108     int digit{0};
109     if (ch >= '0' && ch <= '9') {
110       digit = ch - '0';
111     } else {
112       io.GetIoErrorHandler().SignalError(
113           "Bad character '%lc' in INTEGER input field", ch);
114       return false;
115     }
116     value *= 10;
117     value += digit;
118   }
119   if (negate) {
120     value = -value;
121   }
122   std::memcpy(n, &value, kind);
123   return true;
124 }
125 
126 // Parses a REAL input number from the input source as a normalized
127 // fraction into a supplied buffer -- there's an optional '-', a
128 // decimal point, and at least one digit.  The adjusted exponent value
129 // is returned in a reference argument.  The returned value is the number
130 // of characters that (should) have been written to the buffer -- this can
131 // be larger than the buffer size and can indicate overflow.  Replaces
132 // blanks with zeroes if appropriate.
133 static int ScanRealInput(char *buffer, int bufferSize, IoStatementState &io,
134     const DataEdit &edit, int &exponent) {
135   std::optional<int> remaining;
136   std::optional<char32_t> next;
137   int got{0};
138   std::optional<int> decimalPoint;
139   auto Put{[&](char ch) -> void {
140     if (got < bufferSize) {
141       buffer[got] = ch;
142     }
143     ++got;
144   }};
145   if (ScanNumericPrefix(io, edit, next, remaining)) {
146     Put('-');
147   }
148   if (next.value_or(' ') == ' ') { // empty/blank field means zero
149     remaining.reset();
150     Put('0');
151     return got;
152   }
153   char32_t decimal = edit.modes.editingFlags & decimalComma ? ',' : '.';
154   char32_t first{*next >= 'a' && *next <= 'z' ? *next + 'A' - 'a' : *next};
155   if (first == 'N' || first == 'I') {
156     // NaN or infinity - convert to upper case
157     // Subtle: a blank field of digits could be followed by 'E' or 'D',
158     for (; next &&
159          ((*next >= 'a' && *next <= 'z') || (*next >= 'A' && *next <= 'Z'));
160          next = io.NextInField(remaining)) {
161       if (*next >= 'a' && *next <= 'z') {
162         Put(*next - 'a' + 'A');
163       } else {
164         Put(*next);
165       }
166     }
167     if (next && *next == '(') { // NaN(...)
168       while (next && *next != ')') {
169         next = io.NextInField(remaining);
170       }
171     }
172     exponent = 0;
173   } else if (first == decimal || (first >= '0' && first <= '9') ||
174       first == 'E' || first == 'D' || first == 'Q') {
175     Put('.'); // input field is normalized to a fraction
176     auto start{got};
177     bool bzMode{(edit.modes.editingFlags & blankZero) != 0};
178     for (; next; next = io.NextInField(remaining)) {
179       char32_t ch{*next};
180       if (ch == ' ' || ch == '\t') {
181         if (bzMode) {
182           ch = '0'; // BZ mode - treat blank as if it were zero
183         } else {
184           continue;
185         }
186       }
187       if (ch == '0' && got == start && !decimalPoint) {
188         // omit leading zeroes before the decimal
189       } else if (ch >= '0' && ch <= '9') {
190         Put(ch);
191       } else if (ch == decimal && !decimalPoint) {
192         // the decimal point is *not* copied to the buffer
193         decimalPoint = got - start; // # of digits before the decimal point
194       } else {
195         break;
196       }
197     }
198     if (got == start) {
199       Put('0'); // emit at least one digit
200     }
201     if (next &&
202         (*next == 'e' || *next == 'E' || *next == 'd' || *next == 'D' ||
203             *next == 'q' || *next == 'Q')) {
204       // Optional exponent letter.  Blanks are allowed between the
205       // optional exponent letter and the exponent value.
206       io.SkipSpaces(remaining);
207       next = io.NextInField(remaining);
208     }
209     // The default exponent is -kP, but the scale factor doesn't affect
210     // an explicit exponent.
211     exponent = -edit.modes.scale;
212     if (next &&
213         (*next == '-' || *next == '+' || (*next >= '0' && *next <= '9') ||
214             (bzMode && (*next == ' ' || *next == '\t')))) {
215       bool negExpo{*next == '-'};
216       if (negExpo || *next == '+') {
217         next = io.NextInField(remaining);
218       }
219       for (exponent = 0; next; next = io.NextInField(remaining)) {
220         if (*next >= '0' && *next <= '9') {
221           exponent = 10 * exponent + *next - '0';
222         } else if (bzMode && (*next == ' ' || *next == '\t')) {
223           exponent = 10 * exponent;
224         } else {
225           break;
226         }
227       }
228       if (negExpo) {
229         exponent = -exponent;
230       }
231     }
232     if (decimalPoint) {
233       exponent += *decimalPoint;
234     } else {
235       // When no decimal point (or comma) appears in the value, the 'd'
236       // part of the edit descriptor must be interpreted as the number of
237       // digits in the value to be interpreted as being to the *right* of
238       // the assumed decimal point (13.7.2.3.2)
239       exponent += got - start - edit.digits.value_or(0);
240     }
241   } else {
242     // TODO: hex FP input
243     exponent = 0;
244     return 0;
245   }
246   // Consume the trailing ')' of a list-directed or NAMELIST complex
247   // input value.
248   if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
249     if (next && (*next == ' ' || *next == '\t')) {
250       next = io.NextInField(remaining);
251     }
252     if (!next) { // NextInField fails on separators like ')'
253       next = io.GetCurrentChar();
254       if (next && *next == ')') {
255         io.HandleRelativePosition(1);
256       }
257     }
258   } else if (remaining) {
259     while (next && (*next == ' ' || *next == '\t')) {
260       next = io.NextInField(remaining);
261     }
262     if (next) {
263       return 0; // error: unused nonblank character in fixed-width field
264     }
265   }
266   return got;
267 }
268 
269 // If no special modes are in effect and the form of the input value
270 // that's present in the input stream is acceptable to the decimal->binary
271 // converter without modification, this fast path for real input
272 // saves time by avoiding memory copies and reformatting of the exponent.
273 template <int PRECISION>
274 static bool TryFastPathRealInput(
275     IoStatementState &io, const DataEdit &edit, void *n) {
276   if (edit.modes.editingFlags & (blankZero | decimalComma)) {
277     return false;
278   }
279   if (edit.modes.scale != 0) {
280     return false;
281   }
282   const char *str{nullptr};
283   std::size_t got{io.GetNextInputBytes(str)};
284   if (got == 0 || str == nullptr ||
285       !io.GetConnectionState().recordLength.has_value()) {
286     return false; // could not access reliably-terminated input stream
287   }
288   const char *p{str};
289   std::int64_t maxConsume{
290       std::min<std::int64_t>(got, edit.width.value_or(got))};
291   const char *limit{str + maxConsume};
292   decimal::ConversionToBinaryResult<PRECISION> converted{
293       decimal::ConvertToBinary<PRECISION>(p, edit.modes.round, limit)};
294   if (converted.flags & decimal::Invalid) {
295     return false;
296   }
297   if (edit.digits.value_or(0) != 0 &&
298       std::memchr(str, '.', p - str) == nullptr) {
299     // No explicit decimal point, and edit descriptor is Fw.d (or other)
300     // with d != 0, which implies scaling.
301     return false;
302   }
303   for (; p < limit && (*p == ' ' || *p == '\t'); ++p) {
304   }
305   if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) {
306     // Need a trailing ')'
307     if (p >= limit || *p != ')') {
308       return false;
309     }
310     for (++ ++p; p < limit && (*p == ' ' || *p == '\t'); ++p) {
311     }
312   }
313   if (p < limit) {
314     return false; // unconverted characters remain in field
315   }
316   // Success on the fast path!
317   // TODO: raise converted.flags as exceptions?
318   *reinterpret_cast<decimal::BinaryFloatingPointNumber<PRECISION> *>(n) =
319       converted.binary;
320   io.HandleRelativePosition(p - str);
321   return true;
322 }
323 
324 template <int KIND>
325 bool EditCommonRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
326   constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)};
327   if (TryFastPathRealInput<binaryPrecision>(io, edit, n)) {
328     return true;
329   }
330   // Fast path wasn't available or didn't work; go the more general route
331   static constexpr int maxDigits{
332       common::MaxDecimalConversionDigits(binaryPrecision)};
333   static constexpr int bufferSize{maxDigits + 18};
334   char buffer[bufferSize];
335   int exponent{0};
336   int got{ScanRealInput(buffer, maxDigits + 2, io, edit, exponent)};
337   if (got >= maxDigits + 2) {
338     io.GetIoErrorHandler().Crash("EditCommonRealInput: buffer was too small");
339     return false;
340   }
341   if (got == 0) {
342     io.GetIoErrorHandler().SignalError("Bad REAL input value");
343     return false;
344   }
345   bool hadExtra{got > maxDigits};
346   if (exponent != 0) {
347     buffer[got++] = 'e';
348     if (exponent < 0) {
349       buffer[got++] = '-';
350       exponent = -exponent;
351     }
352     if (exponent > 9999) {
353       exponent = 9999; // will convert to +/-Inf
354     }
355     if (exponent > 999) {
356       int dig{exponent / 1000};
357       buffer[got++] = '0' + dig;
358       int rest{exponent - 1000 * dig};
359       dig = rest / 100;
360       buffer[got++] = '0' + dig;
361       rest -= 100 * dig;
362       dig = rest / 10;
363       buffer[got++] = '0' + dig;
364       buffer[got++] = '0' + (rest - 10 * dig);
365     } else if (exponent > 99) {
366       int dig{exponent / 100};
367       buffer[got++] = '0' + dig;
368       int rest{exponent - 100 * dig};
369       dig = rest / 10;
370       buffer[got++] = '0' + dig;
371       buffer[got++] = '0' + (rest - 10 * dig);
372     } else if (exponent > 9) {
373       int dig{exponent / 10};
374       buffer[got++] = '0' + dig;
375       buffer[got++] = '0' + (exponent - 10 * dig);
376     } else {
377       buffer[got++] = '0' + exponent;
378     }
379   }
380   buffer[got] = '\0';
381   const char *p{buffer};
382   decimal::ConversionToBinaryResult<binaryPrecision> converted{
383       decimal::ConvertToBinary<binaryPrecision>(p, edit.modes.round)};
384   if (hadExtra) {
385     converted.flags = static_cast<enum decimal::ConversionResultFlags>(
386         converted.flags | decimal::Inexact);
387   }
388   // TODO: raise converted.flags as exceptions?
389   *reinterpret_cast<decimal::BinaryFloatingPointNumber<binaryPrecision> *>(n) =
390       converted.binary;
391   return true;
392 }
393 
394 template <int KIND>
395 bool EditRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
396   constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)};
397   switch (edit.descriptor) {
398   case DataEdit::ListDirected:
399     if (IsNamelistName(io)) {
400       return false;
401     }
402     return EditCommonRealInput<KIND>(io, edit, n);
403   case DataEdit::ListDirectedRealPart:
404   case DataEdit::ListDirectedImaginaryPart:
405   case 'F':
406   case 'E': // incl. EN, ES, & EX
407   case 'D':
408   case 'G':
409     return EditCommonRealInput<KIND>(io, edit, n);
410   case 'B':
411     return EditBOZInput(
412         io, edit, n, 2, common::BitsForBinaryPrecision(binaryPrecision));
413   case 'O':
414     return EditBOZInput(
415         io, edit, n, 8, common::BitsForBinaryPrecision(binaryPrecision));
416   case 'Z':
417     return EditBOZInput(
418         io, edit, n, 16, common::BitsForBinaryPrecision(binaryPrecision));
419   case 'A': // legacy extension
420     return EditDefaultCharacterInput(
421         io, edit, reinterpret_cast<char *>(n), KIND);
422   default:
423     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
424         "Data edit descriptor '%c' may not be used for REAL input",
425         edit.descriptor);
426     return false;
427   }
428 }
429 
430 // 13.7.3 in Fortran 2018
431 bool EditLogicalInput(IoStatementState &io, const DataEdit &edit, bool &x) {
432   switch (edit.descriptor) {
433   case DataEdit::ListDirected:
434     if (IsNamelistName(io)) {
435       return false;
436     }
437     break;
438   case 'L':
439   case 'G':
440     break;
441   default:
442     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
443         "Data edit descriptor '%c' may not be used for LOGICAL input",
444         edit.descriptor);
445     return false;
446   }
447   std::optional<int> remaining;
448   std::optional<char32_t> next{io.PrepareInput(edit, remaining)};
449   if (next && *next == '.') { // skip optional period
450     next = io.NextInField(remaining);
451   }
452   if (!next) {
453     io.GetIoErrorHandler().SignalError("Empty LOGICAL input field");
454     return false;
455   }
456   switch (*next) {
457   case 'T':
458   case 't':
459     x = true;
460     break;
461   case 'F':
462   case 'f':
463     x = false;
464     break;
465   default:
466     io.GetIoErrorHandler().SignalError(
467         "Bad character '%lc' in LOGICAL input field", *next);
468     return false;
469   }
470   if (remaining) { // ignore the rest of the field
471     io.HandleRelativePosition(*remaining);
472   } else if (edit.descriptor == DataEdit::ListDirected) {
473     while (io.NextInField(remaining)) { // discard rest of field
474     }
475   }
476   return true;
477 }
478 
479 // See 13.10.3.1 paragraphs 7-9 in Fortran 2018
480 static bool EditDelimitedCharacterInput(
481     IoStatementState &io, char *x, std::size_t length, char32_t delimiter) {
482   bool result{true};
483   while (true) {
484     auto ch{io.GetCurrentChar()};
485     if (!ch) {
486       if (io.AdvanceRecord()) {
487         continue;
488       } else {
489         result = false; // EOF in character value
490         break;
491       }
492     }
493     io.HandleRelativePosition(1);
494     if (*ch == delimiter) {
495       auto next{io.GetCurrentChar()};
496       if (next && *next == delimiter) {
497         // Repeated delimiter: use as character value
498         io.HandleRelativePosition(1);
499       } else {
500         break; // closing delimiter
501       }
502     }
503     if (length > 0) {
504       *x++ = *ch;
505       --length;
506     }
507   }
508   std::fill_n(x, length, ' ');
509   return result;
510 }
511 
512 static bool EditListDirectedDefaultCharacterInput(
513     IoStatementState &io, char *x, std::size_t length) {
514   auto ch{io.GetCurrentChar()};
515   if (ch && (*ch == '\'' || *ch == '"')) {
516     io.HandleRelativePosition(1);
517     return EditDelimitedCharacterInput(io, x, length, *ch);
518   }
519   if (IsNamelistName(io)) {
520     return false;
521   }
522   // Undelimited list-directed character input: stop at a value separator
523   // or the end of the current record.
524   std::optional<int> remaining{length};
525   for (std::optional<char32_t> next{io.NextInField(remaining)}; next;
526        next = io.NextInField(remaining)) {
527     switch (*next) {
528     case ' ':
529     case '\t':
530     case ',':
531     case ';':
532     case '/':
533       remaining = 0; // value separator: stop
534       break;
535     default:
536       *x++ = *next;
537       --length;
538     }
539   }
540   std::fill_n(x, length, ' ');
541   return true;
542 }
543 
544 bool EditDefaultCharacterInput(
545     IoStatementState &io, const DataEdit &edit, char *x, std::size_t length) {
546   switch (edit.descriptor) {
547   case DataEdit::ListDirected:
548     return EditListDirectedDefaultCharacterInput(io, x, length);
549   case 'A':
550   case 'G':
551     break;
552   default:
553     io.GetIoErrorHandler().SignalError(IostatErrorInFormat,
554         "Data edit descriptor '%c' may not be used with a CHARACTER data item",
555         edit.descriptor);
556     return false;
557   }
558   std::optional<int> remaining{length};
559   if (edit.width && *edit.width > 0) {
560     remaining = *edit.width;
561   }
562   // When the field is wider than the variable, we drop the leading
563   // characters.  When the variable is wider than the field, there's
564   // trailing padding.
565   std::int64_t skip{*remaining - static_cast<std::int64_t>(length)};
566   for (std::optional<char32_t> next{io.NextInField(remaining)}; next;
567        next = io.NextInField(remaining)) {
568     if (skip > 0) {
569       --skip;
570       io.GotChar(-1);
571     } else {
572       *x++ = *next;
573       --length;
574     }
575   }
576   std::fill_n(x, length, ' ');
577   return true;
578 }
579 
580 template bool EditRealInput<2>(IoStatementState &, const DataEdit &, void *);
581 template bool EditRealInput<3>(IoStatementState &, const DataEdit &, void *);
582 template bool EditRealInput<4>(IoStatementState &, const DataEdit &, void *);
583 template bool EditRealInput<8>(IoStatementState &, const DataEdit &, void *);
584 template bool EditRealInput<10>(IoStatementState &, const DataEdit &, void *);
585 // TODO: double/double
586 template bool EditRealInput<16>(IoStatementState &, const DataEdit &, void *);
587 } // namespace Fortran::runtime::io
588