2020-01-10 00:10:57 +08:00
|
|
|
//===-- 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_
|
|
|
|
|
2020-01-24 08:59:27 +08:00
|
|
|
#include "environment.h"
|
2020-02-05 08:55:45 +08:00
|
|
|
#include "io-error.h"
|
2020-02-25 23:11:52 +08:00
|
|
|
#include "flang/Common/Fortran.h"
|
|
|
|
#include "flang/Decimal/decimal.h"
|
2020-01-10 00:10:57 +08:00
|
|
|
#include <cinttypes>
|
|
|
|
#include <optional>
|
|
|
|
|
2020-01-17 05:51:25 +08:00
|
|
|
namespace Fortran::runtime::io {
|
2020-01-10 00:10:57 +08:00
|
|
|
|
|
|
|
enum EditingFlags {
|
2020-03-29 12:00:16 +08:00
|
|
|
blankZero = 1, // BLANK=ZERO or BZ edit
|
|
|
|
decimalComma = 2, // DECIMAL=COMMA or DC edit
|
|
|
|
signPlus = 4, // SIGN=PLUS or SP edit
|
2020-01-10 00:10:57 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
struct MutableModes {
|
2020-03-29 12:00:16 +08:00
|
|
|
std::uint8_t editingFlags{0}; // BN, DP, SS
|
2020-02-05 08:55:45 +08:00
|
|
|
enum decimal::FortranRounding round{
|
2020-01-24 08:59:27 +08:00
|
|
|
executionEnvironment
|
2020-03-29 12:00:16 +08:00
|
|
|
.defaultOutputRoundingMode}; // RP/ROUND='PROCESSOR_DEFAULT'
|
2020-08-13 07:09:51 +08:00
|
|
|
bool pad{true}; // PAD= mode on READ
|
2020-03-29 12:00:16 +08:00
|
|
|
char delim{'\0'}; // DELIM=
|
|
|
|
short scale{0}; // kP
|
2021-05-06 02:37:49 +08:00
|
|
|
bool inNamelist{false}; // skip ! comments
|
2021-06-29 02:41:04 +08:00
|
|
|
bool nonAdvancing{false}; // ADVANCE='NO', or $ or \ in FORMAT
|
2020-01-10 00:10:57 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// A single edit descriptor extracted from a FORMAT
|
|
|
|
struct DataEdit {
|
2020-03-29 12:00:16 +08:00
|
|
|
char descriptor; // capitalized: one of A, I, B, O, Z, F, E(N/S/X), D, G
|
2020-02-05 08:55:45 +08:00
|
|
|
|
2021-05-06 02:37:49 +08:00
|
|
|
// Special internal data edit descriptors for list-directed & NAMELIST I/O
|
2020-03-29 12:00:16 +08:00
|
|
|
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
|
2020-02-05 08:55:45 +08:00
|
|
|
constexpr bool IsListDirected() const {
|
|
|
|
return descriptor == ListDirected || descriptor == ListDirectedRealPart ||
|
|
|
|
descriptor == ListDirectedImaginaryPart;
|
|
|
|
}
|
2021-10-21 04:56:47 +08:00
|
|
|
constexpr bool IsNamelist() const {
|
|
|
|
return IsListDirected() && modes.inNamelist;
|
|
|
|
}
|
2020-02-05 08:55:45 +08:00
|
|
|
|
2021-06-26 01:40:08 +08:00
|
|
|
static constexpr char DefinedDerivedType{'d'}; // DT user-defined derived type
|
|
|
|
|
2020-03-29 12:00:16 +08:00
|
|
|
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
|
2020-01-10 00:10:57 +08:00
|
|
|
MutableModes modes;
|
|
|
|
int repeat{1};
|
|
|
|
|
2021-06-26 01:40:08 +08:00
|
|
|
// "iotype" &/or "v_list" values for a DT'iotype'(v_list)
|
|
|
|
// user-defined derived type 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];
|
2020-01-10 00:10:57 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
// Generates a sequence of DataEdits from a FORMAT statement or
|
|
|
|
// default-CHARACTER string. Driven by I/O item list processing.
|
2021-06-26 01:40:08 +08:00
|
|
|
// Errors are fatal. See subclause 13.4 in Fortran 2018 for background.
|
2020-03-29 12:00:16 +08:00
|
|
|
template <typename CONTEXT> class FormatControl {
|
2020-01-10 00:10:57 +08:00
|
|
|
public:
|
2020-02-05 08:55:45 +08:00
|
|
|
using Context = CONTEXT;
|
|
|
|
using CharType = typename Context::CharType;
|
|
|
|
|
2020-01-24 08:59:27 +08:00
|
|
|
FormatControl() {}
|
2020-02-05 08:55:45 +08:00
|
|
|
FormatControl(const Terminator &, const CharType *format,
|
|
|
|
std::size_t formatLength, int maxHeight = maxMaxHeight);
|
2020-01-10 00:10:57 +08:00
|
|
|
|
|
|
|
// 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
|
2021-06-26 01:40:08 +08:00
|
|
|
// along the way. If maxRepeat==0, this is a peek at the next data edit
|
|
|
|
// descriptor.
|
2020-02-05 08:55:45 +08:00
|
|
|
DataEdit GetNextDataEdit(Context &, int maxRepeat = 1);
|
2020-01-10 00:10:57 +08:00
|
|
|
|
2020-02-14 06:41:56 +08:00
|
|
|
// Emit any remaining character literals after the last data item (on output)
|
|
|
|
// and perform remaining record positioning actions.
|
|
|
|
void Finish(Context &);
|
2020-01-10 00:10:57 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
static constexpr std::uint8_t maxMaxHeight{100};
|
|
|
|
|
|
|
|
struct Iteration {
|
|
|
|
static constexpr int unlimited{-1};
|
2020-03-29 12:00:16 +08:00
|
|
|
int start{0}; // offset in format_ of '(' or a repeated edit descriptor
|
|
|
|
int remaining{0}; // while >0, decrement and iterate
|
2020-01-10 00:10:57 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
void SkipBlanks() {
|
2022-02-24 08:31:07 +08:00
|
|
|
while (offset_ < formatLength_ &&
|
|
|
|
(format_[offset_] == ' ' || format_[offset_] == '\t' ||
|
|
|
|
format_[offset_] == '\v')) {
|
2020-01-10 00:10:57 +08:00
|
|
|
++offset_;
|
|
|
|
}
|
|
|
|
}
|
2020-02-05 08:55:45 +08:00
|
|
|
CharType PeekNext() {
|
2020-01-10 00:10:57 +08:00
|
|
|
SkipBlanks();
|
|
|
|
return offset_ < formatLength_ ? format_[offset_] : '\0';
|
|
|
|
}
|
2020-02-14 06:41:56 +08:00
|
|
|
CharType GetNextChar(IoErrorHandler &handler) {
|
2020-01-10 00:10:57 +08:00
|
|
|
SkipBlanks();
|
|
|
|
if (offset_ >= formatLength_) {
|
2020-02-14 06:41:56 +08:00
|
|
|
handler.SignalError(
|
|
|
|
IostatErrorInFormat, "FORMAT missing at least one ')'");
|
|
|
|
return '\n';
|
2020-01-10 00:10:57 +08:00
|
|
|
}
|
|
|
|
return format_[offset_++];
|
|
|
|
}
|
2020-02-14 06:41:56 +08:00
|
|
|
int GetIntField(IoErrorHandler &, CharType firstCh = '\0');
|
2020-01-10 00:10:57 +08:00
|
|
|
|
|
|
|
// 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.
|
2020-02-05 08:55:45 +08:00
|
|
|
int CueUpNextDataEdit(Context &, bool stop = false);
|
2020-01-10 00:10:57 +08:00
|
|
|
|
2020-02-05 08:55:45 +08:00
|
|
|
static constexpr CharType Capitalize(CharType ch) {
|
2020-01-10 00:10:57 +08:00
|
|
|
return ch >= 'a' && ch <= 'z' ? ch + 'A' - 'a' : ch;
|
|
|
|
}
|
|
|
|
|
2022-01-20 01:16:07 +08:00
|
|
|
void ReportBadFormat(Context &context, const char *msg, int offset) const {
|
|
|
|
if constexpr (std::is_same_v<CharType, char>) {
|
|
|
|
context.SignalError(IostatErrorInFormat,
|
|
|
|
"%s; at offset %d in format '%s'", msg, offset, format_);
|
|
|
|
} else {
|
|
|
|
context.SignalError(IostatErrorInFormat, "%s; at offset %d", msg, offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-10 00:10:57 +08:00
|
|
|
// 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};
|
2020-02-05 08:55:45 +08:00
|
|
|
const CharType *format_{nullptr};
|
2020-01-24 08:59:27 +08:00
|
|
|
int formatLength_{0};
|
2020-03-29 12:00:16 +08:00
|
|
|
int offset_{0}; // next item is at format_[offset_]
|
2020-01-10 00:10:57 +08:00
|
|
|
|
|
|
|
// must be last, may be incomplete
|
|
|
|
Iteration stack_[maxMaxHeight];
|
|
|
|
};
|
2020-03-29 12:00:16 +08:00
|
|
|
} // namespace Fortran::runtime::io
|
|
|
|
#endif // FORTRAN_RUNTIME_FORMAT_H_
|