1 //===-- runtime/format.cpp --------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "format.h" 10 #include "io-stmt.h" 11 #include "flang/common/format.h" 12 #include "flang/decimal/decimal.h" 13 #include <limits> 14 15 namespace Fortran::runtime::io { 16 17 // Default FormatContext virtual member functions 18 void FormatContext::Emit(const char *, std::size_t) { 19 Crash("Cannot emit data from this FORMAT string"); 20 } 21 void FormatContext::Emit(const char16_t *, std::size_t) { 22 Crash("Cannot emit data from this FORMAT string"); 23 } 24 void FormatContext::Emit(const char32_t *, std::size_t) { 25 Crash("Cannot emit data from this FORMAT string"); 26 } 27 void FormatContext::HandleSlash(int) { 28 Crash("A / control edit descriptor may not appear in this FORMAT string"); 29 } 30 void FormatContext::HandleAbsolutePosition(int) { 31 Crash("A Tn control edit descriptor may not appear in this FORMAT string"); 32 } 33 void FormatContext::HandleRelativePosition(int) { 34 Crash("An nX, TLn, or TRn control edit descriptor may not appear in this " 35 "FORMAT string"); 36 } 37 38 template<typename CHAR> 39 FormatControl<CHAR>::FormatControl(Terminator &terminator, const CHAR *format, 40 std::size_t formatLength, int maxHeight) 41 : maxHeight_{static_cast<std::uint8_t>(maxHeight)}, format_{format}, 42 formatLength_{static_cast<int>(formatLength)} { 43 // The additional two items are for the whole string and a 44 // repeated non-parenthesized edit descriptor. 45 if (maxHeight > std::numeric_limits<std::int8_t>::max()) { 46 terminator.Crash("internal Fortran runtime error: maxHeight %d", maxHeight); 47 } 48 stack_[0].start = offset_; 49 stack_[0].remaining = Iteration::unlimited; // 13.4(8) 50 } 51 52 template<typename CHAR> 53 int FormatControl<CHAR>::GetMaxParenthesisNesting( 54 Terminator &terminator, const CHAR *format, std::size_t formatLength) { 55 using Validator = common::FormatValidator<CHAR>; 56 typename Validator::Reporter reporter{ 57 [&](const common::FormatMessage &message) { 58 terminator.Crash(message.text, message.arg); 59 return false; // crashes on error above 60 }}; 61 Validator validator{format, formatLength, reporter}; 62 validator.Check(); 63 return validator.maxNesting(); 64 } 65 66 template<typename CHAR> 67 int FormatControl<CHAR>::GetIntField(Terminator &terminator, CHAR firstCh) { 68 CHAR ch{firstCh ? firstCh : PeekNext()}; 69 if (ch != '-' && ch != '+' && (ch < '0' || ch > '9')) { 70 terminator.Crash( 71 "Invalid FORMAT: integer expected at '%c'", static_cast<char>(ch)); 72 } 73 int result{0}; 74 bool negate{ch == '-'}; 75 if (negate) { 76 firstCh = '\0'; 77 ch = PeekNext(); 78 } 79 while (ch >= '0' && ch <= '9') { 80 if (result > 81 std::numeric_limits<int>::max() / 10 - (static_cast<int>(ch) - '0')) { 82 terminator.Crash("FORMAT integer field out of range"); 83 } 84 result = 10 * result + ch - '0'; 85 if (firstCh) { 86 firstCh = '\0'; 87 } else { 88 ++offset_; 89 } 90 ch = PeekNext(); 91 } 92 if (negate && (result *= -1) > 0) { 93 terminator.Crash("FORMAT integer field out of range"); 94 } 95 return result; 96 } 97 98 static void HandleControl( 99 FormatContext &context, std::uint16_t &scale, char ch, char next, int n) { 100 MutableModes &modes{context.mutableModes()}; 101 switch (ch) { 102 case 'B': 103 if (next == 'Z') { 104 modes.editingFlags |= blankZero; 105 return; 106 } 107 if (next == 'N') { 108 modes.editingFlags &= ~blankZero; 109 return; 110 } 111 break; 112 case 'D': 113 if (next == 'C') { 114 modes.editingFlags |= decimalComma; 115 return; 116 } 117 if (next == 'P') { 118 modes.editingFlags &= ~decimalComma; 119 return; 120 } 121 break; 122 case 'P': 123 if (!next) { 124 scale = n; // kP - decimal scaling by 10**k (TODO) 125 return; 126 } 127 break; 128 case 'R': 129 switch (next) { 130 case 'N': modes.roundingMode = common::RoundingMode::TiesToEven; return; 131 case 'Z': modes.roundingMode = common::RoundingMode::ToZero; return; 132 case 'U': modes.roundingMode = common::RoundingMode::Up; return; 133 case 'D': modes.roundingMode = common::RoundingMode::Down; return; 134 case 'C': 135 modes.roundingMode = common::RoundingMode::TiesAwayFromZero; 136 return; 137 default: break; 138 } 139 break; 140 case 'X': 141 if (!next) { 142 context.HandleRelativePosition(n); 143 return; 144 } 145 break; 146 case 'S': 147 if (next == 'P') { 148 modes.editingFlags |= signPlus; 149 return; 150 } 151 if (!next || next == 'S') { 152 modes.editingFlags &= ~signPlus; 153 return; 154 } 155 break; 156 case 'T': { 157 if (!next) { // Tn 158 context.HandleAbsolutePosition(n); 159 return; 160 } 161 if (next == 'L' || next == 'R') { // TLn & TRn 162 context.HandleRelativePosition(next == 'L' ? -n : n); 163 return; 164 } 165 } break; 166 default: break; 167 } 168 if (next) { 169 context.Crash("Unknown '%c%c' edit descriptor in FORMAT", ch, next); 170 } else { 171 context.Crash("Unknown '%c' edit descriptor in FORMAT", ch); 172 } 173 } 174 175 // Locates the next data edit descriptor in the format. 176 // Handles all repetition counts and control edit descriptors. 177 // Generally assumes that the format string has survived the common 178 // format validator gauntlet. 179 template<typename CHAR> 180 int FormatControl<CHAR>::CueUpNextDataEdit(FormatContext &context, bool stop) { 181 int unlimitedLoopCheck{-1}; 182 while (true) { 183 std::optional<int> repeat; 184 bool unlimited{false}; 185 CHAR ch{Capitalize(GetNextChar(context))}; 186 while (ch == ',' || ch == ':') { 187 // Skip commas, and don't complain if they're missing; the format 188 // validator does that. 189 if (stop && ch == ':') { 190 return 0; 191 } 192 ch = Capitalize(GetNextChar(context)); 193 } 194 if (ch == '-' || ch == '+' || (ch >= '0' && ch <= '9')) { 195 repeat = GetIntField(context, ch); 196 ch = GetNextChar(context); 197 } else if (ch == '*') { 198 unlimited = true; 199 ch = GetNextChar(context); 200 if (ch != '(') { 201 context.Crash("Invalid FORMAT: '*' may appear only before '('"); 202 } 203 } 204 if (ch == '(') { 205 if (height_ >= maxHeight_) { 206 context.Crash("FORMAT stack overflow: too many nested parentheses"); 207 } 208 stack_[height_].start = offset_ - 1; // the '(' 209 if (unlimited || height_ == 0) { 210 stack_[height_].remaining = Iteration::unlimited; 211 unlimitedLoopCheck = offset_ - 1; 212 } else if (repeat) { 213 if (*repeat <= 0) { 214 *repeat = 1; // error recovery 215 } 216 stack_[height_].remaining = *repeat - 1; 217 } else { 218 stack_[height_].remaining = 0; 219 } 220 ++height_; 221 } else if (height_ == 0) { 222 context.Crash("FORMAT lacks initial '('"); 223 } else if (ch == ')') { 224 if (height_ == 1) { 225 if (stop) { 226 return 0; // end of FORMAT and no data items remain 227 } 228 context.HandleSlash(); // implied / before rightmost ) 229 } 230 if (stack_[height_ - 1].remaining == Iteration::unlimited) { 231 offset_ = stack_[height_ - 1].start + 1; 232 if (offset_ == unlimitedLoopCheck) { 233 context.Crash( 234 "Unlimited repetition in FORMAT lacks data edit descriptors"); 235 } 236 } else if (stack_[height_ - 1].remaining-- > 0) { 237 offset_ = stack_[height_ - 1].start + 1; 238 } else { 239 --height_; 240 } 241 } else if (ch == '\'' || ch == '"') { 242 // Quoted 'character literal' 243 CHAR quote{ch}; 244 auto start{offset_}; 245 while (offset_ < formatLength_ && format_[offset_] != quote) { 246 ++offset_; 247 } 248 if (offset_ >= formatLength_) { 249 context.Crash("FORMAT missing closing quote on character literal"); 250 } 251 ++offset_; 252 std::size_t chars{ 253 static_cast<std::size_t>(&format_[offset_] - &format_[start])}; 254 if (PeekNext() == quote) { 255 // subtle: handle doubled quote character in a literal by including 256 // the first in the output, then treating the second as the start 257 // of another character literal. 258 } else { 259 --chars; 260 } 261 context.Emit(format_ + start, chars); 262 } else if (ch == 'H') { 263 // 9HHOLLERITH 264 if (!repeat || *repeat < 1 || offset_ + *repeat > formatLength_) { 265 context.Crash("Invalid width on Hollerith in FORMAT"); 266 } 267 context.Emit(format_ + offset_, static_cast<std::size_t>(*repeat)); 268 offset_ += *repeat; 269 } else if (ch >= 'A' && ch <= 'Z') { 270 int start{offset_ - 1}; 271 CHAR next{Capitalize(PeekNext())}; 272 if (next < 'A' || next > 'Z') { 273 next = '\0'; 274 } 275 if (ch == 'E' || 276 (!next && 277 (ch == 'A' || ch == 'I' || ch == 'B' || ch == 'O' || ch == 'Z' || 278 ch == 'F' || ch == 'D' || ch == 'G'))) { 279 // Data edit descriptor found 280 offset_ = start; 281 return repeat && *repeat > 0 ? *repeat : 1; 282 } else { 283 // Control edit descriptor 284 if (ch == 'T') { // Tn, TLn, TRn 285 repeat = GetIntField(context); 286 } 287 HandleControl(context, scale_, static_cast<char>(ch), 288 static_cast<char>(next), repeat && *repeat > 0 ? *repeat : 1); 289 } 290 } else if (ch == '/') { 291 context.HandleSlash(repeat && *repeat > 0 ? *repeat : 1); 292 } else { 293 context.Crash("Invalid character '%c' in FORMAT", static_cast<char>(ch)); 294 } 295 } 296 } 297 298 template<typename CHAR> 299 void FormatControl<CHAR>::GetNext( 300 FormatContext &context, DataEdit &edit, int maxRepeat) { 301 302 // TODO: DT editing 303 304 // Return the next data edit descriptor 305 int repeat{CueUpNextDataEdit(context)}; 306 auto start{offset_}; 307 edit.descriptor = static_cast<char>(Capitalize(GetNextChar(context))); 308 if (edit.descriptor == 'E') { 309 edit.variation = static_cast<char>(Capitalize(PeekNext())); 310 if (edit.variation >= 'A' && edit.variation <= 'Z') { 311 ++offset_; 312 } else { 313 edit.variation = '\0'; 314 } 315 } else { 316 edit.variation = '\0'; 317 } 318 319 edit.width = GetIntField(context); 320 edit.modes = context.mutableModes(); 321 if (PeekNext() == '.') { 322 ++offset_; 323 edit.digits = GetIntField(context); 324 CHAR ch{PeekNext()}; 325 if (ch == 'e' || ch == 'E' || ch == 'd' || ch == 'D') { 326 ++offset_; 327 edit.expoDigits = GetIntField(context); 328 } else { 329 edit.expoDigits.reset(); 330 } 331 } else { 332 edit.digits.reset(); 333 edit.expoDigits.reset(); 334 } 335 336 // Handle repeated nonparenthesized edit descriptors 337 if (repeat > 1) { 338 stack_[height_].start = start; // after repeat count 339 stack_[height_].remaining = repeat; // full count 340 ++height_; 341 } 342 edit.repeat = 1; 343 if (height_ > 1) { 344 int start{stack_[height_ - 1].start}; 345 if (format_[start] != '(') { 346 if (stack_[height_ - 1].remaining > maxRepeat) { 347 edit.repeat = maxRepeat; 348 stack_[height_ - 1].remaining -= maxRepeat; 349 offset_ = start; // repeat same edit descriptor next time 350 } else { 351 edit.repeat = stack_[height_ - 1].remaining; 352 --height_; 353 } 354 } 355 } 356 } 357 358 template<typename CHAR> 359 void FormatControl<CHAR>::FinishOutput(FormatContext &context) { 360 CueUpNextDataEdit(context, true /* stop at colon or end of FORMAT */); 361 } 362 363 template class FormatControl<char>; 364 template class FormatControl<char16_t>; 365 template class FormatControl<char32_t>; 366 } 367