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