forked from OSchip/llvm-project
[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:
parent
fa2762a251
commit
e6873bfbcd
|
@ -68,6 +68,7 @@ enum Iostat {
|
||||||
IostatBadUnformattedRecord,
|
IostatBadUnformattedRecord,
|
||||||
IostatUTF8Decoding,
|
IostatUTF8Decoding,
|
||||||
IostatUnitOverflow,
|
IostatUnitOverflow,
|
||||||
|
IostatBadRealInput,
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *IostatErrorString(int);
|
const char *IostatErrorString(int);
|
||||||
|
|
|
@ -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 == '+') {
|
||||||
io.SkipSpaces(remaining);
|
if (!bzMode) {
|
||||||
|
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) {
|
||||||
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 &&
|
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};
|
||||||
|
|
|
@ -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,7 +142,9 @@ public:
|
||||||
if (edit.width.value_or(0) > 0) {
|
if (edit.width.value_or(0) > 0) {
|
||||||
remaining = *edit.width;
|
remaining = *edit.width;
|
||||||
}
|
}
|
||||||
SkipSpaces(remaining);
|
if (skipSpaces) {
|
||||||
|
SkipSpaces(remaining);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return NextInField(remaining, edit);
|
return NextInField(remaining, edit);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
Loading…
Reference in New Issue