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