1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
|
//===-- runtime/format.h ----------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// FORMAT string processing
#ifndef FORTRAN_RUNTIME_FORMAT_H_
#define FORTRAN_RUNTIME_FORMAT_H_
#include "environment.h"
#include "io-error.h"
#include "flang/Common/Fortran.h"
#include "flang/Decimal/decimal.h"
#include <cinttypes>
#include <optional>
namespace Fortran::runtime {
class Descriptor;
} // namespace Fortran::runtime
namespace Fortran::runtime::io {
class IoStatementState;
enum EditingFlags {
blankZero = 1, // BLANK=ZERO or BZ edit
decimalComma = 2, // DECIMAL=COMMA or DC edit
signPlus = 4, // SIGN=PLUS or SP edit
};
struct MutableModes {
std::uint8_t editingFlags{0}; // BN, DP, SS
enum decimal::FortranRounding round{
executionEnvironment
.defaultOutputRoundingMode}; // RP/ROUND='PROCESSOR_DEFAULT'
bool pad{true}; // PAD= mode on READ
char delim{'\0'}; // DELIM=
short scale{0}; // kP
bool inNamelist{false}; // skip ! comments
bool nonAdvancing{false}; // ADVANCE='NO', or $ or \ in FORMAT
};
// A single edit descriptor extracted from a FORMAT
struct DataEdit {
char descriptor; // capitalized: one of A, I, B, O, Z, F, E(N/S/X), D, G
// Special internal data edit descriptors for list-directed & NAMELIST I/O
static constexpr char ListDirected{'g'}; // non-COMPLEX list-directed
static constexpr char ListDirectedRealPart{'r'}; // emit "(r," or "(r;"
static constexpr char ListDirectedImaginaryPart{'z'}; // emit "z)"
static constexpr char ListDirectedNullValue{'n'}; // see 13.10.3.2
constexpr bool IsListDirected() const {
return descriptor == ListDirected || descriptor == ListDirectedRealPart ||
descriptor == ListDirectedImaginaryPart;
}
constexpr bool IsNamelist() const {
return IsListDirected() && modes.inNamelist;
}
static constexpr char DefinedDerivedType{'d'}; // DT defined I/O
char variation{'\0'}; // N, S, or X for EN, ES, EX
std::optional<int> width; // the 'w' field; optional for A
std::optional<int> digits; // the 'm' or 'd' field
std::optional<int> expoDigits; // 'Ee' field
MutableModes modes;
int repeat{1};
// "iotype" &/or "v_list" values for a DT'iotype'(v_list)
// defined I/O data edit descriptor
static constexpr std::size_t maxIoTypeChars{32};
static constexpr std::size_t maxVListEntries{4};
std::uint8_t ioTypeChars{0};
std::uint8_t vListEntries{0};
char ioType[maxIoTypeChars];
int vList[maxVListEntries];
};
// Generates a sequence of DataEdits from a FORMAT statement or
// default-CHARACTER string. Driven by I/O item list processing.
// Errors are fatal. See subclause 13.4 in Fortran 2018 for background.
template <typename CONTEXT> class FormatControl {
public:
using Context = CONTEXT;
using CharType = char; // formats are always default kind CHARACTER
FormatControl() {}
FormatControl(const Terminator &, const CharType *format,
std::size_t formatLength, const Descriptor *formatDescriptor = nullptr,
int maxHeight = maxMaxHeight);
// For attempting to allocate in a user-supplied stack area
static std::size_t GetNeededSize(int maxHeight) {
return sizeof(FormatControl) -
sizeof(Iteration) * (maxMaxHeight - maxHeight);
}
// Extracts the next data edit descriptor, handling control edit descriptors
// along the way. If maxRepeat==0, this is a peek at the next data edit
// descriptor.
std::optional<DataEdit> GetNextDataEdit(Context &, int maxRepeat = 1);
// Emit any remaining character literals after the last data item (on output)
// and perform remaining record positioning actions.
void Finish(Context &);
private:
static constexpr std::uint8_t maxMaxHeight{100};
struct Iteration {
static constexpr int unlimited{-1};
int start{0}; // offset in format_ of '(' or a repeated edit descriptor
int remaining{0}; // while >0, decrement and iterate
};
void SkipBlanks() {
while (offset_ < formatLength_ &&
(format_[offset_] == ' ' || format_[offset_] == '\t' ||
format_[offset_] == '\v')) {
++offset_;
}
}
CharType PeekNext() {
SkipBlanks();
return offset_ < formatLength_ ? format_[offset_] : '\0';
}
CharType GetNextChar(IoErrorHandler &handler) {
SkipBlanks();
if (offset_ >= formatLength_) {
if (formatLength_ == 0) {
handler.SignalError(
IostatErrorInFormat, "Empty or badly assigned FORMAT");
} else {
handler.SignalError(
IostatErrorInFormat, "FORMAT missing at least one ')'");
}
return '\n';
}
return format_[offset_++];
}
int GetIntField(
IoErrorHandler &, CharType firstCh = '\0', bool *hadError = nullptr);
// Advances through the FORMAT until the next data edit
// descriptor has been found; handles control edit descriptors
// along the way. Returns the repeat count that appeared
// before the descriptor (defaulting to 1) and leaves offset_
// pointing to the data edit.
int CueUpNextDataEdit(Context &, bool stop = false);
static constexpr CharType Capitalize(CharType ch) {
return ch >= 'a' && ch <= 'z' ? ch + 'A' - 'a' : ch;
}
void ReportBadFormat(Context &context, const char *msg, int offset) const {
if constexpr (std::is_same_v<CharType, char>) {
// Echo the bad format in the error message, but trim any leading or
// trailing spaces.
int firstNonBlank{0};
while (firstNonBlank < formatLength_ && format_[firstNonBlank] == ' ') {
++firstNonBlank;
}
int lastNonBlank{formatLength_ - 1};
while (lastNonBlank > firstNonBlank && format_[lastNonBlank] == ' ') {
--lastNonBlank;
}
if (firstNonBlank <= lastNonBlank) {
context.SignalError(IostatErrorInFormat,
"%s; at offset %d in format '%.*s'", msg, offset,
lastNonBlank - firstNonBlank + 1, format_ + firstNonBlank);
return;
}
}
context.SignalError(IostatErrorInFormat, "%s; at offset %d", msg, offset);
}
// Data members are arranged and typed so as to reduce size.
// This structure may be allocated in stack space loaned by the
// user program for internal I/O.
const std::uint8_t maxHeight_{maxMaxHeight};
std::uint8_t height_{0};
bool freeFormat_{false};
const CharType *format_{nullptr};
int formatLength_{0}; // in units of characters
int offset_{0}; // next item is at format_[offset_]
// must be last, may be incomplete
Iteration stack_[maxMaxHeight];
};
} // namespace Fortran::runtime::io
#endif // FORTRAN_RUNTIME_FORMAT_H_
|