[flang][runtime] Don't skip input spaces when they are significant

When formatted input (not list-directed or NAMELIST) is in "BZ" mode,
either because a BZ control edit descriptor appeared in a FORMAT or
BLANK="ZERO" appeared in OPEN or READ, input editing must not skip
over blanks before or within the input field.

Differential Revision: https://reviews.llvm.org/D123725
This commit is contained in:
Peter Klausler 2022-04-06 14:42:29 -07:00
parent fa2762a251
commit e6873bfbcd
5 changed files with 28 additions and 10 deletions

View File

@ -68,6 +68,7 @@ enum Iostat {
IostatBadUnformattedRecord,
IostatUTF8Decoding,
IostatUnitOverflow,
IostatBadRealInput,
};
const char *IostatErrorString(int);

View File

@ -58,12 +58,15 @@ static inline char32_t GetDecimalPoint(const DataEdit &edit) {
// Returns true if there's a '-' sign.
static bool ScanNumericPrefix(IoStatementState &io, const DataEdit &edit,
std::optional<char32_t> &next, std::optional<int> &remaining) {
next = io.PrepareInput(edit, remaining);
bool bzMode{(edit.modes.editingFlags & blankZero) != 0};
next = io.PrepareInput(edit, remaining, !bzMode);
bool negative{false};
if (next) {
negative = *next == '-';
if (negative || *next == '+') {
io.SkipSpaces(remaining);
if (!bzMode) {
io.SkipSpaces(remaining);
}
next = io.NextInField(remaining, edit);
}
}
@ -153,7 +156,8 @@ static int ScanRealInput(char *buffer, int bufferSize, IoStatementState &io,
if (ScanNumericPrefix(io, edit, next, remaining)) {
Put('-');
}
if (next.value_or(' ') == ' ') { // empty/blank field means zero
bool bzMode{(edit.modes.editingFlags & blankZero) != 0};
if (!next || (!bzMode && *next == ' ')) { // empty/blank field means zero
remaining.reset();
if (!io.GetConnectionState().IsAtEOF()) {
Put('0');
@ -181,10 +185,11 @@ static int ScanRealInput(char *buffer, int bufferSize, IoStatementState &io,
}
exponent = 0;
} else if (first == decimal || (first >= '0' && first <= '9') ||
first == 'E' || first == 'D' || first == 'Q') {
(bzMode && (first == ' ' || first == '\t')) || first == 'E' ||
first == 'D' || first == 'Q') {
Put('.'); // input field is normalized to a fraction
auto start{got};
bool bzMode{(edit.modes.editingFlags & blankZero) != 0};
bool anyDigit{false};
for (; next; next = io.NextInField(remaining, edit)) {
char32_t ch{*next};
if (ch == ' ' || ch == '\t') {
@ -195,8 +200,10 @@ static int ScanRealInput(char *buffer, int bufferSize, IoStatementState &io,
}
}
if (ch == '0' && got == start && !decimalPoint) {
anyDigit = true;
// omit leading zeroes before the decimal
} else if (ch >= '0' && ch <= '9') {
anyDigit = true;
Put(ch);
} else if (ch == decimal && !decimalPoint) {
// the decimal point is *not* copied to the buffer
@ -206,7 +213,11 @@ static int ScanRealInput(char *buffer, int bufferSize, IoStatementState &io,
}
}
if (got == start) {
Put('0'); // emit at least one digit
if (anyDigit) {
Put('0'); // emit at least one digit
} else {
return 0; // no digits, invalid input
}
}
if (next &&
(*next == 'e' || *next == 'E' || *next == 'd' || *next == 'D' ||
@ -372,7 +383,7 @@ bool EditCommonRealInput(IoStatementState &io, const DataEdit &edit, void *n) {
return false;
}
if (got == 0) {
io.GetIoErrorHandler().SignalError("Bad REAL input value");
io.GetIoErrorHandler().SignalError(IostatBadRealInput);
return false;
}
bool hadExtra{got > maxDigits};

View File

@ -132,8 +132,8 @@ public:
// For fixed-width fields, initialize the number of remaining characters.
// Skip over leading blanks, then return the first non-blank character (if
// any).
std::optional<char32_t> PrepareInput(
const DataEdit &edit, std::optional<int> &remaining) {
std::optional<char32_t> PrepareInput(const DataEdit &edit,
std::optional<int> &remaining, bool skipSpaces = true) {
remaining.reset();
if (edit.descriptor == DataEdit::ListDirected) {
std::size_t byteCount{0};
@ -142,7 +142,9 @@ public:
if (edit.width.value_or(0) > 0) {
remaining = *edit.width;
}
SkipSpaces(remaining);
if (skipSpaces) {
SkipSpaces(remaining);
}
}
return NextInField(remaining, edit);
}

View File

@ -79,6 +79,8 @@ const char *IostatErrorString(int iostat) {
return "UTF-8 decoding error";
case IostatUnitOverflow:
return "UNIT number is out of range";
case IostatBadRealInput:
return "Bad REAL input value";
default:
return nullptr;
}

View File

@ -688,6 +688,8 @@ TEST(IOApiTests, FormatDoubleInputValues) {
{"(1P,F18.0)", " 125", 0x4029000000000000}, // 12.5
{"(BZ,F18.0)", " 125 ", 0x4093880000000000}, // 1250
{"(BZ,F18.0)", " 125 . e +1 ", 0x42a6bcc41e900000}, // 1.25e13
{"(BZ,F18.0)", " . ", 0x0},
{"(BZ,F18.0)", " . e +1 ", 0x0},
{"(DC,F18.0)", " 12,5", 0x4029000000000000},
};
for (auto const &[format, data, want] : testCases) {