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

View File

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

View File

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

View File

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