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