195696d56Speter klausler //===-- runtime/format-implementation.h -------------------------*- C++ -*-===// 295696d56Speter klausler // 395696d56Speter klausler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 495696d56Speter klausler // See https://llvm.org/LICENSE.txt for license information. 595696d56Speter klausler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 695696d56Speter klausler // 795696d56Speter klausler //===----------------------------------------------------------------------===// 895696d56Speter klausler 995696d56Speter klausler // Implements out-of-line member functions of template class FormatControl 1095696d56Speter klausler 1195696d56Speter klausler #ifndef FORTRAN_RUNTIME_FORMAT_IMPLEMENTATION_H_ 1295696d56Speter klausler #define FORTRAN_RUNTIME_FORMAT_IMPLEMENTATION_H_ 1395696d56Speter klausler 14bad52055SPeter Klausler #include "emit-encoded.h" 1595696d56Speter klausler #include "format.h" 1695696d56Speter klausler #include "io-stmt.h" 17cc180f4cSPeter Klausler #include "memory.h" 1864ab3302SCarolineConcatto #include "flang/Common/format.h" 1964ab3302SCarolineConcatto #include "flang/Decimal/decimal.h" 20830c0b90SPeter Klausler #include "flang/Runtime/main.h" 21231fae90SIsuru Fernando #include <algorithm> 22cc180f4cSPeter Klausler #include <cstring> 2395696d56Speter klausler #include <limits> 2495696d56Speter klausler 2595696d56Speter klausler namespace Fortran::runtime::io { 2695696d56Speter klausler 2795696d56Speter klausler template <typename CONTEXT> 288ebf7411SSlava Zakharin RT_API_ATTRS FormatControl<CONTEXT>::FormatControl(const Terminator &terminator, 29cc180f4cSPeter Klausler const CharType *format, std::size_t formatLength, 30cc180f4cSPeter Klausler const Descriptor *formatDescriptor, int maxHeight) 3195696d56Speter klausler : maxHeight_{static_cast<std::uint8_t>(maxHeight)}, format_{format}, 3295696d56Speter klausler formatLength_{static_cast<int>(formatLength)} { 333b635714Speter klausler RUNTIME_CHECK(terminator, maxHeight == maxHeight_); 34cc180f4cSPeter Klausler if (!format && formatDescriptor) { 35cc180f4cSPeter Klausler // The format is a character array passed via a descriptor. 3627d666b9SV Donaldson std::size_t elements{formatDescriptor->Elements()}; 3727d666b9SV Donaldson std::size_t elementBytes{formatDescriptor->ElementBytes()}; 3827d666b9SV Donaldson formatLength = elements * elementBytes / sizeof(CharType); 39cc180f4cSPeter Klausler formatLength_ = static_cast<int>(formatLength); 40cc180f4cSPeter Klausler if (formatDescriptor->IsContiguous()) { 41cc180f4cSPeter Klausler // Treat the contiguous array as a single character value. 4227d666b9SV Donaldson format_ = const_cast<const CharType *>( 43cc180f4cSPeter Klausler reinterpret_cast<CharType *>(formatDescriptor->raw().base_addr)); 44cc180f4cSPeter Klausler } else { 45cc180f4cSPeter Klausler // Concatenate its elements into a temporary array. 46cc180f4cSPeter Klausler char *p{reinterpret_cast<char *>( 47cc180f4cSPeter Klausler AllocateMemoryOrCrash(terminator, formatLength * sizeof(CharType)))}; 4827d666b9SV Donaldson format_ = p; 49cc180f4cSPeter Klausler SubscriptValue at[maxRank]; 50cc180f4cSPeter Klausler formatDescriptor->GetLowerBounds(at); 5127d666b9SV Donaldson for (std::size_t j{0}; j < elements; ++j) { 52cc180f4cSPeter Klausler std::memcpy(p, formatDescriptor->Element<char>(at), elementBytes); 53cc180f4cSPeter Klausler p += elementBytes; 54cc180f4cSPeter Klausler formatDescriptor->IncrementSubscripts(at); 55cc180f4cSPeter Klausler } 56cc180f4cSPeter Klausler freeFormat_ = true; 57cc180f4cSPeter Klausler } 58cc180f4cSPeter Klausler } 593b635714Speter klausler RUNTIME_CHECK( 603b635714Speter klausler terminator, formatLength == static_cast<std::size_t>(formatLength_)); 6195696d56Speter klausler stack_[0].start = offset_; 6295696d56Speter klausler stack_[0].remaining = Iteration::unlimited; // 13.4(8) 6395696d56Speter klausler } 6495696d56Speter klausler 6595696d56Speter klausler template <typename CONTEXT> 668ebf7411SSlava Zakharin RT_API_ATTRS int FormatControl<CONTEXT>::GetIntField( 6779f6b812SPeter Klausler IoErrorHandler &handler, CharType firstCh, bool *hadError) { 6895696d56Speter klausler CharType ch{firstCh ? firstCh : PeekNext()}; 6995696d56Speter klausler bool negate{ch == '-'}; 704fb679d3Speter klausler if (negate || ch == '+') { 7104eb93b1SPeter Klausler if (firstCh) { 7295696d56Speter klausler firstCh = '\0'; 7304eb93b1SPeter Klausler } else { 7404eb93b1SPeter Klausler ++offset_; 7504eb93b1SPeter Klausler } 7695696d56Speter klausler ch = PeekNext(); 7795696d56Speter klausler } 78fc71a49eSPeter Klausler if (ch < '0' || ch > '9') { 79fc71a49eSPeter Klausler handler.SignalError(IostatErrorInFormat, 80fc71a49eSPeter Klausler "Invalid FORMAT: integer expected at '%c'", static_cast<char>(ch)); 81fc71a49eSPeter Klausler if (hadError) { 82fc71a49eSPeter Klausler *hadError = true; 83fc71a49eSPeter Klausler } 84fc71a49eSPeter Klausler return 0; 85fc71a49eSPeter Klausler } 86fc71a49eSPeter Klausler int result{0}; 8795696d56Speter klausler while (ch >= '0' && ch <= '9') { 8832334b91SPeter Klausler constexpr int tenth{std::numeric_limits<int>::max() / 10}; 8932334b91SPeter Klausler if (result > tenth || 9032334b91SPeter Klausler ch - '0' > std::numeric_limits<int>::max() - 10 * result) { 913b635714Speter klausler handler.SignalError( 923b635714Speter klausler IostatErrorInFormat, "FORMAT integer field out of range"); 9379f6b812SPeter Klausler if (hadError) { 9479f6b812SPeter Klausler *hadError = true; 9579f6b812SPeter Klausler } 963b635714Speter klausler return result; 9795696d56Speter klausler } 9895696d56Speter klausler result = 10 * result + ch - '0'; 9995696d56Speter klausler if (firstCh) { 10095696d56Speter klausler firstCh = '\0'; 10195696d56Speter klausler } else { 10295696d56Speter klausler ++offset_; 10395696d56Speter klausler } 10495696d56Speter klausler ch = PeekNext(); 10595696d56Speter klausler } 10695696d56Speter klausler if (negate && (result *= -1) > 0) { 1073b635714Speter klausler handler.SignalError( 1083b635714Speter klausler IostatErrorInFormat, "FORMAT integer field out of range"); 10979f6b812SPeter Klausler if (hadError) { 11079f6b812SPeter Klausler *hadError = true; 11179f6b812SPeter Klausler } 11295696d56Speter klausler } 11395696d56Speter klausler return result; 11495696d56Speter klausler } 11595696d56Speter klausler 116c2a95ad2SPeter Klausler // Xn, TRn, TLn 117c2a95ad2SPeter Klausler template <typename CONTEXT> 118c2a95ad2SPeter Klausler static RT_API_ATTRS bool RelativeTabbing(CONTEXT &context, int n) { 119c2a95ad2SPeter Klausler ConnectionState &connection{context.GetConnectionState()}; 120c2a95ad2SPeter Klausler if constexpr (std::is_same_v<CONTEXT, 121c2a95ad2SPeter Klausler ExternalFormattedIoStatementState<Direction::Input>> || 122c2a95ad2SPeter Klausler std::is_same_v<CONTEXT, 123c2a95ad2SPeter Klausler ExternalFormattedIoStatementState<Direction::Output>>) { 124c2a95ad2SPeter Klausler if (n != 0 && connection.isUTF8) { 125c2a95ad2SPeter Klausler const char *p{}; 126c2a95ad2SPeter Klausler if (n > 0) { // Xn or TRn 127c2a95ad2SPeter Klausler // Skip 'n' multi-byte characters. If that's more than are in the 128c2a95ad2SPeter Klausler // current record, that's valid -- the program can position past the 129c2a95ad2SPeter Klausler // end and then reposition back with Tn or TLn. 130c2a95ad2SPeter Klausler std::size_t bytesLeft{context.ViewBytesInRecord(p, true)}; 131c2a95ad2SPeter Klausler for (; n > 0 && bytesLeft && p; --n) { 132c2a95ad2SPeter Klausler std::size_t byteCount{MeasureUTF8Bytes(*p)}; 133c2a95ad2SPeter Klausler if (byteCount > bytesLeft) { 134c2a95ad2SPeter Klausler break; 135c2a95ad2SPeter Klausler } 136c2a95ad2SPeter Klausler context.HandleRelativePosition(byteCount); 137c2a95ad2SPeter Klausler bytesLeft -= byteCount; 138c2a95ad2SPeter Klausler // Don't call GotChar(byteCount), these don't count towards SIZE= 139c2a95ad2SPeter Klausler p += byteCount; 140c2a95ad2SPeter Klausler } 141c2a95ad2SPeter Klausler } else { // n < 0: TLn 142c2a95ad2SPeter Klausler n = -n; 143c2a95ad2SPeter Klausler if (std::int64_t excess{connection.positionInRecord - 144c2a95ad2SPeter Klausler connection.recordLength.value_or(connection.positionInRecord)}; 145c2a95ad2SPeter Klausler excess > 0) { 146c2a95ad2SPeter Klausler // Have tabbed past the end of the record 147c2a95ad2SPeter Klausler if (excess >= n) { 148c2a95ad2SPeter Klausler context.HandleRelativePosition(-n); 149c2a95ad2SPeter Klausler return true; 150c2a95ad2SPeter Klausler } 151c2a95ad2SPeter Klausler context.HandleRelativePosition(-excess); 152c2a95ad2SPeter Klausler n -= excess; 153c2a95ad2SPeter Klausler } 154c2a95ad2SPeter Klausler std::size_t bytesLeft{context.ViewBytesInRecord(p, false)}; 155c2a95ad2SPeter Klausler // Go back 'n' multi-byte characters. 156c2a95ad2SPeter Klausler for (; n > 0 && bytesLeft && p; --n) { 157c2a95ad2SPeter Klausler std::size_t byteCount{MeasurePreviousUTF8Bytes(p, bytesLeft)}; 158c2a95ad2SPeter Klausler context.HandleRelativePosition(-byteCount); 159c2a95ad2SPeter Klausler bytesLeft -= byteCount; 160c2a95ad2SPeter Klausler p -= byteCount; 161c2a95ad2SPeter Klausler } 162c2a95ad2SPeter Klausler } 163c2a95ad2SPeter Klausler } 164c2a95ad2SPeter Klausler } 165c2a95ad2SPeter Klausler if (connection.internalIoCharKind > 1) { 166c2a95ad2SPeter Klausler n *= connection.internalIoCharKind; 167c2a95ad2SPeter Klausler } 168c2a95ad2SPeter Klausler context.HandleRelativePosition(n); 169c2a95ad2SPeter Klausler return true; 170c2a95ad2SPeter Klausler } 171c2a95ad2SPeter Klausler 172c2a95ad2SPeter Klausler // Tn 173c2a95ad2SPeter Klausler template <typename CONTEXT> 174c2a95ad2SPeter Klausler static RT_API_ATTRS bool AbsoluteTabbing(CONTEXT &context, int n) { 175c2a95ad2SPeter Klausler ConnectionState &connection{context.GetConnectionState()}; 176c2a95ad2SPeter Klausler n = n > 0 ? n - 1 : 0; // convert 1-based position to 0-based offset 177c2a95ad2SPeter Klausler if constexpr (std::is_same_v<CONTEXT, 178c2a95ad2SPeter Klausler ExternalFormattedIoStatementState<Direction::Input>> || 179c2a95ad2SPeter Klausler std::is_same_v<CONTEXT, 180c2a95ad2SPeter Klausler ExternalFormattedIoStatementState<Direction::Output>>) { 181c2a95ad2SPeter Klausler if (connection.isUTF8) { 182c2a95ad2SPeter Klausler // Reset to the beginning of the record, then TR(n-1) 183c2a95ad2SPeter Klausler connection.HandleAbsolutePosition(0); 184c2a95ad2SPeter Klausler return RelativeTabbing(context, n); 185c2a95ad2SPeter Klausler } 186c2a95ad2SPeter Klausler } 187c2a95ad2SPeter Klausler if (connection.internalIoCharKind > 1) { 188c2a95ad2SPeter Klausler n *= connection.internalIoCharKind; 189c2a95ad2SPeter Klausler } 190c2a95ad2SPeter Klausler context.HandleAbsolutePosition(n); 191c2a95ad2SPeter Klausler return true; 192c2a95ad2SPeter Klausler } 193c2a95ad2SPeter Klausler 19495696d56Speter klausler template <typename CONTEXT> 1958ebf7411SSlava Zakharin static RT_API_ATTRS void HandleControl( 1968ebf7411SSlava Zakharin CONTEXT &context, char ch, char next, int n) { 19795696d56Speter klausler MutableModes &modes{context.mutableModes()}; 19895696d56Speter klausler switch (ch) { 19995696d56Speter klausler case 'B': 20095696d56Speter klausler if (next == 'Z') { 20195696d56Speter klausler modes.editingFlags |= blankZero; 20295696d56Speter klausler return; 20395696d56Speter klausler } 20495696d56Speter klausler if (next == 'N') { 20595696d56Speter klausler modes.editingFlags &= ~blankZero; 20695696d56Speter klausler return; 20795696d56Speter klausler } 20895696d56Speter klausler break; 20995696d56Speter klausler case 'D': 21095696d56Speter klausler if (next == 'C') { 21195696d56Speter klausler modes.editingFlags |= decimalComma; 21295696d56Speter klausler return; 21395696d56Speter klausler } 21495696d56Speter klausler if (next == 'P') { 21595696d56Speter klausler modes.editingFlags &= ~decimalComma; 21695696d56Speter klausler return; 21795696d56Speter klausler } 21895696d56Speter klausler break; 21995696d56Speter klausler case 'P': 22095696d56Speter klausler if (!next) { 22195696d56Speter klausler modes.scale = n; // kP - decimal scaling by 10**k 22295696d56Speter klausler return; 22395696d56Speter klausler } 22495696d56Speter klausler break; 22595696d56Speter klausler case 'R': 22695696d56Speter klausler switch (next) { 2271f879005STim Keith case 'N': 2281f879005STim Keith modes.round = decimal::RoundNearest; 2291f879005STim Keith return; 2301f879005STim Keith case 'Z': 2311f879005STim Keith modes.round = decimal::RoundToZero; 2321f879005STim Keith return; 2331f879005STim Keith case 'U': 2341f879005STim Keith modes.round = decimal::RoundUp; 2351f879005STim Keith return; 2361f879005STim Keith case 'D': 2371f879005STim Keith modes.round = decimal::RoundDown; 2381f879005STim Keith return; 2391f879005STim Keith case 'C': 2401f879005STim Keith modes.round = decimal::RoundCompatible; 2411f879005STim Keith return; 24295696d56Speter klausler case 'P': 24395696d56Speter klausler modes.round = executionEnvironment.defaultOutputRoundingMode; 24495696d56Speter klausler return; 2451f879005STim Keith default: 2461f879005STim Keith break; 24795696d56Speter klausler } 24895696d56Speter klausler break; 24995696d56Speter klausler case 'X': 250c2a95ad2SPeter Klausler if (!next && RelativeTabbing(context, n)) { 25195696d56Speter klausler return; 25295696d56Speter klausler } 25395696d56Speter klausler break; 25495696d56Speter klausler case 'S': 25595696d56Speter klausler if (next == 'P') { 25695696d56Speter klausler modes.editingFlags |= signPlus; 25795696d56Speter klausler return; 25895696d56Speter klausler } 25995696d56Speter klausler if (!next || next == 'S') { 26095696d56Speter klausler modes.editingFlags &= ~signPlus; 26195696d56Speter klausler return; 26295696d56Speter klausler } 26395696d56Speter klausler break; 26495696d56Speter klausler case 'T': { 26595696d56Speter klausler if (!next) { // Tn 266c2a95ad2SPeter Klausler if (AbsoluteTabbing(context, n)) { 26795696d56Speter klausler return; 26895696d56Speter klausler } 269c2a95ad2SPeter Klausler } else if (next == 'R' || next == 'L') { // TRn / TLn 270c2a95ad2SPeter Klausler if (RelativeTabbing(context, next == 'L' ? -n : n)) { 27195696d56Speter klausler return; 27295696d56Speter klausler } 273c2a95ad2SPeter Klausler } 27495696d56Speter klausler } break; 2751f879005STim Keith default: 2761f879005STim Keith break; 27795696d56Speter klausler } 27895696d56Speter klausler if (next) { 2793b635714Speter klausler context.SignalError(IostatErrorInFormat, 2803b635714Speter klausler "Unknown '%c%c' edit descriptor in FORMAT", ch, next); 28195696d56Speter klausler } else { 2823b635714Speter klausler context.SignalError( 2833b635714Speter klausler IostatErrorInFormat, "Unknown '%c' edit descriptor in FORMAT", ch); 28495696d56Speter klausler } 28595696d56Speter klausler } 28695696d56Speter klausler 28795696d56Speter klausler // Locates the next data edit descriptor in the format. 28895696d56Speter klausler // Handles all repetition counts and control edit descriptors. 28995696d56Speter klausler // Generally assumes that the format string has survived the common 29095696d56Speter klausler // format validator gauntlet. 29195696d56Speter klausler template <typename CONTEXT> 2928ebf7411SSlava Zakharin RT_API_ATTRS int FormatControl<CONTEXT>::CueUpNextDataEdit( 2938ebf7411SSlava Zakharin Context &context, bool stop) { 294a1db3e62SPeter Klausler bool hitUnlimitedLoopEnd{false}; 29504eb93b1SPeter Klausler // Do repetitions remain on an unparenthesized data edit? 29604eb93b1SPeter Klausler while (height_ > 1 && format_[stack_[height_ - 1].start] != '(') { 29704eb93b1SPeter Klausler offset_ = stack_[height_ - 1].start; 29804eb93b1SPeter Klausler int repeat{stack_[height_ - 1].remaining}; 29904eb93b1SPeter Klausler --height_; 30004eb93b1SPeter Klausler if (repeat > 0) { 30104eb93b1SPeter Klausler return repeat; 30204eb93b1SPeter Klausler } 30304eb93b1SPeter Klausler } 30495696d56Speter klausler while (true) { 30571e0261fSSlava Zakharin Fortran::common::optional<int> repeat; 30695696d56Speter klausler bool unlimited{false}; 307c6cb726aSpeter klausler auto maybeReversionPoint{offset_}; 30827505565Speter klausler CharType ch{GetNextChar(context)}; 30995696d56Speter klausler while (ch == ',' || ch == ':') { 31095696d56Speter klausler // Skip commas, and don't complain if they're missing; the format 31195696d56Speter klausler // validator does that. 31295696d56Speter klausler if (stop && ch == ':') { 31395696d56Speter klausler return 0; 31495696d56Speter klausler } 31527505565Speter klausler ch = GetNextChar(context); 31695696d56Speter klausler } 31795696d56Speter klausler if (ch == '-' || ch == '+' || (ch >= '0' && ch <= '9')) { 318fc71a49eSPeter Klausler bool hadSign{ch == '-' || ch == '+'}; 31995696d56Speter klausler repeat = GetIntField(context, ch); 32095696d56Speter klausler ch = GetNextChar(context); 321fc71a49eSPeter Klausler if (hadSign && ch != 'p' && ch != 'P') { 322fc71a49eSPeter Klausler ReportBadFormat(context, 323fc71a49eSPeter Klausler "Invalid FORMAT: signed integer may appear only before 'P", 324fc71a49eSPeter Klausler maybeReversionPoint); 325fc71a49eSPeter Klausler return 0; 326fc71a49eSPeter Klausler } 32795696d56Speter klausler } else if (ch == '*') { 32895696d56Speter klausler unlimited = true; 32995696d56Speter klausler ch = GetNextChar(context); 33095696d56Speter klausler if (ch != '(') { 33104eb93b1SPeter Klausler ReportBadFormat(context, 33204eb93b1SPeter Klausler "Invalid FORMAT: '*' may appear only before '('", 33304eb93b1SPeter Klausler maybeReversionPoint); 3343b635714Speter klausler return 0; 33595696d56Speter klausler } 336fb9ec95cSPeter Klausler if (height_ != 1) { 337fb9ec95cSPeter Klausler ReportBadFormat(context, 338fb9ec95cSPeter Klausler "Invalid FORMAT: '*' must be nested in exactly one set of " 339fb9ec95cSPeter Klausler "parentheses", 340fb9ec95cSPeter Klausler maybeReversionPoint); 341fb9ec95cSPeter Klausler return 0; 342fb9ec95cSPeter Klausler } 34395696d56Speter klausler } 34427505565Speter klausler ch = Capitalize(ch); 34595696d56Speter klausler if (ch == '(') { 34695696d56Speter klausler if (height_ >= maxHeight_) { 34704eb93b1SPeter Klausler ReportBadFormat(context, 34804eb93b1SPeter Klausler "FORMAT stack overflow: too many nested parentheses", 34904eb93b1SPeter Klausler maybeReversionPoint); 3503b635714Speter klausler return 0; 35195696d56Speter klausler } 35295696d56Speter klausler stack_[height_].start = offset_ - 1; // the '(' 353c6cb726aSpeter klausler RUNTIME_CHECK(context, format_[stack_[height_].start] == '('); 35495696d56Speter klausler if (unlimited || height_ == 0) { 35595696d56Speter klausler stack_[height_].remaining = Iteration::unlimited; 35695696d56Speter klausler } else if (repeat) { 35795696d56Speter klausler if (*repeat <= 0) { 35895696d56Speter klausler *repeat = 1; // error recovery 35995696d56Speter klausler } 36095696d56Speter klausler stack_[height_].remaining = *repeat - 1; 36195696d56Speter klausler } else { 36295696d56Speter klausler stack_[height_].remaining = 0; 36395696d56Speter klausler } 364ec4ba0f5SPeter Klausler if (height_ == 1 && !hitEnd_) { 365ec4ba0f5SPeter Klausler // Subtle point (F'2018 13.4 para 9): the last parenthesized group 366c6cb726aSpeter klausler // at height 1 becomes the restart point after control reaches the 367c6cb726aSpeter klausler // end of the format, including its repeat count. 36804eb93b1SPeter Klausler stack_[0].start = maybeReversionPoint; 369c6cb726aSpeter klausler } 37095696d56Speter klausler ++height_; 37195696d56Speter klausler } else if (height_ == 0) { 37204eb93b1SPeter Klausler ReportBadFormat(context, "FORMAT lacks initial '('", maybeReversionPoint); 3733b635714Speter klausler return 0; 37495696d56Speter klausler } else if (ch == ')') { 37595696d56Speter klausler if (height_ == 1) { 376fc71a49eSPeter Klausler hitEnd_ = true; 37795696d56Speter klausler if (stop) { 37895696d56Speter klausler return 0; // end of FORMAT and no data items remain 37995696d56Speter klausler } 38095696d56Speter klausler context.AdvanceRecord(); // implied / before rightmost ) 38195696d56Speter klausler } 38204eb93b1SPeter Klausler auto restart{stack_[height_ - 1].start}; 38304eb93b1SPeter Klausler if (format_[restart] == '(') { 38404eb93b1SPeter Klausler ++restart; 38504eb93b1SPeter Klausler } 38695696d56Speter klausler if (stack_[height_ - 1].remaining == Iteration::unlimited) { 387fb9ec95cSPeter Klausler if (height_ > 1 && GetNextChar(context) != ')') { 388fb9ec95cSPeter Klausler ReportBadFormat(context, 389fb9ec95cSPeter Klausler "Unlimited repetition in FORMAT may not be followed by more " 390fb9ec95cSPeter Klausler "items", 391fb9ec95cSPeter Klausler restart); 392fb9ec95cSPeter Klausler return 0; 393fb9ec95cSPeter Klausler } 394a1db3e62SPeter Klausler if (hitUnlimitedLoopEnd) { 39504eb93b1SPeter Klausler ReportBadFormat(context, 39604eb93b1SPeter Klausler "Unlimited repetition in FORMAT lacks data edit descriptors", 39704eb93b1SPeter Klausler restart); 398fb9ec95cSPeter Klausler return 0; 39995696d56Speter klausler } 400a1db3e62SPeter Klausler hitUnlimitedLoopEnd = true; 401fb9ec95cSPeter Klausler offset_ = restart; 40295696d56Speter klausler } else if (stack_[height_ - 1].remaining-- > 0) { 403c6cb726aSpeter klausler offset_ = restart; 40495696d56Speter klausler } else { 40595696d56Speter klausler --height_; 40695696d56Speter klausler } 40795696d56Speter klausler } else if (ch == '\'' || ch == '"') { 40895696d56Speter klausler // Quoted 'character literal' 40995696d56Speter klausler CharType quote{ch}; 41095696d56Speter klausler auto start{offset_}; 41195696d56Speter klausler while (offset_ < formatLength_ && format_[offset_] != quote) { 41295696d56Speter klausler ++offset_; 41395696d56Speter klausler } 41495696d56Speter klausler if (offset_ >= formatLength_) { 41504eb93b1SPeter Klausler ReportBadFormat(context, 41604eb93b1SPeter Klausler "FORMAT missing closing quote on character literal", 41704eb93b1SPeter Klausler maybeReversionPoint); 4183b635714Speter klausler return 0; 41995696d56Speter klausler } 42095696d56Speter klausler ++offset_; 42195696d56Speter klausler std::size_t chars{ 42295696d56Speter klausler static_cast<std::size_t>(&format_[offset_] - &format_[start])}; 4231a3ac586SPeter Klausler if (offset_ < formatLength_ && format_[offset_] == quote) { 42495696d56Speter klausler // subtle: handle doubled quote character in a literal by including 42595696d56Speter klausler // the first in the output, then treating the second as the start 42695696d56Speter klausler // of another character literal. 42795696d56Speter klausler } else { 42895696d56Speter klausler --chars; 42995696d56Speter klausler } 430bad52055SPeter Klausler EmitAscii(context, format_ + start, chars); 43195696d56Speter klausler } else if (ch == 'H') { 43295696d56Speter klausler // 9HHOLLERITH 43395696d56Speter klausler if (!repeat || *repeat < 1 || offset_ + *repeat > formatLength_) { 43404eb93b1SPeter Klausler ReportBadFormat(context, "Invalid width on Hollerith in FORMAT", 43504eb93b1SPeter Klausler maybeReversionPoint); 4363b635714Speter klausler return 0; 43795696d56Speter klausler } 438bad52055SPeter Klausler EmitAscii(context, format_ + offset_, static_cast<std::size_t>(*repeat)); 43995696d56Speter klausler offset_ += *repeat; 44095696d56Speter klausler } else if (ch >= 'A' && ch <= 'Z') { 44195696d56Speter klausler int start{offset_ - 1}; 442e5746415Speter klausler CharType next{'\0'}; 443e5746415Speter klausler if (ch != 'P') { // 1PE5.2 - comma not required (C1302) 444e5746415Speter klausler CharType peek{Capitalize(PeekNext())}; 445e5746415Speter klausler if (peek >= 'A' && peek <= 'Z') { 446*cd92c425SPeter Klausler if ((ch == 'A' && peek == 'T' /* anticipate F'202X AT editing */) || 447*cd92c425SPeter Klausler ch == 'B' || ch == 'D' || ch == 'E' || ch == 'R' || ch == 'S' || 448*cd92c425SPeter Klausler ch == 'T') { 44944ff4df6SPeter Klausler // Assume a two-letter edit descriptor 450e5746415Speter klausler next = peek; 45195696d56Speter klausler ++offset_; 45244ff4df6SPeter Klausler } else { 45344ff4df6SPeter Klausler // extension: assume a comma between 'ch' and 'peek' 45444ff4df6SPeter Klausler } 455e5746415Speter klausler } 45695696d56Speter klausler } 45743fadefbSpeter klausler if ((!next && 45843fadefbSpeter klausler (ch == 'A' || ch == 'I' || ch == 'B' || ch == 'E' || ch == 'D' || 45943fadefbSpeter klausler ch == 'O' || ch == 'Z' || ch == 'F' || ch == 'G' || 46043fadefbSpeter klausler ch == 'L')) || 46143fadefbSpeter klausler (ch == 'E' && (next == 'N' || next == 'S' || next == 'X')) || 46243fadefbSpeter klausler (ch == 'D' && next == 'T')) { 46395696d56Speter klausler // Data edit descriptor found 46495696d56Speter klausler offset_ = start; 46595696d56Speter klausler return repeat && *repeat > 0 ? *repeat : 1; 46695696d56Speter klausler } else { 46795696d56Speter klausler // Control edit descriptor 46895696d56Speter klausler if (ch == 'T') { // Tn, TLn, TRn 46995696d56Speter klausler repeat = GetIntField(context); 47095696d56Speter klausler } 47195696d56Speter klausler HandleControl(context, static_cast<char>(ch), static_cast<char>(next), 47295696d56Speter klausler repeat ? *repeat : 1); 47395696d56Speter klausler } 47495696d56Speter klausler } else if (ch == '/') { 47595696d56Speter klausler context.AdvanceRecord(repeat && *repeat > 0 ? *repeat : 1); 476cd0a1226Speter klausler } else if (ch == '$' || ch == '\\') { 477cd0a1226Speter klausler context.mutableModes().nonAdvancing = true; 478fbb020fbSpeter klausler } else if (ch == '\t' || ch == '\v') { 479fbb020fbSpeter klausler // Tabs (extension) 480fbb020fbSpeter klausler // TODO: any other raw characters? 481bad52055SPeter Klausler EmitAscii(context, format_ + offset_ - 1, 1); 48295696d56Speter klausler } else { 48304eb93b1SPeter Klausler ReportBadFormat( 48404eb93b1SPeter Klausler context, "Invalid character in FORMAT", maybeReversionPoint); 4853b635714Speter klausler return 0; 48695696d56Speter klausler } 48795696d56Speter klausler } 48895696d56Speter klausler } 48995696d56Speter klausler 49043fadefbSpeter klausler // Returns the next data edit descriptor 49195696d56Speter klausler template <typename CONTEXT> 4928ebf7411SSlava Zakharin RT_API_ATTRS Fortran::common::optional<DataEdit> 4938ebf7411SSlava Zakharin FormatControl<CONTEXT>::GetNextDataEdit(Context &context, int maxRepeat) { 49495696d56Speter klausler int repeat{CueUpNextDataEdit(context)}; 49595696d56Speter klausler auto start{offset_}; 49695696d56Speter klausler DataEdit edit; 497c13f7e17SPeter Klausler edit.modes = context.mutableModes(); 498c13f7e17SPeter Klausler // Handle repeated nonparenthesized edit descriptors 499c13f7e17SPeter Klausler edit.repeat = std::min(repeat, maxRepeat); // 0 if maxRepeat==0 500c13f7e17SPeter Klausler if (repeat > maxRepeat) { 501c13f7e17SPeter Klausler stack_[height_].start = start; // after repeat count 502c13f7e17SPeter Klausler stack_[height_].remaining = repeat - edit.repeat; 503c13f7e17SPeter Klausler ++height_; 50443fadefbSpeter klausler } 505c13f7e17SPeter Klausler edit.descriptor = static_cast<char>(Capitalize(GetNextChar(context))); 506c13f7e17SPeter Klausler if (edit.descriptor == 'D' && Capitalize(PeekNext()) == 'T') { 5077cf1608bSPeter Klausler // DT['iotype'][(v_list)] defined I/O 50843fadefbSpeter klausler edit.descriptor = DataEdit::DefinedDerivedType; 50943fadefbSpeter klausler ++offset_; 51043fadefbSpeter klausler if (auto quote{static_cast<char>(PeekNext())}; 51143fadefbSpeter klausler quote == '\'' || quote == '"') { 51243fadefbSpeter klausler // Capture the quoted 'iotype' 51379f6b812SPeter Klausler bool ok{false}; 51443fadefbSpeter klausler for (++offset_; offset_ < formatLength_;) { 51543fadefbSpeter klausler auto ch{static_cast<char>(format_[offset_++])}; 51643fadefbSpeter klausler if (ch == quote && 51743fadefbSpeter klausler (offset_ == formatLength_ || 51843fadefbSpeter klausler static_cast<char>(format_[offset_]) != quote)) { 51943fadefbSpeter klausler ok = true; 52043fadefbSpeter klausler break; // that was terminating quote 52179f6b812SPeter Klausler } 52279f6b812SPeter Klausler if (edit.ioTypeChars >= edit.maxIoTypeChars) { 52379f6b812SPeter Klausler ReportBadFormat(context, "Excessive DT'iotype' in FORMAT", start); 52471e0261fSSlava Zakharin return Fortran::common::nullopt; 52579f6b812SPeter Klausler } 52643fadefbSpeter klausler edit.ioType[edit.ioTypeChars++] = ch; 52743fadefbSpeter klausler if (ch == quote) { 52895696d56Speter klausler ++offset_; 52995696d56Speter klausler } 53095696d56Speter klausler } 53143fadefbSpeter klausler if (!ok) { 53204eb93b1SPeter Klausler ReportBadFormat(context, "Unclosed DT'iotype' in FORMAT", start); 53371e0261fSSlava Zakharin return Fortran::common::nullopt; 53443fadefbSpeter klausler } 53543fadefbSpeter klausler } 53643fadefbSpeter klausler if (PeekNext() == '(') { 53743fadefbSpeter klausler // Capture the v_list arguments 53879f6b812SPeter Klausler bool ok{false}; 53943fadefbSpeter klausler for (++offset_; offset_ < formatLength_;) { 54079f6b812SPeter Klausler bool hadError{false}; 54179f6b812SPeter Klausler int n{GetIntField(context, '\0', &hadError)}; 54279f6b812SPeter Klausler if (hadError) { 54379f6b812SPeter Klausler ok = false; 54479f6b812SPeter Klausler break; 54543fadefbSpeter klausler } 54679f6b812SPeter Klausler if (edit.vListEntries >= edit.maxVListEntries) { 54779f6b812SPeter Klausler ReportBadFormat(context, "Excessive DT(v_list) in FORMAT", start); 54871e0261fSSlava Zakharin return Fortran::common::nullopt; 54979f6b812SPeter Klausler } 55079f6b812SPeter Klausler edit.vList[edit.vListEntries++] = n; 55143fadefbSpeter klausler auto ch{static_cast<char>(GetNextChar(context))}; 55243fadefbSpeter klausler if (ch != ',') { 55343fadefbSpeter klausler ok = ch == ')'; 55443fadefbSpeter klausler break; 55543fadefbSpeter klausler } 55643fadefbSpeter klausler } 55743fadefbSpeter klausler if (!ok) { 55804eb93b1SPeter Klausler ReportBadFormat(context, "Unclosed DT(v_list) in FORMAT", start); 55971e0261fSSlava Zakharin return Fortran::common::nullopt; 56043fadefbSpeter klausler } 56143fadefbSpeter klausler } 562c13f7e17SPeter Klausler } else { // not DT'iotype' 563c13f7e17SPeter Klausler if (edit.descriptor == 'E') { 564c13f7e17SPeter Klausler if (auto next{static_cast<char>(Capitalize(PeekNext()))}; 565c13f7e17SPeter Klausler next == 'N' || next == 'S' || next == 'X') { 566c13f7e17SPeter Klausler edit.variation = next; 567c13f7e17SPeter Klausler ++offset_; 56843fadefbSpeter klausler } 569c13f7e17SPeter Klausler } 570c13f7e17SPeter Klausler // Width is optional for A[w] in the standard and optional 571c13f7e17SPeter Klausler // for Lw in most compilers. 572c13f7e17SPeter Klausler // Intel & (presumably, from bug report) Fujitsu allow 573c13f7e17SPeter Klausler // a missing 'w' & 'd'/'m' for other edit descriptors -- but not 574c13f7e17SPeter Klausler // 'd'/'m' with a missing 'w' -- and so interpret "(E)" as "(E0)". 575c13f7e17SPeter Klausler if (CharType ch{PeekNext()}; (ch >= '0' && ch <= '9') || ch == '.') { 57695696d56Speter klausler edit.width = GetIntField(context); 577aa77cf90SPeter Klausler if constexpr (std::is_base_of_v<InputStatementState, CONTEXT>) { 578aa77cf90SPeter Klausler if (edit.width.value_or(-1) == 0) { 579aa77cf90SPeter Klausler ReportBadFormat(context, "Input field width is zero", start); 580aa77cf90SPeter Klausler } 581aa77cf90SPeter Klausler } 582c13f7e17SPeter Klausler if (PeekNext() == '.') { 58395696d56Speter klausler ++offset_; 58495696d56Speter klausler edit.digits = GetIntField(context); 585c13f7e17SPeter Klausler if (CharType ch{PeekNext()}; 586c13f7e17SPeter Klausler ch == 'e' || ch == 'E' || ch == 'd' || ch == 'D') { 58795696d56Speter klausler ++offset_; 58895696d56Speter klausler edit.expoDigits = GetIntField(context); 58995696d56Speter klausler } 59095696d56Speter klausler } 591c13f7e17SPeter Klausler } 59295696d56Speter klausler } 59395696d56Speter klausler return edit; 59495696d56Speter klausler } 59595696d56Speter klausler 59695696d56Speter klausler template <typename CONTEXT> 5978ebf7411SSlava Zakharin RT_API_ATTRS void FormatControl<CONTEXT>::Finish(Context &context) { 59895696d56Speter klausler CueUpNextDataEdit(context, true /* stop at colon or end of FORMAT */); 599cc180f4cSPeter Klausler if (freeFormat_) { 600cc180f4cSPeter Klausler FreeMemory(const_cast<CharType *>(format_)); 601cc180f4cSPeter Klausler } 60295696d56Speter klausler } 6031f879005STim Keith } // namespace Fortran::runtime::io 60495696d56Speter klausler #endif // FORTRAN_RUNTIME_FORMAT_IMPLEMENTATION_H_ 605