summaryrefslogtreecommitdiff
path: root/flang/runtime/format.h
blob: c072b3b9805d45a18776304671504cfc477a74ee (plain)
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
//===-- 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 "terminator.h"
#include "flang/common/Fortran.h"
#include "flang/decimal/decimal.h"
#include <cinttypes>
#include <optional>

namespace Fortran::runtime::io {

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{false};  // PAD= mode on READ
  char delim{'\0'};  // DELIM=
  short scale{0};  // kP
};

// 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 to distinguish list-directed 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)"
  constexpr bool IsListDirected() const {
    return descriptor == ListDirected || descriptor == ListDirectedRealPart ||
        descriptor == ListDirectedImaginaryPart;
  }

  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};
};

// FormatControl<A> requires that A have these member functions;
// these default implementations just crash if called.
struct DefaultFormatControlCallbacks : public IoErrorHandler {
  using IoErrorHandler::IoErrorHandler;
  DataEdit GetNextDataEdit(int = 1);
  bool Emit(const char *, std::size_t);
  bool Emit(const char16_t *, std::size_t);
  bool Emit(const char32_t *, std::size_t);
  bool AdvanceRecord(int = 1);
  bool HandleAbsolutePosition(std::int64_t);
  bool HandleRelativePosition(std::int64_t);
};

// Generates a sequence of DataEdits from a FORMAT statement or
// default-CHARACTER string.  Driven by I/O item list processing.
// Errors are fatal.  See clause 13.4 in Fortran 2018 for background.
template<typename CONTEXT> class FormatControl {
public:
  using Context = CONTEXT;
  using CharType = typename Context::CharType;

  FormatControl() {}
  FormatControl(const Terminator &, const CharType *format,
      std::size_t formatLength, int maxHeight = maxMaxHeight);

  // Determines the max parenthesis nesting level by scanning and validating
  // the FORMAT string.
  static int GetMaxParenthesisNesting(
      const Terminator &, const CharType *format, std::size_t formatLength);

  // 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.
  DataEdit GetNextDataEdit(Context &, int maxRepeat = 1);

  // Emit any remaining character literals after the last data item.
  void FinishOutput(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_] == ' ') {
      ++offset_;
    }
  }
  CharType PeekNext() {
    SkipBlanks();
    return offset_ < formatLength_ ? format_[offset_] : '\0';
  }
  CharType GetNextChar(const Terminator &terminator) {
    SkipBlanks();
    if (offset_ >= formatLength_) {
      terminator.Crash("FORMAT missing at least one ')'");
    }
    return format_[offset_++];
  }
  int GetIntField(const Terminator &, CharType firstCh = '\0');

  // 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;
  }

  // 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};
  const CharType *format_{nullptr};
  int formatLength_{0};
  int offset_{0};  // next item is at format_[offset_]

  // must be last, may be incomplete
  Iteration stack_[maxMaxHeight];
};
}
#endif  // FORTRAN_RUNTIME_FORMAT_H_