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