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 default: 87 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 88 "Data edit descriptor '%c' may not be used with an INTEGER data item", 89 edit.descriptor); 90 return false; 91 } 92 std::optional<int> remaining; 93 std::optional<char32_t> next; 94 bool negate{ScanNumericPrefix(io, edit, next, remaining)}; 95 common::UnsignedInt128 value; 96 for (; next; next = io.NextInField(remaining)) { 97 char32_t ch{*next}; 98 if (ch == ' ' || ch == '\t') { 99 if (edit.modes.editingFlags & blankZero) { 100 ch = '0'; // BZ mode - treat blank as if it were zero 101 } else { 102 continue; 103 } 104 } 105 int digit{0}; 106 if (ch >= '0' && ch <= '9') { 107 digit = ch - '0'; 108 } else { 109 io.GetIoErrorHandler().SignalError( 110 "Bad character '%lc' in INTEGER input field", ch); 111 return false; 112 } 113 value *= 10; 114 value += digit; 115 } 116 if (negate) { 117 value = -value; 118 } 119 std::memcpy(n, &value, kind); 120 return true; 121 } 122 123 // Parses a REAL input number from the input source as a normalized 124 // fraction into a supplied buffer -- there's an optional '-', a 125 // decimal point, and at least one digit. The adjusted exponent value 126 // is returned in a reference argument. The returned value is the number 127 // of characters that (should) have been written to the buffer -- this can 128 // be larger than the buffer size and can indicate overflow. Replaces 129 // blanks with zeroes if appropriate. 130 static int ScanRealInput(char *buffer, int bufferSize, IoStatementState &io, 131 const DataEdit &edit, int &exponent) { 132 std::optional<int> remaining; 133 std::optional<char32_t> next; 134 int got{0}; 135 std::optional<int> decimalPoint; 136 auto Put{[&](char ch) -> void { 137 if (got < bufferSize) { 138 buffer[got] = ch; 139 } 140 ++got; 141 }}; 142 if (ScanNumericPrefix(io, edit, next, remaining)) { 143 Put('-'); 144 } 145 if (next.value_or(' ') == ' ') { // empty/blank field means zero 146 remaining.reset(); 147 Put('0'); 148 return got; 149 } 150 char32_t decimal = edit.modes.editingFlags & decimalComma ? ',' : '.'; 151 char32_t first{*next >= 'a' && *next <= 'z' ? *next + 'A' - 'a' : *next}; 152 if (first == 'N' || first == 'I') { 153 // NaN or infinity - convert to upper case 154 // Subtle: a blank field of digits could be followed by 'E' or 'D', 155 for (; next && 156 ((*next >= 'a' && *next <= 'z') || (*next >= 'A' && *next <= 'Z')); 157 next = io.NextInField(remaining)) { 158 if (*next >= 'a' && *next <= 'z') { 159 Put(*next - 'a' + 'A'); 160 } else { 161 Put(*next); 162 } 163 } 164 if (next && *next == '(') { // NaN(...) 165 while (next && *next != ')') { 166 next = io.NextInField(remaining); 167 } 168 } 169 exponent = 0; 170 } else if (first == decimal || (first >= '0' && first <= '9') || 171 first == 'E' || first == 'D' || first == 'Q') { 172 Put('.'); // input field is normalized to a fraction 173 auto start{got}; 174 bool bzMode{(edit.modes.editingFlags & blankZero) != 0}; 175 for (; next; next = io.NextInField(remaining)) { 176 char32_t ch{*next}; 177 if (ch == ' ' || ch == '\t') { 178 if (bzMode) { 179 ch = '0'; // BZ mode - treat blank as if it were zero 180 } else { 181 continue; 182 } 183 } 184 if (ch == '0' && got == start && !decimalPoint) { 185 // omit leading zeroes before the decimal 186 } else if (ch >= '0' && ch <= '9') { 187 Put(ch); 188 } else if (ch == decimal && !decimalPoint) { 189 // the decimal point is *not* copied to the buffer 190 decimalPoint = got - start; // # of digits before the decimal point 191 } else { 192 break; 193 } 194 } 195 if (got == start) { 196 Put('0'); // emit at least one digit 197 } 198 if (next && 199 (*next == 'e' || *next == 'E' || *next == 'd' || *next == 'D' || 200 *next == 'q' || *next == 'Q')) { 201 // Optional exponent letter. Blanks are allowed between the 202 // optional exponent letter and the exponent value. 203 io.SkipSpaces(remaining); 204 next = io.NextInField(remaining); 205 } 206 // The default exponent is -kP, but the scale factor doesn't affect 207 // an explicit exponent. 208 exponent = -edit.modes.scale; 209 if (next && 210 (*next == '-' || *next == '+' || (*next >= '0' && *next <= '9') || 211 (bzMode && (*next == ' ' || *next == '\t')))) { 212 bool negExpo{*next == '-'}; 213 if (negExpo || *next == '+') { 214 next = io.NextInField(remaining); 215 } 216 for (exponent = 0; next; next = io.NextInField(remaining)) { 217 if (*next >= '0' && *next <= '9') { 218 exponent = 10 * exponent + *next - '0'; 219 } else if (bzMode && (*next == ' ' || *next == '\t')) { 220 exponent = 10 * exponent; 221 } else { 222 break; 223 } 224 } 225 if (negExpo) { 226 exponent = -exponent; 227 } 228 } 229 if (decimalPoint) { 230 exponent += *decimalPoint; 231 } else { 232 // When no decimal point (or comma) appears in the value, the 'd' 233 // part of the edit descriptor must be interpreted as the number of 234 // digits in the value to be interpreted as being to the *right* of 235 // the assumed decimal point (13.7.2.3.2) 236 exponent += got - start - edit.digits.value_or(0); 237 } 238 } else { 239 // TODO: hex FP input 240 exponent = 0; 241 return 0; 242 } 243 // Consume the trailing ')' of a list-directed or NAMELIST complex 244 // input value. 245 if (edit.descriptor == DataEdit::ListDirectedImaginaryPart) { 246 if (next && (*next == ' ' || *next == '\t')) { 247 next = io.NextInField(remaining); 248 } 249 if (!next) { // NextInField fails on separators like ')' 250 next = io.GetCurrentChar(); 251 if (next && *next == ')') { 252 io.HandleRelativePosition(1); 253 } 254 } 255 } else if (remaining) { 256 while (next && (*next == ' ' || *next == '\t')) { 257 next = io.NextInField(remaining); 258 } 259 if (next) { 260 return 0; // error: unused nonblank character in fixed-width field 261 } 262 } 263 return got; 264 } 265 266 template <int KIND> 267 bool EditCommonRealInput(IoStatementState &io, const DataEdit &edit, void *n) { 268 constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)}; 269 static constexpr int maxDigits{ 270 common::MaxDecimalConversionDigits(binaryPrecision)}; 271 static constexpr int bufferSize{maxDigits + 18}; 272 char buffer[bufferSize]; 273 int exponent{0}; 274 int got{ScanRealInput(buffer, maxDigits + 2, io, edit, exponent)}; 275 if (got >= maxDigits + 2) { 276 io.GetIoErrorHandler().Crash("EditCommonRealInput: buffer was too small"); 277 return false; 278 } 279 if (got == 0) { 280 io.GetIoErrorHandler().SignalError("Bad REAL input value"); 281 return false; 282 } 283 bool hadExtra{got > maxDigits}; 284 if (exponent != 0) { 285 got += std::snprintf(&buffer[got], bufferSize - got, "e%d", exponent); 286 } 287 buffer[got] = '\0'; 288 const char *p{buffer}; 289 decimal::ConversionToBinaryResult<binaryPrecision> converted{ 290 decimal::ConvertToBinary<binaryPrecision>(p, edit.modes.round)}; 291 if (hadExtra) { 292 converted.flags = static_cast<enum decimal::ConversionResultFlags>( 293 converted.flags | decimal::Inexact); 294 } 295 // TODO: raise converted.flags as exceptions? 296 *reinterpret_cast<decimal::BinaryFloatingPointNumber<binaryPrecision> *>(n) = 297 converted.binary; 298 return true; 299 } 300 301 template <int KIND> 302 bool EditRealInput(IoStatementState &io, const DataEdit &edit, void *n) { 303 constexpr int binaryPrecision{common::PrecisionOfRealKind(KIND)}; 304 switch (edit.descriptor) { 305 case DataEdit::ListDirected: 306 if (IsNamelistName(io)) { 307 return false; 308 } 309 return EditCommonRealInput<KIND>(io, edit, n); 310 case DataEdit::ListDirectedRealPart: 311 case DataEdit::ListDirectedImaginaryPart: 312 case 'F': 313 case 'E': // incl. EN, ES, & EX 314 case 'D': 315 case 'G': 316 return EditCommonRealInput<KIND>(io, edit, n); 317 case 'B': 318 return EditBOZInput( 319 io, edit, n, 2, common::BitsForBinaryPrecision(binaryPrecision)); 320 case 'O': 321 return EditBOZInput( 322 io, edit, n, 8, common::BitsForBinaryPrecision(binaryPrecision)); 323 case 'Z': 324 return EditBOZInput( 325 io, edit, n, 16, common::BitsForBinaryPrecision(binaryPrecision)); 326 default: 327 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 328 "Data edit descriptor '%c' may not be used for REAL input", 329 edit.descriptor); 330 return false; 331 } 332 } 333 334 // 13.7.3 in Fortran 2018 335 bool EditLogicalInput(IoStatementState &io, const DataEdit &edit, bool &x) { 336 switch (edit.descriptor) { 337 case DataEdit::ListDirected: 338 if (IsNamelistName(io)) { 339 return false; 340 } 341 break; 342 case 'L': 343 case 'G': 344 break; 345 default: 346 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 347 "Data edit descriptor '%c' may not be used for LOGICAL input", 348 edit.descriptor); 349 return false; 350 } 351 std::optional<int> remaining; 352 std::optional<char32_t> next{io.PrepareInput(edit, remaining)}; 353 if (next && *next == '.') { // skip optional period 354 next = io.NextInField(remaining); 355 } 356 if (!next) { 357 io.GetIoErrorHandler().SignalError("Empty LOGICAL input field"); 358 return false; 359 } 360 switch (*next) { 361 case 'T': 362 case 't': 363 x = true; 364 break; 365 case 'F': 366 case 'f': 367 x = false; 368 break; 369 default: 370 io.GetIoErrorHandler().SignalError( 371 "Bad character '%lc' in LOGICAL input field", *next); 372 return false; 373 } 374 if (remaining) { // ignore the rest of the field 375 io.HandleRelativePosition(*remaining); 376 } else if (edit.descriptor == DataEdit::ListDirected) { 377 while (io.NextInField(remaining)) { // discard rest of field 378 } 379 } 380 return true; 381 } 382 383 // See 13.10.3.1 paragraphs 7-9 in Fortran 2018 384 static bool EditDelimitedCharacterInput( 385 IoStatementState &io, char *x, std::size_t length, char32_t delimiter) { 386 bool result{true}; 387 while (true) { 388 auto ch{io.GetCurrentChar()}; 389 if (!ch) { 390 if (io.AdvanceRecord()) { 391 continue; 392 } else { 393 result = false; // EOF in character value 394 break; 395 } 396 } 397 io.HandleRelativePosition(1); 398 if (*ch == delimiter) { 399 auto next{io.GetCurrentChar()}; 400 if (next && *next == delimiter) { 401 // Repeated delimiter: use as character value 402 io.HandleRelativePosition(1); 403 } else { 404 break; // closing delimiter 405 } 406 } 407 if (length > 0) { 408 *x++ = *ch; 409 --length; 410 } 411 } 412 std::fill_n(x, length, ' '); 413 return result; 414 } 415 416 static bool EditListDirectedDefaultCharacterInput( 417 IoStatementState &io, char *x, std::size_t length) { 418 auto ch{io.GetCurrentChar()}; 419 if (ch && (*ch == '\'' || *ch == '"')) { 420 io.HandleRelativePosition(1); 421 return EditDelimitedCharacterInput(io, x, length, *ch); 422 } 423 if (IsNamelistName(io)) { 424 return false; 425 } 426 // Undelimited list-directed character input: stop at a value separator 427 // or the end of the current record. 428 std::optional<int> remaining{length}; 429 for (std::optional<char32_t> next{io.NextInField(remaining)}; next; 430 next = io.NextInField(remaining)) { 431 switch (*next) { 432 case ' ': 433 case '\t': 434 case ',': 435 case ';': 436 case '/': 437 remaining = 0; // value separator: stop 438 break; 439 default: 440 *x++ = *next; 441 --length; 442 } 443 } 444 std::fill_n(x, length, ' '); 445 return true; 446 } 447 448 bool EditDefaultCharacterInput( 449 IoStatementState &io, const DataEdit &edit, char *x, std::size_t length) { 450 switch (edit.descriptor) { 451 case DataEdit::ListDirected: 452 return EditListDirectedDefaultCharacterInput(io, x, length); 453 case 'A': 454 case 'G': 455 break; 456 default: 457 io.GetIoErrorHandler().SignalError(IostatErrorInFormat, 458 "Data edit descriptor '%c' may not be used with a CHARACTER data item", 459 edit.descriptor); 460 return false; 461 } 462 std::optional<int> remaining{length}; 463 if (edit.width && *edit.width > 0) { 464 remaining = *edit.width; 465 } 466 // When the field is wider than the variable, we drop the leading 467 // characters. When the variable is wider than the field, there's 468 // trailing padding. 469 std::int64_t skip{*remaining - static_cast<std::int64_t>(length)}; 470 for (std::optional<char32_t> next{io.NextInField(remaining)}; next; 471 next = io.NextInField(remaining)) { 472 if (skip > 0) { 473 --skip; 474 io.GotChar(-1); 475 } else { 476 *x++ = *next; 477 --length; 478 } 479 } 480 std::fill_n(x, length, ' '); 481 return true; 482 } 483 484 template bool EditRealInput<2>(IoStatementState &, const DataEdit &, void *); 485 template bool EditRealInput<3>(IoStatementState &, const DataEdit &, void *); 486 template bool EditRealInput<4>(IoStatementState &, const DataEdit &, void *); 487 template bool EditRealInput<8>(IoStatementState &, const DataEdit &, void *); 488 template bool EditRealInput<10>(IoStatementState &, const DataEdit &, void *); 489 // TODO: double/double 490 template bool EditRealInput<16>(IoStatementState &, const DataEdit &, void *); 491 } // namespace Fortran::runtime::io 492