summaryrefslogtreecommitdiff
path: root/flang/runtime/format.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'flang/runtime/format.cpp')
-rw-r--r--flang/runtime/format.cpp390
1 files changed, 41 insertions, 349 deletions
diff --git a/flang/runtime/format.cpp b/flang/runtime/format.cpp
index f31139ebb5ac..91a6b6749514 100644
--- a/flang/runtime/format.cpp
+++ b/flang/runtime/format.cpp
@@ -6,356 +6,48 @@
//
//===----------------------------------------------------------------------===//
-#include "format.h"
-#include "io-stmt.h"
-#include "main.h"
-#include "flang/common/format.h"
-#include "flang/decimal/decimal.h"
-#include <limits>
+#include "format-implementation.h"
namespace Fortran::runtime::io {
-template<typename CHAR>
-FormatControl<CHAR>::FormatControl(Terminator &terminator, const CHAR *format,
- std::size_t formatLength, int maxHeight)
- : maxHeight_{static_cast<std::uint8_t>(maxHeight)}, format_{format},
- formatLength_{static_cast<int>(formatLength)} {
- if (maxHeight != maxHeight_) {
- terminator.Crash("internal Fortran runtime error: maxHeight %d", maxHeight);
- }
- if (formatLength != static_cast<std::size_t>(formatLength_)) {
- terminator.Crash(
- "internal Fortran runtime error: formatLength %zd", formatLength);
- }
- stack_[0].start = offset_;
- stack_[0].remaining = Iteration::unlimited; // 13.4(8)
-}
-
-template<typename CHAR>
-int FormatControl<CHAR>::GetMaxParenthesisNesting(
- Terminator &terminator, const CHAR *format, std::size_t formatLength) {
- using Validator = common::FormatValidator<CHAR>;
- typename Validator::Reporter reporter{
- [&](const common::FormatMessage &message) {
- terminator.Crash(message.text, message.arg);
- return false; // crashes on error above
- }};
- Validator validator{format, formatLength, reporter};
- validator.Check();
- return validator.maxNesting();
-}
-
-template<typename CHAR>
-int FormatControl<CHAR>::GetIntField(Terminator &terminator, CHAR firstCh) {
- CHAR ch{firstCh ? firstCh : PeekNext()};
- if (ch != '-' && ch != '+' && (ch < '0' || ch > '9')) {
- terminator.Crash(
- "Invalid FORMAT: integer expected at '%c'", static_cast<char>(ch));
- }
- int result{0};
- bool negate{ch == '-'};
- if (negate) {
- firstCh = '\0';
- ch = PeekNext();
- }
- while (ch >= '0' && ch <= '9') {
- if (result >
- std::numeric_limits<int>::max() / 10 - (static_cast<int>(ch) - '0')) {
- terminator.Crash("FORMAT integer field out of range");
- }
- result = 10 * result + ch - '0';
- if (firstCh) {
- firstCh = '\0';
- } else {
- ++offset_;
- }
- ch = PeekNext();
- }
- if (negate && (result *= -1) > 0) {
- terminator.Crash("FORMAT integer field out of range");
- }
- return result;
-}
-
-static void HandleControl(FormatContext &context, char ch, char next, int n) {
- MutableModes &modes{context.mutableModes()};
- switch (ch) {
- case 'B':
- if (next == 'Z') {
- modes.editingFlags |= blankZero;
- return;
- }
- if (next == 'N') {
- modes.editingFlags &= ~blankZero;
- return;
- }
- break;
- case 'D':
- if (next == 'C') {
- modes.editingFlags |= decimalComma;
- return;
- }
- if (next == 'P') {
- modes.editingFlags &= ~decimalComma;
- return;
- }
- break;
- case 'P':
- if (!next) {
- modes.scale = n; // kP - decimal scaling by 10**k
- return;
- }
- break;
- case 'R':
- switch (next) {
- case 'N': modes.roundingMode = common::RoundingMode::TiesToEven; return;
- case 'Z': modes.roundingMode = common::RoundingMode::ToZero; return;
- case 'U': modes.roundingMode = common::RoundingMode::Up; return;
- case 'D': modes.roundingMode = common::RoundingMode::Down; return;
- case 'C':
- modes.roundingMode = common::RoundingMode::TiesAwayFromZero;
- return;
- case 'P':
- modes.roundingMode = executionEnvironment.defaultOutputRoundingMode;
- return;
- default: break;
- }
- break;
- case 'X':
- if (!next) {
- context.HandleRelativePosition(n);
- return;
- }
- break;
- case 'S':
- if (next == 'P') {
- modes.editingFlags |= signPlus;
- return;
- }
- if (!next || next == 'S') {
- modes.editingFlags &= ~signPlus;
- return;
- }
- break;
- case 'T': {
- if (!next) { // Tn
- context.HandleAbsolutePosition(n);
- return;
- }
- if (next == 'L' || next == 'R') { // TLn & TRn
- context.HandleRelativePosition(next == 'L' ? -n : n);
- return;
- }
- } break;
- default: break;
- }
- if (next) {
- context.Crash("Unknown '%c%c' edit descriptor in FORMAT", ch, next);
- } else {
- context.Crash("Unknown '%c' edit descriptor in FORMAT", ch);
- }
-}
-
-// Locates the next data edit descriptor in the format.
-// Handles all repetition counts and control edit descriptors.
-// Generally assumes that the format string has survived the common
-// format validator gauntlet.
-template<typename CHAR>
-int FormatControl<CHAR>::CueUpNextDataEdit(FormatContext &context, bool stop) {
- int unlimitedLoopCheck{-1};
- while (true) {
- std::optional<int> repeat;
- bool unlimited{false};
- CHAR ch{Capitalize(GetNextChar(context))};
- while (ch == ',' || ch == ':') {
- // Skip commas, and don't complain if they're missing; the format
- // validator does that.
- if (stop && ch == ':') {
- return 0;
- }
- ch = Capitalize(GetNextChar(context));
- }
- if (ch == '-' || ch == '+' || (ch >= '0' && ch <= '9')) {
- repeat = GetIntField(context, ch);
- ch = GetNextChar(context);
- } else if (ch == '*') {
- unlimited = true;
- ch = GetNextChar(context);
- if (ch != '(') {
- context.Crash("Invalid FORMAT: '*' may appear only before '('");
- }
- }
- if (ch == '(') {
- if (height_ >= maxHeight_) {
- context.Crash("FORMAT stack overflow: too many nested parentheses");
- }
- stack_[height_].start = offset_ - 1; // the '('
- if (unlimited || height_ == 0) {
- stack_[height_].remaining = Iteration::unlimited;
- unlimitedLoopCheck = offset_ - 1;
- } else if (repeat) {
- if (*repeat <= 0) {
- *repeat = 1; // error recovery
- }
- stack_[height_].remaining = *repeat - 1;
- } else {
- stack_[height_].remaining = 0;
- }
- ++height_;
- } else if (height_ == 0) {
- context.Crash("FORMAT lacks initial '('");
- } else if (ch == ')') {
- if (height_ == 1) {
- if (stop) {
- return 0; // end of FORMAT and no data items remain
- }
- context.HandleSlash(); // implied / before rightmost )
- }
- if (stack_[height_ - 1].remaining == Iteration::unlimited) {
- offset_ = stack_[height_ - 1].start + 1;
- if (offset_ == unlimitedLoopCheck) {
- context.Crash(
- "Unlimited repetition in FORMAT lacks data edit descriptors");
- }
- } else if (stack_[height_ - 1].remaining-- > 0) {
- offset_ = stack_[height_ - 1].start + 1;
- } else {
- --height_;
- }
- } else if (ch == '\'' || ch == '"') {
- // Quoted 'character literal'
- CHAR quote{ch};
- auto start{offset_};
- while (offset_ < formatLength_ && format_[offset_] != quote) {
- ++offset_;
- }
- if (offset_ >= formatLength_) {
- context.Crash("FORMAT missing closing quote on character literal");
- }
- ++offset_;
- std::size_t chars{
- static_cast<std::size_t>(&format_[offset_] - &format_[start])};
- if (PeekNext() == quote) {
- // subtle: handle doubled quote character in a literal by including
- // the first in the output, then treating the second as the start
- // of another character literal.
- } else {
- --chars;
- }
- context.Emit(format_ + start, chars);
- } else if (ch == 'H') {
- // 9HHOLLERITH
- if (!repeat || *repeat < 1 || offset_ + *repeat > formatLength_) {
- context.Crash("Invalid width on Hollerith in FORMAT");
- }
- context.Emit(format_ + offset_, static_cast<std::size_t>(*repeat));
- offset_ += *repeat;
- } else if (ch >= 'A' && ch <= 'Z') {
- int start{offset_ - 1};
- CHAR next{Capitalize(PeekNext())};
- if (next >= 'A' && next <= 'Z') {
- ++offset_;
- } else {
- next = '\0';
- }
- if (ch == 'E' ||
- (!next &&
- (ch == 'A' || ch == 'I' || ch == 'B' || ch == 'O' || ch == 'Z' ||
- ch == 'F' || ch == 'D' || ch == 'G' || ch == 'L'))) {
- // Data edit descriptor found
- offset_ = start;
- return repeat && *repeat > 0 ? *repeat : 1;
- } else {
- // Control edit descriptor
- if (ch == 'T') { // Tn, TLn, TRn
- repeat = GetIntField(context);
- }
- HandleControl(context, static_cast<char>(ch), static_cast<char>(next),
- repeat ? *repeat : 1);
- }
- } else if (ch == '/') {
- context.HandleSlash(repeat && *repeat > 0 ? *repeat : 1);
- } else {
- context.Crash("Invalid character '%c' in FORMAT", static_cast<char>(ch));
- }
- }
-}
-
-template<typename CHAR>
-void FormatControl<CHAR>::GetNext(
- FormatContext &context, DataEdit &edit, int maxRepeat) {
-
- // TODO: DT editing
-
- // Return the next data edit descriptor
- int repeat{CueUpNextDataEdit(context)};
- auto start{offset_};
- edit.descriptor = static_cast<char>(Capitalize(GetNextChar(context)));
- if (edit.descriptor == 'E') {
- edit.variation = static_cast<char>(Capitalize(PeekNext()));
- if (edit.variation >= 'A' && edit.variation <= 'Z') {
- ++offset_;
- } else {
- edit.variation = '\0';
- }
- } else {
- edit.variation = '\0';
- }
-
- if (edit.descriptor == 'A') { // width is optional for A[w]
- auto ch{PeekNext()};
- if (ch >= '0' && ch <= '9') {
- edit.width = GetIntField(context);
- } else {
- edit.width.reset();
- }
- } else {
- edit.width = GetIntField(context);
- }
- edit.modes = context.mutableModes();
- if (PeekNext() == '.') {
- ++offset_;
- edit.digits = GetIntField(context);
- CHAR ch{PeekNext()};
- if (ch == 'e' || ch == 'E' || ch == 'd' || ch == 'D') {
- ++offset_;
- edit.expoDigits = GetIntField(context);
- } else {
- edit.expoDigits.reset();
- }
- } else {
- edit.digits.reset();
- edit.expoDigits.reset();
- }
-
- // Handle repeated nonparenthesized edit descriptors
- if (repeat > 1) {
- stack_[height_].start = start; // after repeat count
- stack_[height_].remaining = repeat; // full count
- ++height_;
- }
- edit.repeat = 1;
- if (height_ > 1) {
- int start{stack_[height_ - 1].start};
- if (format_[start] != '(') {
- if (stack_[height_ - 1].remaining > maxRepeat) {
- edit.repeat = maxRepeat;
- stack_[height_ - 1].remaining -= maxRepeat;
- offset_ = start; // repeat same edit descriptor next time
- } else {
- edit.repeat = stack_[height_ - 1].remaining;
- --height_;
- }
- }
- }
-}
-
-template<typename CHAR>
-void FormatControl<CHAR>::FinishOutput(FormatContext &context) {
- CueUpNextDataEdit(context, true /* stop at colon or end of FORMAT */);
-}
-
-template class FormatControl<char>;
-template class FormatControl<char16_t>;
-template class FormatControl<char32_t>;
+DataEdit DefaultFormatControlCallbacks::GetNextDataEdit(int) {
+ Crash("DefaultFormatControlCallbacks::GetNextDataEdit() called for "
+ "non-formatted I/O statement");
+ return {};
+}
+bool DefaultFormatControlCallbacks::Emit(const char *, std::size_t) {
+ Crash("DefaultFormatControlCallbacks::Emit(char) called for non-output I/O "
+ "statement");
+ return {};
+}
+bool DefaultFormatControlCallbacks::Emit(const char16_t *, std::size_t) {
+ Crash("DefaultFormatControlCallbacks::Emit(char16_t) called for non-output "
+ "I/O statement");
+ return {};
+}
+bool DefaultFormatControlCallbacks::Emit(const char32_t *, std::size_t) {
+ Crash("DefaultFormatControlCallbacks::Emit(char32_t) called for non-output "
+ "I/O statement");
+ return {};
+}
+bool DefaultFormatControlCallbacks::AdvanceRecord(int) {
+ Crash("DefaultFormatControlCallbacks::AdvanceRecord() called unexpectedly");
+ return {};
+}
+bool DefaultFormatControlCallbacks::HandleAbsolutePosition(std::int64_t) {
+ Crash("DefaultFormatControlCallbacks::HandleAbsolutePosition() called for "
+ "non-formatted "
+ "I/O statement");
+ return {};
+}
+bool DefaultFormatControlCallbacks::HandleRelativePosition(std::int64_t) {
+ Crash("DefaultFormatControlCallbacks::HandleRelativePosition() called for "
+ "non-formatted "
+ "I/O statement");
+ return {};
+}
+
+template class FormatControl<InternalFormattedIoStatementState<false>>;
+template class FormatControl<InternalFormattedIoStatementState<true>>;
+template class FormatControl<ExternalFormattedIoStatementState<false>>;
}