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