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