FLANG
format.h
1//===-- include/flang/Common/format.h ---------------------------*- 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#ifndef FORTRAN_COMMON_FORMAT_H_
10#define FORTRAN_COMMON_FORMAT_H_
11
12#include "Fortran-consts.h"
13#include "enum-set.h"
14#include <cstring>
15#include <limits>
16
17// Define a FormatValidator class template to validate a format expression
18// of a given CHAR type. To enable use in runtime library code as well as
19// compiler code, the implementation does its own parsing without recourse
20// to compiler parser machinery, and avoids features that require C++ runtime
21// library support. A format expression is a pointer to a fixed size
22// character string, with an explicit length. Class function Check analyzes
23// the expression for syntax and semantic errors and warnings. When an error
24// or warning is found, a caller-supplied reporter function is called, which
25// may request early termination of validation analysis when some threshold
26// number of errors have been reported. If the context is a READ, WRITE,
27// or PRINT statement, rather than a FORMAT statement, statement-specific
28// checks are also done.
29
30namespace Fortran::common {
31
32// AddOverflow and MulOverflow are copied from
33// llvm/include/llvm/Support/MathExtras.h and specialised to int64_t.
34
35// __has_builtin is not defined in some compilers. Make sure it is defined.
36#ifndef __has_builtin
37#define __has_builtin(x) 0
38#endif
39
42static inline bool AddOverflow(
43 std::int64_t x, std::int64_t y, std::int64_t &result) {
44#if __has_builtin(__builtin_add_overflow)
45 return __builtin_add_overflow(x, y, &result);
46#else
47 // Perform the unsigned addition.
48 const std::uint64_t ux{static_cast<std::uint64_t>(x)};
49 const std::uint64_t uy{static_cast<std::uint64_t>(y)};
50 const std::uint64_t uresult{ux + uy};
51
52 // Convert to signed.
53 result = static_cast<std::int64_t>(uresult);
54
55 // Adding two positive numbers should result in a positive number.
56 if (x > 0 && y > 0) {
57 return result <= 0;
58 }
59 // Adding two negatives should result in a negative number.
60 if (x < 0 && y < 0) {
61 return result >= 0;
62 }
63 return false;
64#endif
65}
66
69static inline bool MulOverflow(
70 std::int64_t x, std::int64_t y, std::int64_t &result) {
71#if __has_builtin(__builtin_mul_overflow)
72 return __builtin_mul_overflow(x, y, &result);
73#else
74 // Perform the unsigned multiplication on absolute values.
75 const std::uint64_t ux{x < 0 ? (0 - static_cast<std::uint64_t>(x))
76 : static_cast<std::uint64_t>(x)};
77 const std::uint64_t uy{y < 0 ? (0 - static_cast<std::uint64_t>(y))
78 : static_cast<std::uint64_t>(y)};
79 const std::uint64_t uresult{ux * uy};
80
81 // Convert to signed.
82 const bool isNegative = (x < 0) ^ (y < 0);
83 result = isNegative ? (0 - uresult) : uresult;
84
85 // If any of the args was 0, result is 0 and no overflow occurs.
86 if (ux == 0 || uy == 0) {
87 return false;
88 }
89
90 // ux and uy are in [1, 2^n], where n is the number of digits.
91 // Check how the max allowed absolute value (2^n for negative, 2^(n-1) for
92 // positive) divided by an argument compares to the other.
93 if (isNegative) {
94 return ux >
95 (static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max()) +
96 std::uint64_t{1}) /
97 uy;
98 } else {
99 return ux >
100 (static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) /
101 uy;
102 }
103#endif
104}
105
107 const char *text; // message text; may have one %s argument
108 const char *arg; // optional %s argument value
109 int offset; // offset to message marker
110 int length; // length of message marker
111 bool isError; // vs. warning
112};
113
114// This declaration is logically private to class FormatValidator.
115// It is placed here to work around a clang compilation problem.
116ENUM_CLASS(TokenKind, None, A, B, BN, BZ, D, DC, DP, DT, E, EN, ES, EX, F, G, I,
117 L, O, P, RC, RD, RN, RP, RU, RZ, S, SP, SS, T, TL, TR, X, Z, Colon, Slash,
118 Backslash, // nonstandard: inhibit newline on output
119 Dollar, // nonstandard: inhibit newline on output on terminals
120 Star, LParen, RParen, Comma, Point, Sign,
121 UnsignedInteger, // value in integerValue_
122 String) // char-literal-constant or Hollerith constant
123
124template <typename CHAR = char> class FormatValidator {
125public:
126 using Reporter = std::function<bool(const FormatMessage &)>;
127 FormatValidator(const CHAR *format, size_t length, Reporter reporter,
128 IoStmtKind stmt = IoStmtKind::None)
129 : format_{format}, end_{format + length}, reporter_{reporter},
130 stmt_{stmt}, cursor_{format - 1} {
131 CHECK(format);
132 }
133
134 bool Check();
135 int maxNesting() const { return maxNesting_; }
136
137private:
138 common::EnumSet<TokenKind, TokenKind_enumSize> itemsWithLeadingInts_{
139 TokenKind::A, TokenKind::B, TokenKind::D, TokenKind::DT, TokenKind::E,
140 TokenKind::EN, TokenKind::ES, TokenKind::EX, TokenKind::F, TokenKind::G,
141 TokenKind::I, TokenKind::L, TokenKind::O, TokenKind::P, TokenKind::X,
142 TokenKind::Z, TokenKind::Slash, TokenKind::LParen};
143
144 struct Token {
145 Token &set_kind(TokenKind kind) {
146 kind_ = kind;
147 return *this;
148 }
149 Token &set_offset(int offset) {
150 offset_ = offset;
151 return *this;
152 }
153 Token &set_length(int length) {
154 length_ = length;
155 return *this;
156 }
157
158 TokenKind kind() const { return kind_; }
159 int offset() const { return offset_; }
160 int length() const { return length_; }
161
162 bool IsSet() { return kind_ != TokenKind::None; }
163
164 private:
165 TokenKind kind_{TokenKind::None};
166 int offset_{0};
167 int length_{1};
168 };
169
170 void ReportWarning(const char *text) { ReportWarning(text, token_); }
171 void ReportWarning(
172 const char *text, Token &token, const char *arg = nullptr) {
173 FormatMessage msg{
174 text, arg ? arg : argString_, token.offset(), token.length(), false};
175 reporterExit_ |= reporter_(msg);
176 }
177
178 void ReportError(const char *text) { ReportError(text, token_); }
179 void ReportError(const char *text, Token &token, const char *arg = nullptr) {
180 if (suppressMessageCascade_) {
181 return;
182 }
183 formatHasErrors_ = true;
184 suppressMessageCascade_ = true;
185 FormatMessage msg{
186 text, arg ? arg : argString_, token.offset(), token.length(), true};
187 reporterExit_ |= reporter_(msg);
188 }
189
190 void SetLength() { SetLength(token_); }
191 void SetLength(Token &token) {
192 token.set_length(cursor_ - format_ - token.offset() + (cursor_ < end_));
193 }
194
195 CHAR NextChar();
196 CHAR LookAheadChar();
197 void Advance(TokenKind);
198 void NextToken();
199
200 void check_r(bool allowed = true);
201 bool check_w();
202 void check_m();
203 bool check_d(bool checkScaleFactor = false);
204 void check_k();
205 void check_e();
206
207 const CHAR *const format_; // format text
208 const CHAR *const end_; // one-past-last of format_ text
209 Reporter reporter_;
210 IoStmtKind stmt_;
211
212 const CHAR *cursor_{}; // current location in format_
213 const CHAR *laCursor_{}; // lookahead cursor
214 Token previousToken_{};
215 Token token_{}; // current token
216 Token knrToken_{}; // k, n, or r UnsignedInteger token
217 Token scaleFactorToken_{}; // most recent scale factor token P
218 std::int64_t integerValue_{-1}; // value of UnsignedInteger token
219 std::int64_t knrValue_{-1}; // -1 ==> not present
220 std::int64_t scaleFactorValue_{}; // signed k in kP
221 std::int64_t wValue_{-1};
222 char argString_[3]{}; // 1-2 character msg arg; usually edit descriptor name
223 bool formatHasErrors_{false};
224 bool unterminatedFormatError_{false};
225 bool suppressMessageCascade_{false};
226 bool reporterExit_{false};
227 int maxNesting_{0}; // max level of nested parentheses
228};
229
230template <typename CHAR> static inline bool IsWhite(CHAR c) {
231 // White space. ' ' is standard. Other characters are extensions.
232 // Extension candidates:
233 // '\t' (horizontal tab)
234 // '\n' (new line)
235 // '\v' (vertical tab)
236 // '\f' (form feed)
237 // '\r' (carriage ret)
238 return c == ' ' || c == '\t' || c == '\v';
239}
240
241template <typename CHAR> CHAR FormatValidator<CHAR>::NextChar() {
242 for (++cursor_; cursor_ < end_; ++cursor_) {
243 if (!IsWhite(*cursor_)) {
244 return toupper(*cursor_);
245 }
246 }
247 cursor_ = end_; // don't allow cursor_ > end_
248 return ' ';
249}
250
251template <typename CHAR> CHAR FormatValidator<CHAR>::LookAheadChar() {
252 for (laCursor_ = cursor_ + 1; laCursor_ < end_; ++laCursor_) {
253 if (!IsWhite(*laCursor_)) {
254 return toupper(*laCursor_);
255 }
256 }
257 laCursor_ = end_; // don't allow laCursor_ > end_
258 return ' ';
259}
260
261// After a call to LookAheadChar, set token kind and advance cursor to laCursor.
262template <typename CHAR> void FormatValidator<CHAR>::Advance(TokenKind tk) {
263 cursor_ = laCursor_;
264 token_.set_kind(tk);
265}
266
267template <typename CHAR> void FormatValidator<CHAR>::NextToken() {
268 // At entry, cursor_ points before the start of the next token.
269 // At exit, cursor_ points to last CHAR of token_.
270
271 previousToken_ = token_;
272 CHAR c{NextChar()};
273 token_.set_kind(TokenKind::None);
274 token_.set_offset(cursor_ - format_);
275 token_.set_length(1);
276 if (c == '_' && integerValue_ >= 0) { // C1305, C1309, C1310, C1312, C1313
277 ReportError("Kind parameter '_' character in format expression");
278 }
279 integerValue_ = -1;
280
281 switch (c) {
282 case '0':
283 case '1':
284 case '2':
285 case '3':
286 case '4':
287 case '5':
288 case '6':
289 case '7':
290 case '8':
291 case '9': {
292 const CHAR *lastCursor{};
293 integerValue_ = 0;
294 bool overflow{false};
295 do {
296 lastCursor = cursor_;
297 if (!overflow) {
298 overflow = MulOverflow(
299 static_cast<std::int64_t>(10), integerValue_, integerValue_);
300 }
301 if (!overflow) {
302 overflow = AddOverflow(
303 integerValue_, static_cast<std::int64_t>(c - '0'), integerValue_);
304 }
305 c = NextChar();
306 } while (c >= '0' && c <= '9');
307 cursor_ = lastCursor;
308 token_.set_kind(TokenKind::UnsignedInteger);
309 if (overflow) {
310 SetLength();
311 ReportError("Integer overflow in format expression");
312 break;
313 }
314 if (LookAheadChar() != 'H') {
315 break;
316 }
317 // Hollerith constant
318 if (laCursor_ + integerValue_ < end_) {
319 token_.set_kind(TokenKind::String);
320 cursor_ = laCursor_ + integerValue_;
321 } else {
322 token_.set_kind(TokenKind::None);
323 cursor_ = end_;
324 }
325 SetLength();
326 if (stmt_ == IoStmtKind::Read) { // 13.3.2p6
327 ReportError("'H' edit descriptor in READ format expression");
328 } else if (token_.kind() == TokenKind::None) {
329 ReportError("Unterminated 'H' edit descriptor");
330 } else {
331 ReportWarning("Legacy 'H' edit descriptor");
332 }
333 break;
334 }
335 case 'A':
336 token_.set_kind(TokenKind::A);
337 break;
338 case 'B':
339 switch (LookAheadChar()) {
340 case 'N':
341 Advance(TokenKind::BN);
342 break;
343 case 'Z':
344 Advance(TokenKind::BZ);
345 break;
346 default:
347 token_.set_kind(TokenKind::B);
348 break;
349 }
350 break;
351 case 'D':
352 switch (LookAheadChar()) {
353 case 'C':
354 Advance(TokenKind::DC);
355 break;
356 case 'P':
357 Advance(TokenKind::DP);
358 break;
359 case 'T':
360 Advance(TokenKind::DT);
361 break;
362 default:
363 token_.set_kind(TokenKind::D);
364 break;
365 }
366 break;
367 case 'E':
368 switch (LookAheadChar()) {
369 case 'N':
370 Advance(TokenKind::EN);
371 break;
372 case 'S':
373 Advance(TokenKind::ES);
374 break;
375 case 'X':
376 Advance(TokenKind::EX);
377 break;
378 default:
379 token_.set_kind(TokenKind::E);
380 break;
381 }
382 break;
383 case 'F':
384 token_.set_kind(TokenKind::F);
385 break;
386 case 'G':
387 token_.set_kind(TokenKind::G);
388 break;
389 case 'I':
390 token_.set_kind(TokenKind::I);
391 break;
392 case 'L':
393 token_.set_kind(TokenKind::L);
394 break;
395 case 'O':
396 token_.set_kind(TokenKind::O);
397 break;
398 case 'P':
399 token_.set_kind(TokenKind::P);
400 break;
401 case 'R':
402 switch (LookAheadChar()) {
403 case 'C':
404 Advance(TokenKind::RC);
405 break;
406 case 'D':
407 Advance(TokenKind::RD);
408 break;
409 case 'N':
410 Advance(TokenKind::RN);
411 break;
412 case 'P':
413 Advance(TokenKind::RP);
414 break;
415 case 'U':
416 Advance(TokenKind::RU);
417 break;
418 case 'Z':
419 Advance(TokenKind::RZ);
420 break;
421 default:
422 token_.set_kind(TokenKind::None);
423 break;
424 }
425 break;
426 case 'S':
427 switch (LookAheadChar()) {
428 case 'P':
429 Advance(TokenKind::SP);
430 break;
431 case 'S':
432 Advance(TokenKind::SS);
433 break;
434 default:
435 token_.set_kind(TokenKind::S);
436 break;
437 }
438 break;
439 case 'T':
440 switch (LookAheadChar()) {
441 case 'L':
442 Advance(TokenKind::TL);
443 break;
444 case 'R':
445 Advance(TokenKind::TR);
446 break;
447 default:
448 token_.set_kind(TokenKind::T);
449 break;
450 }
451 break;
452 case 'X':
453 token_.set_kind(TokenKind::X);
454 break;
455 case 'Z':
456 token_.set_kind(TokenKind::Z);
457 break;
458 case '-':
459 case '+':
460 token_.set_kind(TokenKind::Sign);
461 break;
462 case '/':
463 token_.set_kind(TokenKind::Slash);
464 break;
465 case '(':
466 token_.set_kind(TokenKind::LParen);
467 break;
468 case ')':
469 token_.set_kind(TokenKind::RParen);
470 break;
471 case '.':
472 token_.set_kind(TokenKind::Point);
473 break;
474 case ':':
475 token_.set_kind(TokenKind::Colon);
476 break;
477 case '\\':
478 token_.set_kind(TokenKind::Backslash);
479 break;
480 case '$':
481 token_.set_kind(TokenKind::Dollar);
482 break;
483 case '*':
484 token_.set_kind(LookAheadChar() == '(' ? TokenKind::Star : TokenKind::None);
485 break;
486 case ',': {
487 token_.set_kind(TokenKind::Comma);
488 CHAR laChar = LookAheadChar();
489 if (laChar == ',') {
490 Advance(TokenKind::Comma);
491 token_.set_offset(cursor_ - format_);
492 ReportError("Unexpected ',' in format expression");
493 } else if (laChar == ')') {
494 ReportError("Unexpected ',' before ')' in format expression");
495 }
496 break;
497 }
498 case '\'':
499 case '"':
500 for (++cursor_; cursor_ < end_; ++cursor_) {
501 if (*cursor_ == c) {
502 if (auto nc{cursor_ + 1}; nc < end_ && *nc != c) {
503 token_.set_kind(TokenKind::String);
504 break;
505 }
506 ++cursor_;
507 }
508 }
509 SetLength();
510 if (token_.kind() != TokenKind::String) {
511 ReportError("Unterminated string");
512 } else if (stmt_ == IoStmtKind::Read &&
513 previousToken_.kind() != TokenKind::DT) { // 13.3.2p6
514 ReportWarning("String edit descriptor in READ format expression");
515 }
516 break;
517 default:
518 if (cursor_ >= end_ && !unterminatedFormatError_) {
519 suppressMessageCascade_ = false;
520 ReportError("Unterminated format expression");
521 unterminatedFormatError_ = true;
522 }
523 token_.set_kind(TokenKind::None);
524 break;
525 }
526
527 SetLength();
528}
529
530template <typename CHAR> void FormatValidator<CHAR>::check_r(bool allowed) {
531 if (!allowed && knrValue_ >= 0) {
532 ReportError("Repeat specifier before '%s' edit descriptor", knrToken_);
533 } else if (knrValue_ == 0) {
534 ReportError("'%s' edit descriptor repeat specifier must be positive",
535 knrToken_); // C1304
536 }
537}
538
539// Return the predicate "w value is present" to control further processing.
540template <typename CHAR> bool FormatValidator<CHAR>::check_w() {
541 if (token_.kind() == TokenKind::UnsignedInteger) {
542 wValue_ = integerValue_;
543 if (wValue_ == 0) {
544 if (*argString_ == 'A' || stmt_ == IoStmtKind::Read) {
545 // C1306, 13.7.2.1p6
546 ReportError("'%s' edit descriptor 'w' value must be positive");
547 } else if (*argString_ == 'L') {
548 ReportWarning("'%s' edit descriptor 'w' value should be positive");
549 }
550 }
551 NextToken();
552 return true;
553 }
554 if (*argString_ != 'A' && *argString_ != 'L') {
555 ReportWarning("Expected '%s' edit descriptor 'w' value"); // C1306
556 }
557 return false;
558}
559
560template <typename CHAR> void FormatValidator<CHAR>::check_m() {
561 if (token_.kind() != TokenKind::Point) {
562 return;
563 }
564 NextToken();
565 if (token_.kind() != TokenKind::UnsignedInteger) {
566 ReportError("Expected '%s' edit descriptor 'm' value after '.'");
567 return;
568 }
569 if ((stmt_ == IoStmtKind::Print || stmt_ == IoStmtKind::Write) &&
570 wValue_ > 0 && integerValue_ > wValue_) { // 13.7.2.2p5, 13.7.2.4p6
571 ReportError("'%s' edit descriptor 'm' value is greater than 'w' value");
572 }
573 NextToken();
574}
575
576// Return the predicate "d value is present" to control further processing.
577template <typename CHAR>
578bool FormatValidator<CHAR>::check_d(bool checkScaleFactor) {
579 if (token_.kind() != TokenKind::Point) {
580 ReportError("Expected '%s' edit descriptor '.d' value");
581 return false;
582 }
583 NextToken();
584 if (token_.kind() != TokenKind::UnsignedInteger) {
585 ReportError("Expected '%s' edit descriptor 'd' value after '.'");
586 return false;
587 }
588 if (checkScaleFactor) {
589 check_k();
590 }
591 NextToken();
592 return true;
593}
594
595// Check the value of scale factor k against a field width d.
596template <typename CHAR> void FormatValidator<CHAR>::check_k() {
597 // Limit the check to D and E edit descriptors in output statements that
598 // explicitly set the scale factor.
599 if (stmt_ != IoStmtKind::Print && stmt_ != IoStmtKind::Write) {
600 return;
601 }
602 if (!scaleFactorToken_.IsSet()) {
603 return;
604 }
605 // 13.7.2.3.3p5 - The values of d and k must satisfy:
606 // −d < k <= 0; or
607 // 0 < k < d+2
608 const int64_t d{integerValue_};
609 const int64_t k{scaleFactorValue_};
610 // Exception: d = k = 0 is nonstandard, but has a reasonable interpretation.
611 if (d == 0 && k == 0) {
612 return;
613 }
614 if (k <= 0 && !(-d < k)) {
615 ReportError("Negative scale factor k (from kP) and width d in a '%s' "
616 "edit descriptor must satisfy '-d < k'");
617 } else if (k > 0 && !(k < d + 2)) {
618 ReportError("Positive scale factor k (from kP) and width d in a '%s' "
619 "edit descriptor must satisfy 'k < d+2'");
620 }
621}
622
623template <typename CHAR> void FormatValidator<CHAR>::check_e() {
624 if (token_.kind() != TokenKind::E) {
625 return;
626 }
627 NextToken();
628 if (token_.kind() != TokenKind::UnsignedInteger) {
629 ReportError("Expected '%s' edit descriptor 'e' value after 'E'");
630 return;
631 }
632 NextToken();
633}
634
635template <typename CHAR> bool FormatValidator<CHAR>::Check() {
636 if (!*format_) {
637 ReportError("Empty format expression");
638 return formatHasErrors_;
639 }
640 NextToken();
641 if (token_.kind() != TokenKind::LParen) {
642 ReportError("Format expression must have an initial '('");
643 return formatHasErrors_;
644 }
645 NextToken();
646
647 int nestLevel{0}; // Outer level ()s are at level 0.
648 Token starToken{}; // unlimited format token
649 bool hasDataEditDesc{false};
650
651 // Subject to error recovery exceptions, a loop iteration processes one
652 // edit descriptor or does list management. The loop terminates when
653 // - a level-0 right paren is processed (format may be valid)
654 // - the end of an incomplete format is reached
655 // - the error reporter requests termination (error threshold reached)
656 while (!reporterExit_) {
657 Token signToken{};
658 knrValue_ = -1; // -1 ==> not present
659 wValue_ = -1;
660 bool commaRequired{true};
661
662 if (token_.kind() == TokenKind::Sign) {
663 signToken = token_;
664 NextToken();
665 }
666 if (token_.kind() == TokenKind::UnsignedInteger) {
667 knrToken_ = token_;
668 knrValue_ = integerValue_;
669 NextToken();
670 }
671 if (signToken.IsSet() && (knrValue_ < 0 || token_.kind() != TokenKind::P)) {
672 argString_[0] = format_[signToken.offset()];
673 argString_[1] = 0;
674 ReportError("Unexpected '%s' in format expression", signToken);
675 }
676 // Default message argument.
677 // Alphabetic edit descriptor names are one or two characters in length.
678 argString_[0] = toupper(format_[token_.offset()]);
679 argString_[1] = token_.length() > 1 ? toupper(*cursor_) : 0;
680 // Process one format edit descriptor or do format list management.
681 switch (token_.kind()) {
682 case TokenKind::A:
683 // R1307 data-edit-desc -> A [w]
684 hasDataEditDesc = true;
685 check_r();
686 NextToken();
687 check_w();
688 break;
689 case TokenKind::B:
690 case TokenKind::I:
691 case TokenKind::O:
692 case TokenKind::Z:
693 // R1307 data-edit-desc -> B w [. m] | I w [. m] | O w [. m] | Z w [. m]
694 hasDataEditDesc = true;
695 check_r();
696 NextToken();
697 if (check_w()) {
698 check_m();
699 }
700 break;
701 case TokenKind::D:
702 case TokenKind::F: {
703 // R1307 data-edit-desc -> D w . d | F w . d
704 bool isD{token_.kind() == TokenKind::D};
705 hasDataEditDesc = true;
706 check_r();
707 NextToken();
708 if (check_w()) {
709 check_d(/*checkScaleFactor=*/isD);
710 }
711 break;
712 }
713 case TokenKind::E:
714 case TokenKind::EN:
715 case TokenKind::ES:
716 case TokenKind::EX: {
717 // R1307 data-edit-desc ->
718 // E w . d [E e] | EN w . d [E e] | ES w . d [E e] | EX w . d [E e]
719 bool isE{token_.kind() == TokenKind::E};
720 hasDataEditDesc = true;
721 check_r();
722 NextToken();
723 if (check_w() && check_d(/*checkScaleFactor=*/isE)) {
724 check_e();
725 }
726 break;
727 }
728 case TokenKind::G:
729 // R1307 data-edit-desc -> G w [. d [E e]]
730 hasDataEditDesc = true;
731 check_r();
732 NextToken();
733 if (check_w()) {
734 if (wValue_ > 0) {
735 if (check_d()) { // C1307
736 check_e();
737 }
738 } else if (token_.kind() == TokenKind::Point && check_d() &&
739 token_.kind() == TokenKind::E) { // C1308
740 ReportError("A 'G0' edit descriptor must not have an 'e' value");
741 NextToken();
742 if (token_.kind() == TokenKind::UnsignedInteger) {
743 NextToken();
744 }
745 }
746 }
747 break;
748 case TokenKind::L:
749 // R1307 data-edit-desc -> L w
750 hasDataEditDesc = true;
751 check_r();
752 NextToken();
753 check_w();
754 break;
755 case TokenKind::DT:
756 // R1307 data-edit-desc -> DT [char-literal-constant] [( v-list )]
757 hasDataEditDesc = true;
758 check_r();
759 NextToken();
760 if (token_.kind() == TokenKind::String) {
761 NextToken();
762 }
763 if (token_.kind() == TokenKind::LParen) {
764 do {
765 NextToken();
766 if (token_.kind() == TokenKind::Sign) {
767 NextToken();
768 }
769 if (token_.kind() != TokenKind::UnsignedInteger) {
770 ReportError(
771 "Expected integer constant in 'DT' edit descriptor v-list");
772 break;
773 }
774 NextToken();
775 } while (token_.kind() == TokenKind::Comma);
776 if (token_.kind() != TokenKind::RParen) {
777 ReportError("Expected ',' or ')' in 'DT' edit descriptor v-list");
778 while (cursor_ < end_ && token_.kind() != TokenKind::RParen) {
779 NextToken();
780 }
781 }
782 NextToken();
783 }
784 break;
785 case TokenKind::String:
786 // R1304 data-edit-desc -> char-string-edit-desc
787 if (knrValue_ >= 0) {
788 ReportError("Repeat specifier before character string edit descriptor",
789 knrToken_);
790 }
791 NextToken();
792 break;
793 case TokenKind::BN:
794 case TokenKind::BZ:
795 case TokenKind::DC:
796 case TokenKind::DP:
797 case TokenKind::RC:
798 case TokenKind::RD:
799 case TokenKind::RN:
800 case TokenKind::RP:
801 case TokenKind::RU:
802 case TokenKind::RZ:
803 case TokenKind::S:
804 case TokenKind::SP:
805 case TokenKind::SS:
806 // R1317 sign-edit-desc -> SS | SP | S
807 // R1318 blank-interp-edit-desc -> BN | BZ
808 // R1319 round-edit-desc -> RU | RD | RZ | RN | RC | RP
809 // R1320 decimal-edit-desc -> DC | DP
810 check_r(false);
811 NextToken();
812 break;
813 case TokenKind::P: {
814 // R1313 control-edit-desc -> k P
815 if (knrValue_ < 0) {
816 ReportError("'P' edit descriptor must have a scale factor");
817 } else {
818 scaleFactorToken_ = knrToken_;
819 if (signToken.IsSet() && format_[signToken.offset()] == '-') {
820 scaleFactorValue_ = -knrValue_;
821 } else {
822 scaleFactorValue_ = knrValue_;
823 }
824 }
825 // Diagnosing C1302 may require multiple token lookahead.
826 // Save current cursor position to enable backup.
827 const CHAR *saveCursor{cursor_};
828 NextToken();
829 if (token_.kind() == TokenKind::UnsignedInteger) {
830 NextToken();
831 }
832 switch (token_.kind()) {
833 case TokenKind::D:
834 case TokenKind::E:
835 case TokenKind::EN:
836 case TokenKind::ES:
837 case TokenKind::EX:
838 case TokenKind::F:
839 case TokenKind::G:
840 commaRequired = false;
841 break;
842 default:;
843 }
844 cursor_ = saveCursor;
845 NextToken();
846 break;
847 }
848 case TokenKind::T:
849 case TokenKind::TL:
850 case TokenKind::TR:
851 // R1315 position-edit-desc -> T n | TL n | TR n
852 check_r(false);
853 NextToken();
854 if (integerValue_ <= 0) { // C1311
855 ReportError("'%s' edit descriptor must have a positive position value");
856 }
857 NextToken();
858 break;
859 case TokenKind::X:
860 // R1315 position-edit-desc -> n X
861 if (knrValue_ == 0) { // C1311
862 ReportError("'X' edit descriptor must have a positive position value",
863 knrToken_);
864 } else if (knrValue_ < 0) {
865 ReportWarning(
866 "'X' edit descriptor must have a positive position value");
867 }
868 NextToken();
869 break;
870 case TokenKind::Colon:
871 // R1313 control-edit-desc -> :
872 check_r(false);
873 commaRequired = false;
874 NextToken();
875 break;
876 case TokenKind::Slash:
877 // R1313 control-edit-desc -> [r] /
878 commaRequired = false;
879 NextToken();
880 break;
881 case TokenKind::Backslash:
882 check_r(false);
883 ReportWarning("Non-standard '\\' edit descriptor");
884 NextToken();
885 break;
886 case TokenKind::Dollar:
887 check_r(false);
888 ReportWarning("Non-standard '$' edit descriptor");
889 NextToken();
890 break;
891 case TokenKind::Star:
892 // NextToken assigns a token kind of Star only if * is followed by (.
893 // So the next token is guaranteed to be LParen.
894 if (nestLevel > 0) {
895 ReportError("Nested unlimited format item list");
896 }
897 starToken = token_;
898 if (knrValue_ >= 0) {
899 ReportError(
900 "Repeat specifier before unlimited format item list", knrToken_);
901 }
902 hasDataEditDesc = false;
903 NextToken();
904 [[fallthrough]];
905 case TokenKind::LParen:
906 if (knrValue_ == 0) {
907 ReportError("List repeat specifier must be positive", knrToken_);
908 }
909 if (++nestLevel > maxNesting_) {
910 maxNesting_ = nestLevel;
911 }
912 break;
913 case TokenKind::RParen:
914 if (knrValue_ >= 0) {
915 ReportError("Unexpected integer constant", knrToken_);
916 }
917 do {
918 if (nestLevel == 0) {
919 // Any characters after level-0 ) are ignored.
920 return formatHasErrors_; // normal exit (may have messages)
921 }
922 if (nestLevel == 1 && starToken.IsSet() && !hasDataEditDesc) {
923 SetLength(starToken);
924 ReportError( // C1303
925 "Unlimited format item list must contain a data edit descriptor",
926 starToken);
927 }
928 --nestLevel;
929 NextToken();
930 } while (token_.kind() == TokenKind::RParen);
931 if (nestLevel == 0 && starToken.IsSet()) {
932 ReportError("Character in format after unlimited format item list");
933 }
934 break;
935 case TokenKind::Comma:
936 if (knrValue_ >= 0) {
937 ReportError("Unexpected integer constant", knrToken_);
938 }
939 if (suppressMessageCascade_ || reporterExit_) {
940 break;
941 }
942 [[fallthrough]];
943 default:
944 ReportError("Unexpected '%s' in format expression");
945 NextToken();
946 }
947
948 // Process comma separator and exit an incomplete format.
949 switch (token_.kind()) {
950 case TokenKind::Colon: // Comma not required; token not yet processed.
951 case TokenKind::Slash: // Comma not required; token not yet processed.
952 case TokenKind::RParen: // Comma not allowed; token not yet processed.
953 suppressMessageCascade_ = false;
954 break;
955 case TokenKind::LParen: // Comma not allowed; token already processed.
956 case TokenKind::Comma: // Normal comma case; move past token.
957 suppressMessageCascade_ = false;
958 NextToken();
959 break;
960 case TokenKind::Sign: // Error; main switch has a better message.
961 case TokenKind::None: // Error; token not yet processed.
962 if (cursor_ >= end_) {
963 return formatHasErrors_; // incomplete format error exit
964 }
965 break;
966 default:
967 // Possible first token of the next format item; token not yet processed.
968 if (commaRequired) {
969 const char *s{"Expected ',' or ')' in format expression"}; // C1302
970 if (previousToken_.kind() == TokenKind::UnsignedInteger &&
971 previousToken_.length() > 1 &&
972 itemsWithLeadingInts_.test(token_.kind())) {
973 // F10.32F10.3 is ambiguous, F10.3F10.3 is not
974 ReportError(s);
975 } else {
976 ReportWarning(s);
977 }
978 }
979 }
980 }
981
982 return formatHasErrors_; // error reporter (message threshold) exit
983}
984
985} // namespace Fortran::common
986#endif // FORTRAN_COMMON_FORMAT_H_
Definition bit-population-count.h:20
Definition format.h:106