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